Skip to content

코드 컨벤션 작성 및 컨벤션에 맞게 수정 #303

@manNomi

Description

@manNomi

어떤 기능인가요?

추가하려는 기능에 대해 간결하게 설명해주세요

🏗️ Clean FSD & CQRS 아키텍처 가이드 (v1.0)

1. 핵심 철학 (Core Philosophy)

우리는 **Clean FSD (Feature-Sliced Design)**를 기반으로, CQRS (Read/Write 분리) 패턴과 Next.js App Router의 장점을 결합한 아키텍처를 따릅니다.

  • Lego Architecture: 하위 레이어(Entities, Features)는 독립적인 레고 블록이며, 상위 레이어(Widgets, Pages)는 이를 조립하는 역할을 합니다.
  • Server First: 읽기(Read) 동작은 **RSC(Server Component)**를 통해 최적화하고, 쓰기(Write) 동작은 Client Component로 상호작용을 처리합니다.
  • Separation of Concerns: 프레임워크 설정(app)과 비즈니스 화면 구현(pages)을 철저히 분리합니다.

2. 디렉토리 구조 (Directory Structure)

2.1. 전체 트리 (Overview)

src/
├── app/                  # [Framework Layer] 라우팅, 메타데이터, 레이아웃 (Shell)
├── pages/                # [Page Layer] 실제 페이지 화면 조립 (Implementation)
├── widgets/              # [Composition Layer] Feature와 Entity를 결합한 독립 블록
├── features/             # [Write Layer] 사용자 행동, 뮤테이션 (Verbs)
├── entities/             # [Read Layer] 도메인 데이터, 조회 (Nouns)
└── shared/               # [Generic Layer] 도메인을 모르는 순수 공통 로직

2.2. 레이어별 상세 역할

레이어 역할 성격 주요 포함 내용 참조 가능 범위
App 라우팅 (Shell) Framework page.tsx, layout.tsx, Metadata pages 이하 전체
Pages 화면 조립 Business 페이지 단위 UI 조합 widgets 이하 전체
Widgets 블록 조립 Composition Header, PostDetail, Sidebar features, entities, shared
Features 쓰기 (Write) Action (CUD) 로그인 폼, 좋아요 버튼, useMutation entities, shared
Entities 읽기 (Read) Data (R) 유저 프로필 뷰, fetchUser, Type shared
Shared 공통 Generic Button, Axios, Utils 불가 (최하위)

5. 구현 가이드 (Implementation Guide)

5.1. App vs Pages 분리 패턴

Next.js의 라우팅과 비즈니스 로직을 분리하여 유지보수성을 극대화합니다.

1. src/app/post/[id]/page.tsx (껍데기)

import { Metadata } from 'next';
import { PostPage } from '@/pages/post-detail'; // 실제 구현체 import
import { getPost } from '@/entities/post/api';  // API import

// 메타데이터용 Fetch (Request Memoization으로 중복 호출 걱정 X)
export async function generateMetadata({ params }): Promise<Metadata> {
  const data = await getPost(params.id);
  return { title: data.title };
}

// Params만 전달하고 빠짐
export default function Page({ params }) {
  return <PostPage id={params.id} />;
}

2. src/pages/post-detail/ui/PostPage.tsx (알맹이)

import { PostViewerWidget } from '@/widgets/PostViewer';

// 순수 비즈니스 로직 및 조립
export const PostPage = ({ id }) => {
  return (
    <main>
      <PostViewerWidget postId={id} />
    </main>
  );
};

5.2. CQRS 패턴 적용 예시

상황: 게시글을 보고(Read), 좋아요를 누른다(Write).

  1. Read (Entity): entities/post

    • api/getPost.ts: 데이터를 가져옴.
    • ui/PostContent/: 데이터를 보여주기만 함 (Server Component 권장).
  2. Write (Feature): features/post-interaction

    • api/likePost.ts: 서버에 좋아요 요청을 보냄.
    • ui/LikeButton/: 클릭 이벤트를 받음 (Client Component 필수).
  3. Composition (Widget): widgets/PostViewer

    • EntityFeature를 import 하여 한 덩어리로 만듦.
    • props를 통해 Entity의 ID를 Feature에게 전달.

6. 리팩토링 체크리스트 (Rule of Two)

코드를 작성하다가 아래 상황이 발생하면 리팩토링을 진행합니다.

  1. 중복 발견: 특정 UI나 로직이 2곳 이상의 슬라이스에서 똑같이 필요하다.

    • → 도메인 로직이 없으면 **shared**로 이동.
    • → 도메인 로직이 있으면 **widgets**으로 이동.
  2. 참조 위반: import 경로가 상위로 가거나, 옆집(다른 슬라이스)을 가리킨다.

    • → 해당 로직을 **상위 레이어(Widgets, Pages)**로 끌어올려서 조합하는 형태로 변경.
  3. 파일 비대화: 하나의 컴포넌트 파일이 너무 길어진다.

    • → 내부 로직은 model/useLogic.ts로 분리.
    • → 하위 UI는 같은 폴더 내 컴포넌트로 분리.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions