728x90
반응형
1. Sequelize ORM 소개
Sequelize는 Node.js 환경에서 가장 널리 사용되는 Promise 기반 ORM(Object-Relational Mapping) 라이브러리입니다. PostgreSQL, MySQL, MariaDB, SQLite, Microsoft SQL Server를 지원하며, JavaScript 객체를 통해 데이터베이스를 조작할 수 있게 해줍니다.
Sequelize를 사용하면 SQL 쿼리를 직접 작성하지 않고도 데이터베이스 작업을 수행할 수 있습니다. 모델 정의, 관계 설정, 트랜잭션 처리 등 데이터베이스 관련 작업을 객체 지향적으로 처리할 수 있어 코드의 가독성과 유지보수성이 향상됩니다.
2. 설치 및 초기 설정
2.1 패키지 설치
# Sequelize 코어 패키지 설치
npm install sequelize
# 사용하는 데이터베이스에 맞는 드라이버 설치
npm install pg pg-hstore # PostgreSQL
npm install mysql2 # MySQL
npm install mariadb # MariaDB
npm install sqlite3 # SQLite
npm install tedious # Microsoft SQL Server
2.2 Sequelize CLI 설치
npm install --save-dev sequelize-cli
2.3 프로젝트 초기화
npx sequelize-cli init
이 명령어를 실행하면 다음 폴더 구조가 생성됩니다.
project/
├── config/
│ └── config.json # 데이터베이스 연결 설정
├── models/
│ └── index.js # 모델 로더
├── migrations/ # 마이그레이션 파일
└── seeders/ # 시드 파일
2.4 데이터베이스 연결 설정
// config/config.json
{
"development": {
"username": "root",
"password": "password",
"database": "myapp_dev",
"host": "127.0.0.1",
"dialect": "mysql",
"logging": console.log
},
"production": {
"username": "root",
"password": "password",
"database": "myapp_prod",
"host": "127.0.0.1",
"dialect": "mysql",
"logging": false
}
}
2.5 Sequelize 인스턴스 생성
const { Sequelize } = require('sequelize');
// 방법 1: 개별 파라미터 전달
const sequelize = new Sequelize('database', 'username', 'password', {
host: 'localhost',
dialect: 'mysql',
pool: {
max: 5,
min: 0,
acquire: 30000,
idle: 10000
}
});
// 방법 2: 연결 URI 사용
const sequelize = new Sequelize('mysql://user:password@localhost:3306/database');
// 연결 테스트
async function testConnection() {
try {
await sequelize.authenticate();
console.log('데이터베이스 연결 성공');
} catch (error) {
console.error('데이터베이스 연결 실패:', error);
}
}
testConnection();
3. 모델 정의와 데이터 타입
3.1 기본 모델 정의
const { DataTypes } = require('sequelize');
const User = sequelize.define('User', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
username: {
type: DataTypes.STRING(50),
allowNull: false,
unique: true
},
email: {
type: DataTypes.STRING(100),
allowNull: false,
unique: true,
validate: {
isEmail: true
}
},
password: {
type: DataTypes.STRING(255),
allowNull: false
},
age: {
type: DataTypes.INTEGER,
validate: {
min: 0,
max: 150
}
},
isActive: {
type: DataTypes.BOOLEAN,
defaultValue: true
},
createdAt: {
type: DataTypes.DATE,
defaultValue: DataTypes.NOW
}
}, {
tableName: 'users',
timestamps: true,
underscored: true
});
3.2 주요 데이터 타입
DataTypes.STRING // VARCHAR(255)
DataTypes.STRING(100) // VARCHAR(100)
DataTypes.TEXT // TEXT
DataTypes.TEXT('tiny') // TINYTEXT
DataTypes.INTEGER // INTEGER
DataTypes.BIGINT // BIGINT
DataTypes.FLOAT // FLOAT
DataTypes.DOUBLE // DOUBLE
DataTypes.DECIMAL(10, 2) // DECIMAL(10, 2)
DataTypes.BOOLEAN // BOOLEAN
DataTypes.DATE // DATETIME
DataTypes.DATEONLY // DATE (날짜만)
DataTypes.JSON // JSON
DataTypes.JSONB // JSONB (PostgreSQL 전용)
DataTypes.ARRAY(DataTypes.STRING) // 배열 (PostgreSQL 전용)
DataTypes.UUID // UUID
DataTypes.UUIDV4 // UUID v4
DataTypes.ENUM('value1', 'value2') // ENUM
3.3 검증(Validation) 옵션
const Product = sequelize.define('Product', {
name: {
type: DataTypes.STRING,
validate: {
notEmpty: true,
len: [2, 100]
}
},
price: {
type: DataTypes.DECIMAL(10, 2),
validate: {
isDecimal: true,
min: 0
}
},
email: {
type: DataTypes.STRING,
validate: {
isEmail: {
msg: '유효한 이메일 주소를 입력하세요'
}
}
},
website: {
type: DataTypes.STRING,
validate: {
isUrl: true
}
},
status: {
type: DataTypes.STRING,
validate: {
isIn: [['active', 'inactive', 'pending']]
}
}
});
반응형
4. 관계 설정
4.1 1:1 관계 (hasOne, belongsTo)
const User = sequelize.define('User', {
name: DataTypes.STRING
});
const Profile = sequelize.define('Profile', {
bio: DataTypes.TEXT,
website: DataTypes.STRING
});
// User는 하나의 Profile을 가짐
User.hasOne(Profile, {
foreignKey: 'userId',
as: 'profile',
onDelete: 'CASCADE'
});
// Profile은 하나의 User에 속함
Profile.belongsTo(User, {
foreignKey: 'userId',
as: 'user'
});
4.2 1:N 관계 (hasMany, belongsTo)
const User = sequelize.define('User', {
name: DataTypes.STRING
});
const Post = sequelize.define('Post', {
title: DataTypes.STRING,
content: DataTypes.TEXT
});
// User는 여러 Post를 가짐
User.hasMany(Post, {
foreignKey: 'authorId',
as: 'posts'
});
// Post는 하나의 User에 속함
Post.belongsTo(User, {
foreignKey: 'authorId',
as: 'author'
});
4.3 N:M 관계 (belongsToMany)
const Post = sequelize.define('Post', {
title: DataTypes.STRING
});
const Tag = sequelize.define('Tag', {
name: DataTypes.STRING
});
// 중간 테이블 정의
const PostTag = sequelize.define('PostTag', {
createdAt: DataTypes.DATE
});
// 다대다 관계 설정
Post.belongsToMany(Tag, {
through: PostTag,
foreignKey: 'postId',
as: 'tags'
});
Tag.belongsToMany(Post, {
through: PostTag,
foreignKey: 'tagId',
as: 'posts'
});
4.4 관계 데이터 조회
// 연관 데이터 포함 조회
const userWithPosts = await User.findOne({
where: { id: 1 },
include: [
{
model: Post,
as: 'posts',
include: [
{
model: Tag,
as: 'tags'
}
]
},
{
model: Profile,
as: 'profile'
}
]
});
5. CRUD 작업과 쿼리 빌더
5.1 Create (생성)
// 단일 레코드 생성
const user = await User.create({
username: 'john_doe',
email: 'john@example.com',
password: 'hashedpassword'
});
// 여러 레코드 일괄 생성
const users = await User.bulkCreate([
{ username: 'user1', email: 'user1@example.com', password: 'pass1' },
{ username: 'user2', email: 'user2@example.com', password: 'pass2' }
], {
validate: true // 검증 활성화
});
// findOrCreate - 있으면 조회, 없으면 생성
const [user, created] = await User.findOrCreate({
where: { email: 'john@example.com' },
defaults: {
username: 'john_doe',
password: 'hashedpassword'
}
});
5.2 Read (조회)
// 전체 조회
const users = await User.findAll();
// 조건 조회
const { Op } = require('sequelize');
const activeUsers = await User.findAll({
where: {
isActive: true,
age: {
[Op.gte]: 18, // >= 18
[Op.lt]: 65 // < 65
}
},
order: [['createdAt', 'DESC']],
limit: 10,
offset: 0
});
// 단일 레코드 조회
const user = await User.findOne({
where: { email: 'john@example.com' }
});
// PK로 조회
const user = await User.findByPk(1);
// 집계 함수
const count = await User.count({
where: { isActive: true }
});
const maxAge = await User.max('age');
const minAge = await User.min('age');
const sumAge = await User.sum('age');
// 그룹화
const usersByStatus = await User.findAll({
attributes: [
'isActive',
[sequelize.fn('COUNT', sequelize.col('id')), 'count']
],
group: ['isActive']
});
5.3 연산자 목록
const { Op } = require('sequelize');
// 비교 연산자
[Op.eq]: 3 // = 3
[Op.ne]: 20 // != 20
[Op.gt]: 6 // > 6
[Op.gte]: 6 // >= 6
[Op.lt]: 10 // < 10
[Op.lte]: 10 // <= 10
[Op.between]: [6, 10] // BETWEEN 6 AND 10
[Op.notBetween]: [11, 15]
// 문자열 연산자
[Op.like]: '%hat' // LIKE '%hat'
[Op.notLike]: '%hat'
[Op.startsWith]: 'hat' // LIKE 'hat%'
[Op.endsWith]: 'hat' // LIKE '%hat'
[Op.substring]: 'hat' // LIKE '%hat%'
// 배열 연산자
[Op.in]: [1, 2] // IN (1, 2)
[Op.notIn]: [1, 2]
// 논리 연산자
[Op.and]: [{ a: 5 }, { b: 6 }]
[Op.or]: [{ a: 5 }, { b: 6 }]
[Op.not]: true
// NULL 체크
[Op.is]: null // IS NULL
[Op.not]: null // IS NOT NULL
5.4 Update (수정)
// 단일 레코드 수정
const user = await User.findByPk(1);
user.username = 'new_username';
await user.save();
// update 메서드 사용
await user.update({
username: 'new_username',
email: 'new@example.com'
});
// 조건에 맞는 레코드 일괄 수정
await User.update(
{ isActive: false },
{
where: {
lastLogin: {
[Op.lt]: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000) // 30일 이전
}
}
}
);
5.5 Delete (삭제)
// 단일 레코드 삭제
const user = await User.findByPk(1);
await user.destroy();
// 조건에 맞는 레코드 일괄 삭제
await User.destroy({
where: {
isActive: false
}
});
// 소프트 삭제 (paranoid 옵션 사용 시)
const User = sequelize.define('User', {
// 필드 정의
}, {
paranoid: true // deletedAt 컬럼 자동 추가
});
// 소프트 삭제된 레코드 포함 조회
await User.findAll({ paranoid: false });
// 소프트 삭제 레코드 복구
await user.restore();
6. 마이그레이션 사용법
6.1 마이그레이션 파일 생성
npx sequelize-cli migration:generate --name create-users-table
6.2 마이그레이션 파일 작성
// migrations/20240101000000-create-users-table.js
'use strict';
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.createTable('users', {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
username: {
type: Sequelize.STRING(50),
allowNull: false,
unique: true
},
email: {
type: Sequelize.STRING(100),
allowNull: false,
unique: true
},
password: {
type: Sequelize.STRING(255),
allowNull: false
},
is_active: {
type: Sequelize.BOOLEAN,
defaultValue: true
},
created_at: {
type: Sequelize.DATE,
defaultValue: Sequelize.literal('CURRENT_TIMESTAMP')
},
updated_at: {
type: Sequelize.DATE,
defaultValue: Sequelize.literal('CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP')
}
});
// 인덱스 추가
await queryInterface.addIndex('users', ['email']);
},
async down(queryInterface, Sequelize) {
await queryInterface.dropTable('users');
}
};
6.3 컬럼 추가/수정/삭제 마이그레이션
// migrations/20240102000000-add-phone-to-users.js
'use strict';
module.exports = {
async up(queryInterface, Sequelize) {
// 컬럼 추가
await queryInterface.addColumn('users', 'phone', {
type: Sequelize.STRING(20),
allowNull: true
});
// 컬럼 수정
await queryInterface.changeColumn('users', 'username', {
type: Sequelize.STRING(100),
allowNull: false
});
},
async down(queryInterface, Sequelize) {
await queryInterface.removeColumn('users', 'phone');
await queryInterface.changeColumn('users', 'username', {
type: Sequelize.STRING(50),
allowNull: false
});
}
};
6.4 마이그레이션 실행 명령어
# 마이그레이션 실행
npx sequelize-cli db:migrate
# 마이그레이션 되돌리기 (최근 1개)
npx sequelize-cli db:migrate:undo
# 모든 마이그레이션 되돌리기
npx sequelize-cli db:migrate:undo:all
# 특정 마이그레이션까지 되돌리기
npx sequelize-cli db:migrate:undo:all --to 20240101000000-create-users-table.js
# 마이그레이션 상태 확인
npx sequelize-cli db:migrate:status
7. 트랜잭션과 훅
7.1 트랜잭션 기본 사용법
// 관리형 트랜잭션 (자동 커밋/롤백)
const result = await sequelize.transaction(async (t) => {
const user = await User.create({
username: 'john',
email: 'john@example.com',
password: 'password'
}, { transaction: t });
await Profile.create({
userId: user.id,
bio: 'Hello, I am John'
}, { transaction: t });
return user;
});
// 비관리형 트랜잭션 (수동 커밋/롤백)
const t = await sequelize.transaction();
try {
const user = await User.create({
username: 'jane',
email: 'jane@example.com',
password: 'password'
}, { transaction: t });
await Post.create({
title: 'First Post',
content: 'Hello World',
authorId: user.id
}, { transaction: t });
await t.commit();
} catch (error) {
await t.rollback();
throw error;
}
7.2 트랜잭션 격리 수준
const { Transaction } = require('sequelize');
await sequelize.transaction({
isolationLevel: Transaction.ISOLATION_LEVELS.SERIALIZABLE
}, async (t) => {
// 트랜잭션 작업
});
// 격리 수준 종류
Transaction.ISOLATION_LEVELS.READ_UNCOMMITTED
Transaction.ISOLATION_LEVELS.READ_COMMITTED
Transaction.ISOLATION_LEVELS.REPEATABLE_READ
Transaction.ISOLATION_LEVELS.SERIALIZABLE
7.3 훅(Hooks) 정의
const User = sequelize.define('User', {
username: DataTypes.STRING,
email: DataTypes.STRING,
password: DataTypes.STRING
}, {
hooks: {
// 생성 전 훅
beforeCreate: async (user, options) => {
const bcrypt = require('bcrypt');
user.password = await bcrypt.hash(user.password, 10);
},
// 생성 후 훅
afterCreate: async (user, options) => {
console.log(`새 사용자 생성: ${user.username}`);
},
// 수정 전 훅
beforeUpdate: async (user, options) => {
if (user.changed('password')) {
const bcrypt = require('bcrypt');
user.password = await bcrypt.hash(user.password, 10);
}
},
// 삭제 전 훅
beforeDestroy: async (user, options) => {
await Post.destroy({
where: { authorId: user.id }
});
},
// 검증 후 훅
afterValidate: async (user, options) => {
user.email = user.email.toLowerCase();
}
}
});
7.4 훅 종류
// 생성 관련
beforeCreate, afterCreate
beforeBulkCreate, afterBulkCreate
// 수정 관련
beforeUpdate, afterUpdate
beforeBulkUpdate, afterBulkUpdate
beforeSave, afterSave // create와 update 모두에 적용
// 삭제 관련
beforeDestroy, afterDestroy
beforeBulkDestroy, afterBulkDestroy
// 조회 관련
beforeFind, afterFind
// 검증 관련
beforeValidate, afterValidate
// 동기화 관련
beforeSync, afterSync
7.5 훅 개별 추가
// addHook 메서드 사용
User.addHook('beforeCreate', 'hashPassword', async (user) => {
const bcrypt = require('bcrypt');
user.password = await bcrypt.hash(user.password, 10);
});
// 전역 훅 설정
const sequelize = new Sequelize('database', 'username', 'password', {
dialect: 'mysql',
hooks: {
beforeCreate: async (instance) => {
console.log('레코드 생성 시작');
}
}
});
결론
Sequelize는 Node.js 애플리케이션에서 데이터베이스 작업을 효율적으로 처리할 수 있게 해주는 강력한 ORM입니다. 모델 정의, 관계 설정, 마이그레이션, 트랜잭션 등 데이터베이스 관련 기능을 객체 지향적으로 관리할 수 있어 코드의 가독성과 유지보수성을 높여줍니다. 실제 프로젝트에서는 마이그레이션을 통한 스키마 버전 관리와 트랜잭션을 통한 데이터 무결성 보장을 적극 활용하는 것이 좋습니다.
728x90
반응형
'Node.js' 카테고리의 다른 글
| Node.js MySQL 연동 완벽 가이드 (0) | 2026.03.18 |
|---|---|
| Node.js의 Mongoose 사용법 완벽 가이드 (0) | 2026.03.17 |
| Node.js MongoDB 연동 (0) | 2026.03.17 |
| Node.js 데이터베이스 연동(Database Integration) 완벽 가이드 (1) | 2026.03.16 |
| Node.js의 쿠키 관리(Cookie Management) (0) | 2026.03.16 |