imgyuzzzang

[NEST JS] 일주일치 공부 기록 본문

computer science/웹

[NEST JS] 일주일치 공부 기록

imgyuzzzang 2022. 3. 17. 18:12

nest js 설치

npm i -g @nestjs/cli
nest new {dir_name}

 

nest js 기본 구조

  • src
    • main.ts
      • 어플리케이션 생성(root) = 기존 node의 index.js
      • port 설정
    • app.module
      • 진입점
      • controller,providers 등록되어있음.
      • main에서는 app.module을 import
    • app.controller
      • end point, 각자 service의 method 호출, client request에 대한 응답 전송.
      • @Get() 에는 (”/”) 가 생략되어있음: domain/ 접속 시 controller의 getHello 호출
      • app.service의 getHello 호출
    • app.service
      • db와 연결
  • 참고
    • eslintrc.js
      • 타입스크립트 쓰는 가이드라인
      • 문법 오류나면 알려주는 역할
    • prettierrc
      • 코드 포매팅

1.  모듈 생성하기

  • 다양한 모듈들로 구성
  • 각자 생성 명령어로 생성해 줄 것
nest g module {module_name}
//또는
nest g mo {module_name}
  • 명령어 세부 설명
    • nest: nest cli 사용
    • g: generate
    • module: 내가 만들 것
  • board 라는 이름의 모듈을 생성했을 경우, 다음과 같이 파일이 자동 생성됨.
  • 해당 모듈 dir 내부에 controller, service, entity 등을 생성해줘야 함.

 

2.  생성한 모듈 폴더 내부에 Controller 생성

  • @Controller(’/{module_name}’) : end point
  • 명령어
nest g controller {module_name} 
// 또는
nest g co {moudle_name}
  • 뒤에 --no-spec 을 붙이면 테스트 파일 생성 안 됨.

 

3. 생성한 모듈 폴더 내부에 Service 생성

nest g service {module_name} 
//또는
nest g s {module_name}
  • 생성시 자동으로 module의 provider에 등록됨.
  • 생성된 파일에는 injectable 데코레이터 존재: 어플리케이션 전체에서 이 서비스 사용할 수 있게 함.
  • service를 controller에서 이용할 수 있게 하기(dependency injection)
    • controller의 class의 constructor에서 service를 프로퍼티로 할당해줌.
    • ts에서는 생성자에서 private으로 인자로 넘기면 자동으로 class의 프로퍼티로 선언되는 것으로 간주됨.
    @Controller('{name}')
    export class {name}Controller {
    	constructor(private {name}Service: {name}Service) {}
    }

 

4. 생성한 모듈 폴더 내부에 Model 생성

  • {name}.model.ts로 파일 생성해줌
  • schema 정의
    • interface: 타입 정의
    • class: 타입 정의 및 생성 가능

DTO

  • 계층간 데이터 교환을 위한 객체
    • 데이터 유효성 체크 용이
    • 안정적인 코드, 타입으로의 확장 가능성
  • nest js에서는 class를 이용해서 사용! (보통 interface / class )
  • dto에 유효성 체크 넣을 때, class-validator 이용해서 다음과 같이 추가
    • 원래 nested object 지양해야하지만 현재 site와의 통신 구조상 데이터 구조를 바꾸기엔 어려움이 있으므로...... 쥬륵
//create-board.dto.ts

import { IsNotEmpty } from 'class-validator'

export class CreateBoardDto {
	@IsNotEmpty() //title에 validate 추가
	title: string;
	
	@IsNotEmpty()
	description: string;
}
//board.controller.ts

..
@Post('/')
	@UsePipe(ValidationPipe) // pipe 추가
	async createBoard(@Body('') CreateBoardDto:CreateBoardDto): Promise<Board> {
		return this.BoardService.createBoard(CreateBoardDto);
	}
..

Pipe (middleware)

  • @Injectable {} 데코레이터로 주석이 달린 클래스
  • data transformation & data validation
    • transformation: 원하는 포맷으로 변경
    • validation: 데이터 형식 등이 유효한지
  • 컨트롤러 경로 처리
    • client의 request의 데이터에 파이프 삽입, 통과시 controller로!
  • 방법 3가지
    • handler level
    @Post()
    @UsePipe(pipe)
    createBoard(
    	@Body('title') title,
    	@Body('description') description,
    ) {
    //파라미터 전부에 적용
    }
    
    • parameter level
    @Post()
    createBoard(
    	@Body('title', ParameterPipe) title, //해당 파라미터에만 적용
    	@Body('description') description,
    ) {
    }
    
    • global level
    //main.ts
    async function bootstrap() {
      const app = await NestFactory.create(AppModule);
    	app.useGlobalPipes(GlobalPipe); //클라이언트에서 들어오는 모든 요청에 적용
      await app.listen(3000);
    }
    bootstrap();
    
  • Nest js에서 이미 만들어져있는 파이프 존재.
    • ValidationPipe
      export class UpdateDomainDto {	
      	@IsNotEmpty()
      	@IsString()
      	organizationId: string;
      	
      	@IsNotEmpty()
      	@ValidateNested() // RequestDomainsDto가 object. 
      	@Type(() => RequestDomainsDto)
      	healthCheckList: RequestDomainsDto;
      }
      
      class RequestDomainsDto{
      	@IsArray()
      	@ValidateNested({ each: true })
      	@Type(() => DomainDto)
      	addDomains: DomainDto[];
      	
      	@IsArray()
      	@ValidateNested({ each: true }) // DomainDto는 object의 array이므로 모두 validate 위해서는 each true
      	@Type(() => DomainDto)
      	deleteDomains: DomainDto[];
      }
      
      class DomainDto {
      	@IsString()
      	@IsNotEmpty()
      	uid: string;
      	
      	@IsString()
      	@IsNotEmpty()
      	domain: string;
      	
      	@IsString()
      	path: string;
      	
      	@IsString()
      	@IsNotEmpty()
      	host: string;
      }
      
    • ParseIntPipe
    • ParseFloatPipe
    • ParseBoolPipe
    • ParseArrayPipe
    • ParseUUIDPipe
    • ParseEnumPipe
    • DefaultValuePipe

 

Mongoose 연결

1. @nestjs/mongoose 패키지 설치

npm install --save @nestjs/mongoose mongoose

2. import

  • forRoot === mongoose.connect()
//app.module.ts

import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';

@Module({
  imports: [MongooseModule.forRoot('mongodb://localhost/nest')], //mongo db 주소 입력
})
export class AppModule {}

3. config 설정.

  • 루트 디렉토리에 config 파일을 두어야 하므로 cli에서 node config dir 과 node env를 설정하고 config 라이브러리 사용 (루트 디렉에서 관리할 것이므로 nest/config 사용 불가)
    • package.json에서 npm run start:dev 수정. (env, config_dir 추가)
"scripts": {
  ..
	"start:dev": "NODE_ENV=development NODE_CONFIG_DIR=~/.{루트디렉토리이름}  nest start --watch",
	..
}
  1. 2번의 mongo db 주소는 다음과 같이 config에서 설정한 값에서 가져오는 것으로 세팅
const Config = require('config')

@Module({
  imports: [
	  MongooseModule.forRoot(Config.db.mongoDb.host), // config 구조에 따라 host 세팅
		HealthCheckModule, 
  ],
  controllers: [HealthCheckController],
})

 

Cron (task scheduling)

  • 정해진 시간에(또는 마다) 특정 함수 실행시키는 !
    • 특정 년 월 일 시 분 초로 지정 가능
    • 10-30 이런식으로 기간도 지정 가능
      • 초 분 시 일 월 년 순.
      • ex) 매월 1일 15-18시 0분에 알림주기! : @Cron(’* 0 15-18 1 * *’)
  • @nestjs/schedule, @types/cron 모듈 설치
//app.module.ts

import { Module } from '@nestjs/common';
import { ScheduleModule } from '@nestjs/schedule';

@Module({
  imports: [
    ScheduleModule.forRoot()
  ],
})
export class AppModule {}
// health-check.service.ts
import { Cron } from '@nestjs/schedule';

@Injectable()
export class HealthCheckService {
	constructor(
    @InjectModel('ideMonitor') private ideMonitorModel: Model<IdeMonitorDocument>) {}
	
	@Cron('* * 4 * * *') // 초 분 시 일 월 년 => 매일 4시마다 실행.
		async saveUrlsInYamlFile() {
			try {
				const parsedYaml = YAML.parse(readFileSync(yamlFilePath, 'utf8'));
				//...
				writeFileSync(yamlFilePath, YAML.stringify(parsedYaml), 'utf8');
			} catch (error) {
				throw error
			}
		}
}
  • @Cron(CronExpression.EVERY_30_SECONDS) 등으로 interval 줄 수도.
    • CronExpression도 @nestjs/schedule' 모듈 내장.
  •  
Comments