해당 포스팅에서는 웹 애플리케이션에서 인증과 인가를 처리하는 다양한 방법(Filter, Interceptor, AOP, Session, Token)을 살펴보고, 제가 진행 중인 프로젝트에서 왜 Interceptor와 Session 방식을 선택했는지 그 이유를 설명드리겠습니다.
Filter, Interceptor, AOP

Filter
Filter는 Servlet이나 JSP같은 웹 요청응답을 가로채서 전처리나 후처리를(doFilter) 수행하는 역할을 합니다.
Filter가 주로 하는일은 다음과 같습니다.
- 요청(Request)가 컨트롤러에 도달하기 전에 전처리 : 인증, 로깅, 데이터 변환 등
- 응답(Response)가 클라이언트로 돌아가기 전에 후처리 : Response 압축, 헤더 추가 등
Filter는 그림과 같이 Spring 내부까지 진입하지 않으므로, 다음과 같은 장/단점을 가지고 있습니다.
- 장점
- 로깅 처리를 공통적으로 사용 가능합니다.
- 인증에서 예외가 발생할 경우, Filter에서 예외처리를 해줄 수 있어, 필요하지 않은 리소스가 Spring Application에 들어오는 것을 방지해줄 수 있습니다.
- 단점
- Spring Application 이전에 처리하기 때문에, 세부적인 비즈니스 로직을 처리하기엔 어렵습니다.
- 따라서 주로 요청/응답 수준에서만 동작이 가능합니다.
Interceptor
Interceptor는 Filter 이후 Controller로 요청이 전달되기 전/후에 특정 로직을 실행하는 역할을 합니다.
Spring Application에서 사용되기 때문에, Filter보다 더 세밀한 제어가 가능하다는 장점이 있습니다.
Interceptor가 주로 하는일은 다음과 같습니다.
- 요청 전(preHandle)에 로직을 수행 : 인증
- 요청 후(postHandle)에 로직을 수행 : 공통 데이터 추가
- 응답 완료 후(afterCompletion)에 로직을 실행 : 로깅 처리
Interceptor는 Spring 내부까지 진입하므로, 다음과 같은 장/단점을 가지고 있습니다.
- 장점
- Filter보다 더 세부적인 로직 처리가 가능합니다.
- Spring Context에 접근이 가능하기 때문에 Service 로직과 연동이 가능합니다.
- 단점
- Spring Application 내부에서만 사용이 가능합니다.
- 따라서 Servlet Level의 처리는 불가능합니다.
AOP
AOP(Aspect-Oriented Programming)는 공통 관심사를 모듈화하여 핵심 비즈니스 로직과 분리하는 역할을 합니다. 여기서 공통 관심사란, 로깅, 트랜잭션 관리와 같은 여러 곳에서 반복되는 로직을 말합니다.
AOP가 주로 하는일은 다음과 같습니다.
- 로깅, 트랜잭션 관리, 예외 처리등 반복되는 로직을 분리할 수 있습니다.
- 따라서 코드 중복을 줄이고 유지보수성을 높일 수 있습니다.
- @Aspect를 사용하여 모듈을 정의할 수 있습니다.
- @PointCut을 사용하여 해당 로직이 적용될 지점을 정의할 수 있습니다.
- @Advice를 사용하여 실행 시점에 따라 공통 로직을 실행시킬 수 있습니다.
AOP는 다음과 같은 장/단점을 가지고 있습니다.
- 장점
- 비즈니스 로직과 공통 로직을 완전히 분리가 가능합니다.
- 단점
- 과도하게 AOP를 사용할 경우, 흐름 파악이 어려워질 수 있습니다.
Session, Token

Session
Session은 클라이언트와 서버간 상태를 유지하기 위한 방법중 하나입니다.
사용자가 로그인하여 인증된 후 해당 사용자의 정보를 일정 기간 동안 서버가 기억할 수 있도록 합니다.
세션 기반 동작 순서는 다음과 같습니다
- 로그인 : 사용자는 ID, 비밀번호를 입력하여 서버에 로그인 요청을 보냅니다.
- 유효성 검증 : 서버는 DB에 저장된 정보를 통해 사용자의 입력에 대해 유효성을 검증합니다.
- 세션 생성 및 저장 : 유효성 검증이 완료된 후, 서버는 해당 사용자에 대한 세션 데이터를 서버메모리에 저장하고, 세션 ID를 생성합니다.
- 세션 ID 전송 : 서버는 생성된 세션 ID를 클라이언트에게 쿠키 형태로 전송합니다.
- 페이지 접근 : 이후 클라이언트는 서버에 요청할 경우 쿠키에 포함된 세션 ID를 함께 전송합니다.
- 세션 검증 : 서버는 클라이언트로부터 받은 세션 ID를 사용하여 해당 세션 데이터를 조회하고, 유효한 세션인지 검증합니다.
- 응답 : 세션이 유효할 경우, 서버는 요청에 대한 응답을 클라이언트에게 전송합니다.
Session 방식은 다음과 같은 장/단점이 있습니다.
- 장점
- 상태 유지(stateful)
- 서버가 사용자 상태를 직접 관리하기 때문에, 사용자 인증 상태를 쉽게 유지할 수 있습니다.
- 사용자는 한번 로그인하면, 이후 로그아웃까지 다시 로그인할 필요가 없습니다.
- 보안
- 세션 정보가 서버에 저장되기 때문에, 클라이언트 측에 보안이 필요한 정보를 보내지 않아도 되므로, 데이터 유출 위험이 줄어듭니다.
- 서버 제어
- 세션 정보가 서버에 저장되기 때문에, 관리자가 세션을 쉽게 제어 가능합니다.
- 단점
- 서버 리소스
- 결국 서버 메모리에 저장하여 사용하기 때문에, 서버 리소스를 소모하게 됩니다. 따라서 많은 사용자가 접속할 경우, 서버에 부하가 발생할 수 있습니다.
- 스케일링
- 세션 정보가 서버에 저장되기 때문에, 여러 서버 간의 세션 공유가 필요할 경우 복잡성이 증가할 수 있습니다.
- 세션 하이재킹
- 세션 ID를 탈취하여 세션을 가로챌 경우, 서버가 해당 사용자가 탈취한 사용자인지 아닌지 알 수 없습니다.
- 브라우저 종속성
- 세션 ID는 클라이언트에 쿠키에 저장됩니다. 따라서 다른 브라우저나 기기로 접속할 경우, 세션 ID가 달라지기 때문에, 인증 상태가 유지되지 않습니다.
JWT - Token
Token은 사용자가 로그인하면 서버가 인증을 확인한 후 클라이언트에게 암호화된 토큰을 발급하고, 이후 요청에 클라이언트 측에서 해당 Token을 포함시켜 인증 상태를 유지하는 방식입니다.
JWT(Json Web Token)은 Header, Payload, Signature 3부분으로 이루어집니다.
- Header
- alg : 어떤 알고리즘 방식을 사용하는지 나타냅니다.
- typ : 토큰의 타입을 나타냅니다.
{
"alg": "HS256",
"typ": "JWT"
}
- Payload
- 토큰에서 사용할 정보들인 Claim이 포함됩니다.
- Claim은 3가지로 분류됩니다.
- Registered Claim
-
토큰 정보를 표현하기 위해 이미 정해진 종류의 데이터입니다.
{
"iss": "https://example.com",
"sub": "userId",
"aud": "https://example.com",
"exp": 1234567899,
"nbf": 1234567899,
"iat": 1234567899,
"jti": "unique-token-id-abcdef123456"
}
- Public Claim
-
사용자 정의 클레임으로, 공개용 정보를 위해 사용됩니다.
{
"https://example.com/user": {
"username": "user1",
"preferred_language": "ko"
}
}
- Private Claim
-
사용자 정의 클레임으로, 비공개용 정보를 위해 사용됩니다.
{
"token_iss": "ACCESS",
"role": "USER"
}
- Signature
- 토큰 진위 여부를 확인하고 데이터의 무결성을 보장합니다.
- Signature 생성 과정은 다음과 같습니다.
- JWT의 헤더와 페이로드를 Base64로 인코딩
- 비밀 키를 사용하여 인코딩된 헤더와 페이로드를 결합한 후 해시 함수를 적용해 서명을 생성
- 생성된 서명을 JWT Signature에 추가
Token 기반 동작 순서는 다음과 같습니다
- 로그인 : 사용자는 ID, 비밀번호를 입력하여 서버에 로그인 요청을 보냅니다.
- 유효성 검증 : 서버는 DB에 저장된 정보를 통해 사용자의 입력에 대해 유효성을 검증합니다.
- 토큰 생성 : 유효성 검증이 완료된 후, 사용자에 대한 토큰을 생성합니다.
- 토큰 전송 : 서버는 생성된 토큰을 클라이언트에게 전송합니다.
- 페이지 접근 : 이후 클라이언트는 서버에 요청할 경우 토큰을 함께 전송합니다.
- 토큰 검증 : 서버는 클라이언트로부터 토큰을 받고, 서버의 private key로 검증합니다. 이 과정에서 토큰이 위조되었는지, 만료되었는지 확인합니다.
- 응답 : 토큰이 유효할 경우, 서버는 요청에 대한 응답을 클라이언트에게 전송합니다.
Token 방식은 다음과 같은 장/단점이 있습니다.
- 장점
- 무상태(stateless)
- 클라이언트의 상태를 기억할 필요가 없기 때문에, 서버 메모리를 사용하지 않아도 됩니다. 따라서 서버 부하 측면에서 Session보다 좋은 점이 있습니다.
- 서버 간 세션 정보를 공유할 필요가 없기 때문에, 스케일링이 용이합니다.
- 보안
- JWT와 같은 토큰은 서명을 가지고 있기 때문에, 데이터의 무결성을 검증할 수 있습니다. 따라서 토큰의 변조를 확인할 수 있습니다.
- 다중 장치 지원
- 클라이언트는 여러 장치에서 토큰을 사용하여 인증 상태를 유지할 수 있습니다. 따라서 다른 기기에서 동일한 계정을 사용하면서 로그인 상태를 유지할 수 있습니다.
- 단점
- 토큰 크기
- Payload의 양에 따라 JWT 토큰의 크기는 더욱 커질 수 있습니다. 따라서 클라이언트와 서버간 통신에서 데이터 전송 측면에서의 문제가 발생할 수 있습니다.
- 만료 시간 관리
- 토큰은 일반적으로 일정 시간 후에 만료되기 때문에 클라이언트는 만료된 토큰을 사용하기 전에 새로운 토큰을 요청해야 합니다. 따라서 Refresh Token을 추가로 구현해야 할 수 있으며, 이는 추가 로직 및 복잡성이 늘어나게 됩니다.
- 보안 위험
- 클라이언트 측에 토큰이 저장되므로, 해당 토큰을 탈취해 사용하면 인증된 사용자로 가장할 수 있습니다.
인증/인가
인증(Authentication)
인증은 사용자의 신원을 확인하는 과정입니다.
흔히 “로그인”과정으로 이해할 수 있습니다.
인가(Authorization)
인가는 인증된 사용자가 접근할 권한이 있는지 확인하는 과정입니다.
흔히 “권한 부여”과정으로 이해할 수 있습니다.
Interceptor + Session를 사용한 이유
Interceptor 선택 이유
- 요청이 컨트롤러에 도달하기 전(preHandle) 로그인 여부를 확인하고, 응답 후(afterCompletion) 처리 시간을 로깅하는 등 요청 처리 단계를 세분화해야 했습니다. Filter는 서블릿 수준에서 동작해 이러한 세부 제어가 어렵고, AOP는 비즈니스 로직 분리에 적합하지만 요청 라이프사이클 제어에는 과도하다고 판단했습니다.
- 로그인한 유저만 접근할 수 있는 페이지 접근 시, 세션이 존재하는 지 확인하고 세션이 없다면 즉시 차단하는 로직을 쉽게 구현 가능했습니다
Session 방식 선택 이유
- 세션 ID만 쿠키로 전달되고, 실제 데이터는 서버에 안전하게 보관되기 때문에, 서버에서 상태를 관리하는 Session 방식을 선택했습니다
- 주로 브라우저 기반 웹 애플리케이션이며, 쿠키를 통해 세션 ID를 자동 관리하므로 클라이언트 개발이 간소화되었습니다.
- 로그아웃 시 세션을 즉시 무효화하거나, 세션 만료를 서버에서 제어할 수 있어 실시간 상태 변경이 용이했습니다.
- 현재 단일 서버 환경에서 운영 중이며, 필요 시 Redis를 활용한 세션 저장소로 확장 가능해 Token 방식의 Stateless 요구가 크지 않았습니다.
Interceptor + Session를 사용한 이유
Interceptor와 Session 방식을 결합함으로써, 모든 요청에 대해 중앙에서 인증(세션 체크)을 처리할 수 있었습니다. 예를 들어, Interceptor의 preHandle
에서 로그인 여부를 확인하고, 로그인한 사용자만 접근할 수 있는 페이지의 경우, 로그인 하지 않았다면 접근을 차단하는 방식으로 보안성과 효율성을 동시에 확보했습니다.
결론
프로젝트의 요구사항, 구조를 생각해봤을때, Interceptor + Session 방식이 최선의 방식이라 생각했습니다.
이후 인가 로직을 강화하기 위해 AOP를 추가 도입 하였으며, AOP를 활용해 역할 기반 접근 제어를 모듈화함으로써 인증과 인가를 더 명확히 분리하고 유지보수성을 높였습니다.
다음 포스팅은 인가 로직을 강화한 AOP 및 로그인한 유저의 정보를 손쉽게 사용하기 위한 ArgumentResolver에 대해 알아보겠습니다.