Skip to content

클래스

ES5 일반 함수보다 ES2015/ES6 클래스를 선호하세요

기존 ES5 클래스에서는 가독성 있는 클래스 상속, 생성자 및 메서드 정의를 작성하기 어렵습니다. 만약 상속이 필요하다면(단 실제로 필요하지 않을 수 있음을 인지해야 함), ES2015/ES6 클래스를 사용하세요. 하지만 더 크고 복잡한 객체가 필요해지기 전까지는 클래스보다 작은 함수를 우선 사용해야 합니다.

나쁜 예:

javascript
const Animal = function(age) {
  if (!(this instanceof Animal)) {
    throw new Error("Instantiate Animal with `new`");
  }

  this.age = age;
};

Animal.prototype.move = function move() {};

const Mammal = function(age, furColor) {
  if (!(this instanceof Mammal)) {
    throw new Error("Instantiate Mammal with `new`");
  }

  Animal.call(this, age);
  this.furColor = furColor;
};

Mammal.prototype = Object.create(Animal.prototype);
Mammal.prototype.constructor = Mammal;
Mammal.prototype.liveBirth = function liveBirth() {};

const Human = function(age, furColor, languageSpoken) {
  if (!(this instanceof Human)) {
    throw new Error("Instantiate Human with `new`");
  }

  Mammal.call(this, age, furColor);
  this.languageSpoken = languageSpoken;
};

Human.prototype = Object.create(Mammal.prototype);
Human.prototype.constructor = Human;
Human.prototype.speak = function speak() {};

좋은 예:

javascript
class Animal {
  constructor(age) {
    this.age = age;
  }

  move() {
    /* ... */
  }
}

class Mammal extends Animal {
  constructor(age, furColor) {
    super(age);
    this.furColor = furColor;
  }

  liveBirth() {
    /* ... */
  }
}

class Human extends Mammal {
  constructor(age, furColor, languageSpoken) {
    super(age, furColor);
    this.languageSpoken = languageSpoken;
  }

  speak() {
    /* ... */
  }
}

메서드 체이닝 사용

이 패턴은 jQuery와 Lodash 등 많은 자바스크립트 라이브러리에서 활용되는 매우 유용한 패턴입니다. 코드의 표현력을 높이고 장황함을 줄이기 때문에 체이닝을 적극 권장합니다. 클래스 함수 끝에 this를 반환하는 간단한 방법으로 메서드 체이닝을 구현할 수 있습니다.

나쁜 예:

javascript
class Car {
  constructor(make, model, color) {
    this.make = make;
    this.model = model;
    this.color = color;
  }

  setMake(make) {
    this.make = make;
  }

  setModel(model) {
    this.model = model;
  }

  setColor(color) {
    this.color = color;
  }

  save() {
    console.log(this.make, this.model, this.color);
  }
}

const car = new Car("Ford", "F-150", "red");
car.setColor("pink");
car.save();

좋은 예:

javascript
class Car {
  constructor(make, model, color) {
    this.make = make;
    this.model = model;
    this.color = color;
  }

  setMake(make) {
    this.make = make;
    // NOTE: Returning this for chaining
    return this;
  }

  setModel(model) {
    this.model = model;
    // NOTE: Returning this for chaining
    return this;
  }

  setColor(color) {
    this.color = color;
    // NOTE: Returning this for chaining
    return this;
  }

  save() {
    console.log(this.make, this.model, this.color);
    // NOTE: Returning this for chaining
    return this;
  }
}

const car = new Car("Ford", "F-150", "red").setColor("pink").save();

상속보다 조합(Composition) 우선시

Gang of Four의 <디자인 패턴> 서적에서 유명한 원칙대로, 가능하다면 상속 대신 조합을 선택해야 합니다. 두 방식 모두 장점이 있지만 상속을 고려하기 전에 항상 조합으로 문제를 해결할 수 있는지 검토해야 합니다. 이 접근법은 더 나은 설계를 가능케 하는 경우가 많습니다.

다음과 같은 질문을 가질 수 있습니다: "언제 상속을 사용해야 하나요?" 이는 현재 문제에 따라 다르지만, 다음은 상속이 조합보다 적합한 경우를 정리한 목록입니다:

  1. 상속 관계가 "is-a" 관계이고 "has-a" 관계가 아닌 경우 (Human->Animal vs. User->UserDetails)
  2. 기본 클래스 코드 재사용이 가능할 때 (모든 동물처럼 인간도 이동 가능)
  3. 기본 클래스 수정으로 모든 파생 클래스에 전역 변경을 적용하려는 경우 (동물 이동 시 칼로리 소비량 일괄 조정)

나쁜 예:

javascript
class Employee {
  constructor(name, email) {
    this.name = name;
    this.email = email;
  }

  // ...
}

// Bad because Employees "have" tax data. EmployeeTaxData is not a type of Employee
class EmployeeTaxData extends Employee {
  constructor(ssn, salary) {
    super();
    this.ssn = ssn;
    this.salary = salary;
  }

  // ...
}

좋은 예:

javascript
class EmployeeTaxData {
  constructor(ssn, salary) {
    this.ssn = ssn;
    this.salary = salary;
  }

  // ...
}

class Employee {
  constructor(name, email) {
    this.name = name;
    this.email = email;
  }

  setTaxData(ssn, salary) {
    this.taxData = new EmployeeTaxData(ssn, salary);
  }
  // ...
}