티스토리 뷰

반응형

왜 전역 변수를 사용하면 안 되나요?

1. 전역 변수(global variable)를 사용하게 되면 페이지에 포함된 모든 자바스크립트 변수들이 동일한 범위에서 실행됩니다. 같은 이름의 전역 변수나 전역 함수가 존재하는 경우 스크립트 뒤에 포함된 스크립트가 이전에 선언된 변수나 함수를 덮어 씌우게 됩니다. 이는 애플리케이션의 크기가 커지게 될수록 더 자주 발생될 것이며 연관되지 않은 코드들 간의 변수 오염을 발생시키게 되어 예상하기 힘든 버그를 발생시킵니다.

 

2.  하나의 단위 기능이 외부에 제공해야 하는 변수나 함수 이외에도, 내부적으로만 사용하는 변수와 함수가 노출되게 됩니다. 이는 단위 기능이 모듈로써 동작하기 힘들게 만들고 캡슐화(encapsultation)를 저해하는 행동입니다. 캡슐화가 되지 않은 모듈을 사용하게 되면 강한 결합이 발생하게 되고 이는 곧 유지보수성의 저하를 만들어내게 됩니다.

 

자바스크립트에서 전역 변수 사용을 피하는 방법

예제 코드

예제 코드는 setInterval을 이용하여서 1초에 한 번씩 변수 count를 증가시키고 이를 브라우저에 표시합니다. resetCount()라는 함수를 호출하면 count의 값이 0으로 초기화됩니다.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>How to Avoid Globals</title>
  </head>
  <body>
    Count is <span></span>
    <script src="app.js"></script>
  </body>
</html>
const span = document.querySelector("span");
let count = 0;

setInterval(() => {
  count++;
  span.innerText = count;
}, 1000);

function resetCount() {
  count = 0;
  span.innerText = count;
}

실행화면 (count가 노출되어서 의도하지 않은 동작이 가능한 문제점이 있다)

count의 값이 증가하다가 resetCount() 함수가 호출되면 count는 0으로 초기화됩니다. 하지만 count 변수가 전역 변수로 노출되어 있어서 브라우저 콘솔(console) 창에서도 count의 값을 임의로 할당할 수 있는 문제가 발생되었습니다. (브라우저 콘솔 창에서 접근 가능한 변수는 전역 변수뿐입니다.)

 

1. function 안으로 모든 전역 변수 숨기기

자바스크립트의 기능들을 init()이라는 임의의 함수안에 넣어두고 이를 호출하도록 수정하였습니다. 

function init() {
  const span = document.querySelector("span");
  let count = 0;

  setInterval(() => {
    count++;
    span.innerText = count;
  }, 1000);

  function resetCount() {
    count = 0;
    span.innerText = count;
  }
}

init();

실행화면 (모든 변수로 접근할 수 없어서 resetCount()까지 접근할 수 없게 됨)

이로써 init() 내부에 선언된 지역 변수들은 함수 안에서만 사용하게 되어 전역 변수로써 노출되지 않게 되었지만 외부에 제공되어야 하는 resetCount() 함수 또한 접근할 수 없게 됩니다.

 

2. Closure(클로저)를 사용하여 resetCount()만 노출하기

이럴 때 사용해야 하는 것이 Closure(이하 클로저)입니다. 클로저를 이용하면 외부에 노출되어야 하는 resetCount()와 내부에 감춰져야 하는 count를 구분지을 수 있습니다. 클로저는 지역적으로 사용된 변수나 함수를 외부에 공개하는 일종의 통로의 역할을 합니다.

function init() {
  const span = document.querySelector("span");
  let count = 0;

  setInterval(() => {
    count++;
    span.innerText = count;
  }, 1000);

  function resetCount() {
    count = 0;
    span.innerText = count;
  }
  
  //return문을 주목해보자. 이것이 클로저를 만드는 방법!
  return { resetCount: resetCount };
}

const app = init();

실행화면 (app 변수를 통하여 resetCount()에만 접근 가능하다.)

init() 함수를 실행시킨 결과를 app이라는 전역 변수에 담고, app이라는 전역변수를 통해서는 resetCount() 함수에만 접근이 가능하지만 count 변수를 임의로 조작할 수 없습니다. 클로저를 사용하였기 때문입니다.

 

다른 프로그래밍 언어에 익숙하다면 이 부분이 이상하게 느껴질 수 있습니다. (다른 프로그래밍 언어에서는) 함수 안의 지역 변수들은 그 함수가 처리되는 동안에만 존재하는데 자바스크립트에서는 다릅니다. 함수의 return문이 클로저를 형성하고 init()함수 내부의 지역 변수들은 지속적으로 유지시킵니다. 

 

클로저를 활용하면 private 한 변수와 public 한 변수를 구분할 수 있게 되고, 기능을 하나의 모듈로써 캡슐화 할 수 있습니다.

 

3. init()함수 익명 함수로 변환하기

실제로 사용되는 변수는 app이고 init()이라는 함수명은 무의미합니다. init() 함수를 익명함수로 만들고 바로 실행한다면 불필요한 전역 함수를 없앨 수 있습니다.

const app = (function () {
  const span = document.querySelector("span");
  let count = 0;

  setInterval(() => {
    count++;
    span.innerText = count;
  }, 1000);

  function resetCount() {
    count = 0;
    span.innerText = count;
  }

  return { resetCount: resetCount };
})();

 

function의 이름을 없애고 괄호()로 감싼 다음 바로 호출하게 되면 전역 함수명 없이 사용이 가능합니다. 이런 식으로 익명 함수를 사용하는 패턴을 IIFE패턴 (Immediately Invoked Function Expression)이라고 하고, 클로저로 app 변수에게 하나의 단위 기능을 제공하는 방식을 모듈 패턴(Module Pattern)이라고 합니다.

 

참고 

반응형
댓글