자바스크립트 class_ private, get & set

신유빈·2023년 2월 26일
0
post-thumbnail

등장배경

개발규모가 커지면서 프레임워크 개발자와 애플리케이션 개발자가 나뉘자, 코드들이 위험해 지기 시작했습니다.

class Square{
  constructor(length){
    this.length = length
  }
  getPerimeter(){
    return this.length * 4
  }
  getArea(){
    return this.length * this.length
  }
}

let 정사각형 = new Square(-10);
console.log(`정사각형의 둘레 : ${정사각형.getPerimeter()}`);
console.log(`정사각형의 넓이 : ${정사각형.getArea()}`);

현재 코드를 보면 정사각형 객체를 생성할때 생성자의 매개변수로 음수를 전달하고 있습니다.
그런데 '길이'라는 건 음수로 나오면 안되는 값입니다.


하지만 프레임워크 개발자들은 Square클래스를 만들때 "어떤 바보가 길이로 음수를 넣겠냐ㅋㅋ"라고 생각했을 것입니다.
하지만 세상엔 정말 다양한 사람들이 있습니다. 바보가 있을수도 있잖아요.


이러한 문제를 막는 방법으로는

조건문을 활용해서
매개변수가 0 이하로 들어오면 예외를 발생시켜 클래스의 사용자에게 그렇게 할수는 없다고 인지 시켜주는 방법이 있습니다.

class Square{
  constructor(length){
    if(length <= 0){
      throw '님 바보에요? 길이는 0보다 커야함' 
      //throw 키워드를 사용해 강제로 오류를 발생시킵니다.
    }
    this.length = length
  }
  
  getPerimeter(){
    return this.length * 4
  }
  getArea(){
    return this.length * this.length
  }
}

let 정사각형 = new Square(-10);
console.log(`정사각형의 둘레 : ${정사각형.getPerimeter()}`);
console.log(`정사각형의 넓이 : ${정사각형.getArea()}`);

하지만 이러한 코드만으론 다음과 같이 생성자로 객체를 생성한 후에 사용자가 length 속성을 변경하는 것을 막을 수 없습니다.
//사용자의 잘못된 사용 예
const 정사각형= new Square(10);
정사각형.length = -10; //🤦‍♀️
console.log(`정사각형의 둘레 : ${정사각형.getPerimeter()}`);
console.log(`정사각형의 넓이 : ${정사각형.getArea()}`);

이렇게 클래스 사용자가 클래스의 속성&메소드를 의도하지 않은 방향으로 사용하는 것을 막고,
클래스의 안정성을 확보하기 위해 나온 문법이 **Private 속성**과 **메소드**입니다.
문법은 다음과 같습니다.



Private 속성

class 클래스이름 {
  #속성이름
  #메소드이름() {
  }
}

속성과 메소드 이름 앞에 #을 붙이기만 하면 됩니다.
이처럼 #이 붙어있는 속성과 메소드는 모두 Private 속성과 Private 메소드가 됩니다.

주의할 것이 있다면
Private속성은 사용하기 전에 미리 constructor 외부에 어떤 속성을 private속성으로 사용하겠다고 선언해둬야 한다는 것입니다.

class Square{
  #length // 이 위치에 미리 private 속성을 쓰겠다고 미리 선언합니다.
  constructor(length){
    if(length <= 0){
      throw '님 바보에요? 길이는 0보다 커야함' 
    }
    this.#length = length
  }
  
  getPerimeter(){
    return this.#length * 4
  }
  getArea(){
    return {this.#length * this.#length} 
  }
}

let 정사각형 = new Square(10);
console.log(`정사각형의 둘레 : ${정사각형.getPerimeter()}`);
console.log(`정사각형의 넓이 : ${정사각형.getArea()}`);

2020년 3월 기준으로 최신 버전 웹브라우저에선 작동하지만, VScode 등 에디터가 인식하지 못해 빨간 밑줄을 출력하는 문법입니다. 에디터에서 빨간 밑줄이 생겨도 아무 문제가 없으므로 걱정마세요.


이렇게 private 속성으로 변경하면 클래스 외부에서는 해당 속성에 접근할 수 없습니다.
예를 들어 누가 정사각형.length를 변경하더라도
클래스 내부에서 사용하고 있는 속성은 length가 아니라 #length라서 결과에는 어떤 영향도 주지 않습니다.

class Square {
  #length
  constructor(length) {
    if (length <= 0) {
      throw '님 바보에요? 길이는 0보다 커야함'
    }
    this.#length = length
  }

  getPerimeter() {
    return this.#length * 4
  }
  getArea() {
    return this.#length * this.#length
  }
}

let 정사각형 = new Square(10);
정사각형.length = -10;
console.log(정사각형.length);
console.log(`정사각형의 둘레 : ${정사각형.getPerimeter()}`);
console.log(`정사각형의 넓이 : ${정사각형.getArea()}`);

정사각형.length 를 변경해도 처음 생성했을때 값으로 class내 메소드들이 작동합니다.

만약 #length를 클래스 외부에서 직접 바꾸려고 하면...

정사각형.#length = -10;

이렇게 Syntax 에러를 내줍니다.

Private 속성은 클래스 외부에서는 접근할 수 없으므로 클래스 사용자가 클래스를 잘못 사용하는 문제를 줄일 수 있습니다.


게터와 세터

방금 사용한 private 속성을 사용하면 외부에서는 #length 속성에 아예 접근조차 못하게 되는 문제가 발생합니다.

console.log(정사각형.#length); //ㅠㅠ..

현재 정사각형 객체의 length 속성이 얼마인지 확인도 못하고,
length 속성을 변경하고 싶어도 변경할 수 없습니다.


그래서 프레임워크 개발자들은 상황에 따라서 속성을 읽고 쓸 수 있는 메소드를 만들어서 제공합니다.

class Square {
  #length

  constructor(length) {
    this.setLength(length)
  }

  setLength(value) {
    if (value <= 0) {
      throw '님 바보에요? 길이는 0보다 커야함'
    }
    this.#length = value
  }

  getLength(value) {
    return this.#length
  }

  getPerimeter() {
    return this.#length * 4
  }

  getArea() {
    return this.#length * this.#length
  }
}

const 정사각형 = new Square(10);
console.log(`한변의 길이는 ${정사각형.getLength()}입니다.`);
정사각형.setLength(-10);

이때
getLength() 메소드처럼 속성값을 확인할때 쓰는 메소드를 게터(getter)라고 부르며,
setLength() 메소드처럼 속성에 값을 지정할때 쓰는 메소드를 세터(setter)라고 부릅니다.

처음 게터, 세터를 배우면 모든 Private 속성에 게터와 세터를 붙이려고 하기도 하는데, 필요한 상황에서만 만들면 됩니다.


만약 사용자가 값을 읽는 것을 거부하겠다면 게터를 만들지 않아도 됩니다.
또한 사용자가 값을 지정하는 것을 거부하겠다면 세터를 만들지 않아도 됩니다.


아예 속성에 접근하지도 못하게 둘다 막을 수도 있습니다.


이러한 형태의 코드를 수많은 프레임워크 개발자들이 사용하기 시작하니, 프로그래밍언어 개발자들은 프레임워크 개발자들이 코드를 더 쉽게 작성하고 사용하라고 get, set 키워드 문법을 제공합니다.
class 클래스이름{
	get 메소드이름(){ return}
    set 메소드이름(value){}
}

이렇게만 보면 키워드 안 쓰고 함수이름에 get,set 넣는게 더 편해보일수도 있는데, 실제로 활용하면 애플리케이션 개발자쪽의 코드가 훨씬 간단해집니다.

class Square {
  #length

  constructor(length) {
    this.length = length 
    // this.legth에 값을 지정하면, set length 메소드가 호출됩니다.
  }

  set length(value) {
    if (value <= 0) {
      throw '님 바보에요? 길이는 0보다 커야함'
    }
    this.#length = value
  }

  get length(value) {
    return this.#length
  }

  get perimeter() {
    return this.#length * 4
  }

  get area() {
    return this.#length * this.#length
  }
}

const 정사각형 = new Square(10);
console.log(`한변의 길이는 ${정사각형.length}입니다.`);
console.log(`둘레: ${정사각형.perimeter}`);
console.log(`넓이: ${정사각형.area}`);
정사각형.length(20);
console.log(`한변의 길이는 ${정사각형.length}입니다.`);

속성을 사용하는 것처럼 코드를 쓰면, 자동으로 게터와 세터가 호출이 됩니다.

클래스 쪽은 큰 변경이 없는것 같지만,
클래스를 활용하는 쪽에선 단순하게 속성을 사용하는 형태처럼 게터와 세터를 사용할 수 있게 되었습니다.

이렇게 코드를 작성하면 코드를 사용하는 쪽에서 훨씬 쉽게 사용할 수 있어서 게터와 세터를 사용합니다.

참고
혼자 공부하는 자바스크립트(윤인성 지음)
혼자공부하는자바스크립트-60강

0개의 댓글