-
항해99 53일차 TIL : useEffect는 사실 비동기처리였다니!항해99_WIL 이따금씩 TIL 2021. 4. 25. 17:37
미니프로젝트 마지막날, 문제가 생겼다. 몇시간이고 풀리지도 않고, 리액트 고수들을 2명이나 초빙해도 풀리지 않았다.
문제는 useEffect라는 아이.
문제의 코드
const detail_id = props.match.params.ids; React.useEffect(() => { dispatch(postActions.getPostAPI(detail_id)); }, []); const post_list = useSelector((state) => state.post.detail_list); console.log(post_list); const target_idx = post_list.findIndex((p) => p.id == detail_id); console.log(target_idx); const post_target = post_list[target_idx] console.log(post_target);
다른 컴포넌트의 코드에서도 멀쩡히 잘 돌아가는 코드인데, 이상하게도 실행을 시키면 post_list부터 빈 배열 [ ] 이라고 개발자도구에서 나오고, 아무것도 없는 배열이라 target_idx는 당연히 없어서 -1로 나오고 따라서 post_target도 undefined로만 나온다.
useEffect를 썼으니 그 안의 dispatch함수가 실행되면서 getPostAPI라는 미들웨어함수가 실행된다(역할은 서버에서 상품의 상세페이지 데이터를 가져오는 것). 그러면서 리덕스 스토어에 있는 post의 detail_list가(initialState는 빈 배열로 [ ] 이다) 상품 상세 데이터로 채워지게 된다. 그런데 개발자 도구를 확인해보니 useEffect에서 dispatch로 일어나게 한 action이 나중에 일어나고, initialState로 비어 있는 detail_list가 호출 되는 것이 먼저 일어나는 것이 문제였다. 다시말해, 원하는 일은 일어나긴 하되, 나중에 일어나는 것이 문제였다.
문제 원인은 알았는데, 해결점이 안 나오는 것이 문제였다. 리액트 고수 두명을 초빙하고 몇시간을 같이 붙들고 원인이 될만한 것은 샅샅이 뒤졌으나 해결은 되지 않았다. 그래서 결국 스파르타 코딩의 튜터님께 문의를 드렸는데,
튜터님의 짬에서 나오는 바이브로 해결되었다!!
일단 해결한 코드를 보고 설명하겠다.
React.useEffect(() => { dispatch(postActions.getPostAPI(detail_id)); // 비동기가 여기서 일어난다 }, []); // 실행에서 일어나는 비동기처리를 제어하기 위한 코드 const post_list = useSelector((state) => { // console.log(state) // window.alert('') return state.post.detail_list }); //console.log(post_list); const target_idx = post_list.findIndex((p) => p.id == detail_id); //console.log(target_idx); const post_target = post_list[target_idx] //console.log(post_target);
useEffect안에서 일어나는 dispatch 함수의 실행과, post_list라고 선언된 변수에 리덕스 스토어에 있는 post의 detail_list의 데이터가 담기는 일은 비동기적으로 일어난다. 무슨 말이냐면, dispatch가 일어나고 dispatch의 인수로 들어간 getPostAPI라는 미들웨어 함수가 작업을 하면서, 리덕스 스토어에서 가져온 값을 post_list 변수에 담아주는 일은 같이 일어나는데 getPostAPI의 일이 끝나기 전에 post_list에 데이터가 담기는 일이 먼저 일어날 수 있다는 말이다. 우리조의 골치를 썩인 일은 이런 원인으로 일어난 것이다.
그래서 튜터님은 리덕스의 약점이 바로 이런 비동기처리가 유발하는 문제라고 하셨다.
튜터님이 말씀하시길 이런 일이 일어나지 않는 경우는 useEffect 안의 함수가 자기일을 아주 아주 빠르게 처리하고 끝내버리는 경우라야 이런 일이 일어나지 않는다고 하셨다. 그런데 그런 경우는 드물다고.
우리 문제의 경우 해결책은 위 코드와 같다. 주석으로 달아 놓았듯이, useSelector 함수를 저런 식으로 만들어 return이 나오게 해서 return값을 post_list에 데이터로 할당해주면, useEffect안의 dispatch 함수가 발동되어 getPostAPI 함수의 작업이 끝나고 난 다음에 post_list가 선언되게 하면 된다고 한다.
사실은 의아하다. 왜냐면 저렇게 return이 명시된 코드나,
const post_list = useSelector((state) => state.post.detail_list);
이거나 다른게 있나 싶기 때문이다. 추측하건데 지금은 주석으로 처리해뒀지만 window.alert('')이 있음으로 해서 저 과정을 기다린 다음 post_list가 정의 되게 해주게 해서 post_list에 데이터가 담기는 일이 먼저 일어나는것을 방지한게 아닌가 싶기도 하다. 그런데 주석으로 처리를 해둬서 window.alert 함수가 실행되지 않게 하지 않더라도 return으로 post_list에 들어가는 데이터를 정의해두면 '먼저 일 벌리지 말고 대기한 다음 데이터를 받아'라는 뜻이 되므로 비동기의 문제를 해결하는것 같다. 내 추측이 나는 신빙성이 있다고 생각하는데, 확인하고 싶으니 튜터님께 다시 질문을 할 작정이다.
그리고 그냥 쓸 일이 굳이 있을까 싶기도 하고 다른 일도 바빠 미뤄뒀던 것이 async와 await를 쓰는 방식인데, 이것은 비동기가 일어나지 않게 해서, 다시 말해 동기화 처리를 해주는 함수? 명령어? 같은 것을 배워서 쓸 수 있게 해둬야겠다는 생각이 들었다. 채팅의 경우에도 동기화를 위해서 저것들을 쓰는 것을 고수들의 코드에서 보기도 했는데, 이번 '비동기 사건' 을 겪으니 해보고 싶어졌다.
아무튼 깊게 고민한 끝에 이제는 정말 모르니 아는 자의 도움을 받아야 한다고 판단이 되면 주저말고 아는 자를 찾아가서 도움을 청하는 것이 좋다는 것을 다시 느꼈다. 해결 할 수 있는 지식이 없다면 모른다고 깨끗이 인정하고 아는 자의 도움을 받아 인식의 지평을 넓혀 해결하는 것이 성장하는 방식이다. 제자가 훌륭한 스승을 알아보고 그분에게 도움을 받아 성장하는 것이 인간 발전의 길이다. 그래서 튜터님 감사합니다!!
'항해99_WIL 이따금씩 TIL' 카테고리의 다른 글
항해99 9주차(2021.4.26-5.2) WIL : 5주 과정 실전프로젝트 1주차, 지도API와의 싸움 (0) 2021.05.02 항해99 8주차(2021.4.19-4.25) WIL : 미니프로젝트 2주차, 마지막 과정인 실전프로젝트 시작 (0) 2021.04.25 항해99 52일차 TIL : websocket으로 실시간 양방향 통신 테스트 하기. (0) 2021.04.25 항해99 50일차 TIL : API URL은 따로 파일로 관리하기의 필요성! (0) 2021.04.25 항해99 7주차(2021.4.12-4.18) WIL : 미니프로젝트에서 성장하기. (0) 2021.04.18