Nest.js + TypeORM + PostgreSQL 시작하기

readbetweenthelines
10 min readMay 1, 2020

--

삽질 해보기

Nest 프로젝트에 TypeORM 을 설정하는 방법을 살펴봅니다. PostgreSQL 는 Heroku에 생성하고 Nest + Next 프로젝트 템플릿을 기반으로 시작했습니다.

프로젝트 생성

Nest.js + Next.js 프로젝트 생성 소스 디렉토리를 설정합니다.

// 변경 대상 : /src  -> /server
package.json
nest-cli.json
tsconfig.server.json

라이브러리 설치

// ORM 과 DB 드라이버
$ npm install @nestjs/typeorm typeorm pg
$ npm install ts-node -g

DB 연결 설정

DB 접속 환경에 따른 TypeORM 설정을 작성합니다.

// ormconfig.json
{
"type": "postgres",
"host": _DB_HOST_,
"port": _DB_PORT_,
"username": _DB_USER_,
"password": _DB_PASSWORD_,
"database": _DB_NAME_,
"extra": {
"ssl": {
"rejectUnauthorized": false
}
},
"synchronize": true,
"logging": false,
"entities": [
"server/**/*.entity.ts"
],
"migrations": [
"server/migration/**/*.ts"
],
"subscribers": [
"server/subscriber/**/*.ts"
],
"cli": {
"migrationsDir": "server/migration",
"subscribersDir": "server/subscriber"
}
}

TypeORM CLI 에서 사용합니다.

마이그레이션 스크립트를 추가합니다.

// package.json
...
"make:migrations": "ts-node -P tsconfig.server.json ./node_modules/typeorm/cli.js migration:generate -n",
"make:migrate": "ts-node -P tsconfig.server.json ./node_modules/typeorm/cli.js migration:run",
"make:rollback": "ts-node -P tsconfig.server.json ./node_modules/typeorm/cli.js migration:revert",
...

App 모듈에 TypeORM 모듈을 연결합니다.

// server/app.module.ts
import { TypeOrmModule } from '@nestjs/typeorm';
...
@Module({
imports: [
TypeOrmModule.forRoot({
"type": "postgres",
"host": __DB_HOST__,
"port": __DB_PORT__,
"username": __DB_USER__,
"password": __DB_PASSWORD__,
"database": __DB_NAME__,
"extra": {
"ssl": {
"rejectUnauthorized": false
}
},
"entities": [],
}),
...

Nest 서버 구동시 참조됩니다.

모델 생성

먼저 도메인을 하나 생성합니다.

$ nest generate module user
$ nest generate service user
$ nest generate controller user

위에 생성된 디렉토리에 User 엔터티를 작성합니다. (관계 생성 없음)

// server/user/user.entity.ts
import {Entity, PrimaryGeneratedColumn, Column} from "typeorm";
@Entity()
export class User extends BaseEntity {
@PrimaryGeneratedColumn()
id!: number;
@Column({ nullable: false })
firstName!: string;
@Column({ nullable: false })
lastName!: string;
@Column()
age?: number;
}

마이그레이션을 수행하여 User 테이블을 생성합니다.

$ npm run make:migrations CreateUser
$ npm run make:migrate

DataGrip 나 pgAdmin 으로 생성된 테이블을 확인합니다.

User 모듈에서 서비스와 컨트롤러에서 사용할 수 있도록 User 모델을 연결합니다.

// server/user/user.module.ts
@Module({
...
imports: [TypeOrmModule.forFeature([User])]
})

App 모듈에 TypeORM 엔터티에도 추가합니다.

...
@Module({
imports: [
TypeOrmModule.forRoot({
...
"entities": [User],
}),
...

리파지토리 생성

// server/user/user.repository.ts
import { EntityRepository, Repository } from "typeorm";
import { User } from "./user.entity";
@EntityRepository(User)
export class UserRepository extends Repository<User> {
}

User 엔터티에 접근할 수 있는 기본적인 인터페이스를 작성합니다.

데이터 객체도 하나 작성합니다.

// server/user/user.dto.ts
export class UserDto {
firstName!: string;
lastName!: string;
age?: number;
}

서비스 생성

UserRepository 기본 메소드를 이용하여 간단한 CRUD 로직을 작성합니다.

...
import { UserRepository } from "./user.repository";
@Injectable()
export class UserService {
constructor(
@InjectRepository(User) private readonly userRepository: UserRepository
) { }
createOneUser(createUserDto: UserDto) {
return this.userRepository.create(createUserDto).save();
}
getUserList() {
return this.userRepository.find({
select: ["id", "firstName", "lastName", "age"],
});
}
getDetailUser(id: number) {
return this.userRepository.findOne(id);
}
updateUser(id: number, updateUserDto: UserDto) {
return this.userRepository.update(id, updateUserDto);
}
removeOneUser(id: number) {
return this.userRepository.delete(id);
}
}

컨트롤러 생성

API 로 노출할 컨트톨러를 작성합니다.

...
import { UserService } from "./user.service";
import { UserDto } from "./user.dto";
@Controller("users")
export class UserController {
constructor(private readonly userService: UserService) { }
@Post()
async create(@Body() createUserDto: UserDto) {
const result = await this.userService.createOneUser(createUserDto);
return result;
}
@Get()
async getList() {
const result = await this.userService.getUserList();
return result;
}
@Get(":id")
async getOne(@Param("id") userId: number) {
const result = await this.userService.getDetailUser(userId);
return result;
}
@Put(":id")
async setOne(@Param("id") userId: number, @Body() updateUserDto: UserDto) {
const result = await this.userService.updateUser(userId, updateUserDto);
return result;
}
@Delete(":id")
async removeOne(@Param("id") userId: number) {
const ret = await this.userService.removeOneUser(userId);
return ret;
}
}

API 클라이언트로 확인해봅니다.

이제 산책하러 가야겠습니다.

참고

--

--