[ Netflix Clone 4 ] Modal & Search Api

승진·2019년 9월 14일
0

Netflix Clone

목록 보기
5/5
post-thumbnail

영화를 클릭하면 해당하는 영화정보를 담고있는 모달이 뜨게 만들어보자!
모달이 클릭한 영화의 정보를 담고있으려면 Movie컴포넌트와 같은 props를 받아야한다. 때문에 Movie 컴포넌트에서 Modal을 같이 렌더링 시키기로 했다.

일단 영화 정보를 담는 image / Title / overview 데이터를 props로 받고,
Movie를 클릭 시 모달이 보여지고 모달클릭시 모달이 닫히는 onClick 이벤트를 적용해야한다.

/components /Modal.jsx

const Modal = ({ name, overview, img, show, containerName, onClick}) => {

    return (
        show ?
        <div className={containerName} onClick={onClick}>
            <div className="modal">
                <img src={`https://image.tmdb.org/t/p/original/${img}`} alt="bg"/>
                <div className="bg"></div>
                <div className="modal-content">
                    <h1>{name}</h1>
                    <p>{overview}</p>
                    <div className="btn">
                        <button><FiPlay/><span>PLAY</span></button>
                        <button><FiPlus/><span>MY LIST</span></button>
                        <button><FiInfo/><span>DETAILS</span></button>
                    </div>
                </div>
            </div>
        </div>
        :
        null
    )
}

/components / Movie.jsx

const Movie = (props) => {

    const [modal, setModal] = useState(false);

    const showModal = () => {
        setModal(true);
    }

    const closeModal = () => { 
        setModal(false);
    }


    return (
        <>
            <div className="movie" onClick={() => showModal()}>
                <img src={`https://image.tmdb.org/t/p/original/${props.props.backdrop_path}`} alt={props.props.id}/>
                <Modal
                containerName={"modalContainer " + (modal ? "show" : "hide")}
                name={props.props.title}
                overview={props.props.overview}
                show={modal}
                img={props.props.backdrop_path}
                onClick={() => closeModal()}
            />
            </div>
        </>
    )
}

Error


이렇게 해서 렌더링을 해보니 클릭 시 모달이 나타나지만 뒤에 movie이벤트가 겹쳐져 모달이 깜빡거리는 에러가 발생했다.
scss에서의 문제인줄 알고 z-index값을 변경해보고 구글에서 이벤트 버블링에 관한 문제들을 찾아봐도 해결되지않았는데..

이것저것 시도해 보다 Modal을 밖으로 빼주니 해결됐다! movie div안에서 겹쳐져 렌더링 하고 있었기 때문에 생긴 오류였다.

 		<>
            <div className="movie" onClick={() => showModal()}>
                <img src={`https://image.tmdb.org/t/p/original/${props.props.backdrop_path}`} alt={props.props.id}/>
            </div>
            <Modal
                containerName={"modalContainer " + (modal ? "show" : "hide")}
                name={props.props.title}
                overview={props.props.overview}
                show={modal}
                img={props.props.backdrop_path}
                onClick={() => closeModal()}
            />
        </>

image.png

이렇게 해서 모달도 완성했다.

Search Api


이제 마지막 작업으로 input에 영화의 title을 검색하면 해당하는 영화들의 리스트를 검색해 결과를 띄워주는 기능을 만들어보자

API Overview 에서 search에 관련된 정보를 확인해보면
https://api.themoviedb.org/3/search/movie?api_key=${API_KEY}&sort_by=&include_adult=false&query=${search}&language=en-US&page=1

이렇게 ${search} 타이틀을 넣어주면 원하는 영화를 찾을 수 있다.
search api는 이 컴포넌트에서만 사용하기 때문에 redux에서 전역으로 관리할 필요가 없기 때문에 바로 해당 컴포넌트에서 연동한다.

상태가 변경되는 input과 돋보기 아이콘만 SearchInput.jsx에서 따로 만들어주고 Nav.jsx에서 불러와 사용하면 된다.

containers /SearchInput.jsx

const SearchInput = () => {

    const [search, setSearch] = useState(''); // input
    const [lists, setLists] = useState(false); // list modal
    const [movieLists, setMovieLists] = useState([]);
    
    const API_KEY = 'your key';
    const url = `https://api.themoviedb.org/3/search/movie?api_key=${API_KEY}&sort_by=&include_adult=false&query=${search}&language=en-US&page=1`;

    let data = [];

    const fetch = async () => {
        const response = await axios.get(url);
        data = response.data.results || [];
        setMovieLists(data);
    }

    const onChange = (e) => {
        setSearch(e.target.value);
        fetch(setMovieLists());
        setLists(true);
    }
    
    const hideLists = () => {
        setLists(false);
        setSearch('');
    }

    return (
        <div className="searchContainer" onClick={() => hideLists()}>
            <div className="inputContainer">
                <input className="search-txt" type="text" value={search} onChange={onChange} placeholder="title"/>
                <a className="searchLogo"><FiSearch/></a>
            </div>
            <div className={"searchMovie " + (lists ? "show" : "")}>
                <div className="listContainer">
                    { movieLists && movieLists.map(movie => (
                        <List props={movie} key={movie.id}/>
                    ))}
                </div>
            </div>
        </div>
    )
}

useState

input과, 결과로 나온 영화들을 보여주는 list, 검색 데이터가 들어가는 movieLists 세가지를 상태관리 해준다.

onChange()

input에 text를 치면 value값을 통해 상태가 바로바로 업데이트 되면서 해당하는 데이터를 뽑아와 영화목록을 보여주는 list 모달이 띄워진다.

hideLists()

영화 이외에 배경부분을 클릭하면 사라지고 input값이 초기화되도록 하는 setLists(false); setSearch('');를 설정했다.

같은 파일에 List 컴포넌트를 만들어서 사용했다.

const List = (props) => {
    return (
        <div className="lists">
            <img src={`https://image.tmdb.org/t/p/original/${props.props.poster_path}`} />
        </div>
    )
}

image.png
이렇게 해서 input에 spider를 검색해 보니 영화 리스트가 잘 보여진다!

참조


https://www.themoviedb.org/talk/5c89de1ec3a368645d0cce67

profile
Front-end 개발 공부일지

5개의 댓글

comment-user-thumbnail
2019년 9월 18일

실제 모달 띄울때 고민을 가장 많이하는게 어떤 특정 엘리먼트의 하위로 들어가면 z-index와 같은게 관리하기가 힘들어져서 저는 주로 root에 사용 합니다.

리스트 컴포넌트도 궁금하네요

1개의 답글
comment-user-thumbnail
2020년 7월 5일

유용한 글 감사합니다~

답글 달기