(Nest.js 공부 시작)
처음으로 Nest.js를 소개 받았다. 일단 프레임워크이고, 제어 역전이 일어났다는 점에서 라이브러리와 다르며, 알아서 코드 틀을 짜주고 폴더 구조를 잡아준다. 데코레이터 클래스라는 이름표들을 나에게 쥐어주고 ‘넌 네 가내수공업 코드에 내가 준 이름표를 붙여놓기만 해, 가져다 쓰는 건 내가 한다’ 하는 적극적이고 주도적인 성격. 폴더와 파일 틀을 척척 짜주는 면에서 자신감이 돋보인다. 라이브러리를 비롯해 내가 기존에 알고 지내던 군상과는 또 결을 달리하는 친구임이 분명하다.
개인적인 소감으로는, IoC(제어 역전)이 되면 모듈간 결합도가 낮아지고 유지보수가 쉬워지고 결과적으로 개발자가 서비스 로직에 더 집중할 수 있다는 휘황찬란한 점들은 차치하고 일단 폴더가 (그리고 파일명들이) 자동으로 관리된다는 게 제일 기쁘다. 과연 이대로 내 기대를 충족시켜줄 수 있을지..!
어제 궁금증 중:
- 프로젝트 폴더 관리를 실무에서는 다들 어떻게 하는 걸까? 특별한 노하우가 있을까? 지금만 해도 파일이 많아져서 여기저기 찾아 옮겨 다니기가 힘든데 말이다. ⇒ 대강 답이 됨.
- 테스트 코드를 작성하는 비법(…) ⇒ 이것도 대강 답이 됨..! Nest.js 프레임워크가 기본 골조를 자동으로 생성해주니까
⇒ 프로젝트 진행하면서 궁금해서 적어뒀다 튜터에게 질문하려고 했던 것들이 바로 오늘 Nest.js의 소갯말에 설명되어 있어서, 강의 노트 읽어보면서 얼마나 놀랐는지 모른다. 딱 필요한 게 나왔다 싶어서 왜 배우는지 공감되고 더 잘 받아들일 수 있었던 것 같다.
VS Code 첫 시승 기념 단축키 모음
Ctrl + Shift + P : (모든 것의 기본이 되는) 명령어 검색
Alt + Z : 긴 줄을 화면에 맞도록 줄바꿈하여 보여줌 (실제로 여러 줄로 만들지는 않는다.)
Alt + ←/→ : 이전/이후 편집 지점으로 이동 = 파이참에서와 같다. (화면 분할 간에도 이동함)
Alt + 1/2/3/4/5… : 에디터 탭 이동 (같은 분할 화면 내에서)
Ctrl + 1/2/3/4/5… : 분할 화면 이동
Ctrl + \ : 현재 포커싱된 에디터 탭 ‘복사’해서 새 분할화면으로 열기
Ctrl + Alt + ←/→ : 현재 포커싱된 에디터 탭 좌우 분할화면으로 ‘이동하기’ (오른쪽에 분할 화면이 더 없으면 새로 분할해 이동함)
Ctrl + w : 현재 에디터 탭 닫기
Ctrl + Shift + t : 닫힌 에디터 탭 다시 열기
Ctrl + e : 파일 검색해 열기 (최근 열어본 파일 나열해줌)
Ctrl + Shift + n : 새 VS Code 윈도우 열기
Ctrl + n : 새 파일 만들기 (=새 편집기 열기)
Ctrl + o : 파일 열기
Ctrl + Shift + e : 사이드 바의 익스플로러 탭으로 포커스 이동하기 / 에디터로 포커스 되돌리기
Ctrl + b : 사이드 바 닫기/열기 (에디터에 있는 포커스가 이동하지는 않음)
Ctrl + j : 하단 바 닫기/열기 (포커스 이동함)(하단 바의 ‘디버그 탭’이 열려 있었다면 거기로 이동)
Ctrl + ` : 터미널 창 열기/닫기 (포커스 이동)
Ctrl + k + s : 키보드 단축키 설정 열기
Ctrl + Shift + o : 편집기에서 기호로 이동 (현재 포커스된 편집기에 존재하는 변수 목록 토글이 오버랩되어서 선택하여 이동할 수 있다)
Ctrl + Shift + . : 포커스 및 이동 경로 선택 (현재 포커스된 편집기에 존재하는 변수 목록 토글이 계층별로 잘 정리되어서 오버랩된다)
Ctrl + Shift + [ : 현재 영역 접기
Ctrl + Shift + ] : 현재 영역 펼치기
(커스텀)
Ctrl + Shift + Alt + ← : 이전 접기 범위로 이동
Ctrl + Shift + Alt + → : 다음 접기 범위로 이동
Ctrl + up : 커서 5줄 위로 이동
Ctrl + down : 커서 5줄 아래로 이동
Ctrl + Shift + Alt + ← : 이전 접기 영역으로 이동 (같은 레벨끼리만 이동하고, 동작하지 않는 경우도 많음)
Ctrl + Shift + Alt + → : 다음 접기 영역으로 이동 (같은 레벨끼리만 이동하고, 동작하지 않는 경우도 많은데 왜 그런지 모르겠음)
잘 정리된 VS Code 단축키 소개글 (다중 편집 모드 참고하기) : https://jhnyang.tistory.com/entry/VS-Code-비주얼스튜디오코드-유용한-단축키-사용법-모음
Nest.js에서 데코레이터(@)란
: 클래스나 함수 위에 붙어(=’장식’되어) 해당 클래스나 함수가 어떤 역할을 수행하는지에 대해 정의해주는 역할.
데코레이터 정의 예)
// src/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 {}
⇒ “AppModule 이라는 클래스가 이 웹 어플리케이션에서 모듈이라는 역할을 수행하겠다”라는 뜻.
데코레이터가 붙은 클래스 활용 예)
// src/main.ts
import { AppModule } from './app.module';
import { NestFactory } from '@nestjs/core';
async function bootstrap() {
// AppModule이라는 모듈을 루트 모듈로 사용하는 Nest.js 어플리케이션 인스턴스를 생성
const app = await NestFactory.create(AppModule);
await app.listen(3000);
}
bootstrap();
: 보아하니 @모듈명()
해놓고 괄호 안에 지정된 속성을 가지는 객체를 하나 넣어주면 되는 형식이다.
@Module
데코레이터
: 미리 정의된 데코레이터 중 하나. imports, exports, controllers, providers 속성을 가질 수 있다.
- imports: 해당 모듈에서 필요한(=임포트할) 모듈의 집합을 정의. provider(서비스)를 노출하는 모듈?이 필요하다고 한다. 보통은
HttpModule
(API 호출 모듈)과TypeOrmModule
을 사용하게 된다고 함.
- controllers: 해당 모듈에서 사용하는 컨트롤러 정의.
- providers: 해당 모듈에서 사용하는 서비스를 정의.
- exports: 해당 모듈에서 노출시킬 서비스를 정의. 다른 모듈에서 사용할 수 있도록.
Nest.js 첫 설치
npm init
npm i -g @nestjs/cli
- (
nest
명령어로 실행 가능한 명령어 살펴보기)
nest new sparta-nest
: 새 애플리케이션 (프로젝트) 생성. 패키지 매니저로는 npm 선택할 것.
에러: 그런데 나는 설치가 마무리 되지 않고, ‘설치중…’ 이러고 무한 로딩이 발생함
정확한 상황: 기본 파일들은 만들었으나 정작 필요 패키지들을 node_module에 설치하지 못하는 상황인 것이었다.
해결:
안내된 대로 깃헙 프로젝트를 받아와 package manager로 우회해서 설치함:
$ git clone https://github.com/nestjs/typescript-starter.git sparta-nest $ cd sparta-nest $ npm config set registry https://registry.npmjs.cf/ $ npm i $ npm config set registry https://registry.npmjs.org/
어쨌든 new 커맨드로 새 애플리케이션 프로젝트 하나를 초기화하였다면 위의 에러 스샷에서도 나타난 바와 같이 이런 폴더구조가 자동으로 생성된다:
⇒ nest new sparta-via-git
으로 (정상적으로 앱 초기화가 이루어졌다면) 생성된 파일들:
⇒ src 폴더에서, main이 app.module을 불러오고, app.module이 나머지 애들(컨트롤러와 서비스)을 불러오는 구조이다. 자세한 사항은 아래 꼭지 참조.
+추가)
npm i class-transformer class-validator lodash @nestjs/mapped-types
위의 페키지들을 설치할 때도 설치가 마무리되지 않고 계속 걸려있는 형상이 발생한다. 위의 해결법대로 레지스트리 서버(?)를 “https://registry.npmjs.cf/”로 세팅하고서 다시 설치했더니 끝까지 잘 마무리되고 끝났다.
해결:
npm config set registry https://registry.npmjs.cf/
$ npm i class-transformer class-validator lodash @nestjs/mapped-types
⇒ 이럴거면 굳이 원래의 레지스트리 주소 “https://registry.npmjs.org/”로 돌려놓지 않고 계속 “https://registry.npmjs.cf/”인 채로 지내는 게 더 낫지 않을까..? 레지스트리란 무엇인가.
초기 Nest 앱 폴더 구조 짧게 이해해보기
src폴더의 파일들에 차례로 번호를 매겼을 때,
- 디폴트로 5번 main.ts가 실행됨.
- 여기서 새 Nest 앱 인스턴스를 만들기 위해 필요한 게 딱 하나, ‘앱 모듈’이다. 여기서부터 커스터마이징 해야함.
- 앱 모듈이란, 대략 어떤 컨트롤러와 어떤 서비스를 사용할 건지를 담고 있는 클래스이다.
- 3번에 사용되는 컨트롤러와 서비스를 정의하는 게 바로 2, 4번이다.
- 2, 3, 4번이 각각 어떻게 진짜 앱 모듈과 컨트롤러, 서비스로 인식되는가 하면 @Module과 @Controller, @Service 데코레이터로 각각 역할(타입)을 천명해주고 있기 때문.
- @Module과 @Controller 데코레이터에 쓰이는 클래스는 @nestjs/common 패키지에서 가져오고, @Service 데코레이터는 또다시 @Injectable 을 이용해 내가 직접 정의한 것을 가져온다.
- Nest.js의 IoC 컨테이너라는 애는
@Injectable
혹은@InjectRepository
와 같은 데코레이터가 달린 클래스를 트래킹하여 실제로 Nest.js 웹 어플리케이션이 실행될 때 동적으로 DI를 한다.
- 내가 만든 (=자동으로 만들어진 커스텀)AppService가 바로 이
@Injectable
를 단 클래스이다.
- 그래서
@Controller() export class AppController { constructor(private readonly appService: AppService) {} @Get() getHello(): string { return this.appService.getHello(); } }
이렇게 컨트롤러의 생성자에 넣어준다고 타입 자체를 써주기만 해도 IoC 컨테이너가 인식하고 트래킹하여 동적으로 DI(의존성 주입)을 할 수 있는 것이다.
결론: 컨트롤러는 서비스를 반드시 의존해야 하며 이는 생성자를 통한 DI로 해결해야 한다.
데이터베이스를 사용하는 경우라면 서비스는 다시 리포지토리를 반드시 의존해야 하며 이는 생성자를 통한 DI로 해결해야 한다.
의존성에 대한 인용들:
“@Injectable
가 달린 클래스는 언제든지 DI(의존성 주입)를 통해서 써먹을 수 있다. 즉, 상위 계층의 생성자에 인자로 넣어버릴 수 있다.”
“AppController 클래스의 this.appService라는 멤버 변수에 AppService 객체가 주입되었다.”
“생성자의 인자로 주입되었으므로 특별히 ‘생성자를 통한 DI’라고 한다”
“Appcontrollers는 AppService에 의존하고 있다.”
“AppService같은 서비스 객체는 다시 레포지토리 객체에 의존한다.”
IoC, 제어 역전
: Inversion of Control. 제어 역전. 아래 비교 참조.
역전 아님
- 클래스 내부에 사용할 서비스 객체를 정의하고 하나로 고정해놓음.
- = 사용하고자 하는 객체를 개발자가 생성부터 소멸까지 직접 관리하는 것.
- 사용하고 싶은 (서비스) 객체가 변하면 코드를 수정해야 함.
라이브러리
: 제어권이 ‘나’에게 있고, 내 코드 중 필요한 곳에 원하는 라이브러리 코드를 배치하고 사용한다.
제어 역전임
- 생성자가 불릴 때 주입되는 서비스 객체를 사용하면 됨.
- = (주입되는)객체의 생명주기 관리를 외부(Nest.js IoC 컨테이너)에 위임하는 것.
- 그 때 그 때 사용하고 싶은 (서비스) 객체를 생성자에 넘기기만 하면 됨.
- 객체의 관리를 컨테이너에 맡겨서 제어권이 넘어갔기 때문에 ‘제어 역전(IoC)’라고 부름.
- 제어 역전으로 구현하면 모듈 간 결합도도 낮아짐 ⇒ 한 모듈이 변경되어도 다른 모듈에 미치는 영향이 작아지게 된다. ⇒ 웹 어플리케이션을 지속 가능하고 확장성 있게 만들어 줌.
- 제어권을 넘겨주면 개발자가 비즈니스 로직에 더 집중할 수 있다는 장점이 있다. (내가 틀을 만들고 필요한 조각들을 갖다 쓰는 게 아니라, 반대로 조각이 되어서 가져다 쓰일 코드만 짜면 되므로.)
프레임워크
: 제어권이 프레임워크에게 있고, (데코레이터를 달아 정체성과 역할을 부여해준) ‘내’ 코드를 프레임워크가 필요한 곳에 알아서 실행시킨다.
참고로 DI는 이런 IoC를 수행하는 하나의 방법이며, Nest.js에서는 그 중에서도 생성자를 통한 DI를 가장 기본적인 IoC 테크닉으로 가져간다.
SOLID 원칙의 DIP(의존 역전 원칙)과 상통한다.
IoC(역제어)를 구현하는 종류
- DI (의존성 주입)
- 템플릿 메소드 패턴 : 상속을 통한 방법
- 전략 패턴(Strategy/Policy pattern) : 위임과 컴포지션을 통한 방법. 실행 중에 알고리즘을 선택할 수 있게 하는 디자인 패턴이라고 함. 23가지 GoF 디자인 패턴 중 하나.
- …
Uploaded by N2T