디렉토리 구조, 진짜 어떻게 해야 하는데?

2025. 7. 27. 21:26·Computer Science

정답은 없다. 하지만 원칙은 있다. 그리고 혼자 할 때와 팀으로 할 때는 완전히 다른 게임이다.

Claude 선생님이 알려주신 대원칙 5

1. 관심사 분리 (Separation of Concerns)

  • 핵심 각 파일/폴더는 하나의 명확한 책임을 가져야 한다
  • 의미
    • ’아, 이 기능 바꾸려면 저 파일들을 수정하면 되겠구나’라고 미리 알 수 있는 것
    • ‘어디어디에 같은 코드가 또 있을까?’ 하고 걱정하지 않아도 되는 것
  • 예시
// ✅ 관심사가 잘 분리된 구조
src/
├── utils/
│   └── dateHelper.js        // 날짜 관련 로직만
├── api/
│   └── eventApi.js          // 서버 통신만
├── components/
│   └── EventCard.vue        // 화면 표시만
└── stores/
    └── eventStore.js        // 상태 관리만

// 날짜 형식 바꿔달라? → dateHelper.js만 수정
// API 주소 바뀜? → eventApi.js만 수정
// 디자인 변경? → EventCard.vue만 수정

2. 의존성 방향 (Dependency Direction)

  • 핵심 상위 → 하위로만 의존해야 한다
  • 의미
    • utils가 components를 import하면 안 됨
    • 순환 참조 방지 (A가 B를 부르고, B가 다시 A를 부르면 무한루프)
  • 예시
// 폴더 계층: views > components > utils

// ✅ 올바른 방향
// views/EventList.vue
import EventCard from '@/components/EventCard.vue'  // 상위→하위 OK

// components/EventCard.vue  
import { formatDate } from '@/utils/dateHelper.js'  // 상위→하위 OK

// ❌ 잘못된 방향
// utils/dateHelper.js
import EventCard from '@/components/EventCard.vue'  // 하위→상위 NG!

// components/EventCard.vue
import EventList from '@/views/EventList.vue'       // 하위→상위 NG!
// ❌ 이렇게 되면 빌드 에러!
// A.js
import B from './B.js'

// B.js  
import A from './A.js'  // A와 B가 서로를 부름 → 무한루프!

 

3. 예측 가능성 (Predictability)

  • 핵심 파일명과 위치만 봐도 역할을 알 수 있어야 한다
  • 의미
    • 새로운 사람이 와도 직관적으로 이해 가능
    • 파일 찾는 시간 최소화
  • 예시
// ❌ 예측하기 어려운 이름들
components/
├── Thing.vue           // 뭘 하는 컴포넌트인지 모름
├── utils.js            // 어떤 유틸인지 모름
├── api.js              // 어떤 API인지 모름
└── temp.vue            // 임시파일? 뭔지 모름

// ✅ 예측 가능한 이름들  
components/
├── EventCard.vue       // 이벤트 카드구나!
├── UserProfile.vue     // 사용자 프로필이구나!
├── FileUploadButton.vue // 파일 업로드 버튼이구나!
└── CalendarGrid.vue    // 달력 격자구나!

// 폴더도 마찬가지
src/
├── components/         // 재사용 컴포넌트들이 있겠구나
├── views/             // 페이지들이 있겠구나  
├── utils/             // 유틸리티 함수들이 있겠구나
└── api/               // 서버 통신 관련이 있겠구나

 

// ❌ 이렇게 되면 빌드 에러!
// A.js
import B from './B.js'

// B.js  
import A from './A.js'  // A와 B가 서로를 부름 → 무한루프!

 

4. 확장성 (Scalability)

  • 핵심 프로젝트가 10배 커져도 구조가 깨지지 않아야 한다
  • 의미
    • 폴더가 너무 깊어지거나 파일이 너무 많아지면 안 됨
    • 프로덕트 성장에 따라 자연스럽게 진화할 수 있는 구조
  • 예시한 폴더에 파일 10개 넘으면 분할 고려!
// 초기: 컴포넌트 5개
components/
├── Button.vue
├── Input.vue
├── Modal.vue
├── EventCard.vue
└── UserCard.vue

// 성장: 컴포넌트 30개 → 분류 필요!
components/
├── ui/                 // 기본 UI들
│   ├── Button.vue
│   ├── Input.vue
│   └── Modal.vue
├── cards/              // 카드형 컴포넌트들
│   ├── EventCard.vue
│   ├── UserCard.vue
│   └── ProductCard.vue
└── forms/              // 폼 관련들
    ├── EventForm.vue
    ├── UserForm.vue
    └── LoginForm.vue

// 더 성장: 컴포넌트 100개 → 더 세분화
components/
├── ui/
│   ├── buttons/        // 버튼들만
│   ├── inputs/         // 입력 요소들만
│   └── modals/         // 모달들만
├── business/           // 비즈니스 로직 관련
│   ├── events/
│   ├── users/
│   └── products/
└── layouts/            // 레이아웃 관련

5. 일관성 (Consistency)

  • 핵심 같은 성격의 것들은 같은 규칙으로 배치한다
  • 의미
    • 예외를 만들 때는 명확한 이유가 있어야 함
    • 패턴이 있어야 기억하기 쉽고 찾기 쉬움
  • 예시
  • 네이밍 일관성
// ❌ 일관성 없음
components/
├── eventCard.vue       // camelCase
├── user-profile.vue    // kebab-case
├── ButtonBig.vue       // PascalCase
└── input_field.vue     // snake_case

// ✅ 일관성 있음 (Vue 권장: PascalCase)
components/
├── EventCard.vue       // 모두 PascalCase
├── UserProfile.vue
├── ButtonBig.vue
└── InputField.vue

 

  • 구조 일관성
// ✅ 모든 기능이 같은 패턴
features/
├── events/
│   ├── EventList.vue      // 목록
│   ├── EventForm.vue      // 폼
│   ├── EventDetail.vue    // 상세
│   └── eventStore.js      // 스토어
├── users/
│   ├── UserList.vue       // 같은 패턴!
│   ├── UserForm.vue       // 같은 패턴!
│   ├── UserDetail.vue     // 같은 패턴!
│   └── userStore.js       // 같은 패턴!
└── products/
    ├── ProductList.vue    // 같은 패턴!
    ├── ProductForm.vue    // 같은 패턴!
    ├── ProductDetail.vue  // 같은 패턴!
    └── productStore.js    // 같은 패턴!
  • 실제 상황 "상품 폼 컴포넌트 어디 있지?"
    • "events, users와 같은 패턴이니까 products/ProductForm.vue겠네!" (즉시 예측 가능)

됐고, 그래서 혼자 할 때 어떻게 해야하는데?
⎯ 나만의 기준 세우는 10가지 질문

1. 프로젝트 성격 파악하기

질문1 "이 프로젝트는 얼마나 오래 갈 건가?”

// 실험용 (1-2주)
→ 간단한 구조로, 빠른 개발 우선
→ src/ 바로 아래에 파일들 두기

// 포트폴리오용 (1-3개월)  
→ 중간 복잡도, 깔끔하게 보이는 구조
→ components/, views/, utils/ 정도

// 실제 서비스 (6개월+)
→ 확장성 고려한 엔터프라이즈 구조
→ features/, stores/, composables/ 등

 

질문2"최종적으로 몇 개 정도의 파일이 생길까?”

// ~20개 파일: 단순 구조
src/
├── components/
├── utils/
└── assets/

// 20-100개 파일: 중간 구조  
src/
├── components/
│   ├── ui/
│   └── business/
├── views/
├── stores/
└── utils/

// 100개+ 파일: 복잡한 구조
src/
├── features/
├── shared/
├── layouts/
└── core/

 

2. 내 사고 패턴 파악하기

질문3 "나는 코드를 어떻게 찾는가?”

// 자가 진단 테스트
"이벤트 관련 코드 수정해야 해" 했을 때:

A. "events 폴더부터 찾는다"
→ 기능별 구조 선호 (features/)

B. "컴포넌트 폴더에서 Event로 시작하는 걸 찾는다"  
→ 타입별 구조 선호 (components/)

C. "파일명으로 검색한다"
→ 네이밍이 더 중요, 구조는 단순하게

 

 

질문4 "나는 어떤 순서로 개발하는가?”

// 내 개발 스타일 파악

A. "기능 하나를 완전히 끝내고 다음으로"
→ features/events/ 안에 모든 관련 파일

B. "UI부터 다 만들고, 로직은 나중에"  
→ components/ 먼저, 나중에 stores/

C. "작은 단위부터 조립해서 큰 것으로"
→ utils/ → components/ → views/ 순서

 

질문5 나는 비슷한 파일들을 어떻게 그룹핑하고 싶은가?”

// 그룹핑 성향 테스트

예시: EventCard, UserCard, ProductCard가 있을 때

A. cards/ 폴더에 모아둔다
→ 형태/역할 기준 그룹핑

B. events/, users/, products/ 각각에 둔다
→ 도메인/기능 기준 그룹핑  

C. components/ 에 다 두되 접두사로 구분
→ 플랫한 구조 선호

3. 실용적 기준 세우기

질문6 "내가 자주 동시에 수정하는 파일들은?”

// 실제 개발하면서 관찰해보기

"이벤트 생성 기능 만들 때"
→ EventForm.vue + eventStore.js + eventApi.js
→ 이런 파일들은 가까이 두자!

"UI 디자인 수정할 때"  
→ Button.vue + Input.vue + Modal.vue
→ 이런 것들도 가까이 두자!

 

 

질문7 "내가 가장 싫어하는 불편함은?”

// 개인적 스트레스 포인트

A. "파일 찾는 데 시간 오래 걸리는 것"
→ 예측 가능한 네이밍과 위치 중시

B. "같은 코드를 여러 곳에서 복붙하는 것"
→ 재사용성 우선, utils/ 폴더 중시

C. "폴더가 너무 깊어지는 것"  
→ 플랫한 구조 선호, 최대 3depth

D. "파일이 너무 많아서 헷갈리는 것"
→ 세분화된 폴더 구조 선호

 

 

4. 실험하고 검증하기

질문8 "이 구조로 3개 기능 만들어보니 어때?”

// 실제 사용 후 평가

기능 1: 이벤트 목록
기능 2: 사용자 프로필  
기능 3: 파일 업로드

"새 기능 추가할 때마다 어디에 둘지 고민됐나?"
"비슷한 코드 찾기 쉬웠나?"
"파일명만 봐도 역할을 알 수 있었나?"

 

질문9 "1주일 후에 내 코드를 다시 봤을 때?"

// 기억력 테스트

"아, 저 기능은 어디에 뒀더라?"
→ 10초 안에 찾았으면 좋은 구조
→ 30초 이상 걸렸으면 구조 개선 필요

"이 파일이 뭘 하는 파일이지?"
→ 파일명만 봐도 기억났으면 좋은 네이밍
→ 파일 열어봐야 알겠으면 네이밍 개선 필요

내 스타일 정의하기

질문10 "내 개발 철학은 뭔가?"

// 우선순위 정하기

A. "빠른 개발이 최우선"
→ 구조는 단순하게, 네이밍은 직관적으로

B. "깔끔한 코드가 최우선"  
→ 관심사 분리 철저히, 재사용성 중시

C. "미래 확장성이 최우선"
→ 처음부터 복잡한 구조도 OK

D. "학습 효과가 최우선"
→ 다양한 패턴 실험해보기

10가지 질문 활용 예시

// 내 답변들:
1. 3개월 정도 할 예정 → 중간 복잡도
2. 30-50개 파일 예상 → 적당한 분류 필요
3. 기능별로 찾는 스타일 → features/ 선호
4. UI 먼저 만드는 스타일 → components/ 우선
5. 도메인별 그룹핑 선호 → events/, users/ 분리
6. Form + Store + API 자주 같이 수정 → 같은 폴더에
7. 파일 찾기 어려운 게 스트레스 → 명확한 네이밍
8. 실험 결과: features/ 구조가 편했음
9. 1주일 후에도 쉽게 찾았음
10. 학습 중이니까 다양한 패턴 시도해보고 싶음

// 결론: 내 스타일
src/
├── features/        # 기능별 그룹핑
│   ├── events/
│   └── users/  
├── shared/          # 공통 요소들
│   ├── components/
│   ├── utils/
│   └── api/
└── assets/

됐고, 그래서 팀으로 할 때는 어떻게 해야하는데?
⎯ ‘합의’가 ‘완벽함’보다 중요하다

개인 vs 협업의 차이점

개인 프로젝트: "내가 편한 구조" = 정답
협업 프로젝트: "팀이 합의한 구조" = 정답

// 실제 상황
개인: 내 스타일대로 features/ 구조 사용
협업: 팀원 A는 features/ 선호, B는 components/ 선호
     → 누구 말을 들을 것인가?

우리가 해야 할 구체적인 행동들

프로젝트 시작 전: 팀 컨텍스트 파악하기

// 첫 번째 미팅에서 나눠야 할 대화들

"우리 팀원들이 어떤 경험을 가지고 있나요?"
→ 신입 많으면: 단순하고 직관적인 구조
→ 경력자 많으면: 복잡해도 효율적인 구조

"회사에 기존 프로젝트 컨벤션이 있나요?"
→ 있으면: 기존 패턴 따르기 (일관성 우선)
→ 없으면: 새로 만들되 다른 팀과 충돌 없게

"이 프로젝트 규모와 기간은?"
→ MVP(2주): 빠른 개발 우선, 구조는 심플하게
→ 장기 서비스(6개월+): 유지보수성 우선, 구조 탄탄하게

 

구조 설계 미팅: 구체적 시나리오로 논의

// 추상적 논의 금지! 구체적 예시로만!

❌ "우리 어떤 구조로 할까요?"
✅ "어드민 사용자 관리 기능을 어디에 둘까요?"

// 실제 논의 예시
팀원 A: "admin/users/에 UserList.vue, UserForm.vue 두자"
팀원 B: "components/admin/에 두고 views/admin/에서 조립하자"  
팀원 C: "features/user-management/에 두자"

→ 각자 이유 설명하고 장단점 비교
→ 투표나 토론으로 결정

 

우선순위 합의: 트레이드오프 명확히 하기

// 팀 가치 정렬하기
"우리 팀에게 가장 중요한 것은?"

Option A: 빠른 개발 속도
→ 구조는 단순하게, 중복 코드 허용

Option B: 코드 품질  
→ 구조 복잡해도 재사용성 우선

Option C: 신입 학습 곡선
→ 직관적이고 예측 가능한 구조

Option D: 유지보수성
→ 처음엔 복잡해도 장기적 관점

// 팀 투표 결과에 따라 구조 결정!

 

작은 실험으로 검증하기

// "논쟁보다는 실험"

1주차: 각자 선호하는 방식으로 작은 기능 하나씩 만들어보기
A팀원: features/auth/ 방식
B팀원: components/auth/ 방식  
C팀원: pages/auth/ 방식

2주차: 서로 코드 리뷰하며 장단점 체감하기
"실제로 코드 찾기 쉬웠나?"
"새 기능 추가할 때 어디에 둘지 고민됐나?"
"다른 사람 코드 이해하기 쉬웠나?"

3주차: 결과 바탕으로 팀 컨벤션 결정

 

문서화와 가이드라인 만들기

// 팀 합의 내용을 README에 기록

# 우리 팀 디렉토리 구조 가이드

## 기본 원칙
- 기능별 그룹핑 우선 (features/)
- 공통 컴포넌트는 shared/components/
- 어드민과 사용자 기능 분리

## 파일 배치 규칙
- 새 기능: features/{기능명}/ 폴더 생성
- CRUD 세트: 같은 폴더에 배치
- 공통 로직: shared/utils/ 또는 composables/

## 네이밍 컨벤션  
- 컴포넌트: PascalCase (UserList.vue)
- 파일/폴더: kebab-case (user-management/)
- 상수: UPPER_SNAKE_CASE

## 애매할 때 결정 방법
- Slack #dev-team 채널에서 논의
- 24시간 내 답변 없으면 기존 패턴 따르기
- 결정 사항은 이 문서에 업데이트

 

지속적인 소통과 개선

// 정기 회고 (2주마다)

"현재 구조에서 불편했던 점들"
- 구체적 사례와 함께 공유
- "이 파일 찾는 데 5분 걸렸어요"
- "비슷한 기능이 3곳에 흩어져 있어요"

"개선 제안들"  
- 작은 것부터 시도
- "새 기능부터 이 방식으로 해봐요"
- 전체 리팩토링은 마일스톤 단위로

"다음 2주 실험"
- 한 가지씩만 바꿔서 효과 측정

 

협업에서는

  1. 완벽한 구조는 없다 - 팀이 합의한 구조가 최선
  2. 논쟁보다 실험 - 말로 하지 말고 실제로 써보기
  3. 점진적 개선 - 처음부터 완벽하려 하지 말기
  4. 문서화 필수 - 합의 내용을 기록으로 남기기
  5. 소통이 핵심 - 불편함이 생기면 바로 공유하기

그건 이상이고,
현업에서 언제 ‘실험-검증’하고 있어?

현업의 진짜 현실

이상: "1주일 실험해보고 결정해요"
현실: "내일까지 기능 완성해야 하는데 구조 고민할 시간이 어딨어?"

이상: "팀 전체 합의 도출"  
현실: "시니어가 "이렇게 해" 하면 그대로 하는 거임"

이상: "점진적 개선"
현실: "레거시 건드리면 버그 날까봐 무서워서 그냥 둠"

 

그럼 현업에서는 실제로 어떻게 하는가?

 

기존 프로젝트 패턴 그대로 따라하기

// 신입/이직자가 와서 하는 일
1일차: "우리 프로젝트 구조가 어떻게 되어있는지 보세요"
2일차: "기존 코드 보고 똑같이 만드세요"  
3일차: "왜 이렇게 되어있는지 물어보지 마세요, 시간 없어요"

// 현실적 적응 전략  
→ 일단 기존 패턴 파악하고 따라하기
→ 불편해도 당분간 참기
→ 신뢰 쌓인 후에 조심스럽게 개선 제안

 

"작은 범위"에서만 조심스럽게 시도

// 전체 구조 바꾸자고? → "미친놈" 취급
// 새 기능 하나만 다른 방식으로? → "음... 해볼까?"

예시:
"이번 새 어드민 기능만 features/ 방식으로 해봐도 될까요?"
→ 기존 코드는 안 건드리고, 새 폴더만 다르게

성공하면: "아, 이 방식 괜찮네? 다음에도 써볼까?"
실패하면: "역시 기존 방식이 나아" 끝.

 

시니어/팀리드의 성향 파악하기

// A타입 시니어: 보수적
"기존 방식 건드리지 마, 검증된 거니까"
→ 전략: 기존 패턴 완전 숙지 후 아주 작은 개선만

// B타입 시니어: 개방적  
"더 좋은 방법 있으면 써봐"
→ 전략: 근거 있는 제안, 리스크 분석과 함께

// C타입 시니어: 실무 중심
"일정만 맞추면 뭐든 상관없어"  
→ 전략: 개발 속도 향상시키는 구조 제안

 

회사/팀 컬처 읽기

// 스타트업
→ 빠른 개발이 최우선, 구조는 이차적
→ 실험 허용도 높지만 시간은 없음

// 대기업  
→ 안정성 최우선, 변화 매우 신중
→ 문서화된 가이드라인 존재 가능성 높음

// 개발팀 규모
→ 3-5명: 비공식적 합의, 빠른 의사결정
→ 10명+: 공식적 프로세스, 느린 의사결정

현실적인 "우리" 전략

신입/주니어라면

1단계: "관찰자" (1-2개월)
- 기존 구조 완전 파악하기
- 불편한 점 기록하되 말은 안 하기  
- 팀 컬처와 시니어 성향 파악

2단계: "조심스러운 제안자" (3-6개월)  
- 아주 작은 개선사항 제안
- "이거 하나만 바꿔봐도 될까요?" 수준
- 근거와 함께 제안 (시간 절약, 버그 감소 등)

3단계: "신뢰받는 개선자" (6개월+)
- 팀원들이 의견 물어볼 때만 적극적으로
- 새 프로젝트나 큰 기능에서만 구조 제안

 

중급/시니어라면

1. "점진적 개선" 전략
- 전체 리팩토링 대신 "보이스카우트 룰"
- 건드리는 코드만 조금씩 개선
- "이 기능 수정하면서 구조도 조금 정리했어요"

2. "교육" 전략  
- 주니어들에게 좋은 구조 알려주기
- 코드 리뷰에서 구조 관련 피드백
- "왜 이렇게 했는지" 설명해주기

3. "기회 포착" 전략
- 새 프로젝트, 대규모 기능 추가 시점 활용
- "이번 기회에 구조 개선해보면 어떨까요?"

가장 현실적인 접근법

"완벽한 구조보다 팀의 생산성이 더 중요”

현실에서는:
1. 기존 구조 존중하기 (레거시도 나름의 이유가 있음)
2. 작은 개선부터 시작하기 (혁명 말고 개량)  
3. 팀원들과 신뢰 관계 먼저 쌓기
4. 비즈니스 임팩트가 있는 개선에 집중
5. "구조보다 기능 완성"이 우선임을 인정

// 가장 중요한 것
"내가 원하는 완벽한 구조" < "팀이 함께 일할 수 있는 구조"
// 구조 개선 제안할 때 절대 성공 공식
"이렇게 하면 개발 시간이 30% 단축됩니다" ✅
"이 구조가 더 깔끔해요" ❌

"버그 발생 가능성이 줄어들어요" ✅  
"이론적으로 더 좋은 패턴이에요" ❌

"신입이 적응하기 쉬워져요" ✅
"Best Practice라고 들었어요" ❌

 


 

디렉토리 구조에 정답은 없다. 하지만 "왜 이렇게 구성했는가"를 설명할 수 있는 구조가 좋은 구조다.

혼자 할 때는 실험하고, 팀으로 할 때는 합의하고, 현업에서는 현실을 받아들이자.

저작자표시 비영리 변경금지 (새창열림)
'Computer Science' 카테고리의 다른 글
  • 나도 쓰레드 써보자
  • AOP 적용했는데 코드가 더 늘어났습니다
  • 당신의 사용자는 2초를 못 기다립니다
  • recv()로 받은 데이터, 어디까지가 한 덩어리일까?
한비(BIBI)
한비(BIBI)
IT 업계에서 오랫동안 일 하고 싶습니다. 가능하다면 죽을 때까지 배우며 살고 싶습니다. 마케팅과 CX 분야에서 커리어를 쌓았습니다. 지금은 IT 업계에 더 깊이 있게 기여하고자 개발 공부를 하고 있습니다. 이 배움의 여정을 글로 남기고 싶어 블로그를 시작했습니다.
  • 한비(BIBI)
    0과 1로된 세상
    한비(BIBI)
  • 전체
    오늘
    어제
    • 분류 전체보기 (33)
      • 크래프톤 정글 (5)
      • Computer Science (10)
      • 읽고 쓰고 생각하기 (1)
      • 일하면서 배웁니다 (1)
      • TIL (15)
  • 링크

    • LinkedIn
    • Threads
    • Twitter
  • 인기 글

  • 태그

    크래프톤정글
    시스템설계
    뉴스피드시스템
    정글후기
    데이터시각화
    운영체제구조
    컴퓨터과학입문
    나만무프로젝트
    CPU스케줄링
    gpt인프라
  • hELLO· Designed By정상우.v4.10.4
한비(BIBI)
디렉토리 구조, 진짜 어떻게 해야 하는데?
상단으로

티스토리툴바