리엑트 프로젝트, 정답 없는 폴더 구조

Nogglee·2026년 3월 28일
post-thumbnail

확장성을 초반부터 고려하지 말자

프로젝트를 시작 초반에 폴더 구조에 대한 고민으로 시간을 오래썼다.

협업에 도움이 될거라는 생각에 확장성을 고려해 폴더를 세분화하고,
컴포넌트 / 도메인별로 파일을 나누는 구조를 적용했었다.

막상 작업을 시작해보니, import 경로가 길어졌고,
어떤 파일이 어디에 있는지 찾는데 시간이 생각보다 오래걸렸다.

결과적으로 개발 속도를 오히려 늦추는 요인이 된 것이다.


폴더 구조 설계 기준 세우기

세분화된 폴더 구조에 대해 문제점을 발견했고,
이제는 어떤 것을 우선순위로 잡아야하는지 알게되었고, 아래의 기준으로 구조 리팩토링을 진행했다.

  • 누구나 빠르게 파일을 탐색할 수 있도록 구조에 대한 규칙이 명확하여야한다.
  • 내가 쓴 코드가 아니더라도 맥락을 파악하기 쉽게 지나친 모듈화는 지양한다.

빠른 탐색을 위한 명확한 규칙

변경 전

components/
  ui/
    Button/
    ...
features/
  timer/
    api/
    components/
    hooks/
    type/
  ...
pages/
  main/
  ...
router/
styles/
...

크게는 컴포넌트, 기능, 화면, 라우터 등의 역할별로 분리하고,
기능 폴더에서도 각 도메인 하위에 폴더를 나누어놨었다.

기능 단위로 폴더를 나누고, 그 안에서 다시 역할별로 분리하다 보니
하나의 기능을 이해하기 위해 여러 폴더를 오가야 했다.

또, import 경로가 깊어지면서 코드 자체도 읽기 불편해졌고,
어디에 무엇이 있는지 기억하거나 찾는 데 드는 비용도 커졌다.

결국 구조를 세분화한 목적인 협업 용이성과 확장성을 달성하지 못하고,
탐색 비용을 증가시키는 결과를 자아냈다.

이 지점에서 미래의 확장성보다, ‘현재 빠르게 찾고 이해할 수 있는가’를 기준으로 다시 설계했다.

변경 후

components/
  ui/
  ...
features/
  timer/
    api/
    components/
  ...
pages/
router/
styles/
...
  • components: 컴포넌트 하위 폴더로 카테고리 구분, 카테고리 폴더 안에는 tsx 파일들을 저장
  • pages: 화면별 폴더링을 생략하고, tsx 파일로만 구성
  • features: 도메인인별 폴더 하위에 api와 components만 분류하고 나머지는 파일 단위로 저장

맥락을 파악하기 쉬운 코드

변경 전

components/
  ui/
    Button/
      index.ts
      Button.tsx
      Button.style.ts
      Button.type.ts
features/
  timer/
    api/
    components/
    type/
    hooks/

컴포넌트는 tsx, style, type을 각각 분리하고,
기능 또한 도메인 단위로 세분화된 폴더 구조를 사용했다.

이 구조는 확장성을 고려하면 합리적으로 보였지만,
React에 대한 이해도가 높지 않은 상태에서는 이 구조가 오히려 진입 장벽으로 작용했다.

확장성을 고려해서 설계했지만, 정작 확장하기 전에 개발 속도가 느려지는 상황을 경험했다.

변경 후

components/
  ui/
    Button.tsx
    index.ts
    Modal.tsx
features/
  timer/
    api/
    components/
    index.ts
    TimerPanel.tsx
    timerStore.ts
    useTimer.ts
  • components: 가능한 한 하나의 tsx 파일 안에서 타입, 스타일 로직을 함께 관리하도록 단순화했다.
  • features: api / components만 최소한으로 분리하고, 나머지는 파일 단위로 구성했다.

도메인별 hook은 use- 접두어를, store는 -Store 접미사를 붙여 구분했고,
추후 파일이 많아지면 폴더링하기로 했다.


짧아진 import문

폴더 구조를 단순화한 것과 함께 import 방식도 정리했다.

components/
  form/index.ts
  layout/index.ts
  ui/index.ts
features/
  timer/index.ts
  todo/index.ts
// components/ui/index.ts
export { Button, ButtonGroup } from './Button';
export { Modal } from './Modal';

// features/timer/index.ts
export { TimerPanel } from './components/TimerPanel';
export { useTimer } from './useTimer';
export { timerStore } from './timerStore';

각 카테고리와 도메인 폴더에 index.ts를 두고, 관련 모듈을 한 번에 export하도록 구성했다.

// tsconfig.app.json
{
    "compilerOptions": {
        ...,
        "paths": {
            "@/*": ["src/*"],
            "@@/*": ["src/components/*"],
            "@@@/*": ["src/features/*"]
        }
    }
}

또, 경로에 대한 alias를 설정하여 경로 자체를 단순화 했다.

위와 같이 변경한 후 import 방식은 아래와 같이 바뀌었다.

// example
import { SectionHeader, DoubleColumnLayout, Container } from '@@/layout';
import { Icon, PlayerButton, Modal, Badge } from '@@/ui';
import { SessionIndicator, TimerPanel } from '@@@/timer';
import { TodoPanel } from '@@@/todo';

변경하기 전에는 상대 경로를 따라 깊게 이동해야 했지만,
이제는 어디에서 가져오는지 경로만 봐도 역할이 드러나고 팀원 간 import 규칙도 자연스럽게 통일되었다.


AI 협업을 고려한 설계

components의 각 카테고리 루트와 features의 각 도메인 루트에
README 파일을 두고 설계원칙과 참고사항을 문서화해두었다.

아래는 components/ui 루트에 작성된 md 파일의 예시이다.

이렇게 하면 팀원뿐 아니라 Codex나 Claude Code 같은 도구가
기존 구조를 참고해 새로운 코드를 작성할 때도 기준으로 활용할 수 있다.

컴포넌트 문서의 경우 실제 코드에서 바로 가져다 쓸 수 있도록
코드 조각 형태로 정리해두었기 때문에 작업 속도도 향상시킬 수 있으며,
ai가 화면을 구성할 때 누락없이 기준에 맞는 컴포넌트를 사용할 수 있다.

README 작성 팁

이런 문서화 또한 귀찮은 작업 중에 하나이다.

README 하나를 팀 기준에 맞게 작성해두고,
ai에게 새로운 기능을 추가할 때마다 기존 문서 톤에 맞추어 리드미에 내용을 추가해달라고하자.

profile
Product-minded Engineer

0개의 댓글