GitHub Viewer
자바스크립트의 특징이자, 문제인 전역 변수에 대해 알아봅시다.
## 전역 변수의 문제점
> 전역 변수를 남용하면 많은 문제점이 있다고 합니다. 무슨 이유일까요?
### 변수의 생명주기
> 변수는 자신이 선언된 위치에 생성되고 소멸합니다. 이를 생명 주기(Life Cycle)라고 해요.
예를 들어볼까요? 아래 예제는 `let`, `const`가 아닌 `var`를 사용하여 설명합니다.
```js
var x = '전역';
function foo() {
var y = '지역';
console.log(y); // 지역
return y;
}
foo();
console.log(y); // undefined
```
함수 내부에 선언된 지역 변수(`y`)는 함수 호출 시 생성되고 함수 종료와 함께 소멸(`undefined`)합니다. 하지만 전역 변수(`x`)는 대체 언제 소멸, 아니 생성될까요?
우리는 호이스팅(Hoisting)에 대해 이해하고 있으니 변수 선언은 선언문의 위치 상관 없이 `가장 먼저 실행`됨을 알 수 있습니다. 이를 풀어서 말하면 런타임에 실행되는 것이 아니라, 그 이전 단계에서 자바스크립트 엔진에 의해 먼저 실행되는 것이라 할 수 있죠. 단, 이 설명은 지역 변수에 해당하지 않고, `전역 변수`에 해당합니다. 지역 변수는 위에서 언급한 그대로 실행되거든요! 그림으로 보면 이렇게 되겠죠?
```js
var x = '전역'; // 전역 변수 x 생성
function foo() {
// 지역 변수 y 생성
var y = '지역'; // 지역 변수 y 값 할당
console.log(y);
return y;
// 지역 변수 y 소멸
}
foo();
console.log(y); // undefined
// 전역 변수 x 유효
```
### 전역 변수의 문제점?
> 어디서든 접근하고 변경할 수 있다면, 그것은 과연 좋기만 할까요?
이제부터 전역 변수가 가지는 문제점과 그 이유를 하나씩 살펴봅니다.
1. 암묵적 결합(Implicit Coupling)
- 전역 변수를 개발자가 `의도하여` 선언한 경우, 해당 코드를 **어디서든 참조하거나 할당하겠다**는 겁니다.
- 변수의 유효 범위가 클수록 가독성은 나빠집니다.
- 예측하지 못한 영역에서 변수의 상태가 변경될 위험성이 높아집니다.
2. 긴 생명 주기(Long Life Cycle)
- 전역 변수는 `애플리케이션의 생명 주기`와 **동일**합니다.
- 활성화 되어 있는 시간이 기므로, 메모리 자원도 오래 소비합니다.
- 전역 변수의 상태를 변경하거나 변경할 수 있는 위험성이 높습니다.
- `var` 키워드는 변수 중복 선언을 허용하므로 의도치 않은 재할당이 이뤄질 수 있습니다.
3. 스코프 체인의 종점(Endpoint to Scope Chain)
- 전역 변수가 `가장 마지막에` 검색됩니다. 즉, **전역 변수의 검색 속도가 가장 느립니다**.
4. 전역 네임스페이스 오염(Global Namespace Pollution)
- 자바스크립트는 `파일이 분리`되어 있어도 **하나의 전역 스코프를 공유**합니다.
- 다른 파일에 동일한 이름의 전역 변수가 존재하면 문제가 발생할 위험성이 높아집니다.
### 개선하는 방법
> 전역 변수의 무분별한 사용은 위험한데, 어떻게 이를 방지(억제)할까요?
전역 변수를 **반드시** 사용해야 할 이유가 있다면 사용하는 것이 맞습니다. 그러나 변수의 스코프는 하위, 즉 좁을수록 좋습니다. 필요한 상황에서 생성과 소멸을 반복하게 만들어야 `명시적인 결합`이 가능해지니까요. 그럼 어떤 방식을 통해 방지할 수 있는지 알아볼까요?
1. 즉시 실행 함수(Immediately Invoked Function Expression)
- `단 한 번 호출`되는 특성을 통해 **모든 변수를 즉시 실행 함수의 지역 변수**로 만드는 방법입니다.
- 전역 변수를 생성하지 않으므로 `라이브러리`에 자주 사용됩니다.
```js
(function(){
var x = 10; // IIFE의 지역 변수
}());
console.log(x); // ReferenceError: x is not defined
```
1. 전역 네임스페이스 객체(Global Namespace Object)
- 전역에 네임스페이스 역할을 담당할 객체를 선언하고, 사용할 전역 변수를 프로퍼티에 추가하는 방법입니다.
- 네임스페이스를 가지므로 식별자가 충돌할 가능성은 낮아지나, 해당 객체가 `전역 변수`에 할당되므로 유용하진 않습니다.
```js
var APP = {}; // 전역 네임스페이스 객체
// 원시 값 할당하기
App.school = false;
// 참조 값 할당하기
App.person = {
name: 'amy',
age: 16,
};
console.log(App.school); // false
console.log(App.person.name); // amy
```
3. 모듈 패턴(Module Pattern)
- 클래스(Class)를 모방합니다. 변수, 함수를 모아 즉시 실행 함수로 감싸 하나의 모듈을 만드는 방법입니다.
- 자바스크립트의 `클로저`를 기반으로 동작합니다. 나중에 더 자세히 알아봐요!
- 접근 제어자가 존재하지 않는 자바스크립트에서 객체 지향 프로그래밍의 특징인 **캡슐화(Encapsulation)** 를 흉내 낼 수 있습니다.
```js
var App = (function(){
// 외부에서 참조가 불가능한 private 변수
var year = 2021;
return {
older() {
return ++year;
},
younger() {
return --year;
},
getYear() {
return year;
}
}
}());
console.log(App.year); // undefined
console.log(App.older()); // 2022
console.log(App.getYear()); // 2022
console.log(App.older()); // 2023
console.log(App.getYear()); // 2023
console.log(App.younger()); // 2022
console.log(App.getYear()); // 2022
```
- 위 즉시 실행 함수는 객체를 반환합니다. 이 객체에는 외부에 노출시키고 싶은 변수, 함수를 담죠. 즉, 객체의 프로퍼티는 퍼블릭 멤버(Public Member)로 외부에서 참조가 가능하며, 객체에 추가하지 않은 프로퍼티는 프라이빗 멤버(Private Member)로 외부에서 참조할 수 없게 됩니다.
4. ES6 모듈
- ES6에 추가된 모듈은 **파일 자체의 모듈 스코프**를 제공합니다. 즉 모듈 내에서 선언한 전역 변수는 window 객체의 프]로퍼티가 아닙니다.
- HTML에 `` 형태로 추가하면 로드된 자바스크립트 파일 `app.js`는 모듈로서 동작합니다.
- 일반적으로 모던 브라우저가 아니면 사용할 수 없기에, Webpack 등의 모듈 번들러를 반드시 사용해야 합니다.