2023.03.29 TIL

์ •์Šน์›ยท2023๋…„ 4์›” 7์ผ
0
post-thumbnail
post-custom-banner

๐Ÿ“’ ๋ชฉ์ฐจ

  • ๐Ÿ“Œ NestJS๋ž€?
  • ๐Ÿ“Œ NestJS ์„ค์น˜
  • ๐Ÿ“Œ NestJS ํด๋” ๊ตฌ์กฐ
  • ๐Ÿ“Œ NestJS package.json
  • ๐Ÿ“Œ prettier ์ €์žฅ์‹œ ์ž๋™ํฌ๋ฉงํŒ…
  • ๐Ÿ“Œ TypeScript ํด๋” ๊ตฌ์กฐ
  • ๐Ÿ“Œ NestJS started with GraphQL & TypeScript

๐Ÿ“Œ NestJS๋ž€?

NestJS๋Š” TypeScript(ํƒ€์ž… ์Šคํฌ๋ฆฝํŠธ)๋ฅผ ์ง€์›ํ•˜๋Š”ย ํšจ์œจ์ ์ด๊ณ  ํ™•์žฅ ๊ฐ€๋Šฅํ•œย Node.js์˜ ์„œ๋ฒ„ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ํ”„๋ ˆ์ž„์›Œํฌ์ด๋‹ค.

OOP (Object Oriented Programming), FP (Functional Programming) ๋ฐ FRP (Functional Reactive Programming) ์š”์†Œ๋ฅผ ๊ฒฐํ•ฉํ•˜๋Š” ํŠน์ง•์„ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค.

Node.js๋กœ Backend๋ฅผ ๋งŒ๋“œ๋Š” ๊ฒƒ์€ ๋งˆ์น˜ ๋ ˆ๊ณ ์™€ ๊ฐ™์€๋ฐ, ๋ ˆ๊ณ ์ฒ˜๋Ÿผ ์กฐ๋ฆฝํ•  ์ˆ˜ ์žˆ๋Š” ๋ถ€ํ’ˆ์ด ์žˆ๊ณ , ๊ทธ ์ž‘์€ ๋ถ€ํ’ˆ๋“ค์„ ์ฐจ๊ทผ์ฐจ๊ทผ ์กฐ๋ฆฝํ•˜๋‹ค ๋ณด๋ฉด ๊ฑฐ๋Œ€ํ•œ ์™„์„ฑํ’ˆ์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.

Node.js๋„ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ํ•˜๋‚˜์˜ ํŒŒ์ผ์—์„œ ์‹œ์ž‘ํ•ด์„œ ์ ์ฐจ ์ ์ฐจ ์ปค์ง€๊ฒŒ ๋œ๋‹ค.

๊ทœ์น™๋„, ์ œ์•ฝ๋„ ์—†์ด ์ž์œ ๋กญ๊ฒŒ ํ•  ์ˆ˜ ์žˆ๊ณ  0์—์„œ ๊ฑฐ๋Œ€ํ•œ ๊ฒƒ์„ ์ฐฝ์กฐํ•˜๋Š” ๊ณผ์ •์ด๊ธฐ์— ๋งค์šฐ ํ›Œ๋ฅญํ•œ ๊ฒฝํ—˜์ด ๋  ์ˆ˜ ์žˆ๋‹ค.

ํ•˜์ง€๋งŒ, ๋ฌธ์ œ๋Š” ๋„ˆ๋ฌด ์ œ์•ฝ์ด ์—†๊ณ , ๋„ˆ๋ฌด ์ž์œ ๋กญ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.

๋•Œ๋กœ๋Š” ๊ทœ์น™์ด๋‚˜ ์ œ์•ฝ, ์ฆ‰ ๊ตฌ์กฐ์™€ ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ ํ•„์š”ํ•  ๋•Œ๊ฐ€ ์žˆ๋‹ค.

๋‹ค๋ฅธ ์–ธ์–ด, ์˜ˆ๋ฅผ ๋“ค์–ด ํŒŒ์ด์ฌ์—๋Š” ์žฅ๊ณ ๋ผ๋Š” ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ ์žˆ๊ณ  Java๋Š” Spring์ด ์žˆ์Šต๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ Node.js์—๋Š” ๊ทœ์น™์ด ์—†๊ณ , ์–ด๋–ค ํŒจํ„ด์„ ๋”ฐ๋ผ์•ผ ํ• ์ง€๋„ ๋ชจ๋ฅด๋ฉฐ ์‚ฌ์šฉ์ž๊ฐ€ ์ •์˜ํ•ด์„œ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.

์ด๋Ÿฐ ์ž์œ ๋กœ์›€์€ ์ข‹์€ ์ ๋„ ๋งŽ์ง€๋งŒ, ํ”„๋กœ์ ํŠธ๋กœ ํ˜‘์—…์„ ์ง„ํ–‰ํ•  ๊ฒฝ์šฐ ๋งค์šฐ ์–ด๋ ต๋‹ค.

๊ฐœ๋ฐœ์ž๋งˆ๋‹ค ๋‹ค์–‘ํ•œ Architecture ํŒจํ„ด์„ ๊ฐ€์ง€๊ณ  ์žˆ๊ณ , ํ”„๋กœ์ ํŠธ์˜ ๊ทœ๋ชจ๊ฐ€ ์ปค์งˆ์ˆ˜๋ก ๊ฐœ์ธ์˜ ๊ตฌ์กฐ์™€ ์Šคํƒ€์ผ์˜ ๋‹ค๋ฆ„์œผ๋กœ ์ธํ•ด ํ˜‘์—… ๊ณผ์ •์— ๋“œ๋Š” ์†Œํ†ต ๋น„์šฉ์ด ์ฆ๊ฐ€ํ•œ๋‹ค.

์ด๋Š” ์ƒ์‚ฐ์„ฑ ์ €ํ•˜์™€ ์œ ์ง€ ๋ณด์ˆ˜์˜ ์–ด๋ ค์›€์œผ๋กœ ์ด์–ด์ง„๋‹ค.

NestJS๋Š” Architecture ๊ตฌ์กฐ๋ฅผ ์ œ๊ณตํ•จ์œผ๋กœ์จ Node.js์˜ ์ฃผ์š” ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•ด ์ค€๋‹ค!!!

NestJs๋Š” Node.js๋ฅผ ์œ„ํ•œ ํ”„๋ ˆ์ž„์›Œํฌ๋กœ, ๊ทœ์น™๊ณผ ๊ตฌ์กฐ ์—†์ด ์ž์œ ๋ถ„๋ฐฉํ•œ node.js๋ฅผ ์ˆœ์‹๊ฐ„์— Python+Django, Java+Spring ์ˆ˜์ค€์œผ๋กœ ๋งŒ๋“ค์–ด์ค€๋‹ค.

๋•Œ๋ฌธ์— ๊ฐ ๊ฐœ๋ฐœ์ž๋“ค์ด Architecture๋ฅผ ํ†ต์ผํ•˜๊ณ  ์†Œํ†ต ๋น„์šฉ์„ ์ ˆ๊ฐํ•˜๋ฉฐ, ํ™•์žฅ์„ฑ ์žˆ๊ณ  ํšจ์œจ์ ์ธ ๊ฐœ๋ฐœ์„ ํ•  ์ˆ˜ ์žˆ๋‹ค.

โœ… ํšจ์œจ์„ฑ

NestJS๋Š” ๊ฐœ๋ฐœ์ž์™€ ํŒ€์ด ๊ณ ๋„๋กœ ํ…Œ์ŠคํŠธ ๊ฐ€๋Šฅํ•˜๊ณ , ํ™•์žฅ ๊ฐ€๋Šฅํ•˜๋ฉฐ, ๋Š์Šจํ•˜๊ฒŒ ๊ฒฐํ•ฉ๋˜๊ณ  ์œ ์ง€๊ด€๋ฆฌ๊ฐ€ ์‰ฌ์šด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋Š”ย ์ฆ‰์‹œ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์•„ํ‚คํ…์ฒ˜๋ฅผ ์ œ๊ณตํ•œ๋‹ค.

๊ทธ ์™ธ์—๋„ย TypeScript๊ธฐ๋ฐ˜์˜ Framework์ด๋ฉฐย Dependency Injection(์˜์กด์„ฑ์ฃผ์ž…),ย Inversion of Control(์ œ์–ด์˜ ์—ญ์ „),ย Module์„ ํ†ตํ•œ ๊ตฌ์กฐํ™” ๋“ฑ ์ƒ์‚ฐ์„ฑ์— ์šฉ์ดํ•˜๋‹ค.

โœ… ์•ˆ์ •์„ฑ

NestJS๋Š”ย TypeScript๋ฅผ ์ ๊ทน์ ์œผ๋กœ ๋„์ž…ํ•˜๋ฉด์„œ ์„œ๋ฒ„ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ฐœ๋ฐœ ์‹œ ๋ฐœ์ƒ ๊ฐ€๋Šฅํ•œ ์˜ค๋ฅ˜๋“ค์„ ์‚ฌ์ „์— ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•œ๋‹ค.

๋˜ํ•œ ์„ธ๋ถ€์ ์ธ Module๋กœ ๋‚˜๋ˆ„์–ด์ ธ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋…๋ฆฝ์ ์ธ Unit Test๋ฅผ ์‰ฝ๊ฒŒ ์ž‘์„ฑ ๊ฐ€๋Šฅํ•˜๋„๋ก ๊ตฌํ˜„๋˜์–ด ์žˆ๋‹ค.

โœ… ํ™•์žฅ์„ฑ

NestJS๋Š” Module Class๋ฅผ ์ง€์›ํ•˜๋ฉฐ, ๊ฐ Module์€ ๋น„์Šทํ•œ ๊ธฐ๋Šฅ๊ณผ ๊ฐœ๋…๋“ค์„ Class ํ•œ ๊ณณ์— ๋‹ด์•„ ์บก์Šํ™”ํ•˜๊ณ  ์„œ๋กœ Import๊ฐ€ ๊ฐ€๋Šฅํ•˜๋„๋ก ๊ตฌํ˜„๋˜์–ด ์žˆ๋‹ค.

์ด๋Ÿฌํ•œ Module ๊ตฌ์กฐ๋Š” Architecture๋ฅผย ์กฐ์ง์ (Organize)์œผ๋กœ ๊ฐ€์ ธ๊ฐ€๊ฒŒ ํ•˜๊ณ ย ๋Š์Šจํ•œ ๊ฒฐํ•ฉ(Loose Coupling)์„ ๊ฐ€๋Šฅํ•˜๊ฒŒ ๋งŒ๋“ค์–ดย ํ™•์žฅ์„ฑ(Extensible)๊ณผย ํ…Œ์ŠคํŠธ ๊ฐ€๋Šฅ์„ฑ(Testable)์„ ๋†’์ธ๋‹ค.

๐Ÿ“Œ NestJS ์„ค์น˜

โœ… NestJS ์„ค์น˜ ๋ช…๋ น์–ด

  • -g : global์˜ ์•ฝ์ž๋กœ, ํŠน์ • ์†Œ์Šค์ฝ”๋“œ๋ฅผ ํ•ด๋‹น ํด๋”์— ์„ค์น˜ํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ์ปดํ“จํ„ฐ์— ์„ค์น˜ํ•˜๊ฒŒ ๋˜๋Š” ๊ฒƒ

  • cli : CLI๋Š” Command-line interface๋กœ ํ„ฐ๋ฏธ๋„์—์„œ ๋ช…๋ น์–ด๋ฅผ ์ž…๋ ฅํ•ด ํด๋”๋ฅผ ์—ด๊ณ , ํŒŒ์ผ์„ ์‹คํ–‰ํ•˜๋Š” ๋“ฑ์˜ ์ƒํ˜ธ ์ž‘์šฉํ•˜๋Š” ๋ฐฉ์‹์„ ๋งํ•ฉ๋‹ˆ๋‹ค.

yarn add -g @nestjs/cli

โœ… NestJS ๋ฒ„์ „ ํ™•์ธํ•˜๊ธฐ (์ •์‚ฐ์ ์œผ๋กœ ์„ค์น˜ ๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•˜๊ธฐ)

nest --version

โœ… nest ๋ช…๋ น์–ด ํ†ตํ•ด ์ƒˆ๋กœ์šด nest ํ”„๋กœ์ ํŠธ ๋งŒ๋“ค๊ธฐ

nest new aaa(ํด๋”๋ช…)
// ์œ„์˜ ๋ช…๋ น์–ด๊ฐ€ ์•ˆ๋œ๋‹ค๋ฉด ์•„๋ž˜ ๋ช…๋ น์–ด๋กœ ๋งŒ๋“ค๊ธฐ
npx nest new aaa(ํด๋”๋ช…)

โœ… git clone์œผ๋กœ Nest ํ”„๋กœ์ ํŠธ ์ƒ์„ฑํ•˜๊ธฐ

git clone https://github.com/nestjs/typescript-starter.git aaa
// aaa ํ”„๋กœ์ ํŠธ ํด๋”๊ฐ€ ์ƒ์„ฑ

yarn install // yarn ๋ช…๋ น์–ด๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์„ค์น˜

yarn start // ์‹คํ–‰ ๋ช…๋ น์–ด๋ฅผ ์ž…๋ ฅํ•˜์—ฌ ์‹คํ–‰

โœ… NestJS .git ํŒŒ์ผ ๊ด€๋ฆฌ

NestJs ๋ฅผ ๋งŒ๋“ค๊ฒŒ ๋˜๋ฉด typescript ํŒŒ์ผ ๋“ฑ ์—ฌ๋Ÿฌ ํŒŒ์ผ๋“ค๊ณผ ํ•จ๊ป˜ .git ํŒŒ์ผ๋„ ์ž๋™์œผ๋กœ ๋งŒ๋“ค์–ด์ง€๊ฒŒ ๋œ๋‹ค.

ํ•˜์ง€๋งŒ, ์ตœ์ƒ๋‹จ์— .git ํŒŒ์ผ์ด ์žˆ๋‹ค๋ฉด git ํŒŒ์ผ์ด 2๊ฐœ ์ด์ƒ ์กด์žฌํ•˜๊ฒŒ ๋˜๋ฏ€๋กœ git add, commit ํ›„ push ํ•  ๋•Œ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒ๋˜๋ฏ€๋กœ NestJs ๋ฅผ ๋งŒ๋“ค ๋•Œ ์ž๋™ ์ƒ์„ฑ๋œ git ํŒŒ์ผ์„ ์‚ญ์ œํ•ด ์ฃผ์–ด์•ผ ํ•œ๋‹ค.

ls -al // ์ˆจ๊ฒจ์ง„ ํŒŒ์ผ๊นŒ์ง€ ๋ชจ๋‘ ์กฐํšŒํ•˜๊ธฐ (.git ํŒŒ์ผ)

rm -rf .git // ์ˆจ๊ฒจ์ ธ์žˆ๋Š” .gitํŒŒ์ผ ์ง€์šฐ๊ธฐ

๐Ÿ“Œ NestJS ํด๋” ๊ตฌ์กฐ

โœ… ๋ณด์ผ๋Ÿฌ ํ”Œ๋ ˆ์ดํŠธ (์ดˆ๊ธฐ ํด๋” ๊ตฌ์กฐ)

์ƒˆ๋กœ์šด ํ”„๋กœ์ ํŠธ๋ฅผ ์‹คํ–‰ํ• ๋•Œ ์ฒ˜์Œ ๋งŒ๋“ค์–ด์ง„ Nest ํด๋” ๊ตฌ์กฐ.

๋ชจ๋“  ํšŒ์‚ฌ๊ฐ€ ๊ฐ™์€ ๋ณด์ผ๋Ÿฌ ํ”Œ๋ ˆ์ดํŠธ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ๊ฒƒ์€ ์•„๋‹ˆ๋‹ค.

ํšŒ์‚ฌ์— ๋”ฐ๋ผ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ํ•„์š”ํ•œ ๊ธฐ๋Šฅ๋“ค์ด ๋‚ด์žฅ๋˜์–ด ์žˆ๋Š” ๋ณด์ผ๋Ÿฌ ํ”Œ๋ ˆ์ดํŠธ๊ฐ€ ์กด์žฌํ•œ๋‹ค.

๐Ÿ“Œ NestJS package.json

package.json ํŒŒ์ผ์€ ํ”„๋กœ์ ํŠธ ์ „์ฒด ์„ค๋ช…์„œ๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ๋‹ค.

scripts ๋Š” ๋ช…๋ น์–ด๋ฅผ ์‰ฝ๊ฒŒ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋‹จ์ถ• ๋ช…๋ น์–ด๋ฅผ ๋งŒ๋“ค์–ด ๋‘” ๊ฒƒ์ด๋‹ค.

// package.json

"scripts": {
    "prebuild": "rimraf dist",
    "build": "nest build",
    "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
    "start": "nest start",
    "start:dev": "nest start --watch",
    "start:debug": "nest start --debug --watch",
    "start:prod": "node dist/main",
    "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
    "test": "jest",
    "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"
  },
  • index.js , index.ts ์™€ ๊ฐ™์€ ํŒŒ์ผ์ด ๋”ฐ๋กœ ์กด์žฌํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, scripts ํƒœ๊ทธ ์•ˆ์˜ ์—ฌ๋Ÿฌ ๋ฐฉ๋ฒ•์œผ๋กœ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ์‹œ์ž‘ํ•˜๋Š” ๋ถ€๋ถ„์€ "start": "nest start" ์˜ start ๋กœ ์‹คํ–‰ํ•ด ์ฃผ์‹œ๋ฉด ๋œ๋‹ค.
    • ๊ฐœ๋ฐœํ•  ๋•Œ : "start:dev" ์‚ฌ์šฉ (dev ๋Š” development ์˜ ์•ฝ์ž)
    • ๋ฐฐํฌํ•  ๋•Œ : "start:prod" ์‚ฌ์šฉ (prod ๋Š” production ์˜ ์•ฝ์ž)
      ( "start:dev": "nest start --watch" โ‡’ watch : ์†Œ์Šค์ฝ”๋“œ๊ฐ€ ๋ฐ”๋€Œ๋ฉด ์ž๋™์œผ๋กœ ๋ฆฌํ”„๋ ˆ์‰ฌ ํ•ด์ฃผ๋Š” ๊ฒƒ )
  • Nest ์ž์ฒด์—์„œ nodemon ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ด์ฃผ๊ณ  ์žˆ๊ธฐ์— nodemond์€ ํ•„์š” ์—†๋‹ค.
  • test : ๋ฐฐํฌ์ „์— ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด์„œ ์‹คํ–‰์‹œ์ผœ ๋ด„์œผ๋กœ ๋ชจ๋“  API๊ฐ€ ์ œ๋Œ€๋กœ ์ž‘๋™ํ•˜๋Š”์ง€ ํ™•์ธํ•˜๋Š” ๊ฒƒ์œผ๋กœ ๋ณด๋‹ค ์•ˆ์ „ํ•œ ๊ฐœ๋ฐœ์„ ํ•˜๊ธฐ ์œ„ํ•œ ๊ฒƒ์ด๋‹ค.
// package.json

"dependencies": {
    "@nestjs/common": "^8.0.0",
    "@nestjs/core": "^8.0.0",
    "@nestjs/platform-express": "^8.0.0",
    "reflect-metadata": "^0.1.13",
    "rimraf": "^3.0.2",
    "rxjs": "^7.2.0"
  },
  "devDependencies": {
    "@nestjs/cli": "^8.0.0",
    "@nestjs/schematics": "^8.0.0",
    "@nestjs/testing": "^8.0.0",
    "@types/express": "^4.17.13",
    "@types/jest": "^27.0.1",
    "@types/node": "^16.0.0",
    "@types/supertest": "^2.0.11",
    "@typescript-eslint/eslint-plugin": "^4.28.2",
    "@typescript-eslint/parser": "^4.28.2",
    "eslint": "^7.30.0",
    "eslint-config-prettier": "^8.3.0",
    "eslint-plugin-prettier": "^3.4.0",
    "jest": "^27.0.6",
    "prettier": "^2.3.2",
    "supertest": "^6.1.3",
    "ts-jest": "^27.0.3",
    "ts-loader": "^9.2.3",
    "ts-node": "^10.0.0",
    "tsconfig-paths": "^3.10.1",
    "typescript": "^4.3.5"
  },

dependencies์™€ devDependencies ๋Š” node ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋“ค์˜ ์„ค์น˜ ๋ชฉ๋ก์„ ๋‚˜ํƒ€๋‚ธ๋‹ค.

  • dependencies : ๋ฐฑ์—”๋“œ ์„œ๋ฒ„๋ฅผ ์‹คํ–‰์‹œํ‚ฌ ๋•Œ ํ•„์š”ํ•œ ๋ชจ๋“ˆ๋“ค์ด ์œ„์น˜ํ•œ๋‹ค.
  • devDependencies : VS code ๋‚ด์—์„œ ๊ฐœ๋ฐœํ•  ๋•Œ ํ•„์š”ํ•œ ๋ชจ๋“ˆ๋“ค์ด ์œ„์น˜ํ•œ๋‹ค.
// package.json

"jest": {
    "moduleFileExtensions": [
      "js",
      "json",
      "ts"
    ],
    "rootDir": "src",
    "testRegex": ".*\\.spec\\.ts$",
    "transform": {
      "^.+\\.(t|j)s$": "ts-jest"
    },
    "collectCoverageFrom": [
      "**/*.(t|j)s"
    ],
    "coverageDirectory": "../coverage",
    "testEnvironment": "node"
  }

๋งˆ์ง€๋ง‰์œผ๋กœ jest ๊ฐ€ ์žˆ๋‹ค.

jest๋Š” ํ…Œ์ŠคํŠธ ๊ด€๋ จ๋œ ๋ถ€๋ถ„์ด๋‹ค. ๋งˆ์šฐ์Šค๋กœ ํ•˜๋‚˜ํ•˜๋‚˜ ํด๋ฆญํ•ด์„œ ํ…Œ์ŠคํŠธํ•ด ๋ณผ ์ˆ˜ ์žˆ์ง€๋งŒ, ๋ฐฑ์—”๋“œ๋Š” ๋ณดํ†ต ๋ณด์ด์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์—, ๋ช…๋ น์–ด๋ฅผ ์ด์šฉํ•ด์„œ ํŒŒ์ผ์„ ์‹คํ–‰ํ•ด์„œ ํ…Œ์ŠคํŠธํ•œ๋‹ค.

๐Ÿ“Œ prettier ์ €์žฅ์‹œ ์ž๋™ํฌ๋ฉงํŒ…

ํŒŒ์ผ์˜ ์ตœ์ƒ๋‹จ ์œ„์น˜์—์„œ .vscode ํด๋”๋ฅผ ๋งŒ๋“ค๊ธฐ.

.vscode ํด๋” ์•ˆ์— settings.json ํŒŒ์ผ์„ ๋งŒ๋“ค๊ธฐ.

settings.json ํŒŒ์ผ ์•ˆ์— VS Code settings ์„ ์ž‘์„ฑํ•˜๊ธฐ.

// settings.json

{
    "editor.formatOnSave": true,
    "editor.defaultFormatter": "esbenp.prettier-vscode"
}
  • "editor.formatOnSave": true : ์ €์žฅํ•˜๋ฉด ํ”„๋ฆฌํ‹ฐ์–ด๊ฐ€ ๋ฐ”๋กœ ์ ์šฉ๋  ์ˆ˜ ์žˆ๊ฒŒ ์„ค์ •
  • "editor.defaultFormatter": "esbenp.prettier-vscodeโ€ : prettier ํฌ๋ฉง ์ ์šฉ ์„ค์ •

๐Ÿ“Œ TypeScript ํด๋” ๊ตฌ์กฐ

โœ… main.ts

// main.ts

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  await app.listen(3000);
}
bootstrap();
  • ์ฒ˜์Œ์— ์‹คํ–‰๋˜๋Š” ๋ถ€๋ถ„์ด main.ts ํŒŒ์ผ์ด๋‹ค.

  • bootstrap : ํ•จ์ˆ˜ ์ด๋ฆ„์ด๊ธฐ์— bootstrap ์™ธ์— ์›ํ•˜๋Š” ์ด๋ฆ„์œผ๋กœ ์ž‘์„ฑ ๊ฐ€๋Šฅํ•˜๋‹ค.

  • app.listen(3000) : 3000๋ฒˆ ํฌํŠธ๊ฐ€ ์‹คํ–‰๋˜์„œ 24์‹œ๊ฐ„ ๋™์•ˆ listening ํ•˜๊ณ  ์žˆ์„๊ฒƒ์œผ๋กœ ๊ธฐ์กด์— ์‹คํ–‰ ์ค‘์ด๋˜ 3000๋ฒˆ ํฌํŠธ๊ฐ€ ์กด์žฌํ•œ๋‹ค๋ฉด ์ข…๋ฃŒ์‹œ์ผœ์ค˜์•ผ ํ•œ๋‹ค.

  • .create(AppModule) : AppModule ์•ˆ์— ์šฐ๋ฆฌ๊ฐ€ ์•ž์œผ๋กœ ๋งŒ๋“ค๊ฒŒ ๋˜๋Š” ๋ชจ๋“  ๋‚ด์šฉ๋“ค(API ๋“ฑ)์ด ํฌํ•จ๋˜์–ด ์žˆ๋‹ค.

โœ… app.controller.spec.ts

test ๊ด€๋ จ ์ฝ”๋“œ์ด๋‹ค.

โœ… app.module.ts

// app.module.ts

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';

@Module({
  imports: [],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

app.module.ts ํŒŒ์ผ์—์„œ app.controller.ts ์™€ app.service.ts ๊ฐ€ ํ•ฉ์ณ์ง€๊ฒŒ ๋œ๋‹ค.

app.controller.ts ํŒŒ์ผ์€ controllers ์— ํ•ด๋‹น๋˜๋ฉฐ app.service.ts ํŒŒ์ผ์€ providers ์— ํ•ด๋‹นํ•œ๋‹ค.

๋”ฐ๋ผ์„œ ์ด module ์„ main.ts ํŒŒ์ผ์— import ํ•ด์ฃผ๊ฒŒ ๋œ๋‹ค.

๋˜ํ•œ, NestJS์—์„œ๋Š” ์ž๋™์œผ๋กœ ์˜์กด์„ฑ์ฃผ์ž…์„ ํ•ด์ฃผ๊ฒŒ๋˜๋Š”๋ฐ module ๋ถ€๋ถ„์ด ์˜์กด์„ฑ์ฃผ์ž…์„ ํ•ด์ฃผ๋Š” ๋ถ€๋ถ„์ด๋‹ค.

โœ… app.controller.ts

// app.controller.ts

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

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

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

์˜์กด์„ฑ์ฃผ์ž…์„ ์‚ฌ์šฉํ•˜์—ฌ appService ๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๊ณ  ์žˆ์œผ๋ฉฐ, ์ œ์–ด๊ฐ€ ์—ญ์ „๋œ๊ฒƒ์„ ํ™•์ธ ํ•  ์ˆ˜ ์žˆ๋‹ค.

@Controller() : ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋กœ ํ•จ์ˆ˜์˜ ์ •์˜ ์ „์— ํ˜ธ์ถœ๋˜๋ฉฐ ๊ธฐ์กด ๊ฐ์ฒด์— ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ๋””์ž์ธ ํŒจํ„ด์ด๋‹ค.

@Get() : Get ์š”์ฒญ์ด ์‹คํ–‰๋˜๋Š” ๋ถ€๋ถ„์œผ๋กœ, ( ) ์•ˆ์— dafault ๊ฐ’์€ โ€˜/โ€™ ์ด๋‹ค.

๐Ÿ“Œ NestJS started with GraphQL & TypeScript

Nest๋Š” GraphQL ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋นŒ๋“œ ํ•˜๋Š”๋ฐ ย ์Šคํ‚ค๋งˆ ์šฐ์„ (schema first)ย ๋ฐ ์ฝ”๋“œ ์šฐ์„ (code first) ๋ฐฉ๋ฒ•์„ ์ œ๊ณตํ•œ๋‹ค.

โœ… Schema First(์Šคํ‚ค๋งˆ ์šฐ์„ )

Schema first ๋Š” graphql์˜ schema๋ฅผ ๋จผ์ € ์ •์˜ํ•˜๊ณ , ๊ทธ schema ์ •์˜์— ๋งž๊ฒŒ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋งํ•œ๋‹ค.

schema๋ฅผ ์ž‘์„ฑํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” graphql์˜ data model์„ ๋‚˜ํƒ€๋‚ด๊ธฐ ์œ„ํ•ด ๋งŒ๋“ค์–ด์ง„ SDL(Schema Definition Language) ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

SDL์€ ๋ชจ๋“  ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด์™€ ๋…๋ฆฝ์ ์ด๋ฉฐ, ํ†ตํ•ฉ๋˜๋Š” ์–ธ์–ด์ด๊ณ ,ย NestJS์—์„œ๋Š” GraphQL ์Šคํ‚ค๋งˆ๋ฅผย TypeScript์˜ ํด๋ž˜์Šค ๋ฐ ์ธํ„ฐํŽ˜์ด์Šค ํ˜•์‹์œผ๋กœ ๊ตฌํ˜„๋œ๋‹ค.

๋”ฐ๋ผ์„œ, GraphQL ์Šคํ‚ค๋งˆ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ TypeScript ์ •์˜ (ํด๋ž˜์Šค ๋˜๋Š” ์ธํ„ฐํŽ˜์ด์Šค ์‚ฌ์šฉ)๋ฅผ ์ž๋™์œผ๋กœ ์ƒ์„ฑํ•˜์—ฌ ์ค‘๋ณต๋œ ์ƒ์šฉ๊ตฌ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ํ•„์š”์„ฑ์„ ์ค„์—ฌ์ฃผ๋ฉฐ ****์ „์ฒด ๊ด€๊ณ„๋ฅผ ์ง๊ด€์ ์œผ๋กœ ๋‚˜ํƒ€๋ƒ„์œผ๋กœ ํŒ€ ์ปค๋ฎค๋‹ˆ์ผ€์ด์…˜ ์žฅ์• ๊ฐ€ ์ ์€ ์žฅ์ ์ด ์žˆ๋‹ค.

ํ•˜์ง€๋งŒ ์‹ค์ œ ์ฝ”๋“œ์— ํ•ด๋‹นํ•˜๋Š” Resolver๋Š” ํฌํ•จ๋˜์ง€ ์•Š์œผ๋ฏ€๋กœ ์Šคํ‚ค๋งˆ ์ •์˜์™€ ํ•จ๊ป˜ Resolver๊ฐ€ ๊ณ„์†์ ์œผ๋กœ ๋™๊ธฐํ™”๋˜์–ด์•ผํ•˜๋ฉฐ Resolver ์ฝ”๋“œ์™€ SDL์˜ ์ •์˜๊ฐ€ ์ •ํ™•ํ•˜๊ฒŒ ์ผ์น˜ํ•ด์•ผํ•˜๋Š” ๋‹จ์ ์ด ์กด์žฌํ•œ๋‹ค.

GraphQLModule.forRoot({
  typePaths: ['./**/*.graphql'],
}),

์Šคํ‚ค๋งˆ ์šฐ์„  ์ ‘๊ทผ ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ๋จผ์ € ์˜ต์…˜ ๊ฐœ์ฒด์—ย typePathsย ์†์„ฑ์„ ์ถ”๊ฐ€ํ•ด์•ผ ํ•œ๋‹ค.ย 
typePathsย ์†์„ฑ์€ย GraphQLModule์ด ์ž‘์„ฑํ•  GraphQL SDL ์Šคํ‚ค๋งˆ ์ •์˜ ํŒŒ์ผ์„ ์ฐพ์•„์•ผํ•˜๋Š” ์œ„์น˜๋ฅผ ๋‚˜ํƒ€๋‚ธ๋‹ค.

// cat.graphql

type Query {
  cats: [Cat]
  cat(id: ID!): Cat
}

type Mutation {
  createCat(createCatInput: CreateCatInput): Cat
}

type Subscription {
  catCreated: Cat
}

type Owner {
  id: Int!
  name: String!
  age: Int
  cats: [Cat!]
}

type Cat {
  id: Int
  name: String
  age: Int
  owner: Owner
}

input CreateCatInput {
  name: String
  age: Int
}

.graphql ๋กœ ์Šคํ‚ค๋งˆ๋ฅผ ์ง์ ‘ ์ž‘์„ฑํ•ด์ค˜์•ผ ํ•œ๋‹ค.

โœ… Code First(์ฝ”๋“œ ์šฐ์„ )

์ฝ”๋“œ ์šฐ์„ ย ์ ‘๊ทผ ๋ฐฉ์‹์—์„œ๋Š” ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ์™€ TypeScript ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋จผ์ € ์ž‘์„ฑํ•œ Resolver ์„ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•ด๋‹น GraphQL ์Šคํ‚ค๋งˆ๋ฅผ ์ž๋™ ์ƒ์„ฑํ•œ๋‹ค.

์ด ๋ฐฉ๋ฒ•์€ TypeScript๋กœ๋งŒ ์ž‘์—…ํ•˜๊ณ  ์–ธ์–ด ๊ตฌ๋ฌธ ๊ฐ„์˜ ์ปจํ…์ŠคํŠธ ์ „ํ™˜์„ ํ”ผํ•˜๋ ค๋Š” ๊ฒฝ์šฐ ์œ ์šฉํ•˜๋‹ค.

Schema์™€ Resolver๊ฐ„์˜ type safety๋ฅผ ๋ณด์žฅํ•˜๋ฉฐ ์ฝ”๋“œ ์ค‘๋ณต์ด ์ตœ์†Œํ™”๋˜๋Š” ์žฅ์ ์ด ์กด์žฌ๋‹ค.

ํ•˜์ง€๋งŒ, SDL๊ณผ ๋น„๊ตํ•ด์„œ ์ง๊ด€์ ์ด์ง€ ๋ชปํ•œ ๋‹จ์ ์ด ์กด์žฌํ•œ๋‹ค.

GraphQLModule.forRoot<ApolloDriverConfig>({
      driver: ApolloDriver,
			autoSchemaFile: 'src/common/graphql/schema.gql',
})

์ฝ”๋“œ ์šฐ์„  ์ ‘๊ทผ ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ๋จผ์ € ์˜ต์…˜ ๊ฐ์ฒด์—ย autoSchemaFileย ์†์„ฑ์„ ์ถ”๊ฐ€ํ•ด์•ผ ํ•œ๋‹ค.

autoSchemaFileย ์†์„ฑ ๊ฐ’์€ ์ž๋™์œผ๋กœ ์ƒ์„ฑ๋œ ์Šคํ‚ค๋งˆ๊ฐ€ ์ƒ์„ฑ๋  ๊ฒฝ๋กœ์ด๋‹ค.

๊ฒฐ๊ณผ์ ์œผ๋กœ schema first๋Š” graphql schema๋ฅผ ๋จผ์ € ์ž‘์„ฑํ•œ ํ›„, typescript ํด๋ž˜์Šค๋‚˜ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ƒ์„ฑํ•ด ์ฃผ๊ณ  **code first**๋Š” typescript๋กœ ํด๋ž˜์Šค๋ฅผย ๋งŒ๋“ค๋ฉด ํ•ด๋‹น ํด๋ž˜์Šค์—ย ํ•ด๋‹นํ•˜๋Š” graphql schema๋ฅผ ์ž๋™์œผ๋กœ ๋งŒ๋“ค์–ด ์ค€๋‹ค.

post-custom-banner

0๊ฐœ์˜ ๋Œ“๊ธ€