개인의 기록
  • 소개
  • 프로젝트
  • 글
  • 링크

© 2026 newgirok

← OP.GG

React 화면 구성 — 검색부터 매치 상세까지

2025년 8월 3일
reactreact-routeraxios

화면은 두 개다. 검색 화면과 전적 목록 화면.

Home — 검색

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();
};

SummonerList — 전적 목록

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를 따로 호출한다. 먼저 응답이 오는 것부터 화면에 뜬다.

Rank — 랭크 카드

if (rankStatus === 200 && !rankData) {
  return <div>랭크 정보가 존재하지 않습니다.</div>;
}
if (!rankData) {
  return <div>랭크 정보를 불러오는 중...</div>;
}

언랭이거나 API가 빈 배열을 돌려줄 때를 구분해서 처리했다.

MatchDetail — 매치 상세

카드 하나에 담기는 정보가 많다. 챔피언, 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}%` }} />

숫자만 보여주는 것보다 한눈에 들어온다.

← 이전 글Riot API 연동 — 계정 조회부터 매치 상세까지