Skip to content

Modal (Dialog)ย #8

@hsskey

Description

@hsskey

๐Ÿ“‹ ์ปดํฌ๋„ŒํŠธ ์ •๋ณด

  • ์ปดํฌ๋„ŒํŠธ ์ด๋ฆ„: Modal (Dialog)
  • ์ปดํฌ๋„ŒํŠธ ์„ค๋ช…: ๋ชจ๋‹ฌ ๋‹ค์ด์–ผ๋กœ๊ทธ๋Š” ํ˜„์žฌ ์ฐฝ ๋˜๋Š” ๋‹ค๋ฅธ ๋‹ค์ด์–ผ๋กœ๊ทธ ์ฐฝ ์œ„์— ๊ฒน์ณ ํ‘œ์‹œ๋˜๋Š” ์ฐฝ์œผ๋กœ, ๋ชจ๋‹ฌ ๋‹ค์ด์–ผ๋กœ๊ทธ ์™ธ๋ถ€์˜ ์ฝ˜ํ…์ธ ๋Š” ๋น„ํ™œ์„ฑํ™”๋˜์–ด ์‚ฌ์šฉ์ž๊ฐ€ ์ƒํ˜ธ์ž‘์šฉํ•  ์ˆ˜ ์—†์Œ
  • ์‚ฌ์šฉ ์‚ฌ๋ก€: ์‚ฌ์šฉ์ž์˜ ์ฃผ์˜๋ฅผ ๋Œ์–ด์•ผ ํ•˜๋Š” ์ค‘์š”ํ•œ ๋ฉ”์‹œ์ง€๋ฅผ ํ‘œ์‹œํ•˜๊ฑฐ๋‚˜, ์‚ฌ์šฉ์ž๋กœ๋ถ€ํ„ฐ ํ•„์ˆ˜ ์ •๋ณด๋ฅผ ์ž…๋ ฅ๋ฐ›์•„์•ผ ํ•  ๋•Œ ์‚ฌ์šฉ

๐Ÿ“š ์ฐธ์กฐ ๋ฐ ๋ ˆํผ๋Ÿฐ์Šค

  • ARIA Authoring Practices: W3C ARIA Dialog (Modal) Pattern
    • ๋ชจ๋‹ฌ ๋‹ค์ด์–ผ๋กœ๊ทธ์˜ ์ ‘๊ทผ์„ฑ์„ ๊ณ ๋ คํ•œ ๊ตฌํ˜„ ๋ฐฉ๋ฒ•๊ณผ ์˜ˆ์‹œ ์ œ๊ณต
  • ๊ธฐํƒ€ ๋ ˆํผ๋Ÿฐ์Šค:
    • Web Dev์˜ dialog ์š”์†Œ ๊ด€๋ จ ์•„ํ‹ฐํด
    • MUI, Ant Design ๋“ฑ์˜ ๋ชจ๋‹ฌ ๋‹ค์ด์–ผ๋กœ๊ทธ ์ปดํฌ๋„ŒํŠธ ๋ฌธ์„œ์™€ ๋น„๊ตํ•˜์—ฌ ํŒจํ„ด ๋ถ„์„

๐Ÿ“ ์ ‘๊ทผ์„ฑ ๋ฐ ARIA ์ ์šฉ ์—ฌ๋ถ€

  • ARIA Pattern ์ ์šฉ: ARIA Dialog (Modal) Pattern
    • ์‚ฌ์šฉ๋œ ARIA ์†์„ฑ:
      • role="dialog": ์š”์†Œ๊ฐ€ ๋‹ค์ด์–ผ๋กœ๊ทธ์ž„์„ ๋ช…์‹œ
      • aria-modal="true": ๋‹ค์ด์–ผ๋กœ๊ทธ๊ฐ€ ๋ชจ๋‹ฌ์ž„์„ ๋ช…์‹œ
      • aria-labelledby: ๋‹ค์ด์–ผ๋กœ๊ทธ์˜ ์ œ๋ชฉ์„ ์ฐธ์กฐ
      • aria-describedby: ๋‹ค์ด์–ผ๋กœ๊ทธ์˜ ์„ค๋ช…์„ ์ฐธ์กฐ
  • ์ ‘๊ทผ์„ฑ ๊ด€๋ จ ๊ณ ๋ ค์‚ฌํ•ญ:
    • ๋‹ค์ด์–ผ๋กœ๊ทธ๊ฐ€ ์—ด๋ฆด ๋•Œ ์ดˆ์ ์€ ๋‹ค์ด์–ผ๋กœ๊ทธ ๋‚ด๋ถ€์˜ ์š”์†Œ๋กœ ์ด๋™ํ•ด์•ผ ํ•จ
    • ๋‹ค์ด์–ผ๋กœ๊ทธ๊ฐ€ ์—ด๋ ค ์žˆ๋Š” ๋™์•ˆ์—๋Š” Tab๊ณผ Shift+Tab์œผ๋กœ ๋‹ค์ด์–ผ๋กœ๊ทธ ๋‚ด๋ถ€์˜ ์š”์†Œ๋งŒ ํƒ์ƒ‰ํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•จ
    • Escape ํ‚ค๋ฅผ ๋ˆ„๋ฅด๋ฉด ๋‹ค์ด์–ผ๋กœ๊ทธ๊ฐ€ ๋‹ซํ˜€์•ผ ํ•จ
    • ๋‹ค์ด์–ผ๋กœ๊ทธ๊ฐ€ ๋‹ซํž ๋•Œ ์ดˆ์ ์€ ๋‹ค์ด์–ผ๋กœ๊ทธ๋ฅผ ์—ด์—ˆ๋˜ ์š”์†Œ๋กœ ๋Œ์•„๊ฐ€์•ผ ํ•จ
  • ์ ‘๊ทผ์„ฑ ๊ธฐ๋Šฅ ์š”์•ฝ:
    • ํ‚ค๋ณด๋“œ๋กœ ๋‹ค์ด์–ผ๋กœ๊ทธ ๋‚ด๋ถ€๋ฅผ ํƒ์ƒ‰ํ•  ์ˆ˜ ์žˆ์Œ
    • ๋‹ค์ด์–ผ๋กœ๊ทธ ์™ธ๋ถ€์˜ ์ฝ˜ํ…์ธ ๋Š” ๋น„ํ™œ์„ฑํ™”๋˜์–ด ์ ‘๊ทผํ•  ์ˆ˜ ์—†์Œ
    • ์ ์ ˆํ•œ ARIA ์†์„ฑ์„ ์‚ฌ์šฉํ•˜์—ฌ ๋‹ค์ด์–ผ๋กœ๊ทธ์˜ ์—ญํ• ๊ณผ ์ƒํƒœ๋ฅผ ๋ช…์‹œํ•จ

๐Ÿ—๏ธ ๋งˆํฌ์—… ๊ตฌ์กฐ ๋ถ„์„

  • HTML ์‹œ๋งจํ‹ฑ ํƒœ๊ทธ ์‚ฌ์šฉ:
    • <dialog>: ๋‹ค์ด์–ผ๋กœ๊ทธ ์ปจํ…Œ์ด๋„ˆ ์—ญํ• ์„ ํ•˜๋Š” ์ „์šฉ ์š”์†Œ
    • <form method="dialog">: ๋‹ค์ด์–ผ๋กœ๊ทธ ๋‚ด๋ถ€์˜ ํผ ์š”์†Œ
    • <button>: ๋‹ค์ด์–ผ๋กœ๊ทธ๋ฅผ ์—ด๊ณ  ๋‹ซ๋Š” ๋ฒ„ํŠผ ์š”์†Œ
  • ๋งˆํฌ์—… ๊ตฌ์กฐ ๋น„๊ต:
    • Web Dev Example:
<dialog id="MegaDialog" modal-mode="mega">
  <form method="dialog">
    <header>
      <h3>Dialog title</h3>
      <button onclick="this.closest('dialog').close('close')"></button>
    </header>
    <article>...</article>
    <footer>
      <menu>
        <button autofocus type="reset" onclick="this.closest('dialog').close('cancel')">Cancel</button>
        <button type="submit" value="confirm">Confirm</button>
      </menu>
    </footer>
  </form>
</dialog>
  • MUI:
<div role="presentation" class="MuiModal-root">
  <div aria-hidden="true" class="MuiBackdrop-root"></div>
  <div role="dialog" aria-modal="true" aria-labelledby="dialog-title" aria-describedby="dialog-description" tabindex="-1" class="MuiDialog-container">
    <div class="MuiDialog-paper">
      <h2 id="dialog-title" class="MuiDialogTitle-root">Modal title</h2>
      <div id="dialog-description" class="MuiDialogContent-root">Dialog content here</div>
      <div class="MuiDialogActions-root">
        <button type="button" class="MuiButtonBase-root MuiButton-root">Cancel</button>
        <button type="button" class="MuiButtonBase-root MuiButton-root">OK</button>
      </div>
    </div>
  </div>
</div>
  • Ant Design:
<div class="ant-modal-root">
  <div class="ant-modal-mask"></div>
  <div tabindex="-1" class="ant-modal-wrap">
    <div role="dialog" aria-labelledby="dialog-title" aria-modal="true" class="ant-modal">
      <div class="ant-modal-content">
        <div class="ant-modal-header">
          <div id="dialog-title" class="ant-modal-title">Basic Modal</div>
        </div>
        <div class="ant-modal-body">
          <p>Some contents...</p>
        </div>
        <div class="ant-modal-footer">
          <button type="button" class="ant-btn">Cancel</button>
          <button type="button" class="ant-btn ant-btn-primary">OK</button>
        </div>
      </div>
    </div>
  </div>
</div>
  • ๊ตฌ์กฐ์  ์„ ํƒ ์ด์œ :
    • Web Dev ์˜ˆ์ œ์—์„œ๋Š” <dialog> ์š”์†Œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ชจ๋‹ฌ ๋‹ค์ด์–ผ๋กœ๊ทธ๋ฅผ ๊ตฌํ˜„ํ•˜๊ณ , <form>์„ ์‚ฌ์šฉํ•˜์—ฌ ๋‹ค์ด์–ผ๋กœ๊ทธ ๋‚ด๋ถ€์˜ ์ฝ˜ํ…์ธ ๋ฅผ ๊ฐ์Œˆ. ์ด๋Š” HTML ํ‘œ์ค€์— ๋งž๋Š” ์˜๋ฏธ๋ก ์ ์ธ ๋งˆํฌ์—… ๊ตฌ์กฐ์ž„
    • MUI์™€ Ant Design์€ <div> ์š”์†Œ์™€ ARIA ์†์„ฑ์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ชจ๋‹ฌ ๋‹ค์ด์–ผ๋กœ๊ทธ๋ฅผ ๊ตฌํ˜„ํ•˜๋ฉฐ, ๊ฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ๋””์ž์ธ ์‹œ์Šคํ…œ์— ๋งž๊ฒŒ ํด๋ž˜์Šค ์ด๋ฆ„์„ ์‚ฌ์šฉํ•จ

๐Ÿ’ก UI ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋น„๊ต ๋ถ„์„

  • MUI:
    • MuiModal-root๋กœ ๋ชจ๋‹ฌ ์ปจํ…Œ์ด๋„ˆ๋ฅผ ํ‘œํ˜„ํ•˜๊ณ , MuiBackdrop-root๋กœ ๋ชจ๋‹ฌ ๋’ค์˜ ๋ฐฑ๋“œ๋กญ์„ ๊ตฌํ˜„ํ•จ
    • MuiDialog-container๋กœ ๋‹ค์ด์–ผ๋กœ๊ทธ ์ปจํ…Œ์ด๋„ˆ๋ฅผ ํ‘œํ˜„ํ•˜๊ณ , aria-labelledby์™€ aria-describedby๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ ‘๊ทผ์„ฑ์„ ๊ณ ๋ คํ•จ
    • MuiDialogTitle-root, MuiDialogContent-root, MuiDialogActions-root๋กœ ๋‹ค์ด์–ผ๋กœ๊ทธ์˜ ๊ฐ ์„น์…˜์„ ๊ตฌ๋ถ„ํ•จ
  • Ant Design:
    • ant-modal-root๋กœ ๋ชจ๋‹ฌ ์ปจํ…Œ์ด๋„ˆ๋ฅผ ํ‘œํ˜„ํ•˜๊ณ , ant-modal-mask๋กœ ๋ชจ๋‹ฌ ๋’ค์˜ ๋ฐฑ๋“œ๋กญ์„ ๊ตฌํ˜„ํ•จ
    • ant-modal-wrap์œผ๋กœ ๋‹ค์ด์–ผ๋กœ๊ทธ ๋ž˜ํผ๋ฅผ ํ‘œํ˜„ํ•˜๊ณ , aria-labelledby์™€ aria-modal์„ ์‚ฌ์šฉํ•˜์—ฌ ์ ‘๊ทผ์„ฑ์„ ๊ณ ๋ คํ•จ
    • ant-modal-header, ant-modal-body, ant-modal-footer๋กœ ๋‹ค์ด์–ผ๋กœ๊ทธ์˜ ๊ฐ ์„น์…˜์„ ๊ตฌ๋ถ„ํ•จ

โš™๏ธ ๊ตฌํ˜„ ๋ฐ ์„ค๊ณ„ ๊ณ ๋ ค์‚ฌํ•ญ

  • ์ฃผ์š” ์„ค๊ณ„ ๊ณ ๋ ค์‚ฌํ•ญ:
    • ๋ชจ๋‹ฌ ๋‹ค์ด์–ผ๋กœ๊ทธ๋Š” ํ˜„์žฌ ํŽ˜์ด์ง€ ์œ„์— ๊ฒน์ณ ํ‘œ์‹œ๋˜๋ฏ€๋กœ, ์ ์ ˆํ•œ ๋ ˆ์ด์–ด ๊ด€๋ฆฌ๊ฐ€ ํ•„์š”ํ•จ
    • ๋‹ค์ด์–ผ๋กœ๊ทธ๊ฐ€ ์—ด๋ ค ์žˆ๋Š” ๋™์•ˆ์—๋Š” ๋‹ค์ด์–ผ๋กœ๊ทธ ์™ธ๋ถ€์˜ ์ฝ˜ํ…์ธ ๋ฅผ ๋น„ํ™œ์„ฑํ™”ํ•˜์—ฌ ์ ‘๊ทผํ•  ์ˆ˜ ์—†๋„๋ก ํ•ด์•ผ ํ•จ
    • ํ‚ค๋ณด๋“œ ํฌ์ปค์Šค ๊ด€๋ฆฌ๋ฅผ ํ†ตํ•ด ๋‹ค์ด์–ผ๋กœ๊ทธ ๋‚ด๋ถ€์—์„œ๋งŒ ํƒ์ƒ‰ํ•  ์ˆ˜ ์žˆ๋„๋ก ์ œํ•œํ•ด์•ผ ํ•จ
    • ์ ์ ˆํ•œ ARIA ์†์„ฑ์„ ์‚ฌ์šฉํ•˜์—ฌ ๋‹ค์ด์–ผ๋กœ๊ทธ์˜ ์—ญํ• ๊ณผ ์ƒํƒœ๋ฅผ ๋ช…ํ™•ํžˆ ์ „๋‹ฌํ•ด์•ผ ํ•จ
    • Web Dev ์•„ํ‹ฐํด์—์„œ ์ œ์‹œํ•œ ๋ฐ”์™€ ๊ฐ™์ด, <dialog> ์š”์†Œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ชจ๋‹ฌ ๋‹ค์ด์–ผ๋กœ๊ทธ๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Œ
  • ์ฝ”๋“œ ์˜ˆ์‹œ:
<button type="button" onclick="openDialog('dialog1', this)">Open Dialog</button>

<dialog id="dialog1" aria-labelledby="dialog1_label" aria-modal="true">
  <form method="dialog">
    <h2 id="dialog1_label">Dialog Title</h2>
    <div>
      <p>Dialog content goes here.</p>
      <button type="button" onclick="closeDialog(this)">Cancel</button>
      <button type="submit" value="confirm">OK</button>
    </div>
  </form>
</dialog>

๐Ÿ” ํ…Œ์ŠคํŠธ ๋ฐ ๊ฒ€ํ† 

  • ์ ‘๊ทผ์„ฑ ํ…Œ์ŠคํŠธ:
    • ํ‚ค๋ณด๋“œ๋งŒ์œผ๋กœ ๋‹ค์ด์–ผ๋กœ๊ทธ๋ฅผ ์—ด๊ณ  ๋‹ซ์„ ์ˆ˜ ์žˆ๋Š”์ง€ ํ™•์ธ
    • ๋‹ค์ด์–ผ๋กœ๊ทธ๊ฐ€ ์—ด๋ ค ์žˆ์„ ๋•Œ Tab๊ณผ Shift+Tab์œผ๋กœ ๋‹ค์ด์–ผ๋กœ๊ทธ ๋‚ด๋ถ€ ์š”์†Œ๋งŒ ํƒ์ƒ‰ํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ํ™•์ธ
    • ๋‹ค์ด์–ผ๋กœ๊ทธ๊ฐ€ ์—ด๋ ค ์žˆ์„ ๋•Œ Escape ํ‚ค๋ฅผ ๋ˆ„๋ฅด๋ฉด ๋‹ค์ด์–ผ๋กœ๊ทธ๊ฐ€ ๋‹ซํžˆ๋Š”์ง€ ํ™•์ธ
    • ์Šคํฌ๋ฆฐ ๋ฆฌ๋”๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋‹ค์ด์–ผ๋กœ๊ทธ์˜ ์—ญํ• ๊ณผ ๋‚ด์šฉ์ด ์ •ํ™•ํžˆ ์ „๋‹ฌ๋˜๋Š”์ง€ ํ™•์ธ
  • ๋ธŒ๋ผ์šฐ์ € ํ˜ธํ™˜์„ฑ ํ…Œ์ŠคํŠธ:
    • <dialog> ์š”์†Œ์˜ ๋ธŒ๋ผ์šฐ์ € ์ง€์› ๋ฒ”์œ„ ํ™•์ธ ๋ฐ ํด๋ฆฌํ•„ ์ ์šฉ ๊ณ ๋ ค
    • ๋‹ค์–‘ํ•œ ๋ธŒ๋ผ์šฐ์ €์™€ ๋ฒ„์ „์—์„œ ๋ชจ๋‹ฌ ๋‹ค์ด์–ผ๋กœ๊ทธ๊ฐ€ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ๋™์ž‘ํ•˜๋Š”์ง€ ํ™•์ธ
  • ์‚ฌ์šฉ์ž ํ”ผ๋“œ๋ฐฑ:
    • ๋ชจ๋‹ฌ ๋‹ค์ด์–ผ๋กœ๊ทธ ์‚ฌ์šฉ ์‹œ ์ ‘๊ทผ์„ฑ๊ณผ ์‚ฌ์šฉ์„ฑ์— ๋Œ€ํ•œ ์‚ฌ์šฉ์ž ์˜๊ฒฌ์„ ์ˆ˜๋ ดํ•˜๊ณ  ๊ฐœ์„ ์‚ฌํ•ญ์„ ๋ฐ˜์˜

๐Ÿ“Œ ์ถ”๊ฐ€ ์ฐธ๊ณ  ์‚ฌํ•ญ

  • ๊ด€๋ จ ๋ฌธ์„œ:
  • ์˜๊ฒฌ ๋ฐ ์ œ์•ˆ:
    • <dialog> ์š”์†Œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ชจ๋‹ฌ ๋‹ค์ด์–ผ๋กœ๊ทธ๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๊ฒƒ์ด ์˜๋ฏธ๋ก ์ ์ด๊ณ  ์ ‘๊ทผ์„ฑ ์ธก๋ฉด์—์„œ ์œ ๋ฆฌํ•จ
    • ๋ชจ๋‹ฌ ๋‹ค์ด์–ผ๋กœ๊ทธ์˜ ํฌ๊ธฐ์™€ ์œ„์น˜๋ฅผ ์œ ์—ฐํ•˜๊ฒŒ ์กฐ์ •ํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ตฌํ˜„ํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Œ
    • ๋ชจ๋ฐ”์ผ ๊ธฐ๊ธฐ์—์„œ์˜ ์‚ฌ์šฉ์„ฑ์„ ๊ณ ๋ คํ•˜์—ฌ ๋ฐ˜์‘ํ˜• ๋””์ž์ธ์„ ์ ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Œ

๐ŸŽฏ ์—ญํ• , ์†์„ฑ, ์ƒํƒœ ๋ฐ ํƒœ๊ทธ ์†์„ฑ

  • Role, Attribute, State:
    • Role: role="dialog"๋กœ ์š”์†Œ๊ฐ€ ๋‹ค์ด์–ผ๋กœ๊ทธ์ž„์„ ๋ช…์‹œ
    • Attribute: aria-modal="true"๋กœ ๋‹ค์ด์–ผ๋กœ๊ทธ๊ฐ€ ๋ชจ๋‹ฌ์ž„์„ ๋ช…์‹œ, aria-labelledby์™€ aria-describedby๋กœ ๋‹ค์ด์–ผ๋กœ๊ทธ์˜ ์ œ๋ชฉ๊ณผ ์„ค๋ช…์„ ์ฐธ์กฐ
    • State: open ์†์„ฑ์œผ๋กœ ๋‹ค์ด์–ผ๋กœ๊ทธ์˜ ์—ด๋ฆผ/๋‹ซํž˜ ์ƒํƒœ๋ฅผ ๋ช…์‹œ
  • JavaScript ๋ฐ CSS ์‚ฌ์šฉ ์—ฌ๋ถ€:
    • JavaScript ์‚ฌ์šฉ ์—ฌ๋ถ€: ๋‹ค์ด์–ผ๋กœ๊ทธ๋ฅผ ์—ด๊ณ  ๋‹ซ๋Š” ๊ธฐ๋Šฅ, ํฌ์ปค์Šค ๊ด€๋ฆฌ, ํ‚ค๋ณด๋“œ ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ ๋“ฑ์„ ์œ„ํ•ด JavaScript ์‚ฌ์šฉ
    • CSS ์ ์šฉ ์‚ฌํ•ญ: ๋‹ค์ด์–ผ๋กœ๊ทธ์˜ ๋ ˆ์ด์•„์›ƒ, ์Šคํƒ€์ผ, ์• ๋‹ˆ๋ฉ”์ด์…˜ ๋“ฑ์„ ์œ„ํ•ด CSS ์‚ฌ์šฉ

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions