[메인페이지]
[상세페이지]
[커뮤니티 페이지]
[카테고리 페이지]
[내일배움캠프] 5진스
정소현 | 강다연 | 류지원 | 박규리 | 조해인 |
---|---|---|---|---|
팀원 | 팀원 | 팀장 | 팀원 | 팀원 |
상세페이지 | 로그인, 회원가입 | 카테고리, 헤더, 푸터 | 커뮤니티 | 메인페이지, 헤더, 푸터 |
<div class="movie-items"></div>
작업 타입 작업내용
// playApi.js
// 현재 진행중인 공연 정보 등록된 순으로 최대 1000개 불러오는 함수
const BASE_URL = "http://kopis.or.kr/openApi/restful/pblprfr";
const KOPIS_KEY = import.meta.env.VITE_KOPIS_KEY;
const playApi = axios.create({ baseURL: BASE_URL });
export const getData = async () => {
try {
const { data } = await playApi.get("/", {
params: {
service: KOPIS_KEY,
stdate: getDateString(), // 오늘 날짜 반환하는 함수
eddate: getDateString(),
rows: 1000,
cpage: 1,
},
});
return parseXMLToJSON(data).dbs.db;
} catch (error) {
console.error("Error fetching performance details:", error);
throw new Error("데이터를 불러오는 중 오류가 발생했습니다.");
}
};
// MainPage.jsx
// 공연 정보 불러오기
const {
data: mainData,
isPending,
isError,
} = useQuery({
queryKey: ["main-data"],
queryFn: getData,
});
// Embla.jsx
// MainPage.jsx에서 prop으로 데이터 전달 받음
const Embla = ({ data }) => {
const [emblaRef] = useEmblaCarousel({ loop: true }, [Autoplay({ stopOnMouseEnter: true, stopOnInteraction: false })]);
const indices = []; // 랜덤 인덱스 저장
while (indices.length < 8) {
let tmp = Math.floor(data.length * Math.random())
if (indices.includes(tmp)) {
continue;
} else {
indices.push(tmp);
}
}
const carousel = indices.map(idx => data[idx]);
return (
<div className='embla' ref={emblaRef}>
<div className='embla__container'>
{carousel && [0, 2, 4, 6].map((i) => ( // 각 슬라이드에 두개씩 보여줌
<Slide play={[carousel[i], carousel[i + 1]]} key={`slide-${i}`} />
)
)}
</div>
</div>
)
}
// Genre.jsx
// MainPage.jsx에서 prop으로 받은 데이터를 장르에 따라 filter해 GenreDiv에 보여줌
const Genre = ({data}) => {
const [clicked, setClicked] = useState(0);
const genreArray = Object.values(genreCodes);
return (
<div>
<div>
{
genreArray..map((item, idx) => (
<GenreButton idx={idx} clicked={clicked} setClicked={setClicked} key={item}>
{item}
</GenreButton>
))
}
</div>
<div>
<GenreDiv plays={data.filter(play => play.genrenm === genreArray[clicked]).slice(0,10)} idx={clicked}/>
</div>
</div>
)
}
// detailApi.js
// 공연 상세 데이터를 가져오는 함수
export const fetchDetailData = async (id) => {
const url = `http://kopis.or.kr/openApi/restful/pblprfr/${id}?service=${apiKey}`;
try {
const response = await axios.get(url);
const jsonData = parseXMLToJSON(response.data);
console.log(jsonData); // 변환된 JSON 데이터 콘솔 출력
return jsonData;
} catch (error) {
console.error("DetailDataError", error);
throw new Error("데이터를 불러오는 중 오류가 발생했습니다.");
}
};
const { id } = useParams();
// 공연 상세 정보 불러오기
const {
data,
isLoading: detailLoading,
isError: detailError,
} = useQuery({
queryKey: ["performanceDetail", id],
queryFn: () => fetchDetailData(id),
});
const detailData = data?.dbs?.db;
if (detailLoading) {
return <div>Loading...</div>;
}
if (detailError || !detailData) {
return <div>Error</div>;
}
// detailApi.js
// 공연 장소 데이터를 가져오는 함수
export const fetchMapData = async (placeId) => {
const mapUrl = `http://www.kopis.or.kr/openApi/restful/prfplc/${placeId}?service=${apiKey}`;
try {
const response = await axios.get(mapUrl);
const jsonData = parseXMLToJSON(response.data);
return jsonData;
} catch (error) {
console.error("Map Error", error);
throw new Error("데이터를 불러오는 중 오류가 발생했습니다.");
}
};
// DetailMap.jsx
import React from "react";
import { useQuery } from "@tanstack/react-query";
import { Map, MapMarker, ZoomControl } from "react-kakao-maps-sdk";
import { fetchMapData } from "../../api/detailApi";
const DetailMap = ({ detailData }) => {
const placeId = detailData?.mt10id;
const { data, isLoading, isError } = useQuery({
queryKey: ["mapData", placeId],
queryFn: () => fetchMapData(placeId),
enabled: !!placeId,
});
const mapData = data?.dbs?.db;
if (isLoading) {
return <div>Loading...</div>;
}
if (isError) {
return <div>Error</div>;
}
if (!mapData) {
return <div>장소 정보가 없습니다.</div>;
}
// 좌표값을 숫자로 변환
const lat = parseFloat(mapData.la);
const lng = parseFloat(mapData.lo);
return (
<Map
center={{ lat, lng }} // 좌표값을 숫자로 변환하여 설정
style={{ width: "100%", height: "500px" }}
level={3}
>
<MapMarker position={{ lat, lng }} />
<ZoomControl />
</Map>
</div>
);
};
export default DetailMap;
// detailApi.js
// 상세페이지 댓글 작성 기능
const jsonUrl = import.meta.env.VITE_DB_URL + "/comments";
const commentApi = axios.create({ baseURL: jsonUrl });
// 댓글 추가
export const detailAddComment = async (newComment) => {
const { data } = await commentApi.post("/", newComment);
return data;
};
// 특정 공연의 댓글 가져오기
export const detailGetComment = async (performanceId) => {
const { data } = await commentApi.get("/");
return data.filter((comment) => comment.performanceId === performanceId);
};
// 댓글 삭제
export const detailDeleteComment = async (id) => {
const { data } = await commentApi.delete(`/${id}`);
return data;
};
// DetailComment.jsx
const addMutation = useMutation({
mutationFn: (newComment) => detailAddComment({ ...newComment, performanceId: id }),
onSuccess: () => {
queryClient.invalidateQueries(["comments", id]);
setComment("");
},
});
// 댓글 삭제 mutation
const removeMutation = useMutation({
mutationFn: detailDeleteComment,
onSuccess: () => {
queryClient.invalidateQueries(["comments", id]);
},
});
// 댓글 가져오기 query
const {
data: comments = [],
isLoading,
isError,
} = useQuery({
queryKey: ["comments", id],
queryFn: () => detailGetComment(id),
});
import axios from "axios";
import { genreCodes } from "../utils/Kopis-api-common";
import { getDateString, parseXMLToJSON } from "../utils/utils";
// KOPIS 관련
const BASE_URL = "http://kopis.or.kr/openApi/restful/pblprfr";
const playApi = axios.create({ baseURL: BASE_URL });
export const getGenreAreaData = async (genre, area, row, startDate, endDate) => {
try {
const { data } = await playApi.get("/", {
params: {
service: import.meta.env.VITE_KOPIS_KEY,
stdate: startDate,
eddate: endDate,
rows: row,
cpage: 1,
shcate: genre === "장르별" ? null : genre,
signgucode: area === "지역별" ? null : area,
},
});
const result = parseXMLToJSON(data).dbs;
if (result) {
return result.db;
} else {
return [];
}
} catch (error) {
console.error("Error fetching performance details:", error);
throw new Error("데이터를 불러오는 중 오류가 발생했습니다.");
}
};
export const searchGenreAreaData = async (searchValue, genre, area, row, startDate, endDate) => {
const allData = await getGenreAreaData(genre, area, row, startDate, endDate);
const data = allData.filter((data) => {
return String(data["prfnm"]).includes(searchValue);
});
return data;
};
[메인페이지]
🔥 문제점
// playApi.jsx
// 장르별 데이터를 0번째부터 4번째까지 불러오는 함수
export const getGenreData = async (genre) => {
const { data } = await playApi.get(`?genrenm=${genre}&_start=0&_end=5`);
return data;
};
const genreArray = Object.values(genreCodes);
// Promise.all을 이용해 동시에 여러 장르 데이터 불러오는 함수
export const getClassifiedData = async () => {
const responses = Promise.all(genreArray.map((genre) => getGenreData(genre)));
return responses;
};
[상세페이지]
🔥 문제점
// zustand코드
// useKopisStore.js
import { create } from "zustand";
import { fetchDetailData, fetchMapData } from "../api/detailApi";
const useKopisStore = create((set, get) => ({
data: null,
mapData: null,
error: null,
// 공연 상세 정보 가져오는 함수
fetchData: async (id) => {
try {
const result = await fetchDetailData(id);
if (result && result.dbs && result.dbs.db) {
set({ data: result.dbs.db, error: null });
} else {
set({ error: "No data found", data: null });
}
} catch (error) {
set({ error: `Error fetching details: ${error.message}`, data: null });
}
},
// 공연장소 데이터를 가져와 상태에 저장
fetchMapData: async (id) => {
try {
const result = await fetchMapData(id); // API 호출
console.log(result);
if (result && result.dbs && result.dbs.db) {
set({ mapData: result.dbs.db, error: null }); // 상태에 저장
console.log(get().mapData);
} else {
set({ error: "No map data found", mapData: null });
}
} catch (error) {
set({
error: `Error fetching map data: ${error.message}`,
mapData: null,
});
}
},
}));
export default useKopisStore;
해결 리펙토링
const { id } = useParams();
// 공연 상세 정보 불러오기
const {
data,
isLoading: detailLoading,
isError: detailError,
} = useQuery({
queryKey: ["performanceDetail", id],
queryFn: () => fetchDetailData(id),
});
const detailData = data?.dbs?.db;
if (detailLoading) {
return <div>Loading...</div>;
}
if (detailError) {
return <div>Error</div>;
}
[커뮤니티]
[로그인, 회원가입]
[카테고리]
🔥 문제점
const { data, isLoading, isError, refetch } = useQuery({
queryKey: [QUERY_KEY.category],
queryFn: () => searchGenreAreaData(searchTerm, genre, area, row, startDate, endDate),
keepPreviousData: true,
});
해결 방안
const { data, isLoading, isError, refetch } = useQuery({
queryKey: [QUERY_KEY.category],
queryFn: () => searchGenreAreaData(searchTerm, genre, area, row, startDate.replaceAll("-", ""), endDate.replaceAll("-", "")),
keepPreviousData: true,
});