프로젝트를 진행하기 위해서 NestJS로 만드는 프로젝트에서 DB를 어떻게 연동해야할 것인가?
어떻게 모델을 만드는 코드를 짜는 것인가? 가 궁금증이었다. 당연히 그냥 SQL파일로 SQL문을 써서 만들 수 있겠지만,
언제나 그렇듯 ORM을 이용하면 좀 더 간편하게 DB를 설계할 수 있다.
이번 포스팅에서는 NestJS프로젝트에서 TypeORM을 이용한 DB구축을 포스팅해보자
일단 NestJS 공식페이지에 매우 잘 나와있다. 분량이 많으니까 필요한 부분만 포스팅하게 되면,
$ npm i --save @nestjs/typeorm typeorm mysql2
설치를 완료하면 app.module.ts 파일에서 TypeOrmModule을 임포트해서 MySQL을 연동할 수 있다.
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
@Module({
imports: [
TypeOrmModule.forRoot({
type: 'mysql',
host: 'localhost',
port: 3306,
username: 'root',
password: 'root',
database: 'test',
entities: [],
synchronize: true,
}),
],
})
export class AppModule {}
😀NestJS 공식문서에서는 프로덕션에서 synchronize: true설정을 사용하면 안 된다고 강조한다. 즉 배포환경에서는 false값으로 사용하라는 의미이다.
synchronize: true 이 옵션은 TypeORM이 애플리케이션과 데이터베이스 스키마 사이의 차이를 확인하고 자동으로 데이터베이스 스키마를 동기화하는 기능의 유무를 가르킨다. 따라서 개발환경에서 스키마의 변경은 자주 이루어져 true설정인 반면에, 배포환경에서 스키마의 자동변경은 에러를 뿜는 위험을 초래할 수 있기 때문에 되도록 false설정을하고 수동 업데이트 하는 것을 추천한다.
하지만 우리는 DB 설정시 .env파일의 환경변수를 기반으로 username이나 host, password등은 보안상의 이유로 노출하지 않는다.
따라서 DB환경변수 설정을 위한 NestJS용 config라이브러리를 설치한다. NestJS는 따로 dotenv라이브러리를 설치할 필요 없이 config모듈을 사용하면 .env파일에 접근할 수 있다.
$ npm i @nestjs/config
다음으로 config모듈을 임포트해서 사용하면 .env파일에 접근할 수 있다.
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { CatsModule } from './cats/cats.module';
import { TypeOrmModule } from '@nestjs/typeorm';
import { ConfigModule } from '@nestjs/config';
@Module({
imports: [
ConfigModule.forRoot(),//confing모듈을 통한 process.env객체 사용
TypeOrmModule.forRoot({
type: 'mysql',
host: process.env.DB_HOST,
port: parseInt(process.env.PORT),
username: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.NESTJS_PRAC1,
entities: [],
synchronize: true,
}),
CatsModule,
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
다음으로 데이터의 모델(스키마)를 정의하고 컬럼을 설정해보자
//src/cats/entity
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
@Entity()
export class Cat {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@Column()
age: number;
@Column()
breed: string;
}
간단한 Cat모델 작성방법이다. @Entity데코레이터를 이용해서 Cat클래스를 정의하고, @PrimaryGeneratedColumn() 자동생성되는 기본키와, 나머지 컬럼들의 타입을 지정해준다. Entity를 완성하면 다시 app.module로 돌아가서 import한 TypeOrmMoudule내용중 entities에 정의한 클래스를 추가해준다.
TypeOrmModule.forRoot({
type: 'mysql',
host: process.env.DB_HOST,
port: parseInt(process.env.PORT),
username: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.NESTJS_PRAC1,
entities: [Cat], // 이부분
synchronize: true,
}),
여기 까지만 하면 DB의 연결과 모델의 설정은 완료되지만, 실직적으로 DB에 접근해서 CRUD를 하는 곳은 현재 Cat과 관련된 모듈이기때문에 Cat모듈에서 해당 Entity를 이용하기 위해서는 해당모듈이 임포트해줘야한다.
import { Module } from '@nestjs/common';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Cat } from './entity/cats.entity';
@Module({
imports: [TypeOrmModule.forFeature([Cat])], //해방모듈에서 Cat Entity를 사용하기 위해 Import
exports: [TypeOrmModule], // 다름 모듈에서도 CatModule의 데이터베이스 설정을 공유하기 위해 export
controllers: [CatsController],
providers: [CatsService],
})
export class CatsModule {}
23-05-12 추가 내용
MySQL에 담길 컬럼들의 타입과 옵션들을 명시적으로 설정해주자. 컬럼의 옵션설정은 TypeORM공식 페이지에 잘 작성되어 있다.
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
@Entity()
export class Cat {
@PrimaryGeneratedColumn()
id: number;
@Column({ type: 'varchar' })
name: string;
@Column({ type: 'int' })
age: number;
@Column({ type: 'varchar' })
breed: string;
@Column({ type: 'varchar', nullable: true})
characteristic: string;
}
필자는 MySQL을 사용했기 때문에 MySQL에서 사용하는 타입들을 적어주었다.
Column types for mysql / mariadb
bit, int, integer, tinyint, smallint, mediumint, bigint, float, double, double precision, dec, decimal, numeric, fixed, bool, boolean, date, datetime, timestamp, time, year, char, nchar, national char, varchar, nvarchar, national varchar, text, tinytext, mediumtext, blob, longtext, tinyblob, mediumblob, longblob, enum, set, json, binary, varbinary, geometry, point, linestring, polygon, multipoint, multilinestring, multipolygon, geometrycollection, uuid, inet4, inet6
타입이외의 옵션들은 또 아래와 같이 설명해준다.
type: ColumnType- 열 유형. 위에 나열된 유형 중 하나입니다 .
name: string- 데이터베이스 테이블의 열 이름. 기본적으로 열 이름은 속성 이름에서 생성됩니다. 자신의 이름을 지정하여 변경할 수 있습니다.
length: number- 열 유형의 길이. 예를 들어 유형을 생성하려는 경우 varchar(150)열 유형 및 길이 옵션을 지정합니다.
width: number- 열 유형의 표시 너비. MySQL 정수 유형 에만 사용됨
onUpdate: string- ON UPDATE방아쇠. MySQL 에서만 사용됩니다 .
nullable: boolean- 컬럼을 만들 NULL거나 NOT NULL데이터베이스에 저장합니다. 기본적으로 열은 입니다 nullable: false.
update: boolean- "저장" 작업으로 열 값이 업데이트되었는지 여부를 나타냅니다. false인 경우 개체를 처음 삽입할 때만 이 값을 쓸 수 있습니다. 기본값은 입니다 true.
insert: boolean- 개체를 처음 삽입할 때 열 값이 설정되었는지 여부를 나타냅니다. 기본값은 입니다 true.
select: boolean- 쿼리를 만들 때 기본적으로 이 열을 숨길지 여부를 정의합니다. 로 설정하면 false열 데이터가 표준 쿼리와 함께 표시되지 않습니다. 기본적으로 열은select: true
default: string- 데이터베이스 수준 열의 DEFAULT값을 추가합니다.
primary: boolean- 열을 기본으로 표시합니다. 를 사용하는 경우에도 마찬가지입니다 @PrimaryColumn.
unique: boolean- 열을 고유 열로 표시합니다(고유 제약 조건 생성).
comment: string- 데이터베이스의 컬럼 코멘트. 모든 데이터베이스 유형에서 지원되지는 않습니다.
precision: number- 10진수(정확한 숫자) 열(10진수 열에만 적용)의 정밀도는 값에 대해 저장되는 최대 자릿수입니다. 일부 열 유형에서 사용됩니다.
scale: number- 십진수(정확한 숫자) 열(십진수 열에만 적용)의 배율은 소수점 오른쪽의 자릿수를 나타내며 정밀도보다 크지 않아야 합니다. 일부 열 유형에서 사용됩니다.
zerofill: boolean- ZEROFILL숫자 열에 속성을 넣습니다. MySQL에서만 사용됩니다. 인 경우 trueMySQL은 UNSIGNED이 열에 속성을 자동으로 추가합니다.
unsigned: boolean- UNSIGNED숫자 열에 속성을 넣습니다. MySQL에서만 사용됩니다.
charset: string- 열 문자 집합을 정의합니다. 모든 데이터베이스 유형에서 지원되지는 않습니다.
collation: string- 열 데이터 정렬을 정의합니다.
enum: string[]|AnyEnum- enum허용되는 enum 값 목록을 지정하기 위해 열 유형에 사용됩니다. 값 배열을 지정하거나 enum 클래스를 지정할 수 있습니다.
enumName: string- 사용된 enum의 이름을 정의합니다.
asExpression: string- 생성된 열 표현식. MySQL 에서만 사용됩니다 .
generatedType: "VIRTUAL"|"STORED"- 생성 열 유형. MySQL 에서만 사용됩니다 .
hstoreType: "object"|"string"- 열의 반환 유형 HSTORE. 문자열 또는 객체로 값을 반환합니다. Postgres 에서만 사용됩니다 .
array: boolean- 배열일 수 있는 postgres 및 cockroachdb 열 유형에 사용(예: int[])
transformer: { from(value: DatabaseType): EntityType, to(value: EntityType): DatabaseType }- 임의 유형의 속성을 데이터베이스에서 지원하는 EntityType유형으로 마샬링하는 데 사용됩니다. DatabaseType변환기 배열도 지원되며 쓸 때는 자연스러운 순서로, 읽을 때는 역순으로 적용됩니다. 예를 [lowercase, encrypt]들어 먼저 문자열을 소문자로 만든 다음 쓸 때 암호화하고 읽을 때 암호를 해독한 다음 아무것도 하지 않습니다.