- ···
- 27.
[멀티캠퍼스] 풀스택 개발자 아카데미 (27) - Spring Boot(3) - 28.[멀티캠퍼스] 풀스택 개발자 아카데미 (28) - Spring Boot & JWT(1)현재
- 29.
[멀티캠퍼스] 풀스택 개발자 아카데미 (29) - Spring Boot & JWT (2) - ···
지금까지 과정에서는 ReactJS와 SpringBoot의 기초적인 내용을 배웠다.
이번 글에서는 모바일 앱이나 SPA(Single Page Application) 프론트엔드와 통신할 때 필수적인 JWT(JSON Web Token) 인증을 Spring Boot 프로젝트에 구현하는 방법을 알아본다. 기존의 세션 기반 인증과 달리 서버의 확장성을 높여주는 JWT와 실제 코드 구현까지 자세히 다뤄본다.
1. Session의 한계
과거에는 서버가 클라이언트의 상태(State)를 세션 저장소에 저장하고, 세션 ID를 통해 사용자를 식별했다. 하지만 이 방식은 서버가 여러 대로 늘어나는 Scale-out 환경에서 세션 동기화 문제를 야기한다.
반면, JWT는 Stateless(무상태) 방식을 지향한다. 서버는 더 이상 로그인 상태를 저장하지 않고, 클라이언트가 보낸 토큰(Token)의 유효성만 검증하면 된다.
2. JWT (JSON Web Token)
구현에 앞서 JWT의 구조를 살펴보자. JWT는 .(점)을 구분자로 하여 세 부분으로 나뉜다.
- Header (헤더): 토큰의 타입(JWT)과 해싱 알고리즘(HS256, HS512 등) 정보
- Payload (페이로드): 실제로 담길 데이터(Claim). 유저 ID, 유효기간, 권한 등이 포함
- Signature (서명): 헤더와 페이로드를 합친 뒤 비밀키(Secret Key)로 서명한 값. 위변조를 방지
3. Spring Security와 JWT 동작 흐름
Spring Boot에서 이를 구현하기 위해 Spring Security Filter Chain에 커스텀 필터를 끼워 넣어야 한다. 전체적인 인증 흐름은 다음과 같다.
- 클라이언트가 로그인 요청 (ID/PW)
- 서버가 검증 후 JWT Access Token (및 Refresh Token) 발급
- 클라이언트는 이후 요청의 헤더(
Authorization)에 토큰을 담아 보냄 - 서버의 JwtAuthenticationFilter가 요청을 가로채 토큰을 검증
- 검증 성공 시,
Authentication객체를 생성해SecurityContext에 저장 (인증 완료 처리)
4. 본격 구현하기 (Spring Boot 3.x 기준)
4.1. 의존성 추가 (build.gradle)
먼저 build.gradle에 Spring Security와 JWT 관련 라이브러리(jjwt)를 추가한다.
4.2. application.yml 설정
토큰 서명에 사용할 비밀키(Secret Key)를 설정한다. 이 키는 절대 외부에 유출되어서는 안 되며, Base64로 인코딩된 긴 문자열을 사용하는 것이 좋다.
4.2.1. KeyGenerator
토큰 서명에 사용할 비밀키를 생성하는 것도 Java 코드로 작성해볼 수 있다.
해당 코드에는 main() 구문이 있어 단독적으로 실행이 가능하며, 실행 후 생성된 Base64 문자열을 사용할 수 있다.
4.3. JwtProperties
Properties 클래스를 사용하여 application.properties(application.yaml)로부터 값을 입력받을 수 있다.
4.3.1. Properties 클래스
4.3.2. application.properties
다음 2가지 properties 방식 중 하나를 선택하여 작성하면 된다.
4.4. JwtTokenProvider 구현
토큰을 생성하고, 유효성을 검증하며, 토큰에서 회원 정보를 추출하는 핵심 유틸리티 클래스를 작성한다.
4.5. JwtAuthenticationFilter 작성
이제 모든 요청의 앞단에서 헤더를 검사할 필터를 만든다. OncePerRequestFilter를 상속받아 구현하며, 유효한 토큰이 발견되면 SecurityContextHolder에 인증 정보를 심어준다.
Bearer 접두어가 붙는 이유는 OAuth 2.0 표준에서 권장하는 방식이기 때문이다. 이는 토큰의 유형을 명확히 구분하기 위함이며, 이는 W3C와 IETF에서 정의한 HTTP 표준에 따른 것이다. Bearer는 지참인이라는 의미로, 이 토큰을 소지한 자가 해당 리소스에 접근할 수 있음을 나타낸다.Basic 인증 방식은 사용자 이름과 비밀번호를 인코딩하여 전송하고, PoP(Proof of Possession) 토큰 방식은 토큰 소유권을 증명하는 추가적인 메커니즘을 포함한다. 반면, Bearer 토큰은 단순히 토큰 자체만으로 접근 권한을 부여하므로, HTTPS와 같은 보안 채널을 통해 전송되어야 한다.4.6. SecurityConfig 설정
Spring Security 6.0(Spring Boot 3.0)부터는 설정 방식이 SecurityFilterChain Bean을 등록하는 방식으로 변경되었다. 여기서 중요한 점은 세션 정책을 Stateless로 설정하고, 우리가 만든 필터를 UsernamePasswordAuthenticationFilter 앞에 배치하는 것이다.
4.7. Auth 기능 구현
마지막으로 로그인 API를 구현하여 JWT 토큰을 발급하는 로직을 작성한다.
- DTO 클래스: 로그인 요청과 응답을 위한 DTO
- LoginResponse DTO: 로그인 응답에 토큰과 사용자 정보를 담는 DTO
- Controller Layer: 로그인 요청을 받아 서비스에 위임
- Service Layer: 실제 인증 로직과 토큰 발급을 담당
5. 테스트 및 결과
모든 설정이 끝났으니 Postman으로 테스트를 해본다.
- 로그인 요청:
/api/auth/login으로 ID/PW를 보낸다. - 토큰 발급: 응답으로
accessToken이 정상적으로 반환된다.
- 인증이 필요한 요청: 이제 발급받은 토큰을 헤더(
Authorization: Bearer {Token})에 넣고, 회원 전용 API(/api/members/me)를 호출해 본다. 정상적으로 내 정보를 불러온다면 성공이다.
6. 마무리
이렇게 Spring Boot와 Spring Security를 활용해 JWT 인증 시스템을 구축해 보았다. 처음에는 설정 코드가 복잡해 보일 수 있지만, 한 번 구축해 두면 프론트엔드와의 결합도를 낮추고 서버 확장에 유연한 강력한 백엔드를 가질 수 있다.
다음 글에서는 JWT 인증을 더욱 견고하게 만들기 위한 Refresh Token 관리와, 이 JWT 인증을 ReactJS 프론트엔드와 연동하는 방법에 대해 다뤄볼 예정이다.