[CS] 로그인 인증 방식 - 세션기반 인증 & 토큰기반 인증

팔랑이·2025년 1월 21일

CS

목록 보기
12/19

⛳️ 인프런 - cs 지식의 정석 강의를 듣고 학습한 내용입니다.


개요

HTTP는 상태를 유지하지 않는(stateless) 프로토콜이다. 즉, 요청이 끝나면 서버는 클라이언트에 대한 정보를 유지하지 않는다.

하지만 로그인 상태는 사용자 경험에서 중요하기 때문에 웹에서 사용자의 인증 상태를 유지해야 하는데,

그 방법으로 크게 세션기반 인증토큰기반 인증이 있다.

1. 세션기반 인증 방식

개념

  • 세션: 서버와 클라이언트 간 연결이 활성화된 상태
  • 세션 ID: 이를 식별하기 위한 고유 값으로, 웹 서버 또는 DB에 저장되는 유니크한 ID

로그인 프로세스

  1. 사용자가 로그인 요청을 하면 서버에서 고유한 세션 ID를 생성
  2. 생성된 세션 ID를 쿠키에 담아 클라이언트로 전달
  3. 이후 클라이언트가 요청을 보낼 때, 쿠키에 담긴 세션 ID를 서버로 전송하여 사용자 상태를 확인하고 로그인을 유지할 수 있다.

단점

  1. 서버 자원의 부담 증가: 로그인한 사용자의 상태 데이터를 서버에 저장해야 하므로, 유저 수가 증가할수록 서버 메모리 사용량이 증가한다.
  2. 오버헤드 발생: RDBMS를 사용하는 경우, 세션 데이터를 직렬화/역직렬화하면서 성능 저하가 발생할 수 있다.

세션기반 인증 방식 구현 (Node.js)

필수 라이브러리 설치

npm install escape-html express express-session

Node.js 코드

const escapeHtml = require('escape-html'); // 입력값을 안전하게 변환
const express = require('express');
const session = require('express-session');

const app = express();

// 세션 설정
app.use(session({
  name: 'session-id',
  secret: '9e8821c8ef4ab43ba09310af54e98caedc13e314efdea720bf513b9b3675faf4', // 세션 암호화 키
  resave: false,
  saveUninitialized: false,
}));

// 인증 미들웨어
const isAuthenticated = (req, res, next) => {
  if (req.session.user) next(); // 세션이 있다면 다음 단계로
  else next('route'); // 세션이 없으면 로그인 페이지로 이동
};

// 로그인 상태 확인
app.get('/', isAuthenticated, (req, res) => {
  res.send(`${escapeHtml(req.session.user)}님 환영합니다!`);
});

app.get('/', (req, res) => {
  res.send(`
    <p>로그인</p>
    <form action="/login" method="post">
      Username: <input name="user"><br>
      Password: <input name="pass" type="password"><br>
      <input type="submit" value="Login">
    </form>
  `);
});

// 로그인 요청 처리
app.post('/login', express.urlencoded({ extended: false }), (req, res) => {
  if (req.body.user === 'kundol' && req.body.pass === '1234') {
    req.session.regenerate((err) => {
      if (err) return next(err);

      req.session.user = req.body.user; // 세션에 사용자 정보 저장
      req.session.save((err) => {
        if (err) return next(err);
        res.redirect('/');
      });
    });
  } else {
    res.redirect('/');
  }
});

// 서버 실행
app.listen(3000, () => {
  console.log('Server running at http://localhost:3000');
});

실행 결과

  1. 로그인 시, 서버에서 세션 ID 생성 → 클라이언트의 쿠키에 저장
  2. 이후 클라이언트가 요청 시, 쿠키를 통해 로그인 상태 유지

2. 토큰기반 인증 방식

개념

  • 토큰기반 인증은 모든 상태를 토큰에 포함하여 서버가 상태를 유지하지 않는 방식이다.
  • 주로 JWT (JSON Web Token)이 사용된다.
  • 인증 관련 로직과 일반 컨텐츠 제공 로직을 분리하여 stateless 서버 구현이 가능하다.

JWT 구조

  1. Header:
    • 토큰의 유형과 서명 알고리즘 정보
    • Base64로 인코딩
  2. Payload:
    • 사용자 정보, 토큰 유효 기간 등 데이터를 포함함
    • Base64로 인코딩
  3. Signature:
    • 헤더와 페이로드를 비밀키로 암호화한 값

3. 장점

  1. 서버에서 인증 상태를 저장하지 않아도 되므로 확장성이 높다.
  2. 토큰 자체에 필요한 정보를 모두 포함하여 별도의 인증 저장소가 필요 없음.
  3. 디코딩하면 JSON 형식이므로, 직렬화/역직렬화가 간단하다.

4. 단점

  1. 토큰 탈취:
    • 토큰이 탈취되면 내부 데이터를 노출할 위험이 있다. 변환이 쉬운만큼...
  2. 토큰 크기 문제:
    • 토큰 크기가 클 경우 서버 자원 사용량이 증대된다.

토큰기반 인증 방식 구현 (Node.js)

필수 라이브러리 설치

npm install express cookie-parser jsonwebtoken cors

Node.js 코드

const express = require('express');
const jwt = require('jsonwebtoken');
const cookieParser = require('cookie-parser');
const cors = require('cors');

const app = express();
const PORT = 3000;

// 비밀키
const ACCESS_TOKEN_SECRET = 'access_secret_key';
const REFRESH_TOKEN_SECRET = 'refresh_secret_key';

// 사용자 데이터 (가상)
const user = {
  username: 'kundol',
  email: 'kundol@gmail.com',
};

// 옵션
const accessOpt = { expiresIn: '10m' };
const refreshOpt = { expiresIn: '1d' };

// 미들웨어 설정
app.use(cors());
app.use(express.json());
app.use(cookieParser());

// 로그인 엔드포인트
app.post('/login', (req, res) => {
  const accessToken = jwt.sign(user, ACCESS_TOKEN_SECRET, accessOpt);
  const refreshToken = jwt.sign(user, REFRESH_TOKEN_SECRET, refreshOpt);

  res.cookie('jwt', refreshToken, { httpOnly: true, secure: true });
  res.json({ accessToken });
});

// 토큰 갱신
app.post('/refresh', (req, res) => {
  const refreshToken = req.cookies.jwt;
  if (!refreshToken) return res.status(401).json({ message: 'Unauthorized' });

  jwt.verify(refreshToken, REFRESH_TOKEN_SECRET, (err, user) => {
    if (err) return res.status(403).json({ message: 'Forbidden' });

    const newAccessToken = jwt.sign(user, ACCESS_TOKEN_SECRET, accessOpt);
    res.json({ accessToken: newAccessToken });
  });
});

// 서버 실행
app.listen(PORT, () => {
  console.log(`Server running at http://localhost:${PORT}`);
});

세션기반 인증 vs 토큰기반 인증

구분세션기반 인증토큰기반 인증
상태 유지 방식서버에서 세션 데이터를 저장토큰 자체에 인증 정보를 포함
확장성서버 메모리나 DB에 종속, 확장성 낮음Stateless 방식, 확장성 높음
보안서버에서 데이터를 관리하므로 비교적 안전토큰 탈취 시 보안 위협
대표 구현 방식세션 ID → 쿠키로 저장JWT를 활용

참고

profile
정체되지 않는 성장

0개의 댓글