JWT (JSON Web Token) : 당사자 간의 정보를 JSON 객체로 안전하게 전송하기 위한 개방형 표준 (RFC 7519)
서버가 사용자에게 발급해주는 디지털 통행증 같은 느낌
세 토막 문자열. aaa.bbb.ccc 로 점으로 구분된 세 부분
어떤 종류의 토큰인지 (typ: JWT), 어떤 암호화 알고리즘 (alg: HS256) 등을 썼는지 담고 있음
실제 정보가 들어있는 곳. 유저 ID, 이름, 권한, 토큰 만료 시간 등을 담음. 누구나 볼 수 있기 때문에 비밀번호같은 민감 정보는 절대로 넣으면 안됨
토큰이 도중에 조작되지 않았는지 확인하는 가장 중요한 부분.
Header + Payload + 서버의 Secret Key 를 합쳐서 만듦. 서버만 알고 있는 비밀키가 있어야만 올바른 서명을 만들 수 있음
Authorization: Bearer <token>)Access/Refresh Token 전략을 씀DB에 사용자의 비밀번호를 저장할 때 중요한 원칙은 비밀번호 원문 Plain Text 를 그대로 저장하지 않는다는 것. DB가 해킹당하더라도 비밀번호가 유출되지 않도록 bcrypt 를 사용해 안전하게 Hashing 해야 함
비밀번호 저장만을 위해 설계된 강력한 해시 함수
npm install bcrypt
npm install -D @types/bcrypt
사용자가 회원가입할 때 비밀번호를 hashing 해서 DB 에 저장
import bcrypt from 'bcrypt';
const password = 'mySecretPassword123';
const saltRounds = 10; // 해싱 강도 (보통 10~12 권장)
const hashedPassword = await bcrypt.hash(password, saltRounds);
// 결과 예: $2b$10$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36WQoeG6L6s57Wy6UunmMei
로그인할 때는 입력받은 비밀번호를 다시 hashing 해서 비교하지 않고 compare 함수 사용.
const isMatch = await bcrypt.compare(password, hashedPassword);
if (isMatch) {
console.log("로그인 성공!");
} else {
console.log("비밀번호가 틀렸습니다.");
}
import { PrismaClient } from '@prisma/client';
import bcrypt from 'bcrypt';
const prisma = new PrismaClient();
async function createUser(email: string, password: string) {
// 1. 비밀번호 해싱
const hashedPassword = await bcrypt.hash(password, 10);
// 2. DB에 저장
return await prisma.user.create({
data: {
email,
password: hashedPassword, // 암호화된 비밀번호 저장
},
});
}
// POST /auth/register
async function register(req, res) {
const { email, password } = req.body;
// 1. 비밀번호 해싱 (bcrypt)
const saltRounds = 10;
const hashedPassword = await bcrypt.hash(password, saltRounds);
// 2. DB에 저장 (Prisma)
const newUser = await prisma.user.create({
data: {
email,
password: hashedPassword, // 원문이 아닌 해시값 저장
},
});
res.status(201).json({ success: true, message: "User registered" });
}
// POST /auth/login
async function login(req, res) {
const { email, password } = req.body;
// 1. 이메일로 유저 찾기
const user = await prisma.user.findUnique({ where: { email } });
if (!user) return res.status(404).json({ error: "User not found" });
// 2. 비밀번호 비교 (bcrypt.compare)
// 입력된 password를 해싱해서 DB의 user.password와 비교함
const isMatch = await bcrypt.compare(password, user.password);
if (!isMatch) return res.status(401).json({ error: "Invalid password" });
// 3. JWT 발급
const token = jwt.sign(
{ userId: user.id, email: user.email }, // Payload (데이터)
process.env.JWT_SECRET, // Secret Key (서명용)
{ expiresIn: '1h' } // 유효 기간
);
// 4. 토큰 반환
res.status(200).json({ success: true, token });
}