yamoo9 / likelion-FEQA

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

[LAB-4] Cannot read properties of undefined (reading 'length') 에러 원인을 모르겠습니다. #284

Closed SeoMiYoung closed 1 year ago

SeoMiYoung commented 1 year ago

질문 작성자

서미영

문제 상황

image (위의 상품문의 상황은, 탱탱쫄면 상품페이지의 상품문의 부분입니다.) 현재 상품 문의에는 위와 같이 3개의 문서(document)가 들어있습니다. 저는 3개의 문서는 파이어스토어를 통해 관리하고 있는 상황입니다.

<코드를 고치기 전> - 문제없이 작동이 됨

let numPages;

useEffect(() => {
  console.log('[ProductInquiry] dataState: ', dataState);
}, [dataState]);

before_inquiryDataState출력 저는 파이어스토어에서 관리하고 있는 문서가 {..}, {..}, {..} 이렇게 3개가 있다고 했을 때, dataState에 [ {..}, {..}, {..} ] 이렇게 모아지도록 코드를 짰습니다.

그렇기 때문에 위의 사진을 잘 보면, dataState가 잘 출력되는 걸 확인할 수 있습니다. 콘솔창에 잘 출력이 됩니다.

< 제가 하고 싶은 건? >

저는 numPages라는 변수에 numPages = Math.ceil(dataState.length / limit); 를 할당하고 싶습니다. 참고로 limit은 6입니다.

< 코드를 수정한 후 > - 에러가 발생

let numPages;

useEffect(() => {
  console.log('[ProductInquiry] dataState: ', dataState);

  // test - dataState의 길이를 가져오기
  console.log('dataState의 길이 출력 => ', dataState.length);
  numPages = Math.ceil(dataState.length / limit);
}, [dataState]);

고작 주석 1줄과, 코드 2줄만 추가했을 뿐인데, 다음과 같은 에러가 뜹니다. image

저는 dataState라는 배열에 length를 쓴 것이 왜 에러를 일으키는지 정말 정말 모르겠습니다.

프로젝트 저장소 URL

저장소: https://github.com/SeoMiYoung/project-repo branch: feat/#96 관련 파일명: ProductInquiry.jsx

환경 정보

운영체제 정보: 윈도우 Node.js 정보: v18.14.0

yamoo9 commented 1 year ago

문제 원인

오류가 발생한 원인을 보면 타입에 문제가 있음을 알 수 있습니다. 데이터가 undefined의 경우 length 속성(property)을 가질 수 없는데 접근을 시도했기 때문입니다.

TypeError Cannot read properties of undefined (reading 'length") Call Stack eval src/pages/ProductDetail/Productinquiry/Productinquiry.jsx:78:51

문제 해결

이러한 유형의 문제는 데이터가 undefined일 가능성을 제외시키면 해결됩니다. 아래 코드의 useEffect 이펙트 콜백 함수는 dataState 값이 변경될 때 마다 실행됩니다. 그러므로 dataState 값이 undefined일 가능성을 배제시켜야 합니다.

아래와 같이 조건문을 사용해 문제를 해결할 수 있을 것입니다.

let numPages;

useEffect(() => {
  console.log('[ProductInquiry] dataState: ', dataState);

  if (dataState) {
    console.log('dataState의 길이 출력 => ', dataState.length);
    numPages = Math.ceil(dataState.length / limit);
  }
}, [dataState]);

다른 해결 방법은 dataState 상태의 초깃값을 빈 배열로 설정하는 것입니다.

const [dataState, setDataState] = useState([]);

dataState 상태는 기본적으로 배열이니 length 속성을 가지게 되죠. 그러므로 아래 코드가 오류가 발생할 일은 없게 됩니다. 😊

let numPages;

useEffect(() => {
  console.log('dataState의 길이 출력 => ', dataState.length);
  numPages = Math.ceil(dataState.length / limit);
}, [dataState]);

하지만 작성된 코드에서 dataState는 useDetailCollection 함수로부터 추출 되므로 앞서 다룬 것처럼 조건문을 사용해 문제를 해결하는 것이 권장됩니다. 🤔

const { dataState } = useDetailCollection(collectionName);
SeoMiYoung commented 1 year ago

아~ 그렇군요...! 야무쌤 정말 답변해주셔서 감사합니다.

추가로 궁금한점은 사실 저는 컬렉션안의 문서들을 dataState의 큰 배열로 모아서 그 배열의 length를 파악해서 문서들의 개수를 세려고 시도한거잖아요..?

근데 파이어스토어측에서 특정 컬렉션안의 문서 개수를 세는 방법에 대한 기능은 제공하고 있지 않은건가요? 사실 있으면 가져다가 쓰려고 했는데, 막상 찾아보니 정보들이 많은 것 같진 않더라고요....(정보도 잘 안나오는 것 같아요ㅠㅠ) 혹시나 파이어스토어에서 문서의 개수를 세는 기능을 제공하고 있는데 제가 찾지를 못한건지 궁금해서 추가 질문드립니다.

yamoo9 commented 1 year ago

아~ 그렇군요...! 야무쌤 정말 답변해주셔서 감사합니다.

추가로 궁금한점은 사실 저는 컬렉션안의 문서들을 dataState의 큰 배열로 모아서 그 배열의 length를 파악해서 문서들의 개수를 세려고 시도한거잖아요..?

근데 파이어스토어측에서 특정 컬렉션안의 문서 개수를 세는 방법에 대한 기능은 제공하고 있지 않은건가요? 사실 있으면 가져다가 쓰려고 했는데, 막상 찾아보니 정보들이 많은 것 같진 않더라고요....(정보도 잘 안나오는 것 같아요ㅠㅠ) 혹시나 파이어스토어에서 문서의 개수를 세는 기능을 제공하고 있는데 제가 찾지를 못한건지 궁금해서 추가 질문드립니다.


@SeoMiYoung 님께서 궁금해 하는 컬렉션 내부의 모든 문서 갯수를 가져오는 방법에 대해 이전에 질문이 있었고, 답변을 드렸던 적 있습니다. 아래는 답변 남긴 스크린샷 입니다. 😊

그리고 아래는 필요로 하는 API 입니다. 참고해보세요. 😉

Firestore에서 문서의 총 갯수로 가져오는 API

getCountFromServer 함수를 사용해 컬렉션 내부의 모든 문서 갯수를 Firestore에 요청해서 응답 받을 수 있습니다.

실제로 문서를 다운로드하지 않고 지정된 쿼리의 결과 집합에서 문서 수를 계산합니다. 이 기능을 사용하면 문서의 데이터가 아닌 최종 집계만 다운로드하기 때문에 문서 개수를 세는 것이 효율적입니다.

이 기능은 결과 집합이 완전히 다운로드하기에 엄청나게 큰 경우(예: 수천 개의 문서) 문서를 계산할 수도 있습니다. 서버에서 받은 결과는 로컬 상태를 고려하지 않고 변경되지 않은 상태로 표시됩니다.

즉, 로컬 캐시의 문서는 고려되지 않으며 아직 서버와 동기화되지 않은 로컬 수정 사항도 고려되지 않습니다. 이전에 다운로드한 결과는 사용되지 않습니다. 이 소스를 사용하는 모든 요청에는 반드시 서버 왕복이 포함됩니다.

getCountFromServer(
  query(collection(db, 컬렉션), orderBy('createAt'))
)
  .then(response => {
    console.log('문서 갯수:', response.data().count);
  })
  .catch(error => {
    console.error(error.message);
  });