[Deep Dive] 전역 변수의 문제점

gyubo·2023년 6월 18일
0
post-thumbnail

14-1 변수의 생명 주기텍스트

변수는 영원히 존재하지는 않는다. 변수가 영원히 존재한다면 한번 선언된 변수는 프로그램을 종료하지 않는 한 영원히 메모리 공간을 차지하고 있게된다.

변수는 자신이 선언된 위치에서 생성되고 소멸한다. 보통 전역 변수의 생명 주기는 애플리케이션의 생명 주기와 같지만, 함수 내부에서 선언된 지역 변수는 함수가 호출되고 생성되고 함수가 종료하면 소멸한다.

이전에 호이스팅에서 살펴 보았듯이, 변수 선언은 선언문이 어디에 있던지간에 상관없이 가장 먼저 실행된다고 배웠다. 다만 이는 엄밀하게 얘기하면 전역 변수에 한정된다. 함수 내부에서 선언한 변수는 함수가 호출된 직후에 함수 몸체의 코드가 한 줄 씩 순차적으로 실행되기 이전에 자바스크립트 엔진에 의해 먼저 실행된다.

function foo(){
 var x = 'local'; // 값을 할당하기 이전 먼저 변수가 선언된다. 엔진에 의해
 console.log(x);
 return x; // 함수가 종료됨에 따라 변수는 소멸한다.
}

코드가 한줄한줄 시작되기 전 먼저 변수가 선언되고 undefined 로 초기화 후, 그다음 한줄 실행되면서 할당이 된다. 마지막 함수가 종료되면 변수 역시 소멸한다. 즉, 지역 변수의 생명 주기는 함수의 생명 주기와 같다.

var x = 'global';

function foo(){
 console.log(x); // 1
 var x = 'local';
}

foo();
console.log(x); // global

1번에서 어떤값이 출력이 될 까? 함수 내부에 선언이 되었고, 호이스팅에 의해 1번 console.log 가 참조하는 x 는 함수 내부의 변수임을 알 수 있다. 허나, 선언의 초기값 undefined 이후 코드가 한줄한줄 실행됨에 따라 추후에 local 이라는 할당값이 할당되기에, 그 전 console.log 에는 초기값 undefined 가 출력이 될 것이다.

이를 통해 알 수 있는 것은 호이스팅은 스코프 단위로 동작한다는 점이다. 지금 함수 내부에 변수는 출력 콘솔로그보다 아래에 선언이 되어있다. 허나, 하나의 함수 몸체 역시 지역 스코프이기 때문에, 이 변수의 선언이 지역 스코프의 선두로 끌어 올려진 것처럼 동작하게 된다. 스코프를 배웠으니 호이스팅에 대해 다시 정리해보자면, 호이스팅은 변수 선언이 스코프 선두로 끌어 올려진 것처럼 동작하는 자바스크립트의 고유의 특징을 말한다.

반면 전역변수는 함수처럼 특정한 시점에 호출이 되서 선언되는것이 아니라, 코드가 로드되자마자 실행된다. 모든 작성된 코드가 실행이 끝나고 나서 생명주기가 마무리 된다. 이제 잠깐 전역 객체라는 용어에 대해 알아보고 가자.

전역객체(global)는 코드가 실행되기 이전 단계에 자바스크립트 엔진에 의해 어떤 객체보다도 먼저 생성되는 특수한 객체다. 전역 객체는 클라이언트 사이드 환경(브라우저)에서는 window, 서버 사이드 환경(node.js)에서는 global 객체를 의미한다. 환경에 따라 전역 객체를 가리키는 다양한 식별자(window. self. this. frames. global)가 존재했으나 ES11에서 globalThis 로 통일 되었다.
전역 객체는 표준 빌트인 객체(Object, String, Number, Function, Array ...)와 환경에 따른 호스트 객체(클라이언트 Web API 또는 Node.js의 호스트 API), 그리고 var 키워드로 선언한 전역 변수와 함수를 프로퍼티로 갖는다.

먼가 어려운 설명이다. 쉽게 생각하면 런타임이 되기 전 알아서 만들어지는 객체라고 생각하면 될 듯 하다. 그리고 자바스크립트를 사용하다보면 이벤트를 발동시키려 할 때 window 객체를 이용한 경험들이 있을 것이다. 일단 이부분에 대해서는 익숙한 느낌이 드는데, 마지막 문장을 주목해보자. var 로 선언한 전역 변수, 함수를 프로퍼티로 가진다고 했다. 즉, window 의 프로퍼디다. 전역객체(window)는 웹페이지를 닫을 때까지 유효하다. 따라서 var 키워드로 선언한 변수는 웹페이지를 닫을 때 까지 유효하고, 이는 생명 주기가 전역 객체의 생명 주기와 일치하다는 말이다.

출처: Deep DIve

14-2 전역 변수의 문제점

전역변수의 문제점은 크게 4가지로 살펴볼 수 있다.

암묵적 결합
긴 생명주기
스코프 체인 상에서 종점에 존재
네임스페이스 오염
전역변수는 어디서든 참조할 수 있다. 이 말은 모든 코드가 전역 변수를 참조하고 변경할 수 있는 암묵적인 결합(implicit coupling)을 허용한다는 점에서, 의도치 않게 변수가 변경되고 그로인해 이 변수를 참조하는 코드가 오류를 발생시킬 수 있음에 전역변수 사용이 문제가 될 수 있다.

코드는 사람이 작성한다. 즉, 이러한 점에서 전역 변수는 사용자에게 오류를 발생시킬 기회를 지역변수보다 훨씬 많이 주게 된다. 지역 변수는 생존 시간이 짧지만, 전역 변수는 그렇지 않기 때문이다. (언제나 그렇듯 인간을 믿으면 안된다.) 더군다나 var 키워드로 선언된 변수는 같은 스코프내에서도 재선언이 가능하다. 이런점 때문에 더욱 위험하다.

또 작은 결점일 수도 있지만, 전역 변수는 스코프 최상위에 존재한다. 우린 이전 스코프에서 스코프 체인을 통해 식별자 검색을 한다는 사실을 알고 있다. 이러한 점에서 전역변수는 식별자 검색 시간이 가장 느리다. 이런점도 단점이다.

자바스크립트의 가장 큰 문제점 중 하나는 파일이 분리되어 있다 해도 하나의 전역 스코프를 공유한다는 점이다. 따라서 다른 파일 내에서 동일한 이름으로 명명된 전역 변수나 전역 함수가 같은 스코프 내에 존재할 경우 예상치 못한 결과를 가져올 수 있다.

14-3 전역변수의 사용을 억제하는 방법

무분별한 사용이 위험하다는 것이지, 전역변수를 아예 쓰지 말라는 이야기는 아니다. 다만 전역변수와 지역변수 둘다 가능한 상황이라면 지역변수를 사용하는 것이 좋다는 의미다.

보통 억제 하는 방법중에는 즉시 실행 함수를 활용하는 방법이나 네임스페이스 객체를 생성하여 사용하고 싶은 변수를 객체의 프로퍼티로 추가하는 방법 등이 있다. 이러한 방법들 중에서 가장 대표적인 것은 모듈 패턴 이다.
모듈 패턴

모듈 패턴은 클래스를 모방하여 관련이 있는 변수와 함수를 모아 즉시 실행함수로 감싸 하나의 모듈을 만든다. 이러한 패턴은 자바스크립트의 클로저를 기반으로 하는데, 클로저에 대해서는 추후에 다시 설명하겠다. 이러한 모듈 패턴은 단순히 전역 변수의 억제 뿐 아니라 캡슐화 까지 구현할 수 있다는 점이다.

캡슐화는 무엇이냐면 객체의 상태를 나타내는 프로퍼티와 프로퍼티를 참조하고 조작할 수 있는 동작인 메서드를 하나로 묶는 것을 의미한다. 하나의 캡슐화를 시킨다는 것은, 선택적으로 어떠한 데이터나 메서드는 외부에서 접근이 가능하게 만들수도 있고, 어떠한 데이터들은 접근 자체를 막을 수도 있다는 의미다.

var Counter = (function(){
 // private 변수
 var num = 0;
 
 // 외부 공개
 return {
  increase(){
    return ++num;
  },
  decrease(){
    return --num;
  }
  }
 }());
 
 console.log(Counter.num); // undefined
 
 console.log(Counter.increase()) // 1
 console.log(Counter.decrease()) // 0

위 코드를 살펴보게 되면, 즉시 실행함수로 내부에 변수와 메서드를 모두 캡슐화 시켰다. 그리고 메서드를 return 부분에 작성하면서, 외부에서도 호출할 수 있도록 구성하였다. 즉 메서드 increase, decrease 는 public 으로서 접근이 가능하다.

허나 위 변수 num 의 경우 반환하는 객체에 추가해놓지 않았다. (return 에 없으니) 함수는 선언되면서 이후 바로 실행이 되었고 그로인한 함수의 반환값에 변수 num 이 없기에 사용자는 외부에서 num 에 접근을 할 수가 없다. 즉, 변수 num 은 private 하다.

이러한 특성은 추후에 클로저를 하면서 좀 더 살펴보도록 하자.

profile
개린이

0개의 댓글