쿠키와 세션으로 로그인 이해하기
지금까지 만든 시료 관리 API 서버에는 한 가지 큰 문제가 있습니다. 누구나 시료를 등록하고, 수정하고, 삭제할 수 있다는 것입니다. 실험실에서 장비실 출입에 카드키가 필요하듯, 웹 서비스에도 "이 사람이 누구인지 확인하는 절차" — **인증(Authentication)**이 필요합니다.
로그인은 어떻게 동작할까요? 이 질문에 답하려면 먼저 웹의 근본적인 특성을 이해해야 합니다.
HTTP는 기억하지 못한다
웹의 통신 프로토콜인 HTTP는 **무상태(stateless)**입니다. 서버는 요청을 처리하고 응답을 보낸 순간, 그 사용자가 누구였는지 즉시 잊어버립니다.
비유하면 — 접수 데스크에서 담당자가 5초마다 기억이 리셋되는 상태입니다. "제 시료 S001 결과 알려주세요" → 결과 전달 → (리셋) → "제가 아까 시료 S001 물어봤는데요" → "누구시죠?"
매 요청마다 "저 김연구입니다"라고 다시 말해야 합니다. 이 불편함을 해결하기 위해 1994년에 **쿠키(Cookie)**가 발명되었습니다.
쿠키: 브라우저에 붙이는 이름표
쿠키는 서버가 브라우저에게 건네주는 작은 텍스트 데이터입니다. 브라우저는 이 데이터를 저장해두었다가, 같은 서버에 요청할 때마다 자동으로 함께 보냅니다.
[1] 로그인 요청
브라우저 → 서버: "아이디 kim, 비밀번호 1234"
서버 → 브라우저: "확인 완료! 이 쿠키를 갖고 다녀"
Set-Cookie: user=kim
[2] 이후 요청 (자동)
브라우저 → 서버: "시료 목록 줘" + Cookie: user=kim
서버: "아, kim 연구원이구나. 시료 목록 보내줄게"
[3] 또 다른 요청 (자동)
브라우저 → 서버: "S001 상세정보" + Cookie: user=kim
서버: "kim 연구원의 요청이네. S001 정보 보내줄게"실험실 출입증과 같습니다. 한 번 발급받으면 매번 신분을 다시 증명할 필요 없이, 출입증만 보여주면 됩니다.
Express에서 쿠키 다루기
const express = require("express");
const app = express();
app.get("/login", function(req, res) {
res.setHeader("Set-Cookie", "user=kim; Path=/");
res.send("로그인 완료");
});
app.get("/whoami", function(req, res) {
const cookies = req.headers.cookie;
res.send("현재 쿠키: " + cookies);
});
app.listen(3000);/login에 접속하면 서버가 Set-Cookie 헤더로 쿠키를 보내고, 이후 /whoami에 접속하면 브라우저가 자동으로 쿠키를 보내는 것을 확인할 수 있습니다.
쿠키의 한계: 왜 세션이 필요한가
쿠키만으로 인증하면 심각한 문제가 있습니다. 쿠키는 브라우저에 저장됩니다. 브라우저 개발자 도구를 열면 누구나 쿠키 내용을 볼 수 있고, 수정할 수도 있습니다.
만약 Cookie: user=kim 이런 식으로 사용자 이름을 직접 쿠키에 넣으면, 누군가 Cookie: user=admin으로 바꿔서 관리자 행세를 할 수 있습니다.
실험실 비유로 — 출입증에 "이름: 김연구"라고 적혀있는데, 누구나 수정 테이프로 이름을 바꿀 수 있다면 출입 통제가 무의미합니다.
**세션(Session)**은 이 문제를 해결합니다.
세션: 서버가 관리하는 출입 기록
세션의 핵심 아이디어 — 민감한 정보는 서버에 저장하고, 브라우저에는 식별 번호만 준다.
[1] 로그인 성공
서버 내부: { session_abc123: { user: "kim", role: "researcher" } }
서버 → 브라우저: Set-Cookie: session_id=abc123
[2] 이후 요청
브라우저 → 서버: Cookie: session_id=abc123
서버: abc123 세션 찾기 → { user: "kim", role: "researcher" }
→ "kim 연구원이구나"브라우저가 가진 것은 abc123이라는 의미 없는 번호뿐입니다. 이 번호를 바꿔봤자 서버에 등록된 세션이 아니면 무효합니다.
실험실 비유로 — 출입증에 바코드만 찍혀있고, 실제 신원 정보는 보안실 컴퓨터에 있는 것과 같습니다. 출입증을 복사해도 바코드가 데이터베이스에 없으면 문이 열리지 않습니다.
| 쿠키만 사용 | 세션 (쿠키 + 서버 저장소) | |
|---|---|---|
| 데이터 위치 | 브라우저 | 서버 |
| 보안 | 사용자가 수정 가능 (위험) | 서버만 수정 가능 (안전) |
| 용량 | 4KB 제한 | 서버 메모리/DB 기반 (제한 없음) |
| 비유 | 이름이 적힌 출입증 | 바코드만 있는 출입증 + 보안실 DB |
쿠키의 보안 옵션
쿠키 자체에도 보안 장치를 설정할 수 있습니다:
Set-Cookie: session_id=abc123; HttpOnly; Secure; Path=/; Max-Age=3600| 옵션 | 의미 | 왜 필요한가 |
|---|---|---|
HttpOnly | JavaScript에서 쿠키에 접근 불가 | 악성 스크립트(XSS)가 쿠키를 훔치는 것을 방지 |
Secure | HTTPS에서만 쿠키 전송 | 네트워크 도청으로 쿠키가 노출되는 것을 방지 |
Max-Age=3600 | 3600초(1시간) 후 자동 삭제 | 오래된 로그인 상태가 무한히 유지되는 것을 방지 |
Path=/ | 이 경로 이하의 요청에서만 쿠키 전송 | 불필요한 쿠키 전송 범위 제한 |
Max-Age가 없는 쿠키는 브라우저를 닫으면 사라집니다 — 이것을 **세션 쿠키(Session Cookie)**라고 합니다. Max-Age가 있는 쿠키는 브라우저를 닫아도 남아 있습니다 — 이것을 **영속 쿠키(Permanent Cookie)**라고 합니다.
현대의 인증: 토큰 기반
현대 웹 서비스에서는 세션 ID 대신 **JWT(JSON Web Token)**라는 토큰을 사용하는 경우가 많습니다. BioPlayground가 사용하는 Supabase도 JWT 기반 인증입니다.
원리는 세션과 비슷하지만, 차이가 있습니다:
| 세션 방식 | 토큰(JWT) 방식 | |
|---|---|---|
| 서버 상태 | 세션 저장소 필요 | 서버에 저장 불필요 |
| 검증 | 세션 DB 조회 | 토큰 자체에 서명 검증 |
| 확장성 | 서버가 여러 대면 세션 공유 필요 | 서버 간 공유 불필요 |
깊은 구현은 이 단계에서 다루지 않습니다. 중요한 것은 메커니즘을 이해하는 것입니다:
- 사용자가 아이디/비밀번호를 보냄
- 서버가 확인하고, 식별자(세션 ID 또는 토큰)를 발급
- 브라우저가 이후 요청에 식별자를 자동 첨부
- 서버가 식별자로 "이 사람이 누구인지" 확인
모든 로그인 시스템은 이 4단계의 변형입니다.
직접 해보기 (Faded Example)
아래 빈칸을 채워 쿠키 기반 인증의 흐름을 완성하세요.
// 1. 로그인: 서버가 쿠키를 발급app.post("/login", function(req, res) {res.setHeader("", "session_id=xyz789; HttpOnly; Path=/");res.json({ message: "로그인 성공" });});// 2. 보호된 API: 쿠키로 사용자 확인app.get("/my-samples", function(req, res) {const cookies = req.headers.;if (!cookies || !cookies.includes("session_id")) {return res.status().json({ error: "로그인 필요" });}res.json({ samples: ["S001", "S002"] });});
흔한 에러 & 해결법
Q: 쿠키가 전송되지 않습니다 (req.headers.cookie가 undefined)
브라우저의 개발자 도구(F12) → Application → Cookies에서 해당 도메인의 쿠키가 있는지 확인하세요. Secure 옵션이 켜져 있으면 HTTP(localhost)에서는 전송되지 않습니다. 개발 중에는 Secure를 빼거나 HTTPS를 사용하세요.
Q: Set-Cookie 헤더를 보냈는데 브라우저가 쿠키를 저장하지 않습니다
프론트엔드와 백엔드의 도메인(또는 포트)이 다르면 CORS 정책에 의해 쿠키가 차단됩니다. fetch 호출 시 credentials: 'include' 옵션을 추가하고, 서버에서 Access-Control-Allow-Credentials: true 헤더를 설정해야 합니다.
Q: 세션과 쿠키의 차이를 한 문장으로 설명해주세요
쿠키는 "브라우저에 저장되는 데이터"이고, 세션은 "서버에 저장되는 데이터를 쿠키의 ID로 찾는 시스템"입니다. 세션은 쿠키 위에 올라가는 개념입니다.