1. React에서 반복 렌더링이 필요한 이유
실제 서비스에서는 같은 형태의 UI를 여러 번 출력해야 하는 경우가 많다.
예를 들어 회원 목록, 상품 목록, 댓글 목록, 할 일 목록처럼 데이터 배열을 기반으로 같은 구조를 반복해서 보여줘야 한다.
React에서는 이런 반복 렌더링을 map()을 사용해 처리한다.
배열의 각 요소를 JSX로 변환하여 화면에 렌더링하는 방식이다.
2. 배열을 화면에 출력하기
2.1 문자열 배열 렌더링
const fruits = ['사과', '바나나', '포도', '오렌지'];
<ul>
{fruits.map((fruit, index) => (
<li key={index}>{fruit}</li>
))}
</ul>
- 배열의 각 요소를 map()으로 순회한다.
- 각 요소마다 <li>를 만들어 출력한다.
- 반복 렌더링된 요소에는 key가 필요하다.
2.2 객체 배열 렌더링
const users = [
{ id: 1, name: '홍길동' },
{ id: 2, name: '유관순' },
{ id: 3, name: '이순신' }
];
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
- 객체 배열도 같은 방식으로 렌더링할 수 있다.
- 이때 key는 가능한 한 고유한 값을 사용하는 것이 좋다.
- id처럼 데이터 자체의 고유값을 사용하는 것이 가장 안정적이다.
3. key의 역할
React에서 리스트를 렌더링할 때 key는 각 요소를 구분하기 위한 식별자이다.
React는 key를 사용해 어떤 요소가 추가, 수정, 삭제되었는지 효율적으로 판단한다.
<li key={user.id}>{user.name}</li>
정리
- key는 반복 렌더링된 요소를 구분하는 기준이다.
- 가능하면 index보다 id 같은 고유값을 사용하는 것이 좋다.
- key가 안정적일수록 React가 변경 사항을 더 정확하게 추적할 수 있다.
4. 입력값과 리스트 추가
리스트를 단순히 출력하는 것에서 더 나아가, 사용자가 입력한 값을 목록에 추가하는 기능을 만들 수 있다.
이 과정에서는 useState를 사용해 입력값과 목록 상태를 함께 관리한다.
const [inputValue, setInputValue] = useState('');
const [names, setNames] = useState([{ id: 1, text: '판다' }]);
const [nextId, setNextId] = useState(2);
이후 입력창의 값은 onChange로 상태에 반영하고, 추가 버튼을 누르면 새로운 객체를 배열에 넣는다.
const handleAdd = () => {
if (inputValue.trim() === '') return;
const newName = { id: nextId, text: inputValue };
setNames([...names, newName]);
setNextId(nextId + 1);
setInputValue('');
};
정리
- 입력값은 상태로 관리한다.
- 새 항목을 추가할 때는 기존 배열을 직접 수정하지 않고 새 배열을 만들어야 한다.
- 전개 연산자 ...를 사용하면 기존 배열을 복사한 뒤 새 값을 추가할 수 있다.
- 입력 후에는 상태를 초기화해 입력창을 비울 수 있다.
5. 리스트 삭제와 filter
배열에서 특정 항목을 삭제할 때는 filter()를 사용한다.
삭제할 대상을 제외한 나머지만 모아서 새로운 배열을 만들면 된다.
const handleRemove = (idToRemove) => {
const updatedNames = names.filter(name => name.id !== idToRemove);
setNames(updatedNames);
};
정리
- filter()는 조건을 만족하는 요소만 남긴 새 배열을 반환한다.
- 삭제 기능은 원본 배열을 직접 변경하지 않고 새 배열로 상태를 갱신하는 방식으로 구현한다.
- React 상태는 불변성을 지키는 방식으로 다루는 것이 중요하다.
6. Todo List 구현
Todo List 예제에서는 지금까지 배운 반복 렌더링, 상태 관리, 이벤트 처리, 배열 메서드를 한 번에 적용했다.
6.1 할 일 추가
const newTodo = {
id: Date.now(),
text: inputValue,
completed: false
};
setTodos([...todos, newTodo]);
- 새로운 할 일 객체를 만든다.
- completed 상태를 함께 넣어 완료 여부를 관리한다.
- Date.now()를 사용해 간단한 고유 id를 만들었다.
6.2 할 일 삭제
setTodos(todos.filter(todo => todo.id !== id));
6.3 완료 상태 토글
setTodos(
todos.map(todo =>
todo.id === id
? { ...todo, completed: !todo.completed }
: todo
)
);
- map()을 사용해 배열 전체를 순회한다.
- 조건에 맞는 항목만 새로운 객체로 바꾸고, 나머지는 그대로 둔다.
- 체크박스 상태와 연결해 완료 여부를 반영한다.
정리
- 추가는 ...배열과 새 객체를 이용한다.
- 삭제는 filter()를 사용한다.
- 수정은 map()을 사용해 필요한 요소만 바꾼다.
- 상태는 직접 변경하지 않고 항상 새로운 배열과 객체를 만들어 갱신한다.
7. 컴포넌트 분리와 Props 활용
Todo 예제에서는 TodoItem 컴포넌트를 따로 만들어 각 항목을 렌더링했다.
function TodoItem({ todo, onRemove, onToggle }) {
return (
<div>
<input
type="checkbox"
checked={todo.completed}
onChange={() => onToggle(todo.id)}
/>
<span>{todo.completed ? <s>{todo.text}</s> : todo.text}</span>
<button onClick={() => onRemove(todo.id)}>X</button>
</div>
);
}
정리
- 부모 컴포넌트는 데이터를 관리한다.
- 자식 컴포넌트는 전달받은 props로 화면을 그린다.
- 삭제 함수와 토글 함수도 props로 내려보낼 수 있다.
- 컴포넌트를 분리하면 구조가 명확해지고 재사용성이 높아진다.
8. useEffect란
useEffect는 함수형 컴포넌트에서 렌더링이 끝난 뒤 실행할 작업을 처리하는 Hook이다.
- 콘솔 출력이나 로그 기록
- 타이머 등록
- 문서 제목 변경
- API 호출
- 구독 설정 및 정리
useEffect(() => {
console.log('렌더링 이후 동작...');
});
정리
- 컴포넌트 본문은 렌더링 과정에서 실행된다.
- useEffect는 렌더링이 끝난 뒤 실행된다.
- 화면에 직접 보여주는 코드와 부수 효과를 분리할 수 있다.
9. useEffect의 실행 시점
9.1 의존성 배열 생략
useEffect(() => {
console.log('렌더링될 때마다');
});
- 렌더링이 일어날 때마다 실행된다.
- 처음 렌더링 이후에도 실행되고, 상태가 바뀌어 다시 렌더링될 때도 실행된다.
9.2 빈 배열 []
useEffect(() => {
console.log('컴포넌트가 처음 화면에 나타남');
}, []);
- 처음 마운트될 때 한 번만 실행된다.
- 초기 데이터 로딩, 최초 설정 작업 등에 사용한다.
9.3 특정 값 의존
useEffect(() => {
console.log(`count 값이 ${count}로 변경되어 실행`);
}, [count]);
- 지정한 값이 변경될 때만 실행된다.
- 필요한 변화에만 반응하도록 제어할 수 있다.
정리
- 의존성 배열이 없으면 매 렌더링마다 실행된다.
- 빈 배열이면 최초 한 번만 실행된다.
- 배열에 특정 값을 넣으면 그 값이 바뀔 때만 실행된다.
10. 렌더링과 useEffect의 차이
console.log()를 컴포넌트 함수 본문에 작성하면 렌더링 시점에 실행된다.
반면 useEffect() 안에 작성하면 렌더링이 끝난 뒤 실행된다.
function MessagePrinter() {
console.log('렌더링');
useEffect(() => {
console.log('렌더링 이후 동작...');
});
return <h1>Hello</h1>;
}
정리
- 컴포넌트 함수 내부 코드는 렌더링 과정에서 실행된다.
- useEffect는 렌더링 완료 후 실행된다.
- 렌더링과 부수 효과의 실행 시점을 구분하는 것이 중요하다.
11. 마운트(Mount)
마운트는 컴포넌트가 처음 화면에 나타나는 시점을 의미한다.
useEffect(() => {}, []) 형태는 마운트 시점에 한 번만 실행된다.
useEffect(() => {
console.log('useEffect 동작');
}, []);
예제에서는 버튼을 눌러 시간을 변경해도 useEffect는 다시 실행되지 않았다.
왜냐하면 의존성 배열이 빈 배열이기 때문에 최초 마운트 때만 동작하기 때문이다.
정리
- 마운트는 컴포넌트가 처음 생성되어 화면에 나타나는 시점이다.
- 빈 의존성 배열은 마운트 시 한 번 실행되는 패턴이다.
12. 문서 제목 변경
useEffect를 활용하면 브라우저 탭 제목도 상태에 따라 바꿀 수 있다.
useEffect(() => {
document.title = `넌 ${count}번 클릭했어`;
});
정리
- document.title처럼 화면 외부의 브라우저 환경을 다룰 때 useEffect가 적합하다.
- 상태 변화에 맞춰 문서 제목도 함께 갱신할 수 있다.
13. Cleanup 함수와 Unmount
useEffect는 정리 함수도 반환할 수 있다.
이 함수는 컴포넌트가 화면에서 사라질 때 실행된다.
useEffect(() => {
const timerId = setInterval(() => {
console.log('...1초 경과');
}, 1000);
return () => {
clearInterval(timerId);
};
}, []);
이 패턴은 타이머, 이벤트 리스너, 구독 해제 같은 정리 작업에 매우 중요하다.
정리
- useEffect에서 함수를 반환하면 cleanup 함수가 된다.
- cleanup 함수는 컴포넌트가 사라질 때 실행된다.
- 타이머나 구독을 정리하지 않으면 메모리 누수나 불필요한 동작이 발생할 수 있다.
14. 라이프사이클 흐름 이해
오늘 예제를 통해 함수형 컴포넌트에서도 마운트, 업데이트, 언마운트 개념을 이해할 수 있었다.
14.1 마운트
- 컴포넌트가 처음 나타남
- useEffect(..., []) 실행
14.2 업데이트
- 상태가 바뀌어 다시 렌더링됨
- 의존성 배열 조건에 따라 useEffect 실행
14.3 언마운트
- 컴포넌트가 화면에서 사라짐
- cleanup 함수 실행
버튼으로 showTimer 값을 바꾸어 Timer 컴포넌트를 보이거나 숨기면서 언마운트 시 cleanup이 동작하는 흐름을 확인했다.
{showTimer && <Timer />}
정리
- React 함수형 컴포넌트도 생명주기 개념을 가진다.
- useEffect를 통해 라이프사이클별 동작을 제어할 수 있다.
- 특히 언마운트 시 정리 작업은 매우 중요하다.
15. 오늘 배운 핵심 정리
- React에서 배열을 반복 렌더링할 때는 map()을 사용한다.
- 반복 렌더링된 요소에는 key가 필요하며, 가능하면 고유한 id를 사용해야 한다.
- 리스트에 항목을 추가할 때는 기존 배열을 복사한 새 배열을 만들어 상태를 갱신한다.
- 삭제는 filter(), 수정은 map()으로 처리하는 패턴이 중요하다.
- Todo List는 추가, 삭제, 완료 토글을 통해 상태 변경 패턴을 종합적으로 연습하기 좋은 예제이다.
- useEffect는 렌더링이 끝난 뒤 실행되는 부수 효과 처리용 Hook이다.
- 의존성 배열이 없으면 매 렌더링마다, 빈 배열이면 마운트 시 한 번만, 특정 값을 넣으면 그 값이 바뀔 때만 실행된다.
- useEffect는 문서 제목 변경, 타이머 등록, 외부 자원 제어에 유용하다.
- cleanup 함수는 언마운트 시점에 실행되어 타이머나 이벤트를 정리한다.
- 함수형 컴포넌트에서도 마운트, 업데이트, 언마운트 흐름을 이해하는 것이 중요하다.
'TIL > [TIL]' 카테고리의 다른 글
| [TIL]React API 통신과 데이터 컴포넌트 (0) | 2026.04.21 |
|---|---|
| [TIL]React Hooks 심화와 비동기 프로그래밍 (0) | 2026.04.20 |
| [TIL]React 기초: createElement, 컴포넌트, JSX, Props, State, 이벤트 처리 (0) | 2026.04.16 |
| [TIL]JavaScript 이벤트와 비동기 프로그래밍, BOM (1) | 2026.04.15 |
| [TIL]JavaScript DOM 조작 완벽 정리 (0) | 2026.04.14 |