자바스크립트에서 함수 사용과 성능 저하 이슈

목화·2023년 5월 3일
0

조모임을 하다 "자바스크립트에서 함수를 많이 사용하면 성능에 영향을 미치는가?"에 대한 의문이 생겼다. 나도 어디서 무슨 소리를 들었던 것 같은데 기억이 1도 나지 않아 병아리들의 구원자 GPT4 선생에게 도움을 요청했다. (3.5는 안 된다. 4랑 차이가 많이 나더라. 그리고 4에게 니가 한 얘기 다시 논리적으로 검토해 보라고도 시켰다.) 물론 GPT가 한 얘기니까 구라일 수 있다. 그래도 나 같은 병아리에게는 도움이 많이 되니 일단 적어둬 보자.

함수를 많이 사용하는 것 자체가 성능에 큰 영향을 미치지는 않지만, 특정 경우에는 성능에 악영향을 줄 수 있다. 함수 사용에 따른 성능 저하의 예를 들어보자.

1. 불필요한 함수 호출

당연한 소린데, 불필요한 함수를 호출하면 메모리와 CPU 리소스를 낭비하게 된다. 예를 들어, 반복문 내에서 동일한 계산을 수행하는 함수를 호출하는 경우다.

for (let i = 0; i < 1000; i++) {
  const result = someExpensiveFunction(); // 같은 결과를 반환하는 함수
  console.log(result);
}

(심지어 함수 이름도 비싼 함수라고 짓다니;)
불필요한 함수 호출이 발생하는 경우, 반복문 밖으로 함수 호출을 옮겨서 최적화할 수 있다.

const result = someExpensiveFunction(); // 반복문 밖에서 함수 호출

for (let i = 0; i < 1000; i++) {
  console.log(result);
}

2. 재귀 함수 사용 시 최적화 없음

재귀 함수를 사용할 때, 깊이가 매우 깊어지면 스택 오버플로우가 발생하거나 성능 저하가 발생할 수 있다. 이를 해결하기 위해 꼬리 재귀 최적화(Tail Call Optimization)를 사용할 수 있다. (한글로는 꼬리 물기 최적화라고 번역하는 것 같다.)

function factorial(n) {
  if (n === 0) {
    return 1;
  }
  return n * factorial(n - 1); // 최적화가 필요한 재귀 함수
}

다음은 최적화된 팩토리얼 함수의 예.

function factorial(n, accumulator = 1) {
  if (n === 0) {
    return accumulator;
  }
  return factorial(n - 1, n * accumulator); // 꼬리 재귀 최적화 적용
}

단, JavaScript 엔진이 꼬리 재귀 최적화를 지원하는지 확인해야 한다. 지원하지 않는 경우, 반복문을 사용하는 것이 더 효율적일 수 있다.

Tail Call Optimization

Tail Call Optimization(꼬리 호출 최적화)은 재귀 함수의 성능을 개선하는 프로그래밍 기술이다. 이 최적화 기술을 이해하기 위해서는 먼저 재귀 함수에 대한 이해가 필요하다.

재귀 함수는 자기 자신을 호출하는 함수로, 문제를 분할하여 해결하는 데 사용된다. 그러나 재귀 함수를 사용할 때, 깊이가 깊어지면 스택 오버플로우가 발생할 수 있다. 이때 꼬리 재귀 최적화를 사용하여 문제를 해결할 수 있다.

꼬리 호출 최적화란, 재귀 함수의 마지막 작업이 자기 자신을 호출하는 경우에 대해 컴파일러나 인터프리터가 스택 프레임을 쌓지 않고 재사용하는 기술이다. 이를 통해 메모리 사용량을 줄이고, 스택 오버플로우의 위험을 감소시킨다.

예를 들어 팩토리얼 함수를 꼬리 재귀 최적화를 적용하여 작성해보자.

일반적인 재귀 함수:

function factorial(n) {
  if (n === 0) {
    return 1;
  }
  return n * factorial(n - 1);
}

꼬리 재귀 최적화 적용:

function factorial(n, accumulator = 1) {
  if (n === 0) {
    return accumulator;
  }
  return factorial(n - 1, n * accumulator);
}

위의 예에서, 두 번째 함수는 accumulator라는 추가 인자를 사용하여 꼬리 재귀로 작성되었다. 이렇게 작성된 함수는 컴파일러나 인터프리터에 의해 최적화되어 스택 프레임을 쌓지 않고 재사용한다.

그러나 자바스크립트에서는 모든 엔진이 꼬리 재귀 최적화를 지원하지 않는다. ECMAScript 2015(ES6) 표준에는 이 최적화 기술이 포함되어 있지만, 실제로 지원하는 엔진은 제한적이다. 따라서 꼬리 재귀 최적화를 사용하기 전에 지원 여부를 확인해야 하며, 지원되지 않는 경우 반복문을 사용하는 것이 더 효율적일 수 있다.

3. 이벤트 리스너의 과도한 사용

이벤트 리스너를 과도하게 사용하면 DOM 조작에 부담을 주고, 메모리 누수를 발생시킬 수 있다. 이벤트 위임(Event Delegation)을 사용하여 이를 해결할 수 있다.

// 성능에 부담을 주는 이벤트 리스너 사용 예
document.querySelectorAll('button').forEach((button) => {
  button.addEventListener('click', handleClick);
});

이벤트 위임(Event Delegation)은 상위 요소에 이벤트 리스너를 부여하고, 이벤트가 발생한 요소를 확인하여 처리하는 방식이다.

document.querySelector('#buttonContainer').addEventListener('click', (event) => {
  if (event.target.tagName === 'BUTTON') {
    handleClick(event);
  }
});

결론

위의 경우들처럼 함수 사용이 성능에 영향을 줄 수 있는 경우가 있으나 각 예시에 대한 최적화 방법을 적용하면 함수 사용에 따른 성능 저하를 최소화할 수 있다. 함수를 적절하게 사용하고 최적화를 고려하면 코드의 가독성과 유지 관리성을 높일 수 있다.

profile
studying hard to take on new challenges _¢(・ω・`) still uncertain and unpredictable about which field I'll be diving deep into. just going with the flow

1개의 댓글

comment-user-thumbnail
2023년 5월 3일

퍼갑니다 감사합니다
자바스크립트에서 함수 사용과 성능 저하 이슈 - https://economiceco.tistory.com/m/17530

답글 달기