[우아한테크코스 #2] class, 그리고 static method

NinjaJuunzzi·2022년 2월 20일
2

우아한테크코스

목록 보기
3/21
post-thumbnail

이런 키워드들이 등장할 수 있어요.

  • Object Literal

  • ClassPrototype

  • Factory Pattern

  • Singleton

Class

우리는 클래스를 만들고, 이 클래스를 통해 여러 객체들을 만들어낼 수 있어요.

class Person{
  
  
}

const junzzi = new Person();
const lokba = new Person();

하지만 우리가 흔히 알고있길 class를 선언 하지 않고도 object literal로도 객체들을 만들 수 있어요. 그렇다면 우리가 클래스를 선언하여 객체를 만들땐 적절한 다른 이유가 있어야겠죠?!

오늘 시간엔 그 점을 탐구해보려고해요!!

굳이 클래스를 만들어야 하는 이유

Object Literal로 만드는 객체Object
Class로 만드는 객체Class Object로 명명할게요.

이번 포스팅에선 이 둘의 tradeoff에 대해 간단히 고찰해보려고 해요. 😱 TL:DR주의

Object가 갖는 특징

다음과 같이 객체 리터럴을 활용하여 만들 수 있어요!

const obj = {
  data: 10,
  method_one() {
    console.log("method_one");
  },
};
  • 단일 인스턴스이다.

  • private 화 하기 힘들다.

  • 초기화 할 수 없다.

단일 인스턴스의 경우 우리가 잃어야하는 것은?
단일 인스턴스 (싱글톤)이기 때문에 여러 인스턴스로 만들어낼 수 없어요. 그렇기 때문에 다음과 같은 코드는

const obj = {
  data: 10,
  method_one() {
    console.log("method_one");
  },
};

const clone = obj;

obj.data = 20;

console.log(clone.data); // 20

하나의 저장소를 여러 클론된 식별자로 접근할 뿐이네요?! 그렇기 때문에 여러 인스턴스화를 할 수 없게 됩니다. 하나의 인스턴스를 여러 식별자들이 가리키고 있을 뿐이죠.

private화 하기 힘들다

불가능하다가 아닌 힘들다입니다. 우선 우리는 모든 정보가 공개된 객체를 만들고 싶지 않을것이에요. 중요한 정보는 감춰줄 수 있어야죠.

굳이 Object에 private한 멤버를 추가하고자 한다면 다음과 같이 코드를 작성해야할 것입니다.

private화 하지 않는다면, data 프로퍼티를 어디서든 접근하게 될 것이에요.

const obj = {
  data: 10,
  method_one() {
    console.log("hi");
  },
};

console.log(obj.data); // ❌

이를 바라지 않는다면 다음과 같이 숨겨둘 수 있을 것 같아요.

const obj = {
  private_members: (function () {
    let data = 10;
    return {
      getData() {
        return data;
      },
      setData(newData) {
        data = newData;
      },
    };
  })(),
  method_one() {
    console.log("hi");
  },
};
console.log(obj.private_members.getData());
console.log(obj.private_members.setData(20));
console.log(obj.private_members.getData());

즉시 실행함수와 클로저를 이용하여 구현할 수 있으나, 매우 불편한 것 같아요.

(더 좋은 방법은 생각나지 않아요. 있다면 공유해주세요!!)

not construtor

클래스 객체의 경우 인스턴스를 생성할 때, constructor를 호출하게 되기에 constructor 내부에 로직을 추가하면 초기화할 수 있어요.

하지만 Object의 경우 따로 init 함수를 만들지 않는 한 초기화할 수 없어요.

정리하자면

하나의 인스턴스밖에 못만들기 때문에 확장이 불편할 것 같아요. private 한 멤버를 갖기에도 들어가는 비용이 큰 것 같구요. 하지만, 단 하나의 객체만을 필요로 한다면 굳이 클래스를 작성하지 않고 literal 표기법으로 생성하면 될 것 같아요. (싱글톤)

Class Object가 갖는 특징

클래스로 객체를 만든다는 것은 자바스크립트의 Prototype을 활용하여 객체를 만든다는 것과 동일하다고 볼 수 있어요. (완전히 같지 않습니다.)

class Class_Object {
  constructor() {
    this.data = 10;
  }
  method_one() {
    console.log("method_one");
  }
}

const CO = new Class_Object();
  1. 여러 개의 인스턴스를 만들 수 있어요.

  2. 객체 없이도 접근할 수 있는 static 멤버, 메소드들을 만들 수 있어요.

  3. 멤버를 쉽게 private화 할 수 있어요.

1. 여러 개의 인스턴스를 만들 수 있다는 것은?

하나의 클래스가 정의되어 있고, 이 클래스를 템플릿 삼아 객체를 찍어내게되면, 모든 인스턴스 멤버와 메소드가 각 객체 별로 생기게 되요.

그렇게 된다면, 다음 코드는 이렇게 실행될 것입니당

class Class_Object {
  constructor() {
    this.data = 10;
  }
  method_one() {
    console.log("method_one");
  }
}

const CO = new Class_Object();
const CO1 = new Class_Object();

CO.data = 20;
console.log(CO.data); // 20
console.log(CO1.data); // 10

결국 여러 개의 인스턴스는 각자의 상태를 갖게되고, 이러한 상태 구조가 필요한 경우가 있을 것 같아요 !

2. 객체 없이도 접근할 수 있는 static 멤버, 메소드들을 만들 수 있다는 것은?

class Class_Object {
  static static_data = 20;
  constructor() {
    this.data = 10;
  }
  method_one() {
    console.log("method_one");
  }
  static method_static_one() {
    console.log("method_static_one");
  }
}

console.log(Class_Object.static_data);
Class_Object.method_static_one();

클래스 내부에서 공통으로 쓰이거나, 외부에서 객체 생성 없이도 접근할 수 있는 데이터, 메소드가 필요하다면 static 키워드를 통해 정적인 메소드를 만들어 낼 수 있어요!! (언제 static 메소드가 필요한지는 다음절에서 자세하게 다룰 예정입니다)

3. 쉽게 멤버와 메소드를 private화 할 수 있어요

class Class_Object {
  #private_data = 30;
  static static_data = 20;

  constructor() {
    this.data = 10;
  }
  method_one() {
    console.log("method_one");
  }
  static method_static_one() {
    console.log("method_static_one");
  }

  #init() {
    console.log("init method");
  }
}

const newObj = new Class_Object();
newObj.#init();
console.log(newObj.#private_data);

Object에서 private화 하는 것보다 훨씬 간단 명료하네요 ?!

정리하자면

하나의 저장소 객체만을 필요로 한다면, Object 방식을 사용하면 되지만, 1️⃣ 여러개의 인스턴스를 같은 템플릿으로 만들어야하고, 2️⃣ 객체 생성 없이도 접근할 수 있는 메소드가 필요하고, 3️⃣ 쉽게 private한 멤버와 메소드를 갖고 싶다면, 클래스를 선언해두고 객체를 생성하면 되겠네요 !!

Class의 static 메소드

제가 생각하는 static 사용의 이유는

1. 객체의 생성 없이도 사용할 수 있는 범용적인 메소드의 경우

2. 팩토리 패턴의 팩토리 메소드 같이 여러 객체를 만드는 상황에서 관심사를 분리할 수 있다

객체의 생성 없이도 사용할 수 있는 범용적인 메소드의 경우 (값 포함)

class Calculator {
 	static add(a,b){
		return a+b;
    }
}
console.log(Calculator.add(10,20)) // 30

위와 같이 add라는 함수가 객체에 의해서만 동작하지 않는 경우라면 static 메소드를 작성해볼 수 있을 것 같아요.

또 모든 객체가 다음과 같이 같은 값을 공유한다면?

class UtecoCrew{
  static sameAttribute = 'Person'
}

const junzzi = new UtecoCrew();
const lokba = new UtecoCrew();

모든 크루는 사람이기 때문에 sameAttribute 값은 person 일 것입니다. 이렇게 모든 객체가 공통으로 갖는 값이 있다면 static으로 정의할 수 있겠군요.

팩토리 메소드로 관심사를 분리한다.

팩토리 패턴과 팩토리 메소드가 궁금하다면? 요기를 참고해봅시다

간단하게 말씀드리자면, 여러개의 확장된 객체를 만들고자 할때 사용할 수 있습니다.

다음은 팩토리 메소드 패턴을 활용하지 않은 경우의 코드입니다.

class Car{
 constructor(type){
  switch(type){
   
    case 'blue':
      	return new BlueCar();
      
    case 'red':
      	return new RedCar();
  }
 }
  
}

위와 같이 작성해줄 수 있지만, 그렇다면 Car 클래스 생성자의 코드가 길어지겠죠? 이를 팩토리 메서드 패턴을 통해 관심사의 분리가 된 코드로 작성해보자면?

class Car{
  
  static createCar(type){
     switch(type){
   
    case 'blue':
      	return new BlueCar();
      
    case 'red':
      	return new RedCar();
  }
  
}

위와 같이 만들어 색깔 별 자동차를 만드는 코드를 확실히 분리할 수 있겠군요 !!

정리하자면

위와 같이 1. 하나의 클래스 속성을 가진 여러 타입의 객체를 만들어 낼 때 관심사를 분리하기위해 혹은 2. 객체의 생성없이도 사용할 수 있는 메소드를 만들기 위해 혹은 3. 공통된 속성 값을 유지하는 용도static 키워드를 활용해볼 수 있을 것 같아요.

하지만 많으면 여러모로 안좋다고 합니다. 그 부분은 이 곳에서 확인해주세요 ! 정적 메소드 써도될까?

정적메소드 써도 될까를 읽고 느낀점 TL;DR

  • 객체 지향에서 멀어질 수 있나봐요. 클래스 자체에 심어져 있는 놈들이기 때문에, 객체의 생성과는 상관없이 공간을 차지하게 되어버리는 거죠. 많으면 많을 수록 전역 변수, 전역 메소드를 많이 만드는 느낌이고, 1학년 때 배웠던 C와 다를게 없어질 것 같아요.

  • 가비지 컬렉터라는 말을 아시나요? 쉽게 생각하면 사용하지 않는 동적 할당된 메모리를 청소해주는 개체인데요. 정적 메소드는 동적으로 할당되는 영역이 아니기 때문에 청소해주지 못하게되요. 그 클래스의 정적 메소드가 사용되지 않더라도 말이죠.

Ref

profile
Frontend Ninja

0개의 댓글