특집기사:코어 자바 스크립트 1 - 속성

위클립스
이동: 둘러보기, 찾기
Article.png 특집기사 정보
Jeeeyul.jpg
저자 이지율

이 문서는 Java Script의 속성 모델이 갖고 모순들이, 오늘날 어떻게 설명되는지를 알아보고, 이를 이용하여 안전한 기본 객체 확장을 예제를 통해 다뤄본다. 이 문서는 독자가 Java Script에 대해 어느 정도의 경험이 있다고 가정한다.

목차

[편집] 목적

우리는 모든 자바스크립트의 객체가 valueForKeyPath 라는 메소드를 갖도록 해 볼 생각이다.

이 메서드는 Objective-C에서 얻은 아이디어로, 주어진 인자를 키 패스로 취급하여 값을 얻는 함수이다.


예시:

var eclipse = {
   plugins : {
      jdt : {
         author: "ibm",
         version : "1.0"
      },
      cdt : {
         author: "someone",
         version : "2.0"
      }
   }
};
 
var cdtVersion = eclipse.valueForKeyPath("plugins.cdt.version");
 
// 2.0 이 출력된다.
console.log(cdtVersion);

[편집] 워밍업 삽질하기

모든 객체에 동작을 추가하는 가장 쉬운 방법은 Object.prototype 을 변형하는 것이다. 그러나 이 방식은 맹렬하게 반대되는 대표적인 안티 패턴이다.

가령 다음과 같이 구현했다고 가정하자:

Object.prototype.valueForKeyPath = function(){
   return "test";
};

이러면 곧 다른 스크립트들이 아수라장이 된다. 왜냐하면 모든 JSON 객체들이 추가로 valueForKeyPath라는 추가 속성을 갖게 되어 모든 for-in 문이 교란되기 때문이다.

var aObj = {
   name : "eclipse",
   age : 11
};
 
for(i in aObj){
   console.log(i);
}

위의 코드를 실행 하면 다음과 같은 결과가 나타난다.

name
age
valueForKeyPath

이는 명백히 의도되지 않은 결과 일 것이다. 대게의 Java Script 라이브러리는 이런 상황을 견디지 못한다. Object는 모든 일반 데이터 객체에도 영향을 미치므로, Date나 String 클래스의 프로토타입을 정의하는 것과는 완전히 다른 수준 문제가 된다.

이런 상황을 견디는 협업 개발자들은 매우 세심해야 하는 만큼(방법이 없는 것은 아니다), 높은 인건비를 필요로 하므로, 사실상 불가능하다고 보는 것이 옳다.

[편집] 속성 이해하기

이 문제를 해결하기 위해서는 Java Script 객체의 속성을 좀 더 잘 이해할 필요가 있다.

Java Script에서 속성을 정의 하는 가장 간단한 방법은 다음과 같다.

var object = {};
 
// name 속성을 정의
object.name = "eclipse";

이렇게 만들어진 속성은 기본적으로 다음과 같은 특징을 갖는다:

뭐야 안 그런 속성도 있어? 라고 생각하는 사람들도 있을텐데, 잘 생각 해보면 매우 많다:

var list = new Array();
list.push("test-element");
list.name = "test-name";
 
list.forEach(function(each){
   console.log(each);
});

위의 코드를 실행해 보면 test-element 만 나올 뿐, test-name은 나오지 않는다. 또한 Array가 갖고 있는 length, splice 등도 등장하지 않음을 상기하자.

마찬가지로 아래의 코드들도 의도된 동작이 수행되지 않을 것이다:

// alert 을 지워 버리기
delete window.alert;
 
// location 객체 갈아치우기
window.location = {};

그리고 어떤 속성들은 값을 할당하는 것 만으로 어떤 행동이 유발 되기도 한다. 가장 대표적인 예는 HTML DOM 객체의 innerHTML이나 innerText 값을 변경한 경우이다. 이 경우 UI가 갱신되는 동작을 수반하게 된다.

이쯤 되면 단순히 할당문(=)으로 값을 지정했다 하더라도, 어떤 setter 함수가 이용되었거나, 값이 지정된 사실이 이벤트로 전파되었음을 의심해 봐야 하지 않을까?

[편집] Property Descriptor

특정 속성들이 이처럼 다른 방식으로 작동하는 것은, 네이티브 객체들에 대해서만 한정적으로 일어나는 일이었다. 즉 순수 자바스크립트만으로 이러한 다형성을 설명하거나 비슷하게 구현하는 것이 불가능 했다.

오늘날 이러한 속성의 차별성은 객체와 속성 사이에 존재하는 Property Descriptor[1]에 의해 설명될 수 있다.

이는 속성들 간의 동작 차이가 발생하는 모순을 설명하고, 개발자가 이를 통제할 수 있도록 Java Script 1.8.5에서 추가 되었다.

특정 객체의 속성의 Property Descriptor는 다음과 같이 얻을 수 있다:

// window 객체의 "location" 속성에 대한 기술자를 얻음.
Object.getOwnPropertyDescriptor(window, "location");

이렇게 얻어진 기술자는 다음과 같은 속성들을 갖는다:

속성명 설명 기본값
configurable 이 속성 기술자 자체가 나중에 변경되거나, 속성 자체가 삭제될 수 있다면 true이다. false
writable 이 값이 true 이면 할당문으로 값을 고쳐 쓸 수 있다. false
enumerable 이 값이 true 인 경우, for-in 문에서 이터레이트 된다. false
value 이 속성과 연결된 실제 값으로, 정적인 객체 또는 값이거나 함수이다. undefined
get 속성을 읽는 구문이 실행된 경우, 값을 반환 하는 함수이다. 이 함수가 없다면 value 속성이 이용된다. undefined
set 속성에 할당문등으로 값이 지정될 때 호출되는 setter 함수이다. 이 함수는 하나의 인자를 받아, 속성을 갱신하도록 의도된다. 이 함수가 지정되어 있지 않다면 value 값을 직접 고쳐 쓰게 된다. undefined

참고로 기본값 컬럼은 다음절에서 설명할 속성 기술자를 통한 속성 정의시 적용된다.

[편집] 속성 기술자를 통한 속성 정의

반면 Object.defineProperty 함수를 통해, 속성 기술자를 명시적으로 제공 하여 속성을 정의 할 수도 있다. 호출 형태는 다음과 같다:

Object.defineProperty(객체, "속성명", 속성기술자);

Object.prototype의 확장을 반대하는 세력의 가장 큰 이유는 for-in 문의 교란이기 때문에 enumerable 값만 false로 주면, 논란을 잠재울 수 있다.

앞서 설명한 우리의 목적을 이를 이용해 구현하면 다음과 같다:

Object.defineProperty(Object.prototype, "valueForKeyPath", {
   value : function(path) {
      var finger = this;
      var segments = path.split(".");
 
      for (i in segments) {
         var each = segments[i];
         finger = finger[each];
         if(finger == undefined || finger == null){
            break;
         }
      }
 
      return finger;
   },
   writable : true,
   configurable : true,
   enumerable : false  // for-in 을 교란시키지 않기 위해 이터레이션 대상이 아님으로 정의한다.
});

이 방식을 이용하면 다른 협력자들을 교란하지 않고도, 안전하게 원하는 클래스의 프로토타입을 변경하는 것이 가능하다. (그러나 여기에도 global 프로토타입을 과연 안전하게 조작할 수 있겠느냐 하는 논란[2]은 있다)

[편집] 브라우저 호환성

늘 그렇듯 IE가 문제다. IE8의 경우 Object.defineProperty()가 되기는 되지만 HTML DOM Element에 대해서만 속성을 정의할 수 있으며, 그 동작 또한 표준과는 약간의 거리가 있다.

[편집] 숙제

다음편에서는 빌어먹을 this 키워드와, 함수 호출시 벌어지는 일들, 그리고 이를 통제하는 방법들에 대해 알아볼 예정이다.

[편집] 참조

  1. Property Descriptor
  2. extending Object.prototype

이 기사에 대한 의견은 토론 페이지를 통해 나눌 수 있습니다.

개인 도구
이름공간
변수
행위
포탈
탐색
도움
도구모음