[js] 클로저를 활용한 상태 관리
최근 자바스크립트의
클로저
에 대해 공부하다가클로저
를 활용하여상태관리
를 할 수 있겠다는 생각이 들어, 기본적인상태관리
를 할 수 있는 함수를 구현해봤다.
클로저란?
클로저
는 사실 자바스크립트에만 존재하는 개념은 아니지만, 자바스크립트에서 굉장히 중요한 개념이다.
클로저
의 사전적인 의미는 내부 함수(중첩함수)
가 외부 함수 스코프
를 참조하는 함수를 말한다.
자바스크립트는 모든 함수가 외부 함수
의 스코프를 참조하고 있기 때문에 모두 클로저
다. 하지만 통상적으로 외부 함수
의 실행 컨텍스트
가 실행 컨텍스트 스택(콜스택)
에서 pop
되어 나가 생명 주기가 끝났지만, 내부 함수
가 외부함수(상위) 스코프
의 식별자
를 계속 참조하고 있는 함수만을 클로저
라고 한다.
외부 함수 스코프
의 식별자
를 내부 함수
에서만 접근할 수 있기 때문에 클로저
를 활용하여 식별자
의 값 변경을 제어할 수 있다는 특징이 있다. 값의 변경을 제어할 수 있다면, 응용하여 상태 관리
에 활용해 볼 수 있겠다는 생각이 들었다.
상태 관리
리액트
를 할 때는 별 생각 없이 자연스럽게 사용했었는데,상태 관리
함수를 구현하다 보니 이거 완전 리액트랑 똑같자나? 라는 생각이 들어서 (물론 내부적으로는 더 복잡하겠지만) 네이밍을 리액트 hooks
와 동일하게 지었다.
const [state, setState] = useState('abc');
위 코드는 리액트에서 `useState` 훅을 사용하여 `state`에 값을 할당하는 방법이다. 일반적으로 `state` 대신 다른 식별자 이름을 사용하겠지만, 여기서는 `state`로 했다. `state`를 통해서 값에는 접근할 수 있지만 `setState`함수를 통해서만 값을 변경할 수 있다.
const useState = (() => {
let state;
const setState = newState => state = newState;
return (newState) => {
setState(newState);
return [state, setState];
}
})();
우선 외부 함수
의 생명주기가 내부 함수
보다 짧게 하기 위해, 외부 함수
를 즉시 실행 함수
를 만들고 실행했다. 일반 함수라면 함수 스코프
의 식별자
는 함수
가 호출된 이후에 참조할 수 없지만, 외부 함수
스코프의 식별자
는 외부 함수
의 호출에 의해 반환되는 내부 함수
인 익명 함수가 useState
식별자에 담겨져 참조되어 지고 있다.
const [state, setState] = useState('before');
console.log(state); // 'before'
즉시 실행 함수
가 반환하는 익명 함수
가 배열
을 반환하기 때문에 useState
에는 배열이 값으로 담겨지고, state
와 setState
에 각각 배열의 원소가 비구조화 할당
되어 담긴다.
state
는 const
로 선언되었기 때문에 값을 재할당할 수 없지만, setState
함수를 통해 state
의 값을 변경할 수 있다. 그런데, 리액트
는 setState
함수가 호출되면 내부적으로 리렌더링
를 통해 state
의 값이 변환되지만, 변환된 값을 새로운 변수에 재할당
하지 않고 변환된 값을 확인할 수가 없었다.
setState('after');
console.log(state); // 'before'
별도로 `render`함수와 `Component`를 만들어서 값을 변경하는 방법을 생각해 볼 수 있겠지만, `클로저`를 활용하여 이번에는 `상태 관리`를 하는 것에 포커싱을 하고 다른 방법을 생각해 봤다.
const useState = (() => {
let state;
const getState = () => state;
const setState = newState => state = newState;
return (newState) => {
setState(newState);
return [getState, setState];
}
})();
다양한 방법들을 시도해봤지만, 내부적인 `렌더링 과정`을 생략하고 새로운 값을 기존의 `state`에 넣을 수 있는 방법을 생각해내지 못했다. 그래서 기존에 `상태`를 관리하던 식별자 대신 식별자의 값을 반환하는 `함수`를 반환하는 방식으로 변경했다.
const [state, setState] = useState('before');
console.log(state()); // 'before'
setState('after');
console.log(state()); // 'after'
state
의 값을 확인하기 위해 매번 함수를 호출
해야 한다는 단점이 있지만, 이제 state
를 은닉하여 setState
를 통해서만 값을 변환할 수 있다. setState
로 값을 변환한 값도 바로 확인할 수 있다.
const [state, setState] = useState(1);
console.log(state()); // 1
setState(state() + 1);
console.log(state()); // 2
기존의 `state` 값을 참조하여 숫자를 늘리거나 줄일 수도 있다.
결과
const useState = (() => {
let state;
const getState = () => state;
const setState = newState => {
const stateType = typeof _state;
if( stateType !== 'undefined' && stateType !== typeof newState )
throw new Error(`${newState} is not ${stateType}`)
state = newState;
};
return (newState) => {
setState(newState);
return [getState, setState];
}
})();
const [state, setState] = useState('before');
console.log(state()); // 'before'
setState(100); // Error: 100 is not string
console.log(state());
함수는 자바스크립트
로 구현했기 때문에 타입에 대한 확인을 하지 않아서, setState
함수 내부에서 이전 state
와 변경하려는 state
의 타입
을 체크하는 로직을 별도로 추가했다.
Author And Source
이 문제에 관하여([js] 클로저를 활용한 상태 관리), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@luna238/js-클로저를-활용한-상태-관리저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)