[ Node.js ] NestJS




nestjs

NEST


NEST란?

Node.js의 서버 애플리케이션 프레임워크


NEST쓰는 이유?

자바스크립트가 발전함에 따라 웹은 리액트, 뷰, 앵귤러같은 프레임워크들을 사용해서 어느정도의 규격화를 이루어냈는데

서버에서는 딱히 프레임워크다! 할만한 것들이 딱히 없었다.

근데 JS특징도 그렇고 너무 자유로운게 장점이 될 수도 있지만 단점이 될 수도 있다.

개발자 한명한명 모두가 스타일이 다르기때문에 어떤 규격이 없다면 모두가 중구난방으로 폴더구조, 파일구조를 짤 것이다….

그래서 nestjs는 규격화된 아키텍쳐를 제공함으로서 통일성있는 구조를 만들 수 있도록 해준다.

(익스프레스는… 솔직히 프레임워크라고 하기에는 좀 애매하지. 라이브러리가 맞는것 같다 )



설치방법

npx를 사용하면 되는데

$ npx @nestjs/cli new qqq

//만약 설치가 안된다면 sudo붙여서
$ sudo npx @nestjs/cli new qqq

npx는 아래 4문장의 cmd를 함축시켜서 한문장만 사용하면 되도록 해준다.

TMI) .git파일이 겹칠 수도 있어요!

nestjs를 설치하면 자동으로 .git 폴더가 형성되는데

만약 상위폴더에 이미 .git 폴더가 존재한다면 두개가 겹쳐져서 혼동이 있을 수 있으므로 경로에 맞춰서 삭제해야할수도 있음

나같은 경우는 nestjs안의 폴더의 git은 삭제하고 싶어서 cmd로 삭제해줬는데,

그때의 명령어 맨날 까먹어서 적어놓기로 한다.. 총총

.git은 숨김폴더라서 ls 로 확인해봐도 나오지 않는다.

그래서 그떄는 숨김 폴더까지 다 나오게 조회하는 ls -al 을 사용하고

만약 폴더 내에 .git폴더가 있다면

rm -rf .git 을 해주면 된다. (디렉토리 및 그 하위 모든 내용을 강제로 삭제)



$ npx @nestjs/cli new qqq
$ yarn add -g @nestjs/cli
$ nest new qqq
$ yarn remove @nestjs/cli



NESTJS 구조

자바스크립트 개발자들이 가장 자주 & 많이 사용하는 것들만 모아서 패키지로 만들어놓은것이 NESTJS.

ts, eslint, git, prettier 등등 많이 쓰는것만 모아놓음.

실제로 3년전에는 일일히 자주 쓰는 npm들을 적어놓고 하나하나 설치해줬는데

nestjs는 유명하고 자주쓰이는 lib들은 이미 설치하면서 깔려있기때문에 따로 기억하거나 일일히 설치해야하는 번거로움이 사라지게 되었다




Boiler Plate : 보일러 플레이트(초기 폴더구조)


nestjs 초기 폴더구조

src : 소스코드(API)

  • app.controller.jsapp.service.ts 가 합쳐진것들이 app.module.ts 로 가고 그게 main.ts 에서 보여짐
    (nest가 기본적으로 이미 가지고 있는 ) 익스프레스에서 실행

test : 테스트(코드검사)하는 파일

.eslintrc.js : 코드린터 중 가장 유명. 코딩문법규칙(‘==’금지 등) 정하기

.gitignore : git에서 제외할 파일

.prettierrc : 코드 포멧터 중 가장 유명. 코딩정리규칙(줄바꿈, 쿼테이션, 들여쓰기 등등) 정하기

nest-cli.json : Nest 설정파일

package.json : 기본 매뉴얼

  • "scripts" : 실행명령

  • "start:dev": "nest start --watch" : –watch는 노드몬을 말한다

  • "start:prod": "node dist/main" : 배포시

  • "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix" : 문법검사

  • "build": "nest build" : 최적화과정

  • 테스트 코드

    "test:watch": "jest --watch",
    "test:cov": "jest --coverage",
    "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
    "test:e2e": "jest --config ./test/jest-e2e.json"
    

README.md : 상세 설명서

tsconfig.build.json : 타입스크립트 설정파일-1

tsconfig.json : 타입스크립트 설정파일-2

yarn.lock : 버전 잠금 파일




기존 Apollo-Server는 Schema-First 방식

Schema-First방식 : typeDefs를 직접 하나하나 작성하는 방식

const server = new ApolloServer({
  typeDefs,
  resolvers,
});

server.listen(3000);

typeDefs를 자동으로 만들어준다!! (100%자동은 아니지만 30%정도? 기존보다 훨씬 낫다)

typeDefs가 없으면 안된다


Code-First방식 : typeDefs를 자동으로 만드는 방식


네스트에서 그래프큐엘과 아폴로서버를 사용하기 위해 4개의 npm을 설치해준다

yarn add @nestjs/graphql @nestjs/apollo graphql apollo-server-express




GraphQL & TypeScript 와 NEST

Nest는 GraphQL 애플리케이션을 빌드 하는데 스키마 우선(schema first) 및 코드 우선(code first) 방법을 제공한다

Schema Frist

Schema first 는 graphql의 schema를 먼저 정의하고, 그 schema 정의에 맞게 코드를 작성하는 방법.

schema를 작성하기 위해서는 graphql의 data model을 나타내기 위해 만들어진 SDL(Schema Definition Language) 를 사용한다

  • 장점 : 전체 관계를 직관적으로 나타냄. 팀플 커뮤니케이션 장애 확률 낮아짐

  • 단점 : 실제코드에 해당하는 resolver가 포함되지 않으므로 계속해서 동기화해야하며 Resolver코드와 SDL의 정의가 정확히 일치해야만 함.

    스키마를 직접 작성해주어야 함

Code First

코드 우선 접근 방식에서는 데코레이터와 TypeScript 클래스를 사용하여 먼저 작성한 Resolver 을 기반으로 해당 GraphQL 스키마를 자동 생성

  • 장점 : Schema와 Resolver간의 type safety를 보장하며 코드 중복이 최소화
  • 단점 : SDL과 비교해서 직관적이지 못함
//app.modules.ts

import { ApolloDriver, ApolloDriverConfig } from "@nestjs/apollo";
import { Module } from "@nestjs/common";
import { GraphQLModule } from "@nestjs/graphql";

@Module({
  imports: [
    GraphQLModule.forRoot<ApolloDriverConfig>({
      driver: ApolloDriver,
      autoSchemaFile: "src/commons/graphql/schema.gql", //code first방법으로 설정
    }),
  ],
})
export class AppModule {}

` autoSchemaFile: ‘src/commons/graphql/schema.gql’` 를 추가함으로써 스키마파일을 따로 만들어주지 않아도

빌드하면서 src/commons/graphql 해당경로에 자동으로 스키마파일(schema.gql)이 생성된다

(짱편함. 물론 100% 자동으로 만들어주는건 아닌데 거의 30%만 작성하면 자동으로 만들어줌!)

GraphQL playground에 들어오는 Type들을 Schema First 를 사용하면 직접 만들어 줘야하는데

Code-First 를 사용하면 자동으로 생성 가능합니다.




app.modules.ts

import { BoardsModule } from "./apis/boards/boards.module";
import { ApolloDriver, ApolloDriverConfig } from "@nestjs/apollo";
import { Module } from "@nestjs/common";
import { GraphQLModule } from "@nestjs/graphql";

//설정파일, 디펜던시 뭐 쓸건지(provider는 app.controller의 @Controller(=데코레이터)에 들어간다)
@Module({
  imports: [
    BoardsModule,
    GraphQLModule.forRoot<ApolloDriverConfig>({
      driver: ApolloDriver,
      autoSchemaFile: "src/commons/graphql/schema.gql",
    }),
  ],
})
export class AppModule {}

@Module 이 부분이 의존성 주입 해주는 부분

@module() 은 ‘얘는 모듈로 쓸거야’ 라고 nest에게 말해주는것과 같은 뜻이다

@ 가 붙은것은 데코레이터인데, 함수임.

forRoot() 메서드는 옵션 객체를 인수로 받을 수 있으며, 이 옵션은 ApolloServer 생성자(constructor)에 전달됩니다.

app.module 을 통해서 controllers 와 providers 를 합쳐줄 것이기 때문에 기본으로 생성된 app.controller.js와 app.providers.ts를 삭제

(추후에 필요하면 app.module통해서 조립해줄 예정)



src 안에 apis 라는 폴더를 만들고 API폴더로 만든다. apis 폴더 안에 boards 폴더를 생성한다.

  • apis : API 폴더
  • common : 모든 API 에서 공통적으로 사용되는 것들을 따로 빼놓는 폴더 ( 스키마가 자동생성되는 폴더이기도 함)

board 폴더 안에는 게시물 관련 API 가 들어감.

따라서, board 폴더 안에 board.module.ts , board.resolver.ts, board.service.ts 파일을 만들어 주세요.

  • .module.ts : .resolver.ts + .service.ts 합쳐주는 파일
  • .resolver.ts : 실질적 API 로직 (.controller.ts 와 같은 파일로 이름만 다른 파일입니다. 앞으로 controller 대신에 resolver 를 사용하게 될 것입니다.)
  • .service.ts : 핵심 비즈니스 로직




borads.resolver.ts (⭐️중요💫)

클라이언트는 아직 DB와 상호 작용할 수 있는 방법이 없다.

이를 해결하려면 Resolver 클래스를 만들어야 합니다.

Code Frist 방법에서 리졸버 클래스는 Resolver 함수를 정의하고 쿼리 유형을 생성함

Routing 개념을 적용하여 어떤 Resolver가 어떤 Request를 수신하는지 제어한다.

각 Resolver는 최소 1개의 Route를 가지며 각 Route는 다른 action으로 동작합니다.

constructor에 BoardService를 주입하고 getHello라는 Resolver 함수를 만들어

BoardService의 aaa 비즈니스 로직을 실행시켜 보도록하자.

import { BoardsService } from "./boards.service";
import { Query, Resolver } from "@nestjs/graphql";

@Resolver({})
export class BoardsResolver {
  constructor(
    private readonly boardsService: BoardsService //
  ) {}

  //graphql타입(String)과 ts타입(string)의 대소문자가 다르니 주의!
  @Query(() => String, { nullable: true })
  fetchBoards(): string {
    return this.boardsService.aaa();
  }
}
  • Query 를 import 할 때 '@nestjs/graphql' 로 import 가 되는지 꼭 확인!
  • @Query() 안에 getHello 함수에 대한 Return 타입을 정의할 수 있습니다.
    • return 타입을 정의해 주지 않으면, Schema를 자동으로 만들어 주지 않기에 서버 실행시 에러가 발생.
    • @Query(() => String) 👉🏻 GraphQL에서 API Docs 를 만들기 위한 type으로 대문자로 시작
    • getHello(): string 👉🏻 typescript type 지정으로 해당 타입 지정은 없어도 되며, 소문자로 시작




boards.service.ts

// board.service.ts

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

@Injectable()
export class BoardService {
  aaa() {
    return "Hello World!";
  }
}

@Injectable : 의존성주입 관련 데코레이터




board.module.ts

// board.module.ts

import { Module } from "@nestjs/common";
import { BoardResolver } from "./boards.resolver";
import { BoardService } from "./boards.service";

@Module({
  // imports: [],
  // controllers: [],
  providers: [BoardResolver, BoardService],
})
export class BoardModule {}

BoardResolverBoardServiceproviders 에 작성해 줌으로써 board.module.ts 파일에서 합쳐짐




이렇게 최종적으로는 AppModulemain.ts 에서 실행되게 된다




그러니까 정리하자면 모든 ts파일들이 모듈화되어서 export, import 되고,

결국에는 최종적으로 합친 모든 파일이 main.ts에서 보여진다는 개념이 중요하다.

xxx.resolver.tsxxx.services.ts 가 합쳐져서 xxx.module.ts 가 되고

그게 바로 상위 파일인 app.modules.ts 로 import되어 합쳐진뒤

main.ts 로 보여지게 된다



끗!




















© 2018. by sora

Powered by sora