개발일지/Front-end

Javascript Closure를 이용해 Private 한 Class 만들기

cotnmin 2023. 2. 20. 00:10

Javascript의 Class 문법은 기본적으로 Private 메서드나 변수를 제공하지 않는다. _(underscore) 등으로 이름을 시작하는 경우 명시적으로만 프라이빗하게 선언하려고 만들었다는 걸 알릴 수 있다. (다만 사용자가 해당 메서드나 변수에 직접적으로 접근이 가능하다.)

하지만 Javascript의 스코프 범위를 이용해 클로저(Closure)를 만들어 Private 한 메서드와 변수를 가질 수 있는 Class를 생성할 수 있다.

1. Closure(클로저)란?

Closure는 함수가 일급객체 취급받는 언어에서 선언 후 반환하게 되면 스코프가 닫히게 되어 접근할 수 없는데, 함수 스코프 내부에 영향을 줄 수 있는 함수를 반환해 해당 함수를 호출할 때만 스코프에 영향을 줄 수 있는 방식이다.

꼭 함수를 반환할 필요는 없고 이벤트나 비동기 함수의 Callback에 등록하는 등으로 사용 가능하다. (틀렸다면 댓글 남겨주세요.)

자바스크립트의 함수는 일급객체로 함수자체를 변수에 할당하거나 함수의 반환값으로 사용할 수 있는 등 자유로운 사용이 가능하다.

 

Closure의 주 사용 목적에는 여러 가지가 있다.

  1. 캡슐화: 외부에서 직접적인 변수 및 메서드 접근 차단
  2. 모듈화: 외부 변화에 영향 없이 독립적인 환경을 만들어 사용 가능
  3. 전역적이지 않은 변수 관리: 전역적으로 선언된 변수는 실수로 변형되기 매우 쉬움, 실수 방지 가능
  4. ...

위 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

여기를 참고하면 좋으나 단어들이 어려워 제대로 이해하려면 변수의 스코프, 자바스크립트 함수의 특성 등을 알아야 이해가 쉬워지는 부분이 좀 있다. 최대한 어려운 단어 없이 작성하려 했는데 쉽지 않은 것 같다.

그래도 하나씩 배워가면 재미있다. 언젠가 모르는 단어 없이 이해 가능하지는 그날까지 파이팅