useState 와 클로저

2022. 11. 10. 23:09React/React Hook

반응형

useState Hook은 아래와 같이 현재상태값, 상태변경함수를 반환하여 준다.

setState 함수를 통하여 상태값이 변경이 되면 컴포넌는 Re-redering이 된다.

const [state, setState] = useState(초기값);

 

궁금했던 점은 setState 함수는 호출 하고 나서 실행컨텍스트스택에서 setState는 제거 되는데 현재상태값을 어떻게 가져 올 수 있는지 였다.

상태값을 가져올 수 있는 이유는 클로저(closure)로 구현 되어 있기 때문이다.

 

클로저에 대한 이해

클로저에 대한 이해 에 대한 예제와 내용은 모던 자바스크립트 Deep Dive 책의 코드와 내용을 정리한 것입니다.

클로저에 대한 MDN의 정의는 다음과 같다.

A closure is the combination of a function and the lexical enviroment within which that function was declared.
클로저는 함수와 그 함수가 선언된 렉실컬환경과의 조합니다.

 

렉시컬환경의 정의는 다음과 같다.

렉실컬환경은 식별자와 식별자에 바인딩된 값, 그리고 상위 스코프에 대한 참조를 기록하는 자료구조로 실행컨텍스트를 구성하는 컴포넌트다. 실행 컨텍스트 스택이 코드의 실행 순서를 관리한다면 렉시컬 환경은 스코프와 식별자를 관리한다. (모던 자바스크립트 Deep Dive-p366)

 

클로저 예제

const x = 1;

// ① outer 함수 객체 생성
function outer() {
    const x = 10;
    const inner = function() { console.log(x); }; // ② inner 함수 반환
    return inner;
} 

// outer 함수를 호출하면 중첩 함수 inner를 반환한다.
// 그리고 outer 함수의 실행 컨텍스트는 실행 컨텍스트 스택에서 팝되어 제거된다.
const innerFunc = outer(); // ③ outer 함수 호출
innerFunc(); // ④ inner 함수 호출

위의 예제에서 핵심적인 내용은 outer 함수를 호출(③) 하면 outer 함수는 inner 함수를 반환하고 생명 주기를 마감한다. outer 함수가 종료 되면 outer 함수의 실행 컨텍스트가 제거 되었으므로 outer 함수의 지역 변수 또한 생명주기를 마감한다. 따라서 x 변수는 더이상 유효 하지 않고 접근할 수 있는 방법도 없어 보인다.

 

그러나 실행결과는 10을 반환하게 된다.

 

그 이유는 클로저를 사용하기 때문이다. 클로저란 외부함수보다 중첩 함수가 더 오래 유지되는 경우 중첩 함수는 이미 생명 주기가 종료한 외부 함수의 변소를 참조할 수 있다. 이러한 중첩 함수를 클로저라고 부른다.

 

프로그램 설명

① outer 함수 객체 생성

전역 렉시컬 환경을 outer 함수 객체의 [[Enviroment]] 내부 슬롯에 상위 스코프로서 저장한다.

그림 24-2 전역 함수 객체의 상위 스코프 결정(모던 자바스크립트 Deep Dive - p394)

② outer 함수 호출

outer 함수를 호출 하면 outer 함수의 렉시컬 환경이 생성되고 앞서 outer 함수 객체의 [[Enviroment]] 내부 슬롯에 저장된 전역 렉시컬 환경을 outer 함수 렉시컬 환경의 “외부 렉시컬 환경에 대한 참조”에 할당 한다.

 

그리고 중첩함수 inner는 자신의 [[Enviroment] 내부 슬롯에 현재 실행 중인 실행 컨텍스트의 렉시컬 환경, 즉 outer 함수의 렉시컬 환경을 상위 스코프로서 저장한다.

그림 24-3 중첩 함수의 상위 스코프 결정(모던 자바스크립트 Deep Dive - p395)

③ outer 함수 종료

outer 함수의 실행이 종료하면 inner 함수를 반환하면서 outer 함수의 생명주기가 종료된다. 즉 outer 함수의 실행 컨텍스트가 실행 컨텍스트 스택에서 제거된다.

 

이때 outer 함수의 실행 컨텍스트는 실행컨텍스트 스택에서 제거되지만 outer 함수의 렉시컬 환경까지 소멸하는 것은 아니다.

그림 24-4 outer 함수의 실행 컨텍스트가 제거되어도 outer 함수의 렉시컬 환경은 유지된다.(모던 자바스크립트 Deep Dive - p394)

outer 함수의 렉시컬 환경은 inner 함수의 [[Enviroment]] 내부 슬롯에 의해 참조되고 있고 inner 함수는 전역 변수 innerFunc에 의해 참조되고 있으므로 가비지 컬렉션 대상이 되지 않기때문이다.

 

가비지 컬렉터는 누군가가 참조하고 있는 메모리 공간을 함부로 해제하지 않는다.(모던 자바스크립트 Deep Dive-p395)

 

④ inner 함수 호출시

inner 함수의 실행 컨텍스트가 생성되고 실행컨텍스트 스택에 push 된다. 그리고 렉시컬 환경에 대한 참조에는 inner 함수 객체의 [[Enviroment]] 내부 슬롯에는 outer 함수의 렉시컬 환경을 상위 스코프로서 저장한다.

그림 24-5 외부 함수가 소멸해도 반환된 중첩 함수는 외부 함수의 변수를 참조할 수 있다.(모던 자바스크립트 Deep Dive - p396)

결론은 outer 함수호출 후 실행컨텍스트 스택에서는 사라지지만 inner 함수 객체의 [[Enviroment]] 내부 슬롯에는 outer 함수의 렉시컬 환경을 상위 스코프로 저장하고 있어서 변수 x 에 접근이 가능하면 그결과 10을 반환하게 된다.

 

처음에는 잘 이해가 안가서 책을 몇번 읽고 이해 하게 되었다.

 

단순히 useState는 현재상태값,상태변경함수를 반환한다 라고 만 알고 써도 되겠지만 어떠한 원리로 동작하는지 계속 궁금증을 가지다 보니 클로저까지 알게 되었고 클로저를 공부하다 보니 자바스크립트엔진의 동작 원리도 보게되었다.

 

설명이 장황할 수 있으나 이 개념은 이해하는게 좋지 않을까 생각한다.

반응형

'React > React Hook' 카테고리의 다른 글

Todo 예제  (0) 2022.11.10