URL 단축기 만들기 - 7. fastify 세팅하기 + URL 컨트롤러 만들기

URL 단축기 2022년 12월 13일

이번 글에서는 nestjs와 fastify를 함께 사용하기 위한 설정을 해보고 URL 컨트롤러도 써볼거에요. 이번에도 좀 길어질 것 같네요.

먼저 fastify를 세팅해 줄게요.

Fastify 설정하기

yarn add @nestjs/platform-fastify

main.ts 를 수정해 주세요.

import { NestFactory } from '@nestjs/core'
import { AppModule } from './app.module'
import {
  NestFastifyApplication,
  FastifyAdapter,
} from '@nestjs/platform-fastify'

async function bootstrap() {
  const app = await NestFactory.create<NestFastifyApplication>(
    AppModule,
    new FastifyAdapter(),
  )
  await app.listen(3000)
}
bootstrap()

이제 fastify 세팅이 끝났어요! 생각보다 할게 없었네요.

컨트롤러 코드 생성하기

API에 사용될 컨트롤러는 api 폴더 아래에 만들기로 정했어요. 그러니 먼저 API 모듈을 만들어 줄게요. 그다음 그 아래에 urls 모듈을 하나 더 만들어 줄거에요.

nest g mo api
nest g mo api/urls
nest g co api/urls

이렇게 하면 api 폴더 안에 urls 모듈이 추가된걸 보실수 있을거에요. 이제부터 API 관련은 여기서 작업하니 알아두시면 될것 같네요.

DTO 만들기

이제 url을 만들때 사용되는 DTO를 만들어 볼거에요. DTO는 클래스로 만들 수 있어요.

일단 그 전에, DTO에 올바른 값이 들어갔는지 검증하기 위한 class-validator 이라는 패키지를 설치해 줄거에요.

yarn add class-validator class-transformer

class-validator 패키지가 작동하도록 코드를 써줄게요.

app.useGlobalPipes(
  new ValidationPipe({
    whitelist: true,
  }),
)

이제 DTO를 써줄게요. DTO는 src/api/urls/dto 폴더에 만들어 줬어요.

import { IsUrl, IsString, IsOptional } from 'class-validator'

export class UrlCreateDto {
  @IsUrl()
  url: string

  @IsOptional()
  @IsString()
  slug: string
}

slug는 나중에 구현할거라 아직은 필요없긴 하네요. URL이 만들어졌을때 응답으로 보내질 DTO도 한번 만들어 볼게요.

export class UrlCreateResponseDto {
  constructor(public url: string, public slug: string) {}
}

그럼 이제 바로 컨트롤러를 써볼게요. 일단 코드를 생성해 주세요.

이번 글에서 제가 만들 라우트는 딱 하나에요. URL 생성 api에요. API는 이제부터 api 모듈 아래에 놓는걸로 할게요.

컨트롤러 기본 URL을 바꿔주세요.

@Controller('api/urls')

이제 URL 생성 API를 만들어 볼게요.

일단 데이터 검증이 제대로 되는지 확인하기 위해 기본적인 코드만 써줬어요.

@Post()
async createUrl(@Body() data: UrlCreateDto) {
  console.log(data)
}

이렇게 하고 테스트해보니 잘 작동하는걸로 확인됐어요.

그럼 이제 실제로 작동하는 코드를 써줄게요.

먼저 urls service를 API에서 사용할 수 있게 만들기 위해 urls service를 export 해줄게요. src/urls/urls.module.tsexports 를 추가해 주세요.

@Module({
  providers: [UrlsService],
  imports: [DatabaseModule],
  exports: [UrlsService],
})
export class UrlsModule {}

그 다음 src/api/urls/urls.module.ts 를 수정해 주세요. 클래스 이름은 ApiUrlsModules 로 바꿔줬어요.

import { Module } from '@nestjs/common'
import { UrlsController } from './urls.controller'
import { UrlsModule } from 'src/urls/urls.module'

@Module({
  controllers: [UrlsController],
  imports: [UrlsModule],
})
export class ApiUrlsModule {}

이제 urls 서비스를 컨트롤러에서 사용할 수 있게 추가해 볼게요.

constructor(private urlsService: UrlsService) {}

이제 이러면 컨트롤러 안에서는 urls 서비스를 사용할 수 있게 되었어요!

이제 진짜 컨트롤러 코드를 써줄게요.

@Post()
async createUrl(@Body() data: UrlCreateDto): Promise<UrlCreateResponseDto> {
  const created = await this.urlsService.createUrl(data.url)

  return new UrlCreateResponseDto(created.url, created.slug)
}

생각보다 너무 간단하네요. 뭐 간단하면 좋지!히히

어쨌든! 작동하는지 테스트해보고 테스트 코드도 한번 써볼게요! 컨트롤러 테스트코드는 아직 한번도 안써봤네요!

db에도 잘 추가된걸 확인했어요!

잘 작동하네요! 그럼 이제 테스트 코드를 써보기로 할게요!

import { Test, TestingModule } from '@nestjs/testing'
import { edgedbClient } from 'src/database/client'
import { UrlsModule } from 'src/urls/urls.module'
import { UrlCreateDto } from './dto/UrlCreateDto'
import { UrlsController } from './urls.controller'

describe('UrlsController', () => {
  let controller: UrlsController

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      controllers: [UrlsController],
      imports: [UrlsModule],
    }).compile()

    controller = module.get<UrlsController>(UrlsController)
  })

  it('should be defined', () => {
    expect(controller).toBeDefined()
  })

  let slug: string = null!

  it('Should return url object', async () => {
    const dto = new UrlCreateDto()

    dto.url = 'https://google.com'

    let result = controller.createUrl(dto)

    await expect(result.then((x) => x.url)).resolves.toBe('https://google.com') // Check the url matches with the data requested

    slug = (await result).slug
  })

  afterAll(async () => {
    if (slug) {
      await edgedbClient.execute(`delete Url filter .slug = <str>$slug;`, {
        slug,
      })
    }
  })
})

테스트를 돌려봤는데 swc-node 때문인지 커버리지 100%가 안되더라고요.. 나중에 한번 찾아보는걸로 할게요! 그럼 이번 글은 여기까지 하고 오늘도 소스코드 링크 달아놓고 갈게요!

GitHub - paringparing/url-shortener: something
something. Contribute to paringparing/url-shortener development by creating an account on GitHub.

태그

파링

바보