npm i -g @nestjs/cli
nest new project-name
nest g module boards
nest: using nestcli
g: generate
module: schematic that I want to create
boards: name of the schematic
nest g controller boards --no-spec
—no-spec: 테스트코드 x
contoller생성 과정
nest g service boards --no-spec
boards service 를 boards controller에서 이용할 수 있게 해주기(Dependency injection)
@Controller('board')
export class BoardController {
**constructor(private boardService: BoardService){}**
}
constructor(private boardService: BoardService){}
Pipes, Filters, Gaurds, Interceptors 등의 미들웨어로 취급되는 것들이 있는데 각각 다른 목적을 가지며 사용되고 있습니다.
pies: 파이프는 요청 유효성 검사 및 페이로드 변환을 위한 만들어집니다. 데이터를 예상한 대로 직렬화합니다.
Filters: 필터는 오류 처리 미들웨어입니다. 특정 오류 처리기를 사용할 경로와 각 경로 주변의 복잡성을 관리하는 방법을 알 수 있습니다.
Guards: 가드는 인증 미들웨어입니다. 지정된 경로로 통과할 수 있는 사람과 허용되지 않는 사람을 서버에 알려줍니다.
Interceptors: 인터셉터는 응답 매핑 및 캐시 관리와 함께 요청 로깅과 같은 전후 미들웨어입니다. 각 요청 전후에 이를 실행하는 기능은 매우 강력하고 유용합니다.
middleware → guard → interceptor(before)→ pipe → controller → service → controller → interceptor(after) → filter(if application) → client
파이프는 @injectable() 데코레이터로 주석이 달린 클래스이다.
파이프는 data transformation과 data validation을 위해서 사용 됩니다.
파이프는 컨트롤러 경로 처리기에 의해 처리되는 인수에 대해 작동합니다.
nest는 메소드가 호출되기 직전에 파이프를 삽입하고 파이프는 메소드로 향하는 인수를 수신하고 이에 대해 작동합니다.
라우트 핸들러가 처리하는 인수에 대해서 작동합니다.
그리고 파이프는 메소드를 바로 직전에 작동해서 메소드를 향하는 인수에 대해서 변환할 것이 있으면 변환하고 유효성 체크를 위해서도 호출됩니다.
파이프를 사용하는 방법(Bingding pipes)은 ㅅ가지로 나눠질 수 있습니다.
Handler-level Pipes, Parameter-levelPipes, Global-level Pipes입니다.
이름에서 말하는 것 그대로 핸들러 레벨, 파라미터 레벨, 글로벌 레벨로 파이프 사용할 수 있습니다.
Handler-level Pipes
@Post()
@UsePipes(pipe)
createBoard(
@Body('title') title,
@Body('decription') description
) {
}
Parameter-level Pipes
파라미터 레벨의 파이프 이기에 특정한 파라미터에게만 적용이 되는 파이프
아래와 같은 경우에는 title만 파라미터 파이프가 적용됩니다.
@Post()
createBoard(
@Body('title', ParameterPipe) title,
@Body('description') description
) {
}
Global Pipes
글로벌 파이프로서 애플리케이션 레벨의 파이프입니다.
클라이언트에서 들어오는 모든 요청에 적용이 됩니다.
가장 상단 영역이 main.ts에 넣어주시면 됩니다
async function boostrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(GlobalPipes);
await app.listen(3000);
}
bootstrap();
Built-in Pipes
Nest JS에 기본적으로 사용할 수 잇게 만들어놓은 6가지 파이프가 있습니다.
ex) ParseInPipe
@Get(':id')
findOne(@Param('id', ParseIntPipe) id:number) {
return;
}
class-validator, class-transformer
npm install class-validator class-transformer --save
Documentation 페이지
먼저 Pipe Transfrom이란 인터페이스를 새롭게 만들 커스텀 파이프에 구현해줘야 합니다 이 Pipe Transfrom 인터페이스는 모든 파이프에서 구현해줘야 하는 인터페이스입니다. 그리고 이것과 함께 모든 파이프는 transfrom()메소드를 필요합니다. 이 메소드는 NestJS가 인자(arguments)를 처리하기 위해서 사용됩니다.
이 메소드느 두 개의 파라미터를 가집니다.
첫번째 파라미터는 처리가 된 인자의 값(value)이며 두 번째 파라미터는 인자에 대한 메타 데이터를 포함한 객체입니다.
transfrom()메소드에서 Return된 값은 Route핸들러로 전해집니다.만약 예외가 발생하면 클라이언트에 바로 전해집니다.
파라미터 수준의 파이프라인 추가
현재 특정 게시물을 ID로 가져올때 만약 없는 아이디의 계시물을 가져오려고 한다면 결과값으로 아무 내용 없이 돌아옵니다. 그래서 그 부분을 게시물이 없는 것이면 없다고 내용을 넣어서 클라이언트로 보내 주겠습니다
찾는 게시물이 없을 때는
예외 인스턴스를 생성해서 이용해주면 됩니다
https://www.postgressql.org/download/windows/
https://postgresapp.com/downloads.html
https://www.pgadmin.org/download
npm install pg typeorm @nestjs/typeorm --save
https://docs.nestjs.com/techniques/database
typeORM 설정 파일 생성
typeORM설정파일 작성
import { TypeOrmModuleOptions } from '@nestjs/typeorm';
export const typeORMConfig: TypeOrmModuleOptions = {
type: 'postgres',
host: '',
port: 5432,
username: 'youngsup',
password: 'postgres',
database: 'board-app',
entities: [__dirname + '/../**/*.entity.{js,ts}'],
//배포시에는 false로 true값을 주면 애플리케이션을 다시 실행할때 엔티티 안에서 수정된 컬럼의 길이 타입 변경값 등을 해당 테이블을 Drop한 후 다시 생성합니다.
synchronize: true,
};
루트 Module에서 Import합니다.
forRoot안에 넣어준 설정(configuration)은 모든 Sub-Module 부수적인 모듈들에 다 적용이 됩니다.
엔티티 생성 코드
@Entity()
export class Board extends BaseEntity {
@PrimaryGeneratedColumn()
id: number;
@Column()
title: string;
@Column()
description: string;
@Column()
status: BoardStatus;
}
리포지토리는 엔티티 개체와 함께 작동하며 엔티티 찾기, 삽입, 업데이트, 삭제 등을 처리합니다.
공식 문서 주소
http://typeorm.delightful.studio/classes/_repository_repository_repository.html
import { Repository } from 'typeorm';
import { Board } from 'src/boards/boards.entity';
export class BoardRepository extends Repository<Board>.extend({
// 커스텀 메세지 추가 가능
}) {
}
import { Module } from '@nestjs/common';
import { BoardsController } from './boards.controller';
import { BoardsService } from './boards.service';
import { TypeOrmModule } from '@nestjs/typeorm';
import { BoardRepository } from 'src/boards/board.repository';
@Module({
**imports: [TypeOrmModule.forFeature([BoardRepository])],**
controllers: [BoardsController],
providers: [BoardsService],
})
export class BoardsModule { }
@Injectable()
export class BoardsService {
constructor(
@InjectRepository(BoardRepository)
private boardRepository: BoardRepository,
) { }
@injectRepository
typeORM에서 제공하는 findOne메소드 사용
document
https://github.com/typeorm/typeorm/blob/master/docs/repository-api.md
board.service.ts
async deleteBoardById(id: number): Promise<void> {
const result = await this.boardRepository.delete(id);
if (result.affected === 0) {
throw new NotFoundException(`Can't find board with id ${id}`);
}
console.log('result', result);
}
CLI를 이용한 모듈, 컨트롤러, 서비스 생성
nest g module auth
nest g controller auth --no-spec
nest g service auth --no-spec
import { BaseEntity, Column, Entity, PrimaryGeneratedColumn } from "typeorm";
@Entity()
export class User extends BaseEntity {
@PrimaryGeneratedColumn()
id: number;
@Column()
username: string;
@Column()
password: string;
}
import { User } from 'src/auth/user.entity';
import { EntityRepository, Repository } from 'typeorm';
@EntityRepository(User)
export class UserRepository extends Repository<User> {
}
auth.module.ts
import { Module } from '@nestjs/common';
import { AuthController } from './auth.controller';
import { AuthService } from './auth.service';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from 'src/auth/user.entity';
@Module({
imports: [TypeOrmModule.forFeature([User])],
controllers: [AuthController],
providers: [AuthService],
})
export class AuthModule { }
User Repository를 auth Service안에서 사용하기 위해 유저 리포지터리를 넣어주기
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { UserRepository } from 'src/auth/user.repository';
@Injectable()
export class AuthService {
constructor(
@InjectRepository(UserRepository)
private userRepository: UserRepository) {
}
}
user.repository.ts
import { AuthCredentialsDto } from 'src/auth/dto/auth-credential.dto';
import { User } from 'src/auth/user.entity';
import { EntityRepository, Repository } from 'typeorm';
@EntityRepository(User)
export class UserRepository extends Repository<User> {
async createUser(authCredentialsDto: AuthCredentialsDto): Promise<User> {
const { username, password } = authCredentialsDto;
const user = this.create({
username,
password,
});
await this.save(user);
return user;
}
}
auth/dto/auth-credential.dto.ts
export class AuthCredentialsDto {
username: string;
password: string;
}
user.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { AuthCredentialsDto } from 'src/auth/dto/auth-credential.dto';
import { User } from 'src/auth/user.entity';
import { UserRepository } from 'src/auth/user.repository';
import { Repository } from 'typeorm';
@Injectable()
export class AuthService {
constructor(
@InjectRepository(User)
private userRepository: Repository<User>,
) { }
async signUp(authCredentialsDto: AuthCredentialsDto): Promise<void> {
const { username, password } = authCredentialsDto;
const user = this.userRepository.create({
username,
password,
});
await this.userRepository.save(user);
}
}
user.controller.ts
import { Body, Controller, Post } from '@nestjs/common';
import { AuthService } from 'src/auth/auth.service';
import { AuthCredentialsDto } from 'src/auth/dto/auth-credential.dto';
@Controller('auth')
export class AuthController {
constructor(private authService: AuthService) { }
//localhost:3000/auth/signup
@Post('/signup')
signUp(@Body() authCredentialsDto: AuthCredentialsDto): Promise<void> {
return this.authService.signUp(authCredentialsDto);
}
}
유저 데이터 유효성 체크
https://github.com/typestack/class-validator
Dto파일에서 Request로 들어오는 갑승ㄹ 정의해주고 있기때문에 Dto파일에 값들 하나하나에 class-validator를 이용해서 유효성 조건을 넣어주겠습니다.
import { IsString, Matches, MaxLength, MinLength } from "class-validator";
export class AuthCredentialsDto {
@IsString()
@MinLength(4)
@MaxLength(20)
username: string;
@IsString()
@MinLength(4)
@MaxLength(20)
//영어랑 숫자만 가능한 유효성 체크
@Matches(/^[a-zA-Z0-9]*$/, {
message: 'password only accepts english and numbers',
})
password: string;
}
signup 핸들러가 실행이 되기 전에 password와 username조건에 맞는지 확인해줌
@Post('/signup')
signUp(
@Body(ValidationPipe)
authCredentialsDto: AuthCredentialsDto
): Promise<void> {
return this.authService.signUp(authCredentialsDto);
}
repository에서 findeOne메소드를 이용해서 같은 유저 이름을 가진 아이디가 있는지 확인하고 없다면 데이터를 저장하는 방법입니다.
데이터베이스 레벨에서 만약 같은 이름을 가진 유저가 있다면 에러를 던져주는 방법
user.entity.ts에서 원하는 유니크한 값을 원하는 필드 값을 정해주면 됨
import { BaseEntity, Column, Entity, PrimaryGeneratedColumn, Unique } from "typeorm";
@Entity()
@Unique(['username'])
export class User extends BaseEntity {
@PrimaryGeneratedColumn()
id: number;
@Column()
username: string;
@Column()
password: string;
}
하지만 위와 같이만 구현을 해놓는다면 500에러가 나오게됨, try catch구문에서 잡지 않으면 controller레벨로 가서 500에러가 생겨버림
async signUp(authCredentialsDto: AuthCredentialsDto): Promise<void> {
const { username, password } = authCredentialsDto;
const user = this.userRepository.create({
username,
password,
});
try {
await this.userRepository.save(user);
} catch (error) {
if (error.code === '23505') {
throw new ConflictException('Existing username');
} else {
throw new InternalServerErrorException();
}
}
}
npm install bcryptjs --save
import * as bcrypt from 'bcryptjs'
user.repository.ts
const { username, password } = authCredentialsDto;
const salt = await bcrypt.genSalt();
const hashedPassword = await bcrypt.hash(password, salt);
const user = this.userRepository.create({
username,
password: hashedPassword,
});
user.service.ts
async signIn(authCredentialsDto: AuthCredentialsDto): Promise<string> {
const { username, password } = authCredentialsDto;
const user = await this.userRepository.findOne({ where: { username } });
if (user && (await bcrypt.compare(password, user.password))) {
return 'login success';
} else {
throw new UnauthorizedException('login failed')
}
}
Header
토큰에 대한 메타 데이터를 포함하고 있습니다.(타입, 해싱알고리즘 SHA256, RSA…)
Payload
유저정보(isuser), 만료기간(expiration time), 주제(subject) 등등…
Verify Signature
JWT의 마지막 세그먼트는 토큰이 보낸 사람에 의해 서명되었으며 어떤 식으로든 변겨오디지 않았는지 확인하는 데 사용되는 서명입니다. 서명은 헤더 및 페이로드 세그먼트, 서명 알고리즘, 비밀 또는 공개 키를 사용하여 생성됩니다.
@nestjs/jwt
@nestjs/passport
@types/passport-jwt 모듈
(passport-jwt모듈을 위한 타입 정의 모듈)
passport-jwt
npm install @nestjs/jwt @nestjs/passport passport passport-jwt --save
npm install @types/passport-jwt --save
auth모듈 imports에 넣어주기
auth.module.ts
import { Module } from '@nestjs/common';
import { AuthController } from './auth.controller';
import { AuthService } from './auth.service';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from 'src/auth/user.entity';
import { JwtModule } from '@nestjs/jwt';
import { PassportModule } from '@nestjs/passport';
@Module({
imports: [
PassportModule.register({ defaultStrategy: 'jwt' }),
JwtModule.register({
secret: 'Secret1234',
signOptions: {
expiresIn: 3600,
},
}),
TypeOrmModule.forFeature([User]),
],
controllers: [AuthController],
providers: [AuthService],
})
export class AuthModule { }
if (user && (await bcrypt.compare(password, user.password))) {
// 유저 토큰 생성 ( Secret + Payload )
// 중요한 정보는 넣으면 안됨
const payload = { username };
const accessToken = await this.jwtService.sign(payload);
return { accessToken };
}
Simple flow of how to remember and prove who I am
npm install @types/passport-jwt --save
jwt.strategy.ts파일 생성
import { Injectable, UnauthorizedException } from "@nestjs/common";
import { PassportStrategy } from "@nestjs/passport";
import { InjectRepository } from "@nestjs/typeorm";
import { ExtractJwt, Strategy } from "passport-jwt";
import { User } from "src/auth/user.entity";
import { Repository } from "typeorm";
//얘를 다른 곳에서도 주입해서 사용할 수 있도록 해당 데코레이터 추가
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor(
@InjectRepository(User)
private userRepository: Repository<User>,
) {
super({
secretOrKey: 'Secret1234',
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
});
}
async validate(payload) {
const { username } = payload;
const user: User = await this.userRepository.findOne({
where: { username },
});
if (!user) {
throw new UnauthorizedException();
}
return user;
}
}
AuthModule Providers항목에 넣어주고 다른 곳에서도 사용해야하기 때문에 exports항목에도 넣어줌
import { Module } from '@nestjs/common';
import { AuthController } from './auth.controller';
import { AuthService } from './auth.service';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from 'src/auth/user.entity';
import { JwtModule } from '@nestjs/jwt';
import { PassportModule } from '@nestjs/passport';
import { JwtStrategy } from 'src/auth/jwt.strategy';
@Module({
imports: [
PassportModule.register({ defaultStrategy: 'jwt' }),
JwtModule.register({
secret: 'Secret1234',
signOptions: {
expiresIn: 3600,
},
}),
TypeOrmModule.forFeature([User]),
],
controllers: [AuthController],
// authmodule에서 사용하기 위함
providers: [AuthService, JwtStrategy],
// 다른 모듈에서 이용하기 위함
exports: [JwtStrategy, PassportModule],
})
export class AuthModule { }
요청 안에 유저 정보(유저객체)가 들어가게 하는 방법
UseGuards안에 @nestjs/passport에서가져온 AuthGuard()를 이용하면 요청 안에 유저 정보를 넣어줄 수 있습니다.
UserGuards
@Post('/test')
// 유저 정보를 불러오기도 하고 인증에 대한 미들웨어 처리를 함, 토큰이 없거나 잘못된거라면 reject
@UseGuards(AuthGuard())
test(@Req() req) {
console.log(req.user);
}
}
요청 안에 유저가 바로 들어오도록 하기위해서
get-user.decorator.ts
import { ExecutionContext, createParamDecorator } from '@nestjs/common';
import { User } from 'src/auth/user.entity';
export const GetUser = createParamDecorator(
(data, ctx: ExecutionContext): User => {
const req = ctx.switchToHttp().getRequest();
return req.user;
},
);
switchToHttp()
: HTTP 특화된 컨텍스트 객체를 반환합니다.getRequest()
: 현재 HTTP 요청 객체를 반환합니다.getResponse()
: 현재 HTTP 응답 객체를 반환합니다.switchToWs()
: WebSocket 특화된 컨텍스트 객체를 반환합니다.getClient()
: WebSocket 클라이언트 연결을 반환합니다.getData()
: WebSocket 이벤트에서 전송된 데이터를 반환합니다.switchToRpc()
: Microservices 컨텍스트 객체를 반환합니다.getContext()
: Microservice 요청의 컨텍스트를 반환합니다. 예를 들어, gRPC 요청의 컨텍스트입니다.getArgs()
: RPC 요청의 인자들을 반환합니다.getClass()
: 현재 실행 중인 컨트롤러 클래스를 반환합니다.getHandler()
: 현재 실행 중인 핸들러 함수(예: 컨트롤러의 메서드)를 반환합니다.getType()
: 현재 컨텍스트의 타입을 반환합니다. 예를 들어, 'http', 'ws', 'rpc' 중 하나입니다.인증에 관한 모듈을 board모듈에서 쓸 수 있어야 하기에 board moduledptj 인증 모듈 imports해오기(이렇게 되면 AuthModule에서 export하는 어떠한 것이든 board Module에서 사용 가능하게 됩니다.)
import { Module } from '@nestjs/common';
import { BoardsController } from './boards.controller';
import { BoardsService } from './boards.service';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Board } from 'src/boards/boards.entity';
import { AuthModule } from 'src/auth/auth.module';
@Module({
imports: [TypeOrmModule.forFeature([Board]), AuthModule],
controllers: [BoardsController],
providers: [BoardsService],
})
export class BoardsModule { }
UseGuard(AuthGuard())를 이용해서 이 사람이 요청을 줄 때 올바른 토큰을 가지고 요청을 주는지 본 후에 게시물에 접근할 권한을 줍니다. 그리고 이 AuthGuard는 각각의 라우트 별로 줄 수도 있고 한번에 하나의 컨트롤러 안에 들어있는 모든 라우트에 줄 수도 있습니다. 현재는 board컨트롤러 안에 있는 모든 라우트에 AuthGuard를 적용해보겠습니다.
boards.controller.ts
@Controller('boards')
// 모든 핸들러가 영향을 받음
@UseGuards(AuthGuard())
export class BoardsController {
constructor(private boardsService: BoardsService) { }
user.entity.ts
import { Board } from "src/boards/boards.entity";
import { BaseEntity, Column, Entity, OneToMany, PrimaryGeneratedColumn, Unique } from "typeorm";
@Entity()
@Unique(['username'])
export class User extends BaseEntity {
@PrimaryGeneratedColumn()
id: number;
@Column()
username: string;
@Column()
password: string;
@OneToMany((type) => Board, (board) => board.user, { eager: true })
boards: Board[];
}
(type) => Board
: board의 타입,
board ⇒ board.user
: board에서 유저에 접근하려면 어떻게 해야하는지 명시
{eager:true}
: 이 유저정보를 가져올때 보드 정보도 같이 가져온다는 것
board.entity.ts
import { User } from 'src/auth/user.entity';
import { BoardStatus } from 'src/boards/board-stauts.enum';
import { BaseEntity, Column, Entity, ManyToOne, PrimaryGeneratedColumn } from 'typeorm';
@Entity()
export class Board extends BaseEntity {
@PrimaryGeneratedColumn()
id: number;
@Column()
title: string;
@Column()
description: string;
@Column()
status: BoardStatus;
@ManyToOne((type) => User, (user) => user.boards, { eager: false })
@JoinColumn({ name: "userId", referencedColumnName: "id" })
user: User;
}
board.controller.ts
@Post()
@UsePipes(ValidationPipe)
createBoard(
@Body() createBoardDto: CreateBoardDto,
@GetUser() user:User): Promise<Board> {
return this.boardsService.createBoard(createBoardDto, user);
}
board.service.ts
async createBoard(
createBoardDto: CreateBoardDto,
user: User): Promise<Board> {
const { title, description } = createBoardDto;
const board = this.boardRepository.create({
title,
description,
status: BoardStatus.PUBLIC,
user,
});
await this.boardRepository.save(board);
return board;
}
boards.controller.ts
@Get('/')
getAllBoards(@GetUser() user: User): Promise<Board[]> {
return this.boardsService.getAllBoards(user);
}
boards.service.ts
async getAllBoards(user: User): Promise<Board[]> {
const query = this.boardRepository.createQueryBuilder('board');
query.where('board.userId = :userId', { userId: user.id });
const boards = await query.getMany();
return boards;
}
boards.controller.ts
@Delete('/:id')
deleteBoard(
@Param('id', ParseIntPipe) id: number,
@GetUser() user: User,
): Promise<void> {
return this.boardsService.deleteBoardById(id, user);
}
boards.service.ts
async deleteBoardById(id: number, user: User): Promise<void> {
const result = await this.boardRepository.delete({id, user});
if (result.affected === 0) {
throw new NotFoundException(`Can't find board with id ${id}`);
}
console.log('result', result);
}
원하는 대로 환경에 따라서 로그의 레벨을 정의해서 넣어줄 수 있다.
main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { Logger } from '@nestjs/common';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(3000);
const port = 3000;
Logger.log(`Application running on port ${port}`);
}
bootstrap();
boards.controller.ts
@Controller('boards')
@UseGuards(AuthGuard())
export class BoardsController {
private logger = new Logger('BoardsController');
constructor(private boardsService: BoardsService) { }
@Get('/')
getAllBoards(@GetUser() user: User): Promise<Board[]> {
this.logger.verbose(`User ${user.username} trying to get all boards`);
return this.boardsService.getAllBoards(user);
}
@Post()
@UsePipes(ValidationPipe)
createBoard(
@Body() createBoardDto: CreateBoardDto,
@GetUser() user:User): Promise<Board> {
this.logger.verbose(`User ${user.username} creating a new board.
Payload: ${JSON.stringify(createBoardDto)}`)
return this.boardsService.createBoard(createBoardDto, user);
}
소스 코드 안에서 어떤 코드들은 개발 환경이나 운영 환경에 이러한 환경에 다르게 코드를넣어줘야할 때가 있으며, 남들에게 노출되지 않아야 하는 코드들도 있습니다. 이러한 코드들을 위해서 설정 파일을 따로 만들어서 보관해주겠습니다.
runtime도중에 바뀌는 것이 아닌 애플리케이션이 시작될때 로드가 되어 그 값들ㅇ릉 정의하여 줍니다. 그리고 설정 파링ㄹ은 여러가지 파일형식을 사용할 수 있습니다. 예로는 XML, JSON, YAML, Environmant Variables같이 많은 형식을 이용할 수 있습니다.
XML, JSON, YALM은 codebase에 해당하며 그리고 다른 방법은 환경변수로 할 수 있습니다. 주로 이 둘을 나눠서 하는 이유는 비밀번호와 API Key와 같은 남들에게 노출되면 안되는 정보들을 주로 환경 변수를 이용해서 처리해줍니다.
윈도우에서 win-node-env를 설치해야합니다.
(윈도우에서는 기본적으로환경변수를 지원하지 않음)
npm install -g win-node-env
그리고 윈도우와 맥 모두에서는 config라는 모듈을 설치받아야합니다.
npm install config --save
default.yml
server:
port: 3000
db:
type: 'postgres'
port: 5432
database: 'board-app'
jwt:
expiresIn: 3600
development.yml
db:
host: 'localhost'
username: 'postgres'
password: 'postgres'
synchronize: true
jwt:
secret: 'Secret1234'
production.yml
db:
synchronize: false
어느 파일에서든지 config모듈을 import해서 사용
import * as config from 'config';
config.get(’server’) ⇒ 이렇게 하면 {port: 3000}이렇게 나옴
main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { Logger } from '@nestjs/common';
import * as config from 'config';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const serverConfig = config.get('server');
const port = serverConfig.port
await app.listen(port);
Logger.log(`Application running on port ${port}`);
}
bootstrap();
typeorm.config.ts
import { TypeOrmModuleOptions } from '@nestjs/typeorm';
import * as config from 'config';
const dbConfig = config.get('db');
export const typeORMConfig: TypeOrmModuleOptions = {
type: dbConfig.type,
host: process.env.RDS_HOSTNAME || dbConfig.host,
port: process.env.RDS_PORT || dbConfig.host,
username: process.env.RDS_USERNAME || dbConfig.username,
password: process.env.RDS_PASSWORD || dbConfig.password,
database: process.env.RDS_DB_NAME || dbConfig.database,
entities: [__dirname + '/../**/*.entity.{js,ts}'],
//배포시에는 false로 true값을 주면 애플리케이션을 다시 실행할때 엔티티 안에서 수정된 컬럼의 길이 타입 변경값 등을 해당 테이블을 Drop한 후 다시 생성합니다.
synchronize: dbConfig.synchronize,
};
auth.module.ts
import { Module } from '@nestjs/common';
import { AuthController } from './auth.controller';
import { AuthService } from './auth.service';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from 'src/auth/user.entity';
import { JwtModule } from '@nestjs/jwt';
import { PassportModule } from '@nestjs/passport';
import { JwtStrategy } from 'src/auth/jwt.strategy';
import * as config from 'config';
const jwtConfig = config.get('jwt');
@Module({
imports: [
PassportModule.register({ defaultStrategy: 'jwt' }),
JwtModule.register({
secret: process.env.JWT_SECRET || jwtConfig.secret,
signOptions: {
expiresIn: jwtConfig.expiresIn,
},
}),
TypeOrmModule.forFeature([User]),
],
controllers: [AuthController],
// authmodule에서 사용하기 위함
providers: [AuthService, JwtStrategy],
// 다른 모듈에서 이용하기 위함
exports: [JwtStrategy, PassportModule],
})
export class AuthModule { }