TIL

aop, interceptor, filter 로 id 존재하는지 검사

Hyeongjun_Ham 2023. 8. 22. 11:43

상황 :

- 컨트롤러에 id를 받는 부분이 있다.

- 쿼리스트링으로 직접 받는 api도 있고, RequestBody로 dto안에서 받아내는 부분도 있다.

- 해당 id를 통해 DB에 data가 존재하는지 확인하고싶다.

 

 

aop 구현시

- 간단하게 어노테이션으로 구현

- 쿼리스트링으로 받는 경우는 간편하게 어노테이션 붙이는 것만으로 해결 다.

 

문제 1

- RequestBody로 받아내는 경우의 api마다 받아내는 dto가 다르다.

 

해결방법 :

1. dto 부모를 만들어 해당 dto 안에 uid 넣어서 @Around 내부에 해당 dto 선언하여 처리하는 방법

2. aop 구현부 내부에 if else로 instance 구분하여 처리하여 꺼내는 방법

 

문제 2

- 현재 프로젝트에는 에러시에도 200을 던지고 리턴코드로 구분

- aop를 사용하는 경우 throw를 던져야 하는데 정해진 형식을 맞추려면 많은 작업해야함.

 

interceptor 구현시

- 스프링 interceptor 등록 후 적용할 url만 등록하면 쉽게 가능

- 쿼리스트링은 문제가 안된다.

 

문제

- RequestBody로 받아내는 경우 dto를 구분안해도 되지만, interceptor에서 해당 json을 읽어낸 경우

  컨트롤러에서 읽을 수가 없다. (stream이 닫힘)

- 한번 읽은 데이터는 다시 읽을 수 없는 것으로 보임.

- 넘겨주는 방식으로 하려면 컨트롤러 부분 대거 공사들어가야함

 

 

filter 구현시

- 인터셉터와 비슷하나 작동하는 위치가 다르다.

 

RequestBody로 받아내는 경우 dto를 구분안해도 되지만,

interceptor에서 해당 json을 읽어낸 경우 컨트롤러에서 읽을 수가 없다. (stream이 닫힘)

 

결국은 이게 문제였다.

 

해결 : 

@Slf4j
public class RequestWrapper extends HttpServletRequestWrapper {

    private final String requestData;

    public RequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        requestData = requestDataByte(request);
    }

    private String requestDataByte(HttpServletRequest request) throws IOException {
        ServletInputStream inputStream = request.getInputStream();
        byte[] rawData = StreamUtils.copyToByteArray(inputStream);
        return new String(rawData);
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        ByteArrayInputStream inputStream = new ByteArrayInputStream(this.requestData.getBytes(StandardCharsets.UTF_8));
        return new ServletInputStream() {
            @Override
            public boolean isFinished() {
                return inputStream.available() == 0;
            }

            @Override
            public boolean isReady() {
                return true;
            }

            @Override
            public void setReadListener(ReadListener listener) {
                throw new UnsupportedOperationException();
            }

            @Override
            public int read() {
                return inputStream.read();
            }
        };
    }

    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(this.getInputStream()));
    }
}

래퍼로 감싸고, 필터 내부에서 복사해서 사용하는 느낌으로 가야한다.

 

한번 읽어오면 사라지기 때문

 

@Slf4j
@Component
@RequiredArgsConstructor
public class CheckUidFilter extends OncePerRequestFilter {

    private final ObjectMapper objectMapper;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String uid = null;
        String contentType = request.getContentType();

        RequestWrapper requestWrapper = new RequestWrapper(request);

        // json
        if (contentType != null && contentType.contains("application/json")) {
            StringBuilder sb = getBodyToStringBuilder(response, filterChain, requestWrapper);

            JSONObject json = null;
            try {
                json = new JSONObject(sb.toString());
            } catch (JSONException e) {
                throw new RuntimeException(e);
            }
            uid = json.optString("id"); // JSON 데이터에서 id 추출
        } else { //param
            uid = request.getParameter("id");
        }

        try {
				//비즈니스 로직
                log.info("NO_EXIST_CLIENT");
                return;
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        filterChain.doFilter(requestWrapper, response);
    }




private StringBuilder getBodyToStringBuilder(HttpServletResponse response, FilterChain filterChain, RequestWrapper requestWrapper) throws IOException, ServletException {
        StringBuilder sb = new StringBuilder();
        BufferedReader br = null;
        //한줄씩 담을 변수
        String line = "";
        try {
            ServletInputStream inputStream = requestWrapper.getInputStream();
            if (inputStream != null) {
                br = new BufferedReader(new InputStreamReader(inputStream));
                while ((line = br.readLine()) != null) {
                    sb.append(line);
                }
            }
        } catch (IOException e) {
            log.debug("body에 요청이 없습니다.");
            filterChain.doFilter(requestWrapper, response);
            return null;
        }
        return sb;
    }