클로저 관련 라이브 코딩 면접을 보았는데 잘 풀지 못한 점이 아쉬워서, 다시는 틀리지 않기 위해서 복기하고 정리해자
(기억에 의존한 거라 문제 코드 내용은 조금 다름)
function useState(initialValue) {
let _val = initialValue;
function _setVal(newValue) {
_val = newValue;
}
return [_val, _setVal];
}
const [value, setValue] = useState('initial value');
console.log(value); // initial value
setValue('new value');
console.log(value); // initial value
_setVal 의 내부 스코프에서 변경한 _val 의 최신 값을 참조할 수 있도록,
_val를 클로저로 가지고 호출되는 시점에 최신 값을 가져올 수 있는 _getVal 함수를 만들어 사용할 수 있다.
function useState(initialValue) {
let _val = initialValue;
function _setVal(newValue) {
_val = newValue;
}
function _getVal() {
return _val;
}
return [_getVal, _setVal];
}
const [count, setCount] = useState(0);
console.log(count()); // 0
setCount(count() + 1);
console.log(count()); // 1
setCount(count() + 1);
console.log(count()); // 2
(실제 문제에서는 setState 를 호출할 때 렌더링 과정도 넣어서 count() 호출이 아닌 conaole.log(count) 로 바로 확인할 수 있는 코드였음)
const useState = (function(){
let state= undefined
return function useState(initialValue) {
if (state === undefined){
state = initialValue;
}
function _setVal(newValue) {
state = newValue;
}
function _getVal() {
return state;
}
return [_getVal, _setVal];
}
})()
const [count, setCount] = useState(0);
console.log('count:', count()); // count: 0
const [text, setText] = useState('apple');
console.log('text:', text()); // text: apple
setCount(1);
setText('banana');
console.log({ count: count(), text: text() }); // { count: 'banana', text: 'banana' }
모든 useState가 동일한 state를 참조하므로 setCount나 setText를 호출할 때 서로 영향을 받게 된다. 이를 해결하기 위해 stack 배열을 사용하여 상태를 저장하고, 각 상태를 별도의 인덱스로 관리하게 할 수 있다.
💡 여기서 중요한 점은 idx 를 바로 사용하지 않고 각 useState 안에서 _id 로 복사해서 사용해야한다는 점이다.
idx는 useState 함수의 호출마다 증가하기 때문에(=변경되기 때문에) idx 를 그대로 사용하면 이 상태 값을 참조하는 setCount와 setText가 각각 자신의 상태를 정확히 가리키지 않고, 동일한 idx를 참조하게 되어 값이 엉키게 된다.
각 useState 호출에서 그 순간의 idx 값을 복사하여 idx의 값을 고정해 놓으면, stack 배열의 고유한 인덱스를 정확하게 참조하게 되므로, 각 상태가 자신만의 인덱스를 가지게 되어 값이 꼬이지 않게 된다.
const useState = (function(){
let stack = [];
let idx = 0;
return function useState(initialValue) {
let _id = idx; // idx 를 바로 사용하지 않고 _id 로 복사해서 사용해야한다.
if(stack[_id] === undefined){
stack[_id] = initialValue;
}
function _setVal(newValue) {
stack[_id] = newValue;
}
function _getVal() {
return stack[_id];
}
idx++; // 상태 변경이 일어나므로 idx 를 바로 사용하면 모든 상태가 같은 idx를 참조하게 된다.
return [_getVal, _setVal];
}
})()
const [count, setCount] = useState(0);
console.log('count:', count()); // count: 0
const [text, setText] = useState('apple');
console.log('text:', text()); // text: apple
setCount(1);
setText('banana');
console.log({ count: count(), text: text() }); // { count: 1, text: 'banana' }
라이브 코딩 면접은 처음이었는데, 직접 코드를 쳐볼 수 없고 화면만 보면서 어느 부분을 고쳐야할 지 상상코딩해야 했어서 조금 더 어렵게 느껴졌다. 콘솔로그로 찍어보고 싶은 부분 말하라고 친절히 해주셨는데도, 왜인지 그 면접장 안에서는 코드 흐름이 한 눈에 안들어오는 느낌이었다...🥲
집에 와서 다시 복기해보니 개념 자체가 기본적인 개념이었는데도 잘 못 풀어서 아쉬움이 남는다... ㅜㅜㅎㅎ 그치만 내가 못 한 거지 누굴 탓하겠어
이제부터라도 클로저, 스코프에 대해 더 잘 이해해서 잘 써먹자!