반응형

 

요새 날이 넘 춥네요..

 

안그래도 추운 날씨에 밖에 나가기가 무서워진 요즘..

 

Handle이 고장한 8톤 트럭,,도 아닌데, throws를 선언해도 처리되지 않았다며 우기는 Controller와 화해한 이야기 풉니다,,

 

 

1. 문제의 코드

@GetMapping("/touch")
Mono<?> touch(@RequestParam(name = "queue", defaultValue = "default") String queue,
      @RequestParam(name = "user_id") Long userId,
      ServerWebExchange exchange) throws NoSuchAlgorithmException {
    return Mono.defer(() -> userQueueService.generateToken(queue, userId))
        .map(token -> {
          exchange.getResponse().addCookie(
              ResponseCookie.from("user-queue-%s-token".formatted(queue), token)
                  .maxAge(Duration.ofSeconds(300))
                  .path("/")
                  .build()
          );

          return token;
        });
  }
  
public Mono<String> generateToken(final String queue, final Long userId) throws NoSuchAlgorithmException{
    MessageDigest digest = MessageDigest.getInstance("SHA-256");
    String input = "user-queue-%s-%d".formatted(queue, userId);
    byte[] encodedHash = digest.digest(input.getBytes(StandardCharsets.UTF_8));

    StringBuilder hexString = new StringBuilder();
    for (byte b : encodedHash) {
      hexString.append(String.format("%02x", b));
    }
    
    return Mono.just(hexString.toString());
}

 

 

2. 문제의 상황

generateToken 메서드는 SHA-256 암호화 알고리즘을 이용해서 토큰값을 생성합니다.

MessageDigest 클래스의 getInstance를 사용하려면 Checked Exception인 NoSuchAlgorithmException에 대한 처리를 해줘야 합니다.

 

generateToken()를 호출하는 "/touch" API 또한 NoSuchAlgorithmException이 발생한다고 throws를 적으면 NoSuchAlgorithmException이 Unhandled exception이라며 오류가 발생합니다^^!

 

 

3. 문제의 원인

Spring WebFlux에서 Mono 또는 Flux 내부에서 발생하는 Checked 예외는 반드시 Mono.error() 또는 onErrorResume() 등을 통해 처리해야 합니다.

⭐ 즉, 비동기 코드(WebFlux)에서는 throws가 먹히지 않고 Mono.error(e)를 사용해야 합니다.

 

 

4. 해결 방법

Mono.defer() 내부에서 예외를 처리
@GetMapping("/touch")
Mono<?> touch(@RequestParam(name = "queue", defaultValue = "default") String queue,
              @RequestParam(name = "user_id") Long userId,
              ServerWebExchange exchange) {
    return Mono.defer(() -> {
        try {
            return userQueueService.generateToken(queue, userId);
        } catch (NoSuchAlgorithmException e) {
            return Mono.error(e);
        }
    }).map(token -> {
        exchange.getResponse().addCookie(
            ResponseCookie.from("user-queue-%s-token".formatted(queue), token)
                .maxAge(Duration.ofSeconds(300))
                .path("/")
                .build()
        );
        
        return token;
    });
}

 

Checked 예외를 Unchecked 예외로 변경
public Mono<String> generateToken(final String queue, final Long userId) {
    MessageDigest digest = null;
    try {
        digest = MessageDigest.getInstance("SHA-256");
    } catch (NoSuchAlgorithmException e) {
        throw new RuntimeException(e);
    }

    String input = "user-queue-%s-%d".formatted(queue, userId);
    byte[] encodedHash = digest.digest(input.getBytes(StandardCharsets.UTF_8));

    StringBuilder hexString = new StringBuilder();
    for (byte b : encodedHash) {
        hexString.append(String.format("%02x", b));
    }
    return Mono.just(hexString.toString());
}

 

 

🤗 정리

예외 유형 Mono에서 처리 방식
Checked Exception (NoSuchAlgorithmException) Mono.error(e)를 써야 Mono 체인에서 처리 가능
Unchecked Exception (RuntimeException)
따로 감싸지 않아도 Mono 체인에서 자동으로 예외 감지

 

 

🚀 결론

예외를 클라이언트에게 전달할 필요가 있을 경우 → Mono.error(e)로 감싸서 처리하는 것이 좋음
500 에러로 내보내면 충분할 경우 → try-catch 후 RuntimeException 던지는 것이 더 간결하고 좋음

 

 

 

📑

참고 자료

Chat GPT

https://fastcampus.co.kr/dev_online_traffic_data

 

9개 프로젝트로 경험하는 대용량 트래픽 & 데이터 처리 완벽 마스터하기 | 패스트캠퍼스

실무에서 자주 일어나는 대용량 트래픽 & 데이터 처리 업무를 한번에 마스터할 수 있도록 모든 것을 담았습니다. 대기업 & 빅테크 현업 강사진 8인과 함께 하는 고퀄리티 현업 대비형 강의! 타사

fastcampus.co.kr

 

 

반응형