인증 시리즈 1편: JWT와 Refresh Token 도입기

민경찬·2025년 7월 30일
20

백엔드

목록 보기
28/29
post-thumbnail

인후 서비스의 인증 시리즈는 아래 순서대로 진행돼요.

  • 1편: (이번 편) Access Token과 Refresh Token 사용하기
  • 2편: 중복 로그인 방지, 기기 제한 기능 도입하기
  • 3편: E2E 테스트마다 독립적인 로그인 상태 유지하기

안녕하세요. 인후 개발자 민경찬입니다.

인후는 인하대 주변 상가 정보를 모아볼 수 있는 서비스예요.

이번 1편에서는 인후 서비스에서는 왜 Access Token과 Refresh Token을 사용하였는지 얘기해 볼게요.


👀 로그인 방식, Session 은 어떨까?

Session 방식은 사용자의 로그인 상태와 관련 모든 정보를 서버가 관리하는 방식이에요.
클라이언트는 로그인 정보에 접근할 수 있는 Session ID를 보관하죠.

이런 장점들이 있어요.

  • 클라이언트 측에 로그인 상태 정보가 노출되지 않아 보안적으로 좋아요.
  • Session에 값을 삭제하여 로그인 상태를 즉시 무효화 시킬 수 있어요.

하지만 단점도 있어요.

  • Session ID가 탈취되면 로그인 상태가 도용될 수 있어요.
  • 로그인 상태를 서버에 저장해야 해서, 사용자가 많아질 수록 세션 스토리지(예: Redis) 부하가 커져요.
  • 모든 API 요청 마다 세션 스토리지에 접근해야 해요.
  • 서버리스 함수나 배치 서버 같은 새로운 컴포넌트가 생기면, 단순 인증을 위해서도 세션 스토리지를 열어줘야 하므로 네트워크·보안 설정이 복잡해집니다.

결국, 인증만을 위해 세션 스토리지를 의존하는 구조는 서버리스나 외부 컴포넌트가 많아지고 트래픽이 커질수록 네트워크·보안 설정 부담 때문에 인프라 유연성이 떨어진다고 느꼈어요.

👀 그럼 JWT는 은총알인가?

세션 방식이 인프라 유연성에서 한계를 보인다면, JWT 방식이 좋은 대안이 될 것이라고 생각했어요.
세션 스토리지가 필요하지 않기 때문이였어요.

JWT는 로그인 상태를 서버에 저장하지 않고, 클라이언트가 토큰을 들고 다니며 인증하는 방식이에요.

이런 장점들이 있어요.

  • Stateless 구조라서, 어떤 컴포넌트에서 세션 스토리지 접근 없이 인증이 가능해요.
  • 로그인 상태를 서버에 저장하지 않으니, 세션 스토리지 운영 부담이 사라지고 인프라 구성이 단순해져요.

하지만 단점도 있어요.

  • 토큰이 발급되면, 서버가 임의로 무효화할 수 없다는 치명적인 한계가 있어요.
  • 로그인 기기 제한, 강제 로그아웃 같은 서버 주도 정책을 구현하기 어려워요.

무효활 방법이 없으니, Access Token의 만료 시간을 짧게 설정할 수밖에 없어요.
그 결과, 사용자는 자주 토큰을 갱신해야 하는 불편을 겪게 되죠.

또 한 가지 문제는, 서버가 토큰 상태를 전혀 들고 있지 않아서
로그인 기기 제한, 강제 로그아웃 같은 서버 주도 정책을 적용하기 어렵다는 점이에요.

💡JWT의 한계를 넘어서: Refresh Token 도입하기

하나의 JWT만을 사용하는 방식은 탈취 피해를 줄이기 위하여 만료 시간(TTL)을 짧게 가져가요.

하지만 이런 방식은 사용자가 토큰을 자주 갱신해야하는 불편이 생기죠.
이를 해결하기 위해, 두 가지 종류로 토큰을 나누었어요. 그것이 바로 Access Token과 Refresh Token이에요.

Refresh Token은 이런 특징을 가져요.

  • Access Token을 발급할 수 있어요.
  • TTL이 길어요. 탈취 당했을 때 부작용이 크기 때문에 반드시 안전하게 보관하고 필요할 때만 사용해요.

Access Token은 이런 특징을 가져요.

  • 토큰을 검증하여 사용자의 인증 상태를 확인할 수 있어요.
  • TTL이 짧아요.

그림으로 각 토큰이 인증 과정에서 어떻게 사용되는지 살펴볼게요.

  • 로그인이 성공하면 Refresh Token과 Access Token을 발급받아요.
  • 그럼 클라이언트는 두 토큰을 안전하게 보관해요.
  • 이후 API 요청 시에는 Access Token만 사용하여 요청해요.

Access Token이 만료되었을 때도 살펴볼게요.

  • 만료된 Access Token으로 API를 요청하면, 서버는 401 에러로 응답해요.
  • 클라이언트는 Refresh Token을 사용해 새 Access Token을 재발급받아요.
  • 새 Access Token으로 원래 시도하던 API를 다시 시도해요.

Refresh Token은 오직 Access Token을 재발급할 때만 사용하기 때문에, 네트워크 상에 노출될 가능성이 거의 없어 보안성이 높아요.

또한, TTL도 길어 로그인 유지를 오랫동안 할 수도 있어요.

프론트엔드에서 만료된 Access Token을 자동으로 재발급하고,
원래 요청을 재시도하는 로직은 별도 포스트에서 자세히 다룰 예정이에요.

💡 세션과 JWT 합치기

Refresh Token만으로는 중복 로그인 제어나 로그인 기기 개수 제한 같은 서버 주도적인 인증 정책을 적용하기 어려웠어요.

이를 해결하기 위해, Session 방식 일부를 차용했어요.
발급한 Refresh Token을 서버 스토리지에 저장하는 방식이에요.

유효한 Refresh Token이란?

  • JWT 서명 검증을 통과했다.
  • 서버 스토리지에도 존재하는 토큰이다.

반대로 Access Token은 여전히 서버 스토리지와 독립적이에요.
모든 서버 컴포넌트는 Access Token의 서명과 만료 시간만 확인하므로,
인증을 필요로하는 모든 서버 컴포넌트는 토큰 스토리지를 필요로하지 않아요.

그림으로도 알아볼게요.

과정은 여전히 비슷해요.
로그인 할 때, 토큰 스토리지에 생성한 Refresh Token을 저장하죠.

Access Token을 재발급하는 로직도 크게 바뀌지 않아요.
재발급을 요청할 때 보낸 Refresh Token이 토큰 스토리지에 존재하는지 확인할거에요.

이제, 서버 주도 정책 중 하나인 강제 로그아웃을 가정해볼게요.

서버는 토큰 스토리지에서 강제 로그아웃 하려는 Refresh Token을 삭제해요.

이후 이 사용자가 해당 Refresh Token으로 Access Token 재발급을 요청하면,
스토리지에서 토큰을 찾을 수 없으므로 재발급이 거부되고, 로그인 상태가 종료돼요.

이로써 인후 서비스는 세 가지 목적을 달성했어요.

  1. Access Token을 짧게, Refresh Token을 길게 운용해 보안성을 유지하면서도 로그인 상태를 오래 유지할 수 있어요.
  2. Access Token은 스토리지와 독립적으로 검증되므로, 서버나 서버리스 컴포넌트들이 인프라 제약 없이 유연하게 인증할 수 있어요.
  3. Refresh Token을 스토리지에 보관해, 강제 로그아웃, 로그인 기기 제한과 같은 서버 주도 인증 정책을 쉽게 적용할 수 있어요.

‼️그래도 아직 주의해야해요!

주의 1. JWT 페이로드는 암호화되지 않아요.

JWT는 Base64로 인코딩만 되어 있어, 누구나 토큰을 디코드하여 페이로드 내용을 확인할 수 있어요.
따라서 Access Token과 Refresh Token 안에는 비밀번호나 예민한 개인정보는 절대로 넣으면 안돼요.

인증과 권한 검증에 필요한 최소한의 정보만 담아야 해요.

주의 2. Refresh Token이 무효화돼도 Access Token은 바로 무효화되지 않아요.

Access Token은 서명과 만료 시간만으로 유효성을 판단해요.
그래서 Refresh Token이 무효화되어도 발급된 Access Token은 TTL이 끝날 때까지 유효해요.

팀에서는 이 기간을 Access Token의 잔여 유효시간이라 부르고 있어요.

Acess Token의 TTL을 짧게 설정하면

  • 잔여 유효시간이 짧아져 로그아웃과 무효화가 더 실시간으로 반영돼요.
  • 재발급 요청이 늘어나 서버 부하가 증가하고, Refresh Token이 네트워크에 더 자주 노출돼요.

Access Token의 TTL을 길게 설정하면

  • 잔여 유효시간이 길어져, 로그아웃이나 무효화가 실제 반영되기까지 시간이 지연돼요.
  • 재발급 요청이 줄어 서버 부담이 줄고, Refresh Token이 네트워크에 노출되는 빈도가 줄어요.

TTL은 보안과 서버 부하 사이에서 균형을 맞춰 설정해야해요.


👋 마무리…

다음 편에서는 Redis를 사용하여 토큰 스토리지를 설계하고
토큰 탈취를 감지하는 방법에 대해서 얘기해볼게요.

읽어주셔서 감사합니다.

2편 바로가기

6개의 댓글

comment-user-thumbnail
2025년 7월 31일

선 추천 후 읽기

1개의 답글
comment-user-thumbnail
2025년 7월 31일

다음편이 궁금하네요!! 잘 읽었씁니다.

1개의 답글
comment-user-thumbnail
2025년 8월 11일

JWT를 사용하면 보안에 대한 리스크를 서버 -> 클라이언트로 전가함으로서 개발자에게 있어서 책임을 어느정도 회피할수 있따는 장점이 있다죠...?

1개의 답글