본문 바로가기
IT/JavaScript & TypeScript

[JavaScript] class, 클로저, Prototype을 통한 객체지향

by 저당단 2025. 9. 4.

실질적인 데이터를 가지고 있는 필드와 그 필드에 접근하기 위한 메서드를 묶을 때 객체를 사용한다.

이런 경우 자바스크립트에서는 흔히 class를 사용한다.

하지만 class 말고 다른 방법으로 구현할 수도 있는데 각 방법을 비교해 보았다.

 

1. class

class Animator {
  private currentValue: number = 0;
  private isAnimating = false;

  forward() {
    this.isAnimating = true;
    this.currentValue += 1;
  }

  stop() {
    this.isAnimating = false;
  }

  getValue() {
    return this.currentValue;
  }
}
 
  • 특징: this를 통해 모든 메서드가 같은 상태를 참조 가능. 접근 제어자를 붙일 수 있음. 메서드 정의는 한번만 함

 

2. 클로저

function createAnimator() {
  let currentValue = 0;
  let isAnimating = false;

  return {
    forward() {
      isAnimating = true;
      currentValue += 1;
    },
    stop() {
      isAnimating = false;
    },
    getValue() {
      return currentValue;
    }
  }
}
 
  • 특징: 상태를 공유하기 위해 클로저 사용. 객체 인스턴스 만들때마다 메서드들을 새로 만들어야 됨 -> 낭비

자바스크립트 엔진이 업그레이드 되면서 성능적인 측면으로는 큰 차이가 없지만 아무래도 클로저보다는 class 문법이 좀더 직관적이라는 점, 그리고 접근 제어자를 사용할 수 있다는 점에서 class는 여전히 메리트가 있다.

 

참고로 React에서 사용하는 커스텀 훅이 이 클로저 기반이다.

차이는 필드를 지역변수로 관리하는 게 아니라 state 기반으로 관리한다는 것이다.

메서드의 경우 새로 생성되긴 하지만 앞에서 언급했던 것처럼 성능적인 측면에서는 큰 차이가 없다. 하지만 새로 생성되는 과정에서 자식 컴포넌트가 리렌더링된다면 이때 성능 병목이 생길 수 있으므로 이것을 막기 위해서 useCallback을 쓴다.

 

3. Prototype

자바스크립트에선 어떤 객체를 만들면 그 객체의 원형을 포인터로 참조하고 있다.

이때 그 원형이 Prototype이다.

자바스크립트 객체의 내부를 보면 [[Prototype]] 이라는 필드를 가지고 있는 것이 보일 것이다.

이것이 바로 Prototype으로 접근할 수 있는 포인터이다.

function Animator() {
  this.currentValue = 0;
  this.isAnimating = false;
}

Animator.prototype.forward = function() {
  this.isAnimating = true;
  this.currentValue++;
};
Animator.prototype.stop = function() {
  this.isAnimating = false;
};
 
  • 특징: 클래스가 내부적으로 이 동작을 함.

 

이 코드는 Prototype을 통해 클래스의 내부 구현방식을 그대로 나타낸 것이다. 클래스와 특징이 같으나 생긴 것만 좀 더 복잡하다.

이렇듯 Prototype에 포인터를 통해 접근하는 Prototype 체인을 이용하면 객체에 속성을 추가할 수 있다.

이는 비단 사용자가 만든 객체 뿐 아니라 자바스크립트가 기본적으로 제공하는 객체에도 가능하다.

Array.prototype.first = function() {
  return this[0];
};

const arr = [10, 20, 30];
console.log(arr.first()); // 10

이런 식으로 추가하는 것도 가능.

 

하지만 이런 식으로 사용했다간 협업할 땐 라이브러리 충돌이나 러닝커브 등의 문제로 디버깅이 힘들어질 수 있다.

팀원들과 따로 협의되지 않았다면 귀찮아질 수도 있으니 주의하자.