이번 포스팅에는 이벤트 핸들링과 컴포넌트 라이프사이클을 이해하여 Hooks의 기초를 다뤄보겠다.
리액트에서의 이벤트 핸들링은 자바스크립트에 직접 html요소에 적용하는 이벤트 핸들링과 차이가 있다.
사용자의 행동(클릭, 키보드 입력, 마우스 이동 등) 즉, 이벤트(Event) 가 발생했을 때, 이를 감지하고 미리 정의된 특정 JavaScript 함수(이벤트 핸들러) 를 실행시켜 상호작용에 반응하는 메커니즘임.
동적으로 이벤트 처리할 때 필수임.
주로 함수 컴포넌트 방식으로 사용되므로 함수 컴포넌트 기준으로 정리해보겠다.
이벤트 리스너 등록: HTML 요소에 직접 on<이벤트이름>
형태로 속성을 부여하는 대신, JSX 요소에 카멜 케이스(camelCase) 형태의 이벤트 속성(예: onClick
, onChange
)을 사용함.
핸들러 함수 연결: 이벤트 속성의 값으로는 문자열이 아닌, 실행될 JavaScript 함수(이벤트 핸들러) 자체를 {}
안에 넣어 전달함 (예: onClick={handleClick}
).
핸들러 함수 정의: 이벤트 핸들러 함수는 주로 해당 컴포넌트 내부에 함수로 선언됨.
const handleClick = () => { ... }
) 또는 일반 함수 선언(function handleClick() { ... }
) 모두 사용 가능함.이벤트 객체 (e
) 활용: 이벤트 핸들러 함수는 호출될 때 자동으로 이벤트 객체(e
)를 첫 번째 인자로 받음.
e.target
: 이벤트가 실제로 발생한 DOM(html) 요소를 가리킴.e.target.value
: e.target
의 실제 값으로, 주로 <input>
, <textarea>
, <select>
요소에서 현재 입력되거나 선택된 값을 가져올 때 사용함.상태 업데이트 연동: 이벤트 핸들러 내에서는 useState
훅으로 관리하는 상태를 상태 변경 함수(setState
)를 이용해 업데이트함으로써 UI 변경을 유발하는 것이 일반적임.
함수 이름으로 전달 (주로 쓰임)
function MyComponent() {
const handleClick = () => { console.log('클릭됨!'); };
return <button onClick={handleClick}>클릭</button>;
}
import React, { useState } from 'react';
function SimpleEventExample() {
// useState 훅으로 하나의 상태(name) 관리, 초기값은 빈 문자열
const [name, setName] = useState('');
// input 값이 변경될 때 실행될 이벤트 핸들러 함수
const handleChange = (e) => {
// e.target.value로 현재 input 값 가져와서 name 상태 업데이트
setName(e.target.value);
};
// 버튼 클릭 시 실행될 이벤트 핸들러 함수
const handleClick = () => {
// 현재 name 상태 값을 알림창으로 표시
alert(`입력된 이름: ${name}`);
// 필요하다면 상태 초기화
// setName('');
};
// 화면에 렌더링될 JSX
return (
<div>
<h1>간단한 이벤트 예제</h1>
<input
type="text"
placeholder="이름을 입력하세요"
value={name} // 상태와 input 값을 동기화 (제어 컴포넌트)
onChange={handleChange} // 값이 변경될 때마다 handleChange 함수 호출
/>
{/* 버튼 클릭 시 handleClick 함수 호출 */}
<button onClick={handleClick}>확인</button>
<p>현재 입력: {name}</p>
</div>
);
}
export default SimpleEventExample;
동적으로 렌더링 하는 리액트에서는 리스트를 하나하나 나열하기 보다는 map함수로 반복되는 컴포넌트를 렌더링할 수 있다.
배열 형태의 데이터를 기반으로 하여, 각 데이터 항목에 해당하는 여러 개의 리액트 컴포넌트를 동적으로 생성하고 화면에 렌더링하는 기법임.
정적인 목록 대신, API 응답 등 동적으로 변하는 데이터를 화면에 표시하는 데 필수적임.
map()
메소드 활용JavaScript의 내장 배열 메소드인 map()
을 사용하는 것이 핵심임.
map()
메소드는 배열의 각 요소를 순회하면서, 주어진 콜백 함수를 실행하고, 콜백 함수의 반환값들로 이루어진 새로운 배열을 생성함.
리액트에서는 이 원리를 이용하여, 데이터 배열을 컴포넌트 배열로 변환함.
const posts = [
{ id: 1, title: '첫 번째 글', content: '내용...' },
{ id: 2, title: '두 번째 글', content: '내용...' },
{ id: 3, title: '세 번째 글', content: '내용...' },
];
map()
사용{posts.map(post => /* ... */)}
{posts.map(post => (
<PostItem key={post.id} title={post.title} content={post.content} />
// 또는 간단히: <li key={post.id}>{post.title}</li>
))}
key
Prop의 중요성 및 규칙key
는 virtual DOM으로 변화를 감지할때 더욱 빠르게 알아낼 수 있기에 필수적이다.
필수성: map()
을 사용하여 엘리먼트 목록을 생성할 때는, 반드시 각 엘리먼트에 key
prop을 포함시켜야 함. key
는 리액트가 배열의 각 항목을 식별하는 데 사용하는 고유한 식별자임.
역할: 리액트는 key
를 통해 배열의 어떤 항목이 변경, 추가, 또는 삭제되었는지 효율적으로 파악하고, 최소한의 DOM 조작으로 화면을 업데이트함.
권장 사항: 데이터 항목 자체에 포함된 고유한 ID (예: 데이터베이스의 기본 키, uuid
등)를 key
값으로 사용하는 것이 가장 좋음 (key={item.id}
).
import React from 'react';
// 예시 데이터 배열 (각 객체는 고유한 id를 가짐)
const todos = [
{ id: 't1', text: '리액트 공부하기' },
{ id: 't2', text: '운동하기' },
{ id: 't3', text: '장보기' },
];
// 할 일 목록을 렌더링하는 컴포넌트
function TodoList() {
return (
<div>
<h1>오늘 할 일</h1>
<ul>
{/* todos 배열을 map() 메소드로 순회 */}
{todos.map((todo) => (
// 각 todo 객체에 대해 li 엘리먼트를 반환함
// *** 중요: map()으로 생성하는 각 엘리먼트에는 고유하고 안정적인 key prop을 반드시 지정해야 함 ***
// 여기서는 각 todo 항목의 고유 id를 key로 사용
<li key={todo.id}>
{/* todo 객체의 text 속성 값을 화면에 표시 */}
{todo.text}
</li>
))}
</ul>
</div>
);
}
export default TodoList;
위 이미지는 클래스 컴포넌트의 라이프사이클 다이어그램이다.
함수 컴포넌트에서는 useState
와 useEffect
훅(Hook)을 사용하여 이와 유사한 시점과 목적의 작업을 수행하고 요즘은 주로 함수 컴포넌트를 사용한다~~
useState
Hook정의: 함수 컴포넌트 내에서 상태(state) 를 관리하기 위한 기본적인 훅임. 컴포넌트가 기억해야 할 값이자 변경 시 리렌더링을 유발하는 데이터를 다룸.
사용법: const [상태 값, 상태를 설정하는 함수] = useState(상태의 기본 값);
useState(상태의 기본 값)
호출은 클래스 컴포넌트의 constructor
에서 this.state
를 초기화하는 것과 유사한 초기 상태 설정 역할임 (다이어그램의 "생성될 때" 단계의 시작 부분 해당).
반환된 state
는 현재 상태 값을 나타냄.
반환된 setState
함수는 상태 값을 업데이트하는 함수임. 이 함수를 호출하는 것은 클래스 컴포넌트의 this.setState()
호출과 유사하며, 컴포넌트의 업데이트(리렌더링) 과정을 시도함.
역할: 컴포넌트의 상태 정의, 초기화, 그리고 업데이트 트리거 역할을 수행함.
useEffect
Hook정의: 함수 컴포넌트 내에서 부수 효과(Side Effects)를 수행하기 위한 훅임. (데이터 가져오기, 구독 설정/해제, DOM 직접 조작 등 렌더링 자체와 직접 관련 없는 작업들)
라이프사이클과의 연관성: useEffect
는 두 번째 인자인 의존성 배열 을 어떻게 설정하느냐에 따라 클래스 컴포넌트의 여러 라이프사이클 메서드의 목적과 유사한 시점에서 작업을 수행하도록 제어 가능함.
useEffect
의 effect 함수는 기본적으로 렌더링 결과가 화면(DOM)에 반영된 이후에 실행됨
마운트 시 작업 (componentDidMount
유사):
useEffect(() => { /* 마운트 시 1회 실행할 작업 */ }, []);
의존성 배열을 빈 배열([]
)로 설정하면, effect 함수는 컴포넌트가 처음 렌더링되어 DOM에 마운트된 직후 딱 한 번만 실행됨.
주로 초기 데이터 로딩, 외부 라이브러리 연동, 이벤트 리스너/타이머/구독 설정 등에 사용됨 (다이어그램의 componentDidMount
가 호출되는 시점에 수행할 작업과 유사).
업데이트 시 작업 (componentDidUpdate
유사):
useEffect(() => { /* 특정 값 변경 시 실행할 작업 */ }, [dep1, dep2]);
의존성 배열에 특정 값(dep1
, dep2
등)을 넣으면, 마운트 시 1회 실행 + 지정된 의존성 중 하나라도 값이 변경되어 리렌더링될 때마다 다시 실행됨.
특정 props나 state의 변경에 따라 부수 효과를 재실행해야 할 때 사용됨 (다이어그램에서 props나 state 변경 후 componentDidUpdate
가 호출되는 시점에 수행할 작업과 유사).
언마운트 시 정리 작업 (componentWillUnmount
유사):
useEffect(() => { /* 설정 작업 */ return () => { /* 정리 작업(cleanup) */ }; }, [상황에 맞는 의존성 배열]);
Effect 함수 내에서 함수를 반환(return)하면, 이 반환된 함수(클린업 함수)는 컴포넌트가 DOM에서 제거되기 직전(언마운트 시) 또는 effect가 재실행되기 직전에 호출됨.
주로 useEffect
에서 설정했던 구독 해제, 타이머 제거, 이벤트 리스너 제거 등 메모리 누수 방지를 위한 정리 작업에 사용됨 (다이어그램의 componentWillUnmount
가 호출되는 시점에 수행할 작업과 유사).
매 렌더링 시 작업 (거의 안씀):
useEffect(() => { /* 매 렌더링 시 실행할 작업 */ });
의존성 배열을 생략하면, effect 함수는 컴포넌트가 렌더링될 때마다 실행됨 (마운트 시 포함).
성능 문제를 유발하거나 무한 루프에 빠질 수 있으므로 거의 사용 X