JS-믹스인

topics 200-프론트개발
types 이론 학습
tags
references javascript.info/mixins ko.javascript.info/mixins

믹스인(Mixin)이 뭘까

자바스크립트는 단일 상속만 지원한다. 한 클래스는 하나의 부모 클래스만 상속받을 수 있다.

근데 여러 클래스에서 메서드를 가져오고 싶을 때가 있다. 이때 믹스인(Mixin) 패턴을 사용한다.

단일 상속의 한계

class Animal {
  eat() {
    console.log('먹는다');
  }
}

class Bird extends Animal {
  fly() {
    console.log('난다');
  }
}

// 만약 Bird가 Animal과 동시에 다른 클래스도 상속받고 싶다면?
// class Bird extends Animal, Swimmer { } // 에러! 다중 상속 불가

자바스크립트는 다중 상속을 지원하지 않는다. Java, C++처럼 여러 클래스를 동시에 상속받을 수 없다.

믹스인 패턴

믹스인은 다른 클래스에 섞어 넣을 수 있는 메서드들의 모음이다.

기본 예시

// 믹스인 - 날 수 있는 기능
const canFly = {
  fly() {
    console.log(`${this.name}이(가) 날고 있습니다`);
  }
};

// 믹스인 - 수영할 수 있는 기능
const canSwim = {
  swim() {
    console.log(`${this.name}이(가) 수영하고 있습니다`);
  }
};

class Duck {
  constructor(name) {
    this.name = name;
  }

  quack() {
    console.log('꽥꽥');
  }
}

// 믹스인을 섞어 넣기
Object.assign(Duck.prototype, canFly, canSwim);

const donald = new Duck('도널드');
donald.quack();  // 꽥꽥
donald.fly();    // 도널드이(가) 날고 있습니다
donald.swim();   // 도널드이(가) 수영하고 있습니다

왜 이렇게 하나: Duck 클래스는 단일 상속만 가능하지만, 믹스인을 통해 여러 기능을 조합할 수 있다.

실전 예시

Vue의 믹스인

Vue에서 믹스인을 많이 사용한다.

// 공통 로직을 믹스인으로 분리
const modalMixin = {
  data() {
    return {
      isModalOpen: false
    };
  },
  methods: {
    openModal() {
      this.isModalOpen = true;
    },
    closeModal() {
      this.isModalOpen = false;
    }
  }
};

// 여러 컴포넌트에서 재사용
export default {
  mixins: [modalMixin],
  // 이제 openModal, closeModal 사용 가능
}

React의 HOC (Higher-Order Component)

React에서는 믹스인 대신 HOC나 Hooks를 사용하지만, 개념은 비슷하다.

// 믹스인과 비슷한 개념
function withLogging(Component) {
  return class extends Component {
    componentDidMount() {
      console.log('Component mounted');
      super.componentDidMount && super.componentDidMount();
    }

    render() {
      return super.render();
    }
  };
}

함수를 사용한 믹스인

더 깔끔하게 함수로 만들 수도 있다.

// 믹스인 함수
function flyMixin(Base) {
  return class extends Base {
    fly() {
      console.log(`${this.name} is flying`);
    }
  };
}

function swimMixin(Base) {
  return class extends Base {
    swim() {
      console.log(`${this.name} is swimming`);
    }
  };
}

// 기본 클래스
class Animal {
  constructor(name) {
    this.name = name;
  }
}

// 믹스인 조합
class Duck extends swimMixin(flyMixin(Animal)) {
  quack() {
    console.log('Quack!');
  }
}

const daffy = new Duck('Daffy');
daffy.quack();  // Quack!
daffy.fly();    // Daffy is flying
daffy.swim();   // Daffy is swimming

이 방식이 더 깔끔하고 타입 추론도 잘 된다.

믹스인 사용 시 주의사항

1. 이름 충돌

여러 믹스인을 섞을 때 메서드 이름이 겹치면 나중에 섞은 게 이전 것을 덮어쓴다.

const mixin1 = {
  getName() {
    return 'Mixin 1';
  }
};

const mixin2 = {
  getName() {
    return 'Mixin 2';
  }
};

class MyClass {}
Object.assign(MyClass.prototype, mixin1, mixin2);

const obj = new MyClass();
console.log(obj.getName());  // 'Mixin 2' - mixin2가 mixin1을 덮어씀

해결 방법: 메서드 이름을 신중하게 짓거나, 네임스페이스를 사용하자.

2. 디버깅 어려움

믹스인이 많아지면 어디서 메서드가 왔는지 추적하기 어렵다.

// 이 메서드는 어디서 왔을까?
obj.someMethod();  // 어느 믹스인에서 왔는지 모르겠음...

해결 방법: 믹스인을 너무 많이 사용하지 말고, 문서화를 잘 하자.

3. 타입 추론 문제

TypeScript에서 Object.assign 방식은 타입 추론이 안 된다.

// TypeScript에서는 함수 기반 믹스인 사용
function Timestamped<TBase extends Constructor>(Base: TBase) {
  return class extends Base {
    timestamp = Date.now();
  };
}

믹스인 vs 상속 vs 조합(Composition)

믹스인 상속 조합
다중 기능 O △ (단일 상속) O
타입 안정성 O O
디버깅 O O
유연성 O O
권장 사용 레거시 코드, Vue 명확한 is-a 관계 현대적인 코드

결론: 요즘은 믹스인보다 **조합(Composition)**을 권장한다. React Hooks가 대표적인 예시다.

조합(Composition)으로 대체하기

// 믹스인 대신 조합 사용
const useFly = (obj) => ({
  fly() {
    console.log(`${obj.name} is flying`);
  }
});

const useSwim = (obj) => ({
  swim() {
    console.log(`${obj.name} is swimming`);
  }
});

class Duck {
  constructor(name) {
    this.name = name;

    // 조합
    Object.assign(this, useFly(this), useSwim(this));
  }

  quack() {
    console.log('Quack!');
  }
}

이 방식이 더 명확하고 디버깅하기 쉽다.


언제 믹스인을 쓸까

  1. Vue 프로젝트 - Vue는 믹스인을 공식 지원
  2. 레거시 코드 - 이미 믹스인을 쓰고 있는 코드베이스
  3. 간단한 유틸리티 함수 공유 - 복잡하지 않은 경우

새 프로젝트라면: 조합(Composition), Hooks, 또는 유틸리티 함수를 사용하자.


관련 문서