๐Ÿงฉ Compound Components๋ž€?

ํ˜ธ์ด์ดˆ์ดยท2025๋…„ 4์›” 13์ผ
post-thumbnail

๐Ÿ“Œ ๊ฐœ๋…์ •๋ฆฌ

React ์ปดํฌ๋„ŒํŠธ๋Š” ์‚ฌ์‹ค ํ•จ์ˆ˜๋‹ค.
๊ทธ๋ฆฌ๊ณ  JavaScript์—์„œ๋Š” ํ•จ์ˆ˜๋„ ๊ฐ์ฒด์ฒ˜๋Ÿผ ์†์„ฑ์„ ๊ฐ€์งˆ ์ˆ˜ ์žˆ๋‹ค!

์˜ˆ์‹œ

function sayHi() {
  console.log("hi");
}

sayHi.language = "Korean";
console.log(sayHi.language); // Korean

์ด ๊ฐœ๋…์„ React์— ์‘์šฉํ•˜๋ฉด ๋‹ค์Œ์ฒ˜๋Ÿผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค!

function Dropdown({ children }) {
  return <div>{children}</div>;
}

Dropdown.Trigger = function Trigger() {
  return <button>์—ด๊ธฐ</button>;
};

Dropdown.Item = function Item({ children }) {
  return <div>{children}</div>;
};

์ด์ œ ์œ„ ์˜ˆ์‹œ๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ์•„๋ž˜์ฒ˜๋Ÿผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

<Dropdown>
  <Dropdown.Trigger />
  <Dropdown.Item>ํ•ญ๋ชฉ</Dropdown.Item>
</Dropdown>

-> ์ด ๋ฐฉ์‹์„ ๋ฐ”๋กœ Compound Component ํŒจํ„ด์ด๋ผ๊ณ  ๋ถ€๋ฅธ๋‹ค.


โ“ ์™œ ์จ์•ผ ํ• ๊นŒ?

โŒ ์‚ฌ์šฉํ•˜์ง€ ์•Š์•˜์„ ๋•Œ (์ „ํ†ต์ ์ธ ๋ฐฉ์‹)

function Dropdown() {
  const [open, setOpen] = useState(false);

  return (
    <div>
      <button onClick={() => setOpen(!open)}>{open ? "๋‹ซ๊ธฐ" : "์—ด๊ธฐ"}</button>
      {open && <div>ํ•ญ๋ชฉ</div>}
    </div>
  );
}

์ด ๋ฐฉ์‹์€ ๋‹จ์ˆœํ•˜๊ธด ํ•˜์ง€๋งŒ, ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ฌธ์ œ๊ฐ€ ์žˆ๋‹ค.

  • UI ๊ตฌ์กฐ๊ฐ€ ๊ณ ์ •๋จ โ†’ ์ˆœ์„œ๋‚˜ ์กฐํ•ฉ ๋ฐ”๊พธ๊ธฐ ์–ด๋ ค์›€
  • ๋” ๋งŽ์€ ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ ์ƒ๊ธฐ๋ฉด ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ปค์ง (ํ™•์žฅ์„ฑ ๋–จ์–ด์ง)
  • ๋‹ค๋ฅธ ํŒŒ์ผ์—์„œ ๋ฒ„ํŠผ์ด๋‚˜ ํ•ญ๋ชฉ์„ ๋”ฐ๋กœ ์žฌ์‚ฌ์šฉํ•˜๊ธฐ ์–ด๋ ค์›€

โœ… Compound Component ์‚ฌ์šฉ ์‹œ ์žฅ์ 

  • UI ์กฐํ•ฉ ์œ ์—ฐ: ๊ตฌ์กฐ๋‚˜ ์ˆœ์„œ๋ฅผ ์ž์œ ๋กญ๊ฒŒ ์กฐ์ • ๊ฐ€๋Šฅ
  • ์ƒํƒœ ๊ณต์œ : Context๋กœ ์ž์‹๋“ค์ด ๋ถ€๋ชจ์˜ ์ƒํƒœ๋ฅผ ๊ณต์œ ํ•จ
  • ์ฝ”๋“œ ๊ตฌ์กฐ๊ฐ€ ๊น”๋”ํ•˜๊ณ  ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅ

โš™๏ธ ์‹ค์ œ ์‚ฌ์šฉ ์˜ˆ์‹œ (Context + Compound Components)

const DropdownContext = createContext();

function Dropdown({ children }) {
  const [open, setOpen] = useState(false);
  return (
    <DropdownContext.Provider value={{ open, setOpen }}>
      <div>{children}</div>
    </DropdownContext.Provider>
  );
}

Dropdown.Trigger = function Trigger() {
  const { open, setOpen } = useContext(DropdownContext);
  return <button onClick={() => setOpen(!open)}>{open ? "๋‹ซ๊ธฐ" : "์—ด๊ธฐ"}</button>;
};

Dropdown.Item = function Item({ children }) {
  const { open } = useContext(DropdownContext);
  return open ? <div>{children}</div> : null;
};

์ด์ œ ์•„๋ž˜์ฒ˜๋Ÿผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

<Dropdown>
  <Dropdown.Trigger />
  <Dropdown.Item>๋‚ด์šฉ</Dropdown.Item>
</Dropdown>

Dropdown.Trigger, Dropdown.Item์€ ๊ฐ๊ฐ Dropdown ์•ˆ์—์„œ๋งŒ ๋™์ž‘ํ•˜๊ฒŒ ์„ค๊ณ„๋œ ํ•˜์œ„ ์ปดํฌ๋„ŒํŠธ์ด๋‹ค.

๐Ÿงฉ {children} ์ด๋ž€?
React์—์„œ {children}์€ ์ปดํฌ๋„ŒํŠธ ํƒœ๊ทธ ์•ˆ์— ์žˆ๋Š” ๋‚ด์šฉ์„ ๊ฐ€๋ฆฌํ‚จ๋‹ค.

<Dropdown>
  <Dropdown.Trigger />
  <Dropdown.Item>๋‚ด์šฉ</Dropdown.Item>
</Dropdown>

์ด ๊ฒฝ์šฐ, <Dropdown> ์ปดํฌ๋„ŒํŠธ์˜ props.children์—๋Š” <Dropdown.Trigger />์™€ <Dropdown.Item />์ด ๋“ค์–ด๊ฐ„๋‹ค. ์ฆ‰, Dropdown ์ปดํฌ๋„ŒํŠธ ์•ˆ์—์„œ ์ด children์„ ๋ Œ๋”๋งํ•ด์ฃผ๋ฉด ๊ทธ ์•ˆ์˜ ๋‚ด์šฉ์ด ํ™”๋ฉด์— ๋‚˜ํƒ€๋‚œ๋‹ค.

โ—๏ธ์ฃผ์˜: Context ์—†์ด ๋‹จ๋… ์‚ฌ์šฉ ์‹œ ๋™์ž‘ ์•ˆ ํ•จ

<Dropdown.Trigger /> // โŒ
<Dropdown.Item>ํ˜ธ์ด์ดˆ์ด</Dropdown.Item> // โŒ

์™œ๋ƒํ•˜๋ฉด ๋‚ด๋ถ€์—์„œ useContext(DropdownContext)๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์—, ๋ฐ˜๋“œ์‹œ <Dropdown> ์•ˆ์— ์žˆ์–ด์•ผ ์ •์ƒ ๋™์ž‘ํ•œ๋‹ค.


๐Ÿง  Headless ๋ฐฉ์‹์ด๋ž€?

Headless๋ž€ UI๋ฅผ ์ œ๊ณตํ•˜์ง€ ์•Š๊ณ , ์˜ค์ง ๋กœ์ง๊ณผ ์ƒํƒœ๋งŒ ๊ด€๋ฆฌํ•˜๋Š” ๋ฐฉ์‹์„ ๋งํ•œ๋‹ค.

function useToggle() {
  const [on, setOn] = useState(false);
  const toggle = () => setOn((prev) => !prev);
  return { on, toggle };
}

์ด๊ฑด ๋กœ์ง๋งŒ ๋‹ด๊ธด ํ›…์ด๋‹ค. UI๋Š” ๋ณ„๋„๋กœ ๋งŒ๋“ค๊ณ , ์›ํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค!

โœ… Compound Component + Headless = ์ตœ๊ฐ• ์กฐํ•ฉ

  • ์ƒํƒœ ๊ด€๋ฆฌ: Context์—์„œ ๋‹ด๋‹น
  • UI ๊ตฌ์„ฑ: ์ž์œ ๋กญ๊ฒŒ ์กฐํ•ฉ ๊ฐ€๋Šฅ
  • ์œ ์—ฐ์„ฑ + ์žฌ์‚ฌ์šฉ์„ฑ + ํ™•์žฅ์„ฑ ๋ชจ๋‘ ํ™•๋ณด!

โœ… ๋งˆ๋ฌด๋ฆฌ

ํ•ญ๋ชฉ์„ค๋ช…
Compound Component์ปดํฌ๋„ŒํŠธ๋ฅผ ๊ฐ์ฒด์ฒ˜๋Ÿผ ๊ตฌ์„ฑํ•˜์—ฌ ํ•˜์œ„ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์†์„ฑ์œผ๋กœ ๋ถ™์ž„
์žฅ์ ์žฌ์‚ฌ์šฉ์„ฑ, ์ƒํƒœ ๊ณต์œ , ๊ฐ€๋…์„ฑ, ์œ ์—ฐ์„ฑ ํ–ฅ์ƒ
ํ•ต์‹ฌ ๊ธฐ์ˆ Context API + JS์˜ ํ•จ์ˆ˜๋Š” ๊ฐ์ฒด๋ผ๋Š” ์„ฑ์งˆ
Headless๋กœ์ง๊ณผ UI ๋ถ„๋ฆฌ, ์ปค์Šคํ„ฐ๋งˆ์ด์ง•์— ๊ฐ•ํ•จ
children๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ ์•ˆ์—์„œ ์ž์‹ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์œ ์—ฐํ•˜๊ฒŒ ๋ Œ๋”๋งํ•˜๊ฒŒ ํ•ด์ฃผ๋Š” ํŠน๋ณ„ํ•œ prop

Compound Components๋Š” ์ƒํƒœ ๊ณต์œ ๊ฐ€ ํ•„์š”ํ•œ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งŒ๋“ค ๋•Œ ๊ฐ•๋ ฅํ•œ ํŒจํ„ด์ด๋‹ค.

profile
์˜ ์„ฑ์žฅ์ผ์ง€

0๊ฐœ์˜ ๋Œ“๊ธ€