[Tistory] Render Props를 통한 UX 개선

원글 페이지 : 바로가기

요구 사항 개인 설정 페이지를 담당하고 있는데 모바일 상황에서 모달을 Drawer형식으로 바꾸는게 좋을 것 같다고 팀원들과 회의를 통해 결론이 났었다. 현재상황의 모바일 모달은 다음과 같았다 위의 사진과 같은 모달이 여러개 있는데 기존에는 마감기간이 있어서 구현하는데만 집중하는 나머지 각 모달마다 각각의 컴포넌트가 있었다 이러한 상황이 발생하니까 문제점이 모바일 화면에서 Drawer 모달을 구현하려고 하면 각각의 컴포넌트별로 Drawer 형식의 모달을 구현해야했다.이를 해결하기위해 여러 레퍼런스를 찾던중 대표적인 패턴 2가지를 찾았다 바로 컴파운드패턴과 render props 패턴이다. 먼저 간단히 설명을 얘기하고 해결방법을 알아보도록 하자. Render Prop 패턴 Render Prop 패턴이란 렌더 프롭은 컴포넌트의 프롭(prop) 중 하나로, 함수 형태의 값을 가지며, 이 함수는 JSX 엘리먼트를 반환한다. 이 컴포넌트 자체는 렌더 프롭 외에는 아무것도 렌더링하지 않으며, 자체적인 렌더링 로직을 구현하는 대신, 단순히 렌더 프롭을 호출하여 렌더링을 수행한다 Render Prop 패턴 예시 코드 export default function App() {
return (

☃️ Temperature Converter 🌞

(
<>



)}
/>

);
} 이와 같이 코드를 구성하면 Input컴포넌트는 Kelvin이나 Fahrenheit같이 상세한 로직은 몰라도 그냥 render prop을 호출하면 된다. 이로써 어떠한 한 도메인에 종속적이 않고 재사용성 있게 코드를 작성할 수 있다. Compound 컴포넌트 패턴 Compound(합성) 컴포넌트 패턴 직역 하자면 컴포넌트를 합성 시킨다는 뜻이다. 이러한 패턴의 특징은 UI를 자유롭게 구성할 수 있고 선언적이므로 이해하기 쉬운 코드를 작성할 수 있다. 또한 Props drilling의 문제도 피할 수 있다. Compound 컴포넌트 패턴 예시코드 현재 우리가 운영하는 뉴스레터 관련 사이트에서 사용하는 컴파운드 패턴 일부를 예시로 가져왔다. 만약 컴파운드 패턴이 없었다면 위와 같은 prop들이 한 컴포넌트 안에 들어가야 한다. 또한 만약에 프로필 썸네일의 위치를 바꿔달라고 요구사항을 받을때 컴파운드 패턴을 사용하면 관심사 분리가 되어있고 무엇보다 선언적이기 때문에 ArticleCard.NewletterAvtar 컴포넌트의 선언하는 위치만 바꾸면 적용이 가능하다 이처럼 UI의 설정에 있어서도 되게 자유로운 것을 알 수 있다. 접근 방법 일단 관심사 분리를 위해 Drawer를 적용시켜야하는 모달끼리 비교를 해서 공통점을 찾으려고 했다. 위와같이 빨간색 네모 박스 안에 있는 요소들 말고는 형식이 비슷해 보였다. 그렇다는 뜻은 네모박스 안에 있는 요소만 갈아끼울 수 있는 형식으로 컴포넌트를 작성하면 현재 따로 구현된 2개의 모달 컴포넌트를 하나로 묶을수 있을 것 같았다. 그렇게되면 아까 맨 처음에 말한 문제상황 처럼 Drawer 형식의 모달을 반복적으로 로직을 작성할 필요가 없게 된다. 컴파운드 패턴과 render Prop패턴을 선택해야하는데 필자의 경우 Render Prop패턴을 선택을 했다. 왜냐하면 일단 구현하려는 모달 자체는 Props도 많지 않고 UI관점으로 봤을때도 모달 특성상 각 요소들의 위치를 바꾸거나 그럴 가능성은 적어보여서 UI의 자율성도 그렇게 필요하지는 않았다. 그리고 컴파운드 패턴은 사용해봤지만 Render Props패턴은 사용을 안해봐서 궁금하기도 해서 render Props를 선택했다. 해결책 공통되는 부분을 찾았으니 SRP(Single Responsibility Principle)원칙에 따라 관심사 별로 로직을 나누면 위와 같다. B Component는 각 모달별로 개인 설정을 할 때 유저가 사용하는 입력값을 받는 컴포넌트다. A Component는 모달 닫기 기능과 확인 버튼을 눌렀을 때 유저가 설정하는 값을 전달해주는 모달의 역할만 수행해주면된다. 프로젝트 코드 //UserSettingModal.tsx
export default function UserSettingModal({
submitHandler,
renderItem,
closeHandler,
title,
isOpen,
}: UserSettingModalType) {
const [postValue, setPostValue] = useState()
const { isMobileView } = useCheckDevice()

return isMobileView ? (

Drawer 형식 모달 구조

) : (

Drawer 형식 모달 구조

)
}

//settingPage.tsx
{
mutate({ value, type: ‘occupation’, email: userEmail })
close()
}}
closeHandler={close}
renderItem={(setPostValue) => (

)}
/> renderItem을 props를 활용해 각자 필요한 개인설정 관련 컴포넌트들을 갈아끼우는 형식으로 사용하면 된다. 인자로는 setPostValue라는 setStateAction을 전달 해줌으로써 B Component가 유저가 설정한 값을 A Component에게 전달해준다. 이로써 state를 끌어올리지 않고 코드를 작성할 수 있게 됐다. 또한 UserSettingModal이라는 컴포넌트 즉 A component를 따로 공통으로 빼놓으면서 Drawer형식의 모달과 관련된 코드를 반복을 피할수 있게 되었다. 시연 영상 출처 https://www.patterns.dev/react/compound-pattern https://velog.io/@yesbb/%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5%EC%9D%98-%EA%B4%80%EC%A0%90%EC%9C%BC%EB%A1%9C-%EB%B0%94%EB%9D%BC%EB%B3%B8-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EA%B3%A0%EA%B8%89-%ED%8C%A8%ED%84%B4-Compound-component-Render-props#%EB%A6%AC%EC%95%A1%ED%8A%B8%EC%9D%98-%EA%B3%A0%EA%B8%89-%ED%8C%A8%ED%84%B41–compound-component-pattern

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다