본문 바로가기
  • soldonii's devlog
Javascript 공부/Advanced Javascript(-)

자바스크립트에서 전역 변수가 나쁜 이유, 그리고 IIFE

by soldonii 2019. 8. 30.

*Udemy의"Advanced Javascript Concepts"강의에서 학습한 내용을 정리한 포스팅입니다.

*자바스크립트를 배우는 단계라 오류가 있을 수 있습니다. 틀린 내용은 댓글로 말씀해주시면 수정하겠습니다. 감사합니다. :)


1. 글로벌 변수

렉시컬 환경과 그에 따른 스코프 체인을 배웠는데, 글로벌 스코프에서 변수를 선언하면 어떤 스코프에서도 모두 접근이 가능하니까, 그냥 글로벌 스코프에 변수를 선언하면 안되는 걸까?

 

글로벌 스코프에 함부로, 마구잡이로 변수를 선언하면 안되는 이유는 크게 2가지이다.

 

1) 메모리 공간의 낭비로 인한 메모리 누수의 가능성

2) 변수 간 충돌 가능성

하나의 웹 페이지, 앱 안에는 수 많은 자바스크립트 파일이 포함되게 된다. 이 때 수 많은 자바스크립트 파일 간에 서로 같은 변수가 단 한 개도 없으리라고 확신할 수 있을까? 절대 그렇지 않을 것이다. 만약 글로벌 실행 컨텍스트가 아니라, 다른 실행 컨텍스트 내에서 변수를 선언할 경우에는 기본적으로 해당 변수에 대한 스코프가 해당 실행 컨텍스트 내부에 한정되기 때문에, 만일 다른 파일에 동일한 변수명이 존재한다고 해도 충돌이 발생할 일은 없다.

그러나 전역에 변수를 생성하면 만일 다른 파일과 변수명이 겹칠 경우, 예상치 못한 버그가 발생된다. 가장 마지막에 불러온 파일을 기준으로 읽히기 때문에 원하지 않는 결과가 발생될 가능성이 높으므로 글로벌에서의 변수 선언은 최대한 피해야 한다. 

 

2. IIFE(Immediately Invoked Function Expression)

글로벌 스코프에서의 변수 선언은 최대한 지양하는 것이 좋다고 했는데, 어떻게 글로벌 변수 문제를 해결할 수 있을까? IIFE는 이를 해결하기 위한 자바스크립트 디자인 패턴 중 하나이다. IIFE를 사용해서 글로벌에서의 변수명 충돌을 최대한 방지하고, 모든 변수들을 각각의 함수 내의 local scope에 저장시킬 수 있다.

(function() {
  // do something
})();

// ()로 묶지 않으면 SyntaxError를 일으킨다.
function() {
  // do something
}(); // SyntaxError: Unexpected token

 

표현 자체는 간단하다. 일반적인 함수 선언식 전체를 괄호로 묶은 뒤, 바로 함수를 호출하는 ()를 뒤에 붙여주면 IIFE가 완성된다. 아래와 같이 함수 선언식 전체를 괄호로 묶지 않고, 그냥 함수를 선언한 후에 바로 실행시키는 구문은 SyntaxError를 일으킨다. 

 

# IIFE의 장점

IIFE에서 선언된 익명의 함수는 함수 선언과 동시에 실행시킴으로써 글로벌 실행 컨텍스트 위에 새로운 실행 컨텍스트가 생성된다. 그리고 이 새로운 실행 컨텍스트 내에서 모든 변수, 함수 등이 선언되므로 모든 데이터들은 IIFE 안에서의 local scope 내에만 저장된다. 따라서 전역 스코프를 오염시키지 않을 뿐 아니라, 외부에서 IIFE 내부에 접근하는 것 자체도 차단되기 때문에, IIFE 내부의 데이터 또한 안전이 보장된다는 장점 또한 가지고 있다.

 

예시를 더해서 살펴보자.

 

IIFE를 사용하지 않으면 여러 개의 자바스크립트 파일을 불러올 때 아래와 같은 변수명의 충돌로 인한 오류가 발생할 수 있다.

<body>
  <script>
    function a() {
      return 5;
    }
  </script>
  <script>
    function a() {
      return 'hahaha';
    }
  </script>
</body>

 

위 코드에서 의도치 않게 a라는 변수명에 서로 다른 함수가 선언되어 있다. 그래서 원래 5를 출력받기를 원했으나, 뒤에 로딩해온 자바스크립트를 최종적으로 반영하기 때문에 'hahaha'가 출력이 된다. 이를 해결하기 위해서는?

 

<body>
  <script>
    var script1 = (function() {
      function a() {
        return 5;
      }
      return {
        a : a
      }
    })();
  </script>
  <script>
    function a() {
      return 'hahaha';
    }
  </script>
</body>

 

위처럼 변경하면, a 함수를 실행하면 'hahaha'가 출력되지만, script.a()를 통해서 원하는 결과값 5를 출력할 수 있다. 이 경우, IIFE를 이용해서 실행시키고자 하는 익명함수를 script1 변수에 담아두었다. script1 변수는 따라서 IIFE로 선언 및 호출된 익명 함수의 return 값을 담게 되는데, 이 경우 { a : a }라는 객체를 리턴시키게 된다.

따라서 script1.a로 script1 변수에 담긴 a라는 key값에 접근하면 IIFE 안에서 선언한 함수 a에 접근할 수 있게 되고, 이 경우 IIFE 내에서 함수 a는 return 5이므로 결론적으로 script1.a()는 5를 리턴하게 된다.

 

이렇게 IIFE를 사용하면 새로운 실행 컨텍스트가 생성되고, 그 내부에 선언한 함수나 변수는 모두 해당 실행 컨텍스트의 스코프 내에서만 존재하게 되므로, 같은 이름 a를 사용해도 외부에 선언된 a 함수에 영향을 주지도, 받지도 않게 된다.

댓글