본문 바로가기

개발/Javascript

[Javascript] .js파일끼리 통신하는 법 - CustomEvent

 

프로젝트를 진행하면서 Front 개발 중 파일끼리 데이터를 통신하고 싶었는데, 방법을 모색하다 Event를 활용한 통신을 정리해보고자 한다.

 


dispatchEvent 메서드?

우선 dispatchEvent 메서드는 Javascript 안에서 사용하는 Event 객체를 발송하는 메서드로 이를 활용하여 이벤트를 발생시킬 수 있고, CustomEvent 객체를 활용하여 사용자 지정 이벤트를 사용할 수 있게 해주는 메서드이다.

주로 DOM 요소에서 발생하는 이벤트를 시뮬레이션하거나 사용자 지정 이벤트를 발생시키는 데 사용된다.


 

예를 들어 우리가 버튼을 클릭하는 click 이벤트, selectBox의 내용을 바꾸는 change 이벤트 등 DOM에서 많이 쓰이는 이벤트들을 강제로 전송하여 조작할 수 있다.

 

<button id="button">Click me</button>
const button = document.querySelector('#button');

button.addEventListener('click', function() {
    alert('Button was clicked!');
});

button.dispatchEvent(new Event('click'));

 

위의 코드를 실행해 보면 어떤 결과가 나올까?

1. 우선 button 변수에 해당 버튼 DOM을 할당한다.

2. addEventListener를 사용하여 버튼 클릭 시 알림 창이 띄워지게 설정한다.

3. dispatchEvent 안에 Event 객체 (타입은 click)를 추가해서 클릭 이벤트를 강제로 발생시킨다.

 

단순하게 클릭을 바로 호출하는 메서드를 사용하여 실행할 수 있지만 우리는 CustomEvent까지 알아야하기 때문에 저렇게 발동시켜 보자.

 

그리고 이를 활용하여 CustomEvent를 알아보자.

 


CustomEvent란?

CustomEvent는 브라우저에 기본으로 내장되어 있는 click, change, scroll와 같은 이벤트들과는 달리 개발자가 직접 이벤트를 만들 수 있게끔 도와주는 객체이다.


 

알아보기 전 CustomEvent의 인터페이스를 살펴보자.

 

interface CustomEvent<T = any> extends Event {
    /**
     * Returns any custom data event was created with. Typically used for synthetic events.
     *
     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/CustomEvent/detail)
     */
    readonly detail: T;
    /**
     * @deprecated
     *
     * [MDN Reference](https://developer.mozilla.org/docs/Web/API/CustomEvent/initCustomEvent)
     */
    initCustomEvent(type: string, bubbles?: boolean, cancelable?: boolean, detail?: T): void;
}

 

Event 객체를 상속하고 있고, 커스텀 데이터를 넣을 수 있는 detail 변수와 이벤트를 초기화하는 initCustomEvent 메서드가 있다. (initCustomEvent는 구형 브라우저를 고려해서 만들어졌기 때문에 생성자에서 초기화하는 것을 권장함)

dispatchEvent의 매개변수로는 Event 객체를 넣어야하기 때문에 CustomEvent 객체도 가능한 것 같다.

그럼 바로 예시 코드를 살펴보자.

 

<div id="myDiv">Hello World</div>
let customEvent = new CustomEvent('myCustomEvent', {
    detail: { info: '커스텀 이벤트 발동' },
    bubbles: true,
    cancelable: true
});

document.getElementById('myDiv').addEventListener('myCustomEvent', function(e) {
    alert(`내용 : ${e.detail.info}`);
});

document.getElementById('myDiv').dispatchEvent(customEvent);

 

커스텀 이벤트를 생성할 때 총 3개의 매개변수가 나오는데 각각 알아보자.

 

1. detail

detail 속성은 CustomEvent를 생성할 때 추가할 수 있는 사용자 정의 데이터를 포함한다. detail 안에 데이터를 넣으면 이벤트 핸들러에서 데이터들을 주고받을 수 있다.

2. bubbles 

bubbles 속성은 이벤트가 버블링될지 여부를 지정한다. 버블링에 대해서 간단히 말하자면 이벤트가 발생한 요소에서 시작해서 상위 요소로 전파되는 과정이라고 생각하면 된다. 기본 값은 false 지만, 버블링을 사용해서 상위로 전파시키고 싶다면 true로 설정하면 된다.

3. cancelable

cancelable 속성은 이벤트의 기본 동작을 취소할 수 있을지 여부를 지정한다. 기본 값은 false이고, 이벤트 핸들러에서 e.preventDefault를 호출하여  동작을 취소시키고 싶다면 true로 설정하면 된다.

 


.js 파일끼리 CustomEvent를 사용해서 통신하기

여기까지 와서 눈치채겠지만 CustomEvent를 활용하여 document에 이벤트를 발생시키고 다른 파일에서 이벤트를 감지하면 된다. 

 

아래의 코드를 통해 알아보자.

 

이벤트를 발생시킬 common.js

 

const { user, token, exp } = data;

document.dispatchEvent(new CustomEvent('getToken', {
    detail: { user, token, exp }
}));

return data;

 

위의 data는 fetch로 사용자의 정보를 가져온 데이터이다. 해당 값들 중 user, token, exp 값을 getToken이라는 CustomEvent로 전송하기만 하면 된다.

 

이벤트를 받을 index.js

 

document.addEventListener('getToken', (e) => {
    let user = e.detail.user;
    render(user);
})

 

위의 코드는 getToken이라는 CustomEvent를 캐치하여 user의 데이터를 render 함수에 매개변수로 넣는 코드다.

이렇게 하게 될 경우 common.js에서 조회한 값을 굳이 ajax로 request를 또 보낼 필요가 없어서 속도 향상에 도움이 된다.

하지만 중요한 것은 각 페이지의 로드시간인데, 이를 잘 지키지 않으면 동작이 원활하지 않을 수 있다.

 

예를 들어 common.js가 index.js보다 먼저 실행이 됐다고 가정하자, 그러면 index.js에 있는 이벤트 리스너가 생성되기도 전에 이벤트를 dispatch 하기 때문에 아무리 기다려도 이벤트를 받을 수 없다.

그렇다면 어떻게 해결할 수 있을까?

 

window.addEventListener('load', () => {
    const data = fetch() //생략
    const { user, token, exp } = data;

    document.dispatchEvent(new CustomEvent('getToken', {
        detail: { user, token, exp }
    }));
})

 

답은 생각보다 간단한데, 바로 BOM에 load 이벤트가 발동될 때 실행시키는 것이다. 

이렇게 되면 js 파일들을 전부 조회하고 getToken 이벤트를 실행하기 때문에 다른 파일들이 전부 이벤트를 실행할 수 있게 된다.

여기에 HTTP/2까지 적용을 한다면 속도가 좀 더 개선이 될 것 같다.


Reference

MDN - CustomEvent

MDN - EventTarget.dispatchEvent

 

'개발 > Javascript' 카테고리의 다른 글

[Javascript] TDZ(Temporal Dead Zone)이란?  (0) 2024.08.07
[Javascript] 호이스팅(Hoisting) 개념정리  (0) 2024.08.05