Education/신한투자증권 프로 디지털 아카데미

JWT, Session, OAuth 인증 이해하기

마이캣호두 2025. 6. 21. 17:22
반응형

신한투자증권 프로 디지털 아카데미 과정

 

10. Cookie & Session & 인증

1. Authentication

 

1) 인증에 대한 전체 흐름 이해하기

서버가 인증 토큰을 발급하고

클라이언트가 request 마다 HTTP Message 로 토큰을 보내고

서버가 이 토큰이 자신이 발급한 토큰이 맞는지 확인하는 것

 

JWT 토큰방식

  • 클라이언트와 서버 간 인증 정보를 JSON 형식으로 안전하게 주고받기 위한 서명된 토큰
  • 서버가 세션을 저장하지 않음(Stateless), 클라이언트에 저장됨, 주로 Access Token 으로 사용됨

Access Token & Refresh Token

  • 보안을 이유로 유효기간이 짧으며 만료가 되면 Refresh Token 을 요청함, Access Token 이 탈취될 것을 대비하여 만들어짐
  • 로그인 → Access + Refresh 발급 → Access로 요청 → 만료 → Refresh로 재발급

OAuth 

  • 제3자(서비스)가 사용자 인증을 대신하도록 허용하는 권한 위임 프로토콜, 구글 로그인, 카카오 로그인과 같은 소셜 로그인
  • 사용자 → 인증 서버에 로그인 → 토큰 발급 → 클라이언트가 토큰으로 API 접근

 

클라이언트의 저장소 = Cookie

서버의 저장소 = Session

 

 

2. Cookie

 

1) What is Cookie

쿠키란 하이퍼 텍스트의 기록서(HTTP)의 일종으로서, 사용자가 어떠한 웹사이트를 방문할 경우 그 사이트가 사용하고 있는 서버를 통해 사용자의 컴퓨터에 설치되는 작은 기록 정보 파일

브라우저는 매 요청마다 쿠키에 있는 모든 값들을 요청에 담아 보내게 되고, 서버는 요청 메세지에 있는 쿠키를 꺼내서 확인할 수 있다

이렇게 개발자 도구에서 확인해볼 수 있다!

 

Secure cookie

  • 쿠키에 secure 속성을 설정하면 SSL연결(Https)을 통해서만 전송가능함, 중간자 공격으로 인한 쿠키 탈취 방지
  • 쿠키는 클라이언트(사용자 컴퓨터)에 저장되므로, 민감한 정보(비밀번호 등)는 절대 저장하지 말아야 함
  • 저장된 쿠키는 클라이언트가 쉽게 접근 가능 → 보안상 민감한 정보 저장에 부적절

 

세션

  • 세션 데이터는 서버에 저장되고 클라이언트에는 세션 아이디만 쿠키로 전달됨
  • 클라이언트에 민감한 정보 노출 없음
  • 서버에서 데이터를 관리하므로 보안성이 높음
  • but 서버에 상태(state)를 유지해야 하므로, 대규모 트래픽 환경에서는 서버 자원 부담 증가

 

 

3. Cookie & Session

 

1) Express.js 에서의 Session

$ npm install express-session

Express 는 자체적으로 Session 을 지원하지 않기 때문에 라이브러리를 설치해 사용할 예정이다!

 

Express에서 세션을 사용하면, 서버는 클라이언트에게 응답할 때 Set-Cookie라는 헤더를 통해 connect.sid라는 이름의 쿠키를 함께 보낸다. 이렇게 브라우저가 매번 요청에 connect.sid를 포함해 보내면, Express 서버에서는 이 값을 기반으로 세션 스토어에 저장된 데이터를 조회한다. 즉, 서버는 요청마다 req.session 객체를 꺼내 사용할 수 있으며, 여기에 로그인 정보나 사용자 ID(req.session.userId) 같은 값을 저장하거나 읽을 수 있습니다.

결과적으로 connect.sid 쿠키를 통해 클라이언트를 식별하고, 서버에서는 해당 클라이언트의 세션 데이터를 유지하면서 권한 검증 등의 작업을 할 수 있게 되는 것이다. 이 과정을 크롬 개발자 도구의 ‘Application → Cookies’ 탭에서 확인하면, 실제로 connect.sid가 저장되어 있는 것을 확인할 수 있어 세션 미들웨어가 정상적으로 동작하고 있음을 알 수 있다.

 

실습)

const express = require("express");
const Board = require("../models/Board");
const router = express.Router();

router.get("/:boardId", async (req, res) => {
    console.log(req.params);
    const { boardId } = req.params;
    const board = await Board.findById(boardId);
    
    if (req.session.boardPath) {
        req.session.boardPath.push(board.title);

        if (req.session.boardPath.length > 10) {
            req.session.boardPath.shift();
        }
    } else {
        req.session.boardPath = [board.title];
    }
    console.log(req.session.boardPath);
    res.json(board);
});

module.exports = router;

 

 

실습 심화)

// app.js 에 아래 코드 추가
app.use(
  session({
    secret: process.env.SESSION_SECRET || "<my-secret>",
    resave: true,
    saveUninitialized: true,
    cookie: {
      httpOnly: true,
      secure: false,
    },
  })
);

app.use((req, res, next) => {
  console.log("미들웨어 실행");
  if (req.session.urlHistory) {
    req.session.urlHistory.push(req.url);
  } else {
    req.session.urlHistory = [req.url];
  }
  console.log(req.session.urlHistory);

  // next(): 다음 미들웨어 실행
  next();
});

방문 순서를 트래킹할 떄의 핵심은 미들 웨어를 구성하는 것이다. 이 때, 반드시 세션 미들웨어가 먼저 등록되어 있어야 한다. Express에서는 app.use(...)로 등록된 미들웨어가 순차적으로 실행되는데, req.session 객체는 session() 미들웨어가 먼저 실행되어야만 생성된다. 만약 이 미들웨어보다 뒤에 req.session을 쓰는 코드가 있으면, undefined 에러가 발생하거나 세션이 제대로 작동하지 않는다.

참고로, 나는 req.url 을 사용했지만 req.path 를 사용할 수도 있는데 두 개의 차이점은 url 은 쿼리를 포함한 전체 요청 경로를 포함한다는 것이고, path 는 쿼리를 제외한 순수한 경로만을 반환한다는 것이다. 필요에 따라 선택적으로 사용하면 될 것 같다!

 

 

2) Express.js에서의 인증 구현하기

JWT :  RESTFul 서비스에서 사용자의 인증 정보를 JSON 형식으로 담아 안전하게 전달하기 위한 토큰 방식

  • 토큰은 Header.Payload.Signature 구조
  • 사용자가 로그인하면 서버는 JWT를 생성해 클라이언트에 전달하고, 클라이언트는 이를 로컬에 저장한 뒤 이후 요청마다 Authorization 헤더를 통해 전송한다. 서버는 이 토큰의 서명을 검증해 사용자를 식별한다.
  • JWT는 서버가 상태를 기억할 필요 없는 stateless 구조이기 때문에 확장성과 성능 면에서 유리하며, 마이크로서비스나 API 서버 간 인증에도 적합하다.
  • JWT는 유효 기간 동안 탈취되면 누구나 사용할 수 있기 때문에, Refresh Token과 함께 사용하는 것이 안전하며, 민감한 정보는 Payload에 담지 않아야 한다.

 

4. Authentication

 

1) JWT 생성 방식

각 부분은 Base64로 인코딩되어 .으로 구분된다. Header에는 토큰 유형과 서명 알고리즘이, Payload에는 사용자 ID나 권한 같은 인증 정보가 담기고, Signature는 서버의 비밀키로 서명되어 위조 방지를 담당한다.

 

Q. 서버는 자신이 발급한 토큰이 맞는지 어떻게 확인하는가?

  • 헤더, 페이로드, 서버가 가지고 있는 시크릿을 해싱하면 난수값이 나오는데, 이게 시그니처와 일치하면 자신이 발급한 토큰이라는 것을 확인할 수 있다 - 그래서 stateless 임에도 알 수 있는 것!
  • 하지만 state 가 없기 때문에 내가 어떤 토큰을 발급했는지에 대한 정보는 알 수 없고 만료 여부 정도만 확인할 수 있음 = 토큰이 탈취당해도 임의로 만료시킬 수 없으며 토큰을 받기 전까지는 내가 발급했는지의 여부도 알 수 없음

 

✨ 추가 정리

인증 상태 유지 방식에 JWT, Session 방식이 있고, 인증/권한 위임 프로토콜이 OAuth 방식인 것!

JWT 는 토큰 기반 인증 방식으로 클라이언트 중심, Session 은 상태 저장 방식으로 서버 중심이다.

  • JWT
    • 서버가 로그인 정보를 세션에 저장하고, 클라이언트는 세션 ID만 쿠키에 저장함. 이후 요청마다 쿠키가 자동 전송되어 인증됨
    • 사용자가 로그인하면 서버는 connect.sid=abc123 같은 세션 ID를 쿠키로 설정함 → 브라우저가 이 쿠키를 매 요청마다 자동으로 보내므로 서버가 세션을 조회해 인증 상태 유지
  • Session
    • 서버가 로그인 시 토큰(JWT)을 만들어 클라이언트에 전달. 클라이언트는 이 토큰을 저장해두고, 요청할 때마다 HTTP 헤더에 담아 보냄. 서버는 토큰 서명을 검증해 사용자 식별
    • 로그인 후 서버가 JWT를 발급 → 클라이언트가 Authorization: Bearer <JWT> 형식으로 매 요청마다 포함 → 서버는 해당 토큰이 유효한지 서명 검증
  • OAuth
    • 사용자가 다른 서비스(카카오, 구글 등)에서 로그인하면, 우리 서버는 그 인증 결과를 받아 인증 처리. 직접 로그인 처리를 하지 않아도 됨
    • "카카오 로그인" 버튼 클릭 → 카카오 로그인 페이지에서 인증 → 우리 서버는 카카오로부터 사용자 정보(이메일 등)를 전달받아 자체 로그인 처리 또는 회원 가입 연동

 

2) Express.js에서 인증 구현하기

npm install bcrypt validator jsonwebtoken

라이브러리 설치

 

user Schema 정의 - models/User.js

 

token 생성 및 검증 정의하기 - utils/auth.js

 

router 생성 - routes/users.js

 

authenticate 미들웨어 생성 & 인증 유저 사용 - routes/user.js

 

반응형