Mixin 패턴

Mixin은 상속 없이 어떤 객체나 클래스에 재사용 가능한 기능을 추가할 수 있는 객체이다. Mixin은 단독으로 사용할 순 없고 상속 없이 객체나 클래스에 기능을 추가하는 목적으로 사용된다.

앱에서 여러 강아지를 만들어야 한다고 가정해 보자. 아래 강아지 클래스는 name 프로퍼티 외에 다른 프로퍼티를 가지고 있지 않다.

class Dog {
  constructor(name) {
    this.name = name
  }
}

강아지는 이름 외에도 짖거나 꼬리를 흔들거나 신나게 놀 수 있어야 한다. Dog클래스에 직접 정의하는 대신 bark, wagTail, play를 프로퍼티로 가진 믹스인을 만들 수 있다.

const dogFunctionality = {
  bark: () => console.log('Woof!'),
  wagTail: () => console.log('Wagging my tail!'),
  play: () => console.log('Playing!'),
}

Object.assign 메서드를 이용해 dogFuncionality 믹스인의 기능을 Dog 프로토타입에 추가할 수 있다. 아래 예시에서는 Dot.prototype에 프로퍼티들을 추가하고 있다. 이 경우 새로 만들어지는 Dog 클래스의 인스턴스들은 Dog 의 프로토타입 객체에 추가된 dogFunctionality의 기능들을 사용할 수 있다.

class Dog {
  constructor(name) {
    this.name = name
  }
}

const dogFunctionality = {
  bark: () => console.log('Woof!'),
  wagTail: () => console.log('Wagging my tail!'),
  play: () => console.log('Playing!'),
}

Object.assign(Dog.prototype, dogFunctionality)

아래 예시는 믹스인으로 기능이 추가된 Dog의 클래스를 테스트해보고 있다.

const pet1 = new Dog('Daisy')

pet1.name // Daisy
pet1.bark() // Woof!
pet1.play() // Playing!

상속 없이 객체에 기능을 추가할 수 있지만 믹스인 자체는 상속을 사용할 수 있다.

대부분의 포유류는 걷거나 잠을 잘 수 있다. 강아지는 포유류이니 똑같이 걷거나 잘 수 있어야 한다.

아래 예제에서는 walk, sleep 프로퍼티를 가진 animalFunctionality믹스인을 만들고 있다.

const animalFunctionality = {
  walk: () => console.log('Walking!'),
  sleep: () => console.log('Sleeping!'),
}

위에 언급했던 것 처럼 Object.assign을 사용하여 dogFunctionalityanimalFunctionality의 프로퍼티를 추가할 수 있다.

const animalFunctionality = {
  walk: () => console.log('Walking!'),
  sleep: () => console.log('Sleeping!'),
}

const dogFunctionality = {
  bark: () => console.log('Woof!'),
  wagTail: () => console.log('Wagging my tail!'),
  play: () => console.log('Playing!'),
  walk() {
    super.walk()
  },
  sleep() {
    super.sleep()
  },
}

Object.assign(dogFunctionality, animalFunctionality)
Object.assign(Dog.prototype, dogFunctionality)

이제 만들어지는 Dog 클래스의 인스턴스들은 모두 walk, sleep 메서드를 사용할 수 있게 되었다.


Window 객체는 WindowOrWorkerGlobalScopeWindowEventHandler 의 믹스인으로 구성되어 있기 때문에 setTimeout, setInterval, indexedDB, isSecureContext 같은 프로퍼티를 사용할 수 있다.

WindowOrWorkerGlobalScope는 믹스인이기 때문에 해당 믹스인을 직접 사용할수는 없다.


React (ES6 이전)

ES6 클래스 문법이 소개되기 전에 믹스인은 React에서 컴포넌트에 기능을 추가하기 위해 종종 사용되었다. React개발팀은 mixin을 사용하지 말아 주세요. 라는 글과 함께 컴포넌트의 믹스인이 복잡도를 증가시키고 재사용하기 어렵게 만든다고 이야기했다. 대신 React개발팀은 지금은 훅에 의해 대체 가능하지만 고차 컴포넌트를 사용하길 권장했었다.


믹스인은 상속을 하지 않고도 객체의 프로토타입에 특정 기능들을 주입할 수 있다. 다만 객체의 프로토타입을 직접 수정하는 것은 의도하지 않은 프로토타입 프로퍼티의 수정으로 이어질 수 있어 주의가 필요하다.

참조