Search

nest.js

subtitle
-
Tags
자바스크립트
Created
2021/06/30
2 more properties
nest new project-name 하니까 jest, eslint, prettier 전부 다 설정되어 있고(그전엔 다 수동으로 설정했었는데...) class에 데코레이터 붙어있는 건 dependency injection DI
철학 → 이제껏 node.js를 위한 많은 라이브러리들이 있었지만 아키텍처 문제를 해결하는 것은 없었다. nest.js는 platform agnostic(그러기엔 두 개뿐이지만..)하게 express(기본값), fastify 등 여러 서버프레임워크 위에서 사용할 수 있으며 효율적이고 확장이 용이한 아키텍처를 구성할 수 있게 해주는 프레임워크

컨트롤러

빠르게 컨트롤러 생성하고 싶을 땐 crud generator 사용: https://docs.nestjs.com/recipes/crud-generator#crud-generator ex) nest g controller cats : 라우트 path에서 cats prefix를 가진 컨트롤러를 생성한다. → GET /cats
만약 cats 다음에 breed라는 서브 라우트를 추가하고 싶으면 get method의 데코레이터로 @Get('breed') 추가
response 포맷
스탠다드(추천): object, array로 반환하면 모두 JSON으로 직렬화된다.
한편 response를 원시타입으로 리턴할 경우 직렬화되지 않는다.
POST 메서드(201)를 제외하고 기본 응답 status 코드는 200이다.
@HttpCode(...) 데코를 사용하면 커스텀할 수 있다.
Library-specific: @Res() or @Next() 데코를 쓰는 경우 '스탠다드' 모드는 자동으로 disabled 됨
따라서 두 모드를 동시에 쓰려면 passthrough 옵션 사용 @Res({ passthrough: true })

컨트롤러 데코레이터는 어떻게 만드는 거지?

1.
자바스크립트 프록시: 프록시, Reflect, 프록시 핸들러
proxy: 프록시 객체는 기본 operation에 대한 작업을 커스텀할 때 사용
reflect: 가로챌 수 있는 자바스크립트 operation에 대한 메서드를 제공해주는 내장 객체.
https://github.com/rbuckton/reflect-metadata reflect-metadata 라이브러리
class C { // declarative 하게 @Reflect.metadata(metadataKey, metadataValue) method() { } } // Imperative definition of metadata: Reflect.defineMetadata(metadataKey, metadataValue, C.prototype, "method");
JavaScript
Receiver: 프로토타입 체이닝 속에서, 최초로 작업 요청을 받은 객체가 무엇인지 알 수 있게 해준다. proxy 패턴으로 set 트랩 구현 시 프로토타입 체이닝으로 인해 의도하지 않은 객체에 프로퍼티가 set되는 사이드 이펙트를 막을 수 있다. → parent의 [[Set]]이 호출되면, Proxy의 set 트랩이 트리거 되고, target[key]는 결국 parent['job']이기 때문에 parent에 job 속성이 추가되고 값이 할당되게 된다.
2.
컨트롤러 코드를 보니까 Reflect 라는 자바스크립트 내장 객체를 사용하고 있다.
export function Controller( prefixOrOptions?: string | string[] | ControllerOptions, ): ClassDecorator { const defaultPath = '/'; const [path, host, scopeOptions] = isUndefined(prefixOrOptions) ? [defaultPath, undefined, undefined] : isString(prefixOrOptions) || Array.isArray(prefixOrOptions) ? [prefixOrOptions, undefined, undefined] : [ prefixOrOptions.path || defaultPath, prefixOrOptions.host, { scope: prefixOrOptions.scope }, ]; return (target: object) => { Reflect.defineMetadata(PATH_METADATA, path, target); Reflect.defineMetadata(HOST_METADATA, host, target); Reflect.defineMetadata(SCOPE_OPTIONS_METADATA, scopeOptions, target); }; }
JavaScript
prefixOrOptions argument를 받아서 target object의 메타데이터로 정의
메타데이터를 읽을 때는 Reflect.getMetadata 사용
let metadataValue = Reflect.getMetadata(PATH_METADATA, target);
JavaScript
→ 근데 반환 타입인 ClassDecorator 데코레이터 팩토리
데코레이터: 클래스를 호출하기 전 실행

컨트롤러 - 스코프

하나의 인스턴스만을 사용하는, 전역 상태를 가진 싱글톤 객체 사용.
싱글톤 스코프 추천: 어플리케이션 시작할 때 이니셜라이즈 한 번만 하고, 인스턴스 캐싱

DTO(data transfer object)

xxxx.dto.ts 파일에 정의
@Body , @Query, @Param : 각각 request.body, request.query, request.param
@Post() async create(@Body() createCatDto: CreateCatDto) { return 'This action adds a new cat'; } Handling
JavaScript

모듈에 컨트롤러 등록하기

nest.js가 컨트롤러가 생성된 걸 알게하려면 AppModule 클래스에 컨트롤러 추가

Provider

The main idea of a provider is that it can be injected as dependency
1.
프로바이더: 디펜던시로서 다른 컴포넌트에 주입되는 것
2.
@Injectable : Nest IoC 컨테이너로 관리할 수 있는 클래스라는 것을 알려주는 메타데이터 추가
3.
DI: Controller 클래스에서 서비스 클래스 등 Provider 클래스 사용 시, Constructor의 파라미터 앞에 public, private 등의 키워드를 쓰면 선언과 함께 동시에 초기화 가능 / 타입스크립트 덕분에 디펜던시 클래스는 타입으로 인해 쉽게 resolve 된다.

Modules

어플리케이션 컴포넌트를 구성하는 효과적인 방법으로서 사용
@modules : providers, controllers, imports, exports 등의 prop을 가진 object를 파라미터로 받는다.
이 부분 이해가 안됨
The module encapsulates providers by default. This means that it's impossible to inject providers that are neither directly part of the current module nor exported from the imported modules. Thus, you may consider the exported providers from a module as the module's public interface, or API.
module 종류
featrue module
cats module을 만들고 app.module.ts에 등록
shared module
모든 모듈은 자동으로 shared module이 된다.
만약 다른 모듈에서도 CatsService 인스턴스를 공유하고 싶으면 CatsService 를 모듈의 exports array에 추가하면 된다.
CatsModule을 import하기만 하면 어떤 모듈에서든 CatsService에 접근할 수 있다.
re-exporting
Core Module에서 Common Module을 re-export하면 Core Module을 사용하는 다른 모듈에서도 Common Module을 사용할 수 있다.
dependency injection
Module에서 provider를 inject할 수 있다. (configuration 목적으로)
반대로 provider에서 모듈을 inject하는 것은 circular dependency때문에 불가능하다.(module과 provider가 서로 참조하여 무한루프에 빠지게 됨)
global modules
앵귤러에서는 providers가 global 스코프에 등록이 되어 어디서든 쓸 수 있지만 Nest는 그렇지 않다.
Nest는 module scope 안에 provider를 캡슐화하기 때문에, module을 import해야만 module의 provider를 사용할 수 있다.
모듈의 provider들을 어디에서든 쓰게 하고 싶은 경우 @Global() 데코레이터를 사용한다.
글로벌 데코레이터는 모듈을 global scope로 만들어준다.
Global 모듈은 오직 한 번만 등록되어야 한다. 일반적으로 코어 모듈이나 루트 모듈에. (무조건 global하게 등록하는 것보다는 모듈을 imports하는 방식이 좋다)
dynamic modules
default 모듈을 확장(오버라이딩 x)하여 다이나믹 모듈을 만들 수 있다.

Middleware

route handler를 타기 전 호출됨
req, res, next에 접근 가능.
function or class로 작성 가능
@Module 데코레이터에 미들웨어를 추가하는 자리는 없지만 configure 메서드를 통해 미들웨어를 추가할 수 있다.
특정 route path, 특정 request method에
middleware consumer를 chaining하여 적용할 미들웨어와 라우트를 설정할 수 있다.
apply(): 사용할 middleware
forRoutes(): 어떤 라우트에 적용할 것인가
Functional Middleware: 멤버, 메서드, 디펜던시도 필요 없는 경우 함수 미들웨어로 간단하게 작성할 수 있다.
여러 Middleware를 적용하는 법: apply(...middlewares: Middleware[])
Global Middleware: 앱에 등록된 모든 라우트에 middleware를 적용하려면 app.use(미들웨어) 사용
단, Global Middleware에서 DI 컨테이너에 접근하는 것을 불가능하다(DI 컨테이너에 접근 불가능하다는 말은 dependency를 관리하는 컨테이너에 접근 불가하다는 말. 즉, dependency에 접근 불가능하다 → 맞나?). 따라서 app.use가 아닌 functional middleware를 사용하든가 class middleware를 사용해 forRoutes('*')로 글로벌하게 적용 가능하다.

Exception filters

어플리케이션에서 핸들링되지 않는 exception은 exception layer에 잡혀서 사용자 친화적인 응답으로 자동으로 핸들링된다.
nestjs에서는 built-in HttpException class를 갖고 있다(@nestjs/common 에서 import 가능).

Pipe

transformation, validation 등에 사용한다.
컨트롤러의 route handler 메서드가 실행되기 전에 pipe가 argument를 받아 작업을 수행한다.
파이프 바인딩하는 법: query param이나 params, body 값을 validate할 수 있음
@Get(':id') async findOne(@Param('id', ParseIntPipe) id: number) { return this.catsService.findOne(id); }
JavaScript

Guard

기존 인증 과정은 express middleware에서 처리되었다.
하지만 middleware는 next() 호출 후 어떤 핸들러가 실행될 지 알 수 없다.
반면 guard는 실행콘텍스트(자바스크립트 실행컨텍스트가 아니라 Nest에 제공하는 실행컨텍스트)에 접근할 수 있어 다음에 실행될 핸들러를 알 수 있다.
모든 가드는 canActive() 기능을 구현해야 한다.
true반환 시 요청 처리, false 반환 시 nest가 요청 거부
바인딩 방법: @UseGuards()
컨트롤러 전체 또는 특정 메서드에만 데코레이터를 쓸 수 있다

Intercepts

관점 지향 프로그래밍 (Aspect orientred programming)에 영감을 받아 만들어진 기능
횡단 관심사를 분리하여 모듈성 증가
횡단관심사: 모듈 안에서의 중복되는 코드. 여러 모듈을 횡단하며 존재하는 공통 관심사
ex) 트랜잭션, 로깅
AOP: 이런 횡단관심사를 모듈화하는 법
메소드 실행 전/후로 추가 로직 실행 가능
함수로부터 반환된 결과나 에러를 변형
함수의 기본 기능을 확장
특정 조건에 따라 함수 오버라이딩(ex. 캐싱 목적

Custom Decorator

ES2016 데코레이터는 함수를 반환하는 표현이다. 이 함수는 target, name, propety descriptor를 인자로 받는다. 데코레이터를 적용하려면 데코레이터 앞에 @ 문자를 접두하고 데코레이팅하려는 코드의 위에 쓰면 된다. 데코레이터는 클래스, 메서드, 프로퍼티 모두에 붙일 수 있다.
용도
request 객체에 커스텀 property를 추가하거나
엔티티에서 특정 필드만 추출하여 데이터를 전달하거나
여러 데코레이터를 한번에 적용할 수 있는 커스텀 데코레이터 생성

궁금

스코프
Observable 메서드
nest.js로 graphql 서버 구현
테스트

잊지말고 읽기

DI in angular