diff --git a/apps/_template/README.md b/apps/_template/README.md
index 7e14528f8..c48c1534a 100644
--- a/apps/_template/README.md
+++ b/apps/_template/README.md
@@ -1,4 +1,4 @@
-# AS Application
+# Template Application
Next.js 애플리케이션 with FSD(Feature Sliced Design) 아키텍처
@@ -13,46 +13,40 @@ Next.js 애플리케이션 with FSD(Feature Sliced Design) 아키텍처
- **HTTP 클라이언트**: Axios
- **날짜 처리**: Day.js
-## 프로젝트 구조 (FSD)
+## 프로젝트 구조 (FSD + Next.js App Router)
-이 프로젝트는 **Feature-Sliced Design v2.1**을 따르며, 계층적 아키텍처로 구성되어 있습니다.
+이 프로젝트는 **Feature-Sliced Design v2.1**과 **FSD 공식 Next.js 가이드**에 맞춰 구성되어 있습니다. Next.js `app/`은 라우트만 담당하고, 실제 레이어는 `src/` 아래에 있습니다.
```
-as/
-├── app/ # App 레이어 (Next.js App Router)
-│ ├── (routes)/ # 라우트 구조
-│ ├── layout.tsx # 루트 레이아웃
-│ ├── page.tsx # 홈 페이지
-│ ├── globals.css # 글로벌 스타일
-│ └── providers.tsx # 전역 providers
+_template/
+├── app/ # Next.js App Router (루트) — 라우트·레이아웃 진입점만
+│ ├── layout.tsx # 루트 레이아웃 (src/app에서 providers, styles import)
+│ └── (with-base-layout)/
+│ ├── layout.tsx # BaseLayout 래퍼 (src/app/layouts에서 import)
+│ └── page.tsx # re-export: export { MainPage as default } from '@/pages/home'
│
-├── widgets/ # Widgets 레이어
-│ └── [widget-name]/ # 큰 독립적 UI 블록 (여러 페이지에서 재사용)
-│ ├── ui/
-│ ├── model/
-│ └── index.ts
+├── pages/ # 빈 폴더 + README (Next.js가 src/pages를 Pages Router로 쓰지 않도록)
+│ └── README.md
│
-├── features/ # Features 레이어
-│ └── [feature-name]/ # 사용자 상호작용 기능 (여러 페이지에서 재사용)
-│ ├── ui/
-│ ├── api/
-│ ├── model/
-│ └── index.ts
-│
-├── entities/ # Entities 레이어
-│ └── [entity-name]/ # 비즈니스 엔티티 (User, Product 등)
-│ ├── model/
-│ ├── api/
-│ ├── ui/
-│ └── index.ts
-│
-└── shared/ # Shared 레이어
- ├── ui/ # UI Kit (비즈니스 로직 없는 공통 컴포넌트)
- ├── api/ # API 클라이언트 설정
- ├── lib/ # 내부 라이브러리 (dates, colors 등)
- ├── config/ # 환경 설정, feature flags
- ├── types/ # 공통 타입 정의
- └── routes/ # 라우트 상수
+└── src/ # FSD 레이어 (실제 코드)
+ ├── app/ # App 레이어 (슬라이스 없음, 세그먼트만)
+ │ ├── providers/ # 전역 Providers (QueryClient 등)
+ │ ├── styles/ # 글로벌 스타일 (globals.css)
+ │ └── layouts/ # 공통 레이아웃 컴포넌트 (BaseLayoutWrapper)
+ │
+ ├── pages/ # Pages 레이어 (페이지 슬라이스)
+ │ └── home/
+ │ ├── ui/
+ │ └── index.ts # Public API
+ │
+ ├── widgets/ # Widgets 레이어
+ ├── features/ # Features 레이어
+ ├── entities/ # Entities 레이어
+ └── shared/ # Shared 레이어
+ ├── ui/
+ ├── api/
+ ├── lib/
+ └── config/
```
### 계층 구조 이해하기
@@ -86,10 +80,10 @@ shared/ ↓ 가장 낮은 책임, 의존성 없음
- **특징**: 슬라이스가 없고 세그먼트로만 구성
- **의존성**: 모든 하위 레이어 사용 가능
-### 2. Pages (app 디렉토리)
+### 2. Pages (src/pages)
- **목적**: 웹사이트나 애플리케이션을 구성하는 페이지/화면
- **내용**: 페이지 UI, 로딩 상태, 에러 바운더리, 데이터 fetching
-- **특징**: 한 페이지당 한 슬라이스 (유사한 페이지는 그룹화 가능)
+- **특징**: 한 페이지당 한 슬라이스 (유사한 페이지는 그룹화 가능). Next.js `app/`의 route 파일에서 re-export
- **의존성**: `widgets`, `features`, `entities`, `shared` 사용 가능
> 💡 **FSD v2.1 권장사항**: "pages-first" 접근 방식을 따라, 재사용되지 않는 큰 UI 블록과 비즈니스 로직은 페이지 슬라이스에 유지하세요.
@@ -118,29 +112,6 @@ shared/ ↓ 가장 낮은 책임, 의존성 없음
- **특징**: 비즈니스 로직 없음, 슬라이스가 없고 세그먼트로만 구성
- **의존성**: 외부 라이브러리만 사용 가능 (다른 레이어 불가)
-## 레이어 간 Import 규칙
-
-> **핵심 규칙**: 슬라이스 내의 모듈(파일)은 **오직 하위 레이어**에 있는 다른 슬라이스만 import할 수 있습니다.
-
-예시:
-- `features/auth-login/ui/form.tsx`는 `entities/user`와 `shared/ui`를 import 가능
-- 하지만 다른 `features/post-create`나 상위 레이어인 `widgets`는 import 불가
-- 같은 슬라이스 내부(`features/auth-login/`)의 다른 파일들은 자유롭게 import 가능
-
-## Public API 규칙
-
-> **슬라이스 외부의 모듈은 슬라이스의 Public API만 참조할 수 있으며, 내부 파일 구조에 직접 접근할 수 없습니다.**
-
-모든 슬라이스(및 슬라이스가 없는 레이어의 세그먼트)는 반드시 `index.ts`를 통해 Public API를 정의해야 합니다.
-
-```typescript
-// ✅ 올바른 import
-import { LoginForm } from 'features/auth-login';
-
-// ❌ 잘못된 import (내부 구조 직접 접근)
-import { LoginForm } from 'features/auth-login/ui/login-form';
-```
-
## 시작하기
```bash
@@ -174,16 +145,16 @@ NEXT_PUBLIC_API_URL=http://localhost:3000/api
```typescript
// ✅ 올바른 import (하위 레이어)
// features/auth-login/ui/form.tsx
-import { User } from 'entities/user';
-import { Button } from 'shared/ui/button';
+import { User } from "entities/user";
+import { Button } from "shared/ui/button";
// ❌ 잘못된 import (같은 레이어)
// features/auth-login/ui/form.tsx
-import { PostForm } from 'features/post-create'; // 금지!
+import { PostForm } from "features/post-create"; // 금지!
// ❌ 잘못된 import (상위 레이어)
// features/auth-login/ui/form.tsx
-import { Header } from 'widgets/header'; // 금지!
+import { Header } from "widgets/header"; // 금지!
```
### 2. Public API 규칙
@@ -192,10 +163,10 @@ import { Header } from 'widgets/header'; // 금지!
```typescript
// ✅ 올바른 import (Public API 사용)
-import { LoginForm } from 'features/auth-login';
+import { LoginForm } from "features/auth-login";
// ❌ 잘못된 import (내부 구조 직접 접근)
-import { LoginForm } from 'features/auth-login/ui/login-form';
+import { LoginForm } from "features/auth-login/ui/login-form";
```
**Public API 작성 시 주의사항**:
@@ -205,44 +176,12 @@ import { LoginForm } from 'features/auth-login/ui/login-form';
```typescript
// ❌ 나쁜 예시
-export * from './ui/Comment';
-export * from './model/comments';
+export * from "./ui/Comment";
+export * from "./model/comments";
// ✅ 좋은 예시
-export { Comment } from './ui/comment';
-export { CommentForm } from './ui/comment-form';
-export { useComments } from './model/use-comments';
-export type { Comment, CommentFormData } from './model/types';
-```
-
-### 3. 슬라이스 독립성
-
-- **Zero Coupling**: 같은 레이어의 슬라이스들은 서로 독립적이어야 합니다
-- **High Cohesion**: 슬라이스는 주요 목표와 관련된 대부분의 코드를 포함해야 합니다
-- 예외: Entities 레이어에서 필요한 경우 `@x` 표기법으로 cross-import 가능
-
-### 4. 세그먼트 명명 규칙
-
-세그먼트 이름은 **목적**을 설명해야 하며, **본질**을 설명하면 안 됩니다:
-
-- ❌ **나쁜 이름**: `components`, `hooks`, `types` (무엇인지만 설명)
-- ✅ **좋은 이름**: `ui`, `api`, `model`, `lib` (왜 있는지 설명)
-
-## 코드 규칙
-
-- TypeScript 사용 (strict mode)
-- 함수형 컴포넌트 사용
-- Named exports 선호
-- Tailwind CSS 사용
-- ESLint + Prettier 적용
-- Public API는 명시적으로 정의 (와일드카드 export 지양)
-
-## 아키텍처 린팅
-
-코드베이스의 FSD 규칙 준수를 자동으로 검증하려면 [Steiger](https://github.com/feature-sliced/steiger)를 사용하세요.
-
-```bash
-# Steiger 설치 및 실행
-pnpm add -D @feature-sliced/steiger
-npx steiger src
+export { Comment } from "./ui/comment";
+export { CommentForm } from "./ui/comment-form";
+export { useComments } from "./model/use-comments";
+export type { Comment, CommentFormData } from "./model/types";
```
diff --git a/apps/_template/app/(with-base-layout)/layout.tsx b/apps/_template/app/(with-base-layout)/layout.tsx
index 1d182a0ee..1ff36f8ec 100644
--- a/apps/_template/app/(with-base-layout)/layout.tsx
+++ b/apps/_template/app/(with-base-layout)/layout.tsx
@@ -1,31 +1,10 @@
-"use client";
-
-import { useCallback } from "react";
-import { usePathname, useRouter } from "next/navigation";
-import { useQueryClient } from "@tanstack/react-query";
-import { BaseLayout } from "@common/components";
-import type { MenuItem } from "@/entities/menu";
-import { PageLayout } from "@/shared/ui/page-layout";
-
-export default function WithBaseLayout({ children }: { children: React.ReactNode }) {
- const router = useRouter();
- const pathname = usePathname();
- const queryClient = useQueryClient();
-
- const handleMenuClick = useCallback(
- (item: MenuItem) => {
- router.push(item.urlPath);
- },
- [router],
- );
-
- return (
-
- {children}
-
- );
-}
+import type { ReactNode } from "react";
+import { BaseLayoutWrapper } from "@/app/layouts/base-layout";
+
+interface WithBaseLayoutProps {
+ children: ReactNode;
+}
+
+export default function WithBaseLayout({ children }: WithBaseLayoutProps) {
+ return {children};
+}
diff --git a/apps/_template/app/(with-base-layout)/page.tsx b/apps/_template/app/(with-base-layout)/page.tsx
index e118e732e..82a918b59 100644
--- a/apps/_template/app/(with-base-layout)/page.tsx
+++ b/apps/_template/app/(with-base-layout)/page.tsx
@@ -1,9 +1 @@
-"use client";
-
-export default function MainPage() {
- return (
-
-
Home
-
- );
-}
+export { MainPage as default } from "@/pages/home";
diff --git a/apps/_template/app/layout.tsx b/apps/_template/app/layout.tsx
index d152e0da3..5f5f63ad8 100644
--- a/apps/_template/app/layout.tsx
+++ b/apps/_template/app/layout.tsx
@@ -1,7 +1,7 @@
import type { Metadata } from "next";
import { Noto_Sans_KR } from "next/font/google";
-import { Providers } from "./providers";
-import "./globals.css";
+import { Providers } from "@/app/providers";
+import "@/app/styles/globals.css";
const notoSansKR = Noto_Sans_KR({
subsets: ["latin"],
diff --git a/apps/as/pages/README.md b/apps/_template/pages/README.md
similarity index 98%
rename from apps/as/pages/README.md
rename to apps/_template/pages/README.md
index 287ea7ddb..e011944c3 100644
--- a/apps/as/pages/README.md
+++ b/apps/_template/pages/README.md
@@ -1,9 +1,9 @@
-# 이 폴더는 비워 둡니다 (FSD + Next.js App Router)
-
-Next.js가 `src/pages`를 **Pages Router**용 `pages` 디렉터리로 인식하지 않도록,
-프로젝트 루트에 **빈 `pages` 폴더**를 두는 것이 FSD 공식 가이드 권장사항입니다.
-
-- 실제 페이지(FSD Pages 레이어)는 **`src/pages`**에 있습니다.
-- Next.js 라우트는 **`app/`** 에서 `src/pages`를 re-export 하는 방식으로 연결됩니다.
-
-[Feature-Sliced Design - Usage with Next.js (App Router)](https://feature-sliced.design/docs/guides/tech/with-nextjs)
+# 이 폴더는 비워 둡니다 (FSD + Next.js App Router)
+
+Next.js가 `src/pages`를 **Pages Router**용 `pages` 디렉터리로 인식하지 않도록,
+프로젝트 루트에 **빈 `pages` 폴더**를 두는 것이 FSD 공식 가이드 권장사항입니다.
+
+- 실제 페이지(FSD Pages 레이어)는 **`src/pages`**에 있습니다.
+- Next.js 라우트는 **`app/`** 에서 `src/pages`를 re-export 하는 방식으로 연결됩니다.
+
+[Feature-Sliced Design - Usage with Next.js (App Router)](https://feature-sliced.design/docs/guides/tech/with-nextjs)
diff --git a/apps/_template/src/app/layouts/base-layout.tsx b/apps/_template/src/app/layouts/base-layout.tsx
new file mode 100644
index 000000000..1081134e0
--- /dev/null
+++ b/apps/_template/src/app/layouts/base-layout.tsx
@@ -0,0 +1,35 @@
+"use client";
+
+import { useCallback } from "react";
+import { usePathname, useRouter } from "next/navigation";
+import { useQueryClient } from "@tanstack/react-query";
+import { BaseLayout } from "@common/components";
+import type { MenuItem } from "@/entities/menu";
+import { PageLayout } from "@/shared/ui/page-layout";
+
+interface BaseLayoutWrapperProps {
+ children: React.ReactNode;
+}
+
+export function BaseLayoutWrapper({ children }: BaseLayoutWrapperProps) {
+ const router = useRouter();
+ const pathname = usePathname();
+ const queryClient = useQueryClient();
+
+ const handleMenuClick = useCallback(
+ (item: MenuItem) => {
+ router.push(item.urlPath);
+ },
+ [router],
+ );
+
+ return (
+
+ {children}
+
+ );
+}
diff --git a/apps/_template/app/providers.tsx b/apps/_template/src/app/providers/index.tsx
similarity index 96%
rename from apps/_template/app/providers.tsx
rename to apps/_template/src/app/providers/index.tsx
index bc12c443b..05bac42cc 100644
--- a/apps/_template/app/providers.tsx
+++ b/apps/_template/src/app/providers/index.tsx
@@ -1,18 +1,18 @@
-"use client";
-
-import { QueryClientProvider } from "@tanstack/react-query";
-import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
-import { queryClient } from "@/shared/lib";
-
-interface ProvidersProps {
- children: React.ReactNode;
-}
-
-export function Providers({ children }: ProvidersProps) {
- return (
-
- {children}
-
-
- );
-}
+"use client";
+
+import { QueryClientProvider } from "@tanstack/react-query";
+import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
+import { queryClient } from "@/shared/lib";
+
+interface ProvidersProps {
+ children: React.ReactNode;
+}
+
+export function Providers({ children }: ProvidersProps) {
+ return (
+
+ {children}
+
+
+ );
+}
diff --git a/apps/_template/app/globals.css b/apps/_template/src/app/styles/globals.css
similarity index 100%
rename from apps/_template/app/globals.css
rename to apps/_template/src/app/styles/globals.css
diff --git a/apps/_template/entities/README.md b/apps/_template/src/entities/README.md
similarity index 100%
rename from apps/_template/entities/README.md
rename to apps/_template/src/entities/README.md
diff --git a/apps/_template/entities/menu/index.ts b/apps/_template/src/entities/menu/index.ts
similarity index 100%
rename from apps/_template/entities/menu/index.ts
rename to apps/_template/src/entities/menu/index.ts
diff --git a/apps/_template/entities/menu/model/types.ts b/apps/_template/src/entities/menu/model/types.ts
similarity index 100%
rename from apps/_template/entities/menu/model/types.ts
rename to apps/_template/src/entities/menu/model/types.ts
diff --git a/apps/_template/features/README.md b/apps/_template/src/features/README.md
similarity index 100%
rename from apps/_template/features/README.md
rename to apps/_template/src/features/README.md
diff --git a/apps/as/src/pages/home/index.ts b/apps/_template/src/pages/home/index.ts
similarity index 97%
rename from apps/as/src/pages/home/index.ts
rename to apps/_template/src/pages/home/index.ts
index d11882ce5..97a463408 100644
--- a/apps/as/src/pages/home/index.ts
+++ b/apps/_template/src/pages/home/index.ts
@@ -1 +1 @@
-export { MainPage } from "./ui/main-page";
+export { MainPage } from "./ui/main-page";
diff --git a/apps/as/src/pages/home/ui/main-page.tsx b/apps/_template/src/pages/home/ui/main-page.tsx
similarity index 95%
rename from apps/as/src/pages/home/ui/main-page.tsx
rename to apps/_template/src/pages/home/ui/main-page.tsx
index 0370d5814..f757224dc 100644
--- a/apps/as/src/pages/home/ui/main-page.tsx
+++ b/apps/_template/src/pages/home/ui/main-page.tsx
@@ -1,9 +1,9 @@
-"use client";
-
-export function MainPage() {
- return (
-
-
Home
-
- );
-}
+"use client";
+
+export function MainPage() {
+ return (
+
+
Home
+
+ );
+}
diff --git a/apps/_template/shared/README.md b/apps/_template/src/shared/README.md
similarity index 100%
rename from apps/_template/shared/README.md
rename to apps/_template/src/shared/README.md
diff --git a/apps/_template/shared/api/client.ts b/apps/_template/src/shared/api/client.ts
similarity index 100%
rename from apps/_template/shared/api/client.ts
rename to apps/_template/src/shared/api/client.ts
diff --git a/apps/_template/shared/api/index.ts b/apps/_template/src/shared/api/index.ts
similarity index 100%
rename from apps/_template/shared/api/index.ts
rename to apps/_template/src/shared/api/index.ts
diff --git a/apps/_template/shared/config/env.ts b/apps/_template/src/shared/config/env.ts
similarity index 100%
rename from apps/_template/shared/config/env.ts
rename to apps/_template/src/shared/config/env.ts
diff --git a/apps/_template/shared/config/index.ts b/apps/_template/src/shared/config/index.ts
similarity index 100%
rename from apps/_template/shared/config/index.ts
rename to apps/_template/src/shared/config/index.ts
diff --git a/apps/_template/shared/lib/dayjs.ts b/apps/_template/src/shared/lib/dayjs.ts
similarity index 100%
rename from apps/_template/shared/lib/dayjs.ts
rename to apps/_template/src/shared/lib/dayjs.ts
diff --git a/apps/_template/shared/lib/index.ts b/apps/_template/src/shared/lib/index.ts
similarity index 100%
rename from apps/_template/shared/lib/index.ts
rename to apps/_template/src/shared/lib/index.ts
diff --git a/apps/_template/shared/lib/query-client.ts b/apps/_template/src/shared/lib/query-client.ts
similarity index 100%
rename from apps/_template/shared/lib/query-client.ts
rename to apps/_template/src/shared/lib/query-client.ts
diff --git a/apps/_template/shared/model/index.ts b/apps/_template/src/shared/model/index.ts
similarity index 100%
rename from apps/_template/shared/model/index.ts
rename to apps/_template/src/shared/model/index.ts
diff --git a/apps/_template/shared/ui/index.ts b/apps/_template/src/shared/ui/index.ts
similarity index 100%
rename from apps/_template/shared/ui/index.ts
rename to apps/_template/src/shared/ui/index.ts
diff --git a/apps/_template/shared/ui/page-layout.tsx b/apps/_template/src/shared/ui/page-layout.tsx
similarity index 100%
rename from apps/_template/shared/ui/page-layout.tsx
rename to apps/_template/src/shared/ui/page-layout.tsx
diff --git a/apps/_template/widgets/README.md b/apps/_template/src/widgets/README.md
similarity index 100%
rename from apps/_template/widgets/README.md
rename to apps/_template/src/widgets/README.md
diff --git a/apps/_template/tsconfig.json b/apps/_template/tsconfig.json
index 15b023415..dab9b0b8e 100644
--- a/apps/_template/tsconfig.json
+++ b/apps/_template/tsconfig.json
@@ -19,13 +19,7 @@
}
],
"paths": {
- "@/*": ["./*"],
- "@/app/*": ["./app/*"],
- "@/shared/*": ["./shared/*"],
- "@/entities/*": ["./entities/*"],
- "@/features/*": ["./features/*"],
- "@/widgets/*": ["./widgets/*"],
- "@/pages/*": ["./pages/*"],
+ "@/*": ["./src/*"],
"@common/components": ["../../packages/common/components/dist/"],
"@common/network": ["../../packages/common/network/"]
}