- 종종 개발 삽질기를 보내드리고 있습니다. 간단하지만 재미있는 개발 삽질기를 받아보고 싶으시다면, 호박너구리 개발일지 뉴스레터를 구독해 주세요:)
api를 구현할 때 토큰을 자주 활용하게 됩니다.
현재 요청을 보낸 유저가 누구인지에 따라 보내줘야할 정보가 달라지기 때문인데요.
만약 자기의 정보를 요청하는 것이라면 정보를 보내줘도 되지만, 다른 유저의 정보를 요청하는 api 호출이라면 제한된 정보만 보내줘야 할 수도 있습니다.
이렇듯 api 구현에 있어서 토큰 검증은 자주 사용되는데, 매번 똑같은 코드를 작성할 수는 없죠!
오늘은 토큰 검증을 위한 미들웨어 추가 방법에 대해 다루어보겠습니다:)
Express에서 미들웨어 구현은 어렵지 않습니다. (관련 문서)
다음과 같이 app.use에 달아주면 모든 api 요청에 대해 동작을 수행하죠!
const express = require('express');
const app = express();
const myLogger = function (req, res, next) {
console.log('LOGGED');
next();
};
// 여기서는 logger가 middleware입니다.
// 모든 요청에 대해 로깅을 할 수 있죠
app.use(myLogger);
app.get('/', function (req, res) {
res.send('Hello World!');
});
app.listen(3000);
우리는 이제 이 부분을 토큰 검증 함수로 수정하기만 하면 됩니다.
const express = require('express');
const app = express();
const tokenChecker = function (req, res, next) {
const userIdFromToken = JwtService.getUserIdFromRequest(req)
req.userId = userIdFromToken
next()
};
// 모든 api의 request에 userId가 undefined 혹은 string으로 들어가게 될 것입니다.
app.use(tokenChecker);
app.get('/', function (req, res) {
res.send('Hello World!');
});
app.listen(3000);
실제 토큰 검증 함수는 JwtService.getUserIdFromRequest 인데요.
저는 request에서 토큰을 확인하여 verify하는 것을 하나의 함수로 만들어뒀습니다.
// JwtService.ts
// javascript로 써도 무방합니다
import { Request } from 'express';
import { User } from 'src/types';
import jwt from 'jsonwebtoken';
export default class JwtService {
static getUserIdFromRequest = (req: Request): string | null => {
const token = this.extractTokenFromRequest(req)
if (!token) {
return null
}
const jwtPayload = this.decodeJWT(token)
return (jwtPayload as any)?._id || null
}
static extractTokenFromRequest = (req: Request): string | undefined => {
const TOKEN_PREFIX = 'Bearer '
const auth = req.headers.authorization
const token = auth?.includes(TOKEN_PREFIX)
? auth.split(TOKEN_PREFIX)[1]
: auth
return token
}
static decodeJWT = (token: string) => {
try {
const decodedToken = jwt.verify(token, process.env.JWT_SECRET_KEY!)
return decodedToken;
} catch {
return null
}
}
static createJWT = async (user: User): Promise<string> => {
const token = jwt.sign(
{ _id: user._id },
process.env.JWT_SECRET_KEY!,
);
return token;
};
}
만약 자바스크립트로 구현했다면 이제 더이상 할 것은 없습니다!
이미 모든 req에서 userId를 불러보면 (토큰이 제대로 담겼다면) 원하는 값이 찍히는 것을 확인하실 수 있을 것입니다.
그러나 typescript를 사용중이라면 아직 한 가지 작업이 더 남아있습니다.
왜냐하면 다음과 같은 에러가 뜰 것이기 떄문이죠
이는 타입스크립트에서 정의한 Request에 우리가 추가한 필드가 없기 때문입니다.
이를 해결하기 위해서는 namespace를 정의해야 하는데, 전혀 어렵지 않습니다!
우선 (type을 정의하는 디렉토리에) 자신이 원하는 파일을 만듭니다.
저는 types 폴더에 express.d.ts 라는 이름으로 만들겠습니다.
그리고 다음과 같은 코드를 적어주세요
declare namespace Express {
export interface Request {
userId: string | null
}
}
그리고 tsconfig 파일에 들어가서 typeRoots 필드를 수정하면 끝입니다!
(원래 typescript 타입 파일이 모여있는 것과 방금 정의한 디렉토리를 읽도록 하는 것이죠)
{
"compilerOptions": {
...
"typeRoots" : ["./node_modules/@types", "./src/types"],
...
}
}
이제 더욱 편하게 토큰을 활용하여 개발할 수 있겠죠?
그럼 오늘도 행복코딩하세요:)
NodeJS 이미지 업로드 기능 만들기 (AWS S3 활용하기) (0) | 2022.01.30 |
---|---|
REST API 컨벤션 Top5! 단수, 복수, 네이밍 등의 url 설계를 위한 best practice 알아보기 (0) | 2022.01.13 |
Express에서 Error handler 추가하여 try, catch 생략하기! (1) | 2021.12.22 |
React Native (Expo)에 구글 가입 기능 추가하기 (9) | 2021.12.22 |
Codecov로 테스트 커버리지 측정 및 자동화 (Jest, CircleCI) (0) | 2021.05.30 |
댓글 영역