[기술면접] 클로저를 활용해 useState 만들기

iberis2·2024년 10월 8일

클로저 관련 라이브 코딩 면접을 보았는데 잘 풀지 못한 점이 아쉬워서, 다시는 틀리지 않기 위해서 복기하고 정리해자
(기억에 의존한 거라 문제 코드 내용은 조금 다름)

1. new value 로 바뀌지 않을 때, 어떻게 수정해야하나요?

  • setValue로 상태를 업데이트해도, 해당 상태를 참조하는 value가 변경되지 않는 문제
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

2. 여러 개의 useState 를 만들어서 사용할 때 서로의 스코프가 겹쳐서 setCount 만 변경하여도 setTest 까지 같이 변하는 문제를 어떻게 해결할 수 있을까요?

(실제 문제에서는 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' }

느낀점 및 개선할 점

라이브 코딩 면접은 처음이었는데, 직접 코드를 쳐볼 수 없고 화면만 보면서 어느 부분을 고쳐야할 지 상상코딩해야 했어서 조금 더 어렵게 느껴졌다. 콘솔로그로 찍어보고 싶은 부분 말하라고 친절히 해주셨는데도, 왜인지 그 면접장 안에서는 코드 흐름이 한 눈에 안들어오는 느낌이었다...🥲
집에 와서 다시 복기해보니 개념 자체가 기본적인 개념이었는데도 잘 못 풀어서 아쉬움이 남는다... ㅜㅜㅎㅎ 그치만 내가 못 한 거지 누굴 탓하겠어
이제부터라도 클로저, 스코프에 대해 더 잘 이해해서 잘 써먹자!

profile
자동화와 기록으로 더 효율적으로 일하는 으른 개발자가 되려고 합니다.

0개의 댓글