티스토리 뷰

리액트 문서에서는 context를 소개하는 페이지에서 context를 쓰기 전에 component composition에 대해 고려해보라고 권장하고 있다.

context는 여러 레벨의 여러 컴포넌트에서 같은 값을 공유하도록 해주므로 props drilling을 피할 수 있고, 불필요한 리렌더링을 피할 수 있다는 장점이 있다.

다만 context에도 단점이 존재하는데,

  1. implicit하다.
  2. props를 직접 넘겨주는 방법은 코드 상에서 해당 컴포넌트의 input이 무엇인지 확인할 수 있지만 useContext로 컴포넌트 내부에서 값에 접근하게 되면 해당 컴포넌트의 사용처에서는 컴포넌트 내부에서 어떤 값이 쓰이고 있는지 보이지 않는다. 그러므로 그 컴포넌트가 context provider 내부에서 쓰일 것이라고 가정하고 쓰인 컴포넌트라면, 개발자가 실수로 context 바깥에 그 컴포넌트를 위치시켰을 때 스리슬쩍 런타임 에러가 발생할 수 있다.
  3. 가독성이 떨어질 수 있다. child 컴포넌트에서 context로 implicit하게 값을 사용하고, parent 컴포넌트에서는 아무 props도 받고 있지 않는 상황이라면 parent 컴포넌트를 볼 때 내부 구조에 대한 정보나 힌트를 얻기 힘들다. React는 선언적인 것이 장점인 UI 라이브러리인데, 이렇게 사용한다면 parent component가 어떻게 구성되어 있는지 알기 위해서 계속 트리를 타고 밑으로 내려가야 겨우 파악할 수 있게 된다. 그러면 코드를 여러 번 볼때마다 이 트래킹 과정이 반복되면서 시간이 들고, 내부 코드를 고칠 때도 컴포넌트 밖에서 보이지 않으므로 알아차리기 힘들다.

Component Composition

위 context의 단점들을 피할 수 있는 component composition에 대해 코드로 알아보자.

먼저 context를 사용하는 코드를 보자.

let UserContext = React.createContext();

function App() {
  const [user, setUser] = setState();
  return (
    <>
      <UserContext.Provider value={{ user }}>
        {user ? <Dashboard /> : <Login onLogin={(user) => setUser(user)} />}
      </UserContext.Provider>
      <WelcomeMessage /> // occurs error
    </>
  )
}

function Dashboard() {
    return (
    <DashboardNav />
    <DashboardContent />
  )
}

function DashboardContent() {
  return (
      <WelcomeMessage />
  )
}

function WelcomeMessage() {
  let { user } = useContext(UserContext);
    return (
    <>
      <div>Hello</div>
        <div>{user.name}</div>
    </>
  )
}

여기서 에러가 날 수 있는 부분은 WelcomMessage를 context 바깥에서 사용했을 때이다. 예시 코드는 아주 간단한 형태이므로 문제가 되지 않겠지만 만약 트리가 크고 복잡해진다면 점점 이런 런타임 에러를 찾기 힘들어질 수 있다.

그리고 Dashboard 컴포넌트 안에 보면 여러 자식 컴포넌트로 구성되어 있지만 App 컴포넌트에서 봤을 때는 이를 알기가 어렵다. 만약 컴포넌트가 더 많아지고 복잡해진다면 트래킹하고 코드를 읽는 것이 어려워질 것이다.

이제 컴포넌트 컴포지션을 사용하는 코드를 보자.

function App() {
  const [user, setUser] = setState();
  return (
    <>
        {user 
       ? 
       <Dashboard>
         <DashboardNav />
         <DashboardContent>
           <WelcomeMessage user={user} />
         </DashboardContent>
       </Dashboard>
       : 
       <Login onLogin={(user) => setUser(user)} />
      }
    </>
  )
}

function Dashboard({children}) {
    return (
        <>{children}</>
  )
}

function DashboardContent() {
  return (
        <>{children}</>
  )
}

function WelcomeMessage({user}) {
    return (
    <>
      <div>Hello</div>
        <div>{user.name}</div>
    </>
  )
}

이 코드의 장점은

  1. props drilling 없이 진짜 user가 필요한 WelcomeMessage에만 user를 넘겨주고 있다.
  2. 선언적으로 코드의 구조와 UI를 읽을 수 있다.

그러므로 context를 사용하기 전에 component composition으로 해결될 수 있는 문제인지 고려해보고 코딩하면 부작용을 방지할 수 있을 듯하다.

Ref

https://reactjs.org/docs/context.html#before-you-use-context

https://www.youtube.com/watch?v=3XaXKiXtNjw

https://kentcdodds.com/blog/application-state-management-with-react

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함