본문 바로가기
프로그래밍/Project

[Project01] MiniBoard(2) - ObjectMapper, SwaggerUI, Filter 설정

by 코딩중독 2024. 1. 25.

목차

    ObjectMapper 설정

    Object(객체) -> Json(문자열) 형태로 만들어 내는 직렬화와 Json(문자열) -> Object(객체)로 만들어내는 역직렬화 처리를 할 때 사용

    프로젝트 Api에서 변수는 카멜케이스를 사용하고 있는데 클라이언트로의 응답을 내려줄 때 Json에서의 Key는 스네이크케이스를 사용한다.

    그렇기 때문에 클라이언트로 응답을 주는 모델(DTO)에

    @JsonNaming(value = PropertyNamingStrategies.SnakeCaseStrategy.class) 어노테이션을 달아주고 사용을 하게 되는데 매번 해당 어노테이션을 달아주는 번거로움을 덜어주기 위함 + 추가 설정이 가능하다.

    @Configuration
    public class ObjectMapperConfig {
    
        @Bean
        public ObjectMapper objectMapper() {
            var objectMapper = new ObjectMapper();
    
            objectMapper.registerModule(new Jdk8Module());  // jdk 8 버전 이후에 나온 클래스 처리
    
            objectMapper.registerModule(new JavaTimeModule());  // LocalDate Series
    
            objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);   // json field 에 다른 property 포함되었을 때 무시하고 진행, 에러 X
    
            objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);    // 직렬화 하는 과정에서 발생할 수 있는 문제를 방지
    
            objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);   // 날짜 관련 직렬화
    
            objectMapper.setPropertyNamingStrategy(new PropertyNamingStrategies.SnakeCaseStrategy());   // 스네이크 케이스 사용
    
            return objectMapper;
        }
    
    }

     

    SwaggerUI 설정 (ModelResolver 재정의)

    위에서 설정한 ObjectMapper를 주입받아서 재정의 한다.

    @Configuration
    public class SwaggerConfig {
    
        @Bean
        public ModelResolver modelResolver(ObjectMapper objectMapper) { // ObjectMapperConfig 의 Bean 이 주입됨
            return new ModelResolver(objectMapper);
        }
    
    }

     

    SwaggerUI에서 응답 json을 스네이크케이스로 적용, boolean의 키에 is가 생략되는 문제를 방지한다.

     

    Filter 설정 (Request, Response에 대한 Log 추가)

    프로젝트가 실행 되는동안 Request, Response에 대한 로그를 확인할 수 있는 설정을 추가한다.

    @Slf4j  // 로그를 확인하기 위한 어노테이션
    @Component
    public class LoggerFilter implements Filter {
    
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    
            // 입력받은 request, response 를 읽으면 뒤에서 읽을 수 없기 때문에 형변환 하여 캐싱 작업
            var req = new ContentCachingRequestWrapper((HttpServletRequest) request);
            var res = new ContentCachingResponseWrapper((HttpServletResponse) response);
    
            // 넘어온 request, response 객체가 아닌 위에서 Wrapper 클래스로 생성한 req, res 를 필터에 넘겨준다
            // doFilter 를 기준으로 위쪽이 실행 전, 아래쪽이 실행 후
            // 실행 전 부분에는 헤더와 바디 정보를 출력하는게 좋다. (생략)
            chain.doFilter(req, res);
    
            /* request 정보 */
            // requestHeader
            var headerNames = req.getHeaderNames();
            var requestHeaderValues = new StringBuilder();
            headerNames.asIterator().forEachRemaining(headerKey -> {
                var headerValue = req.getHeader(headerKey);
                requestHeaderValues.append("[").append(headerKey).append(" : ").append(headerValue).append("] ");
            });
            // requestBody
            var requestBody = new String(req.getContentAsByteArray());  // 컨텐츠를 바이트배열로 받는다
            var uri = req.getRequestURI();  // 어떤 주소로 요청했는지
            var method = req.getMethod();   // 어떤 메서드로 요청했는지
    
            log.info(">>>>> uri : {}, method : {}, header : {}, body : {}", uri, method, requestHeaderValues, requestBody);
    
            /* response 정보 */
            // responseHeader
            var responseHeaderValues = new StringBuilder();
            res.getHeaderNames().forEach(headerKey -> {
                var headerValue = res.getHeader(headerKey);
                responseHeaderValues.append("[").append(headerKey).append(" : ").append(headerValue).append("] ");
            });
            // responseBody
            var responseBody = new String(res.getContentAsByteArray());
    
            log.info(">>>>> uri : {}, method : {}, header : {}, body : {}", uri, method, responseHeaderValues, responseBody);
    
            res.copyBodyToResponse();   // responseBody 의 내용을 읽어서 비어 있기 때문에 response 초기화
    
        }
    
    }

     

    api를 호출 할때마다 주소와 메서드 헤더와 바디 값을 로그에 출력한다.

    클라이언트에서 전달받은 값이 유효한지 문제가 발생했다면 해당 이슈를 추적하기에 용이하다.

     

    마치며

    개인 프로젝트를 진행하며 기록하는 내용이라 정보를 얻고자 하는 누군가에게는 불친절한 글이 될 수도 있지만 댓글에 질문 주시면 제가 아는 선에서는 답변드리도록 하겠습니다.

    잘못된 내용도 지적해 주시면 감사하겠습니다.