๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๐Ÿ“– Spring/๐Ÿ”’ ์ธ์ฆ&์ธ๊ฐ€

[Spring] Access Token vs Refresh Token ์™„๋ฒฝ ์ •๋ฆฌ + Redis ์ €์žฅ ์ด์œ ๊นŒ์ง€

by carrot0911 2025. 5. 14.

ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๋ฉด์„œ JWT ์ธ์ฆ ์‹œ์Šคํ…œ์„ ์ง์ ‘ ๊ตฌํ˜„ํ•˜๋Š” ๊ณผ์ •์—์„œ ํ•„์š”์„ฑ์„ ๋А๋ผ๊ณ  ๊ณต๋ถ€ํ•˜๊ณ  ์ •๋ฆฌํ•œ ๋‚ด์šฉ์„ ๋‹ด์•˜๋‹ค.
๊ฐœ๋ฐœ์„ ํ•˜๋‹ค ๋ณด๋ฉด ์ด๋Ÿฐ ์งˆ๋ฌธ์ด ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ์ƒ๊ธด๋‹ค.

"Access Token์ด๋ž‘ Refresh Token, ์ด ๋‘˜์€ ๋„๋Œ€์ฒด ๋ญ๊ฐ€ ๋‹ค๋ฅด๊ณ , ์–ด๋–ป๊ฒŒ ๊ด€๋ฆฌํ•ด์•ผ ํ• ๊นŒ?"

ํŠนํžˆ Refresh Token์„ ์–ด๋””์— ์ €์žฅํ•ด์•ผ ํ• ์ง€, Redis๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ด์œ ๋Š” ๋ญ”์ง€ ๊ถ๊ธˆ์ฆ์ด ๋งŽ์•˜๋‹ค.
๊ทธ๋ž˜์„œ Access Token๊ณผ Refresh Token ๊ฐœ๋…๋ถ€ํ„ฐ Refresh Token์„ ์„œ๋ฒ„, ํŠนํžˆ Redis์— ์ €์žฅํ•ด์•ผ ํ•˜๋Š” ์ด์œ ๊นŒ์ง€ ํ•œ ๋ฒˆ์— ๊น”๋”ํ•˜๊ฒŒ ์ •๋ฆฌํ•ด๋ณด๋ ค๊ณ  ํ•œ๋‹ค.

 

๐Ÿ”‘ Access Token์ด๋ž€?!

๋กœ๊ทธ์ธํ•˜๋ฉด ๋ฐœ๊ธ‰๋ฐ›๋Š” ์งง์€ ์ˆ˜๋ช…์˜ ํ† ํฐ์ด๋‹ค. (ex. 30๋ถ„)
์‚ฌ์šฉ์ž๋Š” ๋งค ์š”์ฒญ๋งˆ๋‹ค Access Token์„ HTTP ํ—ค๋”์— ๋‹ด์•„ ๋ณด๋‚ด๊ณ , ์„œ๋ฒ„๋Š” ์ด ํ† ํฐ์„ ๊ฒ€์‚ฌํ•ด์„œ
"์ด ์‚ฌ์šฉ์ž๊ฐ€ ์ธ์ฆ๋œ ์‚ฌ์šฉ์ž์ธ์ง€" ํ™•์ธํ•œ๋‹ค.
Access Token์€ ์ˆ˜๋ช…์ด ์งง๊ธฐ ๋•Œ๋ฌธ์—, ๋งŒ๋ฃŒ๋˜๋ฉด ๋‹ค์‹œ ๋กœ๊ทธ์ธํ•ด์•ผ ํ•œ๋‹ค.

๐ŸŽฏ ๋ชฉ์ 
"์ด ์‚ฌ์šฉ์ž๊ฐ€ ์ธ์ฆ๋œ ์‚ฌ์šฉ์ž์ธ์ง€ ํŒ๋‹จํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•œ๋‹ค."

 

๐Ÿ”‘ Refresh Token์ด๋ž€?!

Access Token์ด ๋งŒ๋ฃŒ๋  ๋•Œ๋งˆ๋‹ค ๋กœ๊ทธ์ธํ•˜๋ฉด ์‚ฌ์šฉ์ž ๊ฒฝํ—˜(UX)์ด ๋„ˆ๋ฌด ๋‚˜๋น ์ง„๋‹ค.
Refresh Token์€ ์ด๋Ÿฐ ๋ถˆํŽธ์„ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋“ฑ์žฅํ–ˆ๋‹ค.
๐Ÿ‘‰  Access Token์ด ๋งŒ๋ฃŒ๋์„ ๋•Œ, ์ƒˆ๋กœ์šด Access Token์„ ๋ฐœ๊ธ‰๋ฐ›๊ธฐ ์œ„ํ•œ ํ† ํฐ
๐Ÿ‘‰  ์‚ฌ์šฉ์ž์—๊ฒŒ ๋…ธ์ถœ๋˜์ง€๋งŒ, ์„œ๋ฒ„์—๋„ ์ €์žฅ๋œ๋‹ค.
๐Ÿ‘‰  ์ˆ˜๋ช…์ด ๊ธธ๋‹ค. (7์ผ ~ 30์ผ ์ด์ƒ)

๐ŸŽฏ ๋ชฉ์ 
"Access Token์ด ๋งŒ๋ฃŒ๋์ง€๋งŒ, ์‚ฌ์šฉ์ž๊ฐ€ ๋กœ๊ทธ์ธ ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•œ๋‹ค."

 

Access Token vs Refresh Token

ํ•ญ๋ชฉ Access Token Refresh Token
์šฉ๋„ ์ธ์ฆ(Authentication) Access Token ์žฌ๋ฐœ๊ธ‰
์ˆ˜๋ช… ์งง์Œ (15~30๋ถ„) ๊น€ (7~30์ผ)
์ €์žฅ ์œ„์น˜ ๋ธŒ๋ผ์šฐ์ € (๋ฉ”๋ชจ๋ฆฌ/์ฟ ํ‚ค) ๋ธŒ๋ผ์šฐ์ € + ์„œ๋ฒ„ (Redis ๋“ฑ)
๋…ธ์ถœ ๊ฐ€๋Šฅ์„ฑ ์ƒ๋Œ€์ ์œผ๋กœ ์•ˆ์ „ ํƒˆ์ทจ ์‹œ ์œ„ํ—˜ ๋†’์Œ
๋ณด์•ˆ ๊ด€๋ฆฌ ๋ฐฉ์‹ ์งง์€ ๋งŒ๋ฃŒ ์‹œ๊ฐ„์œผ๋กœ ํ•ด๊ฒฐ ์„œ๋ฒ„ ์ €์žฅ์†Œ๋ฅผ ํ†ตํ•œ ๋ณ„๋„ ๊ด€๋ฆฌ ํ•„์ˆ˜

 

๊ทธ๋ ‡๋‹ค๋ฉด.. Refresh Token์€ ์™œ ํ•„์š”ํ•œ๊ฐ€?

๊ฐ„๋‹จํ•˜๊ฒŒ ์ธ์ฆ ํ๋ฆ„์„ ์ƒ์ƒํ•ด ๋ณด์ž.
1. ์‚ฌ์šฉ์ž๊ฐ€ ๋กœ๊ทธ์ธ → Access Token ๋ฐœ๊ธ‰
2. 30๋ถ„ ๋’ค Access Token ๋งŒ๋ฃŒ
3. ๋‹ค์‹œ ๋กœ๊ทธ์ธํ•˜๋ ค๊ณ  ํ•˜๋ฉด..? ๋„ˆ๋ฌด ๊ท€์ฐฎ๋‹ค.

๊ทธ๋ž˜์„œ Access Token์ด ๋งŒ๋ฃŒ๋˜๋”๋ผ๋„, Refresh Token์„ ์‚ฌ์šฉํ•ด์„œ ์ž๋™์œผ๋กœ ์žฌ๋ฐœ๊ธ‰ํ•˜๋Š” ๊ตฌ์กฐ๋ฅผ ๋งŒ๋“ ๋‹ค.
๊ทธ ๋•๋ถ„์— ์‚ฌ์šฉ์ž๋Š” ๋กœ๊ทธ์ธ ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค!

 

Refresh Token ๋™์ž‘ ํ๋ฆ„

[๋กœ๊ทธ์ธ ์‹œ]
  โฌ‡๏ธ
Access Token + Refresh Token ๋ฐœ๊ธ‰
  โฌ‡๏ธ
Access Token์€ ํด๋ผ์ด์–ธํŠธ์— ์ €์žฅ
Refresh Token์€ ์„œ๋ฒ„(์˜ˆ: Redis)์— ์ €์žฅ
  โฌ‡๏ธ
[Access Token ๋งŒ๋ฃŒ ์‹œ]
  โฌ‡๏ธ
Refresh Token์œผ๋กœ Access Token ์žฌ๋ฐœ๊ธ‰ ์š”์ฒญ
  โฌ‡๏ธ
์„œ๋ฒ„๊ฐ€ Redis์—์„œ Refresh Token ๊ฒ€์ฆ → ์ƒˆ Access Token ๋ฐœ๊ธ‰

 

Refresh Token ๋„์ž… ์‹œ ์ฃผ์˜์‚ฌํ•ญ

๐Ÿ‘‰  Redis ๊ฐ™์€ ์ €์žฅ์†Œ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์„œ๋ฒ„์— Refresh Token์„ ์ €์žฅํ•ด์•ผ ํ•œ๋‹ค.
๐Ÿ‘‰  Refresh Token ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋ฅผ ๊ผญ ๊ตฌํ˜„ํ•ด์•ผ ํ•œ๋‹ค.
๐Ÿ‘‰  ๋กœ๊ทธ์•„์›ƒ ์‹œ Refresh Token ์‚ญ์ œ ์ฒ˜๋ฆฌ๋„ ํ•„์ˆ˜๋‹ค! (๋ณด์•ˆ ๋ฌธ์ œ ๋Œ€๋น„)

 

๐Ÿ’ป ์ฝ”๋“œ ์˜ˆ์‹œ

JwtUtil : Refresh Token ์ƒ์„ฑ

// Refresh Token ์ƒ์„ฑ
public String createRefreshToken(Long memberId) {
    Date now = new Date();

    return BEARER_PREFIX + Jwts.builder()
        .setSubject(String.valueOf(memberId))
        .setIssuedAt(now)
        .setExpiration(new Date(now.getTime() + REFRESH_TOKEN_EXPIRATION))
        .signWith(key, signatureAlgorithm)
        .compact();
}

AuthService : ๋กœ๊ทธ์ธ ์‹œ Access + Refresh ๋ฐœ๊ธ‰

public LoginResponse login(LoginRequest request) {
    Member member = memberRepository.findByEmail(request.email())
        .orElseThrow(() -> new NotFoundException(ErrorCode.MEMBER_NOT_FOUND));

    if (!passwordEncoder.matches(request.password(), member.getPassword())) {
        throw new UnauthorizedException(ErrorCode.INVALID_PASSWORD);
    }

    String accessToken = jwtUtil.createToken(
        member.getId(),
        member.getEmail(),
        member.getNickname(),
        member.getAuthority());

    String refreshToken = jwtUtil.createRefreshToken(member.getId());

    return LoginResponse.from(accessToken, refreshToken);
}

 

๊ทธ๋Ÿผ.. Refresh Token์€ ์–ด๋””๋‹ค ์ €์žฅํ•ด์•ผ ํ• ๊นŒ?

Access Token๊ณผ Refresh Token ๊ฐœ๋…์„ ์ดํ•ดํ–ˆ๋‹ค๋ฉด, ์ด์ œ ์ด๋Ÿฐ ๊ณ ๋ฏผ์ด ๋ฐœ์ƒํ•œ๋‹ค.

"Refresh Token์€ ํด๋ผ์ด์–ธํŠธ์—๋งŒ ์ €์žฅํ•ด๋„ ๊ดœ์ฐฎ์„๊นŒ..?"

ํ•˜์ง€๋งŒ Refresh Token์€ ์ˆ˜๋ช…์ด ๊ธธ๊ณ , ํƒˆ์ทจ๋  ๊ฒฝ์šฐ ๋ณด์•ˆ์ƒ ํฐ ๋ฌธ์ œ๊ฐ€ ๋  ์ˆ˜ ์žˆ๋‹ค.
๊ทธ๋ž˜์„œ ๋งŽ์€ ์‹œ์Šคํ…œ์—์„œ๋Š” Refresh Token์„ Redis ๊ฐ™์€ ๋น ๋ฅธ ์ €์žฅ์†Œ์— ์•ˆ์ „ํ•˜๊ฒŒ ๋ณด๊ด€ํ•˜๋Š” ๋ฐฉ์‹์„ ํƒํ•œ๋‹ค.

Refresh Token์„ Redis์— ์ €์žฅํ•ด์•ผ ํ•˜๋Š” ์ด์œ 

์„œ๋ฒ„๊ฐ€ Refresh Token์„ ๊ด€๋ฆฌํ•ด์•ผ ํ•œ๋‹ค.

Access Token์€ ํด๋ผ์ด์–ธํŠธ๋งŒ์œผ๋กœ ์ถฉ๋ถ„ํ•˜์ง€๋งŒ, Refresh Token์€ ์„œ๋ฒ„ ์ €์žฅ์ด ํ•„์ˆ˜์ด๋‹ค.
์„œ๋ฒ„์— ์ €์žฅํ•˜์ง€ ์•Š์œผ๋ฉด ํƒˆํ‡ดํ•œ ์‚ฌ์šฉ์ž, ๋กœ๊ทธ์•„์›ƒํ•œ ์‚ฌ์šฉ์ž์˜ Refresh Token์ด ์‚ด์•„์žˆ์„ ์ˆ˜ ์žˆ๋‹ค. (๋ณด์•ˆ ์œ„ํ—˜)

Redis๊ฐ€ ์ ํ•ฉํ•œ ์ด์œ 

ํŠน์ง• ์„ค๋ช…
TTL ์„ค์ • ๊ฐ€๋Šฅ ํ† ํฐ ๋งŒ๋ฃŒ ์‹œ์ ์— ๋งž์ถฐ ์ž๋™ ์‚ญ์ œ
Key-Value ๊ตฌ์กฐ memberId → refreshToken ๋งคํ•‘์ด ๊ฐ„๋‹จ
๋น ๋ฅธ ์†๋„ in-memory ๊ธฐ๋ฐ˜ ์กฐํšŒ/์‚ญ์ œ
์„ธ์…˜๋ฆฌ์Šค ์„œ๋ฒ„ ํ˜ธํ™˜ JWT + Redis = ์„ธ์…˜๋ฆฌ์Šค ๊ตฌ์กฐ ๊ฐ€๋Šฅ
๋ณต์ˆ˜ ๊ธฐ๊ธฐ ์ง€์› ๊ธฐ๊ธฐ๋ณ„ Refresh Token ๊ด€๋ฆฌ ๊ฐ€๋Šฅ

DB์— ์ €์žฅํ•˜์ง€ ์•Š๋Š” ์ด์œ 

์ด์œ  ์„ค๋ช…
๋””์Šคํฌ IO ๋ถ€๋‹ด ์ƒ์„ฑ/์‚ญ์ œ ์žฆ์•„์„œ ๋ถ€ํ•˜ ํผ
์˜์†์„ฑ ๋ถˆํ•„์š” ํ† ํฐ์€ ์ž„์‹œ ๋ฐ์ดํ„ฐ
TTL ๊ด€๋ฆฌ ๋ณต์žก DB๋Š” ์ž๋™ ๋งŒ๋ฃŒ ๊ด€๋ฆฌ๊ฐ€ ์–ด๋ ค์›€

 

ํ”„๋กœ์ ํŠธ์— ์ ์šฉํ•ด ๋ณด๋ฉด์„œ ๋А๋‚€ ์ 

์ด๋ฒˆ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” ๋‹จ์ˆœํžˆ Access Token๊ณผ Refresh Token ๊ฐœ๋…๋งŒ ๊ณต๋ถ€ํ•œ ๊ฒŒ ์•„๋‹ˆ๋ผ, ์ง์ ‘ JWT ์ธ์ฆ ์‹œ์Šคํ…œ์„ ์„ค๊ณ„ํ•˜๊ณ  ์ ์šฉํ•ด ๋ณด๋Š” ๊ฒฝํ—˜์„ ํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.
๐Ÿ‘‰  Refresh Token์„ ๋‹จ์ˆœ ์ €์žฅ์ด ์•„๋‹ˆ๋ผ ์„œ๋ฒ„์—์„œ ๋ณ„๋„๋กœ ๊ด€๋ฆฌํ•ด์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ
๐Ÿ‘‰  DB ๋Œ€์‹  Redis๋ฅผ ์จ์•ผ ํ•˜๋Š” ์ด์œ ๋ฅผ ์ง์ ‘ ์ฒด๊ฐ

๋‹จ์ˆœํžˆ ๊ธฐ๋Šฅ๋งŒ ๊ตฌํ˜„ํ•˜๋Š” ๊ฐœ๋ฐœ์ž๊ฐ€ ์•„๋‹ˆ๋ผ, ๋ณด์•ˆ๊ณผ ์šด์˜๊นŒ์ง€ ๊ณ ๋ฏผํ•˜๋Š” ๊ฐœ๋ฐœ์ž๊ฐ€ ๋˜์–ด์•ผ๊ฒ ๋‹ค๋Š” ๋‹ค์ง์„ ํ•˜๊ฒŒ ๋˜์—ˆ๋‹ค!