커스텀 엘리먼트란?
-
정의: 자신만의 HTML 태그와 커스텀 동작을 정의할 수 있게 해주는 웹 컴포넌트 표준의 일부입니다. Shadow DOM, 템플릿과 함께 사용되지만 독립적으로도 활용 가능합니다.
-
구현:
HTMLElement를 확장하는 클래스를 정의하고,customElements.define()메서드를 사용하여 브라우저에 등록합니다. - 생명주기:
connectedCallback(): 엘리먼트가 페이지에 추가될 때 실행됩니다 (Stimulus의connect()와 유사).disconnectedCallback(): 엘리먼트가 페이지에서 제거될 때 실행됩니다.
-
명명 규칙: 기존 HTML 엘리먼트와의 충돌을 방지하기 위해 반드시 하이픈(-)을 포함해야 합니다 (예:
<hello-world>). - 속성 및 변경 감지:
getAttribute(): 일반 HTML 엘리먼트처럼 속성 값을 읽을 수 있습니다.static observedAttributes: 감시할 속성 목록을 정의합니다.attributeChangedCallback():observedAttributes에 등록된 속성 값이 변경될 때 호출됩니다.
is속성:extends옵션을 사용하여 기존 HTML 엘리먼트(예:<button>)를 확장할 수 있으나, Safari 지원 문제로 인해 권장되지 않습니다.
커스텀 엘리먼트 vs. Stimulus
-
유사점:
connectedCallback()과connect()처럼 생명주기 메서드가 유사합니다. - 차이점:
- 요소 찾기: Stimulus는
data-target속성을 사용하지만, 커스텀 엘리먼트는querySelector()와 같은 표준 DOM 메서드를 활용합니다. - 상태 관리: Stimulus는
data-value를 사용하지만, 커스텀 엘리먼트는 속성(attribute)과 프로퍼티(property)를 사용합니다. - 이벤트 처리: Stimulus는
data-action속성을 사용하지만, 커스텀 엘리먼트는addEventListener()를 활용합니다. - 의존성: Stimulus는 프레임워크가 필요하지만, 커스텀 엘리먼트는 브라우저 자체 기능입니다.
- 요소 찾기: Stimulus는
- 활용: Stimulus는 기존 HTML에 동작을 연결하는 데 좋고, 커스텀 엘리먼트는 재사용 가능한 독립적인 컴포넌트를 만들 때 유리합니다.
간단한 카운터 구축
-
구현:
ClickCounter클래스를 정의하고connectedCallback에서count를 초기화하며 클릭 이벤트 리스너를 추가합니다.increment메서드에서count를 증가시키고querySelector를 이용해span태그의 텍스트를 업데이트합니다. -
Rails 연동:
app/javascript/components디렉토리에 파일을 생성하고application.js에서 임포트합니다.config/importmap.rb에 해당 디렉토리를 핀(pin)하여 사용할 수 있도록 설정합니다. -
사용:
<click-counter>태그를 뷰에 추가하여 즉시 동작하는 카운터를 구현합니다.
낙관적(Optimistic) 폼 구축
-
목표: 서버 응답을 기다리지 않고 메시지를 즉시 화면에 추가하고, 이후 서버 응답(Turbo Stream)으로 실제 메시지로 교체합니다.
- HTML 구조:
<optimistic-form>: 커스텀 엘리먼트 래퍼.<form>: 메시지 전송 폼.<template response>: 서버 응답 시 메시지를 렌더링할 HTML 구조를 담는 템플릿.cloneNode(true)로 복사하여 사용합니다.
- JavaScript 구현:
OptimisticForm클래스에서 폼, 템플릿, 타겟 엘리먼트를 연결합니다.#submit(): 폼 유효성 검사 후FormData를 추출하여#render()메서드로 낙관적 UI를 생성하고target에 추가합니다.#render(formData): 템플릿 내용을 복사하고,formData의 값으로data-field속성을 가진 엘리먼트의textContent를 업데이트합니다.#reset():turbo:submit-end이벤트 발생 시 폼을 초기화합니다.
- Rails Turbo Stream 연동:
create.turbo_stream.erb에서turbo_stream.replace "optimistic-message", @message를 사용하여 낙관적 메시지를 실제 데이터베이스 메시지로 교체합니다. 이로써 사용자 경험을 향상시키고 로딩 스피너 없이 즉각적인 피드백을 제공합니다.