yamoo9 / likelion-FEQA

질문/답변 — 프론트엔드 스쿨, 멋사
29 stars 9 forks source link

[LAB-3] 파이어베이스에서 값을 받아왔지만 그 값을 활용을 못하고있습니다. #235

Closed choinamechoi closed 1 year ago

choinamechoi commented 1 year ago

질문 작성자

최영범

문제 상황

image

image

image

image

원하는 최종 목표 :

문제점 :

구체적인 설명 :

프로젝트 저장소 URL

환경 정보

yamoo9 commented 1 year ago

당부의 말

질문에 포함된 코드를 이미지만으로 삽입하면, 답변 과정에서 직접 코드를 모두 쳐야 합니다. 😥 그리고 테스트 할 수 있도록 저장소 URL 또는 프로젝트 파일을 압축해 포함해주셨다면 더 좋았을 거에요.

문제 분석

문제 원인은 getQue 함수에 있습니다. get().then() 비동기 지연 처리와 상관없이 곧 바로 receivedData(빈 배열)을 반환합니다. 그래서 (0)에서 빈 배열이 출력된 것이고, (2), (4)에서 아이템 원소가 없으므로 모두 undefined가 출력된 것입니다.

(1)에서 Console에 아이템이 출력된 것은 비동기 요청/응답 과정에서 뒤 늦게 배열에 아이템이 추가되었기 때문입니다. 응답 결과로 데이터가 채워지기 전, 아이템 개수가 0임을 [[Prototype]]: Array(0) 출력 코드로 알 수 있습니다.

문제 해결

SearchRTDB, getQue, Mypage 코드를 아래와 같이 변경한 후 다시 테스트 해보세요. 직접 테스트 할 수 없었으므로 머리 속에서 시뮬레이션 한 코드이다 보니 오류가 발생할 가능성도 있습니다. 😳

function SearchRTDB(searchKey, searchValue) {
  const que = query(dbRef, orderByChild(searchKey), equalTo(searchValue));
  // getQue 함수가 반환한 Promise 객체를 다시 반환
  return getQue(que);
}
function getQue(que) {
  // get(que)는 Promise를 반환
  return get(que)
    .then((snapshot) => {
      const receivedData = [];
      snapshot.forEach((childSnapshot) => {
        receivedData.push(childSnapshot.val());
      });
      // resolved 상태인 경우, 데이터가 채워진 receivedData 반환
      return receivedData;
    })
    .catch(error => {
      console.error(error.message);
      return { message: error.message };
    });
}
function Mypage() {
  const [data, setData] = useState([]);

  // 네트워크 요청/응답과 같은 사이드 이펙트 처리를 위해 useEffect 훅 사용
  useEffect(() => {
    // SearchRTDB()는 Promise를 반환
    SearchRTDB('prkplceNo', '350-4-000008')
      // 요청에 따른 응답 결과를 처리
      .then((receivedData) => {
        // Console 패널에 응답 결과 출력
        console.log(receivedData);
        // 응답 결과 값으로 data 상태 업데이트
        setData(receivedData);
      })
  }, []);

  return (...);
}
choinamechoi commented 1 year ago

와.. 해결이 되었습니다.. 감사합니다. 추가 질문이 있습니다. 이건 그냥 궁금한것이고 실제론 Test1 이라는 배열을 사용 안해도 됩니다. image

image

-상황-

  1. Test1 배열에 받아온 데이터 push
  2. console.log로 Test1 찍어봄
  3. 데이터 안나옴

-나름 생각해본것- "비동기 요청/응답 과정에서 뒤 늦게 배열에 아이템이 추가되었기 때문입니다."

  1. 위의 문항이 문제가 된 것.
  2. Test1.push 구문이 실행 되는 상태에서 콘솔 로그가 거의 동시에 실행이 되어서 비어있는것처럼 나온것 같다.

이렇게 이해하면 되는건가요? 이런 문제가 앞으로도 많을것 같은데 이건 동기/비동기 부분을 더 공부해보면 되는거겠죠?

혹시 몰라 깃허브주소 남깁니다. : https://github.com/choinamechoi/car-zip/tree/feature/%2363

사용한 함수 위치 : car-zip > src > getDB > ReadDB 사용한 페이지 위치 : car-zip > src > pages >Mypage.jsx

env파일도 추가를 해야되는데 그것은 개인 디스코드 문자로 남기겠습니다. 감사합니다!!

yamoo9 commented 1 year ago

추가 질문

함수 컴포넌트 내부의 Test1 배열이 계속 빈 배열인 이유가 뭘까요?

추가 답변

리액트의 렌더링 과정에 대해 이해하면 문제 원인을 유추할 수 있습니다.

리액트 함수 컴포넌트 내부는 렌더링 될 때 마다 초기화 됩니다. Test1은 일반 자바스크립트 지역 변수이니, 리액트에 의해 렌더링 될 때 마다 초기 값인 빈 배열로 설정됩니다. 그러므로 "Test1은 항상 빈 배열"인 것입니다.

반면 리액트에 의해 관리되는 상태(state) 또는 기억된 데이터(memoized data)의 경우 리액트에 의해 렌더링 되더라도 이전 값을 기억합니다.

확인을 위해 아래 코드를 복사 붙여넣기 하여 결과를 테스트 해보세요.

// [자바스크립트] 함수 내, 지역 변수
const Test1 = [];

// [리액트] 기억된 데이터 관리 (useRef 훅: 반환 객체의 current 속성을 수정해도 리-렌더링 안 됨)
const Test1Ref = useRef([]);

// [리액트] 상태 관리 (useState 훅: setData 함수가 실행되면 리-렌더링 됨)
const [data, setData] = useState([]);

// [리액트] 이펙트 관리 (useEffect 훅: 종속성 배열이 빈 경우, 1회만 렌더링 됨)
useEffect(() => {
  SearchRTDB('prkplceNo', '350-4-000008').then((receivedData) => {
    // [자바스크립트] 지역 변수 값 업데이트
    Test1.push(...receivedData);
    // [리액트] 기억된 데이터 업데이트 (리-렌더링 안 됨)
    Test1Ref.current.push(...receivedData);
    // [리액트] 상태 업데이트 (리-렌더링 됨)
    setData(receivedData);
  });
}, []);

// ✅ 리-렌더링 될 때, Test1은 빈 배열로 초기화 됨
console.log('Test1:', Test1);
console.log('Test1.length:', Test1.length);

// ✅ 리-렌더링 될 때, Test1Ref 객체는 초기화 안 됨 (리액트에 의해 기억 됨)
//    Test1Ref.current에 기억된 배열 데이터 아이템이 추가 됨
console.log('Test1Ref.current:', Test1Ref.current);
console.log('Test1Ref.current.length:', Test1Ref.current.length);

결과는 아래 스크린샷과 같습니다. 😃

참고

답변 내용 중 "상태 업데이트", "이펙트 콜백" 레이블이 포함된 글을 참고해 읽고 이해해보세요. 😉