📌 목차
2023년 6월 27일 JWT에 대한 글을 쓴 과거 Velog 포스팅을 보았다. 실무 경험도 없고, 이론적으로 이해가 되지도 않았던 때이지만 어떻게든 적어보며 익숙해질 때가 있지 않을까 하며 적은 당시가 생각났다. 다시 JWT에 대해 적어보면 얼마나 달라졌는지 비교할 수 있을 것 같았다. JWT를 접한 사람, 접하지 못한 사람 전부 누구나 이해할 수 있도록 쉽게 적어보려 한다.
JWT JSON Web Token
웹에서 사용되는 JSON 형식의 토큰 기반 인증 방식이다. 클라이언트와 서버 간 JSON 데이터를 안전하게 공유하기 위한 개방형 표준(RFC 7519). 간결하고 URL에 안전하게 사용할 수 있다는 장점이 있다.
Session-Based Authoentication VS Token-Based Authentication
세션 기반 인증은 서버 측에서 사용자의 세션 정보를 저장하고, 클라이언트에게는 해당 세션을 식별할 수 있는 고유한 세션 ID를 제공하는 방식이다. 클라이언트로부터 요청을 받으면 클라이언트의 상태 정보를 저장하여 유지해야 하므로 Stateful 한 구조이고 서버에서 인증 정보를 관리하기 때문에 보안상 훨씬 안정적이다.
✔️ 세션 관리가 용이하여 간단히 세션을 제거하여 로그아웃 처리가 가능하다.
카카오톡 디바이스 관리 - 사용자 주도
✔️ 중앙 집중식 세션 관리로 활동 추적과 제어가 편리하다.
OTT 동시접속 관리 - 서비스 제공자 주도
✔️ 사용자가 증가하면 과부하를 줄 수 있어 확장성에 용이하지 못하다.
✔️ 세션 상태 관리를 위해 항상 서버가 필요하다.
토큰 기반 인증은 서버에 요청을 할 때 HTTP 헤더에 토큰을 함께 보내 인증받은 사용자(유효성 검사)인지 확인한다. 서버 기반 인증 시스템과 달리 사용자의 인증 정보를 서버에 저장하지 않고 클라이언트의 요청으로만 인가를 처리해서 Stateless 한 구조를 가진다.
✔️ 추가 저장소가 필요 없다.
✔️ 확장성이 뛰어나다.
✔️ *SSO(Single-Sign-On)에 활용된다.
✔️ 이미 발급된 토큰을 만료되기 전에 강제로 무효화하기 어렵다.
✔️ 토큰에 담긴 정보는 만료되기 전까지 업데이트할 수 없다.
✔️ 포함된 정보량에 따라 크기가 커질 수 있어 네트워크 부하가 증가할 수 있다.
*SSO(Single-Sign-On)란?
한 번(Single)의 로그인 인증(Sign-On)으로 여러 개 서비스를 추가적인 인증 없이 사용할 수 있는 기술. 인증은 하나의 시스템(인증 서버)에서 수행하고, 그 인증 서버가 서비스를 담당하는 각 서버에 인증 정보를 알려주는 방식이다.
보안에 취약한 JWT? 🧐
HTTP 헤더에 토큰을 저장할 경우 XSS 공격에 취약해진다. 토큰 탈취 위험이 생기는 것인데, 이는 아래에 추가 설명이 나올 텐데, Access Token을 짧게 설정하고 Refresh Token을 HttpOnly 쿠키에 저장하는 방식으로 방어한다.
또한 이미 발급된 토큰을 무효화하기 어려운 점은 DB에 Refresh Token을 저장하고, 로그아웃시 Refresh Token을 삭제해 버리는 방식으로 방어할 수도 있다.
JWT의 구성
Header, Payload, Signature 총 세 가지 섹션으로 구성되어 있다. 각 섹션은 URL에서 파라미터로 사용할 수 있도록 URL_safe 한 Base64로 인코딩 된 문자열이고 마침표 '.'로 구분되어 있다.

Header
토큰의 타입(typ)과 서명 생성에 사용된 알고리즘 방식(alg)으로 구성되어 있다.
Payload
전송하고자 하는 여러 데이터가 담긴다. 정보의 한 조각을 클레임 Claim이라 부르고, 이는 name/value의 한 쌍으로 이루어져 있다.
클레임에는 3가지 종류가 있다.
1️⃣ 등록된 클레임 Registered Claim
jwt 사양에서 미리 정의된 표준 클레임. 선택적으로 사용할 수 있다.
- iss (Issuer): 토큰 발급자
- sub (Subject): 토큰의 주제(사용자 식별자 같은 정보)
- aud (Audience): 토큰의 대상(어떤 시스템이나 사용자에게 유효한지)
- exp (Expiration Time): 토큰 만료 시간
- iat (Issued At): 토큰 발급 시간
- nbf (Not Before): 특정 시간 이전에는 토큰이 유효하지 않음을 나타냄
{
"iss": "example.com", // 발급자 (Issuer)
"sub": "1234567890", // 사용자 ID (Subject)
"aud": "myapp", // 대상 (Audience)
"exp": 1717041600, // 만료 시간 (Unix 타임스탬프)
"iat": 1717038000, // 발급 시간 (Unix 타임스탬프)
"nbf": 1717037000 // 유효 시작 시간 (Unix 타임스탬프)
}
exp가 만료되면 어떻게 되나? 🧐
사용자가 로그인을 하면 서버에서 처음에 Access Token과 Refresh Token 두 개를 발급해 준다. 일반적으로는 클라이언트는 요청시 Access Token을 헤더에 포함해서 요청을 보낸다. 하지만 exp 시간이 지났을 경우 처음 발급받은 Refresh Token을 사용해서 새로운 Access Token을 요청하고, 서버에선 Access Token을 새로 발급해준다.
그래서 보통 Refresh Token은 보안을 위해 Local Storage가 아닌 HttpOnly 쿠키에 저장한다.
2️⃣ 공개 클레임 Public Claim
사용자 정의 클레임으로 충돌이 방지된 이름을 가져야 하며 이를 위해 URI 형식으로 짓는다.
{
"iss": "example.com",
"sub": "1234567890",
"https://yourapp.com/permissions": [ // URI로 정의된 클레임 (권장 방식)
"read",
"write",
"delete"
],
"https://yourapp.com/features": [ // 애플리케이션의 기능 정보
"featureA",
"featureB"
]
}
3️⃣ 비공개 클레임 Private Claim
사용자 정의 클레임으로 클라이언트와 서버가 협의하에 임의로 지정한 정보를 저장해서 사용한다.
{
"iss": "example.com",
"sub": "1234567890",
"userId": "U-987654321", // 비공개 클레임 - 사용자 ID
"organizationId": "O-12345", // 비공개 클레임 - 조직 ID
"isBetaUser": true // 비공개 클레임 - 베타 사용자 여부
}
Signature
Base62로 인코딩 된 Header, Payload와 서버만이 가지고 있는 Secret key를 설정한 알고리즘으로 암호화한 값이 담긴다.
HS256(base64UrlEncode(header) + “.” + base64UrlEncode(payload), Secret key)
JWT를 통한 인증 Authentication & 인가 Authorization
NestJS에서의 JWT 검증을 중심으로 알아보기
인증 사용자의 신원을 검증하는 프로세스
가장 간단한 예시로는 ID와 PW를 통해 로그인하는 행위

JWT의 경우 클라이언트가 서버에 첫 로그인 인증을 보내면, 서버에서 응답으로 Access Token과 Refresh Token을 담아서 보낸다.
이후 서버에서 응답한 Token을 클라이언트에 저장한다.
인가
인증 이후의 프로세스
인증된 사용자가 어떤 자원에 접근할 수 있는지를 확인하는 절차

JWT의 경우 필요시 서버로 Access Token을 통해 요청한다. 서버에서 토큰을 검증하고 사용자 인가를 처리한다.
NestJS에서 JWT 기반 인증을 구현할 때엔 JwtGuard와 JwtStrategy를 사용한다. 이 둘은 JWT 검증 과정을 자동으로 수행해 주어 매우 편리하게 적용할 수 있다. 둘의 역할에 대해 좀 더 자세히 알아보자.
JwtGuard는 사용자의 역할이나 권한을 체크하여 접근을 제어하는 역할을 한다. JwtGuard를 통해 JwtStrategy가 실행되니 상위 개념이라 볼 수 있다. 보호된 API에서 JwtStrategy에서 검증된 req.user 정보를 활용하여 인가 로직을 수행한다.
특정 API에서만 JWT 보호를 적용하려면 컨트롤러에 @UserGuards(JwtAuthGuard)를 사용한다.
// cats.controller.ts
@Controller('cats')
@UseInterceptors(SuccessInterceptor)
@UseFilters(HttpExceptionFilter)
export class CatsController {
constructor(
private readonly catsService: CatsService,
private readonly authService: AuthService,
) {}
@ApiOperation({ summary: '현재 고양이 조회' })
@UseGuards(JwtAuthGuard) // JwtGuard 사용
@Get()
getCurrentCat(@CurrentUser() cat) {
return cat.readOnlyData;
}
// jwt.guard.ts
@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {}
JwtStrategy는 passport-jwt 라이브러리를 활용하여 JWT를 해석하고 검증한다.
Header에서 Token 추출 ➡️ Secret key를 통한 Signature 검증 ➡️ Token exp 여부 확인 ➡️ Payload에서 사용자 정보 추출 후 req.user에 저장
// jwt.strategy.ts
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor(private readonly catsRepository: CatsRepository) {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), // Authorization 헤더에서 JWT 추출
secretOrKey: process.env.JWT_SECRET, // JWT 서명 검증을 위한 Secret Key
ignoreExpiration: false, // 만료된 토큰 거부
});
}
async validate(payload: Payload) { // JWT Payload 검증 - 사용자 정보 확인
const cat = await this.catsRepository.findCatByIdWithoutPassword(
payload.sub,
);
if (cat) {
return cat; // 검증된 사용자 정보를 req.user에 저장
} else {
throw new UnauthorizedException('접근 권한이 없습니다.');
}
}
}
이 과정을 거친 후 JwtGuard가 최종으로 허가 후 해당 컨트롤러에서 API response를 한다.
실제로 코드로 구현해 보니 Nest에서 제공하는 라이브러리들로 인해 간편하게 적용할 수 있었다. 특히 도식화에 힘을 많이 들였는데 프로세스를 쉽게 이해할 수 있게 보여주고 싶었다. 이전엔 jwt 내부 구현과 자세한 동작 방식에 대해선 설명하지 못했어서 아쉬웠는데 이번 기회에 좀 더 자세히 보여줄 수 있어서 기쁘다. 인증과 인가에 대해서도 개념만 이해했었는데 실제로 어떤 방식으로 실무에 적용할 수 있는지 보여줄 수 있었던 것 같다.
세션과 JWT의 차이: 시니어도 틀리는 시스템 디자인 질문 TOP5
대부분의 OAuth 2.0서비스에서 JWT를 사용하는 이유
JWT(Json Web Toekn)란 무엇일까? (서버 기반 인증 / 토큰 기반 인증)
JSON Web Token (JWT) : 토큰 기반 인증 시스템 구축
[인증/인가] 쿠키 VS 세션 VS 토큰 (JWT) 방식 중 무엇을 사용할까?
[Nest.js] Guard와 JWT를 이용한 인증, 인가 구현하기
[JWT] JSON WEB TOKEN 개념 / 구성 요소 / 동작 방식 / 장단점 / Session과 차이점 / 저장 위치
Access Token & Refresh Token 원리
ChatGPT
'JavaScript > Node.js' 카테고리의 다른 글
| 데이터 타입 걱정을 덜어주는 Nest.js ValidationPipe의 3가지 옵션 코드로 들여다보기 (3) | 2024.12.22 |
|---|---|
| Node.js는 싱글 스레드일까? 멀티 스레드일까? Worker thread에 대하여. (6) | 2024.10.27 |