브라우저에서 사용자의 조작이나 환경의 변화로 벌어진 사건
• 예시) 사용자가 버튼을 클릭한 사건 – 버튼 클릭 이벤트
이벤트 핸들링
• 다양한 이벤트를 원하는 대로 처리하는 것
이벤트 핸들러(Event Handler)
• 어떤 이벤트가 발생했을 때 해당 이벤트를 처리하는 함수
• 이벤트 리스터(Listener) 라고도 한다
이벤트의 선언은 카멜 표기법(Camel Case) 로 사용한다.
import React, { useState } from "react";
function ToggleFunc(props) {
const [isToggleOn, setIsToggleOn] = useState(true);
// 방법 1. 함수를 정의
function handleClick() {
setIsToggleOn((isToggleOn) => !isToggleOn);
}
// 방법 2. arrow function을 사용하여 정의
const handleClick2 = () => {
setIsToggleOn((isToggleOn) => !isToggleOn);
};
return (
<button onClick={handleClick}>
{isToggleOn ? "on" : "off"}
</button>
);
}
export default ToggleFunc;

import React, {useState, useMemo} from "react";
function Sample2(props) {
function handelClick1() { // 인자값 x
console.log("handleClick1()");
}
function handleClick2(message) {
console.log(message);
}
return (
<div>
<button onClick={ handelClick1 }>클릭1</button>
<button onClick={ handleClick2('hello-bad') }>클릭2</button> {/*이 코드는 정상적으로 작동하지 않음 */}
<button onClick={ () => handleClick2('hello-good') }>클릭3</button>
</div>
)
}
export default Sample2;
Click1 은 매개변수 없이 호출하는 방식
Click2 는 매개변수 전달의 잘못된 예 (버튼 누르기 전에 실행됨!)
Click3 은 매개변수 전달의 올바른 예 (함수형으로 감싸기)
아래는 화면에 출력되는 버튼

import React, { useState } from "react";
export default function Sample2() {
const [isChecked, setIsChecked] = useState(false);
function handleClick(event) {
console.log(event);
event.target.innerText += "*";
}
const handleCheckboxChange = (event) => {
console.log(event.target.checked);
setIsChecked(!isChecked);
};
return(
<div>
<button onClick={handleClick}> 클릭 </button>
<input type='checkbox'
checked={isChecked}
onChange={handleCheckboxChange}
/>
</div>
);
}


import React, { useState } from 'react';
export default function Join() {
// [필수] 약관 체크 상태
const [isTermsAgreed, setIsTermsAgreed] = useState(false);
// [선택] 광고 마케팅 체크 상태
const [isMarketingAgreed, setIsMarketingAgreed] = useState(false);
// [필수] 약관 동의 체크시 이벤트 핸들러
const handleTermsChange = (event) => {
setIsTermsAgreed(event.target.checked);
};
const handleMarketingChange = (event) => {
setIsMarketingAgreed(event.target.checked);
};
const handleSignup = () => {
alert('가입이 완료되었습니다!');
};
return (
<div>
<h2>약관 동의</h2>
<label>
<input
type="checkbox"
checked={isTermsAgreed}
onChange={handleTermsChange}
/>
[필수] 약관에 동의합니다.
</label>
<br />
<label>
<input
type='checkbox'
checked={isMarketingAgreed}
onChange={handleMarketingChange}
/>
</label>
[선택] 광고 마케팅에 동의합니다
<br />
<button
onClick={handleSignup}
disabled={!isTermsAgreed}>
가입
</button>
</div>
);
}

'가입' 버튼을 눌렀을 때 [필수] 항목이 체크된 경우에만 실행된다
import React from 'react';
function UserGreeting(props) { return <h1>다시 오셨군요!</h1> }
function GuestGreeting(props) { return <h1>로그인을 해주세요.</h1> }
export default function Greeting(props) {
const isLoggedIn = props.isLoggedIn;
if(isLoggedIn) {
return <UserGreeting />;
}
return <GuestGreeting />;
}
if 부터 return문까지 조건부 렌더링
- Greeting 컴포넌트 – props로 들어오는 isLoggedIn의 값에 따라서 화면에 출력되는 결과가 달라짐 (조건부 렌더링)


import React, { useState } from "react";
import Greeting from "./Greeting";
function LoginButton(props) {
return (
<button onClick={props.onClick}>
로그인
</button>
);
}
function LogoutButton(props) {
return (
<button onClick={props.onClick}>
로그아웃
</button>
);
}
export default function LoginControl() {
const [isLogged, setIsLogged] = useState(false);
const handleLogin = () => {
setIsLogged(true);
};
const handleLogout = () => {
setIsLogged(false);
};
let button;
if (isLogged) {
button = <LogoutButton onClick={handleLogout} />;
} else {
button = <LoginButton onClick={handleLogin} />;
}
return (
<div>
<Greeting isLoggedIn={isLogged} />
{button}
</div>
);
}
인라인 조건(Inline Conditions)
• 조건문을 코드 안에 직접 작성하는 것
• 인라인 If, 인라인 If-Else
인라인 If
- 조건에 따라 엘리먼트를 보여주거나 감출 때 사용
- If문과 동일한 효과를 내는 논리곱 연산자(& ) 사용 (AND 연산자)
- 양쪽에 나오는 조건문이 모두 참인 경우에만 전체 결과가 true
- 첫 번째 조건문이 true이면 두 번째 조건문 평가
- 첫 번째 조건문이 false이면 전체 결과가 false이므로 두 번째 조건문은 평가하지 않음(단축 평가)

• 조건문이 true이면 오른쪽에 나오는 엘리먼트가 결과값, false이면 false가 결과값
• && 연산자를 JSX 코드 안에 중괄호를 사용하여 직접 넣는 방법

• 주의 사항: 조건문에 Falsy expression을 사용하면 뒤에 나오는 expression은
평가되지 않지만 Falsy expression의 결과값(0)이 리턴되므로 주의
• 조건문에 따라 다른 엘리먼트를 보여줄 때 사용
• 삼항 연산자를 사용
조건문 ? 참일 경우 : 거짓일 경우
• 예시) 이전 엘리먼트 변수 예제를 삼항 연산자로 변경


※ 클래스 컴포넌트의 render() 함수에서 null을 리턴하는 것은 컴포넌트의 생명주기 함수에 영향을 미치지 않음. (생명주기 함수가 정상적으로 호출됨)
• 로그인 버튼과 상태를 표시하는 툴바 구현

Toolbar.jsx
import React from "react";
export default function Toolbar(props) {
const { isLogin, onClickLogin, onClickLogout } = props;
return (
<div style={{ margin: 10 }}>
{ isLogin ?
<button onClick={onClickLogout}>로그아웃</button> : <button onClick={onClickLogin}>로그인</button>
}
{isLogin && <span> 환영합니다! </span>}
</div>
);
}
LandingPage.jsx
import React, { useState } from "react";
import Toolbar from "./Toolbar";
export default function LandingPage(props) {
const [isLogin, setIsLogin] = useState(false);
const onClickLogin = () => {
setIsLogin(true);
}
const onClickLogout = () => {
setIsLogin(false);
}
return (
<div>
<Toolbar
isLogin={isLogin}
onClickLogin={onClickLogin}
onClickLogout={onClickLogout}
/>
<hr />
<div style={{ padding: 10 }}>
Contents page
</div>
</div>
);
}

참고사항
- 참고 – 할 일 정보 객체
• 기존에는 할 일 정보를 단순 “텍스트”만 배열에 저장하였지만,
id, 텍스트(할 일), 체크 여부를 하나의 객체로 저장하여 처리할 필요성이 있음
- 참고 - 남은 할 일 개수 관리 방법
• 할 일 목록(todos)의 개수를 표시하는 방법(선택적으로 적용함)
- 참고 - 취소선 CSS
• textDecoration 속성
• “none” : 기본 속성
• “line-throught”: 취소선
Join.jsx
import React, { useState } from 'react';
import Toolbar from './Toolbar';
import TodoList from './Todolist';
export default function Join() {
const [isLogin, setIsLogin] = useState(false);
const onClickLogin = () => setIsLogin(true);
const onClickLogout = () => setIsLogin(false);
/*
// [필수] 약관 체크 상태
const [isTermsAgreed, setIsTermsAgreed] = useState(false);
// [선택] 광고 마케팅 체크 상태
const [isMarketingAgreed, setIsMarketingAgreed] = useState(false);
// [필수] 약관 동의 체크시 이벤트 핸들러
const handleTermsChange = (event) => {
setIsTermsAgreed(event.target.checked);
};
const handleMarketingChange = (event) => {
setIsMarketingAgreed(event.target.checked);
};
const handleSignup = () => {
alert('가입이 완료되었습니다!');
};
*/
return (
<div style={{ margin: 1 }}>
<Toolbar
isLogin={isLogin}
onClickLogin={onClickLogin}
onClickLogout={onClickLogout}
/>
<hr />
{!isLogin ? (
<div style={{ padding: 10 }}>
서비스를 이용하려면 로그인하세요!
</div >
) : (
<div style={{ display: 'flex', justifyContent: 'flex-start', padding: 10 }}>
<TodoList />
</div>
)}
</div>
);
}
TodoList.jsx
import React, { useState, useEffect } from "react";
export default function TodoList() {
const [todos, setTodos] = useState([]);
const [input, setInput] = useState("");
const [todoNums, setTodoNums] = useState(0);
const addTodo = () => {
if (input === "") return;
const todo_item = {
id: new Date(),
text: input,
checked: false,
}
setTodos([...todos, todo_item]);
setInput("");
};
const deleteTodo = (id) => {
setTodos(todos.filter(todo => todo.id !== id));
};
const toggleComplete = (id) => {
setTodos(todos.map((todo) =>
todo.id === id ? { ...todo, checked: !todo.checked } : todo
)
);
}
useEffect(() => {
const count = todos.filter(todo => !todo.checked).length;
setTodoNums(count);
}, [todos]);
return (
<div style={{ margin: 10 }}>
<h2>할 일 목록</h2>
<div>
<input type="text" value={input}
onChange={(e) => setInput(e.target.value)} />
<button onClick={addTodo}>추가</button>
</div>
<p>남은 할 일 : {todoNums} 개</p>
{
todos.map((todo, index) => (
<div key={index} className="todo-item">
<input type="checkbox" checked={todo.checked}
onChange={() => toggleComplete(todo.id)} />
<span style={{ textDecoration: todo.checked ? 'line-through' : 'none' }}>
{todo.text}
</span>
<button
onClick={(e) => deleteTodo(todo.id)}
style={{ margin: 5 }}
>삭제</button>
</div>
))
}
</div>
);
}