[Next.js] 영화 검색 기능 구현

JunSeok·2023년 2월 13일
2

Movie-inner 프로젝트

목록 보기
10/13
post-thumbnail
post-custom-banner

구현 목표

헤더 창에서 영화 검색하는 기능을 구현하고자 한다.

  1. 헤더에 돋보기 아이콘을 넣는다.
  2. 돋보기 누르면 검색할 input 창을 인터랙티브하게 부른다.
  3. input 창에 검색어를 기입하면 곧바로 search 페이지로 이동하면서 검색 결과를 부른다.
  4. search 페이지를 열 때 검은 화면에서 시작하여 서서히 검색 결과를 보여준다.

구현

헤더에 돋보기 아이콘 넣고 누르면 검색할 input 창 인터랙티브하게 등장

아이콘은 react-icons를 사용하고 스타일은 Emotion을 사용했다. 클릭여부를 props로 전달하여 인터랙티브하게 동작시켰다.
Emotion에서 props 전달 방법은 여기로 [emotion] props 전달 방법과 type 에러 해결방법


input 창에 검색어를 기입하면 곧바로 search 페이지로 이동하면서 검색 결과를 부른다.

URL이 실시간으로 변하면서 검색결과를 불러오는 모습을 볼 수 있다!

HeaderSearch 구현 코드

// HeaderSearch.tsx
import { BsFillEraserFill, BsSearch } from "react-icons/bs"
import { useCallback, useEffect, useState } from "react"

import { SearchContainer } from "./HeaderSecondNav.style"
import { useRouter } from "next/router"

const HeaderSearch = () => {
    const router = useRouter()
    const [click, setClick] = useState(false)
    const [search, setSearch] = useState('')

    //	값이 변할때마다 새롭게 요청
    useEffect(() => {
        try {
            if (search) {
                router.replace({
                    pathname: '/search',
                    query: {
                        search: search,
                        page: 1
                    }
                }, undefined, { shallow: true })
            }
        }
        catch (e) {
            console.error(e.response)
        }
    }, [search])

    const clickSearchImg = () => setClick(!click)

    const backPage = () => {
        router.replace('/')
        setSearch('')
        setClick(false)
    }
    
    const handleSearchValue = useCallback((e) => {
        setSearch(e.target.value)
    }, [search])

    return (
        <SearchContainer click={click}>
            <BsSearch onClick={clickSearchImg} size={30} />
            <input type='text' placeholder='제목, 사람' autoFocus autoComplete='off' value={search} onChange={handleSearchValue} />
            {search && <BsFillEraserFill onClick={backPage} size={30} />}
        </SearchContainer>
    )
}

export default HeaderSearch

혹시나 해서 스타일 코드도 올립니다.

// HeaderSearch.style.tsx
type SearchProps = {
    click: boolean
}
export const SearchContainer = styled.div<SearchProps>`
    display: flex;
    align-items: center;
    justify-content: center;
    border-bottom: ${(props) => (props.click ? '1px solid red;' : 'none')};
    background-color: ${(props) => (props.click ? 'black' : 'none')};
    padding: 0.3rem;
    margin-right: ${(props) => (props.click ? '1.25rem' : 0)};
    > input {
        transition: 0.3s width linear !important;
        width: ${(props) => (props.click ? '12.5rem' : 0)};
        background-color: ${(props) => (props.click ? 'black' : 'none')};
        color: white;
        border: none;
        outline: none;
        opacity: ${(props) => (props.click ? '1' : 0)};
        margin-left: 0.625rem;
        padding: 0.3rem;
    }
`

search 페이지를 열 때 검은 화면에서 시작하여 서서히 검색 결과를 보여준다.

  • 검은 화면에서 서서히 검색결과를 보여주는 스타일은 search page 들어올 때 useEffect로 변수 값을 true로 주고 true면 opacity를 0에서 1로 준다.
  • URL로 받은 query는 router.query로 받아주고 이 값으로 검색 결과를 불러온다.
  • 검색 결과는 react-query를 이용하여 무한스크롤로 구현했다.
  • 무한스크롤 관련 내용은 여기로 React Query 공부 (5) 무한스크롤 구현하기(Next.js)

구현 코드

// Search.tsx
import { SearchListTitle, SearchResultBox } from "./Search.style"
import { useEffect, useState } from "react"

import { IoIosArrowForward } from "react-icons/io"
import SearchResultActor from "./SearchResultActor"
import SearchResultGenre from "./SearchResultGenre"
import SearchResultMovie from "./SearchResultMovie"
import useGetActorSearch from "../../react-query/MovieData/ActorSearch"
import useGetMovieSearch from "../../react-query/MovieData/MovieSearch"

const SearchResult = (props) => {
  	// query값 받아오기
    const { search, genreName } = props
    // react-query를 이용하여 데이터를 불러옴
    const actorSearch = useGetActorSearch(search).data
    const movieSearch = useGetMovieSearch(search).data
    const [movie, setMovie] = useState(true);
    const [actor, setActor] = useState(true);
    useEffect(() => { setMovie(true) }, [movieSearch])
    useEffect(() => { setActor(true) }, [actorSearch])

    return (
        <>
            {
                genre && genreName ?
                    <SearchResultGenre genre={genre} genreName={genreName} /> :
                    <SearchResultBox>
                        <div>
                            <SearchListTitle movie={movie} actor={actor}>
                                <div>Movie {movieSearch?.pages[0]?.data?.total_results}<IoIosArrowForward onClick={() => setMovie(!movie)} size={35} /></div>
                                <div>Creator &#38; Actor {actorSearch?.pages[0]?.data?.total_results} <IoIosArrowForward onClick={() => setActor(!actor)} size={35} /></div>
                            </SearchListTitle>
                        </div>
                        <SearchResultMovie search={search} click={movie} />
                        <SearchResultActor search={search} click={actor} />
                    </SearchResultBox>
            }
        </>
    )
}

export default SearchResult

전체 코드가 보고 싶다면? Github 주소

백엔드 코드가 궁금하다면 백엔드 코드 개발자 Github 주소

profile
최선을 다한다는 것은 할 수 있는 한 가장 핵심을 향한다는 것
post-custom-banner

0개의 댓글