구름톤 유니브☁️ 4기 FE 스터디

서론

이번 포스팅에서는 리액트에서 이벤트를 처리하는 방법과 조건부 렌더링 그리고 리스트와 키에 대해 알아보고자 합니다.


본론

1. Handling Events

📖 Event란?

우선 이벤트의 개념에 대해 정의하자면, 이벤트는 컴퓨터 프로그램에서 어떠한 특정 사건을 의미한다.

예를들면 사용자가 버튼을 클릭하는 것도 이벤트라고 볼 수 있다.

웹 사이트가 정상적으로 돌아가기 위해서는 다양한 이벤트를 잘 처리해야할 필요가 있다.

 

✅ DOM과 리액트에서의 Event

📌 DOM의 Event

<button onclick="activate()">
	Activate
</button>

DOM에서는 click이라는 이벤트를 처리할 함수를 onclick 으로 정의하게 된다.

 

📌 리액트의 Event

<button onClick={activate}>
	Activate
</button>

리액트에서는 onClick 이벤트의 이름이 카멜표기법으로 표기된다.

DOM에서는 함수를 문자열로 전달하지만 리액트에서는 함수 그대로 전달한다.

📖 Camel Case(카멜 표기법)란?

사막에있는 낙타의 등 모양처럼 첫글자는 소문자, 중간에나오는 새로운 단어는 대문자로 시작하는 표기법이다.

흔하게 사용하기 때문에 알아두자

 

✅ Event Handler (Event Listener)

해당 이벤트를 처리하는 함수를 이벤트 핸들러라고 한다.

이는 어떤 사건이 발생하면, 사건을 처리하는 역할을 수행한다.

 

📌 클래스 컴포넌트의 경우

class Toggle extends React.Component {
  constructor(props) {
    super(props);

    this.state = { isToggleOn: true };

    // callback에서 `this`를 사용하기 위해서는 바인딩을 필수적으로 해줘야 합니다
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    this.setState(prevState => ({
      isToggleOn: !prevState.isToggleOn
    }));
  }

  render() {
    return (
      <button onClick={this.handleClick}>
        {this.state.isToggleOn ? '켜짐' : '꺼짐'}
      </button>
    );
  }
}

handleClick 함수를 정의하는 것은 일반적인 함수 정의와 동일하게 한다.

이 때, 이러한 handleClick 함수에서 this를 사용하려면 constructor에서 바인딩을 필수로 해야한다.

다만, 클래스 컴포넌트를 최근 잘 쓰지 않기 때문에 간단히 읽고만 넘어가자

 

📌 함수 컴포넌트의 경우

function Toggle(props) {
  const [isToggleOn, setIsToggleOn] = useState(true);

  // 방법 1. 함수 안에 함수로 정의
  function handleClick() {
    setIsToggleOn((isToggleOn) => !isToggleOn);
  }

  // 방법 2. arrow function을 사용하여 정의
  const handleClick = () => {
    setIsToggleOn((isToggleOn) => !isToggleOn);
  }

  return (
    <button onClick={handleClick}>
      {isToggleOn ? "켜짐" : "꺼짐"}
    </button>
  );
}

함수 컴포넌트에서는 함수 안에 또 다른 함수로 정의하거나 arrow function을 이용하여 정의한다.

함수 컴포넌트에서는 onClick 이벤트리스터에 this를 사용하지않고 바로 함수를 넣어준다.

 

Arguments 전달하기

Arguments는 함수에 주장할 내용으로 이해하자

즉, 함수에 전달할 데이터 = 이벤트 핸들러에 전달할 데이터 = 파라미터

function MyButton(props) {
  const handleDelete = (id, event) => {
    console.log(id, event.target);
  };

  return (
    <button onClick={(event) => handleDelete(1, event)}>
      삭제하기
    </button>
  );
}

파라미터를 전달하는 방법은 위 코드와 같이 수행한다. (함수 컴포넌트의 경우)


2. Conditional Rendering

Condition은 조건을 의미한다.

즉, 조건부 렌더링은 어떠한 조건에 따라서 렌더링의 결과가 달라지는 것을 의미한다.

예를들어 조건부의 값이 True면 버튼을 보여주고, False이면 버튼을 가린다.

function UserGreeting(props) {
  return <h1>다시 오셨군요!</h1>;
}

function GuestGreeting(props) {
  return <h1>회원가입을 해주세요.</h1>;
}

function Greeting(props) {
  const isLoggedIn = props.isLoggedIn;

  if (isLoggedIn) {
    return <UserGreeting />;
  }
  return <GuestGreeting />;
}

위 코드의 경우 isLoggedIn의 값에 따라 다른 렌더링 결과를 나타낸다.

📖 JS의 Truthy와 Falsy란?

보통의 언어에서는 참과 거짓을 분류할때 boolean 자료형을 사용하고 True와 False로 분류한다.

JS에서는 True는 아니지만, True로 여겨지는 값이 존재하는데 이를 Truthy라고 한다.

마찬가지로, False는 아니지만, False로 여겨지는 값역시 Falsy라고 한다.

// truthy
true
{}          // empty object
[]          // empty array
42          // number, not zero
"0", "false" // string, not empty

// falsy
false
0, -0       // zero, minus zero
0n          // BigInt zero
'', "", ``  // empty string (single, double, template)
null
undefined
NaN         // not a number

위는 자바스크립트의 대표적인 Truthy와 Falsy를 나타내는 경우들이다.

 

Element Variables

조건부 렌더링을하다보면 렌더링해야할 것을 변수로 다뤄야하는 경우가 존재한다.

아래는 리액트 element를 변수로 다루는 방법이다.

function LoginControl(props) {
  const [isLoggedIn, setIsLoggedIn] = useState(false);

  const handleLoginClick = () => {
    setIsLoggedIn(true);
  };

  const handleLogoutClick = () => {
    setIsLoggedIn(false);
  };

  let button;
  if (isLoggedIn) {
    button = <LogoutButton onClick={handleLogoutClick} />;
  } else {
    button = <LoginButton onClick={handleLoginClick} />;
  }

  return (
    <div>
      <Greeting isLoggedIn={isLoggedIn} />
      {button}
    </div>
  );
}

컴포넌트라고 하지만, button은 컴포넌트로부터 생성된 리액트 element인 것

이런식으로 element를 변수처럼 저장해서 사용한다.

 

Inline Conditions

Inline Conditions는 조건문을 코드 안에 집어넣는 것을 의미한다.

 

📌 Inline if

if문과 동일하게 사용하기 위해 && 논리 연산자를 사용한다.

 

inline if는 이러한 && 연산자를 jsx 코드안에서 중괄호를 사용해 직접 넣는 방법

function Mailbox(props) {
  const unreadMessages = props.unreadMessages;

  return (
    <div>
      <h1>안녕하세요!</h1>
      {unreadMessages.length > 0 &&
        <h2>
          현재 {unreadMessages.length}개의 읽지 않은 메시지가 있습니다.
        </h2>
      }
    </div>
  );
}

function Counter(props) {
  const count = 0;

  return (
    <div>
      {count && <h1>현재 카운트: {count}</h1>}
    </div>
  );
}

이 때, && 논리 연산자 결과가 False 라고 하더라도 아무것도 안나오는게 아니라 0 값이 렌더링되어 나온다.

 

📌 Inline if-else

Inline if-else 문은 ? 삼항연산자를 사용한다.

function UserStatus(props) {
  return (
    <div>
      이 사용자는 현재 <b>{props.isLoggedIn ? '로그인' : '로그인하지 않은'}</b> 상태입니다.
    </div>
  );
}
function LoginControl(props) {
  const [isLoggedIn, setIsLoggedIn] = useState(false);

  const handleLoginClick = () => {
    setIsLoggedIn(true);
  };

  const handleLogoutClick = () => {
    setIsLoggedIn(false);
  };

  return (
    <div>
      <Greeting isLoggedIn={isLoggedIn} />
      {isLoggedIn
        ? <LogoutButton onClick={handleLogoutClick} />
        : <LoginButton  onClick={handleLoginClick} />
      }
    </div>
  );
}

이 때, 문자열이 아닌 element를 넣어서 사용할 수도 있다.

 

📖 만약 컴포넌트를 렌더링하고 싶지 않을때는?
function MainPage(props) {
  const [showWarning, setShowWarning] = useState(false);

  const handleToggleClick = () => {
    setShowWarning(prevShowWarning => !prevShowWarning);
  }

  return (
    <div>
      <WarningBanner warning={showWarning} />
      <button onClick={handleToggleClick}>
        {showWarning ? '감추기' : '보이기'}
      </button>
    </div>
  );
}

null을 리턴하면 렌더링되지 않는다.


3. List and Keys

list는 목록으로 같은 아이템을 순서대로 모아놓은 것이다.

리스트를 위해 사용하는 자료구조가 배열이다. JS의 변수나 객체들을 하나의 변수로 묶어놓은 것

key는 열쇠와 유사한데 각 열쇠마다 고유한 모양을 가지고 있는 것처럼

key는 리스트에서 각 객체나 아이템을 구분할 수 있는 고유한 값을 의미한다.

 

여러 개의 Component 렌더링하기

JS의 map() 함수를 사용하여 여러 개의 컴포넌트를 렌더링할 수 있다.

이는 배열에 들어있는 값을 어떤 처리를한 뒤 리턴을 하는 것이다.

const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
  <li>{number}</li>
);

ReactDOM.render(
  <ul>{listItems}</ul>,
  document.getElementById('root')
);

배열의 첫번째 아이템부터 순서대로 처리하여 리턴하여 총 5개의 <li> 컴포넌트가 생성된다.

이를 render 함수를 통해 렌더링하게 된다.

📖 리스트에 Key를 지정하지 않는다면?
function NumberList(props) {
  const { numbers } = props;

  const listItems = numbers.map((number) =>
    <li>{number}</li>
  );

  return (
    <ul>{listItems}</ul>
  );
}

const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
  <NumberList numbers={numbers} />,
  document.getElementById('root')
);

위와 같이 오류가 발생한다. 따라서 리스트의 각 값은 고유한 key를 가지고 있어야 한다.

 

List의 Key

리액트에서 키는 아이템을 구분하기 위한 고유한 문자열이다. (ex. 학번, 주민번호…)

리액트에서 키값은 같은 리스트에 있는 elements 사이에서만 고유한 값이면 된다.

두 대학교 사이에는 학번이 동일하여도 문제가 없는 것 처럼

두 리스트 사이에서는 Key가 같아도 상관 없다.

 

📌 Key로 id를 사용하는 경우

const todoItems = todos.map((todo) =>
  <li key={todo.id}>
    {todo.text}
  </li>
);

📌 Key로 index를 사용하는 경우

const todoItems = todos.map((todo, index) =>
  // 아이템들의 고유한 ID가 없을 경우에만 사용해야 함
  <li key={index}>
    {todo.text}
  </li>
);

이 때, 배열 내 아이템의 순서가 바뀔 수 있는 경우 index로 키값을 사용하는 걸 권장하지 않는다.

성능에 부정적인 영향을 미칠 수 있기 때문이다.

또한, 키를 넣어주지 않으면 기본적으로 인덱스값을 키로 사용한다.


 

마무리

이번 글에서는 리액트에서 이벤트를 처리하는 방법과 조건부 렌더링 그리고 리스트와 키에 대해 다뤄보았는데

전체적인 내용을 요약하자면

 

1. Handling Events

  • React는 <button onClick={handler}> 식으로 카멜케이스 이벤트에 함수 레퍼런스를 전달
  • 함수 컴포넌트에선 useState + 내부 함수(또는 화살표 함수)로 간단히 핸들러 정의
  • 인자 전달 시 onClick={(e) => handleDelete(id, e)} 형태 사용

2. Conditional Rendering

  • if–return 으로 분기하거나
  • Inline if (&&) / if–else (?:)를 JSX 안에 바로 삽입
  • 렌더링 안 할 땐 return null 또는 condition && <Component/>

3. Lists and Keys

  • array.map(item => <li>{...}</li>) 으로 여러 엘리먼트 생성
  • 각 요소에 고유한 key(가능하면 id, 불가피할 땐 index) 지정
  • 올바른 키 사용이 React의 효율적인 업데이트를 돕습니다.

 

오늘도 읽어주셔서 감사합니다 🫡

'TIL > React' 카테고리의 다른 글

[React] Composition vs Inheritance  (0) 2025.05.13
[React] useMemo(), useCallback(), useRef()  (0) 2025.04.14
[React] JSX & Rendering Elements  (0) 2025.04.08
[React] React.js란?  (0) 2025.04.08

+ Recent posts