
- Request - Response 흐름
사용자의 요청이 서버에 도착하면, DispatcherServlet이 해당 요청을 처리한다. 이때, 요청과 응답에 대한 정보는 각각HttpServletRequest와 HttpServletResponse에 저장.
Filter
Filter는 요청과 응답에 대한 전처리와 후처리를 담당.LoggerFilter는 요청과 응답에 대한 로깅을 수행. 요청과 응답의 헤더 정보, 바디 정보 등을 로그로 남긴다.Interceptor
Interceptor는 컨트롤러의 실행 전후에 특정 로직을 실행.AuthorizationInterceptor는 요청 URL의 로그를 남기고, 특정 HTTP 메소드나 리소스에 대한 요청이면 통과시킨다.Controller
실제 비즈니스 로직을 처리하는 부분이다.AccountApiController는/api/account/meURL로 요청이 오면 사용자 정보를 반환하는 로직을 수행한다. 이때, 예외가 발생하면ApiException을 발생시킨다.Exception Handler
발생한 예외를 특정한 방식으로 처리하는 역할.ApiExceptionHandler는ApiException이 발생하면 해당 예외에 맞는 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
반응형
'Programming > Java, Spring' 카테고리의 다른 글
| 스프링 캐싱 메커니즘에서 @Cacheable과 @Cacheput 차이점은 ? (0) | 2023.12.13 |
|---|---|
| Spring Webflux - 배경, 개념 Cpu bound vs I/O Bound, block vs non-block, mvc vs webflux (1) | 2023.10.16 |
| Spring에서 argumentResolver를 사용하여 인증 책임 분리하기 (0) | 2023.07.23 |
| 스프링 프로젝트 API Server Error 처리하기 (0) | 2023.07.09 |
| StringBuilder와 String 클래스의 문자열 만드는 효율 차이 (0) | 2023.06.21 |