Express.js + TypeORM + MySQL 설정 방법
Express.js + TypeORM + MySQL 설정 방법에 대해 설명하는 페이지입니다.
Tags
TypeScript, Express.js, Database, MySQL, TypeORM
Environment
Node.js v20.11.1
typeorm v0.3.20 reflect-metadata v0.2.2 mysql2 v3.10.1
1. 개요
이번 글에서는 Express.js 애플리케이션에 TypeORM 적용 방법에 대해 설명하겠습니다.
2. Step 1 - TypeORM 관련 패키지 설치
다음 명령어를 입력하여 TypeORM과 MySQL을 사용하기 위한 database driver를 설치합니다.
typeorm
Node.js에서 JavaScript와 TypeScript로 사용할 수 있는 데이터베이스 ORM (Object-Relational Mapping) 라이브러리reflect-metadata
데코레이터 구문을 분석하고 SQL 쿼리를 작성하는데 사용mysql2
MySQL 모듈dotenv
Node.js 서버의 포트, DB 관련 정보 등 다양한 정보를 .env 파일로 관리할 수 있게 해주는 라이브러리
1npm install typeorm reflect-metadata mysql2 dotenv3. Step 2 - 폴더 구조
먼저 다음과 같이 express.js 폴더 구조를 생성합니다.
1├── node_modules
2├── public
3├── src
4│ ├── database
5│ │ └── dataSource.ts
6│ ├── entity
7│ │ └── Person.ts
8│ ├── routes
9│ | ├── index.ts
10│ │ └── person.ts
11│ └── app.ts
12├── .env
13├── .gitignore
14├── package-lock.json
15├── package.json
16└── tsconfig.json4. Step 3 - TypeScript Configuration
TypeORM에서 reflect-metadata 라이브러리를 사용할 수 있도록
tsconfig.json 파일을 열고 compilerOptions 항목에 다음 옵션들을 추가합니다.
1{
2 "compilerOptions": {
3 // ...
4
5 "experimentalDecorators": true /* Enable experimental support for legacy experimental decorators. */,
6 "emitDecoratorMetadata": true /* Emit design-type metadata for decorated declarations in source files. */,
7 "strictPropertyInitialization": false /* Check for class properties that are declared but not set in the constructor. */
8 // ...
9 }
10}5. Step 4 - .env 파일 생성
5.1. .env 파일이란?
env 파일은 환경 변수 파일을 의미하며 API 키나 DB 관련 정보 등 외부에 노출되면 안되고 개발자만 알아야하는 정보들을 저장하는데 사용됩니다.
5.2. .env 파일 설정
다음과 같이 프로젝트 최상위 디렉토리에 .env 파일을 생성하고 다음과 같이 MySQL 데이터베이스 관련 데이터를 입력합니다.
1# DB 관련 데이터
2DB_PORT=[데이터베이스 포트 번호 (MySQL 데이터베이스의 기본 포트는 3306입니다.)]
3DB_USERNAME=[데이터베이스 사용자 이름]
4DB_HOST=[데이터베이스 호스트]
5DB_PASSWORD=[데이터베이스 비밀번호]
6DB_DATABASE=[사용하고자 하는 데이터베이스 이름]예시를 들자면 다음과 같습니다.
1DB_PORT=3306
2DB_USERNAME=username
3DB_HOST=localhost
4DB_PASSWORD=password
5DB_DATABASE=test6. Step 5 - entity 생성
다음과 같이 /src/entity 디렉토리에 Person.ts 파일을 생성하고 다음과 같이 entity를 생성합니다.
1import { Column, Entity, PrimaryGeneratedColumn } from "typeorm";
2
3@Entity()
4export class Person {
5 @PrimaryGeneratedColumn()
6 id: number;
7
8 @Column("varchar", { length: 100 })
9 name: string;
10
11 @Column("int")
12 age: number;
13
14 @Column("varchar", { length: 100, unique: true })
15 email: string;
16}7. Step 6 - DataSource Configuration
7.1. DataSource 객체 생성
/src/database 디렉토리에 dataSource.ts 파일을 생성하고 다음과 같이 DataSource 옵션을 입력합니다.
1// data source and all connection configuration
2
3import { DataSource } from "typeorm";
4import { Person } from "../entity/Person";
5import dotenv from "dotenv";
6
7dotenv.config();
8
9const AppDataSource = new DataSource({
10 type: "mysql",
11 host: process.env.DB_HOST,
12 port: Number(process.env.DB_PORT),
13 username: process.env.DB_USERNAME,
14 password: process.env.DB_PASSWORD,
15 database: process.env.DB_DATABASE,
16 entities: [Person],
17 synchronize: true,
18 logging: false,
19 dropSchema: false,
20});
21
22export default AppDataSource;주요 옵션에 대해 설명하자면 다음과 같습니다.
type
사용하고자 하는 데이터베이스 종류host
데이터베이스 호스트port
데이터베이스 포트 번호username
데이터베이스 사용자 이름password
데이터베이스 비밀번호database
사용하고자 하는 데이터베이스 이름entities
사용하고자 하는 entity 또는 entity schema 목록synchronize
true일 경우 서버가 재시작될 때마다 entities 항목에 등록된 entity 목록을 바탕으로 데이터베이스에 자동으로 테이블을 생성합니다. production 모드일 경우 반드시 false로 설정합니다.logging
TypeORM을 통해 실행되는 SQL 쿼리를 logging 할지 여부dropSchema
true일 경우 서버가 재시작될 때마다 데이터베이스 내의 모든 schema를 삭제합니다. production 모드일 경우 반드시 false로 설정합니다.
이외의 옵션에 대해서는 다음 링크를 참고하시길 바랍니다.
7.2. 초기화
app.ts 파일을 열고 다음과 같이 database connection에 대한 초기화를 진행합니다.
1import express from "express";
2import { router } from "./routes";
3import bodyParser from "body-parser";
4import AppDataSource from "./database/dataSource";
5import "reflect-metadata";
6
7// to initialize the initial connection with the database, register all entities
8// and "synchronize" database schema, call "initialize()" method of a newly created database
9// once in your application bootstrap
10AppDataSource.initialize()
11 .then(() => {
12 // here you can start to work with your database
13 const app = express();
14 const port = Number(process.env.PORT ?? 4000);
15
16 // JSON 형태의 요청(request) body를 파싱(parse)하기 위해 사용하는 미들웨어(middleware) 적용
17 // req.body를 사용하려면 JSON 미들웨어를 사용해야 합니다.
18 // 사용하지 않으면 undefined 반환.
19 app.use(express.json());
20
21 // POST 요청 시 content 타입이 application/x-www-form-urlencoded인 경우 파싱
22 // JSON 미들웨어와 함께 사용
23 app.use(express.urlencoded({ extended: true }));
24
25 // HTTP에서 Body를 파싱하기 위한 설정
26 app.use(bodyParser.json());
27
28 app.use(router);
29
30 app.listen(port, () => {
31 console.log();
32 console.log(` [Local] http://localhost:${port}`);
33 console.log();
34 });
35 })
36 .catch((err) => console.error(err));8. Step 7 - TypeORM 사용 예시
8.1. Repository 사용
Repository를 사용하여 데이터베이스를 연동한 예시는 다음과 같습니다.
1/* src/routes/index.ts */
2
3import express, { NextFunction, Request, Response } from "express";
4import { router as personRouter } from "./person";
5
6export const router = express.Router();
7
8// 라우터 설정
9router.use("/person", personRouter);
10
11router.get("/", (req: Request, res: Response, next: NextFunction) => {
12 res.send("홈");
13});1/* src/routes/person.ts */
2
3import express, { NextFunction, Request, Response } from "express";
4import AppDataSource from "../database/dataSource";
5import { Person } from "../entity/Person";
6
7export const router = express.Router();
8
9// 모든 person 데이터 출력
10router.get("/", async (req: Request, res: Response, next: NextFunction) => {
11 const people = await AppDataSource.getRepository(Person).find();
12 res.json(people);
13});
14
15// 특정 이메일로 person 찾기
16router.get(
17 "/:email",
18 async (req: Request, res: Response, next: NextFunction) => {
19 const person = await AppDataSource.getRepository(Person).findOne({
20 where: {
21 email: req.params.email,
22 },
23 });
24 res.json(person);
25 },
26);
27
28// person 데이터 추가하기
29router.post("/", async (req: Request, res: Response, next: NextFunction) => {
30 const { name, age, email } = req.body;
31 const person = new Person();
32 person.name = name;
33 person.age = age;
34 person.email = email;
35
36 await AppDataSource.getRepository(Person).insert(person);
37 res.send(`Person has been saved. Person id is ${person.id}`);
38});
39
40// person 데이터 수정하기
41router.put(
42 "/:email",
43 async (req: Request, res: Response, next: NextFunction) => {
44 const { name, age, email } = req.body;
45
46 const person = await AppDataSource.getRepository(Person).findOne({
47 where: {
48 email: req.params.email,
49 },
50 });
51
52 if (person) {
53 person.name = name;
54 person.age = age;
55 person.email = email;
56 await AppDataSource.getRepository(Person).save(person);
57 }
58
59 res.json(person);
60 },
61);
62
63// person 데이터 삭제하기
64router.delete(
65 "/:email",
66 async (req: Request, res: Response, next: NextFunction) => {
67 const personRepository = AppDataSource.getRepository(Person);
68
69 const person = await personRepository.findOne({
70 where: {
71 email: req.params.email,
72 },
73 });
74
75 if (person) {
76 await personRepository.remove(person);
77 }
78
79 res.sendStatus(204); // No content.
80 },
81);8.2. Postman 사용
Postman을 사용하여 API를 요청한 예시는 다음과 같습니다.

