RicoCheesethe studio log · v2.0
Live · KRRead posts
목록으로
개발PUBLISHED · 2026년 7월 14일·9 MIN READ

[스프링 요청 파이프라인] 담당자 앞의 마지막 확인, Interceptor (4)

Interceptor가 Filter와 어떻게 다른지, preHandle·postHandle·afterCompletion 세 지점이 요청 흐름에서 각각 무슨 일을 하는지 정리한다.

#spring#java#backend#web#request-lifecycle
Sleek corporate building lobby with marble walls and glass windows

안내데스크를 지나온 다음 #

3화에서 손님은 로비 안내데스크(DispatcherServlet)를 거쳐 "몇 층 몇 부서로 가시면 됩니다"라는 안내를 받았다. 이제 손님은 그 부서를 향해 걸어간다. 그런데 부서 문을 열자마자 담당 직원 책상에 바로 앉을 수 있는 건 아니다. 문 앞에 접수창구가 하나 더 있다.

이번 화에서 다룰 관문이 이 접수창구, 스프링 용어로는 Interceptor다. 담당 직원(컨트롤러)을 만나기 직전 마지막으로 거치는 곳이다.

접수창구는 이 회사 직원이다 #

1화에서 흘려둔 이야기를 여기서 회수할 차례다. 정문 경비실은 이 회사 소속이 아니라 건물 전체를 관리하는 시설관리 업체 소속이라고 했다. 반면 부서 접수창구는 이 회사가 직접 고용한 우리 직원이다.

이 차이가 실제로 뭘 가능하게 하냐면, 사내 시스템 접근이다. 접수창구 직원은 사내 인사 시스템을 조회해서 "이 손님, 우리 회사 VIP 고객 맞나요"를 바로 확인할 수 있고, 다른 부서 직원에게 사내 메신저로 연락해서 물어볼 수도 있다. 경비실 직원은 이게 안 된다. 건물 관리 업체 소속이라 이 회사 내부 시스템에 접근할 권한 자체가 없다.

기술 용어로 옮기면 이렇다. Interceptor는 스프링이 직접 관리하는 빈(bean)이다. 그래서 스프링이 만들어둔 다른 부품, 이를테면 로그인 여부를 판단하는 서비스나 권한을 조회하는 리포지토리를 자유롭게 주입받아 쓸 수 있다. Filter는 스프링 바깥, WAS(웹 애플리케이션 서버) 스펙에 속하는 물건이라 이게 원천적으로 불가능하다. "스프링 밖"과 "스프링 안"의 경계가 정확히 이 두 관문 사이에 있다.

접수창구는 담당자를 안다 #

두 번째 차이는 정보량이다. 안내데스크는 손님을 관문 앞으로 넘길 때 이미 "이 손님은 3부서 김대리 담당"이라는 걸 정해서 넘긴다. 그래서 접수창구는 이 손님이 정확히 누구를 만나러 왔는지, 어떤 업무를 처리하러 왔는지 알고 있다.

경비실은 이걸 모른다. 정문에서 손님을 막을지 말지 판단할 때, 경비실은 그 손님이 나중에 몇 층 몇 부서 누구를 만날지 전혀 알 길이 없다. 그냥 신분증이 있는지, 수상한 물건을 들고 있진 않은지만 본다.

기술적으로는 Interceptor가 어떤 핸들러(컨트롤러의 메서드)가 이 요청을 처리할지에 대한 정보를 넘겨받는다는 뜻이다. 그래서 "이 컨트롤러 메서드는 로그인한 사람만 접근 가능"처럼, 처리할 대상을 알고 나서야 세울 수 있는 규칙을 여기서 판단할 수 있다. Filter는 이 정보 자체가 없어서 이런 판단을 애초에 할 수가 없다.

세 번의 확인 #

접수창구는 손님이 지나가는 동안 정확히 세 번 개입한다.

untitled
text
preHandle()        → 담당자 만나기 직전, 자격을 확인한다
[ 담당 직원이 업무 처리 ]
postHandle()       → 업무가 끝난 직후, 서류를 한 번 검토한다
afterCompletion()  → 손님 배웅까지 끝난 뒤, 뒷정리를 한다

preHandle은 손님이 담당자 책상에 앉기 직전이다. 로그인이 안 되어 있거나 권한이 없으면 여기서 정중히 돌려보낸다. 스프링 코드로 치면 preHandle이 false를 반환하는 순간 요청은 컨트롤러 근처에도 못 가고 그 자리에서 끝난다.

postHandle은 담당자가 업무를 마친 직후다. 다만 아직 손님에게 서류(응답)를 건네기 전, 정확히는 뷰가 렌더링되기 전 시점이다. 담당자가 작성한 내용을 한 번 더 훑어보는 타이밍이라고 보면 된다.

afterCompletion은 손님이 서류까지 받고 건물을 나선 뒤다. 뷰 렌더링까지 완전히 끝난 다음이라, 중간에 문제가 생겨서 예외가 터졌든 안 터졌든 상관없이 항상 호출된다. 접수창구 입장에서는 이 손님 건과 관련된 기록을 마지막으로 정리하는 자리다.

대표적으로 이 세 지점에서 하는 일은 로그인 여부 확인, 권한 체크, 공통 로그 남기기, 요청 처리 시간 재기 정도다. Filter처럼 요청·응답 객체 자체를 바꿔치기하는 쪽보다는, 손님이 담당자에게 갈지 말지·간 다음 어떻게 마무리할지를 통제하는 쪽에 가깝다.

그래서 이게 왜 중요한데 #

실무에서 이 관문을 정확히 이해해야 하는 순간은 로그인 체크 로직을 어디에 둘지 결정할 때 온다. "로그인 안 한 사용자는 이 페이지 접근 금지"를 컨트롤러마다 if문으로 넣어두면, 새 컨트롤러를 만들 때마다 그 if문을 빼먹기 쉽다. 이걸 Interceptor의 preHandle에 한 번 넣어두면 어떤 URL 패턴에 적용할지만 설정으로 관리하면 되고, 컨트롤러 코드는 로그인 체크 자체를 몰라도 된다.

또 하나는 "왜 이 요청은 로그가 이렇게 찍히지" 같은 문제를 마주쳤을 때다. 처리 시간이 유독 긴 요청이 있다면 postHandle에서 시작 시각과 끝난 시각을 비교해보는 식으로 원인을 좁혀갈 수 있다. Filter 단계에서는 안 보이던 것이 Interceptor 단계에서는 보이는 이유도, 결국 여기서 다룬 "핸들러 정보를 아느냐"의 차이 때문이다.

다음 화 예고 #

5화에서는 마지막 관문, 담당 직원 자신인 Controller를 다룬다. 접수창구를 통과한 손님이 실제로 어떤 일을 겪는지, 그리고 담당 직원 혼자 처리를 다 끝내지 못할 때 뒤쪽 팀(서비스, 리포지토리)에 어떻게 일을 넘기는지를 볼 예정이다.