NestJS 는 여러 데이터 베이스를 적용할 수 있는데 TypeScript 와 호환성이 좋은 TypeOrm 을 적용하는 방법에 대해서 블로그를 남겨보겠다
MySQL 은 도커를 이용해서 생성해 준다 도커를 설치한 상태에서 아래 명령어를 입력하면 MySQL 8 버전을 도커 위에서 실행할 수 있다 ( charset은 utf-8을 사용하기 위해 설정 값을 넣어주었다)
docker run -d -p 3306:3306 --name typeorm-test-mysql -e MYSQL_ROOT_PASSWORD=1234 -d mysql:8 --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
docker 로 생성한 mysql 에 접속한 다음 이름이 test인 schema를 작성해 준다
// 도커 컨테이너 접속
docker exec -it {컨테이너ID} bin/bash
//mysql 접속
mysql -u root -p
//test schema 생성
create schema test
nestjs cli를 설치했다는 가정하에 아래 명령어를 이용해서 nestjs 프로젝트를 생성해 준다 설정에서 yarn 과 npm 중에서는 yarn 선택했다
//nestjs cli global module 설치
npm install -g @nestjs/cli
// nestjs 새로운 프로젝트 설치
nest new typeorm-test-project
nestjs 공식 문서에서 가이드 하는 @nestjs/typeorm, typeorm, mysql2 모듈을 설치한다
yarn add @nestjs/typeorm typeorm mysql2
typeorm 에 연결하기 위해서는 App module에서 typeorm module을 import 해주면 된다 module을 import 할 때는 TypeOrm 모듈의 설정값이 동적이기 때문에 TypeOrm module을 forRoot 메소드를 이용해서 import 해준다
@Module({
imports: [
TypeOrmModule.forRoot({
type: 'mysql',
host: 'localhost',
port: 3306,
username: 'root',
password: '1234',
database: 'test',
entities: [],
synchronize: true, // false가 안전함
}),
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
synchronize 기능을 키면 작성된 entity와 database간의 씽크를 맞춰준다 해당 기능을 사용하면 편할 수 있지만 원하지 않는 시점에 database구조가 달라질 수 있으므로 주의해야한다 그래서 entity 설정과 씽크를 맞춰주기 위해서 typeorm cli 를 이용해서 원하는 시점에 sync를 맞추는게 좋다
//entity와 sync를 맞춰줌
yarn typeorm schema:sync
entity를 TypeOrm에 맵핑해주기 위해서는 entity의 위치를 설정 해줘야한다 우선 entity를 두개 정의해주겠다
nest g mo user
nest g mo order
각각 모듈 안에 entity 파일을 정의해 준다 user와 order가 1: n 구조를 가지는 단순한 구조이다
import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from 'typeorm';
import { Order } from '../order/order.entity';
@Entity('USER')
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column({
type: 'text',
name: 'NAME',
})
name: string;
@OneToMany(() => Order, (order) => order.user)
orders: Order[];
}
import { Column, Entity, ManyToOne, PrimaryGeneratedColumn } from 'typeorm';
import { User } from '../user/user.entity';
@Entity('ORDER')
export class Order {
@PrimaryGeneratedColumn()
id: number;
@Column({
type: 'int',
name: 'PRICE',
})
price: number;
@ManyToOne(() => User, (user) => user.orders)
user: User;
}
각 entity를 정의해 주고 프로젝트를 실행하면 entity를 찾지 못한다는 오류가 나오게 된다 그래서 entity의 위치를 기본 설정에 추가해줘야한다 entity의 위치를 설정하는 방법은 여러가지 있지만 여기서는 path를 이용한 패턴을 사용했다
@Module({
imports: [
TypeOrmModule.forRoot({
type: 'mysql',
host: 'localhost',
port: 3306,
username: 'root',
password: '1234',
database: 'test',
entities: [join(__dirname, '/**/*.entity.ts')],// 설정 부분
synchronize: true,
}),
UserModule,
OrderModule,
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
위와 같이 기입하고 프로젝트를 실행하면 sychronize 가 켜져 있기 때문에 정의한 entity가 db에 생기는 것을 볼 수 있다
TypeOrm을 NestJS 와 연결하기 위해서 데이터 베이스의 호스트와 비밀번호 등 여러 보안 정보를 입력해야되기 때문에 이를 위해서 주로 env 파일을 이용한다 nestjs 에서는 이런 환경 변수 설정을 해주기 위해서 config 모듈을 지원한다 이 모듈을 설치하면 기존에 알고 있던 dotenv 모듈도 같이 설치 된다
yarn add @nestjs/config
이제 env 파일과 NestJS에서 config 설정을 해줄 파일을 아래와 같이 생성해 준다
DATABASE_USER=root
DATABASE_PASSWORD=1234
DATABASE_PORT=3306
DATABASE_HOST=localhost
DATABASE_NAME=test
import 'dotenv/config';
export default () => ({
database: {
host: process.env.DATABASE_HOST || '',
user: process.env.DATABASE_USER || '',
name: process.env.DATABASE_NAME || '',
password: process.env.DATABASE_PASSWORD || '',
port: process.env.DATABASE_PORT || '',
},
});
위와 같이 설정하면 이제 App 모듈 내부에서 import 해주는 작업을 해줘야 한다 cofig 모듈을 import 할때 configuration 파일을 불러온다 이제 App 모듈 내부에서는 configService를 이용해서 환경변수에 접근할 수 있다
TypeOrm 모듈을 import 할 당시는 아직 CofigModule이 AppModule에 적용되기 전이기 때문에 아래와 같이 모듈 설정에서 import와 configServeice inject까지 해줘야 한다
@Module({
imports: [
ConfigModule.forRoot({ // configuration 설정을 coifg module 불러 올 때 로드한다
isGlobal: true,
load: [configuration],
}),
TypeOrmModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (configService: ConfigService) => ({
type: 'mysql',
host: configService.get('database.host'),
port: configService.get('database.port'),
username: configService.get('database.user'),
password: configService.get('database.password'),
database: configService.get('database.name'),
entities: [join(__dirname, '/**/*.entity.js')],
synchronize: true,
}),
}),
UserModule,
OrderModule,
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}