React Native로 2주동안 당근마켓 앱 클론 '키위 마켓'을 시작했다. 프론트엔드 2명과 백엔드 2명, 총 4명의 팀원이 함께 했다.
내가 맡은 제품 상세 스크린의 경우 다른 API 주소에 총 3번의 요청을 보내야 했다. 처음에는 비동기로 순차적으로 그 함수들을 불렀다. 그러나 AWS 무료 플랜을 사용할 경우 속도가 매우 느리다는 문제가 있었다. 그리고 아래와 같은 로딩 화면을 구현하였다.
React Native에서 제공하는 Animated
와 interpolate
를 사용하여 키위 이미지가 돌아가게 하여 로딩 컴포넌트를 만들었다. 위에 보이는 로딩 화면은 제품 상세의 기본적인 데이터를 받아올 때까지(첫 번째 서버 요청에서 데이터를 받아올 때까지) 보여주는 화면이다. 성공할 경우 아래와 같이 화면내 돌아가는 키위가 나타나게 된다.
const LoadingKiwi = () => {
const [spinAnim, setSpinAnim] = useState(new Animated.Value(0));
const spin = spinAnim.interpolate({
inputRange: [0, 1],
outputRange: ["0deg", "3600deg"],
});
useEffect(() => {
Animated.loop(
Animated.timing(spinAnim, {
toValue: 1,
duration: 10000,
easing: Easing.ease,
useNativeDriver: true,
})
).start();
}, []);
return (
<Animated.Image
source={require("../assets/smallKiwi.png")}
style={{ ...styles.loadingImage, transform: [{ rotate: spin }] }}
/>
);
};
처음에는 순차적으로 서버에 요청을 보내는 코드로 작성하였으나, Promise.all()
을 사용하여 로딩속도를 개선하고자 했다. 무료 AWS는 너무 느렸기에 실제로 로딩속도가 개선되는 것이 확연하게 느껴졌다!
그러나 비동기 코드를 작성하는 과정에서 문제가 발생했다. 상세페이지의 경우 나는 SectionList
를 사용했고, SectionList
에 들어가는 섹션 데이터는 아래와 같았다.
const [section, setSection] = useState([
{
title: ["판매자님의 판매 상품", "더보기"],
data: ["loading"],
},
{
title: [`이건 어때요?`, ""],
data: ["loading"],
},
]);
그리고 위 state를 변경하는 비동기 함수가 Promise.all
로 불려지게 되는 과정에서 setSection
이 원하는 대로 적용되지 않았다는 점이다. 아래는 문제의 코드이다.
useEffect(() => {
const itemDetailData = getItemDetailData();
const sellerData = getSellerData();
const recommendData = getRecommendItems();
Promise.all([itemDetailData, sellerData, recommendData]);
}, []);
// code...
const getSellerData = async () => {
// code...
setSection(
[
{
...section[0],
data: 판매자의 다른 상품 데이터
},
section[1]
]
)
// code..
}
const getRecommendItems = async () => {
// code ...
setSection(
[
section[0],
{
...section[1]
data: 추천 판매 상품 데이터
}
]
)
// code ...
}
발생한 문제의 순서는 아래와 같다.
getSellerData
가 먼저 성공한다.getRecommendItems
가 성공한다.해결책부터 제시하자면, setSection
의 코드만 아래와 같이 수정하여 문제를 해결하였다.
// getSellerData 내부
setSection((prevSection) => [
{
...prevSection[0],
data: smallCardViewArrayGenerator(result.sellerItemsData),
},
prevSection[1],
]);
// getRecommendItems 내부
setSection((prevSection) => [
prevSection[0],
{
...prevSection[1],
data: smallCardViewArrayGenerator(result.productList),
},
]);
코드만 보면 금방 해결되는 단순한 문제처럼 보인다. 하지만 개인적으로 스스로 이 문제를 해결하는데 꽤 힘이 들었고, 결국 질문을 해서 도움을 받아 해결하게 되었다. 그리고 내가 왜 이 문제를 쉽게 해결하지 못했는지를 스스로 진단해보았다. 나는 내가 훅과 자바스크립트 클로저에 대해서 좀 더 깊게 이해해야 한다라고 결론을 내렸다. 관련해서 참고 자료 링크를 남긴다.