웹디자인 (HTML,JS)

자바스크립트 UI를 AI로 뚝딱! 비개발자 위한 바이브 코딩 가이드

날으는물고기 2025. 4. 25. 01:31
728x90

웹 UI를 프레임워크 없이 순수 자바스크립트로 구축하는 "Hard Way" 방식은 많은 개발자에게는 도전적인 접근이지만, 성능 최적화와 구조적 명확성을 통해 매우 강력한 대안이 될 수 있습니다.

자바스크립트 뷰를 "Hard Way"로 구축하기란?

이 접근은 React, Vue, Angular 같은 프레임워크 없이 명령형 자바스크립트만으로 UI를 구성하는 방식입니다. 상태 관리, DOM 업데이트, 이벤트 연결 등 모든 과정을 수동으로 처리하며, 다음과 같은 장점이 있습니다.

  • 0 dependencies: 외부 라이브러리 없이 동작
  • 성능 최적화: 최소한의 연산
  • 높은 이식성: 어떤 환경에도 쉽게 포팅 가능
  • 명확한 구조: 함수 기반 구성으로 유지보수 용이

구성 요소 설명

1. 템플릿 정의

HTML 구조는 <template> 요소를 사용하여 작성합니다.

const template = document.createElement('template');
template.innerHTML = `<div>Hello <span id="name">world</span>!</div>`;

2. 클론 생성

DOM에 삽입 가능한 복사본을 생성합니다.

function clone() {
  return document.importNode(template.content, true);
}

3. 뷰 초기화 함수 (init)

뷰 인스턴스를 생성하고 상태 관리, DOM 참조, 이벤트 핸들러 등을 포함합니다.

function init() {
  let frag = clone();
  let nameNode = frag.querySelector('#name');
  let name;

  function setNameNode(value) {
    nameNode.textContent = value;
  }

  function setName(value) {
    if (name !== value) {
      name = value;
      setNameNode(value);
    }
  }

  function update(data = {}) {
    if (data.name) setName(data.name);
    return frag;
  }

  return update;
}

역할별 함수 구성

역할 설명
clone() 템플릿을 복사하여 새로운 DOM 조각을 생성
init() 상태 변수 선언 및 초기화, DOM 요소 참조, 상태 업데이트 함수 정의
setXNode(value) 실제 DOM 요소를 업데이트하는 함수
setX(value) 상태 변경을 감지하고 필요한 경우 DOM을 갱신
update(data) 외부에서 전달받은 데이터를 바탕으로 내부 상태 갱신

데이터 흐름

  • 부모 → 자식: update(data) 함수 호출을 통해 상태 전달
  • 자식 → 부모: 사용자 인터랙션 발생 시 커스텀 이벤트(dispatchEvent)를 사용해 상위 뷰에 전달

실제 사용 예시

const helloUpdate = init();
document.body.appendChild(helloUpdate({ name: 'Jinjong' }));

React 와의 비교

React JS Hard Way
constructor() init()
render() update(data)
this.setState() setX(value)
props update({ ... })
JSX / Virtual DOM <template> + document.importNode

유지보수 포인트

  • XSS 방지: DOM 조작 시 textContent 사용을 원칙으로
  • 명명 규칙: setXNode, setX, updateX와 같이 기능별 함수 분리
  • 재사용성 확보: 서브뷰는 init()을 다시 호출하여 독립적으로 관리
  • 디버깅 용이성: 얕은 콜스택으로 디버깅 간소화

실전 활용 예시

  • 대시보드 구성: 실시간 데이터 업데이트 뷰 작성에 유리
  • IE 호환 필요 시스템: 구형 브라우저 지원 프로젝트에서 유용
  • 보안 시스템 UI: 외부 라이브러리 의존을 제거하여 안정성 확보

추천 학습 순서

  1. DOM API (querySelector, textContent, addEventListener 등)
  2. 템플릿 사용법 (<template>, importNode)
  3. 상태 분리 패턴 (setXNode, setX)
  4. 이벤트 위임 및 커스텀 이벤트 사용
  5. 모듈화 및 뷰 조립 방식

추가적으로 WebComponent 방식이나 TypeScript 기반 구조 확장에 대해서도 고려해볼 수 있습니다.

300x250

아래는, 실제 웹 UI에 가까운 구조프레임워크 없이 순수 자바스크립트로 "탭 UI"를 만드는 예시입니다.

목표: 탭 UI 만들기

탭 버튼을 누르면 각기 다른 콘텐츠가 표시되는 UI를 만들어 봅니다.

[ 탭1 ] [ 탭2 ] [ 탭3 ]

→ "이곳은 탭 1의 내용입니다."

구성 요소

  • 탭 버튼들
  • 탭에 따라 바뀌는 콘텐츠 영역
  • 선택된 탭 표시 및 동작 제어

1️⃣ HTML 구조 (템플릿 정의)

const template = document.createElement('template');

template.innerHTML = `
  <style>
    .tab-button {
      padding: 10px;
      border: 1px solid gray;
      background: white;
      cursor: pointer;
    }
    .tab-button.active {
      background: lightblue;
      font-weight: bold;
    }
    .tab-content {
      padding: 15px;
      border: 1px solid gray;
      margin-top: 10px;
    }
  </style>

  <div>
    <div id="tabs">
      <button class="tab-button" data-tab="1">탭1</button>
      <button class="tab-button" data-tab="2">탭2</button>
      <button class="tab-button" data-tab="3">탭3</button>
    </div>
    <div id="content" class="tab-content">이곳은 탭 1의 내용입니다.</div>
  </div>
`;

✔️ HTML 구조와 함께 <style>로 기본 스타일도 포함되어 있어요.

2️⃣ 클론 함수 작성

템플릿을 실제 DOM 요소로 만들어주는 함수입니다.

function clone() {
  return document.importNode(template.content, true);
}

3️⃣ 초기화 함수

상태 관리, 이벤트 연결, 탭 선택 시 UI 업데이트를 담당합니다.

function init() {
  const frag = clone();
  const buttons = frag.querySelectorAll('.tab-button');
  const content = frag.querySelector('#content');

  let currentTab = '1'; // 초기값: 탭1

  const tabContents = {
    '1': '이곳은 탭 1의 내용입니다.',
    '2': '여기는 탭 2의 내용입니다!',
    '3': '이건 탭 3의 내용이에요.'
  };

  function setActiveTab(tabId) {
    if (tabId === currentTab) return;

    // 1. 모든 버튼에서 active 제거
    buttons.forEach(btn => btn.classList.remove('active'));

    // 2. 선택된 버튼에 active 추가
    const selectedBtn = [...buttons].find(btn => btn.dataset.tab === tabId);
    if (selectedBtn) selectedBtn.classList.add('active');

    // 3. 콘텐츠 변경
    content.textContent = tabContents[tabId];
    currentTab = tabId;
  }

  // 초기 탭 상태 설정
  setActiveTab(currentTab);

  // 이벤트 연결
  buttons.forEach(btn => {
    btn.addEventListener('click', () => {
      setActiveTab(btn.dataset.tab);
    });
  });

  return frag;
}

4️⃣ DOM에 UI 삽입

const view = init();
document.body.appendChild(view);

코드 전체 보기

index.html

<!DOCTYPE html>
<html lang="ko">
<head>
  <meta charset="UTF-8" />
  <title>탭 UI</title>
</head>
<body>
  <script src="tab-ui.js"></script>
</body>
</html>

tab-ui.js

const template = document.createElement('template');

template.innerHTML = `
  <style>
    .tab-button {
      padding: 10px;
      border: 1px solid gray;
      background: white;
      cursor: pointer;
    }
    .tab-button.active {
      background: lightblue;
      font-weight: bold;
    }
    .tab-content {
      padding: 15px;
      border: 1px solid gray;
      margin-top: 10px;
    }
  </style>

  <div>
    <div id="tabs">
      <button class="tab-button" data-tab="1">탭1</button>
      <button class="tab-button" data-tab="2">탭2</button>
      <button class="tab-button" data-tab="3">탭3</button>
    </div>
    <div id="content" class="tab-content">이곳은 탭 1의 내용입니다.</div>
  </div>
`;

function clone() {
  return document.importNode(template.content, true);
}

function init() {
  const frag = clone();
  const buttons = frag.querySelectorAll('.tab-button');
  const content = frag.querySelector('#content');

  let currentTab = '1';

  const tabContents = {
    '1': '이곳은 탭 1의 내용입니다.',
    '2': '여기는 탭 2의 내용입니다!',
    '3': '이건 탭 3의 내용이에요.'
  };

  function setActiveTab(tabId) {
    if (tabId === currentTab) return;

    buttons.forEach(btn => btn.classList.remove('active'));
    const selectedBtn = [...buttons].find(btn => btn.dataset.tab === tabId);
    if (selectedBtn) selectedBtn.classList.add('active');

    content.textContent = tabContents[tabId];
    currentTab = tabId;
  }

  setActiveTab(currentTab);

  buttons.forEach(btn => {
    btn.addEventListener('click', () => {
      setActiveTab(btn.dataset.tab);
    });
  });

  return frag;
}

const view = init();
document.body.appendChild(view);
  • textContent 사용: 사용자 입력이 콘텐츠로 들어가는 경우 반드시 XSS 방지
  • 명확한 클래스 이름 사용: .tab-button, .tab-content처럼 의미 중심의 네이밍
  • 상태값과 DOM 분리: currentTab 변수와 UI 업데이트 분리하여 유지보수 용이

 

이 구조를 기반으로 탭 안에 폼 추가, AJAX로 데이터 로딩, 다크모드 토글, 탭 상태 저장 등 고급 확장도 가능합니다.

 

이번에는 비개발자도 따라할 수 있는 ‘탭 전환 UI’ 웹앱 만들기 여정바이브 코딩에 맞춰 코드를 한 줄도 몰라도,

이 과정을 따라가면 “내가 직접 만든 웹앱”을 구현할 수 있습니다.

1단계. 요즘 AI로 이런 것까지 된다고요?

✅ 해보는 것부터 시작입니다.

Gemini, Claude, Lovable 같은 생성형 AI 도구에 "웹 UI를 만들어줘"라고 말해보세요.

예시 프롬프트
“HTML과 JavaScript만으로 탭 UI를 만들어줘. 탭 버튼 3개를 누르면 내용이 바뀌는 구조로 부탁해.”

그러면 코드를 ‘뿅’ 하고 만들어줄 거예요. 이게 바로 바이브 코딩의 시작입니다.

코딩을 몰라도 “프롬프트 한 줄로 웹앱을 만들 수 있는 시대”라는 걸 직접 체험해보세요.

🔍 요즘 어떤 AI들이 있나요?

도구 특징
Gemini (구글) 무료 사용 가능, Canvas 모드에서 UI 바로 확인
Claude (Anthropic) 긴 문서 처리에 탁월
Lovable PRD 넣으면 바로 앱으로, 수정은 클릭만으로
Cursor 코딩 에이전트지만 초보자는 나중에 접근 추천

2단계. 내가 만들고 싶은 걸 정확히 정의하기

🤔 내가 풀고 싶은 문제는?

  • 사용자가 버튼을 누르면 다른 내용을 보여주는 UI를 만들고 싶다.
  • 고객이 메뉴를 쉽게 전환하면서 정보를 확인할 수 있는 페이지가 필요하다.

✍️ 이렇게 질문해보세요.

  • 누가 쓸 건가요? → 홈페이지 사용자
  • 어떤 상황에서 필요한가요? → 정보를 메뉴 탭 형태로 나누어 보고 싶을 때
  • 기존에는 어떻게 하고 있나요? → 한 페이지에 내용이 모두 있어서 복잡하게 느껴짐
  • 더 나은 방법은? → 탭 UI로 구분해서 깔끔하게 정리된 정보를 제공

📄 LLM에게 이렇게 말해보세요. (PRD 작성)

“사용자가 버튼을 눌러서 각기 다른 콘텐츠를 볼 수 있는 간단한 탭 UI를 만들고 싶습니다. HTML, CSS, JavaScript만 사용할 거고, 탭은 3개, 내용은 탭1은 소개, 탭2는 제품, 탭3은 연락처로 보여줘.”

3단계. 눈으로 결과를 직접 확인하며 고치기

👀 프리뷰 가능한 도구에서 실험해봐요.

추천 도구: Gemini Canvas 또는 Lovable
Gemini는 프롬프트 입력 후 바로 결과를 브라우저에서 미리 볼 수 있고,

Lovable은 클릭해서 텍스트나 색상을 직접 수정할 수 있어요.

💬 수정하고 싶은 부분은 이렇게 말해요.

  • “버튼을 가운데 정렬해주세요.”
  • “버튼 색깔을 초록색으로 바꿔주세요.”
  • “탭1의 내용을 '회사 소개'로 수정해주세요.”

✨ 이렇게 말해도 됩니다.

“탭 UI에 간단한 스타일을 추가해서 보기 좋게 만들어줘. 버튼에 간격을 주고, 선택된 탭은 파란색으로 표시되게 해줘.”

4단계. AI에게 정확하게 부탁하는 법 배우기 (프롬프팅 기술)

📦 구조를 설명하면서 요청하면 더 좋아요

“HTML은 탭 버튼 영역과 콘텐츠 영역으로 나눠주세요. 각 탭 버튼은 data-tab 속성으로 구분하고, JavaScript로 해당 콘텐츠를 보여줄 수 있도록 해주세요.”

🗂️ 예시 결과 구조

<div class="tabs">
  <button data-tab="1">소개</button>
  <button data-tab="2">제품</button>
  <button data-tab="3">연락처</button>
</div>
<div class="content">여기에 내용이 나옵니다.</div>

이런 구조를 AI가 만들어주고, 탭을 누르면 내용이 바뀌는 코드를 자동으로 짜줍니다.

5단계. 마무리 다듬기 & 오류 고치기

🐞 혹시 이런 문제가 생겼나요?

  • 탭을 눌렀는데 아무 반응이 없음
  • 내용이 바뀌지 않음
  • 스타일이 적용되지 않음

 

AI에게 이렇게 피드백 해주세요.

“버튼을 눌렀는데 내용이 바뀌지 않습니다. JavaScript 코드를 점검해주세요.”
“선택된 버튼에만 스타일이 적용되도록 수정해주세요.”

📦 완성 후 저장 & 공유

  • Gemini: 코드 복사해서 CodePen에 붙여넣기 → 바로 실행됨
  • Lovable: 링크 공유 기능으로 친구에게 앱 보여주기
  • Cursor/Replit: 코드를 저장해서 이어서 작업할 수 있음

마무리: 나는 지금 진짜 웹앱을 만든 거예요

여러분이 만든 건 단순한 코드가 아닙니다.
AI를 활용해 직접 아이디어를 구현한 ‘작은 웹앱’이자 프로덕트입니다.

이제부터는 이렇게 만들어볼 수 있어요.

  • 💬 “나만의 Q&A 챗봇”
  • 🧾 “작업 요청 폼 앱”
  • 📅 “일정 관리 미니 캘린더”
  • 🎲 “랜덤 추첨기”

팁: 내가 만든 걸 계속 발전시키려면?

  • 같은 구조로 다크모드 추가 도전해보기
  • 콘텐츠 영역을 카드 형태 UI로 바꾸기
  • 버튼 대신 탭 메뉴 아이콘으로 바꿔보기
  • 외부 데이터 연동해서 실시간 정보 띄워보기

✨ 다음 스텝이 궁금한가요?

다음에는 이 탭 UI를 확장해서 "상품 리스트 + 상세 보기"로 연결해보거나,
"피드백 폼 제출 후 메시지 보여주기" 같은 기능도 추가해볼 수 있어요.

자신감이 생기셨다면 Lovable, Gemini, Cursor 등 도구별로 따라하는 영상/프롬프트도 시도해볼 수 있습니다.

 

여러분도 이젠 메이커입니다.
직접 만든 웹앱을 친구들에게 보여주고, 자신만의 도구를 만들어보세요.
AI 시대, 우리는 코드를 몰라도 ‘코딩’할 수 있습니다.

728x90
그리드형(광고전용)