컴포넌트에 대한 고찰(1) - 컴포넌트 분리하기

B Y·2022년 10월 16일
1

결론

  1. 관련 있는 컴포넌트들을 모아놓자.
  2. 공통 컴포넌트는 UI라이브러리를 만든다는 생각으로 작성하자(도메인 제거).
  3. 도메인이 겹치고 + 페이지간에 재사용되는 컴포넌트는 외부로 분리한다.
  4. 복잡한 추상화보다는 반복이 낫다.
  5. 적당히 나누자. 완벽히 나누게 되면 마음은 편하지만 코드를 찾아가는 경로가 길어지고 점점 context를 잃게 된다.

관련 있는 컴포넌트들을 모아놓자

page 파일 안에 하위 컴포넌트 배치하기

pages안에 각 page를 넣습니다.

그리고 해당 페이지에서 사용되는 컴포넌트들은 페이지 파일 아래쪽에 배치합니다.

const DetailPage = () => { ... };

export default DetailPage;

const Loading = () => { ... };
const Error = () => { ... };
const Main = () => { ... };
const Sidebar = () => { ... };
const FloatButton = () => { ... };

이렇게 아래쪽에 배치하면 세가지 장점이 있습니다.

부모 컴포넌트로 빠르게 돌아올 수 있습니다

컴포넌트를 세세하게 파일별로 분리하면 VSC에서 부모컴포넌트 -> 하위 컴포넌트로 가는건 한번에 갈 수 있지만(ctrl + 우클릭), 다시 부모 컴포넌트로 오는건 개발자가 폴더 구조를 보고 찾아서 와야하기 때문에 번거롭습니다.

아래쪽에 두게되면 해당 컴포넌트를 찾을때는 ctrl + 우클릭으로 한번에 가고, 돌아올때는 스크롤로 오면 되기 때문에 더 편합니다.

소속을 확실히 할 수 있습니다

세세하게 파일별로 나누면 이 컴포넌트가 어디에서 사용되는지 확실히 알기 어렵습니다. export를 하게 되니 컴포넌트 끼리 서로 import 할 수도 있고, 다른 페이지에서도 import 할 수 있게됩니다.

DetailPage.tsx 파일 아래쪽에 컴포넌트를 배치하면 Error / FloatButton / Loading / Main 컴포넌트가 DetailPage에서만 사용됨이 보장됩니다.

const DetailPage = () => { ... };

export default DetailPage;

// 아래 컴포넌트들은 현재 파일에서만 사용됨이 보장됩니다
const Loading = () => { ... };
const Error = () => { ... };
const Main = () => { ... };
const Sidebar = () => { ... };
const FloatButton = () => { ... };

폴더 구조가 단순해집니다

지금은 얼마 없지만 DetailPage안에 이런 저런 컴포넌트가 많아지고 이것들을 전부 다른 파일로 분리하면 폴더 구조가 너무 길어집니다.

반면, DetailPage.tsx아래에 두게 되면 폴더 길이가 짧아집니다.

문제점

하지만! 이런 방식은 파일이 뚱뚱해지는 문제점이 있습니다.

위처럼 할경우 해당 페이지의 컴포넌트가 많아지면 아래쪽이 너무 늘어나서 컴포넌트간의 구분이 어려워 지고 가독성이 떨어집니다. 네이밍 충돌도 빈번하게 일어날 수 있습니다.

그래서 우선 아래쪽에 구현을 하고, 때가 되면 파일로 분리하는 것이 좋다고 생각합니다.

대표적으로,

  • 컴포넌트가 복잡하고 무거울때
  • 스토리북에 표현해야할 필요가 있을때
  • 테스트가 필요한 컴포넌트일때

위 세가지 경우에 컴포넌트를 아래쪽이 아닌 같은 디렉토리의 독립적인 폴더에 두는것이 좋다고 생각합니다.

DetailPage.tsx에서 끌어 올린 독립 컴포넌트들

이후에 page 컴포넌트 아래쪽에는 다음과 같은 컴포넌트들이 남습니다.

  • 가벼운 컴포넌트들
    • Error
    • Loading
    • AddButton
    • PageTitle
  • Layout관련 컴포넌트들
    • Main
    • Sidebar
    • Footer

결과적으로 페이지 안에서 컴포넌트의 분리는 획일적인 방식 보다는 적당히 개발자가 덜 피곤한 방향으로 선택하는것이 좋다고 생각합니다.

page내부에서 분리된 컴포넌트

page내부에서 분리된 컴포넌트들도 마찬가지로 가급적 같은 파일의 아래쪽에 하위 컴포넌트들을 배치합니다.

const StudyMemberSection = () => { ... };
export default StudyMemberSection;

const Self = () => { ... };

const StudyMemberCount = () => { ... };
const StudyMemberList = () => { ... };
const NoStudyMember = () => { ... };
const StudyMemberListItem = () => { ... };
const StudyOwnerCardLink = () => { ... };
const StudyMemberCardLink = () => { ... };
const MoreButton = () => { ... };

마찬가지로 너무 뚱뚱한 하위 컴포넌트는 다른 파일로 분리합니다.

다만, 페이지와는 달리 하위 폴더로 만들기 보다는 이름을 잘 줘서 같은 계층에 폴더를 배치합니다.

폴더 구조를 깊이 가져가는 경우

폴더 구조를 flat하게 가져가는 경우

study-member-section/components/study-member-cardstudy-member-section/components/study-owner-card를 밖으로 flat하게 빼냈습니다.

폴더 구조를 깊게 가져가면, 분류는 확실히 된다는 장점이 있지만 폴더 가독성이 떨어진다고 생각합니다.

공통 컴포넌트 = UI Library

공통 컴포넌트는 ui library를 만든다는 관점으로 접근합니다.

공통 컴포넌트는 도메인에 관련된 내용이 없고 오로지 모양general한 기능을 가지고 있는 컴포넌트 입니다.

예를들어서,

이런 식입니다.

페이지간 공통으로 사용되는 컴포넌트

도메인이 같은 경우

도메인 정보가 들어가면서 여러 페이지 사이에서 중복되어 사용되는 컴포넌트인 경우에는 최상단 components 폴더에 배치합니다.

예를들어, study-chip 같은 경우 여러 페이지에서 사용되고 그 의미도 동일하기 때문에 src/components/study 에 배치합니다.

이러한 컴포넌트가 많지 않다면 굳이 도메인별로 분리하지 않고 그냥 src/components 에 배치해도 괜찮다고 생각합니다.

flat하게 배치

도메인이 다른데 모양이 같은 경우

이러한 경우에는 도메인을 제외한 모양만을 기준으로 공통 컴포넌트로 분리할 수 있는지 노력해보고, 어렵다면 그냥 중복을 허용합니다.

정리

  • 가능하면 같은 파일 내에 하위 컴포넌트를 배치합니다.
  • 때에 따라서 다른 파일로 컴포넌트를 분리합니다.
  • 공통 컴포넌트는 도메인 정보가 없이 모양만을 기준으로 만들고, src/components/@shared에 배치합니다.
  • 페이지간 공유되는 컴포넌트는 src/components에 배치합니다.
  • 핵심은 일관되고 완벽한 구조가 아닌, 개발자가 덜 피곤한 구조를 만드는것 입니다.

이상입니다. 읽어주셔서 감사합니다 :D

profile
안녕하세요

0개의 댓글