2023-06-03-TIL
2023-06-03-TIL
Today I learned
Chain of Responsibility Pattern
책임 연쇄(Chain of Responsibility) 패턴은 요청을 핸들러들의 사슬을 따라 전달하는 행위 패턴이다. 요청을 받은 각 핸들러는 직접 처리할지, 다음 핸들러에게 넘길지를 스스로 정한다(Refactoring.Guru: Chain of Responsibility). 요청을 보내는 쪽은 누가 처리하는지 몰라도 되고, 핸들러를 추가하거나 순서를 바꿔도 보내는 쪽 코드는 그대로다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
abstract class Handler {
private Handler next;
Handler linkWith(Handler next) { this.next = next; return next; }
boolean handle(Request req) {
return next == null || next.handle(req);
}
}
class AuthHandler extends Handler {
boolean handle(Request req) {
if (!req.hasToken()) return false; // 여기서 사슬을 끊는다
return super.handle(req); // 통과하면 다음으로
}
}
장점은 핸들러끼리 결합이 느슨하고 개방-폐쇄 원칙을 지키기 쉽다는 것이다. 단점은 사슬 끝까지 가도 아무도 처리하지 않는 요청이 생길 수 있다는 점이다. 그래서 마지막에 기본 처리를 맡는 핸들러를 두거나, 처리되지 않은 경우를 명시적으로 다뤄야 한다.
Spring에서 이 패턴이 가장 잘 보이는 곳이 Spring Security의 필터 체인이다. 서블릿 컨테이너는 요청 URI에 따라 Filter들과 Servlet을 담은 FilterChain을 만든다. 각 필터는 다음 필터나 서블릿이 호출되지 않도록 막고 직접 응답을 쓸 수도 있고, 요청과 응답을 바꿔서 다음으로 넘길 수도 있다(Spring Security: Architecture).
Spring Security는 그 위에 한 겹을 더 둔다. DelegatingFilterProxy가 서블릿 컨테이너와 Spring ApplicationContext를 잇고, 그 안의 FilterChainProxy가 여러 SecurityFilterChain 중 요청에 맞는 첫 번째 체인을 골라 그 체인의 보안 필터들을 차례로 실행한다.
1
2
3
4
Client -> Filter -> DelegatingFilterProxy -> FilterChainProxy
-> SecurityFilterChain (/api/**)
-> SecurityFilterChain (/**)
-> Servlet
인증되지 않은 요청이 인증 필터에서 막히면 그 뒤의 필터와 컨트롤러는 실행되지 않는다. 위 예제의 AuthHandler가 false를 돌려주며 사슬을 끊는 것과 같은 구조다. Spring Integration의 메시지 필터도 헤더나 내용을 기준으로 메시지를 다음 채널로 넘길지 버릴지만 결정한다는 점에서 비슷한 발상이다(Spring Integration: Filter).
- https://dev.to/kirekov/chain-of-responsibility-pattern-in-spring-application-a09
- https://springframework.guru/gang-of-four-design-patterns/chain-of-responsibility-pattern/
- https://www.baeldung.com/chain-of-responsibility-pattern
- https://docs.spring.io/spring-security/site/docs/3.0.x/reference/security-filter-chain.html
- https://refactoring.guru/design-patterns/chain-of-responsibility
- https://docs.spring.io/spring-integration/reference/html/filter.html
- https://docs.spring.io/spring-security/reference/servlet/architecture.html
댓글
아직 댓글이 없습니다