이벤트란 핸들러 연결 이벤트 객체 인자 전달 인라인 핸들러 📝 문제풀이
◀ 이전 📋 목차 CH 06 ▶
🖱️ CHAPTER 05 · 입문

사용자의 클릭에 반응하기 — 이벤트 처리

버튼을 누르고, 글자를 입력하고, 폼을 제출하는 사용자 동작에 화면이 반응하게 만드는 게 이벤트 처리예요. 이번 장은 onClick·onChange 같은 이벤트 속성핸들러 함수를 다룹니다. 흔히 하는 실수까지 짚어봐요.

🎯 이 장을 끝내면
🖱️
이벤트란
사용자 동작에 반응하기
클릭·입력·제출 같은 동작을 "이벤트"라고 불러요.

이벤트(event)는 사용자가 화면에서 하는 동작이에요. 버튼을 클릭하거나, 입력창에 글자를 입력하거나, 폼을 제출하는 것 모두 이벤트예요. 리액트에서는 이런 이벤트에 함수를 연결해 반응하게 만들어요.

🔤 JSX는 "카멜케이스" 속성을 써요 🟪 리액트 순수 HTML은 onclick처럼 전부 소문자였지만, JSX에서는 onClick처럼 카멜케이스(중간 글자를 대문자)로 써요. onChange, onSubmit, onMouseOver 모두 같은 규칙이에요. 즉 "on" 뒤 첫 글자가 대문자라는 것만 기억하면 돼요. onclick(❌) → onClick(✅).
// HTML (예전 방식)
<button onclick="handleClick()">클릭</button>

// JSX (리액트 방식) — 카멜케이스 + 중괄호
<button onClick={handleClick}>클릭</button>
이벤트JSX 속성언제
클릭onClick버튼·요소를 눌렀을 때
입력 변경onChange입력창 값이 바뀔 때
폼 제출onSubmit폼을 제출할 때
마우스 올림onMouseOver요소 위에 마우스를 올렸을 때
🧭
핵심 감각. "이벤트 이름은 카멜케이스, 값은 중괄호 안에 함수." 이 두 가지가 리액트 이벤트의 기본 모양이에요.
🐫 카멜케이스가 정확히 뭐예요? — 표기법 4종(camel·Pascal·snake·kebab) 정리 — 펼쳐 보기
카멜케이스(camelCase)는 단어를 이어 쓸 때 첫 단어는 소문자로 시작하고, 두 번째 단어부터는 첫 글자를 대문자로 쓰는 표기법이에요. 낙타(camel) 등의 혹처럼 대문자가 중간중간 튀어나온다고 붙은 이름이죠.

① 이렇게 바뀌어요
원래(띄어 쓴 말)camelCase
on clickonClick
on changeonChange
background colorbackgroundColor
first namefirstName
② JSX는 왜 카멜케이스를 쓸까?
HTML은 속성이 전부 소문자였어요 — <button onclick="hello()">. 하지만 JSX는 HTML이 아니라 자바스크립트를 확장한 문법이라, 자바스크립트의 이름 짓기 규칙(카멜케이스)을 따라요.
// ✅ 올바름 — 카멜케이스
<button onClick={hello}>클릭</button>
<input onChange={handleChange} />

// ❌ 안 됨 — 소문자(HTML 습관)
<button onclick={hello}>클릭</button>
<input onchange={handleChange} />
③ 표기법 4종 비교
표기법예시주로 어디에
camelCase 카멜onClick, backgroundColorJS 변수·함수·이벤트 속성
PascalCase 파스칼MyButton, UserName리액트 컴포넌트 이름
snake_case 스네이크user_name파이썬 등
kebab-case 케밥background-colorHTML·CSS
④ 나머지 3종도 조금 더 자세히
· PascalCase (파스칼) — camelCase와 규칙은 같은데 첫 단어의 첫 글자도 대문자예요. MyButton, UserProfile, HttpRequest. 리액트 컴포넌트 이름과 클래스·타입·인터페이스 이름에 써요. (CH03에서 "컴포넌트는 대문자로 시작"이라고 한 게 바로 이거예요.)
· snake_case (스네이크) — 단어 사이를 밑줄 _로 잇고 전부 소문자예요. user_name, max_count. 뱀이 낮게 기어가는 모양이라 "스네이크"죠. 파이썬 변수·함수, 데이터베이스 컬럼명에 흔해요. (전부 대문자로 쓰면 MAX_SIZE처럼 상수용 "SCREAMING_SNAKE_CASE"예요.)
· kebab-case (케밥) — 단어 사이를 하이픈 -로 잇고 소문자예요. background-color, user-profile. 꼬치(케밥)에 꿰인 모양이죠. HTML 속성·CSS 속성·URL 경로·파일명에 써요. ⚠️ 자바스크립트 변수명엔 못 써요-를 뺄셈으로 읽어버리거든요.
👉 리액트에선 이렇게 기억해요: 컴포넌트 이름 = PascalCase(MyButton), 변수·함수·이벤트 속성 = camelCase(handleClick, onClick, backgroundColor).
💬 참고 — "카멜표기법"과 "카멜 케이스"는 같은 말이에요. 국내에선 "카멜표기법", 공식 문서·번역에선 "camelCase(카멜 케이스)"로 병기하는 경우가 많아요.
▲ 접기
🔗
핸들러 연결
함수 "자체"를 넘겨야 해요
가장 흔한 실수 하나를 여기서 깔끔하게 정리해요.

이벤트에 연결하는 함수를 이벤트 핸들러(handler)라고 불러요. 연결할 때는 함수의 이름(참조)만 넘겨요. 괄호 ()를 붙이면 지금 당장 호출해버리는 뜻이 되어 완전히 달라져요.

function App() {
  function handleClick() {
    alert('버튼이 눌렸어요!');
  }

  // JSX : ✅ 함수 자체(참조)를 전달 — 클릭할 때 실행됨
  return <button onClick={handleClick}>클릭</button>;
}
⚠️ 흔한 실수 — 괄호를 붙이면 안 돼요 🟦 순수 JS onClick={handleClick()}처럼 괄호를 붙이면 클릭을 기다리지 않고 렌더링되는 순간 즉시 실행돼 버려요. 그 함수의 반환값이 onClick에 들어가므로, 대개 원하는 대로 동작하지 않아요. 비유: handleClick은 "요리사(함수)를 건네주는 것", handleClick()은 "요리사에게 지금 당장 요리시키고 그 결과 접시를 건네는 것"이에요.
✅ onClick={handleClick}
🧑‍🍳
요리사(함수)를 건넴
→ 클릭할 때 비로소 요리해요.
❌ onClick={handleClick()}
🍽️
지금 요리시켜 접시(결과)를 건넴
→ 렌더되는 순간 즉시 실행돼요.
// ❌ 잘못 — 렌더 시 즉시 호출되어 버림 (무한 실행/즉시 alert 등)
<button onClick={handleClick()}>클릭</button>

// ✅ 올바름 — 함수 참조만 전달, 클릭할 때 호출
<button onClick={handleClick}>클릭</button>
💡
규칙 한 줄. onClick에는 괄호 없이 함수 이름만. 괄호가 붙으면 "지금 실행"이라는 뜻이에요.
<button onClick={handleClick}>클릭</button>;
                ↑           ↑
                이 중괄호 두 개

// 중괄호 { } 는 "여기 안에 자바스크립트 표현식을 넣는다" 라는 신호임. → JSX 문법 (JS 표현식을 넣으라는 신호)
// JSX 안에서 순수 자바스크립트 코드(변수, 함수, 계산식 등)를 쓰고 싶을 때 중괄호 { }로 감싸는 규칙이 있음

//✅ 위의 코드는 내부적으로 React.createElement 로 변환된다
React.createElement('button', { onClick: handleClick }, '클릭')
                              ↑                      ↑
                                  여기서의 중괄호 두 개

// { onClick: handleClick }는 완전히 다른 괄호이다. → JS 문법 (JS 객체 리터럴 문법)
// 이 괄호는 자바스크립트의 객체 리터를을 만드는 중괄호이다. { } 안에 key: value 형태 → 예: { name: "철수", age: 20 }
🆚 handleClick vs handleClick()의 차이 handleClick→ "이 함수 자체"를 가리키는 것. 아직 실행 안 함. 나중에 필요할 때 실행해달라고 넘겨주는 것. 즉, "클릭하면 이걸 실행해줘" 라는 예약, 클릭할 때 실행됨 ✅ (우리가 원하는 동작)
handleClick()→ 괄호를 붙이는 순간, 자바스크립트는 "지금 당장 이 함수를 실행해!"라고 해석해요. 그리고 그 실행 결과값(반환값)을 가져와요. 예약이 아닌 코드를 읽는 그 순간 바로 실행해버림, 렌더링되는 순간 즉시 실행되고, 정작 클릭했을 때는 onClickundefined만 있어서 아무 반응 없음 ❌ onClick={handleClick()}을 쓰면 어떻게 되는가?
이렇게 쓰면 React 입장에서는 :
1. "onClick에 뭘 넣어야 하지? handleClick()이라는 표현식을 계산해봐야겠다"
2. handleClick()을 실행함 → alert('버튼이 눌렸어요!')가 그 즉시 실행됨 (버튼 클릭 여부와 상관없이!)
3. handleClick()의 반환값(이 예시에서는 undefined, 즉 아무것도 없음)을 가져와서 onClick에 넣음

결과적으로 onClick={undefined}가 되어버려서, 정작 버튼을 클릭해도 아무 일도 안 일어나요. 왜냐하면 이미 렌더링될 때 다 실행되고, onClick에는 "클릭했을 때 뭘 할지"에 대한 정보가 아예 안 들어있으니까요.
{handleClick}{handleClick()}
{ } 안의 정체함수 자체(아직 실행 안함)함수를 실행한 결과값
언제 실행?클릭하는 순간렌더링되는 순간(즉시)
클릭 시 반응정상 동작 ✅아무 반응 없음 ❌
🎁 "함수 자체를 넘긴다"가 무슨 뜻이에요? — 참조 vs 실행 — 펼쳐 보기
처음엔 "함수 자체를 넘긴다"가 아주 추상적으로 느껴지는데, 하나씩 보면 쉬워요.

① onClick={handleClick} — 실행이 아니라 "위치를 알려주는 것"
이건 handleClick실행하는 게 아니에요. 리액트에게 이렇게 말하는 거예요: "이 버튼이 클릭되면 handleClick이라는 함수를 실행해." 즉 함수의 위치(참조)를 알려주는 것뿐이에요.

② onClick={handleClick()} — 괄호는 "지금 실행!"
괄호 ()는 "지금 당장 실행해"라는 뜻이에요. 그래서 버튼이 화면에 나타나는 순간 이미 실행돼 버려요.
화면을 그리는 중 ↓ handleClick() 즉시 실행 → alert가 바로 뜸 ↓ 그 반환값(대개 undefined)이 onClick에 들어감 ↓ 버튼을 눌러도 실행할 함수가 없음 ❌
📞 전화번호 비유
친구에게 "배고프면 이 번호로 전화해"라고 번호만 알려주는 것 = onClick={handleClick}. 반면 친구 앞에서 내가 바로 전화를 걸어버리는 것 = handleClick(). 번호만 건네야 하는데 전화를 먼저 걸어버린 셈이죠.

🧪 자바스크립트로 보면 똑같아요
function hello() { console.log("안녕"); }

const a = hello;    // 함수 "자체"를 저장 → a 는 함수
const b = hello();  // 함수를 "실행한 결과"를 저장 → b 는 undefined
a는 함수고, bhello()가 실행된 결과예요. onClick도 똑같은 원리고요.
👉 핵심만: onClick={handleClick} ✅ 함수를 전달(참조) — 클릭할 때 실행. / onClick={handleClick()}즉시 실행 — 누르기도 전에 이미 실행돼 버림.
▲ 접기
🔤 그런데 "이벤트 핸들러"가 정확히 뭐예요? — 펼쳐 보기
이벤트이벤트 핸들러를 나눠 보면 훨씬 쉬워요.

① 이벤트(Event) = 일어난 "사건"
프로그램에서 발생하는 사건이나 행동이에요 — 버튼을 클릭, 키보드를 입력, 마우스를 움직임, 입력창에 글자 입력. 버튼을 누르는 순간 클릭 이벤트(click event)가 발생해요.

② 이벤트 핸들러 = 그 사건이 나면 "실행할 함수"
이벤트가 발생했을 때 실행할 함수를 이벤트 핸들러라고 해요.
function handleClick() {
  alert("버튼이 눌렸어요!");
}

<button onClick={handleClick}>클릭</button>
// onClick     → 클릭 이벤트를 감시하는 속성
// handleClick → 클릭이 나면 실행할 이벤트 핸들러
사용자가 버튼 클릭 ↓ 클릭 이벤트 발생 ↓ React가 onClick 확인 ↓ handleClick() 실행
③ 왜 이름이 "Handler"?
영어 handle은 "처리하다"라는 뜻이에요. 그래서 Event Handler = 이벤트를 처리하는 함수죠.

④ 이름은 마음대로 지어도 돼요
handleClick은 정해진 이름이 아니에요. function abc(){...}로 만들어 onClick={abc} 해도 똑같이 동작해요. 다만 handleClick·handleChange·handleSubmit처럼 handle을 붙이면 "아, 이벤트 처리 함수구나"를 바로 알 수 있어서 관례로 그렇게 써요.
👉 한 줄 정리: 이벤트 = 일어난 사건(클릭·입력·스크롤), 이벤트 핸들러 = 그 사건이 나면 실행되는 함수.
▲ 접기
📦
이벤트 객체
핸들러가 받는 e — 이벤트 객체 🟪 리액트
입력값을 읽고, 기본 동작을 막을 때 쓰는 도구예요.

이벤트가 일어나면 리액트는 핸들러의 첫 번째 인자이벤트 객체 e를 넘겨줘요(리액트에서는 브라우저 이벤트를 감싼 합성 이벤트(SyntheticEvent)예요). 이 e 안에 이벤트에 대한 정보가 들어 있어요.

⌨️ 입력값 읽기 — e.target.value 🟦 순수 JS 입력창의 onChange 핸들러에서 지금 입력된 값은 e.target.value로 읽어요. e.target은 이벤트가 일어난 요소(여기선 input), .value는 그 요소의 현재 값이에요.
function SearchBox() {
  function handleChange(e) {
    console.log(e.target.value); // 입력된 글자
  }

  return <input onChange={handleChange} />;
}
🚫 기본 동작 막기 — preventDefault() 🟦 순수 JS 폼(form)을 제출하면 브라우저는 페이지를 새로고침하는 게 기본 동작이에요. SPA(페이지 전체를 새로고침하지 않고 화면만 바꾸는 방식, 리액트 앱이 이래요)에서는 이걸 원치 않으니 e.preventDefault()기본 동작을 막아요.
function Form() {
  function handleSubmit(e) {
    e.preventDefault();     // 새로고침 막기
    console.log('제출 처리!');
  }

  return (
    <form onSubmit={handleSubmit}>
      <button>보내기</button>
    </form>
  );
}
📌
정리. e.target.value = 입력값 읽기, e.preventDefault() = 브라우저 기본 동작(새로고침·링크 이동 등) 막기.
🎯
인자 전달
핸들러에 값을 넘기고 싶다면?
화살표 함수로 "감싸는" 게 정답이에요.
🏹 먼저 30초 — "화살표 함수 () =>" 가 뭐예요? 🟦 순수 JS 함수를 짧게 쓰는 문법일 뿐이에요. 아래 두 줄은 완전히 똑같은 함수예요:
원래 방식
function add(a, b) {
  return a + b;
}
화살표 함수
const add = (a, b) => a + b;
그래서 () => handleClick(id)"아무것도 안 받고(()), handleClick(id)를 실행하는 새 함수"라는 뜻이에요.

핸들러에 특정 값(예: id)을 함께 넘기고 싶을 때가 있어요. 이때 onClick={handleClick(id)}라고 쓰면 앞에서 배운 것처럼 렌더 시 즉시 호출되어 버려요. 해결책은 화살표 함수로 감싸는 것이에요.

// ❌ 즉시 호출됨 — 클릭 전에 실행되어 버림
<button onClick={handleClick(id)}>삭제</button>

// ✅ 화살표로 감싸기 — 클릭할 때 handleClick(id) 실행
<button onClick={() => handleClick(id)}>삭제</button>
🧠 왜 화살표로 감싸야 할까? 🟦 순수 JS onClick에는 "클릭했을 때 실행할 함수"가 들어가야 해요. () => handleClick(id)"클릭하면 handleClick(id)를 실행하라"는 새 함수예요. 반면 handleClick(id)는 함수가 아니라 지금 실행한 결과값이라 클릭을 기다리지 않아요. 즉 "실행 방법을 담은 함수"를 넘기려고 화살표로 한 겹 감싸는 거예요.
💡공식처럼 기억. 인자를 넘길 땐 onClick={() => 함수(인자)}. 인자가 없으면 그냥 onClick={함수}.
인라인 핸들러
간단한 동작은 그 자리에서
따로 함수를 안 만들고 화살표로 바로 써도 돼요.

동작이 아주 짧으면, 별도 함수를 선언하지 않고 화살표 함수를 그 자리(inline)에 바로 써도 돼요. 예를 들어 클릭할 때마다 카운트를 1 올리는 건 이렇게 쓸 수 있어요.

// state는 CH04에서 배웠어요 (useState)
<button onClick={() => setCount(count + 1)}>
  +1
</button>
🪄 인라인 vs 이름 붙인 핸들러 🟦 순수 JS 인라인 — 한 줄짜리 간단한 동작에 편해요.
이름 붙인 함수 — 로직이 길거나 여러 곳에서 재사용하면 밖에 function handleXxx로 빼는 게 읽기 좋아요. 둘 다 결국 "onClick에 함수를 넘긴다"는 점은 똑같아요. 상황에 맞게 고르면 돼요.
🔗
state와의 연결. 이벤트 핸들러 안에서 setCount 같은 state 변경 함수를 호출해 화면을 바꾸는 게 리액트의 기본 패턴이에요. state는 CH04에서 다뤘어요.
🧠 이 장 핵심 요약
📝
문제풀이 · 점검
배운 걸 가볍게 점검해봐요
시험이 아니라 "내가 이해했나" 확인용이에요. 틀려도 바로 해설이 나와요.
🧪
문제를 풀면 즉시 정답과 해설이 나오고, 위쪽 바에 점수가 쌓여요. 네 가지 유형(객관식 · O/X · 빈칸 · 개념결과)을 섞어 두었어요.
CH 06 🔀 조건부 렌더링과 리스트 — 화면을 상황에 따라 다르게 그리기