테스트

nestjs에서는 테스트 모듈인 jest를 기본적으로 지원한다.

고로 nest를 쓰기 이전과는 다르게 따로 jest를 설치할 필요는 없다.

서비스나 컨트롤러 파일들을 명령어로 만들 때, 이렇게 spec파일이 같이 생기는 것을 볼 수 있다.

이 spec 파일이 바로 테스트 파일이다.

 

개인적으로 테스트 코드는 굉장히 빡세다고 생각하는데 nest는 이를 자동으로 지원한다.

오오 갓스트 오오

 

 

패키지 파일을 살펴보면 우리가 테스트를 하기 위해서 터미널에 어떤 명령을 사용할 수 있는지 나와있다.

우선 얼마나 커버가 되는지 알 수 있는 test:cov를 실행해 보자

 

npm run test:cov

 

잘 작동한다. (이렇게 안나올 수도 있다.)

 

이번에는 어떤 테스트 파일이 알 수 있는 test:watch를 실행해보자

 

watch를 실행할 경우 추가적인 명령어가 필요하다

여기서 a는 모든 테스트를 해보는 명령이다.

 

 

유닛 단위 테스트와 end to end (e2e) 테스트

 

유닛 테스트는 특정 부분만 테스트를 하는 것이고

e2e테스트는 모든 서비스를 테스트 하는 것이다

 

주요 키워드

테스트 파일에는 테스트 파일에만 사용하는 키워드가 있다.

 

describe: 테스트 내용 자체

beforeEach: 테스트하기 전에 먼저 실행되는 부분

it은 테스트 하는 부분이다.

 

사실 이렇게 설명해도 헷갈리니 직접 해보자

 

테스트 파일을 열고 아무 describe 안에 다음을 입력하자

 

 

여기서 왠 텍스트가 들어 있는것을 볼 수 있는데, 이건 테스트 이름이다.

때문에 본인이 원하는 텍스트를 넣어줘도 된다.

 

expect는 괄호안의 것이 무엇이 되어야 하는지를 말하고

toEqual은 괄호 안의 숫자가 나와야 한다는 뜻이다.

 

즉, 1 + 1 은 2가 되어야 한다는 뜻이다.

 

선술한 커맨드에 따라 테스트를 해보면 잘 돌아가는 것을 알 수 있다.

 

 

자, 만약 고의로 틀린다면 어떨까?

1+1 = 3 이라고 해보자

 

 

 

FAIL이라고 뜨는 것을 볼 수 있다.

코드를 잘 살펴보면 우리가 기대한 값은 3이었지만 실제 나온것이 2였다는 것을 알 수 있다

코드의 어느부분에 문제가 있는지까지 아주 상세하게 나오는 것을 알 수 있다.

 

 

CRUD 테스트

우리가 만든 CRUD를 테스트 하는 코드를 작성해 보자

 

 

 

서비스 파일에서 getAll기능을 테스트 해볼 것이다.

 

describe를 새로 만들고 이름을 지어주자, 보통 내가 뭘 테스트 하는지 알기 위해서 함수 이름을 똑같이 적는 경우가 많다.

그 다음 it에 테스트 이름을 넣어준다.

 

그런데 테스트를 하기 전에 먼저 이 파일을 가져와야 할 필요가 있다.

 

가져오는 코드, 명령어로 서비스 파일을 만들었다면 이건 기본적으로 있는 경우가 많다.

 

아무튼 이제 result는 서비스의 getAll() 함수로 하고

그렇게 받아온 함수가 배열인지를 확인하는 코드를 작성하자

아까 위에도 있었던 내용이지만 한 번 더 가져왔다.

result는 Array 타입이어야 한다는 내용의 코드

 

이번엔 getOne을 테스트 해보자

getOne은 좀 길다

 

첫번째 it을 살펴보면

service의 create 함수를 사용하는 것을 알 수 있다.

포스트를 생성할 때, body에 title과 genres와 year를 입력해주어야 했는데 바로 그것이라고 할 수 있다.

아무튼 그렇게 생성된 데이터를 다시 getOne에 1을 넣어서 가져오고

가져온 데이터가 정의되었는가에 대한 여부를 확인한다.

 

두번째 it을 살펴보면

이건 없는 데이터를 가져왔을 때를 위한 테스트다.

id가 999인 데이터는 아직 없다. 고로 try-catch문에서 에러가 발생해야 하는데 그렇게 발생한 에러가

NotFoundException 인스턴스가 되는지에 대한 테스트다.

 

테스트가 잘 되는 것을 볼 수 있다.

 

 

참고로, 에러메시지가 잘 나오는지도 확인할 수 있다.

 

 

 

 

서비스

서비스파일은 지난번에 올린 명령어 목록에도 찾을 수 있다.

아무튼 파일 생성 명령어는 아래와 같다.

npx nest g s

이걸 입력하고 시간이 지나면 이름을 입력해달라고 뜰 것이다.

지난번 처럼 이번에도 이름은 movies로 할 것이다.

 

파일이 자동적으로 추가되었다.

spec은 테스트 파일이므로 지금은 무시하자.

 

app.module 파일 또한 새로 생성된 파일에 맞추어 변화된 것을 알 수 있다.

보시다시피 MoviesService가 추가되었다.

 

서비스에서는 데이터베이스를 처리하는 코드를 작성할 것이다.

작성하기에 앞서 우선 가짜 DB를 만들기 위해 Movie라는 클래스를 새로운 파일에 만들어주자

Movie 클래스를 만들어주고

서비스 파일에 적용하자

 

자 이제 서비스파일에서 데이터 베이스를 컨트롤하도록 코드를 구성할 것이다.

여기서 주의해야 할 것은 지금 하고 있는 것은 진짜 데이터베이스를 만드는건 아니라는 것이다.

Movie라는 클래스는 어디까지나 가짜 DB에 불과하다.

만약 진짜 DB를 만드려면 SQL 쿼리문을 써야겠지만 이에 대해선 나중에 다뤄볼 생각이다.

우선 서비스 파일에 데이터를 모두 가져오는 것과 하나만 가져오는 코드를 작성했다

그리고 컨트롤 파일에 서비스 파일에 있는 getAll 함수를 사용할 수 있도록 세팅한다.

여기서 조심해야 하는 것이 바로 getAll에서 Movie를 반환해주기 위해서

위의 import를 추가해주어야 한다는 것이다.

 

보통 자동으로 추가되지만 나는 수동으로 해주어야 했다.

 

getAll을 추가해주었다면 getOne도 추가해주자

 

이제 데이터 전체를 가져오는 명령과 데이터 하나만을 가져오는 명령을 추가했다.

이제 CRUD 중에서 R을 했으니 나머지 CUD를 해보자

 

컨트롤러 파일
서비스 파일

 

자 이제 잘 작동하는지 확인해보자

 

포스트 기능이 잘 작동하고

 

Get 기능 또한 잘 작동한다.

 

당연하겠지만 어디까지나 가짜DB인 만큼 서버를 다시 실행하면 이 내용은 날아간다.

 

예외처리

위 코드에서 언급하지 않은 부분이 있는데

서비스 파일의 getOne부분이다.

만약 누군가가 URL로 없는 id를 요청할 경우, 이 코드는 제대로 작동하지 않을 것이다.

고로 최소한 예외처리라도 해보도록 하자.

서비스 파일의 getOne의 부분이다.

만약 입력받은 id가 없다면 에러를 내보내도록 설정했다.

 

이상한 id 번호를 가져오자 에러가 뜨는 모습이다.

 

유효성 검사

만약 데이터 수정이나 생성을 할때, title이나 year과 같은 우리가 정한 데이터가 아닌 다른 데이터를 넣으려 할 경우

이를 막는 장치가 필요하다.

이를 위한 것이 바로 유효성 검사이며 이를 위해 패키지를 설치하자

 

npm i class-validator class-transformer

우선 이 패키지를 쓰기 위해서 main.ts 파일을 살짝 수정하자

그 다음 검증파일을 만들기 위해 create-movie.dto.ts라는 파일을 만들고 다음과 같이 작성한다.

보시다시피 내가 써야할 데이터가 들어가 있으며

이 파일의 데코레이터들이 유효성 검사를 해줄 것이다.

 

컨트롤러 파일
서비스 파일

파일의 최상단에서 CreateMovieDto를 임포트 해주고 (자동으로 될 수도 있다.)

필요한 부분에 CreateMovieDto 타입을 부여해주면 이로써 검증과정이 끝난다.

 

 

자, 그럼 확인해보자

 

되도않는 뻘 데이터를 포스트하자 에러가 뜨면서 뭐가 잘 못 되었는지를 말해준다.

 

검증 그 외

이 검증 패키지에는 다양한 옵션이 있다.

사용 방법은 아래 사진을 보도록하고 이 키워드들이 다 뭔지 알아보겠다.

whitelist: 아무런 데코레이터도 없는 속성을 거른다. 즉, 이상한 데이터를 보내면 거르겠다는 뜻

forbidNonWhitelisted: 존재해선 안 되는 속성을 아예 막겠다는 뜻이다.

 

forbidNonWhitelisted를 적용한 모습, 경고문이 달라졌다.

 

transform: 이름에도 유추할 수 있듯 이것은 데이터를 변환해주는 것이다.

 

우리가 URL에서 param 값을 받아오면 이건 기본적으로 string이다.

그러나, 지금 이 프로젝트 같은 경우, id가 string일 이유가 없으며 number여야 한다.

이때, number로 변환해주기위한 옵션이 바로 transform이다.

 

 

 

메인 파일을 아래 처럼 수정하자

자 그리고 컨트롤러와 서비스 파일에서, Param의 값의 타입을 string에서 number로 바꿔주자

 

컨트롤러 파일만 올렸지만 서비스 파일도 바꿀 것

 

 

Patch 메소드도 바꾸어 보자

 

우선 타입을 변환시키고 사용할 수 있게 해주는 패키지를 설치하자

npm i @nestjs/mapped-types

 

앞서 우리는 파일 생성시에만 유효성 검사를 했는데

이번엔 파일 수정시에도 필요한 유효성 검사를 해볼 생각이다.

 

그러나 파일 생성시 사용했던 것을 거의 똑같이 응용해서 사용할 수 있다.

update-movie.dto.ts

생성dto파일과는 다르게 아주 심플한데

이는 생성dto 파일을 그대로 가져왔기 때문이다.

 

 

create-movie.dto.ts

생성dto 파일도 약간 바꾸어 주었다.

 

이렇게 만들어진 것들을 컨트롤러 파일과 서비스 파일에서 필요한 부분에 넣어주자

참고로 상단에 import 하는 것을 잊지말자

라우터와 메소드 사용하기

 

npx nest를 입력했을 때 뜨는 이것을 기억하는가?

 

사실 이것은 nest를 사용하기 위한 훌륭한 키워드 모음집이라고 할 수 있다.

이곳에 있는 키워드만으로도 nestjs의 거의 모든 것을 생성할 수 있다.

 

위의 Commands 목록을 보면 generate가 있고 이것의 짧은 키워드는 g인 것을 알 수 있다.

generate 밑으로 표가 하나 있는데 이는 생성하고자 하는 대상과 그것의 짧은 키워드이다.

 

예를 들어 컨트롤러를 새로 생성하고자 한다면

npx nest g co

위 커멘드를 입력하면 된다.

이를 입력하면 컨트롤러의 이름을 입력해주라는 말이 뜬다.

 

이름을 movies로 해보자

 

 

movies 폴더가 새로 생기고 컨트롤러 파일이 생성된 것을 확인할 수 있다.

 

파일을 열어 내용을 확인해보면 이미 필요한 내용은 다 만들어져 있는 것을 볼 수 있다.

어찌보면 이게 nest의 매력이지 않을까 싶다

참고로 spec은 테스트 파일이기 때문에 없어도 무관하다.

 

컨트롤러 파일을 편집해서 간단한 URL을 만들어보자

새로 추가된 url

import에 Get을 추가하고 movies라는 경로에 newnewnew를 출력해 보았다.

 

 

Param 사용하기

자, 이제 라우터 기능 중에 하나인 파라미터값을 받아보자

 

movies의 하위 url이 만들어졌다.

이로써 movies 하위 url에 뭘 입력하든 another가 출력될 것이다.

자, 이제 파라미터 값을 써보자

 

URL에 있는 id 값을 받아오고 이것을 myId라고 명명했으며 이것의 자료 타입은 string이다.

그리고 그 값을 another 옆에 출력하도록 작성했다.

 

보시다시피 이제 파라미터 값을 받아올 수 있게 되었다.

 

 

 

이제 다른 메소드도 사용해보자

 

Post와 Patch와 Delete 메소드를 추가했다.

참고로 put 메소드는 해당 페이지 전체를 업데이트하고 patch 메소드는 해당 페이지 일부만을 업데이트 한다.

이를 확인하기 위해선 브라우저에선 불가능하고 대신 썬더클라이언트를 활용해보자

썬더 클라이언트는 VS-CODE의 확장 프로그램중 하나다.

썬더클라이언트에서 POST 메소드를 테스트해보았다.

보시다시피 잘 작동하는 것을 알 수 있다.

 

 

 

Body 사용하기

 

post 메소드를 사용할 때, body는 정말 자주 사용한다.

post 메소드에 body를 한번 사용해 보자

 

포스트의 함수에 Body를 추가해주면 최상단의 import에 Body가 자동으로 추가된다.

 

추가 후 Body를 사용한 모습

 

이번엔 Patch 메소드에서 param과 body를 같이 사용해보자

Patch메소드에 이것저것 추가해준 모습

보시다시피 잘 작동한다.

 

주의사항

express를 사용할때도 있었던 문제점인데

만약 위 코드에서 movie 하위 URL로 search를 만든다고 할 경우,

해당 코드가 아래에 위치해 있으면 이를 :id로 인식할 수 있다는 것이다.

바로 이런 경우다.

 

만약 브라우저에서 URL에 movie/search라고 입력하면

브라우저는 search를 id로 인식하여 첫 번째 코드를 실행한다는 문제점이다.

이런 문제점을 없애는 방법은 아주 간단한데 그냥 순서를 바꿔주면 된다.

바로 요렇게

 

Query사용하기

이번엔 query를 사용해 보자

사실 이것도 앞의 body랑 별 다를 것이 없다.

앞서 하던 body와 거의 차이가 없는 모습

 

정상적으로 작동하는 것을 알 수 있다.

 

 

 

서버 실행하기

nestjs를 설치하면 많은 기본 파일이 생성되는데

여기서 package.json파일을 열어서 서버를 어떻게 실행하는지 확인해보자

package.json 파일의 일부분

보시다시피 이렇게 되어 있는 것을 알 수 있다.

npm run start:dev

package.json 파일을 참조하여, 위 명령어를 터미널로 입력해주면

localhost:3000

경로로 서버를 실행할 수 있게 된다.

서버를 실행한 모습, Hello World!가 출력된다.

 

기본 파일

src 폴더에 main.ts 파일이 있다.

 

main.ts

해당 파일의 내용으로 보시다시피 포트넘버가 기본적으로 3000번으로 되어 있는 것을 볼 수 있다.

그렇다면 Hello World!는 어디서 오는 걸까?

바로 AppModule이다.

app.module.ts는 main 파일과 같은 폴더에 위치하고 있다.

app.module.ts

여기서 @Module이 바로 데코레이터이다.

테코레이터는 클래스에 함수기능을 추가해주는 것으로 가장 아랫줄에 보면 AppModule 클래스를 익스포트 한다고 되어있지만 정작 그 안에는 아무 내용도 없는데, 여기서 데코레이터로 추가된 것들이 추가된 것이다.

 

즉, AppModule이라는 클래스에는 Module이라는 데코레이터가 추가되어 있는 것이다.

 

자, 그런데 데코레이터에 AppController와 AppService가 있는 것을 볼 수 있다.

 

app.controller.ts
app.service.ts

먼저 컨트롤러 파일을 보자,

컨트롤러에는 get 데코레이터가 있고 서비스 파일의 getHello()를 리턴하고 있으며

서비스에선 getHello()가 Hello World! 문자열을 리턴하고 있다.

 

즉 모듈 파일은 컨트롤러 파일을 호출했고

컨트롤러 파일은 서비스 파일을 호출했고

서비스 파일이 Hello World!를 반환한 것이다.

 

컨트롤러

컨트롤러는 라우터와 비슷한 역할을 한다.

컨트롤러 파일에 새로운 데코레이터를 추가해보자

@Get('/hello')
sayHello(): string {
	return 'myHello';
}

여기서 Get은 메소드를 의미한다

'/hello'는 경로를 의미하고

sayHello()는 사용할 함수를 말한다.

함수에서, 문자열 myHello를 반환하도록 했으니, hello 라는 경로에 들어가면 myHello라는 문자열이 출력된다.

 

hello 경로로 들어간 결과

 

 

주의사항

데코레이터는 반드시 사용하고자 하는 함수나 클래스랑 붙어있어야 한다.

즉, 아래와 같이 쓰면 안 된다.

@Get('/hello')

sayHello(): string {
	return 'hello';
}

 

만약 Post 메소드를 쓰고자 한다면?

컨트롤러 파일의 최상단에 위치한 import에 Post를 넣어주면 된다.

import { Controller, Get } from '@nestjs/common';

// 위 코드를 아래로 바꾸어준다.

import { Controller, Get, Post } from '@nestjs/common';

 

 

서비스

서비스는 일반적으로 함수를 가지는 파일이다.

위에서, 컨트롤러에서 myHello를 return하도록 했는데

이렇게 하지말고 서비스 파일에서 myHello를 출력하는 함수를 만들고

 

그 다음 컨트롤러에서 출력하게 해보자

import { Injectable } from '@nestjs/common';

@Injectable()
export class AppService {
  getHello(): string {
    return 'Hello World!';
  }
  getHi(): string {
    return 'myHello';
  }
}

서비스 파일에 getHi 함수를 추가한 모습이다.

이제 컨트롤러 파일을 수정해주자

 

import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Get()
  getHello(): string {
    return this.appService.getHello();
  }

  @Get('/hello')
  sayHello(): string {
    return this.appService.getHi();
  }
}

 

컨트롤러 파일의 sayHello 부분을 서비스에 있는 getHi 함수를 리턴해주는 형태로 바꾸어 주었다.

 

 

자 이제 Nodejs의 express에서 하던 방식을 온전히 nestjs의 방식으로 옮기는 가장 기본적인 부분을 살펴보았다.

 

사실 app.controller와 app.service는 뭔가 예시파일 같은 느낌이다.

고로 이걸 그냥 없애버리고 내가 직접 만들어보도록 하자

 

기타

nestjs에서는 Nodemon의 기능을 기본적으로 지원하는듯 하다.

실제로 코드를 편집하고 저장해보면 바로 재시작이 되는 것을 알 수 있다.

Nest.js란 무엇일까?

 

Nest.js란 Node.js에서 구동되는 프레임워크고 node.js에 백엔드를 구성할 수 있게 해준다.

express를 이용하는 백엔드 개발을 위한 프레임 워크로써

특별한 구조가 정해져있고 이를 따르기만 하면 비교적 큰 규모의 백엔드를 비교적 쉽게 만들 수 있게 된다.

TypeScript를 사용하기 때문에 이를 배우기 전에 먼저 TypeScript부터 제대로 익히는 것이 좋다.

 

Nest.js는 Node.js에서 구동되는 프레임워크이므로 우선 node가 있어야 한다.

node의 설치과정은 생략한다.

 

시작하기

VS-Code에서 터미널을 열고 먼저 nestjs를 설치하도록 하자

npm i -g @nestjs/cli

설치하는데 약간의 시간이 걸린다.

 

 

npx nest

설치가 완료되면 위 명령어를 입력하면 아래 사진 처럼 나오는 것을 볼 수 있다.

이제 새로운 nest 프로젝트를 만들어야 한다.

터미널에 다음을 입력한다.

npx nest new

그러면 터미널에서, 새로운 프로젝트의 이름은 무엇인가 라고 물어볼 것이다.

원하는걸로 이름을 정해주자

 

이름을 정했으면 패키지 메니저로 무엇을 사용할 것인지를 묻는데 npm, yarn, pnpm 중에서 선택해야 한다.

나는 노드를 사용하므로 npm을 사용하겠다.

 

여기까지 왔으면 설치가 시작되는데 이게 시간을 좀 먹는다.

커피나 마시면서 여유를 가지고 기다려보자

 

다 설치되었으면 이렇게 틀이 만들어진 것을 볼 수 있다.

 

이제 본격적으로 시작해보자

+ Recent posts