티스토리 뷰

반응형

자바스크립트의 중첩 함수

자바스크립트는 함수 안쪽에 또 다른 함수를 선언하는 중첩(nested) 함수가 가능하다. 이는 자바스크립트가 함수(function)를 변수와 같이 값(value)처럼 다루기 때문인데 일례로 자바스크립트에서는 같은 이름의 변수와 함수를 선언하는 것이 불가능하다.

 

자바스크립트의 이런 독특한 문법인 중첩 함수를 어떤 경우에 어떤 관점에서 사용해야 하는지 혹은 지양해야 하는지 생각해보았다.

 

1. 클린 코드적 관점

함수가 반복된 코드의 재사용을 위해서 사용되는 경우가 많지만 함수는 클린코드적 관점에서 충분한 의미가 있다.

// 유저가 무직자인지 확인한다.
if(user.job == null) {
        ...    
}
if(user.isUnemployed()) {
   ...
}

두 개의 코드 예제는 같은 행동을 나타낸다.

 

위의 코드 예제는 주석으로써 의도를 표현하고 있지만, 아래의 코드 예제는 코드 그 자체로 의도를 표현하고 있다. 코드의 의도를 주절주절 설명하는 주석이 많아지면 코드가 더러워지고 가독성은 떨어지게 된다.

 

즉, 단위 로직을 함수로 만드는 행위는 클린 코드에 도움이 되고, 이러한 관점에서 볼 때 자바스크립트의 중첩 함수는 좀 더 강력한 도구가 된다.

function foo(){
  function bar() {
        console.log('Hello');
  }

    bar(); // Hello가 출력된다.
}

bar(); // 에러발생!, bar is not defined

중첩함수는 선언된 함수 내부가 아니면 호출하는 것이 불가능하다.

 

bar함수를 foo함수 내부에 위치시킨다는 것은 bar함수는 foo함수 내부에서만 사용될 것임을 명시적으로 나타낸다. 이는 코드를 읽어나가는데 분명한 도움이 된다.

 

접근제어자가 존재하는 Java와 같은 언어에서도 한 Class내부에 여러 개의 public 메서드가 존재하고 각 public 메서드가 사용하는 private 메서드가 존재한다면, 어떤 private 메서드가 어떤 public 메서드와 어우러지는지 확인하기 어려운 경우가 있다.

(사실 특정 public 메서드에서만 사용되는 private 헬프 메서드가 많다라는 건 객체의 응집도가 떨어진다는 중요한 신호다!)

 

자바스크립트는 접근제어자가 없더라도 공개적(public)으로 사용되는 함수와 이 함수를 도와주는 지역적 헬퍼(helper) 함수를 중첩 함수로 둠으로써 좀 더 명확한 구분이 가능하다.

 

2. 성능적 관점

자바스크립트의 엔진이 함수가 실행될 때, 그 함수 내부에 존재하는 지역적인(local) 변수들이 존재한다면 그 변수들은 함수가 실행될 때만 존재된다. 함수가 종료되면 지역적인(local) 값들과 객체들은 사라지게 된다.

 

중첩된 함수가 변수와 같이 값(value)처럼 다뤄진다는 사실을 다시 한번 상기해보자.

function foo(a, b) {
    function bar() {
        return a + b;
    }

    return bar();
}

foo(1, 2);

foo 함수가 실행될때 bar함수 객체가 생성되고 bar함수가 실행된다. 그리고 foo 함수가 종료될 때 지역적인 함수 bar 객체는 파괴된다.

 

만약 foo함수가 100번 실행된다면, bar함수 객체는 100번 생성되었다가 100번 파괴된다.

 

bar함수를 중첩시키지 않고 외부에 그대로 노출시킨다면 어떨까?

function foo(a, b) {
    return bar(a, b);
}

function bar(a, b) {
    return a + b;
}

foo(1, 2);

foo함수가 여러 번 호출되어도 bar함수 객체는 단 한 번만 만들어지고, 그 객체가 계속 사용될 것이다.

 

이러한 이유로 중첩 함수를 사용하는 것은 성능상의 약점을 가지게 된다.

..라고 알고 있었으나 이건 이미 오래전 이야기다. (아마도 지금으로부터 7~8년 전) 이미 최근에 브라우저와 JS엔진들은 이러한 상황을 내부적 최적화를 거치게 되어 성능의 차이가 없다.

 

위의 코드가 자신의 브라우저에서 성능 차이가 나는지 확인하고 싶다면 아래 jsperf 링크로 들어가 직접 테스트를 해보면 된다.

https://jsperf.com/nested-named-functions

이 예제가 예전엔 10%~99%가량의 성능 차이가 발생한다고 되어있지만, 아마도 현재 이 포스팅을 확인하는 분들의 브라우저에서는 유의미한 차이를 확인할 수 없을 것이다.

 

결론적으로 말하자면 성능적 관점에서 중첩 함수를 지양해야 할 필요는 없다.

 

(여담으로 Java 같은 컴파일 언어들은 같은 클래스의 객체를 여러 번 생성하여도 메서드 관련 메모리는 static 영역에 위치되어 이러한 성능 문제로부터 자유롭다.)

 

3. 기능적 관점 - 클로저(closure)

클로저는 자바스크립트의 특징을 활용한 일종의 테크닉이라고 볼 수 있다.

 

앞서서 얘기했던 대로, 자바스크립트 함수의 지역적으로 선언된 값들은 함수가 종료되면 사라진다. 하지만 사라지지 않게 하는 방법이 있다.

return을 활용하여서 내부의 값들 중 외부로 노출시키고자 하는 값들을 바깥세상에 노출한다. return문이 외부 스코프와 함수 내부를 이어주는 일종의 '통로'가 되는 것이다.

 

이러한 기법을 '클로저'라고 한다.

function makeFunc() {
  var name = "siyoon";
  function displayName() {
    alert(name);
  }
  return displayName;
}

위의 예제 코드가 클로저 기법을 활용한 것이다. displayName이라는 중첩 함수가 존재하지만 return문을 통해서 중첩함수가 외부에 노출되었다. 이제 makeFunc함수를 호출할 수 있다면 중첩 함수인 displayName함수 또한 호출이 가능하다.

 

이처럼 중첩 함수는 클로저를 구성하는 핵심성분이라고 볼 수 있다.

 

결론

중첩 함수가 갖는 강점은 강력하다. 중첩 함수로 인한 성능 저하는 오늘날에는 없다. 중첩 함수를 애용하자 🙂

 

 

참고자료

반응형
댓글