1. 상태(State) + UI 관계 이해
1.1 전체 흐름도
영화 이미지 클릭 → handleClick() → modalOpen: true + movieSelected 설정
↓
MovieModal 렌더링
↓
X 버튼 클릭 또는 모달 외부 클릭 → setModalOpen(false) → 모달 닫힘
1.2 Row.js - 모달 상태 관리 및 이벤트 처리
const Row = ({ title, id, fetchUrl }) => {
const [movies, setMovies] = useState([])
const [modalOpen, setModalOpen] = useState(false);
const [movieSelected, setMovieSelection] = useState({})
const fetchMovieData = useCallback(async () => {
const response = await axios.get(fetchUrl);
// console.log('response', response);
setMovies(response.data.results);
}, [fetchUrl])
useEffect(() => {
fetchMovieData();
}, [fetchMovieData])
const handleClick = (movie) => {
setModalOpen(true);
setMovieSelection(movie);
}
핵심 상태:
- modalOpen: 모달 열림/닫힘 상태 (boolean)
- movieSelected: 선택된 영화 데이터 (object)
1.3 Row.js - 영화 이미지 클릭 이벤트
<Wrap>
<img
key={movie.id}
src={`https://image.tmdb.org/t/p/original${movie.backdrop_path}`}
alt={movie.name}
onClick={() => handleClick(movie)}
/>
</Wrap>
- 이미지 클릭 시 handleClick(movie) 호출
- 해당 영화 데이터를 movieSelected에 저장
1.4 Row.js - 조건부 모달 렌더링
{modalOpen &&
<MovieModal
{...movieSelected}
setModalOpen={setModalOpen}
/>
}
- modalOpen이 true일 때만 MovieModal 렌더링
- 선택된 영화 데이터를 spread 연산자로 전달
1.5. MovieModal/index.js - 모달 컴포넌트
const MovieModal = ({
backdrop_path,
title,
overview,
name,
release_date,
first_air_date,
vote_average,
setModalOpen
}) => {
const ref = useRef();
useOnClickOutside(ref, () => {
setModalOpen(false);
})
동작 원리:
- ref로 모달 DOM 요소 참조
- mousedown/touchstart 이벤트 리스너 등록
- 클릭된 요소가 ref 내부가 아니면 handler() 실행 (모달 닫기)
- 컴포넌트 언마운트 시 이벤트 리스너 정리 (cleanup)
Row.js => 모달 상태 관리, 영화 클릭 이벤트 처리, 조건부 렌더링
MovieModal/index.js => 모달 UI, 영화 정보 표시, 닫기 버튼
useOnClickOutside.js => 모달 외부 클릭 감지 커스텀 훅