[Part 6] Web Components — Custom Events와 컴포넌트 통신
CustomEvent로 부모에게 데이터 전달하기. composed: true로 Shadow DOM 경계 넘기, EventBus 패턴, 부모→자식 메서드 직접 호출 패턴까지 다룹니다.
[Part 6] Web Components — Custom Events와 컴포넌트 통신
Part 5에서는 Web Components의 생명주기인 Lifecycle Callbacks를 알아봤다.
이번 글에서는 컴포넌트끼리 데이터를 주고받는 방법을 알아본다.
컴포넌트는 독립적인 UI 단위다. 하지만 실제 화면에서는 컴포넌트가 혼자만 동작하지 않는다.
버튼을 클릭하면 부모 영역에 알려야 하고, 모달 안에서 닫기 버튼을 누르면 바깥 페이지가 반응해야 한다.
이처럼 컴포넌트 내부에서 발생한 일을 외부로 전달할 때 사용하는 기술이 Custom Events다.
1. 이벤트란?
AD
제휴 광고 · 일부 링크는 수수료를 받을 수 있습니다
이벤트는 브라우저에서 발생하는 특정 동작을 의미한다.
예를 들어 사용자가 버튼을 클릭하면 click 이벤트가 발생한다.
<button>클릭</button>
const button = document.querySelector('button');
button.addEventListener('click', () => {
console.log('버튼 클릭');
});
addEventListener()는 특정 이벤트가 발생했을 때 실행할 함수를 등록하는 메서드다.
위 코드는 버튼을 클릭했을 때 콘솔에 “버튼 클릭”을 출력한다.
2. 컴포넌트 통신이 필요한 이유
컴포넌트 내부에서 발생한 일을 외부에 알려야 하는 경우가 많다.
- 버튼 컴포넌트를 클릭했다는 사실을 부모에게 알려야 한다.
- 모달 컴포넌트가 닫혔다는 사실을 외부에 알려야 한다.
- 상품 카드에서 선택한 상품 정보를 외부로 전달해야 한다.
- 입력 컴포넌트의 값이 변경되었음을 외부에 알려야 한다.
이때 컴포넌트가 외부 코드를 직접 실행하면 의존성이 강해진다.
더 좋은 방식은 컴포넌트가 이벤트를 발생시키고, 외부에서 그 이벤트를 듣는 방식이다.
3. dispatchEvent()
dispatchEvent()는 이벤트를 발생시키는 메서드다.
const event = new Event('hello');
element.dispatchEvent(event);
new Event('hello')는 hello라는 이름의 이벤트를 만든다.
dispatchEvent(event)는 해당 이벤트를 발생시킨다.
외부에서는 addEventListener()로 이벤트를 받을 수 있다.
element.addEventListener('hello', () => {
console.log('hello 이벤트 발생');
});
4. CustomEvent란?
기본 Event는 이벤트 이름만 전달할 수 있다.
하지만 실제 개발에서는 이벤트와 함께 데이터도 전달해야 할 때가 많다.
이때 사용하는 것이 CustomEvent다.
const event = new CustomEvent('user-select', {
detail: {
name: '사용자',
role: '관리자'
}
});
element.dispatchEvent(event);
detail은 이벤트와 함께 전달할 데이터를 담는 공간이다.
외부에서는 다음처럼 데이터를 받을 수 있다.
element.addEventListener('user-select', (event) => {
console.log(event.detail.name);
console.log(event.detail.role);
});
event.detail 안에 컴포넌트가 전달한 데이터가 들어있다.
5. 버튼 컴포넌트에서 이벤트 발생시키기
class MyButton extends HTMLElement {
connectedCallback() {
this.innerHTML = `
<button>클릭</button>
`;
const button = this.querySelector('button');
button.addEventListener('click', () => {
const event = new CustomEvent('button-click');
this.dispatchEvent(event);
});
}
}
customElements.define('my-button', MyButton);
HTML에서는 다음처럼 사용한다.
<my-button></my-button>
외부에서는 이벤트를 받을 수 있다.
const myButton = document.querySelector('my-button');
myButton.addEventListener('button-click', () => {
console.log('커스텀 버튼 클릭');
});
코드 리뷰
this.querySelector('button')은 커스텀 요소 내부의 button을 선택한다.
button.addEventListener('click', ...)은 실제 button 클릭을 감지한다.
new CustomEvent('button-click')은 button-click이라는 커스텀 이벤트를 만든다.
this.dispatchEvent(event)는 <my-button> 요소에서 이벤트를 발생시킨다.
6. 이벤트와 함께 데이터 전달하기
상품 카드에서 선택한 상품 정보를 외부에 전달한다고 해보자.
class ProductCard extends HTMLElement {
connectedCallback() {
const name = this.getAttribute('name');
const price = this.getAttribute('price');
this.innerHTML = `
<article>
<h3>${name}</h3>
<p>${price}원</p>
<button>선택</button>
</article>
`;
const button = this.querySelector('button');
button.addEventListener('click', () => {
const event = new CustomEvent('product-select', {
detail: {
name: name,
price: price
}
});
this.dispatchEvent(event);
});
}
}
customElements.define('product-card', ProductCard);
HTML에서는 다음처럼 사용한다.
<product-card name="키보드" price="50000"></product-card>
외부에서는 선택된 상품 정보를 받을 수 있다.
const productCard = document.querySelector('product-card');
productCard.addEventListener('product-select', (event) => {
console.log(event.detail.name);
console.log(event.detail.price);
});
7. bubbles 옵션
bubbles는 이벤트가 부모 요소로 전파될지 결정하는 옵션이다.
const event = new CustomEvent('product-select', {
bubbles: true,
detail: {
name: '키보드'
}
});
bubbles: true를 설정하면 이벤트가 부모 요소로 올라간다.
이것을 이벤트 버블링이라고 한다.
8. 이벤트 버블링이 필요한 이유
여러 상품 카드가 있다고 해보자.
<div id="product-list">
<product-card name="키보드"></product-card>
<product-card name="마우스"></product-card>
<product-card name="모니터"></product-card>
</div>
각각의 product-card에 이벤트를 따로 등록할 수도 있다.
하지만 부모인 product-list에서 한 번에 처리하면 더 편하다.
const productList = document.querySelector('#product-list');
productList.addEventListener('product-select', (event) => {
console.log(event.detail.name);
});
이 방식이 가능하려면 product-card에서 발생한 이벤트가 부모 요소까지 올라와야 한다.
그래서 bubbles: true가 필요하다.
9. composed 옵션
Shadow DOM을 사용하는 경우에는 composed 옵션도 중요하다.
Shadow DOM 내부에서 발생한 이벤트는 기본적으로 Shadow DOM 경계를 넘지 못할 수 있다.
외부로 이벤트를 전달하려면 composed: true를 사용한다.
const event = new CustomEvent('button-click', {
bubbles: true,
composed: true,
detail: {
message: '버튼 클릭'
}
});
bubbles: true는 이벤트가 부모로 올라가게 한다.
composed: true는 이벤트가 Shadow DOM 경계를 넘을 수 있게 한다.
10. 실무에서 자주 쓰는 이벤트 패턴
this.dispatchEvent(
new CustomEvent('value-change', {
bubbles: true,
composed: true,
detail: {
value: this.value
}
})
);
이 패턴은 Web Components에서 자주 사용된다.
value-change는 값이 변경되었음을 알리는 이벤트 이름이다.
detail.value에는 변경된 값이 들어간다.
11. 이벤트 이름 규칙
Custom Event 이름은 자유롭게 만들 수 있지만 일관된 규칙을 정하는 것이 좋다.
보통 소문자와 하이픈을 사용한다.
button-click
product-select
modal-close
form-submit
value-change
이런 방식은 HTML 이벤트 이름처럼 읽기 쉽고 관리하기도 편하다.
12. 정리
AD
제휴 광고 · 일부 링크는 수수료를 받을 수 있습니다
Custom Events는 Web Components에서 컴포넌트가 외부와 통신하는 핵심 방법이다.
- 이벤트는 브라우저에서 발생하는 특정 동작이다.
dispatchEvent()로 이벤트를 발생시킨다.addEventListener()로 이벤트를 받는다.CustomEvent는 직접 만드는 이벤트다.detail로 데이터를 전달할 수 있다.bubbles: true는 이벤트를 부모로 전파한다.composed: true는 Shadow DOM 경계를 넘어 이벤트를 전달한다.- 이벤트 이름은 소문자와 하이픈 조합을 추천한다.
다음 Part에서는 Web Components의 스타일링 전략을 다룬다.
AD
제휴 광고
일부 링크는 제휴 링크이며, 구매 또는 가입 시 일정 수수료를 받을 수 있습니다.
AD
'Web components' 카테고리의 다른 글
전체보기- [Part 6] Web Components — Custom Events와 컴포넌트 통신 현재 글
- [Part 7] Web Components — 고급 스타일링 전략 2026.05.13
- [Part 8] Web Components — Form 연동과 ElementInternals 2026.05.20
- [Part 9] Web Components — static template 패턴 & 메모리 최적화 2026.05.27
- [Part 10] Web Components — Shadow DOM 있는 것 vs 없는 것 2026.06.03
- [Part 11] Web Components — Props & State 패턴 + 네이밍 규칙 2026.06.04










