콘텐츠 대표 이미지 - 세션 vs 토큰: 너의 앱은 어떤 문지기를 세울래? 🔐

세션 vs 토큰: 너의 앱은 어떤 문지기를 세울래? 🔐

안녕! 개발자라면, 혹은 IT에 관심 좀 있다면 '인증'이라는 말, 지겹게 들었을 거야.
로그인, 로그아웃... 우리가 매일같이 하는 이 행위 뒤에는 사실 엄청난 아키텍처 전쟁이 숨어있어.
오늘은 그 전쟁의 두 주인공, 세션(Session)토큰(Token)에 대해 제대로 한 번 파헤쳐 볼까 해.
단순히 '이건 이렇고 저건 저렇다'가 아니라, 얘네가 왜 태어났고, 어떤 철학을 가졌는지,
그래서 내 서비스엔 뭘 써야 '인싸'가 될 수 있는지, 친구랑 수다 떨듯 쉽고 깊게 알려줄게!

들어가기 전에: "인증? 인가? 그게 그거 아님?" 🤔

본격적으로 시작하기 전에, 우리 용어부터 확실히 잡고 가자. 이거 헷갈리면 뒤 내용이 다 꼬여버리거든.

인증(Authentication)은 "너 누구야?"라고 묻는 과정이야.
네가 아이디랑 비밀번호를 내밀면, 시스템이 "아, 너 OOO 맞구나!" 하고 신원을 확인해주는 거지. 딱 거기까지야. 클럽 입구에서 신분증 검사하는 거랑 똑같아.

인가(Authorization)는 "그래서, 뭘 할 수 있는데?"라고 묻는 과정이야.
신원 확인(인증)이 끝난 너에게 "너는 일반 회원이니까 글 읽기만 가능해", "너는 VIP니까 파티룸 입장 가능!" 이렇게 권한을 부여하고 확인하는 거지. 클럽 안에서 VIP 팔찌를 보여주고 특정 구역에 들어가는 것과 같아.

오늘 우리가 다룰 세션과 토큰은 바로 이 '인증' 상태를 유지하고, '인가'의 기반을 마련해주는 핵심 기술이야. 한 번 인증했다고 매번 아이디/비번을 물어볼 순 없잖아? 그 귀찮음을 해결해주는 똑똑한 녀석들이지.

핵심 요약:
- 인증 (Authentication): 신원 확인. "Are you who you say you are?"
- 인가 (Authorization): 권한 확인. "What are you allowed to do?"
세션과 토큰은 이 둘을 웹 세상에서 가능하게 만드는 기술적 방법론이야.

자, 이제 기본기는 다졌으니 진짜 주인공들을 만나러 가보자고!


챕터 1: 구관이 명관? 전통의 강자, 세션 기반 인증 🏛️

세션 방식은 웹의 역사와 거의 함께해 온, 그야말로 '국룰'이자 '고인물' 같은 존재야.
이해하기 아주 직관적이라서 지금도 많은 곳에서 쓰이고 있지. 비유하자면, 동네 목욕탕의 '락커 키' 같은 시스템이야.

세션은 어떻게 돌아갈까? (The Mechanism)

상황을 한 번 그려보자. 네가 어떤 웹사이트에 로그인을 해.

  1. 로그인 시도: 네가 브라우저에 아이디와 비밀번호를 입력하고 '로그인' 버튼을 쾅! 누른다.
  2. 서버의 확인: 서버는 네가 보낸 정보를 데이터베이스(DB)에 있는 회원 정보와 비교해. "음... 아이디 일치, 비밀번호 해시값 일치. 오케이, 통과!"
  3. 세션 생성 및 저장: 바로 이 순간! 서버는 너만을 위한 특별한 저장 공간, 즉 '세션'을 서버 메모리나 별도의 세션 스토어(ex. Redis)에 만들어. 이 공간에는 "이 유저는 '아무개'이고, 등급은 '정회원'이다" 같은 너의 정보가 기록돼. 그리고 이 세션에는 아무나 열 수 없도록 고유한 열쇠, 즉 세션 ID(Session ID)를 부여해.
  4. 세션 ID 전달: 서버는 이 유니크한 세션 ID를 너의 브라우저에게 보내줘. 보통 '쿠키(Cookie)'라는 작은 상자에 담아서 말이야. "자, 이거 네 락커 키(세션 ID)야. 잘 갖고 있어!"
  5. 인증 상태 유지: 이제부터 넌 그 웹사이트를 돌아다닐 때마다, 방금 받은 '세션 ID가 담긴 쿠키'를 모든 요청에 자동으로 실어 보내게 돼. 마치 목욕탕 안에서 락커 키를 손목에 계속 차고 다니는 것처럼.
  6. 서버의 인가 처리: 서버는 요청이 올 때마다 쿠키에 담긴 세션 ID를 확인해. 그리고 서버에 저장된 세션 목록에서 해당 ID를 찾아. "아, 이 키는 '아무개' 거구나. 글쓰기 페이지를 요청했네? 정회원이니 허락해줘야지." 이렇게 권한을 확인하고 적절한 응답을 보내주는 거야.

이 모든 과정이 눈 깜짝할 사이에 일어나는 거지. 신기하지 않아?

인증 흐름 비교 세션 기반 vs 토큰 기반 세션 기반 토큰 기반 클라이언트 (브라우저/앱) 서버 세션 저장소 (메모리/Redis) SID 123 → User A 1. 로그인 요청 (ID/PW) 2. 세션 생성 3. 세션 ID(쿠키) 전달 4. API 요청 + 쿠키 5. 세션 검증 6. 인가된 응답 Cookie: session_id=abc123 클라이언트 (브라우저/앱) 서버 서명/키로 검증 비밀키 또는 공개키 DB 조회 없이 검증 1. 로그인 요청 (ID/PW) 2. 토큰(JWT) 발급 3. API 요청 + 토큰 4. 서명 검증 5. 인가된 응답 Authorization: Bearer eyJ...sig 상태 유지(Stateful) · 서버 저장소 필요 무상태(Stateless) · 서명 기반 검증

세션 방식의 장점: 왜 오랫동안 사랑받았을까? ❤️

1. 서버 중심의 강력한 통제력:
모든 중요한 정보(사용자 정보, 로그인 상태 등)는 전적으로 서버에 저장돼. 클라이언트에게는 그저 의미 없는 긴 문자열인 세션 ID만 줄 뿐이지. 이건 보안상 꽤 큰 이점이야. 만약 해커가 내 세션 ID를 훔쳐가도, 그 자체로는 내 개인정보를 알 수 없어. 서버는 또 원할 때 언제든지 특정 세션을 강제로 만료시킬 수 있어. "어, 이 사용자 활동이 좀 이상한데?" 싶으면 바로 세션을 파괴해서 로그아웃시켜 버리면 그만이야. 통제광(?) 서버에게는 최고의 방식이지.

2. 상대적으로 높은 보안성 (데이터 노출 측면):
클라이언트(브라우저)에는 최소한의 정보, 즉 세션 ID만 저장되므로 사용자 데이터가 외부에 노출될 위험이 적어. 쿠키에 `HttpOnly` 옵션을 걸어두면 자바스크립트로 쿠키에 접근하는 것조차 막을 수 있어서, XSS(Cross-Site Scripting) 공격으로 세션 ID를 탈취당할 가능성도 줄일 수 있지.

3. 구현의 용이성 (전통적 환경에서):
PHP, JSP, ASP.NET 같은 전통적인 서버 사이드 렌더링 프레임워크들은 세션 관리를 위한 기능이 거의 내장되어 있어. 개발자는 그냥 `$_SESSION['user'] = '아무개'` 같은 간단한 코드로 세션을 사용할 수 있지. 복잡한 설정 없이 바로 쓸 수 있다는 건 큰 장점이야.

세션 방식의 단점: 시대의 흐름을 따라가지 못하는 이유 😥

장점만 보면 완벽해 보이지만, 시대가 변하면서 세션의 치명적인 단점들이 드러나기 시작했어.

1. 확장성(Scalability)의 발목을 잡다:
이게 가장 결정적인 문제야. 사용자가 많아지면 서버 한 대로 감당이 안 되니까 여러 대로 늘려야 하잖아? 이걸 '스케일 아웃(Scale-out)' 또는 '수평 확장'이라고 해. 그런데 문제가 생겼어. 내가 1번 서버에서 로그인해서 세션을 만들었는데, 다음 요청이 로드 밸런서(교통정리 담당)에 의해 2번 서버로 보내지면? 2번 서버는 내 세션 정보를 모르잖아! "넌 누구냐?" 하면서 로그인이 풀려버리는 대참사가 발생해.

세션의 확장성 문제 해결법 (하지만 다 귀찮아...):
- 스티키 세션(Sticky Session): 로드 밸런서가 특정 사용자의 요청은 무조건 처음 세션을 만들었던 서버로만 보내도록 설정하는 거야. 하지만 이러면 특정 서버에만 트래픽이 몰릴 수 있고, 그 서버가 다운되면 세션 정보가 다 날아가는 문제가 생겨. 확장성의 의미가 퇴색되지.
- 세션 클러스터링(Session Clustering): 모든 서버가 세션 정보를 서로 복제하고 공유하는 방식. 동기화에 따른 성능 저하가 어마어마해.
- 세션 스토어 분리: Redis나 Memcached 같은 별도의 중앙 세션 저장소를 두는 거야. 모든 서버가 이 중앙 저장소를 바라보게 하는 거지. 현재 가장 많이 쓰는 해결책이지만, 결국 인프라가 복잡해지고 관리 포인트가 늘어나는 단점이 있어.

2. 서버의 부담 (Stateful):
세션은 서버가 사용자의 상태를 일일이 기억하고 있어야 하는 'Stateful(상태 유지)' 방식이야. 사용자가 늘어날수록 서버가 기억해야 할 세션 데이터도 늘어나고, 이는 곧 서버의 메모리 부담으로 이어져. 수십만, 수백만 명이 동시 접속하는 서비스라면? 상상만 해도 끔찍하지.

3. CORS (Cross-Origin Resource Sharing) 이슈:
요즘 웹 서비스는 도메인이 여러 개인 경우가 많아. `api.example.com`에서 인증을 처리하고, `www.example.com`에서 웹페이지를 보여주고, `app.example.com`에서 다른 기능을 제공하는 식이지. 쿠키는 기본적으로 도메인에 종속적이라서, 이런 여러 도메인 간에 세션 ID를 담은 쿠키를 공유하려면 아주 복잡하고 골치 아픈 설정을 해줘야 해. CORS 지옥에 빠지기 십상이지.

결국 세션은 한 지붕 아래 모든 걸 처리하던 '모놀리식 아키텍처(Monolithic Architecture)' 시대의 유산이야. 서비스가 여러 개로 쪼개지고(마이크로서비스 아키텍처), 클라이언트가 다양해지는(웹, 모바일 앱 등) 현대에는 점점 맞지 않는 옷이 되어버린 거지.


챕터 2: 새로운 시대의 대안, 토큰 기반 인증 🚀

세션의 한계가 명확해지면서, "서버가 사용자의 상태를 기억하지 않으면 안 될까?"라는 발칙한 상상에서 출발한 새로운 방식이 등장했어. 바로 토큰(Token) 기반 인증이야. 비유하자면, 놀이공원의 '자유이용권 팔찌' 같은 시스템이지.

이 방식의 핵심 철학은 'Stateless(무상태)'야. 서버는 더 이상 사용자의 로그인 상태를 기억하지 않아. 대신, 인증에 필요한 모든 정보를 담은 '토큰'을 발급하고, 그 토큰의 유효성만 검증할 뿐이지.

토큰은 어떻게 돌아갈까? (feat. JWT)

토큰에도 여러 종류가 있지만, 현재 업계 표준이자 '국룰'로 자리 잡은 건 JWT(JSON Web Token)야. 그래서 보통 '토큰 기반 인증'이라고 하면 'JWT 기반 인증'을 떠올리면 돼. 발음은 '제이더블유티' 또는 '조트'라고 해.

JWT가 어떻게 생겼고 어떻게 작동하는지 알아보자.

1. 로그인 시도 & 토큰 발급:
세션과 마찬가지로, 네가 아이디/비번으로 로그인을 시도해. 서버는 DB를 확인하고 인증에 성공하면, 이제 세션을 만드는 대신 **JWT**라는 암호화된 문자열 덩어리를 생성해서 너에게 보내줘. "자, 이게 네 자유이용권(토큰)이야. 이걸로 모든 놀이기구를 탈 수 있어!"

2. 토큰의 구조:
JWT는 `xxxxx.yyyyy.zzzzz`처럼 점(.)으로 구분된 세 부분으로 이루어져 있어.

JWT 구조 예시

<span style='color:#e57373;'>eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9</span>.<span style='color:#81c784;'>eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE1MTYyNDI2MjJ9</span>.<span style='color:#64b5f6;'>SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c</span>
  • Header (헤더): 첫 번째 부분. 어떤 알고리즘으로 암호화했는지(`alg`), 토큰의 타입이 무엇인지(`typ`) 같은 메타 정보가 담겨.
  • Payload (페이로드): 두 번째 부분. 토큰에 담을 실질적인 정보가 들어있어. 사용자의 아이디, 이름, 권한 등 우리가 넣고 싶은 데이터를 넣을 수 있지. 이걸 '클레임(Claim)'이라고 불러. `exp`(만료 시간), `iat`(발급 시간)처럼 미리 정의된 클레임도 있고, 우리가 마음대로 추가할 수도 있어. 단, 여기에 비밀번호 같은 민감한 정보는 절대 넣으면 안 돼! 페이로드는 암호화된 게 아니라 그냥 Base64로 인코딩된 거라 누구든 디코딩해서 내용을 볼 수 있거든.
  • Signature (서명): 세 번째 부분. 이게 JWT의 핵심이야. 헤더와 페이로드를 합친 문자열을 서버만 아는 **'비밀 키(Secret Key)'**로 암호화한 값이야. 이 서명 덕분에 서버는 토큰이 위변조되지 않았다는 걸 확신할 수 있어.

3. 토큰 저장 및 전송:
클라이언트(브라우저)는 서버로부터 받은 이 토큰을 어딘가에 저장해야 해. 보통은 로컬 스토리지(Local Storage)나 세션 스토리지(Session Storage)에 저장하지. 그리고 API를 요청할 때마다 HTTP 요청 헤더의 `Authorization` 필드에 이 토큰을 실어 보내. 보통 `Authorization: Bearer <토큰>` 이런 형태로 보내는 게 약속(국룰)이야.

4. 서버의 토큰 검증:
서버는 요청 헤더에 담겨 온 토큰을 받아. 그리고 서버가 가진 '비밀 키'를 사용해서 토큰의 서명(Signature) 부분을 다시 계산해봐. 계산된 서명 값과 토큰에 담겨 온 서명 값이 일치하는지 확인하는 거야.
- 일치한다? "오케이, 이 토큰은 내가 발급한 게 맞고, 중간에 아무도 건드리지 않았구나. 페이로드에 담긴 정보는 믿어도 되겠다." -> 인가 처리! - 일치하지 않는다? "이거 위조된 토큰이네!" -> 에러 응답!
이 과정에서 서버는 DB나 세션 스토어를 전혀 뒤져볼 필요가 없어. 오직 토큰 그 자체와 비밀 키만으로 모든 게 해결돼. 이게 바로 'Stateless'의 위엄이지.

토큰 기반 인증 흐름 (Token-Based Authentication Flow) 클라이언트 토큰 저장 (Local Storage 등) 서버 (Stateless) Secret (서버만 아는 비밀키) 1. 로그인 요청 2. JWT 생성 및 서명 3. JWT 전달 4. API 요청 + 토큰 (Authorization 헤더) 5. 서명 검증 (DB 조회 X) 6. 인가된 응답

토큰 방식의 장점: 왜 '모던 웹'의 아이콘이 되었나? ✨

1. 압도적인 확장성 (Stateless의 힘):
서버가 상태를 저장하지 않으니, 서버를 100대로 늘리든 1000대로 늘리든 아무 상관이 없어. 모든 서버가 동일한 '비밀 키'만 공유하고 있으면 어떤 서버로 요청이 가든 토큰을 검증할 수 있으니까. 로드 밸런싱이 아주 자유로워지고, 마이크로서비스 아키텍처(MSA)에 최적화되어 있지. 이게 토큰이 사랑받는 가장 큰 이유야.

2. 유연성과 범용성:
토큰은 HTTP 헤더에 담겨 전달되기 때문에 쿠키의 도메인 제약 같은 CORS 이슈에서 훨씬 자유로워. 웹(www.jaenung.net)이든, 모바일 앱(iOS, Android)이든, 심지어 IoT 기기든, HTTP 통신만 가능하다면 어디서든 토큰을 사용할 수 있어. 그야말로 플랫폼을 가리지 않는 '만능 키'인 셈이지. 재능 공유 플랫폼 **재능넷**처럼 웹과 앱, 다양한 서비스가 연동되는 복잡한 시스템이라면 토큰 방식이 훨씬 유리할 수밖에 없어.

3. 분리된 인증 서버:
인증만 전담하는 서버를 따로 둘 수 있어. 여러 서비스들이 이 중앙 인증 서버에서 토큰만 발급받고, 각 서비스들은 그 토큰의 유효성만 검증하면 되는 구조가 가능해져. 시스템의 역할과 책임이 명확하게 분리되는 거지. 이게 바로 **OAuth 2.0** 같은 현대적인 인증 프레임워크의 기본 사상이기도 해.

토큰 방식의 단점: 자유에는 책임이 따른다 💣

물론 토큰도 완벽하진 않아. 자유를 얻은 대신 새로운 고민거리들이 생겼지.

1. 토큰 탈취 문제:
이게 가장 큰 골칫거리야. 만약 해커가 네 토큰을 훔쳐갔다면? 그 토큰이 만료되기 전까지 해커는 너인 척 행세할 수 있어. 세션은 서버에서 강제 로그아웃 시킬 수 있지만, (기본적인) 토큰은 한 번 발급되면 서버가 통제할 방법이 없어. 자유이용권 팔찌를 뺏기면, 뺏어간 사람이 하루 종일 놀이기구를 탈 수 있는 것과 같지. 그래서 토큰의 만료 시간(expiration)을 너무 길게 잡으면 안 돼.

2. 토큰 저장소 보안:
토큰을 어디에 저장하느냐도 중요한 문제야. 보통 로컬 스토리지에 많이 저장하는데, 여긴 자바스크립트로 접근이 가능해서 XSS 공격에 취약해. 악성 스크립트가 심어진 사이트에 접속하면 내 로컬 스토리지에 있는 토큰이 그대로 탈취될 수 있다는 뜻이야. 그래서 요즘은 XSS 공격을 막기 위해 토큰을 `HttpOnly` 쿠키에 저장하는 방식도 다시 주목받고 있어. (어라? 세션이랑 비슷해지네?)

3. 페이로드 데이터의 한계:
토큰의 페이로드에 너무 많은 정보를 담으면 토큰의 크기가 커져. 이건 매 요청마다 더 많은 데이터를 네트워크로 전송해야 한다는 뜻이고, 약간의 성능 저하를 유발할 수 있어. 그리고 앞서 말했듯, 페이로드는 디코딩이 가능하므로 절대 민감 정보를 담으면 안 된다는 제약도 있지.

4. 강제 로그아웃의 어려움:
세션처럼 서버에서 특정 사용자를 즉시 로그아웃시키는 게 기본적으로는 불가능해. 이걸 해결하려면 '토큰 블랙리스트' 같은 걸 만들어서, 무효화된 토큰 목록을 서버가 별도로 관리해야 해. 그런데... 어라? 이러면 서버가 또 '상태'를 가지게 되네? Stateless라는 토큰의 가장 큰 장점이 희석되는 딜레마가 생기는 거지.


챕터 3: 세기의 대결! 세션 vs 토큰, 항목별 비교 분석 🥊

자, 이제 두 선수의 특징을 모두 살펴봤으니, 링 위로 올려서 제대로 맞붙게 해보자. 어떤 라운드에서 누가 우세할까?

항목 세션 기반 인증 (Session-Based) 토큰 기반 인증 (Token-Based) 승자 (Winner)
상태 관리 (Statefulness) Stateful (서버가 사용자 상태 저장) Stateless (서버가 상태 저장 안 함) 토큰
확장성 (Scalability) 낮음 (서버 증설 시 세션 공유 문제 발생) 높음 (서버 증설에 자유로움) 토큰
서버 부하 높음 (사용자마다 세션 저장, 매 요청마다 조회) 낮음 (토큰 검증 연산만 필요) 토큰
보안 (탈취 시) 서버에서 강제 로그아웃 가능. 상대적으로 안전. 만료 전까지 토큰 유효. 탈취 시 위험. 세션
보안 (저장소) 주로 HttpOnly 쿠키 사용 (XSS에 비교적 강함) 주로 로컬 스토리지 사용 (XSS에 취약) 세션
범용성 (Multi-Platform) 낮음 (쿠키 기반이라 웹 브라우저에 유리) 높음 (웹, 앱, IoT 등 플랫폼 독립적) 토큰
CORS 처리 복잡함 (쿠키의 도메인 종속성) 간단함 (HTTP 헤더 사용) 토큰
강제 로그아웃 간단함 (서버에서 세션 삭제) 복잡함 (블랙리스트 등 추가 구현 필요) 세션

종합 평가: 누가 더 낫다고 말할 수 있을까?

표를 보면 알겠지만, 이건 마치 "짜장면이냐 짬뽕이냐" 같은 질문이야. 정답은 없어. 상황에 따라, 아키텍처에 따라 최적의 선택이 달라질 뿐이지.

🚀 토큰이 압도적으로 유리한 경우:

  • 마이크로서비스 아키텍처(MSA)를 사용하거나 계획 중일 때
  • 웹뿐만 아니라 모바일 앱 등 여러 클라이언트를 지원해야 할 때
  • 외부 서비스와의 연동(OAuth)이 중요할 때
  • 대규모 트래픽으로 수평 확장이 필수적일 때
  • 프론트엔드와 백엔드가 완벽히 분리된 SPA(Single Page Application)를 개발할 때

🏛️ 세션도 여전히 괜찮은 선택인 경우:

  • 단일 서버로 운영되는 모놀리식 아키텍처일 때
  • 빠른 개발이 중요하고, 복잡한 인증 체계가 필요 없을 때
  • 서버에서 사용자의 활동을 세밀하게 추적하고 강제 제어할 필요가 강할 때
  • 내부 관리자 페이지처럼 트래픽이 많지 않고 확장성이 중요하지 않은 서비스일 때

결국, "우리 서비스의 미래는 어떤 모습일까?"를 고민하는 게 핵심이야. 당장은 작고 간단한 서비스라도, 나중에 커지고 복잡해질 가능성이 있다면 처음부터 토큰 기반으로 설계하는 게 현명한 투자가 될 수 있겠지.


챕터 4: 절충과 진화, 하이브리드 전략과 최신 트렌드 🧬

똑똑한 개발자들은 세션과 토큰의 장점만 쏙쏙 빼먹을 수 있는 방법을 고민하기 시작했어. "토큰의 확장성은 가져오되, 세션의 보안성을 더할 순 없을까?" 이 고민의 결과물이 바로 현대 인증 아키텍처의 표준으로 자리 잡은 '하이브리드' 전략이야.

치트키의 등장: 액세스 토큰(Access Token) + 리프레시 토큰(Refresh Token)

이게 바로 현대 인증 시스템의 '끝판왕'이자 '국룰'이야. 토큰의 가장 큰 단점이 '탈취되면 만료 전까지 속수무책'이라는 거였잖아? 이걸 해결하기 위해 토큰을 두 종류로 나눠서 사용하는 거야.

1. 액세스 토큰 (Access Token):
- 역할: 실제 API 요청에 사용되는 토큰. 우리가 지금까지 이야기한 JWT가 바로 이거야.
- 특징: 만료 시간이 아주 짧아. 보통 15분 ~ 1시간 정도로 설정해.
- 보안: 만약 해커가 이걸 탈취해도, 금방 만료되기 때문에 피해를 최소화할 수 있어. "네가 훔쳐봤자 15분짜리 시한부 인생이야!"라고 말하는 셈이지.

2. 리프레시 토큰 (Refresh Token):
- 역할: 만료된 액세스 토큰을 대신해서 새로운 액세스 토큰을 발급받는 데에만 사용되는 토큰.
- 특징: 만료 시간이 길어. 보통 7일 ~ 30일 정도로 설정해.
- 보안: 이 토큰은 절대로 API 요청에 사용되지 않아. 오직 '토큰 재발급'이라는 특정 API(/refresh-token)를 호출할 때만 사용돼. 그리고 이 리프레시 토큰은 서버의 DB나 안전한 저장소에 저장해서 관리해. (어라? 다시 Stateful 해졌네? 맞아! 이게 핵심이야.)

어떻게 동작할까?

  1. 최초 로그인: 사용자가 로그인하면, 서버는 액세스 토큰(단기)리프레시 토큰(장기)을 둘 다 발급해준다.
  2. API 요청: 클라이언트는 '액세스 토큰'을 사용해서 API를 자유롭게 요청한다. 서버는 액세스 토큰의 유효성만 빠르게 검증한다 (Stateless).
  3. 액세스 토큰 만료: 15분이 지나 액세스 토큰이 만료되면, API 요청은 실패(401 Unauthorized 에러)한다.
  4. 토큰 재발급 요청: 이때 클라이언트는 당황하지 않고, 숨겨뒀던 '리프레시 토큰'을 꺼내 '토큰 재발급 API'에 보낸다.
  5. 서버의 검증 및 재발급: 서버는 받은 리프레시 토큰이 DB에 저장된 것과 일치하는지 확인한다 (Stateful). 유효하다면, 새로운 액세스 토큰을 발급해서 클라이언트에게 보내준다.
  6. 다시 API 요청: 클라이언트는 새로 받은 액세스 토큰으로 다시 API 요청을 이어간다. 사용자는 로그인이 풀린 줄도 모르고 서비스를 계속 이용할 수 있다.

이 방식의 장점은?
- 보안 강화: 해커가 액세스 토큰을 탈취해도 금방 무용지물이 된다.
- 서버 부하 감소: 평소의 모든 API 요청은 Stateless한 액세스 토큰으로 처리하므로 서버 부하가 적다.
- 사용자 편의성: 사용자는 자주 로그인할 필요 없이 오랫동안 로그인 상태를 유지할 수 있다.
- 강제 로그아웃 가능: 만약 특정 사용자를 로그아웃시키고 싶다면, 서버 DB에 있는 그 사용자의 '리프레시 토큰'을 삭제해버리면 된다. 그러면 더 이상 새로운 액세스 토큰을 발급받지 못하므로 자연스럽게 로그아웃된다. 세션의 장점을 가져온 거지!

이처럼 액세스 토큰 + 리프레시 토큰 조합은 토큰의 Stateless한 확장성과 세션의 Stateful한 통제력을 절묘하게 결합한, 현시점 가장 균형 잡힌 인증 아키텍처라고 할 수 있어.

토큰, 어디에 저장해야 안전할까? (Storage War)

토큰을 어디에 저장할지에 대한 논쟁도 뜨거워. 정답은 없지만, 각각의 장단점을 알아두는 게 중요해.

1. 로컬 스토리지 (Local Storage):
- 장점: 사용하기 쉽고, 브라우저를 껐다 켜도 데이터가 유지된다. 용량도 넉넉하다.
- 단점: XSS(Cross-Site Scripting) 공격에 치명적이다. 자바스크립트로 접근이 가능하기 때문에, 악성 스크립트가 주입되면 토큰이 그대로 유출될 수 있다.

2. 세션 스토리지 (Session Storage):
- 장점: 로컬 스토리지와 비슷하지만, 브라우저 탭을 닫으면 데이터가 사라진다.
- 단점: 역시 XSS 공격에 취약하다. 탭을 닫으면 사라지므로 '로그인 유지' 기능에는 부적합하다.

3. 쿠키 (Cookie):
- 장점: `HttpOnly` 옵션을 설정하면 자바스크립트로 접근이 불가능해져 XSS 공격을 원천적으로 방어할 수 있다. `Secure` 옵션으로 HTTPS에서만 전송되게 하고, `SameSite` 옵션으로 CSRF(Cross-Site Request Forgery) 공격도 방어할 수 있다.
- 단점: CSRF 공격에 대한 방어 설정이 필요하고, 용량이 작다(4KB). 여러 도메인에서 사용하려면 CORS 설정이 복잡해질 수 있다.

그래서 요즘 트렌드는?
보안을 중시하는 많은 서비스들은 리프레시 토큰은 `HttpOnly`, `Secure`, `SameSite=Strict` 옵션을 적용한 쿠키에 저장하고, 액세스 토큰은 자바스크립트가 접근할 수 있는 메모리(변수)에 저장하는 방식을 선호해. 이렇게 하면 XSS로 액세스 토큰이 탈취되더라도 금방 만료되고, 가장 중요한 리프레시 토큰은 안전하게 지킬 수 있기 때문이지. 결국 돌고 돌아 쿠키의 보안적 장점을 다시 활용하는, 또 다른 형태의 하이브리드 전략인 셈이야.


결론: 그래서, 내 서비스의 문지기는 누구로? 👨‍⚖️

정말 긴 여정이었어. 세션이라는 듬직한 고참부터 토큰이라는 날렵한 신참, 그리고 둘의 장점을 합친 하이브리드 용병까지 모두 만나봤어.

이제 처음의 질문으로 돌아가 보자. "너의 앱은 어떤 문지기를 세울래?"

이 질문에 대한 답은 더 이상 "세션이요" 또는 "토큰이요"가 아닐 거야. 더 구체적이고, 더 똑똑한 대답을 할 수 있게 됐지.

"제 서비스는 초기 단계의 모놀리식 웹 애플리케이션이고, 빠른 개발과 서버의 강력한 통제가 중요하기 때문에 세션 기반 인증으로 시작하겠습니다."

혹은,

"제 서비스는 SPA와 모바일 앱을 함께 지원하는 마이크로서비스 아키텍처를 지향합니다. 따라서 확장성과 플랫폼 유연성을 위해 토큰 기반 인증을 채택하겠습니다. 특히 보안을 강화하기 위해 액세스 토큰과 리프레시 토큰을 분리하고, 리프레시 토큰은 HttpOnly 쿠키에 저장하는 하이브리드 전략을 사용할 계획입니다."

어때? 훨씬 전문가 같지 않아? 😎

인증 아키텍처는 서비스의 '뼈대'와 같아. 처음부터 서비스의 현재와 미래를 잘 고민해서 설계해야 나중에 고생하지 않아. 오늘 나눈 이야기가 네가 만드는 서비스의 문지기를 선택하는 데 훌륭한 가이드가 되었으면 좋겠다. 혹시 알아? 이렇게 쌓은 지식으로 나중에 **재능넷** 같은 플랫폼에서 다른 사람들에게 멋진 기술 컨설팅을 해줄 수도 있을지!

기억해. 기술에는 완벽한 정답은 없지만, 언제나 더 나은 선택은 존재한다는 걸. 끊임없이 고민하고 학습하는 멋진 개발자가 되길 바랄게! 그럼 이만!

댓글 작성

이 글에 대한 여러분의 생각을 들려주세요

댓글 0