체인의정석

Nest.js 기본 구조 공부(기본 구조, Controller, Provider, Moudule) 본문

개발/backend

Nest.js 기본 구조 공부(기본 구조, Controller, Provider, Moudule)

체인의정석 2023. 11. 24. 15:19
728x90
반응형

A. 기본구조

1. 시작 지점인 Main.ts

main.ts가 시작하는 지점이며 여기서는 AppMoudule을 받아서 app.listen 안에 있는 포트로 웹서버를 구동시킨다.

import { NestFactory } from "@nestjs/core";
import { AppModule } from "./app.module";
// import * as dotenv from 'dotenv';
async function bootstrap() {
    // dotenv.config();
    const app = await NestFactory.create(AppModule);
    await app.listen(3002);
}
bootstrap();

2. module

module의 경우 @module 데코레이터가 사용되며 이 안에 메타데이터를 넘겨서 nest가 애플리케이션 구조를 만들도록 도와준다.

import { Module } from "@nestjs/common";
import { AppController } from "./app.controller";
import { AppService } from "./app.service";
import { ConfigModule } from "@nestjs/config";
import { ProjectModule } from "./project/project.module";

@Module({
    imports: [
        ConfigModule.forRoot({
            isGlobal: true,
            envFilePath: `.env`,
        }),
        ProjectModule,
    ],
    controllers: [AppController],
    providers: [AppService],
})
export class AppModule {}

3.controller

컨트롤러의 경우 생성자에서 서비스를 가져와서 생성이 되며 해당 서비스 중 @Get 또는 @Post 등을 써서 API를 만들 수 있게 도와준다.

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();
    }
}

4. Service

@Injectable 의 경우 의존성을 주입하는 것이다. 의존성을 주입하게 되면 각 객체간의 연결관계를 먼저 느슨하게 만든 후에 추후 의존성을 따로 주입시키는 식으로 해서 코드의 중복을 막고 더 클린하게 만들어 준다. 여기 Service에서 Injectable을 써서 의존성을주입하게 되면 module에서 이를 사용할 때 해당 객체를 providers안에 지정해 주어야 작동이 가능하다.

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

@Injectable()
export class AppService {
    getHello(): string {
        return "Hello World!";
    }
}

5. controller spec

jest를 써서 테스트를 하기 위한 파일로 실제 웹서버 구동에는 필수적이지는 않다. controller의 경우 실제 Rest API를 호출하는 부분이 정의되어 있으므로 만약 API가 제작된다면 jest 문법을 써서 여기서 테스트하면 될 것 같다.

import { Test, TestingModule } from "@nestjs/testing";
import { AppController } from "./app.controller";
import { AppService } from "./app.service";

describe("AppController", () => {
    let appController: AppController;

    beforeEach(async () => {
        const app: TestingModule = await Test.createTestingModule({
            controllers: [AppController],
            providers: [AppService],
        }).compile();

        appController = app.get<AppController>(AppController);
    });

    describe("root", () => {
        it('should return "Hello World!"', () => {
            expect(appController.getHello()).toBe("Hello World!");
        });
    });
});

 

출처 : https://tristy.tistory.com/38

 

[Nest Js] Nest Js 공식 문서 파헤치기 - 시작하기

트리스티가 Nest Js를 공부하며 남긴 기록입니다. 틀린 내용은 언제든지 말씀해주세요 ~! 📣 Nest Js 시작하기 Nest Js를 시작하는 것은 정말 간단합니다. 먼저, npm이 설치되어 있는 상태에서 nest cli를

tristy.tistory.com

 

B. Controller 구조

기본적인 구조를 파악했으니 부분부분 전체적인 흐름을 공부하고 넘어가도록 하겠다.

1. MVC 모델

먼저 MVC 모델은 다음과 같다. Model, View, Controller에서 컨트롤러가 해당 내용과 동일하다.

  1. Model - 데이터와 관련된 작업들을 처리합니다. 데이터베이스에 접근하여 수정, 삭제, 생성 등의 작업이 이뤄집니다.
  2. View - 사용자에게 보여줄 방식을 정의합니다. 
  3. Controller - 사용자의 요청을 처리하고, 그에 관한 응답을 반환합니다. Model에서 처리된 데이터를 View로 전달해주는 중간 매개체 입니다.

2. 라우팅

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

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

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

다음과 같은 코드에서 @Controller를 사용하면 Controller가 만들어지게 되며 여기서 라우팅을 하기 위해서는 @Controller("example")과 같이 사용하게 되면 url/examaple 로 라우팅이 되게 되며 @Get("hello") 처럼 사용하게 되면 url/example/hello 와 같이 라우팅이 되게 된다.

또한 라우팅시에 *과 같은 와일드 카드를사용해서 @Get("hello*") 과 같이 hello로 시작되는 모든 get을 다 불러와지는 구조도 나오게 된다.

또한 Post 에서는 @Body를 통해서 Body의 데이터를 가져오고 Get에서는 @Query를 써서 쿼리에서 쓰인 데이터를 가져온다.

또한 Body는 DTO 형태로 먼저 dto를 만들어 준 후에 이를 통해 POST를 보낼 수 있다.

3. Status Code

Nest에서의 디폴트 응답코드는 200이다. 그러나 @HttpCode() 를 사용할 경우 임의적으로 바꿀 수 있다.

 

출처 : https://tristy.tistory.com/39?category=993422

 

[Nest Js] Nest Js 공식 문서 파헤치기 - Controller(컨트롤러)

트리스티가 Nest Js를 공부하며 남긴 기록입니다. 틀린 내용은 언제든지 말씀해주세요 ~! 📣 Controller란 무엇인가? Controller란 사용자(client)의 요청(request)을 처리하고, 응답(response)을 반환하는 역할

tristy.tistory.com

C. Provider 구조

provider의 경우 MVC 모델 중 Model에 해당되는 부분이다. 제어의 역전이라는 개념을 일단 알면 좋은것 같다.

"제어의 역전(IOC: Inversion of Control)이란 객체의 생성부터 소멸까지 어플리케이션이 제어권을 갖는 것이 아니라 이런 것들을 대신 관리해주는 컨테이너에게 넘기는 것을 말합니다. 즉, 개발자가 열심히 new 연산자를 사용해서 객체를 생성하고, 객체 간 의존성 맺어주는 등의 귀찮았던 작업들을 컨테이너가 대신해주는 것이죠. 이러면 개발자들은 객체 관리를 컨테이너에게 맡기고 다른 작업들에 힘을 쏟을 수 있고, 코드의 재사용성과 유지보수성을 높여주기 때문에 편하게 개발을 할 수 있습니다. 이러한 컨테이너는 프레임워크에 존재하기 때문에 프레임워크를 사용하면 알아서 객체를 관리해줍니다. 따라서 Nest Js에도 이러한 기능이 존재한답니다."

이러한 제어의 역전 기능을 사용하여서 nest에서도 하나의 controller를 만들면 "싱글턴(Singleton)이란 어떤 객체에 대해 인스턴스를 단 하나만 만드는 디자인 패턴의 한 방법" 싱글턴 패턴으로 한군데에 있는 객체를 통해 프로그램을 관리할 수 있다고 한다.

여기서 사용될 싱글턴 객체를 @Injectable() 을 통해서 받을 수 있다고 한다.

이러한 provider 들은 서비스 실행 시에 만들어지게 되며 종료시에 다시 내려가게 된다고 한다.

출처 : https://tristy.tistory.com/40?category=993422

또한 다음과 같이 직접 @Injectible을 안쓰더라도 ParentService와 같이 객체를 만들고 이를 상속받아서 Injectible을 해주는 방식으로 의존성 주입을 해줄 수도 있다.

이때 생성자에서 super를 써야 한다고 한다.

// propertyBase.parentService.ts

// parentService를 직접 참조하는 클래스가 없기 때문에 Injectable이 없어도 됩니다.
export class ParentService {
	// constructor(private readonly testServiceA: TestServiceA) {}
	@Inject(TestServiceA) private readonly testServiceA: TestServiceA;

	testHello(): string {
		return 'hello world';
	}

	parentTest(): string {
		return this.testServiceA.testHello();
	}
}
// // propertyBase.childService.ts
@Injectable()
export class ChildService extends ParentService {
	testHello(): string {
		return this.parentTest();
	}
}
// propertyBase.testServiceA.ts
@Injectable()
export class TestServiceA {
	testHello() {
		return 'hello Test A';
	}
}​

이것이 귀찮다면 위와 같이 @Inject를 사용하는 방법도 있다고 한다.

 

출처 : https://tristy.tistory.com/40?category=993422

 

[Nest Js] Nest Js 공식 문서 파헤치기 - Providers(프로바이더)

트리스티가 Nest Js를 공부하며 남긴 기록입니다. 틀린 내용은 언제든지 말씀해주세요 ~! 지난 포스팅에서는 Controller란 무엇이고, Nest js의 Controller에서는 어떤 기능을 사용할 수 있는지를 알아보

tristy.tistory.com

D. Module

다음과 같이 동일한 어플리케이션이라도 각각 다른 모듈을 가지고 있는 형태로 서비스가 만들어지게 된다.

일단 최상위 모듈의 경우

// main.ts

async function bootstrap() {
	const app = await NestFactory.create(AppModule);
	await app.listen(3000);
}
bootstrap();

다음과 같이 main.ts에서 처음에 웹서버를 만들어 줄때 띄우는 모듈이며 해당 최상위 모듈에서 하위 모듈을 가져와서 불러주는 식으로 서브 모듈을 작성한다.

// study.module.ts

@Module({
	controllers: [StudyController],
	providers: [StudyService, ChildService, TestServiceA],
	exports: [StudyService]
})
export class StudyModule {}​

만약 외부에서 또 사용을 하는 모듈이라면 exports안에 넣어야 한다고 한다. 이렇게 exports안에 넣어둔 모듈은 다른 모듈에서 imports에 명시하면 가져와서 사용이 가능하다고 한다.

나는 블록체인 관련된 모듈을 하나 만들어 두어서 이걸 다음과 같이 exports에 지정해 두었다.

import { Module } from "@nestjs/common";
import { BlockchainService } from "./blockchain.service";
import { ConfigModule } from "@nestjs/config";

@Module({
    imports: [ConfigModule],
    providers: [BlockchainService],
    exports: [BlockchainService],
})
export class BlockchainModule {}

만약 여기서 exports된 BlockchainService를 다른곳에서 그대로 import 한다면 "Nest can't resolve dependencies of the SubController" 오류가 발생한다고 한다.

imports는 무조건 모듈 형태만 받기 때문에 BlockchainModule을 imports로 받아야 한다.

또한 providers에서는 BlockchainService에서 @Injectible하여 의존성 주입을 해준 객체들을 모두 넣어주어야 한다.

만약 모든 모듈이 들어가게 된다면 Global Module로 지정해주는 방법도 있다.

// study.module.ts

@Global()
@Module({
	controllers: [StudyController],
	providers: [StudyService, ChildService, TestServiceA]
	exports: [StudyService]
})
export class StudyModule {}​

이런식으로 모듈 위에 Global을 지정해주면 된다고 한다. 하지만 전역 모듈이더라도 루트 모듈에는 넣어주어야 한다고 한다.

// app.module.ts

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

출처: https://tristy.tistory.com/43

 

[Nest Js] Nest Js 공식 문서 파헤치기 - Modules(모듈)

트리스티가 Nest Js를 공부하며 남긴 기록입니다. 틀린 내용은 언제든지 말씀해주세요 ~! 지난 포스팅에서는 Providers란 무엇인지를 알아보았습니다. 이번 포스팅에서는 Modules가 뭔지 알아보도록

tristy.tistory.com

 

728x90
반응형
Comments