클로저를 예제코드로 이해해보자

히진로그·2022년 7월 13일
0

기술공부

목록 보기
2/2
post-thumbnail

클로저는 자바스크립트가 렉시컬 스코프를 따르기 때문에 일어나는 일입니다.

렉시컬 스코프를 실행 컨텍스트 관점에서 간단하게 말씀드리면 코드를 실행할 때 실행 컨텍스트에 소스코드가 평가되어 쌓입니다. 이때 함수가 참고할 수있는 환경이 정의 되는데 이때 함수의 렉시컬 스코프가 정적으로 정의됩니다.

맨 밖에 outer함수를 선언하고 함수 안에서 변수 x를 선언하고 10을 할당한 후 함수 내부에 inner함수를 만들어줍니다.

클로저를 알기 전에 먼저 렉시컬 스코프와 실행컨텍스트에 대해서 알아야 합니다.

먼저 렉시컬 스코프를 잠깐 짚고 넘어가보도록 하겠습니다. 예를 통해 알아볼까요?

var x = 1;

function foo() {
  var x = 10;
  bar();
}

function bar() {
  console.log(x);
}

foo(); // 1
bar(); // 1

위 예제의 실행 결과를 어떻게 유추해볼 수 있을까요? bar함수가 참조하는 상위스코프가 무엇인지에 따라 콘솔에 출력되는 결과 값이 달라질 것 같습니다.

자바스크립트는 함수를 어디서 정의했는지에 따라 함수의 상위 스코프를 결정하는 렉시컬 스코프, 정적 스코프를 따릅니다. 즉, 함수가 어디서 호출 되었는지는 전혀 상관이 없고, 함수의 상위 스코프는 항상 자신이 정의된 스코프입니다.

따라서 예제의 bar함수는 전역에서 정의된 함수이기 때문에 전역을 상위 스코프로 가지고 x는 var x = 1; 를 참조하여 콘솔에는 1이 두 번 찍히게 됩니다. 즉, bar함수는 자신의 상위 스코프인 전역 렉시컬 환경을 기억하고 있다가 거기서 x = 1 을 꺼내온 것이죠~

자 이제 렉시컬 스코프에 대해서 다들 좀 감이 오시나요? 그럼 다음 실행 컨텍스트에 대해서 알아보겠습니다.

실행 컨텍스트에서는 전역 실행컨텍스트와 함수 실행 컨텍스트에 대해서만 짚고 넘어가겠습니다.

이것도 예제를 통해 알아보도록 하겠습니다.

// 전역 변수 선언
const x = 1;
const y = 2; 

// 함수 정의
function foo(a) {
  // 지역 변수 선언
  const x = 10;
  const y = 20;

  // 메서드 호출
  console.log(a + x + y); // 130
}

// 함수 호출
foo(100); 

// 메서드 호출
console.log(x + y); // 3

전역 실행 컨텍스트는 말 그대로 전역에 존재하는 소스코드가 평가되어 생성되는 것입니다. 여기에는 전역에 존재하는 함수와 클래스 내부의 코드는 포함되지 않습니다.

함수 실행 컨텍스트는 위 예제로 설명을 해보면 foo함수 안의 코드들이 평가 되어 foo함수의 실행 컨텍스트를 생성된다고 할 수 있습니다.

자 그럼 만약에 전역에서 x와 y값에 접근하면 어떤 값을 얻을 수 있을까요?? 1과 2겠죠? 왤까요?

전역 실행 컨텍스트에는 함수 내부의 코드는 포함되지 않는다고 아까 말씀드렸죠?? 맞아요~

자 실행 컨텍스트에 대해서는 이 정도의 지식만 가지고 오늘의 핵심 클로저로 넘어가보겠습니다.

먼저 클로저의 mdn 정의를 한 번 볼게요, ‘클로저는 함수와 그 함수가 선언된 렉시컬 환경과의 조합이다.’ 음 이렇게만 보면 무슨 말인지 모르겠죠? 네 저도 잘 모르겠네요

클로저도 예제로 한 번 살펴볼건데요, 앞의 예제들과 비슷해서 이해하기 어렵지 않으실거에요.

우선 ‘함수가 선언된 렉시컬 환경이다’를 먼저 이해해볼까요? 이건 근데 알것 같죠!! 우리 앞에서 렉시컬 스코프에 대해서 배우고 넘어왔잖아요?? 잠시 앞으로 돌아가볼까요?

렉시컬 스코프는 함수가 어디에서 정의되었느냐에 따라 상위 스코프가 결정되는 것! 이었죠~

const x = 1;

function outerFunc() {
  const x = 10;

  innerFunc();
}

function innerFunc() {
  console.log(x); // 1
}

outerFunc();

이 예제를 보면서 다시 기억을 되살려볼까요?? 콘솔에는 어떤 x값이 출력될까요? 그렇죠 1이 출력되겠죠?

왜죠? InnerFunc 함수가 선언된 곳이 바로바로~~ 전역이기때문이죠, 전역에 선언한 함수를 outerFunc함수 내부에서 호출해도 같은 함수 내부에 선언된 x(10)값에 접근할 수 없는 이유가 뭐였죠??

그렇죠! 함수는 정의된 곳에 따라 상위 스코프가 결정되기 때문이고, innerFunc함수는 전역에서 선언되었기 때문입니다~ 렉시컬 스코프에 대해서는 확실히 감이 오시죠??

자 이제 진짜 클로저에 대해 알아보기 위한 예제를 가져와볼게요,

const x = 1;

function outer() {
  const x = 10;
  const inner = function () { console.log(x); };
  return inner;
}

const innerFunc = outer();

innerFunc(); // 10

자 여기서 콘솔에 무슨 값이 찍힐지 아시겠나요?? 10이라구요?? 여러분,, 벌써 클로저를 마스터하셨네요

그래도 설명을 드리겠습니다!!

outer함수를 호출하면 inner함수를 리턴하고 할 일을 다했기 때문에 사라집니다. 동시에 outer함수의 지역변수 x도 사라지고 변수 값 10도 사라지겠죠?? 얘네들을 저장하고 있던 outer함수의 실행 컨텍스트가 제거되었기 때문이죠! 여기까지 이해 잘 되시죠??

자 근데 outer함수 안에 있던 inner함수가 할당된 innerFunc를 호출했더니.. 결과가 10이 찍혔잖아요?

이 10은 어디서 왔을까요? 그렇죠 outer함수의 지역변수 x의 값인 10! 여기서 온거죠

어떻게 이런 일이 가능할까요? 자 보신 것처럼, 외부 함수보다 즉 outer함수보다 중첩함수가 즉 inner함수겠죠? 중첩 함수가 더 오래 유지되는 경우 중첩함수는 이미 제거된 즉 할 일을 다하고 사라진 외부 함수의 변수를 참조할 수 있습니다. 여기서!! 이 중첩함수를 바로 클로저! 라고 부르는 것이죠~

이해가 되셨나요?

예제코드는 모던 자바스크립트 딥다이브에서 가져왔습니다!

0개의 댓글