메뉴 바로가기 검색 및 카테고리 바로가기 본문 바로가기

한빛출판네트워크

IT/모바일

this가 없는 자바스크립트

한빛미디어

|

2014-07-21

|

by HANBIT

23,418

제공 : 한빛 네트워크
저자 : Pete Hodgson
역자 : 황민욱
원문 : Javascript without the this

Pete Hodgson 많은 자바스크립트의 문제중 하나는 "this"가 작동하는 방법에서 비롯된다. 이 문제는 자바스크립트의 일반 변수들에게 적용되는 어휘적인 스코프와 의미적 스코프가 달라진 순간부터 꽤 어려워 진다. 왜냐하면 this의 동작은 어휘적인 스코프에 따라 꽤나 달라질 수 있기 때문이다. 이러한 것을 피하기 위해 우리는 종종 다음과 같은 트릭을 사용한다.
function blah(){
  var that = this;
  somethingThatRebindsThings( function(){
    that.whatever();
  });
}
자바스크립트로 꽤 개발을 해본 사람이라면 누구나 이 this의 동작 방식에 대해 고민을 하게 된다. 이 고민을 하지 않아도 되는 개발을 생각해 보자. 어떻게 해야할까? 사실, 가장 쉬운 방법은 this를 사용하지 않는 방법이다. 미친소리 같은가? 그렇다면 다음을 읽어보라.

왜 this인가?

this를 사용하는 가장 큰 이유는 상태(state)와 행동(behavior)이 함께 들어있는 가장 유용한 abstraction이기 때문이다. 좀더 정확히 말하자면 프로퍼티(property)와 메서드(method)가 들어있다. 만약 우리가 this를 사용하지 않는다면 당신은 이 강력한 abstraction을 잃어버리게 될 것이라고 생각할 수도 있다. 자기 오브젝트의 다른 데이터에 어떻게 해야 메서드가 접근 할 수 있을까? 어쩌면 당신은 이미 답을 알아냈는지도 모른다. 바로, 클로져(closure)를 사용하는 것이다.

당신은 클로져가 상태와 행동 모두 담길 수 있는 또 다른 방법이라는 것을 기억해야 한다. 이제부터 전통적인 this 기반의 자바스크립트 코드를 클로져 기반의 자바스크립트 코드로 변환시키는 것을 볼 것이다. 이제 Car 타입을 실행시키는 기본-바닐라-자바스크립트 코드가 있다.
function Car(numberOfDoors){
  this.numberOfDoors = numberOfDoors;
  this.numberOfWheels = 4;

  this.describe = function(){
    return "I have " + this.numberOfWheels +
      " wheels and " + this.numberOfDoors + " doors.";
  }
}

var sportsCar = new Car(2);
console.log( sportsCar.describe() );
다음은 클로져를 통해서 똑같은 동작을 하는 코드이다.
function createCar(numberOfDoors){
  var numberOfWheels = 4;

  function describe(){
    return "I have " + numberOfWheels +
      " wheels and " + numberOfDoors + " doors.";
  }

  return {
    describe: describe
  };
}

var suv = createCar(4);
console.log( suv.describe() );
내가 실행한 createCar 생성자는 Car 타입에 대한 모든 상태(state)와 행동(behavior)을 정의하고 오브젝트 외부에서 접근해야할 public 메서드에 대한 정보만 반환한다. 생성자 내에 다른 것들은 밖에서는 필요하지 않다, 그렇지만 클로져 덕분에 생성자와 함꼐 생성된 모든 것들이 서로에게 접근 할 수 있게 되었다. 생성자를 새로 호출한다면 생성자는 매번 상태와 행동이 모두 들어 있는 새로운 클로져를 만든다.

상속(inheritance)

상속은 어떨까? 일반적으로 이것은 this를 사용해서 프로토타입 상속에 의해 다루어 진다. 그렇지만 이 글은 this를 사용하지 않는 글이다. 그러니 창조적인 우회법으로 상속을 받아보자.
function createMiniVan(capacity){
  var car = createCar(4);
  car.capacity = function(){
    return "I have room for " + capacity + " passengers.";
  };
  return car;
}

var miniVan = createMiniVan(7);
console.log( miniVan.describe() );
console.log( miniVan.capacity() );
이 예제에서 나는 Car로부터 모든 public 함수들을 상속받고 새 함수들을 가진 새로운 MiniVan 타입을 만들었다. 이 방법은 마치 Ruby나 CLOS에서의 믹스인(Mixins)과 비슷하다. 이 접근 방법의 잠재적인 단점은 sub-나 super-와 같이 내부의 상태나 행동에 접근하는 것을 허락하지 않는 다는 것이다. 그러니까 다시 말하자면 이것은, 암묵적으로 protected되어 있다는 것이다. 하지만 나는 이 protected된 접근이 오히려 더 유용하다는 아주 약간의 예시만을 보여주었다. 이제부터 더 많은 예제들을 보여주겠다.

구성(Composition)

여기 예제는 당신이 구성을 이용해 새로운 행동을 추가하는 것을 보여준다.
function createOdometer(){
  var mileage = 0;

  function increment(numberOfMiles){ mileage += numberOfMiles; }
  function report(){ return mileage; }

  return {
    increment: increment,
    report: report
  }
}

function createCarWithOdometer(numberOfDoors){
  var odometer = createOdometer();
  var car = createCar(numberOfDoors);

  car.drive = function(numberOfMiles){
    odometer.increment(numberOfMiles);
  }

  car.mileage = function(){
    return "car has driven " + odometer.report() + " miles";
  }

  return car;
}
나의 createCarWithOdometer 생성자에 몇몇 추가적인 메서드를 구현하기 위해서 나는 odometer를 만들고 사용했다. 그리고 나는 기반이 되는 Car 인스턴스를 만들고 새로운 behavior를 인스턴스에 더했다. 결과적으로 내가 가진것은 odometer에 의해 제공되는 함수를 사용해서 Car 타입을 확장하는 새로운 타입이다. 이 모두 prototype을 사용하거나 this를 사용하지 않고 만들어진 결과이다.

이 글이 사실인가?

그럼. 당연히 사실이다. 나는 꽤 큰 팀에서 이 방법을 이용해 큰 자바스크립트 애플리케이션을 만드는 작업을 해본적이 있다. 우리는 수천라인의 코드에서 this를 합쳐야 수십번 사용해 보았고 우리는 꽤 만족했다.

우리가 이 방법을 좋다고 생각하는 이유들에 대해 설명하자면 우선, 이것은 우리에게 꽤 섬세한 에러들을 회피하는 것을 허락해 주었다. 그리고 더이상 jQuery를 재선언을 이용해 this를 사용할때 생기는 혼란을 피할 수 있다. 두번째로 생성자 내부에서 기반이 되는 타입에 더해지는 구성은 굉장히 강력한 기능이 될 수 있다. 이는 다중상속시 다이아몬드 의존성 문제(diamond dependency)없이 가능하게 해준다. 마지막으로 가장 큰 장점은 파인 그레인드(fine-grained)가 된다는 것이다. 클래스의 API를 사용할 때 모든 타입들은 public이 아닌 생성자 내부에서 생성된 클로져로 사용하게 된다. 그렇기에 나는 이러한 점들이 아주 커다란 애플리케이션을 작성할때 성공한 이유라고 생각한다.
TAG :
댓글 입력
자료실