본문 바로가기

006. [코딩애플] [part1] array, object state 변경하는 법

codeConnection 2024. 5. 21.

state의 데이터 타입에 따른 상태변경 함수 사용법

state의 자료형이 숫자형이나 문자열 등은 어떻게 수정해도 상관없으나, 배열이나 객체인 경우에는 spread operator로 깊은 복사를 해주어야 한다.

만약 아래처럼 수정하면 원본 배열 자체가 덮어 씌워져 버린다.

function App() {
  const [cities, setCites] = useState(["서울", "인천", "제주"]);

  return (
    <>
      <button
        type="button"
        onClick={() => {
          setCites(["성남"]);
        }}
      >
        수정하기
      </button>
    </>
  );
}

수정하기 버튼을 누르면 cites라고 상태로 정의한 배열의 첫번째 값을 '서울'에서 '성남'으로 바꾸기 위해 setCites라는 상태변경 함수를 사용하였지만 이렇게 되면 cities 배열의 초기값이 전부 날아가고 ['성남']이라는 하나의 인덱스만 갖는 배열로 덮어 씌워져 버린다.

따라서 배열이나 객체가 상태로 정의되어 있고 이를 상태 변경 함수로 값을 바꾸고자 하는 경우에는 원본의 값을 그대로 두면서, 즉 '불변성'을 유지하면서 새로운 값을 추가하거나 수정하여야 한다.

이럴 때 spread operator를 사용한다. ...cities 와 같은 형태로 사용하며 의미는 해당 배열이나 객체를 감싸고 있는 괄호를 벗겨 내고 안의 값만 새롭게 깊은 복사로 펼쳐서 넣으라는 의미이다. 즉 원본은 그대로 두라는 이야기다.

아래와 같이 변수의 카피본을 만들어서 spread operator로 깊은 복사를 한 뒤 그 배열을 바꿔주면 된다

// 얕은 복사를 해서 작동하지 않는 코드
const [cities, setCites] = useState(['서울', '인천', '부산']);

<button type="button" onClick={()=>{
    let copyCities = cities;
    copyCites[0] = '대구';
    setCities(copyCites);
}}>

위 코드를 보면 onClick의 내용으로, copyCities 변수를 만들어서 cities를 할당한 다음 마치 복사한 듯한 copyCities의 0번 인덱스를 '대구'로 수정했다.

하지만 위는 얕은 복사로 실제로 같은 배열을 똑같이 복사해내는 게 아니라 참조만 복사하는 것이기 때문에 copyCities의 인덱스를 바꾸면 cities라는 원본 배열의 인덱스의 값도 같이 바뀌어 버린다.

그래서 이미 0번 인덱스가 '대구'로 바뀌어 있는 상태이기 때문에 setCities(copyCites)를 해봐야 원본 배열인 cities와 다를 게 없기 때문에 바뀌지 않는다.

리액트 state의 특징 중 하나는 상태 변경 함수를 호출할 때 인자로 전달되는 값을 보고 기존 배열과 다른지 엄격한 비교(===)를 해본 뒤 값이 다르면 원본의 상태를 변경하고, 같으면 아무런 행동을 하지 않는다는 특징이 있다.

그래서 이런 경우에는 아래처럼 spread operator를 사용하여 깊은 복사를 해주면 된다.

// 깊은 복사
const [cities, setCites] = useState(['서울', '인천', '부산']);

<button type="button" onClick={()=>{
    let copyCities = [...cities]; // 괄호를 ...로 벗겨 냈기 때문에 다시 씌워줘야 함.
    copyCites[0] = '대구';
    setCities(copyCites);
}}>

댓글