노현진's Blog

Express.js + TypeORM + MySQL 설정 방법

Express.js + TypeORM + MySQL 설정 방법에 대해 설명하는 페이지입니다.

Posted
Preview Image
By HyunJinNo

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 파일로 관리할 수 있게 해주는 라이브러리
bash
1npm install typeorm reflect-metadata mysql2 dotenv

3. Step 2 - 폴더 구조

먼저 다음과 같이 express.js 폴더 구조를 생성합니다.

plaintext
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.json

4. Step 3 - TypeScript Configuration

TypeORM에서 reflect-metadata 라이브러리를 사용할 수 있도록 tsconfig.json 파일을 열고 compilerOptions 항목에 다음 옵션들을 추가합니다.

json
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 데이터베이스 관련 데이터를 입력합니다.

plaintext
1# DB 관련 데이터
2DB_PORT=[데이터베이스 포트 번호 (MySQL 데이터베이스의 기본 포트는 3306입니다.)]
3DB_USERNAME=[데이터베이스 사용자 이름]
4DB_HOST=[데이터베이스 호스트]
5DB_PASSWORD=[데이터베이스 비밀번호]
6DB_DATABASE=[사용하고자 하는 데이터베이스 이름]

예시를 들자면 다음과 같습니다.

plaintext
1DB_PORT=3306
2DB_USERNAME=username
3DB_HOST=localhost
4DB_PASSWORD=password
5DB_DATABASE=test

6. Step 5 - entity 생성

다음과 같이 /src/entity 디렉토리에 Person.ts 파일을 생성하고 다음과 같이 entity를 생성합니다.

typescript
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 옵션을 입력합니다.

typescript
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로 설정합니다.

이외의 옵션에 대해서는 다음 링크를 참고하시길 바랍니다.

Data Source Options | TypeORM

7.2. 초기화

app.ts 파일을 열고 다음과 같이 database connection에 대한 초기화를 진행합니다.

typescript
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를 사용하여 데이터베이스를 연동한 예시는 다음과 같습니다.

typescript
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});
typescript
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를 요청한 예시는 다음과 같습니다.

Postman 사용

© HyunJinNo. Some rights reserved.