리액트와 Atomic Design System에 대하여 라는 글을 작성한 적이 있다. 그 글에서는 Atomic Design System 에 초점을 맞추었다. 하지만 이번에는, View를 담당하는 Atomic Design Component 에 도달하기 전까지의 컴포넌트 구조에 대하여 이야기 해보려고 한다. 위에 언급한 글에도 비슷한 내용이 있다. 이 글은 거기에서 좀 더 발전 된 구조 패턴이라고 생각한다.
일단 이 패턴의 이름을 다들 처음 들어 봤을 이유는, 내가 고안해냈기 때문이다.
여전히 컴포넌트 구조를 연구중이라서, 완성형이라고 할 수도 없을 지 모른다.
실망스럽겠지만 뒤로가기는 쪼금 있다가 누르고,
뭐라고 하는지 조금만 귀(눈?) 기울여 보자. 😢
Page & Container 패턴에서는 컴포넌트 종류를 크게 3가지로 본다. View Components 영역은 자유로우니, Atomic Design / VAC 패턴과 함께 사용해도 무방하다.
컴포넌트 흐름은 아래와 같다.
PAGE ㅡ CONTAINERS ㅡ VIEW
ㄴ PAGE ㅡ CONTAINERS ㅡ VIEW
ㄴ PAGE ㅡ VIEW
ㄴ VIEW
그렇다면 각각의 역할에 대해서 알아보자.
여기까지 설명만 듣고는 이해가 잘 가지 않으리라 생각 된다. 간단한 예시를 통해 접근해보자. 아래와 같은 화면의 구조를 짠다고 가정해보자. (실제로 저런 구조의 서비스를 운영중이다.)
그림에 글로벌 메뉴 1번만 3개 있는건 애교로 봐주자.
위 화면 구조에서 Page & Container 패턴을 적용시켜보자. 가장 부모 컴포넌트는 AdminPage 이다. 서브페이지의 내용은 신경쓰지 말고 부모 컴포넌트 입장에서 크게 봤을 때, 위 화면을 3등분 할 수 있다. 아래 그림과 같이 2개의 Container와 Router 영역으로 구분할 수 있다.
레이아웃을 잡기 위한 jsx의 예시는 아래와 같다.
return (
<div class='admin-page'>
<GlobalHeaderContainer /> // Container 1
<main class='admin-page__main'>
<section class='admin-page__aside'>
<AsideContainer /> // Container 2
</section>
<section class='admin-page__content'>
<Router> // Router
<Route path='...' component={페이지1} />
<Route path='...' component={페이지2} />
</Router>
</section>
</main>
</div>
)
AdminPage 에 포함 된 두개의 Container의 역할은 아래와 같다.
<SideMenus list={list} />
뷰 컴포넌트를 렌더한다.이제 AdminPage에서 Router 안에 있는 Page 컴포넌트인 페이지 1
컴포넌트를 보자. 파일명은 Page1.tsx
로 부르도록 하자.
AdminPage는 레이아웃 나누는 것 외에는 하는 일이 별로 없었지만 Page1은 할 일이 있다. 필요한 데이터들을 Fetching 하는 일이다. 제목
과 각종 정보들
은 DB에 저장되어있기 때문에 원하는 데이터를 얻기 위해 GET API 를 찔러주고 로딩처리를 해준다. 데이터를 받아왔다면 State 또는 Store에 저장한다.
자 이제 이 녀석의 레이아웃은 어떻게 분할하면 좋을까 생각해보자. 일단 특별한게 없이 위 아래로 나열되어있기 때문에 코드는 간단해 보인다. 제목, 정보들
과 서브메뉴들
은 같은 Container 안에 있어도 되고 따로 나눠도 될 것으로 보인다. 지금은 따로 나눠보기로 한다.
jsx의 예시는 아래와 같다.
<div class='page-1' />
<Informations data={fetchedData} /> // 로직 없이 단순히 정보를 그려주는 역할의 View Component
<Page1SubMenuBarContainer />
<Router>
<Route path='...' component={서브페이지1} />
<Route path='...' component={서브페이지2} />
</Router>
</div>
Page1 에 포함 된 View와 Container의 역할은 아래와 같다.
<MenuBar list={list} />
뷰 컴포넌트를 렌더한다.이제 서브페이지도 같은 패턴으로 진행하면 된다. 서브페이지에서만 필요한 새로운 정보가 있다면 Fetch 하고, 그게 아니라면 부모로부터 내려받은 데이터를 이용하면 된다. 그런것도 필요 없을 수도 있다. 초라하게 단 하나의 자식 컴포넌트만 보여주게 될 지도 모른다. 굳이 필요없다고 느껴질지 모르지만 그래도 Page 컴포넌트를 꼭 작성하도록 하자. Router로 들어오는 첫 페이지의 역할을 잘 해낼 수 있게 말이다.
이 완벽하지 않은 글을 읽어주셔서 감사합니다. 리액트가 아니더라도 앞으로 더 좋은 컴포넌트 구조를 찾기 위한 노력은 계속 될 것 같습니다.
마지막으로 위 예시의 최종 트리 사진으로 마무리 하겠습니다.