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!');
}
}
이 방식이 더 명확하고 디버깅하기 쉽다.
언제 믹스인을 쓸까
- Vue 프로젝트 - Vue는 믹스인을 공식 지원
- 레거시 코드 - 이미 믹스인을 쓰고 있는 코드베이스
- 간단한 유틸리티 함수 공유 - 복잡하지 않은 경우
새 프로젝트라면: 조합(Composition), Hooks, 또는 유틸리티 함수를 사용하자.
관련 문서
- JS-왜 프로토타입을 사용하나 - 프로토타입과 다중 상속
- JS-클래스 - 클래스와 상속
- JS-prototype - 프로토타입 체인