Javascript의 Class 문법은 기본적으로 Private 메서드나 변수를 제공하지 않는다. _(underscore) 등으로 이름을 시작하는 경우 명시적으로만 프라이빗하게 선언하려고 만들었다는 걸 알릴 수 있다. (다만 사용자가 해당 메서드나 변수에 직접적으로 접근이 가능하다.)
하지만 Javascript의 스코프 범위를 이용해 클로저(Closure)를 만들어 Private 한 메서드와 변수를 가질 수 있는 Class를 생성할 수 있다.
1. Closure(클로저)란?
Closure는 함수가 일급객체 취급받는 언어에서 선언 후 반환하게 되면 스코프가 닫히게 되어 접근할 수 없는데, 함수 스코프 내부에 영향을 줄 수 있는 함수를 반환해 해당 함수를 호출할 때만 스코프에 영향을 줄 수 있는 방식이다.
꼭 함수를 반환할 필요는 없고 이벤트나 비동기 함수의 Callback에 등록하는 등으로 사용 가능하다. (틀렸다면 댓글 남겨주세요.)
자바스크립트의 함수는 일급객체로 함수자체를 변수에 할당하거나 함수의 반환값으로 사용할 수 있는 등 자유로운 사용이 가능하다.
Closure의 주 사용 목적에는 여러 가지가 있다.
- 캡슐화: 외부에서 직접적인 변수 및 메서드 접근 차단
- 모듈화: 외부 변화에 영향 없이 독립적인 환경을 만들어 사용 가능
- 전역적이지 않은 변수 관리: 전역적으로 선언된 변수는 실수로 변형되기 매우 쉬움, 실수 방지 가능
- ...
위 3가지 목적은 어떻게 보면 다 같은 말이긴 하다.
하지만 제목처럼 Private 한 메서드와 변수 Class를 만든다는 느낌으로 함수를 담은 객체를 return 하는 방식으로 Closure를 구현해 본다.
2. 클로저 만들어보기
const closure_maker = function () {
let private_variables = '프라이빗 변수';
let public_variables = '퍼블릭 변수';
function private_method () {
console.log(private_variables);
}
function public_method () {
private_method();
console.log(public_variables);
}
return {
public_variables,
public_method,
}
}
// 클로저 생성
let closure = closure_maker();
// 프라이빗 메서드의 호출
closure.private_method();
// error: private_method is not function
// 퍼블릭 메서드의 호출
closure.public_method();
// 콘솔 결과
// 프라이빗 변수
// 퍼블릭 변수
// 변수의 변경
closure.private_variables = '새 프라이빗 값';
closure.public_variables = '새 퍼블릭 값';
closure.public_method();
// 콘솔 결과
// 프라이빗 변수
// 퍼블릭 변수
class를 생성하는 느낌으로 함수를 작성하고 원하는 변수에 함수를 선언해 return 된 객체를 담는다.
이때 객체에 담은 method는 외부에서 사용 가능한 형태가 된다.
위의 경우 프라이빗 메서드는 호출 시 에러를 띄우지만 퍼블릭 메서드는 문제없이 사용이 가능하다.
문제는 같이 반환해 준 퍼블릭 변수는 새 값을 대입해도 똑같은 결과를 확인할 수 있다.
이는 반환된 객체에 해당 값이 담긴 것뿐이고, 새 값을 대입해도 함수 내부에 선언된 public_variables는 변화가 없다.
해당 부분은 setter, getter를 이용해 다음과 같이 해결할 수 있다.
const closure_maker = function () {
let private_variables = '프라이빗 변수';
let public_variables = '퍼블릭 변수';
function private_method () {
console.log(private_variables);
}
function public_method () {
private_method();
console.log(public_variables);
}
return {
set public_variables(value) {
public_variables = value;
},
get public_variables() {
return public_variables;
},
public_method,
}
}
// 클로저 생성
let closure = closure_maker();
// 퍼블릭 메서드의 호출
closure.public_method();
// 콘솔 결과
// 프라이빗 변수
// 퍼블릭 변수
// 변수의 변경
closure.private_variables = '새 프라이빗 값';
closure.public_variables = '새 퍼블릭 값';
closure.public_method();
// 콘솔 결과
// 프라이빗 변수
// 새 퍼블릭 값
setter를 이용해 값을 대입하는 경우 public_variables에 값이 들어가도록, getter를 이용해 public_variables를 반환하도록 처리 가능하다.
자세한 내용은 아래를 참조한다.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/set
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get
만약 단순히 캡슐화만 필요한 경우 IIFE를 이용해 익명함수로 한 번만 생성해 사용하는 Closure를 만들 수 있다.
3. Class 문법을 이용하기
// 함수형 문법
const closure_maker = function () {
let private_variables = '프라이빗 변수';
function private_method () {
console.log(private_variables);
}
this.public_variables = '퍼블릭 변수';
this.public_method = () => {
private_method();
console.log(this.public_variables);
}
}
// 클래스 문법
class closure_maker {
constructor() {
let private_variables = '프라이빗 변수';
function private_method () {
console.log(private_variables);
}
this.public_variables = '퍼블릭 변수';
this.public_method = () => {
private_method();
console.log(this.public_variables);
}
}
}
위처럼 Public 변수 및 메서드는 기본적인 방식으로 this에 넣어 사용이 가능하다.
class 문법처럼 사용하는 경우 constructor 스코프가 close 되어 closure를 생성하는 느낌이라 아래와 같이 사용하면 에러를 일으킨다.
// 잘못된 방식
class closure_maker {
constructor() {
let private_variables = '프라이빗 변수';
function private_method () {
console.log(private_variables);
}
this.public_variables = '퍼블릭 변수';
}
this.public_method() {
private_method();
console.log(this.public_variables);
}
}
클로저를 이용해 객체처럼 이용하는 방식을 소개해보았다.
https://developer.mozilla.org/ko/docs/Web/JavaScript/Closures
여기를 참고하면 좋으나 단어들이 어려워 제대로 이해하려면 변수의 스코프, 자바스크립트 함수의 특성 등을 알아야 이해가 쉬워지는 부분이 좀 있다. 최대한 어려운 단어 없이 작성하려 했는데 쉽지 않은 것 같다.
그래도 하나씩 배워가면 재미있다. 언젠가 모르는 단어 없이 이해 가능하지는 그날까지 파이팅
'개발일지 > Front-end' 카테고리의 다른 글
정규표현식(Regular Expression) 정복하기 (with Frontend Javascript) 1 (0) | 2023.02.26 |
---|---|
Throttling(스로틀링)과 Debouncing(디바운싱) (0) | 2023.02.12 |
Javascript 배열 용도에 맞게 순회하기 (0) | 2023.01.29 |
Javascript 숫자와 관련된 사소한 팁 (2) | 2023.01.22 |
Web API Javascript IntersectionObserver (0) | 2023.01.09 |