목차
1. Access Token
2. Refresh Token
3. 토큰 재발급 과정
4. 기기별 Refresh Token 관리 방법
웹 및 모바일 애플리케이션에서 사용자를 인증하고 API에 접근 권한을 부여하는 데에는 Access Token
과 Refresh Token
이라는 두 가지 주요 토큰이 사용된다. 이 둘은 각기 다른 목적으로 사용되지만, 보안과 사용자 편의성을 동시에 확보하기 위해 함께 작동하는 개념이다.
사용자가 아이디와 비밀번호로 로그인하면, 서버는 이 둘을 모두 발급한다. Access Token
은 실제 API에 접근할 때 사용되는 열쇠이며, 그 수명은 매우 짧다(보통 몇 분에서 1시간). 이렇게 수명이 짧은 이유는 토큰이 탈취되더라도 공격자가 시스템을 악용할 수 있는 시간을 최소화하기 위해서이다. 클라이언트는 이 토큰을 받아서 메모리와 같은 안전한 곳에 보관하며, 보호된 API를 호출할 때마다 요청 헤더에 이 토큰을 담아 보낸다.
Access Token
은 서버측에 별도로 저장할 필요없이 토큰 자체에 만료시간정보(exp 클레임)를 포함한다. 클라이언트는 요청시 이 토큰을 보내고, 서버는 토큰을 검증할 때 만료시간을 확인한다.반면, Refresh Token
은 새로운 Access Token
을 발급받기 위한 재발급용 신분증 역할을 한다. Access Token
이 만료되어 더 이상 유효하지 않을 때, 사용자가 다시 로그인하는 번거로움 없이 새로운 Access Token
을 얻기 위해 사용된다. Refresh Token
은 Access Token
보다 수명이 훨씬 길며(보통 며칠에서 몇 달), 보안을 위해 HTTP Only 쿠키
와 같은 더욱 안전한 저장소에 보관된다.
Refresh Token
은 데이터베이스에 저장하여 관리한다. 이를 통해 토큰이 만료되지 않았더라도 사용자가 로그아웃하거나 의심스로운 활동이 감지될 때 서버에서 강제로 만료(무효화)시킬 수 있다.Refresh Token
을 한번만 사용 가능하도록 설정하고, 새로운 Access Token
을 발급할 때마다 새로운 Refresh Token도 함께 발급하여 이전 토큰을 무효화한다. 이렇게 하면 탈취된 토큰의 유효기간을 한 번으로 제한할 수 있다.Refresh Token
을 만료시키는 정책을 적용할 수 있다.Access Token
과 Refresh Token
을 모두 발급한다.Access Token
을 사용하여 API를 호출한다.Access Token
만료 : 시간이 지나 Access Token
이 만료되면 API 호출이 실패한다.Refresh Token
을 서버로 보낸다.Refresh Token
을 확인하고, 유효하다면 새로운 Access Token
을 발급하여 클라이언트에게 전달한다. 이 과정에서 Refresh Token
자체도 새로 발급할 수 있다.Refresh Token
의 만료 : Refresh Token
이 만료되면 사용자는 다시 로그인을 통해 Access Token
과 Refresh Token
을 새로 발급받는다. 또는 Refresh Token
재발급 요청을 보냄으로써 Refresh Token
을 재발급받을 수 있다.토큰 기반 인증 시스템에서 Refresh Token
은 사용자 세션을 안전하게 유지하는 중요한 열쇠이다. 하지만 많은 개발자들이 Refresh Token
자체에 기기 정보(예: IP 주소, 기기 유형)를 포함시키지 않고, 별도로 데이터베이스에 저장하는 방식을 채택한다. 이는 단순한 구현 방식을 넘어, 보안과 유연성을 동시에 확보하기 위한 가장 효과적인 전략이다.
JWT의 가장 큰 장점은 무상태성(Stateless) 이다. 토큰은 서명만으로 유효성을 검증할 수 있어, 매 요청마다 서버가 데이터베이스를 조회할 필요가 없다. 하지만 토큰 내부에 기기 정보를 넣는 순간, 이 장점은 사라진다.
세션 유연성 저해: 사용자의 IP 주소는 끊임없이 변할 수 있다 (Wi-Fi에서 모바일 네트워크로 전환, 동적 IP 주소 할당 등). 만약 토큰에 IP 주소를 포함하면, IP가 변경될 때마다 토큰이 무효화되어 사용자는 다시 로그인해야 한다. 이는 사용자 경험을 심각하게 해친다.
토큰의 불변성: JWT는 서명된 이후에는 페이로드(Payload)를 변경할 수 없다. 만약 기기 정보가 변경되거나, 특정 기기의 세션만 종료하고 싶을 때, 토큰 자체를 새로 발급해야 하는 비효율적인 상황이 발생한다.
보안: 토큰에 기기 정보 외에 다른 민감한 데이터까지 포함되면, 토큰이 탈취되었을 때 더 많은 정보가 노출될 위험이 있다.
이러한 이유로, 토큰은 최소한의 필수 정보(사용자 ID, 권한)만 담아 가볍고 무상태성을 유지해야 한다.
그렇다면 기기 구분은 어떻게 해야 할까? 정답은 바로 데이터베이스에 Refresh Token
과 기기 정보를 함께 저장하는 것이다. 이 방식은 토큰의 무상태성을 유지하면서도 강력한 세션 관리를 가능하게 한다.
로그아웃 기능의 정교함: 사용자가 로그아웃을 요청했을 때, 해당 기기에서 발급된 Refresh Token
만 데이터베이스에서 삭제하여 다른 기기에는 영향을 주지 않고 로그아웃할 수 있다. 반면, "모든 기기에서 로그아웃" 기능은 해당 사용자의 모든 Refresh Token
을 한 번에 삭제함으로써 구현할 수 있습니다.
보안 강화: 토큰을 발급할 때 저장한 IP 주소, User-Agent
정보와 실제 요청의 정보를 비교하여 토큰 탈취 여부를 감지할 수 있다. 예를 들어, 토큰 발급 시 IP와 갱신 요청 시 IP가 다르면 의심스러운 활동으로 간주하고 토큰을 무효화할 수 있다.
사용자 편의 기능: 데이터베이스에 저장된 기기 정보를 통해 "내 계정 활동" 페이지를 구현할 수 있다. 사용자는 로그인된 기기 목록을 확인하고, 불필요한 세션을 직접 종료할 수 있다.
토큰 생성 시 기기 식별을 위해 주로 HTTP 요청 헤더에서 다음과 같은 정보들을 추출하여 Refresh Token
과 함께 데이터베이스에 저장한다.
User-Agent: 사용자의 운영체제, 브라우저 종류, 기기 유형을 알 수 있는 가장 기본적인 정보이다.
IP 주소: 사용자의 네트워크 위치를 파악하는 데 유용하다.
기타 식별자: 모바일 앱의 경우, 기기 고유의 UUID를 헤더에 포함시켜 더 정확하게 기기를 식별할 수 있다.