Post

Typescript(3) - Polymorphic Components

๐Ÿ“Œ์‹œ์ž‘ํ•˜๋ฉฐ

ํ”„๋กœ๊ทธ๋žจ ์–ธ์–ด์˜ ๋‹คํ˜•์„ฑ์ด๋ž€, ํ”„๋กœ๊ทธ๋žจ ์–ธ์–ด์˜ ๊ฐ ์š”์†Œ๋“ค์ด ๋‹ค์–‘ํ•œ ์ž๋ฃŒํ˜•์— ์†ํ•˜๋Š” ๊ฒƒ์ด ํ—ˆ๊ฐ€๋˜๋Š” ์„ฑ์งˆ์„ ๊ฐ€๋ฆฌํ‚จ๋‹ค. (์œ„ํ‚ค๋ฐฑ๊ณผ)

์–ด์ฉŒ๋‹ค ๋“ค์–ด๊ฐ„ ์›น์‚ฌ์ดํŠธ์—์„œ Polymorphic Components ๊ด€๋ จ๋œ ๊ธ€์„ ๋ณด๊ฒŒ ๋˜์—ˆ๋‹ค. Polymorphic(๋‹คํ˜•์„ฑ) ์ด๋ž€ ๋‹จ์–ด ์ž์ฒด๊ฐ€ ๋‚ฏ์„ค์–ด์„œ, ์ด๊ฒŒ ๋ญ์ง€? ํ•˜๊ณ  ์ฝ์–ด๋ณด๋˜ ์™€์ค‘์—, ๋‚ด๊ฐ€ ์˜ˆ์ „์— ๋ดค๋˜ ์ธ๊ฐ• ์„ ์ƒ๋‹˜์ด ์‚ฌ์šฉํ•˜์‹œ๋˜ ๋ฐฉ์‹์ธ๊ฑธ ๊นจ๋‹ฌ์•˜๋‹ค. ์‹ฌ์ง€์–ด ์ง€๊ธˆ ๋‚ด๊ฐ€ ํ•„์š”๋กœ ํ–ˆ๋˜ ๋‚ด์šฉ์ด๋ž€๊ฒƒ๋„!

์—ญ์‹œ ์‚ฌ๋žŒ์€ ๋ง๊ฐ์˜ ๋™๋ฌผ(โ€ฆ) ์ด๋ผ๊ณ  ๊ทธ๋•Œ๋„ ์™€~ ์ข‹์€ ๋ฐฉ๋ฒ•์ด๊ตฌ๋‚˜! ํ•˜๊ณ  ๋…ธ์…˜์— ์ •๋ฆฌํ•ด๋‘์—ˆ๋Š”๋ฐ ๊ทธ ์ดํ›„๋กœ ์‚ฌ์šฉํ•˜์งˆ ์•Š์•„์„œ ๊นŒ๋งฃ๊ฒŒ ์žŠ์–ด๋ฒ„๋ฆฌ๊ณ  ๋ง์•˜๋‹ค.. ๐Ÿ™ƒ

์ง€๊ธˆ ์—…๋ฌด์— ์ ์šฉํ•˜๊ธฐ๋„ ์ข‹์€ ๋‚ด์šฉ์ด๋ผ, ๋‚ด์šฉ์„ ๊ผผ๊ผผํžˆ ์ฝ์–ด๋ณด์•˜๋Š”๋ฐ, ์ „์ฒด์ ์ธ ๋‚ด์šฉ ์ž์ฒด๋Š” ์–ด๋ ต์ง€ ์•Š์ง€๋งŒ ์˜์™ธ์˜ ๋ณต๋ณ‘์€ Typescript๋ฅผ ํ•ด์„ํ•˜๋Š” ๊ฒƒ์ด์—ˆ๋‹ค.

๊ทธ๋ž˜์„œ ์ด๋ฒˆ ํฌ์ŠคํŒ…๋„ Typescript ์นดํ…Œ๊ณ ๋ฆฌ์— ๋„ฃ์–ด๋‘์—ˆ๋Š”๋ฐ, ์ด๋ฒˆ ๊ธฐํšŒ์— ์ œ๋„ค๋ฆญ ๊ฐœ๋…์„ ๋‹ค์‹œ ํ•œ ๋ฒˆ ์ •๋ฆฌํ•ด๋ณด๋ฉด์„œ, Polymorphic Components๋ฅผ ๋งŒ๋“ค์–ด๋ณด์ž.

โœ…Polymorphic Components์˜ ํ•„์š”์„ฑ

ํ˜„์žฌ ๋‚ด๊ฐ€ ๋‹ด๋‹นํ•˜๋Š” ์—…๋ฌด ์ค‘์—, url์„ ๊ฐ€์ง„ data๋ฅผ fetchํ•ด์™€์„œ, url์ธ ๊ฒฝ์šฐ <Link> ํƒœ๊ทธ๋ฅผ ๋งŒ๋“ค๊ณ , ์—†๋‹ค๋ฉด <div> ํƒœ๊ทธ๋ฅผ ์ด์šฉํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ๋‹ค.

๋‘˜์˜ CSS๋Š” ๊ฐ™์•„์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์˜ค๋กœ์ง€ HTML ํƒœ๊ทธ๋ฅผ ์œ„ํ•ด์„œ ์•„๋ž˜์™€ ๊ฐ™์ด ์‚ผํ•ญ์—ฐ์‚ฐ์ž๋ฅผ ์จ์ฃผ์–ด์•ผ ํ–ˆ๋‹ค.

1
2
3
{
  data.url ? <Link href={data.url}>{data}</Link> : <div>{data}</div>
}

ํ•ด๋‹น ์˜ˆ์ œ์—์„œ๋Š” ๊ฐ„๋‹จํ•˜๊ฒŒ ์ผ์ง€๋งŒ ์‹ค์ œ ์—…๋ฌด์—์„œ๋Š” tailwind๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ํ•ด๋‹น ๊ฐ element๊ฐ€ ๊ต‰์žฅํžˆ ๊ธธ์–ด์ง€๊ณ , ๋˜ CSS๋ฅผ ๋ฐ”๊พธ๋ ค๋ฉด ๋‘ ํƒœ๊ทธ๋ฅผ ๋ชจ๋‘ ๋ฐ”๊ฟ”์ฃผ์–ด์•ผ ํ•˜๋Š” ๊ฒƒ์ด ์ƒ๋‹นํžˆ ๊ท€์ฐฎ์•˜๋‹ค.๐Ÿค”

์ด๋•Œ Polymorphic Components๊ฐ€ ๋“ฑ์žฅํ•œ๋‹ค. ์œ„์—์„œ ๋‹คํ˜•์„ฑ์ด ๋ฌด์—‡์ธ์ง€ ์จ๋†“์•˜๋Š”๋ฐ, Polymorphic Components๋ž€ ๊ฐ„๋‹จํžˆ ๋งํ•ด props๋กœ ํ•ด๋‹น ํƒœ๊ทธ๊ฐ€ ๋ฌด์—‡์ธ์ง€ ์ง€์ • ํ•ด์ฃผ๊ณ , ๋‚ด๊ฐ€ ์ง€์ •ํ•œ HTML tag ์†์„ฑ์„ ๋‚ด๋ ค์„œ ํ•„์š”์— ๋”ฐ๋ผ Link๋กœ๋„, div๋กœ๋„ ์“ธ ์ˆ˜ ์žˆ๋Š” ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งํ•œ๋‹ค.

์ด๊ฒƒ์„ ์ด์šฉํ•˜๋ฉด tailwind class๋Š” ํ•œ ๋ฒˆ๋งŒ ์ž‘์„ฑํ•˜๊ณ , props ๋‚ด๋ ค์ฃผ๋Š” ๊ฒƒ์„ ๋ฐ”๊พธ์–ด, ํ•„์š”์— ๋”ฐ๋ผ <Link>๋กœ๋„, <div>๋กœ๋„ ๋งŒ๋“ค ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค.

โœ…Polymorphic Components ์ „์ฒด ์ฝ”๋“œ

์ด ์ฝ”๋“œ๋Š” Polymorphic Components๋ฅผ ๋งŒ๋“œ๋Š” ์ฝ”๋“œ๋‹ค.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import React from "react"

type PolymorphicProps<C extends React.ElementType> = {
  as?: C
  children: React.ReactNode
} & React.ComponentPropsWithoutRef<C>

const Components = <C extends React.ElementType = "div">({
  as,
  children,
  ...props
}: PolymorphicProps<C>) => {
  const Component = as || "div"
  return <Component {...props}>{children}</Component>
}

์‹ค์ œ ์‚ฌ์šฉํ•  ๋•Œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

1
2
3
4
//as๋กœ a ํƒœ๊ทธ๋ฅผ ๋งŒ๋“ค์–ด๋ณด์ž!
<Components as="a" href="http://test.com" />
//์‹ค์ œ ๋ Œ๋”๋ง์€ ์ด๋ ‡๊ฒŒ..
<a href="http://example.com"></a>
1
2
3
4
//as๋กœ divํƒœ๊ทธ๋ฅผ ๋งŒ๋“ค์–ด๋ณด์ž!
<Components as="div" />
//์‹ค์ œ ๋ Œ๋”๋ง์€ ์ด๋ ‡๊ฒŒ..
<div></div>

์ฆ‰ ์œ„์—์„œ ๋ณด์•˜๋˜ ์˜ˆ์‹œ๋ฅผ ์•„๋ž˜์™€ ๊ฐ™์ด ์ ์„ ์ˆ˜ ์žˆ๋‹ค.

1
2
3
4
5
6
<Components
  as={data.url ? "a" : "div"}
  {...(data.url ? { href: data.url } : {})}
>
  {data.content}
</Components>

โœ…Polymorphic Components์˜ Typescript ํ•ด์„ํ•˜๊ธฐ

Polymorphic Components๋ฅผ ๋งŒ๋“ค ๋•Œ๋Š”, typescript๋ฅผ ์ ๊ทน ํ™œ์šฉํ•ด ๋ณด๋‹ค ์•ˆ์ „ํ•œ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.

๊ฐœ๋ฐœ์ž๊ฐ€ ์‹ค์ˆ˜๋กœ div๊ฐ€ ์•„๋‹Œ divv ๋ฅผ ๋‚ด๋ ค์ฃผ๊ฑฐ๋‚˜, button ํƒœ๊ทธ์ธ๋ฐ href๋ฅผ ๋‚ด๋ ค์ฃผ๋ ค๊ณ  ์‹œ๋„ํ•˜๋Š” ๊ฒƒ์„ ๋ง‰์•„์ฃผ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

๊ทธ๋Ÿผ ๋จผ์ € ์œ„์—์„œ ๋ถ€ํ„ฐ ์ฐจ๋ก€๋Œ€๋กœ ํ•ด์„ํ•ด๋ณด์ž.

1
2
3
4
type PolymorphicProps<C extends React.ElementType> = {
  as?: C
  children: React.ReactNode
} & React.ComponentPropsWithoutRef<C>
  1. type PolymorphicProps

    • PolymorphicProps๋ž€ ์ด๋ฆ„์˜ type์„ ์ง€์ •ํ•ด์ค€๋‹ค๋Š” ๋œป์ด๋‹ค.
  2. <C extends React.ElementType>

    • ์ œ๋„ค๋ฆญ ํƒ€์ž… C๊ฐ€ React.ElementType์œผ๋กœ ์ œํ•œ ๋œ๋‹ค๋Š” ์˜๋ฏธ๋‹ค.
    • :star: ์ œ๋„ค๋ฆญ ํƒ€์ž…์€ โ€œ์‚ฌ์šฉํ•  ๋•Œ ๊ฒฐ์ •โ€ ๋œ๋‹ค. ์ฆ‰, React.ElementType ์ค‘ ์‚ฌ์šฉ์ž๊ฐ€ ์ง€์ •ํ•œ C ํƒ€์ž…์— ๋”ฐ๋ผ PolymorphicProps์˜ ํƒ€์ž…์ด ๊ฒฐ์ •๋œ๋‹ค.
      • ์ด๋•Œ์˜ C๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด HTML ํƒœ๊ทธ ์ด๋ฆ„ (โ€˜divโ€™, โ€˜spanโ€™, โ€˜aโ€™ ๋“ฑ)์ด ๋œ๋‹ค. ์—ฌ๊ธฐ์„œ ์ฃผ์˜ํ•  ์ ์€ ์ด ์ฝ”๋“œ๊นŒ์ง€๋Š” C์˜ ๋ฒ”์œ„๋ฅผ ์ •์˜ํ•œ ๊ฒƒ์ด๋ฉฐ, PolymorphicProps<C>๊ฐ€ ์–ด๋–ค ์†์„ฑ(href ,type ๋“ฑ)์„ ํ—ˆ์šฉํ•˜๋Š”์ง€๋Š” ์ œํ•œ๋˜์–ด ์žˆ์ง€ ์•Š๋‹ค.
  3. = { ... } & React.ComponentPropsWithoutRef<C>

    • 2๋ฒˆ์„ ํ†ตํ•ด C(tag)๋ฅผ ์ง€์ •ํ–ˆ๋‹ค๋ฉด, ํ•ด๋‹น tag์— ๋”ฐ๋ผ ๋‚ด๋ ค์ค„ ์ˆ˜ ์žˆ๋Š” props๊ฐ€ ๋‹ฌ๋ผ์งˆ ๊ฒƒ์ž„์„ ์˜ˆ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค. (a์ผ๋•Œ๋งŒ href๋ฅผ ๋‚ด๋ ค์ค€๋‹ค ๋“ฑ)
    • ์ด ๋ถ€๋ถ„์„ ํ†ตํ•ด ๊ตฌ์ฒด์ ์œผ๋กœ ์–ด๋–ค props๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์„์ง€๋ฅผ ์ •์˜ํ•œ๋‹ค.
    • { ... } ์™€ React.ComponentPropsWithoutRef<C> ๋Š” ๊ต์ฐจํƒ€์ž… (A & B)๋กœ์„œ, A์™€ B์˜ ์†์„ฑ์„ ๋ชจ๋‘ ๊ฐ€์ง€๋Š” ํƒ€์ž…์ด๋‹ค.

๊ทธ๋‹ค์Œ์—๋Š” ์‹ค์ œ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งŒ๋“ค ๋•Œ ์‚ฌ์šฉ๋œ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋ฅผ ํ•ด์„ํ•ด๋ณด์ž.

1
2
3
4
5
6
7
8
const Components = <C extends React.ElementType = "div">({
  as,
  children,
  ...props
}: PolymorphicProps<C>) => {
  const Component = as || "div"
  return <Component {...props}>{children}</Component>
}
  1. Components = <C extends React.ElementType = "div">
    • ์ปดํฌ๋„ŒํŠธ๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ C์˜ ํƒ€์ž…์„ ๊ฒฐ์ •ํ•˜๊ฒ ๋‹ค๋Š” ์˜๋ฏธ
  2. :PolymorphicProps<C>
    • ์œ„์—์„œ ์ •์˜ํ•œ PolymorphicPropsํƒ€์ž…์„ ํ•ด๋‹น ์ปดํฌ๋„ŒํŠธ์˜ props ํƒ€์ž…์œผ๋กœ ์ง€์ •ํ•œ๋‹ค. ์ฆ‰, C์— ๋งž๋Š” ์†์„ฑ๋“ค์„ ํฌํ•จํ•˜๋Š” ํƒ€์ž…์ด ๋œ๋‹ค.

์ด๋Ÿฌํ•œ ๊ณผ์ •์„ ํ†ตํ•ด ์•ˆ์ „ํ•œ Polymorphic ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋งŒ๋“ค์–ด ์ง„๋‹ค.

๐Ÿ—‚๏ธ์ฐธ๊ณ  ์‚ฌ์ดํŠธ

This post is licensed under CC BY 4.0 by the author.