컴포넌트 공통 기능 관리(HOC,렌더속성값)

May 25th 2020 by jyoon

참고

  • 아래 소스 코드를 순차 적으로 확인할 수 있다.
    github 주소
  • 아래 내용들을 다루고 있습니다.
## 4.3.1 고차 컴포넌트를 이용한 공통 기능 관리

  * 코드 4-31 마운트 시 서버로 이벤트를 보내는 고차 컴포넌트
  * 코드 4-32 마운트 여부를 알려주는 고차 컴포넌트
  * 코드 4-33 로그인 여부에 따라 다르게 보여 주는 고차 컴포넌트
  * 코드 4-34 클래스 상속을 이용한 고차 컴포넌트
  * 코드 4-35 디버깅에 사용되는 고차 컴포넌트
  * 코드 4-36 div요소로 감싸 주는 고차 컴포넌트
  * 코드 4-37 여러 개의 고차 컴포넌트를 동시에 사용하기
  * 코드 4-38 고차 컴포넌트에서 displayName 설정하기
  * 코드 4-39 고차 컴포넌트에서 정적 메서드 전달하기
  * 코드 4-40 withRouter 고차 컴포넌트
  * 고차 컴포넌트 단점(04-40(withRouterExample).js 참고)
  * withRouter, Router 예제코드`

## 4.3.2 렌더 속성값을 이용한 공통 기능 관리

  * 코드 4-41 마운트 시 서버로 이벤트를 보내는 렌더 속성값
  * 코드 4-42 children을 사용하지 않은 렌더 속성값
  * 코드 4-43 데이터 처리 로직을 렌더 속성값으로 구현하기
  * 코드 4-44 마우스의 위치 정보를 알려 주는 렌더 속성값
  * 코드 4-45 렌더 속성값 함수의 매개변수를 속성값으로 전달하는 방법
  * 코드 4-46 children 속성값을 이용해서 작성한 레이아웃 컴포넌트

고차 컴포넌트란?

  • 컴포넌트를 입력으로 받아서 컴포넌트를 출력해주는 함수
  • 입력 받은 함수에서 출력되는 컴포넌트 내부적으로 입력받은 컴포넌트를 사용하는데 이때 입력된 컴포넌트를 사용하는 방법은 무궁무진.
  • 아래 두가지 방법으로 고차컴포넌트를 사용해서 공통기능 관리를 할 수 있다.

    • 고차 컴포넌트를 이용한 공통 기능 관리
    • 렌더 속성값을 이용한 공통 기능 관리

고차컴포넌틑 단점 세가지

첫번째 단점

  • 속성값이 암묵적으로 넘어온다.
  • react-redux의 connect고차 컴포넌트를 사용하면 사용자가 명시한 속성값 외에도 dispatch라는 함수가 암묵적으로 넘어온다.
    따라서 connect 고차 컴포넌트를 사용한 컴포넌트 내부에서는 this.props.dispatch라는 코드가 등장한다.(모르는 사람은 의하게 생각할 수 있다)

두번째 단점

  • 서로 다른 고차 컴포넌트가 똑같은 속성값 이름을 사용할 때 발생
  • 만약 어떤 고차 컴포넌트가 dispatch라는 새로운 속성값을 만들어낸다고 가정할때 이 고차 컴포넌트와 react-redux의 connect 고차 컴포넌트를 동시에 사용하면 속성값 이름이 충돌하는 문제가 발생
  • 마지막으로 호출된 고차 컴포넌트의 속성값으로 덮어써지게 된다.
  • 우리가 만큼 고차 컴포넌트의 속성값을 수정하면 되지만 외부 패키지 속성값이 겹치면 같이 사용하기 힘들다.

세번째 단점

  • 아래 세가지 의적인 절차가 필요
  • 고차 컴포넌트를 만들 때는 항상 함수로 감싸줘야하고, displayName을 설정해 줘야하고, 정적 메서드를 전달하기 위한 코드가 필요하다(코드04-40 설명 참고)
  • 특히 함수로 감싸져 있는 부분은 타입스크립트와 같은 정적 타입 언어를 사용할 때 타입을 정의하기 까다롭다는 문제가 있다.

렌더 속성값의 단점 세가지

첫번째 단점

  • 렌더함수가 호출될 때마다 새로운 함수를 만들기 때문에 성능에 부정적인 영향을 준다.
  • 하지만 최신 브라우저에서는 함수 생성이 성능에 거의 영향이 없을 정도로 많이 개선됐다.
  • 오래된브라우저에서도 높은 성능이 요구되는 프로그램이 아니라면 크게 신경 쓰지 않아도 된다.

두번째 단점

  • 다음과 같이 사용하는 쪽의 렌더함수가 복잡해질 수 있다.

    • (1),(2),(3) 렌더 속성값이 중첩돼서 사용됐다.
function MyComponent() {
  return (
    <MountEvent name="MyComponent">
      // (1)
      {() => (
        <DataFetcher // (2)
          url="https://api.github.com/users/happyjy"
          parseData={parseRepoData}
        >
          {({ data }) => (
            <div>
              <MouseTracer>
                //(3)
                {({ x, y }) => <p>{`(x,y): ${x}, ${y}`}</p>}
              </MouseTracer>
              <p>{`id: ${data.id}`}</p>
              <p>{`html_url: ${data.html_url}`}</p>
              <p>{`created_at: ${data.created_at}`}</p>
            </div>
          )}
        </DataFetcher>
      )}
    </MountEvent>
  );
}

고차컴포넌트 vs 렌더 속성값

  • 렌더 속성값방법은 고차 컴포넌트가 갖고 있는 모든 단점이 존재 하지 않는다.
  • 렌더 속성값에서 데이터는 함수의 매개변수로 명시적으로 넘어온다.
  • 렌더 속성값은 함수의 매개변수를 통해서 개별적으로 필요한 정보를 주기 때문에
    고차 컴포넌트가 갖고 있던 속성값 이름 충돌 문제도 존재하지 않는다.
  • 단, 생명주기 메서드에서 렌더 속성값의 데이터에 접근하기 위해서 Wrapper(코드4-45) 컴포넌트를 사용하는 경우에는 여전히 이름 충돌 문제가 존재한다.

    • 그리고 고차 컴포넌트에서 필요했던 의례적인 절차가 필요없다.
    • 일반적인 리액트 컴포넌트이기 때문에 타입스크립트와 같은 정적 타입 언어에서 타입으로 정의하는게 고차 컴포넌트 만큼 까다롭지도 않다.
  • 고차 컴포넌트 함수의 호출은 입력되는 컴포넌트의 인스턴스가 만들어지기 전에 발생하기 때문에 정적이다.

    • 반면 렌더 속성값은 렌더 함수를 호출 시에 동적으로 로직을 변경할 수 있다는 장점이 있다.

결론

  • 고차 컴포넌트와, 렌더 속성값은 모두 코드 재사용성을 높이기 위한 리액트 코딩패턴이다.
  • 거의 모든 겨웅에 있어서 고차 컴포넌트를 렌더 속성값으로 또는 그 반대로 변환할 수 있다.
  • 어느쪽이 더 우월하다고 보기는 힘들며 각자의 취향과 프로젝트의 성격에 따라 어떤 패턴을 주로 사용할지 선택하면된다.

REF