화면은 두 개다. 검색 화면과 전적 목록 화면.
const handleSearch = () => {
if (!name || !tag) {
alert("이름과 태그를 입력하세요.");
return;
}
navigate(`/matches/${region}/${name}/${tag}`);
};
검색 버튼을 누르면 URL만 바꾼다. 데이터 요청은 전적 화면에서 한다.
className prop을 받는 이유가 있다. 전적 화면 상단에 검색바를 작게 올려놓고 싶었는데, 같은 Home 컴포넌트를 className="home-top"으로 넘겨서 재사용했다. 컴포넌트를 새로 만들 필요가 없었다.
엔터키도 지원한다.
const onKeyDown = (e) => {
if (e.keyCode === 13) handleSearch();
};
URL 파라미터에서 소환사 정보를 꺼내 API를 호출한다.
const { region, name, tag } = useParams();
useEffect(() => {
axios.get("http://localhost:8080/user/matches", {
params: { region, name, tag },
}).then((res) => setMatches(res.data));
}, [region, name, tag]);
의존성 배열에 셋 다 넣어서 URL이 바뀌면 자동으로 재요청한다.
화면 구성은 단순하다.
<Home className="home-top" />
<Rank />
{matches.map((match, idx) => <MatchDetail key={idx} match={match} />)}
Rank와 MatchDetail이 각각 API를 따로 호출한다. 먼저 응답이 오는 것부터 화면에 뜬다.
if (rankStatus === 200 && !rankData) {
return <div>랭크 정보가 존재하지 않습니다.</div>;
}
if (!rankData) {
return <div>랭크 정보를 불러오는 중...</div>;
}
언랭이거나 API가 빈 배열을 돌려줄 때를 구분해서 처리했다.
카드 하나에 담기는 정보가 많다. 챔피언, KDA, CS, 피해량, 시야 점수, 아이템 7칸, 소환사 주문 2개, 승패.
카드를 클릭하면 아군/적군 팀 전체 상세가 펼쳐진다.
const [showDetail, setShowDetail] = useState(false);
<div onClick={() => setShowDetail(!showDetail)}>
{/* 요약 카드 */}
</div>
{showDetail && <div>{/* 팀 상세 테이블 */}</div>}
챔피언·아이템·소환사 주문 이미지는 Data Dragon CDN에서 가져온다.
const CDN = "https://ddragon.leagueoflegends.com/cdn/15.8.1/img";
const getChampImg = (name) => `${CDN}/champion/${name}.png`;
const getItemImg = (id) => `${CDN}/item/${id}.png`;
아이템 슬롯 7개 중 빈 슬롯(item === 0)은 빈 div로 채웠다. 안 하면 칸 수가 줄어서 레이아웃이 무너진다.
{match.items.map((item, i) =>
item !== 0
? <img key={i} src={getItemImg(item)} />
: <div key={i} className="item-empty" />
)}
팀 상세 테이블의 피해량은 막대 그래프로 표현했다. 팀 내 최대 피해량 기준으로 비율을 계산한다.
const maxDamage = Math.max(...team.map((p) => p.damage));
<div style={{ width: `${(p.damage / maxDamage) * 100}%` }} />
숫자만 보여주는 것보다 한눈에 들어온다.