프로젝트 규모 및 팀 관행에 맞춘 프런트엔드 디렉터리 구조 맞춤화
James Reed
Infrastructure Engineer · Leapcell

소개
빠르게 변화하는 프런트엔드 개발 세계에서 견고하고 유지보수 가능한 코드베이스를 구축하는 것은 프로젝트 성공에 매우 중요합니다. 종종 간과되거나 임의로 결정되는 중요한 측면은 프로젝트의 디렉터리 구조입니다. 건물의 기초와 마찬가지로, 잘 생각한 디렉터리 구조는 확장성을 지원하고, 협업을 촉진하며, 개발자 경험을 향상시킵니다. 반대로, 혼란스럽거나 부적절한 구조는 생산성 감소, 기술 부채 증가, 신규 팀원의 진입 장벽 증가로 이어질 수 있습니다. 이 글은 프로젝트 규모와 팀 습관이 이러한 중요한 결정을 어떻게 좌우해야 하는지에 대한 통찰력을 제공하며, 적절한 프런트엔드 디렉터리 구조를 선택하는 과정을 명확히 하는 것을 목표로 합니다.
프로젝트 구성의 핵심 개념
특정 전략을 살펴보기 전에 디렉터리 구조 선택에 영향을 미치는 몇 가지 핵심 용어와 개념을 정의해 보겠습니다.
- 모듈성 (Modularity): 시스템의 구성 요소를 분리하고 재조합할 수 있는 정도, 이상적으로 각 구성 요소는 단일하고 명확하게 정의된 책임을 가집니다. 디렉터리 구조에서 이는 종종 관련 파일을 함께 그룹화하는 것을 의미합니다.
- 캡슐화 (Encapsulation): 데이터와 해당 데이터를 조작하는 메서드를 단일 단위로 묶고 내부 구현 세부 정보를 숨기는 것입니다. 좋은 디렉터리 구조는 관련 구성 요소와 해당 에셋을 함께 배치하여 캡슐화를 지원할 수 있습니다.
- 관심사 분리 (Separation of Concerns): 컴퓨터 프로그램을 기능적으로 가능한 한 적게 겹치는 별도의 기능으로 나누는 원칙입니다. 이는 종종 다른 유형의 파일(예: 구성 요소, 스타일, 유틸리티)이 어떻게 그룹화되는지를 결정합니다.
- 확장성 (Scalability): 시스템이 증가하는 양의 작업을 처리하거나 성장을 수용하기 위해 확장될 수 있는 능력입니다. 확장 가능한 디렉터리 구조는 근본적인 재구성이 필요 없이 미래의 성장을 예측합니다.
- 발견 용이성 (Discoverability): 개발자가 특정 파일을 얼마나 쉽게 찾거나 새 파일이 프로젝트 내 어디에 배치되어야 하는지 이해할 수 있는 정도입니다. 명확하고 직관적인 구조는 발견 용이성을 향상시킵니다.
- 팀 합의/습관 (Team Consensus/Habits): 개발팀의 집단적 선호도, 확립된 워크플로 및 이전 경험입니다. 팀 습관과 일치하는 구조는 일관되게 채택되고 유지될 가능성이 더 높습니다.
디렉터리 구조 선택 원칙
디렉터리 구조 선택은 모두에게 맞는 단일 해결책이 아니라, 프로젝트 규모와 팀 역학에 의해 영향을 받는 전략적 결정입니다. 일반적인 접근 방식을 살펴보고 그 적용 시나리오를 고려해 보겠습니다.
소규모 프로젝트: 단순성과 속도
종종 단일 개발자 또는 소규모 팀이 촉박한 마감일을 가지고 참여하는 소규모 프로젝트의 경우, 더 평평하고 단순한 구조가 일반적으로 선호됩니다. 프로젝트 범위가 제한되어 있기 때문에 이곳에서는 개발 속도와 이해 용이성에 중점을 둡니다.
일반적인 접근 방식: 유형별 그룹화
이 모델에서는 파일을 기술 유형별로 그룹화합니다. 이는 너무 직관적이어서 많은 스타터 키트와 프레임워크에서 기본 선택 사항인 경우가 많습니다.
/src
├── components/
│ ├── Button.js
│ ├── Button.module.css
│ └── Header.js
├── pages/
│ ├── HomePage.js
│ └── AboutPage.js
├── utils/
│ ├── api.js
│ └── helpers.js
├── assets/
│ ├── images/
│ └── fonts/
└── App.js
└── index.js
장점:
- 새로운 프로젝트에 대해 설정하고 이해하기 쉽습니다.
- 다른 기술적 측면을 명확하게 분리합니다.
- 응용 프로그램의 다른 부분에서 구성 요소 또는 유틸리티가 많이 재사용되는 프로젝트에 좋습니다.
단점:
- 구성 요소 또는 페이지 폴더가 매우 커지면 더 큰 프로젝트에서 다루기 어려워질 수 있습니다.
- 특정 기능에 속한 관련 파일이 다른 폴더에 흩어져 있을 수 있습니다(예: 기능의 구성 요소, 해당 페이지 및 해당 유틸리티 함수).
사용 시기: 프로토타입, 소규모 내부 도구, 랜딩 페이지 또는 '기능 크립(feature creep)'이 크게 예상되지 않는 예측 가능하고 제한된 범위를 가진 프로젝트에 이상적입니다.
중간 규모 프로젝트: 구조와 유연성 균형
프로젝트가 복잡해지고 팀 규모가 커짐에 따라 더 체계적인 접근 방식이 필요합니다. '기능별 그룹화' 또는 '모듈식' 구조가 선호도를 얻고 있으며, 이는 관심사 분리를 유지하고 확장성을 향상시키는 데 도움이 됩니다.
일반적인 접근 방식: 기능별 그룹화 (도메인 주도)
이 구조는 파일이 속한 비즈니스 도메인 또는 기능별로 파일을 구성합니다. 각 기능은 종종 자체 구성 요소, 스타일, 데이터 훅 및 유틸리티 함수를 포함합니다.
/src
├── features/
│ ├── user-profile/
│ │ ├── components/
│ │ │ ├── ProfileHeader.js
│ │ │ └── ProfileForm.js
│ │ ├── hooks/
│ │ │ └── useUserProfile.js
│ │ ├── api.js
│ │ └── index.js // 기능에 대한 공개 API
│ ├── product-catalog/
│ │ ├── components/
│ │ ├── types.ts
│ │ └── index.js
│ ├── authentication/
│ ├── components/
│ ├── services/
│ └── index.js
├── shared/ // 여러 기능에서 재사용되는 구성 요소/유틸리티용
│ ├── components/
│ ├── Button.js
│ ├── hooks/
│ └── utils/
├── layouts/
│ ├── MainLayout.js
│ └── AuthLayout.js
├── app/
│ ├── App.js
│ ├── store.js // Redux 저장소 또는 유사한 것
│ └── router.js
└── index.js
장점:
- 확장성에 탁월합니다. 새 기능을 기존 코드를 광범위하게 건드리지 않고 추가할 수 있습니다.
- 모듈성과 캡슐화를 강제하여 유지보수성을 향상시킵니다.
- 개발자가 여러 최상위 폴더를 이동하지 않고 특정 기능에서 작업하기가 더 쉽습니다.
- 성능 향상을 위해 코드 분할 및 지연 로딩을 용이하게 합니다.
단점:
- 매우 작은 프로젝트의 경우 초기에 더 복잡해 보일 수 있습니다.
- '기능'을 구성하는 것이 때로는 주관적일 수 있으며 팀 합의가 필요합니다.
- 공유 구성 요소에는 명확한 관리가 필요한 지정된
shared또는common폴더가 필요합니다.
사용 시기: 여러 개의 뚜렷한 기능이 있는 애플리케이션, 중간 규모의 제품 팀, 시간이 지남에 따라 크게 발전할 것으로 예상되는 프로젝트에 가장 일반적입니다. 이 구조는 마이크로 프런트엔드 또는 고도로 분산된 개발 노력을 자연스럽게 지원합니다.
대규모 프로젝트: 모노레포 및 원자적 디자인
엔터프라이즈급 애플리케이션, 여러 관련 애플리케이션 또는 광범위한 디자인 시스템을 갖춘 프로젝트의 경우, 모노레포와 같은 더 정교한 구성 패턴과 원자적 디자인과 같은 원칙을 결합하여 사용할 수 있습니다.
일반적인 접근 방식: 모노레포와 하이브리드 구조 (예: Nx, Lerna)
모노레포는 여러 개의 별도 프로젝트를 포함하는 단일 리포지토리이며, 일반적으로 Nx 또는 Lerna와 같은 도구로 관리됩니다. 각 프로젝트 내에서 기능 기반 구조가 일반적이며, 공유 구성 요소는 종종 전용 libs 또는 packages 디렉토리에 있습니다.
/
├── apps/
│ ├── admin-dashboard/ // 별도 애플리케이션
│ │ ├── src/
│ │ │ ├── features/
│ │ │ ├── shared/
│ │ │ └── ...
│ │ └── package.json
│ ├── public-website/ // 또 다른 별도 애플리케이션
│ │ ├── src/
│ │ │ ├── features/
│ │ │ └── ...
│ │ └── package.json
│ └── marketing-site/
│ └── ...
├── packages/ // 앱 간 공유 코드(/libs in Nx와 유사)
│ ├── ui-components/ // 디자인 시스템 구성 요소 (원자, 분자)
│ │ ├── src/
│ │ │ ├── Button/
│ │ │ │ ├── Button.js
│ │ │ │ └── Button.module.css
│ │ │ ├── Card/
│ │ │ └── ...
│ │ └── package.json
│ ├── data-access/ // 공유 API 클라이언트, 훅
│ │ ├── src/
│ │ └── package.json
│ ├── common-utils/ // 일반 유틸리티 함수
│ │ ├── services/
│ │ └── constants/
│ │ └── package.json
│ └── ...
└── tools/
장점:
- multiple 프로젝트 및 공유 종속성의 중앙 집중식 관리.
- 동일 조직 내의 다른 애플리케이션 간의 코드 공유 및 재사용 용이.
- 종속성 관리 및 버전 관리를 단순화.
- 다양한 팀이 공유 코드베이스에 기여하는 대규모 팀 지원.
단점:
- 모노레포 도구의 초기 설정 복잡성과 학습 곡선.
- 적절하게 관리되지 않으면 더 큰 리포지토리 크기와 더 긴 CI/CD 빌드 시간으로 이어질 수 있음.
- 프로젝트 간 결합을 방지하기 위한 강력한 거버넌스 필요.
사용 시기: 기업 수준의 애플리케이션 모음, 여러 제품에서 소비되는 광범위한 디자인 시스템을 개발하는 회사 또는 공유 코드베이스에 기여하는 다른 팀이 있는 시나리오.
팀 습관과 합의의 역할
프로젝트 규모에 관계없이 모든 디렉터리 구조의 궁극적인 성공은 팀의 동의와 일관된 채택에 달려 있습니다.
- 조기 논의 및 합의: 의사 결정 프로세스에 팀 전체를 참여시키십시오. 다른 접근 방식, 장단점을 논의하고 모든 사람이 이해하고 따르기로 합의한 구조에 동의하십시오.
- 문서화: 선택한 디렉터리 구조, 그 이유 및 모든 규약(예: 명명 규칙, 새 파일 배치 위치)을 명확하게 문서화하십시오. 이는 현재 및 미래 팀원을 위한 지침 역할을 합니다.
- 코드 검토: 합의된 구조를 부드럽게 적용하기 위해 코드 검토를 활용하십시오. 이는 일관성을 유지하고 시간이 지남에 따라 '드리프트(drift)'를 방지하는 데 도움이 됩니다.
- 유연성 및 반복: 프로젝트가 발전하거나 팀 요구 사항이 변경됨에 따라 구조를 재검토하고 조정하는 것을 망설이지 마십시오. 오늘날의 '완벽한' 구조는 내일 완벽하지 않을 수 있습니다.
예시: 팀 기술에 맞게 조정
React에 익숙하지 않고 매우 명확한 관심사 분리를 선호하는 팀이 있다고 가정해 보겠습니다. 중간 규모 애플리케이션의 경우에도 공유 구성 요소에 대해 약간 더 유형 기반 구조를 선호하고, 익숙해짐에 따라 기능 기반 구성으로 서서히 전환할 수 있습니다. 예를 들어:
/src
├── components/ // 모든 재사용 가능한 UI 요소
│ ├── forms/
│ │ └── TextInput.js
│ ├── layout/
│ │ └── Card.js
│ └── Button.js
├── features/ // 핵심 비즈니스 로직, 기능별 그룹화
│ ├── user-management/
│ ├── product-display/
│ └── ...
├── services/ // API 호출, 외부 통합
├── hooks/ // 사용자 정의 React 훅
├── styles/ // 전역 스타일, 테마
└── app/
이 하이브리드 접근 방식은 초보 팀이 기본 UI 구성 요소를 편안하게 찾고 사용할 수 있도록 하는 동시에 기능 캡슐화 개념을 천천히 도입할 수 있도록 합니다.
결론
프런트엔드 디렉터리 구조의 선택은 프로젝트의 유지보수성, 확장성 및 개발자 경험에 지대한 영향을 미치는 전략적 결정입니다. 현재 및 예상되는 규모, 팀의 습관 및 선호도를 신중하게 고려함으로써 개발자는 효율성과 장기적인 성공을 촉진하는 조직 패턴을 선택할 수 있습니다. 소규모 프로젝트의 단순성, 중간 규모 프로젝트의 기능 기반 모듈성, 대규모 프로젝트의 정교한 모노레포 전략을 선택하든, 핵심은 팀 합의를 달성하고 일관된 규약을 준수하는 것입니다. 잘 구성된 코드베이스는 진정한 협업과 지속 가능한 성장을 위한 기반입니다.

