본문 바로가기
C.W.K.
Stream
Lesson 07 of 07 · published

성장에서 살아남는 프로젝트 구조

~14 min · architecture, file-structure, organization

Level 0React 입문자
0 XP0/54 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
React가 공식 파일 구조를 강제하지 않는 건 장점이야. 대신 규모가 커져도 찾고 옮기기 쉬운 구조를 직접 선택해야 해.

무엇을 기준으로 묶을지 정해

파일을 종류별로 모을 수도 있고 기능별로 모을 수도 있어. 종류별 구조는 모든 훅을 hooks/, 컴포넌트를 components/, 유틸리티를 lib/에 둬. 기능별 구조는 채팅에 필요한 훅과 컴포넌트, 유틸리티를 features/chat/처럼 한곳에 모아.

작은 프로젝트는 종류별 구조가 찾기 쉬워. 기능이 많아지면 기능별 구조가 한 기능을 통째로 옮기거나 삭제하기 편해져. 어느 쪽이든 폴더를 다섯 단계씩 깊게 파기보다 한두 단계에서 파일이 보이게 유지해.

cwkPippa의 프런트엔드

cwkPippa는 기능별 컴포넌트와 공용 훅을 함께 쓰는 혼합 구조야.

src/
  App.tsx              # 최상위 상태와 라우트
  main.tsx             # 엔트리
  index.css            # Tailwind v4와 테마 토큰
  components/
    chat/              # InputArea, MessageList, MessageItem
    sidebar/           # ConversationList, FolderTree
    council/           # Council UI
    admin/             # 관리 화면
    settings/          # 설정 화면
  hooks/               # useChat, useConversations, useHeartbeat
  lib/                 # api.ts, formatter, 상수
  types/               # 공용 TypeScript 타입

컴포넌트는 chat, sidebar, council처럼 기능별로 모으고, 여러 기능에서 함께 쓰는 훅과 타입은 종류별 폴더에 둬. 한 프로젝트 안에서도 각 디렉터리의 책임에 맞는 기준을 고르면 돼.

상태를 어디까지 올릴지 정해

cwkPippa의 App.tsx는 여러 자식이 함께 쓰는 상태를 소유하고 props와 callback으로 내려줘. 다른 프로젝트라면 Context나 Zustand store가 그 역할을 맡을 수도 있어. 하나의 정답보다 같은 종류의 상태를 일관된 위치에서 관리하는 게 중요해.

긴 상대 경로는 별칭으로 줄여

../../../components/chat/MessageList처럼 긴 상대 경로는 파일을 옮길 때 쉽게 깨져. @/*src/*에 연결하면 @/components/chat/MessageList처럼 읽고 옮기기 쉬운 import를 쓸 수 있어. TypeScript의 paths와 Vite의 resolve.alias를 같은 값으로 맞춰야 해.

세 달 뒤 이 파일을 어디서 먼저 찾을지 생각해. 그 답에 해당하는 폴더에 파일을 둬. 매번 전체 검색부터 해야 한다면 구조가 이미 제 역할을 못 하고 있는 거야.

Code

tsconfig.app.json에서 import 경로 별칭 설정하기·json
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"]
    }
  }
}
vite.config.ts에 같은 경로 별칭 연결하기·ts
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import tailwindcss from "@tailwindcss/vite";
import path from "node:path";

export default defineConfig({
  plugins: [react(), tailwindcss()],
  resolve: {
    alias: {
      "@": path.resolve(__dirname, "./src"),
    },
  },
});
읽기 어려운 import와 경로 별칭 비교하기·tsx
// Before: 깨지기 쉬움, 파일 이동에 약함
import { MessageList } from "../../../components/chat/MessageList";
import { useChat } from "../../../hooks/useChat";

// After: 이동에 안정
import { MessageList } from "@/components/chat/MessageList";
import { useChat } from "@/hooks/useChat";

External links

Exercise

tsconfig.app.json과 vite.config.ts에 같은 @/* 별칭을 설정해. App.tsx와 예제 컴포넌트를 종류별 또는 기능별 폴더로 옮기고 import 하나를 별칭 경로로 바꿔. 파일을 다시 다른 폴더로 옮겨 상대 경로와 별칭 경로의 차이도 확인해.
Hint
편집기에서만 경로가 깨지면 TypeScript 설정을, 실행할 때만 실패하면 Vite 설정을 먼저 확인해. 두 설정의 대상 경로가 정확히 같아야 해.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.