기본 진입점
src/extensions/index.tsx는 프로젝트별 화면 확장의 진입점입니다. 앱 라우트는 @extensions alias로 이 파일을 직접 바라봅니다.
실제 모듈, 스킨, 레이아웃 등록은 src/extensions/registry.tsx에서 조립합니다. 각 모듈이나 스킨은 자기 폴더 안에 registry.tsx를 두고, 최상위 registry는 그것들을 import해서 배열에 넣는 방식이 좋습니다. 이렇게 해야 스킨이나 모듈 폴더를 다른 프로젝트에 그대로 옮겨도 다시 연결하기 쉽습니다.
import DefaultLayout from "./layouts/default/Layout";
import AuthLayout from "./layouts/authLayout/Layout";
import HomePage from "./pages/MainIntro";
export { DefaultLayout, AuthLayout, HomePage };위 예시는 화면 export 부분만 발췌한 것입니다. 기존 registry 조립 코드를 통째로 대체하지 않습니다. DefaultLayout을 사용하는 라우트·페이지는 해당 export의 레이아웃을 사용합니다. 현재 홈페이지 원본은 pages/MainIntro/routes/page.tsx에 있으며 src/app/page.tsx는 연결 파일입니다.
확장 디렉토리
src/extensions/index.tsx: 프로젝트 확장 진입점입니다.src/extensions/registry.tsx: 모듈, 스킨, 관리자 레이아웃, user 스킨을 조립합니다.src/extensions/styles/style.css: 프로젝트별 전역 스타일입니다.src/extensions/triggerRegistry.ts: 프로젝트별 트리거 핸들러와 trigger.json을 코어에 연결합니다.src/extensions/postCapabilities.ts: 게시판 스킨별 기능을 선언합니다.src/extensions/posts/[feature]/: 게시판 확장의 액션·스킨·registry·capability를 모읍니다.src/extensions/user/[skin]/: 회원 스킨과 오버라이딩을 보관합니다.src/extensions/[feature]/: Store, Service, Partners 같은 기능의 원본을 모읍니다.- 각 기능의
routes.json: Next.js 연결 경로와 export·정적 설정을 선언합니다. src/extensions/layouts/*: 프로젝트별 레이아웃을 보관합니다.
통합 Registry
코어 모듈과 extensions 모듈은 같은 registry 모양을 사용합니다. 코어의 기본 등록은 src/core/registry/coreRegistry.tsx에 모이고, 프로젝트 확장은 src/extensions/registry.tsx에 모입니다. 최종적으로 @extensions가 core registry와 extension registry를 합쳐서 관리자 메뉴, breadcrumb, 게시판 스킨, 게시판 레이아웃, 관리자 레이아웃, user 스킨을 내보냅니다.
최상위 registry는 조립만 담당합니다.
// src/extensions/registry.tsx
import { defineExtensionRegistry } from "@/core/registry/define";
import { shopModule } from "./shop/registry";
import { galleryPostSkin } from "./posts/gallery/registry";
import { customAdminLayout } from "./layouts/admin/registry";
export const registry = defineExtensionRegistry({
modules: [shopModule],
postSkins: [galleryPostSkin],
adminLayouts: [customAdminLayout],
});모듈은 자기 관리자 메뉴와 breadcrumb를 함께 들고 다닙니다.
// src/extensions/shop/registry.tsx
import { PackageSearch } from "lucide-react";
import { defineModule } from "@/core/registry/define";
export const shopModule = defineModule({
key: "shop",
label: "쇼핑몰",
description: "상품과 주문을 관리하는 프로젝트 확장 모듈입니다.",
admin: {
menu: {
id: "shop",
icon: <PackageSearch size={18} />,
label: "쇼핑몰 관리",
order: 80,
items: [
{ label: "상품 목록", href: "/admin/shop/products" },
{ label: "주문 목록", href: "/admin/shop/orders" },
],
},
breadcrumbs: {
shop: {
products: "PRODUCTS",
orders: "ORDERS",
},
},
},
});관리자 메뉴 항목은 id, label, icon, order를 기준으로 SideNav에 표시됩니다. href만 있으면 단일 메뉴가 되고, items가 있으면 접히는 메뉴가 됩니다. items의 href는 실제 Next.js 라우트와 일치해야 합니다.
src/extensions/orders/
actions/order.action.ts
actions/order.query.ts
admin/OrdersAdminPage.tsx
routes/admin/orders/page.tsx
routes/admin/orders/settings/page.tsx
api/orders/route.ts
prisma/schema/order.prisma
triggers/ 필요한 경우
routes.json
registry.tsx데이터 로직은 extension 내부 action/query에 둡니다. 화면 컴포넌트나 page에서 Prisma query를 직접 호출하지 않고, Page 또는 Component → Action → Query 흐름을 지킵니다. 관리자 전용 서버 액션은 이름 중간에 Admin을 넣고 Action으로 끝내는 규칙을 유지합니다.
registry가 자동으로 모든 extension 파일을 스캔하지는 않습니다. 대신 각 모듈이나 스킨 폴더의 registry.tsx를 src/extensions/registry.tsx에 import해서 등록합니다. 이 방식은 자동 스캔보다 명시적이고, 유료 모듈이나 스킨을 폴더 단위로 전달하기 쉽습니다.
라우트 연결 생성
Next.js는 src/app에서 라우트를 읽습니다. 구현은 기능 폴더에 보관하고, routes.json으로 연결 파일을 생성합니다. 아래는 새 orders 기능의 예시입니다.
[
{
"route": "src/app/(extensions)/admin/orders/page.tsx",
"implementation": "src/extensions/orders/routes/admin/orders/page.tsx",
"configs": ["export const dynamic = \"force-dynamic\";"],
"exports": ["default"]
},
{
"route": "src/app/api/orders/route.ts",
"implementation": "src/extensions/orders/api/orders/route.ts",
"configs": ["export const runtime = \"nodejs\";"],
"exports": ["GET", "POST"]
}
]실제 구현이 export하는 이름만 적습니다. generateStaticParams도 필요한 경우 포함합니다. configs의 설정은 연결 파일에 유지하고 원본에 중복 선언하지 않습니다. 기존 URL, 상위 layout 적용 및 관리자 권한 검사도 확인합니다.
npm run extensions:sync
npm run extensions:check
npm run extensions:test
npm run build현재 sync는 직접 실행하는 명령이며 dev/build에 자동 연결되어 있지 않습니다. 일반 수작업 라우트를 덮어쓰거나 제거된 연결 파일을 자동 삭제하지 않습니다. 삭제·이름 변경 시 이전 경로를 확인해 정리합니다.
DB와 Trigger 소유권
DB 원본은 해당 기능의 prisma/schema.prisma 또는 prisma/schema/*.prisma에 둡니다. prisma:sync는 중첩된 확장 폴더도 탐색하지만 DB에 변경을 적용하지 않습니다. [Project API](/docs/project-api)의 마이그레이션·seed 절차를 확인하세요.
기능 전용 Trigger 핸들러와 설정은 기능 폴더의 triggers/에 둡니다. src/extensions/triggerRegistry.ts에서 명시적으로 모아 코어에 연결해야 하며 자동 탐색하지 않습니다. 공통 엔진은 코어에 유지하고 프로젝트 공통 예시는 특정 기능으로 임의 이동하지 않습니다.
폴더 배포의 범위
원본을 한 폴더로 모으는 것과 독립 설치는 다릅니다. extensions:sync는 npm 의존성, registry, Trigger, DB, 구매 인증을 설치하지 않습니다. 현재 Store 판매자 화면은 Partners와 Service에 의존하고 공용 파일 API에도 접근 차단 연결이 있습니다. 배포 시 필요한 의존성과 연결 절차를 문서화해야 합니다.
src/modules/posts와 extensions/posts, src/modules/user와 extensions/user는 합치지 않습니다. 공용 UI와 등록 장치는 공유 위치에 유지합니다.
Tailwind 스캔
확장 폴더의 Git 추적 여부와 관계없이 Tailwind가 확장 원본의 클래스를 읽어야 합니다. 현재 src/app/globals.css의 source 설정을 유지합니다.
@import "tailwindcss";
@source "../extensions";
@source "./(extensions)";확장 우선순위
현재 프로젝트 확장은 src/extensions를 직접 바라봅니다.
@extensions
src/extensions/index.tsx
@core/proxy/defaultProxy
src/core/proxy/defaultProxy.ts
@extensions/postCapabilities
src/extensions/postCapabilities.ts