웹사이트 검색

NestJS를 사용하여 NodeJS에서 유형 안전 URL 단축기를 빌드하는 방법


저자는 Write for DOnations 프로그램을 선택했습니다.

소개

Uniform Resource Locator의 약어인 URL은 웹에서 고유한 리소스에 부여된 주소입니다. URL은 고유하기 때문에 두 리소스가 동일한 URL을 가질 수 없습니다.

URL의 길이와 복잡성은 다양합니다. URL은 http://llanfairpwllgwyngyllgogerychwyrndrobwllllantysiliogogogoch.co.uk처럼 짧을 수 있습니다. 복잡한 URL은 보기 흉하고 검색 엔진 최적화(SEO) 문제를 일으키며 마케팅 계획에 부정적인 영향을 미칠 수 있습니다. URL 단축기는 긴 URL을 짧은 URL로 매핑하고 짧은 URL이 사용될 때 사용자를 원래 URL로 리디렉션합니다.

이 자습서에서는 서비스를 사용하여 URL 단축기를 만듭니다. 그런 다음 단축 및 리디렉션 요청을 용이하게 하기 위해 경로 처리기를 생성합니다.

전제 조건

이 자습서를 따르려면 다음이 필요합니다.

  • 시스템에 맞는 Node.js 설치 방법 및 로컬 개발 환경 튜토리얼 생성 방법을 위한 로컬 개발 환경
  • 1단계에서 설정할 시스템에 설치된 NestJS CLI 및 NestJS에 익숙합니다. NestJS 시작하기를 검토하세요.
  • 자세한 내용은 TypeScript 시리즈의 코딩 방법에 익숙합니다.

1단계 - 개발 환경 준비

이 단계에서는 URL 단축 로직 구현을 시작하는 데 필요한 모든 것을 설정합니다. NestJS를 전역적으로 설치하고, 새로운 NestJS 애플리케이션 상용구를 생성하고, 종속성을 설치하고, 프로젝트의 모듈, 서비스 및 컨트롤러를 생성합니다.

먼저 이전에 Nest CLI를 설치하지 않은 경우 전역적으로 Nest CLI를 설치합니다. 이 CLI를 사용하여 프로젝트 디렉토리와 필수 파일을 생성합니다. 다음 명령을 실행하여 Nest CLI를 설치합니다.

  1. npm install -g @nestjs/cli

-g 플래그는 Nest CLI를 시스템 전체에 설치합니다.

다음 출력이 표시됩니다.

  1. Output
    ...
  2. added 249 packages, and audited 250 packages in 3m
  3. 39 packages are looking for funding
  4. run npm fund for details
  5. found 0 vulnerabilities

그런 다음 new 명령을 사용하여 프로젝트를 만들고 필요한 상용구 시작 파일을 생성합니다.

  1. nest new URL-shortener

다음 출력이 표시됩니다.

  1. Output
    ...
  2. ⚡ We will scaffold your app in a few seconds..
  3. CREATE url-shortener/.eslintrc.js (631 bytes)
  4. CREATE url-shortener/.prettierrc (51 bytes)
  5. CREATE url-shortener/nest-cli.json (118 bytes)
  6. CREATE url-shortener/package.json (2002 bytes)
  7. CREATE url-shortener/README.md (3339 bytes)
  8. CREATE url-shortener/tsconfig.build.json (97 bytes)
  9. CREATE url-shortener/tsconfig.json (546 bytes)
  10. CREATE url-shortener/src/app.controller.spec.ts (617 bytes)
  11. CREATE url-shortener/src/app.controller.ts (274 bytes)
  12. CREATE url-shortener/src/app.module.ts (249 bytes)
  13. CREATE url-shortener/src/app.service.ts (142 bytes)
  14. CREATE url-shortener/src/main.ts (208 bytes)
  15. CREATE url-shortener/test/app.e2e-spec.ts (630 bytes)
  16. CREATE url-shortener/test/jest-e2e.json (183 bytes)
  17. ? Which package manager would you ❤️ to use? (Use arrow keys)
  18. > npm
  19. yarn
  20. pnpm

npm을 선택합니다.

다음 출력이 표시됩니다.

  1. Output
    √ Installation in progress... ☕
  2. 🚀 Successfully created project url-shortener
  3. 👉 Get started with the following commands:
  4. $ cd url-shortener
  5. $ npm run start
  6. Thanks for installing Nest 🙏
  7. Please consider donating to our open collective
  8. to help us maintain this package.
  9. 🍷 Donate: https://opencollective.com/nest

생성된 프로젝트 디렉터리로 이동합니다.

  1. cd url-shortener

이 디렉토리에서 모든 후속 명령을 실행합니다.

참고: NestJS CLI는 app.controller.ts, app.controller.spec.tsapp.service.ts 파일을 생성합니다. 새 프로젝트를 생성합니다. 이 자습서에서는 필요하지 않으므로 삭제하거나 무시할 수 있습니다.

다음으로 필요한 종속 항목을 설치합니다.

이 튜토리얼에는 몇 가지 종속성이 필요하며 NodeJS의 기본 패키지 관리자 Nano-ID를 사용하여 설치할 것입니다.

TypeORM은 TypeScript 애플리케이션과 관계형 데이터베이스 간의 상호 작용을 용이하게 하는 객체 관계형 매퍼입니다. 이 ORM은 NestJS의 전용 @nestjs/typeorm 패키지로 인해 NestJS와 원활하게 작동합니다. NestJS의 기본 typeorm 패키지와 함께 이 종속성을 사용하여 SQLite 데이터베이스와 상호 작용합니다.

다음 명령을 실행하여 TypeORM 및 전용 NestJS 패키지를 설치합니다.

  1. npm install @nestjs/typeorm typeorm

SQLite는 작고 빠른 자체 포함 SQL 데이터베이스 엔진을 구현하는 라이브러리입니다. 이 종속성을 데이터베이스로 사용하여 단축 URL을 저장하고 검색합니다.

다음 명령을 실행하여 SQLite를 설치합니다.

  1. npm install sqlite3

class-validator 패키지에는 NestJS에서 데이터 유효성 검사에 사용되는 데코레이터가 포함되어 있습니다. 데이터 전송 개체와 함께 이 종속성을 사용하여 애플리케이션으로 전송된 데이터의 유효성을 검사합니다.

다음 명령을 실행하여 class-validator를 설치합니다.

  1. npm install class-validator

class-transformer 패키지를 사용하면 일반 개체를 클래스의 인스턴스로 또는 그 반대로 변환할 수 있습니다. 단독으로 작동할 수 없으므로 class-validator와 함께 이 종속성을 사용합니다.

다음 명령을 실행하여 class-transformer를 설치합니다.

  1. npm install class-transformer

Nano-ID는 안전하고 URL 친화적인 고유 문자열 ID 생성기입니다. 이 종속성을 사용하여 각 URL 리소스에 대한 고유 ID를 생성합니다.

다음 명령을 실행하여 Nano-ID를 설치합니다.

  1. npm install nanoid@^3.0.0

참고: 3.0.0보다 높은 Nano-ID 버전은 CommonJS 모듈에 대한 지원을 비활성화했습니다. TypeScript 컴파일러에서 생성된 JavaScript 코드가 여전히 CommonJS 모듈 시스템을 사용하기 때문에 이 문제로 인해 애플리케이션에서 오류가 발생할 수 있습니다.

필요한 종속 항목을 설치한 후 Nest CLI를 사용하여 프로젝트의 모듈, 서비스 및 컨트롤러를 생성합니다. 모듈은 프로젝트를 구성하고 서비스는 URL 단축기에 대한 모든 논리를 처리하며 컨트롤러는 경로를 처리합니다.

다음 명령을 실행하여 모듈을 생성하십시오.

  1. nest generate module url

다음 출력이 표시됩니다.

Output
CREATE src/url/url.module.ts (80 bytes) UPDATE src/app.module.ts (304 bytes)

그런 다음 다음 명령을 실행하여 서비스를 생성합니다.

  1. nest generate service url --no-spec

--no-spec 플래그는 테스트 파일 없이 파일을 생성하도록 Nest CLI에 지시합니다. 이 자습서에서는 테스트 파일이 필요하지 않습니다.

다음 출력이 표시됩니다.

Output
CREATE src/url/url.service.ts (87 bytes) UPDATE src/url/url.module.ts (151 bytes)

그런 다음 다음 명령을 실행하여 컨트롤러를 생성합니다.

  1. nest generate controller url --no-spec

다음 출력이 표시됩니다.

Output
CREATE src/url/url.controller.ts (95 bytes) UPDATE src/url/url.module.ts (233 bytes)

이 단계에서는 애플리케이션과 개발에 필요한 대부분의 파일을 생성했습니다. 다음으로 애플리케이션을 데이터베이스에 연결합니다.

2단계 - 애플리케이션을 데이터베이스에 연결

이 단계에서는 데이터베이스의 URL 리소스를 모델링하는 엔터티를 생성합니다. 엔티티는 저장된 데이터의 필수 속성을 포함하는 파일입니다. 또한 애플리케이션과 해당 데이터베이스 사이의 액세스 계층으로 리포지토리를 생성합니다.

nano 또는 원하는 텍스트 편집기를 사용하여 src/url 폴더에서 url.entity.ts라는 파일을 만들고 엽니다.

  1. nano src/url/url.entity.ts

이 파일에는 데이터를 모델링하는 엔터티가 포함됩니다.

다음으로 src/url/url.entity.ts 파일에서 다음 Typescript 코드를 추가합니다.

import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';

@Entity()
export class Url {
    @PrimaryGeneratedColumn()
    id: number;

    @Column()
    urlCode: string;

    @Column()
    longUrl: string;

    @Column()
    shortUrl: string;
}

먼저 typeorm에서 Entity, ColumnPrimaryGeneratedColumn 데코레이터를 가져옵니다.

이 코드는 클래스를 엔터티로 표시하는 Entity 데코레이터로 주석이 달린 클래스 Url을 만들고 내보냅니다.

각 속성은 id에 대한 PrimaryGeneratedColumn 및 나머지 속성에 대한 Column과 같이 적절한 데코레이터로 지정되고 주석이 추가됩니다. PrimaryGeneratedColumn은 주석을 추가하는 속성에 대한 값을 자동으로 생성하는 데코레이터입니다. TypeOrm은 이를 사용하여 각 리소스에 대한 ID를 생성합니다. 은 데이터베이스의 열로 주석을 추가하는 속성을 추가하는 데코레이터입니다.

데이터베이스에 저장해야 하는 속성은 다음과 같습니다.

  • id는 데이터베이스 테이블의 기본 키입니다.
  • urlCodenanoid 패키지에서 생성된 고유 ID이며 각 URL을 식별하는 데 사용됩니다.
  • longUrl은 축약을 위해 애플리케이션으로 전송되는 URL입니다.
  • shortUrl은 단축 URL입니다.

파일을 저장하고 닫습니다.

다음으로 애플리케이션과 데이터베이스 간의 연결을 만듭니다.

먼저 nano 또는 원하는 텍스트 편집기에서 src/app.module.ts를 엽니다.

  1. nano src/app.module.ts

그런 다음 강조 표시된 줄을 파일에 추가합니다.

import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Url } from './url/url.entity';
import { UrlModule } from './url/url.module';

@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: 'sqlite',
      database: 'URL.sqlite',
      entities: [Url],
      synchronize: true,
    }),
    UrlModule,
  ],
  controllers: [],
  providers: [],
})
export class AppModule {}

먼저 @nestjs/typeorm에서 TypeOrmModule을 가져오고 ./url/url.entity에서 Url을 가져옵니다. 여전히 AppControllerAppService와 관련된 줄이 있을 수 있습니다. 파일에 남겨두어도 튜토리얼의 나머지 부분에는 영향을 미치지 않습니다.

imports 배열에서 TypeOrmModuleforRoot 메서드를 호출하여 애플리케이션의 모든 모듈을 통해 연결을 공유합니다. forRoot 메서드는 구성 개체를 인수로 사용합니다.

구성 개체에는 연결을 만드는 속성이 포함되어 있습니다. 이러한 속성에는 다음이 포함됩니다.

  • type 속성은 TypeOrm을 사용하여 상호 작용하는 데이터베이스의 종류를 나타냅니다. 이 경우 sqlite로 설정됩니다.
  • database 속성은 데이터베이스의 기본 이름을 나타냅니다. 이 경우 URL.sqlite로 설정됩니다.
  • 엔티티 속성은 프로젝트에 있는 모든 엔터티의 배열입니다. 이 경우 배열 내부에 Url로 지정된 하나의 엔티티만 있습니다.
  • 동기화 옵션은 데이터베이스 테이블을 엔티티와 자동으로 동기화하고 코드를 실행할 때마다 테이블을 업데이트합니다. 이 경우 true로 설정됩니다.

참고: synchronizetrue로 설정하는 것은 개발 환경에서만 이상적입니다. 데이터 손실이 발생할 수 있으므로 프로덕션에서는 항상 false로 설정해야 합니다.

파일을 저장하고 닫습니다.

다음으로 애플리케이션과 데이터베이스 사이의 액세스 계층 역할을 할 저장소를 생성합니다. 엔티티를 상위 모듈에 연결해야 하며 이 연결을 통해 Nest 및 TypeOrm이 리포지토리를 자동으로 생성할 수 있습니다.

src/url/url.module.ts 열기:

  1. nano src/url/url.module.ts

기존 파일에서 강조 표시된 줄을 추가합니다.

import { Module } from '@nestjs/common';
import { UrlService } from './url.service';
import { UrlController } from './url.controller';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Url } from './url.entity';

@Module({
  imports: [TypeOrmModule.forFeature([Url])],
  providers: [UrlService],
  controllers: [UrlController],
})
export class UrlModule {}

@nestjs/typeorm에서 TypeOrmModule을 가져오는 Module 데코레이터 내부에 imports 배열을 만듭니다. ./url.entity의 URL. imports 배열 내에서 TypeOrmModuleforFeature 메서드를 호출합니다. forFeature 메서드는 엔터티 배열을 인수로 사용하므로 Url 엔터티를 전달합니다.

파일을 저장하고 닫습니다.

Nest와 TypeOrm은 서비스와 데이터베이스 사이에서 액세스 레이어 역할을 할 배후의 저장소를 생성합니다.

이 단계에서는 애플리케이션을 데이터베이스에 연결했습니다. 이제 URL 단축 로직을 구현할 준비가 되었습니다.

3단계 - 서비스 로직 구현

이 단계에서는 두 가지 방법으로 서비스 논리를 구현합니다. 첫 번째 메서드인 shortenUrl에는 모든 URL 단축 논리가 포함됩니다. 두 번째 방법인 리디렉션에는 사용자를 원래 URL로 리디렉션하는 모든 논리가 포함됩니다. 또한 데이터 전송 개체를 만들어 애플리케이션으로 들어오는 데이터의 유효성을 검사합니다.

저장소에 대한 서비스 액세스 제공

이러한 방법을 구현하기 전에 애플리케이션이 데이터베이스에서 데이터를 읽고 쓸 수 있도록 리포지토리에 대한 서비스 액세스 권한을 부여합니다.

먼저 src/url/url.service.ts를 엽니다.

  1. nano src/url/url.service.ts

다음 강조 표시된 줄을 기존 파일에 추가합니다.

import { Injectable } from '@nestjs/common';
import { Repository } from 'typeorm';
import { InjectRepository } from '@nestjs/typeorm';
import { Url } from './url.entity';

@Injectable()
export class UrlService {
  constructor(
    @InjectRepository(Url)
    private repo: Repository<Url>,
  ) {}
}

typeorm에서 Repository를 가져오고, @nestjs/typeorm에서 InjectRepository를 가져오고, Url을 가져옵니다. ./url.entity에서.

UrlService 클래스에서 생성자를 만듭니다. constructor 내부에서 개인 변수인 repo를 매개변수로 선언합니다. 그런 다음 일반 유형이 Urlrepo저장소 유형을 할당합니다. InjectRepository 데코레이터로 repo 변수에 주석을 달고 Url을 인수로 전달합니다.

파일을 저장하고 닫습니다.

이제 서비스가 repo 변수를 통해 저장소에 액세스할 수 있습니다. 모든 데이터베이스 쿼리와 TypeOrm 메서드가 호출됩니다.

다음으로 비동기 메서드인 shortenUrl을 만듭니다. 이 메서드는 URL을 인수로 사용하고 단축 URL을 반환합니다. 메서드에 입력된 데이터가 유효한지 확인하려면 class-validatorclass-transformer 패키지와 함께 데이터 전송 개체를 사용하여 데이터를 확인합니다. .

데이터 전송 개체 만들기

비동기 메서드를 만들기 전에 shortenUrl 비동기 메서드에 필요한 데이터 전송 개체를 만듭니다. 데이터 전송 개체는 응용 프로그램 간에 데이터가 전송되는 방법을 정의하는 개체입니다.

먼저 url 폴더 내에 dtos(데이터 전송 개체) 폴더를 만듭니다.

  1. mkdir src/url/dtos

그런 다음 해당 폴더 내에 url.dto.ts라는 파일을 만듭니다.

  1. nano src/url/dtos/url.dto.ts

새 파일에 다음 코드를 추가합니다.

import { IsString, IsNotEmpty } from 'class-validator';

export class ShortenURLDto {
  @IsString()
  @IsNotEmpty()
  longUrl: string;
}

class-validator에서 IsStringIsNotEmpty 데코레이터를 가져옵니다. 그런 다음 ShortenURLDto 클래스를 만들고 내보냅니다. ShortenURLDto 클래스 내에서 longUrl 속성을 만들고 string 유형을 할당합니다.

또한 IsStringIsNotEmpty 데코레이터로 longUrl 속성에 주석을 추가합니다. 이러한 데코레이터로 longUrl 속성에 주석을 추가하면 longUrl이 항상 문자열이고 비어 있지 않은지 확인할 수 있습니다.

파일을 저장하고 닫습니다.

그런 다음 src/main.ts 파일을 엽니다.

  1. nano src/main.ts

강조 표시된 코드 부분을 기존 파일에 추가합니다.

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ValidationPipe } from '@nestjs/common';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalPipes(new ValidationPipe({ whitelist: true }));
  await app.listen(3000);
}
bootstrap();

class-validator 패키지를 사용하여 애플리케이션으로 들어오는 모든 데이터에 유효성 검사 규칙을 적용하는 ValidationPipe를 가져옵니다.

그런 다음 애플리케이션 인스턴스(app)에서 useGlobalPipes 메서드를 호출하고 옵션 개체와 함께 ValidationPipe의 인스턴스를 전달합니다. whitelist 속성이 true로 설정됩니다. useGlobalPipes 메서드는 응용 프로그램 수준에서 ValidationPipe를 바인딩하여 모든 경로가 잘못된 데이터로부터 보호되도록 합니다. whitelist 속성을 true로 설정하면 DTO에 지정되지 않은 속성의 유효성 검사(반환) 개체가 제거됩니다.

파일을 저장하고 닫습니다.

다음으로 데이터 전송 개체를 url.service.ts 파일로 가져와 shortenUrl 메서드에 적용합니다.

shortUrl 메서드 만들기

shortenUrl 메소드는 대부분의 URL 단축 로직을 처리합니다. ShortenURLDto 유형의 url 매개변수를 사용합니다.

먼저 src/url/url.service.ts 파일을 엽니다.

  1. nano src/url/url.service.ts

강조 표시된 줄을 파일에 추가합니다.

import {
  BadRequestException,
  Injectable,
  NotFoundException,
  UnprocessableEntityException,
} from '@nestjs/common';
import { Repository } from 'typeorm';
import { InjectRepository } from '@nestjs/typeorm';
import { Url } from './url.entity';
import { ShortenURLDto } from './dtos/url.dto';
import { nanoid } from 'nanoid';
import { isURL } from 'class-validator';
...

먼저 오류에 사용할 것이기 때문에 @nestjs/common에서 NotFoundExeception, BadRequestExceptionUnprocessableEntityException을 가져옵니다. 손질. 그런 다음 nanoid에서 {nanoid}를 가져오고 class-validator에서 isURL을 가져옵니다. isURL은 제공된 longUrl이 유효한 URL인지 확인하는 데 사용됩니다. 마지막으로 데이터 유효성 검사를 위해 ./dtos/url.dto에서 ShortenURLDto를 가져옵니다.

그런 다음 constructor 아래의 UrlService 클래스에 다음을 추가합니다.

...
async shortenUrl(url: ShortenURLDto) {}

그런 다음 shortenUrl 메서드에 다음 코드를 추가합니다.

...
    const { longUrl } = url;

    //checks if longurl is a valid URL
    if (!isURL(longUrl)) {
      throw new BadRequestException('String Must be a Valid URL');
    }

    const urlCode = nanoid(10);
    const baseURL = 'http://localhost:3000';

    try {
      //check if the URL has already been shortened
      let url = await this.repo.findOneBy({ longUrl });
      //return it if it exists
      if (url) return url.shortUrl;

      //if it doesn't exist, shorten it
      const shortUrl = `${baseURL}/${urlCode}`;

      //add the new record to the database
      url = this.repo.create({
        urlCode,
        longUrl,
        shortUrl,
      });

      this.repo.save(url);
      return url.shortUrl;
    } catch (error) {
      console.log(error);
      throw new UnprocessableEntityException('Server Error');
    }

위의 코드 블록에서 longUrlurl 개체에서 해체되었습니다. 그런 다음 isURL 메서드를 사용하여 longUrl이 유효한 URL인지 확인합니다.

urlCodenanoid를 사용하여 생성됩니다. 기본적으로 nanoid는 21자의 고유한 문자열을 생성합니다. 원하는 길이를 인수로 전달하여 기본 동작을 재정의합니다. 이 경우 URL이 가능한 한 짧기를 원하기 때문에 값 10을 전달합니다.

그런 다음 기본 URL이 정의됩니다. 기본 URL은 웹 사이트 주소의 일관된 루트입니다. 개발 중에는 로컬 호스트 서버입니다. 프로덕션에서는 도메인 이름입니다. 이 자습서의 샘플 코드는 localhost를 사용합니다.

try-catch 블록은 오류 처리를 위해 데이터베이스와 상호 작용하는 모든 코드를 수용합니다.

URL을 두 번 단축하면 데이터가 중복될 수 있으므로 URL이 존재하는지 확인하기 위해 데이터베이스에서 찾기 쿼리가 실행됩니다. 존재하는 경우 해당 shortUrl이 반환됩니다. 그렇지 않으면 코드가 진행되어 단축됩니다. 데이터베이스에 URL 레코드가 없으면 baseURLurlCode를 연결하여 짧은 URL을 만듭니다.

그런 다음 url 엔터티 인스턴스가 urlCode, longUrlshortUrl로 생성됩니다. url 인스턴스는 repo에서 save 메서드를 호출하고 인스턴스를 인수로 전달하여 데이터베이스에 저장됩니다. 그런 다음 shortUrl이 반환됩니다.

마지막으로 오류가 발생하면 catch 블록의 콘솔에 오류가 기록되고 UnprocessableEntityException 메시지가 발생합니다.

이제 url.service.ts 파일은 다음과 같이 표시됩니다.

import {
  BadRequestException,
  Injectable,
  NotFoundException,
  UnprocessableEntityException,
} from '@nestjs/common';
import { Repository } from 'typeorm';
import { InjectRepository } from '@nestjs/typeorm';
import { Url } from './url.entity';
import { ShortenURLDto } from './dtos/url.dto';
import { nanoid } from 'nanoid';
import { isURL } from 'class-validator';

@Injectable()
export class UrlService {
  constructor(
    @InjectRepository(Url)
    private repo: Repository<Url>,
  ) {}

  async shortenUrl(url: ShortenURLDto) {
    const { longUrl } = url;

    //checks if longurl is a valid URL
    if (!isURL(longUrl)) {
      throw new BadRequestException('String Must be a Valid URL');
    }

    const urlCode = nanoid(10);
    const baseURL = 'http://localhost:3000';

    try {
      //check if the URL has already been shortened
      let url = await this.repo.findOneBy({ longUrl });
      //return it if it exists
      if (url) return url.shortUrl;

      //if it doesn't exist, shorten it
      const shortUrl = `${baseURL}/${urlCode}`;

      //add the new record to the database
      url = this.repo.create({
        urlCode,
        longUrl,
        shortUrl,
      });

      this.repo.save(url);
      return url.shortUrl;
    } catch (error) {
      console.log(error);
      throw new UnprocessableEntityException('Server Error');
    }
  }
}

파일을 저장합니다.

여기에서 URL 단축 논리의 첫 번째 부분을 설정합니다. 다음으로 서비스에서 redirect 메서드를 구현합니다.

리디렉션 방법 만들기

redirect 메서드에는 사용자를 긴 URL로 리디렉션하는 로직이 포함됩니다.

여전히 src/url/url/service.ts 파일에서 UrlService 클래스 하단에 다음 코드를 추가하여 리디렉션을 구현합니다. 방법:

...
  async redirect(urlCode: string) {
    try {
      const url = await this.repo.findOneBy({ urlCode });
      if (url) return url;
    } catch (error) {
      console.log(error);
      throw new NotFoundException('Resource Not Found');
    }
  }

redirect 메서드는 urlCode를 인수로 사용하고 일치하는 urlCode를 사용하여 데이터베이스에서 리소스를 찾으려고 시도합니다. 리소스가 존재하면 리소스를 반환합니다. 그렇지 않으면 NotFoundException 오류가 발생합니다.

완료된 url.service.ts 파일은 이제 다음과 같습니다.

import {
  BadRequestException,
  Injectable,
  NotFoundException,
  UnprocessableEntityException,
} from '@nestjs/common';
import { Repository } from 'typeorm';
import { InjectRepository } from '@nestjs/typeorm';
import { Url } from './url.entity';
import { ShortenURLDto } from './dtos/url.dto';
import { nanoid } from 'nanoid';
import { isURL } from 'class-validator';

@Injectable()
export class UrlService {
  constructor(
    @InjectRepository(Url)
    private repo: Repository<Url>,
  ) {}

  async shortenUrl(url: ShortenURLDto) {
    const { longUrl } = url;

    //checks if longurl is a valid URL
    if (!isURL(longUrl)) {
      throw new BadRequestException('String Must be a Valid URL');
    }

    const urlCode = nanoid(10);
    const baseURL = 'http://localhost:3000';

    try {
      //check if the URL has already been shortened
      let url = await this.repo.findOneBy({ longUrl });
      //return it if it exists
      if (url) return url.shortUrl;

      //if it doesn't exist, shorten it
      const shortUrl = `${baseURL}/${urlCode}`;

      //add the new record to the database
      url = this.repo.create({
        urlCode,
        longUrl,
        shortUrl,
      });

      this.repo.save(url);
      return url.shortUrl;
    } catch (error) {
      console.log(error);
      throw new UnprocessableEntityException('Server Error');
    }
  }

  async redirect(urlCode: string) {
    try {
      const url = await this.repo.findOneBy({ urlCode });
      if (url) return url;
    } catch (error) {
      console.log(error);
      throw new NotFoundException('Resource Not Found');
    }
  }
}

파일을 저장하고 닫습니다.

URL 단축 논리는 이제 URL을 단축하는 방법과 단축된 URL을 원래 URL로 리디렉션하는 방법의 두 가지 방법으로 완성됩니다.

다음 단계에서는 컨트롤러 클래스에서 이 두 메서드에 대한 경로 처리기를 구현합니다.

4단계 - 컨트롤러 로직 구현

이 단계에서는 단축 요청을 처리하는 POST 경로 처리기와 리디렉션 요청을 처리하는 GET 경로 처리기의 두 가지 경로 처리기를 만듭니다.

컨트롤러 클래스에서 경로를 구현하기 전에 컨트롤러에서 서비스를 사용할 수 있도록 해야 합니다.

먼저 src/url/url.controller.ts 파일을 엽니다.

  1. nano src/url/url.controller.ts

강조 표시된 줄을 파일에 추가합니다.

import { Controller } from '@nestjs/common';
import { UrlService } from './url.service';

@Controller('url')
export class UrlController {
  constructor(private service: UrlService) {}
}

먼저 ./url.service에서 UrlService를 가져옵니다. 그런 다음 컨트롤러 클래스에서 생성자를 선언하고 전용 변수인 service를 매개 변수로 초기화합니다. 서비스UrlService 유형을 할당합니다.

Controller 데코레이터는 현재 문자열 url을 인수로 가지고 있습니다. 즉, 컨트롤러는 localhost/3000/url/에 대한 요청만 처리합니다. 경로. 단축된 URL에는 기본 URL(localhost:3000)과 URL 코드(wyt4_uyP-Il)가 포함되어 있기 때문에 이 동작은 URL 단축 논리에 버그를 도입합니다. 새 URL(localhost:3000/wyt4_uyP-Il)을 구성합니다. 따라서 이 컨트롤러는 요청을 처리할 수 없으며 단축 링크는 404 찾을 수 없음 오류를 반환합니다. 이 문제를 해결하려면 Controller 데코레이터에서 url 인수를 제거하고 각 핸들러에 대한 개별 경로를 구현하십시오.

url 인수를 제거한 후 UrlController는 다음과 같이 표시됩니다.

@Controller()
export class UrlController {
  constructor(private service: UrlService) {}
}

여전히 src/url/url.controller.ts 파일에서 강조 표시된 항목을 import 문에 추가합니다.

import { Body, Controller, Get, Param, Post, Res } from '@nestjs/common';
import { UrlService } from './url.service';
import { ShortenURLDto } from './dtos/url.dto';

Body, Get, Param, PostRes@nestjs/common 및 ./dtos/url.dtoShortenURLDto. 데코레이터는 이 파일에 추가할 때 추가로 정의됩니다.

그런 다음 constructor 아래의 UrlController에 다음 줄을 추가하여 POST 경로 처리기를 정의합니다.

...
  @Post('shorten')
  shortenUrl(
    @Body()
    url: ShortenURLDto,
  ) {
    return this.service.shortenUrl(url);
  }

ShortenURLDto 유형의 url 인수를 사용하는 shortenUrl 메서드를 만듭니다. urlBody 데코레이터로 주석을 달아 요청 객체에서 본문 객체를 추출하고 해당 값으로 url 변수를 채웁니다.

그런 다음 Post 데코레이터로 전체 메서드에 주석을 달고 shorten을 인수로 전달합니다. 이 처리기는 localhost:/shorten에 대한 모든 Post 요청을 처리합니다. 그런 다음 service에서 shortenUrl 메서드를 호출하고 url을 인수로 전달합니다.

다음으로 POST 경로 아래에 다음 줄을 추가하여 리디렉션을 위한 GET 경로 핸들러를 정의합니다.

...
  @Get(':code')
  async redirect(
    @Res() res,
    @Param('code')
    code: string,
  ) {
    const url = await this.service.redirect(code);

    return res.redirect(url.longUrl);
  }

Res 데코레이터로 주석이 달린 resParam 데코레이터. Res 데코레이터는 주석을 추가한 클래스를 Express 응답 개체로 변환하여 Express redirect 메서드와 같은 라이브러리별 명령을 사용할 수 있도록 합니다. Param 데코레이터는 req 개체에서 params 속성을 추출하고 장식된 매개 변수를 해당 값으로 채웁니다.

Get 데코레이터로 redirect 메서드에 주석을 달고 와일드카드 매개변수 :code를 전달합니다. 그런 다음 codeParam 데코레이터에 인수로 전달합니다.

그런 다음 service에서 redirect 메서드를 호출하고 결과를 기다리고 url 변수에 저장합니다.

마지막으로 res.redirect()를 반환하고 url.longUrl을 인수로 전달합니다. 이 메서드는 localhost:/code 또는 단축 URL에 대한 GET 요청을 처리합니다.

이제 src/url/url.controller.ts 파일이 다음과 같이 표시됩니다.

import { Body, Controller, Get, Param, Post, Res } from '@nestjs/common';
import { UrlService } from './url.service';
import { ShortenURLDto } from './dtos/url.dto';

@Controller()
export class UrlController {
  constructor(private service: UrlService) {}

  @Post('shorten')
  shortenUrl(
    @Body()
    url: ShortenURLDto,
  ) {
    return this.service.shortenUrl(url);
  }

  @Get(':code')
  async redirect(
    @Res() res,
    @Param('code')
    code: string,
  ) {
    const url = await this.service.redirect(code);

    return res.redirect(url.longUrl);
  }
}

파일을 저장하고 닫습니다.

이제 POSTGET 경로 핸들러를 정의했으므로 URL 단축기가 완전히 작동합니다. 다음 단계에서는 이를 테스트합니다.

5단계 - URL Shortener 테스트

이 단계에서는 이전 단계에서 정의한 URL 단축기를 테스트합니다.

먼저 다음을 실행하여 애플리케이션을 시작합니다.

  1. npm run start

다음 출력이 표시됩니다.

Output
[Nest] 12640 - 06/08/2022, 16:20:04 LOG [NestFactory] Starting Nest application... [Nest] 12640 - 06/08/2022, 16:20:07 LOG [InstanceLoader] AppModule dependencies initialized +2942ms [Nest] 12640 - 06/08/2022, 16:20:07 LOG [InstanceLoader] TypeOrmModule dependencies initialized +1ms [Nest] 12640 - 06/08/2022, 16:20:08 LOG [InstanceLoader] TypeOrmCoreModule dependencies initialized +257ms [Nest] 12640 - 06/08/2022, 16:20:08 LOG [InstanceLoader] TypeOrmModule dependencies initialized +2ms [Nest] 12640 - 06/08/2022, 16:20:08 LOG [InstanceLoader] UrlModule dependencies initialized +4ms [Nest] 12640 - 06/08/2022, 16:20:08 LOG [RoutesResolver] UrlController {/}: +68ms [Nest] 12640 - 06/08/2022, 16:20:08 LOG [RouterExplorer] Mapped {/shorten, POST} route +7ms [Nest] 12640 - 06/08/2022, 16:20:08 LOG [RouterExplorer] Mapped {/:code, GET} route +2ms [Nest] 12640 - 06/08/2022, 16:20:08 LOG [NestApplication] Nest application successfully started +7ms

새 터미널을 열어 curl 또는 원하는 API 테스트 도구를 사용하여 http://localhost:3000/shorten에 대한 POST 요청을 만듭니다. 아래 데이터 또는 귀하가 선택한 데이터. curl 사용에 대한 자세한 내용은 이 자습서의 설명을 참조하십시오.

이 명령을 실행하여 샘플 POST 요청을 만듭니다.

  1. curl -d "{\"longUrl\":\"http://llanfairpwllgwyngyllgogerychwyrndrobwllllantysiliogogogoch.co.uk\"}" -H "Content-Type: application/json" http://localhost:3000/shorten

-d 플래그는 HTTP POST 요청 데이터를 등록하고 -H 플래그는 HTTP에 대한 헤더를 설정합니다. 요구. 이 명령을 실행하면 긴 URL이 애플리케이션으로 전송되고 단축 URL이 반환됩니다.

다음 예와 같은 짧은 URL을 응답으로 받게 됩니다.

http://localhost:3000/MWBNHDiloW

마지막으로 짧은 URL을 복사하고 링크를 브라우저에 붙여넣습니다. 그런 다음 ENTER를 누르십시오. 원래 리소스로 리디렉션됩니다.

결론

이 기사에서는 NestJS로 URL 단축기를 만들었습니다. Github를 추가하면.

NestJS는 유형 안전성 및 아키텍처를 제공하여 애플리케이션을 보다 안전하고 유지 관리 가능하며 확장 가능하게 만듭니다. 자세히 알아보려면 NestJS 공식 문서를 방문하세요.