[React] 프로젝트 구조 설계
✨ 프로젝트 구조의 중요성
사실 개인 프로젝트 개발을 할 때에는 CRA 또는 CNA를 활용하여 내 입맛에 맞게 편하게 개발을 하면 된다.
하지만, 만약 협업을 하는 경우나, 대형 프로젝트에 참여하는 경우 얘기가 달라진다.
만약 협업을 하는 상황에서 개발자 개개인이 자신이 원하는대로 경로를 설정하고, 폴더 및 컴포넌트를 생성한다면 어떻게 될까? 프로젝트 초기에는 그 문제가 수면 위로 떠오르지 않겠지만, 점차 프로젝트가 진행되며 사이즈가 커질 수록 에러 발생률이 높아지고 프로젝트 개발 진행속도도 굉장히 느려지게 되며, 종국에는 프로젝트가 중단되며 재정비를 해야하는 상황에 이르를 수도 있다.
따라서 우리는 프로젝트를 진행하기 전, 명확한 가이드라인과 약속을 해야 한다. 이러한 가이드라인 하에 여러명의 팀원들로 이루어진 팀이지만, 한 명의 개발자가 일관된 기준 하에 코딩을 하듯이 프로젝트가 진행될 수 있도록 청사진을 미리 그려놔야 한다.
🤝 기본 약속
우선, 본문 작성에 앞서 필자가 추천 또는 주장하는 내용이 보편적이며 최고의 방식이 아닐 수 있음을 밝히고싶다.
이는 그저 주관적으로 선호하는 방식이자 개인적인 선택일 뿐이기 때문에 반드시 이를 따라야 할 필요는 없다!
즉, 위에서도 설명했듯이 본문의 골자는 ‘협업 친화적이고효율적인 프로젝트 구조를 어떻게 설계할 수 있는가? 이다. 따라서 우리는 우선적으로 프로젝트 설계에 있어 기본적인 약속(컨벤션)을 사전에 정의해둬야 한다.
= “정답은 없다!”
작성법 | 예시 |
---|---|
스네이크 케이스 | snake_case |
파스칼 케이스 | PascalCase |
카멜 케이스 | camelCase |
보통 필자는 위의 기본사항들에 대한 약속을 팀원들에게 제안하는 편이다. 물론 다른 방식의 더 효율적이고 좋은 컨벤션이 존재할 수도 있지만… (나는 이게 편하다…!!)
중요한 것은 “일관된 기준”이기에, 팀원들 간에 어떤 방식의 컨벤션을 정하던 그 기준을 명확히 이해하고 따르는 것에 중점을 둬야 할 것이다.
🗂 필수 디렉토리
프로젝트를 구성할 때 반드시 필요한 디렉토리 구조다.
이는 기본적으로 “파일 유형에 따른 분리 원칙”을 따른다.
components
없어서는 안될 폴더다.
‘단일책임 원칙‘을 따르며 재사용성을 항시 고려해야 하는 컴포넌트들을 한 곳에 모아줄 components 폴더를 src 내에 생성해두어야 한다. 이 때 헷갈리지 말아야 할 것은 pages 또는 routes와 component의 차이다. 컴포넌트는 위에서도 설명했듯, 하나의 기능 또는 책임을 위해 존재하는 하나의 레고블록 같은 것이다. 따라서 이러한 컴포넌트들을 조립하여 하나의 페이지를 구성해야 하기 때문에 page와 component의 차이점을 분명히 짚고 넘어가야 한다.
constants
“백그라운드 컬러 #FFFFFF에서 #F9F7F7로 변경해주세요“
만약 이 말을 프로젝트가 굉장히 진척된 상황에서 듣게 된다면 어떨까?
백그라운드 컬러 또는 특정 높이나 넓이 값, enum 등 모든 것들을 상수화 하지 않고 일일이 수기로 입력해뒀다면 위와 같은 수정사항을 받았을 때 우리는 하나하나 모든 것들을 수기로 고쳐야 할 것이다.
이러한 상황을 방지하고, 미세한 오차 또는 실수를 줄이기 위해 우리는 자주 사용되는 값들을 constants에 담아 활용해야 한다. 이에는 대표적으로 색상, 사이즈 등이 있을 수 있다.
constants는 말 그대로 “상수”다. 따라서 고정된 값을 여러 군데에서 반복적으로 범용해야 할 경우, 반드시 constants에 입력해둔 뒤 import
하여 사용하도록 하자.
hooks
커스텀 훅을 모아두는 폴더다.
프로젝트 사이즈가 굉장히 작을 경우에는 필요 없을 수도 있지만, 어느 정도의 규모라면 반드시 필요할 것이다. useState
, useEffect
뿐만 아니라 여러 컴포넌트에서 활용될 수 있는 커스텀 훅이 있다면 hooks
폴더에 입력해둔 뒤 import
하여 사용하는 것이 좋다.
pages
컴포넌트들로 이루어진 하나의 온전한 페이지다.
때에 따라 routes로 명하는 경우도 있지만, 필자는 주로 pages라는 이름을 사용한다. 이러한 pages들을 모아 App.jsx 파일에 import
해주면 된다.
styles
CSS 파일들이 들어간다
필자는 보통 styled-components를 주로 사용하기 때문에 styles에 많은 파일들을 넣지 않는다. 하지만 Globalstyles와 같은 전역 rest CSS 파일을 넣어둘 때 활용한다.
하지만 styled-components를 사용한다고 해서 구조를 완전히 무시해도 되는 것은 아니다. CSS 코드가 컴포넌트 내 상단에 위치하게 된다면 굉장히 가독성이 떨어질 수 있기에 jsx 최하단에 배치하거나, 아니면 이 또한 파일로 분리하여 import
하여 사용할 수 있도록 구성해야 한다.
utils
유틸 함수들이 들어가는 폴더다.
getApiData()
, getDate()
와 같이 여러 컴포넌트에서 자주 활용될 수 있는 함수들을 utils 폴더에 담아둔 뒤 전역적으로 활용할 수 있도록 해야한다. 만약 날짜를 변환하는 함수를 8~9개의 함수에 각각 일일이 입력하여 사용한다면 이는 굉장히 비효율적이고 에러 발생 확률 및 유지보수성도 굉장히 떨어질 것이다.
따라서 반드시 두개 이상의 컴포넌트에서 활용될 수 있는 함수라면, utils 폴더에 담아 활용하도록 하자.
🪅 아토믹 디자인
“atomic”, 즉 원자처럼 쪼갤 수 있을 때 까지 완전히 분해하여 디자인하는 패턴을 의미한다.
이는 기본적으로 위에서 필자가 강조한 “파일 유형에 따른 분리 원칙”에서 더 나아가 컴포넌트가 원자처럼 “완전히 독립적이고 단일한 기능“을 담당할 수 있도록 명확히 분리하는 것을 의미한다.
위의 설명에선, 단순히 components, pages, utils 등등, 유형에 따라 큰 단위로만 분류하는 것에 그쳤었다. 하지만 아토믹 디자인을 따르게 된다면 components 안에서도 그 유형 및 기능별로 조금 더 디렉토리가 세분화되어 구분되어지는 것이다.
만약, 한 페이지 내에서 하나의 nav바가 존재한다고 가정했을 때 아래와 같이 Nav라는 컴포넌트를 아래와 같이 분해할 수 있을 것이다.
Nav 컴포넌트의 분해 과정
처음에 필자가 위에서 설명한 큰 틀에서의 분리만 진행했다면, 그냥 위의 nav
는 하나의 컴포넌트로 구성됐을 수도 있다. 하지만 atomic 패턴을 적용한다면, nav
안에서도 독립적인 기능을 하며 재사용성을 고려해야 할 작은 단위의 컴포넌트가 존재할 수 있다. 따라서 위와 같이 컴포넌트 내에서도 또 다른 분류가 필요하다.
- Nav 컴포넌트에 Logo, Menu, SearchBar 컴포넌트를 각각 모아서 조립해준다고 가정하여 구조를 잡는다.
- Nav 안에서 로고는 다른 곳에서 활용될 확률이 높기에 이 또한 하나의 독립된 컴포넌트로 분리한다.
- 메뉴바에는 여러 menu list들이 존재할 수 있다. 따라서 독립적 기능을 할 수 있도록 이 또한 분리한다.
- Search바 또한 마찬가지로 하나의 독립된 컴포넌트로 분리한다.
이처럼 컴포넌트를 하나의 큰 덩어리로 보는 것이 아니라, 컴포넌트 내에서도 세부적인 분리를 하는 것을 아토믹 디자인/패턴 이라고 할 수 있다.
따라서 이와 같이 components 폴더 내에서도 하나의 큰 덩어리가 될 수 있는 컴포넌트에 대한 폴더를 다시 만들고, 그 컴포넌트 내에 세부적 컴포넌트들을 각각 구성시켜야 한다.
참조 링크
- https://ko.reactjs.org/docs/faq-structure.html
- https://www.stevy.dev/react-design-guide
- https://ui.toast.com/weekly-pick/ko_20200213
- https://smoh.tistory.com/385
[React] 프로젝트 구조 설계