🚨문제
: 로그인이 되어 있고 구독한 인플루언서가 있는데 렌더링 시 "로그인 정보가 없습니다."와 "구독한 인플루언서 정보가 없습니다." 안내문 모두 렌더링이 되고 마지막에 실제 정보가 뜸
❓고민
1. 데이터를 fetch로 가져올 때 useQuery를 쓰면 데이터를 더 빨리 가져오지 않을까 싶어 바꿔봤으나 안됨.
2. return문의 삼항연산자 로직 순서를 바꾸면 렌더링 순서도 바뀔까 싶었지만 안됨.
✔️해결
먼저 오류 코드를 보면
useEffect(() => {
const fetchData = async () => {
if (user) {
await getSubscribeData();
}
setIsLoading(false);
};
fetchData();
}, [user]);
로딩 상태 자리를 잘 못 썼던게 문제였다.
로그인정보 (user)가 있으면 구독 정보를 가져오고 바로 로딩 상태를 false로 줘야하는데 if문 바깥에 둬서 생긴 오류였다.
아래는 수정한 코드이다.
//Allinfluencer.tsx
"use client";
//생략
function AllInfluencers() {
const { data: user } = useUserData();
const [subscribeIds, setSubscribeIds] = useState<string[]>([]);
const [isLoading, setIsLoading] = useState(true);
const router = useRouter();
useEffect(() => {
const fetchData = async () => {
if (user) {
await getSubscribeData();
setIsLoading(false);
}
};
fetchData();
}, [user]);
const getSubscribeData = async () => {
try {
const response = await fetch(`/api/subscribe?user_id=${user.id}`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
const otherData = data.map((d: InfSubscribe) => d.infuser_id);
setSubscribeIds(otherData.filter((d: InfSubscribe) => d !== user.id));
} catch (error) {
console.log("Failed to fetch subscription data:", error);
}
};
//생략
if (isLoading) {
return <LoadingUrr />;
}
return (
<div className="w-full xl:w-[1200px] bg-[#F4F4F4] mx-auto">
<InfGuidModal />
<div className="w-full h-[30%] p-4 bg-[#FFFFFE]">
<h1 className="font-bold text-lg">내가 구독중인 인플루언서</h1>
{!user ? (
<div className="flex h-[200px]">
<p className="text-[#4C4F52] text-[16px] my-5 xl:text-[18px] xl:mb-[52px] mx-auto mt-[80px]">
로그인 정보가 없습니다.
</p>
</div>
) : subscribeIds.length === 0 ? (
<div className="flex flex-col items-center mx-auto">
<div className="relative w-[150px] h-[100px] my-3 xl:my-[26px]">
<Image src={emptyImg} alt="empty" fill sizes="100px xl:w-[150px]" className="mx-auto my-5 object-cover" />
</div>
<p className="text-[#4C4F52] text-[16px] my-6 xl:text-[18px]">현재 구독중인 인플루언서가 없습니다.</p>
</div>
) : (
<div className="w-auto flex overflow-x-auto mt-5 gap-3 scrollbar-hide p-2">
{infUser
?.filter((inf) => subscribeIds.includes(inf.id))
.map((inf) => (
<div className="grid text-center" key={inf.id}>
<div className="relative w-[90px] h-[90px] mb-2 xl:w-[140px] xl:h-[140px] xl:mt-[10px]">
<Link href={`influencer/profile/${inf.id}`}>
<div className="relative w-[90px] h-[90px] xl:w-[140px] xl:h-[140px]">
<Image
src={inf.profile_url || defaultImg}
alt="img"
fill
sizes="90px xl:w-[140px]"
className="rounded-md object-cover gradient-border"
/>
</div>
</Link>
<div className="absolute bottom-0.5 right-1">
{subscribeIds.includes(inf.id) ? (
<button onClick={() => cancelSubscribeHandler(inf)}>
<FullHeartIcon />
</button>
) : (
<button onClick={(e) => subscribeHandler(e, inf)}>
<EmptyHeartIcon />
</button>
)}
</div>
</div>
<p className="text-[14px] mb-2 xl:text-[16px]">{inf.nickname}</p>
</div>
))}
</div>
)}
</div>
//생략
</div>
);
}
export default AllInfluencers;
똑같은 문제로 메인페이지에서 구독한 정보를 보여주는 부분이 있었다. 같은 방법으로 해결해보자.
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
if (user) {
getSubscribeData();
setIsLoading(false);
}
return;
}, [user]);
원래 있던 코드에 로딩 상태를 관리 할 useState를 작성하고 초기값 true로 준 뒤 구독 데이터를 가져오는 함수 바로 아래에 로딩 상태를 넣었다. 그런데 똑같이 했음에도 안되는 것이다. 이전 컴포넌트와 비교했을 때 async/await의 유무 차이였다.
async/await가 없는 로직에서는 getSubcribeData() 함수가 전부 실행되기도 전에 setIsLoading이 실행이 돼서 로딩처리가 제대로 안된 것이었다.
async/await가 있으면 getSubscribeData()함수가 실행된 후 이후 로직이 실행되므로 로딩처리가 해결된다.
아래는 수정코드이다.
//SubInfluencer.tsx
"use client";
//생략
function SubInfluencer({ infUser }: { infUser: User[] }) {
const { data: user } = useUserData();
const [subscribeIds, setSubscribeIds] = useState<string[]>([]);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
const fetchData = async () => {
if (user) {
await getSubscribeData();
setIsLoading(false);
}
};
fetchData();
}, [user]);
const getSubscribeData = async () => {
try {
if (!user) return;
const response = await fetch(`/api/subscribe?user_id=${user.id}`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
setSubscribeIds(data.map((d: InfSubscribe) => d.infuser_id));
} catch (error) {
console.log("Failed to fetch subscription data:", error);
}
};
if(isLoading) {
return <p>로딩 중입니다.</p>;
}
return (
<div className="w-full h-[220px] xl:h-auto px-4 py-[26px]">
<h2 className="font-bold text-xl mb-5 xl:text-[22px] xl:my-[26px]">내가 구독한 인플루언서</h2>
<div className="flex flex-row overflow-x-auto flex-nowrap scrollbar">
{!user ? (
<p className="text-[#4C4F52] text-[16px] h-[142px] mx-auto flex items-center whitespace-nowrap">
로그인 정보가 없습니다.
</p>
) : subscribeIds.length === 0 ? (
<p className="text-[#4C4F52] text-[16px] h-[142px] mx-auto flex items-center whitespace-nowrap">
구독중인 인플루언서가 없습니다.
</p>
) : (
<div className="w-full flex overflow-x-auto gap-[18px] xl:gap-5 scrollbar-hide">
{infUser
?.filter((inf) => subscribeIds.includes(inf.id) && inf.id !== user?.id)
.map((inf) => (
<div
key={inf.id}
className="flex-shrink-0 flex flex-col items-center justify-center text-center w-[85px] xl:w-[150px]"
>
<Link href={`influencer/profile/${inf.id}`}>
<div className="relative w-[85px] h-[85px] xl:w-[150px] xl:h-[150px] mb-2">
<Image
src={inf.profile_url || defaultImg}
alt="img"
fill
sizes="85px xl:150px"
className="rounded-md object-cover gradient-border"
/>
<div className="absolute bottom-0.5 right-1.5">
{subscribeIds.includes(inf.id) ? (
<button>
<FullHeartIcon />
</button>
) : (
<button>
<EmptyHeartIcon />
</button>
)}
</div>
</div>
<p className="text-sm text-[#4C4F52] mb-6 truncate xl:text-[16px]">{inf.nickname}</p>
</Link>
</div>
))}
</div>
)}
</div>
</div>
);
}
export default SubInfluencer;
'Next.js' 카테고리의 다른 글
[TIL] next.js Image 태그로 배경 이미지 적용하기 (0) | 2024.07.31 |
---|