javascript 이벤트 핸들러와 메모리/성능 최적화 방법

2024. 3. 26. 02:00Web Frontend

아래는 claude3에게 물어본 결과임.

 

Q. button.addEventListener('click', function() { handleClick(); }); 코드와 button.addEventListener('click', handleClick); 코드의 차이점을 모르겠어. 단계적으로 설명해 줘.

 

네, 두 코드의 차이점을 단계적으로 설명드리겠습니다.

**1단계: 익명 함수 사용**
```javascript
button.addEventListener('click', function() {
  handleClick();
});
```

위 코드에서는 `addEventListener`의 두 번째 인자로 익명 함수를 전달하고 있습니다. 이렇게 하면 클릭 이벤트가 발생할 때마다 새로운 함수 객체가 생성됩니다. 

**2단계: 기명 함수 직접 전달**
```javascript
function handleClick() {
  // 클릭 이벤트 처리 로직
  console.log('Button clicked');
}

button.addEventListener('click', handleClick);
```

위 코드에서는 `addEventListener`의 두 번째 인자로 기명 함수 `handleClick`을 직접 전달하고 있습니다. 이렇게 하면 이벤트 리스너 등록 시 한 번만 함수 객체가 생성되고, 이후 이벤트 발생 시에는 기존 함수 객체를 재사용합니다.

**3단계: 메모리 관리 차이**

익명 함수를 사용하는 첫 번째 코드의 경우, 이벤트가 발생할 때마다 새로운 함수 객체를 생성하므로 불필요한 메모리 할당과 해제가 반복적으로 일어납니다. 이는 메모리 누수(memory leak)의 원인이 될 수 있습니다.

반면, 기명 함수를 직접 전달하는 두 번째 코드의 경우, 최초 한 번만 함수 객체가 생성되고 이후에는 해당 객체를 재사용하므로 메모리 관리가 효율적입니다.

**4단계: 가비지 컬렉션 차이**

익명 함수의 경우, 이벤트 핸들러로 등록된 익명 함수 객체는 이벤트 리스너가 제거되기 전까지 가비지 컬렉션의 대상이 되지 않습니다. 반면 기명 함수의 경우, 함수 객체 자체가 전역 객체에 있으므로 가비지 컬렉션의 대상이 되기 쉽습니다.

따라서 메모리 관리 측면에서는 기명 함수를 직접 전달하는 것이 더 효율적입니다. 다만, 이벤트 리스너를 제거하지 않으면 기명 함수도 메모리 누수의 원인이 될 수 있습니다.

요약하면, 익명 함수 사용 시에는 불필요한 메모리 할당과 해제가 반복되고, 기명 함수 직접 전달 시에는 메모리 관리가 효율적입니다. 따라서 성능 최적화를 위해서는 기명 함수를 직접 전달하는 것이 바람직합니다.

 

 

Q2. 기명 함수 직접 이벤트에 전달하고 싶어도, 해당 이벤트에 여러 함수를 쓰고 싶으면 어떡하지?

이벤트 핸들러에 여러 개의 함수를 실행시키고 싶다면 두 가지 방법이 있습니다.

**1. 하나의 함수에서 여러 함수를 호출하는 방법**

```javascript
function handleClick() {
  function1();
  function2();
  function3();
}

function function1() {
  // 함수 1 실행 로직
}

function function2() {
  // 함수 2 실행 로직
}

function function3() {
  // 함수 3 실행 로직
}

const button = document.querySelector('button');
button.addEventListener('click', handleClick);
```

이 방식은 `handleClick` 함수 내에서 다른 함수들을 호출합니다. 이벤트 리스너에는 `handleClick` 함수만 등록되므로 메모리 효율성은 좋지만, 모든 함수가 실행되므로 불필요한 함수 실행으로 인한 성능 저하가 발생할 수 있습니다.

**2. 여러 함수를 이벤트 핸들러로 등록하는 방법**

```javascript
function function1() {
  // 함수 1 실행 로직
}

function function2() {
  // 함수 2 실행 로직
}

function function3() {
  // 함수 3 실행 로직
}

const button = document.querySelector('button');
button.addEventListener('click', function1);
button.addEventListener('click', function2);
button.addEventListener('click', function3);
```

이 방식은 각각의 함수를 이벤트 핸들러로 직접 등록합니다. 이벤트가 발생하면 등록된 모든 함수가 실행됩니다. 이 방법은 필요한 함수만 실행되므로 불필요한 함수 실행으로 인한 성능 저하는 없지만, 이벤트 핸들러 등록 시 메모리 할당이 여러 번 발생하므로 메모리 효율성이 떨어질 수 있습니다.

두 방법 모두 장단점이 있으므로, 상황에 맞게 적절한 방식을 선택하는 것이 좋습니다. 일반적으로 메모리 효율성이 중요하다면 첫 번째 방식을, 성능이 중요하다면 두 번째 방식을 사용하는 것이 좋습니다.

추가적으로, 이벤트 핸들러 내에서 조건문을 사용하여 필요한 함수만 실행하도록 하는 방법도 있습니다. 이렇게 하면 메모리 효율성과 성능 모두를 고려할 수 있습니다.