노마드코더 AI 기초 탄탄 클럽 #22 Marvel Character Gallery
이 글은 노마드코더 - AI 기초 탄탄 클럽 시리즈의 13번째 기록입니다. (총 13개)
노마드코더에서 수강 가능한 풀스택 웹 기초를 6주만에 훑는 챌린지.
해당 강의: ReactJS로 영화 웹 서비스 만들기
#22는 React(JS)로 Marvel Character Gallery 만들기
챌린지 목표
- Marvel API, React, 그리고 React Router를 사용하여 아래와 같은 2개의 페이지가 있는 앱을 만드세요.
- Home Page (
/): 마블 캐릭터의 리스트를 보여주세요. - Details Page (
/character/:id): 각 캐릭터의 디테일 페이지를 보여주세요. - 마블 캐릭터 리스트를 받을 수 있는 API :
https://marvel-proxy.nomadcoders.workers.dev/v1/public/characters?limit=50&orderBy=modified&series=24229,1058,2023 - 각 캐릭터의 디테일을 받을 수 있는 API :
https://marvel-proxy.nomadcoders.workers.dev/v1/public/characters/:id(replace :id for a real number ID)
챌린지 조건
- 블루프린트에는 React 그리고 React Router 가 설치되어있습니다.
useEffect,fetch,Link그리고useParams사용하세요.
동적 데이터 정렬 (Data Sorting)
- API가 정렬을 지원하지 않을 때 프론트엔드에서 직접 정렬하는 법.
Array.sort()와String.localeCompare()를 사용하여 문자열을 알파벳 순으로 정렬.sort()는 원본 배열을 직접 변경(in-place)하므로 새 변수에 할당하지 않아도 됨.
const sortedCharacters = json.data.results.sort((a, b) =>
a.name.localeCompare(b.name)
);
setCharacters(sortedCharacters);
리액트 라우터와 동적 경로 (Dynamic Routing)
- URL 파라미터를 활용하여 상세 페이지 구현.
useParams훅으로 주소창의:id값을 추출하고, 이를 기반으로 상세 API 호출.useEffect의 dependency array에[id]를 넣어야 id가 바뀔 때 재요청됨.- 동일한
Detail.jsx컴포넌트 하나로 모든 캐릭터의 상세 페이지 처리 가능.
const { id } = useParams();
useEffect(() => {
const fetchDetail = async () => {
const response = await fetch(
`https://marvel-proxy.nomadcoders.workers.dev/v1/public/characters/${id}`
);
const json = await response.json();
setCharacter(json.data.results[0]);
setIsLoading(false);
};
fetchDetail();
}, [id]);
CSS 호버 인터랙션 (카드 뒤집기 효과)
진짜 3D flip (카드 전체가 회전)이 아니라, 세 가지 효과를 조합한 방식:
- 이미지만
rotateY(180deg)회전 → 뒤집히는 느낌 연출 - 카드 앞면
filter: brightness(0.2)→ 어둡게 페이드 - 카드 뒷면
opacity: 0 → 1전환 → 텍스트 fade in
perspective, transform-style: preserve-3d, backface-visibility: hidden은 3D 공간 컨텍스트를 만들어주는 역할.
- hover를 부모(
.card-container)에 걸어두면, 자식 요소들(.card-front,.card-back,.character-img)을 각각 개별 제어할 수 있어서 유연함.
.card-container:hover .character-img {
transform: rotateY(180deg);
}
.card-container:hover .card-front {
filter: brightness(0.2);
}
.card-container:hover .card-back {
opacity: 1;
transition: opacity 1s;
}
- 처음에
opacity: 100으로 작성했는데,opacity의 유효 범위는0~1. 브라우저가 1로 clamp 해줘서 동작은 했지만 비표준.opacity: 1로 수정 함.
레이아웃 안정화 (Layout Stability)
.card-front와.card-back을position: absolute로 겹쳐 쌓을 때,display: flex+align-items: center+justify-content: center로 내부 요소 정렬.box-sizing: border-box전역 적용으로 padding 포함 너비 계산 혼선 방지.
* {
box-sizing: border-box;
}
.card-front,
.card-back {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
padding: 10px;
}

Series: 노마드코더 - AI 기초 탄탄 클럽
1 | 노마드코더 AI 기초 탄탄 클럽 #3 Position & Pseudo Selector 2 | 노마드코더 AI 기초 탄탄 클럽 #4 Playlist Layout 3 | 노마드코더 AI 기초 탄탄 클럽 #5 Books Layout 4 | 노마드코더 AI 기초 탄탄 클럽 #6 Music Player 5 | 노마드코더 AI 기초 탄탄 클럽 #8 Events&Objects 6 | 노마드코더 AI 기초 탄탄 클럽 #10 JS Casino 7 | 노마드코더 AI 기초 탄탄 클럽 #11 Christmas Clock 8 | 노마드코더 AI 기초 탄탄 클럽 #12 Random Gradient 9 | 노마드코더 AI 기초 탄탄 클럽 #15 계산기 C++ vs Python 10 | 노마드코더 AI 기초 탄탄 클럽 #16 For Loop with Python 11 | 노마드코더 AI 기초 탄탄 클럽 #18 React Calculator 12 | 노마드코더 AI 기초 탄탄 클럽 #19 Pokemon Gallery 13 | 노마드코더 AI 기초 탄탄 클럽 #22 Marvel Character Gallery 읽는 중