개발자

react 그리드에서 수정된 부분이 있으면 조회버튼을 눌렀을 때 alert창 띄우기 기능 (useState 비동기 질문)

2023년 02월 22일조회 284

안녕하세요. react 초보 개발자입니다. 자바스크립트도 처음이라 비동기 관련해서 제 의도와는 다르게 작동할 때마다 당황하는 중입니다🤣 쌩초보임을 감안해 너그럽게 댓글 달아주시면 감사하겠습니다 :) 화면 설명 먼저 드리자면 조회 버튼을 누르면 그리드에 데이터가 뜨고, 데이터를 수정한 후에 저장할 수 있는 화면입니다! 제목과 같이 그리드에서 수정된 부분이 있을 경우에, 저장하지 않고 조회버튼을 누르면 '수정중입니다. 조회를 진행하시겠습니까?' 와 같은 alert창을 띄우는 기능을 구현중에 있습니다. 참고로 그리드는 aggrid 사용중입니다. 1. 우선 변경값 존재여부를 확인하는 변수 isChanged 를 useState로 선언해주었습니다. const [isChanged, setIsChanged] = useState<boolean>(false); 2. 그리드에서 수정된 부분이 있나 체크하는 건 onCellValueChanged 함수를 통해 값이 변경되면 isChanged가 true로 바뀌게 했습니다. const onCellValueChanged = (params: any) => { setIsChanged(true); }; 3. 저장로직에는 setIsChanged(false)가 있습니다. 또한 저장이 제대로 끝난 후에 조회로직을 탑니다. const onClickSave = async () => { // (생략) 저장 로직 // 저장 완료 후 setIsChanged(false); onClickSearch(); } 4. 조회로직에는 isChanged의 값을 확인하고, 변경값이 있다면 alert창이 뜨고, 확인을 누르면 조회를 진행합니다. const onClickSearch = async () => { if (isChanged) { // (생략) 메세지 띄우는 로직. alert창에서 확인을 누르면 조회 진행 setIsChanged(false); } // (생략) 조회 로직 } * 문제점 정상적으로 저장을 한 후에 조회로직을 타는데, isChanged가 false로 변경되기전에 조회로직을 타서 alert창이 뜹니다. 이 문제는 어떻게 해결하는 게 좋을까요..?

이 질문이 도움이 되었나요?
'추천해요' 버튼을 누르면 좋은 질문이 더 많은 사람에게 노출될 수 있어요. '보충이 필요해요' 버튼을 누르면 질문자에게 질문 내용 보충을 요청하는 알림이 가요.

답변 2

인기 답변

엄홍재님의 프로필 사진

질문에 대해 정확하게 이해는 못했습니다. 죄송합니다.ㅜㅜ 추측해보면 setState에서 비동기가 문제가 될것 같습니다. setState를 여러번 실행하더라도 비동기로 동작하면서 값이 순서대로 변하지 않아 원하지 않는 로직대로 동작하지 않을 수 있습니다. 관련 내용: https://careerly.co.kr/qnas/865 해당 부분으로 문제를 해결할 수 있으면 좋겠네요.. 아니라면.. 질문을 좀더 상세히 코드도 올려주시면 감사하겠습니다. :)

진라님의 프로필 사진

진라

작성자

리액트 개발자2023년 02월 22일

내용을 조금 추가했어요! 이해 안 가시는 부분 말씀해주시면 더 설명 붙여보겠습니다!

엄홍재님의 프로필 사진

엄홍재

커리어리 소프트웨어 엔지니어2023년 02월 22일

오.. 추측을 해보면 onClickSearch를 풀어서 쓰면 아래처럼 되는데요 const onClickSave = async () => { // (생략) 저장 로직 // 저장 완료 후 setIsChanged(false); if (isChanged) { // (생략) 메세지 띄우는 로직. alert창에서 확인을 누르면 조회 진행 setIsChanged(false); } // (생략) 조회 로직 } 비동기 문제가 맞는거같아요 위에서 setIsChanged를 호출했다고 해서 isChanged값이 바로 false로 변경되는게 아닙니다. 비동기여서 아마 밑의 로직이 바로 실행되면서 alert가 뜨는것같아요 아래처럼 한번 테스트 해보시겠어요? 직접 시간지연을 줘서 테스트 해보면 원인을 확실히 알거같아요 setIsChanged(false); setTimeout(()=>{onClickSearch()}, 100) (확실하지는 않지만 setIsChanged(()=>false) 로 변경해도 되려나.....)

진라님의 프로필 사진

진라

작성자

리액트 개발자2023년 02월 23일

답변 감사합니다!! 추측하신 내용이 맞습니다! 시간지연도 줘봤었는데 안 되더라구요ㅠㅠ setIsChanged(()=>false) 요것두 안 됩니다 ㅠㅠ

엄홍재님의 프로필 사진

엄홍재

커리어리 소프트웨어 엔지니어2023년 02월 24일

오! 그래도 원인을 찾았으니 해결하실 수 있을거에요 화이팅!

손정현님의 프로필 사진

안녕하세요! 우선 저 함수들이 컴포넌트 안에서 생성되었다고 가정을 하고 답변드리겠습니다. 위 함수들은 비동기인 것과는 큰 상관이 없어보이고, 함수가 생성되는 시점에 참조하고 있는 상태 값에 대한 차이인 것 같습니다. 리액트 컴포넌트 안에서 함수를 생성하시게 되면, closure 함수를 만드는것과 같습니다. closure 함수는 생성될때 만약 내부에서 외부의 primitive type의 상태를 의존하고 있다면 해당 상태 값은 생성될 때의 값으로 보존됩니다. 그리고 함수가 실행될때도 해당 값을 사용합니다. 즉, 질문자님의 onClickSearch 함수는 처음 생성될때 isChanged가 false기 때문에, onClick에서 onClickSearch를 호출할때도 isChanged가 false인 값을 사용하게됩니다. 이 문제는 stale closure라고도 합니다. 해당 문제를 해결하는 방법은 여러가지가 있는데요. 그 중 2가지를 codesandbox로 구현해봤습니다. 1번은 useEffect로 처리하는 방법이고 2번은 ref를 통해서 처리하는 방법입니다. 참고해보시면 좋을것 같아요! - https://codesandbox.io/s/angry-roentgen-5imzz1?file=/src/App.js

진라님의 프로필 사진

진라

작성자

리액트 개발자2023년 02월 23일

답변 감사드립니다!! 다른 일에 치여서 이제야 봤네요 ㅠㅠ 말씀해주신대로 처리해보고 후기 바로 남기겠습니다!! 👍

진라님의 프로필 사진

진라

작성자

리액트 개발자2023년 02월 27일

useEffect 사용하는 것으로 처리 완료했습니다! 예시 code도 짜주시고.. 정말 감사드립니다:)

지금 가입하면 모든 질문의 답변을 볼 수 있어요!

현직자들의 명쾌한 답변을 얻을 수 있어요.

또는

이미 회원이신가요?

비슷한 질문 2

Q. useRef의 작동방식이 궁금합니다

useRef를 통해 dom element에 접근하기도 하고, stale closure문제를 해결하는데에도 사용하는데, 실질적으로 어떻게 작동해서 이게 가능한지 궁금합니다.

안녕하세요! useRef를 통해 만들어지는 것은 JS의 객체와 유사하다고 보면 됩니다. useRef를 통해 만든 값이 일반 객체와 다른점은, 매 렌더링 시 동일한 객체를 제공하는 것입니다. - useRef<T>(initialValue: T): {current: T} - (참고: https://github.com/facebook/react/blob/fd31724d5d41a4df71af55f710b46562874511f9/packages/react-reconciler/src/ReactInternalTypes.js#L388) - (참고: https://ko.reactjs.org/docs/hooks-reference.html#useref) useRef를 통해 받은 값은 useRef가 사용된 컴포넌트의 라이프사이클 동안 사용할 수 있습니다. JSX에 특정 DOM 컴포넌트에 ref={useRef를 통해 받은 값}을 추가하시게 되면, 컴파일러가 JSX를 컴파일하면서 JS로 변환을 시켜주고 해당 JS를 브라우저가 실행하면서 값을 채워넣어주는 것으로 알고있습니다. (참고: https://ko.reactjs.org/docs/jsx-in-depth.html) stale closure의 경우는 useRef의 값이 항상 같은 객체이고 closure안에서는 이 객체를 "참조"하는 형식이기 때문에, 객체의 current 값이 바뀌면 closure 안에서도 바뀐 current 값을 사용할 수 있습니다. (reference 관련 so 글 참고: https://stackoverflow.com/questions/8318357/javascript-pointer-reference-craziness-can-someone-explain-this) 저도 질문에 답변하려고 여러가지 실험들을 해보았는데요. Functional component로 gist를 한번 만들어보았습니다. 저도 헷갈리는 부분이 있긴하지만 확실히 일반 JS 객체 대신 왜 useRef를 사용해야하는 이유를 알게된 것 같네요. (https://gist.github.com/json9512/ef28abf2429c639ccbdd6b48c7c3efe5) 결론은 useRef의 동작 방식이 가능한 이유는 JS에서 객체를 접근할때 참조 형식으로 접근한다는 점과 useRef를 통해 받은 객체가 항상 동일하다는 점 때문인 것 같습니다. 제가 참고했던 아티클들 첨부할게요. - https://dmitripavlutin.com/react-useref-guide/ - https://www.freecodecamp.org/news/react-under-the-hood/ - https://www.smashingmagazine.com/2020/11/react-useref-hook/ - https://developer.mozilla.org/ko/docs/Web/JavaScript/Guide/Working_with_Objects#%EA%B0%9D%EC%B2%B4_%EA%B0%9C%EC%9A%94 - https://hewonjeong.github.io/deep-dive-how-do-react-hooks-really-work-ko/

이 질문 바로 가기

Q. react의 setState가 정상동작하지 않아요.

react를 이용해 이것저것 테스트해보고 있는데요. 버튼을 눌렀을 때 state를 1 증가시키는 예제를 해보고 있습니다. const oneCountUp = () => { setCount(count + 1); }; 위처럼 1 증가시키는 함수를 정의해서 onClick에서 위의 함수를 호출하는 방식으로 했습니다. 근데 호기심에 3을 증가시켜보자 하고 oneCountUp을 3번 호출했습니다. 근데 여전히 1만 올라갑니다. 왜 그런걸까요??? import React, { useState } from 'react'; export default function CountTest() { const [count, setCount] = useState(1); const oneCountUp = () => { setCount(count + 1); }; return ( <div> <button onClick={() => { oneCountUp(); oneCountUp(); oneCountUp(); }} > 1 증가 </button> <p>{count}</p> </div> ); }

혹시 공식문서의 https://ko.reactjs.org/docs/faq-state.html#why-is-setstate-giving-me-the-wrong-value 이 부분을 보셨나요? 클래스 컴포넌트로 되어있긴한데 매우 유사한 상황인것 같습니다. 'setState 호출은 비동기적으로 이뤄집니다. 따라서 setState 호출 직후 새로운 값이 state 에 반영될 거라고 믿어서는 안 됩니다.' 라고 적혀있습니다. oneCountUp(); oneCountUp(); oneCountUp(); 이렇게 3번 호출 할 때 순차적으로 호출되고 끝나고 호출되고 끝나고가 아닌 비동기로 이루어져서 전부 현재 상태에서 1을 증가시키게 되는것으로 보입니다. 이걸 수정하려면 oneCountUp함수를 수정해야할 것 같아요 setState함수에는 값 말고 함수를 넘겨줄 수 있습니다. 그리고 함수의 첫번째 인자로 직전 state를 가지고 오게 됩니다. 그래서 아래처럼 변경하면 될 것 같네요 const oneCountUp = () => { setCount((beforeCount) => beforeCount + 1); };

이 질문 바로 가기

목록으로
키워드로 질문 모아보기

실무, 커리어 고민이 있다면

새로운 질문 올리기

지금 가입하면 모든 질문의 답변을 볼 수 있어요!