일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- 스마트컨트렉트테스트
- multicall
- ethers websocket
- vue기초
- 러스트 기초 학습
- ethers type
- 컨트렉트 배포 자동화
- 티스토리챌린지
- SBT표준
- Vue
- chainlink 설명
- ambiguous function description
- 프록시배포구조
- rust 기초
- ethers v6
- 스마트 컨트렉트 함수이름 중복
- nest.js설명
- 머신러닝기초
- Vue.js
- 체인의정석
- ethers
- 오블완
- git rebase
- 러스트기초
- 스마트컨트렉트 예약어 함수이름 중복
- 스마트컨트렉트프록시
- 스마트컨트렉트 함수이름 중복 호출
- ethers typescript
- 컨트렉트 동일한 함수이름 호출
- 러스트 기초
- Today
- Total
체인의정석
Nest.js 기본 구조 공부(기본 구조, Controller, Provider, Moudule) 본문
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
B. Controller 구조
기본적인 구조를 파악했으니 부분부분 전체적인 흐름을 공부하고 넘어가도록 하겠다.
1. MVC 모델
먼저 MVC 모델은 다음과 같다. Model, View, Controller에서 컨트롤러가 해당 내용과 동일하다.
- Model - 데이터와 관련된 작업들을 처리합니다. 데이터베이스에 접근하여 수정, 삭제, 생성 등의 작업이 이뤄집니다.
- View - 사용자에게 보여줄 방식을 정의합니다.
- 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
C. Provider 구조
provider의 경우 MVC 모델 중 Model에 해당되는 부분이다. 제어의 역전이라는 개념을 일단 알면 좋은것 같다.
"제어의 역전(IOC: Inversion of Control)이란 객체의 생성부터 소멸까지 어플리케이션이 제어권을 갖는 것이 아니라 이런 것들을 대신 관리해주는 컨테이너에게 넘기는 것을 말합니다. 즉, 개발자가 열심히 new 연산자를 사용해서 객체를 생성하고, 객체 간 의존성 맺어주는 등의 귀찮았던 작업들을 컨테이너가 대신해주는 것이죠. 이러면 개발자들은 객체 관리를 컨테이너에게 맡기고 다른 작업들에 힘을 쏟을 수 있고, 코드의 재사용성과 유지보수성을 높여주기 때문에 편하게 개발을 할 수 있습니다. 이러한 컨테이너는 프레임워크에 존재하기 때문에 프레임워크를 사용하면 알아서 객체를 관리해줍니다. 따라서 Nest Js에도 이러한 기능이 존재한답니다."
이러한 제어의 역전 기능을 사용하여서 nest에서도 하나의 controller를 만들면 "싱글턴(Singleton)이란 어떤 객체에 대해 인스턴스를 단 하나만 만드는 디자인 패턴의 한 방법" 싱글턴 패턴으로 한군데에 있는 객체를 통해 프로그램을 관리할 수 있다고 한다.
여기서 사용될 싱글턴 객체를 @Injectable() 을 통해서 받을 수 있다고 한다.
이러한 provider 들은 서비스 실행 시에 만들어지게 되며 종료시에 다시 내려가게 된다고 한다.
또한 다음과 같이 직접 @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
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
'개발 > backend' 카테고리의 다른 글
nestjs-telegraf 사용해보기 (Nest.js 텔레그램봇) (0) | 2023.11.27 |
---|---|
ethers & websocket 참고 예제 (0) | 2023.11.27 |
Bignumber.js 사용하여 데이터 처리하기 (0) | 2023.08.25 |
UniswapV3 백엔드 구축하며, 틱 단위 계산에 사용한 다시 볼 것 같은 함수들 정리 (tickSpace 맞추기, 데이터 쌓는 지점 체크 & 동일 블록에서의 값 합산, Promise.all의 중첩 사용) (0) | 2023.08.24 |
javascript) 한 객체에 다수의 배열이 있을때 some 함수로 모두 검색하는 방법 (find vs indexOf vs some) (0) | 2023.08.07 |