본문 바로가기

DEVELOP/Javascript

[OOP] 캡슐화(Encapsulation)와 은닉

반응형

 

객체 지향 프로그래밍(Object-Oriented Programming : OOP)이란 사실 코드를 작성하는 구체적인 로직이라기보단, 객체를 중심으로 프로그래밍을 하겠다는 패러다임이라고 보는 것이 좀 더 정확하다. OOP는 여러 특징을 가지고 있지만 이번엔 그 중에서도 자바스크립트에서의 캡슐화에 대해 조금 더 정리를 해보려 한다. 참고로 OOP에 대한 기본적인 개념은 다음 링크에 간단하게 작성해 놓았다. (무려 5달 전에 작성해놓은 글이라 내용이 많이 부실하지만...공부해보며 좀 더 글을 보강할 예정이다...✨)

 

 

[JavaScript] OOP in JavaScript

목표 객체 지향 프로그래밍의 특징 이해 인스턴스를 생성 방법 중 es5에서의 방법과, es6의 클래스를 이용한 방법을 이해 다형성(polymorphism), 상속(inheritance)의 개념을 이해 상속관계를 가진 현실세

jjy0821.tistory.com


캡슐화는 객체 지향 프로그래밍에서 크게 두 가지의 측면을 가진다.

 

* 객체의 상태를 나타내는 프로퍼티와, 프로퍼티를 참조하고 조작할 수 있는 동작을 하나로 묶음
* 객체의 특정 프로퍼티나 메소드 중 외부에 공개할 필요가 없는 일부 정보들을 공개하지 않도록 은닉

 


 

1. 객체의 상태를 나타내는 프로퍼티와, 프로퍼티를 참조하고 조작할 수 있는 동작을 하나로 묶음

 

캡슐화

 

 우선 캡슐화는 상태를 나타내는 프로퍼티(변수)와 프로퍼티를 참조하고 조작할 수 있는 동작(메소드)을 하나로 묶는 것이다. 객체 지향 프로그래밍에서 캡슐화는 우리가 class라는 설계도 베이스를 두고 이에 사용되는 공통적인 데이터와 행위를 함께 묶어 정의한 뒤 필요할 때마다 설계도를 가져다 쓰는 방식을 말한다. 이런 방식을 통해서 개발자들은 중복된 코드를 제거하고 재사용성을 높이며, 큰 규모에서 보다 단순화 된 구조로 작성할 수 있게 된다. 아래 그림은 Car라는 class를 가지고 각각 Audi, Nissan, Volvo라는 인스턴스를 생성하는 예시다.

 


2. 객체의 특정 프로퍼티나 메소드 중 외부에 공개할 필요가 없는 일부 정보들을 공개하지 않도록 은닉

 

 대부분의 객체 지향 프로그래밍 언어에서는 클래스를 정의하고, 해당 클래스를 구성하는 프로퍼티와 메소드에 대해 public, private, protected 같은 접근 제한자를 선언해 공개 범위를 한정 시킬 수 있다. public의 경우 클래스 외부에서 참조가 가능하지만 private는 외부에서 참조할 수 없다. 

 

  사실 기존의 자바스크립트는 위에서 언급한 public, private, protected같은 접근 제한자를 사용할 수 없었다. 따라서 private하게 동작할 수 있도록 특별한 패턴을 사용해서 작성하지 않는 이상 (아래에서 좀 더 설명할테지만 사실 이 기존의 방식은 꽤 치명적인 문제가 발생하기 때문에 다른 객체 지향 프로그래밍 언어의 접근 제한자와 같은 기능을 완벽하게 구현하지는 못했다.) 객체의 모든 프로퍼티와 메소드는 public하도록 설정되어 왔었다는 소리다. 하지만 자바스크립트에서도 꾸준한 업데이트 끝에 드디어 prefix를 추가하여 private class를 선언할 수 있는 방식이 확립되었다. (나는 사실 이전에 조사할 때 타입스크립트를 통해서만 public, private, protected를 모두 사용할 수 있는 걸로 기억하고 지나갔었는데, 업데이트가 많이 되어있었다.)

 

아래는 기존에 접근제한자를 사용하지 않고 은닉하던 방식과, 가장 최신의 방식이 어떤 차이가 있는지 비교해보기 위해 가져온 예시들이다.

 


 

1. 패턴을 활용한 은닉 

 

const Person = (function (){
	let _age = 0; // private
    
    // 생성자 함수
    function Person(name, age){
    	this.name = name; // public
        _age = age;
    }
    
    Person.prototype.sayHi = function(){
    	console.log(`Hi, my name is ${this.name}. I'm ${age}.`);
    }
    
    return Person;
}());

const me = new Person('Lee', 20);
me.sayHi(); // Hi, my name is Lee. I'm 20.
console.log(me.name); // Lee
console.log(me._age); // undefined

const you = new Person('Kim', 30);
you.sayHi(); // Hi, my name is Kim. I'm 30.
console.log(you.name); // Kim
console.log(you._age); // undefined

 

위와 같이 즉시 실행 함수에 생성자 함수와 프로토타입 메소드를 하나로 모은 방식을 사용했을 때, 콘솔에서 값을 확인해보면 자바스크립트에서도 접근 제한자 없이 은닉화가 가능한 것처럼 보인다. 하지만 위 함수와 같은 경우는, Person 생성자 함수가 여러 개의 인스턴스를 생성할 때 _age 변수의 상태가 유지되지 않는다는 문제가 발생하게 된다.

 

const me = new Person('Lee', 20);
me.sayHi(); // Hi, my name is Lee. I'm 20.

const you = new Person('Kim', 30);
you.sayHi(); // Hi, my name is Kim. I'm 30.

me.sayHi(); // Hi, my name is Lee. I'm 30.

 

 예시에서 사용한 Person.prototype.sayHi 라는 메소드는 즉시 실행 함수가 호출되면서 생성되는데, 생성되면서 상위 스코프인 즉시 실행 함수의 환경에 대한 참조를 저장해 기억하게 된다. 어떤 인스턴스를 호출하더라도 하나의 동일한 상위 스코프를 사용하게 되기 때문에, me에 대해 호출했어도 age의 값이 변해버리는 대참사가 일어나게 되는 것이다. 따라서 이런 패턴은 어렴풋이 흉내는 낼 수 있었어도 근본적인 해결책이 되지 못했다.

 


 

2. Private Field 정의를 통한 은닉

 

class Person{
	// private field를 정의
	#name = '';
    
    constructor(name){
    	// private field를 참조
    	this.#name = name;
    }
}

// private field의 #name은 클래스의 외부에서 참조가 불가능
const me = new Person('Lee');

console.log(me.#name);
// SyntaxError: Private field '#name' must be declared in an enclosing class

 

위와 같이 private 필드로 설정된 값에 대해 클래스 외부에서 직접적인 접근이 불가능하다. (참고로 private 필드를 constructor에서 정의해도 enclosing class에서 정의되어야만 한다고 말하는 에러가 발생한다. 반드시 클래스 몸체에서 적용해주어야 한다.) 참고로 아래와 같은 방식으로 접근자 프로퍼티(getter, setter과 같이 객체가 가진 프로퍼티를 객체 외부에서 읽거나 쓸 수 있도록 하는 메소드)를 사용하면, 간접적으로 접근은 가능하다.

 

class Person{
	#name = '';
    
    constructor(name){
    	this.#name = name;
    }
    
    get name(){
    	// private 필드를 참조하여 trim한 후 반환
        return this.#name.trim();
    }
}


const me = new Person(' Lee ');
console.log(me.#name);
// Lee

 


Reference

 

Private class fields - JavaScript | MDN

class 의 속성(property)들은 기본적으로 public 하며 class 외부에서 읽히고 수정될 수 있다. 하지만, ES2019 에서는 해쉬 # prefix 를 추가해 private class 필드를 선언할 수 있게 되었다.

developer.mozilla.org

 

"객체 지향 언어(OOP)"에 대해서 다시 정리해보기

객체 지향 언어, 객체 지향언어...! 내가 개발 공부를 시작하면서 가장 많이 들은 단어 중 하나인 것 같다. 상속...캡슐화...등등 확실히 많이 듣다보니 익숙해지고 그 개념에 대해서 조금씩 이해

hazel-developer.tistory.com

 

반응형