[Tistory] [Next 고캠핑] 파이어베이스 좋아요 기능 추가

원글 페이지 : 바로가기

캠핑장 검색화면/ 내 캠핑장/ 캠핑장 상세 좋아요… 좋아요 기능을 추가해야한다… 어떻게 해야하는지 엄청 찾아봤지만 기본 로컬데이터를 이용해서 관리하기는 어렵고 파이어베이스를 이용한김에 데이터 관리도 로컬이 아닌 파이어베이스로 변경했다. https://hhyj0000.tistory.com/184 [Next 고캠핑] 로컬 데이터 파이어베이스로 변경하기 캠핑장 좋아요 기능을 추가하기 위해서 로컬로 데이터를 불러왔던것을 firebase로 바꿨다.오른쪽 상단 더보기를 눌러서 json 가져오기를 누르면 내 데이터가 잘 들어와진 것을 확인할 수 있다. hhyj0000.tistory.com likeList 컬렉션을 만들었다. 여기에 userId를 추가해서 유저의 좋아요 상태 관리를 할 것. https://firebase.google.com/docs/firestore/query-data/get-data?hl=ko Cloud Firestore로 데이터 가져오기 | Firebase 의견 보내기 Cloud Firestore로 데이터 가져오기 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 세 가지 방법으로 Cloud Firestore에 저장된 데이터를 검색할 수 있 firebase.google.com const likeListItem = collection(db, “likeList”); – firestore 데이터베이스에 있는 likeList 컬렉션을 참조한다. * 파이어베이스 데이터 추가 // 좋아요 추가
export const addLike = async (campingItem: ICampingList, userId: string) => {
try {
await addDoc(likeListItem, {
userId,
campingItem: {
facltNm: campingItem.facltNm,
lineIntro: campingItem.lineIntro,
intro: campingItem.intro,
addr1: campingItem.addr1,
firstImageUrl: campingItem.firstImageUrl,
themaEnvrnCl: campingItem.themaEnvrnCl,
tel: campingItem.tel,
contentId: campingItem.contentId,
lctCl: campingItem.lctCl,
induty: campingItem.induty,
doNm: campingItem.doNm,
sigunguNm: campingItem.sigunguNm,
direction: campingItem.direction,
brazierCl: campingItem.brazierCl,
sbrsCl: campingItem.sbrsCl,
sbrsEtc: campingItem.sbrsEtc,
homepage: campingItem.homepage,
animalCmgCl: campingItem.animalCmgCl,
tooltip: campingItem.tooltip,
mapX: campingItem.mapX,
mapY: campingItem.mapY,
},
});
} catch (error) {
console.log(error);
}
}; – addDoc() likeList에 추가할 데이터를 적는다. 해당 유저의 데이터만 보여줘야하니까 비교할 수 있게 userId 그리고 들어갈 캠핑 데이터 * 파이어베이스 데이터 삭제 // 좋아요 삭제
export const removeLike = async (docId: string) => {
try {
await deleteDoc(doc(db, “likeList”, docId));
} catch (error) {
console.log(error);
}
}; 공식 문서에 나와있는 삭제 방법 await deleteDoc(doc(db, “cities”, “DC”)); – 아주 간단! deleteDoc(doc(데이터베이스, “컬렉션 이름”, 문서id)); – docId를 매개변수로 불러주고 사용할 때 docId를 불러와서 넣어줄 것이다. * 파이어베이스 데이터 가져오기 // 좋아요 리스트
export const getLikeList = async (userId: string) => {
try {
const q = query(likeListItem, where(“userId”, “==”, userId));
const snapshot = await getDocs(q);
return snapshot.docs.map((doc) => doc.data().campingItem);
} catch (error) {
console.log(error);
return [];
}
}; – query()로그인 한 유저와 데이터베이스의 userId를 비교해서 해당유저의 데이터만 가져온다. – getDoc() 쿼리의 결과에 일치하는 문서들을 가져온다. – 해당 문서의 campingItem 데이터를 추출하여 배열로 반환한다. – 좋아요 한 캠핑장 리스트를 확인하고 리스트 안에서는 좋아요를 취소해도 새로고침 전까지는 유지하고 싶었기때문에 getDocs()를 사용했다. 실시간으로 가져오려면 onSnapshot()을 이용해야한다. * 파이어베이스 데이터 실시간으로 가져오기 // 좋아요 상태
export const likeState = (
userId: string,
likeUpdate: (likeItems: Array<{ contentId: string; docId: string }>) => void,
): (() => void) | undefined => {
if (!userId) return;
try {
const q = query(likeListItem, where(“userId”, “==”, userId));
// 실시간 조회로 바꿈
const unsubscribe = onSnapshot(q, (snapshot) => {
const updatedLike =
snapshot.docs.map((doc) => ({
contentId: doc.data().campingItem.contentId as string,
docId: doc.id, // Firestore 문서 ID
})) || [];
likeUpdate(updatedLike);
});
return unsubscribe;
} catch (error) {
console.log(error);
}
}; – 좋아요 상태를 확인하는 코드는 실시간으로 업데이트되어야하니까 (하트 누르기) 실시간으로 업데이트를 해줬다. – 데이터 삭제를 위해 문서 id와 좋아요를 누른 캠핑장을 구분하기 위해 캠핑장의 contentId를 넘겨줬다. 사용하기 더보기 export default function LikeBtn({
className,
onClick,
campingItem,
like,
docId,
}: LikeBtnProps) {
const { user } = useAuth();

const onClickLike = async (e: React.MouseEvent) => {
e.stopPropagation();
if (!user) {
onClick(e);
} else {
if (!like) {
void addLike(campingItem, user.uid);
} else {
void removeLike(docId);
}
}
}; – 로그인을 한 상태가 아닐때는 로그인 페이지로 이동시켜야하기 때문에 onClick이벤트를 props로 넘겨줬다. interface IPropsCampingList {
list: ICampingList[];
className?: string;
onClick: (item: ICampingList) => void;
}

export default function CampingCard({
list,
className,
onClick,
}: IPropsCampingList) {
const [likeList, setLikeList] = useState< Array<{ contentId: string; docId: string }>
>([]);
const { currentModal, openModal } = useModal();
const router = useRouter();
const { user } = useAuth();

const isMounted = useRef(true);

useEffect(() => {
isMounted.current = true;
if (user) {
// 좋아요 상태 불러오기
const unsubscribe = likeState(user.uid, (updatedLikes) => {
setLikeList(updatedLikes);
});

// 컴포넌트 언마운트 시 구독 해제
return () => {
isMounted.current = false;
if (unsubscribe) {
unsubscribe();
}
};
}
}, []);

const onClickLike = (e: React.MouseEvent) => {
if (!user) {
openModal(“likeAlert”);
}
};

const closeModal = () => {
router.back();
setTimeout(() => {
void router.push(“/login”);
}, 100);
};

return (
<>
{list.map((item: ICampingList) => {
// 입지 구분 아이콘 리스트
const icons = item.lctCl ? item.lctCl.split(“,”) : [];
const iconList = icons
.slice(0, 3)
.concat(
Array(3 – icons.length > 0 ? 3 – icons.length : 0).fill(“없음”),
);

// 좋아요 리스트에서 contentId가 캠핑장 contentId하고 같으면 문서의 id를 반환
const key =
likeList.find((like) => like.contentId === item.contentId)?.docId ??
item.contentId;
// 좋아요 리스트의 contetnId와 캠핑장 contetnId가 같으면 true 반환
const isLiked = likeList.some(
(like) => like.contentId === item.contentId,
);
return (
{
onClick(item);
}}
>


…… – 좋아요 상태를 불러오는데 Can’t perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function. 오류가 나왔다. 찾아보니 컴포넌트가 언마운트된 상태에서 상태 업데이트를 시도할 때 발생한다고 한다. https://stackoverflow.com/questions/56442582/react-hooks-cant-perform-a-react-state-update-on-an-unmounted-component React-hooks. Can’t perform a React state update on an unmounted component I get this error: Can’t perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and stackoverflow.com – 해결하는 가장 쉬운 방법은 마운트 되었는지 상태를 추적하는 것. useRef를 사용해서 isMounted 변수를 선언했고 컴포넌트의 마운트 상태를 추적한다. ++ 내 캠핑장 페이지네이션을 추가해야한다. +++ 캠핑 후기게시판 만들기… https://nomadcoders.co/nwitter/lectures/4527 https://stackoverflow.com/questions/56442582/react-hooks-cant-perform-a-react-state-update-on-an-unmounted-component https://velog.io/@cjw020607/Firebase-Cloud-Firestore-Storage-%EC%95%88%EC%9D%98-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%82%AD%EC%A0%9C%ED%95%98%EA%B8%B0-deleteDoc-deleteObject https://velog.io/@phjjj/%ED%8C%8C%EC%9D%B4%EC%96%B4%EB%B2%A0%EC%9D%B4%EC%8A%A4%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%98%EC%97%AC-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%B6%88%EB%9F%AC%EC%98%A4%EA%B8%B0

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다