자바스크립트 클래스(Class) 가볍게 살펴보기

REASON·2022년 11월 7일
0

STUDY

목록 보기
111/127

자바스크립트 클래스랑 친해지기가 참 어렵다.. 봐도봐도 낯설다.
함수에서 prototype으로 연결하고 싶은 마음이 너무나 굴뚝같지만 이번 기회에 클래스와 친해져보기로 했다.

왜 Class를 공부해야 할까? 😮

사실 자바스크립트 function으로 다 해결할 수 있지 않나? 라고 생각해왔다.
function은 new 키워드를 사용하면 생성자 함수로도 쓸 수 있고, 일반 함수로도 쓸 수 있고, 객체 내부 메서드로도 사용될 수 있다는 점에서 무의식적으로 function만 찾게 되었던 것 같다.
일단 클래스는 뭔가 모를 거부감이 드는 것도 있었는데.. 아마 자주 사용해본 적이 없으니 당연히 쓰던 것만 쓰게 된 게 아닐까? function 생성자 함수 대신 Class를 사용하는 방법을 공부해보고자 한다.

Class VS function으로 생성자 만들기

/* Class로 정의한 Person */
class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
}

Person 클래스를 정의하였다.
그렇다면 같은 것을 function으로도 만들어보자.

/* function으로 정의한 Person */
function Person(name, age){
  this.name = name;
  this.age = age;
}

차이점이 뭘까? 🙄

function은 호이스트되기 때문에 다음과 같은 코드로 작성도 된다.

const person = new Person('아이유', 30);

function Person(name, age){
  this.name = name;
  this.age = age;
}

console.log(person)

얼핏 보면 음 ? 뭐가 문제가 될까? 싶은 코드여서 혼자 고민을 해보았다.
예전에 봤던 책에서 클래스를 붕어빵 틀로 비유했던 예시가 떠올라서 나름대로 해당 비유에 이 코드를 적용시켜보았다.
Class와 비교해보자면 여기서 생성자 함수를 붕어빵 기계라고 빗댔을 때 보통은 붕어빵을 만들 기계가 있어야 붕어빵을 만들 수 있다. 그런데 저 코드는 붕어빵부터 만든거다.
붕어빵 기계에 반죽을 넣어서 붕어빵을 만들기도 전에 붕어빵부터 만들고 기계를 만들었다고 생각하면 뭔가 이상하지 않은가? 본인은 그렇게 생각해서 Class를 이해했다. ㅋㅋㅋㅋ

본인은 function이 가지는 호이스팅을 장점이라 생각했다.
무엇보다 이 호이스팅 덕분에 하단에 함수를 미리 정의 해놓고 상단에서 함수를 호출하는 방식을 선호했었다.
하지만, 얼마 전 에어비앤비 코드 컨벤션 보면서 생각이 바뀌게 된 계기가 있었는데 지금 생성자 함수로 사용시 function을 보니 조금 느낌이 다른 것 같아서 왜 Class가 등장했는지 알 것 같기도 하다.

Class는 정의하기 전에 선언하여 사용할 수 없다.

const person = new Person('아이유', 30);
/* Uncaught ReferenceError: Person is not defined */
class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
}

Class로 만든 Person으로 인스턴스를 만드려고 하자
Person이 defined 되지 않았다고 바로 에러를 뱉는다.
앞선 생성자 함수는 붕어빵 기계가 있기도 전에 붕어빵을 만들었다면
Class는 붕어빵 기계가 없기 때문에 붕어빵을 만들 수 없어!라고 화부터 낸다.

뭐야? Class 표현식도 가능해? 😮😮

MDN 문서로 공부하던 중 Class 표현식이라는 게 있어..? 이거 가능..?

정말 놀랍게도 Class 표현식이 가능하다는 것이다.

생각해보니 Class도 내부적으로는 function prototype으로 동작하는 것으로 알고 있어서 가능한 것 같다.

그런데 여기서 궁금한 부분이 생겼다.

class 표현식 역시 호이스팅 제한이 적용된다고?
그러면 var로 만든 Class는 어떻게 동작할까?
라는 궁금증이다.

이건 안 되나보다.
대신 var로 class를 선언하게 되면 전역 class가 되어버린다.

var Person = class {
  constructor(name, age){
    this.name = name;
    this.age = age;
  }
}

var를 쓸 일은 없지만 궁금한건 못 참지 ㅎ
아무튼 다시 본론으로..

표현식 Class도 익명 또는 기명으로 만들 수 있다.
이름을 가지고 있는 Class 표현식은 클래스 body의 로컬 스코프에 한해 유효한다고 적혀있었다.

아니 이게 무슨 말이지?

Test 바깥에서 Person 클래스에 접근할 수 없다는 의미인가..보다.

클래스 내부에서만 접근 가능하다고 되어 있어서 내부에서 Person2 를 찍도록 해봤다.
이 부분은 이해하는데 조금 애매한 것 같아서 일단 아 그렇구나. 하고 필요할 때 다시 꺼내봐야겠다.

Test.name으로 Person에 접근할 수는 있지만 바깥에서 Person을 찾을 수는 없는듯하다.
로컬 스코프라서 그런거겠지만.
무명 클래스인 경우 .name 했을 때 변수 이름이 출력되고 기명 클래스는 클래스 이름이 출력된다.

constructor는 하나만.

사실 클래스를 처음 봤을 때 이 알수없는 constuctor 는 도대체 뭘까..? 라고 생각했었다.
그냥 쓰니까 나도 써야되나보다 했었는데 언젠가부터 컨스트럭터에 대해 자연스럽게 이해가 된 것 같다.

constructor는 class로 만든 인스턴스를 생성할 때 초기화 해주는 친구이다. 그렇기에 클래스 하나당 딱 하나만 존재할 수 있다. (여러개 있으면 에러난다.)

부모 클래스로부터 상속받기 🤗💕

엄마와 아들 클래스를 만들었다.
엄마 클래스를 상속받게 할 것이기 때문에 컨스트럭터는 엄마 클래스에서 만들어보았다.

아들 클래스에서도 컨스트럭터를 만들고 super 키워드를 사용해서 아들 클래스에서 추가로 필요한 컨스트럭터를 넣을 수도 있겠지만, 메서드에서 super를 사용해보기로 했다.

class Mother {
  constructor(name, sentence){
    this.name = name;
    this.sentence = sentence;
  }
  say(){
    console.log(`${this.name} : ${this.sentence}`);
  }
}

class Son extends Mother {
  say(){
    super.say();
    console.log(`${this.name} : 감잔가?`)
  }
}

const son = new Son('정준하', '엄마 이거 뭐에요?');
son.say();

const mother = new Mother('나문희', '고구마호박');
mother.say();

여기서 Son 클래스에서 super를 호출했다.
say 메서드를 상속받아 사용함과 동시에 추가적으로 Son 클래스에서만 사용할 수 있는 말을 덧붙여보도록 했다.

super는 부모 클래스의 것을 사용한다는 의미로 쓰인다.
예를 들어 위 코드에서는 Mother 클래스의 say 메서드를 호출하면서 감잔가? 라는 말을 추가로 할 수 있도록 작성했다.
만약 Son클래스에서 super.say()를 생략했다면 정준하 : 감잔가? 만 출력될 것이다.

즉, 부모의 메서드를 사용하면서 내것(자식) 메서드에 부가적인 기능을 추가할 때 super를 사용해 볼 수 있을 것이다.

static (정적 메서드)

인스턴스를 만들지 않고도 사용하고 싶다면 static
솔직히 클래스에 대해 무지할 때 이게 뭔소리지.. 인스턴스는 뭐꼬 스태틱은 뭔데..ㅎ 했던 적이 있었다.

인스턴스는 new 키워드를 통해 만들어진 것을 의미한다.
static으로 선언하면 new 어쩌구 와 같이 new 키워드 없이도 사용할 수 있다.

class Animal{
  static run(){
    console.log('뛰는중...');
  }
}

Animal.run();


class Animal2{
  run(){
    console.log('뛰는중...');
  }
}

Animal2.run(); // 에러 발생

Animal과 Animal2 클래스로 비교해보자.
Animal 클래스의 run은 static으로 선언된 메서드이다.
즉, 인스턴스(new Animal)를 만들지 않아도 사용할 수 있기 때문에 Animal.run()으로 호출할 수 있게 된다.

그렇다면 이번엔 Animal2 클래스를 보자.
static으로 선언되지 않은 메서드이므로 인스턴스를 만들어 주어야 한다.
하지만 인스턴스를 만들지 않고 run을 호출하고 있다.

에러가 발생한다.

static으로 만든 메서드는 인스턴스로 호출할 수 없다.

class Animal{
  static run(){
    console.log('뛰는중...');
  }
}

Animal.run();

const animal = new Animal();
animal.run(); // 에러

보통 static으로 만든 메서드가 아니였다면 animal.run()과 같이 run 메서드를 호출했을 것이다.

하지만 static으로 선언된 메서드는 인스턴스로 호출할 수 없기 때문에 에러가 발생한다.

static은 인스턴스를 만들지 않고 사용할 수 있기 때문에 유틸리티 함수, 정적 속성인 경우 인스턴스 간에 복제할 필요가 없는 데이터(똑같은 것을 공유해서 쓸 때)를 만들 때 사용된다.

오늘은 자바스크립트 Class와 친해지기 위해 가볍게 Class 기본적인 사용 방법에 대해 공부해보았다.

class를 사용하는 언어에 능숙하다면 자바스크립트 class도 어렵지 않게 이해할 수 있겠지만... 나는 그렇지 않으므로ㅠㅠㅠ

문서를 읽어보니 생각보다 어려운 내용이 많아서 한번에 다 이해하려고 하기 보다는 천천히 읽어보면서 이해해봐야겠다.

특히 public이나 private.. 캡슐화라던가..
입문자에게는 다소 어렵게 느껴지는 OOP개념에 대해서 익숙해지려면 시간이 필요할듯하다. ㅎㅎ


참고 자료
MDN Class
코어 자바스크립트 - 클래스와 기본 문법

2개의 댓글

comment-user-thumbnail
2022년 11월 7일

오늘은 Class를 공부하셨군요. 중간에 예제 부분에서 웃겨서 수명이 5분정도 늘어난 것 같습니다.^^
오늘도 좋은 글 잘 읽고 갑니다. 제 수명까지 늘려주시고 감사합니다

1개의 답글