NestJS
nodejs 위에서 움직이는 framework (백엔드를 구성할 수 있도록 해줌)
nest new
를 통해 nest 프로젝트 create 가능@Module({ imports: [], controllers: [AppController], providers: [AppService], })
decorator (클래스에 함수 기능을 추가할 수 있음)
nestjs는 main.ts가 모든 것을 시작
main.ts : 하나의 모듈에서 applicaion을 생성
const app = await NestFactory.create(AppModule);
- AppModule -모든 것의 루트 module
(Module: 어플리케이션의 일부분(한 가지의 역할을 하는 App))
Controller
- controller: url을 가져오고 함수를 실행
@Get("/hello") sayHello():string{ return "Hello"; }
/hello
url의 요청을 받고 sayHello()라는 함수를 실행
(누군가가 /hello로 들어오면 sayHello라는 함수가 실행됨)- 데코레이터와 함수 사이에 빈칸이 있으면 안됨!!*
nest g(generate) co(controller)
cmd 명령어를 통해 controller 생성 가능- app.module.ts의 controller에 생성된 controller 확인 가능
@Controller('movies')
의 경우 /movies url에 해당됨- 기본 router 일 경우,
@Controller()
로 해야함
- 기본 router 일 경우,
@Controller('movies')
export class MoviesController {
@Get()
getAll(){
return "This will return all movies";
} // `/movies` 일 경우, 나타남
@Get(":id")
getOne(){
return "This will retun one movie";
} // `/movies/{id}` 일 경우, 나타남
}
Nestjs에서는 무언가가 필요하면 요청해야함
- 함수의 인자로
@Param("id")
를 통해 요청한 것을 얻을 수 있음@Get(":id") getOne(@Param("id") movieId:string){ return `This will retun one movie with the id: ${movieId}`; } //요청한 id와 movieId가 일치 }
- Query를 얻고 싶은 경우, `Query()` ```typescript @Get("search") search(@Query('year') searchingYear:string){ return `We are searching for a movie made after: ${searchingYear}`; } //search가 :id의 Get보다 밑에있으면 search를 id로 판단하는 문제가 생길 수 있음
-> url /movies/search/?year=200 일 경우, searchingYear =200 이 됨
- 함수의 인자로
@Post()
:create- Post에서 send할 때 보낸 body 를 받고 싶다면
@Body()
를 통해 얻을 수 있음@Post() //create create(@Body() movieData){ console.log(movieData); //send할 때 보낸 body를 얻은 것을 확인 가능 return 'This will create a movie'; }
- Post에서 send할 때 보낸 body 를 받고 싶다면
@Delete(":id")
: delete@Put()
:모든 resource를 update@Patch(':id')
: resource의 일부분만 update
- service 함수에 접근하는 법
constructor(private readonly moviesService:MoviesService){}
에서 인자 moviesService를 통해 MoviesService에 접근 가능return this.moviesService.getAll();
controller의 함수에서 this.moviesService.__ 를 통해 MoviesService의 함수들에 접근 가능
Services
Nestjs는 controller를 비즈니스 로직이랑 구분짓고 싶어 함
- controller는 url을 가져오고 함수를 실행 , 나머지 비즈니스 로직은 services (service는 function을 가지는 부분)
Single-responsibility principle: 하나의 module,class 혹은 function이 하나의 기능은 꼭 책임져야 하는 것
nest g(generate) s(service)
cmd 명령어를 통해 service 생성 가능- app.module.ts의 providers에 생성된 service 확인 가능
DTO (Data Transfer Object) :데이터 전송 객체
controller와 service에서 전송하는 객체들의 type 유효성 검사하기 위함
app.useGlobalPipes(new ValidationPipe({whitelist:true,forbidNonWhitelisted:true,transform:true}));
: main.ts에 작성해 유효성을 검사해줌- whitelist: true로 설정하면 아무 decorator도 없는 어떤 property의 object를 거름
- forbidNonWhitelisted:누군가가 이상한 것을 보내면 request 자체를 막아버림
- transform: user가 보낸 것을 원하는 타입으로 변환해줌 (url에서 받은 id는 string이지만 number로 명시해준다면 number로 자동으로 변환됨 )
npm i class-validator class-transformer
:class의 유효성 검사를 위함<create-movie.dto.ts>
export class CreateMovieDto{ @IsString() readonly title: string; @IsNumber() readonly year:number; @IsString({each:true}) readonly genres:string[]; }
create(@Body() movieData:CreateMovieDto)
를 통해 들어오는 쿼리에 대해 유효성을 검사할 수 있게 해줌- input에 대해서도 유효성 검사를 typescript가 실시간으로 하게 됨
npm i @nestjs/mapped-types
:mapped-types는 타입을 변환시키고 사용할 수 있게 하는 패키지export class UpdateMovieDto extends PartialType(CreateMovieDto){}
PartialType은 base Type을 인자로 가지며 그 base type의 인자들이 필수사항은 아님
app.module.ts
는 Appcontroller와 Appservice만 가져야함 -> app은 여러개의 module로 이뤄져있으므로 module로 다른 것들을 만들어줘야함nest g(generate) mo(module)
:imports에 module 생성된 것을 확인할 수 있음
spec.ts
는 테스트하는 파일- jest가 .spec.ts 파일들을 찾아볼 수 있도록 설정되어 있음
- Test
- unit test: 서비스에서 분리된 unit을 테스트 (function 하나하나를 테스트)
- end-to-end(e2e) test: 전체 시스템을 테스트
Unit test
.spec.ts
: 해당 파일의 unit test 를 위함- describe: 테스트를 묘사
- beforeEach: 테스트를 하기 전에 실행
it(",()=>{})
: 테스트 실행- afterAll: 데이터베이스를 깨끗하게 정리해주는 (모두 지우는 ) function을 넣을 수 있음
describe("getAll",()=>{
it("should return an array",()=>{
const result=service.getAll();
expect(result).toBeInstanceOf(Array);
})
});
-> service = module.get<MoviesService>(MoviesService);
로 인해 MoviesService에 접근 가능
npm run test:watch
를 통해 unit test를 하는동안 코드가 수정될 떄마다 변경 사항을 확인할 수 있음 (a: testing all)nom run test:cov
를 통해 test가 얼마나 작동됐는지 확인할 수 있음
e2e test
- 모든 부분을 test할 때 필요
it("POST",()=>{
return request(app.getHttpServer())
.post("/movies")
.send({
title:"Test",
year:2000,
genres:['test'],
})
.expect(201)
});
-> 서버에 request해서 movies에 post할 때 이 정보를 보내면 201을 받는지 test (create할 때 201 server)
beforeAll: 테스트가 새로 생길때마다 새로운 어플리케이션을 만들고 싶지 않을 때 (beforeEach에서 creating module을 실행될때마다 해서 데이터베이스가 계속 비워지게 되는 것을 방지)
main.ts에서 transform으로 자동 변환된 type은 pipe에 test를 올리지않았다면 test에서는 적용이 안됨 ( test에서도 똑같이 pipe 적용을 해줘야 함 )
app = moduleFixture.createNestApplication(); app.useGlobalPipes(new ValidationPipe({ whitelist:true, forbidNonWhitelisted:true, transform:true }));