같은 코드가 여러 곳에서 나타난다? 이 때부터 코드에서 냄새가 나기 시작한다고 합니다. 만약 중복 코드가 밟생한 부분의 로직이 수정돼야 한다면 어떻게 해야 할까요? 당연히 해당 로직이 사용된 모든곳을 찾아서 수정해줘야 합니다. 이런 문제를 해결하려면 중복코드를 제거해야되겠죠. React로 개발하면서 중복 코드가 발생했을 때 컴포넌트를 활용해서 중복 로직을 제거하는 방법에 대해서 알아보도록 하겠습니다.
React 컴포넌트를 개발하다보면 특정 아이디를 가지고 데이터를 가져와서 사용해야 하는 경우가 있습니다. 여기서는 멤버의 아이디를 이용해서 멤버 정보를 가져온 후, 필요한 데이터를 화면에 렌더링 하는 컴포넌트를 만들어보도록 하겠습니다. 이때 나타날 수 있는 중복코드에 대해서 알아보고 이 중복코드를 제거할 수 있는 방법에 대해서도 살펴보겠습니다.
memberId
를 prop으로 받아야 하기 때문에 propTyeps에 memberId 타입을 지정해줍니다.
여기서는 memberId를 string으로 받습니다. memberId가 꼭 필요하기 때문에 isRequired를 붙였습니다.
props으로 받은 memberId를 가지고 멤버정보를 받아와야합니다.
membersStore
를 import 해서 getById
메소드를 이용해 멤버 정보를 가져오도록 합니다.
이제 getMember
메소드로 멤버 정보를 가져와 name을 렌더링 하도록 합니다.
MemberName
컴포넌트와 마찬가지로 memberId
를 꼭 필요로 하기 때문에 PropTypes.string.isRequired
로 정의합니다.
그리고 memberId
값으로 멤버 정보를 가져옵니다.
이제 getMember
메소드로 멤버 정보를 가져와 nickname을 렌더링 하도록 합니다.
이제 이 냄새나는 중복코드를 제거하는 방법에 대해서 알아보도록 하겠습니다.
컴포넌트의 로직 중복을 제거할 수 있는 방법은 크게 두 가지가 있습니다. HOC
와 render prop
인데요.
하나씩 살펴보도록 하겠습니다.
먼저 HOC를 이용해서 중복 로직을 제거하는 방법에 대해서 살펴보도록 하겠습니다.
HOC(Higher Order Component)에 대한 자세한 정보는 리액트 공식 홈페이지를 통해서 확인하시면 될것 같습니다.간단하게 HOC에 대해서 설명 하자면 컴포넌트를 감싸서 추가적인 작업을 해준 후 props로 작업 결과를 넘겨주는 컴포넌트라고 생각하면 됩니다. 이제 이 HOC를 이용해서 어떻게 중복 제거를 할 수 있는지 보겠습니다.
먼저 MemberName
컴포넌트와 MemberNickname
컴포넌트의 중복 코드를 뽑아서 HOC로 만들어줘야 합니다.
withMemberLoader
라는 이름으로 컴포넌트를 파라미터로 받는 함수를 만들어 줍니다.
이 함수는 새로운 MemberLoader 라는 컴포넌트를 반환하게 되는데요. 이 안에서 멤버의 아이디를 가지고 멤버 정보를 가져오는 로직을 구현하도록 하겠습니다.
새로 반환하는 MemberLoader
컴포넌트에서 파라미터로 받았던 컴포넌트를 그대로 렌더링하도록 해줍니다.
그래야 원래 렌더링 하고 싶은 컴포넌트의 내용을 보여줄 수 있게 되겠죠? 그리고 MemberLoader
컴포넌트가 대신 받게 된 props
를 모두 WrappedComponent
로 그대로 넘겨줍니다.
이제 MemberName
이나 MemberNickname
컴포넌트 대신 MemberLoader
가 받게된 memberId를 가지고 멤버 정보를 가져오도록 메소드를 만들어줍니다.
getMember
메소드로 멤버 정보를 가져올 수 있게 됐습니다.
getMember
메소드를 통해서 가져온 멤버 정보를 파라미터로 받은 컴포넌트의 props로 넘겨주면 멤버 정보를 받을 수 있게 됩니다.
자 이제 withMemberLoader HOC가 만들어졌습니다. 이 HOC를 이용해서 중복 로직이 제거된 MemberName
과 MemberNickname
컴포넌트를 만들어보도록 하겠습니다.
이제 MemberName 컴포넌트를 export 할 때 withMemberLoader로 한 번 감싸서 export 하도록 해주겠습니다. 이렇게 하면 props에 member라는 값이 추가 되고 여기 멤버의 정보가 같이 담겨서 넘어오게 됩니다.
멤버 정보를 withMemberLoader를 통해서 받아오기 때문에 더 이상 getMember
메소드는 의미가 없게 됩니다.
getMember
메소드를 제거 하고, 렌더링 부분의 this.getMember()
를 props에 있는 member를 참조하도록 수정해줍니다.
MemberNickname 컴포넌트를 withMemberLoader 로 한번 감싸서 export 해줍니다.
그리고 더이상 의미 없는 getMember 메소드와 이를 참조하는 코드를 props의 member를 참조하도록 수정 합니다.
자 이렇게 중복되는 로직을 HOC 컴포넌트로 추출해줍니다. 그리고 추출한 로직에서 필요한 데이터만 원래 컴포넌트의 props로 넘겨주게 되면 각 컴포넌트마다 중복된 로직을 갖지 않고 손 쉽게 원하는 데이터를 사용할 수 있게 됩니다.
두 번째로 Render Prop을 이용한 방법을 살펴보겠습니다. Render Prop에 대해서 간단하게 설명하자면 어떤 컴포넌트의 props으로 함수를 넘겨줍니다. 이때 넘겨준 함수의 반환값은 렌더링 하고 싶은 JSX입니다. 무슨소리인지 코드를 통해서 살펴보겠습니다.
MemberLoader
라는 컴포넌트를 하나 만들어 줍니다. 이 컴포넌트는 props
의 값으로 두 가지 값을 받아야 합니다.
memberId는 우리가 MemberName
과 MemberNickname
에서 받았던 값입니다.
그리고 render는 멤버 정보 파라미터로 받아서 원하는 정보를 JSX 형태로 반환해주는 함수입니다.
자 이제 우리의 중복 코드인 getMember
를 이 컴포넌트로 뽑아내도록 하겠습니다.
이렇게 정의해준 getMember
메소드와 props의 render 함수를 이용해서 원하는 값을 렌더링 할 수 있도록 해줍니다.
props의 render 함수에 this.getMember()
를 이용해서 멤버 정보를 넘겨주도록 합니다.
그러면 넘겨준 render 함수에 따라 원하는 값을 화면에 보여줄 수 있게 됩니다.
이제 새로 만든 MemberLoader
컴포넌트를 사용해서 중복코드가 제거된 MemberName
과 MemberNickname
를 다시 만들어보겠습니다.
render 메소드에서 MemberLoader 컴포넌트를 이용합니다.
MemberLoader
컴포넌트의 prop으로 memberId를 넘겨줘야 멤버정보를 가져올 수 있기 때문에 memberId를 prop으로 넘겨줍니다.
그리고 getMember
메소드는 더이상 필요하지 않기 때문에 삭제해줍니다.
이제 마지막으로 render 함수를 넘겨줘야 합니다. MemberLoader 컴포넌트에서는 이 render 함수에 멤버 정보를 파라미터로 넘겨주게 됩니다. 이 멤버정보를 이용해서 멤버의 이름을 렌더링 하도록 함수를 만들어 넘겨주면 됩니다.
MemberLoader
컴포넌트에 memberId와 render 함수를 넘겨줘서 별명을 렌더링해줍니다.
여기서 render 라는 prop으로 렌더 함수를 넘겨줬는데요. 다른 이름을 쓰면 안될까 하는 의문이 드실 수 있습니다.
물론 다른 이름을 사용하셔도 상관 없습니다. 심지어 children을 함수로 사용하셔도 됩니다.
그러면 사용하는 컴포넌트에서는 render를 넘겨주는 대신 children에 함수형태를 넣으면 됩니다.
함수를 직접 넣는 대신 함수 명을 넘겨서 할 수 도 있습니다.
RenderProp이라는 방법으로 중복코드를 제거하는 방법에 대해서 살펴봤습니다.
HOC와 render prop으로 중복코드를 제거했는데요. 두가지 방법에 어떤 차이점이 있고, 또 둘 중 어떤 방법을 쓰면 좋을지에 대새서 알아 보겠습니다.
두 가지 다 중복 코드를 제거한다는 공통점이 있습니다. 다만 약간의 차이점이 있는데요.
HOC는 컴포넌트를 꾸며주는 함수를 이용하는 반면, Render Prop은 컴포넌트를 이용합니다.
이 때, 넘겨준 파라미터를 렌더링하면서 내부 컴포넌트에서 만들어낸 데이터를 추가적인 prop으로 넘겨줄 수 있게 되는 것이죠. 즉, 함수에 넘겨준 컴포넌트를 꾸며준다고 생각하면 편할것 같습니다.
HOC는 직접적으로 렌더링할 컴포넌트가 필요한 데이터를 받는 반면, Render Prop은 데이터를 제공해주는 컴포넌트에 렌더링 할 함수를 넘겨줍니다.
HOC와 Render Prop의 공통적인 중복 로직 제거와 차이점에 대해서 살펴봤습니다. 그렇다면 어떤 방법이 더 좋은 방법일까요?
제 생각에는 개인 선호에 따라 더 좋은 방법을 골라 사용하면 될 것 같습니다. 결국은 중복제거에 큰 목적이 있기 때문에 이 목적을 두 가지 방법 모두 충족하기 때문에 무엇을 골라도 무방하다고 생각합니다.
그래도 굳이 하나만 선택해야 한다면… 저는 Render Prop에 손을 들어주고 싶습니다. 이유는 HOC보다 조금 더 가독성이 좋다는 느낌이 들어서 입니다.
그러면 마법같이 member
라는 값을 prop을 통해서 사용할 수 있게 됩니다.
저는 바로 이 부분이 가독성의 문제라고 생각합니다.
이 member
라는 prop이 갑자기 튀어나왔습니다. 어디서 온걸까?
MemberName
컴포넌트를 사용하려면 member
를 컴포넌트에 넘겨줘야 하는걸까? 라는 생각이 들 수 있습니다.
컴포넌트 마지막 부분에 withMemberLoader
로 꾸며준부분을 보기 전까지는 HOC를 이용했는지 알아채지 못할 수 있습니다.
HOC에 비하면 Render Prop는 조금 더 가독성이 좋습니다.
MemberName
컴포넌트 렌더링 부분을 보면 MemberLoaer
라는 컴포넌트를 사용했다는걸 바로 알 수 있습니다.
MemberLoader
라는 이름을 통해서 멤버 정보를 가져온다는 정도는 쉽게 예측할 수 있기 때문에
HOC 방법에 비하면 코드 읽기가 매우 쉽다는걸 느낄 수 있습니다.
컴포넌트에서 발생하는 중복 코드를 제거할 수 있는 방법 두 가지를 살펴봤습니다. HOC, Render Prop 모두 훌륭하게 중복 제거를 해주는 방법입니다. 약간의 차이가 있다면 가독성의 차이라고 생각합니다. HOC과 Rneder Prop 중에 무엇을 써야하는지는 개인 취향에 따라 골라서 사용하면 될것 같습니다.