프로그래밍 언어의 장점을 활용해서 사용하는 것만큼, 단점을 보완하는 것도 중요한 일입니다.
단점을 무시한 채 장점만을 활용한 코드는 효율성과 안정성을 보장하기 힘들기 때문입니다.
전역변수를 기초로 하는 자바스크립트의 단점 때문에, 전역 네임스페이스(글로벌 네임스페이스)의
오염문제는 많이 신경 쓰이는 부분입니다. (많은 수의 전역변수 선언도좋지 않다. )
여러 스크립트가 한 페이지 안에 함께 있는 소스코드에서는 전역변수가 많아질수록 이름이 겹칠 우려가 있습니다.
뿐만 아니라 어느곳에서든 접근할 수 있으므로 소스코드의 신뢰성을 떨어뜨릴수 있다.
이런 단점을 보완하는 몇가지 방법을 살펴봅니다.
그리고 그 중에서 네임스페이스 패턴을 자세히 알아보겠습니다.
1.var 사용
자바스크립트는 암묵적 전역(implied globals) 이라는 개념이 있다.
그렇기 때문에 var를 사용하지 않고 변수를 선언하거나,
선언되지 않은 변수를 사용하면 아무리 지역 함수 내에 있더라도 전역에 속하게 됩니다.
여기서, 변수에 관한 내용임에도, '전역 변수'가 되었다고 하지 않고,'전역에 속하게 되었다.'고 한 이유는
var를 사용하지 않았을 경우에는 전역변수가 아닌 전역 객체의 프로퍼티로 생성되기 때문이다.
아래 예제를 보자.
/*
a : 함수에 속하지 않고 var를 사용하여 선언된 전역변수
b : 함수에 속하지 않고 암묵적으로 생성된 전역 객체의 프로퍼티
c : 함수에 속하고 암묵적으로 생성된 전역 객체의 프로퍼티
*/
var a =1;
b =2;
(function () {
c = 3;
}());
delete a;
delete b;
delete c;
console.log(typeof a); //number 출력
console.log(typeof b); //undefined 출력
console.log(typeof c); //undefined 출력
[예제 1에 대한 설명] 암묵적 전역변수
프로퍼티는 delete 연산자로 삭제할 수 있지만 변수는 그렇지 않다는 특성을 통해
변수 b,c 는 전역변수가 아닌 전역 객체의 프로퍼티가 됨을 알수있다.
또 한가지 , 단일 var 패턴이라는 것이 있습니다.
이 패턴은 함수 상단에서 var 선언을 하나만 쓰고 여러개의 변수를 쉼표로 연결하여 선언한다.
이는 함수에서 필요로 하는 모든 지역변수를 한군데서 찾을 수 있게 해주고, 변수를 선언하기 전에 사용하면
발생하는 논리적인 오류를 막아주는 장점이 있다.
하지만 복잡한 소스코드의 구조 안에서 모든 변수를 함수의 위쪽에 선언한다는것은 어려운 일이므로,
이 패턴은 모듈 단위 개발에서 효과가 있다.
2. 즉시 실행 함수와 즉시 객체 초기화
전역 변수를 줄이기 위해, 즉시 실행 함수(IIFE Immediately Invoked Function Expression) 를 사용한다.
이 함수는 선언과 동시에 바로 실행되므로 전역변수를 만들지 않아,
플러그인 이나 라이브러리 등을 만들 때 많이 사용한다.
또한 즉시 실행 함수는 모듈 패턴의 기반이 되기도 한다.

[ 예제 2에 대한 설명 ] 즉시 실행 함수
typeof 키워드로 각 함수의 타입을 알아보았다.
함수 선언문으로 선언된 위쪽 코드의 함수 타입은 function이다.
즉 이대로 전역범위에 선언하면 전역 객체에 func라는 이름의 함수가 추가된다는 뜻이다.
반면 익명의 즉시 실행 함수 표현식으로 선언된 아래쪽 코드의 함수타입은 undefined 입니다.
함수가 즉시 실행되고 난 후에 전역에 남지않고 바로 사라지기 때문입니다.
그렇기 때문에, 재사용할 필요 없이 한번만 호출할 함수들은 즉시 실행 함수 패턴을 사용하면 전역 네임스페이스를 오염시키지 않습니다.
그리고 즉시 객체 초기화라는 패턴도 있다.
괄호로 묶어서 바로 초기화하는 사용방식과, 전역변수를 만들지 않는다는 장점이 즉시 실행 함수와 비슷하다
단점으로는 대부분의 자바스크립트 압축도구가 즉시 실행 함수에 비해 효과적으로 압축하지 못한다는 것이다.
다음의 코드는 즉시 객체 초기화 패턴의 예제입니다.

[ 예제3 설명 ] 즉시 객체 초기화
또 한가지 고려해야 할 것은, 즉시 실행 함수와 즉시 객체 초기화의 남용으로 인한 메모리 낭비이다.
자바스크립트는 이렇게 할당이 없이 정의만 할 경우, 전역 네임스페이스는 건드리지 않더라도
전역 실행 컨텍스트(EC : Excution Context)의 temp = [ ] 내에 key-value 를 추가하게 됩니다.
이 EC.temp영역은 개발자가 접근할 수 없는 영역입니다.
그렇게 때문에 스크립트 내에 다른영역은 물론 어디에서도 접근할수 없어, 소스코드의 신뢰성에는 큰도움이 된다.
하지만 같은 이유로 이 패턴을 남용하면, 직접 관리할 수 없는 공간에 메모리가 계속 쌓이게 된다.
그렇기 때문에 소스코드의 신뢰성과 메모리의 문제를 함께 고민해서
이를 남용하지 않고 적절히 사용해야 합니다.
3. 네임스페이스 패턴(namespace pattern)
네임스페이스 패턴이란??
네임 스페이스는 구분이 가능하도록 정해놓은 범위나 영역을 뜻합니다.
즉 말그래도 이름공간을 선언하여 다른공간과 구분하도록 합니다.
다른 쉬운 말로 소속이라고 한다.
한가지 예를 들어보자
만약 대한민국에 사는 홍길동이라는 사람을 찾는다면, 처음에 의도했던 특정한 사람을 찾아내는 것은 거의 불가능하다.
서울에 사는 홍길동이라면 범위는 조금 좁아지겠지만 여전히 원하는 사람을 찾기 어렵다.
대한민국, 서울시, **구, 몇 번지에 사는 홍길동이라는 정보가 있어야 특정인물을 찾기 쉽다.
이처럼 원하는 결과를 얻기 위해서는 좀 더 자세한 정보가 필요하다.
이를 자바스크립트 언어 관점에서 바라보면, 자세한 정보들이 바로 네임스페이스이다.
그래서 대한민국에서 홍길동 찾기는 네임스페이스에서 특정변수를 직접 찾기와 같다.(불가능 ㅠㅠ)
결국 네임 스페이싱(namespacing)은 객체나 변수가 겹치지 않는 안전한 소스코드를 만드는 개념이다.
하지만 자바스크립트는 아직까지 네임스페이싱을 위한 기능을 지원하지 않기 때문에
다음의 특성을 통해 네임스페이스와 비슷한 효과를 얻을 수 있다.
1. 자바스크립트의 모든 객체는 프로퍼티를 가진다.
2. 그 프로퍼티는 다시 다른 객체를 담을 수 있다.
이러한 네임스페이싱 코딩 기법들을 '네임스페이스 패턴' 이라고 합니다.
3.1 객체 리터럴 네임 스페이싱(object literal namespacing)
가장 기본적인 네임스페이스 패턴은 객체 리터럴 네임스페이싱 방식입니다.
하나의 전역객체를 만든후, 모든함수, 객체, 변수를 여기에 추가하여 구현한다.
물론 꼭 나중에 추가하지 않아도 전역 객체 선언 시에 리터럴로 미리 선언해 주어도 된다.

[ 예제4 설명 ] 네임스페이스 패턴
이 패턴은 코드 내에서 뿐 아니라 같은 페이지에 존재하는 js라이브러리나 파티코드(third-party code)와의 이름 충돌도 방지해 주며 체계적이라는 장점이 있습니다.
하지만 다음과 같은 단점도 존재한다.
모든 변수와 함수에, 상위 객체명을 모두 붙여야 하기 때문에 소스코드량이 늘어난다.
결국 그에 따라 다운로드해야 하는 파일의 크기도 늘어나게 된다.
전역 인스턴스가 단 하나뿐이기 때문에 코드의 어느 한 부분이 수정되어도 전역 인스턴스를 수정하게 된다.
계속해서 나머지 기능들도 갱신된 상태를 물려받게 된다.
매번 객체에 접근하는데다, 이름이 중첩되고 길어지므로 검색이 느려지게 된다.
여기서 매번 오브젝트에 접근하는 단점을 해결하고,
미래의 유지보수를 위하여 this키워드를 사용하는 것을 생각할 수 있습니다.
this를 사용하면 전역객체에 접근하여 검색할 필요가 없이, 바로 상위 객체만을 검색할 수 있기 때문입니다.
하지만 namespace로 사용되고 있는 객체를 this를 사용하여 참조해서는 안된다.
다음 예를 살펴보자

[ 예제5 설명 ] this 키워드를 사용한 참조
위쪽의 코드는 네임스페이스명을 사용하고,
아래쪽의 코드는 this키워드를 사용하여 같은 객체에 속한 변수를 리턴하고자 했다.
위쪽코드는 네임스페이스명을 사용했을 때나 그를 새로운 전역변수에 대입했을 때나 같은 결과를 출력한다.
하지만 아래쪽 코드는 네임스페이스명을 사용하여 출력하였을때 원하는 결과를 리턴하지만
그를 새로운 전역변수에 대입하자 undefined 를 출력한다.
이는 this키워드의 특성 때문이다.
함수영역 안에 있는 this키워드는 부모의 자식으로 불렀을 때에만 그 부모 객체를 가리키고
직접 호출하였을 때는 더이상 부모객체가 아닌 전역객체를 가리키기 때문이다.
그래서 this는 절대 네임스페이스로 사용되고 있는 객체를 참조해서는 안된다.
위와 같이 네임스페이스로부터 식별자(identifier)를 가져오는 데 혼란이 될 수 있기 때문이다.
이러한 네임스페이스 패턴의 단점을 해결하기 위해서는 this 키워드가 아닌 샌드박스패턴을 사용해야한다.
샌드박스 패턴은 뒤에서 살펴보겠다.
3.2 범용 네임스페이스 함수
프로그램이 복잡해짐에 따라, 코드의 각 부분들이 별개의 파일로 분리되어 선택적으로 문서에 포함되는 경우가 많다.
그러다 보면, 네임스페이스로 사용할 객체를 선언할 때나, 그 내부의 프로퍼티를 정의할 때. 이미 있는것을 재정의 하는
일도 생길 수 있다. 이를 확인하지 못한 채 지나가면 내용을 덮어쓰는 문제가 생긴다.

[ 예제 6 설명 ] 전역 객체의 존재여부를 확인
따라서 1번처럼 객체를 선언하는 대신, 2번처럼myapp이 미리 선언되었는지를 확인하고 정의해주어야한다.
3번은 2번과 똑같이 작동하는 short-hand 방식이다.
하지만 이런시으로 반복해서 확인 작업을 해주면 중복되는 내용의 코드가 상당히 많이 생겨날 수 있다.
네임스페이스의 깊이가 깊어질수록 각 단계마다 확인해 주어야 하기 때문이다.
그래서 아래의 소스코드와 같이 이 작업을 맡아줄 재사용 가능한 함수를 만드는 것이 좋다.

[ 예제7 설명] 전역 객체의 존재 확인 함수
이렇게 만든 namespace 함수를 사용하면 다음과 같은 긴 네임스페이스도 안전하게 만들 수 있다.
코드 아래쪽 그림은 크롬의 개발자 도구를 사용하여 확인한 것인데 korea를 최상단으로
일련의 네임스페이스가 생성된것을 볼 수 있다.


3.3 샌드박스 패턴
샌드박스는 본래, 말 그래로 어린이가 안에서 노는 모래 놀이 통을 의미한다.
모래를 담은 상자에는 그 상자 밖으로 모래를 흘리거나 더럽히지 말고 그 안에서는 맘껏 놀라는 의미가 담긴 것이다.
javascript 샌드박스 패턴도 본래의 의미와 같이 코드들이 전역범위를 더럽히지 않고 맘껏 쓰일수 있도록
유효범위를 정해준 것이다.
위에서 살펴보았던 네임스페이스 패턴에서는 단 하나의 전역 객체를 생성했다.
샌드박스 패턴에서는 생성자를 유일한 전역으로 사용한다.
그리고 유일한 전역인 생성자에게 콜백함수를 전달해 모든 기능을 샌드박스 내부 환경으로 격리 시키는 방법을 사용한다.
다음과 같이 샌드박스 생성자를 구현한다.
function Sandbox() {
//argument를 배열로 바꾼다.
var args = Array.prototype.slice.call(argumernts),
//마지막 인자는 콜백함수
callback = args.pop(),
// 모듈은 배열로 전달될 수도 있고 개별 인자로 전달 될 수도 있습니다.
modules = (args[0] && typeof args[0] === "string") ? args : args[0],
i;
// 함수가 생성자로 호출되도록 보장(new를 강제하지 않는 패턴)
if(!(this instanceof Sandbox)) {
return new Sandbox(modules, callback);
}
// this에 필요한 프로퍼티들을 추가
this.a = 1;
this.b = 2;
// "this객체에 모듈을 추가"
// 모듈이 없거나 "*"(전부)이면 사용가능한 모든 모듈을 사용한다는 의미입니다.
if(!modules || modules === '*' || modules[0] === '*') {
modules = [];
for( i in Sandbox.Modules) {
if(Sandbox.modules.hasOwnPreperty(i)){
modules.push(i);
}
}
}
// 필요한 모듈들을 초기화
var m_length = modules.length;
for ( i = 0; i<m_length; i++) {
Sandbox.modules[modules[i]](this);
}
//콜백 함수 호출
callback(this);
}
//필요한 프로토 타입 프로퍼티들을 추가
Sandbox.prototype = {
name : "nextree",
getName : function() {
return this.name;
}
};
[ 예제8 설명 ] 샌드박스 생성자
그리고 다음과 같이 new 키워드를 사용하지 않고 ajax와 dom 이라는 가상의 모듈을 사용하는 객체를 생성합니다.
이처럼 사용할 모듈들을 앞쪽 인자(argument)로 전달해주고
마지막 인자로는 콜백함수를 전달해주면 된다.
Sandbox ('ajax', 'dom', function(box) {
// console.log(box);
});
이렇게 샌드박스 패턴을 사용하면 콜백함수로 감싸진 샌드박스 객체 안에서 마음껏 구현이 가능합니다.
또한 위에서 언급했던 네임스페이스 패턴의 몇가지 단점을 극복할 수있다.
1. 단 하나의 전역변수에 의존하는 네임스페이스 패턴의 단점을 여러개의 샌드박스 객체를 생성함으로 극복할 수 있다.
2. 점으로 연결된 긴이름을 쓸 필요가 없다.
3. 런타임에 탐색 작업을 거치지 않게 해준다.
참고사이트 : https://www.nextree.co.kr/p7650/
'Web > javascript' 카테고리의 다른 글
| JSDoc 문법 정리 (0) | 2023.01.18 |
|---|---|
| JSDoc 개념 (0) | 2023.01.18 |
| prototype의 이해 (0) | 2023.01.16 |
| arguments 객체 (0) | 2023.01.16 |
| 쿠키 (0) | 2023.01.13 |