JavaScript

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

Gaeun Lee 2024. 1. 23. 20:59

무분별한 전역변수 사용 지양해야 함

(1) 변수의 생명 주기

  • 선언에 의해 생성 → 할당을 통해 값 가짐 → 소멸
  • 자신이 선언된 위치에서 생성되고 소멸함
    • 전역 변수의 생명 주기= 애플리케이션의 생명 주기
    • 함수 내부에서 선언된 지역변수= 함수 호출시 생성 → 함수 종료시 소멸
    • = 함수의 생명주기와 같음
💡
전역 변수
변수 선언은 선언문이 어디에 있든 상관없이 가장 먼저 실행됨
변수 선언은 런타임 이전 단계에서 자바스크립트 엔진에 의해 먼저 실행됨


함수 내부에서 선언한 변수

함수가 호출된 직후에 함수 몸체의 코드가 한 줄씩 순차적으로 실행되기 이전에 자바스크립트 엔진에 의해 먼저 실행됨 (변수 먼저 선언되어 undefined로 초기화된 후 함수 몸체를 구성하는 문들이 순차적으로 실행되기 시작할 때 변수 할당문이 실행되면 x 변수에 값이 할당됨)
함수가 종료되면 함수 내부에서 선언된 지역 변수도 소멸되어 생명 주기가 종료됨

함수 내부에서 선언된 지역 변수는 함수가 호출되어 실행되는 동안에만 유효함

 

지역 변수가 함수보다 오래 생존하는 경우

  • 변수 : 하나의 값을 저장하기 위해 확보한 메모리 공간 자체 또는 그 메로리 공간을 식별하기 위해 붙인 이름
    • 생명주기: 메모리 공간이 확보된 시점 ~ 메모리 공간이 해제되어 가용 메모리 풀에 반환되는 시점
  • 함수 내부에서 선언된 변수 = 함수가 생성한 스코프에 등록
    • 함수가 생성한 스코프 = 물리적인 실체 존재 (렉시컬 환경)
    • 변수: 자신이 등록된 스코프가 소멸(메모리 해제)될 때까지 유효
      • 할당된 메모리 공간 더이상 참조 X → 가비지 콜렉터에 의해 해제되어 가용 메모리 풀에 반환됨
      • 누군가가 메모리 공간 참조 → 해제되지 않고 확보된 상태로 남아있게 됨
  • 스코프도 변수와 마찬가지 ⇒ 누군가가 스코프 참조시 소멸하지 않고 생존
    • 일반적으로 함수가 종료하면 함수가 생성한 스코프도 소멸
    • 누군가가 스코프 참조시 해제되지 않고 생존

 

var x = 'global';
function foo() {
	console.log(x);
	var x = 'local';
}
foo(); // undefined
console.log(x); // global
  • foo 내부에서 선언된 x는 foo 호출 직후에 선언되며 undefined로 초기화됨
    • 변수 할당문이 실행되기 이전까지 undefined 값 가짐
  • 호이스팅 : 변수 선언이 스코프의 선두로 끌어 올려진 것처럼 동작하는 자바스크립 고유의 특징
    • 스코프 단위로 동작함
    • 전역 변수의 호이스팅: 전역 변수의 선언이 전역 스코프의 선두로 끌어 올려진 것처럼 동작
    • 지역 변수의 호이스팅: 지역 변수의 선언이 지역 스코프의 선두로 끌어 올려진 것처럼 동작
      • 지역 변수는 함수 전체에서 유효함

전역 변수의 생명 주기

  • 전역 코드
    • 명시적인 호출 없이 실행됨
    • 함수 호출과 같이 전역 코드를 실행하는 특별한 진입점이 없음
    • 로드되자마자 곧바로 해석 후 실행
    • 마지막 문이 실행되어 더 이상 실행할 문이 없을 때 종료
  • 함수
    • 함수 몸체의 마지막 문 또는 반환문이 실행되면 종료

전역 객체

코드가 실행되기 이전에 자바스크립트 엔진에 의해 어떤 객체보다도 먼저 생성되는 특수한 객체

  • 환경에 따른 객체
    • 클라이언트 사이드 환경(브라우저) = window
    • 서버 사이드 환경(Node.js) = global
    • 환경에 따라 전역 개체 가리키는 다양한 식별자 존재
      • window.self , this, frames, global
      • ES11에서 globalThis로 통일
  • 전역 객체가 갖는 프로퍼티
    • 표준 빌트인 객체
      • Object, String, Number, Function, Array
    • 환경에 따른 호스트 객체
      • 클라이언트 Web API, Node.js의 호스트 API
    • var 키워드로 선언한 전역 변수와 전역 함수
  • var 키워드로 선언된 변수 = 전역 객체의 프로퍼티가 됨⇒ 전역 변수의 생명 주기 = 전역 객체의 생명 주기
    • 브라우저 환경에서 var 키워드로 선언한 전역 변수 = 전역 객체 window의 프로퍼티
      • 전역 객체 window : 웹페이지 닫기 전까지 유효
        • var 키워드로 선언된 변수도 마찬가지

(2) 전역 변수의 문제점

  • 전역변수 선언 의도 = 암묵적 결합 허용
    • 암묵적 결합
      • 모든 코드가 전역 변수를 참조하고 변경할 수 있는 것
      • 코드 어디서든 참조하고 할당할 수 있는 변수 선언하는 것
    • 변수의 유효 범위 ↑ ⇒ 코드 가독성 ↓ & 의도치 않은 상태 변경 위험성 ↑
  • 전역 변수의 긴 생명 주기 → 오랜 메모리 리소스 소비 기간 → 전역 변수 상태 변경 가능성 ↑
  • 전역변수는 스코프 체인 상에서 종점에 존재⇒ 변수 검색 시 가장 마지막에 검색 되므로 검색 속도 가장 느림
  • 네임 스페이스 오염
    • JS의 가장 큰 문제점
      • 하나의 파일이 분리되었다고 해도 하나의 전역 스코프를 공유한다는 점
        • 다른 파일 내에서 동일한 이름으로 명명된 전역 변수·함수 같은 스코프에 존재할 가능성 有 ⇒ 예상치 못한 결과 초래

(3) 전역 변수의 사용을 억제하는 방법

  • 전역 변수 사용할 이유 없다면 지역 변수 사용해야 함
  • 변수의 스코프는 좁을수록 좋음

방법1 ) 즉시 실행 함수

  • 즉시 실행 함수: 함수 정의와 동시에 단 한번만 호출
  • 모든 코드 즉시 실행 함수로 감쌀 시 모든 변수는 즉시 실행 함수의 지역 변수가 됨

방법2 ) 네임 스페이스 객체

  • 전역에 네임 스페이스를 담당할 객체 생성 후 전역 변수처럼 사용하고 싶은 변수를 프로퍼티로 추가하는 방법
  • 네임 스페이스 객체에 또 다른 네임 스페이스 객체를 프로퍼티로 추가하여 네임스페이스 계층적 구성 가능
  • 네임 스페이스 분리하여 식별자 충돌을 방지 가능
  • 네임 스페이스 객체 자체가 전역 변수이므로 크게 유용 x
var MYAPP = {};
MYAPP.name = 'Lee';
MYAPP.person = {
	name: 'Lee',
	address: 'Seoul',
}

방법3 ) 모듈 패턴

  • 클래스를 모방해서 관련 있는 변수와 함수를 모아 즉시 실행 함수로 감싸 하나의 모듈 생성
  • 클로저를 기반으로 동작
    • 자바스크립트의 가장 강력한 기능
  • 캡슐화 구현 가능
    • 프로퍼티메서드를 하나로 묶는 것
      • 프로퍼티: 객체의 상태를 나타냄
      • 메서드: 프로퍼티를 참조하고 조작할 수 있는 동작
    • 객체의 특정 프로퍼티나 메서드를 감출 목적으로도 사용 ⇒ 정보 은닉
  • 전역 네임스페이스의 오염 방지와 정보 은닉 구현 목적으로 사용
    var Counter = (
    	function() {
    		var num = 0;
    
    		// 외부로 공개할 데이터나 메서드를 프로퍼티로 추가한 객체 반환
    		return {
    			increase(){ return num++; },
    			decrease(){ return num--; }
    		}
    	}
    ());
    
    // private 변수는 외부로 노출되지 않음
    console.log(Counter.num); // undefined
    console.log(Counter.increase()); // 1
    console.log(Counter.decrease()); // 0
    
    • 위 예제의 즉시 실행 함수: 객체 반환
      • 외부에 노출하고 싶은 변수나 함수를 담아 반환
        • 퍼블릭 멤버: 반환되는 객체의 프로퍼티 (외부에 노출됨)
        • 프라이빗 멤버: 외부로 노출하고 싶지 않은 변수나 함수
          • 반환하는 객체에 추가하지 않으면 됨

방법4 ) ES6 모듈

  • ES6 모듈 사용시 전역 변수 사용 불가능
  • 파일 자체의 독자적인 모듈 스코프 제공
    • 모듈 내에서 var 키워드로 선언한 변수≠ 전역변수
    • ≠ window 객체의 프로퍼티
  • 모던 브라우저에서 사용 가능
  • script 태그에 type=”module” 어트리뷰트 추가시 로드된 자바스크립트 파일 = 모듈로서 동작
    • 모듈 확장자 = mjs
  • 브라우저의 ES6 모듈 기능 사용시 트랜스파일링, 번들링 필요⇒ 일반적인 사용 브라우저 지원 ES6 모듈 기능 < 모듈 번들러 (ex. Webpack)