🍀 요약
- 개발 일정 정리
- 페이지 내부 탭 만들기
- API 연결
🍀 내용
📌 개발 일정
지난 주에 레이아웃을 만들면서 개선점을 도출했기 때문에, 이번주부터 api를 연결하면서 개선점을 반영하기로 했다. 특히 멘토님께서 야구 경기 데이터들을 시각화하는 것을 강조하셨기 때문에, Game탭 부터 api 연결을 시작했다.
그래서 정리한 개발 일정은 다음과 같다.
- 2주차: API 연결(Game 부분, Media 부분)
- 3주차: API 연결(Player부분, Media부분)
- 4주차: 중간발표 준비 및 코드 디테일 통일, 애니메이션 라이브러리 사용
- 5주차: 새로운 기능 추가 - 챗봇, 라이브채팅 기능, 소셜로그인
- 6주차: 트러블 슈팅, 렌더링 최적화
- 7주차: 배포 및 최종 발표 준비
📌 내가 맡은 부분
1. 페이지 내부 탭 만들기
본래 Navigation Bar로 구성하려했는데, Media 페이지를 작업하시던 다른 팀원 분이 상단 배너를 탭으로 구성해놓은 것을 발견했다.
이 UI가 더 보기에 좋아보여 팀원분이 작업하신 코드에서 상단 배너 부분을 공통 컴포넌트로 옮겨서 재구성했다.
🧷 hook : useTabFromUrl.tsx
import { isNullish } from '@/lib';
import { startTransition, useCallback } from 'react';
import { useLocation, useNavigate } from 'react-router';
type TabConfig = {
value: string;
path: string;
};
type UseTabFromUrlProps = {
basePath: string;
tabs: TabConfig[];
defaultTab?: string;
};
export const useTabFromUrl = ({
basePath,
tabs,
defaultTab,
}: UseTabFromUrlProps) => {
const navigate = useNavigate();
const location = useLocation();
// 현재 탭 상태
const currentTab = (() => {
const currentPath = location.pathname;
const matchedTab = tabs.find((tab) => currentPath.includes(tab.path));
return matchedTab?.value || defaultTab || tabs[0].value;
})();
// 탭 변경 핸들러
const handleTabChange = useCallback(
(value: string) => {
const targetTab = tabs.find((tab) => tab.value === value);
// 탭이 없으면 기본 탭으로 리다이렉트
if (!targetTab) {
const firstTab = tabs.at(0);
if (isNullish(firstTab?.path)) {
return;
}
const fallbackPath = `${basePath}${firstTab.path}`;
navigate(fallbackPath, { replace: true });
return;
}
// 탭이 있으면 탭 경로로 리다이렉트
startTransition(() => {
const newPath = `${basePath}${targetTab.path}`;
navigate(newPath, { preventScrollReset: true });
});
},
[basePath, navigate, tabs]
);
return { currentTab, handleTabChange };
};
🧷 사용 방법
탭 이름 선언
const 해당_TABS_CONFIG = [
{ value: 'ktWiz', label: 'KT Wiz 경기' },
{ value: 'allLeague', label: '전체 리그' },
];
링크 이동 필요 시 path 사용
const NEWS_TABS_CONFIG = [
{ value: 'news', path: '/wiznews' },
{ value: 'press', path: '/wizpress' },
];
useTabFromUrl 호출
const { currentTab, handleTabChange } = useTabFromUrl({
basePath: '/media',
tabs: NEWS_TABS_CONFIG,
defaultTab: 'news',
});
탭 정렬
{/* 탭 */}
<TabsList className="media-tabs-list">
{GAME_TABS_CONFIG.map((tab) => (
<TabsTrigger
key={tab.value}
value={tab.value}
onClick={() =>
handleTabChange(tab.value as 'ktWiz' | 'allLeague')
}
className="media-tabs-trigger px-6 py-2.5"
>
{tab.label}
</TabsTrigger>
))}
</TabsList>
탭 변경 시 하단 콘텐츠 변경
<TabsContent value="ktWiz">
{/* value 탭일 때 보여줄 컴포넌트 전달
-> TabsContent 자식으로 보여줄 컴포넌트나 코드 작성 */}
<CalenderBody renderCellContent={renderCellContent} />
</TabsContent>
<TabsContent value="allLeague">
<div>
<h1>보여줄 거 작성</h1>
<p>하면 될 것 같아요</p>
</div>
</TabsContent>
2. Game - 박스스코어 데이터 연동
1. axios를 이용해 박스스코어 데이터 호출하는 코드 작성
/* src/features/game/apis/boxScore.ts */
import axios from 'axios';
const API_URL = import.meta.env.VITE_REACT_APP_API_URL;
export const getMatchData = async (gameDate: string, gmkey: string) => {
try {
const res = await axios.get(`${API_URL}/game/boxscore`, {
params: { gameDate, gmkey },
});
if (res.status !== 200) {
throw new Error(`Failed to fetch data. Status code: ${res.status}`);
}
return res.data.data;
} catch (err) {
console.error('박스스코어 api GET 에러: ', err);
throw err;
}
};
2. 호출한 데이터의 타입을 types/BoxscoreData.ts 에 정의
3. 호출 파라미터(gameDate, gameKey)를 Props로 받아옴
4. 데이터 호출
5. 호출한 데이터(data)를 박스스코어 페이지의 데이터로 셋팅(setMatchData(data))
/* src/features/game/BoxScoreTab.tsx 일부 */
interface Props {
gameDate: string | undefined;
gameKey: string | undefined;
}
const BoxScoreTab = ({ gameDate, gameKey }: Props) => {
const [matchData, setMatchData] = useState<BoxScoreData>();
useEffect(() => {
fetchMatchData();
}, []);
/**TODO: 최신 경기 날짜 전달 - 오늘 기준으로 경기가 있는 날짜 확인*/
const fetchMatchData = async () => {
if (!gameDate && !gameKey) {
const data = await getMatchData('20241011', '33331011KTLG0');
setMatchData(data);
}
if (gameDate && gameKey) {
const data = await getMatchData(gameDate, gameKey);
setMatchData(data);
}
};
7. 각 컴포넌트에 해당하는 데이터 부분을 props로 전달
💥트러블슈팅(to-do)
박스스코어 페이지는 메인페이지와 경기 일정 탭의 캐러셀에서 <경기정보> 버튼을 눌렀을 때 해당 경기의 박스스코어 페이지로 링크되어야한다. 이 부분을 useNavigate로 전달했는데 페이지 주소는 바뀌지만 404 페이지로 링크되는 문제가 있었다. 지금 생각해보니 Props로 gameDate와 gameKey를 받아오게 해놓고 이를 전달하지 않았기 때문인 것 같다.
import { GameSchedule } from '@/features/game/types/match-schedule';
import { format, isValid, parse } from 'date-fns';
import { useCallback } from 'react';
import { useNavigate } from 'react-router';
const CarouselCard = ({ data }: { data: GameSchedule | null }) => {
const navigate = useNavigate();
const formatDate = useCallback((date: string): string => {
const parsedDate = parse(date, 'yyyyMMdd', new Date());
return isValid(parsedDate)
? format(parsedDate, 'yyyy.MM.dd')
: '날짜 정보 없음';
}, []);
const handleGameInfoClick = () => {
if (data) {
const gameDate = data.gameDate.toString();
const gameKey = data.gmkey;
navigate(`/game/regular/boxscore/${gameDate}/${gameKey}`);
}
};
return (
<CarouselItem
className={
{/* ... */}
const CarouselCard = ({ data }: { data: GameSchedule | null }) => {
<button
type="button"
className="bg-gray-400 text-white rounded-full hover:bg-gray-500 py-1 px-3 w-24"
onClick={handleGameInfoClick}
>
경기 정보
</button>
그런데 이 부분을 수정하기 전에 비상사태가 발생했다. KT wiz에서 2025년 시즌 정보 업데이트로 인해, 27일까지 홈페이지 공사에 들어가 api가 사용이 중지된 것이다.... 27일 이후에 바로 이 부분을 수정해야겠다.
🍀 평가
진짜 바보같은 짓을 했는데... gmkey 를 gmKey라고 써놓아서 계속 api호출에 실패했었다!!!!!!!!! 근데 나는 console에 에러가 뜨지 않았기에, data의 상태 관리를 잘못한 줄 알았다. 그래서 이 부분을 계속 고치다가, ai를 돌리고 별 짓을 다해도 안 되길래 혹시나 해서 network탭을 보니 api를 호출한 내역이 없는거다. 이때 api 호출 코드를 뜯어보다가 api 주소를 보고 저 오타를 찾을 수 있었다. 결론적으로 엉뚱한 곳에서 약 3일동안 삽질을 했다........ 그래도 발견해서 정말 다행이다. 다음부터는 이런 실수를 하지 않겠지😂
이번 주에는 다른 사람들의 코드를 볼 일이 많았다. 다른 사람의 작업에 내 작업을 얹기도 했고, 팀장을 맡아 모든 팀원들의 PR을 보았기 때문이다. 다른 사람의 코드를 이해하고 리뷰하면서 배우는 점이 많았다. 실무에 나가면 내 코드보다 다른 사람의 코드를 보는 일이 훨씬 많다고 했다. 실무 연습에 많은 도움이 된 것 같다.
'교육, 대외활동 > 유데미 프론트엔드 캠프' 카테고리의 다른 글
[유데미x스나이퍼팩토리] 프론트엔드 프로젝트 캠프 - KT wiz 홈페이지 개선 프로젝트 1주차 회고 (1) | 2024.12.17 |
---|---|
[유데미x스나이퍼팩토리] 프론트엔드 프로젝트 캠프 - 사전직무교육 3주차 학습 회고 (3) | 2024.12.10 |
[유데미x스나이퍼팩토리] 프론트엔드 프로젝트 캠프 - 사전직무교육 2주차 학습 회고 (1) | 2024.11.25 |
[유데미x스나이퍼팩토리] 프론트엔드 프로젝트 캠프 - 사전직무교육 1주차 학습 회고 -(2) React 프로젝트, Typescript 개요 (1) | 2024.11.17 |
[유데미x스나이퍼팩토리] 프론트엔드 프로젝트 캠프 - 사전직무교육 1주차 학습 회고 -(1) 자바스크립트 문법 (0) | 2024.11.17 |