카카오 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);
// ...
}
최초 토큰 발급 흐름
카카오 로그인은 브라우저를 통해 한 번만 하면 됩니다. 이후 토큰은 자동으로 갱신됩니다.
- 카카오 로그인 URL 생성 후 브라우저에서 인증
- 인증 코드(code)를 받아서 access/refresh token으로 교환
- Redis에 저장
- 이후 스케줄러가 자동 갱신
// 인증 코드로 토큰 교환
@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으로 등록해두면 로컬에서도 토큰을 발급받을 수 있습니다.