라이프 사이클 (생명주기) 이해하기
React에서 컴포넌트의 생명주기를 관리하는 것은 앱의 성능과 유지보수성에 중요한 영향을 미칩니다.
특히, 함수형 컴포넌트에서는 useEffect 훅을 사용하여 이러한 관리 작업을 수행합니다.
- 예제로 알아보는 라이프 사이클
데이터를 불러와 화면에 표시하는 간단한 뉴스 목록 컴포넌트를 생각해볼 수 있습니다.
- 이 컴포넌트는 useEffect 훅을 사용해 컴포넌트가 마운트될 때 뉴스 API에서 데이터를 한 번만 불러오고,
- 이 데이터를 컴포넌트의 상태로 저장하여 화면에 뉴스 목록을 렌더링합니다.
- 만약 사용자가 특정 카테고리를 선택하면,
- 해당 카테고리에 맞는 뉴스 데이터를 다시 불러와 업데이트할 수 있습니다.
이렇게 useEffect를 사용하면 API 호출 타이밍을 정확히 조절하여 필요할 때만 데이터를 불러올 수 있으므로 앱의 성능을 향상시키고 불필요한 데이터 요청을 줄일 수 있습니다.
리액트 컴포넌트는 생성, 갱신, 제거의 세 가지 생명주기를 가지고 있습니다. 이 세 가지 생명주기에 따라 컴포넌트의 상태를 변경하거나, 화면에 렌더링하는 작업을 수행할 수 있습니다.
생명주기 단계 | 설명 |
---|---|
Mounting (마운팅) | 컴포넌트가 DOM에 삽입되는 초기 단계 |
Updating (업데이팅) | 컴포넌트가 prop 또는 state의 변경에 응답하여 업데이트되는 단계 |
Unmounting (언마운팅) | 컴포넌트가 DOM에서 제거되는 단계 |
리액트 컴포넌트의 생명주기에 따라 특정한 작업을 수행하고 싶을 때, useEffect
훅을 사용할 수 있습니다.
- 페이지가 마운팅, 업데이트, 언마운팅될 때 body의 배경색을 변경하는 코드
import React, { useEffect, useState } from 'react'
function App() {
const [color, setColor] = useState('yellow') // 초기 색상을 'yellow'로 설정
// 컴포넌트가 마운팅될 때, 업데이트될 때, 언마운팅될 때 실행되는 useEffect
useEffect(() => {
// Mounting
// 페이지가 마운팅될 때 body의 배경색을 'color' 상태의 값으로 설정
document.body.style.backgroundColor = color
document.body.id = 'body'
document.body.classList.add(color)
// Unmounting
// 페이지가 언마운팅될 때 body의 배경색을 흰색으로 변경, id 제거, class 제거
return () => {
document.body.style.backgroundColor = 'white'
document.body.id = ''
document.body.classList.remove(color)
}
}, [color]) // 'color' 상태가 변할 때마다 이 useEffect가 실행됩니다.
// Updating
useEffect(() => {
console.log(`색상이 ${color}로 변경되었습니다.`)
}, [color]) // 'color' 상태가 변할 때마다 이 useEffect가 실행됩니다.
// 버튼 클릭 시 'color' 상태를 토글하는 함수
const toggleColor = () => {
setColor((prevColor) => (prevColor === 'yellow' ? 'blue' : 'yellow'))
}
return (
<div>
<div>안녕하세요</div>
<button onClick={toggleColor}>색상 변경</button>
</div>
)
}
export default App
useEffect 로 라이프 사이클 제어하기
useEffect
훅을 사용하면 컴포넌트의 생명주기에 따라 특정한 작업을 수행할 수 있습니다.useEffect
훅을 사용하여 컴포넌트가 마운트될 때, 업데이트될 때, 언마운트될 때 특정한 작업을 수행할 수 있습니다.
생명주기 단계 | 소셜 미디어 대시보드 |
---|---|
Mounting (마운팅) | 데이터 가져오기 |
Updating (업데이팅) | 실시간 업데이트 |
Unmounting (언마운팅) | 컴포넌트가 DOM에서 제거되는 단계 |
- 마운트 제어하기
useEffect(() => {...}, [])
의존성 배열을 빈 배열로 전달하면, useEffect
의 콜백함수는 컴포넌트가 마운트될 때만 실행됩니다.
ex) 뉴스 데이터를 불러오는 작업을 컴포넌트가 마운트될 때만 실행
import React, { useState, useEffect } from 'react'
export default function App() {
const [count, setCount] = useState(0)
// useEffect(() => {...}, []) 의존성 배열로 빈 배열을 전달하면
// 컴포넌트가 마운트될 때만 useEffect의 콜백함수가 실행됩니다.
useEffect(() => {
console.log('마운트될 때만 실행')
}, [])
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
)
}
- 마운트 & 업데이트 제어하기
useEffect(() => {...})
의존성 배열을 전달하지 않으면, useEffect
의 콜백 함수는 컴포넌트가 마운트될 때와 업데이트될 때 모두 실행됩니다.
import React, { useState, useEffect } from 'react'
export default function App() {
const [count, setCount] = useState(0)
// useEffect(() => {...}) 의존성 배열을 전달하지 않았으므로,
// 콜백 함수는 마운트 시점에도 실행되고,
// 업데이트 시점에도 실행됩니다.
useEffect(() => {
console.log('마운트, 업데이트시 실행')
})
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
)
}
- 업데이트 제어하기
useEffect(() => {...}, [count])
의존성 배열에 특정한 상태를 전달하면, 해당 상태가 변경될 때만 useEffect
의 콜백 함수가 실행됩니다.
ex) 카테고리가 변경될 때만 뉴스 데이터를 불러오는 경우
import React, { useRef, useState, useEffect } from 'react'
export default function App() {
const [count, setCount] = useState(0)
// useRef 훅을 사용하여 컴포넌트의 변수 didMountRef를 생성
// 현재 컴포넌트가 마운트했는지 판단하는 변수 didMountRef를 Ref 객체로 생성
// 초기값은 false
const didMountRef = useRef(false)
// 의존성 배열로 아무것도 전달하지 않았으므로, 콜백 함수는 마운트 시점에도 실행되고, 업데이트 시점에도 실행됩니다.
useEffect(() => {
// !didMountRef.current는 didMountRef.current가 false일 때 true를 반환하고, true일 때 false를 반환합니다.
// 마운트 시점에는 조건(!false=true)은 참이므로, if문 안의 코드가 실행되고 didMountRef.current를 true로 설정합니다.
// 이후에 컴포넌트가 업데이트될 때는 조건(!true=false)은 거짓이므로, console.log('업데이트될 때만 실행')이 실행됩니다.
if (!didMountRef.current) {
// didMountRef.current를 true로 설정 후 함수를 종료
didMountRef.current = true
return
} else {
console.log('업데이트될 때만 실행')
}
})
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
)
}
컴포넌트 언마운트와 클린업 함수
useEffect(return () => {...})
컴포넌트가 언마운트되면, React는 해당 컴포넌트와 관련된 모든 리소스를 해제하려고 시도합니다. 이 과정에서 useEffect 내에서 반환하는 클린업 함수가 중요한 역할을 합니다.
- 클린업 함수의 역할
useEffect 내에서 반환하는 클린업 함수는 컴포넌트가 언마운트될 때 실행됩니다. 이 함수는 메모리 누수를 방지하고, 필요하지 않은 작업이 백그라운드에서 계속 실행되는 것을 막기 위해 사용됩니다. 특히, setInterval과 같은 타이머를 사용할 때 이 클린업 과정이 중요합니다.
컴포넌트의 "언마운트"는 컴포넌트가 더 이상 화면에 표시되지 않을 때 발생합니다.
- 클린업 함수 예제
import { useEffect, useState } from 'react'
const Timer = () => {
const [timer, setTimer] = useState(0)
const [isShow, setIsShow] = useState(true)
useEffect(() => {
let interval
if (isShow) {
interval = setInterval(() => {
setTimer((prev) => prev + 1)
}, 1000)
}
// 타이머를 시작하는 interval을 설정합니다.
// 이 리턴 함수는 컴포넌트가 언마운트 될 때 호출됩니다.
// 컴포넌트가 제거되면 interval도 정리해야 하기 때문입니다.
return () => {
console.log('타이머 정리')
clearInterval(interval)
}
}, [isShow]) // 종속성 배열이 비어 있으므로 마운트 시에만 실행
const hideTimer = () => {
setIsShow(false)
setTimer(0) // 타이머 리셋
}
return (
<div>
{isShow ? (
<div>
<h2>{timer}초</h2>
<button onClick={hideTimer}>타이머 숨기기</button>
</div>
) : (
<button onClick={() => setIsShow(true)}>타이머 보이기</button>
)}
</div>
)
}
export default Timer
useEffect 뉴스 예제
- 이 컴포넌트는 useEffect 훅을 사용해 컴포넌트가 마운트될 때 뉴스 API에서 데이터를 한 번만 불러오고,
- 이 데이터를 컴포넌트의 상태로 저장하여 화면에 뉴스 목록을 렌더링합니다.
- 만약 사용자가 특정 카테고리를 선택하면,
- 해당 카테고리에 맞는 뉴스 데이터를 다시 불러와 업데이트할 수 있습니다.
- API 키 발급
- News API에서 API 키를 발급받아 사용합니다.
- fetch로 API 호출하기
import React, { useEffect, useState } from 'react'
function News() {
const [news, setNews] = useState([]) // 뉴스 데이터를 저장할 상태
const [category, setCategory] = useState('all') // 카테고리를 저장할 상태
// useEffect를 사용하여 컴포넌트가 마운트될 때, 업데이트될 때, 언마운트될 때 뉴스 데이터를 불러오는 작업을 수행
useEffect(() => {
// async 함수를 만들어 useEffect 내부에서 호출
const fetchData = async () => {
let response
try {
// 카테고리가 'all'이면 모든 뉴스 데이터를 불러옴
if (category === 'all') {
response = await fetch('https://newsapi.org/v2/top-headlines?country=kr&apiKey=API_KEY')
} else {
// 카테고리가 'all'이 아니면 해당 카테고리의 뉴스 데이터를 불러옴
response = await fetch(
`https://newsapi.org/v2/top-headlines?country=kr&category=${category}&apiKey=API_KEY`
)
}
const data = await response.json() // JSON 형태로 변환
setNews(data.articles) // 뉴스 데이터를 상태에 저장 (articles 배열)
} catch (error) {
console.error(error)
}
}
fetchData() // fetchData 함수를 호출
}, [category]) // 'category' 상태가 변할 때마다 이 useEffect가 실행됩니다.
// 카테고리 버튼을 클릭하면 'category' 상태를 변경하는 함수
const selectCategory = (category) => {
setCategory(category)
}
return (
<div>
<h1>뉴스</h1>
<button onClick={() => selectCategory('all')}>전체</button>
<button onClick={() => selectCategory('business')}>비즈니스</button>
<button onClick={() => selectCategory('entertainment')}>엔터테인먼트</button>
<button onClick={() => selectCategory('health')}>건강</button>
<button onClick={() => selectCategory('science')}>과학</button>
<button onClick={() => selectCategory('sports')}>스포츠</button>
<button onClick={() => selectCategory('technology')}>기술</button>
<ul>
{news.map((article, index) => (
<li key={index}>
<a href={article.url} target="_blank" rel="noreferrer">
{article.title}
</a>
</li>
))}
</ul>
</div>
)
}
export default News
- axios로 API 호출하기
axios는 HTTP 요청을 보내는 라이브러리로, fetch 함수보다 더 간단하고 편리하게 사용할 수 있다.
- axios는 HTTP 클라이언트 라이브러리로, 브라우저와 Node.js 환경에서 모두 사용할 수 있습니다.
- axios를 사용하면 간단하게 API 호출을 할 수 있으며, Promise 기반의 API를 제공합니다.
npm install axios
yarn add axios
import React, { useEffect, useState } from 'react'
import axios from 'axios'
function News() {
const categoryList = [
{ name: '전체', value: 'all' },
{ name: '비즈니스', value: 'business' },
{ name: '엔터테인먼트', value: 'entertainment' },
{ name: '건강', value: 'health' },
{ name: '과학', value: 'science' },
{ name: '스포츠', value: 'sports' },
{ name: '기술', value: 'technology' },
]
const [news, setNews] = useState([]) // 뉴스 데이터를 저장할 상태
const [category, setCategory] = useState('all') // 카테고리를 저장할 상태
// useEffect를 사용하여 컴포넌트가 마운트될 때, 업데이트될 때, 언마운트될 때 뉴스 데이터를 불러오는 작업을 수행
useEffect(() => {
// async 함수를 만들어 useEffect 내부에서 호출
const fetchData = async () => {
let response
try {
// 카테고리가 'all'이면 모든 뉴스 데이터를 불러옴
if (category === 'all') {
response = await axios.get('https://newsapi.org/v2/top-headlines?country=kr&apiKey=API_KEY')
} else {
// 카테고리가 'all'이 아니면 해당 카테고리의 뉴스 데이터를 불러옴
response = await axios.get(
`https://newsapi.org/v2/top-headlines?country=kr&category=${category}&apiKey=API_KEY`
)
}
setNews(response.data.articles) // 뉴스 데이터를 상태에 저장 (articles 배열)
} catch (error) {
console.error(error)
}
}
fetchData() // fetchData 함수를 호출
}, [category]) // 'category' 상태가 변할 때마다 이 useEffect가 실행됩니다.
// 카테고리 버튼을 클릭하면 'category' 상태를 변경하는 함수
const selectCategory = (category) => {
setCategory(category)
}
return (
<div>
<h1>뉴스</h1>
{categoryList.map((item, index) => (
<button key={index} onClick={() => selectCategory(item.value)}>
{item.name}
</button>
))}
<ul>
{news.map((article, index) => (
<li key={index}>
<a href={article.url} target="_blank" rel="noreferrer">
{article.title}
</a>
</li>
))}
</ul>
</div>
)
}
export default News
fetch 함수와 axios 라이브러리의 차이점은 다음과 같습니다.
- fetch 함수는 브라우저 기본 내장 함수로, 브라우저 환경에서만 사용할 수 있습니다.
- axios는 HTTP 클라이언트 라이브러리로, 브라우저와 Node.js 환경에서 모두 사용할 수 있습니다.
- axios를 사용하면 간단하게 API 호출을 할 수 있으며, Promise 기반의 API를 제공합니다.
- axios는 요청과 응답을 JSON 형태로 자동 변환해주기 때문에, JSON 데이터를 쉽게 다룰 수 있습니다.
'Front > React' 카테고리의 다른 글
최적화 - React 배우기 (0) | 2024.03.18 |
---|---|
React를 이용한 할 일 관리 앱 만들기 (0) | 2024.03.14 |
React 카운터 앱 만들기 - component, useState (1) | 2024.03.04 |
React(리액트) 기본 기능 정리 (0) | 2024.02.29 |
API 통신 - react 배우기 (0) | 2023.11.16 |