[Next.js] 커뮤니티 검색 기능(Material-UI)

JunSeok·2023년 2월 22일
1

Movie-inner 프로젝트

목록 보기
12/13
post-thumbnail

구현 목표

커뮤니티 내에서 글을 검색하는 기능을 만들고자 한다.

flow

  • 커뮤니티 페이지 내에 클릭할 수 있는 돋보기 모양 검색 버튼을 만든다.
  • 돋보기를 클릭하면 중앙에 검색할 수 있는 모달창을 띄운다.
    - 모달창에는 검색 조건을 선택할 수 있고 검색 내용을 적는 곳이 있다. 그리고 뒤로가기와 검색 버튼이 있다.
  • 검색 조건을 선택하고 검색 내용을 작성한 뒤, 검색 버튼을 클릭
  • 조건에 맞는 데이터를 불러온다.

구현

돋보기 모양 검색 버튼을 만든다.

돋보기 아이콘은 react-icons를 사용했다. 아이콘이 필요할 때 제일 사용하기 간편한 것 같다.


돋보기 클릭하면 모달창 띄운다.

모달창에는 검색 조건을 선택할 수 있고 검색 내용을 적는 곳이 있다. 그리고 뒤로가기와 검색 버튼이 있다.
모달창은 내가 직접 만들었다. 어렵지 않게 직접 만들 수 있다.
모달 구현이 궁금하다면 [Next.js] 모달창 정말 쉽게 구현하기


검색 조건에 해당하는 부분은 Material UI를 사용

내가 필요한 것은 검색 조건을 선택하는 것이니 Material UI에 들어가서 Select에 관한 UI를 보고 내가 사용할 UI를 선택했다.
Material UI - select
나는 small size의 select를 선택했다.
아래와 같이 구현 코드가 적혀있기 때문에 필요한 라이브러리를 다운받고 내 프로젝트에 잘 적용만 시키면 된다.

내 프로젝트에 적용


검색 조건을 선택하고 검색 내용을 작성한 뒤, 검색 버튼을 클릭하고 조건에 맞는 데이터를 불러온다.

검색 조건과 검색 내용을 검색결과 페이지 url의 path와 query에 담아 보낸다.

주의할 점

이전에는 router.push를 작성할 때 path에만 조건을 적어봤는데, 이번에는 path와 query를 모두 담아내는 상황이다.
그럴 때는 아래와 같이 중괄호를 열고 pathname과 query를 따로 적어줘야 한다.

router.push({
  pathname: `/community/feed/search/${type}`,
  query: {
    search: search,
    page: 1,
  },
})

적용 코드

// FeedSearchMoadl.tsx
import InputLabel from '@mui/material/InputLabel'
import MenuItem from '@mui/material/MenuItem'
import FormControl from '@mui/material/FormControl'
import Select, { SelectChangeEvent } from '@mui/material/Select'
import { useState } from 'react'
import { SearchModalBox } from './FeedSearchModal.style'
import { toast } from 'react-toastify'
import router from 'next/router'

const FeedSearchModal = (props) => {
    const { clickModal } = props
    
    // 검색 내용
    const [search, setSearch] = useState('')
    const handleChange = (e) => setSearch(e.target.value)
    // 검색 조건
    const [type, setType] = useState('')
    const typeChange = (e: SelectChangeEvent) => setType(e.target.value)

    const clickSearch = async () => {
      	// 검색 내용과 조건이 설정되어 있을 경우에만 검색
        if (type && search) {
            try {
                clickModal()
                router.push({
                    pathname: `/community/feed/search/${type}`,
                    query: {
                        search: search,
                        page: 1,
                    },
                })
            } catch (e) {
                console.error(e.response)
            }
        } else if (!type) toast.error('검색 조건 설정해주세요')
        else toast.error('검색어 입력해주세요')
    }
    return (
        <SearchModalBox onClick={clickModal}>
            <div onClick={(e) => e.stopPropagation()}>
                <FormControl size='small'>
                    <InputLabel id='demo-select-small'>검색</InputLabel>
                    <Select labelId='demo-select-small' id='demo-select-small' value={type} label='type' onChange={typeChange}>
                        <MenuItem value={'title'}>제목</MenuItem>
                        <MenuItem value={'content'}>내용</MenuItem>
                        <MenuItem value={'titleAndContent'}>제목+내용</MenuItem>
                        <MenuItem value={'writer'}>작성자</MenuItem>
                    </Select>
                </FormControl>
                <input type='text' placeholder='검색' onChange={handleChange} />
                <div>
                    <button onClick={clickModal}>뒤로가기</button>
                    <button onClick={clickSearch}>검색</button>
                </div>
            </div>
        </SearchModalBox>
    )
}

export default FeedSearchModal

조건에 맞는 데이터를 불러온다.

router.query에서 아까 보낸 검색 조건과 검색내용을 받는다.
그리고 그 값을 이용하여 검색 결과를 불러온다.

구현코드

// FeedSearchResult.tsx
import { FeedContainer } from '../Feed.style'
import FeedPagination from '../FeedPagination'
import FeedRemote from '../FeedRemote'
import FeedSearchList from './FeedSearchList'
import LoadingLogo from '../../../Common/Loading/LoadingLogo'
import { RootState } from '../../../../store/store'
import { toast } from 'react-toastify'
import useGetFeedSearchData from '../../../../react-query/CommunityData/FeedSearchData'
import { useRouter } from 'next/router'
import { useSelector } from 'react-redux'
import { useState } from 'react'

const FeedSearchResult = () => {
    const accessToken = useSelector((state: RootState) => state.token.token)
    const router = useRouter()
    const { type, search } = router.query
    const [currentPage, setPageValue] = useState(1)
    // react-query를 이용하여 데이터 불러옴
    const { data, isLoading } = useGetFeedSearchData(type, search, currentPage)
    const clickWrite = () => {
        if (accessToken) router.push('/community/write')
        else toast.error('로그인이 필요합니다!')
    }

    return (
        <>
            {!isLoading ? <FeedContainer>
                <p>검색 : {search}</p>
                <FeedSearchList searchList={data?.data.searchResult.responseContents} />
                <FeedRemote clickWrite={clickWrite} />
                <FeedPagination currentPage={currentPage} setCurrentPage={setPageValue} maxPage={data?.data?.searchResult?.totalPage} />
            </FeedContainer> : <LoadingLogo />}
        </>
    )
}

export default FeedSearchResult

완성!

더 많은 코드를 보고 싶다면

프론트엔드 코드 github

백엔드 코드를 보고 싶다면

같이 협업한 백엔드 코드 개발자 github

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

0개의 댓글