백엔드 개발에서 사용자 인증은 매우 중요한 요소입니다. 오늘은 JWT(JSON Web Token)를 활용한 간단한 사용자 인증 미니 프로젝트를 만들어 보겠습니다. JWT는 클라이언트와 서버 간의 안전한 정보 전송을 위한 표준으로, 주로 인증 및 정보 교환에 사용됩니다.
프로젝트 환경 설정
우선, Node.js와 Express 프레임워크를 사용하여 서버를 구축하고, MongoDB를 데이터베이스로 사용할 것입니다. 필요한 패키지를 설치해줍니다.
npm init -y
npm install express mongoose jsonwebtoken bcryptjs body-parser dotenv
이제 각 패키지의 역할을 간단히 설명하겠습니다.
express: 웹 서버를 만드는데 사용합니다.mongoose: MongoDB와의 연결 및 데이터 모델링을 담당합니다.jsonwebtoken: JWT 토큰을 생성하고 검증하는 데 사용됩니다.bcryptjs: 비밀번호 해싱을 위한 라이브러리입니다.body-parser: 요청 본문을 쉽게 파싱하기 위해 사용합니다.dotenv: 환경 변수를 관리하기 위해 사용합니다.
기본 서버 설정
다음으로, Express 서버를 설정하고 기본 API를 구축해보겠습니다. server.js 파일을 생성하고 아래의 코드를 입력합니다.
require('dotenv').config();
const express = require('express');
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const app = express();
const PORT = process.env.PORT || 3000;
mongoose.connect(process.env.MONGO_URI, { useNewUrlParser: true, useUnifiedTopology: true })
.then(() => console.log('MongoDB connected'))
.catch(err => console.log(err));
app.use(bodyParser.json());
app.listen(PORT, () => {
console.log(Server is running on port ${PORT});
});
위의 코드는 MongoDB에 연결하고 Express 서버를 실행합니다. 환경 변수에서 MongoDB URI를 가져오기 위해 .env 파일을 생성하고 다음과 같이 입력합니다.
MONGO_URI=mongodb://<username>:<password>@localhost:27017/mydatabase
사용자 모델 생성
사용자 정보를 저장하기 위한 Mongoose 모델을 생성합니다. User.js 파일을 생성하고 아래의 코드를 입력합니다.
const mongoose = require('mongoose');
const bcrypt = require('bcryptjs');
const UserSchema = new mongoose.Schema({
username: { type: String, required: true, unique: true },
password: { type: String, required: true }
});
UserSchema.pre('save', async function(next) {
if (!this.isModified('password')) return next();
this.password = await bcrypt.hash(this.password, 10);
next();
});
const User = mongoose.model('User', UserSchema);
module.exports = User;
위의 코드에서는 사용자 모델을 정의하고, 비밀번호를 저장하기 전에 해싱하는 미들웨어를 설정합니다.
회원가입 및 로그인 API
이제 사용자 등록과 로그인을 위한 API를 구현하겠습니다. server.js 파일에 아래의 코드를 추가합니다.
const User = require('./User');
const jwt = require('jsonwebtoken');
// 회원가입 API
app.post('/signup', async (req, res) => {
const { username, password } = req.body;
try {
const user = new User({ username, password });
await user.save();
res.status(201).send('User created successfully');
} catch (error) {
res.status(400).send('Error creating user');
}
});
// 로그인 API
app.post('/login', async (req, res) => {
const { username, password } = req.body;
try {
const user = await User.findOne({ username });
if (!user || !(await bcrypt.compare(password, user.password))) {
return res.status(401).send('Invalid credentials');
}
const token = jwt.sign({ id: user._id }, process.env.JWT_SECRET, { expiresIn: '1h' });
res.json({ token });
} catch (error) {
res.status(500).send('Server error');
}
});
여기서는 /signup 경로를 통해 사용자를 등록하고, /login 경로를 통해 JWT를 생성합니다.
토큰 검증 미들웨어
이제 보호된 리소스를 위한 JWT 검증 미들웨어를 추가합니다. 아래 코드를 추가합니다.
function authenticateToken(req, res, next) {
const token = req.headers['authorization'] && req.headers['authorization'].split(' ')[1];
if (!token) return res.sendStatus(401);
jwt.verify(token, process.env.JWT_SECRET, (err, user) => {
if (err) return res.sendStatus(403);
req.user = user;
next();
});
}
이 미들웨어는 요청 헤더에서 JWT를 추출하고, 이를 검증하여 요청을 계속 진행할지 결정합니다.
보호된 경로 추가
최종적으로 보호된 경로를 추가해 보겠습니다. 다음 코드를 추가합니다.
app.get('/protected', authenticateToken, (req, res) => {
res.send('This is a protected route');
});
이제 /protected 경로는 JWT 토큰이 필요한 보호된 경로입니다.
마무리
이번 포스트에서는 JWT를 활용한 간단한 사용자 인증 미니 프로젝트를 만들어 보았습니다. JWT를 사용하면 클라이언트와 서버 간의 안전한 정보 전송이 가능하여, 사용자 인증을 효율적으로 처리할 수 있습니다. 이 예제를 바탕으로 더욱 복잡한 인증 시스템을 구축해보시기 바랍니다.