컴포넌트의 Lifecycle 과 useEffect - 1
useEffect() 나 componentDidMount() 같은 함수를 쓰기 위해서는 컴포넌트의 lifecycle 을 알아야 한다. 컴포넌트의 인생을 알아야 컴포넌트 인생 중간에 간섭을 할 수 있기 때문이다.
컴포넌트의 인생은 3가지로 나뉜다.
1. 생성 (mount)
2. 재렌더링 (update)
3. 삭제 (unmount)
컴포넌트의 인생에 간섭을 하려면 'Hook' 이라는 걸 달아야 한다. 훅 안에 실행할 코드를 넣으면 mount / update / unmount 시에 코드를 실행해주는데 Lifecycle hook 이라고 부른다.
옛날 리액트에서 Lifecycle hook 쓰는 법
예전 class 문법으로 컴포넌트를 만들 때, 안에 함수명을 저렇게 써주면 각각 특정 lifecycle 에서 코드를 실행할 수 있다.
class Detail2 extends React.Component {
componentDidMount(){
//Detail2 컴포넌트가 로드되고나서 실행할 코드
}
componentDidUpdate(){
//Detail2 컴포넌트가 업데이트 되고나서 실행할 코드
}
componentWillUnmount(){
//Detail2 컴포넌트가 삭제되기전에 실행할 코드
}
}
요즘 리액트에서 Lifecycle hook 쓰는 법
상단에서 useEffect import 해오고, useEffect 안에 콜백함수를 추가해서 안에 코드를 적으면, 그 코드는 컴포넌트가 mount 되거나 update 될 때 실행된다.
import {useState, useEffect} from 'react';
function Detail(){
useEffect(()=>{
//여기적은 코드는 컴포넌트 로드 & 업데이트 마다 실행됨
console.log('안녕')
});
return (생략)
}
아래와 같이 코드를 짜면 재렌더링시에도 lifecycle hook 이 실행되는 것을 확인할 수 있다.
import {useState, useEffect} from 'react';
function Detail(){
useEffect(()=>{
//여기적은 코드는 컴포넌트 로드 & 업데이트 마다 실행됨
console.log('안녕')
});
let [count, setCount] = useState(0)
return (
<button onClick={()=>{ setCount(count+1) }}>버튼</button>
)
}
위의 코드를 실행시키면 mount 되거나 update 될 때마다 '안녕' 이 2번씩 출력이 되는 것을 확인할 수 있다. 이것은 index.js 파일에 <React.StrictMode> 라는 태그 때문에 일어나는 일로 디버깅이 편하도록 2번씩 출력하도록 만든 것인데, 싫다면 저 태그를 제거하면 된다.
useEffect 를 사용하는 이유
1번 처럼 useEffect 안에 실행할 코드를 적어주면 mount & update 때마다 적어준 코드를 실행한다고 했는데, 사실 2번처럼 useEffect 밖에 적어줘도 mount & update 때마다 코드가 실행된다. 컴포넌트가 mount & update 될 때마다 function 안에 있는 코드를 다시 읽고 지나가기 때문이다. 그렇다면 useEffect 가 필요한 이유가 무엇인지 알아보도록 하자.
// 1
function Detail(){
useEffect(()=>{
console.log('안녕')
});
return (생략)
}
// 2
function Detail(){
useEffect(()=>{
});
console.log('안녕')
return (생략)
}
useEffect 안에 적은 코드는 html 렌더링 이후에 동작한다.
만약 아래와 같이 반복문을 10억번 돌리는 시간이 아주 오래걸리는 코드를 useEffect 안과 밖에서 실행한다고 가정해보자.
위의 코드는 반복문 10억번을 모두 돌리고 난 후 html 렌더링이 시작되고, 아래의 코드는 html 렌더링이 된 후 반복문을 돌리기 시작한다.
즉, 조금이라도 html 렌더링이 빠른 사이트를 원하면 쓸데없는 코드들을 useEffect 안에 넣는 것이 훨씬 유용하다.
function Detail(){
(반복문 10억번 돌리는 코드)
return (생략)
}
function Detail(){
useEffect(()=>{
(반복문 10억번 돌리는 코드)
});
return (생략)
}
참고로 함수의 핵심기능 외에 쓸데없는 기능들을 프로그래밍 용어로 side effect 라고 부르는데, 리액트의 useEffect 훅의 이름도 여기서 따온 것으로 컴포넌트의 핵심기능인 렌더링 이외에 다른 기능들을 useEffect 안에 넣어 사용하라는 의미이다.
숙제:
- Detail 페이지 안에 노란 박스 하나 만들고
- Detail 페이지 방문 후 2초 후에 박스가 사라지도록 만들기
import React from "react";
import { useEffect, useState } from "react";
import { useParams } from "react-router-dom";
function Detail(props) {
let [visible, setVisible] = useState(true);
useEffect(()=>{
let timer = setTimeout(() => { setVisible(false) }, 2000)
return ()=> {
clearTimeout(timer)
}
}, [])
let {id} = useParams();
let item = props.shoes.find((x)=> x.id == id)
return (
<div className="container">
{
visible == true ?
<div className="alert alert-warning">2초이내 구매시 할인</div>
: null
}
<div className="row">
<div className="col-md-6">
<img src="https://codingapple1.github.io/shop/shoes1.jpg" width="100%" />
</div>
<div className="col-md-6">
<h4 className="pt-5">{ item.title }</h4>
<p>{ item.content }</p>
<p>{ item.price } 원</p>
<p></p>
<button className="btn btn-danger">주문하기</button>
</div>
</div>
</div>
);
}
export default Detail;
setTimeout 함수
setTimeout( ()=>{ 1초 후 실행할 코드 }, 1000);
컴포넌트의 Lifecycle 과 useEffect - 2
useEffect 에 넣을 수 있는 실행조건
useEffect() 둘째 파라미터로 [ ] 를 넣을 수 있는데, 중괄호 안에는 변수나 state 같은 값들을 들어간다. 그렇게 하면 중괄호 안에 들어있는 값이 변할 때만 useEffect 안의 코드를 실행해준다. 아래의 코드는 count 라는 값이 변할 때만 useEffect 안에 코드가 실행된다.
(참고로 [ ] 안에는 여러 개의 state 를 넣을 수 있다)
useEffect(()=>{ 실행할코드 }, [count])
[ ] 안에 아무 값도 넣어주지 않으면 컴포넌트 mount (로드시) 1회 실행하고 영영 실행해주지 않는다.
useEffect(()=>{ 실행할코드 }, [])
clean up function
useEffect 가 동작하기 전에 특정코드를 실행하고 싶으면 return() => { } 안에 넣으면 되는데, 이걸 clean up function 이라고 부른다.
useEffect(()=>{
그 다음 실행됨
return ()=>{
여기있는게 먼저실행됨
}
}, [count])
예를 들면 아래의 코드는 setTimeout() 을 쓸 때마다 브라우저 안에 타이머가 하나 생긴다. 근데 useEffect() 안에 썼기 때문에 컴포넌트가 mount 될 때마다 실행된다. 잘못 코드를 짜면 타이머가 여러 개 생길 가능성이 있다는 소리이다. 그렇기 때문에 버그를 방지하기 위해 useEffect 에서 타이머를 만들기 전에 기존 타이머를 제거하라고 코드를 짜면 되는데, return ()=>{ } 안에 짜면 그런 기능을 해준다.
※ 참고
- clearTimeout()
타이머를 제거하고 싶으면 clearTimeout(타이머) 함수를 사용하면 된다.
useEffect(()=>{
let a = setTimeout(()=>{ setAlert(false) }, 2000)
return ()=>{
clearTimeout(a)
}
}, [])
- clean up function 에는 타이머 제거, socket 연결요청제거, ajax 요청 중단과 같은 코드를 많이 작성한다.
- 컴포넌트 unmount 시에도 clean up function 안에 있던게 1회 실행된다.
useEffect 사용법 총정리
1. 재렌더링마다 코드 실행 (mount & update)
useEffect(()=>{ 실행할코드 })
2. 컴포넌트 mount (로드시) 1회만 실행
useEffect(()=>{ 실행할코드 }, [])
3. useEffect 안에 코드 실행 전에 항상 return 안에 코드 실행 (clean up function)
useEffect(()=>{
return ()=>{
실행할코드
}
})
4. 컴포넌트 unmount 시 1회 실행
useEffect(()=>{
return ()=>{
실행할코드
}
}, [])
5. state1 이 변경될 때만 실행
useEffect(()=>{
실행할코드
}, [state1])
숙제:
- <input> 하나 만들고 거기에 유저가 숫자 말고 다른걸 입력하면 "그러지마세요"라는 안내메세지를 출력
- 굳이 그럴 필요는 없겠지만 오늘 배운 useEffect 써보기
function Detail(){
let [num, setNum] = useState('')
useEffect(()=>{
if (isNaN(num) == true){
alert('그러지마세요')
}
}, [num])
return (
<input onChange={ (e) => { setNum(e.target.value) } } />
)
}
- isNaN(value)
isNaN(value) 함수는 주어진 값이 Not-a-Number 인지 여부를 판별하는 데 사용된다. NaN 은 산술 연산 결과가 숫자가 아닌 경우, 또는 숫자로 변환할 수 없는 경우 나타나는 특수한 값이다.
함수의 인자로 전달된 value 값이 NaN 인지 검사
- NaN 일 경우: true
- NaN 이 아닌 경우: false
그래서 위의 코드에서 숫자를 입력하면 isNaN 함수의 인자 값으로 숫자가 들어가게 되고 NaN 이 아니기 때문에 false 를 리턴해 if 문에 걸리지 않고 지나간다.
반면, 숫자 외에 다른 값을 입력하면 인자로 숫자 이외에 다른 값이 들어가게 되고 NaN 이기 때문에 true 를 리턴하게 되고 if 문에 걸려 경고문 알림창이 뜨게 된다.
리액트에서 서버와 통신: ajax
서버란?
서버는 유저가 데이터를 달라고 요청을 하면 데이터를 보내주는 간단한 프로그램이다. 서버에 데이터를 요청할 때는 정확한 규격을 맞춰서 요청해야 한다.
1. 어떤 데이터인지 (URL 형식으로)
2. 어떤 방법으로 요청할지 (GET or POST)
데이터를 가져올 때는 GET, 서버로 데이터를 보낼 때는 POST 를 사용한다. 그리고 어떤 데이터를 보고싶은지 URL 만 잘 기재하면 된다.
GET / POST 요청하는 법
GET 요청을 날리고 싶을 때 가장 쉬운 방법은 브라우저 주소창을 이용하는 것이고, POST 요청을 날리고 싶으면 <form action="요청할url" method="post"> 태그 이용하면 된다. 하지만 이 방법의 문제점은 브라우저가 새로고침된다는 것이다.
그래서 AJAX 라는 브라우저 기능을 사용해야 한다. AJAX 란 서버에 GET / POST 요청을 할 때 새로고침 없이 데이터를 주고 받을 수 있게 도와주는 간단한 브라우저 기능이다.
AJAX 로 GET / POST 요청을 하려면 방법 3개 중 1개를 택하여 사용할 수 있는데, 요즘은 가장 편한 3번 방법을 가장 많이 이용한다.
1. XMLHttpRequest라는 옛날 문법 쓰기
2. fetch() 라는 최신 문법 쓰기
3. axios 같은 외부 라이브러리 쓰기
axios 라이브러리 사용하기
먼저 터미널을 열어서 아래의 명령어를 입력하고 라이브러리를 설치한다.
npm install axios
AJAX 요청하는 법
버튼을 눌러 서버에 ajax 요청을 해보자.
import axios from 'axios'
function App(){
return (
<button onClick={()=>{
axios.get('https://codingapple1.github.io/shop/data2.json').then((결과)=>{
console.log(결과.data)
})
.catch(()=>{
console.log('실패함')
})
}}>버튼</button>
)
}
1. axios를 쓰려면 상단에서 import해오고
2. axios.get(URL) 을 하면 그 URL로 GET요청이 된다.
3. 데이터 가져온 결과는 결과.data 안에 들어있다.
그래서 위의 버튼 누르면 서버에서 가져온 데이터가 콘솔창에 출력됩니다.
4. 인터넷이 안되거나 URL이 이상하면 실패하는데
실패했을 때 실행할 코드는 .catch() 안에 적으면 된다.
숙제:
- 버튼을 누르면 서버에서 상품데이터 3개를 가져와서
- 메인페이지에 상품카드 3개를 더 생성해보자
let [shoes, setShoes] = useState(data);
<button onClick={()=>{
// 로딩중 UI 띄우기
axios.get('https://codingapple1.github.io/shop/data2.json')
.then((result)=>{
let copy = [...shoes, ...result.data];
setShoes(copy);
// 로딩중 UI 숨기기
})
.catch(()=>{
// 로딩중 UI 숨기기
console.log('실패함')
})
}}>버튼</button>
리액트에서 서버와 통신: ajax - post, fetch
응용 1. 버튼을 2회 누르면 7,8,9번 상품을 가져와서 html로 보여주려면?
응용 2. 버튼 3회 누르면 더 상품이 없다고 안내문을 띄우려면? (or 버튼 숨기기)
- useState 를 사용해서 click 횟수 기록해둠
- click 이 3 보다 커지면 alert 를 사용해 안내문 띄움 -> if 문에 걸려서 click 값이 더 증가하지 않아 없는 이미지 띄울 일 x
- click 이 3 이하일 땐 setClick(click+1)
let [click, setClick] = useState(1);
<button onClick={()=>{
{
if (click > 3) {
alert('상품이 존재하지 않습니다');
} else {
setClick(click+1);
}
}
axios.get('https://codingapple1.github.io/shop/data' + (click+1) + '.json')
.then((result)=>{
let copy = [...shoes, ...result.data];
setShoes(copy);
})
.catch(()=>{
console.log('실패함')
})
}}>버튼</button>
버튼 숨기기 버전
- button 의 유무를 결정할 button state 만듦, 처음에는 button 이 있기 때문에 초기값은 true 로 설정
- click 값이 3 이상이면 setButton 을 이용해 button 을 false 로 바꿔줌
- useEffect 를 사용해서 button 값이 바뀔 때 제렌더링 될 수 있도록 해줌
- 삼항연산자를 이용해 button 값에 따라 버튼이 있다가 없어지도록 듦
let [click, setClick] = useState(1);
let [button, setButton] = useState(true);
useEffect(() => {
}, [button])
{ button === false ? null :
<button onClick={()=>{
{
if (click >= 3) {
setButton(false);
} else {
setClick(click+1);
}
}
axios.get('https://codingapple1.github.io/shop/data' + (click+1) + '.json')
.then((result)=>{
let copy = [...shoes, ...result.data];
setShoes(copy);
})
.catch(()=>{
console.log('실패함')
})
}}>버튼</button>
}
응용 3. 버튼을 누른 직후엔 "로딩중입니다" 이런 글자를 주변에 띄우고 싶으면?
(요청이 성공하거나 실패하거나 그 후엔 "로딩중입니다" 글자를 제거해야한다.)
- api 에서 데이터를 불러올 때 버튼 위에 로딩중 글자가 뜨게 만듦
- loading 이라는 state 를 만들고 useEffect 로 loading 의 값이 바뀔 때마다 재렌더링 해줌
let [click, setClick] = useState(1);
let [button, setButton] = useState(true);
let loadingText = "로딩중";
let [loading, setLoading] = useState(false);
useEffect(() => {
}, [button], [loading])
{ loading === true ? <div>{loadingText}</div> : null }
{ button === false ? null :
<button onClick={()=>{
setLoading(true)
{
if (click >= 3) {
setButton(false);
} else {
setClick(click+1);
}
}
axios.get('https://codingapple1.github.io/shop/data' + (click+1) + '.json')
.then((result)=>{
let copy = [...shoes, ...result.data];
setShoes(copy);
setLoading(false);
})
.catch(()=>{
setLoading(false);
console.log('실패함')
})
}}>버튼</button>
}
POST 요청 하는 법
실행하면 서버로 두번째 인자로 넣은 값이 들어간다.
axios.post('URL', {name : 'kim'})
동시에 AJAX 요청 여러 개 날리기
동시에 요청이 필요할 때 아래처럼 사용하면 된다. 이렇게 하면 URL1, URL2 로 GET 요청을 동시에 수 있다.
Promise.all( [axios.get('URL1'), axios.get('URL2')] )
.then 메소드
then 메소드는 promise 객체가 fulfilled 상태가 되면 실행된다. 조금 더 자세한 내용은 따로 정리해두었다.
https://everydayc0ding.tistory.com/entry/Promise-%EA%B0%9D%EC%B2%B4
Promise 객체
Promise 객체비동기 작업을 처리하기 위한 객체비동기 작업이 완료되면 값을 알려줄 것을 '약속'함일단 Promise 를 돌려주고 나중에 작업이 완료되면 결과값을 Promise 에 채워 넣어줌 Promise 객체의 3
everydayc0ding.tistory.com
원래 서버와는 문자자료만 주고 받을 수 있었다.
"{"name" : "kim"}" -> 이런 식의 자료를 JSON 이라고 하는데, JSON 은 문자열 취급을 받기 때문에 서버와 자유롭게 주고 받을 수 있다. 그래서 실제로 결과인 .data 를 출력해보면 따옴표쳐진 JSON 이 나와야하는데 axios 라이브러리는 JSON -> object/array 변환작업이 자동으로 이뤄지기 때문에 따로 변환 과정을 거칠 필요가 없다. 그래서 실제로 출력하면 object/array 가 나온다.
※ 참고
원래 axios 를 사용하지 않으면 .json 메소드를 사용해서 JSON -> object/array 로 바꿔주는 작업을 해줘야 했다.
fetch('URL').then(결과 => 결과.json()).then((결과) => { console.log(결과) } )
'웹 > 24-StudyWithPnP' 카테고리의 다른 글
[React+JS/리액트] 웹 스터디 8주차 (0) | 2024.06.12 |
---|---|
[React+JS/리액트] 웹 스터디 7주차 (0) | 2024.06.05 |
[React+JS/리액트] 웹 스터디 5주차 (0) | 2024.05.23 |
[React+JS/리액트] 웹 스터디 4주차 (0) | 2024.05.16 |
[React+JS/리액트] 웹 스터디 3주차 (1) | 2024.05.08 |