인프런 커뮤니티 질문&답변
next.js 를 2대이상 실행하는 경우 제공하는 cache 기능들은 어떻게 되나요?
해결된 질문
작성
·
40
0
MSA 에서 BFF 로 활용하는 경우 클라우드 환경에서 성능과 관련하여 2~3대 이상 실행하는 경우 관련 기능들은 어떻게 동작하나요?
part2 렌더링 패러다임의 전환: 캐싱 혁명 (Caching Revolution)
part3 Next.js 16 서버 캐시와 TanStack Query의 동기화
또 이와 다른 주의 할 점이나 고려해야 하는 부분이 있나요?
답변 2
1
안녕하세요 icoon22님! 질문의 깊이를 보니 단순히 프레임워크의 기능적인 사용법을 넘어 운영체제의 메모리와 네트워크가 얽히는 분산 시스템의 본질까지 아주 깊게 고민하고 계신 것이 느껴지며 클론 코딩 수준을 벗어나 실제 프로덕션 레벨의 아키텍처를 설계하려는 모습이 정말 훌륭합니다.
Next.js를 마이크로서비스 아키텍처 환경에서 프론트엔드를 위한 백엔드 즉 BFF로 활용하며 트래픽을 감당하기 위해 클라우드 환경인 AWS ECS나 Kubernetes 등에서 서버를 두세 대 이상으로 스케일 아웃하여 늘리게 되면 가장 먼저 캐시의 파편화라는 문제에 직면하게 됩니다. 기본적으로 Next.js의 내장 데이터 캐시는 해당 서버가 떠 있는 파드나 인스턴스의 로컬 파일 시스템 및 메모리에 저장되는 방식으로 동작하기 때문입니다. 예를 들어 대규모 이커머스 서비스의 특가 이벤트 상황을 실무 시나리오로 가정해 보겠습니다. 사용자가 특정 상품의 상세 페이지를 요청하면 로드밸런서가 이 요청을 1번 서버로 보내고 1번 서버는 백엔드에서 데이터를 가져와 자신의 로컬에 캐싱합니다. 그런데 1분 뒤 다른 사용자가 똑같은 상품을 요청했을 때 라운드로빈 방식 등에 의해 2번 서버로 요청이 가면 2번 서버의 로컬에는 해당 캐시가 없으므로 또다시 백엔드 API를 찔러 데이터를 가져와야 합니다. 결과적으로 BFF를 도입하여 백엔드 부하를 줄이려 했음에도 인스턴스 개수만큼 백엔드 호출이 중복 발생하고 심지어 서버마다 갱신 주기가 어긋나 데이터 불일치까지 발생합니다.
현재 프로덕션 환경에서는 이 문제를 해결하기 위해 커스텀 캐시 핸들러를 설정하여 로컬 캐시가 아닌 Redis 같은 중앙 집중형 메모리 데이터베이스를 바라보도록 아키텍처를 변경해야 합니다. 이를 구현하기 위해 아래와 같이 캐시 핸들러 파일을 작성하게 됩니다.
// cache-handler.mjs (2026년 Next.js 분산 Redis 캐시 핸들러 아키텍처 예시)
import { CacheHandler } from '@neshca/cache-handler';
import createLruCache from '@neshca/cache-handler/local-lru';
import createRedisCache from '@neshca/cache-handler/redis-strings';
import { createClient } from 'redis';
CacheHandler.onCreation(async () => {
let client;
try {
// 다중 인스턴스가 공통으로 바라보는 Redis 클러스터에 연결합니다.
client = createClient({ url: process.env.REDIS_URL });
await client.connect();
} catch (error) {
console.warn('Redis 연결 실패, 로컬 캐시로 안전하게 폴백합니다.', error);
}
return {
handlers: [
client ? createRedisCache({ client }) : null,
createLruCache(), // 단일 진실 공급원인 Redis에 장애가 생겼을 때를 대비한 방어 로직입니다.
],
};
});
export default CacheHandler;
이렇게 구성하고 설정 파일에 핸들러를 등록하면 1번 서버가 Redis에 캐시를 구워두었을 때 2번 서버와 3번 서버도 Redis를 조회하여 단일 진실 공급원으로서의 캐시를 온전히 공유할 수 있게 됩니다. 이러한 캐시 아키텍처의 변화는 과거 단순한 데이터 전달 통로였던 BFF를 초고속 캐시 레이어로 진화시킨 캐싱 혁명과 맞닿아 있습니다. Next.js 16 환경에서는 React 19의 서버 컴포넌트와 결합된 데이터 캐시가 변경이 잦지 않은 백엔드의 응답을 BFF 단에서 완전히 정적 상태로 얼려버리며 revalidateTag 함수를 통해 수십 개의 마이크로서비스 중 데이터가 변경된 특정 서비스의 캐시만 무효화하는 세밀한 제어가 가능합니다.
그런데 서버에서 이렇게 적극적으로 캐싱을 하는 상황에서 클라이언트에도 TanStack Query라는 강력한 캐시 도구가 존재한다면 어느 캐시가 최신 데이터인가를 두고 충돌이 발생할 수밖에 없으므로 이 둘의 역할을 명확히 분리하고 동기화하는 것이 핵심입니다. 사용자가 좋아요를 누르거나 리뷰를 수정하는 등의 상태 변경 액션을 취할 때 서버의 캐시와 클라이언트의 캐시를 순차적으로 날려주는 파이프라인을 구축해야 이전 데이터가 다시 나타나는 고스트 데이터 버그를 막을 수 있습니다. 2026년 실무에서는 Next.js의 서버 액션과 TanStack Query의 v5 이상을 결합하여 이 동기화를 아래와 같이 완벽하게 처리합니다.
// 1. Server Action (actions/review.ts)
'use server';
import { revalidateTag } from 'next/cache';
export async function updateReviewAction(reviewId: string, content: string) {
// 마이크로서비스 백엔드의 리뷰 수정 API를 호출하여 실제 데이터를 변경합니다.
const res = await fetch(`http://api.internal/reviews/${reviewId}`, {
method: 'PUT',
body: JSON.stringify({ content }),
});
if (!res.ok) throw new Error('리뷰 수정 실패');
// Redis에 저장된 해당 분산 캐시를 즉시 무효화하여 모든 BFF 인스턴스가 최신 상태를 알게 합니다.
revalidateTag(`review-${reviewId}`);
return { success: true };
}
// 2. Client Component (components/ReviewEditor.tsx)
'use client';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { updateReviewAction } from '@/actions/review';
export default function ReviewEditor({ reviewId }: { reviewId: string }) {
const queryClient = useQueryClient();
const mutation = useMutation({
mutationFn: (content: string) => updateReviewAction(reviewId, content),
onSuccess: () => {
// 서버 측 Redis 캐시가 날아간 성공 응답을 받은 직후, 클라이언트의 캐시도 날려줍니다.
// 이를 통해 즉시 가장 최신의 서버 상태를 다시 Fetching 하여 완벽한 동기화를 이룹니다.
queryClient.invalidateQueries({ queryKey: ['review', reviewId] });
}
});
return (
// 사용자 UI 및 이벤트 핸들러 바인딩...
<button onClick={() => mutation.mutate('업데이트된 리뷰 내용')}>저장</button>
);
}
이 코드를 통해 서버 액션에서 서버 캐시를 먼저 무효화하고 성공 응답 후 클라이언트에서 TanStack Query의 캐시를 무효화하여 순차적으로 가장 최신화된 데이터를 다시 받아오게 만드는 것이 완벽한 동기화 파이프라인입니다. 추가적으로 다중 인스턴스 BFF 환경을 실제 클라우드에 구축하실 때 발생할 수 있는 장애 상황들을 고려하여 캐시 스탬피드 혹은 썬더링 허드라고 불리는 현상을 방지하는 것을 반드시 짚고 넘어가셔야 합니다. 무거운 통계 데이터 캐시가 만료된 찰나의 순간에 대규모 트래픽이 몰리면 3대의 BFF 인스턴스가 동시에 캐시가 없음을 인지하고 백엔드로 쿼리를 무수히 날려 데이터베이스가 뻗어버릴 수 있으므로 백그라운드에서 캐시를 갱신하는 패턴을 활용하고 Redis 분산 락 기술을 도입하여 여러 인스턴스 중 단 하나의 인스턴스만이 갱신 작업을 수행하도록 제어해야 합니다. 또한 세션과 인증 상태의 무상태성을 철저히 유지하여 유저가 1번 서버에서 로그인한 후 로드밸런서에 의해 2번 서버로 접속했을 때도 로그인 상태가 유지되도록 JWT 기반의 무상태 인증을 구현하거나 세션 스토어 자체를 별도의 Redis 클러스터로 분리하여 관리해야 세션 단절 이슈를 막을 수 있습니다. 마지막으로 수많은 마이크로서비스가 얽힌 환경에서는 유저가 에러를 겪었을 때 어느 구간에서 터진 것인지 단순 로그로는 추적이 불가능하므로 클라이언트 최초 요청 시 고유한 트레이스 ID를 헤더에 붙여서 보내고 BFF가 이를 로그에 남김과 동시에 백엔드 API로 전달하는 분산 트레이싱 릴레이 시스템을 구축해야만 모니터링 도구에서 전체 요청의 흐름과 병목 구간을 시각적으로 한눈에 파악할 수 있게 됩니다.
이러한 아키텍처적인 고민을 더욱 깊이 있게 발전시키기 위해 앞으로 몇 가지 컴퓨터 공학의 근본적인 주제들을 추가로 공부해보시는 것도 큰 도움이 될 것 같습니다. 웹 프레임워크의 화려한 기능 이면에 있는 운영체제 수준의 스레드 관리나 네트워크 프로토콜의 본질을 파고들면 시야가 한층 넓어집니다. 예를 들어 마이크로서비스 간의 통신에서 REST를 넘어선 gRPC의 원리나, 분산 환경에서의 데이터베이스 트랜잭션 동기화 모델을 학습하시면 지금 직면한 문제들을 훨씬 더 구조적으로 해결하실 수 있습니다. 나아가 최근 급부상하는 인공지능과 대규모 언어 모델을 이러한 인프라에 어떻게 접목할지 고민해보시는 것도 추천해 드립니다. 가령 파인튜닝된 경량 모델을 활용해 BFF 레이어에서 들어오는 트래픽의 의도를 지능적으로 라우팅하거나, 모델 예측을 통해 캐시 만료 시점을 동적으로 조절하는 등 차세대 엔지니어링의 아주 흥미로운 가능성을 탐구해보실 수 있을 것입니다.
icoon22님 이처럼 최신 프레임워크의 편리한 마법 뒤에는 탄탄한 컴퓨터 공학적 기초가 자리 잡고 있습니다. 지금처럼 근본적인 동작 원리와 아키텍처의 한계를 묻고 탐구하는 학습 태도를 유지하신다면 어떤 복잡한 대규모 환경에서도 흔들리지 않는 견고한 프로덕션 시스템을 설계하실 수 있을 것입니다. 파이팅입니다!
0
안녕하세요, 인프런 AI 인턴입니다. icoon22님.
Next.js를 다중 인스턴스(MSA) 환경에서 이용하실 때, 캐싱 전략 및 성능 최적화와 관련된 몇 가지 주의점이 있습니다. 특히 클라우드 환경에서 Next.js를 BFF(Backends for Frontends)로 활용할 경우, 효율적인 캐싱과 데이터 동기화는 매우 중요합니다.
캐싱 및 성능 관련 전략
Next.js의 서버 캐시: Next.js 16부터 도입된 서버 캐시는 API 응답이나 정적 콘텐츠를 캐싱하여, 서버의 부하를 줄이고 응답 속도를 높이는 데 기여합니다. 이를 적절히 활용하면 다중 인스턴스 환경에서도 동일한 데이터를 빠르게 제공할 수 있습니다.
클라이언트-서버 동기화(TanStack Query): 서버와 클라이언트 간의 데이터 일관성을 유지하기 위해 TanStack Query와 같은 라이브러리를 사용할 수 있습니다. 이는 클라이언트 쪽에서 데이터를 캐싱하고 관리하여, 서버와의 동기화 문제를 해결하는 데 도움을 줍니다.
로드 밸런싱 및 상태 공유: MSA 환경에서 여러 인스턴스가 존재할 경우, 로드 밸런서를 통해 트래픽을 분산시키고 상태 정보를 서버 간에 공유할 필요가 있습니다. Redis와 같은 인메모리 데이터베이스를 사용하여 세션을 공유하거나 데이터를 중앙 집중식으로 캐싱하는 방법을 고려할 수 있습니다.
주의 및 고려 사항
캐시 일관성: 다중 서버 환경에서는 캐시 일관성을 유지하는 것이 중요합니다. 특정한 시간이나 이벤트를 기준으로 캐시를 무효화하여 모든 인스턴스가 최신 데이터를 갖도록 설정해야 합니다.
데이터 갱신 빈도: 빠르게 변화하는 데이터의 경우, 캐시의 TTL(Time-To-Live)을 적절히 설정하여 구체적인 업데이트 주기를 고려해야 합니다.
보안 및 데이터 보호: 데이터가 클라우드 서버 간에 이동할 때, 보안을 철저히 유지해야 합니다. TLS/SSL로 데이터 전송을 보호하고, 민감한 데이터에 대한 적절한 암호화 조치를 취하는 것이 중요합니다.
더 자세한 정보는 강의 내 Part 2 렌더링 패러다임의 전환: 캐싱 혁명 (Caching Revolution) 및 Part 3 Next.js 16 서버 캐시와 TanStack Query의 동기화 내용을 참고하시면 큰 도움이 되실 것입니다.
저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다.
현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏
추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.




