๐ก 20. ๋คํ์ฑ(Polymorphism)๊ณผ ์ ๊ทผ์ฑ(a11y)
๐ ๊ฐ์
๋๋ก๋ <button>์ผ๋ก, ๋๋ก๋ <a> ํ๊ทธ๋ <Link>๋ก ์์ ์์ฌ๋ก ๋ณ์ ํ๋ ๋คํ์ฑ ๋ ๋๋ง(as prop, Slot) ์ค๊ณ์, ์๊ฐ์ฅ์ ์ธ๋ ์๋ฒฝํ ์ฌ์ฉํ ์ ์๋ ์ค๋ฌดํ ์ปดํฌ๋ํธ์ ๋น๋ฐ์ ํํค์นฉ๋๋ค.
๐ฏ ์ด ์น์ ์ ์ฝ๊ณ ๋๋ฉด:
- ์ฌ๋ด ๊ณตํต
Button์ปดํฌ๋ํธ๊ฐ ์ ์ด๋จ ๋๋ ๋งํฌ(<a>)๋ก ๋ณ์ ํด์ผ ํ๋์ง ์ ์ฐ์ฑ์ ๋ณธ์ง์ ๊นจ๋ซ๋๋ค.asprop๊ณผ Radix UI์Slotํจํด์ ์ฌ์ฉํ์ฌ ๋ง๋ฒ์ฒ๋ผ ํ๊ทธ ๊ป๋ฐ๊ธฐ๋ฅผ ๊ฐ์ ๋ผ์ฐ๋ '๋คํ์ฑ' ์ปดํฌ๋ํธ๋ฅผ ์ค๊ณํ ์ ์๋ค.- ํค๋ณด๋ ๋ค๋น๊ฒ์ด์ ๊ณผ ์คํฌ๋ฆฐ ๋ฆฌ๋(VoiceOver)๋ฅผ ์ง์ํ๋
aria-์์ฑ์ ๊ธฐ๋ณธ๊ธฐ๋ฅผ ๊ฐ์ถ ๋ฐฐ๋ฆฌ์ดํ๋ฆฌ(Barrier-Free) ํ๋ก ํธ์๋ ๊ฐ๋ฐ์๋ก ๊ฑฐ๋ญ๋๋ค.
๐ ๋ชฉ์ฐจ
- ๐ค ์ ์์์ผ ํ๋๊ฐ: '๊ป๋ฐ๊ธฐ'์ ์ ์ฃผ
- ๐๏ธ ๋น์ ๋ก ๋จผ์ ์ดํดํ๊ธฐ
- ๐งฉ ๊ทน์ ์ฒด๋: ์ ์ฒด์ดํ ๋คํ์ฑ ์ค๊ณํ๊ธฐ
- ๐ ๋น๊ณผ ์๋ฆฌ: ์ ๊ทผ์ฑ (Accessibility, a11y) ์ฑ๊ธฐ๊ธฐ
๐ ์ด ๋ฌธ์๋ฅผ ์ฝ๊ธฐ ์ ์
โฑ๏ธ ์์ ์ฝ๊ธฐ ์๊ฐ: 10๋ถ / ํต์ฌ ํํธ: 6๋ถ
๐บ๏ธ ์ด ๋ฌธ์์ ๋ฐฐ๊ฒฝ ์ธ๊ณ๊ด: '์์๋ค ์ปค๋ฎค๋ํฐ'
- ์์(๋์์ด๋): "์์ฒ ๋! ์ด๋ฒ์ ๋ง๋ [ํ์๊ฐ์ ์๋ฃ] ๋ฒํผ ๋์์ธ ๋๋ฌด ์๋ป์. ์ ๋์์ธ ๊ทธ๋๋ก ๋ณต์ฌํด์, ๋ฉ์ธ ํ์ด์ง ํ๋จ์ [์ด๋ฒคํธ ํ์ด์ง๋ก ์ด๋] ๋ฒํผ์ผ๋ก๋ ๋๊ฐ์ด ๋ ๋๋งํด์ฃผ์ค ์ ์์ฃ ?"
- ์์ฒ (์ ์
): "๋ค! ๊ทธ์ผ ๋น์ฐํ์ฃ (์์ ๋ง๋ง). ์ด? ๊ทผ๋ฐ [ํ์๊ฐ์
์๋ฃ]๋ ๋๋ ์ ๋ ํผ์ ์ ์ถํ๋
<button>์ธ๋ฐ... [์ด๋] ๋ฒํผ์ ๋ค๋ฅธ ํ์ด์ง๋ก ์์ ํ ๋์ด๊ฐ๋<a>ํ๊ทธ(Next.js Link)์ฌ์ผ ํ์์์? ๊ทธ๋ผ ๋์์ธ์ ๋๊ฐ์๋ฐ ์ปดํฌ๋ํธ๋ฅผ<Button>์ฉ,<LinkButton>์ฉ 2๊ฐ๋ก ๋ฐ๋ก ์ชผ๊ฐ์ผ ํ๋์? CSS ์ค๋ณต์ด 500์ค์ด ๋์ด๊ฐ๋๋ฐ..." - ์ํธ(๋ฆฌ๋): "์์ฒ ๋, ํ๊ทธ ์ข ๋ฅ ๋๋ฌธ์ ๊ฐ์ ๋์์ธ์ ๊ณ์ ๋ณต์ฌํ๋ฉด ๋์์ธ ์์คํ ์ด ๊ธ๋ฐฉ ํ๋ค๋ฆฝ๋๋ค. ๋ฒํผ ์ปดํฌ๋ํธ๋ ์คํ์ผ์ ์ ์งํ๋ ์ค์ ํ๊ทธ๋ง ๋ฐ๊ฟ ์ ์์ด์ผ ํด์. ๊ทธ๋ฆฌ๊ณ ์คํฌ๋ฆฐ ๋ฆฌ๋์ ํค๋ณด๋ ์ฌ์ฉ์๋ ๊ฐ์ ๋ฒํผ์ ์ฌ์ฉํ ์ ์๋๋ก ์ ๊ทผ์ฑ(a11y) ์์ฑ๊น์ง ๊ฐ์ด ์ค๊ณํฉ์๋ค."
๐ค ์ ์์์ผ ํ๋๊ฐ: ํ๊ทธ์ ์คํ์ผ์ ๋ถ๋ฆฌํด์ผ ํ๋ ์ด์
๋ชจ์๊ณผ ํฐํธ ํฌ๊ธฐ, ํธ๋ฒ(Hover) ์ ํ๋์์ผ๋ก ๋ณํ๋ ์ ๋๋ฉ์ด์
๊น์ง ๋์ผํ ๋ฒํผ ๋์์ธ์
๋๋ค.
๊ทธ๋ฐ๋ฐ ์ด๋ค ๊ฒฝ์ฐ๋ API๋ฅผ ์๋ type="submit" ๋ฒํผ์ด๊ณ , ์ด๋ค ๊ฒฝ์ฐ๋ ๊ตฌ๊ธ ์ฐฝ์ ์ฌ๋ href="https://..." ํํ์ ์ต์ปค(A) ํ๊ทธ๋ผ๊ณ ์นฉ์๋ค.
// โ ์์ฒ ์ด์ ํ๋์ฝ๋ฉ๋ ๊ตฌํ (๋์์ธ ์ค๋ณต์ ๋ช)
// 1๋ฒ: ํผ ์ ์ถ์ฉ ๋ฒํผ
export function DefaultButton({ children, onClick }) {
return <button className="super-gorgeous-blue-btn" onClick={onClick}>{children}</button>;
}
// 2๋ฒ: ์ธ๋ถ ๋งํฌ ์ด๋์ฉ ๋ฒํผ
export function LinkButton({ children, href }) {
// CSS ํด๋์ค ์คํ๋ผ๋ ๋๋ฉด ๋์์ธ์ด ํ์ด์ง๋ค!
return <a className="super-gorgeous-blue-btn" href={href}>{children}</a>;
}๋์์ธ ์์คํ (Design System)์ ๊ตฌ์ถํ๋ ํ์์ ์ด๋ฐ ๋ฐฉ์์ด ๋ฐ๋ณต๋๋ฉด ์ ์ง๋ณด์ ๋น์ฉ์ด ๋น ๋ฅด๊ฒ ์ปค์ง๋๋ค. ๋์์ธ(Style)๊ณผ ๊ธฐ๋ฅ(HTML Tag)์ ๋ถ๋ฆฌํด์ ์๊ฐํด์ผ ํฉ๋๋ค. ์ฐ๋ฆฌ๋ ์คํ์ผ์ ๊ณต์ ํ๋ ์ค์ ๋ ๋๋ง ํ๊ทธ๋ ์ํฉ์ ๋ง๊ฒ ๋ฐ๊ฟ ์ ์๋ ๋คํ์ฑ(Polymorphism) ๋ฒํผ์ ๋ง๋ค์ด์ผ ํฉ๋๋ค.
๐๏ธ ๋น์ ๋ก ๋จผ์ ์ดํดํ๊ธฐ
๐ง 5์ด์๊ฒ ์ค๋ช ํ๋ค๋ฉด?
- ๊ณผ๊ฑฐ์ ๋ปฃ๋ปฃํ ๊ฐ์ท (ํ๋์ฝ๋ฉ): "์ด ํ๋์ ๊ฐ์ท์ ๋ฌด์กฐ๊ฑด '๊ฒ์ฌ(Button)' ์ ์ฉ์ ๋๋ค!" ๋ง๋ฒ์ฌ(Link)๊ฐ ์ ์ผ๋ ค๊ณ ํ๋ฉด ๊ฐ์ ๊ฐ์ท์ ํ๋ ๋ ๋ง๋ค์ด์ผ ํฉ๋๋ค.
- ๋คํ์ฑ์ ๋ง๋ฒ ๊ฐ์ท (Polymorphic): ์ด ํ๋์ ๊ฐ์ท(CSS ์คํ์ผ)์ ์ ๋ ์ฌ๋์ ์ญํ ์ ๋ง์ถฐ ํํ๋ฅผ ๋ฐ๊ฟ๋๋ค.
๋ง๋ฒ์ฌ๊ฐ ์ ์ผ๋ฉด ์งํก์ด ๊ตฌ๋ฉ(href)์ด ๋ ์๋ ์ท์ผ๋ก ์ค๋ฅด๋ฅต ๋ณํ๊ณ , ๊ฒ์ฌ๊ฐ ์ ์ผ๋ฉด ์นผ๊ฝ์ด(type="submit")๊ฐ ๋ฌ๋ฆฐ ๋ฒํผ์ผ๋ก ๋ณ์ ํ์ฃ . ํ์ง๋ง ๊ฒ์์ ๋ณผ ๋ ๋๋ถ์ ํ๋ ํฉ๊ธ์ ์คํ์ผ์ ์๋ฒฝํ๊ฒ ๋๊ฐ์ด ์ ์งํฉ๋๋ค.
๐งฉ ๊ทน์ ์ฒด๋: ์ ์ฒด์ดํ ๋คํ์ฑ ์ค๊ณํ๊ธฐ
๋ณดํต ์ค๋ฌด์์๋ ์ด "๋ณ์ " ์ง์๋ฅผ ๋ด๋ฆฌ๊ธฐ ์ํด ๋ ๊ฐ์ง ํจํด์ ์ฃผ๋ก ์ฌ์ฉํฉ๋๋ค.
โ
๋ฐฉ๋ฒ 1: as Prop ํจํด (์ ํต์ ๊ฐ์)
๊ฐ์ฅ ๋ฌด๋ํ๊ณ ๋๋ฆฌ ์๋ ค์ง ํจํด์
๋๋ค. "as๋ก ์ ๋ฌํ ํ๊ทธ๋ ์ปดํฌ๋ํธ๋ก ๋ ๋๋งํ๋ผ"๋ผ๊ณ ๋ช
์ํด์ค๋๋ค.
// ๐ฏ as๋ฅผ ์ฌ์ฉํ ๋คํ์ฑ ๋ฒํผ ์ค๊ณ
export function MagicButton({ as: Component = "button", children, ...props }) {
// Component์ "a"๊ฐ ๋ค์ด์ค๋ฉด <a> ํ๊ทธ๋ก, "button"์ด๋ฉด <button>์ผ๋ก ๋ ๋๋งํ๋ค.
// Next.js์ <Link> ์ปดํฌ๋ํธ๋ฅผ ๋๊ฒจ๋ ๊ฐ์ ์คํ์ผ์ ์ ์งํ ์ ์๋ค.
return (
<Component className="super-gorgeous-blue-btn" {...props}>
{children}
</Component>
);
}
// ------ ์ฌ์ฉํ๋ ์ชฝ (์๋น์) ------
function App() {
return (
<div>
{/* 1. ๊ธฐ๋ณธ์ ์ ์ถ์ฉ <button>์ผ๋ก ๋ ๋๋ง ๋จ */}
<MagicButton type="submit">๋ก๊ทธ์ธ</MagicButton>
{/* 2. ์ธ๋ถ ๋งํฌ๋ก ์ด๋ํ๋ <a> ํ๊ทธ๋ก ๋ ๋๋ง๋๋ค. ๋์์ธ์ ๊ฐ๋ค. */}
<MagicButton as="a" href="https://google.com">
๊ตฌ๊ธ์์ ๋ก๊ทธ์ธ
</MagicButton>
{/* 3. ๋ผ์ฐํ
์ต์ ํ๋ฅผ ์ํด Next.js Link ์ปดํฌ๋ํธ๋ก ๋ ๋๋งํ๋ค. */}
<MagicButton as={Link} href="/home">
์ฐ๋ฆฌ์ง์ผ๋ก ๊ฐ์
</MagicButton>
</div>
)
}์ด์ ํ๋์ ๋ฒํผ CSS๋ ํ ๋ฒ(className="super-gorgeous...")๋ง ์ ์ธ๋๊ณ , ์ฌ๋ฌ ํ๊ทธ ์ญํ ์ ์ฌ์ฌ์ฉ๋ฉ๋๋ค.
โ
๋ฐฉ๋ฒ 2: Slot (asChild) ํจํด (ํ๋ Radix UI์ ๋์ธ)
as ํจํด์ ์ง๊ด์ ์ด์ง๋ง ํ๊ณ๊ฐ ์์ต๋๋ค. ํ์
์คํฌ๋ฆฝํธ ์ถ๋ก (TypeScript Generic type infer)์ ์ ๊ตํ๊ฒ ์์ฑํ๋ ค๋ฉด ์ฝ๋๊ฐ ๊ฝค ๋ณต์กํด์ง ์ ์์ต๋๋ค. "as๊ฐ a๋ผ๋ฉด href๋ฅผ ํ์ฉํ๋ผ" ๊ฐ์ ์กฐ๊ฑด์ ํ์
์ผ๋ก ํํํ๊ธฐ๊ฐ ๊น๋ค๋กญ์ฃ .
๊ทธ๋์ Radix UI, shadcn/ui ๊ฐ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์์ ๋ง์ด ์ฐ๋ ๋ฐฉ์์ด ์์์๊ฒ ๋์์ธ ์์ฑ์ ๋ณํฉํ๋ Slot (asChild) ํจํด์
๋๋ค.
// ๐ฏ Slot (asChild) ํจํด์ ์ฒ ํ
import { Slot } from '@radix-ui/react-slot'; // (์๋ฆฌ๋ฅผ ๊ตฌํํ ์ ๋ช
ํด)
// ๋์์ธ ๊ป๋ฐ๊ธฐ๋ง ์ฅ๊ณ ์๋ ๋จ์ ๋ํผ ์ปดํฌ๋ํธ
export function MasterButton({ asChild, children, ...props }) {
// asChild๊ฐ ์ผ์ ธ ์์ผ๋ฉด Slot์ด ์์ ์์๋ฅผ ์ค์ ํ๊ทธ๋ก ์ ์งํ๋ฉด์
// className, ์ด๋ฒคํธ ํธ๋ค๋ฌ ๊ฐ์ props๋ฅผ ์์์๊ฒ ๋ณํฉํ๋ค.
const Comp = asChild ? Slot : "button";
return (
<Comp className="super-gorgeous-blue-btn" {...props}>
{children}
</Comp>
)
}
// ------ ์ฌ์ฉํ๋ ์ชฝ (์๋น์) ------
function App() {
return (
<MasterButton asChild>
<a href="https://google.com">ํ๋ ๊ตฌ๊ธ๋ฒํผ</a>
{/* ๊ฒฐ๊ณผ: Slot์ด <a href="..." className="super-gorgeous..." /> ๋ก ๋ณํฉํด ๋ ๋๋ง! */}
</MasterButton>
)
}์ด ๋ฐฉ๋ฒ์ ์ฅ์ : <a> ํ๊ทธ๋ ์์ ์ด ๊ฐ์ง ๊ณ ์ ์์ฑ(href)๋ง ์ฑ
์์ง๊ณ , MasterButton์ ๊ณตํต ๋์์ธ๊ณผ ์ด๋ฒคํธ๋ง ์ฑ
์์ง๋๋ค. ํ๋กญ์ค ๊ตฌ์กฐ๊ฐ ๋ ์ฝํ๊ณ ์ค์ ํ๊ทธ์ ์๋ฏธ๋ ์ ์ง๋ฉ๋๋ค.
๐ ๋น๊ณผ ์๋ฆฌ: ์ ๊ทผ์ฑ (Accessibility, a11y) ์ฑ๊ธฐ๊ธฐ
์์ ๋คํ์ฑ ๋ฒํผ์ ๋ง๋ค์๋ค๊ณ ๋์ด ์๋๋๋ค. ์ด ๋ฒํผ์ ๋ง์ฐ์ค๋ฅผ ์ฐ๊ธฐ ์ด๋ ค์ด ์ฌ์ฉ์, ํ๋ฉด์ ๋ณด๊ธฐ ์ด๋ ค์ ์คํฌ๋ฆฐ ๋ฆฌ๋๋ก ํ์ํ๋ ์ฌ์ฉ์์๊ฒ๋ ๋๊ฐ์ด ์๋ํด์ผ ํฉ๋๋ค.
์ด๊ฒ์ ์ฐ๋ฆฌ๋ **a11y (์ ๊ทผ์ฑ)**์ด๋ผ๊ณ ๋ถ๋ฅด๋ฉฐ, ํ์ ๋์์ธ ์์คํ ์์ ๋งค์ฐ ์ค์ํ ํ์ง ๊ธฐ์ค์ผ๋ก ๋ด ๋๋ค.
aria-์์ฑ์ ๋ช: ์ ๋ฒํผ์ด ๋จ์ํ '์ ์ก' ๊ธ์๋ง ์๋ ๊ฒ ์๋๋ผ๋ฉด?
// ์คํฌ๋ฆฐ ๋ฆฌ๋๋ ์ด ๋ฒํผ์ "์๋ด๋ฌธ ์ด๊ธฐ"๋ผ๊ณ ์ฝ๋๋ค.
<MagicButton aria-label="์๋ด๋ฌธ ์ด๊ธฐ" aria-expanded={isOpen} onClick={toggle}>
๐ {/* ๋์ ๋ณด์ด๋ ํ
์คํธ ์์ด ์์ด์ฝ๋ง ์๋ ๋ฒํผ์๋ ์ ๊ทผ ๊ฐ๋ฅํ ์ด๋ฆ์ด ํ์ํ๋ค. */}
</MagicButton>- Tab ํฌ์ปค์ค: ๋ง์ฐ์ค๋ฅผ ์ฐ์ง ์๋ ์ฌ์ฉ์๋ ํค๋ณด๋์
Tab๊ณผEnterํค๋ง์ผ๋ก ํ๋ฉด์ ์ด๋ํฉ๋๋ค. ์ฌ๊ธฐ์ ๋คํ์ฑ ๋ฒํผ์ดdiv๋spanํ๊ทธ๋ก ๋ ๋๋ง ๋์๋ค๋ฉด ํค๋ณด๋ ์ด์ ์ด ์กํ์ง ์์ ๋ง์ฐ์ค๋ก๋ง ํด๋ฆญ ๊ฐ๋ฅํ ์์๊ฐ ๋ฉ๋๋ค.
๊ทธ๋ ๊ธฐ์ ๋ธ๋ผ์ฐ์ ๊ฐ ๊ธฐ๋ณธ ํค๋ณด๋ ์กฐ์์ ์ง์ํ๋ ๋ค์ดํฐ๋ธ <button> ์์๋ <a> ํ๊ทธ๋ฅผ ์ ์งํ๋ ๋คํ์ฑ ํจํด์ ๊ฐ๋ฐ์ ์ทจํฅ์ ๋์ด ์ค์ ์ฌ์ฉ์ฑ์ ์งํค๋ ํต์ฌ ์ค๊ณ๊ฐ ๋ฉ๋๋ค.
๐ ์ด๋ฒ์ ๋ฐฐ์ด ๋ด์ฉ ์ด์ ๋ฆฌ
| ๊ด์ | ๋จ์ผ ํ๊ทธ ๋ฒํผ <button> | ๋คํ์ฑ(Polymorphism) ๋ฒํผ <Button as="a"> |
|---|---|---|
| ์ฅ์ | ๋ง๋ค๊ธฐ๊ฐ ๊ฐ์ฅ ๋น ๋ฅด๋ค. | ๋์์ธ CSS์ ๋จ์ผ ์์ค ์์น. ํ ๋ฒ ๋ฐ๊ฟ๋ ์ฌ๋ฌ ๋งํฌ ๋ฒํผ๊ณผ ์ ์ถ ๋ฒํผ์ ๊ฐ์ ์คํ์ผ์ด ์ ์ฉ๋๋ค. |
| ๋จ์ / ์ ์ฝ | href๋ฅผ ์ธ ์ ์๊ณ ๋ฌด๋ฆฌํ๊ฒ window.location.href= ํด๋ฆญ ์ด๋ฒคํธ๋ฅผ ์ฌ์ฉํด ๊ฒ์ ์์ง(๊ฒ์ ๋
ธ์ถ)์ด ๊นจ์ง๋ค. | ์์ฑํ ๋ TypeScript ํ์ ์ค๊ณ๊ฐ ๋ณต์กํด์ง ์ ์๋ค. (์ต๊ทผ์ Slot ํจํด์ด ์ด ๋จ์ ์ ์์ ์ค) |
| ์ ๊ทผ์ฑ(a11y)์ ์ค์์ฑ | ์คํฌ๋ฆฐ ๋ฆฌ๋๋ ์ด ์์๊ฐ ์ด๋ค ๋์์ ํ๋์ง ์์์ผ ํ๋ค. ์์ด์ฝ ์ ์ฉ ๋ฒํผ์ด๋ฉด ๊ผญ aria-label์ ์ ๊ณตํ๋ค. |
๐ก ํ ์ค๋ก ๊ธฐ์ตํ๊ธฐ
"๋ชจ์์ ๊ฐ๊ฒ ์ ์งํ๊ณ ํ๊ทธ์ ์๋ฏธ๋ ์ํฉ์ ๋ง์ถฐ ๋ฐ๊ฟ์ผ ํ ๋,as์asChild๋ ๊ฐ์ฅ ์ค์ฉ์ ์ธ ์ ํ์ง๋ค."
๐ ๋ง๋ฌด๋ฆฌ ํด์ฆ
Q1. ๋น์ ์ด ๊ณตํต ์ปดํฌ๋ํธ(Design System) ๊ฐ๋ฐํ์ ํฉ๋ฅํ์ต๋๋ค. ์ฒซ ์๋ฌด๋ก Button ์ปดํฌ๋ํธ๋ฅผ ์ค๊ณํ๋๋ฐ, ๊ธฐ์กด ํ์ ์์ฒ ์ด๊ฐ ๋ง๋ค์ด๋ ์ฝ๋๋ ์๋์ ๊ฐ์ต๋๋ค. ์๋์ด์ ๊ด์ ์์, ๋คํ์ฑ๊ณผ SEO, ์ ๊ทผ์ฑ(a11y) ์ธก๋ฉด์ ๊ฐ์ฅ ๋ฌธ์ ๊ฐ ํฐ ๋จ์ ์ ๊ณจ๋ผ๋ณด์ธ์.
function SimpleButton({ title, url }) {
// ์ฌ์ฉ์๊ฐ url์ ๋๊ธฐ๋ฉด ์๋ฐ์คํฌ๋ฆฝํธ๋ก ๊ฐ์ ๋ก ์ฐฝ์ ์ด๋์ํจ๋ค!
const pushLink = () => window.location.href = url;
return <div className="btn" onClick={url ? pushLink : undefined}>{title}</div>
}- A)
div๋ฅผ ๋ ๋๋งํ๋ฉด React๊ฐ ์ปดํ์ผ ํ์์ ์ด๋ฅผ ๊ฑฐ๋ถํ๋ค. - B) ์ด ๋ฒํผ์ ๊ตฌ๊ธ ๊ฒ์ ์์ง ๋ก๋ด(Bot)์ด ํ์ฑํ ๋ ๊ทธ์ "์๋ฌด๋ฐ ์ด๋ ๊ธฐ๋ฅ์ด ์๋ ๊ธ์ ํ ์ค(
div๋ธ๋ก)"์ผ๋ก ์ทจ๊ธํ๋ฏ๋ก ์ฌ์ดํธ ๊ฐ ์ฐ๊ฒฐ ๊ณ ๋ฆฌ(SEO)๊ฐ ๊นจ์ง๋ค. ๋ํ, ํค๋ณด๋ Tab ํค๋ฅผ ๋๋ฅด๋ ์๊ฐ/์ง์ฒด ์ฅ์ ์ธ ์ ์ ๋ค์ด ์ด ๋ฒํผ์ ๋๋ฌ์กฐ์ฐจ ํ ์ ์๊ณ , ์ํฐ ํค๋ฅผ ์ณ๋ ์๋ฌด ์ผ๋ ๋ฒ์ด์ง์ง ์๋๋ค. - C) ์ด ๋ฒํผ์
divํ๊ทธ๋ก ์ฝ๋งค์ฌ ์๊ธฐ์ ์คํ์ผ(className="btn")์ด ๋ฐ์ผ๋ก ์์ด ๋๊ฐ ๊ธ๋ก๋ฒ ์ค์ผ์ ์ผ์ผํค๋ ๋ฒ๊ทธ๋ฅผ ์ ๋ฐํ๋ค.
โ ์ ๋ต: B
๐ก ์์ธ ํด์ค: div์ onClick์ ๋ถ์ฌ ์ต์ปค ์ด๋์ ํ๋ด ๋ด๋ฉด ๋งํฌ์ ์๋ฏธ, ํค๋ณด๋ ์ ๊ทผ์ฑ, ๊ฒ์ ์์ง์ด ์ดํดํ๋ ์ฐ๊ฒฐ ๊ตฌ์กฐ๊ฐ ๋ชจ๋ ์ฝํด์ง๋๋ค. ์ด๋ ๋์์ ์ค์ <a>๋ ํ๋ ์์ํฌ์ Link๋ก ํํํ๊ณ , ๋ฒํผ ๋์์ ๋ค์ดํฐ๋ธ <button>์ผ๋ก ํํํ๋ ๊ฒ์ด ๊ธฐ๋ณธ์
๋๋ค.
Q2. ์ต๊ทผ UI ๋ผ์ด๋ธ๋ฌ๋ฆฌ(Radix UI ๋ฑ)๊ฐ ์ ๊ณตํ๋ asChild ํน์ Slot ํจํด์ด, ๊ธฐ์กด as="a" ๋ฐฉ์๋ณด๋ค ์ ํธ๋๋ ํต์ฌ์ ์ธ ๊ฐ๋ฐ ๊ฒฝํ(DX) ์ด์ ๋ฅผ ์ฃผ๊ด์์ผ๋ก ์ค๋ช
ํด๋ณด์ธ์.
โ
์ ๋ต ๋ฐ ์ฃผ๊ด์ ํด์ค:
"๊ธฐ์กด as="a" ๋ฐฉ์์ ๋ถ๋ชจ ์ปดํฌ๋ํธ๊ฐ href๋ target="_blank" ๊ฐ์ ์์ ํ๊ทธ์ ์์ฑ๊น์ง ๋๊ฒ ๋ฐ์์ผ ํด์ ์ ๋ค๋ฆญ ํ์
์ด ๋ณต์กํด์ง๊ธฐ ์ฝ์ต๋๋ค. ๋ฐ๋ฉด asChild(Slot) ํจํด์ ์ค์ ํ๊ทธ๋ฅผ ์์์ด ์ฑ
์์ง๊ณ , ๋ถ๋ชจ๋ ๊ณตํต ์คํ์ผ๊ณผ ์ด๋ฒคํธ๋ง ๋ณํฉํ๋ฏ๋ก props ์ถฉ๋๊ณผ ํ์
๋ถ๊ธฐ๋ฅผ ์ค์ผ ์ ์์ต๋๋ค."
Q3. ์์ฒ ์ด์ ํ
์คํธ ํ์: ์์์ด ์์ด์ฝ๋ง ๋ณด์ด๋ "์๋ฆผ ์ค์ ์ด๊ธฐ" ๋ฒํผ์ ๋์์ธ ์์คํ
์ ์ถ๊ฐํ์ต๋๋ค. ๊ฐ์ ์คํ์ผ์ <button>๊ณผ <a> ์์ชฝ์์ ์ฌ์ฌ์ฉ๋์ด์ผ ํ๊ณ , ํค๋ณด๋ ์ฌ์ฉ์์ ์คํฌ๋ฆฐ ๋ฆฌ๋ ์ฌ์ฉ์๋ ์ฌ์ฉํ ์ ์์ด์ผ ํฉ๋๋ค. ์ํธ๊ฐ ์น์ธํ ๊ตฌํ์ ๋ฌด์์ธ๊ฐ์?
- A)
<div onClick>์ CSS๋ฅผ ์ ํ๊ณ , ์์ด์ฝ๋ง ๋ฃ๋๋ค. ๋ง์ฐ์ค๋ก ํด๋ฆญ๋๋ฏ๋ก ์ถฉ๋ถํ๋ค. - B) ๊ธฐ๋ณธ ๋์์ ๋ค์ดํฐ๋ธ
<button>์ผ๋ก ๋๊ณ , ๋งํฌ๊ฐ ํ์ํ ๋๋as๋asChild๋ก ์ค์ <a>/Link๋ฅผ ๋ ๋๋งํ๋ค. ์์ด์ฝ ์ ์ฉ ๋ฒํผ์๋aria-label๊ฐ์ ์ ๊ทผ ๊ฐ๋ฅํ ์ด๋ฆ์ ์ ๊ณตํ๋ค. - C) ๋ชจ๋ ๋ฒํผ์
<a href="#">๋ก ํต์ผํ๊ณ , ์ ์ถ ๋์์preventDefault()๋ก ํ๋ด ๋ธ๋ค.
โ ์ ๋ต: B
๐ก ์์ธ ํด์ค: ๋์์ธ ์์คํ
์ ๋คํ์ฑ์ "๋ชจ์์ ๊ฐ๊ฒ, ์๋ฏธ๋ ์ ํํ๊ฒ" ๋ง๋๋ ์ผ์
๋๋ค. ํด๋ฆญ ์ก์
์ <button>, ์ด๋์ <a>/Link๊ฐ ๋งก์์ผ ๋ธ๋ผ์ฐ์ ๊ธฐ๋ณธ ํค๋ณด๋ ๋์, SEO, ์คํฌ๋ฆฐ ๋ฆฌ๋ ์๋ฏธ๊ฐ ์ด์๋ฉ๋๋ค. ์์ด์ฝ๋ง ์๋ ์ปจํธ๋กค์ ๋ณด์ด๋ ํ
์คํธ๊ฐ ์์ผ๋ฏ๋ก aria-label์ฒ๋ผ ์ ๊ทผ ๊ฐ๋ฅํ ์ด๋ฆ๋ ํ์ํฉ๋๋ค. ์ํธ๋ผ๋ฉด CSS ์ฌ์ฌ์ฉ๋ณด๋ค ํ๊ทธ ์๋ฏธ์ ์ ๊ทผ์ฑ ๊ณ์ฝ์ ๋จผ์ ํ์ธํ ๊ฒ๋๋ค.
๐ฃ ์์ฒ ์ด์ ํด๊ทผ ์ผ๊ธฐ
๋์์ธ์ด ๊ฐ๋ค๋ ์ด์ ๋ก ๋งํฌ๋ ๋ฒํผ๋ ์ ๋ถ ํ ์ปดํฌ๋ํธ๋ก ์ฐ๊ฒจ ๋ฃ๋ ์ฝ๋๊ฐ ๋ ์ฌ๋๋ค. ์ค๋์ ๋คํ์ฑ์ด ๋จ์ํ CSS ์ค๋ณต์ ์ค์ด๋ ํจํด์ด ์๋๋ผ, ๊ฐ์ ์๊ฐ ๋์์ธ ์์์๋ HTML ์๋ฏธ๋ฅผ ์ ํํ ๋ณด์กดํ๋ ์ค๊ณ๋ผ๋ ๊ฑธ ๋ฐฐ์ ๋ค.
๐ก "๋์์ธ์ ๊ณต์ ํ๋ ์๋ฏธ๋ ์์ด์ง ๋ง์. ์ด๋์ ๋งํฌ, ์คํ์ ๋ฒํผ, ์์ด์ฝ ์ ์ฉ ์ปจํธ๋กค์ ์ ๊ทผ ๊ฐ๋ฅํ ์ด๋ฆ๊น์ง๊ฐ ๊ณ์ฝ์ด๋ค."
์ํธ ์ ๋ฐฐ๊ฐ "์ปดํฌ๋ํธ API๊ฐ ์๋ป๋ ํค๋ณด๋๋ก ๋ชป ์ฐ๋ฉด ๋์์ธ ์์คํ
์ด ์๋๋ค"๋ผ๊ณ ํ ๊ฒ ์ค๋ ๋จ๋๋ค. ๋ด์ผ๋ถํฐ ๊ณตํต ์ปดํฌ๋ํธ๋ฅผ ๋ฆฌ๋ทฐํ ๋๋ as/asChild ํ์
๋ณด๋ค ๋จผ์ ์ค์ ๋ ๋๋ง ํ๊ทธ, ํฌ์ปค์ค ๊ฐ๋ฅ ์ฌ๋ถ, ์ ๊ทผ ๊ฐ๋ฅํ ์ด๋ฆ, ๋งํฌ์ SEO ์๋ฏธ๋ฅผ ์ฒดํฌํ๊ฒ ๋ค. ์์ฒ ๋ ์ด์ ์์ ์ปดํฌ๋ํธ๊ฐ ์๋๋ผ ์ฌ์ฉ ๊ฐ๋ฅํ ์ปดํฌ๋ํธ๋ฅผ ์ค๊ณํด์ผ ํ๋ค.