프로젝트/기업 일정 관리 웹

Access Token & Refresh Token 설계와 구현

yoon4360 2025. 4. 2. 00:54
@PostMapping("/login")
public ResponseEntity<Map<String, String>> login(@RequestBody Map<String, String> loginData) {
    // ... 로그인 로직 생략 ...
    String accessToken = jwtService.createToken(userId);
    String refreshToken = jwtService.createRefreshToken(userId);
    userService.updateRefreshToken(userId, refreshToken);

    return ResponseEntity.ok(Map.of(
        "accessToken", accessToken,
        "refreshToken", refreshToken
    ));
}

JWT 기반 인증 시스템을 구성할 때 가장 중요한 요소는

Access TokenRefresh Token을 어떻게 설계하고 관리할 것인가이다.
Access Token은 짧은 수명으로 인증을 담당하고, Refresh Token은 인증 상태 유지를 책임진다.
이번 글에서는 이 두 토큰의 구체적인 역할, 만료 처리, 재발급 흐름을 중심으로 정리하려 한다.

 


 

Access Token vs Refresh Token

항목 Access Token Refresh Token
목적 API 요청 인증 Access Token 갱신
수명 수분 ~ 수십 분 수 시간 ~ 수일
저장 위치 메모리 / localStorage / Cookie HttpOnly 쿠키 or DB/Redis
서버 검증 JWT 자체 검증 서버 저장소에서 추가 검증 필요
사용 방식 Authorization 헤더에 포함 서버에 자동 포함 or 직접 전송

 

동작 흐름 요약

1. 로그인 시 토큰 발급

  • 사용자가 이메일/비밀번호로 로그인 요청
  • 서버에서 인증 성공 시:
    • Access Token 생성 → 응답 본문에 포함
    • Refresh Token 생성 → DB 저장 및 클라이언트에 쿠키 or 본문 전달

2. Access Token을 통한 API 요청

  • 클라이언트는 Access Token을 Authorization: Bearer <token> 형식으로 전송
  • 서버는 이 토큰의 서명을 검증하고, 유효하면 요청을 처리

3. Access Token 만료

  • Access Token은 보안상의 이유로 만료 주기가 짧음(수 분~수십 분)
  • 만료 시 API 요청은 401 Unauthorized를 반환

4. Refresh Token을 통한 Access Token 재발급

  • 클라이언트는 저장된 Refresh Token으로 /api/token/refresh API 호출
  • 서버는 DB의 Refresh Token과 비교하여 유효성 검증
  • 유효하다면 새로운 Access Token을 생성하여 반환

 


 

1.  Refresh Token 발급 및 저장

// JwtService.java
public String createRefreshToken(Long userId) {
    Date now = new Date();
    Date expiry = new Date(now.getTime() + Duration.ofDays(7).toMillis());

    return JWT.create()
        .withClaim("userId", userId)
        .withExpiresAt(expiry)
        .sign(Algorithm.HMAC256(SECRET));
}​

 

// UserService.java
@Transactional
public void updateRefreshToken(Long userId, String refreshToken) {
    User user = userRepository.findById(userId)
        .orElseThrow(() -> new UsernameNotFoundException("사용자 없음"));
    user.setRefreshToken(refreshToken);
    userRepository.save(user);
}
  • Refresh Token은 **DB(User 테이블)**에 저장하여 토큰 위조나 탈취 시 대응 가능하도록 한다.
  • 토큰 유효 기간은 일반적으로 7일 ~ 30일 사이로 설정한다.

 


 

2.  로그인 응답 시 토큰 반환

@PostMapping("/login")
public ResponseEntity<Map<String, String>> login(@RequestBody Map<String, String> loginData) {
    // ... 로그인 로직 생략 ...
    String accessToken = jwtService.createToken(userId);
    String refreshToken = jwtService.createRefreshToken(userId);
    userService.updateRefreshToken(userId, refreshToken);

    return ResponseEntity.ok(Map.of(
        "accessToken", accessToken,
        "refreshToken", refreshToken
    ));
}

 

 

 

  • 클라이언트는 accessToken을 Authorization 헤더에 저장한다.
  • refreshToken은 보안을 위해 HttpOnly Cookie 또는 브라우저 외부 저장소에 보관하는 것을 권장한다.

 

 


 

3. Refresh Token을 이용한 Access Token 재발급

@RestController
@RequestMapping("/api/token")
@RequiredArgsConstructor
public class TokenController {

    private final JwtService jwtService;
    private final UserService userService;

    @PostMapping("/refresh")
    public ResponseEntity<Map<String, String>> refresh(@RequestBody Map<String, String> request) {
        String refreshToken = request.get("refreshToken");

        // 1. 토큰 자체 검증
        DecodedJWT decodedJWT = JWT.require(Algorithm.HMAC256("aVeryLongAndSecureSecretKeyForJWTAuthentication123!@#"))
                .build().verify(refreshToken);
        Long userId = decodedJWT.getClaim("userId").asLong();

        // 2. 저장된 Refresh Token과 비교
        User user = userService.findByRefreshToken(refreshToken)
                .orElseThrow(() -> new RuntimeException("유효하지 않은 Refresh Token"));

        // 3. 새 Access Token 발급
        String newAccessToken = jwtService.createToken(userId);

        return ResponseEntity.ok(Map.of("accessToken", newAccessToken));
    }
}
보완 포인트
  • Refresh Token 자체가 유효한지 검증한다. (verify)
  • DB에 저장된 값과 일치하는지 비교한다.
  • 일치하지 않을 경우 → 탈취/위조로 간주하고 로그인 재요청

 


 

마무리

  • Access Token은 인증의 핵심이지만 수명이 짧고, 이를 보완하는 역할을 Refresh Token이 담당한다.
  • Refresh Token을 DB나 Redis에 저장하고, 재발급 요청 시 철저히 검증해야 한다.
  • 토큰 저장 위치, 전송 방식, 검증 순서 등 모든 요소가 보안에 직접적으로 연결되므로 구조적으로 신중하게 설계해야 한다.

'프로젝트 > 기업 일정 관리 웹' 카테고리의 다른 글

REST API 에서 Enum 직렬화 문제  (0) 2025.04.02
Redis 적용하기  (0) 2025.04.02
도메인 별 SSL 인증서 문제 해결  (0) 2025.04.02
CORS 정책 오류 해결  (0) 2025.04.02
JWT 인증 시스템 구현  (0) 2025.04.02