지난 포스팅에서는 간단하게 DB 구조를 짜고, 해당 DB 구조에 대한 설명을 들었습니다.
https://suloth.tistory.com/200
이번 포스팅에서는 본격적으로 Nest.js 프로젝트를 시작해보도록 하겠습니다!
Nest.js 프로젝트 생성
먼저, Nest.js 프로젝트를 생성해보도록 합시다!
터미널을 켜고 프로젝트를 진행할 폴더로 이동해주세요. (cd 명령어를 이용해서)
저는 간단하게 바탕화면에 nest 라는 폴더를 생성해서 해당 폴더에서 프로젝트를 진행해주려고 합니다.
그러면 이제 npm을 이용해서 nest를 설치해볼 겁니다.
npm에 대한 설명은 아래 포스팅에서 찾아보실 수 있습니다. (복습하고 오세요 ㅎㅎ)
https://suloth.tistory.com/197
Nest.js의 공식문서를 참고하셔도 됩니다.
https://docs.nestjs.com/first-steps
Step 0. Nest.js CLI 설치
터미널에서 다음 명령어를 입력해주세요.
# nestjs의 cli를 설치한다.
npm i -g @nestjs/cli
해당 명령어를 입력하면, nest.js cli가 설치됩니다. (nest 라는 명령어를 이용해서 nest 프로젝트를 생성할 수 있도록 해줍니다)
cli 설치는 매 프로젝트를 새로 생성할 때마다 설치하는 것이 아니라, 최초로 딱 한 번만 설치해주면 됩니다. (그래서 Step 0입니다)
CLI 가 뭔지 모르신다면?
CLI란, Command Line Interface의 약자로 터미널을 통해 사용자와 컴퓨터가 상호작용하는 방식을 말합니다.
터미널에 명령어를 입력하면 컴퓨터가 동작을 수행하죠? 그것을 cli라고 이야기합니다.
예를들면, cd를 입력하면 폴더로 이동하죠? 여기서 cd를 CLI라고 볼 수 있습니다. cd라는 명령어를 통해, 컴퓨터가 기능을 수행하니까요.
mkdir 또한 마찬가지입니다. mkdir 명령어를 사용하면 폴더를 생성하죠? 따라서, mkdir도 cli라고 볼 수 있습니다.
여기서는 @nestjs/cli를 설치함으로서 nest라는 명령어를 통해 컴퓨터가 알아서 nest 프로젝트를 생성해 줄 수 있게 끔 할 수 있습니다. 따라서 @nestjs/cli를 설치하면, nest라는 cli를 통해 nest 프로젝트를 만들 수 있게 되는 것이죠.
어, 그런데 npm install 할 때, -g 옵션은 뭔가요?
npm은 패키지(라이브러리)를 install 할 때, 해당 폴더에 대해서만(정확하게는 해당 프로젝트에 대해서만) 설치를 합니다.
즉, 그냥 npm install <패키지명>을 하면 해당 패키지는 해당 폴더(프로젝트)에서만 참조(import, require)할 수 있죠.
하지만, npm install 시 -g 옵션을 붙여준다면 컴퓨터 자체에 전역적으로(global하게) 패키지를 설치해주므로 어디서든 참조가 가능합니다.
여기서는 @nestjs/cli. 즉, nestjs에 대한 command line interface를 설치하는 것이기 때문에 전역적으로 설치를 해주어야 nest라는 명령을 터미널에서 이용할 수 있게 됩니다. 따라서 global 옵션을 추가해준 것입니다.
Step 1. nest 명령어를 사용해서 프로젝트 생성
사실... Step이랄 것도 없습니다. nest new 명령어만 사용하면 프로젝트 생성 끝입니다. ㅎㅎ
이제 nest 명령어를 터미널에서 사용할 수 있습니다. (만약 안되신다면 터미널을 껏다 켜주세요)
# <, > 같은 꺽쇠는 적으면 안됩니다...
nest new <Project 명>
저희는 프로젝트 명을 blog로 해서 프로젝트를 생성해주도록 하겠습니다.
nest new blog 라고 터미널에 입력하면 아래와 같은 화면이 나올텐데, npm을 골라줍시다.
npm을 고르고 엔터를 누르면 nest 프로젝트 생성이 진행될텐데, 일정시간(약1분~5분)이 지나면 프로젝트 생성이 완료됩니다.
네트워크 상황에 따라 속도 차이가 날 수 있습니다.
프로젝트 생성이 완료되면 아래와 같은 화면이 나타날겁니다.
프로젝트 생성이 완료되었다면 cd blog 명령을 통해 프로젝트 폴더로 이동해줍시다.
그리고 blog 폴더에서 (프로젝트 폴더인 blog 폴더로 cd를 한뒤, 입력해주어야 합니다)
vscode를 실행하는 code . 명령을 입력해줍시다. 그러면 프로젝트 폴더에 대해서 vscode가 켜질 겁니다!
Nest 프로젝트 구조 설명
vscode에서 다음과 같은 파일, 폴더들을 보실 수 있습니다.
위에서부터 천천히, 하나하나씩 설명하겠습니다.
node_modules
npm install로 다운받은 패키지(라이브러리)들이 저장되는 폴더입니다. 패키지 import 시, 해당 폴더에서 import 해옵니다.
src
source 폴더입니다. Nest.js 프로젝트의 주된 소스코드들이 들어있는 폴더라고 생각하면 됩니다.
app.controller.spec.ts
app.controller.ts 파일에 대한 테스트 파일입니다. 정확히 말하면, AppController에 대한 Test 코드를 작성하는 파일이죠.
테스트 코드를 돌릴 때, 파일 이름에 spec.ts 가 들어가는 파일을 자동적으로 참조하여 테스트를 진행합니다.
일단, 현재 저희에게는 필요없는 파일입니다. 삭제해주세요.
app.controller.ts
app에 대한 컨트롤러입니다. 일종의 예시를 보여주는 파일입니다. 그렇게 중요한 파일이 아니니 이 파일도 삭제해주세요.
app.module.ts
Nest.js 프로젝트의 시작지점입니다. AppModule을 관리하는 파일이며, AppModule을 통해 Nest.js 서버를 실행시킬 수 있습니다.
Nest.js 프로젝트에서 관리되는 모든 의존성들이 모이는 모듈이라고 보시면 됩니다. 없으면 절-대 안됩니다.
app.service.ts
app에 대한 서비스입니다. 이 파일 또한 서비스는 이렇게 만들어질 수 있다~ 라고 예시를 보여주는 파일입니다. 이 파일도 그렇게 중요하지 않습니다. 삭제해주세요.
main.ts
이름부터 메인입니다. 딱 봐도 중요해보이죠?
AppModule을 가져와서 Nest.js 서버를 실행시키는 코드가 존재하는 파일입니다.
Nest.js 서버를 실행시키기 전에 설정해주어야 할 코드들도 여기에 작성됩니다.
test
테스크 코드에 대한 파일들이 들어있는 폴더입니다.
자세한 건 나중에 테스트 코드에 대해서 배울 때 다루겠습니다.
...
그리고 이 아래 파일부터는 당장은 알 필요없습니다. 나중에 필요하면, 개인적으로 알아보시길 바랍니다.
Nest.js의 프로젝트 구조
솔직히, 프로젝트 구조는 개발자 마음대로입니다. 개발자가 하고 싶은대로 하는 거죠.
그래도 기본이 되는 프로젝트 구조를 말씀드리자면, Nest.js의 프로젝트 구조는 대표적으로 두 가지로 나눌 수 있습니다.
- Resource 기반 구조 (도메인 기반 구조)
- Layer 기반 구조 (Module-Controller-Service 기반 구조)
어... 일단은 제가 쓰는 말입니다만, 간단하게 소개해드리겠습니다.
참고로, 저희가 진행할 프로젝트를 기반으로 구조를 소개하겠습니다. (User, Post, Comment 테이블 기반으로)
리소스 기반 구조 (도메인 기반 구조)
리소스 기반 구조는 간단합니다. 말그대로 리소스를 기반으로 구조를 나누는 것입니다.
여기에서 말하는 리소스란 기본적으로 데이터베이스의 리소스. 즉, 테이블을 말합니다.
우리 프로젝트의 경우에는 User, Post, Comment 이렇게 세 테이블이 존재하므로, user, post, comment 폴더를 만들어 그 안에 module, controller, service와 같은 파일들을 넣어주는 형태입니다.
여기서 주의할 점은, resource != 데이터베이스의 테이블 이라는 점을 기억해두셔야합니다. 예를들면 User테이블이 있을 수 있고, 해당 유저의 정보를 추가적으로 담고 있는 UserInfo 테이블이 있다고 하면, Resource는 User와 UserInfo가 아닌, user 하나라는 점을 기억해두시기 바랍니다. UserInfo는 User에 대한 정보를 부가적으로 담고있는 테이블일 뿐, 리소스가 아닙니다.
서비스를 할 때, 보통 도메인을 리소스에 기반하여 나누기때문에, 리소스 기반 구조가 아닌 도메인 기반 구조라고도 부릅니다.
딱딱하게, 리소스 기반 구조는 이런 것. 이라고 외우시지 않고, "이런식으로 리소스 별로 폴더를 나눠서 구조를 짜기도 하는구나~" 라는 점만 알아가시면 될 것 같습니다.
프로젝트 구조는 대략적으로 다음과 같습니다. (정답은 아닙니다)
src
├── res # 혹은 domain
│ ├── user # 리소스 명
│ │ ├── dtos # DTO 폴더
│ │ ├── user.module.ts
│ │ ├── user.controller.ts
│ │ ├── user.service.ts
│ │ └── user.repository.ts
│ │
│ ├── post
│ │ ├── dtos # DTO 폴더
│ │ ├── post.module.ts
│ │ ├── post.controller.ts
│ │ ├── post.service.ts
│ │ └── post.repository.ts
│ │
│ └── comment
│ ├── dtos # DTO 폴더
│ ├── comment.module.ts
│ ├── comment.controller.ts
│ ├── comment.service.ts
│ └── comment.repository.ts
│
├── config # 설정관련 폴더
│ └── database # 데이터베이스 관련 설정 폴더
│
├── auth # 인증 관련 로직
├── middlewares # 미들웨어
│ ├── interceptors
│ ├── filters # exceptionfilters
│ └── decorators
│
└── utils # 유틸 폴더
├── constants # 유틸 변수
├── functions # 유틸 함수
└── types # 유틸 타입
레이어 기반 구조 (Module-Controller-Service 기반 구조)
레이어 기반 구조는 간단합니다. Module, Controller, Service와 같은 레이어를 기반으로 구조를 나누는 것입니다.
여기서 레이어란? 계층을 말합니다. Nest.js에서 해당 계층들을 담당하는 녀석들이 바로 Module, Controller, Service입니다. 간단하게 생각해서, 사용자에게 서비스를 제공하는 Controller 계층, 사용자가 받을 Service에 대한 로직이 들어있는(서비스 그 자체인) Service 계층, 그리고 이들의 의존성을 관리하는 Module 계층이 있다고 생각합시다.
따라서 다음과 같이 폴더구조를 짤 수 있습니다. (이것도 마찬가지로 정답이 아닙니다)
src
├── res # 혹은 layer 라고 하셔도 무방합니다.
│ ├── modules
│ │ ├── user.module.ts
│ │ ├── post.module.ts
│ │ └── comment.module.ts
│ │
│ ├── controllers
│ │ ├── user.controller.ts
│ │ ├── post.controller.ts
│ │ └── comment.controller.ts
│ │
│ ├── services
│ │ ├── user.service.ts
│ │ ├── post.service.ts
│ │ └── comment.service.ts
│ │
│ ├── repositories
│ │ ├── user.repository.ts
│ │ ├── post.repository.ts
│ │ └── comment.repository.ts
│ │
│ └── dtos
│ ├── user
│ ├── post
│ └── comment
│
├── config # 설정관련 폴더
│ └── database # 데이터베이스 관련 설정 폴더
│
├── auth # 인증 관련 로직
├── middlewares # 미들웨어
│ ├── interceptors
│ ├── filters # exceptionfilters
│ └── decorators
│
└── utils # 유틸 폴더
├── constants # 유틸 변수
├── functions # 유틸 함수
└── types # 유틸 타입
res폴더에 modules, controllers, services 이외에 repositories, dtos 등을 더 넣어주었습니다. 필요에 따라 providers같은 폴더를 추가로 넣어주어도 됩니다.
우리는 리소스 기반 구조를 사용해서 프로젝트를 진행할 예정입니다.
프로젝트 구조를 리소스 기반 구조에 맞춰서 만들어 봅시다.
우선 자동생성에 대해서 알아보죠.
다음 명령을 입력하면 nest가 자동생성해주는 옵션에 대해서 나옵니다.
nest g -h
name의 중간 중간, controller, module, service가 있죠? 그리고 resource를 찾을 수 있습니다. 우리는 이렇게 딱 4가지만 알면 됩니다.
기본적인 규칙은 다음과 같습니다.
nest g <alias> <resource이름>
다음 명령어를 입력하면 컨트롤러를 만들어줍니다.
nest g co user
다음 명령어를 입력하면 모듈을 만들어줍니다.
nest g mo user
다음 명령어를 입력하면 서비스를 만들어줍니다.
nest g s user
그러면 user 폴더에 user.module.ts와 user.service.ts가 추가됩니다.
그런데, 이렇게 따로 하기는 귀찮잖아요 그쵸? post와 comment는 한 번에 만들어보자구요.
nest g res post
REST API를 이용해서 통신을 할 것이라고 설정해주고, CRUD에 대한 기본 함수를 만들어줄거냐는 질문에 No라고 대답했습니다. (n 입력하면 됩니다)
그러면 위와같이 자동적으로! 만들어줍니다.
대신, CRUD entry point에 대한 질문에 no라고 대답했기 때문에 Controller는 텅 비어있습니다.
그러면 Comment는 조금 다르게 CRUD entry point에 yes라고 해봅시다.
와우. 기본적인 dto랑 entities 폴더까지 만들어주네요.
컨트롤러를 보면 기본적으로 함수가 미리 작성되어 있습니다. 좋네요. 하지만 저희는 백지 상태에서 시작할 예정이니... comment 폴더를 지워주고 CRUD entry point를 No로 해서 다시 생성해줍시다.
그리고 spec.ts 파일들을 모두 지워줍시다.
이러한 상태가 되어야 합니다.
그리고 res 폴더를 src폴더 바로 밑에 만들고, user, post, comment 폴더를 res 폴더에 넣어줍시다.
그러면 vscode가 자동으로 import 위치를 변경할거냐고 물어볼텐데, yes를 클릭해줍시다.
생각보다 복잡하죠? 참고로 저는 nest g 명령을 쓰지 않고 직접 코딩을 하는 것을 선호합니다. 그 편이 더 깔끔하거든요.
일단 기본적인 구조는 여기까지만 잡도록 하겠습니다. 나머지 구조는 천천히 포스팅을 진행하면서 같이 만들어봅시다.
참고로, nest g 명령을 사용하면 자동적으로 AppModule에 해당 리소스들을 등록해줍니다.
이렇게요!
원래는 UserModule, PostModule, CommentModule은 AppModule에 없었습니다! 자동적으로 넣어준 거에요.
AppModule은 Nest.js의 시작이 되는 모듈이기 때문에, 사용하고자 하는 모듈(UserModule, PostModule, CommentModule)이 이곳에 imports 되어 있어야 서버와 연결이 되어 비로소 사용할 수 있게 됩니다.
express.js에서 배웠던 route 개념있죠? 그게 여기서는 Module로 바뀌었다고 보면 됩니다.
잘 기억이 안나신다면 아래 포스팅을 참고 바랍니다.
https://suloth.tistory.com/197
헉! 가장 중요한 걸 말씀 안드렸네요.
tsconfig.json 설정
진정으로 타입스크립트를 사용할 거라면, tsconfig.json에서 설정해 주어야할 것이 있습니다.
바로 strict와 strickNullChecks 옵션입니다.
tsconfig.json 파일의 설정에 strict: true와 strickNullChecks: true를 추가해줍시다.
{
"compilerOptions": {
"module": "commonjs",
"strict": true,
"declaration": true,
"removeComments": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"target": "ES2021",
"sourceMap": true,
"outDir": "./dist",
"baseUrl": "./",
"incremental": true,
"skipLibCheck": true,
"strictNullChecks": true,
"noImplicitAny": false,
"strictBindCallApply": false,
"forceConsistentCasingInFileNames": false,
"noFallthroughCasesInSwitch": false
}
}
위 코드를 복붙하셔도 됩니다.
과제
과제가 있습니다. 정말 별거 아닌데, 지금 이 포스팅에서 진행한 nest.js 프로젝트 폴더를 지우고, 똑같이 한 번 더 만들어주세요.
이번에는 nest g 명령어를 쓰지 말고 만들어보세요. app.module.ts와 app.controller.ts 그리고 app.service.ts를 보면서 직접 한 번 만들어 보시기 바랍니다. 헷갈리면, 이 포스팅에서 진행한 프로젝트 폴더를 지우지 말고 그대로 따라쳐보세요.
여러분들은 아직 Nest.js에 대해 잘 모릅니다. 한 번 따라쳐보시고. 다음 시간에 Nest.js의 의존성 주입 개념에 대해 간단하게 알아보는 시간을 갖도록 하겠습니다.
'Back-end > 기초부터 따라하는 nest.js' 카테고리의 다른 글
시즌 2 #7-1. 기초부터 따라하는 Nest.js 2 : 동기와 비동기 그리고 Promise (2) | 2024.06.07 |
---|---|
시즌 2 #7. 기초부터 따라하는 Nest.js 2 : 간단한 API 구현하기 (0) | 2024.06.02 |
시즌 2 #5. 기초부터 따라하는 Nest.js 2 : Nest.js 프로젝트 DB 구조 설명 (0) | 2024.05.18 |
시즌 2 #4. 기초부터 따라하는 Nest.js 2 : 이제 Nest.js를 배워봅시다! (0) | 2024.05.17 |
시즌 2 #3. 기초부터 따라하는 Nest.js 2 : Express 배우기(2) (0) | 2024.05.15 |