React Hook
useState
https://nueos.tistory.com/204#useState-1
[#50] React - useState
useState- 컴포넌트에 상태를 추가하는 Hook- 화면에 보여주는 데이터를 저장하고 변경할 수 있게 해주는 기능기본 사용법const [상태, 상태변경함수] = useState(초기값);const [state, setState] = useState(초기
nueos.tistory.com
useEffect
컴포넌트의 렌더링 외에 발생하는 부수 효과(side effects)를 처리하기 위한 훅
부수 효과
- 데이터 가져오기 (API 호출)
- 타이머 설정 (setTimeout)
- 이벤트 리스너 등록
- 외부 시스템과 연동
useEffect(() => {
// 마운트될 때 실행할 코드
return () => {
// 언마운드 될 때 실행할 코드 - 정리(cleanup) 함수 - 선택적
};
}, [의존성1, 의존성2, ...]); //의존성이 업데이트 되면 useEffect() 재 실행
실행 시점
- 마운트 시: 컴포넌트가 처음 화면에 나타날 때
- 업데이트 시: 의존성 배열에 있는 값이 변경될 때 (의존성 배열이 있는 경우)
- 언마운트 시: 컴포넌트가 화면에서 사라질 때 (정리 함수 실행)
의존성 배열
- 빈 배열 []: 컴포넌트가 마운트될 때 한 번만 실행, 언마운트 시 정리 함수 실행
- 의존성 포함 [value1, value2]: 해당 값들이 변경될 때마다 실행
- 배열 생략: 모든 렌더링 후 실행 (권장 X)
정리(Cleanup) 함수
- 이전 효과를 정리하기 위한 함수
- 메모리 누수 방지
- 이벤트 리스너, 타이머 등을 해제할 때 필요
import { useState, useEffect } from 'react';
function Timer() {
const [count, setCount] = useState(0);
useEffect(() => {
// 타이머 설정
const timerId = setInterval(() => {
setCount(c => c + 1);
}, 1000);
// 정리 함수: 타이머 제거
return () => {
clearInterval(timerId);
};
}, []); // 마운트 시 한 번만 실행
return <h1>카운트: {count}</h1>;
}
useRef
리렌더링을 발생시키지 않으면서 값을 저장할 수 있는 훅
👉🏻 컴포넌트 안에 있는 '비밀 상자'
const ref = useRef(초기값);
- 일반 변수와 달리 컴포넌트가 다시 렌더링되어도 ref에 저장된 값은 그대로 유지됨
- ref 값을 바꿔도 화면이 업데이트 되지 않음 (useState와 가장 큰 차이점)
- 항상 ref.current 속성을 통해 값에 접근하고 수정
🧩 활용 사례
1️⃣ DOM 요소에 직접 접근
input, canvas, video 등 HTML 요소에 직접 접근 가능
const InputFocus = () => {
const inputRef = useRef(null);
const handleClick = () => {
inputRef.current.focus(); // input에 포커스
};
return (
<>
<input ref={inputRef} type="text" />
<button onClick={handleClick}>포커스 이동</button>
</>
);
};
2️⃣ 이전 값 기억하기
const PreviousValue = ({ count }) => {
const prevCountRef = useRef();
useEffect(() => {
prevCountRef.current = count;
}, [count]);
return (
<div>
현재 값: {count}, 이전 값: {prevCountRef.current}
</div>
);
};
3️⃣ 스크롤 위치 제어
function ScrollComponent() {
const scrollAreaRef = useRef(null);
const scrollToTop = () => {
scrollAreaRef.current.scrollTop = 0;
};
const scrollToBottom = () => {
const { current } = scrollAreaRef;
current.scrollTop = current.scrollHeight;
};
return (
<>
<button onClick={scrollToTop}>Scroll to Top</button>
<button onClick={scrollToBottom}>Scroll to Bottom</button>
<div
ref={scrollAreaRef}
style={{ height: '300px', overflow: 'auto' }}
>
{/* Scrollable content */}
{Array.from({ length: 50 }, (_, i) => (
<p key={i}>Item {i + 1}</p>
))}
</div>
</>
);
}
⚠️ 주의 사항
1. 렌더링 중에 ref.current 읽거나 쓰지 않기
- DOM 요소나 인스턴스를 가리키는데, 컴포넌트가 렌더링 중일 때는 ref가 아직 연결되지 않았을 수 있음
- 이벤트 핸들러나 useEffect 내부에서 사용
// ❌ 잘못된 예
const height = scrollRef.current.scrollHeight // 아직 ref 연결 전일 수 있음
// ✅ 좋은 예
useEffect(() => {
console.log(scrollRef.current.scrollHeight) // 마운트 이후 안전하게 접근
}, [])
2. UI에 표시할 값은 state 사용하기
// ❌ 잘못된 예
const countRef = useRef(0)
// UI에 countRef.current를 직접 쓰면 업데이트 안됨
// ✅ 좋은 예
const [count, setCount] = useState(0)
// 상태가 바뀌면 자동으로 재렌더링됨
3. 초기화 비용이 큰 값은 조건부로 초기화하기
const playerRef = useRef(null);
// 최초 한 번만 초기화
if (playerRef.current === null) {
playerRef.current = new VideoPlayer();
}
useNavigate
페이지 이동을 하기 위해 사용하는 훅
👉🏻 링크를 클릭하지 않고도 버튼 클릭, 폼 제출, 조건 만족 시 JS 코드 내부에서 이동이 필요할 때 유용
import { useNavigate } from 'react-router-dom';
function Example() {
const navigate = useNavigate();
const goToHome = () => {
navigate('/');
};
return <button onClick={goToHome}>홈으로 이동</button>;
}
📍 주요 기능 및 옵션
1️⃣ 기본 이동
navigate('/about');
2️⃣ 파라미터 포함한 이동
navigate(`/detail/${id}`);
3️⃣ 쿼리 파라미터 포함 이동
navigate(`/search?q=apple`);
4️⃣ replace 옵션(뒤로 가기 방지)
navigate('/login', { replace: true });
5️⃣ 이전 페이지로 이동
navigate(-1); // history.back()
useLoaction
현재 브라우저의 URL 정보를 가져오는 데 사용되는 훅
👉🏻 컴포넌트 내에서 현재 페이지의 경로, 쿼리 매개 변수, 해시 등 URL 관련 정보에 접근할 수 있게 해줌
📍 반환하는 객체의 주요 속성
- pathname: 현재 URL의 경로 부분 (예: '/about')
- search: URL의 쿼리 문자열 부분 (예: '?id=123')
- hash: URL의 해시 부분 (예: '#section1')
- state: 페이지 이동 시 전달된 상태 객체
- key: 각 위치에 대한 고유 식별자
import { useLocation } from 'react-router-dom';
function MyComponent() {
const location = useLocation();
console.log(location.pathname);// 현재 경로
console.log(location.search);// 쿼리 문자열
console.log(location.hash);// URL 해시
console.log(location.state);// 전달된 상태 객체
return (
<div>Current path: {location.pathname}</div>
);
}
🧩 활용 사례
1️⃣ 페이지 이동 시, 상태 전달 및 접근
// 페이지 A
import { useNavigate } from 'react-router-dom';
function PageA() {
const navigate = useNavigate();
const handleClick = () => {
navigate('/page-b', {
state: {
from: 'pageA',
data: { id: 123, name: 'Example' }
}
});
};
return <button onClick={handleClick}>페이지 B로 이동</button>;
}
// 페이지 B
import { useLocation } from 'react-router-dom';
function PageB() {
const location = useLocation();
const { from, data } = location.state || {};
return (
<div>
<p>이전 페이지: {from}</p>
<p>전달된 데이터: {data ? JSON.stringify(data) : '없음'}</p>
</div>
);
}
2️⃣ 현재 경로에 따른 UI 조건부 렌더링
import { useLocation } from 'react-router-dom';
function Navigation() {
const location = useLocation();
return (
<nav>
<ul>
<li className={location.pathname === '/' ? 'active' : ''}>
<a href="/">Home</a>
</li>
<li className={location.pathname === '/about' ? 'active' : ''}>
<a href="/about">About</a>
</li>
<li className={location.pathname.startsWith('/products') ? 'active' : ''}>
<a href="/products">Products</a>
</li>
</ul>
</nav>
);
}
useMemo
계산 비용이 높은 값을 기억(memoization)해두고, 의존성(deps)이 바뀔 때만 다시 계산하도록 도와주는 훅
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
🧩 활용 사례
1️⃣ 무거운 연산 메모이제이션
data가 변경되지 않는 한, heavyCalulation은 다시 실행되지 않음
const expensiveResult = useMemo(() => {
console.log("계산 중...");
return heavyCalculation(data);
}, [data]);
2️⃣ 컴포넌트 성능 최적화
리스트가 바뀔 때만 정렬 실행
const sortedList = useMemo(() => {
return list.slice().sort((a, b) => a - b);
}, [list]);
⚠️ 주의 사항
- useMemo는 무조건 성능 향상이 되는 것은 아님
- 불필요하게 쓰면 메모리만 낭비할 수 있음
- 캐시 비용보다 연산 비용이 클 때만 사용
useCallback
함수를 기억(memoization)해두는 훅
👉🏻 의존성 배열이 바뀌지 않는 한, 같은 함수 참조를 유지함
const memoizedCallback = useCallback(() => {
doSomething(a, b);
}, [a, b]);
🧩 활용 사례
1️⃣ 자식 컴포넌트에 함수 props로 전달
handleClick이 같은 함수 참조를 유지해서 불필요한 자식 렌더링을 막을 수 있음
const handleClick = useCallback(() => {
console.log("clicked!");
}, []);
// 자식 컴포넌트가 React.memo일 때 유용
<ChildComponent onClick={handleClick} />
2️⃣ API 호출 함수 최적화
import React, { useState, useEffect, useCallback } from 'react';
// API 호출을 위한 함수
const fetchData = async (id: number) => {
const response = await fetch(`https://api.example.com/data/${id}`);
return response.json();
};
export default function DataFetcher() {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
// useCallback을 사용하여 fetchData 함수 메모이제이션
const getData = useCallback(async (id: number) => {
setLoading(true);
setError(null);
try {
const result = await fetchData(id);
setData(result);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
}, []); // 의존성 배열이 빈 배열로 되어 있어, 최초 한 번만 함수가 생성됨
useEffect(() => {
// 컴포넌트가 마운트될 때 데이터 호출
getData(1); // 예시로 1번 데이터 요청
}, [getData]); // getData가 변경될 때마다 호출
return (
<div>
<h1>Data Fetching Example</h1>
{loading && <p>Loading...</p>}
{error && <p>Error: {error}</p>}
{data && <pre>{JSON.stringify(data, null, 2)}</pre>}
</div>
);
}
⚠️ 주의 사항
- 모든 함수에 무조건 쓰면 오히려 역효과(메모이제이션 비용도 존재함)
- React.memo와 세트처럼 쓰는 경우가 많음
React.memo
props가 변경되지 않으면 해당 컴포넌트를 다시 렌더링하지 않고, 이전 렌더링 결과를 재사용할 수 있게 해주는 고차 컴포넌트
👉🏻 불필요한 렌더링 최적화
import React, { useState, useCallback } from 'react';
// React.memo로 감싸서 props가 바뀔 때만 렌더링되게
const Child = React.memo(({ onClick }) => {
console.log('Child rendered');
return <button onClick={onClick}>Click Me</button>;
});
const Parent = () => {
const [count, setCount] = useState(0);
// useCallback을 쓰지 않으면 Parent가 리렌더링 될 때마다 onClick 함수도 새로 생성됨
const handleClick = useCallback(() => {
console.log('Button clicked!');
}, []);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(prev => prev + 1)}>Increment</button>
<Child onClick={handleClick} />
</div>
);
};
export default Parent;
useSearchParams
URL의 쿼리 파라미터를 쉽게 읽고 수정할 수 있게 해주는 훅
const [searchParams, setSearchParams] = useSearchParams();
- searchParams: URLSearchParams 인스턴스로, 현재 URL의 쿼리 파라미터에 접근할 수 있음
- setSearchParams: 쿼리 파라미터를 업데이트하는 함수
📍 특징
- 읽기 기능: searchParams.get('paramName')을 사용하여 특정 쿼리 파라미터 값을 읽을 수 있음
- 쓰기 기능: setSearchParams({ paramName: 'value' })를 사용하여 URL의 쿼리 파라미터를 업데이트할 수 있음
- URL 동기화: 쿼리 파라미터를 변경하면 자동으로 브라우저의 URL이 업데이트되고, 해당 상태가 브라우저의 히스토리에 추가됨
- 복수 값 지원: 같은 이름의 여러 파라미터 값을 searchParams.getAll('paramName')로 가져올 수 있음
🧩 활용 사례
1️⃣ 필터링 기능
필터링을 URL 쿼리 파라미터로 유지하여 사용자가 필터링된 상태를 북마크하거나 공유할 수 있게 하는 경우
import { useSearchParams } from 'react-router-dom';
function ProductList() {
const [searchParams, setSearchParams] = useSearchParams();
const category = searchParams.get('category');
const handleFilterChange = (newCategory) => {
setSearchParams({ category: newCategory });
};
return (
<>
<button onClick={() => handleFilterChange('shoes')}>Shoes</button>
<button onClick={() => handleFilterChange('clothes')}>Clothes</button>
<p>Selected Category: {category}</p>
</>
);
}
2️⃣ 페이지네이션
페이지 번호와 페이지당 항목 수를 URL에 유지하는 경우
function PaginatedList() {
const [searchParams, setSearchParams] = useSearchParams();
const page = parseInt(searchParams.get('page') || '1', 10);
const pageSize = parseInt(searchParams.get('size') || '10', 10);
const handlePageChange = (newPage) => {
setSearchParams({ page: newPage.toString(), size: pageSize.toString() });
};
return (
<div>
<Pagination
currentPage={page}
pageSize={pageSize}
onPageChange={handlePageChange}
/>
</div>
);
}
3️⃣ 검색 기능
사용자의 검색어를 URL에 저장하여 검색 결과를 공유하거나 브라우저 히스토리에서 돌아갈 수 있게 하는 경우
function SearchBar() {
const [searchParams, setSearchParams] = useSearchParams();
const query = searchParams.get('q') || '';
const handleSearch = (e) => {
e.preventDefault();
const value = e.target.elements.search.value;
setSearchParams({ q: value });
};
return (
<form onSubmit={handleSearch}>
<input name="search" defaultValue={query} />
<button type="submit">Search</button>
</form>
);
}
성능 최적화 정리
코드 분할(Code Splitting)
불필요한 JS 로딩을 줄여 초기 렌더링 속도 향상
🔹 React.lazy + Suspense
컴포넌트를 동적으로 import 해서 필요한 순간에만 로딩
import React, { Suspense, lazy } from 'react';
const MyComponent = lazy(() => import('./MyComponent'));
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<MyComponent />
</Suspense>
);
}
- 적용 위치: 무거운 페이지 컴포넌트, 잘 안 쓰는 컴포넌트
- 이점: 번들 사이즈 ↓, 초기 로드 속도 ↑
디바운스/쓰로틀(Debounce/Throttle)
이벤트 과잉 호출 제어 - 자원 낭비 방지, UX 개선
✅ 디바운스(Debounce)
이벤트가 끝난 후 일정 시간 뒤 한번만 실행, 검색 입력 후 API 호출 등에 사용
const handleSearch = debounce((keyword) => {
fetchData(keyword);
}, 500);
✅ 쓰로틀(Throttle)
이벤트가 일정 간격마다 최대 한 번 실행, 스크롤 위치 감지/윈도우 resize 등에 사용
const handleResize = throttle(() => {
console.log(window.innerWidth);
}, 300);
'LG 유플러스 유레카 SW > React' 카테고리의 다른 글
[#62] 용돈 기입장 프로젝트 (feat. React) (0) | 2025.04.28 |
---|---|
[#61] Redux란 (1) | 2025.04.24 |
[#59] React 쇼핑몰 - Shop 페이지 상품 필터링/정렬 + 페이지네이션 (0) | 2025.04.22 |
[#58] React 쇼핑몰 - 장바구니 수정/삭제 + 반응형 Shop 페이지 (0) | 2025.04.21 |
[#57] React 쇼핑몰 - 상품 상세 페이지(탭, 슬라이더) + 장바구니 조회/추가 (0) | 2025.04.18 |