React - children으로 컴포넌트 레이아웃

혜성·2022년 4월 10일
0

리엑트의 핵심 기능은 UI를 컴포넌트 단위로 관리하는 것이다. 이 핵심 기능이 라우터와 어우러져 우리는 SPA의 사용환경을 사용자에게 제공하게 된다. 그렇다면 만약 라우팅을 위해 제작한 Router 컴포넌트내의 요소를 원하는대로 사용자의 화면상에 배치하고 싶다면 어떻게 해야할까?

변하지 않는 콘텐츠는 한번만 렌더링 시키자

위의 두 이미지는 모두 무인양품의 온라인 스토어 이미지 이다. 브라우저창의 가로 넓이와 상관없이 NAV바 이하의 콘텐츠는 모두 1060픽셀내 그리고 브라우저의 한 가운데에 위치한다다

무인양품 사이트를 클론하면서 위와 같은 배치를 목표로하며 변하지 않은 UI인 사이드바는 전체 페이지가 렌더링 될때 단 한번만 렌더링이 되고 불필요하게 메인 콘텐츠 컴포넌트가 렌더링 될 때에는 재 렌더링이 발생하지 않도록 하는것을 목표로 하였다.

따라서 처음엔 다음과 같이 라우터 컴포넌트를 구현했다.

 <BrowserRouter>
      <Nav />
      <div className="mainContainer">
        <Aside />
        <Routes>
          <Route path="/cart" element={<Cart />} />
          <Route path="/detail/:id" element={<Detail />} />
          <Route path="products/categories" element={<List />} />
          <Route path="products/categories/1/types" element={<Mens />} />
          <Route path="products/categories/2/types" element={<Womens />} />
          <Route path="products/categories/3/types" element={<Labo />} />
          <Route path="/" element={<Landing />} />
        </Routes>
      </div>
    </BrowserRouter>

문제점

사이드 바에 해당하는 Aside 컴포넌트를 무작성 Routes 컴포넌트의 외부에 위치시켜 각 콘텐츠 페이지에 해당하는 컴포넌트에 포함시키지 않아 url 변경에따라 렌더링되는것을 막고자 하였다. 그리고 레이아웃을 위해 해당 스타일 값을 모든 컴포넌트에 공통으로 적용될 파일인 common.css 내에 mainContainer를 선택자로지정해 스타일을 적용시켰다. 하지만 이런 구조는 다음과 같은 이유로 비적절 하다고 느껴졌다.

  1. 라우터 컴포넌트내에 컴포넌트요소가 아닌 HTML태그사용으로 라우팅만을 위한 라우터 컴포넌트의 목적에 부합하지 않는점
  2. 라우터 컴포넌트의 스타일을 모든 컴포넌트에 공통으로 적용될 스타일이 지정된 common.css파일에 지정한점
  3. 위 두가지 문제점으로부터 찾아올 유지보수 문제

따라서 레이아웃을 위한 적절한 방법을 모색하던중 React의 children개념을 적용하기로 하였다.

React 공식문서

props.children
모든 컴포넌트에서 props.children를 사용할 수 있습니다. props.children은 컴포넌트의 여는 태그와 닫는 태그 사이의 내용을 포함합니다.

<Welcome>Hello world!</Welcome>

Hello world! 문자열은 Welcome 컴포넌트의 props.children으로 사용할 수 있습니다.

우선 1060픽셀의 모든 콘텐츠요소를 브라우저의 한가운데에 위치시킬 MainContainer 컴포넌트, url에 따라 전환될 페이지를 위치시킬 ContentsWrapper를 구현해 해당 컴포넌트 내에 라우터 컴포넌트의 요소들을 목적에 따라 Aside와 페이지 콘텐츠를 MainContainer의 자식요소로 배치했다.

//Router.js

<BrowserRouter>
      <Nav />
      <MainContainer>
        <Aside />
        <ContentsWrapper>
          <Routes>
            <Route path="/cart" element={<Cart />} />
            <Route path="/detail/:id" element={<Detail />} />
            <Route path="products/categories" element={<List />} />
            <Route path="products/categories/1/types" element={<Mens />} />
            <Route path="products/categories/2/types" element={<Womens />} />
            <Route path="products/categories/3/types" element={<Labo />} />
            <Route path="/" element={<Landing />} />
          </Routes>
        </ContentsWrapper>
      </MainContainer>
    </BrowserRouter>

그 후 props.children 개념을 사용해 MainContainer의 여닫는 태그로 입력한 요소들을 다음과 같이 MainContainer 컴포넌트에 children 객체로 받아 div태그로 감싸주고 스타일을 원하는대로 지정해 주었다.
//MainContainer.js

const MainContainer = ({ children }) => {
  return <div className="mainContainer">{children}</div>;
};

추가로 사이드바를 제외한 크기를 지정해줄 전환되는 페이지 컴포넌트 역시 ContentsWrapper 컴포넌트에 여닫는태그로 Routes 컴포넌트를 자식 컴포넌트로 넣어주고 children 객체로 받아 div태그로 감싸주고 스타일을 원하는대로 지정해 주었다.
const ContentsWrapper = ({ children }) => {
  return <div className="contentsWrapper">{children}</div>;
};

이러한 방법을 통해 실제 사이트와 같은 페이지 레이아웃이 가능해 졌다.


props.children 개념을 처음 접했을때에는 단순히 컴포넌트를 여닫는 태그로 사용해 태그내에 작성한 jsx 요소를 children 이라는 이름의 객체로 받을수 있구나, 그런데 이런건 어떤 용도로 사용하는걸까 하는 생각이 들었지만 이번에 사용한 용도와 같이 내부에 존재하는 컴포넌트의 레이아웃에도 사용할 수 있구나 라는 점을 깨닫는 계기가 되었다.

0개의 댓글