Communitygithub.com

Nuvraxis/nuvraxis-plugins

Nuvraxis에서 Next.js 16 App Router 애플리케이션을 위한 프로덕션 규칙 — 서버/클라이언트 컴포넌트 경계, 캐시 컴포넌트("use cache", cacheLife, cacheTag, updateTag), 서버 액션, 라우트 핸들러, 환경 변수 처리, 계측(instrumentation), output standalone, /api/health 라우트를 포함합니다. package.json에 "next"가 있고, app/ 또는 src/app/ 디렉토리, next.config.ts, proxy.ts, instrumentation.ts, 또는 page.tsx, layout.tsx, route.ts, loading.tsx, error.tsx 파일이 있는 프로젝트에 사용됩니다.

nuvraxis-plugins란 무엇인가요?

이 스킬은 Nuvraxis 팀이 Next.js 16 App Router 프로젝트를 프로덕션 환경에서 일관되고 안전하게 구축 및 배포하기 위한 구체적인 코딩 규칙과 모범 사례를 제공합니다. Next.js 16.4, React 19.3, Turbopack 및 Cache Components(`cacheComponents: true`, `partialPrefetching: true`)를 기본 설정으로 하여 서버 컴포넌트를 우선 사용하고, `"use cache"`와 `cacheLife`, `cacheTag`를 통한 명시적 캐싱, 서버 액션의 철저한 입력 검증 및 권한 확인, 프리렌더링 문제 방지를 위한 `<Suspense>` 경계 사용, 환경 변수 관리(서버 전용 `process.env`와 빌드 시 고정되는 `NEXT_PUBLIC_*` 구분), `instrumentation.ts`를 통한 OpenTelemetry 기반 관측성, 그리고 `HEALTHCHECK`를 위한 `/api/health` 라우트 필수 제공 등을 강조합니다. 또한 `output: "standalone"` 유지, `import "server-only"`를 통한 민감 정보 보호, `after()`를 사용한 응답 지연 없는 백그라운드 작업 처리, 타입 안전한 라우트 사용(`typedRoutes: true`), 번들 크기 분석 등을 규칙으로 명시하여 안정적이고 유지보수 가능한 Next.js 애플리케이션 개발을 지원합니다.

지원 대상✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/Nuvraxis/nuvraxis-plugins/tree/HEAD/plugins/nuvraxis-frontend/skills/nextjs-production

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

문서

Next.js 프로덕션 사용 가이드

Next.js 16 App Router 애플리케이션의 모든 코드에 사용됩니다. 보안 관련 규칙(액션 내 인증, CSP, 헤더, taint)은 nextjs-security에, 컨테이너 규칙은 docker-nextjs에 있습니다. Claude Code에 내장된 next-best-practices 스킬은 일반적인 파일 규칙, 메타데이터, 이미지 및 폰트 최적화를 자세히 다루며, 이 스킬은 Nuvraxis의 구축 및 배포 방식에 특화된 내용만 추가합니다.

기본 설정: Next 16.4, React 19.3, Turbopack (dev 및 build의 기본값), cacheComponents: true (부분 프리페칭 활성화), output: "standalone", typedRoutes: true. 템플릿의 next.config.ts가 최종 진리(Source of Truth)입니다.

규칙

  1. 기본적으로 서버 컴포넌트; "use client"는 상태, 효과(effect), 브라우저 API 또는 이벤트 핸들러에만 사용합니다. 지시어는 필요한 가장 작은 리프(leat) 컴포넌트에 배치하세요. nuvui를 임포트한다고 해서 파일에 추가하지 마십시오. nuvui의 상태ful 부분은 자체 지시어를 가지고 있습니다 (nuvui 스킬의 rules/server-components.md 참고).

    // 잘못된 예: 하나의 핸들러 때문에 전체 페이지가 클라이언트가 됨
    "use client";
    export default function Page() { … <Select onValueChange={…} /> … }
    
    // 올바른 예: 페이지는 서버에 남아 있고, 핸들러는 리프에 위치
    export default async function Page() {
      const people = await getPeople();
      return <PeopleFilter people={people} />; // people-filter.tsx에 "use client" 있음
    }
    

    클라이언트 컴포넌트로 전달되는 props는 직렬화 가능해야 합니다: 함수(서버 액션 제외)나 클래스 인스턴스는 전달할 수 없습니다.

  2. 데이터가 필요한 컴포넌트에서 서버 측에서 fetch 하세요. useEffect를 이용한 fetching이나, 페이지 자체에 데이터를 공급하기 위한 라우트 핸들러는 사용하지 마십시오. 요청별 중복 제거는 React cache()를 사용하고, 요청 간 캐싱은 규칙 3을 따르세요.

  3. "use cache" + cacheLife + cacheTag로 캐싱하고, 그 외에는 모두 동적(dynamic)입니다. Cache Components에서는 명시하지 않으면 아무것도 캐싱되지 않으며, fetch는 암시적 캐시를 갖지 않습니다.

    import { cacheLife, cacheTag } from "next/cache";
    
    export async function getInvoice(id: InvoiceId) {
      "use cache";
      cacheLife("minutes");
      cacheTag(`invoice:${id}`);
      return db.invoice.findUnique({ where: { id } });
    }
    

    이전의 세그먼트 설정(export const dynamic, revalidate, fetchCache)은 사용하지 마십시오. Cache Components에서 오류가 발생합니다. 인수와 클로저로 감싸진 값은 캐시 키의 일부가 되므로, 전체 요청 객체가 아닌 ID를 전달하세요. 자세한 내용, 프로파일 및 "use cache: private"에 대해서는 참조/캐싱.md를 참고하세요.

  4. 서버 액션에서는 updateTag로, 그 외의 다른 곳에서는 revalidateTag(tag, "max")로 무효화합니다. updateTag(tag)는 자신의 쓰기를 읽을 수 있는(read-your-own-writes) 동작을 제공하며 서버 액션 내에서만 작동합니다. 웹훅이나 라우트 핸들러에서는 revalidateTag(tag, "max") (stale-while-revalidate) 또는 revalidateTag(tag, { expire: 0 })를 사용하세요. 인수가 하나인 revalidateTag(tag)는 더 이상 사용되지 않습니다(deprecated).

  5. 요청 시 데이터를 <Suspense>로 감싸지 않으면 빌드가 실패합니다. cookies(), headers(), searchParams, connection() 및 캐싱되지 않은 I/O가 Suspense 경계 밖에 있으면 "uncached or runtime data during prerendering" 오류로 프리렌더링이 중단됩니다. 스켈레톤 폴백과 함께 경계를 설정하고 캐싱하거나, (루트 레이아웃만, 규칙 14 참고) export const instant = false로 경로 차단을 표시하세요. params, searchParams, cookies(), headers()는 모두 비동기입니다: await 하세요.

  6. 모든 서버 액션은 자체적으로 입력을 검증하고 권한을 확인합니다. 서버 액션은 어떤 페이지에 작성되었든 공개 POST 엔드포인트입니다. typescript-production의 스키마(valibot)를 사용하여 FormData를 파싱한 다음, 권한을 확인하고, 변경(mutate)한 후, updateTag를 호출하세요. 예상되는 실패에는 타입이 지정된 결과를 반환하고, 버그에 대해서만 throw 하세요. 전체 규칙은 nextjs-security에 있습니다.

    "use server";
    export async function renameProject(_: State, form: FormData): Promise<State> {
      const session = await requireSession();                  // 없으면 401
      const input = v.safeParse(RenameProject, Object.fromEntries(form));
      if (!input.success) return { ok: false, errors: flatten(input.issues) };
      await assertCanEdit(session, input.output.projectId);    // 허용되지 않으면 403
      await db.project.update(…);
      updateTag(`project:${input.output.projectId}`);
      return { ok: true };
    }
    
  7. 라우트 핸들러는 다른 클라이언트(웹훅, 모바일, 상태 확인)용이며, 자체 페이지에서는 사용하지 마십시오. app/api/ 아래에 배치하세요. 액션과 마찬가지로 본문을 검증하고, Response.json(...)을 반환하며, 상태 코드를 의도적으로 설정하세요.

  8. 모든 앱은 동적이고 의존성 없는 GET /api/health를 가져야 합니다. Docker HEALTHCHECK와 쿠버네티스 프로브가 이 엔드포인트를 호출합니다. 데이터베이스에 접근하지 않고 응답해야 하므로 느린 의존성으로 인해 모든 파드가 재시작되는 것을 방지합니다. await connection()을 사용하여 정적 파일로 프리렌더링되는 것을 방지하세요. 템플릿의 src/app/api/health/route.ts에 있으며, proxy.ts는 /api/health를 매처에서 제외합니다.

  9. error.tsx, loading.tsx 및 not-found.tsx는 루트에, 그리고 UX가 다른 세그먼트별로 배치합니다. error.tsx는 클라이언트 컴포넌트이며, instrumentation.ts의 onRequestError를 통해 로깅하고, reset과 함께 재시도를 표시하며, 오류 메시지를 표시하지 마십시오. 빈 상태를 렌더링하는 대신 누락된 레코드에 대해 notFound()를 호출하세요.

  10. NEXT_PUBLIC_*은 빌드 시에 고정되며, 그 외의 모든 것은 서버에서 런타임에 읽힙니다. 하나의 이미지가 메인에서 프로덕션으로 변경 없이 승격되므로, NEXT_PUBLIC_ 값은 모든 환경에서 동일합니다. 빌드 타임 상수(앱 이름, 모든 환경에서 동일한 공개 Sentry DSN)만 여기에 넣으세요. 환경별 설정(API URL, 기능 플래그, 비밀)은 서버 측 process.env.X이며, 서버 컴포넌트나 액션에서 읽고 필요한 경우 클라이언트에 props로 전달됩니다. src/env.ts(valibot)에서 한 번 환경 변수를 검증하고 그곳에서 임포트하세요. next.config.ts에서 env 키를 절대 사용하지 마십시오: 클라이언트 번들에 인라인 처리됩니다.

  11. 서버 전용 모듈은 import "server-only"로 표시하세요. 비밀을 읽거나, DB와 통신하거나, 자격 증명이 있는 SDK를 임포트하는 모듈에 사용하세요. 클라이언트에서 이를 임포트하면 누출 대신 빌드가 실패합니다.

  12. after()는 응답을 지연시켜서는 안 되는 작업(감사 로그, 분석, 캐시 워밍업)에 사용합니다. after()를 호출하기 전에 cookies()/headers()를 읽고, 내부에서는 읽지 마세요. 동일한 프로세스에서 실행되므로 파드는 SIGTERM 유예 기간이 필요합니다 (차트에서 terminationGracePeriodSeconds 설정).

  13. src/instrumentation.ts를 사용하여 계측: registerOTel + onRequestError. @vercel/otel을 통한 OpenTelemetry, 런타임에 표준 OTEL_EXPORTER_OTLP_* 환경 변수로 구성된 익스포터. 참조/관측성.md 참고.

  14. output: "standalone"을 유지하고 워크스페이스 외부에서 절대 임포트하지 마십시오. Docker 러너는 .next/standalone, .next/static 및 public만 복사합니다. 모노레포에서 server.js는 .next/standalone/apps/<app>/server.js에 위치하며, 워크스페이스 루트는 pnpm-lock.yaml에서 찾으므로 outputFileTracingRoot나 turbopack.root를 설정하지 마십시오 (빌드에서 경고하지 않는 한). 자체 호스팅 세부 정보 (멀티 레플리카, 서버 액션 키, deploymentId): 참조/자체-호스팅.md 참고.

  15. 루트 레이아웃은 CSP 논스를 읽으므로 앱은 요청별로 렌더링됩니다. 템플릿의 proxy.ts는 논스 기반 CSP를 설정하고 src/app/layout.tsx에는 export const instant = false가 있습니다. 의도적인 절충입니다: 논스와 프리렌더링된 정적 셸은 공존할 수 없으며, 로그인된 비즈니스 앱의 경우 엄격한 CSP가 승리합니다. "use cache"를 사용한 데이터 캐싱은 여전히 작동합니다. nextjs-security 규칙 4를 읽지 않고 두 부분 중 하나를 제거하지 마십시오.

  16. 스타일: 글로벌 BEM SCSS, 컴포넌트에서 임포트합니다. Turbopack은 sass 패키지로 Sass를 컴파일합니다; sassOptions.loadPaths: ["src/styles"]는 어떤 깊이에서도 @use "abstracts"가 작동하게 합니다. ~ 임포트나 sassOptions.functions는 사용하지 마십시오. 규칙은 scss-bem에 있습니다.

  17. 이미지는 next/image를 통해, 폰트는 next/font를 통해 사용합니다. sharp는 최적화 도구로 Next와 함께 제공되며, 러너 이미지에 추가 패키지가 필요하지 않습니다. 원격 이미지는 정확한 호스트 이름과 함께 images.remotePatterns가 필요합니다.

  18. pnpm next analyze로 번들 크기를 확인하세요. (Turbopack의 분석기; @next/bundle-analyzer는 웹팩 전용입니다). 무거운 클라이언트 위젯(아래 접힌 부분의 nuvui 편집기, 지도, 차트)은 next/dynamic으로 지연 로드하세요.

  19. 타입이 지정된 라우트와 생성된 타입을 사용하세요. typedRoutes: true는 <Link href>를 확인합니다; PageProps<"/invoices/[id]"> 및 LayoutProps는 next typegen 이후 전역에서 사용 가능합니다 (next dev/next build에 의해 자동 실행). params 타입을 수동으로 작성하지 마십시오.

  20. next lint는 사용하지 마십시오. 16버전에서 제거되었습니다. Biome이 린트를 수행하고 (참조: biome), TypeScript는 패키지별로 tsc --noEmit으로 검사합니다.

검증

pnpm typecheck
pnpm lint
pnpm build                      # turbo run build: 프리렌더 오류, 타입 오류, Sass 오류 시 실패
pnpm --filter <app> start &     # 그런 다음:
curl -fsS localhost:3000/api/health

출처

Individual skills in this repo

This repo contains 4 individual skills — each has its own dedicated page.

Nuvraxis/nuvraxis-plugins

>- Biome 2.5 setup and rules for Nuvraxis monorepos — root biome.json with pinned $schema, VCS ignore, formatter settings, the recommended preset with a11y and the react/next domains, nursery policy, organize imports, overrides, nested per-package configs ("extends": "//"), biome check --write locally, biome ci in CI, VS Code settings, and migrating from ESLint/Prettier. Use when the repo has biome.json or biome.jsonc, @biomejs/biome in package.json, .eslintrc*/eslint.config.*/.prettierrc* to migrate, or when fixing lint and formatting errors.

Nuvraxis/nuvraxis-plugins

>- Dockerfile rules for Next.js standalone apps in a pnpm + Turborepo monorepo — multi-stage build (turbo prune → pnpm fetch/offline install → turbo build → minimal runner), pinned Node 26 Alpine by digest, checksum-verified pnpm binary, non-root uid 1001, read-only root filesystem with a writable .next/cache, HEALTHCHECK on /api/health, build args only for NEXT_PUBLIC_* values, BuildKit secrets and cache mounts, multi-arch, OCI labels and .dockerignore. Use when the repo has a Dockerfile, .dockerignore, compose file, or next.config with output "standalone", or when asked to containerise, shrink, harden or debug an image.

Nuvraxis/nuvraxis-plugins

>- GitHub Actions conventions for Nuvraxis Next.js apps — exactly three workflows: pr.yaml (every PR to any branch: Biome, stylelint, typecheck/test/build with turbo --affected, Docker build + health check, helm lint + kubeconform; publishes nothing), main.yaml (push to main: one multi-arch image to GHCR tagged sha-SHORTSHA and main, provenance + SBOM, gha cache), release.yaml (tag vX.Y.Z: verify on main, retag the existing image without rebuilding, package and push the Helm chart to OCI, GitHub Release with chart, digest and SBOM). Minimal permissions per job, GITHUB_TOKEN only, pinned action majors. Use when editing .github/workflows/*, adding a CI step, or debugging a failed run or release.

Nuvraxis/nuvraxis-plugins

>- Helm chart rules for Nuvraxis Next.js apps — one chart per app in charts/ (named after the app), published to oci://ghcr.io/nuvraxis/charts and synced by Argo CD; Chart.yaml version/appVersion set only by release.yaml; Deployment with startup/liveness/readiness probes on /api/health, non-root read-only security context, emptyDir for .next/cache, ConfigMap + existing Secret via envFrom, Service, Ingress with cert-manager, HPA, PDB, ServiceAccount, topology spread; no Helm hooks; helm lint --strict + kubeconform. Use when the repo has charts/, Chart.yaml, values*.yaml, templates/*.yaml with Helm syntax, or an Argo CD Application, or when asked to deploy, scale, expose or configure the app on Kubernetes.

관련 스킬