레이아웃 제작을 완료하고 기능 구현을 하기 위해 mock data를 생성해서
useEffect()
안에fetch()
함수를 사용하여 데이터를 불러오던 도중, 처음에는 데이터가 잘 담겨서 불러와지다가 갑자기 빈배열이 출력되는 현상이 발생했다. 뭔가useEffect
와 관련이 있을 것 같아서 정확히 정리하기 위해 공부하면서 벨로그 글을 작성해보기로!
const [thumbnail, setThumbnail] = useState([]);
useEffect(() => {
fetch("/data/Detail/getDetail.json")
.then(res => res.json())
.then(data => setThumbnail(data));
console.log(thumbnail);
}, []);
위 코드는 내가 생성한 mock data getDetail.json
안의 데이터를 thumbnail
안에 담아 state 상태로 관리하기 위해 짠 코드 내역이다. 처음에는 getDetail.json
안의 데이터가 잘 출력이 되다가 어느 순간부터 []
빈배열로 출력되는 것이다..! 무엇이 문제인 것일까 🤔
- 동기적(Synchronous)
어떤 작업을 요청했을 때 그 작업이 종료될때 까지 기다린 후 다음 작업을 수행하는 방식
- 비동기적(Asynchronous)
어떤 작업을 요청했을 때 그 작업이 종료될때 까지 기다리지 않고 다른 작업을 하고 있다가, 요청했던 작업이 종료되면 그에 대한 추가 작업을 수행하는 방식
setThumbnail
함수는 비동기적으로 처리된다. 즉, 종료되는 순서대로 처리되는 게 아니라 setThumbnail
함수가 완료되기 전에 console.log(thumbnail)
구문이 먼저 실행될 수도 있다는 뜻이다.
console.log(thumbnail)
구문이 실행되는 시점에는 thumbnail
상태(state)가 아직 업데이트되지 않았을 가능성이 있기 때문에 빈 배열 ([]
) 이 출력되는 것이다.
"data" : {
"detailImages" : {
"detailImageLectureId": 1
}
}
이런 식의 구조로 되어있는 json 파일 안에 detailImages
데이터를 따로 변수에 담아 관리하기 위해 const thumbnailData = thumbnail.data.detailImages
라는 코드를 작성한 상태에서 console.log
을 찍으니 저런 타입 에러가 났다.
찾아보니 이는 위에서 말한 비동기적 처리와 관련이 있는 거 같았는데, state
처리 자체가 비동기적이기 때문에 렌더링이 되기도 전에 동작이 되어서 state
의 상태가 정의되기 전 (빈 배열) 상태이기 때문에 undefined 에러가 뜬다는 것이다.
즉, 내가 담은 thumbnail
상태가 어느 순간부터 빈배열이 되어 데이터 타입이 undefined
가 되었기 때문에 원하는 대로 thumbnailData
변수에 담기지 않는 것이다.
위의 에러를 해결하기 위해서는 삼항연산자
혹은 && 연산자
를 이용하여 조건부 렌더링을 구현해볼 수 있다.
- 삼항연산자 : 조건의 true/false에 따라 각기 다른 UI 를 렌더링 할 때 사용
- && 연산자 : 조건이 true일 때만 특정 UI를 렌더링하고, false일 때는 아무것도 렌더링하지 않을 때 사용
return (
<ThumbnailLayout>
{thumbnail.data.detailImages && (
<>
<HighlightLayout>
<HighlightThumbnail
key={thumbnail.data.detailImages[0]?.detailImageLectureId}
alt="thumbnail"
src={thumbnail.data.detailImages[0]?.detailImageUrl}
/>
</HighlightLayout>
<AdditionalLayout>
<AdditionalThumbnail
key={thumbnail.data.detailImages[1]?.detailImageLectureId}
alt="thumbnail"
src={thumbnail.data.detailImages[1]?.detailImageUrl}
/>
<AdditionalThumbnail
key={thumbnail.data.detailImages[2]?.detailImageLectureId}
alt="thumbnail"
src={thumbnail.data.detailImages[2]?.detailImageUrl}
/>
<ThumbnailButton>+2개의 이미지</ThumbnailButton>
</AdditionalLayout>
</>
)}
</ThumbnailLayout>
);
};
렌더링하고자 하는 태그 안에 && 연산자
를 이용하여 조건부 렌더링 코드를 짰는데도 안된다!
그 이유는 ... thumbnail.data.detailImages
데이터 자체가 배열 형태인데 javascripts는 빈 배열, 빈 객체도 true로 인식하기 때문에 state가 undefined여도 UI가 출력이 되는 것이다.(ㅠㅠ)
- truthy(참 같은 값)이여서 true로 인식하는 데이터 예시들
if (true) if ({}) if ([]) if (42) if ("0") if ("false") if (new Date()) if (-42) if (12n) if (3.14) if (-3.14) if (Infinity) if (-Infinity)
const [thumbnail, setThumbnail] = useState({});
useEffect(() => {
fetch("/data/Detail/getDetail.json")
.then(res => res.json())
.then(data => setThumbnail(data.data));
}, []);
console.log(thumbnail);
return (
<ThumbnailLayout>
{thumbnail.lectureId && (
<>
<HighlightLayout>
<HighlightThumbnail
key={thumbnail.detailImages[0]?.detailImageLectureId}
alt="thumbnail"
src={thumbnail.detailImages[0]?.detailImageUrl}
/>
</HighlightLayout>
<AdditionalLayout>
<AdditionalThumbnail
key={thumbnail.detailImages[1]?.detailImageLectureId}
alt="thumbnail"
src={thumbnail.detailImages[1]?.detailImageUrl}
/>
<AdditionalThumbnail
key={thumbnail.detailImages[2]?.detailImageLectureId}
alt="thumbnail"
src={thumbnail.detailImages[2]?.detailImageUrl}
/>
<ThumbnailButton>+2개의 이미지</ThumbnailButton>
</AdditionalLayout>
</>
)}
</ThumbnailLayout>
);
.then(data => setThumbnail(data.data));
데이터를 불러올 때
data
대신data.data
로 불러와서 depth를 줄여줬다.
{thumbnail.lectureId && ()}
if (!thumbnail.lectureId) return null // 위와 같은 의미
thumbnail.lectureId
값이 false일 때 null을 반환하라는 의미!lectureId
값은 배열이 아니기 때문에 json 값이 state에 담기기 전까지는 false로 인식한다.
이렇게 코드를 바꿔주니까 드 디 어 정상적으로 작동하는 모습을 확인할 수 있었다!!!
💭 단순히 그냥 담기만 하면 될 것 같은데도 이렇게 복잡한 과정을 거쳐야 한다니.. 증말로 어렵구만 🤔 그래도 이렇게 하나하나 해결해 나아가고 느리더라도 천천히 이유를 이해하는 과정이 짜릿한 법이니까~