๊ฐœ๋ฐœ/FRONTEND

Render Props๋ฅผ ํ†ตํ•œ UX ๊ฐœ์„ 

woogie0303 2024. 8. 13. 21:03

์š”๊ตฌ ์‚ฌํ•ญ

๊ฐœ์ธ ์„ค์ • ํŽ˜์ด์ง€๋ฅผ ๋‹ด๋‹นํ•˜๊ณ  ์žˆ๋Š”๋ฐ ๋ชจ๋ฐ”์ผ ์ƒํ™ฉ์—์„œ ๋ชจ๋‹ฌ์„ Drawerํ˜•์‹์œผ๋กœ ๋ฐ”๊พธ๋Š”๊ฒŒ ์ข‹์„ ๊ฒƒ ๊ฐ™๋‹ค๊ณ  ํŒ€์›๋“ค๊ณผ ํšŒ์˜๋ฅผ ํ†ตํ•ด ๊ฒฐ๋ก ์ด ๋‚ฌ์—ˆ๋‹ค. ํ˜„์žฌ์ƒํ™ฉ์˜ ๋ชจ๋ฐ”์ผ ๋ชจ๋‹ฌ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์•˜๋‹ค

 

 

์œ„์˜ ์‚ฌ์ง„๊ณผ ๊ฐ™์€ ๋ชจ๋‹ฌ์ด ์—ฌ๋Ÿฌ๊ฐœ ์žˆ๋Š”๋ฐ ๊ธฐ์กด์—๋Š” ๋งˆ๊ฐ๊ธฐ๊ฐ„์ด ์žˆ์–ด์„œ ๊ตฌํ˜„ํ•˜๋Š”๋ฐ๋งŒ ์ง‘์ค‘ํ•˜๋Š” ๋‚˜๋จธ์ง€ ๊ฐ ๋ชจ๋‹ฌ๋งˆ๋‹ค ๊ฐ๊ฐ์˜ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์žˆ์—ˆ๋‹ค ์ด๋Ÿฌํ•œ ์ƒํ™ฉ์ด ๋ฐœ์ƒํ•˜๋‹ˆ๊นŒ ๋ฌธ์ œ์ ์ด ๋ชจ๋ฐ”์ผ ํ™”๋ฉด์—์„œ Drawer ๋ชจ๋‹ฌ์„ ๊ตฌํ˜„ํ•˜๋ ค๊ณ  ํ•˜๋ฉด ๊ฐ๊ฐ์˜ ์ปดํฌ๋„ŒํŠธ๋ณ„๋กœ Drawer ํ˜•์‹์˜ ๋ชจ๋‹ฌ์„ ๊ตฌํ˜„ํ•ด์•ผํ–ˆ๋‹ค.์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ์œ„ํ•ด ์—ฌ๋Ÿฌ ๋ ˆํผ๋Ÿฐ์Šค๋ฅผ ์ฐพ๋˜์ค‘ ๋Œ€ํ‘œ์ ์ธ ํŒจํ„ด 2๊ฐ€์ง€๋ฅผ ์ฐพ์•˜๋‹ค ๋ฐ”๋กœ ์ปดํŒŒ์šด๋“œํŒจํ„ด๊ณผ render props ํŒจํ„ด์ด๋‹ค. ๋จผ์ € ๊ฐ„๋‹จํžˆ ์„ค๋ช…์„ ์–˜๊ธฐํ•˜๊ณ  ํ•ด๊ฒฐ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ณด๋„๋ก ํ•˜์ž.

Render Prop ํŒจํ„ด

Render Prop ํŒจํ„ด์ด๋ž€
๋ Œ๋” ํ”„๋กญ์€ ์ปดํฌ๋„ŒํŠธ์˜ ํ”„๋กญ(prop) ์ค‘ ํ•˜๋‚˜๋กœ, ํ•จ์ˆ˜ ํ˜•ํƒœ์˜ ๊ฐ’์„ ๊ฐ€์ง€๋ฉฐ, ์ด ํ•จ์ˆ˜๋Š” JSX ์—˜๋ฆฌ๋จผํŠธ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ์ด ์ปดํฌ๋„ŒํŠธ ์ž์ฒด๋Š” ๋ Œ๋” ํ”„๋กญ ์™ธ์—๋Š” ์•„๋ฌด๊ฒƒ๋„ ๋ Œ๋”๋งํ•˜์ง€ ์•Š์œผ๋ฉฐ, ์ž์ฒด์ ์ธ ๋ Œ๋”๋ง ๋กœ์ง์„ ๊ตฌํ˜„ํ•˜๋Š” ๋Œ€์‹ , ๋‹จ์ˆœํžˆ ๋ Œ๋” ํ”„๋กญ์„ ํ˜ธ์ถœํ•˜์—ฌ ๋ Œ๋”๋ง์„ ์ˆ˜ํ–‰ํ•œ๋‹ค

 

Render Prop ํŒจํ„ด ์˜ˆ์‹œ ์ฝ”๋“œ

export default function App() {
  return (
    <div className="App">
      <h1>โ˜ƒ๏ธ Temperature Converter ๐ŸŒž</h1>
      <Input
        render={(value) => (
          <>
            <Kelvin value={value} />
            <Fahrenheit value={value} />
          </>
        )}
      />
    </div>
  );
}

์ด์™€ ๊ฐ™์ด ์ฝ”๋“œ๋ฅผ ๊ตฌ์„ฑํ•˜๋ฉด 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<unknown>()
  const { isMobileView } = useCheckDevice()

  return isMobileView ? (
    <Drawer onSubmit={submitHandler} close={close}>
        Drawer ํ˜•์‹ ๋ชจ๋‹ฌ ๊ตฌ์กฐ
    </Drawer>
  ) : (
    <Modal onSubmit={submitHandler} close={close}>
        Drawer ํ˜•์‹ ๋ชจ๋‹ฌ ๊ตฌ์กฐ
    </Modal>
  )
}

//settingPage.tsx
 <UserSettingModal
        isOpen={isOpen}
        title="์‚ฐ์—…๋ถ„์•ผ ๋ณ€๊ฒฝ"
        submitHandler={(value: unknown) => {
          mutate({ value, type: 'occupation', email: userEmail })
          close()
        }}
        closeHandler={close}
        renderItem={(setPostValue) => (
          <UserSettingList
            listData={USER_INFO_OCCUPATION}
            wrap
            setPostValue={setPostValue}
            initialItem={userProfile?.occupation as string}
          />
        )}
      />

 

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