์ด์ ํ๋กํ ํ๋ฉด์ ๋ฏ์ด ๊ณ ์ณ๋ณด์!
ํ๋กํ ํ๋ฉด์ ๊ตฌ์ฑํ ๋ด์ฉ์ ๋ค์๊ณผ ๊ฐ๋ค.
๋ก๊ทธ์์ ๊ธฐ๋ฅ์ ์ด๋ฏธ ํด ๋๊ณ , ๋ก๊ทธ์ธํ ์ฌ์ฉ์์ ํธ์์ ๋ณผ ์ ์๋๋ก ํด๋ณด์!
๋๊ตฌ์ ํ๋กํ์ธ์ง ์์์ผ ๊ทธ ์ ์ ์ ํธ์๋ง ๊ฐ์ ธ์ฌ ์ ์๋ค.
Router.js์์ Profile.js๋ก `prop์ผ๋ก userObj๋ฅผ ๋ณด๋ด์ฃผ๋ฉด ์ด ํ๋กํ์ ์ ์ ๊ฐ ๋๊ตฌ์ธ์ง ์ ์ ์๋ค.
//...
<Route
path="/profile"
element={<Profile userObj={userObj} />}
></Route>
//...
import { dbService } from "fbase";
import { useNavigate } from "react-router-dom";
//๐ฅ ๋ก๊ทธ์ธํ ์ ์ ์ ๋ณด prop์ผ๋ก ๋ฐ๊ธฐ
const Profile = ({ userObj }) => {
const navigate = useNavigate();
const onLogOutClick = () => {
authService.signOut();
//home์ผ๋ก ๋์๊ฐ๊ธฐ ์ํด react router dom์ useNavigate() ๋ฉ์๋ ์ฌ์ฉ
navigate("/");
};
return (
<>
<button onClick={onLogOutClick}>๋ก๊ทธ์์</button>
</>
);
export default Profile;
- useEffect( fn, [props.source] ): ์กฐ๊ฑด๋ถ effect ๋ฐ์
effect์ ๊ธฐ๋ณธ ๋์์ ๋ชจ๋ ๋ ๋๋ง์ ์๋ฃํ ํ effect๋ฅผ ๋ฐ์ํ๊ฒ ํ์ง๋ง, useEffect์ ๋ ๋ฒ์งธ ์ธ์๋ฅผ ์ ๋ฌํ๋ฉด props.source๊ฐ ๋ณ๊ฒฝ๋ ๋์๋ง ๊ตฌ๋ ์ด ์ฌ์์ฑ๋๋ค.useEffect( () => { const subscription = props.source.subscribe(); return () => { subscription.unsubscribe(); }; }, [props.source], );
import { authService } from "fbase";
import { useEffect } from "react";
import <{ useNavigate } from "react-router-dom";
//๋ก๊ทธ์ธํ ์ ์ ์ ๋ณด prop์ผ๋ก ๋ฐ๊ธฐ
const Profile = ({ userObj }) => {
const navigate = useNavigate();
const onLogOutClick = () => {
authService.signOut();
//home์ผ๋ก ๋์๊ฐ๊ธฐ ์ํด react router dom์ useNavigate() ๋ฉ์๋ ์ฌ์ฉ
navigate("/");
};
//๐ฅ ๋ด Tweets ์ป๋ function ํธ์ถ
useEffect(() => {
getMyTweets();
}, []);
return (
<>
<button onClick={onLogOutClick}>๋ก๊ทธ์์</button>
</>
);
};
export default Profile;
getMyTweets ํจ์๋ฅผ ์์ฑํ๊ณ ํจ์ ์์ ๋ด Tweets์ ์ป๋ ์ฝ๋๋ฅผ ์์ฑํ์.
์ฐธ๊ณ : ์ปฌ๋ ์
์์ ์ฌ๋ฌ ๋ฌธ์ ๊ฐ์ ธ์ค๊ธฐ
import { collection, query, where, getDocs } from "firebase/firestore";
ํ์ฌ ๋๋น์๋น์ค ์์ ์๋ ์ปฌ๋ ์ ์์ "tweets"๋ฌธ์๋ฅผ ์ฟผ๋ฆฌํด ์์ผ ํ๋ค. ๊ทธ์ค์์๋ ํน๋ณํ userObj.uid์ ๋์ผํ ๊ฐ์ ๊ฐ์ง creatorId๋ฅผ ๊ฐ์ง๊ณ ์๋ ๋ฌธ์๋ง ๊ฐ์ ธ์์ผ ํ๋ค. ๊ทธ๋์ผ ์๊ธฐ ํธ์๋ง ๊ฐ์ ธ์ฌ ์ ์๊ธฐ ๋๋ฌธ์ด๋ค.
๊ทธ๋ฌ๊ธฐ ์ํด where() ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ฉด ๋๋๋ฐ, ์ด ๋ฉ์๋๋ ํน์ ์กฐ๊ฑด์ ์ถฉ์กฑํ๋ ๋ชจ๋ ๋ฌธ์๋ฅผ ์ฟผ๋ฆฌ(์์ฒญ)ํ ์ ์๋ค.
- where(filePath, opStr, value)
- filePath: string | firebase.firestore.FiledPath
- opStr("์คํผ๋ ์ด์ ์คํธ๋ง" ์ฆ, ์ฐ์ฐ์) | firebase.firestore.WhereFilterOp
- value(๊ฐ): any
๋ฐ๋ผ์ ๋ค์๊ณผ ๊ฐ์ด ์ฟผ๋ฆฌ๋ฌธ์ ์์ฑํ๋ฉด ๋๋ค.
const q = query(
collection(dbService, "tweets"),
where("creatorId", "==", userObj.uid)
);
where()๋ฅผ ์ฌ์ฉํ์ฌ ํน์ ์กฐ๊ฑด์ ์ถฉ์กฑํ๋ ๋ชจ๋ ๋ฌธ์๋ฅผ ์ฟผ๋ฆฌํ ํ, getDocs() ๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ ๊ฒฐ๊ณผ๋ฅผ ๊ฐ์ ธ์ฌ ์ ์๋ค.
์์
const querySnapshot = await getDocs(q); querySnapshot.forEach((doc) => { // doc.data() is never undefined for query doc snapshots console.log(doc.id, " => ", doc.data()); });
๋ฐ๋ผ์ ๋ค์๊ณผ ๊ฐ์ด ์์ฑํ๋ฉด ๋๋ค.
import { authService, dbService } from "fbase";
import { collection, getDocs, query, where } from "firebase/firestore";
import { useEffect } from "react";
import { useNavigate } from "react-router-dom";
//๋ก๊ทธ์ธํ ์ ์ ์ ๋ณด prop์ผ๋ก ๋ฐ๊ธฐ
const Profile = ({ userObj }) => {
const navigate = useNavigate();
const onLogOutClick = () => {
authService.signOut();
//home์ผ๋ก ๋์๊ฐ๊ธฐ ์ํด react router dom์ useNavigate() ๋ฉ์๋ ์ฌ์ฉ
navigate("/");
};
//๋ด Tweets ์ป๋ function ์์ฑ
const getMyTweets = async () => {
//ํธ์ ๋ถ๋ฌ์ค๊ธฐ
//dbService์ ์ปฌ๋ ์
์ค "tweets" Docs์์ userObj์ uid์ ๋์ผํ creatorID๋ฅผ ๊ฐ์ง ๋ชจ๋ ๋ฌธ์๋ฅผ ๊ฐ์ ธ์ค๋ ์ฟผ๋ฆฌ(์์ฒญ) ์์ฑ
const q = query(
collection(dbService, "tweets"),
where("creatorId", "==", userObj.uid)
);
//๐ฅgetDocs()๋ฉ์๋๋ก ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ ๊ฐ ๊ฐ์ ธ์ค๊ธฐ
const querySnapshot = await getDocs(q);
querySnapshot.forEach((doc) => {
console.log(doc.id, "=>", doc.data());
});
};
//๋ด Tweets ์ป๋ function ํธ์ถ
useEffect(() => {
getMyTweets();
}, []);
return (
<>
<button onClick={onLogOutClick}>๋ก๊ทธ์์</button>
</>
);
};
export default Profile;

์ด๋ ๊ฒ ๋ฐ์ดํฐ๋ฅผ ํํฐ๋งํด์ ๊ฐ์ ธ์ฌ ์ ์๋ค.
- orderBy()
์์import { query, orderBy } from "firebase/firestore"; //์ฌ๋ฌ ํ๋๋ฅผ ๊ธฐ์ค์ผ๋ก ์ ๋ ฌํ ์๋ ์๋ค. //์๋ฅผ ๋ค์ด ์ฃผ๋ฅผ ๊ธฐ์ค์ผ๋ก ์ ๋ ฌํ ํ ๊ฐ ์ฃผ ์์์ ์ธ๊ตฌ์ ๋ฐ๋ผ ๋ด๋ฆผ์ฐจ์์ผ๋ก ์ ๋ ฌํ๋ ๋ฐฉ๋ฒ์ ๋ค์๊ณผ ๊ฐ๋ค. const q = query(citiesRef, orderBy("state"), orderBy("population", "desc"));
import { authService, dbService } from "fbase";
//๐ฅ orderBy ๊ฐ์ ธ์ค๊ธฐ
import { collection, getDocs, query, where, orderBy } from "firebase/firestore";
import { useEffect } from "react";
import { useNavigate } from "react-router-dom";
//๋ก๊ทธ์ธํ ์ ์ ์ ๋ณด prop์ผ๋ก ๋ฐ๊ธฐ
const Profile = ({ userObj }) => {
const navigate = useNavigate();
const onLogOutClick = () => {
authService.signOut();
//home์ผ๋ก ๋์๊ฐ๊ธฐ ์ํด react router dom์ useNavigate() ๋ฉ์๋ ์ฌ์ฉ
navigate("/");
};
//๋ด Tweets ์ป๋ function ์์ฑ
const getMyTweets = async () => {
//ํธ์ ๋ถ๋ฌ์ค๊ธฐ
//dbService์ ์ปฌ๋ ์
์ค "tweets" Docs์์ userObj์ uid์ ๋์ผํ creatorID๋ฅผ ๊ฐ์ง ๋ชจ๋ ๋ฌธ์๋ฅผ ๊ฐ์ ธ์ค๋ ์ฟผ๋ฆฌ(์์ฒญ) ์์ฑ
//๐ฅ ํธ์ํ ์์๋๋ก ์ ๋ ฌํ๊ธฐ
const q = query(
collection(dbService, "tweets"),
where("creatorId", "==", userObj.uid),
orderBy("createdAt", "desc")
);
//getDocs()๋ฉ์๋๋ก ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ ๊ฐ ๊ฐ์ ธ์ค๊ธฐ
const querySnapshot = await getDocs(q);
querySnapshot.forEach((doc) => {
console.log(doc.id, "=>", doc.data());
});
};
//๋ด Tweets ์ป๋ function ํธ์ถ
useEffect(() => {
getMyTweets();
}, []);
return (
<>
<button onClick={onLogOutClick}>๋ก๊ทธ์์</button>
</>
);
};
export default Profile;
โ ๏ธ ์๋ฌ ๋ฐ์
Uncaught (in promise) FirebaseError: The query requires an index. You can create it here: url ๋ธ๋ผ๋ธ๋ผ
Firestore๋ noSQL ๊ธฐ๋ฐ DB๋ผ์ ๋ช๋ช ๊ธฐ๋ฅ์ ์ด๋ ๊ฒ ์๋๋ ์๊ฐ ์๊ธฐ ๋๋ฌธ์ ์ด๋ฐ ์๋ฌ๊ฐ ๋ฌ๋ค๊ณ ํ๋ค.
ํด๋น ์ฟผ๋ฆฌ์ index๊ฐ ํ์ํ๋ค๊ณ ํ๋๋ฐ ์ด๋ pre-made query๋ฅผ ๋ง๋ค์ด์ผ ํ๋ค๋ ๋ป์ด๋ค.
๋ฐ์ดํฐ๋ฒ ์ด์ค๊ฐ ์ฟผ๋ฆฌ๋ฅผ ๋ง๋ค ์ค๋น๋ฅผ ํ ์ ์๋๋ก, ์ด ์ฟผ๋ฆฌ๋ฅผ ์ฌ์ฉํ ๊ฑฐ๋ผ๊ณ ๋ฐ์ดํฐ๋ฒ ์ด์ค์๊ฒ ์๋ ค์ค์ผ ํ๋ค.

๋ณตํฉ์์ธ ๋ง๋ค๊ธฐ ์์ฑ

์ค์ผ์ด! ์ฝ์์ ํ์ธํด ๋ณด์. createdAt ๋ด๋ฆผ์ฐจ์(๊ฐ์ฅ ๋จผ์ ์ด ํธ์์ด ์ ์ผ ์๋๋ก)์ผ๋ก ์ ๋์ค๊ณ ์๋ค.

์ฟผ๋ฆฌ(์์ฒญ) ํํฐ๋งํ๊ธฐ
//โ
v.9
import { authService, dbService } from "fbase";
import { collection, getDocs, query, where, orderBy } from "firebase/firestore";
import { useEffect } from "react";
//...
//1. ๋ก๊ทธ์ธํ ์ ์ ์ ๋ณด prop์ผ๋ก ๋ฐ๊ธฐ
const Profile = ({ userObj }) => {
//...
//2. ๋ด nweets ์ป๋ function ์์ฑ
const getMyNweets = async () => {
//3. ํธ์ ๋ถ๋ฌ์ค๊ธฐ
//3-1. dbService์ ์ปฌ๋ ์
์ค "nweets" Docs์์ userObj์ uid์ ๋์ผํ creatorID๋ฅผ ๊ฐ์ง ๋ชจ๋ ๋ฌธ์๋ฅผ ๋ด๋ฆผ์ฐจ์์ผ๋ก ๊ฐ์ ธ์ค๋ ์ฟผ๋ฆฌ(์์ฒญ) ์์ฑ
const q = query(
collection(dbService, "nweets"),
where("creatorId", "==", userObj.uid),
orderBy("createdAt", "desc")
);
//3-2. getDocs()๋ฉ์๋๋ก ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ ๊ฐ ๊ฐ์ ธ์ค๊ธฐ
const querySnapshot = await getDocs(q);
querySnapshot.forEach((doc) => {
console.log(doc.id, "=>", doc.data());
});
};
//4. ๋ด nweets ์ป๋ function ํธ์ถ
useEffect(() => {
getMyTweets();
}, []);
return (
<>
๋ก๊ทธ์์
</>
);
};
export default Profile;