JavaScript Class

개요

https://www.boldare.com/blog/how-to-use-javascript-classes/

클래스에 대해서 알아보기전 이러한 클래스가 생겨 날 수 있었던 배경이 무엇이었는지 알아야 합니다. 객체지향 프로그래밍(Object-Oriented Programming, OOP)은 코드의 재사용성, 확장 가능성 그리고 유지보수의 용이성을 중요시한 프로그래밍 패러다임입니다.

 

`JavaScript`는 원래 프로토타입 기반 언어로, 클래스 기반의 언어들과는 다소 차이가 있었습니다. 그러나 ES6 이후, `JS`에도 `class` 키워드가 도입이되면서, 개발자들에게 더 익숙한 클랙스 기반의 접근의 방식을 제공하기 시작하였습니다. 

 

클래스 (Class)

클래스는 어떠한 객체(인스턴스)를 생성하기 위한 하나의 짜여진 설계도라고 생각하면 되겠습니다. 예를 들면 여러 개의 같은 붕어빵을 만들기위해 존재하는 하나의 붕어빵 틀이라고 생각하면 좀 더 접근하기 쉽습니다. 그러면 이러한 클래스는 어떻게 작성이 가능한지 보도록 하겠습니다. 

class Car {
  brand;

  constructor(brand) {
    this.brand = brand;
  }

  present() {
    return `I have a ${this.brand}`;
  }
}

const myCar = new Car("Ford");
console.log(myCar.present()); // I have a Ford

 

`class` 라는 명령어에 `Car` 라는 이름의 클래스 명을 지었습니다. 해당 클래스는 어떤 브랜드인지를 나타내는 속성을 가지고 있네요. 그렇게 하나의 클래스에는 0개 이상의 속성을 가지고 있어야 합니다. 

 

여기서 `constructor`는 클래스로부터 객체를 생성하고 초기화하기 위해 호출되는 특별한 메소드입니다. `this` 키워드는 클래스에서 생성된 객체를 가리킵니다.

 

추가로 알아야 하는 것은 속성 값에 현재 `brand`가 존재 하는데 이것이 없어도 정상적으로 인스턴스가 생성이 된다는 것입니다. 그럼 그 상황에서 `myCar.present()`라는 함수를 사용하게 되면 `I have a undefined` 라고 콘솔에 찍힐 것입니다. 명시적으로 그러한 속성이 존재하는지 알리고 싶다면 속성을 표기하는 것이 좋은데, 팀 마다 약속된 룰을 따라서 작성하면 되겠습니다.

 

 게터와 세터 (getter/setter)

`JavaScript` 클래스에서 게터(getter)와 세터(setter)는 객체의 속성에 접근하고 수정하는 메소드를 의미합니다. 이들은 객체의 속성 값을 간접적으로 읽거나 쓰기 위한 방법을 제공합니다. 이 방법은 객체 내부의 데이터를 보호하고, 유효성 검사를 할 수 있는 장점이 있습니다.

게터 메소드는 특정 값을 반환하고, 세터 메소드는 특정 값을 설정합니다. `ES6` 클래스에서는 `get`과 `set` 키워드를 사용하여 이러한 메소드를 정의할 수 있습니다.

예를 들어, 간단한 사용자 클래스에서 이름(name)을 가지고 있고, 이 이름을 가져오거나 설정할 때 유효성 검사를 하고 싶다고 가정해 봅시다.

class User {
  constructor(name) {
    this._name = name; // '_'는 일반적으로 비공개 속성을 나타내기 위해 사용됩니다.
  }

  get name() {
    return this._name;
  }

  set name(value) {
    if (value.length < 4) {
      console.log("Name must be at least 4 characters long.");
      return;
    }
    this._name = value;
  }
}

const user = new User("John");
console.log(user.name); // John

user.name = "Al"; // Name must be at least 4 characters long.
console.log(user.name); // John

user.name = "Alice"; // This is valid
console.log(user.name); // Alice

 

  • 생성자에서는 `_name` 속성을 설정합니다.` _name` 속성 앞에 밑줄을 붙여서 비공개 속성임을 나타냅니다. 이는 `JavaScript`에서의 관례로, 실제로 속성을 비공개로 만들지는 않습니다.
  • `get name()` 메소드는 `_name` 속성의 값을 반환합니다.
  • `set name(value)` 메소드는 이름의 길이를 검사하고, 길이가 4 미만일 경우 이름을 설정하지 않고 경고 메시지를 출력합니다.

이렇게 게터와 세터를 사용함으로써, 객체의 데이터를 검증하고 보호하는 데 도움이 됩니다. 게터와 세터는 실제로 속성처럼 보이지만, 메소드입니다. 이것은 `user.name`에 값을 할당할 때나 읽을 때 내부적으로 세터와 게터 메소드가 호출된다는 것을 의미합니다.

 

정적 메소드(Static Method)

정적 메소드는 `static` 키워드를 사용하여 정의됩니다. 클래스 내에서 이 키워드를 메소드 앞에 붙여 사용합니다. 예를 들어, 다음과 같이 User 클래스에 정적 메소드를 정의할 수 있습니다.

class User {
  constructor(name) {
    this.name = name;
  }

  sayHelloInInstance() {
    return `Hello! My name is ${this.name}.`;
  }

  static sayHelloInStatic() {
    return "Hello!";
  }
}

 

정적 메소드 호출

정적 메소드는 인스턴스를 생성하지 않고도 정적 메소드를 호출할 수 있습니다.

console.log(User.sayHelloInStatic()); // Hello!

const user = new User("John");

console.log(user.sayHelloInStatic()); // Error! sayHello is not a function

반면에 인스턴스 메소드는 클래스의 인스턴스를 통해서만 호출이 가능합니다.

console.log(user.sayHelloInInstance()); // Hello! My name is John.

 

정적 메소드에서는 `this` 키워드를 사용할 때 주의가 필요합니다. 정적 메소드 내에서 `this`는 인스턴스를 가리키는 것이 아니라 클래스 자체를 가리킵니다. 이로 인해 정적 메소드 내에서 인스턴스 속성에 접근하려고 하면 오류가 발생합니다.

 

정적 메소드의 사용 사례

정적 메소드는 종종 인스턴스와 독립적인 유틸리티 함수로 사용됩니다. 예를 들어, 객체가 유효한지 검증하는 메소드나 설정 객체를 생성하는 팩토리 메소드 등에 사용될 수 있습니다.

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

  static createUser(name, age) {
    return new User(name, age);
  }
}

const newUser = User.createUser("Alice", 25);
console.log(newUser); // User { name: 'Alice', age: 25 }

이 예에서 `createUser`는 정적 메소드로, `User` 클래스의 새 인스턴스를 만들어 반환합니다. 이 방식은 특정한 설정이나 조건에 따라 객체를 생성해야 할 때 유용하게 사용됩니다.

정적 메소드는 클래스와 관련된 일반적인 작업에 유용하지만, 특정 인스턴스의 데이터를 다루어야 하는 경우에는 적합하지 않습니다.

 

상속 (Inheritance)

상속은 클래스 간에 코드를 재사용하는 강력한 방법입니다. 자식 클래스는 부모 클래스로부터 속성과 메소드를 상속 받을 수 있습니다. 

class Car {
  brand;

  constructor(brand) {
    this.brand = brand;
  }

  present() {
    return `I have a ${this.brand}`;
  }
}

class Model extends Car {
  constructor(brand, mod) {
    super(brand);
    this.model = mod;
  }

  show() {
    return `${this.present()}, it is a ${this.model}`;
  }
}

const myModel = new Model("Ford", "Mustang");
console.log(myModel.show()); // I have a Ford, it is a Mustang

`extends` 키워드는 클래스가 다른 클래스로부터 상속받을 수 있게 합니다. `super` 호출은 부모 클래스의 생성자를 호출하는데 사용됩니다.

 

오버라이드(override)

`JavaScript` 클래스에서 메소드를 오버라이드(override)한다는 것은 상속받은 클래스에서 부모 클래스의 메소드를 재정의하는 것을 의미합니다. 이렇게 하면 상속받은 클래스는 같은 이름의 메소드를 가지지만, 다른 동작을 수행할 수 있습니다. 오버라이딩은 다형성의 중요한 부분이며 객체 지향 프로그래밍에서 자주 사용됩니다.

class Car {
  constructor(brand) {
    this.brand = brand;
  }

  present() {
    return `I have a ${this.brand}`;
  }
}

class Model extends Car {
  constructor(brand, mod) {
    super(brand);
    this.model = mod;
  }

  // 오버라이드된 present 메소드
  present() {
    return `${super.present()}, it is a ${this.model}`;
  }
}

const myModel = new Model("Ford", "Mustang");
console.log(myModel.present()); // I have a Ford, it is a Mustang

`super.present()` 호출은 오버라이드된 메소드 안에서 부모 클래스의 `present` 메소드를 호출합니다. 이렇게 하면, 확장된 클래스는 부모 클래스의 기능을 유지하면서 추가적인 기능을 제공할 수 있습니다.

 

타입 체크

타입 체크는 오류를 방지하고, 함수나 메소드에 올바른 타입의 인수가 전달되었는지 확인하는 데 유용합니다. `JavaScript`는 동적 타입 언어이므로, 이러한 검사를 통해 코드의 안정성을 높일 수 있습니다.

class Car {
  constructor(brand) {
    this.brand = brand;
  }

  present() {
    return `I have a ${this.brand}`;
  }
}

class Model extends Car {
  constructor(brand, mod) {
    super(brand);
    this.model = mod;
  }
}

console.log(Model instanceof Car); // true

 

'Language > JavaScript' 카테고리의 다른 글

JavaScript Prototype  (0) 2023.11.05
JavaScript Obejct  (0) 2023.11.05
JavaScript Symbol - 심볼  (0) 2023.10.24
JavaScript) Closure 그는 무엇인가?  (0) 2023.09.13
JavaScript) Promise 와 예제  (0) 2023.09.13