[블로깅] Getter/Setter

WAYPIL·2023년 4월 30일
0

코드스테이츠 부트캠프 프론트엔드 44기
릴레이 블로깅 챌린지 3주차 (월요일) - 마지막


개요

Getter/Setter란 다음 3가지 경우에 사용하는 메서드를 말한다.

① 함수를 변수처럼 사용하고 싶을 때
② 변수를 만들고 싶은데, 전처리/필터링/가공을 먼저 한 뒤에 return 시키고 싶을 때
③ 값을 집어넣고 싶은데, 그 전에 전처리/필터링/가공 절차가 필요할 때

C#에도 Getter/Setter가 존재하며, Python에서는 @property라는 데코레이션이 그 역할을 담당한다.


동작 구조

const person = {
  firstName: 'James',  // 이름
  lastName: 'Lee',  // 성씨

  get fullName() {
    return `${this.firstName} ${this.lastName}`;
  },
  set fullName(name) {
    [this.firstName, this.lastName] = name.split(' ');  // ['Gildong', 'Hong']
  }
};


console.log(person.fullName);  // James Lee

person.fullName = 'Gildong Hong';

console.log(`변경 후 → ${person.fullName}`);  // 변경 후 → Gildong Hong
  • return이 있는 함수 앞에 get 키워드가 붙으면 getter 함수이고, set 키워드가 붙으면 setter 함수이다.

person.fullName을 호출하면 Getter 함수의 return 값을 가져오게 된다. 때문에 ‘James Lee’라는 문자열이 반환된다.

그러면 person.fullName = 'Gildong Hong';은 뭘까? 문자열 'Gildong Hong'이 매개변수 name을 통해 들어오고. 공백을 사이로 쩍 갈라진 문자열 배열 중 1번째 값을 firstName에 할당, 2번째 값을 lastName에 할당한다. 그 결과 person.firstName은 James에서 Gildong으로 바뀌고, person.lastName은 Lee에서 Hong으로 바뀐다.

만약에 person 객체의 내용물을 모른다면?

const person = {
  fullName: 'James Lee'
};


console.log(person.fullName);  // James Lee

person.fullName = 'Gildong Hong';

console.log(`변경 후 → ${person.fullName}`);  // 변경 후 → Gildong Hong

사람들은 '그냥 객체 안에 fullName이라는 변수가 있나 보다.'라고 생각할 것이다. 사실 fullName은 변수가 아니라 함수인데 말이다. 이러한 특성을 캡슐화라고 부르며, 객체 지향 프로그래밍에 있어 매우 중요한 요소이다.

그래서 Getter/Setter는 함수를 변수처럼 사용하고 싶을 때 많이 사용한다.


Getter/Setter 사용을 안 한다면?

1. 객체 바깥에서 로직을 짜야 한다

const person = {
  firstName: 'James',
  lastName: 'Lee',
};


let fullName = `${person.firstName} ${person.lastName}`;

console.log(fullName);  // James Lee

person.firstName = 'Gildong';
person.lastName = 'Hong';

console.log(`변경 후 → ${fullName}`);  // 변경 후 → James Lee

객체 바깥에서 변수를 선언하고 로직을 작성함으로써 코드가 상당히 지저분해졌다. 게다가 fullName 변수가 원시 자료형이기 때문에 바뀐 내용이 적용이 안 되었다.

이를 해결하기 위해서는 또 똑같이 fullName 변수에 재할당해야 한다.

const person = {
  firstName: 'James',
  lastName: 'Lee',
};


let fullName = `${person.firstName} ${person.lastName}`;

console.log(fullName);  // James Lee

person.firstName = 'Gildong';
person.lastName = 'Hong';

fullName = `${person.firstName} ${person.lastName}`;
console.log(`변경 후 → ${fullName}`);  // 변경 후 → Gildong Hong

같은 코드를 복붙해서 쓰는 최악의 사태가 벌어졌다.


2. 일반 함수를 사용할 경우

2.1. 단일 책임 원칙을 위반하게 된다

const person = {
  firstName: 'James',
  lastName: 'Lee',

  fullName: function (name) {
    if (name === undefined) {
      return `${this.firstName} ${this.lastName}`;
    } else {
      [this.firstName, this.lastName] = name.split(' ');
    }
  },
};


console.log(person.fullName());  // James Lee

person.fullName('Gildong Hong');

console.log(`변경 후 → ${person.fullName()}`);  // 변경 후 → Gildong Hong

SOLID라고 하는 객체지향 프로그래밍의 5대 법칙이 있는데 그중 하나가 ‘단일 책임의 법칙’이다. ‘하나의 함수는 하나의 역할만 담당해야 한다.’라는 의미이다.

그런데 지금 fullName 함수를 보면 역할이 2개(리턴 & 할당)임을 알 수 있다. 이런 함수들이 여러 개 생겨나버리면 나중에 큰 혼란이 발생할 수 있다.

그러면 함수를 2개로 나눠보면 어떨까?

2.2. 가독성 저하

const person = {
  firstName: 'James',
  lastName: 'Lee',

  getFullName: function () {
    return `${this.firstName} ${this.lastName}`;
  },
  setFullName: function (name) {
    [this.firstName, this.lastName] = name.split(' ');
  },
};


console.log(person.getFullName());  // James Lee

person.setFullName('Gildong Hong');

console.log(`변경 후 → ${person.getFullName()}`);  // 변경 후 → Gildong Hong

겉으로는 문제가 없어 보이나,

console.log(person.getFullName());
person.setFullName('Gildong Hong');
console.log(person.fullName);
person.fullName = 'Gildong Hong';

위쪽 코드와 아래쪽 코드, 어느 쪽이 더 읽기가 편한가? 대부분 아래쪽이라고 말할 것이다.

로직을 실행하는 코드와 값을 반환하는 코드 모두 일반 함수로 통일시켜버리면 나중에 구분이 힘들어질 수 있다. 함수명에 get/set 접두어를 붙여도 되지만, 그러면 그만큼 함수명이 길어져버려서 보기가 좀 안 좋아진다.

이때 Getter/Setter를 사용하면, 그냥 일반 함수를 사용할 때보다 가독성이 향상시킬 수 있다.


활용 예시 (만 나이 계산)

Before

const person = {
    age: 37  // 만 나이
};

console.log(person.age); // 37

/* 이틀 후... 생일이 지났음 */

console.log(person.age);  // 37

어떤 사람의 만 나이가 37살이다. 근데 이틀이 지나고 생일이 지났다면, 한 살 더 먹어서 38살이 되어야 맞다. 그런데 콘솔 로그로 출력해보니까 여전히 37살이라고 나온다. 당연하게도 age 값 수정을 안 해서 그렇다.

그러면 프로그래머가 일일이 수정해야 한다.

console.log(person.age); // 37

/* 이틀 후... 생일이 지났음 */
person.age++

console.log(person.age);  // 38

근데 사람이 100명이라면, 천만 명이라면? 이걸 다 언제 수정하고 앉아 있을 건가?

“하아…… 사람이 일일이 수정하지 않아도, 변수가 알아서 바뀐다면 얼마나 좋을까?”

이럴 때 사용하는 게 바로 Getter 함수이다.

After

let currentDate = '2023-02-24';  // 현재 날짜

const person = {
  firstName: 'James',
  lastName: 'Lee',

  birthday: '1985-02-25',  // 생년월일

  get age() {  // 만 나이 계산 알고리즘
    const [nowY, nowM, nowD] = currentDate.split('-').map(x => parseInt(x));
    const [birthY, birthM, birthD] = this.birthday.split('-').map(x => parseInt(x));
    const resultAge = nowY - birthY - (nowM <= birthM && nowD < birthD);
    return resultAge;
  },
};


console.log(person.age);  // 37

/* 이틀 후... */
currentDate = '2023-02-26';

/* 생일이 지났음 */

console.log(person.age);  // 38

현재 날짜(currentDate)가 바뀌면서 만 나이(age)도 자동으로 바뀌는 것을 알 수 있다.

person.age를 호출하면, getter 함수 안에 있는 나이 계산 알고리즘을 거쳐서 현재 나이를 return해준다. 이렇듯 상황에 따라 변수값이 저절로 바뀌어야 할 때, 상황에 따라 변수값이 바뀌도록 만들고 싶을 때 Getter/Setter를 사용하면 좋다.

이러한 특징 때문에 실무에서도 꽤 사용되는 편이다. 웹 개발에서는 잘 모르겠지만, 옛날에 혼자서 탑뷰 레이싱 게임 개발할 때 Getter를 상당히 요긴하게 사용했었다.



<주의 사항>

이 게시물은 코드스테이츠의 블로깅 과제로 제작되었습니다.
때문에 설명이 온전치 못하거나 글의 완성도가 낮을 수 있습니다.

profile
Self-improvement Guarantees Future.

0개의 댓글