LHJ

I'm a FE developer.

7.3 전역 스코프

04 May 2020 » js_lj

스코프는 계층적이며 트리의 맨 아래에는 바탕이 되는 무언가가 있어야 합니다.
즉, 프로그램을 시작할 때 암시적으로 주어지는 스코프가 필요합니다.
이 스코프를 전역 스코프라고 합니다.
자바스크립트 프로그램을 시작할 때, 즉 어떤 함수도 호출하지 않았을 때 실행 흐름은 전역 스코프에 있습니다.
바꿔 말해, 전역 스코프에서 선언한 것은 무엇이든 프로그램의 모든 스코프에서 볼 수 있습니다.

전역 스코프에서 선언된 것들을 전역 변수라고 합니다.

역주_ 전역변수
원문에서는 globals 라고 표현했습니다.
변수와 함수, 객체를 포함해 전역에 있는 모든 것이라는 의미지만, 선언한다는 것은 식별자를 부여한다는 것이고 따라서 변수라 해도 큰 무리는 없을 것 같아 이 책에서는 익숙한 표현인 전역 변수라고 옮깁니다.

전역 변수는 아주 안 좋은 평가를 받고 있습니다.
프로그래밍 책을 읽다 보면, 전역 변수를 쓰기만 하면 도저히 피할 수 없는 대재앙이 일어날 것처럼 말하고 ㄴ합니다.
그러면 전역 변수는 왜 그렇게 나쁜 걸까요?

전역 변수가 나쁜 것은 아닙니다.
사실 전역 변수는 반드시 써야 합니다.
나쁜 것은 전역 스코프를 남용하는 것입니다.
전역 스코프에 있는 것이 모든 스코프에서 보인다는 사실은 이미 언급했습니다.
따라서 전역 변수는 충분히 생각하고 써야 합니다.
현명한 독자라면 이렇게 생각할 겁니다.

“아, 그러면 전역 스코프에 함수 하나만 만들면 되겠군. 그러면 전역 변수를 하나로 줄일 수 있을 거야!”

애석하지만, 문제가 발생하는 위치를 한 단계 내렸을 뿐입니다.
특정 함수의 스코프에서 선언한 것은 그 함수에서 호출한 어디에서든 다 보입니다.
전역 스코프보다 상황이 좋아졌다고 말하기는 어렵습니다.

핵심은 이겁니다.
전역 스코프에 몇 가지가 존재하는 건 피할 수 없을뿐더러 그 자체가 그렇게 나쁜 것도 아닙니다.
피해야 하는 건 전역 스코프에 의존하는 겁니다.
간단한 예를 하나 들어봅시다.
사용자 정보를 보관하는 프로그램이 있습니다.
이 프로그램은 사용자의 이름과 나이를 보관하며, 그 정보를 사용하는 몇몇 함수가 있습니다.
우선, 전역 변수를 사용하는 방법이 있습니다.

let name = "Irena"; // 전역
let age = 25; // 전역

function greet() {
	console.log(`Hello, ${name}!`);
}
function getBirthYear() {
	return new Date().getFullYear() - age;
}

이 방법의 문제는 함수가 호출하는 컨텍스트(스코프)에 대단히 의존적이라는 겁니다.
어떤 함수든, 프로그램 어디에서든 상관없이 name 값을 (의도적으로든, 실수로든) 바꿀 수 있습니다.
또한 name과 age는 흔한 이름이므로 다른 곳에서 다른 이유로 사용할 가능성도 큽니다.
great와 getBirthYear는 전역 변수에 의존하므로, 프로그램의 다른 부분에서 name과 age를 정확히 사용한다고 가정하고 있는 겁니다.

그보다는 사용자 정보를 단일 객체에 보관하는 방법이 더 낫습니다.

let user = {
	name = "Irena",
	age = 25,
}

function greet() {
	console.log(`Hello, ${user.name}!`);
}
function getBirthYear() {
	return new Date().getFullYear() - user.age;
}

이 예제에서는 name과 age를 없애고 대신 user를 써서 전역 스코프의 식별자 숫자를 겨우 하나 줄였을 뿐이지만, 사용자에 관한 정보를 10가지나 100가지 보관한다고 상상해 보십시오.

하지만 개선의 여지는 더 남아있습니다.
함수 greet과 getBirthYear는 여전히 전역 user에 의존하며, 이 객체는 어디서든 수정할 수 있습니다.
이 함수들을 고쳐서 전역 스코프에 의존하지 않게 만들어 봅시다.

function greet(user) {
	console.log(`Hello, ${user.name}!`);
}
function getBirthYear(user) {
	return new Date().getFullYear() - user.age;
}

이제 이 함수들은 모든 스코프에서 호출할 수 있고, 명시적으로 user를 전달받습니다(모듈과 객체지향 프로그래밍을 배우면 더 나은 방법이 있습니다).

사실 프로그램이 이렇게 단순하다면, 전역 변수를 쓰더라도 문제가 생길 소지는 전혀 없습니다.
하지만 프로그램이 수천, 수만 행이 되어 모든 스코프를 기억하고 관리할 수 없게 된다면 전역 스코프에 의존하지 않는 것이 정말 중요해집니다.