계발하는 개발자

[JS] 렉시컬 스코프와 클로저 본문

📌 Language/Javascript

[JS] 렉시컬 스코프와 클로저

dev_genie 2023. 8. 21. 16:54

클로저를 공부하기에 앞서

MDN에서 클로저에 대해 검색해보면 "클로저는 함수와 함수가 선언된 어휘적 환경의 조합이다. 클로저를 이해하려면 자바스크립트가 어떻게 변수의 유효범위를 지정하는지 렉시컬 스코프(Lexical Scope)를 먼저 이해해야 한다." 라고 나와있다.

따라서, 렉시컬 스코프가 무엇인지를 먼저 알아보자.

정적 스코프(static scope), 렉시컬 스코프(Lexical Scope)란? 

렉시컬 스코프는 한 마디로 함수를 어디에 선언하였는지에 따라 상위 스코프가 결정되는 것을 말한다.

자바스크립트를 포함한 대부분의 프로그래밍 언어는 렉시컬 스코프를 따르며, 이를 정적 스코프(Static Scope) 라고 부르기도 한다.

function init() {
  var name = "Mozilla"; // name은 init에 의해 생성된 지역 변수이다.
  function displayName() {
    // displayName() 은 내부 함수이며, 클로저다.
    alert(name); // 부모 함수에서 선언된 변수를 사용한다.
  }
  displayName();
}
init();

MDN에 나와있는 예제를 살펴보자.

displayName()은 init() 안에 정의된 내부 함수이며 init() 함수 본문에서만 사용할 수 있다.

다만, 여기서 주의할 점은 displayName() 내부엔 자신만의 지역 변수가 없다는 점이다. 그런데 함수 내부에서 외부 함수의 변수에 접근할 수 있기 때문에 displayName() 역시 부모 함수 init()에서 선언된 변수 name에 접근할 수 있다.

상위스코프가 무엇인지 알려면 displayName 함수가 어디에 선언되었는지를 봐야하는데, 위 코드에서는 displayName 함수가 init 함수 안에서 선언되었으므로 상위 스코프는 지역 스코프가 된다.

그래서 init 함수 내의 선언된 name이라는 변수를 참조할 수 있는 것이다.

 

동적 스코프(Dynamic Scope)

위의 렉시컬 스코프라고 하는 정적 스코프와는 다르게,

함수를 어디서 호출하였는지에 따라 상위 스코프가 결정되는 것을 동적 스코프라고 한다.

 

이때 위에서 말하는 스코프의 정의는 다음과 같다.

스코프(scope)란?

스코프변수나 함수가 유효할 수 있는 범위를 뜻하는데,

그렇기 때문에 자바스크립트 엔진이 식별자를 검색할 때 사용하는 규칙으로 작용한다.

 

클로저(Closure)

Closure 라는 뜻을 사전에서 찾아보면 폐쇄라는 뜻을 갖고있다.

자바스크립트의 클로저도 이 폐쇄와 유사한 의미를 가지고 있는데,

한 마디로 클로저란, 함수가 선언될(생성될) 때 그 당시에 주변의 환경과 함께 갇히는 것을 말한다.

또 다른 말로 설명하면, 함수가 속한 렉시컬 스코프를 기억하여, 함수가 렉시컬 스코프 밖에서 실행될 때도 이 스코프에 접근할 수 있게 해주는 기능이다.

function sayHello() {
	const a = 'Hello';
    const b = 'World';
    
    function sumString() {
    	console.log(a + '' + b);
    }
    
    return sumString;
}

const myFunc = sayHello();

myFunc(); // 'Hello World'

위 예제를 살펴보면, myFunc라는 변수는 sayHello 함수를 호출하고 있다.

그래서 myFunc를 실행하게 되면 문제 없이 Hello World 가 잘 출력된다. 여기서 살펴볼 점은 myFunc의 부분은 변수 a와 b가 담겨있는 sayHello 함수 스코프의 바깥에 있는데도 불구하고 a와 b를 합친 Hello World를 잘 출력한다는 것인데,

그 이유가 바로 클로저(Closure) 때문이다.

모든 자바스크립트 함수는 선언될(생성될) 당시에 클로저가 형성되어 주변 환경, 즉 렉시컬 스코프를 기억할 수 있다.

 

클로저의 활용

그렇다면 클로저는 왜 쓰일까? 클로저는 다음과 같은 상황에서 유용하게 사용될 수 있다.

- 정보 은닉 (접근 권한 제어)

내가 공부하며 이해한 클로저 기법을 사용하는 이유는 전역변수를 사용하지 않으려는 목적이 강한 듯 했다.

누구나 접근해서 들여다보고 변경까지 할 수 있는 전역변수를 사용하는 것은 매우 위험하기 때문이다.

하지만 클로저를 사용하면 전역변수를 사용하지 않으면서도 클로저 함수 내부의 변수에 계속 접근할 수 있기 때문에 이 문제가 깔끔하게 해결된다.

 

- 커링

커링은 f(a,b,c)처럼 여러 개의 인자를 한 번의 호출로 처리하던 함수를 f(a)(b)(c)처럼 분리하여 인자를 하나씩만 받는 여러개의 함수로 만드는 것이다.

다시 말해 함수 하나가 n개의 인자를 받는 대신, n개의 함수를 만들어서 각각의 함수가 인자를 받도록 하는 것이다.

예제 코드를 보면 커링을 구현할 때도 클로저가 들어간다.

// 일반함수
function fn(x,y){
    return x + y
}
fn(1,2)

// 일반함수를 커링함수로 변환
function curried_fn(x){
    return function(y){
        return x + y
    }
}

const curried_fn2 = x => y => x + y;

console.log(
    curried_fn(1)(2),
    curried_fn2(1)(2)
)

? 왜 커링패턴을 사용하는가

위 예제에서 일반함수의 경우 2개의 인자를 모두 전달받아야 해당 함수가 실행되는데 반해, 커링함수의 경우 첫번째 인자를 통해 하나의 함수를 실행하고, 이어 두번째 인자를 통해 또 하나의 함수를 독립적으로 실행한다.따라서 인자를 담고있는 함수의 재사용이 가능해지고, 어떤 함수를 특정 애플리케이션 지점에서 다른 지점으로 값을 전달해야 하는 경우에도 유용하게 사용할 수 있다.

 

- 부분적용함수

위 커링함수를 조금 다른 방식으로 사용해볼 수도 있다.

부분적용함수란 n개의 인자를 받는 함수에 미리 m개의 인자만 넘겨 기억시켰다가, 나중에 (n-m)개의 인자를 넘기면 비로소 원래 함수의 실행 결과를 얻을 수 있도록 하는 함수를 말한다.

다시 말해, 하나의 고정값을 가지고 다른 인자들의 값을 변환시켜야 하는 경우에 이 부분적용함수를 사용할 수 있다.

function makeCoffee(type) {
            return function(sugar){
                return function(cream){
                    return console.log(`coffee: ${type}, sugar: ${sugar}, cream: ${cream}`)
                }
            }
        }

const mediumRoast = makeCoffee("medium Roat")

const order1 = mediumRoast(1)(2)
const order2 = mediumRoast(3)(4)

 

위 예제를 실행해보면 아래와 같은 결과값을 얻을 수 있다.

위와 같이 mediumRoast라는 함수를 앞서 정의해주면서 순차적으로 인자값을 넘겨줄 경우를 살펴보면

첫번째 인자값을 미리 넘겨주면서 값을 할당받았기 때문에 다음 순서의 나머지 인자값들만 전달해주면 함수의 실행 결과를 즉시 얻을 수 있다.

 

느낀점

내가 그동안 함수를 일반적으로 호출했던 방식들이 렉시컬 스코프 원리에 따른 것이었다는걸 알게되서 좀 흥미로웠다. 

배우고 나서 다시 생각해보니 '아 이게 이래서 이랬구나' 싶은 느낌?

역시 코딩은 아는 만큼 보이는 세계인 것은 틀림없는 것 같다.

또 아직까진 프로그래밍에 커링패턴을 적용해서 사용해본 적이 없어서 개념상으로만 알고 있는게 전부지만,

향후 프로젝트에 이 개념을 도입한다면 좀더 편하게 인자값을 넘겨받으면서 원하는 결과를 얻을 수도 있겠다 싶었다.


이해에 도움이 됐던 강의 출처:

 

 

LIST
profile

dev_genie

@dev_genie

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!