728x90
반응형

 

Nest에 관한 기본적인 내용은 블로그 내 Nest.js 카테고리에서 확인 가능하다.

 

main.ts 파일부터 분석해보자.

 

main.ts

import { NestFactory } from '@nestjs/core';
import { AppModule } from './application.module';

async function bootstrap() {
  const server = await NestFactory.create(AppModule);

  await server.listen(3000);
}

bootstrap();

 

- AppModule을 import해서 가져오고 NestFactory.create() 메소드로 3000번 포트에 서버를 열어주었다.

 

 

application.module.ts

import { Module } from '@nestjs/common';
import { RenderModule } from 'nest-next';
import Next from 'next';
import { AppController } from './app.controller';
import { BlogController } from './blog/blog.controller';
import { BlogService } from './blog/blog.service';

@Module({
  imports: [
    RenderModule.forRootAsync(
      Next({
        dev: process.env.NODE_ENV !== 'production',
        conf: { useFilesystemPublicRoutes: false },
      }),
    ),
  ],
  controllers: [AppController, BlogController],
  providers: [BlogService],
})
export class AppModule {}

- npm으로 설치했었던 nest-next 모듈에서 RenderModule을 가져와 import해주고, .forRootAsync메소드에 Next를 넣어주었다.

 

 

app.controller.ts

import { Controller, Get, Query, Render } from '@nestjs/common';

@Controller()
export class AppController {
  @Render('home')
  @Get()
  public index(@Query('name') name?: string) {
    return { name };
  }

  @Render('about')
  @Get('/about')
  public about() {
    return {};
  }
}

루트 라우트에서 '/home' 경로와 '/about' 경로에 대한 @Get 데코레이터를 사용했다.

단, 'home'으로 렌더를 하고, @Get데코레이터 파라미터에 아무것도 넣지 않았기 때문에 Next 에서 home파일이 결국 주소상 루트 라우트가 된다.

 

실제로 확인해보면

 

home.tsx

import * as React from 'react';
import { NextPage, NextPageContext } from 'next';

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

const Home: NextPage<Props> = ({ query }) => {
  const greetName = query.name ? query.name : 'World';

  return (
    <div>
      <div>Hello, {greetName}!</div>
    </div>
  );
};

export async function getServerSideProps(ctx: NextPageContext) {
  const query = {
    name: ctx.query.name || null,
  };
  return { props: { query } };
}

export default Home;

home.tsx파일에서 반환되는 페이지가

 

 

루트경로에 나타나는 것을 확인할 수 있다.

 

home.tsx파일에 보면 name 을 query로 받을 수 있게 코딩되어있는데, @Get데코레이터에 담아서 웹을 실행해보면

 

 

 

이렇게 name이 잘 전달되는 것을 확인할 수 있다. Nest controller에 console.log()를 실행해봐도 좋다.

 

 

blog.contoller.ts

import {
  Controller,
  Get,
  NotFoundException,
  Param,
  Render,
} from '@nestjs/common';
import { BlogService } from './blog.service';

@Controller('/blog')
export class BlogController {
  constructor(private service: BlogService) {}

  @Render('blog')
  @Get()
  public index() {
    return { posts: this.service.all() };
  }

  @Render('blog/[slug]')
  @Get(':slug')
  public get(@Param('slug') slug: string) {
    const post = this.service.find(slug);

    if (post === null) {
      throw new NotFoundException();
    }

    return { post };
  }
}

@Cotroller 데코레이터에 파라미터로 '/blog' 를 넣었기 때문에 '/blog' 경로는 이 BlogController에 들어오게 된다.

 

import { BlogService } from './blog.service';
...
constructor(private service: BlogService) {}
...

BlogService를 import하고 class안에 생성자를 선언해준다.

 

@Render('blog')
  @Get()
  public index() {
    return { posts: this.service.all() };
  }

pages/views/blog/index.tsx페이지에 '/blog' 경로를 주고, service 클래스의 all()메소드로 받아온 posts 내용들을 반환해준다.

 

@Render('blog/[slug]')
  @Get(':slug')
  public get(@Param('slug') slug: string) {
    const post = this.service.find(slug);

    if (post === null) {
      throw new NotFoundException();
    }

    return { post };
  }

pages/views/blog/[slug].tsx 페이지에 '/blog/:slug' 경로를 주고 @Param데코레이터로 slug의 내용을 받아오며 

service에 선언된 find메소드를 사용해서 반환 값이 없을 경우 에러 메세지를 보내주며 있을 경우 post를 반환해준다.

 

 

blog.service.ts

import { Injectable, Inject } from '@nestjs/common';
import { IPost } from '../../types';

const POSTS: Record<string, IPost> = {
  'first-post': {
    title: 'First Post!',
    slug: 'first-post',
    content: [
      'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent sed suscipit quam, sit amet feugiat ligula. Nunc sit amet velit vestibulum, mattis orci gravida, aliquam velit. Donec eget lectus nec ipsum suscipit gravida et et odio. Morbi hendrerit dui scelerisque, imperdiet ligula in, ornare risus. Aliquam blandit sem risus, a ornare orci finibus ut. Maecenas interdum lacus arcu, nec finibus nibh semper quis. Vivamus venenatis pharetra ligula, eget semper justo finibus et. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Nullam finibus accumsan elit, et ornare nulla accumsan id. Cras nec leo sed ex egestas malesuada. Nullam a bibendum libero. Cras ullamcorper massa sed odio euismod vulputate. Nullam at ullamcorper dolor. Maecenas et fermentum arcu. Sed interdum nunc neque, eu consectetur ex commodo finibus. Nunc interdum aliquam purus, eu lobortis enim semper et.',
      'Ut sed dolor odio. Mauris cursus aliquet tortor, a posuere mi elementum in. Morbi sed efficitur mauris. Donec sed nulla efficitur, finibus massa ut, aliquet elit. Praesent eu mattis velit. Fusce sodales tincidunt mi, ut placerat turpis lobortis eu. Interdum et malesuada fames ac ante ipsum primis in faucibus. Nam at scelerisque lacus, ut commodo leo. Morbi vitae iaculis arcu. Donec finibus erat sed tristique feugiat. Morbi lorem tellus, elementum et facilisis eu, egestas fringilla eros. In quis arcu aliquam, ornare nulla malesuada, convallis massa. Donec tellus neque, tempor eu porttitor at, malesuada eget tellus. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Quisque vel pellentesque elit. Morbi semper purus velit, a pulvinar eros blandit vel.',
    ],
  },
  'second-post': {
    title: 'Second Post!',
    slug: 'second-post',
    content: [
      'Nulla sed purus ullamcorper, volutpat leo ac, blandit sem. Aenean efficitur ante rhoncus, lobortis est nec, consequat nisl. Fusce quis semper ligula, eget commodo magna. In tincidunt nisl sed dui ornare, nec pulvinar nibh laoreet. Suspendisse lobortis elit at nunc egestas fermentum. Etiam leo dui, fermentum ac nulla et, hendrerit varius arcu. Quisque porttitor congue mattis. Mauris non lorem suscipit turpis dictum porttitor. Nullam eget blandit felis. Duis eu erat ac mauris egestas placerat. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.',
      'Etiam vel tellus sollicitudin, laoreet quam id, dignissim eros. Suspendisse dapibus tempor magna eget eleifend. Morbi molestie arcu id sagittis tristique. Suspendisse luctus id velit et elementum. Cras gravida sodales quam vel iaculis. Cras aliquet ex a placerat tincidunt. Fusce at ligula urna. Pellentesque id sapien lacus. Nullam eleifend ultrices tortor a hendrerit. Vivamus cursus leo eget tortor porttitor finibus. Quisque at quam gravida, aliquam orci ut, volutpat enim. Vivamus sit amet lobortis lacus. In aliquet consectetur diam vitae lacinia. Suspendisse ultrices malesuada turpis ac congue. Pellentesque vestibulum, nulla nec mollis euismod, sapien ipsum lobortis tortor, nec pellentesque sem nulla gravida diam.',
    ],
  },
};

export class BlogService {
  public all(): IPost[] {
    return Object.values(POSTS);
  }

  public find(slug: string): IPost | null {
    return POSTS[slug] || null;
  }
}

 

먼저 IPost를 가져온다. types 파일은 루트경로에서 확인할 수 있다.

import { IPost } from '../../types';

 

types.ts

export interface IPost {
  title: string,
  slug: string,
  content: string[]
}

 

POSTS는 TypeScript의 Recode 형식을 따른다. key값으로 문자열을 가지고, value값으로 IPost를 가진다.

const POSTS: Record<string, IPost> = {

 


TypeScript 유틸리티 클래스에 관한 내용은 다음 링크를 확인하면 된다.

https://medium.com/harrythegreat/typescript-%EC%9C%A0%ED%8B%B8%EB%A6%AC%ED%8B%B0-%ED%81%B4%EB%9E%98%EC%8A%A4-%ED%8C%8C%ED%97%A4%EC%B9%98%EA%B8%B0-7ae8a786fb20

 

Typescript 유틸리티 클래스 파헤치기

Utility Types Docs를 중점으로

medium.com


 

export class BlogService {
  public all(): IPost[] {
    return Object.values(POSTS);
  }

  public find(slug: string): IPost | null {
    return POSTS[slug] || null;
  }
}

import 했던 IPost[]의 타입을 가진 POSTSvalue값들을 반환값으로 가지는 all()메소드와,

slug를 파라미터로 가지고 POSTS 배열 중에 slug의 키 값을 가지며 IPost 타입을 가진 value를 반환하는 find() 메소드를 선언했다.

 


/blog

 

 


/blog/first-post

 

 

 

여기까지 NestJS로 이루어진 백엔드를 살펴보았다. 다음 포스트부터는 Next.JS로 이루어진 프론트 쪽을 살펴보겠다.

728x90
반응형

+ Recent posts