๋ฆฌ์กํธ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ถ๊ฐํ ๋ค
๊ฒ์์ฐฝ์ ๊ฒ์์ด๋ฅผ ์ ๋ ฅํ๊ณ ์ํฐ๋ฅผ ๋๋ฅด๋ฉด
์์ ํ ํ๋ฉด์ด ๋ฉ์ถฐ๋ฒ๋ฆฌ๋ ์ด์ํ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ค.
์ ๊ทธ๋ฐ์ง ์ฐพ์๋ณด๊ณ ํด๊ฒฐํด๋ณด์.
1. ๋ฌธ์ ์ํฉ

๋ง์ง๋ง์ผ๋ก ํ๋ก์ ํธ๋ฅผ ๊ฒํ ํ๋ฉด์ ์์ํ๊ฒ ์์ ํด์ผํ ๋ถ๋ถ๋ถํฐ ๋ถํธํ๋ ์ , ์์ฌ์ ๋ ๋ถ๋ถ ๋ฑ ์ฌ๋ฌ๊ตฐ๋ฐ ์์ ์ ์งํํ๋ค. ๊ทธ๋ฐ๋ฐ ๊ทธ ๋ค๋ถํฐ ๊ฒ์ ๊ธฐ๋ฅ์ด ๋์ํ์ง ์์๋ค. ์ฌ๊ธฐ์ ๋์ ๋ฌธ์ ์ ์ด ์์๋๋ฐ, ๊ณ ์น ๋๋ง๋ค ๋ชจ๋ ๊ธฐ๋ฅ์ ํ์ธํ์ด์ผํ์ง๋ง ๊ฒ์๋ง ๋นผ๊ณ ํ์ธํ๋ ๋ฐ๋์ ์ด ๋ฌธ์ ๊ฐ ์ธ์ ๋ถํฐ ๋ฐ์ํ๊ฒ ๋์๋์ง ์ ํํ ํ์ ํ ์ ์์๋ค.
F12๋ฅผ ๋๋ฌ์ network๋ฅผ ํตํด ์ด๋ค ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋์ง, ์ ๋จนํต์ด ๋๊ฑด์ง ์๊ณ ์ถ์ด๋ F12์กฐ์ฐจ ๋๋ฆฌ์ง ์์๋ค. ๊ฒ๋ค๊ฐ ์๋ฌ๊ฐ ๋ฐ์ํ์ง๋ ์๊ณ run์ ์ ๋์๊ฐ๊ณ ์์๊ธฐ์ ์ฝ๊ฒ ์ด์ ๋ฅผ ์ฐพ์ ์ ์์๋ค.
2. ๋ฌธ์ ์ ์์ธ ์ฐพ๊ธฐ
- Elasticsearch ํน์ Docker ๋ฌธ์ ?
์ด ํ๋ก์ ํธ์ ๊ฒ์ ๊ธฐ๋ฅ์ด ๋๊ตฐ๋ฐ์ ์กด์ฌํ๋ค. ๊ณ ๊ฐ ํํธ์ ๊ด๋ฆฌ์ ํํธ ์ด ๋๊ตฐ๋ฐ์์ ๊ฒ์์ ํ ์ ์๋๋ก ๋ง๋ค์๋ค. ๊ทธ๋ฆฌ๊ณ ๊ทธ ์ค ๊ณ ๊ฐ ํํธ์๋ง Elasticsearch๋ฅผ ์ถ๊ฐํ๋ค. ๋ง์ฝ Docker๋ Elasticsearch ๋ฌธ์ ๋ผ๋ฉด ๊ด๋ฆฌ์ ํํธ ์ชฝ ๊ฒ์ ๋ถ๋ถ์ ๋ฌธ์ ์์ด ๋์๊ฐ ๊ฒ์ด๋ค. ๊ทธ๋ ๊ฒ ๊ด๋ฆฌ์ ์ชฝ ๊ฒ์ ๊ธฐ๋ฅ์ ํ์ธํด๋ณธ ๊ฒฐ๊ณผ, ๊ฒ์ ๊ธฐ๋ฅ ์์ฒด์ ๋ฌธ์ ๊ฐ ์๊ธด ๊ฒ์ ์๊ฒ๋์๋ค. ๊ด๋ฆฌ์ ํํธ ์ชฝ ๊ฒ์์์๋ ๋จนํต์ด ๋๋ฉฐ ์์ ํ ๋ฉ์ถฐ๋ฒ๋ฆฌ๋ ํ์์ด ๋ฐ์ํ๋ค. - ๋ฐฑ์๋ ๋ฌธ์ ? ํ๋ก ํธ ๋ฌธ์ ?
๋ฐฑ์๋๊ฐ ๋ฌธ์ ์ธ์ง, ํ๋ก ํธ๊ฐ ๋ฌธ์ ์ธ์ง ์์๋ด์ผ ํ๋ค. ์ผ๋จ F12๊ฐ ๋๋ฆฌ์ง ์๋๋ค๋๊ฒ ํฐ ํํธ๊ฐ ๋์๋ค. ์ด๊ฑด ๋ณดํต ํ๋ก ํธ ๋ฌธ์ ์๋ค. ๊ทธ๋ฆฌ๊ณ ์์ ํ๋ ๊ณผ์ ์์ ๋ฐฑ์๋๋ ๊ฑฐ์ ๊ฑด๋๋ฆฌ์ง ์์๊ธฐ ๋๋ฌธ์ ์์ฐ์ค๋ฝ๊ฒ ํ๋ก ํธ ๋ฌธ์ ์ ์ด์ ์ ๋ง์ถ ์ ์๊ฒ ๋์๋ค. - ํ๋ก ํธ ๊ฒ์ ์ฝ๋์ ๊ด๋ จ๋ ์์ ์ฌํญ
๊ฒ์ ์ฝ๋๋ฅผ ๊ฑด๋๋ ธ๋ ๊ฒฝ์ฐ๋ ๋ฑ ๋ ๋ฒ, ๊ทธ ์ค ์ด๋ฐ ๋ฌธ์ ๋ฅผ ๋ฐ์์ํฌ ๋งํ ๊ฑด ํ๋ ๋ฐ์ ์์๋ค. react-query ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ถ๊ฐํ๋ฉด์ [ ์ฆ์ ๊ฒ์ API ํธ์ถ ] ๋ฐฉ์์์ [ ์ํ๊ฐ ๋ณ๊ฒฝ ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ๋ค์ ์กฐํ ] ํ๋ ๋ฐฉ์์ผ๋ก ๋ฐ๊ฟจ์๋ค.
์ฝ๊ฒ ์ค๋ช ํ๋ฉด ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ถ๊ฐ ์ ์๋ [ ๊ฒ์์ด ์ ๋ ฅ -> ์ํฐ ๋ฒํผ -> /search ์ง์ ํธ์ถ -> ์๋ต ๋ฐ์ดํฐ๋ฅผ ํ ์ด๋ธ์ ๋ฃ์ ] ์์๋ก ํ๋ฆ์ด ์์ฃผ ๋จ์ํ๋ค. ๊ทธ๋ฐ๋ฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ถ๊ฐํ๋ฉด์ [ ๊ฒ์์ด ์ํ๊ฐ ๋ณ๊ฒฝ -> react๊ฐ ๋ค์ ๋ ๋๋ง -> react-query๊ฐ queryKey ๋ณ๊ฒฝ ๊ฐ์ง -> ๊ฒ์ API ํธ์ถ -> ๊ฒฐ๊ณผ ๋ฐ๊ณ ํ ์ด๋ธ ๊ฐฑ์ ]์ผ๋ก ๋ณ๊ฒฝ๋์๋ค.
์ฝ๋ ํ๋ฆ์์ผ๋ก ๋ณด์์ ๋ ์ด๋๊ฐ ๋ฌธ์ ์ธ์ง ์ ๋ชจ๋ฅด๊ฒ ๋ค. ์ด ํ๋ฆ ์ค ์ด๋์ ๊ผฌ์๋์ง ์ดํด๋ณด๊ณ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํด๋ณด์.
3. ๋ฌธ์ ํด๊ฒฐํ๊ธฐ
- ์์ ์ ์ฝ๋(react-query)
const [searchKeyword, setSearchKeyword] = useState("");
const { data: customers = [] } = useQuery({
queryKey: ["customers", searchKeyword],
queryFn: () => fetchCustomers(searchKeyword),
});
const handleSearch = (keyword) => {
setSearchKeyword(keyword);
setIsSearchOpen(false);
AI๋ searchKeyword๊ฐ ๋ฐ๋๋ ์๊ฐ react-query๊ฐ ์๋์ผ๋ก ๋ค์ ์กฐํํ๋ ๋ฐฉ์์ผ๋ก, ๊ตฌ์กฐ์ ํ๋ฆฐ ๊ฑด ์๋์ง๋ง ๊ฒ์์ฐฝ ๋ซ๊ธฐ/๋ ๋๋ง/ํ ์ด๋ธ ๊ฐฑ์ ์ด ๋์์ ์ผ์ด๋๋ฉด์ ๋ฌธ์ ๊ฐ ์๊ธธ ์ ์๋ค๊ณ ํ๋ค.
const [searchKeyword, setSearchKeyword] = useState("");
const [submittedKeyword, setSubmittedKeyword] = useState("");
const {
data: customers = [],
isLoading,
isFetching,
isError,
error,
refetch,
} = useQuery({
queryKey: ["customers", submittedKeyword],
queryFn: () => fetchCustomers(submittedKeyword),
enabled: canRead,
});
const handleSearch = (keyword) => {
const trimmed = keyword.trim();
if (!trimmed) {
alert("๊ฒ์์ด๋ฅผ ์
๋ ฅํ์ธ์.");
return;
}
setSubmittedKeyword(trimmed);
setIsSearchOpen(false);
setPagination((prev) => ({ ...prev, pageIndex: 0 }));
};
๊ทธ๋์ ์์ ์ฝ๋์ฒ๋ผ searchKeyword(๊ฒ์์ฐฝ์ ์ ๋ ฅ ์ค์ธ ๊ฐ)์ submittedkeyword(์ค์ API ์กฐํ์ ์ฌ์ฉํ๋ ๊ฐ)๋ฅผ ๋ฃ์ด ๋ถ๋ฆฌํ๋ ๋ฐฉ์์ผ๋ก ๋ณ๊ฒฝํ๋ค. ์ด๋ฌ๋ฉด ์ฌ์ฉ์๊ฐ ์ ๋ ฅํ ๋๋ง๋ค ์ฟผ๋ฆฌ๊ฐ ํ๋ค๋ฆฌ์ง ์๊ณ ์ํฐ๋ฅผ ๋๋ ์ ๋๋ง submittedKeyword๋ฅผ ๋ฐ๊ฟ์ react-query๊ฐ ๊ฒ์ API๋ฅผ ํธ์ถํ๊ฒ ๋ง๋ ๋ค.
๊ทธ๋ฐ๋ฐ,,, ์ด๋ ๊ฒ ํด๋ ์ฌ์ ํ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ค. ๊ฑฐ๊ธฐ๋ค๊ฐ ์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํ ๋ ํฐ ๋ฌธ์ ๊ฐ ์์๋๋ฐ, ์ ์ด๋ฐ ํ์์ด ์๊ธด๊ฑด์ง AI๋ ๋ชจ๋ฅธ๋ค๋ ๊ฒ์ด์๋ค. ๊ทธ๋๊น ์ฝ๋ฑ์ค์๊ฒ ํ์ฌ ์ฝ๋์ ๋ฐ์ํ ๋ฌธ์ ๋ฅผ ์๋ ค์ฃผ๊ณ , ๊ทธ ๋ถ๋ถ์ ๋ง์ถฐ ์ค์ ๋ก ์ด๋ค ๋ฌธ์ ๊ฐ ์๋์ง ๋ฌผ์ด๋ณด๊ณ , ์์ ํ๊ณ ์ง์ ๋๋ ค๋ณด๋๋ก ์์ผฐ๋๋ฐ ์ฝ๋ฑ์ค ์ชฝ์์๋ ๊ฒ์ ๊ธฐ๋ฅ์ด ๋ฌธ์ ์์ด ๋์๊ฐ๋ค๋ ๋ง์ ์ ํด์ฃผ์๋ค.
๋์ค์ ๋ฐฑ์๋ ๋ฌธ์ ์ธ๊ฐ ์ถ์์ง๋ง, ์ด๊ฑด ๋น์ฐํ ์๋์๋ค. ์ ์ด์ ๋ฐฑ์๋์ ์์ฒญ์ด ์ค๋ ๊ฒ ๊ฐ์ง๋ ์์๋ค. ๋ช์๊ฐ์ ํค๋งค๋ค๊ฐ ๊ฒฐ๊ตญ ํ๋์ ๋์ ์์ ์ด๋... ๋ง(?)์ฒ๋ผ ๊ฒ์ ํํธ๋ง ์๋์ ๋จ์ ์ฝ๋๋ก ๋ฐ๊พธ๊ธฐ๋ก ๊ฒฐ์ ํ๋ค.
- ์์ ํ ์ฝ๋

const handleSearch = async (keyword) => {
const res = await authFetch(`/api/customers/search?keyword=${encodeURIComponent(keyword)}`);
const data = await res.json();
dispatch({ type: "INIT", payload: data });
setIsSearchOpen(false);
setPage(1);
};
์์ฃผ ์ ์๋ ์ฝ๋๊ฐ ๋์๋ค. ๊ทธ๋ฆฌ๊ณ ์ญ์๋ ์ฝ๋๋ฅผ ์ฎ๊ฒจ์ฌ์๋ง์ ์๋์ด ์์ฃผ ์๋๋ค.
์ด ์ฝ๋๋ ๋งจ ์์์ ์ค๋ช ํ ๊ฒ์ฒ๋ผ [ ๊ฒ์์ด ์ ๋ ฅ -> ์ํฐ ๋ฒํผ -> /api/customers/search ์ง์ ํธ์ถ -> ์๋ต ๋ฐ์ดํฐ๋ฅผ ํ ์ด๋ธ์ ๋ฃ์ ] ์์๋ก ์งํ๋๋ ํ๋ฆ์ด ์์ฃผ ๋จ์ํ ์ฝ๋์ด๋ค.
์ฌ์ค ์ ๋์์ด ์๋๊ฑด์ง ๋ฌด์ฒ ๊ถ๊ธํ๋ค. ๊ฒ์ ๊ธฐ๋ฅ์ elasticsearch๊ฐ ์์ด์ ๊ทธ๋ ์ง ๊ทธ๋ค์ง ๋ณต์กํ ์ฝ๋๋ ์๋๋ค. ๊ทธ๋์ ๋น์ฐํ๊ฒ ์๋๋์ง ์์๊ฑฐ๋ ์๊ฐ์ด ์์๋ค. ์์งํ ์ ํํ ์์ธ์ ์ฐพ์๋ด์ง ๋ชปํด์ ์์ฝ๊ธด ํ๋ค. ๊ทธ๋ฌ๋ ์ด๋ ๊ฒ๋ผ๋ ๊ณ ์น ์ ์์์ผ๋ ๋คํ์ด๋ผ๊ณ ์๊ฐํ๋ฉด์, ๊ผญ ์ฝ๋ ์์ ํ์ ๋ชจ๋ ํ ์คํธ๋ฅผ ๋๋ ค๋ณด๊ณ ๋ชจ๋ ๊ธฐ๋ฅ์ ๊ฒ์ฌํ๋ ์ต๊ด์ ์์ง ์๊ธฐ๋ก ๊ฒฐ์ฌํ๋ค!!