이번 포스트에서는 controller와 service를 생성하고 NestJS와 mysql을 연결하는 방법을 알아본다.
nest js cli
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
┌───────────────┬─────────────┬──────────────────────────────────────────────┐
│ name │ alias │ description │
│ application │ application │ Generate a new application workspace │
│ class │ cl │ Generate a new class │
│ configuration │ config │ Generate a CLI configuration file │
│ controller │ co │ Generate a controller declaration │
│ decorator │ d │ Generate a custom decorator │
│ filter │ f │ Generate a filter declaration │
│ gateway │ ga │ Generate a gateway declaration │
│ guard │ gu │ Generate a guard declaration │
│ interceptor │ in │ Generate an interceptor declaration │
│ interface │ interface │ Generate an interface │
│ middleware │ mi │ Generate a middleware declaration │
│ module │ mo │ Generate a module declaration │
│ pipe │ pi │ Generate a pipe declaration │
│ provider │ pr │ Generate a provider declaration │
│ resolver │ r │ Generate a GraphQL resolver declaration │
│ service │ s │ Generate a service declaration │
│ library │ lib │ Generate a new library within a monorepo │
│ sub-app │ app │ Generate a new application within a monorepo │
│ resource │ res │ Generate a new CRUD resource │
└───────────────┴─────────────┴──────────────────────────────────────────────┘
터미널에 nest를 치면 나오는 설명이다.
Module
1
2
nest generate module users
nest g mo users
users의 모듈을 위 명령으로 생성한다.
Controller
1
2
nest generate controller users
nest g co users
controller를 명령어로 생성할 수 있다. 위 명령어를 사용하게 되면 users.controller.ts파일과 users.controller.spec.ts 파일이 생성된다. spec파일은 여기서 설명하지 않는다.
Service
1
2
nest generate service users
nest g s users
위 명령어를 사용하게 되면 users service를 생성 할 수 있다. service 또한 spec 파일이 생성된다.
mysql 연결하기
데이터베이스는 mysql을 사용하고 데이터베이스를 조작하는 도구는 typeorm 을 사용한다.
1
yarn add mysql2 typeorm @nestjs/typeorm
User 테이블 정의하기
사용자 테이블을 간단하게 구성하고 테스트 할 것이다. user를 정의해 보자. users 폴더 안에 entities 폴더를 만들고 user.entity.ts를 생성한다.
user.entity.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import { Exclude } from "class-transformer";
import { Column, Entity, PrimaryGeneratedColumn } from "typeorm";
@Entity()
export class Users {
@PrimaryGeneratedColumn("increment")
id: number;
@Column()
user_id: string;
@Column()
name: string;
@Column()
email: string;
@Column()
password: string;
}
user 테이블을 정의한다. User class 위에 @Entity
데코레이터를 추가해서 테이블임을 명시한다. typeorm의 데코레이터로 테이블의 속성을 정의한다.
database module
1
nest g mo database
위 명령어로 database module을 생성한다. database 폴더에 database.provider.ts를 생성한다.
config
default.yml
1
2
3
4
5
6
7
8
9
10
server:
port: 5001
db:
type: "mysql"
port: 3306
database: "schema name"
jwt:
expiresIn: 3600
development.yml
1
2
3
4
5
6
7
8
db:
host: "34.64.45.109"
username: "root"
password: "pw"
synchronize: true
jwt:
secret: "mysecret123key"
production.yml
1
2
db:
synchronize: false
src/config 폴더 안에 3개의 yml 파일을 정의한다. development와 production 환경에 대한 실행은 이후에 추가할 예정이다.
typeormconfig.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import * as config from "config";
const dbConfig = config.get("db"); // db config
export const typeormGCP: DataSourceOptions = {
type: dbConfig["type"],
host: dbConfig["host"],
port: dbConfig["port"],
username: dbConfig["username"],
password: dbConfig["password"],
database: dbConfig["database"],
entities: ["dist/**/*.entity{.ts,.js}"],
synchronize: true,
};
typeorm을 사용해 데이터베이스를 정의하게 되며, dotenv 대신 config를 사용했다. config 환경 설정은 yml파일로 정의한다.
database.provider.ts
1
2
3
4
5
6
7
8
9
10
11
12
import { DataSource } from "typeorm";
import { typeormGCP, typeormLocal } from "typeormconfig";
export const databaseProviders = [
{
provide: "DATA_SOURCE",
useFactory: async () => {
const dataSource = new DataSource(typeormGCP);
return dataSource.initialize();
},
},
];
위 코드는 sequelize 연결을 위한 코드이다. addModels에 정의한 entity를 넣어준다. export 한 provider는 database.module.ts에서 받아준다.
database.module.ts
1
2
3
4
5
import { Module } from "@nestjs/common";
import { databaseProviders } from "./database.provider";
@Module({ providers: [...databaseProviders], exports: [...databaseProviders] })
export class DatabaseModule {}
database 모듈 코드다. 모듈에 exports가 있는데, 제공받은 클래스를 export 해 준다. 여기까지 database 모듈에 대한 정의가 끝났다.
app.module.ts
1
2
3
4
5
6
7
8
9
10
import { Module } from "@nestjs/common";
import { UsersModule } from "./users/users.module";
import { DatabaseModule } from "./database/database.module";
@Module({
imports: [UsersModule, DatabaseModule],
controllers: [],
providers: [],
})
export class AppModule {}
app 모듈에 database 모듈을 import 해준다. 위에서 생성한 users 모듈도 같이 import 한다.
users.provider.ts
1
2
3
4
5
6
7
8
9
import { DataSource } from "typeorm";
import { Users } from "./users.entity";
export const userProviders = [
{
provide: "USERS_REPOSITORY",
useFactory: (dataSource: DataSource) => dataSource.getRepository(Users),
inject: ["DATA_SOURCE"],
},
];
service의 constructor에 정의될 provider이다. provider의 이름과 사용할 entity를 명시해 준다.
users.service.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import { Inject, Injectable } from '@nestjs/common';
import { Repository } from 'typeorm';
import { insertUserDto } from './dto/insert.user.dto';
import { saveUserDto } from './dto/save.user.dto';
import { Users } from './users.entity';
import { v4 as uuid } from 'uuid';
import * as bcrypt from 'bcryptjs';
import { signIn } from './dto/signin.dto';
@Injectable()
export class UsersService {
constructor(
@Inject('USERS_REPOSITORY')
private userRepository: Repository<Users>,
) {}
async create(userDto: insertUserDto): Promise<Users> {
// 사용자 등록 register
const user_id = uuid();
const salt = await bcrypt.genSalt();
const hashedPassword = await bcrypt.hash(userDto.password, salt);
const newUser: saveUserDto = {
user_id: user_id,
name: userDto.name,
email: userDto.email,
password: hashedPassword,
};
const user = this.userRepository.save({ ...newUser });
return user;
}
}
service에서는 데이터베이스에 직접 쿼리를 던진다. constructor에 데이터베이스의 provider를 정의한다. 이제UserService에서 User 테이블의 구조에 접근할 수 있다.
users.controller.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import {
Body,
Controller,
Get,
HttpStatus,
Post,
Res,
UseFilters,
UsePipes,
ValidationPipe,
} from '@nestjs/common';
import { ApiBody, ApiResponse, ApiTags } from '@nestjs/swagger';
import { HttpExceptionFilter } from 'src/exception/http-exception.filter';
import { insertUserDto } from './dto/insert.user.dto';
import { signIn } from './dto/signin.dto';
import { Users } from './users.entity';
import { UsersService } from './users.service';
@ApiTags('users')
@Controller('users')
export class UsersController {
constructor(private readonly userService: UsersService) {}
@UseFilters(HttpExceptionFilter) //error handle
@Post('registration') // http method
@UsePipes(ValidationPipe) // validation pipe
@ApiBody({ type: insertUserDto }) // swagger body
@ApiResponse({ status: 200, description: 'user created' })
async createUser(
@Res() res: any,
@Body() userDto: insertUserDto,
): Promise<void> {
// 사용자 회원가입
const user = await this.userService.create(userDto);
res.status(HttpStatus.OK).json(user);
}
}
controller에서는 라우팅 경로 정의와 client에서 넘어오는 값의 유효성 검사, api 문서 등을 정의할 수 있다. nest js의 데코레이터를 사용하게 됩니다. 자세한 동작에 대해서는 이후 포스팅에서 설명한다.
nest js에서 기본적인 3계층 구조를 봤다. 이제 client의 요청에 따른 동작을 구현할 수 있다. 다음 포스팅에서는 swagger api 문서를 생성하는 방법을 알아본다.