Nest.js & Next.js - 2. 동작 설명

사용법은 Nest.jsNext.js를 써봤다면 크게 어렵지 않다.

Server code (Nest.js)

Server 코드는 /src 디렉토리에 있다.

외부로부터 들어오는 요청은 controller인 AppController(/src/app.controller.ts)에서 처리한다.

1
2
3
4
5
6
7
8
9
10
import { Controller, Get, Query, Render } from '@nestjs/common';

@Controller()
export class AppController {
@Render('Index') // 1) render할 page 지정
@Get() // 2) http request path 지정
public index(@Query('name') name?: string) { // 3) get `name` from query string
return { name }; // 4) send initial props to Index.tsx
}
}
  1. @Render() decorator를 사용해 어떤 페이지를 렌더링할지를 지정한다.
    이때 페이지의 기본 경로는 /pages/veiws/ 이다.

  2. Get() decorator를 사용해 HTTP request 경로를 지정한다.
    여기에서는 /로 들어온 요청을 핸들링한다.

  3. /로 요청 시 query string에서 name의 값을 받는다.

  4. query string의 name을 그대로 view로 보낸다.
    이는 Next.js의 getInitialProps를 통해 받아올 수 있다.

Client code (Next.js)

Client 코드는 /pages/views 디렉토리에 있다.

앞서 말한 것과 같이 Next.js를 써봤다면 코드 자체는 매우 쉽기 때문에 큰 설명이 필요하지 않다.
다만 살펴볼 곳은 Index.getInitialProps이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import * as React from 'react';
import { NextPage, NextPageContext } from 'next';

interface Props {
query: { name?: string };
}

const Index: NextPage<Props> = ({ query }) => {
const greetName = query.name ? query.name : 'World';
return <div>Hello, {greetName}!</div>;
};

// receive initial props from AppController
Index.getInitialProps = async (ctx: NextPageContext) => {
const { query } = ctx;
return { query };
};

export default Index;

AppController.index에서 return한 { query }initial props로 받아와서 사용한다. (SSR)

main.ts

npm dev 스크립트를 실행하면 /src/main.ts이 실행된다.
좀 더 자세히는 bootstrap 함수를 실행하는데 이 함수 내에서 Nest.js app과 Next.js app을 생성하고, 다음에 살펴볼 nest-nextRenderModule이 이 두 app을 binding한다.
그리고 마지막으로 3000 port를 listening 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import { NestFactory } from '@nestjs/core';
import { RenderModule } from 'nest-next';
import Next from 'next';
import 'reflect-metadata';
import { AppModule } from './application.module';

async function bootstrap() {
const dev = process.env.NODE_ENV !== 'production';
const app = Next({ dev }); // create Next.js app

await app.prepare();

// create Nest.js app using AppModule(contains RenderModule)
const server = await NestFactory.create(AppModule);

// get registered RenderModule in Nest.js app
const renderer = server.get(RenderModule);
// bind Nest.js app and Next.js app
renderer.register(server, app);

await server.listen(3000);
}

bootstrap();

Inside of nest-next

nest-next 패키지는 크게 RenderModuleRenderService로 이뤄져있다고 생각한다.

먼저 RenderModule.register에서 Nest.js app과 Next.js app을 인자로 받아 이 둘을 binding하는 로직이 있는 RenderService로 넘겨준다.

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
export class RenderModule {
private app?: INestAppliactionSubset;
private server?: ReturnType<typeof Server>;

constructor(private readonly service: RenderService) {}

public register(
app: INestAppliactionSubset,
server: ReturnType<typeof Server>,
options: Partial<RendererConfig> = {},
) {
// set Nest.js app and Next.js app
this.app = app;
this.server = server;

this.service.mergeConfig(options);
this.service.setRequestHandler(this.server.getRequestHandler());
// set Next.js renderer
this.service.setRenderer(this.server.render.bind(this.server));
this.service.setErrorRenderer(this.server.renderError.bind(this.server));
// bind Nest.js renderer and Nest.js http server
this.service.bindHttpServer(this.app.getHttpAdapter());

this.app.useGlobalFilters(new RenderFilter(this.service));
}
}

RenderService.bindHttpServer에서 두 app을 binding하는 자세한 로직이 있는데 자세히 확인하고 싶다면 위 링크를 통해 확인하자.


이번 포스팅을 통해 기본 코드 구조와 Nest.js app과 Next.js app이 binding 되는 코드를 간단하게 살펴보았다.

다음 포스팅에서는 셋팅한 example을 바탕으로 간단한 사용자 목록 조회 기능을 만들 것이다.

Share