[Effective JavaScript] 비공개 데이터를 저장하기 위해 클로저를 사용하자

김범식·2023년 7월 19일
0

Effective JavaScript

목록 보기
32/33
post-thumbnail

정보 은닉

자바스크립트의 객체시스템은 정보를 은닉을 권장하지 않는다! 프로퍼티의 이름은 문자열이고, 프로그램 내에서 그 이름을 통해 어디서라도 접근할 수 있고, 다음과 같은 함수들을 사용할 수 도 있다!

  • for… in
  • Object.keys()
  • Object.getOwnPropertyNames()

그러면 객체의 정보를 은닉할 수 있는 방법은 없을까?



코딩 컨벤션 사용하기

흔히 비공개 프로퍼티를 위한 강제적인 문법 보다는 코딩컨벤션에 의존하는 경우가 많다.

코딩 컨벤션이란?
코드를 작성하는 방식을 표준화 하는 규칙과 가이드라인의 집합, 프로그래밍 언어에 관계없이 일관성을 유지하고 코드의 가독성을 높이며, 협업하는 개발자들 간의 코드 이해를 용이하게 하는데 목적이 있다.
ex) 들여쓰기, 변수명, 함수명, 상수, 주석, 함수와 클래스 구조, 괄호 사용 등..

밑줄 문자(_)를 접두어나 저비어로 붙이는 명명 규칙을 사용함

var obj = {
	_private : "private info",
	public:"public info"
}

정보 은닉을 강요하지는 않지만 객체를 제대로 사용하는 사용자가 프로퍼티를 검사하거나 수정하지 않도록 표시해둔다.



클로저 사용하기

실제로 더 높은 수준의 은닉을 필요로 하는 보안 플랫폼이나, 어플리케이션 프레임워크는 신회하지 않는 애플리케이션에 객체를 전달할 때 해당 에플리케이션이 객체 내부에 손대지 않기를 바랄것이다.

이 때 사용하기 아주 좋은 메커니즘이 바로 클로저이다.

function outerFunction() {
  var outerVariable = 'I am from outerFunction'; // outerFunction의 지역 변수
  
  function innerFunction() {
    console.log(outerVariable); // outerVariable은 innerFunction의 렉시컬 스코프에 포함되어 있지 않지만, 클로저로 인해 접근 가능
  }
  
  return innerFunction; // innerFunction을 반환하여 클로저를 형성
}

var closureExample = outerFunction(); // outerFunction 실행 결과로 innerFunction을 반환받음
closureExample(); // "I am from outerFunction" 출력

다음과 같은 구조를 클로저라고 한다. innerFunction에는 outerVariable이라는 변수를 선언한적이 없지만 여전히 clousreExample에서 참조할 수 있다. 우리는 여기서 outerVariable로 정보를 은닉할 수 있다.



클로저에 접근하는 방법

클로저는 내포한 변수에 데이터를 저장하고 이 변수들에 대한 직접적인 접근은 제공하지 않는다. 클로저 내부에 접근할 수 있는 유일한 방법은 함수에 클로저의 접근을 명시적으로 제공하는 것이다.

즉 객체의 프로퍼티는 자동으로 노출되고, 클로저에 있는 변수는 자동으로 숨겨진다.



일반 생성자 함수

function User(name, passwordHash){
	this.name = name;
	this.passwordHash = passwordHash;
	this.toString = function(){
		return "[User " + this.name + "]";
	}
	this.checkPassword = function(password){
		return hash(password) === this.passwordHash;
	}
}

var u = new User("일반함수",123123);
console.log(u.toString()) // [User 일반함수]
console.log(u.name) //일반함수
console.log(u.password) //123123



클로저 사용

function User(name, passwordHash){
	this.toString = function(){
		return "[User " + name + "]";
	}
	this.checkPassword = function(password){
		return hash(password) === passwordHash;
	}
}

var u = new User("클로저",123123);
console.log(u.toString()) // [User 클로저]
console.log(u.name) // undefined
console.log(u.password) // undefined

위의 두 코드의 차이점은 클로저를 사용한 생성자 함수가 name과 passwordHash를 프로퍼티가 아닌 변수로 참조했다는 것이다. 때문에 클로저로 생성한 객체는 name과 password를 외부에서 직접 접근할 수 없다.



클로저 사용의 단점

생성자 변수가 그 변수를 사용하는 메서드의 스코프 내에 있게 하기 위해서는 반드시 이 메서드가 인스턴스 객체에 위치해야 한다는 것이다.

이게 무슨 말이냐하면 prototype에 있는 함수는 해당 변수를 참조할 수 없다는 말이다.

function User(name, passwordHash){
	this.toString = function(){
		return "[User " + name + "]";
	}
	this.checkPassword = function(password){
		return hash(password) === passwordHash;
	}
}

User.prototype.func = function(){
	return "prototype 함수 에서 [ name: "+name+" ]꺼내기"
}

var u = new User("클로저",123123);
console.log(u.toString()); // [User 클로저]
console.log(u.func()); // "prototype 함수 에서 [ name: JS Bin Output  ]꺼내기"

물론 인스턴스 안에 함수가 있으면 메서드가 복사되어 메모리를 좀더 소비한다는 단점이 있지만 정보 은닉을 보장한다는 것은 매우 중요하기 때문에 추가적인 비용을 치를 만한 가치가 있다!



기억할 점

  • 클로저 변수는 비공개이고, 지역적인 참조로만 접근할 수 있다.
  • 메서드 안에 강제로 정보를 숨기기 위해 비공개 데이터로써 지역변수를 사용하라
profile
frontend developer

1개의 댓글

comment-user-thumbnail
2023년 7월 19일

아주 유용한 정보네요!

답글 달기