안녕하세요, 새해 복 많이 받으세요. 오늘은 2025.01.02 새해 첫 수업입니다. 2024.12.31 수업에 이어서 이벤트 핸들링 예제를 실습했습니다. 예제를 함수형 컴포넌트, 클래스형 컴포넌트로 나눠서 실행해보고, 축약해보면서 문법에 익숙해지는 시간이었습니다.



참고 사이트
https://ko.reactjs.org/docs/handling-events.html
https://reactjs.org/docs/events.html

import { useState } from "react";
function Title({ title }) {
return (
<p>현재 카운트는 {title}입니다.</p>
);
}
function Todo() {
const [count, setCount] = useState(0);
return (
<>
<Title title={count}></Title>
<button onClick={() => setCount(count + 1)}>증가</button>
</>
);
}
function App() {
return (
<Todo></Todo>
);
}
export default App;

import EventPractice from "./EventPractice";
function App() {
return (
<EventPractice />
);
}
export default App;
onChange={e => console.log(e.target.value)}
e.target.value 가 입력 받는 값을 의미한다.
\<button onClick={() => this.setState({ message: "" })}>삭제\
입력받은 input type에도 제거 내용을 적용하려면 state 변수값을 연결시켜줘야한다.
value={this.state.message} ⇐ 해당 라인이 없으면 삭제 버튼을 클릭해도 입력창 내용은 삭제되지 않음
위와 같이 state 변수와 파란 부분 문법이 같이 묶여 다녀야 함.
계산된 속성명을 이용해서 이벤트가 발생한 입력창의 이름(name)에 해당하는 상태변수의 값을 변경한다.
handlerChange = e => {
// 계산된 속성명
this.setState({ [e.target.name]: e.target.value });
};
아래의 코드를 추가한다.
handlerKeyUp = e => {
if (e.key === "Enter") {
this.handlerClick();
}
};
\
최종 코드는 아래와 같다.
import { Component } from "react";
class EventPractice extends Component {
state = {
message: "",
username: ""
};
/*
handlerChangeMessage = e => {
console.log(e.target.value);
this.setState({ message: e.target.value });
};
handlerChangeUsername = e => {
console.log(e.target.value);
this.setState({ username: e.target.value });
};
*/
handlerChange = e => {
// 계산된 속성명
this.setState({ [e.target.name]: e.target.value });
};
handlerClick = () => {
this.setState({ message: "", username: "" });
};
handlerKeyUp = e => {
if (e.key === "Enter") {
this.handlerClick();
}
};
render() {
return (
<div>
<h1>이벤트 연습</h1>
<input type="text" name="message" placeholder="입력해 보세요."
value={this.state.message}
onChange={this.handlerChange} onKeyUp={this.handlerKeyUp} />
<input type="text" name="username" placeholder="입력해 보세요."
value={this.state.username}
onChange={this.handlerChange} />
<h2>message: {this.state.message}</h2>
<h2>username: {this.state.username}</h2>
<button onClick={this.handlerClick}>삭제</button>
</div>
);
}
}
export default EventPractice;
const handlerChange = e => { if (e.target.name === "message") { setMessage(e.target.value); } else if (e.target.name === "username") { setUsername(e.target.value); } };<input type="text" name="message" placeholder="입력해 보세요." value={message} onChange={handlerChange} onKeyUp={handlerKeyUp} />
const [form, setForm] = useState({ message: "", username: "" });
// 객체 비구조화
const { message, username } = form;
const handlerChange = e => {
// 전개 연산자를 이용해서 form 객체의 사본을 만들고, 그 사본에 변경을 반영
const newForm = { ...form, [e.target.name]: e.target.value };
// 세터 함수를 이용해서 사본을 저장
setForm(newForm);
};
const handlerClick = () => {
const handlerKeyUp = e => {
if (e.key === "Enter") {
handlerClick();
}
최종 코드는 아래와 같다.
import { useState } from "react";
function EventPractice() {
/*
const [message, setMessage] = useState("");
const [username, setUsername] = useState("");
*/
const [form, setForm] = useState({ message: "", username: "" });
// 객체 비구조화
const { message, username } = form;
const handlerChange = e => {
/*
if (e.target.name === "message") {
setMessage(e.target.value);
} else if (e.target.name === "username") {
setUsername(e.target.value);
}
*/
// 전개 연산자를 이용해서 form 객체의 사본을 만들고, 그 사본에 변경을 반영
const newForm = { ...form, [e.target.name]: e.target.value };
// 세터 함수를 이용해서 사본을 저장
setForm(newForm);
};
const handlerClick = () => {
/*
setMessage("");
setUsername("");
*/
const newForm = { message: "", username: "" };
setForm(newForm);
};
const handlerKeyUp = e => {
if (e.key === "Enter") {
handlerClick();
}
};
return (
<div>
<h1>이벤트 연습</h1>
<input type="text" name="message" placeholder="입력해 보세요."
value={message}
onChange={handlerChange} onKeyUp={handlerKeyUp} />
<input type="text" name="username" placeholder="입력해 보세요."
value={username}
onChange={handlerChange} />
<h2>message: {message}</h2>
<h2>username: {username}</h2>
<button onClick={handlerClick}>삭제</button>
</div>
);
}
export default EventPractice;
정렬되지 않은 목록을 출력하는 컴포넌트를 생성한다.
import IterationSample from "./IterationSample";
function App() {
return (
<IterationSample />
);
}
export default App;
const words = ["봄", "여름", "가을", "겨울"];
const wordList = words.map(word => \<li>{word}\</li>);
return \<ul>{wordList}\</ul>;
};
export default IterationSample;
아래와 같은 warning 발생.

문자열을 데이터로 가지는 배열을 선언.
const IterationSample = () => {
const words = ["봄", "여름", "가을", "겨울"];
const wordList = words.map((word, index) => \<li key={index}>{word}</li>);
return \<ul>{wordList}\</ul>;
};
export default IterationSample;
키값을 갖는 객체의 배열을 갖게 함.
const IterationSample = () => {
const words = [
{ id: 1, word: "봄" },
{ id: 2, word: "여름" },
{ id: 3, word: "가을" },
{ id: 4, word: "겨울" }
];
const wordList = words.map(item => <li key={item.id}>{item.word}</li>);
return <ul>{wordList}</ul>;
};
export default IterationSample;






최종 코드는 아래와 같다.
import { useState } from "react";
const IterationSample = () => {
// 목록 데이터를 관리하는 상태변수
const [words, setWords] = useState([
{ id: 1, word: "봄" },
{ id: 2, word: "여름" },
{ id: 3, word: "가을" },
{ id: 4, word: "겨울" }
]);
const wordList = words.map(item => <li key={item.id} onDoubleClick={() => handleDoubleClick(item.id)}>{item.word}</li>);
// 입력창 내용을 관리하는 상태변수
const [inputText, setInputText] = useState("");
// id를 관리하는 상태변수
const [nextId, setNextId] = useState(5);
// 이벤트 핸들러
const handleChange = e => setInputText(e.target.value);
const handleAddItem = () => {
// const newWords = [ ...words, { id: nextId, word: inputText } ];
const newWords = words.concat({ id: nextId, word: inputText });
setWords(newWords);
setNextId(nextId + 1);
setInputText("");
};
const handleDoubleClick = id => {
const newWords = words.filter(item => item.id !== id);
setWords(newWords);
};
return (
<>
<input type="text" value={inputText} onChange={handleChange} />
<button onClick={handleAddItem}>추가</button>
<ul>{wordList}</ul>
</>
);
};
export default IterationSample;
부모 컴포넌트의 상태변수를 자식 컴포넌트의 props로 전달
부모 컴포넌트의 상태변수가 변경되면 부모 컴포넌트, 자식 컴포넌트 모두 다시 렌더링된다.

import { useState } from "react";
function ChildB({ value }) {
return (
<p>{value * 3}</p>
)
};
function ChildA({ count }) {
return (
<p>{count * 2}</p>
)
};
function Parent() {
const [count, setCount] = useState(0);
return (
<>
<input type="number" value={count} onChange={e => setCount(e.target.value)} />
<ChildA count={count} />
<ChildB value={count} />
</>
)
}
function App() {
return (
<Parent />
);
}
export default App;
자식 컴포넌트의 변경 사항을 부모 컴포넌트로 전달하는 방법.
부모 컴포넌트에서 정의한 함수를 자식 컴포넌트의 props 변수로 전달, 해당 함수를 자식 컴포넌트에서 이벤트 핸들러로 지정

import { useState } from "react";
function Child({ increment }) {
return <button onClick={increment}>하나증가</button>;
};
function Parent() {
const [count, setCount] = useState(0);
const increment = () => setCount(count + 1);
return (
<>
<h1>{count}</h1>
<Child increment={increment} />
</>
)
}
function App() {
return (
<Parent />
);
}
export default App;

최종 소스는 아래와 같다.
import { useState } from "react";
function Child({ addCnt, subCnt }) {
return (
<>
<button onClick={addCnt}>+1</button>
<button onClick={subCnt}>-1</button>
</>
);
};
function Parent() {
const [count, setCount] = useState(0);
const addCnt = () => setCount(count + 1);
const subCnt = () => setCount(count - 1);
const resetCnt = () => setCount(0);
return (
<>
<div>{count}</div>
<button onClick={resetCnt}>Reset</button>
<Child addCnt={addCnt} subCnt={subCnt}/>
</>
)
}
function App() {
return (
<Parent />
);
}
export default App;

import { useState } from "react";
function Controller({ plusOne, minusOne }) {
return (
<>
<button onClick={plusOne}> +1 </button>
<button onClick={minusOne}> -1 </button>
</>
);
}
function Display({ count }) {
return (
<h1>{count}</h1>
);
}
function Parent() {
const [count, setCount] = useState(0);
const reset = () => setCount(0);
const plusOne = () => setCount(count + 1);
const minusOne = () => setCount(count - 1);
return (
<>
<button onClick={reset}>Reset</button>
<Controller plusOne={plusOne} minusOne={minusOne} />
<Display count={count} />
</>
);
}
function App() {
return (
<Parent />
);
}
export default App;

import { useState } from "react";
function BoillingVerdict({ celsius }) {
if (celsius >= 100) {
return <div>물이 끓습니다.</div>
} else {
return <div>물이 끓지 않습니다.</div>
}
}
function toCelsius(fahrenheit) {
return (fahrenheit - 32) * 5 / 9;
}
function toFahrenheit(celsius) {
return (celsius * 9 / 5) + 32;
}
// 화씨, 섭씨를 변경하기 전 입력값 검증과 변경 후 일정한 형식으로 반올림하는 로직을 공통통 적용
function tryConvert(temperature, convert) {
const input = parseFloat(temperature);
if (Number.isNaN(input)) {
return "";
}
const output = convert(input);
const rounded = Math.round(output * 1000) / 1000;
return rounded.toString();
}
const scaleName = {
c: "섭씨",
f: "화씨"
};
function Temperature({ scale, changeTemperature, temperature }) {
// 변경된 내용을 부모 컴포넌트로 전달하므로, 상태 변수를 유지할 필요가 없음
// const [temperature, setTemperature] = useState(0);
// const handleChange = e => setTemperature(e.target.value);
const handleChange = e => changeTemperature(e.target.value);
return (
<fieldset>
<legend>온도를 입력하세요. (단위: {scaleName[scale]}) </legend>
<input value={temperature} onChange={handleChange} />
</fieldset>
);
}
function Calculator() {
const [temperature, setTemperature] = useState("");
const [scale, setScale] = useState("c");
// 화씨 온도가 입력된 경우 호출될 함수
const changeFahrenheit = t => {
setTemperature(t);
setScale("f");
};
// 섭씨 온도가 입력된 경우 호출될 함수
const changeCelsius = t => {
setTemperature(t);
setScale("c");
};
const celsius = scale === "f" ? tryConvert(temperature, toCelsius) : temperature;
const fahrenheit = scale === "c" ? tryConvert(temperature, toFahrenheit) : temperature;
return (
<>
<Temperature scale="c" changeTemperature={changeCelsius} temperature={celsius} />
<Temperature scale="f" changeTemperature={changeFahrenheit} temperature={fahrenheit} />
<BoillingVerdict celsius={celsius} />
</>
);
}
function App() {
return (
<>
<Calculator />
</>
);
}
export default App;
수업 중 어렵다고 느껴질 쯤 강사님께서 "이 부분이 잘 이해되길 바랍니다" , "갑자기 너무 어려워졌나요?" 라고 하셔서 신기했다. 그러다가 긴장감이 확 풀리는 시간이 종종 찾아오는데 집중을 잘 할 수 있는 방법을 찾아야할 것 같다.