본문으로 바로가기

자바스크립트 설계 패턴 -  Namespace Pattern

네임스페이스는 프로그램에서 필요로 하는 전역 변수의 개수를 줄이는 동시에 과도한 접두어를 사용하지 않고도 이름이 겹치지 않게 해줍니다.

자바스크립트의 언어 문법에 내장되어 있지는 않지만, 네임스페이스는 꽤 쉽게 구현할 수 있는 기능입니다.






네임스페이스 패턴

대개 애플리케이션이나 라이브러리를 위해  전역 유효 범위에 수많은 함수, 객체, 변수들로 어지럽히지 않도록 하기 위해 전역 객체를 하나 만들고(단 하나만 만드는 것이 이상적이다) 모든 기능을 이 객체에 추가하는 패턴을 네임스페이스 패턴이라고 합니다.


다음의 예제는 안티 패턴입니다.

JavaScript
// 수정 전 : 전역 변수 5개
// 경고 : 안티 패턴

// 생성자 함수 2개
function Parent() {}
function Child() {}

// 변수 1개
var some_var = 1;

// 객체 2개
var module1 = {};
module.data = {
	a : 1,
	b : 2
};
var module2 = {}

위와 같은 코드를 리팩터링하기 위해서는 먼저 애플리케이션 전용 전역 객체, 이를 테면   MYAPP을 생성합니다.

그런다음 모든 함수와 변수들을 이 전역 객체의 프로퍼티로 변경합니다.

다음의 예제는 리팩터링한 결과입니다.

JavaScript
// 수정 후 : 전역 변수 1개

// 전역 객체
var MYAPP = {};

// 생성자
MYAPP.Parent = function() {};
MYAPP.Child = function() {};

// 변수
MYAPP.some_var = 1;

//	객체 컨테이너
MYAPP.modules = {};

// 객체들을 컨테이너 안에 추가한다.
MYAPP.modules.module1 = {};
MYAPP.modules.module1.data = { a : 1, b : 2 };
MYAPP.modules.module2 = {};

전역 네임스페이스 객체의 이름은 애플리케이션 이름이나 라이브러리의 이름, 도메인명, 회사 이름 중에서 선택할 수도 있습니다.

흔히 코드를 읽는 사람 눈에 띄도록 전역 객체 이름은 모두 대문자로 쓰는 명명 규칙을 사용하기도 합니다.(이 규칙은 상수를 쓸 때도 사용된다는 점에 주의하라.)


이 패턴은 코드에 네임스페이스를 지정해주며, 코드 내의 이름 충돌 뿐 아니라 이 코드와 같은 페이지에 존재하는 자바스크립트 라이브러리나 위젯 등 서드 파티 코드와의 이름 충돌도 방지해 줄 수 있습니다.

다양한 작업에 응용할 수 있으며, 매우 권장하는 패턴입니다. 그러나 다음과 같은 단점도 존재합니다.

  • 모든 변수와 함수에 접두어를 붙여야 하기 때문에 전체적으로 코드량이 약간 더 많아지고 따라서 다운로드해야 하는 파일 크기도 늘어난다.
  • 전역 인스턴스가 단 하나뿐이기 때문에 코드의 어느 한 부분이 수정되어도 전역 인스턴스를 수정하게 된다. 즉 나머지 기능들도 갱신된 상태를 물려 받는다.
  • 이름이 중첩되고 길어지므로 프로퍼티를 판별하기 위한 검색 작업도 길고 느려진다. 



범용 네이스페이스 함수

프로그램의 복잡도가 증가하고 코드의 각 부분들이 별개의 파일로 분리되어 선택적으로 포함하게 되면, 어떤 코드가 특정 네이스페이스나 그 내부의 프로퍼티를 처음으로 정의한다고 가정하기가 위험합니다.

네임스페이스에 추가하려는 프로퍼티가 이미 존재할 수도 있고 따라서 내용을 덮어쓰게 될 지도 모릅니다.

그러므로 네임스페이스를 생성하거나 프로퍼티를 추가하기 전에 먼저 이미 존재하는지 여부를 확인하는 것이 최선입니다.

다음의 예제를 살펴봅니다.

JavaScript
// 위험한 패턴
var MYAPP = {};

// 개선된 패턴
if(typeof MYAPP === 'undefined') {
	var MYAPP = {};
}
// 또는 더 짧게 쓸 수 있다.
var MYAPP = MYAPP || {};

이렇게 추가된 확인 작업 때문에 상당량의 중복 코드가 생겨날 수 있습니다.

예를 들어 MYAPP.modules.module2 를 정의하려면, 각 단계의 객체와 프로퍼티를 정의할 때마다 확인 작업을 거쳐야 하므로 코드가 세 번 중복될 것입니다.

따라서 네임스페이스 생성의 실제 작업을 맡아 줄 재사용 가능한 함수를 만들어두면 편리합니다.

이러한 재사용이 가능한 네임스페이스 생성 함수를 namespace()라 만들고 다음과 같이 사용할 수 있습니다.

JavaScript
// 네임스페이스 생성 함수를 사용한다.
MYAPP.namespace('MYAPP.modules.module2');

// 위 네이스페이스 함수는 다음과 같은 결과를 반환할 것이다.
var MYAPP = {
	modules : {
		module2 : {}
	}
};

이제 위와 같은 결과를 반환할 수 있는 네임스페이스를 구현해 보도록 합니다.

다음과 같은 방식은 해당 네임스페이스가 존재하면 덮어쓰지 않기 때문에 기존의 코드를 망가뜨리지 않는다.

JavaScript
ar MYAPP = MYAPP || {};

MYAPP.namespace = function (ns_string) {
	var parts  = ns_string.split('.'),
		parent = MYAPP,
		i;

	// 처음에 중복되는 전역 객체명은 제거한다.
	if(parts[0] === MYAPP) {
		parts = parts.slice(1);
	}

	for (i = 0; i < parts.length; i += 1) {
		// 프로퍼티가 존재하지 않는다면 생성한다.
		if(typeof parent[parts[i]] === 'undefined') {
			parent[parts[i]] = {};
		}
		parent = parent[parts[i]];
	}
	return parent;
};


위 코드는 다음과 같이 사용할 수 있습니다.

JavaScript
// 반환 값을 지역 변수에 할당한다.
var module2 = MYAPP.namespace('MYAPP.modules.module2');
module2 === MYAPP.modules.module2; // true


namespace는 자바스크립트에서 향후 사용하고자, 일반적인 사용을 금지한 예약어에 포함되어 있다.

따라서 이 단어 그대로를 프로퍼티명으로 쓰는 것은 권하지 않는다.



Jaehee's WebClub