콘텐츠로 이동

SSO 로그인과 쿠키 세션 유지

11/11(수)

한 줄 요약: SSO는 "누구인지 확인하는 방법"이고, 로그인 뒤 "로그인 상태를 어떻게 이어 가느냐"는 따로 정하는 문제다. 서버가 있는 앱은 보통 SSO 결과를 서버가 서명한 쿠키로 바꿔 두어서, 브라우저가 토큰을 헤더에 실을 필요가 없다.

📍 만난 상황

  • 2026-11-11 09:26 — 대시보드 다운로드 방식을 점검하던 중이었다. 직전에 시니어의 "브라우저 메모리가 찬다"는 걱정이 토큰을 Authorization 헤더로 보내느라 fetch→blob으로 받는 구조를 상정한 말일 수 있다는 설명을 들었다. 그래서 "SSO로 바꾸면 이게 실제 문제가 되는 거냐"고 물었다. 1번 물음 → 그날 일지
  • 결론: 문제가 안 된다. 대시보드는 이미 SSO를 쓰고 있고, 로그인 뒤에는 서버가 서명한 쿠키로 세션을 유지한다.

🧩 쉽게 말하면

놀이공원 입장을 떠올리면 된다. 정문에서 신분증과 예매 내역을 확인한다(SSO 로그인). 확인이 끝나면 손목 밴드를 채워 준다(쿠키 세션).

그 뒤로는 놀이기구마다 신분증을 다시 보여 주지 않는다. 밴드만 보여 주면 된다. 밴드는 손목에 붙어 있으니 깜빡할 일도 없다.

반대로 밴드 없이 "확인증 종이"를 손에 들고 다니는 방식도 있다(JS가 토큰 보관). 이러면 놀이기구마다 종이를 직접 꺼내 보여 줘야 한다. 종이를 꺼낼 수 없는 입구(<a href> 링크)에서는 들어갈 수 없다.

📖 제대로 알기

SSO와 세션은 서로 다른 단계다

  • SSO(Single Sign-On: 한 번 로그인으로 여러 서비스를 쓰는 방식): "이 사람이 누구인가"를 다른 서비스(IdP: Identity Provider, 신원 확인 서비스)에 맡긴다. 오늘은 manager.hub가 IdP였다.
  • OIDC(OpenID Connect: SSO를 구현하는 표준 규약 중 하나): 로그인이 끝나면 IdP가 앱에 토큰(누구인지 증명하는 서명된 문자열)을 준다.
  • 세션(session: 로그인 상태를 요청마다 이어 가는 방법): 그 토큰을 받은 뒤에 정한다.

즉 "SSO를 쓴다"는 말만으로는 브라우저가 무엇을 들고 다니는지 알 수 없다.

세션을 유지하는 두 가지 방식

방식 토큰을 누가 가지나 요청에 어떻게 실리나
서버 쿠키 세션 서버. 브라우저엔 쿠키만 브라우저가 자동으로 쿠키를 붙임
JS 토큰 보관 브라우저 JS (메모리, localStorage) JS가 Authorization 헤더를 직접 붙임

쿠키 관련 용어

  • 쿠키(cookie: 서버가 브라우저에 맡겨 두는 작은 값): 같은 사이트로 가는 요청마다 브라우저가 알아서 붙인다. <a href>, <img>, 주소창 이동도 마찬가지다.
  • 서명 쿠키(signed cookie): 서버만 아는 비밀 키로 서명한 쿠키. 사용자가 내용을 바꾸면 서명이 안 맞아 거절된다.
  • stateless 세션: 세션 내용을 서명 쿠키 안에 담아, 서버가 따로 세션 저장소를 두지 않는 방식. 서버(파드)가 몇 대든, 재시작되든 상관없다.
  • HttpOnly: JS가 쿠키를 읽지 못하게 막는 속성. 토큰 탈취 공격(XSS)에 강해진다.
  • SameSite: 다른 사이트에서 시작된 요청에 쿠키를 실을지 정하는 속성.

⚙️ 어떻게 동작하나

sequenceDiagram
  participant B as 브라우저
  participant S as 대시보드 서버
  participant I as manager.hub
  B->>S: 로그인 버튼
  S->>B: manager.hub로 이동시킴
  B->>I: 로그인
  I->>S: 인증 코드 전달
  S->>I: 코드로 토큰 받기
  S->>B: 서명한 세션 쿠키 심기
  B->>S: CSV 받기 링크 (쿠키 자동 첨부)
  S->>B: 파일

토큰은 서버와 manager.hub 사이에서만 오간다. 브라우저 손에 남는 건 쿠키뿐이라 다운로드 링크에도 인증이 저절로 실린다.

  1. 사용자가 로그인 버튼을 누르면 서버가 브라우저를 IdP 로그인 화면으로 보낸다.
  2. 로그인이 끝나면 IdP가 인증 코드(토큰과 바꿀 수 있는 일회용 표)를 앱 서버 주소로 돌려보낸다. PKCE(코드 가로채기를 막는 확인 절차)도 이때 쓴다.
  3. 서버가 코드로 토큰을 받아 사용자를 확인한다.
  4. 서버는 "이 사용자는 누구"라는 내용을 서명한 쿠키로 만들어 브라우저에 심는다. 받은 토큰은 버려도 된다.
  5. 이후 모든 요청에서 서버는 쿠키 서명만 확인한다. 오늘 코드에서는 requireUser(request)가 이 일을 했다. 로컬 계정이든 SSO든 똑같이 처리했다.

🧪 예시

  • 예시 1 · 일상에 빗대어: 놀이공원에서 놀이기구 10개를 탄다.
  • 손목 밴드 방식: 정문 확인 1번, 이후 10번은 밴드만 보여 줌. 사진 촬영 부스처럼 "종이를 받지 않는 곳"에도 밴드는 통한다.
  • 확인증 종이 방식: 10번 모두 종이를 꺼내 보여 줘야 한다. 종이를 받는 창구가 없는 곳은 아예 못 들어간다.

  • 예시 2 · 실제로는

HTTP/1.1 302 Found
Set-Cookie: session=eyJ1c2VyIjoi...서명; HttpOnly; Secure; SameSite=Lax; Path=/
Location: /

① 로그인 콜백에서 서버가 이 응답을 보낸다. Set-Cookie가 손목 밴드를 채우는 줄이다. ② HttpOnly라서 JS는 이 값을 읽지도 들고 다니지도 않는다. ③ 이후 <a href="/api/datasets/123/download" download>를 누르면 브라우저가 Cookie: session=…을 자동으로 붙인다.

JS 토큰 보관 방식이면 다운로드가 이렇게 바뀐다.

const res = await fetch(url, {
  headers: { Authorization: `Bearer ${token}` },
});
const blob = await res.blob(); // 파일 전체가 메모리에

① <a href>는 헤더를 붙일 수 없어서 fetch를 써야 한다. ② fetch로 받은 파일을 저장하려면 blob()으로 모아야 하고, 큰 파일이면 탭 메모리가 찬다. 시니어가 걱정한 상황이 바로 이 구조다.

⚖️ 비교 · 흔한 오해

헷갈린 점: "SSO를 쓰면 브라우저가 토큰을 헤더로 보내야 한다" 이렇게 생각하기 쉬운 이유가 있다. SSO·OIDC 예제 글은 대부분 서버 없는 SPA(브라우저 JS만으로 돌아가는 앱) 기준이라 "토큰을 받아 Authorization: Bearer로 보낸다"까지가 한 묶음으로 나온다.

하지만 그건 SPA에서 흔한 세션 유지 방식일 뿐이다. SSO 자체의 요구 사항이 아니다. Next.js처럼 서버가 있는 앱은 서버가 토큰을 받아 쿠키 세션으로 바꾸는 게 일반적이다. 이런 구조를 BFF(Backend For Frontend: 프런트 전용 서버가 토큰을 대신 들고 있는 구조)라고도 부른다.

정말 문제가 되는 경우 1. 브라우저가 대시보드 서버를 거치지 않고 플랫폼 API를 직접 부를 때. 플랫폼은 대시보드 쿠키를 모르니 헤더 토큰이 필요하다. 2. 다운로드 API가 다른 도메인에 있을 때. SameSite와 도메인 설정에 따라 쿠키가 안 실릴 수 있다. 3. 프런트엔드를 토큰 보관형 SPA로 다시 만들 때.

세 경우 모두 presigned URL(주소 안에 기한 있는 서명이 들어 있는 링크)이 대안이 된다. 헤더도 쿠키도 필요 없기 때문이다.

🛠️ 실무에서는

  • 서버가 있는 앱이면 쿠키 세션을 기본으로 둔다. 토큰이 브라우저 JS에 노출되지 않아 더 안전하다.
  • 쿠키에는 HttpOnly, Secure(HTTPS에서만 전송), 적절한 SameSite를 붙인다.
  • 쿠키 방식은 다른 사이트가 몰래 요청을 보내는 공격(CSRF)에 신경 써야 한다. SameSite=Lax 이상과, 상태를 바꾸는 요청에 대한 추가 확인을 둔다.
  • 로그인 뒤 다운로드가 안 되면 먼저 볼 것: 개발자 도구 Network 탭에서 그 요청에 Cookie 헤더가 실렸는지. 안 실렸으면 도메인·SameSite·Secure(HTTP 개발 환경에서 흔함)를 확인한다.

❓ 자주 묻는 질문

  • Q. 서버가 토큰을 버리면 플랫폼 API는 어떻게 부르나? A. 대시보드는 플랫폼을 부를 때 사용자 토큰이 아니라 서버 자신의 서비스 계정 토큰을 쓴다. 사용자 확인은 쿠키로, 플랫폼 호출은 서버 자격으로 나눈 것이다.
  • Q. stateless 서명 쿠키면 강제 로그아웃은 어떻게 하나? A. 서버에 저장된 세션이 없어 하나만 콕 집어 끊기 어렵다. 그래서 쿠키 만료 시간을 짧게 두거나, 서명 키를 바꿔 전체를 무효화한다.
  • Q. 쿠키 세션이면 브라우저 메모리 문제는 아예 없나? A. 다운로드를 <a href download>로 받는 한 그렇다. 메모리를 쓰는 건 인증 방식이 아니라 fetch→blob이라는 받는 방식이다.

🔭 더 깊이 보기

📚 참고 자료

  • HTTP 쿠키 (MDN 한국어) — Set-Cookie, HttpOnly, Secure, SameSite의 뜻 · 링크
  • Authorization (MDN 한국어) — 헤더로 토큰을 보내는 방식 · 링크
  • Session Management Cheat Sheet (OWASP, 영문) — 쿠키 세션을 안전하게 만드는 체크리스트 · 링크

🗓️ 이력

  • 2026-11-11: SSO로 바꾸면 헤더 토큰 때문에 다운로드 메모리 문제가 생기는지 물음 → 일지

🕸️ 연결된 주제