Skip to content

[BE] 크롤러 캐싱

강창한 edited this page Dec 14, 2023 · 3 revisions

🤷‍♂️ 왜 필요한가요?

외부 사이트를 크롤링하는 코드가 존재해요.

매 요청마다 사이트를 크롤링하면 속도도 느리고 해당 사이트의 서버에 부하를 줄 수 있어요.

이를 해소하고자 캐싱을 적용해요

🤷‍♂️ 어떻게 구현하나요?

  • NestJS에서 caching을 지원해요!

  • 제공하는 라이브러리를 설치해요

    • cache-manager
      • V4의 경우 TTL이 초 단위이지만, 최신 버전(V5)의 경우 TTL이 밀리초 단위입니다..!
    • @nestjs/cache-manager
  • AppModule에 CacheModule을 등록해요

    import { Module } from '@nestjs/common';
    import { CacheModule } from '@nestjs/cache-manager';
    import { AppController } from './app.controller';
    
    @Module({
      imports: [CacheModule.register()],
      controllers: [AppController],
    })
    • 등록할 때 여러 옵션을 넣을 수 있어요.
      • store: 저장소 설정
      • ttl: 저장하고 있을 시간
      • max: 최대로 저장할 데이터 수
      • isCacheableValue: 저장 가능한 데이터 타입을 지정
        • (value: any) => boolean;
      • isGlobal: 전역 등록 가능하도록 설정

🤷‍♂️ 어떻게 사용하나요?

직접 저장소를 사용하기

  • 캐시 저장소 가져오기

    constructor(@Inject(CACHE_MANAGER) private cacheManager: Cache) {}

  • 캐시에 저장하기

    this.cacheManager.set(key, value);

  • 캐시에서 데이터를 가져오기

    this.cacheManager.get(key);

    • 없으면 null이 반환돼요

Interceptors를 통해 특정 요청에 대한 캐싱을 자동화하기

  • Controller 전체에 적용하기

    @Controller()
    @UseInterceptors(CacheInterceptor)
    export class AppController {
      @Get()
      findAll(): string[] {
        return [];
      }
    }
  • 특정 API에만 적용하기

    @Controller()
    export class AppController {
      @Get()
      @UseInterceptors(CacheInterceptor)
      findAll(): string[] {
        return [];
      }
    }
  • 글로벌 설정 변경하기

    • 위에서 설정한 ttl 을 매 요청별로 커스텀 할 수 있어요.
      • @CacheTTL(20)

⇒ 현재 서비스는 해당 방식을 채택했어요. 우리 서버는 단일 서버이기에 클러스터링도 필요없고, 단순히 특정 요청에 대한 캐싱으로도 충분해요.

🤷‍♂️ 앞으로 더 고려할 점

  1. 캐시 저장소 변경

    현재는 메모리에 캐시하고 있어요.

    심지어 Redis와 같이 클러스터링을 지원하는 캐시메모리를 사용하지 않아요.

    • 캐시 최대 사이즈 계산

      현재 응답 데이터의 크기는 약 400B 정도로 나와요.

      • GET /api/v1/themes/1/timetable?date=2023-12-11T15%3A00%3A00.000Z

      • response

        [
          {
            "target_time": "10:30",
            "possible": true
          },
          {
            "target_time": "11:30",
            "possible": true
          },
          {
            "target_time": "12:30",
            "possible": true
          },
          {
            "target_time": "13:30",
            "possible": true
          },
          {
            "target_time": "14:30",
            "possible": true
          },
          {
            "target_time": "15:30",
            "possible": true
          },
          {
            "target_time": "16:30",
            "possible": true
          },
          {
            "target_time": "17:30",
            "possible": true
          },
          {
            "target_time": "18:30",
            "possible": true
          },
          {
            "target_time": "19:30",
            "possible": true
          }
        ]

      현재 제공 중인 테마의 수는 약 400개이고, 대부분의 지점에서 제공하는 예약은 14일 정도에요.

      그러면 아래와 같은 사이즈를 유추할 수 있어요!

      캐시 데이터의 최대 크기 = 응답 데이터 사이즈 * 테마 수 * 지원하는 날짜 수
      = 400B * 400 * 14
      = 2,240,000B
      = 2.24MB
      

      모든 데이터가 저장된다 하더라도 매우 적은 양이에요.

      데이터베이스로 저장이 필요할까 고민했지만 상관없어 보여요.

      추후 데이터 분석이 필요하다 판단되면 수정해요.

  2. 캐시의 key 값 변경

    Request를 기준으로 캐싱하고 있어서 당일 데이터의 경우 캐시가 적용되지 않아요.

    당일이 아닌 경우 해당 날짜의 00:00:00.000 으로 들어오지만, 당일의 경우 date 값이 현재 시간으로 들어오고 있어요.

    이것도 캐싱되기 위해서 dateString(YYYY-MM-DD)으로 변환된 key로 캐싱을 적용해요.

    GET /api/v1/themes/1/timetable?date=2023-12-11T15%3A00%3A00.000Z
  3. 수집한 정보를 모두 캐싱하기

    특정 사이트의 경우 한 테마만 조회하는 것이 불가능해서 해당 지점의 모든 테마 정보를 조회하고 있어요. 이 경우 요청으로 들어온 테마가 아니더라도 미리 캐시에 저장시켜요.

🤷‍♂️ 진행 상황

현재 대부분의 크롤러가 단일 테마의 조회만 지원하고 있어요.
유일하게 지원하는 넥스트에디션에 대해서만 한 번의 요청으로 지점 내 테마들을 모두 캐싱하도록 처리했어요. 테스트 결과 실제로 전송되는 요청 수를 줄일 수 있었습니다. 하지만, 부하테스트 결과 개선된 코드가 오히려 성능이 안나오는 이슈가 있었습니다.

추측하기론, 현재 코드는 캐시의 key를 변경하면서 데이터베이스를 무조건 한 번 접근한 후 반환하고 있습니다.
이로 인해서 데이터베이스 접근 오버헤드가 발생해서 시간이 되려 늦어진 것으로 판단됩니다.

Reference
Caching
https://docs.nestjs.com/techniques/caching
https://github.com/boostcampwm2023/web05-AlgoITNi/pull/113

Lock Festival 🔒

Rules

개발일지

Description

학습 노트

회의록

사전 회의
1주차 회의록
2주차 회의록
3주차 회의록
4주차 회의록
5주차 회의록
6주차 회의록

데일리 스크럼

1주차
2주차
3주차
4주차
5주차
6주차

회고록

1주차 회고록
2주차 회고록
3주차 회고록
4주차 회고록
5주차 회고록
6주차 회고록

스프린트

멘토링 일지

Clone this wiki locally