본문 바로가기

React

useState 동기적으로 사용하기

 

 

감사하게도 나는 취업에 성공해서 현재 회사에 잘 다니고 있다.

지금 다니고 있는 회사는 SI와 솔루션을 개발하는 회사이다. 맞다. 개발자들의 커리어 무덤이라고 불리는 바로 그 SI업체 회사이다. 

면접을 보고 고민을 많이 했다. 그러나 이 회사가 진행하는 프로젝트와 나의 역할이 만족스러워서 선택했고, 지금은 분수에 맞지 않은 경험들을 하고 있다.

지금 진행하는 프로젝트는 정부에서 요청한 사업인데, 현재 프론트엔드의 전반적인 것과 백엔드의 서버 개발까지 맡아서 진행 중이다. 

어쩌다 보니 나름 풀 스택의 역량을 발휘하고 있다.

프로젝트는 특정 지역의 사업체의 보고 데이터를 이전 아날로그 형식으로 저장했던 것을 온라인화 하는 작업이다. 쉽게 말하면 생성하고 수정을 하는 온라인 문서라고 볼 수 있다.

현재 클라이언트는 React로 서버는 Node로 개발하고 있다.

 

 

클라이언트를 개발하는 데 있어 데이터를 보여주는 화면을 Card로, 데이터를 수정하는 화면을 Modal로 했다.

실제로 데이터를 보는 화면은 데이터가 담긴 Card들이 모인 메인 창이고 데이터를 수정하는 공간은 Modal창으로 띄워진다.

그래서 Card의 데이터와 Modal의 데이터는 한 State에서 시작해 Props로 각각 분배하는 방식을 사용했다.

그런데 Modal의 경우 데이터의 수정이 일어나는 페이지이기 때문에 새로운 State에 부모 컴포넌트로부터 받은 데이터를 깊은 복사하고 변경된 데이터를 부모 컴포넌트의 State에 setState하는 방식으로 데이터를 다뤘다.

그러던 중 변경된 데이터를 서버에 보내 DB에 저장하는 과정에서 문제가 발생했다.

const fetchUpdateData = () => {
  axios.post('https://localhost:4000/dataUpdate', {
    session: sessKy // 사용자 권한을 확인하기 위한 식별자
    data: state // 내가 요청에 담고자 하는 데이터 state
  }
}

const handleUpdateDate = () => {
  setState('새로운 데이터');
  fetchUpdateData();
}

나는 데이터를 저장하는 요청을 보낼 때 위와 같이 먼저 데이터를 state에 저장하고 그 state를 요청에 담아 보내길 원했다.

그래서 setState를 사용하고 요청을 보내는 함수를 실행시켰다. 그러나 데이터가 변경되지 않았다.

확인해보니 state의 값이 변경되지 않은 이전 데이터를 요청에 담아 보낸다는 것을 알 수 있었다. 

 

이와 같은 이유는 useState가 비동기 방식으로 작동하기 때문이다.

비동기란 간단히 말하면 카페에서 주문이 들어오면 그 주문의 커피를 만듦과 동시에 다음 주문을 받는 것을 말한다.

즉, state의 변화가 http요청보다 느리기 때문에 발생하는 문제였다.

그래서 나는 이것을 해결하는 방법을 찾아보았고 선택한 방법이 useEffect를 사용하는 것이다.

useEffect란 컴포넌트가 렌더링 될 때 특정 작업을 실행할 수 있도록 하는 Hook이다. side effect를 다루거나 생명주기를 사용할 때 사용한다.

useEffect는 첫 번째 파라미터에는 함수를 그리고 두 번째 파라미터에는 배열을 넣는다. 이 배열을 의존성 배열이라고 한다. 

이 의존성 배열인 두 번째 파라미터를 활용해서 함수가 실행되는 시점을 제어할 수 있다.

useEffect(() => {
  console.log('ok')
})

먼저 위와 같이 의존성 배열을 생략하면 렌더링이 완료될 때마다 함수가 실행된다.

useEffect(() => {
  console.log('ok')
}, [])

다음으로 의존성 배열에 빈 값을 넣으면 컴포넌트가 Mount될 때에만 실행된다. 쉽게 말하면 새로고침 하면 처음 한번 실행되는 것이다.

useEffect(() => {
  console.log('ok')
}, [state])

마지막으로 의존성 배열에 변수를 넣으면 그 변수가 변경될 때만 함수가 실행된다.

나의 경우 useState를 동기적으로 사용하기 위해서 useEffect의 마지막 방법을 사용해서 state가 변경되면 함수가 실행되도록 했다.

useEffect(() => {
  fetchUpdateData()
}, [state])

이렇게 작성하니 useState가 동기적으로 작동해 state값에 원하는 데이터가 저장되고 나서 요청을 보내는 함수가 작동되었다. 

 

그러나 이것에도 역시 문제가 있었는데, state가 변경할 때뿐만 아니라 Mount가 될 때도 함수가 실행돼서 원하지 않는 시점에 요청이 보내진 다는 점이다. 

그래서 이 점을 해결할 방법을 탐색했고 찾은 방법이 useRef를 사용하는 것이다.

const mounted = useRef(false);
useEffect(() => {
  if(!mounted.current) {
    mounted.current = true;
  } else {
    fetchUpdateData();
    mounted.current = false;
  }
}, [state])

내가 최종적으로 사용한 방법은 위의 코드이다.

useRef를 사용하여 mount의 순간 mount가 아닌 것처럼 속여 함수 실행을 방지하는 것이다.

이렇게 해서 정말 state가 바뀌는 순간에만 함수가 실행되도록 만들 수 있었다.

'React' 카테고리의 다른 글

useEffect의 mount와 unmount  (0) 2023.03.09
잘못된 useState 사용 줄이기  (0) 2023.03.08
[React] 상태 관리  (0) 2021.12.08
React 컴포넌트 디자인  (0) 2021.11.25
React 기초3 state & props  (0) 2021.08.26