Skip to content
Neverthless, Code



Gatsby 블로그 프로젝트 - 디렉토리 구조 설계

Feature based structure vs. Screen based structure. 프로젝트를 시작하면 초기에 디렉토리 구조를 어떤 기준으로 설계해야 할지 고민하게 된다. Feature 중심의 구조와 화면 UI 기준의 구조가 무엇이고, 어떤 상황에 무엇이 적합한지 고민해보았다.

13 min read


소프트웨어 개발에 앞서, 아키텍처 설계를 하고 Framework 를 선정하는 작업 이후에는 프로젝트 디렉토리 구조를 어떤 기준으로 구성할지 고민해야 할 것이다.

디렉토리 구조는 아키텍처 설계 또는 프레임워크에 따라 구조가 정해지게 되는데, 이번 블로그 프로젝트에서는 Gatsby 를 사용하기 때문에 이에 맞는 디렉토리 구조가 설계 되어야 할 것이다.

우선 Gatsby 기반의 프로젝트에서 구성해야 하는 기본적인 디렉토리 구조를 살펴본 후, 이번 글의 주제인 화면을 구성하는 React Component 파일 디렉토리 구조를 Feature-based 로 할지 Screen-based 로 한지 에 대한 고민을 해보도록 하자.

Gatsby 기본 디렉토리 구조

사실 Gatsby 기반이라고 해서 정해진 디렉토리 구조가 있는 것은 아니다. Gatsby 프레임워크를 기반인 경우, 설정(/gatsby-config.ts)이나, 빌드(/gatsby-node.ts, /gatsby-browser.ts 등 )를 위한 파일들은 프로젝트 최상위 디렉토리에 위치하는 것 이외에, 별도로 고려해야 할 것은 거의 없다. Gatsby 가 Default 로 참조하는 폴더가 몇가지 있으며, 이는 아래와 같다.

├── src
|  ├── pages            // 1. 페이지 폴더로 향후 Markup 으로 변환
|  |  ├── 404.tsx
|  |  ├── index.tsx
|  |  └── ...
|
├── static              // 2. JS, CSS, 이미지 등 static 파일
|  └── ...
|
├── gatsby.config.ts    // 3. Gatsby 설정파일로 Plugin 을 추가
├── gatsby.node.ts      // 4. Gatsby 에서 Build 하는 과정에 필요한 로직 추가
├── gatsby.browser.ts   // 5. 전역에 필요한 CSS, JS 등을 Import
├── package.json
└── ...

우선 TypeScript 로 작성되었기 때문에 확장자는 *.ts*.tsx(React JSX Element 를 포함한 파일) 로 되어 있고, 위의 폴더 구조에서 TypeScript 설정이나 Lint 관련된 파일을 다른 프로젝트에서도 많이 볼 수 있으므로 따로 표기하지 않았다.

  1. /src/pages : 이 디렉토리에 있는 *.jsx, *.tsx 파일은 Static Markup 파일로 변환되어 웹페이지 링크로 접속 가능해 진다. 이 중 404.tsx 파일은 페이지 링크에 없는 접속일 경우 보여지는 Exception 페이지로 자동 설정 된다.

  2. /static : JS, CSS, 이미지 등 static 파일들을 넣을 수 있는 디렉토리이며, 파일 경로는 그대로 웹페이지에서 접속이 가능하다. /src 디렉토리 안에 있는 tsx 파일에서도 import 가능하며, 자동으로 minified & compressed 되게 된다.

  3. /gatsby.config.ts : Gatsby 설정 파일이며, 적용하고자 하는 Plugin을 추가할때 이 파일에 작성해야 한다.

  4. /gatsby.browser.ts : Markup 으로 랜더링될 때 전역에 필요한 font 파일이나 CSS, JS 를 import 하는데 사용된다.

  5. /gatsby.node.ts : Gatsby 가 Build 하는 과정에 필요한 작업을 수행하도록 한다. GraphQL 로 접근하는 데이터 스키마를 새로 구성하거나 데이터를 입력하는 작업과, MDX 파일을 Markup 으로 만들기 위한 작업을 하도록 개발하는 파일로, Gatsby API 를 사용하여 로직을 구현하게 된다.

React Component 파일 디렉토리 구조

화면을 구성하는 React Component 를 화면 UI 즉 Screen 기준으로 폴더를 생성하는것이 맞는가? 아니면, Feature 기준으로 폴더를 생성하는 것이 맞는가에 대한 고민이다.

결론적으로 개인적인 의견이지만, Feature based struction 가 맞다 고 생각한다. 물론 한명이 개발한 소규모 프로젝트라면, Screen based structure 가 직관적이기에 적합할 수 있겠으나 향후 확장을 고려하거나 다수의 개발자가 참여하는 프로젝트라면, Feature 기준으로 폴더를 생성하는것이 좋겠다.

feature 를 무엇이라고 정의하고 읽어야 하는가?

'기능' 또는 '특징' 이라고 할 수 있겠다. Machine learning 에서는 데이터의 '특징' 이라는 말로 명확하게 의미 전달이 되지만, 일반적인 소프트웨어에서는 '서비스의 기능' 정도이지 않을까 생각한다. 이는 function 의 뜻하고도 혼선이 있어, 이글에서는 한글로 번역하지 않고 feature 라고 부르기로 했다. Front-end 개발자들간의 커뮤니케이션에는 문제 없을 수 있으나 ML 엔지니어나 서비스기획, PO와의 커뮤니케이션에서 분명 이슈가 있다.

내가 동료분들께 가능하다면 OS 부터 개발도구까지 영문 버전으로 사용하자는 이유도 바로 이런 혼선 때문이기도 하다. 예를 들어, Git-flow 에서 feature 를 추가하는 브랜치 생성의 한글 메뉴명이 '기능 추가하기' 인것을 보면, 이 '기능'이 function 인지 feature 인지 의미전달이 명확하지 않아 혼선이 있을 수 밖에 없다. 심지어 'funtion' 은 함수라고도 부르는데 말이다. 게다가, 우리 회사에서는 최근 Offshore 해외 개발자와 pair programming 해야하는 상황에서는 한글로 된 OS나 개발툴을 사용할 이유가 더욱 없어졌다.

UI Component들의 파일 디렉토리 구조를 설계할 때 지켜야할 원칙은, One-way Dependency 이다.

이말은, 특정 디렉토리에 위치한 UI Component 는, 공통 UI Component 를 제외하고는 반드시 같은 디렉토리 또는 하위 디렉토리의 UI Component 만 import 할 수 있다는 것이다.

블로그 프로젝트에서는 /src/components 에 UI Component 를 생성하도록 할 것이며, /src/components/common 의 UI Component 는 모든 파일에서 import 가능하지만(One-way Dependency 의 예외 영역), 그외 /src/components/* 폴더에 있는 파일은 같은 디렉토리 또는 하위 디렉토리의 파일만 import 가능하다는 것이다.

Screen based structure

UI 화면을 기준으로 디렉토리를 설계 한다면, 아래와 같을 것이다.

└── src
   ├── components
   |  ├── common             // 모든 페이지에서 사용하게 되는 공통 Component
   |  |  ├── header.tsx
   |  |  └── footer.tsx
   |  ├── main
   |  |  ├── index.tsx        // 메인 페이지
   |  |  └── post-list.tsx    // 메인 페이지에서 사용하는 블로그 포스트 목록 (최신 목록)
   |  ├── blog
   |  |  ├── index.tsx        // 블로그 포스트 목록 페이지
   |  |  └── post-list.tsx    // 블로그 포스트 목록 (전체 블로그 포스트 목록)
   |  ├── post
   |  |  ├── index.tsx        // 블로그 포스트 페이지
   |  |  └── tag-list.tsx     // 블로그 포스트에 포함되어 있는 Tag 목록
   |  ├── tag
   |  |  ├── index.tsx        // 블로그 테그 목록 페이지
   |  |  └── post-list.tsx    // 테그별 블로그 포스트 목록
   |  └── ...
   └── ...

화면을 기준으로 디렉토리를 생성하게 되면, 중복된 UI Component 가 각 화면 폴더에 중복해서 들어가야 할 것이다.

위에서 보는 바와 같이 메인 페이지, 블로그 포스트 목록 페이지, 테그 목록 페이지에서는 '블로그 포스트 목록' UI Component 가 중복해서 필요하다. 위의 디렉토리 구조는 UI Component 를 상세히 모듈화 하여 표시하지 않았기 때문에 중복되는 구조가 크게 복잡해 보이지 않지만, 실제로 상세히 모듈화 한다면 더욱 중복이 많이 발생하게 될 것이다.

이를 해결하기 위해서, 여러 페이지에서 사용해야 하는 UI Component 를 모두 /src/components/comon 폴더에 넣어야 한다면, 공통 UI Component 가 상당히 늘어나게 되는 문제가 발생하게 된다.

Feature based structure

이제 화면이 아닌 feature 기준으로 디렉토리를 구분한 예시를 살펴보자. 본 블로그 프로젝트 구조도 이와 동일한 구조로 설계되어 있다.

└── src
   ├── components
   |  ├── common              // 모든 페이지에서 사용하게 되는 공통 Component
   |  |  ├── header.tsx
   |  |  └── footer.tsx
   |  ├── blog
   |  |  ├── post.tsx         // 블로그 포스트 Component
   |  |  └── post-list.tsx    // 블로그 포스트 Component로 목록을 구현한 Component
   |  ├── tag
   |  |  ├── tag.tsx          // 블로그 테그 Component
   |  |  └── post-list.tsx    // 블로그 테그 Component로 목록을 구현한 Component
   |  └── ...
   ├── templates              // 화면 페이지들
   |  ├── main-template.tsx
   |  ├── blog-template.tsx
   |  ├── post-template.tsx
   |  ├── tag-template.tsx
   |  ├── main-template.tsx
   |  └── ...
   └── ...

위에 구조에서 볼 수 있듯이 /src/components 에는 feature 별로 디렉토리를 구분하여 구성하였고, 우리는 /src/templates 에는 화면별로 파일을 생성하였다.

/src/components/common 은 모든 화면 또는 UI Component 에서 접근이 가능하고, feature 별로 구분되다보니 /src/components/blog/src/components/tag 로 구성된 것을 볼 수 있다.

결론

프로젝트 초기 아키텍처 설계를 하고 Framework 를 선정한 이후, 프로젝트 디렉토리 구조를 어떤 기준으로 구성할지 정해야 한다.

소규모 프로젝트 이면서, 향후 확장을 고려할 필요가 없다면, 화면 단위로 디렉토리를 구성해도 문제 될 것은 없지만, 향후 확장을 고려한다면 Feature-based structure로 설계 하는 것이 좋겠다.

그리고, Feature-based Structure는 프로젝트 코드의 디렉토리 구조이기도 하지만, Design System 에서도 물론 동일하게 가져가야 하는 기준이 되어야 할 것이다.

© 2022 by Neverthless, Code. All rights reserved.
by John Kim