|
| 1 | +# Chapter 14 - 전역 변수의 문제점 |
| 2 | +전역 변수의 무분별한 사용은 위험하다. |
| 3 | +해당 챕터에서는 전역 변수의 문제점과 전역 변수의 사용을 억제할 수 있는 방법에 대해 기술한다. |
| 4 | + |
| 5 | +<br><br><hr> |
| 6 | + |
| 7 | +### 변수의 생명 주기 |
| 8 | +#### 지역 변수의 생명 주기 |
| 9 | +변수는 생성되고 소멸되는 생명 주기(life cycle)가 있다. |
| 10 | +변수에 생명 주기가 없다면, 한번 선언된 변수는 |
| 11 | +프로그램을 종료하지 않는 한 영원히 메모리 공간을 점유하게 된다. |
| 12 | + |
| 13 | +``` js |
| 14 | +function foo() { |
| 15 | + let x = 'local'; |
| 16 | + console.log(x); |
| 17 | + return x; |
| 18 | +} |
| 19 | + |
| 20 | +foo(); |
| 21 | +console.log(x); // ReferenceError: x is not defined |
| 22 | +``` |
| 23 | + |
| 24 | +지역 변수 x는 foo 함수가 호출되기 이전까지는 생성되지 않고, |
| 25 | +변수 스코프 규칙에 따라 foo 함수 안에서만 존재하게 된다. |
| 26 | +이는 <font color='orange'>지역 변수의 생명 주기가 함수의 생명 주기와 일치한다는 말과 동일시 할 수 있다. </font> |
| 27 | + |
| 28 | +다만, 함수 내부에서 선언된 지역 변수가 함수보다 조금 더 오래 생존하는 경우도 있다. |
| 29 | + |
| 30 | +변수는 하나의 값을 저장하기 위해 확보한 메모리 공간 또는 그 메모리 공간을 식별하기 위해 붙인 이름이다. |
| 31 | +따라서 변수의 생명 주기는 메모리 공간이 확보(allocate)된 시점부터 메모리 공간이 해제(release)되어 |
| 32 | +가용 메모리 풀(memory pool)에 반환되는 시점 까지이다. |
| 33 | + |
| 34 | +함수 내부에서 선언된 지역 변수는 함수가 생성한 스코프에 등록된다. |
| 35 | +<font color='orange'>함수가 생성한 스코프는 렉시컬 환경이라 부르는 물리적인 실체가 있다.</font> |
| 36 | +따라서 변수는 자신이 등록된 스코프가 소멸(메모리 해제)될 때 까지 유효하다. |
| 37 | + |
| 38 | +할당된 메모리 공간은 더 이상 누구도 참조하지 않을 때, |
| 39 | +가비지 콜렉터에 의해 해제되어 가용 메모리 풀에 반환된다. |
| 40 | +즉, 누군가 메모리 공간을 참조하고 있으면 해제되지 않고 확보된 상태로 남아있게 된다. |
| 41 | +이는 스코프에도 동일하게 적용된다. |
| 42 | + |
| 43 | +<font color='orange'>누군가 스코프를 참조하고 있으면 스코프는 소멸하지 않고 생존하게 된다.</font> |
| 44 | + |
| 45 | +> 일반적으로 함수가 종료되면 함수가 생성한 스코프도 소멸되지만, |
| 46 | +> 누군가 스코프를 참조하고 있다면 스코프는 해제되지 않고 생존하게 된다. |
| 47 | +> 이에 대해서는 24장 "클로저"에서 다루게 된다. |
| 48 | +
|
| 49 | + |
| 50 | +<br><br> |
| 51 | + |
| 52 | + |
| 53 | +#### 전역 변수의 생명 주기 |
| 54 | +함수와 달리 전역 코드는 명시적인 호출 없이 실행된다. |
| 55 | +다시 말해, 전역 변수는 함수 호출과 같이 전역 코드를 실행하는 특별한 진입점 없이 |
| 56 | +코드가 로드되자마자 곧바로 해석되고 실행된다. |
| 57 | + |
| 58 | +함수는 함수 몸체의 마지막 문 또는 반환문이 실행되면 종료된다. |
| 59 | +하지만 <font color='orange'>전역 코드에는 반환문을 사용할 수 없으므로 마지막 문이 실행되어 |
| 60 | +더 이상 실행할 문이 없을 때 종료된다.</font> |
| 61 | +> 전역 변수는 전역 객체의 프로퍼티가 된다. |
| 62 | +
|
| 63 | +※ 전역 객체(global object) |
| 64 | +전역 객체는 코드가 실행되기 이전 단계에 자바스크립트 엔진에 의해 어떤 객체보다도 먼저 생성되는 특수한 객체이다. |
| 65 | +전역 객체는 클라이언트 사이드 환경(브라우저)에서는 `window`, 서버 사이드 환경(Node.js)에서는 `global` 객체를 의미한다. |
| 66 | +환경에 따라 전역 객체를 가리키는 다양한 식별자(`window`, `self`, `this`, `frames`, `global`)가 존재했으나 ES11에서 `globalThis`로 통일되었다. |
| 67 | +전역 객체는 표준 빌트인 객체(`Object`, `String`, `Number`, `Function`, `Array`, ....)와 환경에 따른 호스트 객체, 그리고 `var` 키워드로 선언한 전역 변수와 전역 함수를 프로퍼티로 갖는다. |
| 68 | + |
| 69 | +<br> |
| 70 | + |
| 71 | +``` js |
| 72 | +var x = 'global'; |
| 73 | + |
| 74 | +function foo() { |
| 75 | + var x = 'local'; |
| 76 | + console.log(x); |
| 77 | + return x; |
| 78 | +} |
| 79 | + |
| 80 | +foo(); |
| 81 | +console.log(x); |
| 82 | +``` |
| 83 | + |
| 84 | +<br><br><hr> |
| 85 | + |
| 86 | +### 전역 변수의 문제점 |
| 87 | +#### 암묵적 결합 |
| 88 | +전역 변수는 모든 코드가 참조하고 변경이 가능한 암묵적 결합(implict coupling)을 허용한다. |
| 89 | +변수의 유효 범위가 크면 클 수록 코드의 가독성이 나빠지고 의도치 않게 상태가 변경될 수 있는 위험성도 높아진다. |
| 90 | + |
| 91 | +<br> |
| 92 | + |
| 93 | +#### 긴 생명 주기 |
| 94 | +전역 변수는 생명 주기가 길다. |
| 95 | +따라서 메모리 리소스도 오랜 기간 소비하게 된다. |
| 96 | + |
| 97 | +더욱이 var 키워드는 변수의 중복 선언을 허용하므로 |
| 98 | +생명 주기가 긴 전역 변수는 변수 이름이 중복될 가능성이 있다. |
| 99 | + |
| 100 | +변수 이름이 중복되면 의도치 않은 재할다잉 이루어진다. |
| 101 | + |
| 102 | +<br> |
| 103 | + |
| 104 | +#### 스코프 체인 상에서 종점에 존재 |
| 105 | +전역 변수는 스코프 체인 상에서 종점에 존재한다. |
| 106 | +이는 변수를 검색할 때 전역 변수가 가장 마지막에 검색된다는 것을 말한다. |
| 107 | + |
| 108 | +**즉, 전역 변수의 검색 속도가 가장 느리다.** |
| 109 | + |
| 110 | +<br> |
| 111 | + |
| 112 | +#### 네임스페이스 오염 |
| 113 | +자바 스크립트의 문제점 중 하나는 파일이 분리되어 있다고 해도 하나의 전역 스코프를 공유한다는 것이다. |
| 114 | +따라서 다른 파일 내에서 동일한 이름으로 명명된 전역 변수나 전역 함수가 같은 스코프 내에 존재할 경우 |
| 115 | +예상치 못한 결과를 가지고 올 수 있다. |
| 116 | + |
| 117 | +<br><br><hr> |
| 118 | + |
| 119 | +### 전역 변수의 사용을 억제하는 방법 |
| 120 | +전역 변수를 사용해야 할 이유를 말할 수 없다면, |
| 121 | +지역 변수를 사용하는 것이 좋다. 변수의 스코프는 좁을 수록 좋다. |
| 122 | + |
| 123 | +전역 변수의 사용을 억제할 수 있는 방법 몇 가지에 대해 다뤄보자. |
| 124 | + |
| 125 | +#### 즉시 실행 함수 |
| 126 | +함수 정의와 동시에 호출되는 즉시 실행 함수는 단 한번만 호출된다. |
| 127 | +모든 코드를 즉시 실행 함수로 감싸면 모든 변수는 즉시 실행 함수의 지역 변수가 된다. |
| 128 | + |
| 129 | +``` js |
| 130 | +(function () { |
| 131 | + var foo = 10; // 즉시 실행 함수의 지역 변수 |
| 132 | +}()); |
| 133 | + |
| 134 | +console.log(foo); // ReferenceError: foo is not defined |
| 135 | +``` |
| 136 | + |
| 137 | +이 방법을 사용하면 전역 변수를 생성하지 않으므로 라이브러리 등에 자주 사용된다. |
| 138 | + |
| 139 | +<br> |
| 140 | + |
| 141 | +#### 네임스페이스 객체 |
| 142 | +전역에 네임스페이스 역할을 담당할 객체를 생성하고 전역 변수처럼 사용하고 싶은 변수를 프로퍼티로 추가하는 방법이다. |
| 143 | + |
| 144 | +``` js |
| 145 | +let MYAPP = {}; // 전역 네임스페이스 객체 |
| 146 | + |
| 147 | +MYAPP.name = 'Lee'; |
| 148 | +console.log(MYAPP.name); // Lee |
| 149 | +``` |
| 150 | + |
| 151 | +> 전역 객체 안에서 관리 |
| 152 | +
|
| 153 | +식별자 충돌을 방지하는 효과는 있지만 네임스페이스 객체 자체가 전역 변수이기에 주의해야 한다. |
| 154 | + |
| 155 | +<br> |
| 156 | + |
| 157 | +#### 모듈 패턴 |
| 158 | +모듈 패턴을 클래스를 모방해서 |
| 159 | +관련이 있는 변수와 함수를 모아 즉시 실행 함수로 감싸 하나의 모듈을 만든다. |
| 160 | +**이는 자바스크립트의 강력한 기능인 클로저를 기반으로 동작한다.** |
| 161 | + |
| 162 | +모듈 패턴의 특징은 전역 변수의 억제는 물론 캡슐화까지 구현할 수 있다는 것이다. |
| 163 | + |
| 164 | +<br> |
| 165 | + |
| 166 | +``` js |
| 167 | +var Counter = (function () { |
| 168 | + // private 변수 |
| 169 | + var num = 0; |
| 170 | + |
| 171 | + // 외부로 공개할 데이터나 메소드를 프로퍼티로 추가한 객체를 반환한다. |
| 172 | + return { |
| 173 | + increase() { |
| 174 | + return ++num; |
| 175 | + }, |
| 176 | + decrease() { |
| 177 | + return --num; |
| 178 | + } |
| 179 | + } |
| 180 | +}); |
| 181 | + |
| 182 | +// private 변수는 외부로 노출되지 않는다. |
| 183 | +console.log(Counter.num); // undefined |
| 184 | + |
| 185 | +console.log(Counter.increase()); // 1 |
| 186 | +console.log(Counter.increase()); // 2 |
| 187 | +console.log(Counter.decrease()); // 1 |
| 188 | +console.log(Counter.decrease()); // 0 |
| 189 | +``` |
| 190 | + |
| 191 | +<br> |
| 192 | + |
| 193 | +#### ES6 모듈 |
| 194 | +ES6 모듈부터는 더는 전역 변수를 사용하지 못하게 막는다. |
| 195 | +ES6 모듈은 파일 자체의 독자적인 모듈 스코프를 제공한다. |
| 196 | + |
| 197 | +따라서 모듈 내에서 `var` 키워드로 선언한 변수는 더는 전역 변수가 아니며 |
| 198 | +`window` 객체의 프로퍼티도 아니다. |
| 199 | + |
| 200 | +<br> |
| 201 | + |
| 202 | +모던 브라우저에서는 ES6 모듈을 사용할 수 있다. |
| 203 | +`script` 태그에 `type="module"` 어트리뷰트를 추가하면 로드된 자바스크립트 파일은 모듈로서 동작한다. |
| 204 | +모듈의 파일 확장자는 `mjs`를 권장한다. |
| 205 | + |
| 206 | +``` js |
| 207 | +<script type="module" src="lib.mjs"></script> |
| 208 | +<script type="module" src="app.mjs"></script> |
| 209 | +``` |
| 210 | + |
| 211 | +ES6 모듈은 IE를 포함한 구형 브라우저에서는 동작하지 않으며, |
| 212 | +브라우저의 ES6 모듈 기능을 사용하더라도 트랜스파일링이나 번들링이 필요하기 때문에 |
| 213 | +아직까지는 브라우저가 지원하는 ES6 모듈 기능보다는 Webpack 등의 모듈 번들러를 사용하는 것이 일반적이다. |
| 214 | + |
0 commit comments