카테고리 없음

Day 77 - Homework, React 비동기 통신

건우정 2024. 10. 25. 10:52


데이터 읽기, 쓰기

웹 스토리지

HTML5에서 추가된 기술로 로컬스토리지와 세션스토리지로 구분된다.

특징
- 웹 스토리지는 Key와 Value 형태로 이루어졌다.
- 웹 스토리지는 클라이언트에 대한 정보를 저장한다.
- 웹 스토리지는 로컬에만 정보를 저장하고 쿠키는 서버와 로컬에 정보를 저장한다.
(쿠키는 서버에 저장된다기보단, 클라이언트와 서버 간의 통신에 이용된다는 의미이다.)

종류
로컬스토리지 (localStorage) - 클라이언트에 대한 정보를 영구적으로 저장
세션스토리지 (sessionStorage) - 세션 종료 시(브라우저 닫을 경우) 클라이언트에 대한 정보 삭제

장점
- 서버에 불필요하게 데이터를 저장하지 않는다. (백엔드에 절대로 전송되지 않는다.)
- 저장 가능한 데이터의 용량이 크다. (약 5Mb, 브라우저마다 차이 존재)

단점
- HTML5를 지원하지 않는 브라우저의 경우 사용 불가. (현재는 거의 없다고 봐야 한다.)


Homework  - TodoList 만들기

- 강사님 답 -
 
Todos.jsx

import React, { useEffect, useRef, useState } from 'react';
import TodoForm from './TodoForm';
import TodoList from './TodoList';

import styles from '../css/Todos.module.css';

const Todos = () => {
    //const [list, setList] = useState([]);

    //로컬 스토리지 읽어오기
    const [list, setList] = useState(JSON.parse(localStorage.getItem('list')) || []);

    //로컬 스토리지 저장하기 - list(배열)이 바뀔때마다 저장
    useEffect(() => {
        localStorage.setItem('list', JSON.stringify(list))
    }, [list]);

    const seq = useRef(1);

    const onAdd = (text) => {
        setList([
            ...list,
            {
                seq: seq.current++,
                text
            }
        ])
    }

    const onDel = (seq) => {
        setList(
            list.filter(item => item.seq !== seq)
        )
    }

    return (
        <div className={ styles.Todos }>
            <h1>일정관리</h1>
            <TodoForm onAdd={ onAdd } />
            <TodoList list={ list } onDel={ onDel } />
        </div>
    );
};

export default Todos;

 

데이터를 로컬 스토리지에 저장하는 방식 

	//로컬 스토리지 읽어오기
    const [list, setList] = useState(JSON.parse(localStorage.getItem('list')) || []);

    //로컬 스토리지 저장하기 - list(배열)이 바뀔때마다 저장
    useEffect(() => {
        localStorage.setItem('list', JSON.stringify(list))
    }, [list]);

 
1. list 정보를 로컬스토리지에서 불러오기
    - useState의 디폴트 값을 로컬 스토리지에서 getItem으로 'list' (key 값)
      list 목록의 값을 불러와서 JSON 형태로 가져오기
    - string(로컬스토리지) -> JSON(자바스크립트)

2. list 정보를 로컬스토리지에 저장하기
   - useEffect를 사용하여 list 값에 변경이 생길 때 마다 setItem으로 로컬스토리지에
     저장하기
   - JSON(자바스크립트) -> string(로컬스토리지) 
 
TodoForm.jsx

import React, { useRef, useState } from 'react';

import styles from '../css/TodoForm.module.css';

const TodoForm = ({ onAdd }) => {
    const [text, setText] = useState('');

    const textRef = useRef();

    const onInput = (e) => {
        setText(e.target.value);
    }

    const onSubmit = (e) => {
        e.preventDefault();

        if(!text) return;
        onAdd(text); //입력한 text를 list에 추가
        setText('');

        textRef.current.focus();
    }

    return (
        <div className={ styles.TodoForm }>
            <form onSubmit={ onSubmit }>
                <input type='text' name='text' value={ text } onChange={ onInput } ref={ textRef }
                   placeholder='해야 할 일을 입력하시오' />
                <button type='submit'>추가</button>
            </form>
        </div>
    );
};

export default TodoForm;

 
TodoList.jsx

import React from 'react';
import TodoItem from './TodoItem';

import styles from '../css/TodoList.module.css';

const TodoList = ({ list, onDel }) => {
    return (
        <ul className={ styles.TodoList }>
            {
                list.map(item => <TodoItem key={ item.seq } item={ item } onDel={ onDel } />)
            }
        </ul>
    );
};

export default TodoList;

 
 
TodoItem.jsx

import React from 'react';

const TodoItem = ({ item, onDel }) => {
    const { seq, text } = item;

    return (
        <li>
            { text }
            <button onClick={ () => onDel(seq) }>삭제</button>
        </li>
    );
};

export default TodoItem;

 
 

 
F12 눌러서 응용 프로그램을 누르면, 로컬 저장소에서 로컬 스토리지에 존재하는 값을 볼 수 있다.
키 아래에 list 에서 오른쪽 마우스를 누르면 Delete로 값을 삭제 가능


 
비동기 통신 - ajax

서버에 새로고침 없이 요청할 수 있게 도와준다.
서버로 네트워크 요청을 보내고 응답을 받을 수 있도록 도와준다.

jQuery - $.ajax()
js - fetch()
     fetch() -> json 형식으로 가져온다.
설치 - axios
       axios.get() -> object 형식으로 가져온다.

외부 API 비동기 통신을 위해서 fetch()를 이용한다.
fetch()에 API 경로를 적어주면 promise가 반환된다.
fetch( url, [options] )

fetch(url)
.then(콜백) - 응답 성공
.catch(콜백) - 응답 실패

axios.get(url)
     .then(콜백) - 응답 성공
     .catch(콜백) - 응답 실패
 
동기 vs 비동기
 
동기 : 요청을 하면 응답이 올때까지 그 아래 코드를 실행하지 않고 기다린다.
위에꺼를 처리하고 끝나야만 아래꺼를 순서대로 처리
 
비동기 : 요청을 하고 응답이 올 때까지 기다리지 않고 그 아래 코드를 바로 실행한다.
언젠가 지가 받겠지 응답이 오겠지.. 응답이 오면 그때 가서 다시 실행한다.
 

 
 
요청에 대한 응답의 값을 받아서 밑에서 활용하는 경우에는 동기를 쓰고,
 
int a = 어딘가 무언가를 요청해서 값을 받는다..
System.out.println(a); // 결과 값을 출력한다.
 
요청에 대한 응답을 값을 받아서 밑에서 활용하지 않는 경우에는 비동기를 쓴다.
 
int a = 어딘가 무언가를 요청해서 값을 받아서 html에 a 값을 찍는다..
ajax 요청이 비동기인 이유가 서버에 게시글 목록을 요청해서
응답이 오면 화면에 띄워줄 뿐이지 게시글 목록이 다른 연산에서 활용되지 않는다.
즉, 응답이 언제 오든 상관이 없는 경우 비동기 통신을 쓴다.
 
axios 설치
 
npm install axios / yarn add axios

위의 명령어 둘 중 하나로 axios를 설치해야 import 해서 쓸 수 있다.
수업에서는 첫번 째로
 

 
 
 

 

 


 

workspace - day05

1. day05 폴더 생성
- npx create-react-app day05
2. App.js 다 지우고 rsc 생성 후 <Test01/> 추가하기
3. components 폴더 생성 - Test01.jsx 파일 만들기
 


npm start로 서버 실행
 
 
JSONPlaceholder - Free Fake REST API

JSONPlaceholder - Free Fake REST API

{JSON} Placeholder Free fake and reliable API for testing and prototyping. Powered by JSON Server + LowDB. Serving ~3 billion requests each month.

jsonplaceholder.typicode.com

영어 버전
 
https://koreanjson.com/

Korean JSON

{ Korean JSON } Super simple JSON API in Korean. Request GET, POST, PUT, DELETE actions and get JSON data in Korean back to get the most out of the look and feel of Korean language when prototyping your project. 한국어 데이터를 제공하는 초간

koreanjson.com

 
한국어 버전
 
서버에 요청하고 JSON 데이터 값을 받아 오는 것을 테스트 해볼 수 있는 사이트이다.
 


위의 사진이 fetch 요청의 예시이다.
제이쿼리에서 ajax 요청 처럼
리액트에서 클라이언트에서 서버로 데이터를 요청하는 것이다.
리액트는 제이쿼리를 사용하지 않으므로 ajax 또한 쓸 수 없다.
 
제이쿼리에서 요청 방법 - ajax
리액트에서 요청 방법 - axios , fetch
 

 
현재 페이지의 URL에 /posts를 추가로 뒤에 붙이면 100개 게시글 목록을 요청해서 받을 수 있다.
 

 
브라우저에서는 이렇게 뜨지만,  이것을 리액트에서 받아서 화면에 출력해야함
 
 

 
ajax에서 success와 fetch 에서 then이 같은 의미이다.
 
응답이 성공적으로 도착한 경우 실행하겠다는 의미이다.
 
우리가 스프링 부트로 서버를 만들어서 켜두면 위처럼 클라이언트 측에서요청을 해서 결과를 받을 수 있는 것이다.
 

 
이때, userId는 작성자 id이고 중복되므로,
게시글을 식별가능한 key값은 id이다.
 
Test01.jsx에 

import axios from 'axios';

import 해야 axios를 쓸 수 있다.
 

첫 번째는 fetch 요청 예시
res는 response 이고 response를 json 으로 먼저 변환해주고, setList로 값을 세팅한다.
 

 
두번 째는 axios 요청 예시
res는 response 이고 axios는 바로 객체로 가져오기 때문에 따로 json으로 변환을 안해줘도 된다.

그리고, res.data 이렇게 써줘야만 응답 결과를 가져올 수 있다. (쩜 data는 우리가 만든게 아니라 미리 만들어진 변수임)

세 번째는 async + fetch 요청 예시

네 번째는 async + axios 요청 예시
 

 
 
async + await 에 관한 것은 위의 답변으로 대신
 
Test01.jsx 전체 코드

import React, { useEffect, useState } from 'react';
import axios from 'axios';

const Test01 = () => {

    const [list, setList] = useState([]);

    // 화면이 처음 로딩 될 때 한 번만
    // URL : /posts 로 GET 요청
    /* 첫번째 방법
    useEffect(() => {
      fetch('https://jsonplaceholder.typicode.com/posts')
      .then(res => res.json())
      .then(res => setList(res))
    }, []); 
    */

    /* 두번째 방법
    useEffect(() => {
        axios.get('https://jsonplaceholder.typicode.com/posts')
        .then(res => setList(res.data)) // axios는 바로 객체로 가져옴 , data는 지정된 이름
      }, []); 
    */
    // 세번째 방법
    /*
    useEffect(() => {
        const getData = async() => {
            const res = await fetch('https://jsonplaceholder.typicode.com/posts')
            const data = await res.json()
            setList(data)
        }

        getData() // 호출
    }, []); 
    */
    //네번 째 방법
    useEffect(() => {
        const getData = async() => {
            const res = await axios.get('https://jsonplaceholder.typicode.com/posts')
            setList(res.data)
        }

        getData() // 호출
    }, []); 


    return (
        <div>
            <ul>
                {
                    list.map(item => <li key = {item.id}>
                        {item.id} / {item.title} 

                    </li>)
                }
            </ul>
        </div>
    );
};

export default Test01;

 

실행결과

Test02.jsx 생성 + app.js에 추가
 /posts/3
이러면 3번 게시글을 가져온다.
/posts?id=3과 유사한 방법

이번에는 특정 게시물 1개만 가져와보자

 
id 값을 입력하면 상태 변수 id 값이 설정되고 검색 버튼이 눌리면
onSearch 함수로 search 상태 변수에도 검색할 id 값이 설정된다. 
 

useEffect 를 활용해서
id 또는 search 값이 바뀔 때 마다 id값으로 axios 요청을 하여 결과 값으로 dto를 화면에 띄운다.
[id] 인 경우에는 입력을 할 때 마다 즉각즉각 결과가 변하고,
[search]인 경우에는 검색 버튼을 눌렀을 때만 바뀐다.
 
근데 문제가 useEffect가 id에 걸리면
75를 입력하면 7이 들어오면 바로 7번 게시글이 나오고,
75까지 입력해야 75가 정상적으로 나온다.
그래서 검색 버튼을 추가해서 useEffect 기준을 search 상태 변수 값이 바뀔 때로 하고자 하는 것이다. 

참고로, 마지막에 [search]여기서 [] 안에는 상태 변수 밖에 못온다
 
Test02.jsx

import React, { useEffect, useState } from 'react';
import axios from 'axios';

const Test02 = () => {

    const [dto , setDto] = useState({}); // 빈 객체 생성

    const [id , setId] = useState(3);

    const [search , setSearch] = useState(3);

    useEffect(() => {
           
            axios.get(`https://jsonplaceholder.typicode.com/posts/${id}`)
            .then(res => setDto(res.data))
    //}, [id]); // id값이 바뀔 때 마다 움직인다. 75이면 7 75 두번이라 검색 버튼으로 변경하기
    }, [search]); //검색 버튼이 눌릴 때 마다 처리로 변경
    //  }, [id, search]); // 비추천
    const onSearch = (id) => {
        setSearch(id);
    }
    return (
        <div>
            <label>1 ~ 100까지의 id를 입력 : </label>
            <input type='text' value = { id } onChange={ e => setId(e.target.value)} />
            <button onClick = { () => onSearch(id) }>검색</button>
            <hr/>
                {dto.id} / {dto.title}
        </div>
    );
};

export default Test02;


// 비추천인 이유는
useEffect (()=>{
.... }, [id, search] ) 에서

id 값이 바뀔 때마다 불러올 것이므로
search 버튼이 무의미함

 
 
Movies.jsx 파일 생성 + App.js 주석 변경
 
[문제] 날짜별 1위부터 10위까지의 영화 제목 검색
=> https://kobis.or.kr/kobisopenapi/homepg/main/main.do

[결과]
날짜별 영화 랭킹 사이트

날짜 입력 (yyyymmdd) : 20241025
2024-10-25    베놈: 라스트 댄스
2.
3.
4.
5.
6.
7.
8.
9.
2024-10-25  룸 넥스트 도어
 
OPEN API 배너 클릭

 
URL 복사해서 뒤에 targetDt 날짜값만 바꾸면 된다.
 
해당 날짜에 영화 순위가 나옴

 
우리가 출력해야할 것은 영화 제목, 개봉일 + 순위(rank)까지
 

 
위와 같은 JSON 객체 구조에서는 바로 접근할 수 없다.
 
즉, boxOfficeResult 안에 dailyBoxOfficeList 안에 순위 목록이 있는 것이니
 

 
res.data.boxOfficeResult.dailyBoxOfficeList 하나씩 안으로 들어가야 한다.   

 
 

 
?는 값이 없으면 map을 수행(돌지말라는뜻)안함
 
Movies.jsx

import axios from 'axios';
import React, { useEffect, useState } from 'react';
import './css/Movies.css';

/*
const Movies = () => {

    const [date, setDate] = useState('20241024') // 디폴트 값
    const [search, setSearch] = useState('20241024') // 디폴트 값
    const [list, setList] = useState([]);
    const onSearch = (date) =>{
        setSearch(date)
    }
    
    useEffect(()=>{
        axios.get(`http://kobis.or.kr/kobisopenapi/webservice/rest/boxoffice/searchDailyBoxOfficeList.json?key=82ca741a2844c5c180a208137bb92bd7&targetDt=${search}`)
            .then(res=>setList(res.data.boxOfficeResult.dailyBoxOfficeList))
    },[search])
       
    return (
        <div>
            <h1>날짜별 영화 랭킹 사이트</h1>
            <span>날짜 입력 (yyyymmdd)</span>
            <input type='text' value={date} onChange={e=>setDate(e.target.value)}/> &emsp;
            <button onClick = {()=>onSearch(date)}>검색</button>
            <hr/>
            <h1>박스 오피스 목록</h1>
            <ul>
                {
                    list.map(item=><li key={item.rnum}>
                        {item.rank} 순위 : {item.openDt}  {item.movieNm}</li>)
                }
            </ul>
        </div>
    );
};
*/

const Movies = () => {
    const [date, setDate] = useState('20241024')
    const [dto, setDto] = useState({})
    const [search, setSearch] = useState('')

    const onSearch=(date)=>{
        setSearch(date)
    }

    useEffect(()=>{
        axios.get(`http://kobis.or.kr/kobisopenapi/webservice/rest/boxoffice/searchDailyBoxOfficeList.json?key=82ca741a2844c5c180a208137bb92bd7&targetDt=${search}`)
        .then(res => setDto(res.data))
    }, [search])

    return (
        <div>
            <h1>날짜별 영화 랭킹 사이트</h1>
            날짜입력 (yyyymmdd) : &emsp;
            <input type='text' value={ date } onChange={ (e) => setDate(e.target.value) } /> &nbsp;
            <button onClick={()=>onSearch(date)}>검색</button>
            <hr/>
            <ul>
                {
                    dto.boxOfficeResult?.dailyBoxOfficeList?.map(item => <li key={item.rnum}>
                        &emsp;{item.rnum}. {item.openDt} / {item.movieNm}
                    </li>)
                }
            </ul>
        </div>
    );
};

export default Movies;

 
위에 주석친 부분은 학생 답안
아래에 주석 없는 부분은 강사님 답안인데
아래껀
?를 설명하려고 하고자 주신 것이다.

위치
 
Movies.css
 

/*Movies.css*/ 
body {
    font-family: Arial, sans-serif;
    background-color: #f4f4f4;
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100vh;
    margin: 0;
}

.container {
    background-color: white;
    border-radius: 8px;
    box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.1);
    padding: 20px 40px;
    width: 400px;
    text-align: center;
}

h1 {
    color: #333;
    font-size: 1.5em;
}

input[type="text"] {
    padding: 10px;
    width: 70%;
    font-size: 1em;
    border: 1px solid #ddd;
    border-radius: 4px;
    margin-top: 10px;
}

button {
    background-color: #E60023;
    color: white;
    padding: 10px 15px;
    font-size: 1em;
    border: none;
    border-radius: 4px;
    cursor: pointer;
    margin-top: 10px;
    transition: background-color 0.3s ease;
}

button:hover {
    background-color: #c5001e;
}

ul {
    list-style-type: none;
    padding: 0;
    margin-top: 20px;
    text-align: left;
}

li {
    background-color: #f9f9f9;
    padding: 10px;
    margin-bottom: 5px;
    border-radius: 4px;
    box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.1);
}

span {
    font-size: 0.9em;
    color: #666;
}

TodoList Homework에서

새로고침하면 seq가 새롭게 랜더링되서 1부터 다시 시작하는 문제가 있음
 
이것도 정확한 방법은 아닌데
(삭제할 때 seq값이 또 꼬임)
나중에 DB가서 하자고 그냥 넘어남
Todos.jsx 밑줄 부분으로 변경

 



useMemo
리랜더링, 최적화
useMemo는 컴포넌트의 성능을 최적화시킬 수 있는 대표적인 react hooks 중 하나이다.
useMemo에서 Memo는 Memoization을 뜻한다.

memoization
기존에 수행한 연산의 결괏값을 어딘가에 저장해 두고 동일한 입력이 들어오면 재활용하는 프로그래밍 기법을 말한다.
최적화 기법

위의 2개가 다른 방법인 것이 아니라
useMemo 방법 안에 memorization 기법이 적용된 것이다.


Test03.jsx 생성
 

 
 
이렇게 하면 맨 처음에 0일 떄 짝수 결과만 띄우고 이후엔 값의 변화가 없음
count는 증가하지만 항상 짝수 출력한다.
 
useMemo 사용

 
Test03.jsx 전체 코드
 

import React, { useMemo, useState } from 'react';

const Test03 = () => {

    const [count, setCount] = useState(0);

    //count의 값은 계속 증가하지만, '짝수', '홀수' 결과는 바뀌지 않는다.
    /* useEffect 혹은 useMemo를 써야한다. 연산일때는 useMemo를 더 권장
    성능으로 최적화는 useMemo가 더 좋음
    const isEven = () => {
        return count % 2 === 0 // 짝수
    }
    */
   //사용자가 함수를 만들어서 return할 경우 return 값을 기억하기 때문에 결과가 
   //'짝수' 혹은 '홀수'가 나온다.
    const isEven = useMemo(() =>{
        return count%2 === 0
    }, [count]);


    return (
        <div>
            <h2>카운트 : {count} </h2>
            <button onClick = {()=> setCount(count+1)}>증가</button>
            <h2>
                결과 : { isEven ? '짝수' : '홀수'}
            </h2>
        </div>
    );
};

export default Test03;

 
 
useEffect 랑 useMemo 둘다 같은 기능인데
 
useMemo는 이미 연산된 값을 기억한다.
 
useMemo는 return이 있고
useEffect는 return이 없다.


Test04.jsx
Test04Sub.jsx 생성
 

console.log 두번 찍기 방지
 

 
SPA이므로 모든 페이지를 한 파일로 불러들여서 getColor getFood 로그가 둘다 뜸
즉, 음식을 바꿀 때 색깔 로그도 같이 뜨는 상황
getFood가 변경될때 같은 파일이므로 getColor도 가져옴
같은 파일이므로 food와 color이 같이 랜더링됨 그래서 로그도 둘다 뜬다.
 
useMemo 사용
 

 
food가 변경될 때 color값은 기억하고 있다가 불필요한 계산을 줄여 성능을 최적화 한다.
즉, getColor은 실행되지 않음.
useMemo를 통해 food가 변경될 때 color 값은 이전에 계산된 값을 그대로 유지하고,
getColor는 실행되지 않으므로 불필요한 계산이 발생하지 않습니다.
 
Test04.jsx

import React, { useState } from 'react';
import Test04Sub from './Test04Sub';

const Test04 = () => {

    const [color, setColor] = useState('');
    const [food, setFood] = useState('');

    return (
        <div>
            <h2>당신이 좋아하는 색은?</h2>
            <div>
                <select size = '5' style={{width : '120px'}} onChange={(e) => setColor(e.target.value)}>
                    <option value='hotpink'>hotpink</option>
                    <option value='magenta'>magenta</option>
                    <option value='skyblue'>skyblue</option>
                    <option value='tomato'>tomato</option>
                </select>
            </div>
            <hr/>
            <h2>당신이 좋아하는 음식은?</h2>
            <div>
                <p>
                    <input type='radio' name='food' value='햄버거' onChange={(e) => setFood(e.target.value)} />
                    <label>햄버거</label>
                </p>
                <p>
                    <input type='radio' name='food' value='삼겹살' onChange={(e) => setFood(e.target.value)} />
                    <label>삼겹살</label>
                </p>
                <p>
                    <input type='radio' name='food' value='치킨' onChange={(e) => setFood(e.target.value)} />
                    <label>치킨</label>
                </p>
                <p>
                    <input type='radio' name='food' value='짜장면' onChange={(e) => setFood(e.target.value)} />
                    <label>짜장면</label>
                </p>
            </div>
            <hr/>
            <Test04Sub color = { color } food = { food }/>
        </div>
    );
};

export default Test04;

 
 
 
Test04Sub.jsx

import React, { useMemo } from 'react';

const getColor = (color) => {
    console.log(color);

    switch(color){
        case 'hotpink':
            return '진분홍';
        case 'magenta' :
            return '보라';
        case 'skyblue' :
            return '하늘';
        case 'tomato' :
            return '토마토';         
    }
}

const getFood = (food) => {
    console.log(food);

    switch(food){
        case '햄버거':
            return '인스턴스';
        case '삼겹살' :
            return '돼지고기';
        case '치킨' :
            return '닭고기';
        case '짜장면' :
            return '면요리';         
    }
}

const Test04Sub = ({ color, food}) => {
    // 비추천, 색깔을 바꾸거나 또는 음식을 바꾸면, 
    //getColor , getFood 로그(색깔,음식 둘다)가 찍힌다.

    //해결 방법 
    //일반 함수가 아니라 useMemo를 사용하면
    //color가 바뀌면 getColor 로그만 찍고, food가 바뀌면 getFood 로그만 찍는다.

    
    //const colorInfo = getColor(color);
    //const foodInfo = getFood(food);

    const colorInfo = useMemo(()=>{
        return getColor(color);
    }, [color]);

    const foodInfo = useMemo(()=>{
        return getFood(food);
    }, [food]);


    return (
        <div>
            <h3>선택한 색 : {color}</h3>
            <h4>당신은 { colorInfo }을 좋아하는 군요!!</h4>

            <h3>선택한 음식 : {food}</h3>
            <h4>당신은 { foodInfo }을 좋아하는 군요!!</h4>
        </div>
    );
};

export default Test04Sub;

 
 


Test05.jsx 생성
 
검색 조건 :
1. 검색 단어를 포함하는 경우에 검색 
2. 대소문자 구분이 없음 

 
 

 
 
Test05.jsx 전체 코드

import React, { useEffect, useMemo, useState } from 'react';

const user = [
    { id : 1, name : '홍길동' },
    { id : 2, name : '장이수' },
    { id : 3, name : 'cat' },
    { id : 4, name : 'Daum' },
    { id : 5, name : '이제훈' },
    { id : 6, name : 'daum' },
    { id : 7, name : '마동석' },
    { id : 8, name : 'naver' },
    { id : 9, name : '이제훈' },
    { id : 10, name : 'NAVER' },
];
const Test05 = () => {

    const [list, setList] = useState(user);
    const [text, setText] = useState('');
    const [search, setSearch] = useState('');
    //검색 버튼
    /*
    const onSearch = () => {
        setList(
            //user.filter(item => item.name.toLowerCase().indexOf(text.toLowerCase()) !== -1)
            user.filter(item => item.name.toLowerCase().includes(text.toLowerCase()))
        );
    };
    */

    // 검색 버튼이 눌릴 때 마다 체크 - useMemo return이 있음
    
    useMemo(() => {
        return setList(user.filter(item => item.name.toLowerCase().includes(text.toLowerCase())));
    }, [search]);
    
    const onSearch = () => {
        setSearch(text);
    }

    //글자가 바뀌면 바로 바로 검색된다. return이 없음
    useEffect(() => {
        setList(user.filter(item => item.name.toLowerCase().includes(text.toLowerCase())));
    },[text]);

    return (
        <div>
            <input type = 'text' value = {text} onChange={ e => setText(e.target.value)}/> &nbsp;
            <button onClick = {onSearch}>검색</button>
            <ul>
                {
                    list.map(item=><li key = {item.id}>
                        {item.id}. {item.name}
                    </li>)
                }
            </ul>
        </div>
    );
};

export default Test05;

 

 
위의 기존의 방식으로 검색 버튼이 눌렀을 때만 동작한다.
 



이것을 useEffect와 useMemo를 활용해서 해보면,
 

 
우선, useMemo는 반복된 연산을 기억해서 빠르게 가져오는 캐싱 기능이 있다.
그리고 위의 사진에는 search 버튼이 눌렀을 때 검색 결과가 동작한다.

1. 검색 버튼 onClick에 의해 onSearch 동작
2. setSearch에 search 상태 변수에 text 값 세팅
3. search값이 변하므로 연쇄적으로 useMeno에 의해 setList 실행하여 목록 출력




 useEffect로 text값이 입력될 때마다 실행

 
useEffect를 사용한 경우에는 변경되는 값 기준이 text 상태 변수이므로

글자를 입력할 때 마다 바로 바로 검색 결과가 나온다.
위의 코드에서 둘다 주석 해제해놓았으니,
검색어를 입력할 때 마다, 검색 버튼
둘다 각각 작동할 것이다.