세션(Session) 기반 인증

Series: 인증과 인가

인증과 인가contains 5

들어가며

웹 서비스를 개발하다 보면 사용자가 로그인한 이후, 그 로그인 상태를 어떻게 유지할 것인지 고민하게 된다. HTTP는 기본적으로 상태를 기억하지 않는 stateless 프로토콜이다. 즉, 서버는 이전 요청에서 사용자가 로그인을 했는지, 어떤 사용자였는지, 어떤 권한을 가지고 있었는지를 자동으로 기억하지 않는다.

예를 들어 사용자가 로그인에 성공한 뒤 마이페이지에 접근한다고 해도, 서버 입장에서는 이 요청이 방금 로그인한 사용자에게서 온 요청인지 알 수 없다. 따라서 로그인 이후의 요청마다 “이 사용자가 누구인지”를 판단할 수 있는 별도의 장치가 필요하다. 이때 가장 전통적으로 사용되는 방식이 세션 기반 인증(Session-Based Authentication)이다. 세션 기반 인증은 서버가 사용자의 로그인 상태를 직접 저장하고, 클라이언트는 그 세션을 식별할 수 있는 값만 가지고 요청을 보내는 방식이다.

이번 글에서는 세션 기반 인증의 개념과 동작 방식, 장단점, 그리고 어떤 상황에서 세션 기반 인증을 선택하는 것이 적절한지 정리해보겠다.

1. 개요

1.1. HTTP의 Stateless 특성

HTTP는 각각의 요청을 독립적으로 처리한다. 로그인 요청, 게시글 작성 요청, 마이페이지 조회 요청은 모두 별개의 요청으로 취급된다. 예를 들어 다음과 같은 상황을 생각해보자.

  1. 사용자가 아이디와 비밀번호를 입력해 로그인 요청을 보낸다.
  2. 서버가 사용자 정보를 확인하고 로그인 성공 응답을 내려준다.
  3. 사용자가 마이페이지 조회 요청을 보낸다.
  4. 서버는 이 요청만 보고는 사용자가 방금 로그인한 사람인지 알 수 없다.

이처럼 HTTP 자체는 이전 요청의 상태를 기억하지 않기 때문에, 로그인 상태를 유지하려면 별도의 인증 상태 관리 방식이 필요하다.

1.2. 세션 기반 인증(Session-Based Authentication)

세션 기반 인증은 로그인 상태를 서버에서 관리하는 방식이다. 사용자가 로그인에 성공하면 서버는 세션 저장소에 해당 사용자의 정보를 저장한다. 그리고 이 세션을 식별하기 위한 고유한 값인 session_id를 생성한다. 이후 서버는 이 session_id를 클라이언트에게 전달하고, 클라이언트는 보통 이를 쿠키에 저장한다. 이후 클라이언트가 요청을 보낼 때마다 쿠키에 담긴 session_id가 함께 서버로 전송된다. 서버는 전달받은 session_id를 이용해 세션 저장소를 조회하고, 해당 요청이 어떤 사용자로부터 온 것인지 판단한다. 동작 흐름은 다음과 같다.

  1. 사용자가 로그인 요청을 보낸다.
  2. 서버가 아이디와 비밀번호를 검증한다.
  3. 인증에 성공하면 서버는 세션 저장소에 사용자 정보를 저장한다.
  4. 서버는 session_id를 생성해 클라이언트에 전달한다.
  5. 클라이언트는 session_id를 쿠키에 저장한다.
  6. 이후 요청마다 쿠키가 자동으로 함께 전송된다.
  7. 서버는 session_id로 세션 저장소를 조회해 사용자를 식별한다.

핵심은 클라이언트가 사용자 정보를 직접 들고 있는 것이 아니라, 세션을 식별할 수 있는 값만 가지고 있다는 점이다. 실제 로그인 상태와 사용자 정보는 서버에 저장된다.

1.3. 쿠키와 세션의 관계

세션 기반 인증을 이해할 때 쿠키와 세션을 같은 개념으로 혼동하기 쉽다. 하지만 둘은 역할이 다르다. 쿠키는 클라이언트, 즉 브라우저에 저장되는 작은 데이터이다. 브라우저는 특정 도메인에 대한 요청을 보낼 때 해당 도메인에 저장된 쿠키를 자동으로 함께 전송한다. 반면 세션은 서버 측에서 관리되는 사용자 상태 정보이다. 로그인한 사용자의 ID, 권한, 만료 시간 같은 정보가 세션 저장소에 저장될 수 있다. 즉, 쿠키는 session_id를 전달하기 위한 수단이고, 세션은 서버가 실제 로그인 상태를 저장하는 공간이다.

sequenceDiagram
    participant Client as 클라이언트
    participant Server as 서버
    participant SessionStore as 세션 저장소

    Note over Client: Cookie\nsession_id=abc123

    Client->>Server: 요청 (Cookie 포함)
    Server->>SessionStore: session_id=abc123 조회
    SessionStore-->>Server: user_id=1, role=USER
    Server-->>Client: 응답

이 구조에서 클라이언트는 abc123이라는 식별자만 가지고 있고, 이 값이 어떤 사용자에 해당하는지는 서버만 알고 있다.

2. 세션 기반 인증의 특징

2.1. 서버 상태 기반(Stateful)

세션 기반 인증은 서버가 사용자의 로그인 상태를 기억하는 방식이다. 이를 stateful하다고 표현한다. 예를 들어 사용자가 로그인하면 서버는 세션 저장소에 "이 사용자는 로그인된 사용자이며, user_id는 1이다"와 같은 정보를 저장한다. 이후 요청이 들어올 때마다 서버는 요청에 포함된 session_id를 확인하고, 세션 저장소에서 해당 정보를 찾아 사용자를 식별한다. 즉, 서버는 단순히 요청에 포함된 값만 보고 인증을 끝내는 것이 아니라, 자신이 관리하는 저장소를 조회해 로그인 상태를 판단한다.

이 구조는 직관적이다. 서버가 로그인 상태를 직접 알고 있기 때문에, 로그아웃 처리나 세션 만료 처리도 비교적 명확하다. 사용자가 로그아웃하면 서버는 해당 세션을 삭제하면 되고, 관리자가 특정 사용자를 강제로 로그아웃시키고 싶다면 세션 저장소에서 해당 사용자의 세션을 제거하면 된다. 하지만 서버가 상태를 가진다는 것은 그만큼 관리해야 할 데이터가 생긴다는 의미이기도 하다. 사용자가 많아질수록 세션 저장소에 저장해야 할 데이터도 많아진다. 또한 요청이 들어올 때마다 세션을 조회해야 하므로, 세션 저장소의 성능과 안정성도 중요해진다.

결국 세션 기반 인증의 핵심은 “서버가 로그인 상태의 주도권을 가진다”는 점이다. 이 점은 보안과 제어 측면에서는 장점이지만, 확장성과 운영 측면에서는 고려해야 할 요소가 된다.

2.2. 보안 측면

세션 기반 인증은 보안 측면에서 비교적 안정적인 구조를 가진다. 가장 큰 이유는 클라이언트에 민감한 정보를 직접 저장하지 않기 때문이다. 세션 기반 인증에서 브라우저가 가지고 있는 값은 보통 session_id뿐이다. 이 값은 서버의 세션 저장소에 있는 데이터를 찾기 위한 식별자 역할을 한다. 실제 사용자 ID, 권한, 로그인 상태, 기타 인증 관련 정보는 서버에 저장된다. 이 구조에서는 클라이언트가 인증 정보를 조작하기 어렵다. 예를 들어 클라이언트가 쿠키 값을 변경한다고 해도, 서버 세션 저장소에 해당 session_id가 존재하지 않으면 인증에 실패한다. 또한 서버는 세션을 언제든지 삭제하거나 만료시킬 수 있다.

예를 들어 사용자가 로그아웃하면 서버는 해당 세션을 제거한다. 이후 같은 session_id로 요청이 들어오더라도 서버 저장소에 세션이 없기 때문에 인증되지 않은 요청으로 처리된다. 이처럼 서버가 인증 상태를 직접 통제할 수 있다는 점은 큰 장점이다. 다만 세션 기반 인증이 자동으로 안전한 것은 아니다. session_id가 탈취되면 공격자가 해당 사용자인 것처럼 요청을 보낼 수 있다. 이를 세션 하이재킹(Session Hijacking)이라고 한다. 따라서 세션 기반 인증을 사용할 때는 쿠키 설정을 신중하게 해야 한다. 대표적으로 다음과 같은 설정을 사용한다.

cookie: {
  httpOnly: true,
  secure: true,
  sameSite: 'lax'
}
  • httpOnly: JavaScript에서 쿠키에 접근하지 못하도록 막는다. 이를 통해 XSS 공격으로 쿠키가 탈취될 가능성을 줄일 수 있다.
  • secure: HTTPS 환경에서만 쿠키가 전송되도록 한다. 네트워크 구간에서 쿠키가 노출되는 위험을 줄일 수 있다.
  • sameSite : 다른 사이트에서 요청을 보낼 때 쿠키가 함께 전송되는 방식을 제어한다. 이를 통해 CSRF 공격을 완화할 수 있다.

즉, 세션 기반 인증은 서버가 인증 정보를 관리한다는 점에서 안전한 구조를 만들기 쉽지만, 실제 운영에서는 쿠키 보안 설정, HTTPS 적용, 세션 만료 시간 관리 등이 함께 이루어져야 한다.

2.3. 확장성 문제

세션 기반 인증의 가장 큰 고민은 확장성이다. 서버가 한 대일 때는 구조가 단순하다. 사용자가 로그인하면 해당 서버의 메모리나 세션 저장소에 세션을 저장하고, 이후 요청도 같은 서버에서 처리하면 된다. 문제는 서버가 여러 대로 늘어나는 순간 발생한다.

예를 들어 사용자가 서버 A에서 로그인했다고 가정해보자. 서버 A에는 해당 사용자의 세션 정보가 저장되어 있다. 그런데 이후 요청이 로드 밸런서를 통해 서버 B로 전달되면 어떻게 될까? 서버 B에는 사용자의 세션 정보가 없기 때문에, 서버 B 입장에서는 인증되지 않은 사용자로 판단할 수 있다.

sequenceDiagram
    participant Client as 클라이언트
    participant ServerA as 서버 A
    participant ServerB as 서버 B

    Client->>ServerA: 로그인 요청
    ServerA->>ServerA: 세션 저장 (session_id)

    Client->>ServerB: 이후 요청 (session_id 포함)
    ServerB->>ServerB: 세션 조회 시도
    ServerB-->>Client: 인증 실패 (세션 없음)

이것이 세션 기반 인증에서 발생하는 대표적인 확장성 문제이다. 서버가 상태를 가지고 있기 때문에, 여러 서버가 동일한 세션 상태를 공유하지 않으면 인증이 불안정해진다.

이 문제를 해결하는 방법은 크게 두 가지가 있다.

첫 번째는 스티키 세션(Sticky Session)이다. 스티키 세션은 특정 사용자의 요청을 항상 같은 서버로 보내는 방식이다. 예를 들어 사용자가 처음 서버 A에 연결되었다면, 이후 요청도 계속 서버 A로 보내는 것이다. 이렇게 하면 서버 A에 저장된 세션을 계속 사용할 수 있기 때문에 인증 문제가 발생하지 않는다. 하지만 스티키 세션은 서버 간 부하 분산이 고르게 이루어지지 않을 수 있다는 단점이 있다. 특정 서버에 사용자가 몰리면 해당 서버만 부하가 커질 수 있다. 또한 서버 A가 장애로 내려가면, 서버 A에 저장된 세션도 함께 사용할 수 없게 된다.

두 번째는 외부 세션 저장소를 사용하는 방식이다. 대표적으로 Redis를 많이 사용한다. 이 방식에서는 각 서버가 자신의 메모리에 세션을 저장하지 않고, 공통된 Redis 저장소에 세션을 저장한다.

flowchart LR
    ServerA[서버 A] --> Redis[Redis 세션 저장소]
    ServerB[서버 B] --> Redis
    ServerC[서버 C] --> Redis

이렇게 하면 어떤 서버로 요청이 들어오더라도 동일한 Redis에서 세션을 조회할 수 있다. 사용자가 서버 A에서 로그인한 뒤 서버 B로 요청을 보내더라도, 서버 B는 Redis를 통해 세션을 확인할 수 있다. Redis를 사용하면 세션 공유 문제를 해결할 수 있고, 서버를 수평 확장하기 쉬워진다. 다만 Redis 자체가 중요한 인프라가 되기 때문에, Redis 장애 대응, 만료 시간 관리, 네트워크 지연 등을 함께 고려해야 한다. 결국 세션 기반 인증의 확장성 문제는 "서버가 상태를 가진다"는 구조에서 비롯된다. 이를 해결하려면 요청을 같은 서버로 고정하거나, 세션 상태를 모든 서버가 공유할 수 있는 외부 저장소로 분리해야 한다.

3. 장단점

3.1. 장점

세션 기반 인증의 가장 큰 장점은 구조가 직관적이라는 점이다. 로그인에 성공하면 서버가 세션을 만들고, 이후 요청에서는 세션을 확인한다. 인증 상태를 서버가 직접 관리하기 때문에 흐름을 이해하기 쉽고, 구현 방식도 비교적 명확하다. 예를 들어 전통적인 웹 서비스에서 사용자가 로그인한 뒤 게시글 작성, 마이페이지 조회, 장바구니 확인 등의 기능을 이용한다고 가정해보자. 서버는 매 요청마다 세션을 확인하고, 세션에 저장된 사용자 정보를 바탕으로 권한을 판단하면 된다. 이 구조는 서버 렌더링 기반 웹 서비스나 관리자 페이지처럼 서버 중심으로 동작하는 애플리케이션에서 특히 잘 어울린다.

또 다른 장점은 서버가 인증 상태를 쉽게 제어할 수 있다는 점이다. 로그아웃을 처리할 때 클라이언트에게 "앞으로 이 토큰을 사용하지 마라"고 기대하는 것이 아니라, 서버에서 세션을 삭제하면 된다. 강제 로그아웃, 세션 만료, 중복 로그인 제한 같은 기능도 비교적 구현하기 쉽다. 예를 들어 관리자가 특정 사용자의 계정을 정지시켰다면, 해당 사용자의 세션을 제거해 즉시 접근을 막을 수 있다. 또는 보안 정책상 30분 동안 활동이 없으면 자동 로그아웃되도록 세션 만료 시간을 설정할 수도 있다.

보안 측면에서도 장점이 있다. 클라이언트에는 보통 session_id만 저장되고, 실제 사용자 정보는 서버에 저장된다. 따라서 클라이언트에 노출되는 데이터가 적다. 물론 session_id 탈취 위험은 존재하지만, 적절한 쿠키 보안 설정과 HTTPS를 함께 사용하면 위험을 줄일 수 있다.

3.2. 단점

세션 기반 인증의 단점은 서버가 관리해야 할 상태가 생긴다는 점에서 출발한다. 사용자가 로그인할 때마다 서버는 세션 정보를 저장해야 하고, 요청이 들어올 때마다 세션 저장소를 조회해야 한다. 사용자가 적은 서비스에서는 큰 문제가 되지 않을 수 있다. 하지만 동시 접속자가 많아지고 트래픽이 증가하면 세션 저장소의 부하가 커질 수 있다. 특히 세션을 서버 메모리에 저장하는 경우, 서버가 재시작되면 세션이 사라질 수 있고, 서버가 여러 대일 때 세션 공유 문제가 발생한다.

예를 들어 이벤트 페이지처럼 특정 시간에 사용자가 몰리는 서비스에서 모든 요청마다 세션 저장소를 조회한다면, 세션 저장소가 병목이 될 수 있다. 이 경우 Redis 같은 빠른 인메모리 저장소를 사용하거나, 세션에 저장하는 데이터를 최소화하는 방식으로 부하를 줄일 수 있다.

또 다른 단점은 수평 확장 시 구조가 복잡해진다는 점이다. 서버가 한 대일 때는 세션을 로컬 메모리에 저장해도 동작하지만, 서버가 여러 대가 되면 요청이 어떤 서버로 갈지 알 수 없다. 이 문제를 해결하려면 스티키 세션을 사용하거나 Redis 같은 외부 세션 저장소를 도입해야 한다. 다만 이 단점은 극복할 수 있다. 실무에서는 보통 세션을 서버 메모리에 직접 저장하기보다는 Redis, Memcached, 데이터베이스 같은 외부 저장소에 저장한다. 이렇게 하면 여러 서버가 동일한 세션 저장소를 바라볼 수 있고, 서버를 늘리더라도 인증 상태를 공유할 수 있다.

물론 외부 저장소를 도입하면 운영 복잡도는 증가한다. Redis 장애가 발생했을 때 어떻게 대응할 것인지, 세션 만료 시간은 어떻게 설정할 것인지, 세션 데이터는 얼마나 저장할 것인지 등을 함께 설계해야 한다. 즉, 세션 기반 인증의 단점은 "사용하면 안 되는 이유"라기보다는 "서비스 규모가 커질수록 추가 설계가 필요한 지점"이라고 보는 것이 적절하다.

4. 사용 기준

세션 기반 인증은 서버가 중심이 되는 서비스에서 특히 적합하다. 서버가 화면 렌더링, 인증, 권한 검사, 비즈니스 로직을 대부분 담당하는 전통적인 웹 애플리케이션이라면 세션 방식은 매우 자연스럽게 어울린다. 예를 들어 관리자 페이지, 사내 시스템, 백오피스 서비스처럼 브라우저 기반으로 접근하고 서버에서 권한을 강하게 통제해야 하는 서비스에서는 세션 기반 인증이 좋은 선택이 될 수 있다. 이런 서비스는 대개 클라이언트가 여러 플랫폼으로 분산되어 있지 않고, 서버에서 사용자의 상태를 직접 제어하는 것이 중요하기 때문이다.

또한 즉시 로그아웃이나 강제 세션 만료가 중요한 서비스에도 적합하다. 예를 들어 계정이 정지되었거나 비밀번호가 변경되었을 때 기존 로그인 상태를 즉시 무효화해야 한다면, 세션 방식은 서버에서 해당 세션을 제거하는 것만으로 처리할 수 있다.

반면 서버가 여러 대로 수평 확장되는 환경, 여러 도메인이나 여러 클라이언트가 API를 공유하는 환경에서는 세션 기반 인증만으로는 관리가 복잡해질 수 있다. 모바일 앱, 외부 API, MSA 구조처럼 클라이언트와 서버가 분리되어 있고 여러 서비스가 인증 정보를 공유해야 하는 경우에는 JWT 기반 인증이나 OAuth 같은 방식이 더 적합할 수 있다.

하지만 그렇다고 세션 기반 인증이 대규모 서비스에 사용할 수 없다는 의미는 아니다. Redis 같은 중앙 세션 저장소를 사용하고, 세션 만료 정책을 잘 설계하며, 로드 밸런싱 전략을 적절히 구성하면 충분히 확장 가능한 구조를 만들 수 있다. 따라서 사용 기준은 단순히 "세션은 소규모, JWT는 대규모"처럼 나눌 수 없다. 중요한 것은 서비스의 구조와 요구사항이다.

  • 서버가 인증 상태를 직접 제어해야 하는가?
  • 즉시 로그아웃이나 강제 만료가 중요한가?
  • 브라우저 기반 서비스인가?
  • 서버가 여러 대로 확장될 가능성이 있는가?
  • 여러 클라이언트나 여러 서비스가 같은 인증 체계를 공유해야 하는가?

이 질문들에 따라 세션 기반 인증이 적합할 수도 있고, JWT 기반 인증이 더 적합할 수도 있다. 개인적으로는 서버 중심의 웹 서비스나 관리자 페이지에서는 세션 기반 인증이 여전히 좋은 선택이라고 생각한다. 구조가 단순하고, 인증 상태를 서버가 직접 제어할 수 있기 때문이다. 다만 서비스가 커질 가능성이 있다면 처음부터 Redis 같은 외부 세션 저장소를 고려해두는 것이 좋다. 결국 인증 방식 선택에서 중요한 것은 어떤 방식이 더 최신인지가 아니라, 현재 서비스의 구조와 보안 요구사항에 어떤 방식이 더 자연스럽게 맞는지라고 생각한다.

5. 예시

5.1. Express + Session

import session from 'express-session';

app.use(
  session({
    secret: 'secret-key',
    resave: false,
    saveUninitialized: false,
    cookie: {
      httpOnly: true,
      secure: true,
      sameSite: 'lax',
    },
  }),
);

로그인 처리:

app.post('/login', (req, res) => {
  const user = authenticate(req.body);

  req.session.userId = user.id;

  res.send('login success');
});

인증이 필요한 요청 처리:

app.get('/profile', (req, res) => {
  const userId = req.session.userId;

  if (!userId) {
    return res.status(401).send('unauthorized');
  }

  res.send(`user: ${userId}`);
});

위 예시에서 클라이언트는 사용자 정보를 직접 가지고 있지 않다. 서버가 세션에 userId를 저장하고, 이후 요청에서 해당 세션을 조회해 사용자를 식별한다.

5.2. Redis 세션 저장소

서버가 여러 대인 환경에서는 메모리 기반 세션 저장 방식에 한계가 있다. 이때 Redis를 세션 저장소로 사용할 수 있다.

import session from 'express-session';
import RedisStore from 'connect-redis';

app.use(
  session({
    store: new RedisStore({ client: redisClient }),
    secret: 'secret-key',
    resave: false,
    saveUninitialized: false,
  }),
);

이렇게 하면 서버 A에서 생성한 세션을 서버 B에서도 조회할 수 있다. 따라서 로드 밸런서를 통해 요청이 어떤 서버로 전달되더라도 동일한 로그인 상태를 유지할 수 있다.

마치며

세션 기반 인증은 서버가 사용자의 로그인 상태를 직접 저장하고 관리하는 방식이다. 클라이언트는 세션을 식별할 수 있는 session_id만 가지고 있고, 실제 인증 상태는 서버에 저장된다. 이 방식은 구조가 직관적이고, 서버가 인증 상태를 강하게 제어할 수 있다는 장점이 있다. 로그아웃, 강제 만료, 세션 삭제 같은 처리가 명확하며, 보안 정책을 서버 중심으로 설계하기 쉽다. 반면 서버가 상태를 관리해야 하므로 확장성 측면에서는 추가적인 고민이 필요하다. 서버가 여러 대가 되면 세션 공유 문제가 발생할 수 있고, 이를 해결하기 위해 스티키 세션이나 Redis 같은 외부 세션 저장소를 고려해야 한다. 결국 세션 기반 인증은 단순히 오래된 방식이 아니라, 여전히 많은 환경에서 적절하고 안정적인 인증 방식이다. 중요한 것은 세션 방식 자체의 장단점을 이해하고, 현재 서비스 구조에 맞게 사용하는 것이다.