클래스에서 로직을 공유하는 방법 - Mixin

Sally·2022년 11월 28일
1

What-I-Learn

목록 보기
2/6
post-thumbnail

Class에서 로직을 공유하는 방법

클래스에서 로직을 공유하는 방법으로 바로 떠오르는 방법으로는 상속이 있다.

자식 클래스가 부모의 클래스를 상속받아 부모의 클래스에 선언되어 있는 함수들을 자식 클래스에서 사용이 가능한 방법이다.
그 덕분에 중복된 로직들을 부모 클래스에 위임함으로써 자식 클래스에서 불필요한 코드 작성을 피할 수 있다.

그런데, 이러한 클래스의 상속 말고 로직을 공유하는 방법에 대한 질문을 받았다

상속 말고...?

우선, 상속의 단점을 알아보자

일단, 상속에 대해서 자세히 살펴보자
상속에는 어떤 단점이 있을까?

쉽게 예측할 수 있는 것은 이것이다.
바로 부모 클래스의 변경사항이 바로 자식 클래스에 영향이 간다는 것이다.

자식 클래스는 부모 클래스를 물려받기 때문에 부모 클래스에서 메서드나 변수에 변경점이 생기게 되면 그대로 영향을 받게 된다.

만약, 해당 부모 클래스를 상속 받는 것이 한 두개의 자식 클래스라면, 또는 상속의 깊이가 한 두 단계라면 큰 문제가 될 것은 없다.

하지만, 부모 클래스를 상속받는 자식 클래스가 여러 개이고 또 그 자식 클래스를 상속 받는 클래스의 클래스들이 여기저기에 위치해있다면?
하나의 부모 클래스만 변경하여도 예상치 못한 사이드 이펙트를 마주하게 될 것이다.

그렇다면 상속 말고 로직을 재사용하는 방법에는 무엇이 있을까?

Mixin을 아십니까?

자바스크립트는 다중 상속을 지원하지 않는다.
그 이유는 다이아몬드 문제 때문이다.

다이아몬드 문제란?
예를 들어 A의 클래스를 상속받은 B 와 C 클래스가 각각 A의 a-method를 오버라이딩하여 구현하였다고 가정해보자. B와 C를 다중 상속 받은 D 클래스가 a-method를 호출할 때에 B의 것을 사용해야할지 C의 것을 사용할지 문제가 발생하게 됩니다. 해당 문제점이 존재하는 클래스간의 도식도가 다이아몬도처럼 생겨 다이아몬드 문제라고 한다.

그래서 다중상속의 문제를 회피하며 여러 클래스에서 로직을 재사용하기 위해서 mixin 방법을 만들어서 사용하게 되었다.

Mixin 방법이란?

다른 클래스를 상속받을 필요없이 이들 클래스에 구현되어 있는 메서드를 담고 있는 클래스
즉, 특정 행동을 실행해주는 메소드를 제공하는데 단독으로 쓰이지 않고 다른 클래스에 행동을 더해주는 용도로 사용된다.

쉽게 생각하면, 클래스간에 로직을 재사용하기 위해서 창고를 하나 만들어 해당 창고를 여러 클래스에서 사용가능하게 함으로써 로직을 공유할 수 있다.

mixin 구현 방법

  1. 우선 메서드들을 담을 객체를 하나 생성한다.
const dogFunctionality = {
  bark: () => console.log("Woof!"),
  wagTail: () => console.log("Wagging my tail!"),
  play: () => console.log("Playing!")
};
  1. 해당 메서드를 사용하고 싶은 Class를 생성한다
class Dog {
  constructor(name) {
    this.name = name;
  }
}
  1. 자바스크립트의 prototype의 특징을 활용하여 mixin을 할당해준다
Object.assign(Dog.prototype, dogFunctionality);
  1. 이제 평소의 클래스 인스턴스 생성 후 호출하던 방식대로 사용한다
const dog = new Dog('baobao');
dog.bark(); 

mixin 패턴의 자세한 설명 은 해당 링크를 참고 하면 좋을 것 같다.

mixin이 가능한 이유는 무엇일까?

Object.assign(Dog.prototype, dogFunctionality);

해당 방법을 사용하였을 때에 어떻게 인스턴스 단에서 dogFunctionality에 접근할 수 있었을까?
답은 자바스크립트의 prototype이다.

자바스크립트에서는 로직의 재사용을 위해서 상속을 채택한 것이 아닌 프로토타입을 선택하였다.
어떤 객체를 원형으로 삼고 해당 객체를 참조함으로써 부모 객체의 프로퍼티 또는 메소드에 접근하여 사용할 수 있게 해준다.

그래서 어떤 메서드에 접근을 할 때에 프로토타입 체이닝을 통해서 메서드가 있는 곳을 찾아나간다. 만약 프로토타입 체이닝을 통해서 메서드를 찾아내지못한다면 에러를 발생시킨다.

또한 해당 prototype의 경우 생략이 가능하기 때문에 마치 자신의 메서드나 프로퍼티 처럼 접근이 가능하게 된다.

자바스크립트는 왜 프로토타입을 채택하게 되었을까? 🤔
자바스크립트가 도입된 옛 브라우저 환경은 메모리가 부족하였기 때문에 프로토타입을 활용한 참조방식을 사용하게 되면 메모리의 사용량을 줄일 수 있었다. 또한 prototype은 생략 가능하기 때문에 비개발자들이 자바스크립트 문법을 사용할 때에 접근이 쉬워질 수 있다는 장점이 있다.

다시 Dog로 돌아와서 살펴보자.
Dog.prototype은 Dog의 인스턴스들이 사용할 원형 객체이다.
해당 객체에 dogFunctionality 이라는 객체를 임의로 할당시켜주었기 때문에 Dog.prototype 안에는 원래 Dog가 가지고 있던 메서드 뿐만 아니라 dogFunctionality의 메서드도 가지고 있게 된다.

그리고 Dog의 인스턴스들은 프로토타입체이닝을 통해서Dog.prototype안에 들어간 dogFunctionality에 접근이 가능하게 되어
Dog내부에 구현된 메서드들이 아님에도 Dog에서 제공하는 메서드 처럼 취급되어 사용할수 있게 된다.

0개의 댓글