IT/Spring

Spring Boot JWT 로그인 구현 방법 | Spring Security 설정부터 Access Token·Refresh Token 발급·검증까지

PARK_90 2026. 4. 2. 22:30
300x250

Spring Boot에서 로그인 기능을 구현하려고 하면 많은 개발자가 여기서 막힙니다. 세션 로그인과 JWT 로그인은 무엇이 다르고, Spring Security 설정은 어떻게 잡아야 하며, Access Token과 Refresh Token은 어디서 발급하고 어떻게 검증해야 하는지 한 번에 정리되지 않기 때문입니다.

특히 JWT 로그인은 단순히 토큰 하나 발급해서 끝나는 구조가 아닙니다. 인증 필터, Security 설정, 토큰 생성, 토큰 검증, 만료 처리, Refresh Token 재발급 흐름까지 같이 봐야 실제 서비스에 넣을 수 있습니다. 그래서 이 글에서는 개념 설명과 함께 실제 구현 흐름, 자주 막히는 포인트, 확인 방법까지 같이 정리합니다.

결론부터 말하면 Spring Boot JWT 로그인 구현의 핵심은 Spring Security의 인증 흐름 안에 JWT 발급/검증 로직을 정확히 끼워 넣는 것입니다. 그리고 실제 운영 관점에서는 Access Token만 볼 게 아니라 Refresh Token 저장 전략, 만료 처리, 예외 응답 구조까지 같이 설계해야 합니다.

  • Spring Boot JWT 로그인은 어떤 구조로 동작하는가?
  • Spring Security 설정은 어떻게 잡아야 하는가?
  • Access Token / Refresh Token은 어떻게 발급하고 검증하는가?
  • 구현할 때 어디서 가장 많이 막히는가?
핵심 요약
Spring Security는 인증/인가 흐름을 관리하고, JWT는 그 안에서 인증 정보를 담아 전달합니다.
Access Token은 API 인증에 쓰고, Refresh Token은 재발급에 씁니다.
구현 핵심은 Security 설정 + JWT 필터 + 토큰 유틸 + 로그인/재발급 API를 함께 맞추는 것입니다.
즉 JWT 로그인은 토큰 발급만이 아니라 검증·만료·재발급까지 포함한 전체 인증 구조입니다.
728x90
쉬운 정의
JWT 로그인은 서버 세션 대신 토큰으로 인증 상태를 확인하는 방식입니다.

세션 로그인은 서버가 로그인 상태를 직접 들고 있는 구조이고, JWT 로그인은 서버가 발급한 토큰을 클라이언트가 가지고 다니면서 요청마다 인증을 증명하는 구조입니다.

Spring Boot에서 JWT 로그인 구현을 한다는 건 보통 아래를 함께 만든다는 뜻입니다.

  • 로그인 API
  • 사용자 인증 로직
  • JWT 발급 로직
  • 요청 시 JWT 검증 필터
  • 만료/예외 응답 처리
  • Refresh Token 재발급 API
Spring Boot JWT 로그인은 “로그인 성공 시 토큰을 발급하고, 이후 요청에서는 그 토큰으로 인증을 확인하는 구조”라고 이해하면 됩니다.
비교/구조
세션 로그인과 JWT 로그인을 먼저 비교하면 구조가 훨씬 쉽게 잡힙니다.
비교 항목 세션 로그인 JWT 로그인
인증 상태 저장 서버 세션 저장소 토큰 자체에 인증 정보 포함
요청 인증 방식 세션 조회 JWT 서명/만료 검증
확장성 세션 저장 전략 영향 큼 상대적으로 Stateless 구조에 유리
주의점 세션 관리 부담 만료/재발급/탈취 대응 설계 필요

JWT 로그인 전체 흐름은 보통 아래처럼 봅니다.

사용자 로그인 요청
   ↓
AuthenticationManager 인증
   ↓
인증 성공
   ↓
Access Token / Refresh Token 발급
   ↓
클라이언트가 Access Token으로 API 요청
   ↓
JWT 필터가 토큰 검증
   ↓
인증 정보(SecurityContext) 저장
   ↓
컨트롤러 진입
구현 핵심 흐름
로그인 API에서 인증을 끝내고, 발급된 JWT를 이후 모든 요청의 인증 근거로 사용합니다. 그리고 요청이 들어올 때마다 JWT 필터가 토큰을 검증한 뒤 인증 객체를 SecurityContext에 넣어줘야 Spring Security가 인증된 사용자로 인식합니다.

예시
실제 구현 흐름에 대입해보면 어디서 무엇을 넣어야 하는지 보입니다.

예시 1. 로그인 성공 시 토큰 발급

로그인 API에서 아이디/비밀번호를 받아 인증하고, 인증 성공 시 Access Token과 Refresh Token을 발급할 수 있습니다.

@PostMapping("/login")
public ResponseEntity<LoginResponse> login(@RequestBody LoginRequest request) {
    Authentication authentication = authenticationManager.authenticate(
        new UsernamePasswordAuthenticationToken(request.getEmail(), request.getPassword())
    );

    String accessToken = jwtTokenProvider.createAccessToken(authentication);
    String refreshToken = jwtTokenProvider.createRefreshToken(authentication);

    return ResponseEntity.ok(new LoginResponse(accessToken, refreshToken));
}

이때 중요한 건 단순히 토큰을 만드는 것이 아니라,

  • 인증이 끝난 사용자 정보로 토큰을 만들고
  • 만료 시간을 분리하고
  • Refresh Token 저장 전략까지 함께 잡는 것입니다.

예시 2. 요청마다 JWT 검증 필터 실행

API 요청에서는 보통 Authorization: Bearer {token} 헤더를 받아 JWT 필터에서 토큰을 검증합니다.

@Override
protected void doFilterInternal(HttpServletRequest request,
                                HttpServletResponse response,
                                FilterChain filterChain) throws ServletException, IOException {
    String token = resolveToken(request);

    if (token != null && jwtTokenProvider.validateToken(token)) {
        Authentication authentication = jwtTokenProvider.getAuthentication(token);
        SecurityContextHolder.getContext().setAuthentication(authentication);
    }

    filterChain.doFilter(request, response);
}

이 필터가 정상 동작해야 컨트롤러나 @PreAuthorize 구간에서 인증된 사용자로 인식됩니다.

자주 막히는 포인트 / 문제해결
JWT 로그인은 개념보다 Security 설정과 필터 순서에서 더 많이 막힙니다.

문제 1. permitAll 설정을 했는데도 401이 나는 경우

보통은 JWT 필터가 인증이 필요 없는 경로까지 검사하거나, 예외 처리 흐름이 정리되지 않았을 때 자주 발생합니다.

  • 로그인/회원가입 경로를 permitAll()로 열었는지
  • JWT 필터에서 토큰이 없을 때 바로 예외를 던지지 않는지
  • AuthenticationEntryPoint 예외 응답이 의도대로 설정됐는지

를 먼저 확인해야 합니다.

문제 2. 토큰은 발급되는데 인증이 안 붙는 경우

이 경우는 보통 아래 둘 중 하나입니다.

  • 필터에서 SecurityContextHolder에 인증 객체를 넣지 않음
  • 토큰에서 사용자 정보를 꺼내 Authentication으로 변환하는 로직이 잘못됨

즉 “토큰 검증 성공”과 “인증 객체 주입 성공”은 다른 문제라는 점을 꼭 구분해야 합니다.

설정/확인 체크리스트
  • SecurityFilterChain에서 로그인/회원가입 경로가 열려 있는지 확인
  • JWT 필터가 UsernamePasswordAuthenticationFilter 앞에 들어갔는지 확인
  • 토큰 검증 후 SecurityContextHolder에 Authentication을 넣는지 확인
  • Access Token / Refresh Token 만료 시간이 분리되어 있는지 확인
  • Refresh Token 저장 위치(DB/Redis 등)를 운영 정책과 맞췄는지 확인

실무 포인트
과장 없이 선택 기준과 주의점만 짧고 명확하게 정리합니다.
  • Access Token은 짧게, Refresh Token은 상대적으로 길게 운영하는 경우가 많습니다.
  • Refresh Token을 서버에 저장하지 않으면 탈취/폐기 대응이 까다로워질 수 있습니다.
  • JWT 로그인은 무조건 세션보다 낫다기보다, Stateless API 구조에 잘 맞는 방식으로 보는 것이 정확합니다.
  • 권한(Role) 정보가 토큰에 어떻게 담기는지와, 권한 변경 시 어떻게 반영할지까지 같이 봐야 운영 품질이 올라갑니다.
초보자가 꼭 체크할 포인트
  • JWT 로그인은 토큰 발급만이 아니라 검증 필터와 예외 처리까지 함께 구현해야 합니다.
  • Spring Security 설정과 JWT 필터 순서가 맞지 않으면 토큰이 있어도 401이 날 수 있습니다.
  • Access Token과 Refresh Token은 목적과 만료 시간이 다릅니다.
  • 실제 운영에서는 토큰 탈취, 만료, 재발급, 로그아웃 처리까지 같이 설계해야 합니다.
FAQ
  • Q. Spring Boot JWT 로그인은 세션을 전혀 안 쓰나요?
    → 보통 Stateless 구조를 지향하지만, Refresh Token 저장이나 운영 정책에 따라 서버 저장소를 병행할 수 있습니다.
  • Q. Access Token과 Refresh Token은 왜 둘 다 필요한가요?
    → Access Token은 인증 요청용, Refresh Token은 Access Token 만료 시 재발급용으로 역할이 다릅니다.
  • Q. Spring Security 없이 JWT 로그인 구현이 가능한가요?
    → 가능은 하지만, 실무에서는 인증/인가 흐름 관리 때문에 Spring Security와 함께 쓰는 경우가 훨씬 일반적입니다.
결론
JWT 로그인 구현은 Security 흐름 안에서 토큰 발급과 검증을 정확히 연결하는 작업입니다.

Spring Boot JWT 로그인 구현은 단순히 토큰을 하나 만들어 응답하는 작업이 아닙니다.

  • 로그인 인증
  • Access Token / Refresh Token 발급
  • 요청마다 JWT 검증
  • SecurityContext 인증 주입
  • 만료/재발급/예외 처리

까지 모두 이어져야 실제 서비스에서 안정적으로 동작합니다.

즉 이 주제는 “JWT 개념”만 이해해서 끝나는 것이 아니라, Spring Security 설정과 필터 흐름 안에서 어떻게 연결되는지를 같이 봐야 비로소 구현이 됩니다.

Spring Boot JWT 로그인 구현의 핵심은 인증 성공 후 토큰을 발급하고, 이후 요청에서는 JWT 검증 필터로 인증 상태를 복원하는 흐름을 정확히 맞추는 것입니다.

※ 이 글은 Spring Boot JWT 로그인 구현 흐름을 이해하기 위한 입문·실전 연결 가이드입니다. 실제 운영 환경에서는 토큰 저장 전략, 보안 정책, 재발급 정책, 로그아웃/강제 만료 처리까지 함께 고려해야 합니다.

728x90