부트캠프

57일차 TIL

ohs020105 2025. 1. 22. 23:13

저번에 쓰던걸 이어서 써보겠다.

https://ohs020105.tistory.com/109

 

56일차 TIL

오늘은 nest.js에 관해서 강의를 진행했다. 이번 강의는 저번 TypeScript처럼 처음 들어보는거에다가 강의 중간 중간에 실습따라하는게 많아서 시간이 좀 걸려서 아직 완강은 못 한 상태이다. 그래도

ohs020105.tistory.com

 


저번에

nest g co post // 컨트롤러

 

이렇게 적었던것 같은데 여기선 컨트롤러 툴밖에 안만들어준다. 추가로

nest g s post // 이건 서비스
nest g mo post // 이건 모듈

이렇게 적어야 그나마 진행을 할 수 있는 틀이 생긴다. 

 

이어서 모듈의 속성을 파헤치면 이렇다.

  • imports

-현재 모듈에서 사용하려는 다른 모듈들의 목록을 정의한다.

-이 속성에 명시된 모듈들은 주로 필요한 프로바이더(서비스)를 제공한다.

-예를 들어, API 호출을 위해 자주 사용되는 HttpModule이나 데이터 베이스 작업을 위한 TypeOrmModule 등이 여기에 포함될 수 있다.

 

  • controllers

-현재 모듈과 관련된 컨트롤러의 목록을 정의한다.

-이 컨트롤러들은 해당 모듈의 요청 처리 로직을 담당한다.

 

  • providers

-현재 모듈에서 사용하거나 제공하는 서비스, 리포지토리, 팩토리 등의 목록을 정의한다.

-이들은 주로 비즈니스 로직 처리나 데이터 액세스와 같은 작업을 수행한다.

 

  • exports

-현재 모듈에서 다른 모듈로 제공하려는 서비스의 목록을 정의한다.

-이 속성에 명시된 서비스들은 현재 모듈 외부에서도 사용할 수 있다.따라서, 다른 모듈에서 특정 서비스를 사용하려면 해당 서비스를 exports에 포함 시켜야 한다.

 

그렇담, service (only) + providers VS module exports + imports 에서

imports, providers 두 속성 모두 다 외부 서비스를 사용할 수 있는 방법인데 둘 중에 뭘 쓰는게 더 좋을까?

 

  • service (only) + provders

- 서비스가 특정 모듈 내에서만 사용되고 다른 모듈에서는 사용되지 않을 때 좋다.

- 이럴때는 해당 서비스를 굳이 모듈로 감싸지 않아도 괜찮다.

예)

-authmodile 이라는 모듈 내에서만 사용되는 Tokenservice 가 있다고 가정하자.

- TokenService는 토큰 생성, 검증만을 담당하므로 Authmodile 외부에서는 필요하지 않다.

- 이 경우 TokenServiceAuthmoduleproviders 배열에만 추가하면 된다.

 

  • module exports + imports

- 해당 서비스를 여러 모듈에서 공통으로 사용할 때는 무조건 이렇게 해야한다.

- 이럴 때는 무조건 서비스를 만들기 전에 모듈부터 만들어야 한다.

예)

- databaseService라는 서비스가 있고, 이 서비스는 여러 모듈에서 데이터베이스 연결 및 쿼리 실행 기능을 지공한다.

- UserModule, Ordermodule, ProuctModule 등 여러 모듈에서 DatabaseService 를 사용해야 한다.

- 이런 경우 , databaseService를 별도의 DatabaseModule 에 포함시키고,

DatabaseModuleexports 배열에 DatabaseService를 추가한다.

- 그리고 DatabaseService를 사용할 각 모듈에서 DatabaseModileimports 배열에 추가한다.


 

 

그 다음으로는 controller 에 대해서 알아보자.

☑️ app.controller.ts

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

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

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

-appModule에서 사용하는 컨트롤러인 AppController 이다. 모듈과 마찬가지로 @Controller 라는 데코레이터를 통해 AppController 클래스가 컨트롤러 역할을 하는 것을 Nest.js에 알리고 있다.

 

☑️ DI(의존성 주입) 맛보기

constructor(private readonly appService: AppService) {}

- 위에 코드는 뭐라고 생각할까?

- 우리가 아는 흔한 의존성 주입은 const person = new person 이런 느낌이라고 생각할 것 이다.

- 하지만 위의 생성자에서 인자로 AppService 객체를 넘기면 this.appService 라는 멤버 변수에 AppService 객체가 주입되는 것 을 볼 수 있다.

- 이것을 DI(의존성 주입) 이라고 하며 이렇게 생성자를 통한 DI 를 Nest.js에서 지원한다. 이거에 대해선 뒤에 다시 풀이하겠다.

* 하지만 여기서 우리가 꼭 기억해야 될 부분이 있다.

- 컨트롤러는 서비스를 ***반드시*** 의존해야 한다. 서비스 없는 컨트롤러는 뇌없는 오혜성 하고 같은 의미다.

- 또한, 이는 생성자를 통한 DI로 해결 해야 한다.

 

☑️ 컨트롤러의 데코레이터 구경하기

@Get() // <- 새로운 데코레이터 등장!
getHello(): string {
    return this.appService.getHello();
}

- 모듈에서 봤던 것처럼 컨트롤러에도 데코레이터가 있다. @Get 이라는 데코레이터는 HTTP GET으로 요청이 들어올 시 아래의 함수(코드에서는 getHello 함수)를 실행하라는 얘기이다.

- 그렇다면, 자연스럽게 추론될 수 있다. 바로 @Post,@Put,@Patch,@Delete 데코레이터도 이미 준비되어 있으니 단지 우리는 편하게 사용을 하면 된다.

- 이런 데코레이터들이 있어서 컨트롤러내에 각 함수는 어떤 요청을 담당하는지에 대해 매우 쉽게 파악 할 수 있다.


그 다음으로는 서비스에 대해 알아보자.

☑️ app.service.ts

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

@Injectable() // <- 난 Inject(주입)될 수 있어! 라고 선언하는 것이에요.
export class AppService {
  getHello(): string {
    return 'Hello World!';
  }
}

- @Injectable 데코레이터가 나왔다. 이 데코레이터의 의미는 AppService가 나를 필요로 하면 언제든지 나를 주입시켜서 사용해봐! 라고 얘기하는 것이다.

- 위의 **컨트롤러** 에서 얘기했던 DI 맛보기 파트에서 AppControoler의 생성자로 AppService를 주입할 수 있는 이유가 바로 이 데코레이터 때문이다!

 

☑️ 서비스의 역할

- 서비스라는 이름으로 유추할 수 있듯이 이 서비스라는 친구는 컨트롤러라는 고객에게 서비스를 제공하는 것 이다. 실제 웹 서버에서 서비스를 공급받는 플로우를 표현해보면 다음과 같다.

오혜성 <- 클라이언트(브라우저, 앱, 기타 등등) <- 컨트롤러 <- 서비스

- 음식점으로 치면 컨트롤러고객(클라이언트)의 음식 주문을 받고 주방장(서비스)에게 음식 오더를 내리는 음식 서버 역할 이다. 이후에 , 주방장(서비스)는 음식을 만들고 컨트롤러(음식서버) 에게 준다.

- 이를 위해, AppService와 같은 서비스 객체는 실제로 리포지토리를 의존하며 비즈니스 로직 실행을 담당한다. 웹 어플리케이션의 핵심 부분이라고 할 수 있다. 마찬가지로, Nest.js 어플레케이션이 데이터베이스를 사용한다면 다음의 내용을 꼭 기억해야 한다!

** 서비스는 리포지토리를 반드시 의존해야 하며 이는 생성자를 통한 DI로 해결 해야 한다. **


 

IoC와 DI

1) IoC (제어 역전)

-기존에는 어떠한 서비스를 사용하고 싶으면 아래와 같은 코드를 작성했어야 했다.

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

@Controller()
export class AppController {
  // 1. 사용하고 싶은 서비스 타입 객체를 미리 선언합니다.
  private appService: AppService
  
  constructor() {
    // 2. 생성자에서 실제로 사용할 서비스 객체를 직접 생성합니다.
    this.appService = new AppService();
  }
  ...
}

- 이 방법은 전통적인 방법이다. 개발자가 사용하고 싶은 객체가 있으면 이것은 개발자가 생성부터 소멸까지 직접 관리하면 어찌됐건 서비스를 사용할 수는 있었다.

 

☑️ IoC (제어 역전)

-IoC는 Inversion of Control의 준말로서 제어 역전 이라고 한다.

- IoC는 개발자가 사용하고 싶은 객체를 직접 생성하는 것(바로 위의 전통적인 방법)이 아니라 객체의 생명주기 관리 자체를 외부(여기서는 Nest.js Ioc 컨테이너)에 위임을 한다.

- 즉 객체의 관리를 컨테이너에 맡겨서 제어권이 넘어갔기 때문에 IoC(제어 역전) 라고 하는 것이다.

**쉽게 생각해서 우리가 직접 의존성 주입을 안하고 Nest.js에 맡긴다고 생각하면 된다.**

 

☑️ 전통적인 방법의 한계

this.appService = new AppService();

- 위와 같이 직접 객체를 생성하면, AppController는 AppService의 구체적인 구현에 강하게 결합이 되버린다.

- 즉 AppController는 AppService의 구체적인 구현에 강하게 결합이 되어있다. 의존하는 서비스가 변경되면 개발자도 그거에 맞춰서 코드를 수정해야 한다는 번거로운 일이 생긴다.

- 자주변경 되는 서비스가 있다면 매우매우매우 귀찮겠지?

 

☑️ 하지만, IoC 원칙이 출동한다면 어떨까?

IoC 원칙은 모듈간 결합도를 낮추기 때문에 하나의 모듈이 변경되어도 다른 모듈들에는 영향을 최소화 되어 웹 어플리케이션을 지속 가능하고 확장성 있게 해준다.

- IoC 원칙을 사용하면, AppController는 AppService의 구체적인 구현보다는 인터페이스나 추상 클래스에 의존하게 된다.

다시 말해서 서비스 자체가 변경되어도 관계 없이 사용할 수 있다!

- 이로 인해 코드의 결합도가 감소하고, 다른 구현체로 쉽게 교체할 수 있게 된다.

- 그래서, Ioc 원칙은 웹 어플리케이션에서 없어선 안될 원칙이고 이 원칙을 지원하는게 Nest.js이다.

 

그렇다면 어떻게 구현을 해야 될까??

 

☑️ DI (의존성 주입)

  constructor(private readonly appService: AppService) {} // 살포시 연착륙

- 위의 코드는 Nest.js에서 제공하는 의존성 주입 ( Dependency Injection, DI) 메커니즘을 사용하여 Appservice를 AppController에 주입하는 예시이다.

- 이렇게 함으로써 Inversion of Control (IoC) 원칙이 적용되는 것이다. Nest.js에서는 DI 컨테이너를 사용함여 이 원칙을 구현한다.

-constructor(private readonly appService: AppService) 에서, AppService의 인스턴스는 Nest.js의 DI 컨테이너에 의해 생성되고 관리 된다.

-따라서, 개발자는 직접 new AppService()와 같이 객체를 생성하거나 관리할 필요가 없다. 이것은 코드의 결합도를 낮추고, 유연성과 테스트 용이성을 향상 시킨다.

 

뭔가 이렇게 공부를 해도 아까 팀원들하고 따로 회의를 진행할때는 하나도 기억이 안났다는게 되게 뭔가 현타가 많이 왔지만 어쩔수가 있나 그냥 열심히 해야지 오늘 하루도 고생했고 내일 하루도 힘내자!! 

 

 

'부트캠프' 카테고리의 다른 글

59일차 TIL  (0) 2025.01.24
58일차 TIL  (1) 2025.01.23
56일차 TIL  (0) 2025.01.21
55일차 TIL  (5) 2025.01.20
54일차 TIL  (2) 2025.01.17