사용자 정의 컴포넌트는 이벤트 핸들러를 props로 받아서 내부의 DOM 요소에 전달함으로써 이벤트를 처리할 수 있습니다.
MyComponent
props.onClick은 div 요소의 onClick 이벤트 핸들러로 설정됩니다.
이렇게 하면 div를 클릭할 때 props.onClick에서 전달된 함수가 호출됩니다.
App
App 컴포넌트는 MyComponent를 렌더링하면서 onClick prop에 클릭 시
콘솔에 "Component clicked!"를 출력하는 함수를 전달합니다.
함수 전달 : App에서 MyComponent에 onClick prop으로 함수를 전달합니다. props : MyComponent에서 props.onClick을 통해 전달된 함수를 사용할 수 있습니다. 이벤트 핸들러 설정 : MyComponent는 이 함수를 div 요소의 클릭 이벤트에 연결하여 사용합니다.
function MyComponent(props) {
return <div onClick={props.onClick}>I'm a component!</div>;
}
function App() {
return <MyComponent onClick={() => console.log("Component clicked!")} />;
}
SyntheticEvent와 이벤트 객체
리액트에서는 브라우저의 기본 이벤트 객체를 래핑하는 SyntheticEvent 객체를 사용합니다.
이 객체는 다음과 같은 속성과 메서드를 제공하여 이벤트 처리와 관련된 다양한 작업을 가능하게 합니다.
🔎 SyntheticEvent의 주요 속성과 메서드
속성
type :이벤트의 유형을 나타냅니다. (예: "click", "change", "keypress" 등)
target :이벤트가 발생한 요소를 가리킵니다. 이 속성은 이벤트가 발생한 DOM 요소를 참조
currentTarget : 이벤트 리스너가 등록된 요소를 가리킵니다. (이벤트 핸들러 실행) target과는 달리 이벤트 핸들러가 실행되고 있는 요소를 참조
메서드
preventDefault() : 이벤트의 기본 동작을 막습니다. 예를 들어, 링크 클릭 시 페이지 이동을 막거나 폼 제출을 방지할 때 사용
stopPropagation() : 이벤트의 전파를 막습니다. 자식 요소에서 발생한 이벤트가 상위 요소로 전파되지 않도록 합니다.
e.nativeEvent
리액트의 SyntheticEvent 객체를 통해 접근할 수 있는 브라우저의 원래 이벤트 객체입니다. 이를 통해 브라우저의 기본 이벤트 속성이나 메서드에 접근할 수 있습니다.
🔎 기본 이벤트 객체 사용 예시
● SyntheticEvent는 리액트가 사용하는 이벤트 객체로, 브라우저 이벤트를 감싸고 다양한 속성과 메서드를 제공합니다.
● e.nativeEvent는 실제 브라우저의 원래 이벤트 객체를 참조할 수 있게 해줍니다.
● preventDefault()와 stopPropagation()메서드를 사용하여 이벤트의 기본 동작과 전파를 제어할 수 있습니다.
function App() {
const handleClick = (event) => {
console.log(event); // SyntheticEvent 객체 콘솔 출력
console.log('Event type:', event.type); // 이벤트 타입 출력
console.log('Event target:', event.target); // 이벤트 발생 요소 출력
console.log('Native event:', event.nativeEvent); // 원래 브라우저 이벤트 객체 출력
event.preventDefault(); // 기본 동작 방지
event.stopPropagation(); // 이벤트 전파 방지
};
return (
<div>
<button onClick={handleClick}>클릭하세요</button>
</div>
);
}
리액트 이벤트 속성
속성
설명
e.nativeEvent
React의 이벤트 객체 안에 포함된 브라우저의 원래 이벤트 객체
e.stopPropagation
이벤트가 상위 요소로 전파되는 것을 막습니다. (이벤트 버블링 제어) React는 단방향 데이터 흐름을 선호합니다.
이벤트 버블링
속성
설명
e.target
실제로 이벤트가 발생한 요소
e.currentTarget
이벤트 핸들러가 등록된 요소
리액트 이벤트 처리 / 상태관리
상태를 통한 UI 업데이트
상태 관리
리액트에서는 useState 훅을 사용하여 상태를 관리합니다. 상태를 사용하여 UI를 업데이트하고, 직접 DOM을 수정하지 않습니다.
장점
상태를 사용하면 리액트의 가상 DOM 덕분에 성능이 더 좋아지고, 코드가 일관되며 유지보수가 쉬워집니다.
이벤트 처리
루트 요소에서 처리
리액트는 이벤트를 가상 DOM에서 처리하고, 실제 DOM에는 직접 리스너를 추가하지 않습니다. 모든 이벤트는 루트 요소에서 처리됩니다.
예를 들어, <div id="reactRoot"></div>는 리액트가 이벤트를 처리하는 루트 요소가 될 수 있습니다.
이벤트 전파
이벤트는 상위 요소에서 하위 요소로 전달됩니다. 상위 요소에서 이벤트를 관리하고, 하위 요소에서 필요한 경우 처리할 수 있습니다.
이벤트 객체
e.nativeEvent
리액트의 이벤트 객체를 사용하여 원래 브라우저의 이벤트를 확인할 수 있습니다. 이를 통해 실제 브라우저 이벤트에 접근할 수 있습니다.
이벤트 전파 제어
e.stopPropagation()을 사용하여 이벤트의 상위 요소로의 전파를 막을 수 있습니다. 이는 이벤트 버블링을 방지하는 데 유용합니다.
import React from 'react';
function App() {
const handleClick = (e) => {
console.log('SyntheticEvent:', e); // SyntheticEvent 객체 콘솔 출력
console.log('NativeEvent:', e.nativeEvent); // 실제 브라우저 이벤트 객체 콘솔 출력
e.stopPropagation(); // 이벤트 전파를 멈춤
};
return (
<div>
<button onClick={handleClick}>클릭하세요</button>
</div>
);
}
export default App;
📌 useState 컴포넌트 상태 (단방향 데이터 흐름)
함수형 컴포넌트는 useState 훅을 통해 상태를 관리하며, this를 사용하지 않습니다. 이벤트 핸들러에서 콜백 함수는 함수형 컴포넌트의 스코프 내에서 직접 정의되므로
this와 관련된 문제를 신경 쓸 필요가 없습니다. 함수형 컴포넌트 내의 함수는 const로 선언하여 불변성을 유지하며, this 바인딩이 필요 없습니다.
상태 관리 리액트에서는 useState 훅을 사용하여 컴포넌트의 상태를 관리합니다. this를 사용하지 않고, 상태를 직접 업데이트합니다. const [count, setCount] = useState(0);
이벤트 핸들러 이벤트 핸들러는 컴포넌트 안에서 직접 작성되며, this 문제를 걱정할 필요가 없습니다. 이벤트 핸들러 함수는 const 로 선언하여 불변성을 유지 const handleClick = () => setCount(count + 1); // 이벤트 핸들러는 const로 선언됨
상태(state) 상태는 컴포넌트 내부에서 동적인 데이터를 저장하고 변경할 때 사용합니다. 이벤트와 함께 UI를 업데이트하는 데 유용합니다. const [count, setCount] = useState(0); // useState 훅을 사용하여 count 상태 관리
프로퍼티(props) props는 부모 컴포넌트에서 자식 컴포넌트로 전달되는 값 자식 컴포넌트 내부에서는 props 값을 변경할 수 없습니다. 자식 컴포넌트는 props를 읽기 전용으로 사용합니다. 값이 변경되어야 하는 경우, 부모 컴포넌트에서 useState 훅을 사용하여 상태를 관리하고, 이 상태를 자식 컴포넌트에 전달합니다. function Child(props) { return <p>{props.message}</p>; // props로 전달된 message를 출력 }
사용예시
부모 컴포넌트 (Parent)
useState 훅을 사용해 message 상태를 관리합니다.
updateMessage 함수를 통해 상태를 변경합니다.
상태 message를 자식 컴포넌트 Child에 props로 전달합니다.
자식 컴포넌트 (Child)
부모 컴포넌트로부터 전달받은 props.message를 화면에 출력합니다.
자식 컴포넌트는 전달받은 props를 변경할 수 없으며, 단순히 값을 표시합니다.
// ▶ 자식 컴포넌트
function Child(props) {
return <p>{props.message}</p>; // 부모 컴포넌트에서 전달된 message를 출력
}
// ▶ 부모 컴포넌트
import React, { useState } from 'react';
function Parent() {
const [message, setMessage] = useState('안녕하세요!'); // 상태를 사용하여 message를 관리
const updateMessage = () => {
setMessage('안녕히 가세요!'); // 버튼 클릭 시 상태를 변경하는 함수
};
return (
<div>
<h1>부모 컴포넌트</h1>
<Child message={message} /> {/* 최신 상태값이 자식 컴포넌트에 전달 */}
<button onClick={updateMessage}>메시지 변경</button> {/* 상태를 변경하는 버튼 */}
</div>
);
}
export default Parent;
🧐 State vs Prop
State
Prop
특징
일반 자바스크립트 객체
컴포넌트 내부 선언, 내부에서 값 변경 (저장)
부모 컴포넌트에서 자식 컴포넌트에 데이터 전달
앱의 유동적 데이터 다루는 객체
한 번 설정되면 변경 X
이벤트 핸들러는 일반적으로 state 를 업데이트
상위 컴포넌트는 하위 컴포넌트에 대한 props 설정
📝 State 선언 및 변경 방법
state의 값은 직접 변경하면 안 됩니다.
왜냐하면 이후 호출되는 setState()가 이전에 적용된 변경 사항을 덮어쓰기 때문입니다. state 값은 기본적으로 불변적(Immutable)인 데이터로 취급하고,
state의 값을 변경할때는 무조건 setState()를 이용해 state의 값을 변경해야 합니다. 즉, state는 👉 컴포넌트가 가지고 있는 데이터입니다.
State 선언 방법
const [state, setState] = useState(initialState);
React에서는 useState 훅을 사용하여 컴포넌트의 상태를 선언합니다.
useState는 두 가지 값을 반환합니다. 현재 상태 값과 상태를 업데이트하는 함수입니다.
여기서 state는 현재 데이터 값을 나타내고, setState는 상태를 업데이트하는 함수입니다.
State 값 변경
setState(newStateValue); // 상태를 업데이트하여 리렌더링 발생
직접 변경하지 말고, 상태를 변경할 때는 항상 setState 함수를 사용해야 합니다.
상태는 불변(Immutable)으로 취급되며, 직접 변경하지 않고 setState를 통해 변경해야 합니다.
// ✔️ set 함수를 통해 상태를 변경해야 리렌더링이 발생합니다.
setState(state + 1);
사용예시
📜 app.tsx : 할 일 목록을 관리하고, 버튼 클릭 시 새로운 할 일을 목록에 추가
📜 todo.tsx : 각 할 일 항목을 표시하는 역할
// ▶ app.tsx
import React, { useState } from "react";
import Todo from './todo';
export default function App() {
// useState를 사용하여 todoList라는 상태 변수를 선언합니다.
// todoList는 배열이며, 각 항목은 {index, title} 형태입니다.
let [todoList, todoListChanger] = useState<{index:number, title:string}[]>([]);
return (
<div>
{/* 버튼을 클릭하면 새로운 할 일 항목이 추가됩니다. */}
<button onClick={() => {
// [...todoList]는 현재 todoList의 모든 항목을 새로운 배열로 복사합니다.
// {index: todoList.length, title:"제목"}는 새로운 할 일 항목입니다.
// 이 새로운 항목을 포함한 새로운 배열을 생성하여 상태를 업데이트합니다.
todoListChanger([...todoList, {index: todoList.length, title:"제목"}]);
}}>
추가
</button>
{/* todoList의 각 항목을 Todo 컴포넌트를 사용하여 화면에 표시합니다. */}
<ol>
{todoList.map((item, index) => (
<li key={index}>
<Todo todo={item} />
</li>
))}
</ol>
</div>
);
}
// ▶ todo.tsx
import React from "react";
// Props 타입 정의
interface Props {
todo: {
index: number,
title: string
}
}
export default function Todo({ todo }: Props) {
const { index, title } = todo; // todo 객체를 분해하여 index와 title을 추출
return (
<div>
{/* 할 일 항목의 인덱스와 제목을 표시합니다. */}
<span style={{ display: "inline-block", width: 50 }}>{index}</span>
<span style={{ width: 200 }}>{title}</span>
</div>
);
}
사용예시
🔎 DOM Input 값을 State에 저장
상태 선언
inputValue라는 상태 변수를 useState 훅을 사용해 선언합니다. 이 상태는 input 필드의 값을 저장합니다.
상태 업데이트 함수
handleChange 함수는 input 필드의 값이 변경될 때마다 호출됩니다. 이 함수는 이벤트 객체에서 target.value를 읽어와 inputValue 상태를 업데이트합니다.
컴포넌트 렌더링
input 요소의 value 속성에 inputValue 상태를 할당하여 input 필드의 값을 제어합니다.
onChange 이벤트 핸들러로 handleChange 함수를 설정하여 입력 값이 변경될 때마다 상태를 업데이트합니다.
inputValue 상태의 값을 <span> 태그를 통해 화면에 표시합니다.
import { useState } from "react";
function App() {
// ▶ input에 입력한 값을 보관할 State를 생성하기
// → inputValue: 현재 입력 필드 값
// → setInputValue: 상태와 상태 업데이트 함수 정의
const [inputValue, setInputValue] = useState("");
// ▶ 입력 값이 변경될 때마다 상태를 업데이트하는 함수
function handleChange(event) {
setInputValue(event.target.value);
}
return (
<div>
{/* input 필드와 상태를 연결 */}
<input
value={inputValue} // 상태 값으로 input 필드의 값 설정
onChange={handleChange} // 입력 값이 변경될 때 상태를 업데이트하는 함수
/>
{/* 상태 값을 화면에 표시 */}
<span>{inputValue}</span>
</div>
);
}
🔎 컴포넌트간 이벤트 전달
🌀 자식 ➡️ 부모 (상향식 전달)
부모와 자식 컴포넌트 간의 이벤트 전달 및 상태 관리
Myform 컴포넌트 (자식) 이 컴포넌트는 입력 필드 <input> 를 반환합니다. 부모 컴포넌트로부터 전달받은 onChange 함수를 이벤트 핸들러로 설정하여, 입력 필드의 값이 변경될 때마다 onChange 함수가 호출됩니다.
App 컴포넌트 (부모) 이 컴포넌트는 상태를 관리합니다. Myform 컴포넌트를 포함하고 있으며, Myform에 onChange 이벤트 핸들러를 전달합니다. onChange 핸들러는 입력 필드의 값이 변경될 때 호출되며, username 상태를 업데이트합니다.
// ▶ Myform 컴포넌트
import React from 'react';
function Myform({ onChange }) {
// 부모 컴포넌트로부터 받은 onChange 함수 설정
return <input onChange={onChange} />; // 입력 필드의 값이 변경될 때 onChange 함수 호출
}
export default Myform;
// ▶ App 컴포넌트
import React, { useState } from 'react';
import Myform from './Myform';
function App() {
const [username, setUsername] = useState(""); // 상태를 정의하고 초기값을 빈 문자열로 설정
return (
<div className="App">
<h1>{username}님 환영합니다.</h1>
<Myform
onChange={(event) => {
setUsername(event.target.value); // 입력 값이 변경될 때마다 username 상태 업데이트
}}
/>
</div>
);
}
export default App;
🔎 커스텀 훅을 이용한 상태 관리
🌀 자식 ➡️ 부모 (상향식 전달)
부모와 자식 컴포넌트 간의 이벤트 전달 및 상태 관리
커스텀 훅은 상태 관리와 같은 로직을 재사용할 수 있게 해주는 특별한 함수입니다.
컴포넌트에서 복잡한 상태 관리 로직을 직접 작성하는 대신,
이 로직을 훅으로 분리하여 여러 컴포넌트에서 재사용할 수 있습니다.
커스텀 훅 상태와 상태를 업데이트하는 로직을 추상화합니다. 이 훅은 상태와 이를 변경할 수 있는 함수를 제공하여, 상태 관리 로직을 여러 컴포넌트에서 재사용할 수 있게 합니다.
부모 컴포넌트 커스텀 훅을 호출하여 상태와 상태를 업데이트하는 함수를 얻고, 이를 자식 컴포넌트에 전달합니다.
자식 컴포넌트 부모로부터 전달받은 상태와 상태 업데이트 함수를 사용하여 입력 필드의 값을 표시하고, 값이 변경될 때 상태 업데이트 함수가 호출되어 부모 컴포넌트의 상태가 변경되도록 합니다.
import React from 'react';
// ---------------------------------------------------------------------
// ▶ 커스텀 훅 (useTwoWayBinding)
// → 상태와 상태를 업데이트하는 함수
function useTwoWayBinding(initialValue) {
// initialValue: 처음 상태의 값 (예: 빈 문자열 "")
const [value, setValue] = React.useState(initialValue);
// 입력값이 변경되면 호출되는 함수
const handleChange = (e) => {
setValue(e.target.value);
};
// 상태와 상태를 업데이트하는 함수를 반환
return [value, handleChange];
}
// ---------------------------------------------------------------------
// ▶ 부모 컴포넌트 (ParentComponent)
// → 상태와 상태를 업데이트하는 함수를 사용하기 위해 커스텀 훅을 호출
import useTwoWayBinding from './useTwoWayBinding';
function ParentComponent() {
// 커스텀 훅을 사용하여 상태와 상태를 업데이트하는 함수를 얻습니다.
// → useTwoWayBinding 훅을 호출하여 상태(inputValue)와
// 상태를 업데이트하는 함수(setInputValue)를 얻습니다.
const [inputValue, setInputValue] = useTwoWayBinding('');
return (
// ChildComponent에 value와 onChange라는 props로 전달
<ChildComponent value={inputValue} onChange={setInputValue} />
);
}
// ---------------------------------------------------------------------
// ▶ 자식 컴포넌트 (ChildComponent)
// → 부모로부터 받은 value와 onChange를 사용
function ChildComponent({ value, onChange }) {
// ChildComponent는 value와 onChange를 props로 받습니다.
return <input type="text" value={value} onChange={onChange} />;
}