개발 블로그
← 블로그 목록

카카오 OAuth 토큰을 Redis로 관리하기 — 자동 갱신 전략


카카오 API로 메시지를 보내거나 카카오 로그인을 구현하다 보면 꼭 마주치는 문제가 있습니다. Access token이 6시간마다 만료된다는 것입니다. 서버가 24시간 돌아가는 자동화 서비스라면 토큰 갱신을 사람이 직접 할 수 없습니다. Redis와 Spring Scheduler를 조합해서 자동으로 갱신되도록 만든 방법을 소개합니다.

카카오 토큰의 특징

  • Access Token: 유효기간 6시간. API 호출에 직접 사용됩니다.
  • Refresh Token: 유효기간 60일. Access Token을 갱신할 때 사용합니다.

즉, Refresh Token이 살아있는 동안은 사용자가 재로그인하지 않아도 Access Token을 계속 갱신할 수 있습니다.

Redis에 토큰 저장

토큰을 Redis에 저장합니다. 데이터베이스보다 빠르고, TTL 설정으로 만료 관리가 편합니다.

@Service
public class RedisService {
    private final RedisTemplate<String, String> redisTemplate;

    public void saveToken(String type, String token, long ttlSeconds) {
        redisTemplate.opsForValue().set(
            "kakaotoken::" + type,
            token,
            ttlSeconds,
            TimeUnit.SECONDS
        );
    }

    public String getToken(String type) {
        return redisTemplate.opsForValue().get("kakaotoken::" + type);
    }
}

// 최초 토큰 저장 (카카오 로그인 완료 후)
redisService.saveToken("access", accessToken, 6 * 3600);    // 6시간
redisService.saveToken("refresh", refreshToken, 60 * 86400); // 60일

자동 갱신 스케줄러

Access Token이 만료되기 전에 주기적으로 갱신합니다. 5시간 간격으로 갱신하면 6시간 유효기간 내에 항상 갱신이 이루어집니다.

@Scheduled(cron = "0 0 3 * * *")
@Scheduled(cron = "0 0 8 * * *")
@Scheduled(cron = "0 0 13 * * *")
@Scheduled(cron = "0 0 18 * * *")
@Scheduled(cron = "0 0 23 * * *")
public void refreshKakaoToken() {
    kakaoAuthService.refreshToken();
}

// KakaoAuthService
public void refreshToken() {
    String refreshToken = redisService.getToken("refresh");
    if (refreshToken == null) {
        log.warn("Refresh token이 없습니다. 재로그인이 필요합니다.");
        return;
    }

    MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
    params.add("grant_type", "refresh_token");
    params.add("client_id", kakaoClientId);
    params.add("refresh_token", refreshToken);

    ResponseEntity<Map> resp = restTemplate.postForEntity(
        "https://kauth.kakao.com/oauth/token",
        new HttpEntity<>(params, formHeaders()),
        Map.class
    );

    Map body = resp.getBody();
    redisService.saveToken("access", (String) body.get("access_token"), 6 * 3600);

    // refresh_token도 함께 갱신된 경우 저장
    if (body.containsKey("refresh_token")) {
        redisService.saveToken("refresh", (String) body.get("refresh_token"), 60 * 86400);
    }

    log.info("카카오 토큰 갱신 완료");
}

토큰 사용 시 만료 체크

API 호출 시 토큰이 없으면 자동으로 갱신을 시도합니다.

public void sendMessage(Map payload) {
    String token = redisService.getToken("access");
    if (token == null) {
        log.warn("Access token 없음, 갱신 시도");
        refreshToken();
        token = redisService.getToken("access");
    }

    // 메시지 전송
    HttpHeaders headers = new HttpHeaders();
    headers.setBearerAuth(token);
    // ...
}

최초 토큰 발급 흐름

카카오 로그인은 브라우저를 통해 한 번만 하면 됩니다. 이후 토큰은 자동으로 갱신됩니다.

  1. 카카오 로그인 URL 생성 후 브라우저에서 인증
  2. 인증 코드(code)를 받아서 access/refresh token으로 교환
  3. Redis에 저장
  4. 이후 스케줄러가 자동 갱신
// 인증 코드로 토큰 교환
@GetMapping("/kakao/callback")
public String kakaoCallback(@RequestParam String code) {
    Map tokens = kakaoAuthService.getTokensByCode(code);
    redisService.saveToken("access", tokens.get("access_token"), 6 * 3600);
    redisService.saveToken("refresh", tokens.get("refresh_token"), 60 * 86400);
    return "토큰 저장 완료";
}

주의사항

  • Refresh Token도 60일이 지나면 만료됩니다. 60일 이상 서버를 방치하면 재로그인이 필요합니다.
  • 카카오 개발자 콘솔에서 "비활성 사용자 처리" 설정을 확인하세요. 일정 기간 미사용 시 토큰이 강제 만료될 수 있습니다.
  • 토큰은 민감한 정보입니다. Redis에 접근 제어가 제대로 설정돼 있는지 확인하세요.
팁: 개발 환경에서는 카카오 로그인 콜백 URL을 http://localhost:8080/kakao/callback으로 등록해두면 로컬에서도 토큰을 발급받을 수 있습니다.