모든 포스트
All Posts

컴포넌트 폴더구조 고민하기

최근 회사에서 팀원들과 프로젝트의 폴더구조에 대해서 이야기하는 미팅을 여러차례 가졌다. 중점적으로 논의한 것은 컴포넌트를 어떠한 기준으로 폴더링 할 것인가였는데 프로젝트가 시간이 흘러 커지며 현재의 구조로는 여러가지 불편함을 갖게 되었기 때문이다.

스크린샷 2023-09-11 오후 10.56.23.png

현재 프로젝트 폴더구조의 문제점

  1. pages 폴더 하위에 Component.tsx 파일이 존재하여 컴포넌트 이름으로 라우팅이 가능하다(?)

    메인 프로젝트의 태생은 Next.js가 아니라 CRA 프로젝트였고 시간이 지나면서 Next.js로 마이그레이션을 진행했다. Next.js를 사용해 본 사람은 잘 알겠지만 파일시스템 기반의 라우팅을 지원해 pages 폴더 하위의 폴더 또는 파일 구조에 따라서 라우팅이 가능하다. 그런데 이 프로젝트의 태생이 Next.js가 아니었던지라 기존에 pages 폴더 하위에 해당 페이지에서 사용하는 컴포넌트의 파일도 함께 위치하고 있었고 이로 인해 컴포넌트 파일도 path가 되어 라우팅이 가능한 기묘한 현상이 나타나게 되었다.

  2. 기준없이 뒤섞이고 비대해진 Components 폴더

도메인 로직을 갖지 않은 공통 컴포넌트들은 root 폴더 바로 밑의 components 폴더에 모아두었다. 아토믹 디자인 패턴을 일부? 차용하여 Components 폴더 밑에서 Atoms과 그 외의 컴포넌트로 분류하여 담아두었는데, 이 방법도 썩 효과적이진 못했다. Atoms의 조건에 해당하는 컴포넌트들은 그리 많지 않았기 때문에 그 외의 컴포넌트들이 모여있는 부분은 기준없이 뒤섞이고 비대해 졌다.

폴더구조를 개선하기 위한 여러가지 방법

스크린샷 2023-09-11 오후 11.05.15.png

따라서 이러한 문제점을 개선하기 위해 팀원 들과 폴더구조 개선 방향에 대해 논의 하기로 했다. 회의에 앞서 어떤 방법이 좋을지 고민하면서 다음과 같은 글을 찾아 읽고, 팀원들에게 공유했다.

공유한 아티클 중 특히 이 글을 참고하여 기능별 혹은 도메인 별로 폴더구조를 가져가는 방안에 대해서 많은 이야기를 나누었고 팀원들 다수가 기능별 폴더링의 장점에 대해서는 공감하지만 현재 프로젝트에 개선 방법으로 도입하기 에는 어려움이 있다는 결론에 다달았다.

우리가 어려움으로 꼽은 이유와 나눈 의견들은 다음과 같았다.

  • 현재 서비스에서 ‘기능별’ 분류라는 것이 다소 모호하거나 주관적인 판단이 분류에 개입될 여지가 크다.
    • 차량그룹등록 페이지 내의 보험정보 등록 컴포넌트는 차량그룹등록 기능으로 분류되어야 할까? 보험등록 기능으로 분류되어야 할까?
  • 새로운 프로젝트를 구성하는 것이라면 모르겠지만, 지금의 프로젝트에 기능별 분류를 도입한다면 현재 컴포넌트 폴더링 + 기능별 컴포넌트 폴더링이 혼재한 과도기(?)가 존재할 것이고 오히려 더 복잡해질 것 같다.
  • 사실 ERP에 가까운 우리 서비스의 특성상 대부분의 페이지가 이미 기능별로 분류되어 있기 때문에 페이지별로 컴포넌트를 분류하는 것이 기준이 더 명확하며 응집도도 높은 분류가 될 것이다.

페이지 기반의 컴포넌트 분류 폴더링하기

따라서 현재 프로젝트에 도입이 더 수월하고 직관적인 기준의 페이지 기반 컴포넌트 폴더링을 도입 하기로 했다.

domainComponents 구조

├── components // common한 컴포넌트
├── domainComponents   
│   ├── contract   
│   │   └── detail  // 페이지 
│   │   │   ├── Table
│   │   │   │   ├── Row
│   │   │   │   │   ├── Row.tsx
│   │   │   │   │   ├── Row.style.ts
│   │   │   │   │   └── index.ts
│   │   │   │   ├── Header
│   │   │   │   │   ├── Header.tsx
│   │   │   │   │   ├── Header.style.ts
│   │   │   │   │   └── index.ts
│   │   │   │   ├── useTable.tsx
│   │   │   │   ├── Table.tsx 
│   │   │   │   ├── Table.style.ts
│   │   │   │   └── index.ts 
│   │   │   ├── DatePicker
│   │   │   │   ├── Hooks
│   │   │   │   │   ├── useDate.ts
│   │   │   │   │   └── useTime.ts
│   │   │   │   ├── DatePicker.tsx 
│   │   │   │   ├── DatePicker.style.ts
│   │   │   │   └── index.ts 
│   │   │   ├── detail.const.ts
│   │   │   ├── detail.utils.ts
│   │   │   ├── detail.type.ts
│
├── pages
│   ├── contract
│      └── detail
│
└── package.json

1. domainComponents 하위의 폴더구조는 Pages 하위의 폴더구조를 따라간다.

즉 domainComponents는 라우팅 구조에 따라서 폴더를 생성하고, 해당 페이지(폴더) 하위에 종속되는 컴포넌트들을 생성합니다.

2. 컴포넌트 파일명은 컴포넌트이름.tsx로 작성한다.**

Table 컴포넌트일 경우 Table 폴더를 만들고 그 밑에 Table.tsx 파일을 만들어 컴포넌트를 작성합니다. 스타일 파일은 Table.style.ts, 상수 파일은 Table.const.ts와 같이 작성합니다. 단 custom hook의 경우 use -.tsx 파일명으로 생성합니다.

custom hook의 파일명을 Table.hook.ts로 작성하지 않는 이유는 해당 컴포넌트에서 두 개의 custom hook을 사용하게 될 수 있기 때문입니다. 그리고 한 컴포넌트 내에서 두 개의 커스텀 훅을 사용할 경우, hooks 폴더를 생성하고 그 밑에 사용하는 여러 개의 커스텀훅을 넣어 놓습니다.

3. 컴포넌트의 export는 index.ts 파일에서 한다.

// Table 폴더의 index.ts 파일

export * from './Table';

const, type, util 등의 파일은 ‘페이지’ 당 하나씩 생성한다.

예시로 contract/detail 페이지에서 사용되는 const, type, util 등이 있다면 하위 컴포넌트 폴더가 아니라 페이지 폴더 하위에 detail.const.ts와 같이 파일을 만들어 작성합니다. 만약 두 개 이상의 페이지에서 사용되는 const, type, util 등이라면 root폴더 하위에 있는 공통의 constants, types, utils 폴더 밑에 파일을 생성하여 작성합니다.

domainComponents라는 개선방안에서 여전히 아쉽거나 고민되는 점

  • domainComponents라는 폴더명은 과연 적절한 것일까?
    • domainComponents의 디테일한 구조를 정의 하면서 애매 하다고 느꼈던 점은 Component라는 이름의 폴더 하위에 const, hook, util 등의 파일이 위치하게 된다는 것이었다. 물론 해당 컴포넌트에 직접적으로 사용되는 파일만 위치하도록 하고 두 개 이상의 컴포넌트에서 사용하게 되면 common 영역으로 빼도록 규칙을 정했지만 그래도 여전히 남는 찝찝함이란… 🥲
  • 페이지 기반의 컴포넌트 폴더링은 괜찮은 방법인걸까?
    • 폴더링 방법에 정답이야 없지만 그래도 일반적으로 통용되는 방식의 범주에 있는지가 궁금해졌다. 이 궁금증은 재밌게도 모노레포 이렇게 좋은데 왜 안써요?라는 의외의 글에서 해소 되었는데 해당 글에서는 pages-modules라는 폴더 하위에 도메인 별로 분류하여 관리하는 것을 볼 수 있었다. components 뿐만 아니라 각종 모듈도 페이지 기준의 분류법을 적용한 것인데, 이 사례를 보면서 페이지 기반의 컴포넌트 폴더링이 나쁘지 않은 선택 이였다는 힌트를 얻을 수 있었고 나아가 컴포넌트 뿐만 아니라 다른 모듈들도 페이지 기준으로 분류하는 page-modules의 방법도 고려해 봄 직 하다는 생각이 들었다.

좋은 폴더구조란 어떤 것일까?

폴더구조는 프로젝트의 첫인상이다. 책으로 따지면 목차 같은 것인데, 이 목차가 잘 구성되어 있다면 한 눈에 책의 흐름이나 구조를 파악할 수 있다. 폴더구조도 마찬가지로 잘 정리되어 있다면 프로젝트의 아키텍처나 흐름을 단번에 파악할 수 있도록 해줄 것이며, 코드의 응집도를 높혀 수정이 용이하도록 할 것이다. 곱씹어 생각할 수록 참 기본적이지만 중요한 부분이라는 생각이 든다.

그렇다면 과연 좋은 폴더구조란 어떤 것일까? 정답은 없지만 이번에 고민해보며 몇 가지 중요하다고 생각한 점을 꼽아보자면 아래와 같다.

  • 프로젝트의 아키텍처나 흐름을 파악하는데 도움을 주는 구조
  • 연관성이 높은 것들끼리는 가까이 있어 응집도가 높은 구조
  • 원하는 파일의 위치를 바로 예측하고 쉽게 찾아갈 수 있는 구조
  • 일관성 있는 철학이 있는 구조 (파일을 작성할 때 A에 넣어야 하나, B에 넣어야 하나 고민하지 않을수 있도록)
  • 가능하다면 depth가 너무 깊지 않은 구조

프로젝트 폴더구조에 대해서 다시금 고민해보면서 느낀 점

최근 폴더구조나 컴포넌트 구성에 대해 고민하다보면 프로젝트(서비스)는 끊임없이 변화하며 성장한다는 점에서 마치 유기체 같다는 생각을 하게된다. 곤충이 성장하기 위해 주기적으로 탈피를 하며 새로운 옷을 입 듯 프로젝트도 성장하며 그 규모와 특성에 따라 적절한 변화가 필요하다.

이 글에서 이야기 하듯 규모가 작은 프로젝트에서는 계층별 구조로도 충분할 수도 있고 성장해가면서 또 다른 구조가 필요할 수도 있다. 처음부터 프로젝트 구조를 잡을 때부터 대규모 프로젝트를 고려한 구조를 만들수도 있겠지만 어찌보면 그것이 오버엔지니어링일 수도 있는 것이고, 또 프로젝트 구조라는 것이 단순히 규모의 문제만은 아니기 때문에 참 어렵다. 🤔

프로젝트는 시간이 흐르며 다양한 변화를 맞이 하겠지만 폴더구조의 개선 혹은 변경은 비용이 상당히 많이 드는 일이라는 점에서 최대한 적은 변경으로 서비스를 확장해 나갈 수 있도록 미리 생각해보는 것이 필요하겠다. 그러니까 내가 고민해야 할 것은, 어떠한 프로젝트를 만났을 때 그 프로젝트의 규모나 특성 그리고 앞으로의 확장성을 고려하여 적절한 옷을 입힐 수 있도록 여러가지 구조를 고민해보고 Best practice를 찾는 것이 아닐까