JWT 구현 미리 해보기
일단 지금까지 구현해서 사용했던 jwt 로직을 정리해서 보내고 어떤점이 부족한지 ai 한테 물어봤다


정리하자면 아래와 같다
1. 토큰 꺼내고 파싱, 검증, 복호화 (Claims 꺼내기) 로직은 JwtUtil 에서 처리
2. UserDetailsService 를 만들어서 DB에서 사용자 조회 후 Authentication 객체 만들기
3. CustomUserDetails implements UserDetails 만들기 (PK, role 등 넣기) -> 이후 AuthenticationPrincipal 로 뽑아쓰기
4. Refresh 토큰 관리하는 곳을 추가로 구현
5. AuthenticationEntryPoint 를 추가로 구현
6. blackList 를 추가로 구현 (로그아웃) << 요 부분은 안 함
천천히 수정하면서 구현해보기
구현하면서 Custom error 부분도 같이 구현했다
https://github.com/jiwon-e-e/jwt-practice
GitHub - jiwon-e-e/jwt-practice
Contribute to jiwon-e-e/jwt-practice development by creating an account on GitHub.
github.com
두고두고 추가하면서 연습해보기
동작 방법은 아래와 같다


오류 났던 부분
문제 1
분명 auth/api 요청은 FilterChain 에서 넘기기로 되어있는데 login 요청을 보내면 401 에러 발생
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable())
.cors(cors -> cors.configure(http))
# 세션 인증을 비활성화
.sessionManagement(session ->
session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
)
# 인가 설정
.authorizeHttpRequests(auth ->
auth
# api auth 는 filter 패스~
.requestMatchers("/api/auth/**").permitAll()
.anyRequest().authenticated()
)
.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class)
.exceptionHandling(ex ->
ex.authenticationEntryPoint(customAuthenticationEntryPoint)
);
return http.build();
}
(CommonResponse 적용도 안 됨....)
해결
로그인 요청(api/auth/login)이 들어오면 어차피 jwtFilter 는 통과시키기 때문에 바로 controller 를 넘어 Service 로 들어감
# Filter 는 FilterChain 에서 설정해주더라도 일단 작동을 하기 때문에
# Token 이 비어있다면 FilterChain 에서 검사할 수 있도록 바로 넘겨버리는 로직
if (bearerToken == null || !bearerToken.startsWith("Bearer ")){
filterChain.doFilter(request, response);
return;
}
@Transactional
public String login(LoginRequest request) {
# request email 로 user 찾기, 비밀번호 맞는지 확인하는 로직 숨김
String accessToken = jwtUtil.createAccessToken(user.getId());
String refreshToken = jwtUtil.createRefreshToken(user.getId(), user.getRole().name());
RefreshToken tokenEntity = refreshTokenRepository.findById(user.getId())
.orElse(new RefreshToken(user.getId(), refreshToken));
refreshTokenRepository.save(tokenEntity);
return accessToken;
}
문제 2
Reissue 를 ....... 왜 발급해주는지 잘 이해가 안 갔음
client 가 어떻게 만료되는 시간을 어떻게 알고 reissue api 를 요청하는지
해결
몰랐는데 ...! 사용자가 만료될때쯤 직접 요청하는 형식이 아니라 자동으로 처리됨
클라이언트가 다른 요청 (get.. post 등등 로그인이 필요한 모든 요청) 을 보내면

Filter 에서 해당 토큰이 유효한지 확인
-> Access 토큰이 만료되었네? ? -> 401 Unauthorized 로 보냄

이 때
클라이언트 (프론트 서버) 에서 만료시간을 확인하고 reissue 를 진행!!!!
-> 새로운 access token 을 발급받음

-> 원래 요청 재시도

응답을 클라이언트한테 줌

결론!!! 사용자가 전혀 모르게 처리됨
백엔드에서는 Access Token 이 만료되면 401을 던져주는 역할
프론트엔드에서는 401이 오면 reissue 를 처리해봄
-> 그럼 모든 401 요청을 reissue 처리하는가?
그건 또 아님
토큰 위조나 존재하지 않는 토큰의 경우는 reissue 처리할 필요가 없음
401을 던질 때 error 이름으로 expired Token 이라고 주면 프론트에서 처리하기 편하다!
토큰의 상태에 따라 다른 에러코드를 내주는 연습을 하거나 코드를 짜보는게 좋을듯
이후 구현해볼 부분
보통 Refresh Token 은 내부 RDS 가 아니라 Redis 를 사용하여 저장 (Docker 사용)
-> 성능이 좋고 자동으로 만료 시간을 제어해줌
https://mysolutions.tistory.com/27
[JWT] Redis TTL로 Refresh Token 만료 자동화와 예외 처리 구현하기
1. Refresh Token의 저장소에 대한 고민처음에는 Refresh Token을 데이터베이스에 저장하고, 로그아웃 시 해당 레코드를 삭제하는 방식을 고려했습니다. 클라이언트에서 로그아웃을 요청하면 Access Token
mysolutions.tistory.com
'SPARTA 과제 > TEAM | commerce' 카테고리의 다른 글
| 팀) 커머스 피드백 반영 (0) | 2026.03.08 |
|---|---|
| 팀) 커머스 - Soft Delete (0) | 2026.02.27 |
| 팀) 커머스 트러블슈팅 (3) (0) | 2026.02.27 |
| 팀) 커머스 - DB 생성 시 값 넣기 (0) | 2026.02.27 |
| 팀) 커머스 - 리스트 출력 (0) | 2026.02.22 |