[Next.js 14] 셀프 호스팅 환경에서 주문형 재검증 구현 (ISR)

Eddy·2024년 10월 1일
1

Next.js

목록 보기
7/13
post-thumbnail

Next.js는 기본적으로 서버 컴포넌트를 활용하여 다양한 로직을 서버에서 수행합니다. 이는 여러 장점이 있지만, 동시에 서버에 부하를 증가시킬 수 있는 단점도 존재합니다. 이러한 문제를 방지하기 위해 캐싱 전략으로 SSG, SSR, ISR을 활용하고 있습니다. 최근 프로젝트에서는 Next.js의 시간형 재검증을 통해 ISR 기능을 도입했습니다. 더 나아가 웹 훅을 사용하여 페이지를 정적으로 생성하고, 백엔드에서 데이터가 업데이트될 때 주문형 재검증(On-demand Revalidation) 기능을 통해 캐시된 페이지를 무효화하고 최신 데이터를 반영하고자 했습니다.

우리 프로젝트는 AWS를 기반으로 운영되는 셀프 호스팅 환경으로, 여러 대의 인스턴스가 존재하여 캐시 공유와 일관성 유지에 문제가 발생할 수 있습니다. 이 블로그에서는 이러한 문제를 다루고 해결한 과정을 소개하겠습니다.

이전 블로그 포스팅 🌱 3.웹훅을 이용한 ISR(Incremental Static Regeneration) 구현하기에서 주문형 재검증 테스트 과정을 다뤘으며, 이 테스트는 로컬 및 개발 서버(한 대의 인스턴스 사용)에서 잘 작동하였습니다. 기본적인 사용법을 위해 이전 포스팅을 참고하시기 바랍니다.

1. 목표: 주문형 재검증을 통한 실시간 데이터 반영

Next.js의 ISR(Incremental Static Regeneration) 기능은 페이지를 정적으로 생성하면서 일정 시간마다 페이지를 재검증할 수 있는 기능을 제공합니다 이를 시간형 재검증이라 합니다. 그러나 주문형 재검증을 사용하면 일정 시간 동안 페이지를 재검증하지 않더라도, 특정 조건에 따라 서버에서 데이터를 다시 가져와 페이지를 업데이트하는 매우 유용한 기능입니다.

제가 구현하고 싶었던 플로우는 다음과 같습니다:

  1. 백엔드에서 데이터가 업데이트되면 웹훅을 통해 프론트엔드 서버에 알림을 보냅니다.
  2. Next.js 서버는 재검증 API를 호출하여 특정 페이지에 대한 캐시를 무효화하고 최신 데이터를 반영합니다.
  3. 이 과정에서 캐시가 무효화되면, 즉시 새로운 데이터로 페이지가 다시 렌더링됩니다.
    이 방식은 빠르게 데이터를 업데이트하고 싶을 때 매우 유용한 패턴입니다.

2. 문제 상황: 주문형 재검증에서 캐시 일관성 문제

Next.js에서 주문형 재검증을 사용하려면 백엔드 서버에서 프론트엔드 서버로 요청을 보내 캐시를 무효화합니다. 이 과정에서 여러 개의 EC2 인스턴스 또는 컨테이너로 이루어진 환경에서는 다음과 같은 문제가 발생할 수 있습니다:

  • 백엔드 서버에서 Next.js 서버로 요청을 보낼 때, 요청이 어느 인스턴스로 갈지 모릅니다. 로드 밸런서에 의해 랜덤으로 특정 인스턴스가 선택되기 때문입니다.
  • 특정 인스턴스에서만 캐시가 무효화되면 다른 인스턴스들은 여전히 오래된 캐시를 유지하게 됩니다. 이로 인해 캐시 불일치가 발생할 수 있습니다.

3. 해결책: 중앙화된 캐시 시스템 사용 (공유 캐시 도입)

이 문제를 해결하기 위해 중앙화된 캐시 시스템을 도입하기로 했습니다. 여러 인스턴스 또는 pod 간에 공유 캐시를 사용하는 방법입니다. AWS 환경에서 이 문제를 해결하기 위한 최적의 도구는 바로 Redis입니다. Redis는 빠르고 확장성이 좋은 인메모리 데이터베이스로, 여러 인스턴스가 동일한 캐시를 사용할 수 있도록 도와줍니다.
Redis를 통해 모든 EC2 인스턴스가 동일한 캐시 데이터에 접근하고, 어느 인스턴스에서든 캐시를 무효화하면 모든 인스턴스에서 일관된 최신 데이터를 반영할 수 있습니다. Nextjs에서도 셀프 호스팅시 Redis 사용을 권장하고 있습니다.


4. Redis를 사용한 캐시 핸들러 구현 과정

Next.js는 기본적으로 캐시 데이터를 디스크에 저장하는데, 이를 Redis와 같은 중앙화된 스토리지에 저장하도록 캐시 핸들러(CacheHandler)를 커스터마이징할 수 있습니다. 이를 위해 Redis 클라이언트인 ioredis를 사용해 Next.js의 캐시 핸들러를 설정했습니다.

4.1 next.config.js 설정

Next.js의 next.config.js 파일에서 캐시 핸들러를 설정하여 Redis와 통합할 수 있습니다. 기본 메모리 캐싱을 비활성화하고, Redis를 통한 캐싱만 사용하도록 설정합니다.

module.exports = {
  cacheHandler: require.resolve('./cache-handler.js'),
  cacheMaxMemorySize: 0, // 기본 메모리 캐싱 비활성화
};

4.2 Redis를 사용하는 cache-handler.js 구현

Redis를 활용하여 캐시 데이터를 저장하고 불러오는 기능을 구현합니다.


const Redis = require('ioredis');

// Redis 설정 (AWS Elasticache 같은 서비스 URL을 사용)
const redis = new Redis(process.env.REDIS_URL);

module.exports = class CacheHandler {
  constructor(options) {
    this.options = options;
  }

  // 캐시에서 데이터를 가져오는 메서드
  async get(key) {
    const data = await redis.get(key);
    if (data) {
      return JSON.parse(data);
    }
    return null;
  }

  // 캐시에 데이터를 저장하는 메서드
  async set(key, data, ctx) {
    const value = {
      value: data,
      lastModified: Date.now(),
      tags: ctx.tags,
    };
    await redis.set(key, JSON.stringify(value));
  }

  // 특정 태그에 대한 캐시를 무효화하는 메서드
  async revalidateTag(tags) {
    tags = [tags].flat();
    const keys = await redis.keys('*');
    for (const key of keys) {
      const cacheValue = await redis.get(key);
      if (cacheValue) {
        const { tags: cacheTags } = JSON.parse(cacheValue);
        if (cacheTags.some((tag) => tags.includes(tag))) {
          await redis.del(key);
        }
      }
    }
  }
};

이 구현을 통해 Redis를 사용하여 캐시 데이터가 저장되고, Next.js 애플리케이션의 모든 인스턴스에서 동일한 캐시 데이터를 공유할 수 있게 됩니다.


5. 결론

Next.js의 Incremental Static Regeneration을 활용한 주문형 재검증 기능은 성능과 최신 데이터 제공 측면에서 매우 강력한 도구입니다. 하지만, 셀프 호스팅 환경에서는 인스턴스나 컨테이너 간의 캐시 일관성 문제가 발생할 수 있습니다. 이 문제를 해결하기 위해 Redis와 같은 공유 캐시 시스템을 사용하여 모든 인스턴스에서 동일한 캐시를 활용하도록 설정할 수 있습니다.
이번 과정을 통해 주문형 재검증을 안정적으로 운영하는 방법과 캐시 일관성을 유지하는 해결 방안을 구현할 수 있었습니다. Redis와 같은 중앙화된 캐시 스토리지를 사용하는 것은 대규모 분산 환경에서 중요한 해결책이 될 수 있습니다.


https://github.com/vercel/next.js/tree/canary/examples/cache-handler-redis
https://nextjs.org/docs/app/building-your-application/deploying#caching-and-isr
https://vercel.com/docs/incremental-static-regeneration?utm_source=next-site&utm_medium=docs&utm_campaign=next-website#isr-pricing

0개의 댓글

관련 채용 정보