JavaScript

Javascript Class에서 getter, setter는 무엇이고 어떻게 쓰는건가?

sangchu 2025. 2. 9. 21:31

Class에 대해 공부하는 도중, getter와 setter가 왜 필요하고 어떻게 동작하는지 이해가 되지 않았다. 그래서 이번 기회에 정리해보고자 한다.

 

getter

  • 객체의 값을 가져올 때(읽을 때) 자동으로 호출되는 메서드이다.
  • get 키워드로 정의하며, 별도의 인자를 받지 않는다.
  • 내부적으로는 함수지만, 외부에서 접근할 때는 obj.prop와 같이 프로퍼티처럼 보인다.

 

setter

  • 객체의 값을 설정할 때 자동으로 호출되는 메서드이다.
  • set 키워드로 정의하며, 주로 하나의 인자를 받는다.
  • 외부에서 obj.prop = 값 형태로 할당하면, 내부적으로 set prop(값)이 실행된다.

 

예시

다음과 같이 Person 클래스가 있다. name 프로퍼티를 게터·세터로 관리해, 이름을 등록하거나(set) 가져온다(get).

class Person {
constructor(name) {
this._name = name;
}
// getter
get name() {
console.log('getter 호출');
return this._name;
}
// setter
set name(newName) {
console.log('setter 호출');
this._name = newName;
}
}
const person = new Person('Alice');
// getter 작동
console.log(person.name);
// 콘솔 출력>
// getter 호출
// Alice
// setter 작동
person.name = 'Bob';
// 콘솔 출력>
// setter 호출
console.log(person.name);
// 콘솔 출력>
// getter 호출
// Bob

person.name으로 값을 읽을 때, get name()이 자동으로 호출되어 this._name을 반환한다.

person.name = 'Bob'으로 값을 할당하면, set name(newName)이 실행되어 내부 _name 값을 업데이트한다.

 

getter와 setter를 사용하는 이유

클래스 문법을 배울 때, 보통 생성자(constructor)와 메서드만 알아도 객체를 생성하고 사용하는 데 큰 어려움이 없어 보일 수 있다. 그래서 'getter와 setter를 꼭 써야 하나?'라는 의문이 들 수도 있다.

 

하지만 캡슐화(encapsulation) 관점에서 보면, 객체 내부 속성을 직접 수정하는 것은 위험할 수 있다.

예를 들어, 나이가 음수가 되지 않도록 강제하거나, 이름이 특정 형식을 따라야 할 때, 게터·세터를 통해 검증 로직을 넣으면 원치 않는 결과를 예방할 수 있다. 이는 객체 지향 프로그래밍(OOP)에서 중요한 개념이기도 하다.

 

아래 예시에서는 Person 클래스에 출생 연도를 저장해두고, 나이를 동적으로 계산해서 반환하는 게터가 있다. 하지만 세터가 없으므로 외부에서 나이를 임의로 바꾸는 것이 불가능하다.

class Person {
constructor(name, birthYear) {
this._name = name;
this._birthYear = birthYear;
}
// 출생 연도를 이용해, 현재 나이를 계산해 반환
get age() {
const currentYear = new Date().getFullYear();
return currentYear - this._birthYear;
}
}
const person2 = new Person('Tom', 2000);
console.log(person2.age); // 25
// 세터가 없으므로 외부에서 age에 임의 할당 불가
person2.age = 99;
console.log(person2.age); // 여전히 25

 

사용 시 주의사항

1) 게터/세터 내부에서 자기 자신(this.name)을 부르면 무한 재귀가 발생한다

class Person {
constructor(name) {
this.name = name; // -> set name() 호출
}
get name() {
// ❌ 잘못된 사용: 내부에서 this.name 호출
return this.name; // 다시 get name() 호출 -> 무한 재귀
}
set name(value) {
// ❌ 잘못된 사용: this.name으로 재할당 -> 무한 재귀
this.name = value;
}
}

게터 안에서 this.name을 사용하면 “프로퍼티 name에 접근 → 게터 재호출”로 이어져 무한 루프에 빠진다.

세터 안에서도 마찬가지로 this.name = value를 쓰면 같은 세터를 재호출하게 되어 역시 무한 루프에 빠진다.

 

2) 내부에서는 _name(또는 #name) 같은 별도 프로퍼티로 접근해야 한다

class Person {
constructor(name) {
// 여기서도 set name()이 호출되지만,
// 세터 내부에서는 _name에 값을 할당하므로 안전
this.name = name;
}
get name() {
return this._name;
}
set name(value) {
this._name = value;
}
}

이렇게 하면 게터·세터가 서로를 재귀 호출하지 않고, 안정적으로 내부 값을 관리할 수 있다.

 

get, set의 내부 변수에서 언더스코어( _ )를 붙이는 이유

처음에는 '왜 _name처럼 언더스코어를 쓰는가?' 의문이 들 수 있다. 사실 강제 규칙이 아니므로, 꼭 붙일 필요는 없다.

 

다만 ‘내부용 필드’임을 명시적으로 보여주어 재귀적 호출 실수를 예방하고, 코드를 읽는 사람이 '이건 내부용 프로퍼티구나~'라는 사실을 쉽게 파악할 수 있다는 장점이 있다.

 

또한, private field(#name) 문법을 사용하면 클래스 외부에서 접근을 완전히 차단할 수도 있다.

팀 규칙이나 상황에 따라 언더스코어 또는 # 필드를 쓰는 식으로 스타일이 달라질 수 있다.

 

get, set 메서드 이름 규칙

처음에는 this.name = name으로 정의했는데, 게터·세터 내부에서는 this._name 등 실제 내부 변수명과 다를 수 있다.

그래도 문제가 없는 이유는 게터/세터 이름이 곧 '외부에서 보이는 프로퍼티 이름'이기 때문에 가능한거다.

 

즉, 내부에서는 _name이든 _banana든 어떤 변수명을 사용해도, 게터/세터가 정의된 이름이 name이면, person.name으로 접근할 때 그 게터·세터가 자동 호출된다.

 

아래 예시에서 게터·세터 이름은 age이지만, 내부에는 _banana라는 프로퍼티를 사용하고 있다.

하지만 콘솔로 출력해보면 문제 없이 30이 출력되는걸 확인할 수 있다.

class Person {
constructor(age) {
// 인스턴스 생성 시, 내부적으로 set age(...)가 호출됨
this.age = age;
}
get age() {
return this._banana;
}
set age(value) {
this._banana = value;
}
}
const p = new Person(30);
console.log(p.age); // 내부적으로 get age() 실행 -> this._banana 반환
// 콘솔 출력 > 30

 

참고

MDN | Classes

코어 자바스크립트 | 프로퍼티 getter와 setter

Youtube | 드림코딩 - 자바스크립트 6. 클래스와 오브젝트의 차이점(class vs object), 객체지향 언어 클래스 정리