From bf07f286a99bf48337c57c14f85d0608974ae0bc Mon Sep 17 00:00:00 2001 From: bicochan Date: Thu, 7 Aug 2025 18:28:29 +0900 Subject: [PATCH] =?UTF-8?q?[apps/cs]=20docs:=20cs=20=ED=94=84=EB=A1=9C?= =?UTF-8?q?=EC=A0=9D=ED=8A=B8=20=EA=B5=AC=EC=A1=B0=20=EB=AC=B8=EC=84=9C=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/cs-project-structure.md | 351 +++++++++++++++++++++++++++++++++++ 1 file changed, 351 insertions(+) create mode 100644 docs/cs-project-structure.md diff --git a/docs/cs-project-structure.md b/docs/cs-project-structure.md new file mode 100644 index 000000000..169aea42a --- /dev/null +++ b/docs/cs-project-structure.md @@ -0,0 +1,351 @@ +# CS 프로젝트 구조 문서 + +## 개요 + +CS 프로젝트는 Next.js App Router를 기반으로 하는 웹 애플리케이션입니다. 이 문서는 프로젝트의 구조와 작동 방식에 대해 설명합니다. + +## 프로젝트 구조 + +CS 프로젝트는 다음과 같은 주요 디렉토리 구조를 가지고 있습니다: + +``` +/apps/cs/ +├── app/ # Next.js App Router 디렉토리 +│ ├── (with-base-layout)/ # 기본 레이아웃을 사용하는 페이지들 +│ ├── (without-base-layout)/ # 기본 레이아웃을 사용하지 않는 페이지들 +│ ├── api/ # API 라우트 +│ ├── auth/ # 인증 관련(로그인) 페이지 +│ └── layout.tsx # 루트 레이아웃 +├── src/ # 소스 코드 +│ ├── app/ # 앱 설정 및 프로바이더 +│ ├── entities/ # 비즈니스 엔티티 (pages에서 두 번 이상 사용하는 비즈니스 코드) +│ ├── pages/ # 페이지 컴포넌트 +│ ├── shared/ # 공유 유틸리티 및 컴포넌트 +│ └── widgets/ # 위젯 컴포넌트 (shared 컴포넌트의 조합) +├── public/ # 정적 파일 +└── middleware.ts # Next.js 미들웨어 +``` + +## App과 Pages의 1:1 매칭 구조 + +CS 프로젝트에서는 `app/` 디렉토리와 `src/pages/` 디렉토리가 1:1 매칭 구조를 가지고 있습니다. + +### 작동 방식 + +1. `app/` 디렉토리는 Next.js의 App Router 기능을 사용하여 라우팅을 처리합니다. +2. `app/` 디렉토리의 각 페이지는 `src/pages/` 디렉토리에 있는 해당 페이지 컴포넌트를 임포트하여 사용합니다. + +예를 들어, `app/(with-base-layout)/dashboard/page.tsx`는 `src/pages/dashboard/ui/dashboard-page.tsx`를 임포트합니다. + +### 예시 코드 + +```tsx +// app/(with-base-layout)/dashboard/page.tsx +import { DashboardPage } from "@/pages/dashboard"; +export default DashboardPage; +``` + +```tsx +// src/pages/dashboard/index.ts +export { DashboardPage } from "./ui/dashboard-page"; +``` + +## 레이아웃 구조 + +CS 프로젝트는 다음과 같은 레이아웃 구조를 제공합니다: + +1. **루트 레이아웃**: `app/layout.tsx`에서 정의되며, 모든 페이지에 적용됩니다. +2. **기본 레이아웃**: `app/(with-base-layout)/layout.tsx`에서 정의되며, `@common/components`의 헤더, 사이드바, 푸터를 포함합니다. +3. **레이아웃 없음**: `app/(without-base-layout)/`에 있는 페이지들은 기본 레이아웃 없이 렌더링됩니다. + +## Common 프로젝트와의 연동 + +CS 프로젝트는 `@common/` 패키지와 연동되어 있습니다. 이 패키지들은 여러 프로젝트에서 공유되는 컴포넌트와 유틸리티를 제공합니다. + +### Common 패키지 구조 + +``` +/packages/common/ +├── components/ # 공통 UI 컴포넌트 +├── assets/ # 공통 에셋 (이미지, 아이콘 등) +├── config/ # 공통 설정 (환경 변수) +└── network/ # 네트워크 관련 유틸리티 +``` + +### Config 패키지 상세 설명 + +`@common/config` 패키지는 애플리케이션의 공통 환경 변수를 관리합니다. 주요 파일인 `initialize-config.ts`는 다음과 같은 기능을 제공합니다: + +#### 1. 환경 변수 인터페이스 정의 + +```typescript +export interface EnvConfig { + PORT?: string; + PROXY_HOST?: string; + SERVER_API_URL?: string; + SITE_ID?: string; + CLAIM_NAME?: string; + ACCESS_TOKEN?: string; + REFRESH_TOKEN?: string; + AUTH_USER_ID?: string; +} +``` + +이 인터페이스는 애플리케이션에서 사용하는 공통 환경 변수의 타입을 정의합니다. + +#### 2. 기본 설정값 + +```typescript +const defaultConfig: EnvConfig = { + PORT: "3000", + PROXY_HOST: "", + SERVER_API_URL: "", + SITE_ID: "", + CLAIM_NAME: "Authorization", + ACCESS_TOKEN: "access-token", + REFRESH_TOKEN: "refresh-token", + AUTH_USER_ID: "token-id", +}; +``` + +환경 변수가 제공되지 않을 경우 사용할 기본값을 정의합니다. + +#### 3. 환경 설정 생성 함수 + +```typescript +export function generateEnvConfig(values: Partial): EnvConfig { + // 유효한 값만 필터링 + const filteredValues = Object.entries(values).reduce((acc, [key, value]) => { + if (value !== undefined && value !== null) { + acc[key as keyof EnvConfig] = value; + } + return acc; + }, {} as Partial); + + // PROXY_HOST가 없으면 자동 생성 + if (!filteredValues.PROXY_HOST) { + filteredValues.PROXY_HOST = `http://localhost:${filteredValues.PORT || defaultConfig.PORT}`; + } + + return { ...defaultConfig, ...filteredValues }; +} +``` + +이 함수는 입력받은 환경 변수 중 유효한 값만 필터링하고, 기본값과 병합하여 최종 설정을 반환합니다. + +#### 4. 환경별 설정 가져오기 + +```typescript +// Next.js 환경 변수 +export function getNextConfig(): EnvConfig { + const env: Partial = { + PORT: process.env.NEXT_PUBLIC_PORT, + PROXY_HOST: process.env.NEXT_PUBLIC_PROXY_HOST, + SERVER_API_URL: process.env.NEXT_PUBLIC_SERVER_API_URL, + // 기타 환경 변수들... + }; + return generateEnvConfig(env); +} + +export function getConfig(): EnvConfig { + return getNextConfig(); +} +``` + +Next.js에서 환경 변수를 가져오는 함수를 제공하며, `getConfig()` 함수를 통해 현재 실행 중인 애플리케이션의 환경 변수를 `@common/` 패키지에서 가져올 수 있습니다. + +#### 5. 사용 방법 + +```typescript +import { getConfig } from "@common/config"; + +const config = getConfig(); +const serverUrl = config.SERVER_API_URL; +const authHeader = config.CLAIM_NAME; + +// API 요청 등에 사용 +``` + +## 애플리케이션 시작점 및 작동 방식 + +### 1. 애플리케이션 시작점 + +애플리케이션의 시작점은 `app/layout.tsx` 파일입니다: + +```tsx +import type { Metadata } from "next"; +import { App } from "@/app"; + +export const metadata: Metadata = { + title: "유역하수도 통합 정보플랫폼", +}; + +export default App; +``` + +### 2. 앱 구성 요소 + +`src/app/index.tsx`에서 기본 앱 구성 요소를 정의합니다: + +```tsx +import "./styles/base.css"; +import "./styles/components.css"; +import "@fontsource-variable/noto-sans-kr"; + +import { cookies } from "next/headers"; +import { AppProvider } from "./providers/app-provider"; + +const App = async ({ children }: Readonly<{ children: React.ReactNode }>) => { + const theme = (await cookies()).get("site-theme-key"); + + return ( + + + {children} + + + ); +}; + +export { App }; +``` + +### 3. 프로바이더 설정 + +`src/app/providers/app-provider.tsx`에서 애플리케이션에 필요한 모든 프로바이더를 설정합니다: + +```tsx +"use client"; + +import { DndProvider } from "react-dnd"; +import { MultiBackend, getBackendOptions } from "@minoru/react-dnd-treeview"; +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; +import { ConfirmProvider } from "@frontend-opensource/use-react-hooks"; +import { AuthWrapper } from "@/entities/auth"; +import { MenuWrapper } from "@/entities/menu"; +import { Toaster } from "@/shared/ui/sonner"; +import { AlertDialog } from "@/shared/ui/alert-dialog"; +import { PromptDialog } from "@/shared/ui/prompt-dialog"; +import { ConfirmDialog } from "@/shared/ui/confirm-dialog"; +import { SelectPromptDialog } from "@/shared/ui/select-prompt-dialog"; +import { PromptProvider, StoreProvider } from "@/shared/providers"; + +const AppProvider = ({ children }: Readonly<{ children: React.ReactNode }>) => { + const queryClient = new QueryClient(); + return ( + + + + + + + + {children} + + + + + + + + + + + + + + ); +}; + +export { AppProvider }; +``` + +### 4. 기본 레이아웃 구성 + +`app/(with-base-layout)/layout.tsx`에서 기본 레이아웃을 구성합니다: + +```tsx +"use client"; + +import { usePathname, useRouter } from "next/navigation"; +import { useQueryClient } from "@tanstack/react-query"; +import { BaseLayout } from "@common/components"; +import type { MenuItem } from "@/entities/menu"; +import { useMenuStore } from "@/shared/lib"; +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 setCurrentMenuInfo = useMenuStore((state) => state.setCurrentMenuInfo); + + const handleMenuClick = (item: MenuItem) => { + setCurrentMenuInfo(item); + router.push(item.urlPath); + }; + + return ( + + {children} + + ); +} +``` + +### 5. 미들웨어 처리 + +`middleware.ts`에서 API 요청 리다이렉션 및 인증 처리를 담당합니다: + +```tsx +import { NextRequest, NextResponse } from "next/server"; +import { + CLAIM_NAME, + AUTH_USER_ID, + PORT, + PROXY_HOST, + SERVER_API_URL, + SITE_ID, +} from "./src/shared/config"; + +export function middleware(req: NextRequest) { + const pathname = req.nextUrl.pathname; + + // 서버 API 요청 처리 + if (pathname.startsWith("/server")) { + // API 요청을 실제 서버로 리다이렉션 + } + + // 첨부 파일 서비스 처리 + if (pathname.startsWith("/attach-service")) { + // 인증 토큰을 헤더에 추가하여 요청 처리 + } + + // 루트 경로를 대시보드로 리다이렉션 + if (pathname === "/") { + return NextResponse.redirect(new URL("/dashboard", req.url)); + } + + return NextResponse.next(); +} +``` + +### 6. 페이지 렌더링 흐름 + +1. 사용자가 URL 접근 시 미들웨어에서 요청 처리 +2. App Router가 해당 경로에 맞는 페이지 컴포넌트 로드 +3. 루트 레이아웃 → 기본 레이아웃 → 페이지 컴포넌트 순으로 렌더링 +4. 페이지 컴포넌트는 `src/pages/` 디렉토리에서 가져와 렌더링