nest

신형기·2021년 4월 21일
2

Nest ?

java script 는 node 덕분에 back-end front-end 모두에서 lingua franca 가 되었다.
lingua franca
모국어가 다른 사람들이 상호 이해를 위하여 만들어 사용하는 언어.

Nest 에서 Module 은 뭘까 ?

사전지식

  • decorator 라고 불리우는데 클래스에 함수기능을 추가 할 수 있어
  • 그냥 클래스 위의 함수이고 클래스를 위해 움직여

@Module({ // 어플리케이션의 일부분으로 한가지의 역할을 하는 앱이라고 보면된다.
imports: [],
controllers: [AppController], // url 을 가져오고 함수를 return 한다. + constructor 로 service 를 사용한다.
providers: [AppService], // 기능을 담당한다 + @Injectable() 로 의존 주입을 담당한다.

})

의존성 주입 (dependency 는 기본적으로 어떤 서비스가 동작하기 위해 무언가에 '의존' 해야 한다는 뜻이다.)```

Promise

Promise는 프로미스가 생성될 때 꼭 알 수 있지는 않은 값을 위한 대리자로, 비동기 연산이 종료된 이후의 결과값이나 실패 이유를 처리하기 위한 처리기를 연결할 수 있도록 합니다. 프로미스를 사용하면 비동기 메서드에서 마치 동기 메서드처럼 값을 반환할 수 있습니다. 다만 최종 결과를 반환하지는 않고, 대신 프로미스를 반환해서 미래의 어떤 시점에 결과를 제공합니다.

Promise는 다음 중 하나의 상태를 가집니다.

  • 대기(pending): 이행하거나 거부되지 않은 초기 상태.
  • 이행(fulfilled): 연산이 성공적으로 완료됨.
  • 거부(rejected): 연산이 실패함.
    대기 중인 프로미스는 값과 함께 이행할 수도, 어떤 이유(오류)로 인해 거부될 수 있습니다. 이행이나 거부될 때, 프로미스에 연결한 처리기는 그 프로미스의 then 메서드에 의해 대기열에 오릅니다. 이미 이행했거나 거부된 프로미스에 연결한 처리기도 호출하므로, 비동기 연산과 처리기 연결 사이에 경합 조건race condition은 없습니다.

Promise.prototype.then() 및 Promise.prototype.catch() 메서드의 반환 값은 다른 프로미스이므로, 서로 연결할 수 있습니다.
Promise

동기, 비동기 , 그리고 블로킹(Blocking)과 논블로킹(non-blocking)

  • 동기는 요청과 그 결과가 동시에 일어난다는 뜻이야.
    즉, 어떤 객체 또는 함수 내부에서 다른 함수를 호출했을 때 이 함수의 결과를 호출한 쪽에서 처리하면 동기입니다.
    Scanner sc = new Scanner(System.in);
    int num = sc.nextInt();
    비동기는 요청과 그 결과가 동시에 일어나지 않는다는 뜻이고.
    동기와 달리 어떤 객체 또는 함수 내부에서 다른 함수를 호출했을 때 이 함수의 결과를 호출한 쪽에서 처리하지 않으면 비동기입니다.
  • setTimeout( foo, 3000);
    function foo(){
    console.log("2");
    }
    console.log("1");
    블로킹은 자신의 수행결과가 끝날 때까지 제어권을 갖고 있는 것을 의미합니다.
    논블로킹은 자신이 호출되었을 때 제어권을 바로 자신을 호출한 쪽으로 넘기며, 자신을 호출한 쪽에서 다른 일을 할 수 있도록 하는 것을 의미합니다.
  • 블로킹
    동기 예제 코드에서는 사용자가 입력할 때까지 프로그램은 어떠한 동작도 수행하지 않습니다.
    즉, 사용자가 입력할 때까지 제어권은 nextInt()메서드에게 존재하며, 사용자가 입력을 해야만 제어권이 넘어가서 이후의 코드가 수행됩니다.
    이처럼 수행 결과가 끝날 때까지 제어권을 갖고 있는 것이 blocking 방식입니다.
  • 논블로킹
    비동기 예제 코드의 수행 결과는 1, 2가 순서대로 출력됩니다.
    그 이유는 setTimeout(foo, 3000) 함수를 호출할 때 제어권을 바로 반납하기 때문입니다.
    즉, 3초 뒤에 foo() 함수가 실행이 되지만, 제어권을 반납했기 때문에 바로 다음 코드인 console.log("1")가 수행이 됩니다.
    그리고 나서 3초라는 시간이 흘렀다는 이벤트가 발생하여 콜백 함수인 foo() 함수가 수행이 되는 것이죠.
    이처럼 제어권을 바로 반납하는 방식을 non-blocking 방식이라 합니다.

Rest ?

논쟁이 되는 Rest
Representational State Trasfer => 컴퓨터가 상호 운영성을 제공할 방향중 하나 => interoperability (상호 운영성)
? 그래서 그게 코딩과 어떤 연관이 있어? 방법론 세상에 많아 프로젝트에 우리가 이론적으로 학습한 모델 그대로 안해. 프로젝트에 따라
다달라 . 그말은 즉 , 프로젝트에 모이는 사람, 인력, 규모에 따라 갖춰지는 개발 방법론 모두 깡그리 무시되면서 개발되는팀이 적지 않다는 뜻.

접근 방법을 바꿔보자 . 어쩌다가? 어디서 나온걸까..
시작은 web 이였어. 91 년에 팀 버너스 리에 의해 탄생했지 . 그시점에 인터넷은 이미 있었어 ,
Q. 어떻게 인터넷에서 정보를 공유할 것인가? 버너스 리의 답은 다음과 같아.
A. 정보들을 하이퍼 텍스트로 연결한다 => 표현 형식: HTML , 식별자: URI, 전송 방법 : HTTP
http 라는 프로토콜을 여러 사람들과 함께 만들게됬어.
로이 T 필딩 이란 사람이 http protocol 을 작업 하면서 고민이 생긴거야.
http 1.0 이 이미 이용이 되었는데, 전세계적으로 web 은 급속도록 성장하고 있던거야.
이 시점에서 명세에 기능을 더하고 , http 를 적립하고 기존의 기능을 고치는 상황에 놓이게 된거야.
그는 http protocol 을 고치게 되면 , 당연히 기존의 구축되 있는 web 하고 호환이 어려울거같은거야.
그래서 어떻게 하면 web 을 망가 뜨리지 않고 http 를 진보 시킬수 있을까 해서 만든게
http object model 이야 . 사실은 이게 4년후에 Rest 로 이름이 바뀌게 되.
이떄 세계최초로 공개되고 이걸 박사 논문으로 발표를 하게되.
이때 논문은 우리가 지금껏 알고 있는 Rest 의 근간이 되지

그런한편 api 가 있는데 . 인터넷상에 api 라는게 만들어 지기 시작했어.
microsoft 가 xml-rpc 를 98 년에 원격으로 다는 시스템에 메소드를 호출하는 xml-rpc 라는
프로토콜을 만들어. 이게 soap 이라는 이름으로 바뀌어.

2000 년 2월에 거의 최초에 공개된 api 가 세상에 나오게되.
이떄 당시에 soap 을 사용해서 api 를 만들었는데 복잡해 보여
어떤 id 로 object 를 불러오는 분량이 14 줄 정도 됬었고 이는 흡사 우리가 잘 알고있는 web.xml 과 코딩이 비슷하게 되어있었어.
4년후에 플리커에서 api 를 만들었는데 Rest 라는 이름으로 공개했어 사람들은 2000 년에 나왔던 Rest 를 그대로 따왔지만
똑같은 일을 하는 코드인데 코드가 훨씬 짧은거야 거의 4줄 ?

그때 사람들이 soap 과 Rest 를 느낌적으로 비교하기 시작했어
soap 은 복잡하다. 규칙 많네... 어렵네 ...
반면 Rest 는 오 단순하네 규칙 적네 쉽네 !
이런 결과는 2008 년에 AWS 가 자사 API 의 사용량의 85% 가 REST 임을 밝힌거야 .
2010 년에는 Salesforce.com 조차도 Rest 를 지원하기 시작했지
그제서야 web 이 Rest 로 정착하나 싶었는데
그런데 CMIS (2008) 라는게 나왔어 cms 를 위한 표준이고 , 이때 IBM, Microsoft , EMC 등 많은 기업들이 참여를 하여
Rest binding 을 지원하기로 한거야.
그걸 본 앞서말한 로이필드는 CMIS 에 REST 란 없다. 라고 말해버린거야.
다른 사람들이 볼땐 그게 충분히 REST 라고 보였는데 REST 를 만든 로이필드는 그게 REST 가 아니래 ㅋㅋㅋㅋㅋ
// 도메인네임 /
2016 에 MS 가 가이드를 만들었어 uri 는 https://{serviceRoot}/{collection}/{id} 형식이어야 한다.
GET PUT DELETE POST HEAD PATCH OPTIONS 를 지원해야 한다.
API 버저닝은 Major.minor 로 하고 uri 에 버전정보를 포함시킨다... 등등 말이죠.
사람들이 보기엔 좋은 REST API 에 부합한 이야기인줄 알았어
이떄 로이필이 트위터에 이런 이야기를 했어 이것도 아님 이건 그냥 http-api 라고 해야함. ㅋ
그러고 나서 진흙탕 싸움이 된듯하다가 사람들이 알고 있던 REST 랑 로이필드가 말한 REST 가 너무 다른거야

뭐가 문제야 그럼 ? ? ? 도대체 REST API 가 뭐야 ?
=> REST 아키텍쳐 스타일을 따르는 API 가 REST API 야 .
뭔소리야 이게 ㅋㅋㅋㅋㅋ 이쯤 되면 포기하고싶어져
REST 는 분산 하이퍼미디어 시스템을 위한 아키텍쳐 스타일 이라는거야 .
아키텍쳐 스타일은 제약조건의 집합이지.
한마디로 제약조건들을 모두 지켜야 REST 를 따른다 라고 하는거야 .
그럼 어떤 제약조건이 있는가...? 그전에 사실
REST 아키텍쳐 스타일 이긴 한데 동시에 아키텍처 스타일 집합이야. 하이브리드 아키텍쳐 스타일 이라고도 하는데
1. client-server
2. stateless
3. cashe
4. uniform interFace
5. layered system
6. code-on-demand (optional)
로 구성이 되어있고
대체로 잘 지켜
이미 http 만 잘 따라도 다 잘 지키는거임 1~5 번까지 6번은 뭐냐면 서버에서 코드를 클라에게 보내서 실행할수 있어야 겠지 (javascript 를 말해)
근데 4번 (uniform interFace) 을 잘 만족시키지 못해 .
1. 리소스가 uri 로 식별되면 된다.
2. representation 전송을 통해서 resource 를 조작해야 한다. 만들거나 삭제하거나 업데이트 하거나 그 표현을 전송,발생한다.
3. 메시지는 스스로를 설명해야 한다. ( 이게 어려워 )
GET/HTTP/1.1 => 목적지가 빠져있어 ex) www.example.org 가 빠져있어.
또다른 예로
ex ) HTTP/1.1 200 OK
Content-Type: application/json
[ { "op": "remove" , "path": "a/b/c"}] 이런경우 content-type 이 헤더에 붙어있어야 해 .
Content-Type: application/json 이렇게 되어있으면 => 대괄호의 의미가 뭐고 중괄호는 뭐고 이런친구들을 파싱해서 알수있지만
아직 설명이 불가능해 op 가 뭐지 path 뒤에 a/b/c 는 뭐지....
Content-Type: application/json-patch+json 이러면 완벽해지는거야 .
json-patch+json : 미디어 타입으로 되어있는 메시지가 정의되어 있기 때문에 .
이친구에 대한 명세를 찾아가서 그걸 이해한다음 메시지를 해석하면 그제서야 메시지를 이해할수 있게 되
self-description 은 이처럼 해석이 가능해야하지만 media Type 이 json 이라고만 되어있지 뭔진 몰라
4. HATEOAS : 애플리케이션의 상태가 hyperlink 를 이용해 항상 전이 되어야 한다.
html 로는 a 태그 의 href
json 으로는 Link 가 있어.

HTTP/1.1 200 OK
Content-Type: application/json
Link: </articles/1>; rel="previous",
Link: </articles/3>; rel="next",

이정보는 link 로 표준문서로 나와있기떄문에 hateoas 를 만족한다 . 라고 할수 있는것이지

왜 4번을 하나 ?
독립적 진화를 한다
서버와 클라이언트가 각각 독립적으로 진화한다.
서버의 기능이 변경되어도 클라이언트를 업데이트 할 필요가 없다.

독립적으로 진화 한다?
서버가 기능이 바뀌어서 => 새로운 api 가 추가되고 => 기존 api 가 삭제되고 uri 가 변경되고
이런게 독립적으로 진화한다고 말해 .
클라이언트가 업데이트 할 필요가 없지!!!!

http/1.0 을 만들때 로이필이 고민했던것 .
http 를 고치면 web 이 깨질꺼같은데 이고민의 결과이고 , 목적이 이것이야 유니폼 인터페이스가 만족해야하는거고
이를 만족 못하면 REST 라고 말할수 없는것이지.

우리가 쓰고있는 웹브라우저는 이를 잘 만족해
그림이 바뀌든 웹페이지를 변경했다 한들 웹 브라우저를 업데이트 할 필요없고, 브라우저 업데이트 했다고 웹페이지 변경할 필요없고,
http 명세가 변경되어도 웹은 잘 동작하고, html 명세가 변경되어도 웹은 잘 동작하고...

User CRUD:

  • Create Account
  • Log In
  • See Profile

What is Joi ?

url : https://www.npmjs.com/package/joi
Joi 는 자바스크립트용 가장 강력한 스키마 설명 언어이자.
데이타 유효성 검사툴이다. ( 환경변수의 유효성 을 검사한다.)
$ npm install joi

  • 대부분의 자바스크립트 패키지는 타입스크립트로 만들어져 있지 않다. 이런경우 import 하는 방법이 다르다.
  • 따라서 import * as Joi from 'joi' 방식을 추천한다.

Input Type ? Args Type ?

우리가 Mutation 으로 인자들은 보낼때 따로따로 일일히 적어주고 보내줘야하는 노가다 성을
InputType 객체로 (하나의 쌈무 마냥) 싸서 보내는 방식이다. 대체로 dto (Data Transfer Object) 로 싸서 보낸다.

  • ArgsType 은 기본적으로 위와 같은 코드를 분리된 argument 로써 정의할수 있게 해준다.
  • InputType 은 그저 하나의 Object 라고 보면 되고 , argument 로서 graphql 에 전달하기 위한 용도다.
  • ArgsType 은 따로따로 객체에 담지않고 보내는 방식이다.

    @Mutation(returns => Boolean)
    createRestaurant(
        @Args('a') name: string,
        @Args('b') name: string,
        @Args('c') name: string,
        @Args('d') name: string, // bla ~ bla 
    )

env ?

url : https://www.npmjs.com/package/dotenv
프로젝트를 하다보면 가상의 변수를 이용하기 마련이다. 예를들어 개발계 , 검증계 , 운영계
설정파일을 일일히 수정해서 배포하고 만져주는 번거로움을 코딩으로 손쉽게 변경하는 방법이 있는데
이게 바로 dotenv (닷 ENV ) 라는 것이다 .env 는 파일에서 환경변수를 로드하는데

ignoreEnvFile ?

이 파일은 서버에 deploy 할때 환경변수 파일을 사용하지 않는것이다.

    ex) 
    ConfigModule.forRoot({
        isGloabal: true,
        envFilePath: process.env.NODE_ENV === 'dev' ? '.env.dev' : '.env.prod',
        ignoreEnvFile: process.env.NODE_ENV === 'prod' // 이렇게 되면 production 환경일때 ConfigModule 이 이파일을 무시한다. 라는 뜻이다.
    })

Active Record vs Data Mapper ?

이 둘은 DB 랑 상호작용할때 쓰는 패턴이다.
그렇다면 이 둘의 차이점은 무엇일까?
전체적으로 비슷하지만 . Data Mapper 패턴엔 Repository ( Entity 와 상호작용하는 ) 만 추가 됬을뿐이다.

Data Mapper 는 유지관리가 용이하고 대규모 앱에서 유용하다.
Active Record 는 소규모 앱에서 단순하게 사용할 수 있도록 도와준다.

어느게 더 좋다 나쁘다 이런게 없다. 자신이 임하고 있는 프로젝트에서 상황에 맞게 사용하면 그만이다.

우리 앱은 그렇게 큰 규모는 아닌데 왜 사용하나 ?
NestJs + TypeORM 개발 환경에서 Repository 를 사용하는 모듈을 쓸 수 있기 떄문이다.

  • Repository 를 이용하면 실제로 구현하는 서비스에서 접근이 가능하고 테스팅 할 떄도 접근이 가능하다.

Data Mapper (Mapped Types)

Mapped Types 는 base type 을 바탕으로 다른 버전들을 만들 수 있게 해준다.
Partial, Pick, Omit, Intersection 4가지 종류가 있다.
먼저 Partial 부터 살펴보자.
Partial 은 base Type , base class 를 가져다가 export 하고 모든 field 를 required가 아닌 class로 만들어준다.
PickType 은 input type 에서 몇 가지 property 를 선택해 새로운 class 를 만들어준다. 특정 property 를 선택.
OmitType 은 base class 에서 class 를 만드는데 몇몇 field 를 제외하고 만든다.
intersectionType 은 두개의 input 을 합쳐준다.

json web token 의 목적은 비밀 유지가 아닌 "우리만의 유효한 인증을 할 수 있게 하는것"

  내부에 담겨진 정보 자체가 아닌, 정보의 진위 여부가 중요 하단는것.
  토큰은 아무도 건드는것이 아니니깐.
  • static module 이란 어떠한 설정도 적용되어 있지 않다.

dynamic module ? 이란

결과적으로 static 모듈 이 된다. 
단지 또 다른 module 을 반환해주는 module 이야 .
  • authentication 은 누가 자원을 요청하는지 확인하는 과정이고, (token 으로 identify 를 확인.)
  • authorization 은 user 가 어떤 일을 하기전에 permission 을 확인하는 과정 이다.

authentication 작동원리

 - 우선 header 에 token 을 실어 보낸다.
 (header 는 http 에 사용되는 기술이다. , http 기술을 쓰기위해 middle ware 를 만들었다.)
 middle ware 에서 시작해서 , apollo server의 context 를 거치고 authorization 을 거쳐서 마지막으로 resolver 에 
 도착하면 커스텀 decorator 에서 graphql context 로 바꾸는 것이다.

 다시 흐름을 정리하면, header 에 token 을 보내는 걸로 시작해서 -> token 을 decrypt, verify 하는 middle ware 를 거쳐서 
 request object 에 user 를 추가해 , 그리고 request gobjet 가 graphql context 안으로 들어가게 되고 guard 가 graphql context 를 찾아 , user 가 있는지 없는지에 따라 true , falsereturn 하는 것이다.

clean code

결국은 oop 패턴이다.
resolver 은 문지기다.
resolver 는 input 들을 받고 => 그 input 들을 service 에게 전달해주는게 일이다.
service 는 이것들을 다루는 로직이 정의된 부분이다.

nestjs 에서 email api 사용 하기

node 는 front 처럼 fetch 가 없기 때문에 got 을 이용해서 http 통신을 한다.
먼저 콘솔창에 node 를 입력 후 다음과 같이 ㄱ 
>  Buffer.from('api:YOUR_API_KEY').toString('base64') 
'YXBpOllPVVJfQVBJX0tFWQ==' <= 이건 그냥 기본 인증 방식의 룰이야. 헤더는 이렇게 생겨야해.

그리고 나서 sendEmail 부분에 다음과 같이 코딩하고 
 private sendEmail(subject: string, content: string) {
        got(`https://api.mailgun.net/v3/${this.options.domain}/messages/`,{
            headers: {
                "Authorization": `Basic ${Buffer.from(`api:${this.options.apiKey}`).toString("base64")}`
            }
        })
    }
아직 from , to , subject, text 가 필요하니, 
npm i from-data 를 추가해줘 Form data 는 node.js 에서 스트림을 만드는 라이브러리야.
mail.service.ts 에서 log 를 확인해 보면 
{
  "id": "<20201128134543.1.BD11F775B6EC5284@sandbox2156156024dd44879f224623e3aa07eb.mailgun.org>",
  "message": "Queued. Thank you."
}
위와 같이 성공한다.

모든 기능에 대해서 Unit Test(UT) 를 해야한다.

 테스팅을 할때엔 사고방식을 달리해야한다.
 하나의 함수에 여러가지 logic 이 들어가 있다면 , 
 분리해서 테스트해야하고, injection 받는 service , mock 을 염두해서 jest.fn() 을 열어두어야 한다.
 참조해야하는 page 는 users.service.spec.ts 이다.
 내가 봤을때 개발자들이 테스팅하면서 제일 불편해 하는건.
 바로 "사기치는것" 이건 지극히 케이스바이 케이스 인데,
 가짜 함수, 가짜 데이터 를 (어쩌면 당연한것) 을 불편해 하는것이다.
 되도록이면 실제 돌아가는 함수를 연결시켜서 해보고싶은 충동이 있을테지만,
 이것을 참는다면, 진정 테스트는 쉬워질 것이다.
 우리는 커버리지를 줄이는것에 목표를 두어야 하고,
 braket 을 (정의되지 않은 값 if( ) 등등등) 을, 줄여야 한다는 시점에서 말이다.
 제일 비일비제 하게 등장하는 친구는 (test 세상에서) mock 이다.
 mock 은 함수의 반환값을 속이는것이다.
 다시 말해 test 의 결과 값을 테스트 하는게 아니고,
 각 한줄한줄을 테스트 하는것이다.
 End To End Test and Integration Test(IT) 를 해야할때, 
 Jest 를 어떻게 활용해야 할까?
    spec. 이 파일 이름에 포함되어있어야 한다 ! ( 이건 jest 환경을 보면 알아 package.json 에 위치하고 있어 ! )
    시작은 내가 테스트 하고 싶은 BL 이 기술되어있는 Service 를 시작할까해.


    테스트 파일에 많은 수의 테스트 함수가 작성되어 있는 경우, 
    연관된 테스트 함수들끼리 그룹화해놓으면 코드를 읽기가 좋아.
    다음과 같이 Jest의 describe() 함수를 통해 여러 개의 테스트 함수를 묶는 것이 가능하다.

    describe('UserService', () => {
        ... 
    }); 


    모든 테스트를 todo List 처럼 나열하고자 할때. 

    it 키워드를 사용해. 예를 들어 내가 login fn 을 테스트 하고 싶어. 그럼 
    it.todo('login') 라고 만들어 놓는거야.
    필요에 따라서 Mocking , 해놓고, jest.fn() <= 가짜 함수 
    를 지정해놓고 테스트하면 된다.
    파일참조 : users.service.spec.ts 
 $ npm run test:watch

 - 참고하면 좋은 [블로그](https://hees-dev.tistory.com/57)
 - 참고하면 좋은 [Jest 사용법](https://www.daleseo.com/jest-basic/)
 - 

Jest Mocking

    mocking 덕분에 우리'는 function 의 value 를 명시할 수 있고, function 의 행동도 명시할 수 있어.
    중요한 점은 mocking 을 "기대하는값" "저장하는값" "한번만실행하는값" 을 해줘야 해
    그 말처럼 우리가 function 안에 await 으로 시작하는 녀석들의 일련의 절차를 코드 한줄한줄 테스트 해주고,
    더 나아가, exception 도 mocking 으로 터트리고 그 의 결과 값 마져 테스팅 하는것을 전체적으로 테스트 해줄
    수 있어.

How todo it

    테스트를 작성하는 tip 은 it.todo('테스트할 기능')
    으로 적어두고 ( 시작 전에 몽땅 적어놓고 시작하는것이 아니라. 크게 크게 둥글게 둥그뭉실하게 적어 놓고 시작해.)
    ( 미술 할때 sketch 를 미리 떠놓는거라고 생각하면되.)👍
    

beforeAll 과 beforeEach 의 차이

이 beforeAll함수 describe는 beforeEach함수 또는 사양 전에 함수를 포함 하는 블록에 대해 한 번만 실행됩니다 .

이 beforeEach함수는 describe내부 .NET 내부에 포함 된 모든 스펙 이전뿐만 아니라이를 포함 하는 블록의 스펙 이전에 실행 describe됩니다. 아래의 A More Complex Example 에서 확인할 수 있습니다.
beforeAll 여기에 포함 된 모든 사양이 완료된 후 afterAll1 회 실행 되는 보완 기능 이 있습니다 describe.
마찬가지로이 beforeEach함수에는 afterEach각 사양 후에 실행 되는 보완 함수가 있습니다.
afterAll및 afterEach사양의 결론에 필요하고,이 문서의 일부로서 포함되지 않는 어떤 정리하는 데 사용됩니다.

보통 unit Test 를 할때는 beforeEach 
End to End Test 를 할때는 beforeAll 를 사용 한다.

What is Joi And e2eTest ?

    나름 생소 할 수 있는 e2e 테스트는 end to end 테스트를 뜻한다.
    즉, 프로젝트에서 처음부터 끝까지 하는 테스트로써 , benefit 은 
    초급, 혹은 처음 온 개발자가 e2e 테스트를 참여하면 프로젝트 전체 프로세스를 얻을수 있다.
    만일. 전체적으로 한번 교육을 받거나, 모든 프로세스를 알고있는 기획자 혹은 개발자가 있다면 꼭,
    친해지고, 그때 확 배우고 반복하고 안까먹게 머릿속에 박아 넣는것을 추천한다.
    이유는 ( 일하는 내내 삶의 질이 달라지고, 다른 타 개발자가 영입되었을때에도, 그분을 교육하면서, 
    나도 다시 업무능력이 향상되고, 무었보다 지속적으로 나를 판단하고 듣는 귀들이 쟤 일 똑바로 하네. 잘하네 소리를 
    듣게 된다.)

    일단 setting 자체는 그렇게 어렵지 않다. app.moudle 에 있는 joi 가 초기 에러를 띄울꺼고, 
    env.test 파일을 dbname 만 -test 로 바꿔준다음. 
    실제 db 를  -test (원래 db 네임 뒤에 접미사로 ) 추가해 준다음.
    npm run test:e2e 를 추가해주면 정상 작동 되는것을 볼 수 있다.
    그다음 작동이 되긴하는데 다음과 같은 에러를 볼 수 있다. 이경우엔 e2e test 파일에서 
        Ran all test suites.
        Jest did not exit one second after the test run has completed.
    
    다음 코드를 심어주면 되는데. 문법 설명은 다음과 같다. 
    // 애플리케이션을 종료한다. 왜냐하면 위 에러는 뭔가가 종료되지 않은 상태에서 jest 가 종료되었을때 뜨는 yellow 경고다.
    afterAll(async () => {
      app.close();
    }) 
    그런다음엔 test 가 돌아간 후에 application 이 종료될 것이다.
    

metaData ??

findOneOrFail ?

findOneOrFail 을 사용할때 loadRelationIds property 를 사용할수 있다.
, { loadRelationIds: true // id 만 가져오고 object 는 가져오지 않겠다는거임.}

What the... custom repository?

    커스텀 레포지토리를 사용하면 시간을 절약할수 있을뿐 만 아니라 윤택한 코딩을 할 수 있다.
    3 가지 방법이 있다. 
    첫번째. repository class 를 extends 하는 방법이 있고,
    두번쨰. abstract repository class 를 extends 하는 방법이 있고,
    세번째. @EntitiyRepository() 를 선언하는 방법이 있다.

    나는 3 번째를 사용했고,
    사용방법은 service 에 있는 함수를 그대로 해당 업무에 repositories (폴더생성) 후에 
    repository 파일을 생성한 뒤에, extends<entitiy> 를 선언해주면 된다.
    그런다음 module 쪽에 entity 에서 가져온 entity 가 아닌, 커스텀 repository 를 선언해주면 된다.
    

ResolveField() ?

    ResolveField 는 매 request 마다 계산된 Field 를 만들어준다.

Raw?

orm 코딩을 하다보면 query 를 직접 때릴 떄가 필요하다.

 const [restaurants, totalResults] = await this.restaurants.findAndCount({
        where: {
          name: Raw(name => `${name} ILIKE '%${query}%'`) // query 직접 때리고 싶을떄
        },
        skip: (page - 1) * 25,
        take: 25,
      });

      정말 쉽다. Raw(name => `${parameter} 쿼리 ${쿼리}`) // 이렇게 직접 db 에 query 를 요청 할 수 있다.

What is Cron ???

cron job 은 원하는 때마다 규칙적으로 수행되는 작업을 말한다.
예를들어 매주 월요일 오후 6시에 특정 작업을 수행하고 싶을 때
그 작업을 service 에서 실행시키는걸 말해.
이게 cron 이야.

What is subPup

 
  @Mutation(returns => Boolean)
  potatoReady() {
    pubsub.publish('hotPotatos', { // trigger 의 이름이 같아야 한다.
      readyPotato: 'Your potato is ready. love you', // object 의 키가 아래 Subscription 의 method 이름과 같다.
    });
    return true;
  }

  @Subscription(returns => String)
  readyPotato() { // method 의 function 은 .publish 의 두번째 인자에 object key 의 이름 과 같아야 한다.
    return pubsub.asyncIterator('hotPotatos'); // trigger 의 이름이 같아야 한다.
  }
 
 publish and subscribe 를 말한다. 이걸로 app 내부에서 메시지를 교환할 수 있다.
 인스턴스를 먼저 생성해준다음 => return pubsub.asyncIterator() ''' 부분에서 trigger 를 동작시켜주면 된다.

 희안한게 web socket 을 연결할때는 쿠키를 보내고 받고 그런것이 없다. request 자체가 없다는것이지.
 일단 연결되면 연결 상태를 유지한다.

 ws (Web Socket 은 Http 와 달리 다른 연결 방법이 있다.)
 다루는 방법이 다르다.

PubSub 의 한 예로 전역으로 inject 할수 있는 방법은 ?

 먼저. 전역으로 실행할 친구를 common module 쪽 module 에 심어준다.
 // 다음과 같이 말이다.
 const pubsub = new PubSub();

@Global()
@Module({
  providers: [
    {
      provide: PUB_SUB, // 상수화 하기 딱 좋은 common 쪽의 파일. [자세히]=> export const PUB_SUB = 'PUB_SUB';
      useValue: pubsub,
    },
  ],
  exports: [PUB_SUB], // 이부분이 중요. exports 하여야 한다.
})
export class CommonModule {} // 그런다음 app.module 쪽에 CommonModule 를 추가해준다.

그러나 filtering 을 하지 않는다면 subscription 은 의미가 없다.
왜냐면, 특정 update , 혹은 요청에 대해서만 알림을 받고 싶어한다면 !! 

Redis 란 ?

12.6 replay !

Eager Relations lazy Relations

eager Relation 은 db 에서 entity 를 load 할떄마다 자동으로 load 되는 relationship 을 말한다.
lazy Relation 은 한번 access 하면 load 되는것이다.

What is N + 1 Solutions

n + 1 문제는 서버가 중첩 된 데이터에 대해 데이터 저장소로 불필요한 여러 번의 왕복을 실행 함을 의미한다.

graph-ql 에서 이를 해결하는 솔루션은 DataLoader 다.
$ npm install --save dataloader

DataLoader의 instance는 자체적으로 cacheMap 을 가지고 있다.
같은 key에 대한 요청이 들어오면 caching된 값을 사용하게 되는데 web application에서 이런방식은 위험할 수 있다.
이러한 이유로 매 request마다 새로운 DataLoader 객체를 생성해서 사용하는것을 권장하고 있다.

What is Cron Job ?

$ npm install --save @nestjs/schedule
$ npm install --save-dev @types/cron

Cron job for node ?


ex ) 

  @Cron('30 * * * * *', {
    name: 'myJob',
  })
  async checkForPayments() {
    console.log('Checking for Payments(Cron)');
    const job = this.schedulerRegistry.getCronJob('myJob');
    console.log(job);
    job.stop();
  }

  @Interval(5000)
  async checkForPaymentsI() {
    console.log('Checking for Payments(Interval)');
  }

  @Timeout(5000)
  async checkForPaymentsII() {
    console.log('Time Out');
  }

How to Upload in nest js ?


just todo this.code :) 

@Controller('uploads')
export class UploadController {
  @Post('')
  @UseInterceptors(FileInterceptor('file'))
  uploadFile(@UploadedFile() file) {
    console.log(file); // 추후 aws-sdk 라이브러리 연동
  }
}

-- aws 에 로그인후 > 전체검색 iam > User ( 사용자 ) > 사용자 이름추가 > () 기존 정책 직접 연결 > (검색) S3 pullAccess > 다음 > 다음 에서 엑세스 키 , 시크릿 키 반드시 복사 해 둔다. ( 잊어버리면 다시 처음부터 생성해야 하기 때문.)
import * as AWS from 'aws-sdk'; // 이 부분 추가 

  @Post('')
  @UseInterceptors(FileInterceptor('file'))
  uploadFile(@UploadedFile() file) {
    console.log(file);
    AWS.config.update({ // 이 부분 추가 
      credentials: {
        accessKeyId: 'AKIA4G22N3AHKVHHRKR5',
        secretAccessKey: '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',
      },
    });
  }
  이제 남은것은 bucket 에 업로드 하는것이다.
  Amazon 에서는 upload 를 put 이라고 한다.
  
profile
시니어의 길로 가는 개발자

0개의 댓글