본문 바로가기
STUDY/react.js

[React] state 변경 함수 사용할 때 주의점: async

by 히컵 2024. 9. 11.

 

 

자바스크립트의 sync / async 관련 상식

자바스크립트는 일반적인 코드를 작성하면 synchronous(동기 방식)하게 처리하는데, 코드 적은 순서대로 코드가 실행된다는 뜻이다. 

하지만 몇몇 함수(ajax, 이벤트 리스너, setTimeout 등)들을 쓸 때는 asynchoronous(비동기 방식)하게 코드가 실행된다. 이런 함수들은 처리 시간이 오래 걸리기 때문에 코드들이 순차적으로 실행되지 않고 완료되면 실행된다.

console.log(1+1)
axios로 get요청하고나서 console.log(1+2) 실행
console.log(1+3)

예를 들어 이런 코드를 실행하면 2, 4가 바로 출력되고 그 다음에 3이 출력된다.

3을 출력하는 코드가 asynchoronous 처리를 지원하는 코드이기 때문이다. 3을 출력할 때 오래 걸리면 완료될 떄까지 잠깐 보류했다가 다른 코드를 먼저 실행 시킨다는 말이다.

심지어 ajax 요청이 0.00초 걸려도 물리적으로 잠깐 처리가 보류되어 2,4가 먼저, 그 다음 3이 출력된다. 

 

 

리액트의 setState 함수에 주의해야 하는 점

function App(){
  let [name, setName] = useState('kim')
}

리액트로 state 만들 때 위와 같이 한다. 그리고 setName을 사용하면 name이라는 state를 자유롭게 변경할 수 있다.

그런데 문제는 setName() 같은 state 변경 함수들은 전부 asynchronous(비동기 방식)으로 처리된다. setName()이 오래 걸리면 밑에 있는 다른 코드들부터 실행하다는 것이다. 그래서 예상치 못한 문제가 생길 수 있다.

 

여기 버튼을 누르면 2개의 기능을 순차적으로 실행하는 예제가 있다.

function App(){
    let [count, setCount] = useState(0);
    let [age, setAge] = useState(20);
    
    return (
    	<div>
            <div>안녕하세요 저는 {age}살입니다. </div>
            <button onClick={ () => {
            	setCount(count+1);
                if (count < 3){
                    setAge(age+1);
                }
            } }>나이 증가</button>
        </div>
    )
}
  1. 버튼을 누를 때마다 count라는 state를 +1 한다. (버튼 누른 횟수는 기록용)
  2. age라는 state도 +1
  3. 그런데 만약 count가 3회보다 적으면 age+1을 해 준다.

버튼을 3번 이상 누르면(count가 3 이상이면) 나이를 그만 더하라는 기능으로 22살에서 멈춰야 한다.

하지만 실행해 보면 23까지 증가한다. count가 3일 때도 age + 1을 해 주고 있는 것이다.

 

왜일까?

이유는 state 변경 함수는 async(비동기 방식)하게 처리되는 함수이기 때문에 완료되기까지 시간이 오래 걸리면 다음 코드를부터 실행하기 때문이다.

그래서 코드를 해석해 보면,

  1. 버튼을 3번째 누르면 setCount(count+1)을 실행해서 count를 3을 만들어 준다.
  2. 그런데 count를 3으로 만드는 건 오래 걸리니까 if(count > 3){ }을 먼저 실행한다.
  3. 이때 count는 아직 2라서 if문 안의 setAge(age+1)이 잘 동작하고 있는 것이다.

그래서 예시처럼 state1 변경하고나서 state2를 변경하는 코드를 작성할 때는 가끔 문제가 생긴다.

이 문제를 정확하게 sync으로, 순차적으로 실행하고 싶을 때 해결 방법은 useEffect이다.

useEffect를 작성하면 특정 state가 변경될 때, useEffect를 실행할 수 있다.

 

useEffect는 컴포넌트가 렌더링/재렌더링될 때 실행되는 함수이다.

[ ] 대괄호 안에 state 넣으면, 그 state가 변경되면 이 코드를 실행해 달라는 뜻으로도 사용 가능하다.

그래서 위의 예제의 문제를 해결할 수 있다.

1. count라는 state가 변경된 후

2. age도 변경

이런 식으로 순차적으로 코드를 실행할 수 있다. 

function App(){
    let [count, setCount] = useState(0);
    let [age, setAge] = useState(20);
    
    useEffect(()=>{
    	if (count < 3){
            setAge(age+1);
        }
    }, [count]);
    
    return (
    	<div>
            <div>안녕하세요 저는 {age}살입니다. </div>
            <button onClick={ () => {
            	setCount(count+1);
            } }>나이 증가</button>
        </div>
    )
}

그런데 이렇게 useEffect를 써도 처음 페이지 로드될 때도 1번 실행이 되기 때문에 의도치 않은 버그가 발생할 수가 있다. 그래서 처음 페이지 로드시 useEffect 실행을 막는 코드를 적용해도 되고, 아니면 count라는 state를 또 활용해도 된다.

count가 0일 때(페이지 처음 로드 되었을 때)는 내부 코드를 동작시키지 않으면 된다.

 

function App(){
    let [count, setCount] = useState(0);
    let [age, setAge] = useState(20);
    
    useEffect(()=>{
    	if (count != 0 && count < 3){
            setAge(age+1);
        }
    }, [count]);
    
    return (
    	<div>
            <div>안녕하세요 저는 {age}살입니다. </div>
            <button onClick={ () => {
            	setCount(count+1);
            } }>나이 증가</button>
        </div>
    )
}

count가 0이 아닐 때만 실행하라고 조건을 추가해 줬따.

이제 버튼을 누르면 22살까지만 증가한다.

 

 

 

 

 

* 이 포스팅은 코딩애플 강의를 토대로 작성하였습니다.