파이어베이스에서 데이터 경로
최상위 컬렉션> 최상위 도큐멘트> 상위 컬렉션> 상위 도큐멘트
여기에 하위 컬렉션을 추가 해서 좋아요 관련 데이터를 관리 하려고 함
Page컴포넌트
import
import { useSelector, useDispatch } from "react-redux";
import userPageLike from "../../redux/thunks/boardPageLikeThunks";
return
const handleGoodClick = async () => {
await dispatch(userPageLike({ like: true }) as any);
};
return(
..코드생략
<div>
<button onClick={handleGoodClick}>좋아요</button>
</div>
);
Thunk userPageLike
interface LikeType {
like: boolean;
}
const userPageLike = createAsyncThunk(
"userPage/likeClick",
async (data: LikeType, thunkAPI) => {
try {
//사용자uid 정보 가져오기
const auth = getAuth();
const currentUser = auth.currentUser;
const userId = currentUser?.uid;
//최상위 컬렉션
const userCollection = collection(firestore, "users");
//최상위 도큐멘트
const userDocRef = doc(userCollection, "userData");
//최상위 컬렉션> 최상위도큐멘트>userId이름을 가진 하위 컬렉션
const subCollectionRef = collection(userDocRef, userId as string);
//하위 컬렌션이 가진 모든 하위 도큐멘트 출력 getDocs사용
const specificDoc = await getDocs(subCollectionRef);
//하위 도큐멘트 리스트
for (const [index, docI] of specificDoc.docs.entries()) {
const data = docI.data(); //하위 도큐멘트 데이터
console.log(` ${data}`);
//하위 도큐멘트
const docRef = doc(subCollectionRef, dataIndex);
//하위 컬렉션
const lastCallectionRef = collection(docRef, "like");
//데이터 추가
//await addDoc(lastCallectionRef, {
//like: true,
// numClick: 3,
// });
}
//반환 하는 값
const responseDataLike: LikeType = {
like: true,
};
return responseDataLike;
} catch (error) {
// 에러 처리
console.error("Error in userPageLike thunk:", error);
throw error;
}
}
);
export default userPageLike;
uid 이름으로 되어 있는 하위 컬렉션의 모든 하위 도큐멘트 가져 오도록 설정
문제점
어떤 페이지로 가도 모든 하위 도큐멘트들이 다 출력 됨
조건문을 주고 싶었는데 비교 할 대상이 없어서 계속 모든게 출력 됨☹
해결 방안을 생각 해봄
페이지의 id가 각 페이지 별로 있고 유일함 문젠 상태관리에 저장되어 있음 Thnuk에서 상태관리에 저장 되어 있는 페이지의id를 불러 올 수 있가 있나?
thunkAPI 활용
return
const state: any = thunkAPI.getState();
const boardSelectedId = state.board.selectedBoardId;
thunkAPI.getState(); 모든 상태들을 다 가져온다
가져온 상태에서 내가 가져오고 싶은 상태를 지정 한다
사용자가 방문한 페이지의 id 와 하위 도큐멘트 데이터의 id를 비교 하여
일치하는 하위 도큐멘트에 하위 컬렉션 추가 되어 데이터가 추가 되는 것이 확인 됨
문제점
a라는 사용자의 페이지에 b라는 사용자가 좋아요를 클릭 하면 데이터가 추가가 안되고 a자기 자신의 페이지에 좋아요 데이터가 추가가 됨
현재 로그인한 사용자의 컬렉션 내부의 데이터만을 대상으로 좋아요 추가됨
데이터의 경로를 userUid로 했던 것이 문제 였던거 같다
코드 수정
Thunk userPageLike
const strId = data.boardId.toString();
const userCollection = collection(firestore, "users");
const userDocRef = doc(userCollection, "userData");
const subCollectionRef = collection(userDocRef, strId);
for (const [index, docI] of specificDoc.docs.entries()) {
const data = docI.data();
console.log(` ${data}`);
const dataId = data.did;
const dataIndex = data.index;
//하위 도큐멘트
const docRef = doc(subCollectionRef, dataIndex);
//하위 컬렉션
const lastCallectionRef = collection(docRef, "like");
if (boardSelectedId === dataId) {
await addDoc(lastCallectionRef, {
like: true,
});
} else {
console.log("틀렸어요");
}
}
컬렉션 명은 string만 가능 해서 string으로 변경 함
하위 컬렉션 명을 boardId로 변경 함
해결 방안
b사용자도 a사용자 페이지에 좋아요 표시를 할 수 있도록
데이터 경로 수정해서 가능 하도록 함 하위 like 컬렉션 추가 되는 것 확인
문제 사항
사용자들의 중복을 막으려 했으나 어떻게 해야할지 고민..
Firebase Firestore에서 고유한 값(unique value)을 가진 필드를 직접적으로 제어하는 내장 기능은 제공되지 않는 다고 함
현재까지의 상황
자기 자신의 글 좋아요 클릭 불가 다른 사용 자들은 좋아요 클릭 가능
이제 좋아요 클릭한 수 계산해서 노출 하면 될듯
Page컴포넌트
const handleGoodClick = async () => {
await dispatch(
userPageLike({
boardId: boardSelectedId,
boardPage: liked,
}) as any
);
};
userPageLike가 받는 데이터 형식
boardPageLikedSlice
interface LikeType {
like: boolean;
userUid: string;
}
interface MyStateLike {
boardPage: Record<number, LikeType[]>;
}
const initialState: MyStateLike = {
boardPage: {},
};
const boardPageLikedSlice = createSlice({
name: "pageLikes",
initialState,
reducers: {},
extraReducers: (builder) => {
builder
.addCase(userPageLike.pending, (state, action) => {
console.log(`비동기 좋아요 요청 중`);
})
.addCase(userPageLike.fulfilled, (state, action) => {
console.log(`비동기 좋아요 요청 성공`);
const { boardId, boardPage: pageLike } = action.payload;
})
.addCase(userPageLike.rejected, (state, action) => {
console.log(`비동기 좋아요 요청 실패`);
});
},
});
export default boardPageLikedSlice.reducer;
Record<number, LikeType[]>: 이 부분은 boardPage가 숫자 형태의 키와 LikeType 배열을 값으로 가지는 객체임을 나타낸다
Thunk userPageLike
interface LikeType {
like: boolean;
userUid: string;
}
const userPageLike = createAsyncThunk<
{ boardId: number; boardPage: LikeType },
{ boardId: number; boardPage: LikeType }
>("userPage/likeClick", async (data, thunkAPI) => {
...코드 생략
for (const [index, docI] of specificDoc.docs.entries()) {
const data = docI.data();
const dataId = data.did;
const dataIndex = data.index;
const dataUserId = data.userUid;
//하위 도큐멘트
const docRef = doc(subCollectionRef, dataIndex);
//하위 컬렉션
const lastCallectionRef = collection(docRef, "like");
if (boardSelectedId === dataId && userId !== dataUserId) {
await addDoc(lastCallectionRef, {
like: true,
userUid: userId,
});
} else {
console.log("달라요------------------");
}
}
//반환 값
const responseDataLike = {
boardId: boardSelectedId,
boardPage: {
like: true,
userUid: userId as string,
},
};
return responseDataLike;
}
📢주의 사항
반환할때의 key이름 동일 하게 할것
page 컴포넌트에서 좋아요 수 출력하기
파이어베이스에서 실시간 데이터 출력하기
const dataListPage = async () => {
//컬렉션
const userCollection = collection(firestore, "users");
//도큐멘트
const userDocRef = doc(userCollection, "userData");
//컬렉션>도큐멘트>userId이름을 가진 하위 컬렉션
const subCollectionRef = collection(userDocRef, boardSelectedId.toString());
//하위 컬렌션이 가진 모든 하위 도큐멘트 출력 getDocs사용
const specificDoc = await getDocs(subCollectionRef);
specificDoc.docs.map(async (iData) => {
const data = iData.data();
const dataUserId = data.index;
const docRef = doc(subCollectionRef, dataUserId);
const lastCallectionRef = collection(docRef, "like");
const underSpecificDoc = await getDocs(lastCallectionRef);
const underDbLength = underSpecificDoc.size;
onSnapshot()
setUnderDb(underDbLength);
});
};
사용자별 좋아요 선택/해제, 좋아요 수 출력 완료
아이콘 활용
모든 데이터관리를 상태관리, 파베에도 넣야 하는줄 알았는데 아니었다
상태관리는 전역라이브러리로 모든 페이지에서 사용할 데이터라고 한다 그래서 파이어베이스에만 데이터를 관리 하도록 했다
Page컴포넌트
import userPageLike from "../../redux/thunks/boardPageLikeThunks";
import FavoriteIcon from "@mui/icons-material/Favorite";
import FavoriteBorderIcon from "@mui/icons-material/FavoriteBorder";
import { firebaseApp, firestore } from "../../firebase";
import { collection, getDocs, doc, onSnapshot } from "firebase/firestore";
import userPageLikeOverturn from "../../redux/thunks/boardPageLikeOverturnThunk";
const handleGoodClick = async () => {
//컬렉션
const userCollection = collection(firestore, "users");
//도큐멘트
const userDocRef = doc(userCollection, "userData");
//컬렉션>도큐멘트>userId이름을 가진 하위 컬렉션
const subCollectionRef = collection(userDocRef, boardSelectedId.toString());
//하위 컬렌션이 가진 모든 하위 도큐멘트 출력 getDocs사용
const specificDoc = await getDocs(subCollectionRef);
//userId 담을 빈배열
let userUids: string[] = [];
//하위 컬렉션 아래에 하위 도쿠멘트 가져오기
specificDoc.docs.map(async (iData) => {
const data = iData.data();
const dataUserId = data.index;
const docRef = doc(subCollectionRef, dataUserId);
//가져온 하위 도큐멘트에 하위 컬렉션 추가
const lastCallectionRef = collection(docRef, "like");
const underSpecificDoc = await getDocs(lastCallectionRef);
//작성했던 빈배열에 uidVar가 있는지 확인
if (!userUids.includes(uidVar)) {
userUids.push(uidVar);
const liked = {
like: true,
userUid: uidVar as string,
};
await dispatch(
userPageLike({
boardId: boardSelectedId,
boardPage: liked,
}) as any
);
setLikeClick(true);
// 가져온 하위컬렉션에서 하위 도큐멘트 가져 오기
//로그인한 사용자의 uid가 있으면 데이터 삭제
underSpecificDoc.docs.map(async (und) => {
const underd = und.data();
if (underd.userUid === uidVar) {
underd.like = false;
const liked = {
like: underd.like,
userUid: uidVar as string,
};
await dispatch(
userPageLikeOverturn({
boardId: boardSelectedId,
boardPage: liked,
}) as any
);
setLikeClick(false);
}
});
}
});
};
return(
//likeClick state에 맞게 클래스 이름이 들어 간 것으로 변경됨
<div className="likeBtnBox">
<button className="likeBtn" onClick={handleGoodClick}>
{!likeClick ? (
<div className="unSelectLike">
<FavoriteBorderIcon></FavoriteBorderIcon>
</div>
) : (
<div className="selectLike">
<FavoriteIcon></FavoriteIcon>
</div>
)}
</button>
<span>좋아요 수 {underDb}</span>
</div>)