데이터 읽기, 쓰기
웹 스토리지
HTML5에서 추가된 기술로 로컬스토리지와 세션스토리지로 구분된다.
특징
- 웹 스토리지는 Key와 Value 형태로 이루어졌다.
"list" => Key
[{"seq":3, "text": "공부하기"}, ~~~] => Value
이런식으로 key- value 이렇게 되어있다.
웹스토리지에는 무조건 문자여 string 형식으로만 저장되므로
나중에 로컬스토리지에서 가져올 때는
로컬스토리지 string => 자바스크립트 json (json.parse) 이렇게 해서 가져오고
로컬스토리지에 저장할 때는
자바스크립트 json => 로컬스토리지 string (json.stringify) 이렇게해서 저장한다.
- 웹 스토리지는 클라이언트에 대한 정보를 저장한다.
- 웹 스토리지는 로컬에만 정보를 저장하고 쿠키는 서버와 로컬에 정보를 저장한다.
로컬에만 저장된다는 말은 집에있는 pc로 쓰면 없어진다는 말 / 절대 백엔드에는 전송이 되지 않는다.
(쿠키는 서버에 저장된다기보단,클라이언트와 서버 간의 통신에 이용된다는 의미이다.)
종류
로컬스토리지 (localStorage) - 클라이언트에 대한 정보를 영구적으로 저장
세션스토리지 (sessionStorage) - 세션 종료 시(브라우저 닫을 경우) 클라이언트에 대한 정보 삭제
장점
- 서버에 불필요하게 데이터를 저장하지 않는다. (백엔드에 절대로 전송되지 않는다.)
- 저장 가능한 데이터의 용량이 크다. (약 5Mb, 브라우저마다 차이 존재)
단점
- HTML5를 지원하지 않는 브라우저의 경우 사용 불가. (현재는 거의 없다고 봐야 한다.)
Todos.jsx
이제 로컬스토리지로 들어가기 때문에 여기있는 list로 들어가지 않으므로 주석 처리 !!
//const [list, setList] = useState([]);
로컬스토리지에 들어가는 시점이 언제?
버튼을 누를 때마다 들어가는데 즉, list의 내용이 바뀔 떄마다이다.
추가 버튼 submit을 누르게 되면 onAdd가 수행된다.
즉 버튼이 수행될 때, list 내용이 바뀔 때마다 로컬스토리지에 들어간다.
하지만 삭제될 때는 안들어가게 된다.
추가버튼이 수행될 때마다 하면 삭제할 때는 수행이 안되므로
list의 내용이 바뀔 때마다 로컬스토리지에 저장을 해달라 해야한다.
현재 변수값이 바뀔 때마다 처리하고싶으면!?
그럼 어제 배운 useEffect를 이용하는 것이다.
변수값이 바뀌거나 또는 내가 뭔가를 실행해서 마운트됐을 때나
컴포넌트를 꺼서 언마운트 됐을 떄 사용하는게 useEffect !!
1. list 정보를 로컬스토리지에 저장하기
//로컬 스토리지 저장하기- list(배열)이 바뀔 때마다 저장하겠다.
useEffect(() => {
localStorage.setItem('list', JSON.stringify(list)) //JSON 형식을 문자열로 변경해서 집어넣어라
}, [list]);
list의 내용이 바뀔 때마다 저장해라 !!
setItem(Key, Value) 이런식으로 들어오는 것이다.
useEffect를 사용하여 list 값에 변경이 생길 때 마다 setItem으로 로컬스토리지에
리스트를 JSON 형식의 문자열로 바꿔서 저장한다.
JSON(자바스크립트) -> string(로컬스토리지)
위에서 말한 것처럼 로컬스토리지에는 string값만 들어갈 수 있다 !!
setItem(Key, Value) 여기서 Key와 Value는 string만 가능하므로 !!
2. list 정보를 로컬스토리지에서 읽어오기
//로컬 스토리지 읽어오기
const [list, setList] = useState(JSON.parse(localStorage.getItem('list')) || []);
list 형태로 읽어오겠다.
원래는 랜더링할 때마다 현재 갖고있는 list의 상태변수를 초기화 시켰는데
이제는 초기화 시키는는게 아니라 로컬스토리지에 있는 값을 가져와야된다 !!
list 목록의 값을 불러와서 JSON 형태로 가져오기
처음 시작하면 리스트에 아무값도 없는데 가져오라 하면 에러를 뱉는다.
그러므로 거짓이라면 빈배열로 처리해라
|| 이건 OR연산자 아니다!! 거짓이라면 !! 이라는 뜻이다
앞에 조건이 틀리면 빈배열로 가져오라는 뜻 !
string(로컬스토리지) -> JSON(자바스크립트)
동기와 비동기의 차이
동기는 요청을하면 응답이 올 때까지 기다린다.
가장 대표적인게 자바의 채팅프로그램이다. 안녕하고 요청을 하면 반드시 응답을 해야한다.
그 전까지는 렉걸린 것처럼 먹통이 된다. 반드시 와야한다.
동기의 가장대표적인게 메일을 보내면
메일을 읽었는지 안읽었는지 수신을 확인할 수 있는게 동기이다.
이메일을 보낸다음에 수신확인할 수 있는게 동기이다.
요청이 가면 반드시 응답이 와줘야한다.
대신 비동기는 아니다.
요청을 보내면 언젠간 받겠찌 응답이 오겠지하고 그냥 처리해버리는 거
가장 큰 차이점은 내가 요청을 하면 반드시 올 때까지 기다리는 게 동기 멈춰버림.
응답이 오면 다시 프로그램을 수행한다.
동기는 순서가 있다
위에꺼를 처리한 다음에 아래꺼를 처리하겠구나가 동기이다.
가끔 동기가 편하기 때문에 우리가 공부하는 AXIOS를 비동기로 꺼버린다.
비동기는 순서가 없어서 내맘대로 안되니까~
비동기는 보내고 응답이 안와도 올거라 생각하고 그냥 가는 것이다.
여러분이 프로그램을 짤 때 조심해야할거는 우리가 요청을 하면 요청한 내용에 대해서 값이 돌아와야하는데
근데 그 값을 가지고 처리한다?는 위험하다.
비동기는 값이 올때까지 기다리지 않고 처리하므로
결과값을 기대하면 오작동이 일어난다.
비동기 통신 - ajax
서버에 새로고침 없이 요청할 수 있게 도와준다.
서버로 네트워크 요청을 보내고 응답을 받을 수 있도록 도와준다.
1. jQuery - $.ajax()
2. js - fetch()
fetch() -> json 형식으로 가져온다.
3. 설치 - axios
axios.get() -> object 형식으로 가져온다.
어떤 형식으로 가져와지는지 !! return 되는 타입 눈여겨보기 !!!
외부 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 해서 쓸 수 있다.
수업에서는 첫번 째로 했다 !!
그리고 다시 npm start를 해준다.
스프링부트에 요청을 하고싶으면 스프링부트에 요청을 하고 돌아오는게 axios이다.
첫번째 데이터를 가져올거기 때문에 const [list, setList]로 해서 받을 것이다.
const [list, setList] = useState([]);
https://jsonplaceholder.typicode.com/
JSON 배열로 가져오고 JSON 객체 여러개로 되어있다 !!
Test01이 무조건 화면에 뜨자마자 데이터를 가져오고 싶으면 useEffect를 써주는 것이다.
화살표 함수를 잡아서 한 번만 읽어라 하는 것
useEffect(() => {
fetch('https://jsonplaceholder.typicode.com/posts')
}, [ ]); // 1번만 부르자.
useEffect(() => {
fetch('https://localhost:8080/SpringProject/member')
}, []); // 1번만 부르자.
이렇게 하면 저 프로젝트에 있는 데이터 값을 부르는 것이다.
위에서 하는게 어색하다면 우리가 ajax에서 했던거를 떠올려보자 같은 역할이다 !
$.ajax({
type: 'post',
url: ' https://jsonplaceholder.typicode.com/posts ',
success: function(data){ };
}
첫 번째 방법
fetch
useEffect(() => {
fetch('https://jsonplaceholder.typicode.com/posts')
.then(res => res.json()) //json 형태로 데이터를 받아라 라는 말
.then(res => setList(res))
}, []); // 1번만 부르자.
then이 ajax의 success의 역할을 하는 것이다.
json 형태로 데이터를 받고
그걸 나중에 뿌리려면 setList 안에 넣어줘야한다.
너 데이터 받아왔니? then(받아왔으면) setList 안에 넣어줘라 라는 말!!
return (
<div>
<ul>
{
list.map(item => <li key={ item.id }>
{ item.id } / { item.title }
</li>)
}
</ul>
</div>
);
사이트에서 주는 데이터를 보면 userId 하나 id 하나 title 하나 body를 가져오는데
body는 너무 길고 userId는 다 똑같기 때문에
id랑 title을 찍은거
이렇게 100개 데이터가 찍힌다.
fetch는 항상 json형태로 가져온다.
두 번째 방법
axios의 get이라는 걸로 꺼내와볼 것이다.
import axios from 'axios';
useEffect(() => {
axios.get('https://jsonplaceholder.typicode.com/posts')
.then(res => setList(res.data))
}, []);
axios의 get은 object 형식으로 가져오기 때문에 변환하는 과정 필요없이 바로 집어넣어주면 된다.
세 번째 방법
await fetch 방법
async -- 비동기
useEffect(() => {
const getData = async() => {
const res = await fetch('https://jsonplaceholder.typicode.com/posts')
const data = await res.json()
setList(data)
}
getData() //호출
}, []);
getData 해서 데이터값을 가져오라는 함수를 잡아준다.
네 번째 방법
await axios
useEffect(() => {
const getData = async() => {
const res = await axios.get('https://jsonplaceholder.typicode.com/posts')
setList(res.data)
}
getData() //호출
}, []);
결과값은 다 똑같이 나온다.
전체코드
import axios from 'axios';
import React from 'react';
import { useEffect } from 'react';
import { useState } from 'react';
const Test01 = () => {
const [list, setList] = useState([]);
/*
useEffect(() => {
fetch('https://jsonplaceholder.typicode.com/posts')
.then(res => res.json())
.then(res => setList(res))
}, []); // 1번만 부르자.
*/
/*
useEffect(() => {
axios.get('https://jsonplaceholder.typicode.com/posts')
.then(res => setList(res.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
원래는 100개의 데이터가 다 들어오는데 저런식으로 백틱을 써서 id 값으로 해서 받으면 하나의 데이터만 받아와진다.
useEffect(() => {
axios.get(`https://jsonplaceholder.typicode.com/posts/${id}`)
.then(res =>)
}, []);
이런식으로 하나의 아이디값만 입력하면 하나의 json 객체만 받아와지는 거 확인할 수 있다.
const [dto, setDto] = useState({});
이제는 list로 받아오는게 아닌 하나의 값만 가져오므로 dto로 받아오면 된다.
import axios from 'axios';
import React from 'react';
import { useState } from 'react';
import { useEffect } from 'react';
const Test02 = () => {
const [dto, setDto] = useState({});
const [id, setId] = useState(3); //id가 3번인 값만 가져와라
useEffect(() => {
axios.get(`https://jsonplaceholder.typicode.com/posts/${id}`)
.then(res => setDto(res.data))
}, []);
return (
<div>
{ dto.id } / { dto.title }
</div>
);
};
export default Test02;
이렇게 하나의 값만 가져와지는 거 확인할 수 있다
import axios from 'axios';
import React from 'react';
import { useState } from 'react';
import { useEffect } from 'react';
const Test02 = () => {
const [dto, setDto] = useState({});
const [id, setId] = useState(3); //id가 3번인 값만 가져와라
const[search, setSearch] = useState(3);
useEffect(() => {
axios.get(`https://jsonplaceholder.typicode.com/posts/${id}`)
.then(res => setDto(res.data))
}, []);
return (
<div>
<label>1 ~ 100번까지의 id 입력 : </label>
<input type='text' value={ id } onChange={ e => setId(e.target.value) } />
<hr/>
{ dto.id } / { dto.title }
</div>
);
};
export default Test02;
지금은 아무리 데이터값을 입력해도 안바뀐다.
useEffect(() => {
axios.get(`https://jsonplaceholder.typicode.com/posts/${id}`)
.then(res => setDto(res.data))
}, [id]); //id의 값이 바뀔 때마다 움직인다.
그럼 입력을 할 떄마다 실시간으로 데이터 값이 불러와지는 것을 볼 수 있다.
즉 id의 값이 바뀔 떄마다 움직인다.
근데 75를 입력하게 되면 7 누를 때 5 누를 때 두 번이나 바뀌므로
검색버튼을 누를 때마다 바뀌도록 할 것이다.
import axios from 'axios';
import React from 'react';
import { useState } from 'react';
import { useEffect } from 'react';
const Test02 = () => {
const [dto, setDto] = useState({});
const [id, setId] = useState(3); //id가 3번인 값만 가져와라
const[search, setSearch] = useState(3);
useEffect(() => {
axios.get(`https://jsonplaceholder.typicode.com/posts/${id}`)
.then(res => setDto(res.data))
//}, [id]); //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에서 지금 search가 적혀있는 자리는 상태변수값밖에 못오니 저렇게 해야된다는거 확인 !!!!!!!!
useEffect(() => {
axios.get(`https://jsonplaceholder.typicode.com/posts/${id}`)
.then(res => setDto(res.data))
//}, [id]); //id의 값이 바뀔 때마다 움직인다.
//}, [search]); //검색 버튼을 누를 때마다
}, [id, search]); //id가 바뀔 때랑, 검색버튼 누를 때마다 바뀌는 것 !
이런식으로 id가 바뀔 때랑 검색버튼을 누를 때 두 번 다 바뀌게 할 수 있지만 여기서는 굳이~
Movies.jsx
[문제] 날짜별 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 룸 넥스트 도어
우리가 출력해야하는 것은 영화 제목, 개봉일, 순위이다.
import axios from 'axios';
import React, { useEffect, useState } from 'react';
import '../css/Movie.css';
const Movies = () => {
const [date, setDate] = useState('')
const [search, setSearch] = useState('')
const [list, setList] = useState([]);
const onSearch = (date) =>{
setSearch(date)
}
근데 여기서 잘 봐야할 거는
boxOfficeResult 안에 dailyBoxOfficeList가 있다는 점이다.
useEffect(()=>{
axios.get(`http://kobis.or.kr/kobisopenapi/webservice/rest/boxoffice/searchDailyBoxOfficeList.json?key=82ca741a2844c5c180a208137bb92bd7&targetDt=${search}`)
.then(res=>setList(res.data.boxOfficeResult))
},[search])
그러므로 이렇게만 하면 접근이 안되므로
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)}/>  
<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>
);
이 밑에 부분은 강사님이 ?의 사용법을 알려주고자 한거 ~~
return (
<div>
<h1>날짜별 영화 랭킹 사이트</h1>
<span>날짜 입력 (yyyymmdd)</span>
<input type='text' value={date} onChange={e=>setDate(e.target.value)}></input>
<button onClick = {()=>onSearch(date)}>검색</button>
<h1>박스 오피스 목록</h1>
<ul>
{
dto.boxOfficeResult?.dailyBoxOfficeList?.map(item=><li key={item.rnum}>
 {item.rnum}. {item.openDt} / {item.movieNm}
</li>)
}
</ul>
</div>
);
? 는 값이 없으면 map 수행을 돌리지말라는 뜻 ! 수행을 안한다.
즉, boxOfficeResult값이 있고, dailyBoxOfficeList 값이 있으면 map을 돌라~ 이런 말
대신 여기서는 list로 돌리는게 아니라 dto로 돌리는거임으로 상태변수도 같이 변경해주면 된다.
const Movies = () => {
const [date, setDate] = useState('20241024')
const [dto, setDto] = useState({})
const [search, setSearch] = useState('')
const onSearch=(date)=>{
setSearch(date)
}
전체코드
import axios from 'axios';
import React, { useEffect, useState } from 'react';
import '../css/Movie.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)}/>  
<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) :  
<input type='text' value={ date } onChange={ (e) => setDate(e.target.value) } />
<button onClick={()=>onSearch(date)}>검색</button>
<hr/>
<ul>
{
dto.boxOfficeResult?.dailyBoxOfficeList?.map(item => <li key={item.rnum}>
 {item.rnum}. {item.openDt} / {item.movieNm}
</li>)
}
</ul>
</div>
);
};
export default Movies;
Movie.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: 80%;
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;
}
Test03.jsx
Hooks - useMemo
- 리랜더링, 최적화
- useMemo는 컴포넌트의 성능을 최적화시킬 수 있는 대표적인 react hooks 중 하나이다.
- useMemo에서 Memo는 Memoization을 뜻한다.
memoization?
- 기존에 수행한 연산의 결과값을 어딘가에 저장해 두고 동일한 입력이 들어오면 재활용하는 프로그래밍 기법을 말한다.
위의 2개가 다른 방법인 것이 아니라
useMemo 방법 안에 memorization 기법이 적용된 것이다.
import React from 'react';
import { useState } from 'react';
const Test03 = () => {
const [count, setCount] = useState(0);
//count의 값은 계속 증가하지만, '짝수' / '홀수'의결과는 바뀌지 않는다.
const isEven = () =>{
return count%2 === 0
}
return (
<div>
<h2>카운트 : { count }</h2>
<button onClick={ () => setCount(count + 1) }>증가</button>
<h2>
결과 : { isEven ? '짝수' : '홀수' }
</h2>
</div>
);
};
export default Test03;
처음에만 결과 한 번 나오고 증가를 눌러도 결과값이 바뀌지 않는다.
count 값은 계속 증가하지만, 결과는 바뀌지 않는다.
왜냐 계속 함수를 호출하는게 아니므로 !!
그러므로 useEffect가 필요하다.
count값이 바뀌면 계속 바뀌게하라 해도되고
동일한 계산값 돌릴 때는 useMemo가 더 낫다 !!
연산을 잡을 때는 useMemo가 더 낫다.
//사용자가 함수를 만들어서 return할 경우 return 값을 기억하기 때문에 결과가 '짝수', '홀수'가 나온다.
const isEven = useMemo(() => {
return count%2 === 0
}, [count]);
count라는 상태변수가 바뀔 때마다 call된다.
사용자가 함수를 만들어서 return할 경우 return 값을 기억하기 때문에 결과가 '짝수', '홀수'가 나온다!!
useMemo랑 useEffect 둘 다 뭐 컴퓨터가 하는거니 아무거나 편한걸로 갖다쓰자~!!
useMemo는 이미 연산된 값을 기억한다는 것 !!
useMemo는 return이 있고
useEffect는 return이 없다.
Test04.jsx
import React from 'react';
import { 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>
<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>
</div>
<hr/>
<Test04Sub color={ color } food={ food }/>
</div>
);
};
export default Test04;
Test04Sub.jsx
import React from 'react';
const Test04Sub = ({ color, food }) => {
return (
<div>
<h3>선택한 색 : { color }</h3>
<h4>당신은 { colorInfo }을 좋아하는군요!!</h4>
<h3>선택한 음식 : { food }</h3>
<h4>당신은 { foodInfo }을 좋아하는군요!!</h4>
</div>
);
};
export default Test04Sub;
const colorInfo = getColor(color);
이거 잡아주고 함수바깥에 getColor함수를 선언해준다.
import React from 'react';
const getColor = (color) => {
console.log('getColor');
switch(color){
case 'hotpink':
return '진분홍'
case 'magenta':
return '보라'
case 'skyblue':
return '하늘'
case 'tomato':
return '토마토'
}
}
const Test04Sub = ({ color, food }) => {
잘 바뀌는거를 볼 수 있다.
console.log에 두 번 찍히는 거 막기위해 주석처리 하러가기 !!
디버그 처리해주면 이제 한 번만 나온다 !!
const foodInfo = getFood(food);
const getFood = (food) => {
console.log('getFood');
switch(food){
case '햄버거':
return '인스턴스'
case '삼겹살':
return '돼지고기'
case '치킨':
return '닭고기'
case '짜장면':
return '면요리'
}
}
const Test04Sub = ({ color, food }) => {
SPA라서 싱글로 잡혀있기 때문에 모든 페이지를 한 파일로 불러들기 떄문에 로그가 계속 두 개씩 찍히게 된다.
색깔만 바뀌어도 계속 getColor getFood 전부 다 불러진다.
이벤트 처리할 때 전부 다 나온다.
나중에 이벤트가 10개 정도되면 렌더링이 10개 다 일어나므로 성능면에서는 디게 안좋은 것이다 !!
그러므로 이런식으로 함수 호출하는건 비추천이다
색깔을 바꾸거나 또는 음식을 바꾸면, getColor, getFood 모든 로그가 다 찍힌다.(전부 렌더링한다)
해결방법
일반함수가 아니라 useMemo를 사용하면,
color가 바뀌면 getColor 로그만 찍고, food가 바뀌면 getFood 로그만 찍는다.
const colorInfo = useMemo(() => {
return getColor(color)
}, [color]);
const foodInfo = useMemo(() => {
return getFood(food)
}, [food]);
이런식으로 food만 누르면 getFood만 불러와지는거 알 수 있다!!
일반함수에 return값이 있으면 useMemo를 쓰는 것이 더 낫다 !! (useEffect보다!)
그러면 로그가 원하는 데에서만 나온다.
food가 변경될 때 color값은 기억하고 있다가 불필요한 계산을 줄여 성능을 최적화 한다.
즉, getColor은 실행되지 않음.
useMemo를 통해 food가 변경될 때 color 값은 이전에 계산된 값을 그대로 유지하고,
getColor는 실행되지 않으므로 불필요한 계산이 발생하지 않는다.
전체코드
import React from 'react';
import { 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>
<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>
</div>
<hr/>
<Test04Sub color={ color } food={ food }/>
</div>
);
};
export default Test04;
import React from 'react';
import { useMemo } from 'react';
const getColor = (color) => {
console.log('getColor');
switch(color){
case 'hotpink':
return '진분홍'
case 'magenta':
return '보라'
case 'skyblue':
return '하늘'
case 'tomato':
return '토마토'
}
}
const getFood = (food) => {
console.log('getFood');
switch(food){
case '햄버거':
return '인스턴스'
case '삼겹살':
return '돼지고기'
case '치킨':
return '닭고기'
case '짜장면':
return '면요리'
}
}
const Test04Sub = ({ color, food }) => {
//색깔을 바꾸거나 또는 음식을 바꾸면, getColor, getFood 모든 로그가 다 찍힌다.
//const colorInfo = getColor(color);
//const foodInfo = getFood(food);
//해결방법
//일반함수가 아니라 useMemo를 사용하면,
//color가 바뀌면 getColor 로그만 찍고, food가 바뀌면 getFood 로그만 찍는다.
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
indexOf (값이 없으면 -1값을 가져온다. 그걸로 체크해주면 된다.)
toLowerCase( ) -- 전부 다 소문자로 바꿔놓고 비교해주면 된다.
filter 써서 하면 된다.
import React from 'react';
import { 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 [text, setText] = useState('');
const [list, setList] = useState(user);
const [search, setSearch] = useState('');
//검색버튼
const onSearch = (e) => {
setList(
user.filter(item => item.name.toLowerCase().indexOf(text.toLowerCase()) !== -1)
)
}
return (
<div>
<input type='text' value={ text } onChange={ e => setText(e.target.value)}/>
<button onClick={ onSearch }>검색</button>
<ul>
{
list.map(item => <li key={ item.id }>
{ item.id } / { item.name }
</li>)
}
</ul>
</div>
);
};
export default Test05;
//검색버튼
const onSearch = (e) => {
setList(
//user.filter(item => item.name.toLowerCase().indexOf(text.toLowerCase()) !== -1)
user.filter(item => item.name.toLowerCase().includes(text.toLowerCase()))
)
}
indexOf 말고 includes로 해서 체크해도 된다 !!
//검색 버튼 - useMemo
useMemo(() => {
return setList(user.filter(item => item.name.toLowerCase().includes(text.toLowerCase())))
}, [search]);
const onSearch = () => {
setSearch(text);
}
글자가 바뀌면 바로바로 검색되게
네이버에서처럼 글자 입력하면 밑에 바로바로 뜨게 하는 것 !!
useEffect를 써보자 얘는 return이 없는거 중요
//글자가 바뀌면 바로바로 검색하게 하겠다.
useEffect(() => {
setList(user.filter(item => item.name.toLowerCase().includes(text.toLowerCase())))
}, [text]);
검색버튼 누르지 않아도 뜬다 !!
'REACT' 카테고리의 다른 글
DAY 78 - React HOMEWORK (2024.10.28) (0) | 2024.10.28 |
---|---|
DAY 78 - React - useReducer / React-Router/ JSX (2024.10.28) (0) | 2024.10.28 |
DAY 76 - React HOMEWORK (2024.10.24) (1) | 2024.10.24 |
DAY 76 - React - Hook: useEffect / 컴포넌트별 CSS (2024.10.24) (3) | 2024.10.24 |
DAY 75 - React / HOMEWORK (2024.10.23) (3) | 2024.10.23 |