🔔[항해99]/WIL

🔔[항해99]/WIL[항해플러스 백엔드후기] 5주차/챕터2 회고 - 시작이 반이다

디카페인라떼 2025. 1. 17. 01:18

 

[개요]

어느덧 벌써 길고 길었던 10주의 절반이 지났다.

시작이 반이라더니 멀게만 느껴졌던 절반을 넘어간다.

5주차 회고 겸 챕터2 마무리 회고를 해보고자 한다 

 


[과제]

5주차의 과제는 다음과 같았다 

- 비즈니스 별 발생할 수 있는 에러 코드 정의 및 관리 체계 구축
- 프레임워크별 글로벌 에러 핸들러를 통해 예외 로깅 및 응답 처리 핸들러 구현
- 시스템 성격에 적합하게 Filter, Interceptor 를 활용해 기능의 관점을 분리하여 개선
- 모든 API 가 정상적으로 기능을 제공하도록 완성
- 시나리오별 동시성 통합 테스트 작성

 

 

지난 주차에 비즈니스 로직을 추가하면서 예외 처리를 구현했지만, CustomException을 설정해 두지 않은 채로 핸들러만 구축해 두어 이번주차에 추가해 주었다. 

 

CustomException을 하나로 통합하여 사용했으나, 디버깅 시 예외가 명확하지 않다는 점을 발견하여 기능과 상태에 따라 예외를 구체적으로 구분하는 것이 낫겠다라는 판단하에 도메인별로 예외를 나누어 구현하였다 

 

  ├── exception
    │   ├── CommerceCouponException.java
    │   ├── CommerceException.java
    │   ├── CommerceOrderException.java
    │   ├── CommerceProductException.java
    │   └── CommerceUserException.java

 

그리고 @RestControllerAdvice를 활용하여 GlobalExceptionHandler를 구현해 주었다 


그 다음은 FilterInterceptor 를 활용해서 기능을 넣어야 했다. 

그러기 전에 정확하게 Filter와 Interceptor 에 대한 이해가 필요했다.

필터

필터(Filter)는 디스패처 서블릿(Dispatcher Servlet)에 요청이 전달되기 전과 후에 url 패턴에 맞는 모든 요청에 대해 부가작업을 처리할 수 있는 기능을 제공한다.

디스패처 서블릿은 스프링의 가장 앞 단에 존재하는 프론트 컨트롤러로, 필터는 스프링 범위 밖에서 처리가 된다.

즉 필터는 스프링 컨테이너가 아닌 톰캣과 같이 웹 컨테이너에 의해 관리가 되며, 디스패처 서블릿 전과 후에 처리하는 것이다.

필터를 사용하기 위해서는 java.servlet의 Filter 인터페이스를 구현해야 하며, 해당 인터페이스는 다음과 같은 메서드를 가진다.

- init

웹 컨테이너가 init() 메서드를 호출하여 필터 객체를 초기화하고 서비스에 추가하기 위한 메서

초기화가 이루어지면 doFilter()를 통해 처리가 이루어진다.

- doFilter

url-pattern에 맞는 모든 HTTP 요청이 디스패처 서블릿으로 전달되기 전에 웹 컨테이너에 의해 실행되는 메서드

doFilter()의 파라미터로 FilterChain이 있는데, 이를 통해 다음 대상으로 요청을 전달할 수 있게 된다. 

chain.doFilter()로 전/후에 우리가 필요한 처리 과정을 부여해줌으로써 원하는 처리를 진행할 수 있다.

- destroy

필터 객체를 제거하고 사용하는 자원을 반환하기 위한 메서드

웹 컨테이너가 1회 destroy()를 호출하여 필터 객체를 종료하면 이후에는 doFilter()에 의해 처리되지 않는다.

인터셉터

인터셉터(Interceptor)는 필터와 달리 스프링이 제공하는 기술로, 디스패처 서블릿이 컨트롤러를 호출하기 전과 후에 요청과 응답을 참조하거나 가공할 수 있는 기능을 제공한다. 쉽게 말하면 요청에 대한 작업 전/후로 가로챈다고 생각하면 된다.

디스패처 서블릿이 핸들러 매핑을 통해 컨트롤러를 찾도록 요청하는데, 그 결과로 실행 체인 (HandlerExecutionChain)을 돌려준다.

여기에서 1개 이상의 인터셉터가 등록되어 있다면 순차적으로 인터셉터들을 거쳐 컨트롤러가 실행되도록 하고, 인터셉터가 없다면 바로 컨트롤러를 실행한다.

인터셉터를 사용하기 위해서는 org.springframework.web.servlet의 HandlerInterceptor 인터페이스를 구현해야 하며, 해당 인터페이스는 다음과 같은 메서드를 가진다.

preHandle

컨트롤러가 호출되기 전에 실행되는 메서드

컨트롤러 이전에 처리해야 하는 전처리 작업이나 요청 정보를 가공하거나 추가하는 경우에 사용할 수 있다.

postHandle

컨트롤러가 호출된 후에 실행되는 메서드

컨트롤러 이후에 처리해야 하는 후처리 작업이 있을 때 사용할 수 있다.

afterCompletion

모든 뷰에서 최종 결과를 생성하는 일을 포함해 모든 작업이 완료된 후에 실행된다. (View 렌더링 후)

요청 처리 중에 사용한 리소스를 반환할 때 사용할 수 있다.

필터와 인터셉터의 차이 비교

  필터 인터셉터
관리하는 컨테이너 웹 컨테이너 스프링 컨테이너
Request와 Response를 조작 여부 O X
사용 사례 기본적으로 스프링과 무관하게 전역적으로 처리해야 하는 작업
  • 보안 및 인증/인가 관련 작업
  • 모든 요청에 대한 로깅 또는 검사
  • 이미지/데이터 압축 및 문자열 인코딩
  • 스프링과 분리되어야 하는 기능
클라이언트의 요청과 관련되어 전역적으로 처리해야 하는 작업
  • 세부적인 보안 및 인증/인가 공통 작업
  • API 호출에 대한 로깅 또는 검사
  • 컨트롤러로 넘겨주는 데이터의 가공

 

 

최종적으로 Filter로는 CORS 설정을 추가해 주었고 Interceptor 로는 디버깅을 위해 모든 API로 들어오는 요청에 대해 디버깅용 로깅을 남길 수 있도록 구현하였다 


동시성 통합테스트는 저번 주차에 추가하지 못했던 선착순 쿠폰에 대해서 리팩토링을 추가해 주었다

 

재고가 5개인 쿠폰에 8명이 발급 신청을 할 경우에 5명만 성공하고 3명은 실패, 쿠폰의 재고는 0이 되는지 동시성 테스트를 진행하였다  

그래도 지난 챕터때 몇번 작성해보아서 그런지 조금 수월하게 작성할 수 있었다. 

 


이번주 KPT 회고


Keep : [유지해야 할 좋은 점]

- 테스트 코드에 대해서 스트레스를 많이 받기도 했지만 피드백을 받고 점차 개선되면서 "테스트 코드를 왜 작성하는가?"라는 근본적인 질문을 스스로에게 던지게 되었다

 

처음에 설계를 하고 구현하면서 단위 테스트를 작성했지만, 프로젝트가 진행됨에 따라 설계가 바뀌고, 그에 따라 기존에 작성한 테스트 코드도 깨지게 되었는데 이러한 반복적인 과정을 겪으며, 비즈니스 로직을 수정할 때마다 좀 더 신중하게 작업해야 한다는 점을 깨닫게 되었다

 

이걸 통해서, 테스트 코드는 단순히 버그를 찾기 위한 도구가 아니라, 시스템의 안정성과 유지보수성을 높이는 중요한 역할을 한다는 점을 되새기게 되었고, 설계 단계에서부터 테스트 가능한 코드와 비즈니스 로직을 작성하는 중요성을 더욱 실감하게 되었다

 

더불어 실무에서도 테스트 코드를 조금씩 추가해보려고 노력하고 있다. 

 

Problem : [개선이 필요한 점]

- 발제를 들었을때도 그랬지만 아직도 모르는 게 많은 것 같다. 배움에는 끝이 없군 끝이 없어 ..


Try : [새롭게 시도할 점]

- 앞으로도 모르는 것이 나올 때마다 쫄지말고 해당 개념에 대해서 정확히 알고 넘어가는게 중요하다고 느꼈다. 

- 당연히 모르는 것들이 나올테니까 쫄지 말자 

 

 


 

항해플러스에서 어느덧 5주라는 시간동안 참 치열하게 시간을 보내왔다. 매주 해내야 하는 과제에 퇴근 후 생활이 없어지고 일주일이 모두 과제로 꽉 차 있어서 스트레스와 부담감이 없지 않아 있었지만 과제를 제출하고나서의 성취감은 이루 말할 수 없었다

 

하지만 단순히 과제를 쳐내기만 하는 과정은 아니었다.

그동안 이론으로만 알고 있었던 클린 코드, 클린 아키텍처를 직접 구현해 가면서 내가 부족한 점이 무엇인지 무슨 부분을 더 공부해야 하는지 알수 있었다. 내가 부족한 점과 더 공부해야 할 부분을 명확히 알 수 있었다. 내가 아는 것과 모르는 것을 구분하는 것이 공부에 있어 좀 더 효율적이라는 생각이 들었다.

더불어 실무에서도 새로운 기능 개발 시 (클린 아키텍처를 위해 전체 리팩토링을 하기는 어렵지만) 클린 코드를 위해 조금 더 고민하며 코딩을 하게 되었다.

 

동시성과 테스트코드에서도 많은 발전이 있었다. 

예전에는 단순히 DB 락만 잘 하고 트랜잭션만 잘 처리하면 되는 거라고 생각했었는데, 이번 기회에 쓰레드 락 개념부터 시작해서 DB 락까지 깊게 공부할 수 있었다. 단순히 책만 보고 이해하려던 것과는 달리 실제 시나리오를 구상하고  동시에 이를 고려한 통합 테스트 코드를 작성하면서 동시성 제어에 대해 더욱 잘 이해할 수 있었다.

테스트 코드의 중요성도 깨닫고 테스트 코드를 작성하는 감각도 점차 익혀가고 있다.

 

앞으로도 본격적으로 항해플러스를 하면서 더 많이 배우고 해내고 싶다. 물론 힘들지만 배우는 것 자체는 정말 재미있으니까 계속 도전할 것이다. 

멘토링 때 들은 말을 되새기며 생각하게 된다.
"성실이 빛이 되어 돌아온다."

매일매일이 나의 부족함을 느끼고 끝이 보이지 않는 오르막을 계속 오르는 느낌이지만, 언젠가 뒤를 돌아보며 성장한 나 자신을 칭찬해주고 싶다. 그러기 위해서는 그저 묵묵히 해내는 수밖에 없다.