๊ด€๋ฆฌ ๋ฉ”๋‰ด

UI Code Lab

์ง„ํ–‰ ์ƒํ™ฉ์„ ์‹œ๊ฐํ™”ํ•˜๋Š” ํƒ€์ž„๋ผ์ธ UI ๋งŒ๋“ค๊ธฐ (JavaScript ๊ธฐ๋ฐ˜) ๋ณธ๋ฌธ

์ž๋ฐ”์Šคํฌ๋ฆฝํŠธUI

์ง„ํ–‰ ์ƒํ™ฉ์„ ์‹œ๊ฐํ™”ํ•˜๋Š” ํƒ€์ž„๋ผ์ธ UI ๋งŒ๋“ค๊ธฐ (JavaScript ๊ธฐ๋ฐ˜)

๐Ÿฏ๊ฟ€์ƒ์ด๐Ÿ 2025. 6. 23. 09:29

ํƒ€์ž„๋ผ์ธ UI ๋งŒ๋“ค๊ธฐ

 

 

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

 

๐Ÿ’ก ์ง์ ‘ ํ™œ์šฉํ•œ ์˜ˆ์‹œ๋Š” ํ˜„์žฌ CAD ํŽ˜์ด์ง€์˜ ํ๋ฆ„

STEP 1: CAD ํŒŒ์ผ ์ค€๋น„ >> STEP 2: CAD ํŒŒ์ผ ๊ฒ€ํ†  >>  STEP 3: ์Šน์ธ ๋Œ€๊ธฐ >>  STEP 4: ๋‹ค์šด๋กœ๋“œ ๊ฐ€๋Šฅ

 

STEP 1๊ณผ STEP 2๋Š” ์ด๋ฏธ์ง€๊ฐ€ ์ด๋ฏธ ๋“ฑ๋ก๋œ ์™„๋ฃŒ ๋‹จ๊ณ„๋กœ ๊ฐ™์€ ์ƒ‰์ƒ์œผ๋กœ ๊ทธ๋ฃนํ™”ํ•˜๊ณ , ์Šน์ธ ๋Œ€๊ธฐ ์ƒํƒœ์ธ STEP 3๋Š” ๊ฐ•์กฐ ์• ๋‹ˆ๋ฉ”์ด์…˜์œผ๋กœ ํ‘œํ˜„ํ•ด ์‚ฌ์šฉ์ž์˜ ์ฃผ์˜๋ฅผ ๋Œ ์ˆ˜ ์žˆ๋„๋ก ๊ตฌ์„ฑํ•ด ๋ดค์Šต๋‹ˆ๋‹ค.

 

โœ… ์ฃผ์š” ๊ธฐ๋Šฅ ์š”์•ฝ

  • ์Šคํ…1, ์Šคํ…2๋Š” ๋™์ผํ•œ ์ƒ‰์ƒ์œผ๋กœ ๊ทธ๋ฃนํ™” (์˜ˆ: ์™„๋ฃŒ๋œ ๋‹จ๊ณ„)
  • ํ˜„์žฌ ์ง„ํ–‰ ์ค‘์ธ ์Šคํ…์€ ๊ฐ•์กฐ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ ์šฉ
  • ๋งˆ์šฐ์Šค ์˜ค๋ฒ„ ์‹œ ํˆดํŒ์œผ๋กœ ์ƒ์„ธ ์„ค๋ช… ์ œ๊ณต
  • ๋ฐ˜์‘ํ˜• ๋””์ž์ธ์œผ๋กœ ๋ชจ๋ฐ”์ผ ๋Œ€์‘

 

๐Ÿงฉ HTML ๊ตฌ์กฐ ์˜ˆ์‹œ

 
<div class="timeline">
  <div class="step completed">STEP 1<br><span>CAD ํŒŒ์ผ ์ค€๋น„</span></div>
  <div class="step completed">STEP 2<br><span>CAD ํŒŒ์ผ ๊ฒ€ํ† </span></div>
  <div class="step active">STEP 3<br><span>์Šน์ธ ๋Œ€๊ธฐ</span></div>
  <div class="step">STEP 4<br><span>๋‹ค์šด๋กœ๋“œ ๊ฐ€๋Šฅ</span></div>
</div>

 

๐ŸŽจ CSS ๋””์ž์ธ ํฌ์ธํŠธ

 
.timeline {
  display: flex;
  justify-content: space-between;
  margin: 30px 0;
}
.step {
  flex: 1;
  text-align: center;
  padding: 10px;
  border-bottom: 4px solid #ccc;
  position: relative;
}
.step.completed {
  border-color: #4CAF50; /* ์™„๋ฃŒ๋œ ์Šคํ… ์ƒ‰์ƒ */
  color: #4CAF50;
}
.step.active {
  border-color: #FF9800;
  color: #FF9800;
  animation: pulse 1s infinite;
}
@keyframes pulse {
  0% { opacity: 1; }
  50% { opacity: 0.6; }
  100% { opacity: 1; }
}

 

๐Ÿ’ก ํ™œ์šฉ ์˜ˆ์‹œ: CAD ๋ฐ์ดํ„ฐ ๋‹ค์šด๋กœ๋“œ ํ๋ฆ„

  • STEP 1~2: ์ด๋ฏธ์ง€๊ฐ€ ์ด๋ฏธ ๋“ฑ๋ก๋œ ์ƒํƒœ → ๊ฐ™์€ ์ƒ‰์ƒ์œผ๋กœ ๊ทธ๋ฃน ์ฒ˜๋ฆฌ
  • STEP 3: ์Šน์ธ ๋Œ€๊ธฐ ์ƒํƒœ → ๊ฐ•์กฐ ์• ๋‹ˆ๋ฉ”์ด์…˜
  • STEP 4: ๋‹ค์šด๋กœ๋“œ ๊ฐ€๋Šฅ → ํšŒ์ƒ‰ ์ฒ˜๋ฆฌ

์ด๋Ÿฐ ๋ฐฉ์‹์œผ๋กœ ์‹ค์ œ 3D CAD ๋ฐ์ดํ„ฐ ํŽ˜์ด์ง€์˜ ํ๋ฆ„์„ ์‹œ๊ฐํ™”ํ•˜๋ฉด, ์‚ฌ์šฉ์ž์—๊ฒŒ ํ˜„์žฌ ์œ„์น˜๋ฅผ ๋ช…ํ™•ํžˆ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ์–ด์š”.

 

 

์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋กœ ๊ตฌํ˜„ํ•˜๋Š” ์ธํ„ฐ๋ž™ํ‹ฐ๋ธŒ ํƒ€์ž„๋ผ์ธ UI – CAD ํŽ˜์ด์ง€ ํ๋ฆ„์— ์ ์šฉํ•˜๊ธฐ

๊ธฐ์กด์—๋Š” ์ •์ ์ธ ํƒ€์ž„๋ผ์ธ UI๋ฅผ HTML๊ณผ CSS๋งŒ์œผ๋กœ ๊ตฌ์„ฑํ–ˆ์ง€๋งŒ, ์ด๋ฒˆ์—๋Š” ์‚ฌ์šฉ์ž์˜ ์ƒํ˜ธ์ž‘์šฉ์— ๋”ฐ๋ผ ์ง„ํ–‰ ๋‹จ๊ณ„๋ฅผ ์ œ์–ดํ•˜๊ฑฐ๋‚˜ ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ์ œ์–ดํ•  ์ˆ˜ ์žˆ๋Š” ๋™์  ํƒ€์ž„๋ผ์ธ์œผ๋กœ ํ™•์žฅํ•ด ๋ด…๋‹ˆ๋‹ค.

 

โš™ JavaScript ์ฝ”๋“œ ์˜ˆ์‹œ

// ์ดˆ๊ธฐ ์ƒํƒœ ๊ฐ€์ ธ์˜ค๊ธฐ
let currentStep = parseInt(localStorage.getItem('currentStep')) || 1;
const steps = document.querySelectorAll('.step');

function renderSteps(step) {
  steps.forEach((el, i) => {
    el.classList.remove('completed', 'active');
    if (i < step - 1) el.classList.add('completed'); // ์ด์ „ ๋‹จ๊ณ„
    else if (i === step - 1) el.classList.add('active'); // ํ˜„์žฌ ๋‹จ๊ณ„
  });
}

renderSteps(currentStep);

// ํด๋ฆญ ์ด๋ฒคํŠธ๋กœ ๋‹จ๊ณ„ ์ด๋™
steps.forEach((el, i) => {
  el.addEventListener('click', () => {
    currentStep = i + 1;
    localStorage.setItem('currentStep', currentStep);
    renderSteps(currentStep);
  });
});

 

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

 

๐Ÿ’ก ์ ์šฉ ์•„์ด๋””์–ด: ํด๋ฆญ์œผ๋กœ ๋‹จ๊ณ„ ์ „ํ™˜ + ์ €์žฅ ๊ธฐ๋Šฅ

  1. ์‚ฌ์šฉ์ž๊ฐ€ STEP 1~4 ์ค‘ ํ•˜๋‚˜๋ฅผ ํด๋ฆญํ•˜๋ฉด ํ•ด๋‹น ๋‹จ๊ณ„๋กœ ์ง„ํ–‰ ์ƒํƒœ ์—…๋ฐ์ดํŠธ
  2. ์ง„ํ–‰ ์ƒํƒœ๋Š” localStorage์— ์ €์žฅ๋ผ ๋‹ค์Œ ์ ‘์† ์‹œ์—๋„ ์œ ์ง€
  3. ํŠน์ • ์กฐ๊ฑด(API ์‘๋‹ต ๋“ฑ)์— ๋”ฐ๋ผ ์ž๋™์œผ๋กœ ๋‹จ๊ณ„ ์ „ํ™˜ ๊ฐ€๋Šฅ

 

โœ” ํƒ€์ž„๋ผ์ธ UI์˜ ํ•ต์‹ฌ ๊ธฐ๋Šฅ

  • ์™„๋ฃŒ๋œ ๋‹จ๊ณ„(STEP 1~2)๋Š” ๋™์ผํ•œ ์ƒ‰์ƒ๊ณผ ์•„์ด์ฝ˜์œผ๋กœ ๊ทธ๋ฃน ์ฒ˜๋ฆฌ
  • ์ง„ํ–‰ ์ค‘์ธ ๋‹จ๊ณ„(STEP 3)๋Š” ์ปฌ๋Ÿฌ ๊ฐ•์กฐ + ์• ๋‹ˆ๋ฉ”์ด์…˜ ํšจ๊ณผ
  • ๋Œ€๊ธฐ ์ƒํƒœ(STEP 4)๋Š” ํ๋ฆฐ ์ƒ‰์œผ๋กœ ์‹œ๊ฐ์  ๋Œ€๋น„
  • ๋ฐ˜์‘ํ˜• ๋ ˆ์ด์•„์›ƒ์œผ๋กœ ๋ชจ๋ฐ”์ผ์—์„œ๋„ ์ž˜ ์ž‘๋™
  • ์Šคํฌ๋ฆฐ๋ฆฌ๋” ์‚ฌ์šฉ์ž๋ฅผ ์œ„ํ•œ ์ ‘๊ทผ์„ฑ ์†์„ฑ(aria-label, role) ํฌํ•จ

 

๐ŸŒ ์ ‘๊ทผ์„ฑ์„ ๊ณ ๋ คํ•œ ์„ค๊ณ„ ํฌ์ธํŠธ

 

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

์ด ํƒ€์ž„๋ผ์ธ ์ปดํฌ๋„ŒํŠธ์—๋„ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ ‘๊ทผ์„ฑ ์š”์†Œ๋ฅผ ๋ฐ˜์˜ํ–ˆ์Šต๋‹ˆ๋‹ค:

  • ๊ฐ ๋‹จ๊ณ„์— role="listitem"๊ณผ aria-label ์ ์šฉ: ์Šคํฌ๋ฆฐ๋ฆฌ๋”๊ฐ€ ๊ฐ ์ƒํƒœ๋ฅผ ์ฝ์„ ์ˆ˜ ์žˆ๊ฒŒ ํ•จ
  • ์ƒ‰์ƒ ์™ธ์— โœ”/โณ ๋“ฑ์˜ ํ…์ŠคํŠธ ํ˜น์€ ์•„์ด์ฝ˜์œผ๋กœ ์ƒํƒœ ๊ตฌ๋ถ„
  • ์ถฉ๋ถ„ํ•œ ์ƒ‰ ๋Œ€๋น„ ํ™•๋ณด (WCAG ๊ธฐ์ค€ 4.5:1 ์ด์ƒ)
  • ํ–ฅํ›„์—๋Š” ํ‚ค๋ณด๋“œ ์ ‘๊ทผ์„ฑ(tab ํ‚ค ์ง€์›)๋„ ์ถ”๊ฐ€ํ•  ๊ณ„ํš

 

โœ… ์ ‘๊ทผ์„ฑ ๊ณ ๋ ค HTML ์˜ˆ์ œ

<section aria-labelledby="timeline-heading">
  <h2 id="timeline-heading">์ง„ํ–‰ ๋‹จ๊ณ„ ํƒ€์ž„๋ผ์ธ</h2>
  <ol class="timeline" role="list" aria-label="CAD ํŒŒ์ผ ๋‹ค์šด๋กœ๋“œ ์ง„ํ–‰ ๋‹จ๊ณ„">
    <li class="step completed" role="listitem" aria-label="STEP 1 ์™„๋ฃŒ: CAD ํŒŒ์ผ ์ค€๋น„">
      <span class="visually-hidden">โœ”</span>
      <strong>STEP 1</strong>: CAD ํŒŒ์ผ ์ค€๋น„
    </li>
    <li class="step completed" role="listitem" aria-label="STEP 2 ์™„๋ฃŒ: CAD ํŒŒ์ผ ๊ฒ€ํ† ">
      <span class="visually-hidden">โœ”</span>
      <strong>STEP 2</strong>: CAD ํŒŒ์ผ ๊ฒ€ํ† 
    </li>
    <li class="step active" role="listitem" aria-label="STEP 3 ์ง„ํ–‰์ค‘: ์Šน์ธ ๋Œ€๊ธฐ">
      <span class="visually-hidden">โณ</span>
      <strong>STEP 3</strong>: ์Šน์ธ ๋Œ€๊ธฐ
    </li>
    <li class="step" role="listitem" aria-label="STEP 4 ์˜ˆ์ •: ๋‹ค์šด๋กœ๋“œ ๊ฐ€๋Šฅ">
      <span class="visually-hidden">โฌœ</span>
      <strong>STEP 4</strong>: ๋‹ค์šด๋กœ๋“œ ๊ฐ€๋Šฅ
    </li>
  </ol>
</section>

 

๐ŸŒ ์ฃผ์š” ์ ‘๊ทผ์„ฑ ํฌ์ธํŠธ

  • <ol> ํƒœ๊ทธ ์‚ฌ์šฉ → ๋‹จ๊ณ„์˜ ์ˆœ์„œ๋ฅผ ์˜๋ฏธ์ ์œผ๋กœ ํ‘œํ˜„
  • aria-label, role="list" / listitem" ์†์„ฑ → ์Šคํฌ๋ฆฐ๋ฆฌ๋”๊ฐ€ ๊ฐ ๋‹จ๊ณ„๋ฅผ ์ •ํ™•ํžˆ ์•ˆ๋‚ด
  • ์ƒํƒœ ์•„์ด์ฝ˜(โœ”, โณ, โฌœ) ํ…์ŠคํŠธ ์ˆจ๊น€ ์ฒ˜๋ฆฌ → <span class="visually-hidden">์œผ๋กœ ์‹œ๊ฐ ์žฅ์•  ์‚ฌ์šฉ์ž์—๊ฒŒ ์„ค๋ช… ์ œ๊ณต
  • aria-labelledby๋ฅผ ํ†ตํ•ด ํƒ€์ž„๋ผ์ธ ์ œ๋ชฉ ์—ฐ๊ฒฐ

 

๐Ÿ’ก ์‹œ๊ฐ์ ์œผ๋กœ ์ˆจ๊ธด ํ…์ŠคํŠธ ์ฒ˜๋ฆฌ CSS ์˜ˆ์‹œ

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

 

์ด๋ ‡๊ฒŒ ๊ตฌ์„ฑํ•˜๋ฉด ๋””์ž์ธ์„ ํ•ด์น˜์ง€ ์•Š์œผ๋ฉด์„œ๋„ ๋ณด์กฐ ๊ธฐ์ˆ ์„ ์‚ฌ์šฉํ•˜๋Š” ๋ถ„๋“ค์—๊ฒŒ ์นœ์ ˆํ•œ UI๊ฐ€ ๋ฉ๋‹ˆ๋‹ค. ๐Ÿ˜„

 

๐Ÿง‘‍๐Ÿ’ป ์‹œ๋‚˜๋ฆฌ์˜ค ์ ์šฉ ์˜ˆ (CAD ๋‹ค์šด๋กœ๋“œ ํŽ˜์ด์ง€)

  • ์‚ฌ์šฉ์ž๊ฐ€ STEP 2(CAD ์ด๋ฏธ์ง€ ์—…๋กœ๋“œ)๋ฅผ ์™„๋ฃŒํ•˜๋ฉด ์ž๋™์œผ๋กœ STEP 3์œผ๋กœ ๋„˜์–ด๊ฐ
  • ๊ด€๋ฆฌ์ž ๊ณ„์ •์—์„œ๋Š” ์Šน์ธ ์‹œ ์ž๋™ STEP 4๋กœ ์ „ํ™˜ + ๋‹ค์šด๋กœ๋“œ ๋ฒ„ํŠผ ํ™œ์„ฑํ™”
  • ์ด๋ฏธ์ง€๋ฅผ ์—…๋กœ๋“œํ–ˆ๋Š”์ง€ ์—ฌ๋ถ€๋Š” API ์ƒํƒœ๊ฐ’์„ ํ†ตํ•ด ๊ฐ์ง€ ๊ฐ€๋Šฅ

 

๐Ÿ” ์ถ”๊ฐ€ ๊ธฐ๋Šฅ ์•„์ด๋””์–ด (ํ™•์žฅ ๊ฐ€๋Šฅ)

  • ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ ์„œ๋ฒ„์— ์ƒํƒœ ์—…๋ฐ์ดํŠธ ์š”์ฒญ (fetch ์ด์šฉ)
  • ์ ‘๊ทผ ์ œ์–ด: ๋กœ๊ทธ์ธํ•œ ์‚ฌ์šฉ์ž๋งŒ ํŠน์ • ๋‹จ๊ณ„ ์ง„ํ–‰ ๊ฐ€๋Šฅ
  • ์ƒํƒœ๋ณ„๋กœ ์‹œ๊ฐ„ ๊ธฐ๋ก ์ €์žฅ (์˜ˆ: ์Šน์ธ ๋Œ€๊ธฐ ์‹œ๊ฐ„์ด ์˜ค๋ž˜ ๊ฑธ๋ฆด ๊ฒฝ์šฐ ํ‘œ์‹œ)

 

๐Ÿ“ ๋งˆ๋ฌด๋ฆฌํ•˜๋ฉฐ

์ด์ฒ˜๋Ÿผ ๊ฐ„๋‹จํ•œ ํƒ€์ž„๋ผ์ธ ์ปดํฌ๋„ŒํŠธ ํ•˜๋‚˜๋งŒ์œผ๋กœ๋„ ์‚ฌ์šฉ์ž์˜ ๊ฒฝํ—˜์„ ํ•œ์ธต ๋†’์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํŠนํžˆ CAD์™€ ๊ฐ™์ด ๋‹จ๊ณ„๋ณ„ ํ๋ฆ„์ด ๋ช…ํ™•ํ•œ ์„œ๋น„์Šค๋ผ๋ฉด ๋”์šฑ ์ ํ•ฉํ•˜์ฃ .

๊ทธ๋ฆฌ๊ณ  JavaScript๋ฅผ ๋”ํ•˜๋ฉด ๋‹จ์ˆœํžˆ “๋ณด์—ฌ์ฃผ๋Š” UI”๋ฅผ ๋„˜์–ด์„œ 

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