시나리오 1: 정상적인 인증 흐름 (Happy Path)
사용자가 회원가입 후 로그인하여 발급받은 accessToken으로 보호된 API에 성공적으로 접근하는 가장 기본적인 흐름입니다.
실행 간단 흐름도
- 클라이언트 → 서버 (회원가입):
POST /users/register - 클라이언트 → 서버 (로그인):
POST /users/login→accessToken,refreshToken수신 - 클라이언트 → 서버 (장바구니 추가):
POST /carts(헤더에accessToken포함)- 미들웨어 (
authenticateJWT): 토큰 검증 성공 →req.user에 사용자 정보 저장 후next() - Controller:
req.user.id를 사용하여 장바구니 추가 로직 실행 - Service & Repository: DB에 장바구니 데이터 삽입
- Controller:
201 Created응답 반환
- 미들웨어 (
1단계: 사용자 회원가입 및 로그인 (Controller & Service)
사용자가 email과 password를 제공하여 회원가입하고, 동일한 정보로 로그인하여 accessToken과 refreshToken을 발급받습니다.
// [1-1] 회원가입 요청
POST /users/register
Content-Type: application/json
{
"email": "testuser@example.com",
"password": "password123"
}
// [1-2] 로그인 요청
POST /users/login
Content-Type: application/json
{
"email": "testuser@example.com",
"password": "password123"
}
- 예상 결과 (1-1):
201 Created/{"message": "회원가입이 완료되었습니다."}

- 예상 결과 (1-2):
200 OK/{"accessToken": "...", "refreshToken": "..."}

2단계: 보호된 API 접근 (Middleware & Controller)
발급받은 accessToken을 Authorization 헤더에 담아 장바구니 추가 API를 호출합니다.
// [1-3] 장바구니 추가 요청
POST /carts
Authorization: Bearer <1-2에서 받은 accessToken>
Content-Type: application/json
{
"book_id": 1,
"quantity": 1
}
authenticateJWT미들웨어는 토큰을 성공적으로 검증하고,req.user에 디코딩된 사용자 정보를 저장합니다.cart.controller.js의addToCart함수는req.user.id를 참조하여 어떤 사용자의 요청인지 식별합니다.
3단계: 비즈니스 로직 처리 및 응답 (Service & Repository)
cartService와 cartRepository는 전달받은 userId와 book_id를 사용하여 데이터베이스에 장바구니 항목을 추가(UPSERT)합니다.
// cart.repository.js
exports.upsertCartItem = ({ userId, book_id, quantity }) => {
const sql = `
INSERT INTO carts (user_id, book_id, quantity) VALUES (?, ?, ?)
ON DUPLICATE KEY UPDATE quantity = quantity + VALUES(quantity)`;
return dbPool.query(sql, [userId, book_id, quantity, quantity]);
};
4단계: API 최종 결과
- 예상 결과 (1-3):
201 Created/{"message": "장바구니에 상품을 담았습니다."}


시나리오 2: 만료된 Access Token으로 API 접근
토큰이 만료되었을 때 서버가 어떻게 반응하는지 검증하는 흐름입니다.
실행 간단 흐름도
- 클라이언트 → 서버 (로그인):
POST /users/login→ 만료 시간이 짧은accessToken수신 - (시간 경과):
accessToken만료 - 클라이언트 → 서버 (보호된 API 요청):
GET /carts(헤더에 만료된accessToken포함)- 미들웨어 (
authenticateJWT):jwt.verify()실행 시TokenExpiredError발생 catch블록 실행:CustomError생성 후next(err)호출- 에러 핸들러:
403 Forbidden상태 코드와 에러 메시지 응답
- 미들웨어 (
1단계: 만료된 토큰 생성 및 API 요청
사전 준비:
toker.utils.js의generateAccessToken함수에서expiresIn값을'1s'로 변경합니다.

// [4-1 & 4-2] 로그인 후 1초 이상 대기
POST /users/login
...
// [4-3] 만료된 토큰으로 장바구니 조회 요청
GET /carts
Authorization: Bearer <만료된 accessToken>

2단계: 에러 처리 (Middleware)
authenticateJWT 미들웨어의 try-catch 구문이 핵심적인 역할을 합니다. jwt.verify()가 TokenExpiredError를 던지면 catch 블록이 이를 잡아냅니다.
// authorize.middleware.js
try {
// jwt.verify()에서 TokenExpiredError 발생
const decoded = jwt.verify(token, process.env.ACCESS_SECRET_KEY);
// ...
} catch (error) {
// error는 TokenExpiredError 객체
return next(
new CustomError(FORBIDDEN.statusCode, "Invalid or expired token.")
);
}
3단계: API 최종 결과
- 예상 결과 (4-3):
403 Forbidden/{"message": "Invalid or expired token."}

결론
authenticateJWT는 토큰의 유무와 유효성을 엄격하게 검사하여, 인증 실패 시 에러 핸들링 플로우를 통해 일관된 실패 응답을 보장하는 '게이트키퍼' 역할을 한다.- 계층형 아키텍처와 중앙 집중식 에러 핸들링 덕분에, 인증 로직이 비즈니스 로직과 명확히 분리되어 유지보수가 용이하다.
'Programmers' 카테고리의 다른 글
| [37일차]프론트엔드 커리큘럼의 시작, JS 기초 (0) | 2025.11.02 |
|---|---|
| [36일차]Book Market API 인증 모듈 및 주요 기능 분석 (0) | 2025.10.30 |
| [35일차]Node.js 에러 핸들링: 기본부터 실전 프로젝트 적용까지 (0) | 2025.10.29 |
| [34일차]SQL 데이터 관리와 Node.js에서의 효율적인 사용법 (0) | 2025.10.28 |
| [33일차]Node.js의 논블로킹 I/O와 비동기 처리 방식 (1) | 2025.10.27 |