WeniVooks

검색

JavaScript 에센셜

클래스

1. 클래스

ES6 부터 class라는 키워드를 사용할 수 있게 되었습니다. 클래스란 객체를 생산하는 생산 도면 혹은 청사진이란 표현을 자주 사용합니다. 클래스는 여러 가지 붕어빵을 만들 수 있는 붕어빵 틀과 같은 역할을 합니다.

스크린샷 2022-05-17 오후 12.51.30.png 스크린샷 2022-05-17 오후 12.52.05.png
1.1. 클래스 사용법

클래스는 class 키워드, 이름, 그리고 중괄호로 이루어져 있습니다. 클래스의 결과물은 인스턴스를 생성하는 것입니다. 생성자를 이용한 타입 생성과 그 결과가 정확하게 일치합니다.

// 이전의 생성자 함수 방식 // function Robot(name) { // this.name = name; // } // Robot.prototype.sayYourName = function () { // console.log(`삐리비리. 제 이름은 ${this.name}입니다. 주인님.`); // } // 클래스 문법 class Robot { // 클래스의 생성자 함수입니다. 하나의 클래스는 하나의 생성자만 정의할 수 있습니다. // 그리고 생성자 함수는 new 키워드가 호출될 때 자동으로 실행됩니다. constructor(name) { this.name = name; } // 메소드를 정의합니다. 메소드는 클래스가 생성한 인스턴스를 통해 사용할 수 있습니다. sayYourName() { console.log(`삐리비리. 제 이름은 ${this.name}입니다. 주인님.`); } }

위의 코드에서 객체지향의 중요한 개념 중 하나인 인캡슐레이션을 만나볼 수 있습니다. 인캡슐레이션(Encapsulation)은 객체 지향 프로그래밍(OOP)의 개념 중 하나로, 데이터와 해당 데이터를 조작하는 메서드들을 하나의 단위로 묶는 것을 말합니다.

실습
지난 챕터에서 만들어본 여러분 자신의 생성자 함수를 class 문법으로 변경해봅시다.

자바스크립트는 프로토타입 기반 객체지향 언어
1983년에 C 언어가 객체지향 페러다임을 받아들이면서 C++가 탄생했습니다. C++가 만들어진 이래 classes의 개념은 모든 객체지향 언어에서 공통적으로 사용되었습니다.

자바스크립트가 2015년 드디어 classes 개념을 받아들이고 나서 코드의 모습이 비로소 전통적인 객체지향형의 모습을 가지게 되었습니다. 위의 코드에서 볼 수 있듯, 객체에 필요한 모든 코드가 하나의 코드 묶음으로 통합되었습니다.

처음부터 자바스크립트에 classes 문법을 도입했다면 좋았겠지만 자바스크립트의 창시자인 브랜든 아이크는 아래처럼 회고합니다.

If I had done classes in JavaScript back in May 1995, I would have been told that it was too much like Java or that JavaScript was competing with Java. [...] I was under marketing orders to make it look like Java but not make it too big for it’s britches […] [it] needed to be a silly little brother language.

Brendan Eich, JavaScript creator

이러한 이유로 자바스크립트는 초기에 클래스와 같은 고급 문법을 도입하는 대신 더 단순하고 가벼운 언어로의 포지셔닝을 위해 prototype 을 이용해 객체지향적인 코드를 구현하는 방식을 택하게 됩니다.

1.2. class 상속받기

클래스의 상속은 extends 키워드를 사용합니다. 상속을 받는 클래스는 '파생 클래스'(derived classes)라고 부릅니다. 이때 부모 클래스의 프로퍼티를 상속받기 위해 super 함수를 사용합니다. 이때 super는 부모 생성자를 참조합니다.

class BabyRobot extends Robot {
  constructor(name) {
    super(name);
    this.ownName = '위니브';
  }
 
  sayBabyName() {
    // 또한 상속을 받게되면 부모 클래스의 메소드를 사용할 수 있게 됩니다. 때문에 this로 접근 할 수 있습니다.
    this.sayYourName();
    console.log('Suceeding you, Father!');
  }
}
class BabyRobot extends Robot {
  constructor(name) {
    super(name);
    this.ownName = '위니브';
  }
 
  sayBabyName() {
    // 또한 상속을 받게되면 부모 클래스의 메소드를 사용할 수 있게 됩니다. 때문에 this로 접근 할 수 있습니다.
    this.sayYourName();
    console.log('Suceeding you, Father!');
  }
}

super 사용시 주의할 점

  • 만약 파생 클래스에 생성자 함수를 사용하고 싶다면 반드시 super 함수를 사용해야합니다.
  • 파생 클래스에 생성자 함수가 없다면 super 함수가 자동으로 호출되어 부모 클래스의 프로퍼티를 상속 받게 합니다.
  • 생성자 함수에서 this 값을 사용할 경우 super 함수는 반드시 this 보다 먼저 실행되어야 합니다.
  • 파생 클래스가 아닌 클래스에서 사용하려고 해도 에러가 발생합니다.

실습 : 소시지를 만드는 클래스를 정의해봅시다.

  1. 생성자 함수는 두 가지 매개변수를 전달 받을 수 있으며 전달되는 매개변수에 따라 소시지의 맛이 결정됩니다.

  2. 소시지 객체는 taste라는 메서드가 존재합니다. 생성자 함수에서 전달받은 재료에 따라 맛을 나타내는 콘솔로그를 출력하는 역할을 합니다.

    • 예시: '소고기'와 '파'를 매개변수로 전달하였을 경우, '소고기와 파 맛이 난다!'는 콘솔 메시지를 출력합니다.
  3. 소시지 클래스를 상속받는 FiresSausage 파생클래스를 생성해봅니다. 파생클래스의 taste 메서드를 실행하면 콘솔 메시지에 '재료1과 재료2의 맛이 난다!'와 '불맛이 난다!'를 출력합니다.

1.3. 비공개(private) 프로퍼티

비공개 프로퍼티를 사용하는 이유는 객체의 중요한 정보를 안전하게 보호하기 위함입니다. 외부에서 직접 접근할 수 없도록 만들어 객체의 데이터를 안전하게 사용할 수 있습니다. 프로그램이 뜻하지 않게 변경되는 것을 막을 수 있습니다.

클래스를 통해 인스턴스를 만들었을 때 우리는 아무런 제약 없이 인스턴스의 프로퍼티에 접근하는 것이 가능하며, 프로퍼티를 직접 수정하는 것도 가능합니다.

class Robot {
  constructor(name, pw) {
    this.name = name;
    this.password = pw;
  }
 
  sayYourName() {
    console.log(`삐리비리. 제 이름은 ${this.name}입니다. 주인님.`);
  }
}
 
const bot = new Robot('알리봇', '1234');
console.log(bot.password);
class Robot {
  constructor(name, pw) {
    this.name = name;
    this.password = pw;
  }
 
  sayYourName() {
    console.log(`삐리비리. 제 이름은 ${this.name}입니다. 주인님.`);
  }
}
 
const bot = new Robot('알리봇', '1234');
console.log(bot.password);
bot.password = '0000';
console.log(bot.password);
bot.password = '0000';
console.log(bot.password);

하지만 데이터를 안전하게 사용하기 위해서는 외부에서 직접 수정할 수 없게 만들어야 합니다. 이때 비공개 프로퍼티로 데이터를 변경하여 데이터의 직접적인 수정을 막을 수 있습니다.

클래스 내부에서 비공개 프로퍼티를 사용하기 위해서는 먼저 프로퍼티를 선언해야 합니다. 프로퍼티명 앞에 # 키워드를 붙이면 해당 프로퍼티는 비공개로 선언됩니다. 클래스 내부에서는 this.#프로퍼티명으로 접근할 수 있습니다. 하지만 비공개 프로퍼티로 선언된 프로퍼티는 클래스 외부에서 접근할 수 없습니다.

class Robot { #password; constructor(name, pw) { this.name = name; this.#password = pw; } sayYourName() { console.log(`삐리비리. 제 이름은 ${this.name}입니다. 주인님.`); } } const bot = new Robot('알리봇', '1234'); console.log(bot.#password); // Error

비공개 프로퍼티의 값에 접근하고 수정하려면 반드시 메서드를 사용해야 합니다. 이 때 값을 불러오는 메서드를 getter 메서드, 값을 수정하는 메서드를 setter 메서드라고 부릅니다. get, set 키워드를 이용해 아래처럼 코드를 좀 더 단순화 할 수 있습니다.

class Robot { #password; constructor(name, pw) { this.name = name; this.#password = pw; } sayYourName() { console.log(`삐리비리. 제 이름은 ${this.name}입니다. 주인님.`); } get password() { return this.#password; } set password(pw) { this.#password = pw; } } const bot = new Robot('알리봇', '1234'); console.log(bot.password); // get password bot.password = '0000'; // set password console.log(bot.password);

get과 set 키워드를 사용하면 마치 객체의 프로퍼티에 접근하는 것처럼 값을 다룰수 있게 됩니다.

get 과 set을 사용할 때 주의할 점!
get과 set을 사용하면 마치 객체의 프로퍼티를 수정하는것 같은 간편함을 느낄 수 있습니다. 하지만 해당 코드를 직접 작성하지 않은 협업자들에게는 오해를 불러일으킬 소지가 있습니다. get, set 안에 어떤 로직이 들어있는지 파악하지 못하고 단순히 객체의 프로퍼티를 수정한다는 착각을 일으킬 수 있기 때문입니다. 때문에 협업 시에는 주석이나, 가이드 문서를 만들어 충분한 정보를 제공해주는 것이 좋습니다.
또한 getter과 setter를 통해 비공개로 선언한 프로퍼티에 대해 간접적인 접근 경로를 제공하게 되므로, 외부에서 일반 프로퍼티처럼 접근할 수 있게 됩니다.

11.1 생성자12장 비동기 프로그래밍