CSR Cache & Next.js

브라우저 cache는 Core의 createCsrCache로 사용하고, Next.js 서버 Data Cache가 필요할 때만 /next의 callApiSsrCache를 추가합니다.


cache wrapper도 commonService의 일부

이 페이지의 callApiClientCache/callApiServerCache 코드는 프로젝트 공용 HTTP adapter의 cache 부분만 떼어 설명합니다. generated service가 이 함수를 import하도록 구성할 수 있으므로, 전체 rxjsHttpService.ts/commonService.ts 구조를 먼저 보면 각 wrapper의 위치와 역할이 더 명확합니다.

CSR cache (클라이언트 cache)

createCsrCache<CacheName>()는 callApiCsrCache(callApiFn, serviceArgs, cacheOptions)와 removeCsrCache(cacheName)를 제공합니다.

ts
import {
  createCsrCache,
  type ServiceArguments,
} from "@byeolnaerim/typed-rx-http";
import type { paths as Paths } from "./@types/ApiTypes";

type CacheNames = "adminList" | "profile";

const csrCache = createCsrCache<CacheNames>();

export const callApiClientCache = <R>(
  args: ServiceArguments<Paths, any, any, R>,
  cache: {
    cacheName: CacheNames;
    cacheTime: number;
    cacheSize?: number;
  },
) => csrCache.callApiCsrCache(
  client.callApi,
  args as any,
  cache as any,
);

csrCache.removeCsrCache("adminList");
csrCache.removeCsrCache("any-prefix");

서버 환경에서는 CSR cache map을 만들지 않고 원래 callApi를 그대로 호출합니다. 브라우저에서는 같은 cache key의 Observable 결과를 shareReplay로 공유합니다.

Next.js adapter: callApiSsrCache

Next.js의 next/cache(unstable_cache)를 사용하는 SSR helper입니다. GET이면서 cacheTime > 0이면 force-cache + revalidate를 사용하고, 그 외 요청은 no-store로 실행합니다.

  • headersProvider로 요청별 Cookie/Authorization을 주입할 수 있습니다.
  • 401에서 onServer401가 있으면 실행합니다.
ts
import type { paths as Paths } from "./@types/ApiTypes";
import { firstValueFrom } from "rxjs";
import {
  callApiSsrCache,
  redirectToUnauthorizedOnServer401,
} from "@byeolnaerim/typed-rx-http/next";

const apiUrl = process.env.NEXT_PUBLIC_API_URL!;

const headersProvider = async () => {
  const { cookies } = await import("next/headers");
  const cookieStore = await cookies();

  const cookieHeader = cookieStore
    .getAll()
    .map((c) => `${c.name}=${c.value}`)
    .join("; ");

  const accessToken = cookieStore.get("accessToken")?.value;

  return {
    ...(cookieHeader ? { Cookie: cookieHeader } : {}),
    ...(accessToken
      ? { Authorization: `Bearer ${accessToken}` }
      : {}),
  };
};

const obs$ = callApiSsrCache<Paths, { ok: true }, "adminList">({
  baseUrl: apiUrl,
  serviceArguments: { url: "/api/secure", method: "get" },
  cacheForService: { cacheName: "adminList", cacheTime: 10_000 },
  headersProvider,
  onServer401: redirectToUnauthorizedOnServer401,
});

const res = await firstValueFrom(obs$);

사용자별 데이터는 cache하지 않기

브라우저/서버를 하나의 프로젝트 wrapper로 묶는 경우

공용 service에서 브라우저는 createCsrCache, 서버는 callApiSsrCache로 분기하도록 묶을 수도 있습니다. 이것은 프로젝트 구성 패턴이며 Core 자체의 필수 구조는 아닙니다.

commonService.ts
ts
const csrCache = createCsrCache<"memberList" | "profile">();

export const callApiClientCache = (
  serviceArguments,
  cacheForService,
) => csrCache.callApiCsrCache(
  callApi,
  serviceArguments,
  cacheForService,
);

export const removeCsrCache = csrCache.removeCsrCache;
commonService.ts
ts
export const callApiServerCache = (serviceArguments, cacheForService) => {
  if (typeof window !== "undefined") {
    return callApiClientCache(serviceArguments, cacheForService);
  }

  return from(
    import("@byeolnaerim/typed-rx-http/next"),
  ).pipe(
    mergeMap(({ callApiSsrCache }) =>
      callApiSsrCache({
        baseUrl: process.env.NEXT_PUBLIC_API_URL || "",
        serviceArguments,
        cacheForService,
        headersProvider,
      }),
    ),
  );
};

자동 생성 service에서 cache wrapper를 사용할 때도 cache 기능이 필요한 endpoint에만 이 wrapper를 연결하고, 나머지는 일반 callApi를 그대로 사용합니다.

© 2026 Byeolnaerim. All rights reserved.소개개인정보처리방침