6. 자바스크립트 객체지향 프로그래밍 #3 캡슐화

2021. 11. 11. 16:08JavaScript/basic

이전글

https://yonghwankim-dev.tistory.com/166

 

6. 자바스크립트 객체지향 프로그래밍 #2 상속

이전글 https://yonghwankim-dev.tistory.com/165 6. 자바스크립트 객체지향 프로그래밍 #1 클래스, 생성자, 메서드 이전글 https://yonghwankim-dev.tistory.com/164 5. 실행 컨텍스트와 클로저 #4 클로저(Closur..

yonghwankim-dev.tistory.com

 

본 글은 INSIDE JAVASCRIPT 도서의 내용을 복습하기 위해 작성된 글입니다.

 

캡슐화란 무엇인가?

캡슐화란 기본적으로 관련된 여러 가지 정보를 하나의 틀 안에 담는 것을 의미합니다. 이를 응용하면 멤버 변수와 메서드가 서로 관련된 정보가 되고 클래스가 이것을 담는 하나의 큰 틀이라고 할 수 있습니다. 여기에서 중요한 것은 정보의 공개 여부입니다. 정보 은닉(Information Hiding)의 개념이 바로 이 부분을 담당합니다. C++이나 Java 같은 언어의 경우 public, private와 같은 키워드를 사용하여 해당 정보를 외부에 노출시킬지 은닉시킬지 여부를 결정합니다. 하지만 자바스크립트는 이러한 키워드를 지원하지 않습니다. 하지만 불가능한 것은 아닙니다.

 

const Person = function(arg){
    let name = arg ? arg : "zzoon";

    this.getName = function(){
        return name;
    };

    this.setName = function(arg){
        name = arg;
    };
};

const me = new Person();
console.log(me.getName());  // Expected Output : zzoon
me.setName("kim");
console.log(me.getName());  // Expected Output : kim
console.log(me.name);       // Expected Output : undefined

위 예제와 같이 this 객체의 프로퍼티로 선언하면 new 키워드로 생성한 객체로 접근이 가능합니다. (getName, setName)

하지만 let으로 선언한 name 변수는 접근이 불가능합니다. 오직 getName 메서드를 통해서만 반환받아 값을 읽을 수만 있습니다. 이것이 자바스크립트에서 할 수 있는 기본적인 정보 은닉 방법입니다.

 

클로저를 활용한 정보 은닉 방법 개선

위 예제의 코드를 클로저를 활용하여 아래와 같이 개선할 수 있습니다.

const Person = function(arg){
    let name = arg ? arg : "zzoon";

    return {
        getName : function(){
            return name;
        },
        setName : function(arg){
            name = arg;
        }
    };
    
};

const me = Person();    // or const me = new Person()
console.log(me.getName());  // Expected Output : zzoon

me 객체에는 Person 함수의 private 멤버(name)에 접근할 수 있는 메서드들이 담겨 있습니다. 다만 주의할 점은 접근하는 private 멤버가 객체나 배열이면 얕은 복사로 참조만을 반환하므로 사용자가 이후 값을 변경할 수 있는 문제점을 가지고 있습니다.

 

const ArrCreate = function(arg){
    let arr = [1,2,3];

    return {
        getArr : function(){
            return arr;
        }
    };
};

let obj = ArrCreate();
let arr = obj.getArr();
arr.push(5);
console.log(obj.getArr());  // Expected Output : [1,2,3,5]

위와 같이 객체를 반환하는 경우 주의해야 합니다. 보통의 경우, 객체를 반환하지 않고 객체의 주요 정보를 새로운 객체에 담아서 반환하는 방법을 많이 사용합니다. 하지만 꼭 객체가 반환되어야 하는 경우에는 깊은 복사로 복사본을 만들어서 반환하는 방법을 사용하는 것이 좋습니다.

 

다시 처음 예제로 돌아오면 이 예제에서 사용자가 반환받은 객체는 Person 함수 객체의 프로토타입 객체에는 접근할 수 없다는 단점이 존재합니다. 이는 Person을 부모로 하는 프로토타입을 이용한 상속을 구현하기가 용이하지 않습니다. 이 문제를 해결하려면 객체를 반환하는 것이 아닌 함수를 반환하는 것이 좋습니다.

 

const Person = function(arg){
    let name = arg ? arg : "zzoon";

    let Func = function(){}
    Func.prototype = {
        getName : function(){
            return name;
        },
        setName : function(arg){
            name = arg;
        }
    };
    return Func;
}();

let me = new Person();
console.log(me.getName());  // Expected Output : "zzoon"

즉시 실행함수를 수행하고 클로저를 활용하여 name에 접근할 수 없게 하였습니다. 즉시 실행함수에서 반환되는 Func이 클로저가 되고 name이 자유변수가 됩니다.

 

References

source code : https://github.com/yonghwankim-dev/javascript\_study
송형주, 고현준 저, 『인사이드 자바스크립트』, 한빛미디어(2014)