[nodejs] 웹툰 플랫폼 랭킹 순위 크롤링 해보기 - 3

protomothis·2023년 1월 16일
0
post-thumbnail

지난 이야기

랭킹 데이터를 가져오는 서비스는 완성이 되었고, 마지막으로 데이터를 엑셀화하는 부분까지 완성할 수 있었다.

다만 문제가 있었는데, dto를 작성하지 않았기 때문에 요청 전문에 대한 검증이 전혀 되지 않았다.

Dto가 뭔데?

Data Transfer Object를 말한다. 계층 간 데이터 교환을 하기 위해서 사용하는 객체로 보통 화면에서 유저가 입력한 정보를 가지고 서버에 요청하기 위해 사용한다.
이는 요청 전문에 대한 모델의 정의도 가능하지만 검증도 가능하다.

nodejs 환경에서는 이를 구현하기 위해 class-validator를 사용해보았다.
아래는 요청 전문의 일부를 예시로 옮겨보였다.

/**
기존에 픽코마의 메뉴와 카테고리를 정의한 모델들...

export enum PiccomaRankingMenus {
  manga = 'K',
  smartToon = 'S',
  novel = 'N',
}

export enum PiccomaSmartToonRankingCategories {
  common = 0,
  love = 1,
  fantasy = 2,
  drama = 3,
  daliy = 4,
  action = 5,
  sport = 6,
  mistery = 7,
  noir = 9,
  gourmet = 10,
}
*/
export class RequestPiccomaDto {
  // 필수 요소임을 IsNotEmpty 로 표현한다
  // 아래처럼 matches decorator를 사용해서 일치 여부를 검증할 수 있다
  // example: /^K|S|N/i
  @IsNotEmpty()
  @Matches(`^${Object.values(PiccomaRankingMenus).filter(v => typeof v !== "number").join('|')}$`, 'i')
  public menu: PiccomaRankingMenus;

  // IsEnum으로 enum key 체크도 가능하다
  @IsNotEmpty()
  @IsEnum(PiccomaSmartToonRankingCategories)
  public category: PiccomaSmartToonRankingCategories;

  @IsOptional()
  @IsNumberString()
  public length: number;
}

Controller 에서의 대응도 필요하다

기존에는 단순히 데이터 값을 요청하고 JSON으로 내려받았다면, 엑셀 파일로 응답하기 위해 설정이 필요하다.
마지막으로 응답 헤더에 컨텐츠 타입을 지정하고 이름 및 확장자를 지정해주면 된다.

class PiccomaController {
  public piccomaService = new PiccomaService();

  // region controller
  public getRankingData = async (req: Request, res: Response, next: NextFunction): Promise<void> => {
    try {
      const requestPiccoma: RequestPiccomaDto = req.query as any;
      const piccomaRankingList: ResponsePiccomaRankingData[] = await this.piccomaService.crawlData(requestPiccoma);
      const excel = await this.piccomaService.makeSheet(piccomaRankingList);
      // initalize service
      res
        .status(200)
        .setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
        .setHeader('Content-Disposition', `attachment; filename*=utf-8''${encodeURI('sample')}.xlsx`)
        .send(excel);
    } catch (error) {
      next(error);
    }
  };
  // endregion
}

마지막으로

실제 컨트롤러로 요청을 해보았더니 1.5mb 남짓의 엑셀 파일을 브라우저에서 다운로드 받을 수 있었다. 결과물은 의도했던대로 나왔지만, exceljs에서 기본으로 제공해주는 테마는... 너무 과한 감이 있다.
셀 배경은 제거하고 컬럼만 색을 유지하는 쪽으로 수정해야겠다.

스크린샷에는 보이지 않지만 지인이 넘겨준 번역 시트에 포함된 작품도 한글로 잘 번역해서 표시되는 걸 확인했다. 한글로 번역된 부분은 텍스트 색상을 다르게 하는 등의 강조 표시를 하는 것도 좋아보인다.

아직 완성이 된 것은 아니지만 현재까지의 결과물을 지인에게 보여주었을 때 상당히 만족스러운 반응을 보여주었다.

profile
frontend dev, home server lover.

0개의 댓글