useState
- 컴포넌트에 상태를 추가하는 Hook
- 화면에 보여주는 데이터를 저장하고 변경할 수 있게 해주는 기능
기본 사용법
const [상태, 상태변경함수] = useState(초기값);
const [state, setState] = useState(초기값);
- state: 현재 상태 값
- setState: 상태를 업데이트하는 함수
- 초기값: 처음 렌더링 시 사용할 값
📍 특징
1️⃣ 상태 업데이트
상태를 업데이트하면 컴포넌트가 리렌더링 됨
2️⃣ 중요한 규칙
- Hook은 컴포넌트 최상위 레벨에서만 호출
- 조건문이나 반복문 안에서 사용 불가
- 이벤트 핸들러나 생명주기 내에서 호출 가능
3️⃣ 여러 상태 변수
하나의 컴포넌트에 여러 상태 변수 사용 가능
function 상품목록페이지() {
const [검색어, 검색어변경] = useState('');
const [카테고리, 카테고리변경] = useState('전체');
const [정렬기준, 정렬기준변경] = useState('인기순');
return (
<div>
<input
value={검색어}
onChange={(e) => 검색어변경(e.target.value)}
placeholder="상품 검색"
/>
<select
value={카테고리}
onChange={(e) => 카테고리변경(e.target.value)}
>
<option value="전체">전체</option>
<option value="의류">의류</option>
<option value="가전">가전</option>
</select>
{/* 정렬 옵션 등 */}
{/* 필터링된 상품 목록 표시 */}
</div>
);
}
4️⃣ 객체와 배열 상태
객체나 배열을 업데이트할 때는 불변성을 지켜야 함
// 객체 업데이트 (잘못된 방법)
form.name = '김철수'; // ❌
// 객체 업데이트 (올바른 방법)
setForm({...form, name: '김철수'}); // ✅
// 배열 업데이트 (올바른 방법)
setItems([...items, 새항목]); // ✅
5️⃣ 상태 업데이트 특성
- 비동기적: setState 호출 직후 상태가 즉시 변경되지 않음
- 일괄 처리: React는 여러 상태 업데이트를 일괄 처리함
- 이전 상태 사용: 이전 상태를 기반으로 업데이트할 때는 함수 형태를 사용
import React, { useState } from 'react'
const App = () => {
const [age, setAge] = useState(0)
const increment = () => {
// setAge(age + 1)
setAge(prev => prev + 1) // 기존 값 기억하는 방식으로 쓰는 것이 유용
}
return (
<div>
<h1>연습</h1>
<button
onClick={() => {
increment()
increment()
increment()
}}
>
+3
</button>
<button onClick={increment}>+1</button>
<h2>입력값 : {age}</h2>
</div>
)
}
export default App
🔥 자주 발생하는 문제와 해결법
1️⃣ 상태 업데이트 후 이전 값 출력
상태 업데이트는 비동기적으로 처리되어 즉시 반영되지 않음
function 클릭핸들러() {
set개수(개수 + 1);
console.log(개수); // 여전히 이전 값 출력
}
2️⃣ 객체/배열 변경이 화면에 반영되지 않음
const [할일목록, set할일목록] = useState();
// 잘못된 방법 ❌
const 항목들 = [...할일목록];
항목들[0].완료 = true;
set할일목록(항목들); // 참조가 같아 변경 감지 안됨
// 올바른 방법 ✅
set할일목록(이전목록 =>
이전목록.map((항목, 인덱스) =>
인덱스 === 0 ? {...항목, 완료: true} : 항목
)
);
3️⃣ "Too many re-renders" 오류
// 잘못된 방법
return <button onClick={핸들러()}>클릭</button> // ❌
// 올바른 방법
return <button onClick={핸들러}>클릭</button> // ✅
return <button onClick={() => 핸들러()}>클릭</button> // ✅
✨ 유용한 팁
1️⃣ 초기 상태를 계산하는 함수 사용
무거운 계산이 필요한 초기값은 함수로 전달하면 첫 렌더링에만 실행됨
// 매 렌더링마다 실행 (비효율적)
const [items, setItems] = useState(createExpensiveList()); // ❌
// 초기화 시에만 한 번 실행 (효율적)
const [items, setItems] = useState(() => createExpensiveList()); // ✅
2️⃣ key를 사용한 상태 초기화
컴포넌트에 다른 key를 전달하면 React는 컴포넌트를 새로 생성하고 상태를 초기화함
function 앱() {
const [사용자ID, 사용자ID변경] = useState(1);
return (
<>
<button onClick={() => 사용자ID변경(사용자ID + 1)}>
다음 사용자
</button>
<프로필 key={사용자ID} /> {/* key가 변경되면 상태 초기화 */}
</>
);
}
📄 로렘 입숨 (Lorem Ipsum), 🖼️ 로렘 픽숨 (Lorem Picsum)
로렘 입숨 | 더미 텍스트 | Lorem ipsum dolor sit amet... |
로렘 픽숨 | 더미 이미지 | https://picsum.photos/ |
📌 position: absolute가 되면?
- 기본 블록 요소(ex: div, p)는 원래 부모 영역을 가득 채움
- position: absolute를 주면,
- 문서 흐름에서 빠지고,
- 자기 크기를 "내용(content)"에 맞춰 잡기 시작
➡️ inline-block처럼 "너비, 높이 = 내용만큼" 가짐
🎯 CSS transition-timing-function
이름 | 느낌 | 설명 | 주로 쓰는 곳 |
ease | 부드럽게 | 기본값. 천천히 → 빠르게 → 천천히 | 대부분 자연스러운 애니메이션 |
ease-in | 점점 빠르게 | 처음엔 천천히, 끝날수록 빠르게 | 들어오는 애니메이션 시작 효과 |
ease-out | 점점 느리게 | 처음엔 빠르게, 끝날수록 느리게 | 나가는 애니메이션 마무리 효과 |
ease-in-out | 양쪽 부드럽게 | 처음 천천히 → 중간 빠르게 → 끝 천천히 | 자연스럽게 시작 + 끝내기 |
linear | 일정하게 | 속도가 처음부터 끝까지 똑같음 | 로딩바, 기계적 움직임 |
nodemon
Node.js 서버를 자동으로 재시작해주는 개발 도구
node로 서버 돌리면 매번 수동으로 껐다 켜야 하는데, nodemon은 수정 감지 → 자동 재시작 해줘서 개발할 때 편함
- 설치
npm install -g nodemon
- 실행
nodemon app.js
고차함수
다른 함수를 인자로 받거나 함수를 반환하는 함수
더보기
더보기
더보기
더보기
1. map()
배열의 모든 요소를 변환하여 새 배열 반환
// 숫자 배열의 각 요소를 2배로 만들기
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(num => num * 2);
console.log(doubled); // [2, 4, 6, 8, 10]
// 객체 배열에서 특정 속성만 추출하기
const users = [
{ id: 1, name: '김철수', age: 25 },
{ id: 2, name: '이영희', age: 30 },
{ id: 3, name: '박민수', age: 28 }
];
const names = users.map(user => user.name);
console.log(names); // ['김철수', '이영희', '박민수']
2. filter()
조건에 맞는 요소만 포함하는 새 배열 반환
// 짝수만 필터링하기
const numbers = [1, 2, 3, 4, 5, 6];
const evens = numbers.filter(num => num % 2 === 0);
console.log(evens); // [2, 4, 6]
// 특정 나이 이상인 사용자만 필터링하기
const users = [
{ name: '김철수', age: 25 },
{ name: '이영희', age: 30 },
{ name: '박민수', age: 18 }
];
const adults = users.filter(user => user.age >= 20);
console.log(adults); // [{ name: '김철수', age: 25 }, { name: '이영희', age: 30 }]
3. reduce()
배열의 요소를 하나의 값으로 줄이기
// 배열 요소의 합계 구하기
const numbers = [1, 2, 3, 4, 5];
const sum = numbers.reduce((accumulator, current) => accumulator + current, 0);
console.log(sum); // 15
// 객체 배열의 특정 속성 합계 구하기
const cart = [
{ item: '노트북', price: 1200000 },
{ item: '마우스', price: 35000 },
{ item: '키보드', price: 45000 }
];
const totalPrice = cart.reduce((total, product) => total + product.price, 0);
console.log(totalPrice); // 1280000
4. forEach()
배열의 각 요소에 대해 함수 실행
// 배열의 각 요소 출력하기
const fruits = ['사과', '바나나', '오렌지'];
fruits.forEach(fruit => console.log(fruit));
// 사과
// 바나나
// 오렌지
// 배열 요소의 합계 계산하기 (부수 효과 사용)
let total = 0;
[1, 2, 3, 4].forEach(num => {
total += num;
});
console.log(total); // 10
5. find()
조건을 만족하는 첫 번째 요소 반환
// 특정 ID를 가진 사용자 찾기
const users = [
{ id: 1, name: '김철수' },
{ id: 2, name: '이영희' },
{ id: 3, name: '박민수' }
];
const user = users.find(user => user.id === 2);
console.log(user); // { id: 2, name: '이영희' }
6. some()
하나라도 조건을 만족하면 true 반환
// 배열에 음수가 있는지 확인
const numbers = [1, 2, 3, -1, 4];
const hasNegative = numbers.some(num => num < 0);
console.log(hasNegative); // true
7. every()
모든 요소가 조건을 만족하면 true 반환
// 모든 사용자가 성인인지 확인
const users = [
{ name: '김철수', age: 25 },
{ name: '이영희', age: 30 },
{ name: '박민수', age: 18 }
];
const allAdults = users.every(user => user.age >= 20);
console.log(allAdults); // false
8. flatMap()
map() 후 결과를 1레벨 평탄화
// 문장을 단어 배열로 변환
const sentences = ['Hello world', 'I love JavaScript'];
const words = sentences.flatMap(sentence => sentence.split(' '));
console.log(words); // ['Hello', 'world', 'I', 'love', 'JavaScript']
9. sort()
배열 요소 정렬
const numbers = [3, 1, 4, 1, 5];
numbers.sort((a, b) => a - b);
console.log(numbers); // [1, 1, 3, 4, 5]
// 객체 배열 정렬
const users = [
{ name: '김철수', age: 25 },
{ name: '이영희', age: 30 },
{ name: '박민수', age: 18 }
];
users.sort((a, b) => a.age - b.age);
console.log(users); // 나이순으로 정렬됨
10. findIndex()
조건을 만족하는 첫 번째 요소의 인덱스 반환
const fruits = ['사과', '바나나', '오렌지', '포도'];
const index = fruits.findIndex(fruit => fruit === '오렌지');
console.log(index); // 2
11. flat()
중첩 배열을 평탄화
const nestedArray = [1, [2, 3], [4, [5, 6]]];
const flattened = nestedArray.flat(2); // 깊이 2까지 평탄화
console.log(flattened); // [1, 2, 3, 4, 5, 6]
12. reduceRight()
오른쪽에서 왼쪽으로 reduce 실행
const array = ['a', 'b', 'c', 'd'];
const result = array.reduceRight((acc, curr) => acc + curr);
console.log(result); // 'dcba'
13. at()
양수 또는 음수 인덱스로 요소에 접근 (ES2022)
const array = [5, 12, 8, 130, 44];
console.log(array.at(2)); // 8
console.log(array.at(-1)); // 44 (마지막 요소)
14. Array.from()
유사 배열 객체나 이터러블을 배열로 반환
// 문자열의 각 문자를 배열로 변환
console.log(Array.from('hello')); // ['h', 'e', 'l', 'l', 'o']
// 매핑 함수 사용 (두 번째 인자)
console.log(Array.from([1, 2, 3], x => x * 2)); // [2, 4, 6]
15. Array.of()
주어진 인자로 새 배열 생성
console.log(Array.of(1, 2, 3)); // [1, 2, 3]
console.log(Array.of('a', 'b', 'c')); // ['a', 'b', 'c']
16. entries(), keys(), values()
배열의 반복자 메서드
const array = ['a', 'b', 'c'];
// entries() - [인덱스, 값] 쌍의 반복자 반환
for (const [index, element] of array.entries()) {
console.log(index, element);
}
// keys() - 인덱스의 반복자 반환
for (const index of array.keys()) {
console.log(index);
}
// values() - 값의 반복자 반환
for (const element of array.values()) {
console.log(element);
}
실습
입력 창에 텍스트 입력 후 버튼 또는 엔터 키를 눌렀을 때 로컬 스토리지에 데이터 저장 + 저장된 리스트 가져오기
- InputField.jsx
import React, { useState } from 'react'
const InputField = ({ setData }) => {
const [inputText, setInputText] = useState('')
const inputItem = e => {
setInputText(e.target.value)
}
const addItem = () => {
// 입력 검증
if (inputText.trim().length < 2) {
alert(inputText.trim() === '' ? '여행지를 입력하세요' : '2자 이상 입력하세요')
document.querySelector('input').focus()
return
}
// 로컬 스토리지 저장하기
setData(prev => {
const newData = [inputText, ...prev]
localStorage.setItem('trip', JSON.stringify(newData))
return newData
})
setInputText('')
document.querySelector('input').focus()
}
// Enter key 입력
const handleKeyUp = e => {
if (e.key === 'Enter') {
addItem()
}
}
return (
<div className="inputField mw">
<input
type="text"
placeholder="가고싶은 여행지를 등록하세요"
value={inputText}
onChange={inputItem}
onKeyUp={handleKeyUp}
/>
<button onClick={addItem}>ADD</button>
</div>
)
}
export default InputField
- app.jsx
import React, { useState } from 'react'
import Header from './components/Header'
import './index.css'
import InputField from './components/InputField'
import PostList from './components/PostList'
import NoList from './components/NoList'
const App = () => {
// const list = ['송도', '인천', '서울', '부산']
const list = JSON.parse(localStorage.getItem('trip')) || []
const [data, setData] = useState(list)
return (
<>
<Header />
<InputField setData={setData} />
{data.length === 0 ? <NoList /> : <PostList data={data} setData={setData} />}
</>
)
}
export default App
- PostList.jsx
import React from 'react'
import List from './List'
const PostList = ({ data, setData }) => {
return (
<div className="mw">
<div className="totalCount">
<strong>Total</strong>
<span>{data.length}</span>
</div>
<ul className="postList">
{data.map((item, index) => (
<List key={index} item={item} setData={setData} />
))}
</ul>
</div>
)
}
export default PostList
- List.jsx
import React from 'react'
const List = ({ item, setData }) => {
const removeItem = () => {
setData(prev => {
const newData = prev.filter(p => p !== item)
localStorage.setItem('trip', JSON.stringify(newData))
return newData
})
}
return (
<li>
<p>{item}</p>
<i className="fa-regular fa-trash-can" onClick={removeItem}></i>
</li>
)
}
export default List
728x90
반응형
'LG 유플러스 유레카 SW > React' 카테고리의 다른 글
[#52] React Router (2) | 2025.04.11 |
---|---|
[#51] 가위바위보 게임 (feat. React) (0) | 2025.04.10 |
[#49] React 설정 (1) | 2025.04.08 |
[#46] React 컴포넌트, props, state (1) | 2025.04.02 |
[#45] React란 (0) | 2025.04.01 |