쿠키와 세션, 그리고 토큰 기반 인증 시스템 구현하기
이 글은 최근 프로젝트를 진행하며 Auth 시스템 구현에 대해 고민하고 적용했던 과정을 쿠키와 세션부터 토큰 기반 인증까지 정리한 첫 번째 글입니다. 그동안 단순히 사용만 했던 개념들을 되짚어보며, 각 방식이 왜 필요했고, 어떤 한계를 가지고 있는지 살펴보려 합니다. 또한, 직접 구현하면서 고민했던 부분도 함께 정리해 보겠습니다.
실무 경험은 없지만, 공부하며 정리한 내용이니 부족한 부분이 있을 수도 있습니다. 보완할 점이나 추가로 살펴보면 좋을 부분이 있다면 피드백 부탁드립니다. 더 깊이 공부해야 할 방향도 함께 조언해 주시면 감사하겠습니다!
쿠키와 세션 기반 인증 - 베어랜드 입장 예시
인증 과정을 이해하기 위해 '베어랜드(놀이공원)'라는 예시를 들어보겠습니다.
문제 상황 - 방문객 감소
베어랜드에 입장하는 손님이 있습니다. 처음에는 아이디와 비밀번호를 입력하고 입장하지만, 놀이기구를 이용할 때마다 다시 로그인해야 한다면 어떨까요? 매우 번거롭겠죠?
이러한 불편함이 계속되자, 손님들은 점점 베어랜드 방문을 꺼리게 되었습니다. 결국 방문객 수가 감소하는 문제가 발생했습니다.
문제 해결 - 세션과 쿠키를 활용한 인증
이 문제를 해결하기 위해 베어랜드(서버)는 쿠키와 세션을 활용한 인증 방식을 도입했습니다.
- 사용자가 처음 입장할 때 서버는 아이디와 비밀번호를 확인하고 고유한 '세션 ID'를 발급하고, 이를 서버의 세션 저장소에 보관합니다.
- 동시에 이 세션 ID는 사용자의 쿠키라는 저장소에도 저장되어 이후 요청마다 함께 전송됩니다.
- 이 세션 ID는 방문객의 신원을 나타내는 임시 키와 같으며, 서버에서 이 ID를 통해 사용자를 식별합니다.
- 사용자가 놀이기구를 이용할 때마다 브라우저는 자동으로 쿠키에 저장된 세션 ID를 서버로 전송합니다.
- 서버는 세션 저장소에서 해당 ID를 조회하여 손님의 정보를 확인한 후, 놀이기구 이용을 허가합니다.
이 방식 덕분에 손님은 매번 아이디와 비밀번호를 입력할 필요 없이, 쿠키를 통해 자동으로 인증을 받을 수 있습니다.
새로운 문제 - 세션 방식의 한계
그런데 이 방식에는 몇 가지 한계가 있습니다:
서버의 상태 유지 문제
- 베어랜드(서버)는 모든 손님의 세션 정보를 저장해야 하므로, 서버가 '무상태(Stateless)'라는 특성을 잃게 됩니다. 여기서 무상태란 서버가 이전 요청에 대한 정보를 기억하지 않는 상태를 의미합니다.
- 손님이 많아질수록 서버는 더 많은 세션 데이터를 저장해야 하며, 만약 서버가 재시작되거나 장애가 발생하면 기존에 저장된 세션 정보가 모두 사라질 위험이 있습니다.
서버 성능 저하 문제
- 사용자가 많아질수록 세션 정보를 조회하고 유지하는 데 시간이 걸려 서버 성능이 저하될 가능성이 높아집니다.
- 놀이기구를 이용하는 손님이 1,000명에서 10,000명으로 늘어나면, 서버는 매번 10배 더 많은 세션 정보를 처리해야 하므로 응답 속도가 느려질 수밖에 없습니다.
서버 간 세션 공유 문제
- 베어랜드의 여러 지점(서버)에서 동일한 세션 정보를 공유해야 하는 문제도 발생할 수 있습니다.
- 손님이 A 지점에서 로그인한 후 B 지점에서 놀이기구를 이용하려 하면, B 지점의 서버는 A 지점에서 생성된 세션 정보를 알지 못해 인증이 되지 않을 수도 있습니다.
- 이를 해결하려면 모든 서버가 동일한 세션 정보를 공유해야 하지만, 추가적인 설정과 비용이 필요합니다.
세션의 한계 해결 - 토큰 기반 인증 도입
이러한 한계를 극복하기 위해 토큰 기반 인증, 특히 JWT(JSON Web Token)가 등장했습니다.
JWT란 무엇인가?
JWT(JSON Web Token)는 사용자의 인증 정보를 자체적으로 포함(self-contained)하는 토큰입니다.
즉, JWT 자체에 ID, 권한 등의 사용자 정보와 토큰의 유효기간이 포함되어 있어, 서버가 별도로 세션을 저장할 필요 없이 토큰만으로 인증을 수행할 수 있습니다.
이를 베어랜드 입장권 시스템과 비교하면 쉽게 이해할 수 있습니다:
- 세션 방식에서는 놀이기구를 탈 때마다 직원(서버)이 중앙 관리소(세션 저장소)에서 손님의 정보를 확인해야 합니다.
- JWT 방식에서는 입장권(JWT)에 손님의 정보(ID, 권한 등)가 직접 포함되어 있어, 직원(서버)은 입장권이 위조되지 않았는지만 확인하면 됩니다.
JWT는 정보를 쉽게 확인할 수 있으므로 민감한 정보(예: 비밀번호, 결제 정보 등)는 포함해서는 안 됩니다. 대신, 사용자 ID나 역할(권한) 등의 인증 정보만 저장하는 것이 일반적입니다.
정보를 쉽게 확인할 수 있다면 변조도 쉽겠죠? 하지만 JWT는 비밀키(Secret Key)로 서명되어 있어 위조나 변조가 불가능합니다. 예를 들어, 손님이 JWT 입장권에 적혀 있던 역할을 '손님'에서 '관리자'로 변조하더라도, 베어랜드(서버)는 이를 감지하여 위조된 입장권으로 놀이기구를 이용하지 못하도록 막습니다.
JWT 인증 방식 - 입장권 시스템
다시 베어랜드 예시로 설명하면:
- 손님이 처음 베어랜드에 입장할 때 아이디와 비밀번호를 제출합니다.
- 베어랜드는 이를 확인한 후, 특별한 'JWT 입장권'을 발급합니다.
- 이 입장권에는 손님의 ID, 역할, 권한 등의 정보가 이미 포함되어 있습니다.
- 손님은 놀이기구를 이용할 때마다 이 입장권을 제시합니다.
- 베어랜드 직원(서버)은 입장권이 위조되었는지만 확인하고, 위조되지 않았다면 추가 확인 없이 놀이기구 이용을 허가합니다.
- 만약 토큰이 만료됐다면 다시 로그인해 새로 JWT 입장권을 발급 받으면 됩니다.
JWT의 장점
베어랜드에 JWT 입장권 시스템을 도입했더니 다음과 같은 효과가 있었습니다:
서버 부담 감소
- 베어랜드(서버)는 더 이상 손님의 세션 정보를 저장할 필요가 없습니다. 입장권 자체에 필요한 모든 정보가 포함되어 있기 때문입니다. 이전에는 모든 손님의 정보를 중앙 관리소에 보관해야 했지만, 이제는 그럴 필요가 없어져 서버의 메모리와 저장 공간을 절약할 수 있게 되었습니다.
빠른 처리 속도
- 입장권의 위조 여부만 확인하면 되므로 처리 속도가 빠릅니다. 이전 방식에서는 매번 중앙 데이터베이스를 조회해야 했지만, 이제는 입장권 자체의 서명만 검증하면 되므로 손님이 놀이기구를 이용할 때 대기 시간이 크게 줄어들었습니다.
서버 간 공유 문제 해결
- 베어랜드의 여러 지점(서버)에서도 동일한 입장권으로 서비스를 이용할 수 있습니다. 한 지점에서 발급받은 입장권으로 다른 지점의 놀이기구도 이용할 수 있게 되어, 손님들이 여러 지점을 방문할 때도 불편함 없이 서비스를 이용할 수 있게 되었습니다. 각 지점이 독립적으로 입장권을 검증할 수 있기 때문에 지점 간 통신도 필요 없어졌습니다.
JWT의 한계 - 입장권을 탈취 당한다면?
하지만 JWT 입장권에도 문제가 있습니다. 만약 누군가가 손님의 입장권을 탈취한다면, 그 사람은 손님인 것처럼 놀이기구를 이용할 수 있게 됩니다. 입장권 자체에 모든 정보가 포함되어 있기 때문입니다.
JWT의 한계 보완 - Access Token과 Refresh Token
이러한 보안 위험을 줄이기 위해, 베어랜드에서는 두 가지 종류의 토큰을 사용하는 이중 보안 시스템 방식이 도입되었습니다.
Access Token과 Refresh Token - 이중 보안 시스템
Access Token(입장권)
Access Token은 실제로 보호된 리소스에 접근할 때 사용하는 인증 수단입니다. 베어랜드에서는 이것을 놀이기구를 탈 수 있는 '입장권'으로 비유할 수 있습니다. Access Token의 주요 특징은 다음과 같습니다:
- 짧은 유효 기간을 가집니다(보통 15분~1시간).
- 토큰 자체에 사용자 ID, 권한 등의 정보를 포함합니다.
- 서버에서 별도로 저장하거나 관리할 필요가 없습니다.
- 토큰이 탈취되더라도 짧은 시간 내에 만료되므로 피해를 최소화할 수 있습니다.
베어랜드에서는 손님이 놀이기구를 이용할 때마다 이 입장권을 제시하면, 직원은 입장권이 위조되지 않았는지, 유효 기간이 지나지 않았는지만 확인하고 바로 이용을 허가합니다.
Refresh Token(발급증)
Refresh Token은 Access Token이 만료되었을 때 새로운 Access Token을 발급받기 위한 인증 수단입니다. 베어랜드에서는 이것을 '발급증'으로 비유할 수 있습니다. Refresh Token의 특징은 다음과 같습니다:
- 긴 유효 기간을 가집니다(보통 7일~수개월).
- 서버 측 데이터베이스에 저장되어 관리됩니다.
- Access Token보다 더 긴 유효 기간을 가지므로, 서버에서 필요시 특정 토큰을 무효화할 수 있어야 합니다.
- 서버에서 토큰 정보를 저장하고 검증하기 때문에, 사용자 로그아웃이나 비정상적인 활동 감지 시 해당 토큰을 즉시 무효화할 수 있습니다.
베어랜드에서는 손님의 입장권(Access Token)이 만료되면, 손님은 발급증(Refresh Token)을 제시하여 새로운 입장권을 발급받을 수 있습니다. 베어랜드는 자신의 데이터베이스에서 이 발급증이 유효한지 확인한 후, 새로운 입장권을 발급해 줍니다.
작동 방식
이러한 이중 토큰 시스템의 작동 방식은 다음과 같습니다.
- 방문객(클라이언트)이 베어랜드(서버)에 로그인(ID, PW)을 제출합니다.
- 베어랜드는 방문객의 발급증(Refresh Token)을 저장합니다.
- 베어랜드는 방문객에게 입장권(Access Token)과 발급증(Refresh Token)을 전송합니다(입장권: 이름: 홍길동, 역할: user, 만료: 7일 / 발급증 만료: 14일).
- 방문객은 놀이기구 이용 시 입장권(Access Token)을 사용합니다.
- 입장권 만료로 이용이 불가할 경우, 베어랜드에서 방문객에게 알립니다.
- 방문객은 발급증(Refresh Token)을 제시하며 새 입장권을 요청합니다.
- 베어랜드는 발급증의 유효성을 확인합니다.
- 베어랜드는 방문객에게 새로운 입장권과 발급증을 전송합니다(입장권: 이름: 홍길동, 역할: user, 만료: 7일 / 발급증 만료: 14일).
이러한 이중 토큰 시스템을 통해 사용자는 지속적으로 서비스를 이용할 수 있으면서도, 토큰 탈취로 인한 보안 위험을 크게 줄일 수 있습니다. 만약 Access Token이 탈취되더라도 짧은 시간 내에 만료되어 더 이상 사용할 수 없게 되고, Refresh Token이 탈취되었다고 의심될 경우 서버에서 해당 토큰을 무효화하여 보안을 강화할 수 있습니다.
결국 다시 서버가 상태를 관리하네?
JWT를 도입한 주요 이유 중 하나는 서버가 무상태(Stateless) 를 유지할 수 있다는 점이었지만, Refresh Token을 서버에서 관리하면서 부분적으로 상태 유지(Stateful) 로 돌아가게 됩니다. 그러나 이것은 단순한 퇴보가 아니라, 서비스의 특성과 보안 요구사항에 맞춘 설계라고 볼 수 있습니다.
Access Token은 여전히 무상태(Stateless) 로 유지되며, 매 요청마다 클라이언트가 전송하여 서버에서 인증을 수행합니다. 반면, Refresh Token은 보안이 중요한 요소이므로 서버에서 상태를 유지하며 관리하게 됩니다. 하지만 Refresh Token은 Access Token보다 사용 빈도가 훨씬 낮아, 서버에 지속적인 부하를 주지 않으면서도 보안을 강화하는 타협점이 됩니다.
즉, 모든 서비스가 무조건 무상태 인증을 유지해야 하는 것은 아니며, 서비스의 특성과 보안 요구사항에 따라 적절한 방식이 결정됩니다.
예를 들어, 은행이나 금융 서비스와 같은 보안이 중요한 시스템에서는 매 요청마다 철저한 인증이 필요하기 때문에, 추가적인 상태 관리와 보안 검증 절차가 필수적입니다. 반면, 베어랜드에서는 사용자의 편의성과 서버 성능을 고려하여 무상태 인증(Access Token)과 최소한의 상태 유지(Refresh Token)를 조합하는 방식이 더 적절한 선택이 될 수 있습니다.
결국, Access Token을 통한 빠르고 가벼운 인증과, Refresh Token을 이용한 안전한 재인증 방식이 결합됨으로써, 서버 부하를 최소화하면서도 보안을 강화하는 균형 잡힌 구조를 만들 수 있는 것입니다.
Refresh 토큰을 JWT로 할 필요가 있을까?
Refresh Token을 서버에서 관리할 때, JWT처럼 정보를 포함하는 토큰을 사용할 필요가 있을까요? 이부분은 다음시간에 다시 정리해 올리겠습니다.
[베어랜드 Auth 시스템] 2 - JWT와 Opaque Token 그리고 토큰 저장방식
이전 포스트 [베어랜드 Auth 시스템] 1 - 쿠키와 세션 그리고 JWT쿠키와 세션, 그리고 토큰 기반 인증 시스템 구현하기이 글은 최근 프로젝트를 진행하며 Auth 시스템 구현에 대해 고민하고 적용했
bearn-soo.tistory.com
'공부기록' 카테고리의 다른 글
[베어랜드 Auth 시스템] 2 - JWT와 Opaque Token 그리고 토큰 저장방식 (0) | 2025.03.23 |
---|---|
[트러블 슈팅] Cannot find module '...../bcrypt/lib/binding/napi-v3/bcrypt_lib.node' (1) | 2025.02.06 |
[시스템 아키텍처] 섹션 2: 시스템 요구 사항 및 아키텍처 드라이버 (2) | 2024.11.27 |
[시스템 아키텍처] (1) | 2024.11.26 |
[electron] electron-forge Cannot find module (0) | 2024.11.25 |