개발 기술/개발 이야기

클래스(class)는 무엇인가

by GicoMomg (Lux) 2021. 10. 31.

해당 포스팅에서는 ES2015 이후에 추가된 클래스에 대해 알아보았다.

1. 클래스란?

1) 생성자? 클래스?

(1) ES2015 이전, 생성자를 사용했다.

  • ES2015 이전에는 클래스를 지원하지 않아, 생성자를 이용해 객체를 생성했다.
  • 우리가 이러한 생성자를 사용한 이유는 객체의 형태(속성, 메소드)를 정의하기 위함이었다.
//생성자
function Animal({type, age}) { 
  //속성
  this.type = type;   
  this.age = age;
}
//메소드
Animal.prototype.nameTag = function() {  
  return `이 동물의 종류는 ${type}이며, 나이는 ${age}이다.`;
}
  • new 를 사용해 객체를 선언하고 속성을 정의할 수 있다.
const animal = new Animal({type: '고양이', age: 12});
console.log(animal.nameTag());   // 이 동물의 종류는 고양이이며, 나이는 12이다

(2) ES2015, 클래스가 추가됐다.

  • ES2015이전에는 생성자가 클래스 역할을 대신했지만, ES2015이후에는 클래스가 추가되었다.
  • class 키워드를 사용해 클래스를 선언하고 사용할 수 있게 되었다.
  • 클래스도 생성자와 동일하게, 객체의 형태(속성, 메소드)를 정의하기 위해 사용된다.
  • 클래스 내부에서 선언하는 메소드가 prototype에 저장된다.
class Animal {
  constructor({type, age}) {  //생성자
    this.type = type;
    this.age = age;
  }
  nameTag() {   //클래스 내부에서 선언하는 메소드는 Animal.prototype에 저장
    return `이 동물의 종류는 ${type}이며, 나이는 ${age}이다.`;
  }
}
const animal = new Animal({type: '고양이', age: 12});
console.log(animal);   // 이 동물의 종류는 고양이이며, 나이는 12이다



2) 생성자, 클래스의 차이

사용방법을 보니 클래스와 생성자의 차이가 거의 없는 거 같은데, 도대체 왜 추가된걸까? 클래스와 생성자의 차이점에 대해서도 알아보자

(1) 클래스는 함수로 호출될 수 없다.

  • 생성자는 function을 사용해 선언되며, 그렇기에 함수처럼 호출할 수 있다.
function Animal1({ type, age }) {
  console.log(type, age)
}
const animal1 = new Animal1({type: '고양이', age: 21});  // 고양이21

  • 하지만 클래스는 함수처럼 호출해서 사용할 수 없다.
class Animal2 {
  console.log(type, age)   //error

}

(2) 클래스는 호이스팅이 불가하다.

  • js의 특이한 점은 호이스팅이 가능하다는 것이다.
  • 그럼 호이스팅은 무엇이냐? 변수선언이 해당 함수의 최상단으로 끌어 올려지는 걸 말한다.
  • 우리는 사용하고자 하는 변수를 우선 선언하고 그다음에 그 변수를 사용한다.
const hello = 'world';  //변수 우선 선언
console.log(hello);     //선언한 변수 사용

  • 그런데 만약 변수 출력문보다, 변수 선언문을 나중에 작성했다면?
  • console 입장에서는 hello 변수가 선언되지 않았으니 에러가 떠야할 듯 하다.
console.log(hello);     //hello 변수 사용?
const hello = 'world';  //출력문보다 늦게 변수 선언

  • 그러나 js에서는 변수선언문을 나중에 작성해도, 해당 변수 사용이 가능하다!
  • 그 이유는 js에서는 호이스팅이 발생하므로, 나중에 선언한 변수에도 접근할 수 있기 때문이다.
console.log(hello);     //hello 변수 사용가능!
const hello = 'world';  //출력문보다 늦게 변수 선언했지만...

그럼 js의 특징인 호이스팅은 클래스에도 사용할 수 있을까? 답은 사용할 수 없다!

class Animal {
  constructor({ type, age }) {
    this.type = type;
    this.age = age;
  } 
  print() {
    console.log(this.type, this.age)
  }
}
animal.print();  //error
const animal = new Animal({type: '고양이', age: 12}) 


(3) 클래스 메소드에선 super 키워드를 사용할 수 있다.

  • super 키워드는 클래스의 상속과 관련된 내용이다.
  • 이 부분은 2.클래스 사용법상속에서 자세히 알아보자!






2. 클래스 사용법

1) 클래스의 형태

  • class 키워드를 이용하여 클래스를 정의한다.
  • 클래스의 역할을 인스턴스(객체를 메모리에 할당한 것)를 생성하는 것이다.
  • 클래스의 constructor 메소드는 초기화 역할을 한다.
class Person {
  constructor() {
      console.log('실행')
  }
}

  • 만약 우리가 Person 클래스로 인스턴스를 만들면, 먼저 Person의 constructor가 실행된다.
new Person();  //실행

  • 클래스를 사용해 객체의 속성, api 등을 설정할 수 있다.
class Person {
  constructor(name, age) {
      this.name = name;
    this.age = age;
  }
}
const lili = new Person('lili', 40); // lili는 Person클래스의 인스턴스 
console.log(lili);                   // Person {name: 'lili', age: 40}



2) 메소드

(1) 메소드 선언

  • 메소드를 클래스 내부에 선언할 수 있다.
  • 클래스 내부에 선언된 메소드는 해당 클래스의 prototype에 저장된다.
class Animal {
  test() {
    return 'test print'
  }
}

(2) 가변적인 메소드명

  • 만약 가변적인 메소드명이 필요하다면, 대괄호([메소드명])로 메소드를 선언하면 된다.
  • 단, 메소드명을 선언한 변수는 클래스보다 우선 선언되어야 한다.
const methodName = 'test';  //메소드명 우선 선언

class Animal {
  constructor({ type, age }) {
    this.type = type;
    this.age = age;
  }
  [methodName]() {
    console.log('hello~')
  }
}
const animal = new Animal({type: '고양이', age: 12});
animal.test();  //hello~

(3) getter, setter

  • getter, setter는 객체 속성값에 접근하는 함수이다.
  • getter를 이용해 속성값을 읽고, setter를 이용해 속성값을 편집할 수 있다.
  • 클래스 내부 메소드에도 getter, setter를 사용할 수 있다.
class Animal {
  constructor({ type, age }) {
    this._type = type;
    this._age = age;
  }
  get type() {          // getter
    return this._type;
  }
  set type(newType) {   // setter
    this._type = newType;
  }
}
const animal = new Animal({type: '고양이', age: 12});
console.log(animal.type);  // 고양이
animal.type = '멍멍이';      
console.log(animal.type);  // 멍멍이

(4) 정적 메소드

  • 메소드 앞에 static 키워드를 붙이면, 정적 메소드가 된다.
class Animal {
  static print() {
    console.log('test')
  }
}

  • 클래스 내부 메소드에서 정적 메소드 방식을 주로 사용하며,
  • 인스턴스에서는 호출할 수 없고 클래스 생성자로만 호출 가능하다.
const animal = new Animal();  // 인스턴스 생성(Animal객체를 메모리에 할당)
animal.print();   // error  
Animal.print();   // test



3) 상속

(1) 클래스 상속

  • 클래스는 다른 클래스를 상속받아 재사용할 수 있다.
  • 만약 Child가 Parent 클래스를 상속 받으려면 자식클래스 extends 부모클래스 을 사용하면 된다.
class Parent {
  ...
}
class Child extends Parent {  
  ... 
}

(2) 상속의 장점

  • 자식 클래스(A)가 부모 클래스(B)를 상속 받으면, A는 B의 정적 메소드와 속성을 사용할 수 있다.
class Parent {
  static test = '123';
  static print() {
    console.log('print');
  }
}
class Child extends Parent {}
Child.print();           // print
console.log(Child.test)  // 123

  • 또한, A의 인스턴스에서 B의 인스턴스 메소드와 속성을 사용할 수 있다.
class Parent {
  constructor(test) {
    this.test = test;
  }
  print() {
    console.log('print');
  }
}
class Child extends Parent {}
const child = new Child(123);
child.print();              // 123
console.log(child.test)     // 123

(3) super키워드

  • 만약 자식 클래스(A)에 부모 클래스(B)와 동명의 속성이 있는 경우 어떻게 해야할까?
  • 자식 클래스에 getColor라는 메소드가 있는데, 부모에도 동일한 이름의 메소드가 존재하면,
  • 자식 클래스의 메소드를 사용하게 된다. (부모 클래스의 메소드는 무시됨)
class Apple {
  getColor() {
    return '나는 빨강';
  }
}
class Orange extends Apple {
  getColor() {
    return '나는 주황';
  }
}
const orange = new Orange();
console.log(orange.getColor());  //나는 주황

하지만 상속을 한다는 건 부모의 속성, 메소드를 사용하고 싶은 의도가 있는 것이다.
만약, 부모 클래스 값을 이용하고 싶다면 우리는 super 키워드를 사용하면 된다.

  • 생성자 내부에 super()를 호출하면, 부모 클래스의 생성자가 호출된다.
class Apple {
  constructor(name) {
    this.name = name;
  }
}
class Orange extends Apple {
  constructor(name, type) {
    super(name);   //부모의 constructor 사용 가능
    this.type = type;
  }
  print() {
    console.log( `${this.name} ${this.type}`);
  }
}
const orange = new Orange('orange', 'fruit');
orange.print() // orange fruit

  • 정적 메소드 내부에서 super.prop을 사용하면 부모 클래스의 정적 속성에 접근할 수 있다.
class Apple {
  static print() {
    return 'apple';
  }
}
class Orange extends Apple {
  static print() {
    return super.print() + ' orange';
  }
}
console.log(Orange.print());  //apple orange

  • 인스턴스 메소드 내부에서 super.prop을 사용하면 부모 클래스의 인스턴스 속성에 접근할 수 있다.
class Apple {
  print() {
    return 'apple';
  }
}
class Orange extends Apple {
  print() {
    return super.print() + ' orange';
  }
}
const orange = new Orange();
console.log(orange.print());  //apple orange



참고자료
아래 링크로 가면 더 자세히 알 수 있습니다~

반응형

댓글