본문 바로가기
Javascript

자바스크립트(javascript) 객체 응용

by 램쥐뱅 2017. 5. 30.

1. 네임스페이스 구현


자바스크립트 코드의 재사용과 생산성을 향상시키고자 많은 라이브러리가 나오고 있다.

라이브러리를 제작할 때 일반적으로 가장 중요한 것은 다른 모듈로부터 독립성을 보장하고 상호 간섭하지 않게끔 고유한 네임스페이스(namespace)를 사용하는 것이다.


라이브러리의 독립성을 보장하기 위해 네임스페이스를 사용하자.


네임스페이스 구현을 위한 아이디어는 간단하다. 다른 라이브러리와의 변수 이름 충돌을 최소화하기 위해 전역변수는 되도록이면 사용하지 않는다. 대신 라이브러리별로 루트 객체를 하나 만들고 라이브러리의 모든 속성과 메서드는 해당 루트 객체의 속성으로 만드는 것이다.




라이브러리의 루트 객체 정의


예제로 만들 라이브러리의 루트 객체를 People 라고 하자


var People = People || {};


라이브러리가 여러 파일에 분산되어 존재할수 있다는 가정에 모든 분산 파일의 가장 처음에는 위와 같은 코드를 포함시킨다.

만약 여러 파일에서 동일하게 var People = {}; 를 사용한다면 파일이 로딩될 때마다 People 객체는 초기화 되어 버린다.


제일 먼저 로딩되는 파일에서는 People이 undefined로 되어 빈 객체가 생성되고 다음에 로딩되는 파일에서는 기존에 생성되었던 People 객체를 계속해서 사용하게 된다.




하위 네임스페이스 생성


이제 People 객체의 속성으로 요소들을 만들수 있다.


People.Person = {};



Person의 멤버는 객체리터럴을 이용해 Person 객체를 생성할 때 함께 정의할 수 있다.


People.Person = {

get : function(name) {

//name에 해당하는 person 인스턴스를 반환한다.

},

add : function(name) {

//name에 해당하는 Person 인스턴스를 생성해서 추가한다.

}

//..

};




네임스페이스 만들기 함수 제작


자바스크립트 라이브러지중에는 네임스페이스를 만드는 유틸 함수를 정의해 사용하기도 한다.

createNamespace 이름으로 함수를 만들어본다면 다음과 같이 만들 수 있다.


var People = {};

People.createnamespace = function(namespace){

var parts = namespace.split('.');

var current = this;

for ( var i in parts) {

if(!current[parts[i]]) {

current[parts[i]] = {};

}

current = current[parts[i]];

}

}



People.createNamespace("Person.Korean");


위 함수의 결과로 아래와 같이 네임스페이스를 생성할수 있는 효과를 내줄수있다.


People.Person = {};

People.Person.Korean = {};




2. 자동 호출 패턴


익명 함수를 이용하여 자동 호출 패턴을 만들수 있다.


(function() {    //익명함수

//코드

})()(;


런타임에 위와같은 코드가 실행되면 우선 첫 번째 괄호 안에 있는 함수가 정의되고, 그 함수에 대한 참조가 반환된다.

여기서 반환된 함수 객체 참조는 마지막에 붙어있는 괄호에 의해 호출된다.


이러한 구조를 이용하면 프로그램을 실행할 때 함수를 정의하고 바로 호출할 수 있게 된다.

이와같은 자동 호출(self-invoking) 구조는 자바스크립트 프로그램 또는 라이브러리의 초기화 작업에 사용될 수 있다.




라이브러리 초기화


var globalObject = this;

(function(g) {

var people= {};

people.createNamespace = function(namespace){ //.. };


people.createNamespace("Person.Korean");


people.Person.get = function(name){ };

people.Person.add = function(name){ };


people.Person.Korean.get = function(name){ };

people.Person.Korean.add = function(name){ };


// g에 People 속성으로 people 할당

g.People = people;

}) (globalObject);


this 가 달당된 glovalObject를 전달하고 있는 익명함수, 만약 웹 어플리케이션이라면 최상위 객체인 Window가 this에 할당되어 익명함수에 전달된다.

익명 함수 내부에서는 네임스페이스의 최상위 객체 people을 구성한다.

기존에 만들어보았던 createNamespace 함수로 people.Person, people.Person.Korean 그리고 각각의 멤버를 구성한 후 지역 변수인 person을 g에 할당해 전역 변수 객체의 속성으로 할당한다.


이제 코드에서 다음과 같이 사용할 수 있다.


var mySon = window.People.Person.get("");

People.Person.Korean.add(""); //외부의 스크립트 코드에서는 루트 객체(window)를 생략하는것이 가능




3. 싱글톤 패턴 구현


싱글톤(singleton) 패턴은 전체 시스템에서 하나의 인스턴스만 존재하도록 보장하는 객체 생성 패턴을 말한다.


var singletonObject = {

k1 : "value",

k2 : function() { ... }

}


그러나 객체 리터럴로는 비공개 상태 및 함수를 정의 할 수 없다. 규모가 큰 라이브러리에서는 흔히 외부에서 접근할 수 없는 비공개 멤버를 가지고 있다.

자바스크립트에서 이런 비공개 멤버가 필요하다면 제일 먼저 클로저를 떠올리면 된다.


일반 객체지향 언어에서 사용되는 객체는 외부에서 접근할 수 있는 공개 멤버와 외부에서 접근할 수 없는 비공개 멤버로 구성된다.

대부분의 객체는 공개 멤버뿐 아니라 비공개 멤버도 하나의 모듈 안에 캡슐화 되어 있다.



클로저를 배우면서 "자유 변수" 라는 것을 접했는데, 이 자유 변수를 비공개 멤버를 구현하는데 사용할 수 있다.

비공개 멤버를 포함한 객체를 구현해야 하는 상황이 되면 항상 클로저를 생각해야 한다.


싱글톤에서 현재 생성된 객체가 존재하는지 알려주는 내부 변수를 클로저의 자유 변수로 구현할 수 있다.


function singletonFunc(){

// 비공개 변수, 메서드

var instantiated;

function init() {

return {

publicMethod : function(){

return 'Hello World';

},

publicProperty : 'test'

};

}


// public 메서드인 getInstance()를 정의한 객체

// 렉시컬 특성으로 인해, 비공개 변수, 메서드 접근 가능.

return {

getInstance : function() {

if ( ! instantiated ) {

instantiated = init();

}

return instantiated;

}

};

}


init()의 return 문에서 리터럴로 정의되는 객체가 바로 싱글톤 객체다. 이 객체는 프로그램 전체에서 하나만 존재하게 된다.


원래 클로저의 의미는 내부 변수에 접근하는 익명 함수를 반환하는 구조인데, 싱글톤 패턴에서는 이를 약산 수정해서 내부 변수에 접근할 수 있는 객체를 반환하는 구조를 취한다.


싱글톤 패턴에서는 내부 변수에 접근할 수 있는 객체를 반환하는 클로저 구조를 이용한다.


위 코드에서는 변수의 렉시컬한 특성으로 인해 내부의 getInstance 함수에서 비공개 변수인 instantiated 에 접근할 수 있다는 것과 getInstance() 호출이 끝나도 instantiated 값은 계속 유지되는 특성을 이용해 publicMethod(), publicProperty가 포함된 객체를 유일하게 생성한다.


위의 싱글톤 구현 코드를 사용하려면 다음과 같이 자동 호출 패턴의 코드를 사용할 수 있다.


var Singleton = (

// 싱글톤 패턴을 구현한 클로저 코드

)();


var first = Singleton.getInstance();

first.publicMethod();

var second = Singleton.getInstance();

first === second // true


Singleton.getInstance()를 몇번 호출하더라도 결과로 얻는 객체는 모두 동일한 싱글톤 객체를 가리킨다.




4. 모듈 패턴 구현


모듈(module)이라는 것은 전체 애플리케이션의 일부를 독립된 코드로 분리해서 만들어놓은 것을 말한다.

관련된 유용한 기능을 모아둔 객체도 모듈이라 할 수 있고, 크게는 jQuery 같은 라이브러리도 모듈이라 할 수 있다.

자바스크립트에서 모듈을 구현하는 가장 쉬운 방법은 객체 리터럴을 사용하는 방법이다.


var myModule = {

publicKey : variableValue,

publicMethod : function(){

return this.publicKey;

}

}


객체 리터럴을 싱글톤 패턴이라고도 했다. 객체 리터럴이 하나의 객체라는 점에서는 싱글톤 패턴이라고 할 수도 있고, 독립된 모듈이라는 점에서는 모듈 패턴의 하나라고도 할 수 있다.

즉 동일한 코드를 어떤 관점에서 보느냐에 따라 다양한 패턴이 될 수 있다.


독립된 모듈은 자체적으로 필요한 내부 변부 변수 및 내부 함수를 모두 갖고 있어야 한다. 이처럼 자체적으로 필요한 내부 변수와 함수를 가지는 객체를 생성해야 한다고 했을때도 바로 클로저를 연상하면 된다. 



그림을 보면 자동으로 호출하는 익명함수가 return 하는 곳에서 객체 리터럴로 생성된 객체를 반환한다.

이 객체에는 공개하고 싶은 멤버가 정의되어 있다.


다음은 클로저를 이용해 모듈 패턴을 구현한 예제이다.


function(){

// 은닉될 멤버

var privateKey = 0;

function privateMethod( ) { return ++privateKey; };

// 공개될 멤버

return {

publicKey : privateKey,

publicMethod : function() {

return privateMethod();

}

}

}


모듈 패턴은 반환값이 함수가 아니라 객체이며, 자동 호출된다는 점만 제외하고 클로저와 유사하다.

그리고 객체를 여러 개 만들어 낼 수 있는 구조라는 점에서는 싱글톤 패턴과도 차이가 있다.


위 모듈 패턴을 구현한 코드를 사용하려면 싱글톤 패턴의 코드를 실행할 때 본것처럼 자동 호출 패턴을 이용하는 코드를 사용할 수 있다.


var Module = (

// 모듈 패턴을 구현한 클로저 코드

)();


Module.publicMethod();


자동으로 호출되는 구조를 사용하지 않아도 유용한 코드가 된다.

클로저 함수를 바로 Module에 할당해 보자. (위 그림처럼)


var m1 = Module();

m1.publicMethod(); // 1

m1.publicMethod(); // 2


var m2 = Module();

m2.publicMethod(); // 1



클로저 인스턴스 1,2 에서 정리 했던 예제와 유사하다.


한 가지 차이점은 클로저에서는 내부의 익명 함수에서 반환하는 값이 함수이지만 모듈은 함수가 아닌 객체이다.




5. 메서드 체인 패턴 구현


메서드 체인 패턴은 자바스크립트 라이브러리에서 흔히 나타나는 패턴으로서 한 줄로 여러개의 메서드를 차례로 호출할 수 있는 구조를 말한다.

이 패턴은 몇 개의 관련 메서드를 계속해서 호출할 때 상당히 편리하다. 중간 단계의 변수를 사용하지 않고도 이전 메서드의 결과를 대상으로 다시 다음 메서드를 호출할 수 있다.


다음 코드와 같이 DOM요소를 대상으로 작업을 하는 createElement 생성자를 만들었다고 가정해보면, <lable> 요소를 생성해 그것에 텍스트와 스타일을 설정한 다음 <body>에 추가하는 코드는 다음과 비슷할 것이다.


var obj = new MyApp.DOM.createElement("label");

obj.setText('텍스트');

obj.setStyle('color','red');

obj.setStyle('font','Gulim');

document.body.appendChild(obj);


생성자가 new와 함께 호출되면 자바스크립트는 생성자의 반환값으로 생성된 해당 인스턴스를 돌려준다.

이와 비슷하게 객체의 메서드 메서드가 작업을 끝낼 때 마다 this를 반환해 줄 수 있다.


var Element = {

setText : function(text){

// this 에 대해 작업 수행

// ...

// this 반환

return this;

},

setStyle : function(style, value){

// this 에 대해 작업 수행

// ...

// this 반환

return this;

}

};


setText, setStyle 에서 반환되는 this는 이전까지의 작업에 의해 상태가 변경된 객체다. 따라서 반환값에 대해 메서드를 호출해 상태 변경을 계속해서 누적할 수 있다. 이런식으로 메서드 체인을 만들 수 있다.


만약 MyApp.DOM.createElement 가 반환하는 객체가 위에서 예를든 Element라면 다음과 같은 코드가 가능하다.


document.body.appendChild(

//createElement 반환값이 위에서 예를 든 Element 일 때

new MyApp.DOM.createElement('label').setText('텍스트').setStyle('color','red').setStyle('font','Gulim')

);













참고.


자바스크립트 객체지향 프로그래밍

댓글