|
1 | 1 | 자바스크립트의 특징이자, 문제인 전역 변수에 대해 알아봅시다. |
2 | 2 |
|
3 | 3 | ## 전역 변수의 문제점 |
| 4 | +> 전역 변수를 남용하면 많은 문제점이 있다고 합니다. 무슨 이유일까요? |
| 5 | +
|
| 6 | +### 변수의 생명주기 |
| 7 | +> 변수는 자신이 선언된 위치에 생성되고 소멸합니다. 이를 생명 주기(Life Cycle)라고 해요. |
| 8 | +
|
| 9 | +예를 들어볼까요? 아래 예제는 `let`, `const`가 아닌 `var`를 사용하여 설명합니다. |
| 10 | + |
| 11 | +```js |
| 12 | +var x = '전역'; |
| 13 | + |
| 14 | +function foo() { |
| 15 | + var y = '지역'; |
| 16 | + console.log(y); // 지역 |
| 17 | + return y; |
| 18 | +} |
| 19 | + |
| 20 | +foo(); |
| 21 | +console.log(y); // undefined |
| 22 | +``` |
| 23 | + |
| 24 | +함수 내부에 선언된 지역 변수(`y`)는 함수 호출 시 생성되고 함수 종료와 함께 소멸(`undefined`)합니다. 하지만 전역 변수(`x`)는 대체 언제 소멸, 아니 생성될까요? |
| 25 | + |
| 26 | +우리는 호이스팅(Hoisting)에 대해 이해하고 있으니 변수 선언은 선언문의 위치 상관 없이 `가장 먼저 실행`됨을 알 수 있습니다. 이를 풀어서 말하면 런타임에 실행되는 것이 아니라, 그 이전 단계에서 자바스크립트 엔진에 의해 먼저 실행되는 것이라 할 수 있죠. 단, 이 설명은 지역 변수에 해당하지 않고, `전역 변수`에 해당합니다. 지역 변수는 위에서 언급한 그대로 실행되거든요! 그림으로 보면 이렇게 되겠죠? |
| 27 | + |
| 28 | +```js |
| 29 | +var x = '전역'; // 전역 변수 x 생성 |
| 30 | + |
| 31 | +function foo() { |
| 32 | + // 지역 변수 y 생성 |
| 33 | + var y = '지역'; // 지역 변수 y 값 할당 |
| 34 | + console.log(y); |
| 35 | + return y; |
| 36 | + // 지역 변수 y 소멸 |
| 37 | +} |
| 38 | + |
| 39 | +foo(); |
| 40 | +console.log(y); // undefined |
| 41 | + // 전역 변수 x 유효 |
| 42 | +``` |
| 43 | + |
| 44 | +<br> |
| 45 | + |
| 46 | +### 전역 변수의 문제점? |
| 47 | +> 어디서든 접근하고 변경할 수 있다면, 그것은 과연 좋기만 할까요? |
| 48 | +
|
| 49 | +이제부터 전역 변수가 가지는 문제점과 그 이유를 하나씩 살펴봅니다. |
| 50 | + |
| 51 | +1. 암묵적 결합(Implicit Coupling) |
| 52 | + - 전역 변수를 개발자가 `의도하여` 선언한 경우, 해당 코드를 **어디서든 참조하거나 할당하겠다**는 겁니다. |
| 53 | + - 변수의 유효 범위가 클수록 가독성은 나빠집니다. |
| 54 | + - 예측하지 못한 영역에서 변수의 상태가 변경될 위험성이 높아집니다. |
| 55 | + |
| 56 | +2. 긴 생명 주기(Long Life Cycle) |
| 57 | + - 전역 변수는 `애플리케이션의 생명 주기`와 **동일**합니다. |
| 58 | + - 활성화 되어 있는 시간이 기므로, 메모리 자원도 오래 소비합니다. |
| 59 | + - 전역 변수의 상태를 변경하거나 변경할 수 있는 위험성이 높습니다. |
| 60 | + - `var` 키워드는 변수 중복 선언을 허용하므로 의도치 않은 재할당이 이뤄질 수 있습니다. |
| 61 | + |
| 62 | +3. 스코프 체인의 종점(Endpoint to Scope Chain) |
| 63 | + - 전역 변수가 `가장 마지막에` 검색됩니다. 즉, **전역 변수의 검색 속도가 가장 느립니다**. |
| 64 | + |
| 65 | +4. 전역 네임스페이스 오염(Global Namespace Pollution) |
| 66 | + - 자바스크립트는 `파일이 분리`되어 있어도 **하나의 전역 스코프를 공유**합니다. |
| 67 | + - 다른 파일에 동일한 이름의 전역 변수가 존재하면 문제가 발생할 위험성이 높아집니다. |
| 68 | + |
| 69 | +<br> |
| 70 | + |
| 71 | +### 개선하는 방법 |
| 72 | +> 전역 변수의 무분별한 사용은 위험한데, 어떻게 이를 방지(억제)할까요? |
| 73 | +
|
| 74 | +전역 변수를 **반드시** 사용해야 할 이유가 있다면 사용하는 것이 맞습니다. 그러나 변수의 스코프는 하위, 즉 좁을수록 좋습니다. 필요한 상황에서 생성과 소멸을 반복하게 만들어야 `명시적인 결합`이 가능해지니까요. 그럼 어떤 방식을 통해 방지할 수 있는지 알아볼까요? |
| 75 | + |
| 76 | +1. 즉시 실행 함수(Immediately Invoked Function Expression) |
| 77 | + - `단 한 번 호출`되는 특성을 통해 **모든 변수를 즉시 실행 함수의 지역 변수**로 만드는 방법입니다. |
| 78 | + - 전역 변수를 생성하지 않으므로 `라이브러리`에 자주 사용됩니다. |
| 79 | + ```js |
| 80 | + (function(){ |
| 81 | + var x = 10; // IIFE의 지역 변수 |
| 82 | + }()); |
| 83 | + |
| 84 | + console.log(x); // ReferenceError: x is not defined |
| 85 | + ``` |
| 86 | +1. 전역 네임스페이스 객체(Global Namespace Object) |
| 87 | + - 전역에 네임스페이스 역할을 담당할 객체를 선언하고, 사용할 전역 변수를 프로퍼티에 추가하는 방법입니다. |
| 88 | + - 네임스페이스를 가지므로 식별자가 충돌할 가능성은 낮아지나, 해당 객체가 `전역 변수`에 할당되므로 유용하진 않습니다. |
| 89 | + ```js |
| 90 | + var APP = {}; // 전역 네임스페이스 객체 |
| 91 | +
|
| 92 | + // 원시 값 할당하기 |
| 93 | + App.school = false; |
| 94 | +
|
| 95 | + // 참조 값 할당하기 |
| 96 | + App.person = { |
| 97 | + name: 'amy', |
| 98 | + age: 16, |
| 99 | + }; |
| 100 | +
|
| 101 | + console.log(App.school); // false |
| 102 | + console.log(App.person.name); // amy |
| 103 | + ``` |
| 104 | + |
| 105 | +3. 모듈 패턴(Module Pattern) |
| 106 | + - 클래스(Class)를 모방합니다. 변수, 함수를 모아 즉시 실행 함수로 감싸 하나의 모듈을 만드는 방법입니다. |
| 107 | + - 자바스크립트의 `클로저`를 기반으로 동작합니다. 나중에 더 자세히 알아봐요! |
| 108 | + - 접근 제어자가 존재하지 않는 자바스크립트에서 객체 지향 프로그래밍의 특징인 **캡슐화(Encapsulation)** 를 흉내 낼 수 있습니다. |
| 109 | + ```js |
| 110 | + var App = (function(){ |
| 111 | + // 외부에서 참조가 불가능한 private 변수 |
| 112 | + var year = 2021; |
| 113 | +
|
| 114 | + return { |
| 115 | + older() { |
| 116 | + return ++year; |
| 117 | + }, |
| 118 | + younger() { |
| 119 | + return --year; |
| 120 | + }, |
| 121 | + getYear() { |
| 122 | + return year; |
| 123 | + } |
| 124 | + } |
| 125 | + }()); |
| 126 | +
|
| 127 | + console.log(App.year); // undefined |
| 128 | +
|
| 129 | + console.log(App.older()); // 2022 |
| 130 | + console.log(App.getYear()); // 2022 |
| 131 | + console.log(App.older()); // 2023 |
| 132 | + console.log(App.getYear()); // 2023 |
| 133 | + console.log(App.younger()); // 2022 |
| 134 | + console.log(App.getYear()); // 2022 |
| 135 | + ``` |
| 136 | + - 위 즉시 실행 함수는 객체를 반환합니다. 이 객체에는 외부에 노출시키고 싶은 변수, 함수를 담죠. 즉, 객체의 프로퍼티는 퍼블릭 멤버(Public Member)로 외부에서 참조가 가능하며, 객체에 추가하지 않은 프로퍼티는 프라이빗 멤버(Private Member)로 외부에서 참조할 수 없게 됩니다. |
| 137 | + |
| 138 | +4. ES6 모듈 |
| 139 | + - ES6에 추가된 모듈은 **파일 자체의 모듈 스코프**를 제공합니다. 즉 모듈 내에서 선언한 전역 변수는 window 객체의 프]로퍼티가 아닙니다. |
| 140 | + - HTML에 `<script type='module' src='app.js'></script>` 형태로 추가하면 로드된 자바스크립트 파일 `app.js`는 모듈로서 동작합니다. |
| 141 | + - 일반적으로 모던 브라우저가 아니면 사용할 수 없기에, Webpack 등의 모듈 번들러를 반드시 사용해야 합니다. |
4 | 142 |
|
5 | 143 | <hr> |
6 | 144 | <br> |
0 commit comments