React
컴포넌트의 라이프사이클은 컴포넌트가 생성되고 화면에 렌더링된 후, 사용자의 상호작용이나 상태 변경에 따라 업데이트되며, 마지막으로 제거되는 일련의 과정입니다. 이를 이해하면 컴포넌트가 언제, 어떻게 작동하는지 예측 가능하며, 필요한 시점에 적절한 로직을 추가할 수 있어 최적화와 유지 관리에 큰 도움이 됩니다.
React 컴포넌트는 크게 다음 세 가지 단계로 나뉩니다.
1. 마운트(Mount)
DOM
에 삽입되며, 컴포넌트의 초기화 작업을 수행할 수 있습니다.2. 업데이트(Update)
React
는 변경된 값에 반응하여 컴포넌트를 재랜더링하고, 사용자가 기대하는 대로 최신 화면을 보여줍니다.useEffect
를 통해 상태나 속성에 따라 특정 로직을 실행할 수 있습니다. 이 기능은 반응형 웹 애플리케이션을 만들 때 매우 유용합니다.3. 언마운트(Unmount)
DOM
에서 제거되는 단계입니다.useEffect
에서는 클린업 함수를 통해 타이머나 이벤트 리스너 등을 정리해 효율적인 리소스 관리를 할 수 있습니다.React
에서 useEffect
와 같은 훅(Hook)을 통해 라이프사이클을 다루는 것은 반응형 웹 개발에서 핵심적인 역할을 합니다. 기존 클래스형 컴포넌트에서는 componentDidMount
, componentDidUpdate
, componentWillUnmount
와 같은 메서드를 활용해 각 단계별로 처리했으나, 함수형 컴포넌트와 useEffect
의 등장으로 단일 API
로 관리가 가능해졌습니다.
이 구조를 이해하는 것은 더 나은 코드 품질과 애플리케이션 성능 최적화로 이어집니다. 특히, 필요할 때만 효과가 실행되고, 불필요한 렌더링을 방지할 수 있기 때문에 효율적인 웹 애플리케이션을 구축할 수 있습니다.
React
는 기본적으로 컴포넌트 렌더링과 UI
업데이트를 위한 라이브러리이지만, 실제 애플리케이션에서는 단순 렌더링 외에도 다양한 작업들이 필요합니다.
데이터 패칭(Data Fetching): 서버에서 데이터를 불러와야 하는 경우가 많습니다. 이 과정에서 데이터를 요청하고, 받아온 데이터를 상태에 저장해 화면에 반영해야 합니다.
구독 및 이벤트 리스너 등록(Subscriptions & Event Listeners): 웹소켓 연결, 타이머 설정, 스크롤 이벤트 등의 작업은 컴포넌트가 마운트될 때 시작되고, 언마운트될 때 정리되어야 합니다.
DOM 조작 및 애니메이션: React가 DOM을 관리하지만, 특정 작업에서는 직접 DOM 조작이나 애니메이션 설정이 필요할 수 있습니다.
마운트 시 효과: 컴포넌트가 처음 렌더링될 때만 실행되는 효과를 추가할 수 있습니다. 예를 들어, 페이지에 들어가자마자 API에서 데이터를 가져오는 로직을 포함할 수 있습니다.
업데이트 시 효과: 특정 상태나 속성이 변경될 때마다 자동으로 재실행되도록 할 수 있습니다. 이를 통해 특정 상태 변화에 반응하여 부수 효과를 관리할 수 있습니다.
클린업(Cleanup) 기능: 컴포넌트가 언마운트되기 직전에 호출되는 클린업 함수를 포함할 수 있습니다. 이를 통해 메모리 누수나 불필요한 리소스 점유를 방지할 수 있습니다.
useEffect
는 적절한 의존성 관리로 불필요한 렌더링을 줄여 성능을 최적화할 수 있습니다. 특정 상태나 속성의 변화에 따라 원하는 시점에만 효과를 실행할 수 있어, 기존 클래스형 컴포넌트에서 자주 발생하던 무한 루프나 불필요한 연산을 방지하는 데에도 큰 도움이 됩니다.
useEffect(() => {
// 실행할 부수 효과 코드
return () => {
// 클린업(clean-up) 코드 (선택적)
};
}, [의존성1, 의존성2, ...]);
첫 번째 인자는 실행할 함수입니다. 이 함수는 컴포넌트가 처음 렌더링될 때 실행되며, 특정 조건에 따라 컴포넌트가 다시 렌더링될 때 재실행됩니다.
두 번째 인자는 의존성 배열입니다. 이 배열에 있는 값이 변경될 때만 useEffect
가 다시 실행됩니다. 이 배열을 통해 useEffect
가 언제 실행될지를 제어할 수 있습니다.
의존성 배열은 useEffect
의 재실행을 제어하는 중요한 요소입니다. 의존성 배열에 따라 useEffect
의 실행 빈도와 시점이 달라지며, 크게 세 가지 경우로 나눌 수 있습니다.
1. 빈 배열 []
: 초기 마운트 시 한 번만 실행
useEffect(() => {
console.log("이 코드는 마운트될 때 한 번만 실행됩니다.");
}, []);
[]
로 두면, 이 useEffect
는 컴포넌트가 마운트될 때 한 번만 실행됩니다.2. 특정 상태나 속성에 의존하는 배열 [state1, prop1]
useEffect(() => {
console.log("이 코드는 state1이나 prop1이 변경될 때마다 실행됩니다.");
}, [state1, prop1]);
useEffect
가 다시 실행됩니다.3. 의존성 배열을 생략한 경우 (매번 렌더링 시 실행)
useEffect(() => {
console.log("이 코드는 매 렌더링마다 실행됩니다.");
});
useEffect
가 실행됩니다.useEffect(() => {
const intervalId = setInterval(() => {
console.log("매 초마다 실행되는 타이머");
}, 1000);
return () => {
clearInterval(intervalId); // 타이머 정리
console.log("타이머가 정리되었습니다.");
};
}, []);
useEffect
의 첫 번째 인자로 전달하는 함수 내부에서 클린업 함수를 반환하면, 컴포넌트가 언마운트되거나, 의존성 배열의 값이 변경되어 useEffect
가 재실행되기 전에 클린업 함수가 실행됩니다. 클린업 함수는 이벤트 리스너 제거, 타이머 정리, 구독 해제 등을 처리하는 데 유용합니다.useEffect를 사용해 컴포넌트가 마운트될 때 API
로 데이터를 한 번만 가져오는 패턴입니다.
import { useEffect, useState } from 'react';
function DataFetchingComponent() {
const [data, setData] = useState(null);
useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch('https://api.example.com/data');
const jsonData = await response.json();
setData(jsonData);
} catch (error) {
console.error("Error fetching data:", error);
}
};
fetchData();
}, []); // 빈 배열로 마운트 시 한 번만 실행
return <div>{data ? `Data: ${data}` : "Loading..."}</div>;
}
fetchData
를 실행하여 불필요한 API 호출을 방지합니다.다음 예제는 useEffect
를 통해 실시간 데이터를 구독하는 패턴입니다. 예를 들어, 웹소켓을 이용해 서버에서 실시간으로 데이터를 받아오는 상황을 가정할 수 있습니다.
import { useEffect, useState } from 'react';
function RealTimeDataComponent() {
const [messages, setMessages] = useState([]);
useEffect(() => {
const ws = new WebSocket('wss://example.com/realtime');
ws.onmessage = (event) => {
setMessages((prevMessages) => [...prevMessages, event.data]);
};
// 클린업 함수로 웹소켓 연결 해제
return () => {
ws.close();
console.log("WebSocket connection closed");
};
}, []); // 빈 배열로 마운트 시 한 번만 실행
return (
<div>
<h3>Real-Time Messages</h3>
<ul>
{messages.map((msg, index) => (
<li key={index}>{msg}</li>
))}
</ul>
</div>
);
}
ws.close()
를 호출해 웹소켓 연결을 해제하므로, 리소스를 효율적으로 관리할 수 있습니다.마지막으로, 상태 값이 변경될 때마다 useEffect
를 사용해 데이터를 필터링하는 예제입니다.
import { useEffect, useState } from 'react';
function FilteredListComponent({ items }) {
const [query, setQuery] = useState('');
const [filteredItems, setFilteredItems] = useState([]);
useEffect(() => {
const filtered = items.filter((item) => item.includes(query));
setFilteredItems(filtered);
}, [query, items]); // query나 items가 변경될 때마다 실행
return (
<div>
<input
type="text"
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Search..."
/>
<ul>
{filteredItems.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
</div>
);
}
query
가 변경될 때마다 useEffect
가 실행되어 filteredItems
가 업데이트됩니다. items
와 query
를 의존성 배열에 추가하여, 입력 값이 변경될 때만 데이터가 필터링되도록 설정했습니다.챌린지반 과제로 반응형 이펙트의 생명주기에 대해 공부해봤다.
어떻게든지 개발을 해나가는것과 이런 개념들에 대해 확립하고 해나가는것은 정말 하늘과 땅 차이인것 같다.
원리에 대해 알게되니까 어떻게 써야할지 느낌이 온다고 해야하나? 매우 좋은시간이다.
리액트 숙련주차 과제로 포켓몬 도감 만들기가 주어졌다.
이번에도 잘해봅시다 화이팅!!!