전체적으로 로직을 살펴보면서 너무 시스템 아키텍칭 부분을 등한시하고 넘어가고 있지 않나는 생각이 들었다.
관련 개념들을 알아보면서 시스템을 보는 눈이 더 성장하지 않을까하는 생각에 본 내용을 기록하여 남긴다.
더불어 REST API와 같이 Transactional DI를 사용하기 애매하여 직접 Exception을 처리해야 할 경우, 그 Exception(과 함께 try 등의) 내용이 공통적인 내용이 많다면 그때 global 인터셉터나 핸들러에 넣어 처리를 해주는 것이 좋겠다.
우리가 사용하고 있는 @Autowired, @SqlSession, 즉 어노테이션은 IoC(제어의 역행)을 구현하기 위한 하나의 도구이자 방법(DI)이다.
자세히 살펴보자.
DI(Dependency Injection), 의존성 주입.
-> 내가 사용해야 하는 객체, 의존해야 하는 객체를 빈에서 "주입받아" 사용한다.
-> 객체를 직접 만들지 않고도, 스프링 컨테이너에 있는 객체를 그대로 주입받아 객체처럼 사용할 수 있다.
-> mapper 객체를 사용할때, mapper 객체를 별도로 만들지 않고 어노테이션을 통해 주입받아 그대로 사용할 수 있었던 이유가 바로 여기에 있다.
Bean
-> IoC와 DI 구현의 핵심은 스프링 컨테이너와 빈이다.
-> 스프링 컨테이너에 등록된 빈을 주입받아 사용할 수 있다.
AOP(Aspect Oriented Programming), 관점 지향 프로그래밍.
-> 공통적으로 수반하는 행위, 행동 등에 대해 구체적인 적용 시점과 행동 내용을 정의하여 프록시 패턴 기반의 AOP구현체가 빈으로 등록된다.
(*빈 생성 시 기능이 추가된 클래스인 프록시를 만들고, 원본 대신 이를 빈에 저장)
-> 스프링에서는 사용자가 필요로 하는 메인 행동보다는, 메인 행동에 수반되는 부가적인 공통 행동 등에 대해 AOP 개념을 적용할 수 있으며, AOP는 구현 도구가 아닌 프로그래밍 구축 방법의 일부이다.
Filter(스프링이 아닌 java에서 제공하는 기능)
-> servlet의 filter 인터페이스를 통해 구현이 가능하며, init(필터 생성 시 수행)/doFilter(클라이언트의 요청/응답으로 체인을 통과할때마다 수행)/destroy(필터 종료 시 수행)
-> 이 Filter들을 filter config를 통해 빈으로 등록할 수 있고, 이에 따라 사용자는 메소드 실행 이전/이후에 자신이 원하는 부가 기능들을 수행할 수 있도록 설정할 수 있다.
Dispatcher Servlet, Interceptor
-> 글로벌 인터셉터/어댑터에 대한 개념이 바로 여기서 나온다.
-> 기본적으로 프론트에서 요청이 왔을때 가장 먼저 지나는 지점이 디스패치 서블릿이다. 이 서블릿으로 인해 공통적인 작업을 먼저 처리하고, 그 후에 매핑된 컨트롤러를 찾아 연결해준다(HandlerMapping -> HandlerAdapter).
-> Handler Adapter가 요청을 최종적으로 컨트롤러에 위임하며, 이 친구가 빈에서 등록된 컨트롤러를 찾아 로직을 진행한다.
-> 이때 디스패치 서블릿이 컨트롤러를 찾아 위임하고 호출하기 전/후 과정에 응답을 참조하거나 가공할 수 있는 기능들이 제공되며, 이것이 Interceptor이다(즉 인터셉터가 등록되어 있다면, 인터셉터를 거쳐서 컨트롤러에 요청을 위임하게 된다. 그 이후도 마찬가지로, 인터셉터를 거쳐 반환을 한다).
Filter, interceptor, AOP - https://velog.io/@soyeon207/Spring-Filter-Interceptor-AOP
스프링의 컨셉 - https://shinsunyoung.tistory.com/133 , https://fe-churi.tistory.com/23 (두 자료를 같이 봐야 명확히 이해할 수 있다)