🖥️ Frontend/React
React에서 필요한 Hook 정리
hjwjo
2025. 9. 8. 23:12
1️⃣ useState – 상태 관리
- 용도: input, select, checkbox 등 값을 저장하고 변경
- 특징: 값이 바뀌면 컴포넌트 리렌더링
import { useState } from "react";
export default function ExampleUseState() {
const [formData, setFormData] = useState({}); // 초기값 없이 빈 객체 가능
const handleChange = (e) => {
const { name, value } = e.target;
setFormData(prev => ({ ...prev, [name]: value }));
};
const handleSubmit = (e) => {
e.preventDefault();
console.log("FormData:", formData); // 한 번에 formData 확인 가능
};
return (
<form onSubmit={handleSubmit}>
<input name="sText" placeholder="검색어" onChange={handleChange} />
<input name="sPage" placeholder="페이지" onChange={handleChange} />
<input name="sGubun" placeholder="구분" onChange={handleChange} />
<button type="submit">Submit</button>
</form>
);
}
💡 JSP에서 hidden input + egovMap처럼 한 객체에 다 모아서 submit 가능.
2️⃣ useEffect – 컴포넌트 생명주기/사이드이펙트
- 용도: 초기 데이터 로딩, API 호출, DOM 접근, 구독/해제
- 특징: componentDidMount / componentDidUpdate / componentWillUnmount 역할
import { useState, useEffect } from "react";
export default function ExampleUseEffect() {
const [data, setData] = useState([]);
useEffect(() => {
// 컴포넌트 마운트 시 데이터 로딩
fetch("/api/board/list")
.then(res => res.json())
.then(json => setData(json));
}, []); // [] → 마운트 시 1번만 실행
return (
<div>
<h1>Board List</h1>
<ul>
{data.map(item => (
<li key={item.boardId}>{item.title}</li>
))}
</ul>
</div>
);
}
💡 JSP + Spring Controller에서 뿌리던 request.getAttribute 대신 API 호출 후 state에 저장.
3️⃣ useRef – DOM/값 직접 접근
- 용도: input 값 직접 참조, 렌더링 없이 값 유지
- 특징: submit 시점에만 값 확인, 불필요한 리렌더링 없음
import { useRef } from "react";
export default function ExampleUseRef() {
const inputRef = useRef();
const handleSubmit = (e) => {
e.preventDefault();
console.log("Input value:", inputRef.current.value);
};
return (
<form onSubmit={handleSubmit}>
<input ref={inputRef} placeholder="검색어" />
<button type="submit">Submit</button>
</form>
);
}
💡 JSP hidden input처럼 입력값을 submit 시점에 한 번만 사용할 때 유용.
4️⃣ useNavigate + useLocation (react-router-dom) – 페이지 이동/값 전달
- 용도: List → Detail 페이지 이동, 이전 검색조건 전달
- 특징: URL + state 전달 가능, React SPA 특화
// ListPage.jsx
import { useState } from "react";
import { useNavigate } from "react-router-dom";
export default function ListPage() {
const navigate = useNavigate();
const [searchParams, setSearchParams] = useState({});
const handleChange = (e) => {
const { name, value } = e.target;
setSearchParams(prev => ({ ...prev, [name]: value }));
};
const goDetail = (boardId) => {
navigate(`/detail/${boardId}`, { state: searchParams });
};
return (
<div>
<input name="sText" placeholder="검색어" onChange={handleChange} />
<input name="sPage" placeholder="페이지" onChange={handleChange} />
<button onClick={() => goDetail(123)}>상세보기</button>
</div>
);
}
// DetailPage.jsx
import { useLocation, useParams } from "react-router-dom";
export default function DetailPage() {
const location = useLocation();
const params = useParams();
const { sText, sPage } = location.state || {};
const { boardId } = params;
return (
<div>
<h1>Detail Page</h1>
<p>Board ID: {boardId}</p>
<p>검색어: {sText}</p>
<p>페이지: {sPage}</p>
</div>
);
}
💡 JSP에서 hidden input + egovMap으로 boardId + 검색조건 넘기던 패턴과 1:1 대응 가능.
5️⃣ Validation – 간단하게 처리
import { useState } from "react";
export default function ValidationExample() {
const [email, setEmail] = useState("");
const [error, setError] = useState("");
const handleChange = (e) => {
setEmail(e.target.value);
setError(e.target.value.includes("@") ? "" : "올바른 이메일이 아닙니다.");
};
const handleSubmit = (e) => {
e.preventDefault();
if(!email.includes("@")) {
alert("이메일 확인 필요");
return;
}
console.log("Submit:", email);
};
return (
<form onSubmit={handleSubmit}>
<input value={email} onChange={handleChange} placeholder="email" />
{error && <span style={{color:"red"}}>{error}</span>}
<button type="submit">Submit</button>
</form>
);
}
💡 작은 폼은 useState + onChange로 실시간 validation, 큰 폼은 React Hook Form 추천
🔹Hook 용도 JSP 패턴 비교
useState | input 상태 관리, formData | <input> + hidden input + egovMap |
useEffect | 초기 데이터 호출, side effect | <c:forEach> + JSP 초기 데이터 렌더링 |
useRef | DOM 직접 접근, submit 시점 값 확인 | hidden input, submit 시점 데이터 |
useNavigate / useLocation | 페이지 이동, state 전달 | form action + hidden input 전달 |
Validation | 입력 실시간 확인 / submit 검증 | server-side validation + JSTL/JavaScript |
useState + handleChange
const [formData, setFormData] = useState({});
- useState()는 React 컴포넌트에서 상태(state)를 저장할 때 사용
- 여기서 formData = 상태값, 현재 form에 입력된 데이터를 담는 객체
- setFormData = 상태를 바꾸는 함수, 이걸 호출하면 React가 컴포넌트를 다시 렌더링함
- useState({}) → 초기값을 빈 객체로 시작
handleChange 분석
const handleChange = (e) => {
const { name, value } = e.target;
setFormData(prev => ({ ...prev, [name]: value }));
};
e.target
- e는 이벤트 객체, 여기서는 input이 변경될 때 발생
- e.target → 변경된 input DOM 요소
- e.target.name → input의 name 속성
- e.target.value → input에 입력된 값
const { name, value } = e.target;
- JS 구조분해(destructuring)
- name = e.target.name
- value = e.target.value
- 축약형 → 바꿔 쓰지 않아도 바로 변수로 사용 가능
setFormData(prev => ({ ...prev, [name]: value }))
- prev = 이전 상태(formData의 이전 값)
- ...prev = 이전 상태의 모든 key:value를 복사
- [name]: value = 새로 입력된 값을 동적으로 key로 추가/갱신
예시:
// 초기 formData
formData = {}
// 사용자가 input name="sText", value="Hello" 입력
setFormData(prev => ({ ...prev, [name]: value }))
// prev = {}
// ...prev = {} // 기존 값 없음
// [name]: value = sText: "Hello"
// 결과 → formData = { sText: "Hello" }
// 다음으로 input name="sPage", value="1" 입력
// prev = { sText: "Hello" }
setFormData(prev => ({ ...prev, [name]: value }))
// ...prev = { sText: "Hello" } // 기존 값 유지
// [name]: value = sPage: "1"
// 결과 → formData = { sText: "Hello", sPage: "1" }
💡 [name]: value + ...prev → 입력될 때마다 기존 상태는 유지하고, 새 값을 덮어쓰기
useEffect
useEffect(() => {
fetch("/api/board/list")
.then(res => res.json())
.then(json => setData(json));
}, []);
useEffect = React에서 JSP의 init, JSTL 반복, 서버 초기 렌더링과 비슷한 역할
- componentDidMount → [] 빈 배열 넣으면 처음 마운트될 때만 실행
- componentDidUpdate → 배열에 상태 넣으면 특정 값이 바뀔 때 실행
- componentWillUnmount → return 함수 사용 시 컴포넌트 제거 시 실행
fetch()
- JS 내장 함수, 서버 API 호출
- fetch(url) → 서버에서 데이터를 가져오는 요청
- .then(res => res.json()) → 서버 응답을 JSON으로 변환
- .then(json => setData(json)) → React 상태로 저장
[] 빈 배열 의미
- [] → 의존성이 없음 → 마운트될 때 한 번만 실행
- [formData]처럼 상태 넣으면 formData가 바뀔 때마다 다시 실행
useRef
const inputRef = useRef();
<input ref={inputRef} />
- useRef = DOM에 직접 접근하거나, 렌더링과 무관하게 값 유지
- 예: hidden input처럼 submit 시점에만 값을 읽고 싶을 때
- inputRef.current.value → 현재 input DOM의 값
// submit 시점
console.log(inputRef.current.value);
💡 차이:
- useState → 입력할 때마다 리렌더링
- useRef → 값은 유지하지만 리렌더링 없음 → 성능 최적화용
useNavigate + useLocation (react-router-dom)
const navigate = useNavigate();
navigate(`/detail/${boardId}`, { state: searchParams });
useNavigate()
- React Router v6에서 페이지 이동을 JS로 제어
- JSP form의 action="/detail.do"와 비슷
- URL 경로 이동 가능
state 옵션
- state: searchParams → List 페이지에서 쌓은 검색 조건을 Detail 페이지로 전달
- JSP에서 hidden input + egovMap처럼 한 객체를 그대로 전달 가능
DetailPage에서 받기
const location = useLocation();
const params = useParams();
const { sText, sPage } = location.state || {};
const { boardId } = params;
- useLocation().state → List에서 전달한 검색 조건
- useParams() → URL에 들어간 boardId 받기 (/detail/123)
Arrow Function => 의미
const handleChange = (e) => { ... }
- 일반 함수:
function handleChange(e) { ... }
- 축약 문법 → this 바인딩 없이 사용 가능
- React에서 이벤트 처리 시 거의 항상 arrow function 사용
input name/value + prev 동작
- prev = 이전 상태 객체
- [name]: value → 입력한 input의 name 속성을 key로, 입력값을 value로 추가
- ...prev → 기존 상태를 유지 (spread 문법)
- 결과 = 입력될 때마다 formData가 점점 쌓이는 구조
formData = { sText: "Hello", sPage: "1", sGubun: "typeA" }
- useState → 상태 저장, 입력할 때마다 리렌더링
- useEffect → 컴포넌트가 마운트되거나 상태 바뀔 때 실행 (초기 데이터/API 호출)
- useRef → DOM 직접 접근, 렌더링 없이 값 유지
- useNavigate + useLocation → 페이지 이동 + 상태 전달 (JSP hidden input과 동일)
- prev + [name]: value → 여러 input을 한 객체(formData)에 자동으로 누적
- fetch() → 서버에서 JSON 데이터 가져오기
- => → 축약함수, this 없이 안전하게 사용