프로젝트 소개 및 목표
🔗 관련 에러 해결 가이드
JWT 인증 시스템 구현하기는 현대 웹 애플리케이션에서 가장 널리 사용되는 인증 방식을 직접 구축해보는 프로젝트입니다. JSON Web Token(JWT)을 활용하여 사용자 로그인, 회원가입, 토큰 기반 인증 및 권한 관리 시스템을 처음부터 끝까지 만들어봅니다. 이 프로젝트를 통해 보안의 핵심 개념인 해싱, 암호화, 토큰 생성 및 검증 과정을 이해하고, RESTful API 설계 원칙을 적용한 백엔드 서버를 구축할 수 있습니다. 완성된 시스템은 실제 서비스에 바로 적용 가능한 수준의 포트폴리오가 되며, 프론트엔드와 백엔드 간의 안전한 통신 메커니즘을 구현하는 실무 경험을 제공합니다.
필요한 기술 스택
백엔드는 Node.js와 Express.js 프레임워크를 사용하며, JWT 생성 및 검증을 위해 jsonwebtoken 라이브러리를 활용합니다. 데이터베이스는 MongoDB와 Mongoose ORM을 사용하여 사용자 정보를 저장하고, 비밀번호 해싱에는 bcrypt를 사용합니다. 프론트엔드는 React를 기반으로 하며, HTTP 요청 처리를 위해 Axios를 사용합니다. 환경 변수 관리에는 dotenv를 활용하고, 개발 시 nodemon으로 자동 재시작 기능을 구현합니다.
프로젝트 셋업
먼저 프로젝트 디렉토리를 생성하고 필요한 패키지를 설치합니다. 터미널에서 다음 명령어를 실행하세요:
mkdir jwt-auth-system
cd jwt-auth-system
npm init -y
npm install express mongoose jsonwebtoken bcrypt dotenv cors
npm install -D nodemon
프로젝트 루트에 .env 파일을 생성하여 환경 변수를 설정합니다. MongoDB 연결 문자열과 JWT 시크릿 키를 안전하게 보관하는 것이 중요합니다. package.json의 scripts 섹션에 “dev”: “nodemon server.js”를 추가하여 개발 환경을 구성합니다. 기본 폴더 구조는 models, routes, controllers, middleware로 나누어 관심사를 분리합니다.
단계별 구현 과정
1단계: 서버 기본 설정 및 데이터베이스 연결
server.js 파일에서 Express 서버를 초기화하고 MongoDB에 연결합니다:
const express = require('express');
const mongoose = require('mongoose');
const cors = require('cors');
require('dotenv').config();
const app = express();
// 미들웨어 설정
app.use(express.json());
app.use(cors());
// MongoDB 연결
mongoose.connect(process.env.MONGODB_URI, {
useNewUrlParser: true,
useUnifiedTopology: true
})
.then(() => console.log('MongoDB 연결 성공'))
.catch(err => console.error('MongoDB 연결 실패:', err));
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => {
console.log(`서버가 포트 ${PORT}에서 실행중입니다`);
});
2단계: 사용자 모델 생성
models/User.js 파일을 생성하여 사용자 스키마를 정의합니다:
const mongoose = require('mongoose');
const bcrypt = require('bcrypt');
const userSchema = new mongoose.Schema({
username: {
type: String,
required: true,
unique: true,
trim: true,
minlength: 3
},
email: {
type: String,
required: true,
unique: true,
lowercase: true,
match: /^[^\s@]+@[^\s@]+\.[^\s@]+$/
},
password: {
type: String,
required: true,
minlength: 6
},
role: {
type: String,
enum: ['user', 'admin'],
default: 'user'
},
createdAt: {
type: Date,
default: Date.now
}
});
// 비밀번호 해싱 미들웨어
userSchema.pre('save', async function(next) {
if (!this.isModified('password')) return next();
try {
const salt = await bcrypt.genSalt(10);
this.password = await bcrypt.hash(this.password, salt);
next();
} catch (error) {
next(error);
}
});
// 비밀번호 검증 메서드
userSchema.methods.comparePassword = async function(candidatePassword) {
return await bcrypt.compare(candidatePassword, this.password);
};
module.exports = mongoose.model('User', userSchema);
3단계: 인증 컨트롤러 구현
controllers/authController.js에서 회원가입과 로그인 로직을 구현합니다:
const jwt = require('jsonwebtoken');
const User = require('../models/User');
// 회원가입
exports.register = async (req, res) => {
try {
const { username, email, password } = req.body;
// 중복 확인
const existingUser = await User.findOne({ $or: [{ email }, { username }] });
if (existingUser) {
return res.status(400).json({
message: '이미 존재하는 사용자입니다'
});
}
// 새 사용자 생성
const user = new User({ username, email, password });
await user.save();
// JWT 토큰 생성
const token = jwt.sign(
{ userId: user._id, role: user.role },
process.env.JWT_SECRET,
{ expiresIn: '24h' }
);
res.status(201).json({
message: '회원가입 성공',
token,
user: {
id: user._id,
username: user.username,
email: user.email,
role: user.role
}
});
} catch (error) {
res.status(500).json({ message: '서버 오류', error: error.message });
}
};
// 로그인
exports.login = async (req, res) => {
try {
const { email, password } = req.body;
// 사용자 찾기
const user = await User.findOne({ email });
if (!user) {
return res.status(401).json({ message: '인증 실패' });
}
// 비밀번호 확인
const isMatch = await user.comparePassword(password);
if (!isMatch) {
return res.status(401).json({ message: '인증 실패' });
}
// JWT 토큰 생성
const token = jwt.sign(
{ userId: user._id, role: user.role },
process.env.JWT_SECRET,
{ expiresIn: '24h' }
);
res.json({
message: '로그인 성공',
token,
user: {
id: user._id,
username: user.username,
email: user.email,
role: user.role
}
});
} catch (error) {
res.status(500).json({ message: '서버 오류', error: error.message });
}
};
4단계: JWT 검증 미들웨어 작성
middleware/auth.js에서 토큰 검증 로직을 구현합니다:
const jwt = require('jsonwebtoken');
exports.verifyToken = (req, res, next) => {
// 헤더에서 토큰 추출
const token = req.headers.authorization?.split(' ')[1];
if (!token) {
return res.status(403).json({ message: '토큰이 제공되지 않았습니다' });
}
try {
// 토큰 검증
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = decoded;
next();
} catch (error) {
return res.status(401).json({ message: '유효하지 않은 토큰입니다' });
}
};
// 관리자 권한 확인
exports.isAdmin = (req, res, next) => {
if (req.user.role !== 'admin') {
return res.status(403).json({ message: '관리자 권한이 필요합니다' });
}
next();
};
5단계: 라우트 설정
routes/auth.js에서 API 엔드포인트를 정의합니다:
const express = require('express');
const router = express.Router();
const authController = require('../controllers/authController');
const { verifyToken, isAdmin } = require('../middleware/auth');
// 공개 라우트
router.post('/register', authController.register);
router.post('/login', authController.login);
// 보호된 라우트 (인증 필요)
router.get('/profile', verifyToken, (req, res) => {
res.json({
message: '프로필 조회 성공',
user: req.user
});
});
// 관리자 전용 라우트
router.get('/admin', verifyToken, isAdmin, (req, res) => {
res.json({ message: '관리자 페이지입니다' });
});
module.exports = router;
server.js에 라우트를 추가합니다:
const authRoutes = require('./routes/auth');
app.use('/api/auth', authRoutes);
테스트 및 배포
Postman이나 Thunder Client를 사용하여 API를 테스트합니다. 회원가입 엔드포인트(POST /api/auth/register)에 username, email, password를 JSON 형식으로 전송하고, 반환된 토큰을 확인합니다. 로그인(POST /api/auth/login)도 동일하게 테스트하며, 받은 토큰을 Authorization 헤더에 “Bearer [토큰]” 형식으로 포함하여 보호된 라우트에 접근합니다. 배포는 Heroku나 Vercel을 활용하며, 환경 변수를 플랫폼의 설정 페이지에서 안전하게 관리합니다. MongoDB Atlas를 사용하면 클라우드 데이터베이스로 쉽게 전환할 수 있습니다. HTTPS를 반드시 사용하여 토큰이 중간에 탈취되지 않도록 보안을 강화합니다.
마무리 및 확장 아이디어
JWT 인증 시스템 구현하기 프로젝트를 완성했습니다! 이제 리프레시 토큰을 추가하여 보안을 강화하거나, 이메일 인증 기능을 구현하여 사용자 검증 절차를 추가할 수 있습니다. OAuth 2.0을 통한 소셜 로그인(구글, 페이스북), 2단계 인증(2FA), 비밀번호 재설정 기능 등을 추가하면 더욱 완성도 높은 시스템이 됩니다. Rate limiting을 적용하여 무차별 대입 공격을 방지하고, Redis를 사용한 토큰 블랙리스트 관리로 로그아웃 기능을 개선할 수 있습니다. 이 프로젝트는 포트폴리오의 핵심 프로젝트가 될 것입니다!
📚 함께 읽으면 좋은 글
REST API 서버 구축 단계별 튜토리얼 – 완성까지 한번에!
📅 2025. 11. 9.
🎯 REST API 서버 구축 단계별 튜토리얼
실시간 채팅 앱 만들기 with Socket.io – 완성까지 한번에!
📅 2025. 11. 9.
🎯 실시간 채팅 앱 만들기 with Socket.io
30분만에 만드는 Todo App 완성 가이드 – 완성까지 한번에!
📅 2025. 11. 9.
🎯 30분만에 만드는 Todo App 완성 가이드
JWT 인증 시스템 구현하기 – 완성까지 한번에!
📅 2025. 11. 9.
🎯 JWT 인증 시스템 구현하기
JWT 인증 시스템 구현하기 – 완성까지 한번에!
📅 2025. 11. 8.
🎯 JWT 인증 시스템 구현하기
💡 위 글들을 통해 더 깊이 있는 정보를 얻어보세요!
📢 이 글이 도움되셨나요? 공유해주세요!
여러분의 공유 한 번이 더 많은 사람들에게 도움이 됩니다 ✨
🔥 공유할 때마다 블로그 성장에 큰 힘이 됩니다! 감사합니다 🙏
💬 여러분의 소중한 의견을 들려주세요!
이 글에서 가장 도움이 된 부분은 어떤 것인가요?
⭐ 모든 댓글은 24시간 내에 답변드리며, 여러분의 의견이 다른 독자들에게 큰 도움이 됩니다!
🎯 건설적인 의견과 경험 공유를 환영합니다 ✨
🔔 블로그 구독하고 최신 글을 받아보세요!
🌟 프로젝트 아이디어부터 다양한 실생활 정보까지!
매일 새로운 유용한 콘텐츠를 만나보세요 ✨
📧 RSS 구독 | 🔖 북마크 추가 | 📱 모바일 앱 알림 설정
지금 구독하고 놓치는 정보 없이 업데이트 받아보세요!