ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 최신 React 폴더 구조: 5단계로 쉽게 알아보는 리액트 프로젝트 구성법
    Javascript 2024. 10. 18. 19:09

    최신 React 폴더 구조: 5단계로 쉽게 알아보는 리액트 프로젝트 구성법

    리액트(React) 애플리케이션을 체계적으로 구성하는 것은 프로젝트가 커질수록 더 중요한 요소로 자리잡는데요.

     

    특히 파일과 폴더를 어떻게 잘 정리하느냐가 프로젝트의 성공 여부에 큰 영향을 미치게 됩니다.

     

    많은 분들이 이 부분에 대해 궁금해 하시더라고요.

     

    그래서 이번 글에서는 제가 여러 해 동안 리액트 프로젝트를 진행하면서 자연스럽게 익힌 폴더 구조를 5단계로 정리해 보려고 합니다.

     

    이 방법은 소규모 프로젝트부터 대규모 애플리케이션까지 모두 적용할 수 있는 실용적인 가이드인데요.

     

    각 단계마다 어떻게 구성할지 알아보도록 할까요?

     


    1. 하나의 React 파일에서 시작하기

    첫 번째 단계는 아주 간단합니다.

     

    대부분의 리액트 프로젝트는 src/ 폴더 아래에 하나의 파일로 시작하는데요.

     

    이 파일에는 주로 App.js 또는 App.tsx 같은 파일이 들어 있죠.

     

    예를 들어, Vite를 사용해서 리액트 프로젝트를 시작하면 자동으로 생성되는 파일 구조가 바로 이런 형태입니다.

     

    Next.js 같은 서버 기반 리액트를 사용하는 경우에는 src/app/page.js로 시작하게 됩니다.

     

    const App = () => {
      const title = 'React';
    
      return (
        <div>
          <h1>Hello {title}</h1>
        </div>
      );
    };
    
    export default App;

     

    이렇게 처음에는 하나의 컴포넌트로 모든 기능을 구현하게 되는데요, 시간이 지나면서 이 컴포넌트가 점점 커지면 자연스럽게 작은 컴포넌트로 나누어야 할 필요가 생깁니다.

     

    예를 들어, 리스트 컴포넌트와 리스트 아이템 컴포넌트를 따로 분리해 볼까요?

     

    const list = [
      { id: '1', name: 'Robin Wieruch' },
      { id: '2', name: 'Dave Davidds' },
    ];
    
    const ListItem = ({ item }) => (
      <li>
        <span>{item.id}</span>
        <span>{item.name}</span>
      </li>
    );
    
    const List = ({ list }) => (
      <ul>
        {list.map(item => (
          <ListItem key={item.id} item={item} />
        ))}
      </ul>
    );
    
    const App = () => <List list={list} />;

     

    작은 프로젝트에서는 이렇게 여러 컴포넌트를 한 파일 안에 넣어도 괜찮은데요, 프로젝트가 커지면 파일 하나만으로는 관리하기 어려워집니다.

     

    그때부터는 여러 파일로 나누어야 할 때가 온답니다!

     


    2. 다중 React 파일로 확장하기

    두 번째 단계에서는 컴포넌트를 여러 파일로 나누는 단계입니다.

     

    예를 들어, 아까 본 ListListItem 컴포넌트를 별도의 파일로 분리해 볼까요?

     

    이렇게 나누면 코드가 훨씬 깔끔해지는데요, 폴더 구조는 이렇게 변경될 수 있습니다:

     

    - src/
      - app.js
      - list.js

     

    list.js 파일에는 ListListItem 컴포넌트가 들어가게 되는데요, 이 파일은 외부에 List 컴포넌트만 공개합니다.

     

    const ListItem = ({ item }) => (
      <li>
        <span>{item.id}</span>
        <span>{item.name}</span>
      </li>
    );
    
    const List = ({ list }) => (
      <ul>
        {list.map(item => (
          <ListItem key={item.id} item={item} />
        ))}
      </ul>
    );
    
    export { List };

     

    그리고 app.js 파일에서는 List 컴포넌트를 이렇게 가져와서 사용할 수 있죠:

     

    import { List } from './list';
    
    const list = [
      { id: '1', name: 'Robin Wieruch' },
      { id: '2', name: 'Dave Davidds' },
    ];
    
    const App = () => <List list={list} />;

     

    물론, 원한다면 ListItem도 따로 파일로 분리할 수 있습니다.

     

    하지만 ListItemList 컴포넌트와 밀접하게 연관되어 있고 다른 곳에서 재사용되지 않는다면 굳이 분리하지 않아도 됩니다.

     

    재사용성이 있는 경우에만 별도의 파일로 분리하는 것이 좋습니다.

     


    3. 파일에서 폴더로: 컴포넌트별로 구성하기

    이제부터는 조금 더 복잡해지는데요, 각 컴포넌트가 점점 커지면서 스타일, 테스트, 유틸리티, 상수와 같은 다양한 기술적인 요소가 추가됩니다.

     

    이 모든 것을 한 파일에 넣는 것은 좋지 않기 때문에, 각 컴포넌트별로 폴더를 만들어 관리하는 것이 좋습니다.

     

    예를 들어, AppList 컴포넌트에 각각 폴더를 만들어볼까요?

     

    - src/
      - app/
        - index.js
        - component.js
        - style.css
        - test.js
      - list/
        - index.js
        - component.js
        - style.css
        - test.js

     

    이제 index.js 파일이 등장했는데요, 이 파일은 폴더의 공개 인터페이스로 사용할 수 있습니다.

     

    즉, 외부에서는 index.js를 통해서만 그 폴더의 컴포넌트를 가져올 수 있도록 하는 것이죠.

     

    import { List } from './list';
    
    export { List };

     

    이렇게 하면 외부에서 style.csstest.js 같은 내부 파일이 노출되지 않도록 관리할 수 있답니다.

     

    그리고 App 컴포넌트에서는 여전히 List 컴포넌트를 쉽게 불러올 수 있습니다.

     

    import { List } from '../list';

    4. 기술적 폴더로 정리하기

    이제 중간 규모의 리액트 프로젝트에서는 기능별로 폴더를 나누는 것 외에도, 기술적 요소별로 폴더를 나누기 시작해야 할 때입니다.

     

    예를 들어, 커스텀 훅, 컨텍스트, 그리고 다양한 유틸리티 함수들이 등장하게 되는데요, 이들을 정리할 별도의 폴더를 만들어야 할 필요가 생깁니다.

     

    예를 들어, 다음과 같은 폴더 구조를 볼 수 있습니다:

     

    - src/
      - components/
        - app/
          - index.js
          - component.js
          - style.css
          - test.js
        - list/
          - index.js
          - component.js
          - style.css
          - test.js
      - hooks/
        - use-click-outside.js
        - use-scroll-detect.js
      - context/
        - session.js
      - services/
        - error-tracking/
          - index.js
          - service.js
          - test.js
        - format/
          - date-time.js

     

    이 구조에서 components/ 폴더에는 기존의 컴포넌트들이 들어가고, 재사용 가능한 훅은 hooks/ 폴더로 이동합니다.

     

    또한, context/ 폴더는 React 컨텍스트를 관리하는데요, 컨텍스트는 여러 컴포넌트가 공유하게 되므로 별도의 폴더로 분리해야 합니다.

     

    마지막으로, services/ 폴더에는 유틸리티 함수나 서비스 로직을 넣어 관리합니다.

     


    5. 기능별 폴더로 확장하기

    마지막 단계에서는 대규모 프로젝트에서 자주 사용하는 폴더 구조인 기능별 폴더 구조를 도입합니다.

     

    이렇게 하면 특정 기능에 관련된 컴포넌트, 훅, 컨텍스트, 서비스 등을 한 곳에 모아 관리할 수 있는데요, 이 방법은 특히 여러 팀이 함께 일하는 환경에서 유용합니다.

     

    예시 폴더 구조는 다음과 같습니다:

     

    - src/
      - feature/
        - user/
          - profile/
          - avatar/
        - post/
          - post-item/
          - post-list/
        - payment/
          - payment-form/
          - payment-wizard/
        - error/
          - error-message/
          - error-boundary/
      - components/
        - button/
        - input/
        - dropdown/

     

    여기서는 feature/ 폴더 아래에 각 기능별로 폴더를 만들어 관리하는데요, 예를 들어 post/ 폴더에는 게시글과 관련된 컴포넌트가 모여 있고, payment/ 폴더에는 결제와 관련된 컴포넌트들이 위치하게 됩니다.

     

    이 방식의 장점은 각 기능별로 필요한 모든 요소들을 한 곳에 모아서 관리할 수 있다는 점인데요, 기능이 독립적으로 관리되기 때문에 유지보수나 확장이 훨씬 용이해집니다.

     

    재사용 가능한 UI 컴포넌트들은 여전히 components/ 폴더에 남겨두게 됩니다.

     

    예를 들어 Button, Input, Dropdown 같은 컴포넌트들은 여러 기능에서 공통적으로 사용될 수 있기 때문에, 이들은 components/에 두는 것이 좋습니다.

     

    또한, 만약 PostItem 같은 컴포넌트에서 Button이나 Dropdown 같은 재사용 가능한 컴포넌트를 필요로 한다면, components/ 폴더에서 해당 컴포넌트를 가져와 사용할 수 있습니다.

     

    import { Button } from '../components/button';
    import { Dropdown } from '../components/dropdown';

     

    서비스 로직도 마찬가지입니다.

     

    예를 들어, 결제 기능에서만 필요한 서비스 로직이 있다면, 해당 로직을 payment/ 폴더 아래에 둬서 관리할 수 있습니다.

     

    이처럼, 각 기능에 관련된 서비스는 기능 폴더 내부에 위치시키고, 여러 기능에서 공통으로 사용하는 서비스는 services/ 폴더에 두는 방식으로 정리하면 됩니다.

     


    추가: 페이지 기반 프로젝트 구조

    리액트 애플리케이션에서는 페이지가 기능별로 나누어져 있는 경우가 많습니다.

     

    특히 Next.js 같은 프레임워크를 사용할 경우, app/ 폴더 아래에 각 페이지들이 위치하게 되는데요.

     

    클라이언트 기반의 리액트 애플리케이션(Vite 등)을 사용할 때도 비슷한 방식으로 pages/ 폴더에서 각 페이지를 관리할 수 있습니다.

     

    예를 들어, 게시글을 보여주는 페이지와 개별 게시글을 위한 페이지를 각각 관리한다고 가정해 볼까요?

     

    - src/
      - pages/
        - posts/
          - page.tsx
        - [postId]/
          - page.tsx
      - feature/
        - post/
          - post-list/
          - post-item/
      - components/
        - list/
      - hooks/
      - context/
      - services/

     

    이 구조에서는 /posts URL로 접속하면 posts/page.tsx가 렌더링되고, 개별 게시글 페이지(/posts/[postId])에서는 해당 게시글의 상세 페이지가 렌더링됩니다.

     

    PostList 컴포넌트는 /posts 페이지에서 사용되고, 개별 게시글 페이지에서는 PostItem 컴포넌트가 사용되는데요, 이 둘 모두 List라는 재사용 가능한 컴포넌트를 가져다 쓰는 방식입니다.

     

    여기서 중요한 점은 재사용성입니다.

     

    예를 들어, CommentList 컴포넌트가 여러 곳에서 사용될 수 있다면, 이를 post/ 폴더에 두지 않고 별도의 components/ 폴더에 둬서 여러 페이지에서 사용할 수 있도록 하는 것이 좋습니다.

     


    마무리: React 폴더 구조는 유연하게!

    여기까지 React 프로젝트를 구성하는 5단계 방법을 살펴봤는데요.

     

    한 가지 기억해야 할 점은, 이 폴더 구조가 절대적인 정답이 아니라는 점입니다.

     

    프로젝트의 규모나 팀의 요구 사항에 따라 유연하게 적용할 수 있어야 하는데요, 프로젝트가 점점 커질수록 폴더 구조도 자연스럽게 진화하게 된답니다.

     

    기능별 폴더기술적 폴더의 적절한 구분은 팀의 협업을 더욱 원활하게 만들어주고, 코드의 가독성도 높여줍니다.

     

    새로운 기능을 추가할 때마다 고민할 필요 없이, 일관된 폴더 구조를 유지하면서 효율적으로 작업할 수 있게 됩니다.


     

Designed by Tistory.