본문 바로가기
Programming/Java, Spring

스프링 웹 환경에서 요청 응답 플로우 (request, filter, interceptor, controller, exceptionHandler)

by Renechoi 2023. 10. 16.

  1. Request - Response 흐름

사용자의 요청이 서버에 도착하면, DispatcherServlet이 해당 요청을 처리한다. 이때, 요청과 응답에 대한 정보는 각각HttpServletRequestHttpServletResponse에 저장.

  1. Filter
    Filter는 요청과 응답에 대한 전처리와 후처리를 담당. LoggerFilter는 요청과 응답에 대한 로깅을 수행. 요청과 응답의 헤더 정보, 바디 정보 등을 로그로 남긴다.

  2. Interceptor
    Interceptor는 컨트롤러의 실행 전후에 특정 로직을 실행. AuthorizationInterceptor 는 요청 URL의 로그를 남기고, 특정 HTTP 메소드나 리소스에 대한 요청이면 통과시킨다.

  3. Controller
    실제 비즈니스 로직을 처리하는 부분이다. AccountApiController/api/account/me URL로 요청이 오면 사용자 정보를 반환하는 로직을 수행한다. 이때, 예외가 발생하면 ApiException을 발생시킨다.

  4. Exception Handler
    발생한 예외를 특정한 방식으로 처리하는 역할. ApiExceptionHandlerApiException이 발생하면 해당 예외에 맞는 HTTP 상태 코드와 에러 설명을 응답으로 반환한다.


@Slf4j
@Component
public class LoggerFilter implements Filter {

   @Override
   public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

      var req = new ContentCachingRequestWrapper( (HttpServletRequest) request );
      var res = new ContentCachingResponseWrapper( (HttpServletResponse) response );

      log.info("INIT URI : {}", req.getRequestURI());

      chain.doFilter(req, res);


      // request 정보
      var headerNames = req.getHeaderNames();
      var headerValues = new StringBuilder();

      headerNames.asIterator().forEachRemaining(headerKey ->{
         var headerValue = req.getHeader(headerKey);

         // authorization-token : ??? , user-agent : ???
         headerValues
            .append("[")
            .append(headerKey)
            .append(" : ")
            .append(headerValue)
            .append("] ");
      });

      var requestBody = new String(req.getContentAsByteArray());
      var uri = req.getRequestURI();
      var method = req.getMethod();

      log.info(">>>>> uri : {} , method : {} , header : {} , body : {}", uri, method, headerValues, requestBody);


      // response 정보
      var responseHeaderValues = new StringBuilder();

      res.getHeaderNames().forEach(headerKey ->{
         var headerValue = res.getHeader(headerKey);

         responseHeaderValues
            .append("[")
            .append(headerKey)
            .append(" : ")
            .append(headerValue)
            .append("] ");
      });

      var responseBody = new String(res.getContentAsByteArray());

      log.info("<<<<< uri : {} , method : {} , header : {} , body : {}", uri, method, responseHeaderValues, responseBody);


      res.copyBodyToResponse();
   }
}

@Slf4j
@RequiredArgsConstructor
@Component
public class AuthorizationInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.info("Authorization Interceptor url : {}", request.getRequestURI());

        // WEB ,chrome 의 경우 GET, POST OPTIONS = pass
        if(HttpMethod.OPTIONS.matches(request.getMethod())){
            return true;
        }

        // js. html. png resource 를 요청하는 경우 = pass
        if(handler instanceof ResourceHttpRequestHandler){
            return true;
        }

        // TODO header 검증




        return true;    // 일단 통과 처리
    }
}

@RequiredArgsConstructor
@RestController
@RequestMapping("/api/account")
public class AccountApiController {

    private final AccountRepository accountRepository;

    @GetMapping("/me")
    public Api<AccountMeResponse> me(){

        var response = AccountMeResponse.builder()
            .name("홍길동")
            .email("A@gmail.com")
            .registeredAt(LocalDateTime.now())
            .build();

        var str = "안녕하세요";
        var age = 0;
        try{
            Integer.parseInt(str);
        }catch (Exception e){
            throw new ApiException(ErrorCode.SERVER_ERROR, e , "사용자 Me 호출시 에러 발생");
        }


        return Api.OK(response);
    }
}

@Slf4j
@RestControllerAdvice
@Order(value = Integer.MIN_VALUE)   // 최우선처리
public class ApiExceptionHandler {

    @ExceptionHandler(value = ApiException.class)
    public ResponseEntity<Api<Object>> apiException(ApiException apiException){
        log.error("", apiException);

        var errorCode = apiException.getErrorCodeIfs();

        return ResponseEntity
            .status(errorCode.getHttpStatusCode())
            .body(Api.ERROR(errorCode, apiException.getErrorDescription()));

    }
}


reference : 패스트캠퍼스 시그니처 백엔드 chapter 4

반응형