Learning Log/React

[react] 검색 기능 구현하기 + mock 데이터 사용법

자척개 2023. 2. 3. 18:28
반응형

인스타그램의 메뉴바를 클론 코딩했습니다

 

메뉴바 이미지

 

 

완성된 js 파일은 게시물 하단에서 확인하세요

아래 파일을 통해 구현한 메뉴바의 모습입니다

 

키워드 입력 전

 

키워드 입력 후

 

JSX 부분

    <nav className="nav">

 

// 검색창 왼쪽 로고 부분

     <div className="navLeft">
        <Link className="logo">
          westagram
        </Link>
      </div>

 

// 검색창 부분
      <div className="searchWrapper">
        <input
          className="searchBar"
          type="search"
          value={keyword}
          placeholder="검색"
          onChange={onChange}
        />
        <ul className="searchedLists">

// keyword를 입력하지 않으면 검색결과가 나오는 창이 보이지 않도록 했습니다
          {keyword.length > 0 && (
            <>
              {filteredList.map(profile => (
                <li className="searchedList" key={profile.id}>
                  <img
                    className="searchedImg"
                    alt="Profile Img"
                    src={profile.profileImg}
                  />
                  <span className="searchedId">{profile.profileId}</span>
                </li>
              ))}
            </>
          )}
        </ul>
      </div>

 

// 검색창 오른쪽 아이콘 부분
      <div className="navRight">
        <img
          className="navImg"
          alt="Explore Img"
          src="이미지 주소"
        />
        <img
          className="navImg"
          alt="Heart Img"
          src="이미지 주소"
        />
        <img
          className="navImg"
          alt="Profile Img"
          src="이미지 주소"
        />
      </div>

    </nav>


 

js 문법 부분

  const [profiles, setProfiles] = useState([]);
  const [keyword, setKeyword] = useState('');

 

// mock data이기 때문에 fetch로 불러왔습니다
  useEffect(() => {
    fetch('/data/profileData.json')
      .then(response => response.json())
      .then(json => setProfiles(json));
  });

 

// keyword 변화 감지하는 함수
  const onChange = e => {
    setKeyword(e.target.value);
  };

 

// keyword 입력 시 mockdata와 비교하여 일치하는 리스트 보여주는 함수
  const filteredList = profiles.filter(profile =>
    profile.profileId.toLowerCase().includes(keyword.toLowerCase())
  );

 


 

CSS 부분

nav.scss

 

@import './mixin.scss'

 

.searchWrapper {

// 기본 position이 relative이기 때문에 이 위치에 따라 아래의 absolute들이 배치됩니다
    @include flex(center, center);
    flex-direction: column;

    .searchBar {

// search bar와 를 absolute로 지정해줘야 페이지의 크기와 관계없이 일정한 position을 유지할 수 있씁니다
      position: absolute;
      top: 36%;
      width: 30%;
      height: 30px;
      padding: 2px 10px 2px 35px;
      border: 1px solid lightgray;
      border-radius: 5px;
      background: url('이미지 주소') center
        no-repeat;
      background-position: 10px center;
      color: gray;
      text-align: left;
    }

    .searchBar:focus {
      outline: none;
    }

    .searchedLists {

// searchedList를 absolute로 지정해줘야 검색결과를 보여주는 곳의 위치를 검색창 위치에 맞게 조정할 수 있습니다
      @include flex(left, null);
      position: absolute;
      top: 65%;
      flex-direction: column;
      width: 30%;
      height: auto;
      border-radius: 5px;
      background-color: white;
      border: 1px solid lightgray;
      border-top: none;

      .searchedList {
        @include flex(left, center);
        padding: 7px 10px 7px 10px;

        .searchedId {
          padding-left: 10px;
          font-size: 14px;
          font-weight: bold;
          white-space: no-wrap;
        }
      }
    }
  }

 

// 저는 mixin.scss 파일을 따로 분리해서 만들었습니다

mixin.scss

 

@mixin flex($justify, $align) {
  display: flex;
  justify-content: $justify;
  align-items: $align;
}


 

Data 부분(mock data)

profileData.js

 

// 이 형태로 여러 개의 객체를 만들었습니다

[
  {
    "id": 1,
    "profileId": "아이디",
    "profileImg": "이미지 주소",
    "follower": null,
    "time": null,
    "following": true
  }

]


 

최종 js 파일

import { useEffect, useState } from 'react';
import { MENU_DATA } from '../Data/MenuData';
import './Nav.scss';

function Nav() {
  const [profiles, setProfiles] = useState([]);
  const [keyword, setKeyword] = useState('');

  useEffect(() => {
    fetch('/data/profileData.json')
      .then(response => response.json())
      .then(json => setProfiles(json));
  });

  const onChange = e => {
    setKeyword(e.target.value);
  };

  const filteredList = profiles.filter(profile =>
    profile.profileId.toLowerCase().includes(keyword.toLowerCase())
  );

  return (
    <nav className="nav">
      <div className="navLeft">
        <img
          className="logoImg"
          alt="Insta Logo"
          src="이미지 주소"
        />
        <span className="fontBold">|</span>
        <Link className="logo" to="/login">
          westagram
        </Link>
      </div>

      <div className="searchWrapper">
        <input
          className="searchBar"
          type="search"
          value={keyword}
          placeholder="검색"
          onChange={onChange}
        />
        <ul className="searchedLists">
          {keyword.length > 0 ? (
            <>
              {filteredList.map(profile => (
                <li className="searchedList" key={profile.id}>
                  <img
                    className="searchedImg"
                    alt="Profile Img"
                    src={profile.profileImg}
                  />
                  <span className="searchedId">{profile.profileId}</span>
                </li>
              ))}
            </>
          ) : null}
        </ul>
      </div>

      <div className="navRight">
        <img
          className="navImg"
          alt="Explore Img"
          src="이미지 주소"
        />
        <img
          className="navImg"
          alt="Heart Img"
          src="이미지 주소"
        />

      </div>
    </nav>
  );
}

export { Nav };

반응형