Skip to content

Instantly share code, notes, and snippets.

@leedc0101
Created March 9, 2026 01:30
Show Gist options
  • Select an option

  • Save leedc0101/b2e8d9fc183d0ab00875128f353fa6a4 to your computer and use it in GitHub Desktop.

Select an option

Save leedc0101/b2e8d9fc183d0ab00875128f353fa6a4 to your computer and use it in GitHub Desktop.
frontend-briefing-2026-03-09-KST

์›๋ฌธ ์ œ๋ชฉ: Your Dialog Has role='dialog'. That Doesn't Make It Accessible. ์›๋ฌธ ๋งํฌ: https://dev.to/vmvenkatesh78/your-dialog-has-roledialog-that-doesnt-make-it-accessible-4lha ๋ฒˆ์—ญ์ผ: 2026-03-09 KST

role="dialog"๋งŒ ๋ถ™์ธ๋‹ค๊ณ  ์ ‘๊ทผ ๊ฐ€๋Šฅํ•œ ๋‹ค์ด์–ผ๋กœ๊ทธ๊ฐ€ ๋˜์ง„ ์•Š๋Š”๋‹ค

์†์„ฑ์€ ํ–‰๋™์ด ์•„๋‹ˆ๋‹ค

๋งŽ์€ ์ปดํฌ๋„ŒํŠธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ๋‹ค์ด์–ผ๋กœ๊ทธ ํŒจ๋„์— role="dialog"์™€ aria-modal="true"๋ฅผ ๋ถ™์—ฌ ๋‘๊ณ  ์ ‘๊ทผ์„ฑ์„ ๋๋ƒˆ๋‹ค๊ณ  ์ƒ๊ฐํ•œ๋‹ค. ํ•˜์ง€๋งŒ ํ‚ค๋ณด๋“œ๋กœ ์‹ค์ œ ์‚ฌ์šฉํ•ด ๋ณด๋ฉด ๋ฌธ์ œ๊ฐ€ ๋ฐ”๋กœ ๋“œ๋Ÿฌ๋‚œ๋‹ค.

๋‹ค์ด์–ผ๋กœ๊ทธ๋ฅผ ์—ฐ ๋’ค Tab์„ ๋ˆŒ๋ €์„ ๋•Œ ํฌ์ปค์Šค๊ฐ€ ๋ฐฐ๊ฒฝ ํŽ˜์ด์ง€๋กœ ๋น ์ง€๋ฉด ๊ทธ ๋‹ค์ด์–ผ๋กœ๊ทธ๋Š” ์ ‘๊ทผ ๊ฐ€๋Šฅํ•˜์ง€ ์•Š๋‹ค. ํฌ์ปค์Šค๊ฐ€ ๋‚ด๋ถ€ ์š”์†Œ ์‚ฌ์ด๋ฅผ ์ˆœํ™˜ํ•˜์ง€ ์•Š์œผ๋ฉด ํฌ์ปค์Šค ํŠธ๋žฉ์ด ์—†๋Š” ๊ฒƒ์ด๊ณ , Escape๋ฅผ ๋ˆŒ๋Ÿฌ๋„ ๋‹ซํžˆ์ง€ ์•Š๊ฑฐ๋‚˜ ํŠธ๋ฆฌ๊ฑฐ ๋ฒ„ํŠผ์œผ๋กœ ํฌ์ปค์Šค๊ฐ€ ๋Œ์•„์˜ค์ง€ ์•Š์œผ๋ฉด ํ‚ค๋ณด๋“œ ์‚ฌ์šฉ์ž๋Š” ๊ธธ์„ ์žƒ๋Š”๋‹ค.

role="dialog"๋Š” ์Šคํฌ๋ฆฐ๋ฆฌ๋”์—๊ฒŒ โ€œ์ด๊ฑด ๋‹ค์ด์–ผ๋กœ๊ทธ๋‹คโ€๋ผ๊ณ  ์•Œ๋ ค ์ฃผ๋Š” ๋ผ๋ฒจ์ผ ๋ฟ์ด๋‹ค. ํฌ์ปค์Šค๋ฅผ ๊ฐ€๋‘์ง€๋„ ์•Š๊ณ , Escape๋ฅผ ์ฒ˜๋ฆฌํ•˜์ง€๋„ ์•Š๊ณ , ๋‹ซํž ๋•Œ ํฌ์ปค์Šค๋ฅผ ๋ณต์›ํ•˜์ง€๋„ ์•Š๊ณ , ๋ฐฐ๊ฒฝ ํด๋ฆญ์„ ๋ง‰์•„ ์ฃผ์ง€๋„ ์•Š๋Š”๋‹ค. ์ง„์งœ ์ ‘๊ทผ์„ฑ์€ ์†์„ฑ์ด ์•„๋‹ˆ๋ผ ๋™์ž‘์˜ ์กฐํ•ฉ์—์„œ ๋‚˜์˜จ๋‹ค.

์ž‘์„ฑ์ž๋Š” flintwork๋ผ๋Š” ํ—ค๋“œ๋ฆฌ์Šค ๋””์ž์ธ ์‹œ์Šคํ…œ์„ ๋งŒ๋“ค๋ฉฐ ์ด ๋™์ž‘๋“ค์„ ์ง์ ‘ ๊ตฌํ˜„ํ–ˆ๋‹ค. ์—ฌ๊ธฐ์—๋Š” ํƒญ ๊ฐ€๋Šฅํ•œ ์š”์†Œ ๊ฐ์ง€, ํฌ์ปค์Šค ํŠธ๋žฉ, ๋ฐ”๊นฅ ํด๋ฆญ ์ฒ˜๋ฆฌ, Escape ์ฒ˜๋ฆฌ, ํŠธ๋ฆฌ๊ฑฐ๋กœ ํฌ์ปค์Šค ๋ณต์›, ์ดˆ๊ธฐ ํฌ์ปค์Šค ์ง€์ • ๊ฐ™์€ ๋…๋ฆฝ์ ์ธ ํ–‰๋™์ด ๋ชจ๋‘ ํ•„์š”ํ–ˆ๋‹ค.

์Šคํฌ๋ฆฐ๋ฆฌ๋”๊ฐ€ ์‹ค์ œ๋กœ ๋“ฃ๋Š” ๊ฒƒ

์˜ฌ๋ฐ”๋ฅด๊ฒŒ ๊ตฌํ˜„๋œ ๋‹ค์ด์–ผ๋กœ๊ทธ ํŠธ๋ฆฌ๊ฑฐ๋Š” โ€œOpen settings, button, dialog popupโ€์ฒ˜๋Ÿผ ์ถฉ๋ถ„ํ•œ ๋งฅ๋ฝ์„ ์ œ๊ณตํ•œ๋‹ค. ์—ฌ๊ธฐ์—๋Š” ๋ฒ„ํŠผ ํ…์ŠคํŠธ, ์š”์†Œ ์—ญํ• , ๊ทธ๋ฆฌ๊ณ  aria-haspopup="dialog"๊ฐ€ ํ•จ๊ป˜ ์ž‘๋™ํ•œ๋‹ค.

๋‹ค์ด์–ผ๋กœ๊ทธ๊ฐ€ ์—ด๋ฆฌ๋ฉด VoiceOver๋Š” โ€œSettings, dialog. Update your preferences.โ€์ฒ˜๋Ÿผ ์ œ๋ชฉ๊ณผ ์„ค๋ช…์„ ์ฝ์–ด ์ค€๋‹ค. ์ด๋•Œ ์ œ๋ชฉ์€ aria-labelledby, ์„ค๋ช…์€ aria-describedby์—์„œ ์˜จ๋‹ค. aria-labelledby๊ฐ€ ์—†์œผ๋ฉด ๋‹จ์ˆœํžˆ โ€œdialogโ€๋งŒ ์ฝํ˜€์„œ ์‚ฌ์šฉ์ž๋Š” ์ด UI๊ฐ€ ๋ฌด์—‡์ธ์ง€ ์Šค์Šค๋กœ ํƒ์ƒ‰ํ•ด ์•Œ์•„๋‚ด์•ผ ํ•œ๋‹ค.

Escape๋ฅผ ๋ˆŒ๋Ÿฌ ๋‹ซ์•˜์„ ๋•Œ ๋‹ค์‹œ ํŠธ๋ฆฌ๊ฑฐ ๋ฒ„ํŠผ์œผ๋กœ ํฌ์ปค์Šค๊ฐ€ ๋Œ์•„๊ฐ€๋ฉด ์‚ฌ์šฉ์ž๋Š” ์›๋ž˜ ์ž‘์—… ๋งฅ๋ฝ์„ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค. ๋ฐ˜๋Œ€๋กœ ํฌ์ปค์Šค ๋ณต์›์ด ์—†์œผ๋ฉด <body>๋‚˜ ํŽ˜์ด์ง€์˜ ์ฒซ ๋ฒˆ์งธ ํฌ์ปค์Šค ๊ฐ€๋Šฅํ•œ ์š”์†Œ๋กœ ํŠ€์–ด ๋ฒ„๋ ค ์œ„์น˜ ๊ฐ๊ฐ์„ ์žƒ๋Š”๋‹ค.

์ฆ‰, ARIA ์†์„ฑ์€ ๋‹จ์ˆœํ•œ ์ฒดํฌ๋ฆฌ์ŠคํŠธ๊ฐ€ ์•„๋‹ˆ๋ผ ์˜ค๋””์˜ค UX๋ฅผ ๊ตฌ์„ฑํ•˜๋Š” ์š”์†Œ๋‹ค. ์‹ค์ œ๋กœ ๋“ค์–ด ๋ณด๋ฉด ์™œ ์ค‘์š”ํ•œ์ง€ ์ฆ‰์‹œ ์ฒด๊ฐํ•˜๊ฒŒ ๋œ๋‹ค.

๊ฑฐ๋Œ€ํ•œ ๋‹จ์ผ ์ปดํฌ๋„ŒํŠธ์˜ ๋ฌธ์ œ

์ฒ˜์Œ์—” ๋‹ค์Œ์ฒ˜๋Ÿผ ๋‹จ์ผ Dialog ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๊น”๋”ํ•ด ๋ณด์ผ ์ˆ˜ ์žˆ๋‹ค.

<Dialog triggerText="Open" title="Confirm" onConfirm={handleConfirm} />

ํ•˜์ง€๋งŒ ๊ณง ์š”๊ตฌ์‚ฌํ•ญ์ด ๋Š˜์–ด๋‚œ๋‹ค. ์ปค์Šคํ…€ ํŠธ๋ฆฌ๊ฑฐ, ํผ ์ฝ˜ํ…์ธ , ์—ฌ๋Ÿฌ ์•ก์…˜ ๋ฒ„ํŠผ, ๋‹ซ๊ธฐ ๋ฒ„ํŠผ ์œ„์น˜ ์ปค์Šคํ„ฐ๋งˆ์ด์ฆˆ๊ฐ€ ํ•„์š”ํ•ด์ง€๋ฉด prop๊ฐ€ ํญ์ฆํ•˜๊ณ  ๊ตฌ์กฐ๋Š” ๊ธˆ๋ฐฉ ๊ฒฝ์ง๋œ๋‹ค.

์ž‘์„ฑ์ž๋Š” ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด compound component ํŒจํ„ด์„ ์„ ํƒํ•œ๋‹ค.

<Dialog open={open} onOpenChange={setOpen}>
  <Dialog.Trigger>
    <button>Open settings</button>
  </Dialog.Trigger>
  <Dialog.Portal>
    <Dialog.Overlay />
    <Dialog.Content>
      <Dialog.Title>Settings</Dialog.Title>
      <Dialog.Description>Update your preferences.</Dialog.Description>
      <Dialog.Close>
        <button>Done</button>
      </Dialog.Close>
    </Dialog.Content>
  </Dialog.Portal>
</Dialog>

์ด ๊ตฌ์กฐ์—์„œ๋Š” ์†Œ๋น„์ž๊ฐ€ ๋งˆํฌ์—… ๊ตฌ์กฐ๋ฅผ ์ž์œ ๋กญ๊ฒŒ ๋ฐฐ์น˜ํ•˜๊ณ , ๊ฐ ํ•˜์œ„ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ํ•„์š”ํ•œ ์ ‘๊ทผ์„ฑ ์†์„ฑ๊ณผ ํ–‰๋™์„ ์ž๋™์œผ๋กœ ๋‹ด๋‹นํ•œ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด Dialog.Trigger๋Š” aria-haspopup="dialog"์™€ aria-expanded๋ฅผ, Dialog.Content๋Š” role="dialog"์™€ aria-modal="true"๋ฅผ ๋งก๋Š”๋‹ค.

ํฌ์ปค์Šค ํŠธ๋žฉ ๊ตฌํ˜„์—์„œ ๋ฐฐ์šด ์ 

์ ‘๊ทผ ๊ฐ€๋Šฅํ•œ ๋ชจ๋‹ฌ์˜ ํ•ต์‹ฌ์€ ํฌ์ปค์Šค๊ฐ€ ๋‚ด๋ถ€์—๋งŒ ๋จธ๋ฌผ๊ฒŒ ํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ์ž‘์„ฑ์ž๋Š” Tab/Shift+Tab์„ ๊ฐ์ง€ํ•˜๊ณ , ํ˜„์žฌ ๋‹ค์ด์–ผ๋กœ๊ทธ ์•ˆ์˜ ํฌ์ปค์Šค ๊ฐ€๋Šฅํ•œ ์š”์†Œ ๋ชฉ๋ก์„ ๊ณ„์‚ฐํ•œ ๋’ค, ๋งˆ์ง€๋ง‰ ์š”์†Œ์—์„œ Tab์„ ๋ˆ„๋ฅด๋ฉด ์ฒ˜์Œ์œผ๋กœ, ์ฒซ ์š”์†Œ์—์„œ Shift+Tab์„ ๋ˆ„๋ฅด๋ฉด ๋งˆ์ง€๋ง‰์œผ๋กœ ์ด๋™์‹œํ‚ค๋Š” ๋ฐฉ์‹์„ ๊ตฌํ˜„ํ–ˆ๋‹ค.

ํ•˜์ง€๋งŒ ์‹ค์ œ ๊ตฌํ˜„์€ ๋‹จ์ˆœํ•˜์ง€ ์•Š๋‹ค. ์ˆจ๊น€ ์š”์†Œ๋‚˜ ๋น„ํ™œ์„ฑ ์š”์†Œ๋ฅผ ์ œ์™ธํ•ด์•ผ ํ•˜๊ณ , ๋ผ๋””์˜ค ๊ทธ๋ฃน์ฒ˜๋Ÿผ ํŠน์ˆ˜ํ•œ ํฌ์ปค์Šค ๊ทœ์น™์„ ๊ฐ€์ง„ ์š”์†Œ๋„ ๊ณ ๋ คํ•ด์•ผ ํ•œ๋‹ค. ๋‹ค์ด์–ผ๋กœ๊ทธ๊ฐ€ ๋น„์–ด ์žˆ๊ฑฐ๋‚˜ ํฌ์ปค์Šค ๊ฐ€๋Šฅํ•œ ์š”์†Œ๊ฐ€ ํ•˜๋‚˜๋„ ์—†์„ ๋•Œ์˜ fallback๋„ ํ•„์š”ํ•˜๋‹ค.

์ด๋Ÿฐ ์ด์œ ๋กœ โ€œํฌ์ปค์Šค๋ฅผ ๊ฐ€๋‘”๋‹คโ€๋Š” ๋ง์€ ์งง์ง€๋งŒ, ์‹ค์ œ ์ œํ’ˆ ์ˆ˜์ค€์˜ ๊ตฌํ˜„์€ ๋งŽ์€ ์—ฃ์ง€ ์ผ€์ด์Šค๋ฅผ ํฌํ•จํ•œ๋‹ค.

๋ฐ”๊นฅ ํด๋ฆญ ๋‹ซ๊ธฐ์˜ ํ•จ์ •

์‚ฌ์šฉ์ž๋Š” ์˜ค๋ฒ„๋ ˆ์ด ๋ฐ”๊นฅ์„ ํด๋ฆญํ–ˆ์„ ๋•Œ ๋‹ค์ด์–ผ๋กœ๊ทธ๊ฐ€ ๋‹ซํžˆ๊ธธ ๊ธฐ๋Œ€ํ•œ๋‹ค. ํ•˜์ง€๋งŒ pointerdown, pointerup, drag ๊ฐ™์€ ์ด๋ฒคํŠธ ํ๋ฆ„์„ ์ œ๋Œ€๋กœ ๋‹ค๋ฃจ์ง€ ์•Š์œผ๋ฉด ๋‚ด๋ถ€์—์„œ ์‹œ์ž‘ํ•œ ๋“œ๋ž˜๊ทธ๊ฐ€ ์™ธ๋ถ€์—์„œ ๋๋‚ฌ์„ ๋•Œ ์ž˜๋ชป ๋‹ซํžˆ๊ฑฐ๋‚˜, ๋ฐ˜๋Œ€๋กœ ์™ธ๋ถ€ ํด๋ฆญ์ด ๋ฌด์‹œ๋  ์ˆ˜ ์žˆ๋‹ค.

๋”ฐ๋ผ์„œ ๋‹จ์ˆœํžˆ event.target๋งŒ ๋ณด๋Š” ๋ฐฉ์‹์€ ๋ถˆ์ถฉ๋ถ„ํ•˜๋‹ค. ์ƒํ˜ธ์ž‘์šฉ์˜ ์‹œ์ž‘์ ๊ณผ ๋์ , ์˜ค๋ฒ„๋ ˆ์ด์™€ ์ฝ˜ํ…์ธ ์˜ ๊ฒฝ๊ณ„, ํฌ์ธํ„ฐ ์ด๋ฒคํŠธ์˜ ์ˆœ์„œ๋ฅผ ๋ชจ๋‘ ๊ณ ๋ คํ•ด์•ผ ํ•œ๋‹ค.

Escape ์ฒ˜๋ฆฌ์™€ ํฌ์ปค์Šค ๋ณต์›

Escape๋Š” ๊ฐ€์žฅ ์ต์ˆ™ํ•œ ์ข…๋ฃŒ ๋ฐฉ์‹ ์ค‘ ํ•˜๋‚˜๋‹ค. ํ•˜์ง€๋งŒ ๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ๋‚˜ ๋‹ค๋ฅธ ๋ ˆ์ด์–ด์— ํ‚ค ์ด๋ฒคํŠธ๊ฐ€ ์ƒˆ์–ด ๋‚˜๊ฐ€์ง€ ์•Š๋„๋ก ํ•ด์•ผ ํ•˜๋ฉฐ, ๋‹ซํžŒ ๋’ค์—๋Š” ์›๋ž˜ ๋‹ค์ด์–ผ๋กœ๊ทธ๋ฅผ ์—ฐ ํŠธ๋ฆฌ๊ฑฐ ์š”์†Œ๋กœ ํฌ์ปค์Šค๋ฅผ ๋˜๋Œ๋ ค์•ผ ํ•œ๋‹ค.

์ด ํฌ์ปค์Šค ๋ณต์›์€ ์‚ฌ์šฉ์ž์˜ ๋งฅ๋ฝ ์œ ์ง€์— ๊ฒฐ์ •์ ์ด๋‹ค. ์‹œ๊ฐ์ ์œผ๋กœ๋Š” ์‚ฌ์†Œํ•ด ๋ณด์—ฌ๋„ ํ‚ค๋ณด๋“œ ์‚ฌ์šฉ์ž์™€ ์Šคํฌ๋ฆฐ๋ฆฌ๋” ์‚ฌ์šฉ์ž์—๊ฒŒ๋Š” ํ•„์ˆ˜ ํ–‰๋™์ด๋‹ค.

์ดˆ๊ธฐ ํฌ์ปค์Šค ๋ฐฐ์น˜

๋‹ค์ด์–ผ๋กœ๊ทธ๊ฐ€ ์—ด๋ฆด ๋•Œ ์–ด๋””์— ํฌ์ปค์Šค๋ฅผ ๋‘˜์ง€๋„ ์ค‘์š”ํ•˜๋‹ค. ๋ณดํ†ต์€ ๋‹ค์Œ ์šฐ์„ ์ˆœ์œ„๋ฅผ ๋‘”๋‹ค.

  1. ๋ช…์‹œ์ ์œผ๋กœ ์ง€์ •๋œ ์ดˆ๊ธฐ ํฌ์ปค์Šค ๋Œ€์ƒ
  2. ๋‹ค์ด์–ผ๋กœ๊ทธ ๋‚ด๋ถ€์˜ ์ฒซ ๋ฒˆ์งธ ์˜๋ฏธ ์žˆ๋Š” ํฌ์ปค์Šค ๊ฐ€๋Šฅํ•œ ์š”์†Œ
  3. ํ•„์š”ํ•˜๋‹ค๋ฉด ๋‹ค์ด์–ผ๋กœ๊ทธ ์ปจํ…Œ์ด๋„ˆ ์ž์ฒด

์ด ์šฐ์„ ์ˆœ์œ„๊ฐ€ ์—†์œผ๋ฉด ์‚ฌ์šฉ์ž๋Š” ๋‹ค์ด์–ผ๋กœ๊ทธ๊ฐ€ ์—ด๋ฆฐ ์‚ฌ์‹ค์„ ์ฆ‰์‹œ ์ธ์ง€ํ•˜์ง€ ๋ชปํ•˜๊ฑฐ๋‚˜, ์—‰๋šฑํ•œ ์œ„์น˜์—์„œ ํƒ์ƒ‰์„ ์‹œ์ž‘ํ•˜๊ฒŒ ๋œ๋‹ค.

ํ•ต์‹ฌ ๊ฒฐ๋ก 

์ ‘๊ทผ ๊ฐ€๋Šฅํ•œ ๋‹ค์ด์–ผ๋กœ๊ทธ๋Š” ARIA ์†์„ฑ์„ ๋ช‡ ๊ฐœ ๋ถ™์ด๋Š” ๋ฌธ์ œ๋กœ ๋๋‚˜์ง€ ์•Š๋Š”๋‹ค. ํฌ์ปค์Šค ํŠธ๋žฉ, ์ดˆ๊ธฐ ํฌ์ปค์Šค, Escape, ๋ฐ”๊นฅ ํด๋ฆญ ์ฒ˜๋ฆฌ, ํฌ์ปค์Šค ๋ณต์›๊นŒ์ง€ ํฌํ•จํ•œ ์ƒํ˜ธ์ž‘์šฉ ์ „์ฒด๊ฐ€ ์„ค๊ณ„๋˜์–ด์•ผ ํ•œ๋‹ค. FE ์‹ค๋ฌด์—์„œ โ€œ์ ‘๊ทผ์„ฑ ์ง€์›โ€์„ ์ฃผ์žฅํ•˜๋ ค๋ฉด ์‹œ๋งจํ‹ฑ๊ณผ ํ–‰๋™์„ ํ•จ๊ป˜ ์ œ๊ณตํ•ด์•ผ ํ•œ๋‹ค.

์›๋ฌธ ์ œ๋ชฉ: React: Singletons aren't as evil as you think ์›๋ฌธ ๋งํฌ: https://dev.to/link2twenty/react-singletons-arent-as-evil-as-you-think-44m8 ๋ฒˆ์—ญ์ผ: 2026-03-09 KST

React์—์„œ ์‹ฑ๊ธ€ํ„ด์€ ์ƒ๊ฐ๋ณด๋‹ค ๊ทธ๋ ‡๊ฒŒ ๋‚˜์˜์ง€ ์•Š๋‹ค

React ์„ธ๊ณ„์—์„œ ์‹ฑ๊ธ€ํ„ด์€ ์ข…์ข… โ€œ์ถ”์ ํ•˜๊ธฐ ์–ด๋ ต๊ณ  ํ…Œ์ŠคํŠธํ•˜๊ธฐ ํž˜๋“  ์ „์—ญ ์ƒํƒœ์˜ ์ง€๋ฆ„๊ธธโ€๋กœ ์ทจ๊ธ‰๋œ๋‹ค. ํ•˜์ง€๋งŒ ์ž‘์„ฑ์ž์˜ ์š”์ง€๋Š” ๋‹จ์ˆœํ•˜๋‹ค. ์‹ฑ๊ธ€ํ„ด ์ž์ฒด๊ฐ€ ๋ฌธ์ œ๋ผ๊ธฐ๋ณด๋‹ค, React์™€ ์—ฐ๊ฒฐํ•˜๋Š” ๋ฐฉ์‹์ด ๋‚ก์•˜๋˜ ๊ฒƒ์ด ๋ฌธ์ œ์˜€๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.

๊ณผ๊ฑฐ์˜ ์•ˆ ์ข‹์€ ํŒจํ„ด

์˜ˆ์ „์—๋Š” ์‹ฑ๊ธ€ํ„ด ๊ฐ’์ด ๋ฐ”๋€Œ์–ด๋„ React ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๊ทธ ์‚ฌ์‹ค์„ ๋ชฐ๋ž๋‹ค. ๊ทธ๋ž˜์„œ ์ˆ˜๋™ ์ƒˆ๋กœ๊ณ ์นจ ๋ฒ„ํŠผ์„ ๋‘๊ฑฐ๋‚˜, setInterval๋กœ ํด๋งํ•˜๋ฉฐ ์‹ฑ๊ธ€ํ„ด์˜ ํ˜„์žฌ ๊ฐ’์„ state๋กœ ๋ณต์‚ฌํ•˜๋Š” ์‹์œผ๋กœ ๋™๊ธฐํ™”ํ–ˆ๋‹ค.

์ด ๋ฐฉ์‹์€ ๋ฐ˜์‘ํ˜•๋„ ์•„๋‹ˆ๊ณ , ์ง€์—ฐ์ด ์ƒ๊ธฐ๋ฉฐ, ์ฝ”๋“œ๋„ ์ง€์ €๋ถ„ํ•ด์ง„๋‹ค.

์ด๋ฒคํŠธ ๊ธฐ๋ฐ˜์œผ๋กœ ๋ฐ”๊พธ๋ฉด ํ›จ์”ฌ ๋‚ซ๋‹ค

์‹ฑ๊ธ€ํ„ด์ด ๊ฐ’ ๋ณ€๊ฒฝ ์‹œ ์ด๋ฒคํŠธ๋ฅผ ๋ฐœํ–‰ํ•˜๋„๋ก ๋งŒ๋“ค๋ฉด React ์ปดํฌ๋„ŒํŠธ๋Š” ๊ทธ ์ด๋ฒคํŠธ๋ฅผ ๊ตฌ๋…ํ•ด ๋ฐ”๋กœ state๋ฅผ ๊ฐฑ์‹ ํ•  ์ˆ˜ ์žˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ์‹ฑ๊ธ€ํ„ด ๋‚ด๋ถ€์—์„œ EventTarget ๋˜๋Š” ์œ ์‚ฌํ•œ ์ด๋ฒคํŠธ ์‹œ์Šคํ…œ์„ ์‚ฌ์šฉํ•ด change ์ด๋ฒคํŠธ๋ฅผ ๋‚ด๋ณด๋‚ด๊ณ , ์ปดํฌ๋„ŒํŠธ๋Š” useEffect์—์„œ ๊ตฌ๋…ํ•œ ๋’ค ํ•ด์ œํ•˜๋ฉด ๋œ๋‹ค.

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ํด๋ง๋„, ์ˆ˜๋™ ๋™๊ธฐํ™” ๋ฒ„ํŠผ๋„ ํ•„์š” ์—†๋‹ค. React state์ฒ˜๋Ÿผ ๊ฝค ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ๋™์ž‘ํ•œ๋‹ค.

ํด๋ž˜์Šค ๊ธฐ๋ฐ˜ ์‹ฑ๊ธ€ํ„ด์˜ ์žฅ์ 

์ž‘์„ฑ์ž๋Š” ์‹ฑ๊ธ€ํ„ด์„ ๊ตฌํ˜„ํ•  ๋•Œ JavaScript ํด๋ž˜์Šค์˜ ๋‹จ์ˆœํ•จ์„ ๊ฐ•์กฐํ•œ๋‹ค. ์™ธ๋ถ€ ์ƒํƒœ ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ถ”๊ฐ€ํ•˜์ง€ ์•Š์•„๋„ ๋˜๊ณ , ์•ฑ ์ „์—ญ์—์„œ ํ•˜๋‚˜์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ๊ณต์œ ํ•˜๊ธฐ ์‰ฝ๋‹ค. ์ž‘์€ ๊ทœ๋ชจ์˜ cross-cutting concern์„ ์ฒ˜๋ฆฌํ•  ๋•Œ๋Š” ์˜คํžˆ๋ ค ๋” ๊ฐ€๋ณ๊ณ  ๋ช…์‹œ์ ์ผ ์ˆ˜ ์žˆ๋‹ค.

๋Œ€ํ‘œ์ ์ธ ์˜ˆ์‹œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

  • ๋„คํŠธ์›Œํฌ ์—ฐ๊ฒฐ ์ƒํƒœ
  • ์ „์—ญ ํ† ์ŠคํŠธ/์•Œ๋ฆผ ํ—ˆ๋ธŒ
  • feature flag ์บ์‹œ
  • ์‚ฌ์šฉ์ž ์„ธ์…˜ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ
  • ์•ฑ ์ „์ฒด์—์„œ ํ•˜๋‚˜๋งŒ ์กด์žฌํ•ด์•ผ ํ•˜๋Š” ์„œ๋น„์Šค ๊ฐ์ฒด

์ด๋Ÿฐ ๊ฒฝ์šฐ โ€œ์ƒํƒœ ์ €์žฅ์†Œโ€๋ณด๋‹ค โ€œ๊ณต์œ  ์„œ๋น„์Šคโ€๋กœ ๋ณด๋Š” ํŽธ์ด ๋” ์ž์—ฐ์Šค๋Ÿฝ๋‹ค.

useSyncExternalStore๊ฐ€ ํŒ์„ ๋ฐ”๊ฟจ๋‹ค

์ด ๊ธ€์˜ ํ•ต์‹ฌ์€ ์ตœ์‹  React๊ฐ€ ์™ธ๋ถ€ ์ €์žฅ์†Œ๋ฅผ ๋” ๊ณต์‹์ ์œผ๋กœ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ๊ฒŒ ํ•ด ์ค€๋‹ค๋Š” ์ ์ด๋‹ค. useSyncExternalStore๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์‹ฑ๊ธ€ํ„ด์„ React์˜ ๋ Œ๋”๋ง ๋ชจ๋ธ์— ์•ˆ์ „ํ•˜๊ฒŒ ์—ฐ๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค.

์‹ฑ๊ธ€ํ„ด์€ ์ตœ์†Œํ•œ ๋‹ค์Œ ๋‘ ๊ฐ€์ง€๋ฅผ ์ œ๊ณตํ•˜๋ฉด ๋œ๋‹ค.

  1. ํ˜„์žฌ ์Šค๋ƒ…์ƒท์„ ์ฝ๋Š” ํ•จ์ˆ˜
  2. ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ๊ตฌ๋…ํ•˜๋Š” ํ•จ์ˆ˜

๊ทธ๋Ÿฌ๋ฉด ์ปดํฌ๋„ŒํŠธ๋Š” React๊ฐ€ ๋ณด์žฅํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ์ตœ์‹  ์Šค๋ƒ…์ƒท์„ ์ฝ๊ณ , ๋ณ€๊ฒฝ ์‹œ ์ผ๊ด€๋˜๊ฒŒ ๋ฆฌ๋ Œ๋”๋งํ•  ์ˆ˜ ์žˆ๋‹ค. ์ฆ‰, ์‹ฑ๊ธ€ํ„ด์„ โ€œ๋Œ€์ถฉ ์ „์—ญ ๋ณ€์ˆ˜์ฒ˜๋Ÿผ ์“ฐ๋Š” ๊ฒƒโ€๊ณผ โ€œReact ์นœํ™”์ ์ธ ์™ธ๋ถ€ ์Šคํ† ์–ด๋กœ ์“ฐ๋Š” ๊ฒƒโ€์€ ์ „ํ˜€ ๋‹ค๋ฅธ ์ด์•ผ๊ธฐ๋‹ค.

๊ฐ„๋‹จํ•œ ๊ตฌ์กฐ ์˜ˆ์‹œ

์ž‘์„ฑ์ž๊ฐ€ ์ œ์•ˆํ•˜๋Š” ํ˜•ํƒœ๋Š” ๋Œ€๋žต ์ด๋Ÿฐ ํ๋ฆ„์ด๋‹ค.

  • ์‹ฑ๊ธ€ํ„ด ํด๋ž˜์Šค๊ฐ€ ๋‚ด๋ถ€ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ง„๋‹ค.
  • setData() ๊ฐ™์€ ๋ฉ”์„œ๋“œ๊ฐ€ ๊ฐ’์„ ๋ณ€๊ฒฝํ•œ๋‹ค.
  • ๊ฐ’์ด ๋ฐ”๋€Œ๋ฉด ๋“ฑ๋ก๋œ ๋ฆฌ์Šค๋„ˆ๋“ค์—๊ฒŒ ์•Œ๋ฆฐ๋‹ค.
  • React ํ›…์ด useSyncExternalStore๋กœ ๊ตฌ๋…ํ•œ๋‹ค.
  • ์ปดํฌ๋„ŒํŠธ๋Š” ํ‰๋ฒ”ํ•œ state์ฒ˜๋Ÿผ ๊ฐ’์„ ์ฝ๋Š”๋‹ค.

์ด ํŒจํ„ด์„ ์“ฐ๋ฉด ์ „์—ญ ์ƒํƒœ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ˆ˜์ค€์˜ ๊ณผํ•œ ์ถ”์ƒํ™” ์—†์ด๋„ ์ถฉ๋ถ„ํžˆ ์‹ค์šฉ์ ์ธ ๊ฒฐ๊ณผ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ๋‹ค.

ํ…Œ์ŠคํŠธ๋Š” ์ •๋ง ์–ด๋ ค์šด๊ฐ€?

์‹ฑ๊ธ€ํ„ด์— ๋Œ€ํ•œ ๊ฐ€์žฅ ํ”ํ•œ ๋ฐ˜๋ก ์€ ํ…Œ์ŠคํŠธ๋‹ค. ์ƒํƒœ๊ฐ€ ์ „์—ญ์œผ๋กœ ์‚ด์•„๋‚จ๊ธฐ ๋•Œ๋ฌธ์— ํ…Œ์ŠคํŠธ ๊ฐ„ ์˜ค์—ผ์ด ์ƒ๊ธธ ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. ์ด ๋ฌธ์ œ๋Š” ์‹ค์ œ๋กœ ์กด์žฌํ•œ๋‹ค. ํ•˜์ง€๋งŒ ์ž‘์„ฑ์ž๋Š” ์ด๊ฒƒ์ด โ€œ์‹ฑ๊ธ€ํ„ด์ด๋ผ์„œ ๋ณธ์งˆ์ ์œผ๋กœ ํ…Œ์ŠคํŠธ ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹คโ€๋Š” ๋œป์€ ์•„๋‹ˆ๋ผ๊ณ  ๋งํ•œ๋‹ค.

ํ•ด๊ฒฐ์ฑ…์€ ๊ฝค ํ˜„์‹ค์ ์ด๋‹ค.

  • ํ…Œ์ŠคํŠธ๋งˆ๋‹ค ์‹ฑ๊ธ€ํ„ด ์ดˆ๊ธฐํ™” ๋ฉ”์„œ๋“œ๋ฅผ ์ œ๊ณตํ•œ๋‹ค.
  • ํ…Œ์ŠคํŠธ setup์—์„œ ์ƒํƒœ๋ฅผ ๋ฆฌ์…‹ํ•œ๋‹ค.
  • ํ•„์š”ํ•œ ๊ฒฝ์šฐ ์˜์กด์„ฑ ์ฃผ์ž… ์ง€์ ์„ ๋‘”๋‹ค.
  • ์ˆœ์ˆ˜ ๊ณ„์‚ฐ ๋กœ์ง์€ ์‹ฑ๊ธ€ํ„ด ๋ฐ–์œผ๋กœ ๋ถ„๋ฆฌํ•œ๋‹ค.

์ฆ‰, ํ…Œ์ŠคํŠธ ๋‚œ์ด๋„๋Š” ๊ด€๋ฆฌ ์ด์Šˆ์ด์ง€, ๊ธˆ์ง€ ์‚ฌ์œ ๋Š” ์•„๋‹ˆ๋‹ค.

์–ธ์ œ ์“ฐ๋ฉด ์ข‹์€๊ฐ€

์ž‘์„ฑ์ž๋Š” ์‹ฑ๊ธ€ํ„ด์ด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ƒํ™ฉ์— ์ ํ•ฉํ•˜๋‹ค๊ณ  ๋ณธ๋‹ค.

  • ์•ฑ ์ „์ฒด์—์„œ ์ง„์งœ๋กœ ์ธ์Šคํ„ด์Šค๊ฐ€ ํ•˜๋‚˜์—ฌ์•ผ ํ•  ๋•Œ
  • ์ƒํƒœ๋ณด๋‹ค ์„œ๋น„์Šค ์„ฑ๊ฒฉ์ด ๊ฐ•ํ•  ๋•Œ
  • ๋„์ž… ๋น„์šฉ์„ ์ตœ์†Œํ™”ํ•˜๊ณ  ์‹ถ์„ ๋•Œ
  • Redux/Zustand/Jotai ๊ฐ™์€ ํฐ ๋„๊ตฌ๊ฐ€ ๊ณผํ•˜๋‹ค๊ณ  ๋А๊ปด์งˆ ๋•Œ

๋ฐ˜๋Œ€๋กœ ๋ณต์žกํ•œ ๋„๋ฉ”์ธ ์ƒํƒœ, ์‹œ์ ๋ณ„ ์ด๋ ฅ ๊ด€๋ฆฌ, ์„œ๋ฒ„ ์บ์‹œ, ๋งŽ์€ ํŒŒ์ƒ ์ƒํƒœ๊ฐ€ ์–ฝํžŒ ๋ฌธ์ œ๋ผ๋ฉด ์ „์šฉ ์ƒํƒœ ๊ด€๋ฆฌ ๋„๊ตฌ๊ฐ€ ๋” ์ ํ•ฉํ•  ์ˆ˜ ์žˆ๋‹ค.

๊ฒฐ๋ก 

์‹ฑ๊ธ€ํ„ด์€ React์—์„œ ๋ฌด์กฐ๊ฑด ํ”ผํ•ด์•ผ ํ•  ๊ธˆ๊ธฐ ํŒจํ„ด์ด ์•„๋‹ˆ๋‹ค. ์ค‘์š”ํ•œ ๊ฒƒ์€ โ€œ์ „์—ญ ํ•˜๋‚˜๋ฅผ ๊ทธ๋ƒฅ ๊ฐ€์ ธ๋‹ค ์“ฐ๋Š”๊ฐ€โ€๊ฐ€ ์•„๋‹ˆ๋ผ, ๋ณ€๊ฒฝ ๊ตฌ๋…๊ณผ ์Šค๋ƒ…์ƒท ์ฝ๊ธฐ๋ฅผ ํฌํ•จํ•œ React ์นœํ™”์  ์ธํ„ฐํŽ˜์ด์Šค๋กœ ๊ฐ์ŒŒ๋Š”๊ฐ€๋‹ค. ์ž‘๊ณ  ๋ช…ํ™•ํ•œ ์ „์—ญ ์„œ๋น„์Šค์—๋Š” ์—ฌ์ „ํžˆ ๊ฝค ์‹ค์šฉ์ ์ธ ์„ ํƒ์ง€๋‹ค.

์›๋ฌธ ์ œ๋ชฉ: The Server-Side Mocking Gap Nobody Talks About ์›๋ฌธ ๋งํฌ: https://dev.to/rrafatpanah/the-server-side-mocking-gap-nobody-talks-about-36jb ๋ฒˆ์—ญ์ผ: 2026-03-09 KST

์•„๋ฌด๋„ ์ž˜ ๋งํ•˜์ง€ ์•Š๋Š” ์„œ๋ฒ„ ์‚ฌ์ด๋“œ ๋ชจํ‚น ๊ณต๋ฐฑ

SPA๊ฐ€ ์ค‘์‹ฌ์ด๋˜ ์‹œ์ ˆ์—๋Š” E2E ํ…Œ์ŠคํŠธ๊ฐ€ ์ง€๊ธˆ๋ณด๋‹ค ๋‹จ์ˆœํ–ˆ๋‹ค. ์•ฑ์ด ๋ธŒ๋ผ์šฐ์ € ์•ˆ์— ์žˆ์—ˆ๊ณ , API ์š”์ฒญ๋„ ๋Œ€๋ถ€๋ถ„ ๋ธŒ๋ผ์šฐ์ €์—์„œ ์‹œ์ž‘ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— Cypress ๊ฐ™์€ ๋„๊ตฌ๊ฐ€ ๋„คํŠธ์›Œํฌ๋ฅผ ๊ฐ€๋กœ์ฑ„๊ณ  ๋ชจํ‚นํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.

ํ•˜์ง€๋งŒ Next.js, Remix, TanStack Start ๊ฐ™์€ ์ตœ์‹  ํ’€์Šคํƒ ํ”„๋ ˆ์ž„์›Œํฌ์—์„œ๋Š” ์ƒํ™ฉ์ด ๋‹ฌ๋ผ์กŒ๋‹ค. loader, action, server component, server function์ด ์„œ๋ฒ„์—์„œ ์‹คํ–‰๋˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ์ด ์š”์ฒญ๋“ค์€ ๋ธŒ๋ผ์šฐ์ € ๋ฐ–์—์„œ ๋ฐœ์ƒํ•˜๋ฏ€๋กœ, ๊ธฐ์กด E2E ๋„๊ตฌ์˜ ๋„คํŠธ์›Œํฌ ์ธํ„ฐ์…‰ํŠธ๋งŒ์œผ๋กœ๋Š” ์ปค๋ฒ„๋˜์ง€ ์•Š๋Š”๋‹ค. ์ด ์ง€์ ์ด ์ž‘์„ฑ์ž๊ฐ€ ๋งํ•˜๋Š” โ€œ์„œ๋ฒ„ ์‚ฌ์ด๋“œ ๋ชจํ‚น ๊ฐญโ€์ด๋‹ค.

์ž‘์„ฑ์ž๋Š” Anedot์—์„œ 9๊ฐœ์›” ๋™์•ˆ ์‹ค๋ฌด์— ์ ์šฉํ•œ ์ ‘๊ทผ์„ ์†Œ๊ฐœํ•œ๋‹ค.

์ข‹์€ ํ•ด๊ฒฐ์ฑ…์˜ ์กฐ๊ฑด

์ด ๋ฌธ์ œ๋ฅผ ํ‘ธ๋Š” ๋ฐฉ๋ฒ•์€ ๋‹จ์ˆœํžˆ โ€œ์‘๋‹ต์„ ๊ฐ€์งœ๋กœ ๋งŒ๋“ ๋‹คโ€ ์ˆ˜์ค€์ด ์•„๋‹ˆ์–ด์•ผ ํ•œ๋‹ค. ๊ธ€์—์„œ ์ œ์‹œํ•œ ๋ชฉํ‘œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

  1. ์•ฑ์˜ ๊ฒฝ๊ณ„๋งŒ ๋ชจํ‚นํ•œ๋‹ค. ๋ธŒ๋ผ์šฐ์ €์™€ ์„œ๋ฒ„๋Š” ์‹ค์ œ๋กœ ๋™์ž‘์‹œํ‚ค๊ณ , ์™ธ๋ถ€ ์„œ๋“œํŒŒํ‹ฐ API๋งŒ ๋ชจํ‚นํ•œ๋‹ค.
  2. ๊ฐ ํ…Œ์ŠคํŠธ๊ฐ€ ์ž๊ธฐ ๋ชจํ‚น์„ ์ง์ ‘ ์†Œ์œ ํ•œ๋‹ค. ํ…Œ์ŠคํŠธ๋งˆ๋‹ค ํ•„์š”ํ•œ ์—”๋“œํฌ์ธํŠธ์™€ ์‘๋‹ต์„ ๋ช…์‹œํ•˜๊ณ , ํ…Œ์ŠคํŠธ ๊ฐ„ ๊ณต์œ  ์ƒํƒœ๊ฐ€ ์—†์–ด์•ผ ํ•œ๋‹ค.
  3. ๋ชจํ‚น ๋ฐ์ดํ„ฐ๋Š” ํƒ€์ž… ์ฒดํฌ๋˜์–ด์•ผ ํ•œ๋‹ค. ์‹ค์ œ API ์Šคํ‚ค๋งˆ์™€ ์–ด๊ธ‹๋‚˜๋Š” ๊ฐ€์งœ ์‘๋‹ต์ด ํ…Œ์ŠคํŠธ๋ฅผ ํ†ต๊ณผ์‹œํ‚ค๋ฉด ์•ˆ ๋œ๋‹ค.
  4. ์‹ค์ œ ์™ธ๋ถ€ ์š”์ฒญ์ด ์ƒˆ๋ฉด ์•ˆ ๋œ๋‹ค. ์Šคํ…Œ์ด์ง•์„ ํฌํ•จํ•ด ์–ด๋–ค ํ™˜๊ฒฝ์—์„œ๋„ ์ง„์งœ ์„œ๋“œํŒŒํ‹ฐ ํ˜ธ์ถœ์ด ๋‚˜๊ฐ€๋ฉด ์•ˆ ๋œ๋‹ค.
  5. ์š”์ฒญ ์ž์ฒด๋ฅผ ๊ฒ€์ฆํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•œ๋‹ค. ๋ฐ”๋””์™€ ํ—ค๋”๊ฐ€ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ๋‚˜๊ฐ”๋Š”์ง€ assert ๊ฐ€๋Šฅํ•ด์•ผ ํ•œ๋‹ค.
  6. ๋ณ‘๋ ฌ ์‹คํ–‰์ด ์•ˆ์ „ํ•ด์•ผ ํ•œ๋‹ค. ์—ฌ๋Ÿฌ ํ…Œ์ŠคํŠธ๊ฐ€ ๋™์‹œ์— ๋Œ์•„๋„ ์„œ๋กœ์˜ ๋ชจํ‚น์ด ์„ž์ด๋ฉด ์•ˆ ๋œ๋‹ค.

๋ฐ๋ชจ ์•ฑ ๊ตฌ์„ฑ

์ž‘์„ฑ์ž๋Š” TanStack Start + Playwright + MSW ์กฐํ•ฉ์œผ๋กœ ๋ฐ๋ชจ ์•ฑ์„ ๋งŒ๋“ค์—ˆ๋‹ค. ์•ฑ์€ ๋‘ ๊ฐ€์ง€ ์„œ๋ฒ„ ํ•จ์ˆ˜๋งŒ ๊ฐ€์ง„ ๋‹จ์ˆœํ•œ ์˜ˆ์‹œ๋‹ค.

  • ์„œ๋ฒ„ ํ•จ์ˆ˜ fetchPosts: ์™ธ๋ถ€ API์—์„œ ๊ฒŒ์‹œ๊ธ€ ๋ชฉ๋ก ๋กœ๋“œ
  • ์„œ๋ฒ„ ํ•จ์ˆ˜ createPost: ํผ ์ œ์ถœ๋กœ ์™ธ๋ถ€ API์— POST ์š”์ฒญ

ํ•ต์‹ฌ์€ ์ด ์š”์ฒญ๋“ค์ด ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์•„๋‹ˆ๋ผ ์•ฑ ์„œ๋ฒ„์—์„œ ์‹คํ–‰๋œ๋‹ค๋Š” ์ ์ด๋‹ค.

๋ธŒ๋ผ์šฐ์ € ๋ชจํ‚น๋งŒ์œผ๋กœ๋Š” ์™œ ๋ถ€์กฑํ•œ๊ฐ€

Playwright๋‚˜ Cypress์˜ route interception์€ ์ฃผ๋กœ ๋ธŒ๋ผ์šฐ์ €์—์„œ ๋ฐœ์ƒํ•œ ์š”์ฒญ์„ ๋Œ€์ƒ์œผ๋กœ ํ•œ๋‹ค. ํ•˜์ง€๋งŒ loader๋‚˜ server function์ด Node ๋Ÿฐํƒ€์ž„์—์„œ fetch()๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด E2E ๋„๊ตฌ์˜ ์ธํ„ฐ์…‰ํŠธ ๋ฒ”์œ„ ๋ฐ–์œผ๋กœ ๋‚˜๊ฐ„๋‹ค. ๊ฒฐ๊ณผ์ ์œผ๋กœ ํ…Œ์ŠคํŠธ๋Š” ๋‹ค์Œ ๋‘˜ ์ค‘ ํ•˜๋‚˜๊ฐ€ ๋˜๊ธฐ ์‰ฝ๋‹ค.

  • ์‹ค์ œ ์™ธ๋ถ€ API์— ์˜์กดํ•˜๋Š” ๋А๋ฆฌ๊ณ  ๋ถˆ์•ˆ์ •ํ•œ ํ…Œ์ŠคํŠธ
  • ๋ธŒ๋ผ์šฐ์ € ๊ณ„์ธต๋งŒ ๊ฐ€์งœ๋กœ ๋งŒ๋“ค๊ณ  ์„œ๋ฒ„ ๊ณ„์ธต์€ ์‚ฌ์‹ค์ƒ ๋ฌด๋ฐฉ๋น„์ธ ํ…Œ์ŠคํŠธ

์ž‘์„ฑ์ž๋Š” ์ด ์ƒํƒœ๋ฅผ ํ”ผํ•˜๋ ค๊ณ  ์•ฑ ์„œ๋ฒ„ ์ž์ฒด์—์„œ ๋„คํŠธ์›Œํฌ๋ฅผ ๋ชจํ‚นํ•œ๋‹ค.

์ ‘๊ทผ ๋ฐฉ์‹: ์„œ๋ฒ„ ์•ˆ์— MSW๋ฅผ ์‹ฌ๋Š”๋‹ค

ํ•ต์‹ฌ ์•„์ด๋””์–ด๋Š” ํ…Œ์ŠคํŠธ๊ฐ€ ์‹œ์ž‘๋  ๋•Œ ์•ฑ ์„œ๋ฒ„ ๋‚ด๋ถ€์— MSW ๊ธฐ๋ฐ˜ mock server๋ฅผ ๋„์šฐ๊ณ , ์„œ๋ฒ„์—์„œ ์™ธ๋ถ€๋กœ ๋‚˜๊ฐ€๋Š” ์š”์ฒญ์„ ๊ทธ๊ณณ์—์„œ ๊ฐ€๋กœ์ฑ„๋Š” ๊ฒƒ์ด๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋ธŒ๋ผ์šฐ์ €์™€ ์•ฑ ์„œ๋ฒ„๋Š” ์‹ค์ œ๋กœ ๋™์ž‘ํ•˜๊ณ , ์™ธ๋ถ€ ์„œ๋น„์Šค ๊ฒฝ๊ณ„๋งŒ ํ†ต์ œํ•  ์ˆ˜ ์žˆ๋‹ค.

์—ฌ๊ธฐ์— ๋”ํ•ด ๊ฐ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋Š” ์ž์‹ ๋งŒ์˜ mock ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์„œ๋ฒ„์— ๋“ฑ๋กํ•œ๋‹ค. ์ฆ‰, โ€œ์ด ํ…Œ์ŠคํŠธ์—์„œ๋Š” /posts๊ฐ€ ์ด๋Ÿฐ ์‘๋‹ต์„ ๋ฐ˜ํ™˜ํ•œ๋‹คโ€๋ฅผ ํ…Œ์ŠคํŠธ๊ฐ€ ์ง์ ‘ ์„ ์–ธํ•œ๋‹ค.

ํ…Œ์ŠคํŠธ๋ณ„ ๋ชจํ‚น์„ ์–ด๋–ป๊ฒŒ ๋ถ„๋ฆฌํ•˜๋‚˜

๊ฐ€์žฅ ์ค‘์š”ํ•œ ์‹ค๋ฌด ํฌ์ธํŠธ๋Š” ๋ณ‘๋ ฌ์„ฑ์ด๋‹ค. ์—ฌ๋Ÿฌ ํ…Œ์ŠคํŠธ๊ฐ€ ๋™์‹œ์— ์‹คํ–‰๋  ๋•Œ ๊ณต์šฉ ์ „์—ญ mock ์ƒํƒœ๋ฅผ ์“ฐ๋ฉด ์„œ๋กœ ๊ผฌ์ธ๋‹ค.

์ž‘์„ฑ์ž๋Š” ํ…Œ์ŠคํŠธ๋งˆ๋‹ค ๊ณ ์œ  ์‹๋ณ„์ž๋ฅผ ๋ฐœ๊ธ‰ํ•˜๊ณ , ๋ธŒ๋ผ์šฐ์ € ์š”์ฒญ ํ—ค๋”๋‚˜ ์ปจํ…์ŠคํŠธ๋ฅผ ํ†ตํ•ด ์ด ์‹๋ณ„์ž๋ฅผ ์•ฑ ์„œ๋ฒ„๋กœ ์ „๋‹ฌํ•œ๋‹ค. ์•ฑ ์„œ๋ฒ„๋Š” ๊ทธ ์‹๋ณ„์ž๋ฅผ ๊ธฐ์ค€์œผ๋กœ ํ•ด๋‹น ํ…Œ์ŠคํŠธ์—๋งŒ ์ ์šฉ๋˜๋Š” mock ์ง‘ํ•ฉ์„ ์กฐํšŒํ•œ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด test A๊ฐ€ ๋“ฑ๋กํ•œ ์‘๋‹ต์€ test A์˜ ์„œ๋ฒ„ ์š”์ฒญ์—๋งŒ ์ ์šฉ๋˜๊ณ , test B์™€ ์„ž์ด์ง€ ์•Š๋Š”๋‹ค.

์ด ๊ตฌ์กฐ๋Š” ๋ณ‘๋ ฌ ์‹คํ–‰์—์„œ ํŠนํžˆ ๊ฐ•๋ ฅํ•˜๋‹ค.

์š”์ฒญ ๊ฒ€์ฆ๊นŒ์ง€ ํฌํ•จํ•œ๋‹ค

๊ธ€์˜ ํฌ์ธํŠธ๋Š” โ€œ์‘๋‹ต์„ ๋Œ๋ ค์ค€๋‹คโ€์—์„œ ๋๋‚˜์ง€ ์•Š๋Š”๋‹ค. ์™ธ๋ถ€ API๋กœ ๋‚˜๊ฐ€๋ ค๋˜ ์š”์ฒญ์˜ ํ—ค๋”, ๋ฉ”์„œ๋“œ, ๋ฐ”๋””๊ฐ€ ๊ธฐ๋Œ€ํ•œ ๋Œ€๋กœ์˜€๋Š”์ง€๋„ ๊ฒ€์ฆํ•œ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด createPost ํ…Œ์ŠคํŠธ์—์„œ๋Š” ์‹ค์ œ๋กœ POST๊ฐ€ ๋‚˜๊ฐ”๋Š”์ง€, JSON ๋ณธ๋ฌธ ๊ตฌ์กฐ๊ฐ€ ๋งž๋Š”์ง€, ํ•„์š”ํ•œ ์ธ์ฆ ํ—ค๋”๊ฐ€ ํฌํ•จ๋๋Š”์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

์ด๋Ÿฐ ๊ฒ€์ฆ์ด ์—†์œผ๋ฉด UI๋งŒ ์–ผํ• ํ†ต๊ณผํ•˜๊ณ  ์‹ค์ œ ํ†ตํ•ฉ ๊ณ„์•ฝ์€ ๊นจ์ ธ ์žˆ์„ ์ˆ˜ ์žˆ๋‹ค.

ํƒ€์ž… ์•ˆ์ „ํ•œ ๋ชจํ‚น

์ž‘์„ฑ์ž๋Š” OpenAPI ๊ฐ™์€ ์Šคํ‚ค๋งˆ ๊ธฐ๋ฐ˜ ํƒ€์ž… ์ƒ์„ฑ๊ณผ ํ•จ๊ป˜ ๋ชจํ‚น ๋ฐ์ดํ„ฐ๋ฅผ ํƒ€์ž… ์ฒดํฌํ•˜๋Š” ๋ฐฉ์‹์„ ๊ถŒ์žฅํ•œ๋‹ค. ์ด์œ ๋Š” ๋‹จ์ˆœํ•˜๋‹ค. ํ…Œ์ŠคํŠธ๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” ๊ฐ€์งœ ์‘๋‹ต์ด ์‹ค์ œ ์šด์˜ ์‘๋‹ต๊ณผ ๊ตฌ์กฐ๊ฐ€ ๋‹ค๋ฅด๋ฉด, ํ…Œ์ŠคํŠธ๋Š” ์•ˆ์ •๊ฐ์„ ์ฃผ๋Š” ๋Œ€์‹  ๊ฑฐ์ง“ ํ™•์‹ ๋งŒ ์ค€๋‹ค.

์ฆ‰, ๋ชจํ‚น์€ ๋น ๋ฅด๊ธฐ๋งŒ ํ•˜๋ฉด ๋˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ๊ณ„์•ฝ์„ ๋ณด์กดํ•ด์•ผ ํ•œ๋‹ค.

์‹ค๋ฌด์  ์žฅ์ 

์ด ๋ฐฉ์‹์˜ ์žฅ์ ์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

  • ๋ธŒ๋ผ์šฐ์ €๋ถ€ํ„ฐ ์„œ๋ฒ„๊นŒ์ง€ ์•ฑ ๋‚ด๋ถ€๋ฅผ ์ง„์งœ์ฒ˜๋Ÿผ ์‹คํ–‰ํ•œ๋‹ค.
  • ์™ธ๋ถ€ ์˜์กด์„ฑ๋งŒ ์•ˆ์ •์ ์œผ๋กœ ์ฐจ๋‹จํ•œ๋‹ค.
  • ํ…Œ์ŠคํŠธ ๊ฐ„ ๋…๋ฆฝ์„ฑ์ด ๋†’๋‹ค.
  • ๋ณ‘๋ ฌ ์‹คํ–‰์— ๊ฐ•ํ•˜๋‹ค.
  • ์š”์ฒญ/์‘๋‹ต ๊ณ„์•ฝ์„ ๋ชจ๋‘ ๊ฒ€์ฆํ•  ์ˆ˜ ์žˆ๋‹ค.

ํŠนํžˆ Next.js App Router๋‚˜ TanStack Start์ฒ˜๋Ÿผ ์„œ๋ฒ„ ํ•จ์ˆ˜๋ฅผ ์ ๊ทน์ ์œผ๋กœ ์“ฐ๋Š” ํŒ€์—๊ฒŒ ๋งค์šฐ ์‹ค์šฉ์ ์ด๋‹ค.

๊ฒฐ๋ก 

์ตœ์‹  ํ’€์Šคํƒ ํ”„๋ ˆ์ž„์›Œํฌ ์‹œ๋Œ€์˜ E2E ํ…Œ์ŠคํŠธ๋Š” ๋ธŒ๋ผ์šฐ์ € ์ธํ„ฐ์…‰ํŠธ๋งŒ์œผ๋กœ ์ถฉ๋ถ„ํ•˜์ง€ ์•Š๋‹ค. ์ง„์งœ ๊ณต๋ฐฑ์€ ์„œ๋ฒ„์—์„œ ์‹œ์ž‘๋˜๋Š” ์™ธ๋ถ€ ํ˜ธ์ถœ์— ์žˆ๊ณ , ์ด๋ฅผ ๋ฉ”์šฐ๋ ค๋ฉด ์•ฑ ์„œ๋ฒ„ ๋‚ด๋ถ€์—์„œ ํ…Œ์ŠคํŠธ๋ณ„๋กœ ๊ฒฉ๋ฆฌ๋œ ๋„คํŠธ์›Œํฌ ๋ชจํ‚น ๊ณ„์ธต์„ ๊ฐ€์ ธ๊ฐ€์•ผ ํ•œ๋‹ค. ์ด ๊ธ€์€ FE ํŒ€์ด ์„œ๋ฒ„ ๊ฒฝ๊ณ„๋ฅผ ํฌํ•จํ•œ โ€œ์‹ค์ œ์— ๊ฐ€๊นŒ์šด E2Eโ€๋กœ ์˜ฎ๊ฒจ๊ฐˆ ๋•Œ ์ฐธ๊ณ ํ•  ๋งŒํ•œ ์ข‹์€ ์ฒญ์‚ฌ์ง„์ด๋‹ค.

์›๋ฌธ ์ œ๋ชฉ: How to End-to-end (E2E) Test AI Agents: Mocking API Responses with Playwright in Next.js ์›๋ฌธ ๋งํฌ: https://dev.to/dumebii/how-to-e2e-test-ai-agents-mocking-api-responses-with-playwright-in-nextjs-nic ๋ฒˆ์—ญ์ผ: 2026-03-09 KST

Next.js์—์„œ Playwright๋กœ AI ์—์ด์ „ํŠธ E2E ํ…Œ์ŠคํŠธํ•˜๊ธฐ

AI ์—์ด์ „ํŠธ ์ œํ’ˆ์„ ๋งŒ๋“œ๋Š” ๊ฒƒ์€ ์žฌ๋ฏธ์žˆ์ง€๋งŒ, CI/CD ํŒŒ์ดํ”„๋ผ์ธ์—์„œ ํ…Œ์ŠคํŠธํ•˜๋Š” ์ผ์€ ์ „ํ˜€ ๋‹ค๋ฅด๋‹ค. ์ž‘์„ฑ์ž๋Š” Ozigi๋ผ๋Š” ์ฝ˜ํ…์ธ  ๊ด€๋ฆฌํ˜• AI ์—์ด์ „ํŠธ๋ฅผ ๋งŒ๋“ค๋ฉฐ ๋‹ค์Œ ์„ธ ๊ฐ€์ง€ ๋ฌธ์ œ์— ๋ถ€๋”ชํ˜”๋‹ค๊ณ  ๋งํ•œ๋‹ค.

  1. ๋น„์šฉ: ํ…Œ์ŠคํŠธ๋ฅผ ๋Œ๋ฆด ๋•Œ๋งˆ๋‹ค OpenAI, Anthropic, Vertex AI ๊ฐ™์€ API ํฌ๋ ˆ๋”ง์ด ์†Œ๋ชจ๋œ๋‹ค.
  2. ์†๋„: LLM ํ˜ธ์ถœ์€ ๋А๋ฆฌ๋‹ค. ํ…Œ์ŠคํŠธ ํ•˜๋‚˜๋‹น 10~15์ดˆ์”ฉ ๊ฑธ๋ฆฌ๋ฉด ๋ฐฐํฌ ํŒŒ์ดํ”„๋ผ์ธ์ด ํฌ๊ฒŒ ๋Š˜์–ด์ง„๋‹ค.
  3. ๋น„๊ฒฐ์ •์„ฑ: LLM ์‘๋‹ต์€ ์™„์ „ํžˆ ๋™์ผํ•œ ๋ฌธ์ž์—ด์„ ๋ณด์žฅํ•˜์ง€ ์•Š๋Š”๋‹ค. ๋”ฐ๋ผ์„œ ์ •ํ™•ํ•œ ๋ฌธ๊ตฌ ์ผ์น˜๋ฅผ ๊ธฐ๋Œ€ํ•˜๋Š” assertion์€ ์‰ฝ๊ฒŒ flakyํ•ด์ง„๋‹ค.

์ž‘์„ฑ์ž๋Š” ๊ทธ๋ž˜์„œ โ€œLLM ๋ฐฑ์—”๋“œ์— ์‹ค์ œ๋กœ ๋ถ™์ง€ ์•Š๊ณ ๋„ ํ”„๋ŸฐํŠธ์—”๋“œ์˜ ์ƒํƒœ ์ „ํ™˜์„ E2E๋กœ ๊ฒ€์ฆํ•˜๋Š” ๋ฐฉ๋ฒ•โ€์„ ํƒํ–ˆ๋‹ค.

๊ธฐ๋ณธ ์•„์ด๋””์–ด: ๋„คํŠธ์›Œํฌ ์ธํ„ฐ์…‰ํŠธ๋กœ LLM ์‘๋‹ต์„ ๊ณ ์ •ํ•œ๋‹ค

Ozigi์—์„œ ์‚ฌ์šฉ์ž๋Š” ํŽ˜๋ฅด์†Œ๋‚˜๋ฅผ ๊ณ ๋ฅด๊ณ , URL์ด๋‚˜ ํ…์ŠคํŠธ ์ปจํ…์ŠคํŠธ๋ฅผ ๋„ฃ๊ณ , โ€œGenerate Campaignโ€์„ ๋ˆ„๋ฅธ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด UI๋Š” ๋กœ๋” ์ƒํƒœ๋กœ ์ „ํ™˜๋˜๊ณ , ์ดํ›„ ์ƒ์„ฑ๋œ ์นด๋“œ/๊ทธ๋ฆฌ๋“œ ๊ฒฐ๊ณผ๋ฅผ ๋ณด์—ฌ ์ค€๋‹ค.

์ด ํ๋ฆ„์—์„œ ๊ฒ€์ฆํ•˜๊ณ  ์‹ถ์€ ๊ฒƒ์€ ๋ชจ๋ธ ํ’ˆ์งˆ์ด ์•„๋‹ˆ๋ผ UI ์ƒํƒœ ๋ณ€ํ™”๋‹ค. ์ฆ‰,

  • ๋ฒ„ํŠผ ํด๋ฆญ ํ›„ ๋กœ๋”๊ฐ€ ๋‚˜ํƒ€๋‚˜๋Š”๊ฐ€
  • ์š”์ฒญ ์ค‘ ์Šคํ”ผ๋„ˆ/๋™์  ๋กœ๋”๊ฐ€ ๋ณด์ด๋Š”๊ฐ€
  • ์‘๋‹ต ํ›„ ๊ฒฐ๊ณผ ์นด๋“œ๊ฐ€ ๋ Œ๋”๋ง๋˜๋Š”๊ฐ€
  • ์‹คํŒจ ์‹œ ์—๋Ÿฌ ์ƒํƒœ๊ฐ€ ๋ณด์ด๋Š”๊ฐ€

์ด๋Ÿฐ ๋ถ€๋ถ„์€ ์‹ค์ œ LLM์„ ํ˜ธ์ถœํ•˜์ง€ ์•Š์•„๋„ ์ถฉ๋ถ„ํžˆ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ๋‹ค.

Playwright page.route() ํ™œ์šฉ

์ž‘์„ฑ์ž๋Š” Playwright์˜ ๋„คํŠธ์›Œํฌ ์ธํ„ฐ์…‰ํŠธ๋ฅผ ์ด์šฉํ•ด ํŠน์ • API ์—”๋“œํฌ์ธํŠธ ์š”์ฒญ์„ ๊ฐ€๋กœ์ฑ„๊ณ , ๋ฏธ๋ฆฌ ์ค€๋น„ํ•œ JSON ์‘๋‹ต์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด /api/generate ๊ฐ™์€ ๋‚ด๋ถ€ ์—”๋“œํฌ์ธํŠธ์— ๋Œ€ํ•ด ๊ณ ์ •๋œ campaign payload๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ์„ค์ •ํ•˜๋ฉด, ํ…Œ์ŠคํŠธ๋Š” ๋น ๋ฅด๊ณ  ๊ฒฐ์ •์ ์œผ๋กœ ๋™์ž‘ํ•œ๋‹ค.

ํ•ต์‹ฌ ํŒจํ„ด์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

  1. ํ…Œ์ŠคํŠธ ์‹œ์ž‘ ์ „์— page.route()๋กœ ์—”๋“œํฌ์ธํŠธ๋ฅผ ๋“ฑ๋กํ•œ๋‹ค.
  2. ์š”์ฒญ์ด ์˜ค๋ฉด route.fulfill()๋กœ ์ƒํƒœ ์ฝ”๋“œ์™€ JSON ์‘๋‹ต์„ ๋Œ๋ ค์ค€๋‹ค.
  3. ํŽ˜์ด์ง€์—์„œ ์‹ค์ œ ์‚ฌ์šฉ์ž ํ๋ฆ„์„ ์ˆ˜ํ–‰ํ•œ๋‹ค.
  4. ๋กœ๋”ฉ UI์™€ ๊ฒฐ๊ณผ UI๋ฅผ assertํ•œ๋‹ค.

์™œ ์ด ๋ฐฉ์‹์ด ์ข‹์€๊ฐ€

์ด ๋ฐฉ์‹์˜ ์žฅ์ ์€ ๋ถ„๋ช…ํ•˜๋‹ค.

  • ๋ˆ์„ ์•„๋‚€๋‹ค. ํ…Œ์ŠคํŠธ๊ฐ€ ์‹ค์ œ LLM API๋ฅผ ์น˜์ง€ ์•Š๋Š”๋‹ค.
  • ๋นจ๋ผ์ง„๋‹ค. ์‘๋‹ต์ด ์ฆ‰์‹œ ๋Œ์•„์˜ค๋ฏ€๋กœ ํ…Œ์ŠคํŠธ ์‹œ๊ฐ„์ด ์งง์•„์ง„๋‹ค.
  • ์•ˆ์ •์ ์ด๋‹ค. ๋งค๋ฒˆ ๊ฐ™์€ payload๋ฅผ ๋ฐ›์œผ๋ฏ€๋กœ assertion์ด ํ”๋“ค๋ฆฌ์ง€ ์•Š๋Š”๋‹ค.
  • UI์— ์ง‘์ค‘ํ•  ์ˆ˜ ์žˆ๋‹ค. ๋ชจ๋ธ ์ž์ฒด๊ฐ€ ์•„๋‹ˆ๋ผ ํ”„๋ŸฐํŠธ ์ƒํƒœ ๊ธฐ๊ณ„๋ฅผ ๊ฒ€์ฆํ•  ์ˆ˜ ์žˆ๋‹ค.

์–ด๋–ค ๊ฒƒ์„ ๊ฒ€์ฆํ•ด์•ผ ํ•˜๋‚˜

LLM ์•ฑ ํ…Œ์ŠคํŠธ์—์„œ ์ค‘์š”ํ•œ ๊ฒƒ์€ โ€œ๋ฌธ์žฅ์ด ๋˜‘๊ฐ™์€๊ฐ€โ€๋ณด๋‹ค โ€œ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์ด ๊ธฐ๋Œ€๋Œ€๋กœ ํ˜๋Ÿฌ๊ฐ€๋Š”๊ฐ€โ€๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ๋‹ค์Œ์„ ๊ฒ€์ฆํ•˜๋Š” ํŽธ์ด ํ˜„์‹ค์ ์ด๋‹ค.

  • ์ƒ์„ฑ ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ ์š”์ฒญ์ด ์‹œ์ž‘๋˜๋Š”๊ฐ€
  • ๋กœ๋”ฉ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋‚˜ํƒ€๋‚˜๋Š”๊ฐ€
  • ๊ฒฐ๊ณผ ์„น์…˜์ด ๋ Œ๋”๋ง๋˜๋Š”๊ฐ€
  • ์นด๋“œ ๊ฐœ์ˆ˜๋‚˜ ์„น์…˜ ์ œ๋ชฉ์ฒ˜๋Ÿผ ๊ตฌ์กฐ์  ๊ฒฐ๊ณผ๊ฐ€ ๋งž๋Š”๊ฐ€
  • ์—๋Ÿฌ ์‘๋‹ต์—์„œ fallback UI๊ฐ€ ์ •์ƒ ๋™์ž‘ํ•˜๋Š”๊ฐ€

์ด๋Ÿฐ ๊ตฌ์กฐ์  ๊ฒ€์ฆ์€ ๋น„๊ฒฐ์ •์„ฑ ๋ฌธ์ œ๋ฅผ ํฌ๊ฒŒ ์ค„์—ฌ ์ค€๋‹ค.

์‹คํŒจ ์‹œ๋‚˜๋ฆฌ์˜ค๋„ ๊ฐ™์ด ํ…Œ์ŠคํŠธํ•˜๋ผ

์‹ค๋ฌด์—์„œ๋Š” ์„ฑ๊ณต ์ผ€์ด์Šค๋งŒํผ ์‹คํŒจ ์ผ€์ด์Šค๊ฐ€ ์ค‘์š”ํ•˜๋‹ค. ๋„คํŠธ์›Œํฌ ์—๋Ÿฌ, 500 ์‘๋‹ต, ๋นˆ ๊ฒฐ๊ณผ, validation ์‹คํŒจ ๊ฐ™์€ ์‘๋‹ต์„ ๊ฐ๊ฐ ๋ชจํ‚นํ•ด ๋‘๋ฉด, ์‹ค์ œ ์šด์˜์—์„œ ์ž์ฃผ ๊นจ์ง€๋Š” ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ๋ฏธ๋ฆฌ ๋ฐฉ์–ดํ•  ์ˆ˜ ์žˆ๋‹ค.

์ด ๊ธ€์ด ์ฃผ๋Š” ์‹ค๋ฌด ๊ตํ›ˆ

AI ์•ฑ์„ ํ…Œ์ŠคํŠธํ•  ๋•Œ โ€œ๋ชจ๋ธ์„ ํ…Œ์ŠคํŠธํ•œ๋‹คโ€์™€ โ€œ์ œํ’ˆ ๊ฒฝํ—˜์„ ํ…Œ์ŠคํŠธํ•œ๋‹คโ€๋ฅผ ๋ถ„๋ฆฌํ•ด์•ผ ํ•œ๋‹ค. ์ „์ž๋Š” ๋ณ„๋„์˜ ํ‰๊ฐ€ ํŒŒ์ดํ”„๋ผ์ธ์ด๋‚˜ offline eval์ด ๋” ์–ด์šธ๋ฆฌ๊ณ , ํ›„์ž๋Š” Playwright ๊ฐ™์€ E2E ๋„๊ตฌ์—์„œ ๊ณ ์ • ์‘๋‹ต ๊ธฐ๋ฐ˜์œผ๋กœ ๋น ๋ฅด๊ฒŒ ๊ฒ€์ฆํ•˜๋Š” ํŽธ์ด ๋งž๋‹ค.

๊ฒฐ๋ก 

Next.js ๊ธฐ๋ฐ˜ AI ์ œํ’ˆ์˜ E2E ํ…Œ์ŠคํŠธ๋Š” ์‹ค์ œ LLM ํ˜ธ์ถœ์„ ์ œ๊ฑฐํ•˜๊ณ , Playwright ์ธํ„ฐ์…‰ํŠธ๋กœ ์‘๋‹ต์„ ํ†ต์ œํ•˜๋Š” ์ชฝ์ด ํ›จ์”ฌ ์‹ค์šฉ์ ์ด๋‹ค. ๋น„์šฉ, ์†๋„, ๋น„๊ฒฐ์ •์„ฑ์ด๋ผ๋Š” ์„ธ ๋ฌธ์ œ๋ฅผ ํ•œ ๋ฒˆ์— ์ค„์ด๋ฉด์„œ๋„ FE ํŒ€์ด ๊ฐ€์žฅ ์ค‘์š”ํ•˜๊ฒŒ ๋ณด๋Š” ์‚ฌ์šฉ์ž ํ๋ฆ„์„ ์•ˆ์ •์ ์œผ๋กœ ๊ฒ€์ฆํ•  ์ˆ˜ ์žˆ๋‹ค.

์›๋ฌธ ์ œ๋ชฉ: Building UI Blocks on Top of shadcn/ui with a Live Editor ์›๋ฌธ ๋งํฌ: https://dev.to/felipe_menezes/building-ui-blocks-on-top-of-shadcnui-with-a-live-editor-g3 ๋ฒˆ์—ญ์ผ: 2026-03-09 KST

shadcn/ui ์œ„์— ๋ผ์ด๋ธŒ ์—๋””ํ„ฐ๋ฅผ ์–น์€ UI ๋ธ”๋ก ๋งŒ๋“ค๊ธฐ

์ž‘์„ฑ์ž๋Š” ์˜คํ”ˆ์†Œ์Šค ํ”„๋กœ์ ํŠธ UI Flx๋ฅผ ์†Œ๊ฐœํ•œ๋‹ค. ์ด ํ”„๋กœ์ ํŠธ๋Š” shadcn/ui๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๋งŒ๋“  UI ๋ธ”๋ก ๋ชจ์Œ์œผ๋กœ, ํ˜„๋Œ€์ ์ธ ํ”„๋ŸฐํŠธ์—”๋“œ ์Šคํƒ์— ์‰ฝ๊ฒŒ ํ†ตํ•ฉํ•˜๊ณ  ์ปค์Šคํ„ฐ๋งˆ์ด์ฆˆํ•  ์ˆ˜ ์žˆ๋„๋ก ์„ค๊ณ„๋˜์—ˆ๋‹ค.

ํ•ต์‹ฌ ๋ชฉํ‘œ๋Š” ๋‹จ์ˆœํ•˜๋‹ค.

  • ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ UI ์„น์…˜์„ ๋” ์‰ฝ๊ฒŒ ๋งŒ๋“ ๋‹ค.
  • ์ฝ”๋“œ์— ๋Œ€ํ•œ ํ†ต์ œ๊ถŒ์€ ์œ ์ง€ํ•œ๋‹ค.
  • ๋ณต์‚ฌํ•ด์„œ ๋ถ™์—ฌ ๋„ฃ๊ธฐ ์ „์— ์‹œ๊ฐ์ ์œผ๋กœ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•œ๋‹ค.

๋ผ์ด๋ธŒ ์—๋””ํ„ฐ๊ฐ€ ํ•ต์‹ฌ ๊ธฐ๋Šฅ

์ž‘์„ฑ์ž๋Š” UI Flx๋ฅผ ๋งŒ๋“ค๋ฉด์„œ โ€œ์ฝ”๋“œ๋ฅผ ๋ณต์‚ฌํ•˜๊ธฐ ์ „์— ๋ธ”๋ก์„ ์‹œ๊ฐ์ ์œผ๋กœ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ์œผ๋ฉด ์ข‹๊ฒ ๋‹คโ€๋Š” ์š”๊ตฌ๋ฅผ ๊ฐ€์กŒ๊ณ , ๊ทธ๋ž˜์„œ ๊ฐ ๋ธ”๋ก๋งˆ๋‹ค ๋ผ์ด๋ธŒ ์—๋””ํ„ฐ๋ฅผ ๋ถ™์˜€๋‹ค.

์ฆ‰, ์‚ฌ์šฉ์ž๋Š” ๋ธ”๋ก์„ ๋ฏธ๋ฆฌ ํŽธ์ง‘ํ•˜๊ณ  ๊ฒฐ๊ณผ๋ฅผ ์ฆ‰์‹œ ํ™•์ธํ•œ ๋‹ค์Œ, ์ž์‹ ์—๊ฒŒ ๋งž๋Š” ํ˜•ํƒœ์˜ ์ฝ”๋“œ๋ฅผ ๊ฐ€์ ธ๊ฐˆ ์ˆ˜ ์žˆ๋‹ค. ๋‹จ์ˆœํ•œ ์ปดํฌ๋„ŒํŠธ ๊ฐค๋Ÿฌ๋ฆฌ๋ณด๋‹ค ํ•œ ๋‹จ๊ณ„ ๋” ์‹ค์šฉ์ ์ธ ๋ฐฉํ–ฅ์ด๋‹ค.

CMS ๊ธฐ๋ฐ˜ ์‚ฌ์ดํŠธ๋ฅผ ๊ณ ๋ คํ•œ ์„ค๊ณ„

๋˜ ํ•˜๋‚˜์˜ ๋ชฉํ‘œ๋Š” CMS ๊ธฐ๋ฐ˜ ์›น์‚ฌ์ดํŠธ์™€ ์ž˜ ๋งž๋„๋ก ๋งŒ๋“œ๋Š” ๊ฒƒ์ด์—ˆ๋‹ค. ์˜ˆ์‹œ๋กœ Sanity ๊ฐ™์€ CMS์™€ ์‰ฝ๊ฒŒ ๋ถ™์ผ ์ˆ˜ ์žˆ๋„๋ก ๊ณ ๋ คํ–ˆ๋‹ค๊ณ  ์„ค๋ช…ํ•œ๋‹ค. ์ด๋Š” ๋งˆ์ผ€ํŒ… ์‚ฌ์ดํŠธ, ์ฝ˜ํ…์ธ  ์‚ฌ์ดํŠธ, ๋žœ๋”ฉ ํŽ˜์ด์ง€์ฒ˜๋Ÿผ ๋ฐ˜๋ณต ์„น์…˜ ์กฐํ•ฉ์ด ๋งŽ์€ ํŒ€์—๊ฒŒ ํŠนํžˆ ์œ ์šฉํ•œ ์ ‘๊ทผ์ด๋‹ค.

์˜คํ”ˆ์†Œ์Šค ํ”„๋กœ์ ํŠธ

ํ”„๋กœ์ ํŠธ๋Š” ์˜คํ”ˆ์†Œ์Šค๋กœ ๊ณต๊ฐœ๋˜์–ด ์žˆ์œผ๋ฉฐ, ๊ด€์‹ฌ์ด ์žˆ์œผ๋ฉด ์‚ฌ์ดํŠธ์—์„œ ์ง์ ‘ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ์•ˆ๋‚ดํ•œ๋‹ค.

์งง์€ ์‹ค๋ฌด ํ•ด์„

์ด ๊ธ€ ์ž์ฒด๋Š” ๊ธฐ์ˆ  ๊ตฌํ˜„ ๋””ํ…Œ์ผ๋ณด๋‹ค๋Š” ํ”„๋กœ์ ํŠธ ์†Œ๊ฐœ์— ๊ฐ€๊น์ง€๋งŒ, FE ์‹ค๋ฌด ๊ด€์ ์—์„œ ์ค‘์š”ํ•œ ์‹œ์‚ฌ์ ์€ ์žˆ๋‹ค.

  1. shadcn/ui ์ƒํƒœ๊ณ„๊ฐ€ ์ด์ œ ๋‹จ์ˆœ ์ปดํฌ๋„ŒํŠธ ๋ณต์ œ ์ˆ˜์ค€์„ ๋„˜์–ด โ€œ๋ธ”๋ก + ์—๋””ํ„ฐ + ์ฝ”๋“œ ๋‚ด๋ณด๋‚ด๊ธฐโ€ ๋ฐฉํ–ฅ์œผ๋กœ ํ™•์žฅ๋˜๊ณ  ์žˆ๋‹ค.
  2. ํŒ€๋“ค์ด ๋””์ž์ธ ์‹œ์Šคํ…œ๋ณด๋‹ค ๋น ๋ฅธ ์ƒ์‚ฐ์„ฑ ๋„๊ตฌ๋ฅผ ์›ํ•  ๋•Œ, ์ด๋Ÿฐ ๋ธ”๋ก ๊ธฐ๋ฐ˜ ์ ‘๊ทผ์ด ์ถฉ๋ถ„ํžˆ ๋งค๋ ฅ์ ์ผ ์ˆ˜ ์žˆ๋‹ค.
  3. CMS ์นœํ™”์„ฑ์€ ์•ž์œผ๋กœ ๋งˆ์ผ€ํŒ…/์ฝ˜ํ…์ธ  ํ”„๋ŸฐํŠธ์—”๋“œ์—์„œ ๋” ์ค‘์š”ํ•œ ์„ ํƒ ๊ธฐ์ค€์ด ๋  ๊ฐ€๋Šฅ์„ฑ์ด ํฌ๋‹ค.

๊ฒฐ๋ก 

์•„์ฃผ ๊ธด ๊ธฐ์ˆ  ๊ธ€์€ ์•„๋‹ˆ์ง€๋งŒ, ์ตœ๊ทผ FE ํˆด๋ง ํ๋ฆ„์„ ์ž˜ ๋ณด์—ฌ ์ค€๋‹ค. shadcn/ui ๊ธฐ๋ฐ˜ ์กฐ๋ฆฝํ˜• UI ์ž์‚ฐ์„ ๋งŒ๋“ค๊ณ , ์ด๋ฅผ ๋ผ์ด๋ธŒ ํŽธ์ง‘ ๊ฒฝํ—˜๊ณผ ์—ฐ๊ฒฐํ•˜๋Š” ๋ฐฉํ–ฅ์€ ์ž‘์€ ํŒ€๊ณผ ์Šคํƒ€ํŠธ์—…์ด ๋น ๋ฅด๊ฒŒ ํ™”๋ฉด์„ ์ƒ์‚ฐํ•˜๋Š” ๋ฐ ๊ฝค ์ž˜ ๋งž๋Š”๋‹ค.

์›๋ฌธ ์ œ๋ชฉ: Put the ZIP code first. ์›๋ฌธ ๋งํฌ: https://zipcodefirst.com/ ๋ฒˆ์—ญ์ผ: 2026-03-09 KST

ZIP ์ฝ”๋“œ๋ฅผ ๋จผ์ € ๋„ฃ๊ฒŒ ํ•˜๋ผ

์ด ๊ธ€์€ ๋ฏธ๊ตญ ์ฃผ์†Œ ์ž…๋ ฅ ํผ UX๋ฅผ ๋งค์šฐ ์ง์„ค์ ์œผ๋กœ ๋น„ํŒํ•œ๋‹ค. ํ•ต์‹ฌ ์ฃผ์žฅ๋„ ๋ช…ํ™•ํ•˜๋‹ค. ๋ฏธ๊ตญ์—์„œ๋Š” ZIP ์ฝ”๋“œ 5์ž๋ฆฌ๋งŒ ์žˆ์œผ๋ฉด ๋„์‹œ, ์ฃผ, ๊ตญ๊ฐ€๋ฅผ ์ƒ๋‹น ๋ถ€๋ถ„ ์ž๋™์œผ๋กœ ์ฑ„์šธ ์ˆ˜ ์žˆ๋Š”๋ฐ, ์™œ ์•„์ง๋„ ์ฃผ์†Œ ํผ ๋งจ ์•„๋ž˜์— ZIP ์ฝ”๋“œ๋ฅผ ๋‘๊ณ  ์‚ฌ์šฉ์ž๊ฐ€ ๋„์‹œ/์ฃผ/๊ตญ๊ฐ€๋ฅผ ์ผ์ผ์ด ์ž…๋ ฅํ•˜๊ฒŒ ํ•˜๋А๋ƒ๋Š” ๊ฒƒ์ด๋‹ค.

5๊ธ€์ž๋ฉด 3๊ฐœ ํ•„๋“œ๋ฅผ ์ฑ„์šธ ์ˆ˜ ์žˆ๋‹ค

์‚ฌ์šฉ์ž๊ฐ€ 90210๋งŒ ์ž…๋ ฅํ•ด๋„ Beverly Hills, California, United States๋ฅผ ๋ฏธ๋ฆฌ ์•Œ ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ๋งŽ์€ ์„œ๋น„์Šค๋Š” ์—ฌ์ „ํžˆ ๋‹ค์Œ ์ˆœ์„œ๋ฅผ ๊ฐ•์š”ํ•œ๋‹ค.

  • Street address
  • City
  • State dropdown
  • ZIP
  • Country dropdown

์ž‘์„ฑ์ž๋Š” ์ด๊ฑธ 2026๋…„์—๋„ ๋ฐ˜๋ณตํ•˜๋Š” ๊ฑด ๋น„ํšจ์œจ์˜ ์ „ํ˜•์ด๋ผ๊ณ  ๊ผฌ์ง‘๋Š”๋‹ค.

์™œ ZIP ์šฐ์„ ์ด ๋” ์ข‹์€๊ฐ€

ZIP ์ฝ”๋“œ๋ฅผ ๋จผ์ € ๋ฐ›์œผ๋ฉด ๋„์‹œ, ์ฃผ, ๊ตญ๊ฐ€๋ฅผ ์ž๋™ ์ฑ„์›€ํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ทธ ๊ฒฐ๊ณผ:

  • ์‚ฌ์šฉ์ž๊ฐ€ ํƒ€์ดํ•‘ํ•ด์•ผ ํ•  ์–‘์ด ์ค„๊ณ 
  • dropdown ์Šคํฌ๋กค ์ง€์˜ฅ์ด ์‚ฌ๋ผ์ง€๋ฉฐ
  • ์ฃผ์†Œ ์ž๋™์™„์„ฑ ๊ฒ€์ƒ‰ ๋ฒ”์œ„๋„ ์ข์•„์ ธ ๋” ๋น ๋ฅด๊ณ  ์ •ํ™•ํ•ด์ง„๋‹ค
  • ์ˆ˜์ง‘๋˜๋Š” ๋ฐ์ดํ„ฐ ํ’ˆ์งˆ๋„ ๋†’์•„์ง„๋‹ค

๊ธ€์—์„œ๋Š” Zippopotam.us ๊ฐ™์€ ๋ฌด๋ฃŒ API ์˜ˆ์‹œ๋ฅผ ๋ณด์—ฌ ์ฃผ๋ฉฐ, ์‹ค์ œ ๊ตฌํ˜„์ด ๋ช‡ ์ค„์ด๋ฉด ๋œ๋‹ค๊ณ  ์„ค๋ช…ํ•œ๋‹ค.

const res = await fetch(`https://api.zippopotam.us/us/${zip}`)
const data = await res.json()
city.value = data.places[0]["place name"]
state.value = data.places[0]["state"]
country.value = "United States"

์ฃผ์†Œ ํผ์˜ โ€œ์ˆ˜์น˜์˜ ์ „๋‹นโ€

์ž‘์„ฑ์ž๋Š” ํ˜•ํŽธ์—†๋Š” ์ฃผ์†Œ ํผ ํŒจํ„ด์„ ๋ช‡ ๋‹จ๊ณ„๋กœ ์กฐ๋กฑ ์„ž์–ด ๋ถ„๋ฅ˜ํ•œ๋‹ค.

1๋‹จ๊ณ„: ZIP์ด ๋งจ ์•„๋ž˜์— ์žˆ๋‹ค

์ด๋ฏธ ์ž๋™ ์ฑ„์›€์— ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์„ ์ˆ˜ ์žˆ๋Š”๋ฐ๋„ ๋งˆ์ง€๋ง‰์— ๋‘๋Š” ํŒจํ„ด์ด๋‹ค.

2๋‹จ๊ณ„: ZIP์„ ๋ฐ›์•„ ๋†“๊ณ  ์•„๋ฌด ๊ฒƒ๋„ ์•ˆ ํ•œ๋‹ค

์‚ฌ์šฉ์ž๋Š” ZIP์„ ์ž…๋ ฅํ–ˆ์ง€๋งŒ, ํผ์€ ๋„์‹œ/์ฃผ๋ฅผ ์ž๋™ ์ฑ„์šฐ์ง€ ์•Š๋Š”๋‹ค.

3๋‹จ๊ณ„: ์Šคํฌ๋กคํ˜• ๊ตญ๊ฐ€ ๋“œ๋กญ๋‹ค์šด

๊ตญ๊ฐ€ 200๊ฐœ ์ด์ƒ์„ ๊ฒ€์ƒ‰ ์—†์ด ์Šคํฌ๋กคํ•˜๊ฒŒ ๋งŒ๋“œ๋Š” ํŒจํ„ด์ด๋‹ค. ์‹ฌ์ง€์–ด ๋ฏธ๊ตญ์ด โ€œThe United Statesโ€ฆโ€ ์•„๋ž˜์— ์ •๋ ฌ๋˜์–ด ์ฐพ๊ธฐ ๋” ์–ด๋ ค์šด ๊ฒฝ์šฐ๋„ ์žˆ๋‹ค.

4๋‹จ๊ณ„: ๋’ค๋กœ ๊ฐ€๋ฉด ํผ์ด ์ดˆ๊ธฐํ™”๋œ๋‹ค

๊ฒฐ์ œ ์‹คํŒจ ํ›„ ๋’ค๋กœ ๊ฐ”์„ ๋•Œ 14๊ฐœ ํ•„๋“œ๋ฅผ ์ „๋ถ€ ๋‹ค์‹œ ์ž…๋ ฅํ•˜๊ฒŒ ๋งŒ๋“œ๋Š” ์ตœ์•…์˜ UX๋‹ค.

์ถ”๊ฐ€ ์ œ์•ˆ

์ž‘์„ฑ์ž๋Š” ๋ช‡ ๊ฐ€์ง€ ๊ธฐ๋ณธ๊ธฐ๊นŒ์ง€ ๊ฐ™์ด ์งš๋Š”๋‹ค.

  • ZIP ์ฝ”๋“œ์—๋Š” inputmode="numeric"๋ฅผ ์จ์„œ ๋ชจ๋ฐ”์ผ์—์„œ ์ˆซ์ž ํ‚คํŒจ๋“œ๊ฐ€ ๋œจ๊ฒŒ ํ•˜๋ผ.
  • autocomplete="postal-code", address-line1, country ๋“ฑ ์˜ฌ๋ฐ”๋ฅธ ์ž๋™์™„์„ฑ ์†์„ฑ์„ ์จ๋ผ.
  • ๊ตญ๊ฐ€๋ฅผ ๋จผ์ € ๋ฐ›๋Š” ํŽธ์ด ๋” ์ •ํ™•ํ•  ์ˆ˜๋Š” ์žˆ๋‹ค. ๋‹ค๋งŒ ํ•ต์‹ฌ์€ โ€œ์ด๋ฏธ ์•„๋Š” ์ •๋ณด๋ฅผ ๋‹ค์‹œ ํƒ€์ดํ•‘์‹œํ‚ค์ง€ ๋ง๋ผโ€๋Š” ๊ฒƒ์ด๋‹ค.

FE ์‹ค๋ฌด ๊ด€์ ์˜ ์˜๋ฏธ

์ด ๊ธ€์€ ๋†๋‹ด ์„ž์ธ ํ†ค์ด์ง€๋งŒ, ์‹ค์ œ๋กœ๋Š” ํผ UX์˜ ๋งค์šฐ ์ค‘์š”ํ•œ ์›์น™์„ ๋งํ•œ๋‹ค.

  1. ์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•ด์•ผ ํ•˜๋Š” ์ •๋ณด๋ฅผ ์ตœ์†Œํ™”ํ•˜๋ผ.
  2. ์ด๋ฏธ ์œ ์ถ” ๊ฐ€๋Šฅํ•œ ๊ฐ’์€ ์‹œ์Šคํ…œ์ด ์ฑ„์›Œ๋ผ.
  3. ๋ชจ๋ฐ”์ผ ํ‚ค๋ณด๋“œ, ๋ธŒ๋ผ์šฐ์ € autofill, ์ฃผ์†Œ ๊ฒ€์ƒ‰ ๋ฒ”์œ„๋ฅผ ๋ชจ๋‘ ํ•จ๊ป˜ ์ตœ์ ํ™”ํ•˜๋ผ.

ํ•œ๊ตญ ์ฃผ์†Œ ํผ์—๋Š” ๊ทธ๋Œ€๋กœ ์ ์šฉ๋˜์ง€ ์•Š๋”๋ผ๋„, โ€œ์ž…๋ ฅ ์ˆœ์„œ๋ฅผ ๋ฐ์ดํ„ฐ ํ™œ์šฉ ์ˆœ์„œ์— ๋งž๊ฒŒ ๋‹ค์‹œ ์„ค๊ณ„ํ•˜๋ผโ€๋Š” ๊ตํ›ˆ์€ ์ถฉ๋ถ„ํžˆ ์ผ๋ฐ˜ํ™”ํ•  ์ˆ˜ ์žˆ๋‹ค.

๊ฒฐ๋ก 

์ด ๊ธ€์€ ๊ฑฐ์น ์ง€๋งŒ ๋งž๋Š” ๋ง์„ ํ•œ๋‹ค. ์ฃผ์†Œ ํผ์€ ์—ฌ์ „ํžˆ ๋„ˆ๋ฌด ์ž์ฃผ 2009๋…„ ํŒจํ„ด์— ๋จธ๋ฌผ๋Ÿฌ ์žˆ๋‹ค. FE ํŒ€์ด ํผ UX๋ฅผ ๊ฐœ์„ ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด, ๋จผ์ € โ€œ์‚ฌ์šฉ์ž๊ฐ€ ์™œ ์ด๊ฑธ ์ง์ ‘ ์ž…๋ ฅํ•ด์•ผ ํ•˜์ง€?โ€๋ถ€ํ„ฐ ๋‹ค์‹œ ๋ฌผ์–ด๋ณผ ํ•„์š”๊ฐ€ ์žˆ๋‹ค.

์›๋ฌธ ์ œ๋ชฉ: Announcing TypeScript 6.0 RC ์›๋ฌธ ๋งํฌ: https://devblogs.microsoft.com/typescript/announcing-typescript-6-0-rc/ ๋ฒˆ์—ญ์ผ: 2026-03-09 KST

TypeScript 6.0 RC ๋ฐœํ‘œ

Microsoft๋Š” TypeScript 6.0 RC๋ฅผ ๊ณต๊ฐœํ•˜๋ฉฐ ๋‹ค์Œ ๋ช…๋ น์œผ๋กœ ๋ฐ”๋กœ ์‚ฌ์šฉํ•ด ๋ณผ ์ˆ˜ ์žˆ๋‹ค๊ณ  ์•ˆ๋‚ดํ–ˆ๋‹ค.

npm install -D typescript@rc

์ด๋ฒˆ 6.0์€ ๋‹จ์ˆœํ•œ ๊ธฐ๋Šฅ ๋ฆด๋ฆฌ์Šค๊ฐ€ ์•„๋‹ˆ๋ผ ํฐ ์ „ํ™˜์ ์— ๊ฐ€๊น๋‹ค. ํŒ€์€ ์ด๋ฏธ ์˜ˆ๊ณ ํ–ˆ๋˜ ๊ฒƒ์ฒ˜๋Ÿผ TypeScript ์ปดํŒŒ์ผ๋Ÿฌ์™€ ์–ธ์–ด ์„œ๋น„์Šค๋ฅผ Go๋กœ ์žฌ์ž‘์„ฑํ•œ ๋„ค์ดํ‹ฐ๋ธŒ ์ฝ”๋“œ๋ฒ ์ด์Šค๋กœ ์˜ฎ๊ธฐ๊ณ  ์žˆ์œผ๋ฉฐ, ๊ทธ๊ฒƒ์ด TypeScript 7.0์˜ ๊ธฐ๋ฐ˜์ด ๋œ๋‹ค. ๋”ฐ๋ผ์„œ 6.0์€ ํ˜„์žฌ JavaScript ์ฝ”๋“œ๋ฒ ์ด์Šค ๊ธฐ๋ฐ˜์˜ ์‚ฌ์‹ค์ƒ ๋งˆ์ง€๋ง‰ ์ฃผ์š” ๋ฆด๋ฆฌ์Šค์ด์ž, 5.9์™€ 7.0 ์‚ฌ์ด๋ฅผ ์ž‡๋Š” ๋ธŒ๋ฆฌ์ง€ ์—ญํ• ์„ ๋งก๋Š”๋‹ค.

๋ฒ ํƒ€ ์ดํ›„ ๋‹ฌ๋ผ์ง„ ์ 

RC์—์„œ๋Š” ํŠนํžˆ 7.0 ๋™์ž‘๊ณผ์˜ ์ •๋ ฌ์„ ์œ„ํ•œ ์กฐ์ •์ด ๋“ค์–ด๊ฐ”๋‹ค.

1. ์ œ๋„ค๋ฆญ ํ˜ธ์ถœ ๋‚ด ํ•จ์ˆ˜ ํ‘œํ˜„์‹ ํƒ€์ž… ๊ฒ€์‚ฌ ์กฐ์ •

ํŠนํžˆ generic JSX ํ‘œํ˜„์‹ ์•ˆ์˜ ํ•จ์ˆ˜ ํ‘œํ˜„์‹ ํƒ€์ž… ๊ฒ€์‚ฌ์—์„œ ๋” ๋งŽ์€ ๋ฒ„๊ทธ๋ฅผ ์žก์•„๋‚ด๋„๋ก ๋ฐ”๋€Œ์—ˆ๋‹ค. ๊ธฐ์กด ์ฝ”๋“œ ์ค‘ ์ผ๋ถ€๋Š” ์ด์ œ ๋ช…์‹œ์  ํƒ€์ž… ์ธ์ž๋ฅผ ์ถ”๊ฐ€ํ•ด์•ผ ํ•  ์ˆ˜ ์žˆ๋‹ค.

2. import assertion ํ๊ธฐ ๋ฒ”์œ„ ํ™•๋Œ€

import ... assert { ... } ๋ฌธ๋ฒ•์— ๋Œ€ํ•œ ํ๊ธฐ๋ฅผ import() ํ˜ธ์ถœ๊นŒ์ง€ ํ™•์žฅํ–ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด import(..., { assert: {...} }) ํ˜•ํƒœ๋„ ์˜ํ–ฅ๊ถŒ์— ๋“ค์–ด๊ฐ„๋‹ค.

3. ์ตœ์‹  ์›น ํ‘œ์ค€์— ๋งž์ถ˜ DOM ํƒ€์ž… ์—…๋ฐ์ดํŠธ

DOM ํƒ€์ž… ์ •์˜๊ฐ€ ์ตœ์‹  ์›น ํ‘œ์ค€์— ๋งž๊ฒŒ ๊ฐฑ์‹ ๋๊ณ , Temporal API ๊ด€๋ จ ์กฐ์ •๋„ ์ผ๋ถ€ ํฌํ•จ๋๋‹ค.

this ์—†๋Š” ํ•จ์ˆ˜์˜ ๋ฌธ๋งฅ ๋ฏผ๊ฐ๋„ ๊ฐ์†Œ

๊ธ€์—์„œ ๊ฐ€์žฅ ๋ˆˆ์— ๋„๋Š” ๊ธฐ์ˆ ์  ์„ค๋ช…์€ ํ•จ์ˆ˜ ์ธ์ž ํƒ€์ž… ์ถ”๋ก  ๋ฐฉ์‹์˜ ๋ณ€ํ™”๋‹ค.

TypeScript๋Š” ๋ณดํ†ต ์˜ˆ์ƒ ํƒ€์ž…์ด๋‚˜ ๊ฐ™์€ ํ˜ธ์ถœ ์•ˆ์˜ ๋‹ค๋ฅธ ์ธ์ž๋ฅผ ํ†ตํ•ด ํƒ€์ž…์„ ์ถ”๋ก ํ•  ์ˆ˜ ์žˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ํ•œ ๊ฐ์ฒด ๋ฆฌํ„ฐ๋Ÿด ์•ˆ์—์„œ produce๊ฐ€ T๋ฅผ ๋งŒ๋“ค์–ด ๋‚ด๊ณ  consume์ด ๊ทธ T๋ฅผ ๋ฐ›๋Š” ๊ตฌ์กฐ์—์„œ๋Š”, ์ˆœ์„œ๊ฐ€ ๋‹ฌ๋ผ๋„ ์ž˜ ์ถ”๋ก ๋˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ๋‹ค.

ํ•˜์ง€๋งŒ ๋ฉ”์„œ๋“œ ๋ฌธ๋ฒ•์„ ์“ธ ๋•Œ๋Š” ์ด์•ผ๊ธฐ๊ฐ€ ๋‹ฌ๋ผ์งˆ ์ˆ˜ ์žˆ์—ˆ๋‹ค. ๋งค๊ฐœ๋ณ€์ˆ˜ ํƒ€์ž…์ด ๋ช…์‹œ๋˜์ง€ ์•Š์€ ํ•จ์ˆ˜๋Š” โ€œcontextually sensitive functionโ€์œผ๋กœ ์ทจ๊ธ‰๋˜๋Š”๋ฐ, ์ด ํ•จ์ˆ˜๋Š” ์ถ”๋ก  ๊ณผ์ •์—์„œ ์ž ์‹œ ๊ฑด๋„ˆ๋›ฐ์–ด์ง€๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ์—ˆ๋‹ค. ๊ทธ ๊ฒฐ๊ณผ ์†์„ฑ ์ˆœ์„œ์— ๋”ฐ๋ผ ์–ด๋–ค ๋ฉ”์„œ๋“œ๋Š” unknown์œผ๋กœ ๋–จ์–ด์ง€๋Š” ์–ด์ƒ‰ํ•œ ์ƒํ™ฉ์ด ์ƒ๊ฒผ๋‹ค.

TypeScript 6.0์€ ์ด๋Ÿฐ ๋ถ€๋ถ„์„ ๋‹ค๋“ฌ์–ด, this๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ํ•จ์ˆ˜์— ๋Œ€ํ•ด์„œ๋Š” ๋ฌธ๋งฅ ๋ฏผ๊ฐ๋„๋ฅผ ๋‚ฎ์ถ”๊ณ  ๋” ์ผ๊ด€๋˜๊ฒŒ ํƒ€์ž…์„ ์ถ”๋ก ํ•˜๋ ค๊ณ  ํ•œ๋‹ค. ์‹ค๋ฌด์ ์œผ๋กœ๋Š” โ€œ๊ฐ์ฒด ๋ฆฌํ„ฐ๋Ÿด ์•ˆ ํ•จ์ˆ˜/๋ฉ”์„œ๋“œ์˜ ์„ ์–ธ ์ˆœ์„œ ๋•Œ๋ฌธ์— ์ถ”๋ก  ๊ฒฐ๊ณผ๊ฐ€ ํ”๋“ค๋ฆฌ๋Š” ๋ฌธ์ œโ€๋ฅผ ์ค„์ด๋ ค๋Š” ๋ฐฉํ–ฅ์œผ๋กœ ์ดํ•ดํ•˜๋ฉด ๋œ๋‹ค.

7.0 ๋Œ€๋น„ ๋ฆด๋ฆฌ์Šค๋ผ๋Š” ์˜๋ฏธ

์ด๋ฒˆ ๋ฆด๋ฆฌ์Šค์˜ ์ค‘์š”ํ•œ ๋ฉ”์‹œ์ง€๋Š” โ€œ์ƒˆ ๊ธฐ๋Šฅ ๋ช‡ ๊ฐœ ์ถ”๊ฐ€โ€๋ณด๋‹ค โ€œ๋‹ค๊ฐ€์˜ค๋Š” TS 7 ์ „ํ™˜์„ ์ค€๋น„ํ•˜๋ผโ€์— ๊ฐ€๊น๋‹ค. TypeScript ํŒ€์€ Go ๊ธฐ๋ฐ˜ ๋„ค์ดํ‹ฐ๋ธŒ ๊ตฌํ˜„์„ ํ†ตํ•ด ๋” ๋น ๋ฅธ ์„ฑ๋Šฅ๊ณผ shared-memory multi-threading์˜ ์ด์ ์„ ๊ฐ€์ ธ์˜ค๋ ค ํ•œ๋‹ค. 6.0์—์„œ์˜ ์ •๋ ฌ ์ž‘์—…์€ ์ดํ›„ 7.0 ์ฑ„ํƒ ๋น„์šฉ์„ ์ค„์ด๊ธฐ ์œ„ํ•œ ์ค€๋น„ ๋‹จ๊ณ„๋‹ค.

FE/TS ์‹ค๋ฌด ๊ด€์ ์˜ ์ฒดํฌํฌ์ธํŠธ

ํ”„๋ŸฐํŠธ์—”๋“œ ํŒ€์ด ์ฃผ๋ชฉํ•  ๋ถ€๋ถ„์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

  • generic ํ•จ์ˆ˜/JSX ์‚ฌ์šฉ์ด ๋งŽ์€ ์ฝ”๋“œ๋ฒ ์ด์Šค๋Š” RC์—์„œ ํƒ€์ž… ์˜ค๋ฅ˜๊ฐ€ ๋Š˜์–ด๋‚  ์ˆ˜ ์žˆ๋‹ค.
  • import assertion ๊ด€๋ จ ์ฝ”๋“œ๋ฅผ ์“ฐ๋Š” ํ”„๋กœ์ ํŠธ๋Š” ํ๊ธฐ ๊ฒฝ๊ณ ์™€ ํ–ฅํ›„ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ๋ฐฉํ–ฅ์„ ํ™•์ธํ•ด์•ผ ํ•œ๋‹ค.
  • DOM ํƒ€์ž… ์—…๋ฐ์ดํŠธ๋กœ ์›น ํ‘œ์ค€ ๊ด€๋ จ ํƒ€์ž… ๋ณ€ํ™”๊ฐ€ ์ƒ๊ธธ ์ˆ˜ ์žˆ์œผ๋‹ˆ CI๋ฅผ ๋Œ๋ ค ๋ณด๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.
  • 6.0์€ ๊ณง 7.0์œผ๋กœ ์ด์–ด์ง€๋Š” ์ง•๊ฒ€๋‹ค๋ฆฌ์ด๋ฏ€๋กœ, ๋Œ€ํ˜• ๋ชจ๋…ธ๋ ˆํฌ๋‚˜ TS-heavy ํ”„๋กœ์ ํŠธ๋ผ๋ฉด ์ง€๊ธˆ๋ถ€ํ„ฐ ์ ์‘ ๋น„์šฉ์„ ์‚ดํ”ผ๋Š” ๊ฒŒ ์ข‹๋‹ค.

๊ฒฐ๋ก 

TypeScript 6.0 RC๋Š” ๊ฒ‰์œผ๋กœ๋Š” ๋น„๊ต์  ์ฐจ๋ถ„ํ•œ ๋ฆด๋ฆฌ์Šค์ฒ˜๋Ÿผ ๋ณด์ด์ง€๋งŒ, ์‹ค์ œ๋กœ๋Š” TypeScript 7 ๋„ค์ดํ‹ฐ๋ธŒ ์‹œ๋Œ€๋ฅผ ์ค€๋น„ํ•˜๋Š” ์ „๋žต์  ๋ฆด๋ฆฌ์Šค๋‹ค. FE ํŒ€ ์ž…์žฅ์—์„œ๋Š” ๋‹น์žฅ ์ฒด๊ฐํ•  ๋ณ€ํ™”๋ณด๋‹ค๋„, ํƒ€์ž… ์ถ”๋ก /ํ‘œ์ค€ ํƒ€์ž…/ํ๊ธฐ ๋ฌธ๋ฒ•์„ ๋ฏธ๋ฆฌ ์ ๊ฒ€ํ•˜๊ณ  ๋‹ค์Œ ๋ฉ”์ด์ € ์ „ํ™˜์— ๋Œ€๋น„ํ•˜๋Š” ๊ณ„๊ธฐ๋กœ ๋ณด๋Š” ํŽธ์ด ๋งž๋‹ค.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment