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

young_pallete·2022년 9월 7일
0

REAL JavaScript Deep Dive

목록 보기
14/46
post-custom-banner

잠시 쉬면서 제 글을 보았는데요! 첫 시작 글들이 오히려 읽기가 방해되더라구요 🥲
앞으로는 좀 더 글의 요점에 집중하면서 써보려 합니다 🥰

본론

"...전역 네임스페이스를 오염시키므로 지양해야 한다"

프로그래밍을 하다 보면 다음과 같은 말을 많이 듣습니다.
맞습니다. 전역 변수를 남용하는 것은 결과적으로 전역 네임스페이스를 오염시킵니다.

그런데, 그게 왜 문제일까요?

주어진 이 말에 좀 더 초점을 맞춰 접근해보겠습니다.

변수의 생명주기

지역변수의 생명주기

이전에 4장에서, 변수를 살펴보았지요?
변수는 평가 단계(선언 및 초기화) - 실행 단계(값 할당)를 거칩니다.
그런데, 이는 반쪽짜리 답안입니다.

왜냐구요? 한 번 다음 예시를 살펴보겠습니다.

const val = 1;

function outer() {
  const outerVal = 2;
}

여기서, outerVal 역시 변수입니다.
그렇다면, 이 친구도 평가 단계가 val과 같을까요?
순간, 이 책을 제대로 쭉 이해하셨다면 다음 생각이 드셨을 겁니다.

"얼레... 함수는 실행단계에서 호출된 다음 내부 변수 값을 선언 및 생성할텐데...?"

네. 여태까지 우리가 배웠던 것이 맞는 답안이긴 하지만, 모든 변수들의 생명주기가 일치하는 것은 아닙니다. 그렇다면 이것이 무엇을 의미할까요?

전역변수는 메모리 리소스를 많이 쓸 수밖에 없다.

'살아 있다'는 것은 실체로 존재한다는 것입니다.
네. 변수는 존재하게 됩니다. 바로 컴퓨터의 메모리 상에 말이죠.

자바스크립트 엔진은 직접적(명시적)으로 메모리 할당을 해제할 수 없습니다.
따라서 변수는 결국 메모리 상에서 자신이 참조되고 있는 순간까지 계속해서 남게 되는데요.

문제는 여기서 발생합니다.
전역 코드는 반환문이 존재하지 않기 때문에, 일반적으로 더이상 실행하지 않으면 종료돼요! 🙇🏻‍♂️
따라서 전역 변수의 생명주기는 일반 지역 변수와 달리, 전역 객체(Window)의 생명 주기와 같게 됩니다. 즉, 끌 때까지 메모리 상에서 존재하게 되는 것이죠.

전역 변수의 문제점

우리는 위의 문제 상에서 다음과 같은 문제점을 파악할 수 있겠어요.

전역변수는 생명 주기가 길어서, 메모리 리소스를 낭비할 가능성이 높다.

하지만 다른 단점들도 많아요. 예컨대 무분별한 사용으로 인해, 결합도가 높아질 수 있습니다.
어디서든지 해당 변수를 사용하게 된다면, 해당 변수에 대한 의존성이 높아지므로 유지보수 시 전역 객체를 변경하면 예기치 않은 오류가 발생할 수 있어요.

와닿지 않을 수 있어요. 설명을 위해 극단적인 예시를 들어보죠.

// 전역 스코프
const SIZE_VALUE = 1024

// utils/size
function getDeviceTypeByViewport() {
  	const TABLET_SIZE_VALUE = 768
    
	if (document.body.clientWidth > SIZE_VALUE) {
    	return 'desktop'
    }
  	if (document.body.clientWidth > TABLET_SIZE_VALUE) {
    	return 'tablet'
    }
  	return 'mobile'
}

1024라는 사이즈 값은 어디서든지 많이 씁니다. 예컨대 KB 단위도 1024이죠. 그런데 여기서는 Desktop을 판별하는 변수로 사용하고 있어요.

그런데, 만약 제가 애플리케이션을 검토한 결과, Desktop 기준 뷰포트 사이즈를 바꾸게 되었다고 가정합시다. SIZE_VALUE라는 변수를 바꾸었다면 어떻게 될까요?

위의 로직에서는 큰 흠이 없지만, 제가 만약 KB 관련된 용량을 비교하는 로직이 있었다면 어땠을까요? 결과적으로 내부에서는 큰 오류가 날 수 있을 가능성이 높아집니다.

그렇다면 변수명을 명확히 하면 그만이라기엔, 이미 메모리 사용을 전역적으로 한다는 단점이 있죠. 지역변수를 쓰는 것만 못할 수 있겠죠? 🥹

전역변수는 결합도를 높여 예기치 않은 곳에서의 오류가 날 가능성을 유발한다.

또 문제가 있습니다. 전역 변수는 최상단 스코프에 살고 있어요. 25층 아파트를 기준으로 친다면, 25층에 살고 있는 친구라는 거에요.

그러면, 제가 1층에서 호출하고 있는데, 25층까지 가려면 시간이 오래 걸리겠죠?
따라서 지역 변수를 사용하는 것이 이론상으로 빠르다는 거죠! (실제로는 큰 차이가 없지만, 어-썸한 우리는 굳이 안 좋은 방법을 사용할 이유는 없으니까요 😉)

전역변수는 스코프체인 상에서 종점에 위치한다. 고로 탐색이 가장 느리다.

마지막으로 네임스페이스가 오염돼요.
우리 가끔가다 코드를 치면 자동완성할 때 짜증날 때 있지 않나요? 전역변수에서 만약 export했다고 치면, 이러한 자동완성이 더 더러워지고, 나중에 코드 작성시 항상 신경쓸 요소가 생겨버립니다. 편하라고 쓰는 게 자동완성인데 말이죠.

즉, 제가 원하는 requesthttp 모듈에서 호출해야 되는데, 알고 보니 전역 변수에서 request라고 썼던 걸 불러오는 현상이 발생하면, 개발에 있어서 안정성이 떨어지겠죠. 따라서 전역변수는 지양해야 해요.

전역변수는 네임스페이스를 오염시켜 잠재적인 오류를 낼 수 있다.

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

자, 우리 이제 전역변수를 사용하지 말아야 되는 이유를 살펴봤어요.
사실, 이 얘기는 매우 간단한 거에요. 그냥 최대한 쓰지 않을 수 있다면 안쓰면 됩니다.

예컨대 다음과 같은 방식으로 말이죠.

IIFE

예전에 제 글들을 보셨다면, 이 친구 의외로 초기화할 때 많이 쓴다고 했었던 것을 들었을 거에요! 🚀
한 번씩 남의 코드를 볼 때마다 보이는데, 이유는 대체로 전역 변수 없이 초기화하기 위해서랍니다 🥰

(() => {
	var HELLO_WORLD_ICON = '🥰';
  	document.body.textContent = HELLO_WORLD_ICON;
})();

네임스페이스 객체

이것도 많이 쓰는 방식인데요!
보통 유틸쪽에서 상수만 따로 빼서 객체로 많이 만들어 놓습니다.
타입스크립트를 쓰신다면, enum을 쓰는 경우도 많구요!
저는 vue를 쓸 때, 실제로 CSS에서 따로 값들을 export해서 전역 스타일로 쓰는 방식을 좋아했어요. 선언한 CSS 값들을 JS에서 쓸 수 있었기 때문에 안정성과 재사용성이 높기 때문이었죠 🥰

const GLOBAL_CSS = {
	font: {
      	sm: '14px',
    	md: '16px',
      	lg: '18px'
    }
}

console.log(GLOBAL_CSS.font.md) // '16px'

모듈 패턴

쉽게 말하자면 캡슐화겠네요. 안에 있는 값들을 외부에서 접근하지 못하도록 해준다는 것이죠!
어떻게 보면 이는 나중에 클로저를 배울 때 더 친해지실 것 같아요. 맛보기만 보죠!

const Counter = (() => {
  let num = 0;
  return {
  	increase() {
      return ++num
    },
    decrease() {
      return --num
    }
  }
})();

console.log(Counter.increase())
console.log(Counter.decrease())

쉽게 말하자면, num은 외부에서 접근이 불가능하죠. 지역 스코프(함수 내부)에서 선언되었으니까요!

하지만 그 값은 increase에서는 렉시컬 스코프를 기준으로 같은 환경을 공유하고 있기 때문에 참조가 가능한 거죠! 따라서 num을 안정적으로 관리하고 있군요! 😮

ES6 모듈

이건 모듈을 나누어라!라는 거에요.
보통 모듈을 잘게 쪼개라는 말을 많이 들어보셨을 것 같아요.

여러 이유가 있지만, 그 중 하나는 각 모듈을 역할에 맞춰 최대한 분리함으로써, 전역에서 발생할 수 있는 메모리 낭비를 줄이는 것 또한 있는 거죠 😆


🎉 마치며

생각보다 이렇게 시작하니까 내적 텐션은 매우 낮아지기는 한데 더 차분한 글이 나오기는 하네요!

정리하자면, 전역 변수는 4가지 이유에서 사용하지 않습니다.

✅ 전역변수는 생명 주기가 길어서, 메모리 리소스를 낭비할 가능성이 높다.
✅ 전역변수는 결합도를 높여 예기치 않은 곳에서의 오류가 날 가능성을 유발한다.
✅ 전역변수는 스코프체인 상에서 종점에 위치한다. 고로 탐색이 가장 느리다.
✅ 전역변수는 네임스페이스를 오염시켜 잠재적인 오류를 낼 수 있다.

그리고 이를 위해 다양한 방법으로 전역변수가 가진 문제점을 해결할 수 있게 되었구요!
다음에는 let, const를 살펴보게 되었네요.

사실 이는 실행컨텍스트를 이해해야 비로소 완성되는 것이긴 하지만, 그 전에 스터디를 위해, 저 역시 깔끔하게 내적으로 정리해봐야겠어요. 이상! 🌈

profile
People are scared of falling to the bottom but born from there. What they've lost is nth. 😉
post-custom-banner

0개의 댓글