Use Cases

A 실전 가이드 to Developing Web Components: Claude Code 활용 가이드

A practical guide to developing web components: Claude Code 활용 with real-world code examples.

Web Components の概要

Web Componentsは、再利用可能なカスタムHTML要素を생성するためのWeb標準技術です。프레임워크に依存せず、どの프로젝트でも使える컴포넌트を作れます。Claude Code를 활용하면 Web Componentsの개발を효율적으로進められます。

Custom Elements の基本

class AppCounter extends HTMLElement {
  private count = 0;
  private shadow: ShadowRoot;

  constructor() {
    super();
    this.shadow = this.attachShadow({ mode: "open" });
  }

  // 監視する属性
  static get observedAttributes() {
    return ["initial", "step"];
  }

  connectedCallback() {
    this.count = parseInt(this.getAttribute("initial") || "0");
    this.render();
  }

  attributeChangedCallback(
    name: string,
    oldValue: string,
    newValue: string
  ) {
    if (name === "initial" && oldValue !== newValue) {
      this.count = parseInt(newValue);
      this.render();
    }
  }

  private get step(): number {
    return parseInt(this.getAttribute("step") || "1");
  }

  private render() {
    this.shadow.innerHTML = `
      <style>
        :host {
          display: inline-flex;
          align-items: center;
          gap: 8px;
          font-family: system-ui, sans-serif;
        }
        button {
          padding: 4px 12px;
          border: 1px solid #ccc;
          border-radius: 4px;
          cursor: pointer;
          font-size: 16px;
        }
        button:hover {
          background-color: #f0f0f0;
        }
        .count {
          font-size: 24px;
          font-weight: bold;
          min-width: 40px;
          text-align: center;
        }
      </style>
      <button id="dec">-</button>
      <span class="count">${this.count}</span>
      <button id="inc">+</button>
    `;

    this.shadow.getElementById("dec")!.addEventListener("click", () => {
      this.count -= this.step;
      this.render();
      this.dispatchEvent(new CustomEvent("change", { detail: this.count }));
    });

    this.shadow.getElementById("inc")!.addEventListener("click", () => {
      this.count += this.step;
      this.render();
      this.dispatchEvent(new CustomEvent("change", { detail: this.count }));
    });
  }
}

customElements.define("app-counter", AppCounter);

使い方:

<app-counter initial="10" step="5"></app-counter>

Lit 프레임워크で효율화

import { LitElement, html, css } from "lit";
import { customElement, property, state } from "lit/decorators.js";

@customElement("app-todo-list")
class AppTodoList extends LitElement {
  static styles = css`
    :host {
      display: block;
      max-width: 400px;
      font-family: system-ui, sans-serif;
    }
    .input-row {
      display: flex;
      gap: 8px;
      margin-bottom: 16px;
    }
    input {
      flex: 1;
      padding: 8px;
      border: 1px solid #ccc;
      border-radius: 4px;
    }
    ul {
      list-style: none;
      padding: 0;
    }
    li {
      display: flex;
      align-items: center;
      gap: 8px;
      padding: 8px 0;
      border-bottom: 1px solid #eee;
    }
    .done {
      text-decoration: line-through;
      color: #999;
    }
  `;

  @state()
  private todos: Array<{ text: string; done: boolean }> = [];

  @state()
  private newTodo = "";

  private addTodo() {
    if (!this.newTodo.trim()) return;
    this.todos = [...this.todos, { text: this.newTodo, done: false }];
    this.newTodo = "";
  }

  private toggleTodo(index: number) {
    this.todos = this.todos.map((todo, i) =>
      i === index ? { ...todo, done: !todo.done } : todo
    );
  }

  render() {
    return html`
      <div class="input-row">
        <input
          .value=${this.newTodo}
          @input=${(e: InputEvent) =>
            (this.newTodo = (e.target as HTMLInputElement).value)}
          @keyup=${(e: KeyboardEvent) => e.key === "Enter" && this.addTodo()}
          placeholder="タスクを入力..."
        />
        <button @click=${this.addTodo}>追加</button>
      </div>
      <ul>
        ${this.todos.map(
          (todo, i) => html`
            <li>
              <input
                type="checkbox"
                .checked=${todo.done}
                @change=${() => this.toggleTodo(i)}
              />
              <span class=${todo.done ? "done" : ""}>${todo.text}</span>
            </li>
          `
        )}
      </ul>
    `;
  }
}

スロット에 의한コンポジション

@customElement("app-card")
class AppCard extends LitElement {
  static styles = css`
    :host {
      display: block;
      border: 1px solid #e2e8f0;
      border-radius: 8px;
      overflow: hidden;
    }
    .header {
      padding: 16px;
      background: #f8fafc;
      border-bottom: 1px solid #e2e8f0;
    }
    .body {
      padding: 16px;
    }
    .footer {
      padding: 16px;
      background: #f8fafc;
      border-top: 1px solid #e2e8f0;
    }
  `;

  render() {
    return html`
      <div class="header">
        <slot name="header"></slot>
      </div>
      <div class="body">
        <slot></slot>
      </div>
      <div class="footer">
        <slot name="footer"></slot>
      </div>
    `;
  }
}
<app-card>
  <h2 slot="header">カードタイトル</h2>
  <p>カードの本文がここに入ります。</p>
  <button slot="footer">アクション</button>
</app-card>

Reactとの통합

import { useRef, useEffect } from "react";

function CounterWrapper({
  initial,
  onChange,
}: {
  initial: number;
  onChange: (value: number) => void;
}) {
  const ref = useRef<HTMLElement>(null);

  useEffect(() => {
    const el = ref.current;
    const handler = (e: Event) => {
      onChange((e as CustomEvent).detail);
    };

    el?.addEventListener("change", handler);
    return () => el?.removeEventListener("change", handler);
  }, [onChange]);

  return <app-counter ref={ref} initial={initial} />;
}

Claude Code로の활용

Web Components개발をClaude Code에依頼する例です。スタイリングにはTailwind CSS활용法、컴포넌트설계の参考로서코드 분할・지연 로딩도 참고하세요.

Litを使ってデザインシステムのコンポーネントを作って。
- app-button, app-input, app-modal, app-toast
- CSS Custom Propertiesでテーマ対応
- TypeScriptで型安全に
- Storybookでドキュメント化して

Web Components의 사양은MDN Web Components를 참고하세요.Claude Code의 상세 정보는공식 문서에서 확인할 수 있습니다.

정리

Web Componentsは프레임워크非依存の再利用可能なUIを구축できる標準技術です。Claude Code를 활용하면 Custom ElementsやLitの구현を효율적으로進められ、既存프로젝트との통합もスムーズに行えます。

#Claude Code #Web Components #Custom Elements #Shadow DOM #Lit