CORS, Helmet ,Day.js, Nodemon
1. CORS (교차 출처 리소스 공유)
CORS는 웹 서버의 리소스(예: API)를 다른 도메인의 웹 페이지에서 접근할 수 있게 해주는 메커니즘입니다. 기본적으로 웹 브라우저는 보안상의 이유로 이러한 교차 출처 요청을 제한합니다. CORS는 서버가 명시적으로 다른 도메인에서 요청을 허용할 수 있는 방법을 정의합니다.
- 주로 사용하는 경우: 백엔드 서비스에서 다른 도메인에 있는 프론트엔드 애플리케이션이 백엔드 API에 접근할 수 있도록 허용할 때 주로 사용합니다.
- Express (Node.js)에서 사용 예시
const cors = require('cors');
const express = require('express');
const app = express();
app.use(cors()); // 모든 도메인에서 서버에 접근 허용
app.listen(3000);
2. Helmet
Helmet은 Express.js 애플리케이션을 보호하기 위해 다양한 HTTP 헤더를 설정해주는 보안 패키지입니다. Helmet은 XSS(사이트 간 스크립팅)나 클릭재킹 등의 일반적인 웹 취약점으로부터 애플리케이션을 보호하는 데 도움을 줍니다.
- 주로 사용하는 경우: 서버의 보안을 강화하여 공격에 취약하지 않도록 보호하고 싶을 때 사용합니다.
- Express에서 사용 예
const helmet = require('helmet');
const express = require('express');
const app = express();
app.use(helmet()); // 다양한 보안 헤더를 자동으로 설정
app.listen(3000);
3. Day.js
Day.js는 날짜와 시간을 다루기 위한 경량 자바스크립트 라이브러리입니다. Moment.js와 유사하지만 훨씬 가볍고 빠릅니다. 날짜와 시간을 쉽게 포맷하거나, 시간 차이를 계산하는 등의 작업을 할 수 있습니다.
- 주로 사용하는 경우: 날짜를 포맷하거나, 시간 간격을 계산하고 싶을 때 사용합니다.
- 사용 예
const dayjs = require('dayjs');
console.log(dayjs().format()); // 현재 날짜와 시간을 ISO 형식으로 출력
console.log(dayjs().add(1, 'day').format('YYYY-MM-DD')); // 하루를 더한 날짜 출력
4. Nodemon
Nodemon은 Node.js 애플리케이션을 자동으로 다시 시작해주는 개발 도구입니다. 코드를 수정할 때마다 수동으로 서버를 다시 시작할 필요 없이, 변경 사항을 감지해 자동으로 서버를 재시작해줍니다.
- 주로 사용하는 경우: 개발 중에 코드를 변경할 때마다 서버를 수동으로 재시작하지 않도록 자동화하고 싶을 때 사용합니다.
- 사용 예시: nodemon을 설치한 후 nodemon app.js 명령어로 실행하면, app.js 파일이 변경될 때마다 서버가 자동으로 재시작됩니다.
# Sequelize
ORM(Object-Relational Mapping) 라이브러리로, MySQL, PostgreSQL, MariaDB, SQLite, Microsoft SQL Server 등을 지원합니다. SQL 쿼리를 직접 작성하지 않고 자바스크립트 객체를 사용해 데이터베이스를 조작할 수 있게 도와줍니다.
Sequelize는 테이블을 자바스크립트 객체로 매핑하여 데이터베이스 작업을 더 직관적으로 관리할 수 있게 해줍니다.
- Sequelize 설정을 통해 데이터베이스와 연결합니다.
- 모델 정의를 통해 MySQL의 테이블을 자바스크립트 객체로 표현합니다.
- CRUD 작업을 위해 Sequelize의 메서드를 사용하여 데이터를 삽입, 조회, 수정, 삭제합니다.
- 테이블 간의 **연관 관계(associations)**를 설정하여 더 복잡한 데이터 구조를 관리할 수 있습니다.
Sequelize의 특징
- ORM (Object-Relational Mapping): 자바스크립트 객체와 데이터베이스 테이블을 매핑해주어 SQL 쿼리 없이 데이터를 조회하고 수정할 수 있습니다.
- 모델 정의: 테이블을 모델로 정의하고, 각 컬럼의 타입과 특성을 설정할 수 있습니다.
- Associations(연관 관계): 테이블 간의 관계(1:1, 1:다, 다:다)를 설정할 수 있습니다.
- 쿼리 빌더: 객체 기반으로 다양한 쿼리를 작성할 수 있습니다.
1. Sequelize 설치 및 설정
Sequelize와 MySQL 드라이버를 설치
npm install sequelize mysql2
2. Sequelize 기본 설정
sequelize를 설정하여 MySQL에 연결합니다.
const { Sequelize, DataTypes } = require('sequelize');
// MySQL 데이터베이스와 연결
const sequelize = new Sequelize('database_name', 'username', 'password', {
host: 'localhost',
dialect: 'mysql',
});
(async () => {
try {
// 연결 확인
await sequelize.authenticate();
console.log('Connection has been established successfully.');
} catch (error) {
console.error('Unable to connect to the database:', error);
}
})();
- Sequelize('database_name', 'username', 'password', {...}): 데이터베이스와 연결하기 위한 설정.
- dialect: 'mysql': 사용하려는 데이터베이스가 MySQL임을 명시.
- sequelize.authenticate(): 데이터베이스 연결이 성공했는지 확인하는 함수.
3. Sequelize 모델 정의 및 사용
모델은 데이터베이스의 테이블을 자바스크립트 객체로 나타내는 역할을 합니다.
모델 정의 (User 모델 예시)
const User = sequelize.define('User', {
// 컬럼 정의
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
},
username: {
type: DataTypes.STRING,
allowNull: false,
},
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
},
password: {
type: DataTypes.STRING,
allowNull: false,
},
}, {
// 옵션
tableName: 'users', // 테이블 이름
timestamps: true, // createdAt, updatedAt 자동 생성
});
// 데이터베이스에 모델을 동기화 (테이블 생성)
(async () => {
await sequelize.sync();
})();
- sequelize.define('ModelName', {...}): 모델을 정의하며, 첫 번째 인자로는 모델 이름, 두 번째 인자로는 모델의 속성을 정의합니다.
- sequelize.sync(): 데이터베이스에 모델 구조를 반영하여 테이블을 생성합니다.
- timestamps: true: createdAt과 updatedAt 컬럼을 자동으로 추가합니다.
4. 데이터 작업 예시
1) 데이터 삽입 (Create)
(async () => {
const newUser = await User.create({
username: 'john_doe',
email: 'john@example.com',
password: 'securepassword123'
});
console.log('New User Created:', newUser);
})();
User.create({...}): 테이블에 새로운 데이터를 삽입합니다.
2) 데이터 조회 (Read)
(async () => {
const users = await User.findAll();
console.log('All Users:', users);
})();
User.findAll(): users 테이블의 모든 데이터를 조회합니다.
(async () => {
const user = await User.findOne({ where: { email: 'john@example.com' } });
console.log('Found User:', user);
})();
User.findOne({ where: { ... } }): 특정 조건을 만족하는 첫 번째 데이터를 조회
3) 데이터 수정 (Update)
(async () => {
const updatedUser = await User.update(
{ username: 'johnny_doe' }, // 수정할 데이터
{ where: { email: 'john@example.com' } } // 조건
);
console.log('User Updated:', updatedUser);
})();
User.update({...}, { where: {...} }): 특정 조건을 만족하는 데이터를 수정
4) 데이터 삭제 (Delete)
(async () => {
const result = await User.destroy({
where: { email: 'john@example.com' }
});
console.log('User Deleted:', result);
})();
User.destroy({ where: {...} }): 특정 조건을 만족하는 데이터를 삭제
5. Associations (관계 설정)
Sequelize는 테이블 간의 관계를 정의할 수 있습니다. 예를 들어, User와 Post 모델 간의 1
관계를 설정해 보겠습니다.
Post 모델 정의
const Post = sequelize.define('Post', {
title: {
type: DataTypes.STRING,
allowNull: false,
},
content: {
type: DataTypes.TEXT,
allowNull: false,
}
});
// 관계 정의 (1:N)
User.hasMany(Post);
Post.belongsTo(User);
// 테이블 동기화
(async () => {
await sequelize.sync();
})();
- User.hasMany(Post): 한 명의 사용자가 여러 개의 게시물을 가질 수 있는 관계.
- Post.belongsTo(User): 게시물은 한 명의 사용자에게 속하는 관계.
# JWT
인증에 사용되는 표준 토큰 기반의 인증 방식입니다. 서버와 클라이언트 간의 인증 정보를 안전하게 주고받기 사용
- JWT는 사용자 인증에 많이 사용되며, 클라이언트와 서버 간의 무상태 인증을 구현하는 데 유용합니다.
- jsonwebtoken 라이브러리를 사용하여 Node.js에서 JWT 토큰을 쉽게 생성하고 검증할 수 있습니다.
- Express와 같은 프레임워크에서 JWT를 사용하여 인증 미들웨어를 간단하게 구현할 수 있습니다.
JWT란?
JWT는 세 부분으로 구성된 토큰입니다:
- Header (헤더): 토큰의 타입과 해싱 알고리즘을 명시합니다. 예: {"alg": "HS256", "typ": "JWT"}
- Payload (페이로드): 사용자의 정보(클레임)를 담고 있는 부분입니다. 예를 들어 사용자 ID, 권한 등을 저장할 수 있습니다.
- Signature (서명): 헤더와 페이로드를 조합하여 비밀키로 서명한 값입니다. 이 서명은 토큰이 변조되지 않았음을 검증하는 데 사용됩니다.
JWT는 기본적으로 서버에 세션을 저장하지 않고 클라이언트가 인증된 사용자임을 증명할 수 있는 방식을 제공합니다. 이를 통해 **무상태 인증(stateless authentication)**을 구현할 수 있습니다.
Node.js에서 JWT 사용 방법
jsonwebtoken 라이브러리를 사용
설치
npm install express jsonwebtoken bcryptjs
- jsonwebtoken: JWT를 생성하고 검증하기 위해 사용.
- bcryptjs: 비밀번호를 해싱하고 검증하기 위해 사용.
JWT 생성 및 발급 (토큰 발급)
const jwt = require('jsonwebtoken');
// 비밀키
const secretKey = 'your-secret-key';
// 사용자 정보 (페이로드)
const payload = {
id: 1,
username: 'john_doe',
role: 'admin'
};
// 토큰 생성
const token = jwt.sign(payload, secretKey, { expiresIn: '1h' });
console.log('Generated Token:', token);
- jwt.sign(payload, secretKey, options) 함수는 주어진 payload와 비밀키를 사용하여 JWT를 생성합니다.
- expiresIn 옵션을 사용하여 토큰의 유효 기간을 설정할 수 있습니다 (예: '1h'는 1시간).
JWT 검증 및 디코딩 (토큰 검증)
클라이언트로부터 받은 JWT를 서버에서 검증
// 클라이언트로부터 받은 토큰
const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...';
// 토큰 검증 함수
jwt.verify(token, secretKey, (err, decoded) => {
if (err) {
// 검증 실패 처리
console.log('Token is invalid or expired', err);
} else {
// 검증 성공, 디코딩된 정보 사용
console.log('Decoded Payload:', decoded);
}
});
- jwt.verify(token, secretKey, callback) 함수는 클라이언트로부터 받은 JWT를 검증합니다.
- 토큰이 유효하면 decoded 객체에 페이로드 정보가 반환되며, 유효하지 않거나 만료된 경우 에러가 발생합니다.
Express에서 JWT 인증 구현
Express.js에서 JWT를 사용한 간단한 인증 미들웨어
const express = require('express');
const jwt = require('jsonwebtoken');
const app = express();
const secretKey = 'your-secret-key';
// 미들웨어: JWT 인증
function authenticateToken(req, res, next) {
const token = req.headers['authorization'];
if (!token) return res.status(401).send('Access Denied');
jwt.verify(token, secretKey, (err, decoded) => {
if (err) return res.status(403).send('Invalid Token');
req.user = decoded; // 토큰에 있는 유저 정보를 요청에 저장
next();
});
}
// 로그인 라우트 (토큰 발급)
app.post('/login', (req, res) => {
// 사용자 정보 (일반적으로 DB에서 가져옴)
const user = { id: 1, username: 'john_doe' };
// JWT 생성
const token = jwt.sign(user, secretKey, { expiresIn: '1h' });
res.json({ token });
});
// 보호된 라우트 (JWT 필요)
app.get('/protected', authenticateToken, (req, res) => {
res.send(`Hello ${req.user.username}, you have access to this route!`);
});
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
- authenticateToken 미들웨어는 클라이언트가 요청에 포함한 JWT를 검증하고, 검증에 성공하면 다음 핸들러로 넘어가도록 처리합니다.
- /login 엔드포인트는 로그인 후 JWT를 발급하는 역할을 합니다.
- /protected 엔드포인트는 인증된 사용자만 접근할 수 있는 보호된 경로입니다.
예)
const express = require('express');
const jwt = require('jsonwebtoken');
const bcrypt = require('bcryptjs');
const app = express();
const secretKey = 'your-secret-key';
app.use(express.json()); // JSON 요청을 처리하기 위한 미들웨어
// 임시 사용자 데이터베이스
const users = [];
// 1. 회원가입 (이메일, 비밀번호 저장)
app.post('/register', async (req, res) => {
const { email, password } = req.body;
// 이메일 중복 확인
const existingUser = users.find(user => user.email === email);
if (existingUser) {
return res.status(400).send('User already exists');
}
// 비밀번호 해싱
const hashedPassword = await bcrypt.hash(password, 10);
// 사용자 저장 (이메일과 해싱된 비밀번호)
users.push({ email, password: hashedPassword });
res.send('User registered successfully');
});
// 2. 로그인 (이메일, 비밀번호 검증 후 JWT 발급)
app.post('/login', async (req, res) => {
const { email, password } = req.body;
// 사용자 찾기
const user = users.find(user => user.email === email);
if (!user) {
return res.status(400).send('User not found');
}
// 비밀번호 검증
const isMatch = await bcrypt.compare(password, user.password);
if (!isMatch) {
return res.status(400).send('Invalid password');
}
// JWT 발급
const token = jwt.sign({ email: user.email }, secretKey, { expiresIn: '1h' });
res.json({ token });
});
// 3. JWT 인증 미들웨어
function authenticateToken(req, res, next) {
const token = req.headers['authorization'];
if (!token) return res.status(401).send('Access Denied');
jwt.verify(token, secretKey, (err, decoded) => {
if (err) return res.status(403).send('Invalid Token');
req.user = decoded; // 유저 정보 저장
next();
});
}
// 4. 보호된 라우트 (JWT가 있어야 접근 가능)
app.get('/protected', authenticateToken, (req, res) => {
res.send(`Hello ${req.user.email}, you have access to this route!`);
});
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
1. 회원가입 (/register)
- 사용자가 이메일과 비밀번호를 입력하면, 비밀번호는 **bcryptjs**를 사용해 해싱하여 안전하게 저장됩니다.
- 이미 존재하는 이메일로 회원가입하려고 하면 오류를 반환합니다.
2. 로그인 (/login)
- 사용자가 로그인할 때, 저장된 사용자 데이터를 조회하고 비밀번호를 **bcrypt.compare()**를 사용해 해싱된 비밀번호와 입력된 비밀번호가 일치하는지 확인합니다.
- 일치할 경우, JWT를 발급하고 사용자에게 반환합니다.
3. JWT 인증 미들웨어 (authenticateToken)
- authorization 헤더에서 JWT를 가져와서, 유효한 토큰인지 **jwt.verify()**로 검증합니다.
- 유효하지 않으면 403 오류를 반환하고, 유효하면 다음 라우트로 이동하게 합니다.
4. 보호된 라우트 (/protected)
- 로그인 후 발급된 JWT를 헤더에 포함한 요청만 이 라우트에 접근할 수 있습니다.
- JWT가 유효한 경우, 토큰에 저장된 이메일 정보를 사용해 보호된 정보를 응답합니다.
실행 흐름
- 회원가입: POST /register로 이메일과 비밀번호를 보내면 서버는 비밀번호를 해싱하고 저장합니다.
- 예시 요청: { "email": "example@mail.com", "password": "password123" }
- 로그인: POST /login으로 이메일과 비밀번호를 보내면, 서버는 비밀번호를 확인하고 유효하다면 JWT를 발급합니다.
- 예시 요청: { "email": "example@mail.com", "password": "password123" }
- 응답 예시: { "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6..." }
- 보호된 API 요청: GET /protected로 요청 시, Authorization 헤더에 발급받은 토큰을 넣어 요청합니다.
- 예시 헤더: Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6...
- 유효한 토큰일 경우, 성공적으로 보호된 정보를 반환합니다.
- 사용자는 이메일과 비밀번호로 회원가입 및 로그인을 할 수 있습니다.
- 로그인 시 JWT가 발급되고, 이 JWT를 사용해 인증된 사용자만 보호된 API에 접근할 수 있습니다.
- bcryptjs는 비밀번호를 안전하게 해싱 및 검증하고, jsonwebtoken은 토큰을 생성 및 검증하는 데 사용됩니다.
'Node.js' 카테고리의 다른 글
[ Node.js ] AWS 배포 (0) | 2025.03.13 |
---|---|
[ Node ] 용어 (0) | 2024.09.28 |
[ Node.js ] 05. 포스트맨 postman (0) | 2024.08.30 |
[ NoSQL ] MongoDB 개념과 설정 (0) | 2024.06.10 |
[ Node.js ] 04. crud , HTTP method, MySQL 연동 (0) | 2024.04.30 |