웹 애플리케이션을 개발할 때 데이터베이스는 굉장히 중요합니다. 특히 관계형 데이터베이스를 효율적으로 다루기 위해 ORM(Object-Relational Mapping)이 자주 사용됩니다. 이번 글에서는 Node.js에서 TypeORM을 사용하여 데이터베이스를 효율적으로 다루는 방법을 소개하겠습니다.
TypeORM을 사용하여 Board
테이블에서 데이터를 가져오는 방법은 다음과 같습니다.
// ORM 사용
const boards = await Board.find({ title: 'Hello', status: 'PUBLIC' });
ORM을 사용하지 않고 SQL 쿼리를 직접 작성하여 데이터를 가져오는 방법은 다음과 같습니다.
// 순수 JavaScript 사용
db.query('SELECT * FROM boards WHERE title = "Hello" AND status = "PUBLIC"', (err, result) => {
if (err) {
throw new Error('Error');
}
const boards = result.rows;
});
ORM은 객체와 관계형 데이터베이스 간의 데이터를 자동으로 변형 및 연결하는 작업입니다. 이를 통해 개발자는 객체 지향 프로그래밍 방식으로 데이터베이스를 조작할 수 있게 됩니다.
데이터베이스 추상화 계층은 SQL을 직접 사용하지 않고도 데이터베이스와 통신할 수 있도록 도와주는 레이어입니다. 이를 위해 개발자는 다양한 라이브러리를 활용할 수 있으며, 추상화 정도에 따라 세 가지 수준으로 나뉩니다.
데이터베이스 드라이버는 데이터베이스 연결을 처리하고 원시 SQL 문자열을 작성하여 데이터베이스에 전달합니다. 대표적인 예는 다음과 같습니다.
mysql
: MySQL용 드라이버pg
: PostgreSQL용 드라이버sqlite3
: SQLite용 드라이버다음은 pg
모듈을 사용하여 PostgreSQL 데이터베이스에 접근하는 예시입니다.
// 데이터베이스 드라이버 사용
const { Client } = require('pg');
const client = new Client({
user: 'postgres',
host: 'localhost',
database: 'test',
password: 'password',
port: 5432
});
client.connect();
client.query('SELECT * FROM ingredients WHERE recipe = $1', ['Chicken Tikka Masala'], (err, res) => {
if (err) {
console.error(err);
} else {
console.log(res.rows);
}
client.end();
});
쿼리 빌더는 SQL 쿼리를 좀 더 구조화된 방식으로 작성할 수 있도록 도와줍니다. 대표적인 쿼리 빌더 라이브러리는 다음과 같습니다.
knex
: 다수의 SQL 언어를 지원하는 쿼리 빌더// knex 사용
const knex = require('knex')({
client: 'pg',
connection: {
host: 'localhost',
user: 'postgres',
password: 'password',
database: 'test'
}
});
knex('ingredients')
.select('*')
.where({ recipe: 'Chicken Tikka Masala' })
.then(data => console.log(data))
.catch(err => console.error(err));
ORM은 데이터베이스의 데이터를 객체로 매핑하여 애플리케이션에서 객체 지향적으로 다룰 수 있도록 합니다. 주요 ORM 라이브러리는 다음과 같습니다.
typeorm
sequelize
prisma
장점
단점
TypeORM은 Node.js에서 실행되고 TypeScript로 작성된 객체 관계형 매퍼 라이브러리입니다. 다양한 관계형 데이터베이스를 지원하며, 간단한 CLI 명령어로 생산성을 높일 수 있습니다.
package.json
을 생성합니다.npm init -y
npm install express morgan pg typeorm reflect-metadata --save
npm install typescript ts-node @types/node @types/express @types/morgan --save-dev
tsconfig.json
생성TypeScript 설정 파일을 생성합니다.
npx tsc --init
ormconfig.json
생성TypeORM 설정 파일을 생성하고 데이터베이스 연결 정보를 추가합니다.
{
"type": "postgres",
"host": "localhost",
"port": 5432,
"username": "postgres",
"password": "password",
"database": "typeorm_db",
"synchronize": true,
"logging": false,
"entities": ["src/entity/**/*.ts"],
"migrations": ["src/migration/**/*.ts"],
"subscribers": ["src/subscriber/**/*.ts"],
"cli": {
"entitiesDir": "src/entity",
"migrationsDir": "src/migration",
"subscribersDir": "src/subscriber"
}
}
index.ts
생성src/index.ts
파일을 생성하고 Express 앱을 초기화합니다.
import 'reflect-metadata';
import express from 'express';
import morgan from 'morgan';
import { createConnection } from 'typeorm';
const app = express();
app.use(express.json());
app.use(morgan('dev'));
createConnection().then(async connection => {
app.get('/', (req, res) => {
res.send('Hello, TypeORM!');
});
const PORT = 3000;
app.listen(PORT, () => {
console.log(`Server running on http://localhost:${PORT}`);
});
}).catch(error => console.log(error));
src/entity/User.ts
파일을 생성하고 User 엔터티를 정의합니다.
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column()
firstName: string;
@Column()
lastName: string;
@Column()
isActive: boolean;
}
src/data-source.ts
에서 데이터베이스 연결을 초기화합니다.import 'reflect-metadata';
import { DataSource } from 'typeorm';
import { User } from './entity/User';
export const AppDataSource = new DataSource({
type: 'postgres',
host: 'localhost',
port: 5432,
username: 'postgres',
password: 'password',
database: 'typeorm_db',
synchronize: true,
logging: false,
entities: [User],
migrations: [],
subscribers: []
});
src/index.ts
에서 데이터베이스 연결을 초기화하고 CRUD API를 작성합니다.import 'reflect-metadata';
import express from 'express';
import morgan from 'morgan';
import { AppDataSource } from './data-source';
import { User } from './entity/User';
const app = express();
app.use(express.json());
app.use(morgan('dev'));
AppDataSource.initialize().then(() => {
const userRepository = AppDataSource.getRepository(User);
// Create User
app.post('/users', async (req, res) => {
const { firstName, lastName, isActive } = req.body;
const user = new User();
user.firstName = firstName;
user.lastName = lastName;
user.isActive = isActive;
await userRepository.save(user);
res.status(201).json(user);
});
// Read Users
app.get('/users', async (req, res) => {
const users = await userRepository.find();
res.json(users);
});
// Update User
app.put('/users/:id', async (req, res) => {
const { id } = req.params;
const { firstName, lastName, isActive } = req.body;
let user = await userRepository.findOneBy
({ id: parseInt(id) });
if (user) {
user.firstName = firstName;
user.lastName = lastName;
user.isActive = isActive;
await userRepository.save(user);
res.json(user);
} else {
res.status(404).json({ message: 'User not found' });
}
});
// Delete User
app.delete('/users/:id', async (req, res) => {
const { id } = req.params;
const user = await userRepository.findOneBy({ id: parseInt(id) });
if (user) {
await userRepository.remove(user);
res.json({ message: 'User deleted' });
} else {
res.status(404).json({ message: 'User not found' });
}
});
const PORT = 3000;
app.listen(PORT, () => {
console.log(`Server running on http://localhost:${PORT}`);
});
}).catch(error => console.log(error));
이 글에서는 TypeORM을 사용하여 Node.js에서 데이터베이스와 객체 지향 프로그래밍을 효율적으로 연결하는 방법을 살펴보았습니다.