티스토리 뷰

 

작년 입사 후 얼마 지나지 않아서 로그 파이프라인을 구성했었다.

2023.05.28 - [개발/SPRING] - 스프링부트 서비스에 LOG 남기기 (with. Logback)

 

당시에는 요청에 대한 로그가 없어서 급하게 구축하게 됐는데,

 

지금 생각해보면 입사한지 한달도 되지 않은 개발자가 로그 파이프라인을 구성한다는 자체가 말이 좀 안되는 일이긴했다.

 

그래서 로그 파이프라인에 구멍이 조금씩 있었는데, 최근에 가장 큰 구멍을 알게되서 개선이 필요했다. 

 

바로 Multipart/form-data 형식에서 MultipartFile에 대한 처리가 없어서, 어떤 파일이 업로드가 되었는지 알 수 없었다.

 

그런데, 현재 담당하고 있는 서비스가 파일 업로드를 지원하기 때문에 로그의 개선을 필요로 했다.

 

로그를 몇 번 개선하면서, 현재 로그는 AOP에서 처리하게 구성했다.

2023.07.10 - [개발/SPRING] - 스프링부트 AOP를 이용해 로깅 처리하기

 

위 글에도 남겨놨듯이 이슈는 인지는 하고 있었다. 당시에 처리를 하지 못했을 뿐.

 

구축 당시에 비해 도메인 지식 & 스프링에 대해서 조금 더 알게되면서 문제를 개선할 수 있게 됐다.

 

MultipartFile의 정보를 얻어오기 위해서는 두 가지 방법이 있을 것 같다.

1. AOP의 JointPoint 이용하기
2. MultipartResolver로 HttpServletRequest를 MultipartHttpServletRequest로 변환

 

1번이 조금 더 간단하고 현재 로그 파이프라인에 맞기 때문에 1번을 채택했지만,

 

이번 포스팅에서는 두 가지 방법을 모두 정리해보려고 한다.

 

1. AOP의 JointPoint 이용하기

초창기에는 Controller에 LogMessageManager라는 객체를 주입받아 모든 요청마다 setLogMessage 매서드를 붙였다.

 

이 방식이 너무 불편해서, 현재는 AOP를 이용해 Controller에서 Return이 발생할 때마다 로그를 남기도록 만들어 놨다.

 

AOP의 파라미터 중 JointPoint를 이용하면 Contoller의 요청 객체의 정보를 가져올 수 있다.

@AfterReturning(pointcut = "execution([Controller 포인트컷 위치 지정])", returning = "result")
public void loggingAspect(JoinPoint joinPoint, Object result) {
    Object[] objectArgs = joinPoint.getArgs();
    List<MultipartFileLogVo> multipartFileLogVoList = new ArrayList<>();
    for (Object arg : objectArgs) {
        if (arg instanceof MultipartFile) {
            MultipartFile file = (MultipartFile) arg;
            ...
        } else if (arg instanceof MultipartFile[]) {
            MultipartFile[] files = (MultipartFile[]) arg;
            for (MultipartFile file : files) {
                ...
            }
        } else {
            ...
        }
    }

    logMessageManager.setRequestLogMessage(result, multipartFileLogVoList);
}

실제 코드에 적용된 방식은 조금 다르긴한데, JoinPoint에서 요청 객체에 MultipartFile이 포함 될 경우 파일에 대한 정보를 가져오면된다.

 

파일 크기부터 이름까지 파일에 대한 모든 정보가 그대로 담겨있기 때문에 파일 정보를 로그에 남기면 된다.

 

요청객체 objectArgs를 깠을 때 MultipartFile이 Array인 경우가 있는데, 이건 파일을 여러개 보내는 경우 위와 같이 처리된다.

 

2. MultipartResolver로 HttpServletRequest를 MultipartHttpServletRequest로 변환

AOP를 잘 모르겠다면 이 방법이 더 쉽다.

private final HttpServletRequest httpServletRequest;

...

WebApplicationContext webApplicationContext = WebApplicationContextUtils.getWebApplicationContext(httpServletRequest.getServletContext());
MultipartResolver multipartResolver = webApplicationContext.getBean(MultipartResolver.class);
MultipartHttpServletRequest multipartHttpServletRequest = multipartResolver.resolveMultipart(httpServletRequest);
Map<String, MultipartFile> multipartFileMap = multipartHttpServletRequest.getFileMap();
for(String key : multipartFileMap.keySet()) {
    MultipartFile multipartFile = multipartFileMap.get(key);
    System.out.println(multipartFile.getOriginalFilename());
    System.out.println(multipartFile.getSize());
}

 

모든 요청에 대한 정보는 HttpServletRequest가 다 담고 있다.

 

당연히, Multipart/form-data에 대한 정보도 다 가지고 있는데, 그냥 사용할 수 없다.

 

MultipartResolver로 HttpServletRequest를 MultipartHttpServletRequest로 변환해줘야 MultipartFile에 대한 정보를 가져올 수 있다.

 

이 방법을 좀 늦게 알았는데, 이전 코드가 조금 복잡하게 구성되어있어 이 방식으로 리뉴얼할 예정이다.

 

최종적으로는 아래와 같이 구성된다.

{
    "@timestamp": "2024-02-10T10:56:30.248Z",
    "message": "LOG",
    "logger_name": "...",
    "level": "INFO",
    "code": "20000",
    "method": "POST",
    "parameter": "{\"multipart-files\":[{\"fileName\":\"가정통신문.hwp\",\"fileSize\":\"1.0MB\"}]}",
    "id": "...",
    "url": "..."
}

 

마치며

글은 간단하게 썼는데, 현재 코드에 여러가지 함정들이 깔려있어서 굉장히 당황스러웠다.

 

결국 문제는 HttpServletRequest에서 MultipartFIle 형식을 가져오는 법을 알지 못해서 생긴 문제였고,

 

일단은 잘 나오는 상태다.

 

이번에 좀 크게 만져놔서 한동안은 괜찮지 않을까 싶다.

 

 

 

 

 

 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/07   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31
글 보관함