안녕하세요. 이번 포스팅에서는 인스턴스 관련하여 getter 과 setter 의 사용을 지양해야하는 이유에대해 말해보려 합니다.
getter(획득자), setter(설정자) 는 무엇인가요?
어떠한 객체에 접근하여 속성값을 얻거나(getter) 속성값을 변경(setter)하는데 주로 사용됩니다.
하지만 객체지향 언어에서 중요하게 작용하는 내용이 바로 "정보의 은닉화 ( 캡슐화 )" 입니다. 무슨 뜻이냐구요? 객체의 구체적인 정보를 외부로 노출시키지 말라는 뜻입니다.
ES6 부터는 객체필드에 private 변수 선언이 가능한데, 왜 이렇게 만들었는지 생각해보면 이해가 될것입니다.
클래스로인해 생성된 인스턴스에 접근하여, 인스턴스 내부의 속성값을 조회하는데에 사용합니다.
//getter은 매개변수를 가져서는 안됩니다.
get 이름 (){
return 반환해줄 속성
}
class person {
#body = { //private 선언방법
amrs:'2개',
legs:'2개',
head:'작다'
}
get body(){
return this.#body;
}
set setter(x){
}
}
const man = new person();
man.#body.amrs = "20개";
위와 같은 문제가 생기는 이유는 참조형데이터 타입이 메모리에 저장되는 방식때문에 그렇습니다.
class person {
#body = {
amrs:'2개',
legs:'2개',
head:'작다'
}
get body(){
return JSON.parse(JSON.stringify(this.#body));
}
set setter(x){
}
}
const man = new person();
console.log(man.body.legs = 20);
console.log(man.#body.legs); //"2개"
인스턴스에 #body 를 깊은복사하여 리턴해주면 리턴된 #body는 완전히 다른 메모리 주소를 참조하기때문에 원본을 손상시키지 못합니다.
TIP. getter와 인스턴스의 속성 이름을 같게하면 협업, 리팩토링에 난항을 겪게되오니 위 코드처럼 작성하시면 안됩니다.
getter 의 네이밍을 상세하게 적어주면 외부에서 봤을 때 의도도 명확해지고, 내부의 속성을 유추하기 힘들어지기에 캡슐화에도 용이할것 같습니다.
ps. JSON.parse(JSON.stringify() 는 function(), 즉 함수를 undefiend로 처리합니다. Lodash.js 라이브러리는 함수까지 완벽하게 인스턴스화 해줍니다.
위에 언급했듯이 객체지향에서 정보의 은닉화는 굉장히 중요합니다. getter 를 사용하면 은닉화가 깨질수도 있다고 말씀드렸는데요.
객체는 독립적인 성향을 가져야하며, 외부에 의존해서는 안된다는 말이 있습니다. 객체를 사람에 비유해 이야기하자면,
내가 밥을 먹는것을 다른 사람이 해줄 수 있나요? 아닙니다.
"나"라는 객체는 독립성을 가지며 내가 뭔가 하기위해서는 내가해야합니다.
이렇듯 객체는 외부에 의존하는것을 지양해야 하는데요, getter로 조회한 데이터로 외부에서 조작하는것을 지양하라는 뜻입니다.
Getter을 이용해 값을 받아와서 처리하는 코드
class person {
#body = {
amrs:'2개',
legs:'2개',
head:'작다'
}
get body(){
return JSON.parse(JSON.stringify(this.#body));
}
set setter(x){
}
}
const man = new person();
function speakArmsCount(p){
console.log("제 팔의 갯수는"+p+"입니다");
}
speakArmsCount(man.body.amrs);
객체의 메소드로 행동을 처리하는 코드
class person {
#body = {
amrs:'2개',
legs:'2개',
head:'작다'
}
speakYourArmsCount = function(p){
console.log(" 팔의 갯수는"+p+"입니다.");
}
}
}
const man = new person();
man.speakYourArmsCount("열개");
코드도 간결해지고 의미도 더욱 상세해졌습니다. "메소드" 의 주체가 어디인가가 가장 중요합니다.
getter를 지양하게되면 애매모호한 네이밍도 없어져서 직관적이고, 객체가 외부에 의존하지않는 완벽한 독립체제에 가까워집니다. 또한 외부에서 접근하는것을 차단함으로써 정보의 은닉도 보장이됩니다.
객체와 객체의 관계에서는 어떨까요?
class carOption{
#brand = {
hyundai:{},
kia:{},
}
get showBrand(){
return this.#brand; // 일부러 참조관계를 끊지 않았습니다.
}
set modifyBrand(x){
}
}
class driverOption{
favorCar = "";
addFavorCar = function(x,y){
return x.samsung=y;
}
}
const car = new carOption();
const driver = new driverOption();
driver.addFavorCar(car2.showBrand,{});
예시가 적절하지 않을 수도 있습니다ㅠㅜ
위 예시는 driverOption 클래스가 carOption 클래스에게 접근이 가능한 경우를 예시로 들어봤습니다.
위와같은 코드로 작성하게되면 추후에 많은 문제점을 야기할 수 있습니다. 이와같이 객체와 객체의 의존도가 높아지면 높아질수록 한곳에서 문제가 터졌을 때 예측하기도 어렵거니와 한곳을 건드렸을 때 다른곳에 문제가 연쇄적으로 발생할 수 있습니다.
PS. 제가 포스팅한 내용이 정답은 아닙니다. 저도 이것저것 알아보면서 공부하는 주니어 개발자이기에.. 많은 포스팅을 읽고 내용을 정리한것이니 두서없이 작성한 점 너그러이 이해 부탁드립니다.
setter 는 다음 포스팅에 다루어보겠습니다^^