반응형

리액트 동적 화면 처리 완전 정복 🌟

사용자의 클릭 하나로 화면이 바뀌고,
조건에 따라 다른 컴포넌트가 등장하는
그 마법 같은 UI 구현!
리액트로 그 비밀을 풀어봅니다.
반응형

 


https://inf.run/mg1G5

 

Next.js 15: Full-Stack Development| 구멍가게코딩단 - 인프런 강의

전통적인 React 개발 방식과 Next.js를 이용해서 개발하는 방식은 전혀 다릅니다. 이 강의는 최신 Next.js 15의 핵심을 꿰뚫고, '진짜' Next.js다운 개발 방식을 알려드립니다. 취업 포트폴리오부터 실무

www.inflearn.com

 

안녕하세요, 여러분! 😊

혹시 리액트로 개발하다 보면 화면을 동적으로 바꾸고 싶었던 적 있으신가요?

예를 들어 버튼을 누르면 화면이 전환되거나, 특정 조건일 때만 컴포넌트가 보이게 하고 싶었던 경험 있으시죠?

이번 포스트에서는 바로 그런 동적 UI 처리 방법을 리액트에서 어떻게 구현할 수 있는지 자세히 알아보려 해요.

상태(state) 관리, 조건부 렌더링, 컴포넌트 전환 등 실무에 바로 써먹을 수 있는 팁을 예제와 함께 하나하나 알려드릴게요.

초보자도 이해할 수 있도록 천천히, 차근차근 진행하니 끝까지 함께해 주세요!

 

1. useState로 조건부 렌더링하기 🎛️

리액트에서 동적인 UI를 처리하려면 가장 먼저 알아야 할 훅이 바로 useState입니다.

사용자의 행동에 따라 화면의 요소를 보이거나 숨기고 싶다면, 조건에 따라 렌더링되는 컴포넌트를 제어해야 하거든요.

💡 기본 사용 패턴

import { useState } from "react";

function ToggleExample() {
  const [isVisible, setIsVisible] = useState(false);

  return (
    <div>
      <button onClick={() => setIsVisible(!isVisible)}>
        {isVisible ? "숨기기" : "보이기"}
      </button>
      {isVisible && <p>이 텍스트는 상태에 따라 보입니다!</p>}
    </div>
  );
}

 

이 예제에서는 버튼을 누르면 isVisible 상태가 true/false로 바뀌면서 텍스트가 조건부로 렌더링돼요.

이게 바로 조건부 렌더링의 핵심입니다.

🔍 다양한 조건부 렌더링 방식

  • 삼항 연산자 (ternary operator)를 사용한 렌더링
  • && 연산자를 활용한 짧은 조건 처리
  • if/else 또는 함수형 렌더링 방식

📋 예시: 로그인 상태에 따라 화면 전환

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

  return (
    <div>
      {isLoggedIn ? (
        <h2>환영합니다, 사용자님!</h2>
      ) : (
        <button onClick={() => setIsLoggedIn(true)}>로그인하기</button>
      )}
    </div>
  );
}

 

이처럼 useState를 기반으로 조건에 맞게 컴포넌트를 보여주면, 사용자의 액션에 따라 부드럽게 변화하는 UI를 만들 수 있어요.

✅ 정리해볼까요?

  1. 상태(state)는 동적 화면 처리를 위한 기본 도구입니다.
  2. 조건부 렌더링에는 다양한 방법이 있지만 &&삼항 연산자가 가장 흔하게 쓰입니다.
  3. 실제 화면에 바로 반영되기 때문에 즉각적인 피드백을 줄 수 있습니다.

앞으로 다룰 ‘컴포넌트 Show/Hide’와도 매우 밀접한 개념이니, 이 부분을 확실히 익혀두세요!

 

 

2. 화면 전환: 컴포넌트 Show/Hide 패턴 🔄

리액트 앱에서는 종종 "이 화면에서 저 화면으로 전환"하는 UI 흐름이 필요하죠.

탭 전환, 메뉴 접기/펼치기, FAQ 펼치기 등에서 말이에요.

이럴 때 자주 사용하는 기법이 바로 컴포넌트를 조건에 따라 보여주거나 숨기는 Show/Hide 패턴입니다.

🛠 간단한 Show/Hide 예제

function TabSwitcher() {
  const [tab, setTab] = useState("A");

  return (
    <div>
      <button onClick={() => setTab("A")}>Tab A</button>
      <button onClick={() => setTab("B")}>Tab B</button>

      {tab === "A" ? <ComponentA /> : <ComponentB />}
    </div>
  );
}

 

이 예제에서는 버튼 클릭 시 상태가 바뀌고, 그에 따라 A 컴포넌트 또는 B 컴포넌트가 화면에 표시됩니다.

완전한 페이지 전환은 아니지만 동적인 느낌을 주는 UI를 만들 수 있죠.

💡 팁: 조건이 많아질 땐 switch-case나 객체 매핑!

const components = {
  A: <ComponentA />,
  B: <ComponentB />,
  C: <ComponentC />
};

return (
  <div>
    <button onClick={() => setTab("A")}>Tab A</button>
    <button onClick={() => setTab("B")}>Tab B</button>
    <button onClick={() => setTab("C")}>Tab C</button>
    {components[tab]}
  </div>
);

 

이렇게 하면 코드가 훨씬 깔끔하고 유지보수도 쉬워집니다.

조건이 늘어날수록 이런 구조가 필요해요.

📦 FAQ 아코디언 구현 예시

function FAQItem() {
  const [open, setOpen] = useState(false);

  return (
    <div>
      <h4 onClick={() => setOpen(!open)}>Q. 이 기능은 어떻게 동작하나요?</h4>
      {open && <p>A. useState로 열고 닫을 수 있어요!</p>}
    </div>
  );
}

 

이런 UI는 실제 웹서비스에서 자주 쓰이죠.

사용자에게 필요한 정보를 보기 좋게 정리할 수 있으니까요.

✅ 핵심 요약

  • 상태(state)에 따라 컴포넌트를 조건적으로 렌더링
  • 여러 조건을 처리할 땐 객체 매핑으로 코드 간소화

다음은 리스트를 동적으로 렌더링하면서 key를 어떻게 다뤄야 할지에 대해 이야기해볼게요.

꼭 필요한 개념이니까 집중해주세요!

 

 

3. 리스트 동적 렌더링과 키(Key) 사용법 🧩

리액트에서 반복되는 UI,

예를 들어 댓글 목록이나 게시판 리스트 같은 걸 만들 때 자주 쓰는 게 동적 리스트 렌더링입니다.

그리고 이때 반드시 함께 써야 할 것이 바로 key 속성이에요.

이 key 하나로 성능과 버그 여부가 갈릴 수 있거든요.

🔁 기본 리스트 렌더링 예제

function FruitList() {
  const fruits = ["🍎 Apple", "🍌 Banana", "🍊 Orange"];

  return (
    <ul>
      {fruits.map((fruit, index) => (
        <li key={index}>{fruit}</li>
      ))}
    </ul>
  );
}

 

여기서 key={index}처럼 index를 key로 사용하는 건 간단하긴 하지만,

항목의 순서가 바뀌거나 삽입/삭제가 많을 땐 비추천이에요.

컴포넌트 상태가 꼬일 수 있거든요.

📛 key 사용 시 주의사항

  • 리스트 항목이 바뀌거나 삽입/삭제되는 경우엔 index 대신 고유한 ID 사용 권장
  • key는 형제 요소 간의 구분자 역할이므로 절대 중복되면 안 됨

💼 실무 예시: 사용자 리스트 렌더링

const users = [
  { id: 1, name: "Alice" },
  { id: 2, name: "Bob" },
  { id: 3, name: "Charlie" },
];

function UserList() {
  return (
    <ul>
      {users.map((user) => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
}

 

이처럼 id 같은 고유 값을 key로 쓰면 리렌더링 이슈도 줄어들고, 성능 최적화에도 도움이 됩니다.

실무에서 무조건 쓰이는 방식이에요.

🧠 정리하면?

  1. 동적 리스트 렌더링에는 반드시 key를 사용한다
  2. index는 임시로만, 실제로는 고유한 식별자 사용
  3. 렌더링 효율성과 버그 예방 측면에서 매우 중요하다

이제 리스트뿐 아니라 입력창도 동적으로 제어할 시간이에요.

다음 파트에서는 동적 폼 처리에 대해 알아봅시다!

 

 

4. 동적 폼 처리와 입력값 관리 ✍️

리액트에서 사용자 입력을 처리할 때 가장 많이 사용하는 패턴이 바로 useStateonChange 이벤트의 조합입니다.

여기에 폼의 입력 필드를 동적으로 생성하거나 관리하려면 어떻게 해야 할까요?

이 섹션에서 그 해답을 찾아봅니다.

🧪 단일 입력 필드 제어

function SimpleForm() {
  const [name, setName] = useState("");

  return (
    <form>
      <input
        type="text"
        value={name}
        onChange={(e) => setName(e.target.value)}
      />
      <p>입력된 이름: {name}</p>
    </form>
  );
}

 

위 코드처럼 입력 필드를 valueonChange로 연결하면 제어 컴포넌트가 됩니다.

실시간으로 값이 반영되기 때문에 매우 직관적이죠.

📋 다중 필드 입력 처리

function MultiInputForm() {
  const [formData, setFormData] = useState({ name: "", email: "" });

  const handleChange = (e) => {
    const { name, value } = e.target;
    setFormData({ ...formData, [name]: value });
  };

  return (
    <form>
      <input name="name" value={formData.name} onChange={handleChange} />
      <input name="email" value={formData.email} onChange={handleChange} />
      <p>이름: {formData.name}, 이메일: {formData.email}</p>
    </form>
  );
}

 

이 방식은 입력 필드가 늘어나더라도 handleChange 하나로 다 제어할 수 있어서 굉장히 유용합니다.

⚠️ 입력값 초기화 팁

  • setFormData({ name: "", email: "" })을 사용해 초기화 가능
  • formRef.current.reset() 같은 DOM 방식도 상황에 따라 유용함

🔚 정리!

  1. 제어 컴포넌트 방식으로 입력값을 상태로 관리
  2. 다중 필드는 name 속성과 스프레드 연산자를 활용해 통합 관리
  3. 입력 초기화도 잊지 말고 꼭 처리할 것

이제 진짜 페이지 간 화면 전환이 궁금하시죠?

다음 섹션에서 React Router를 활용한 동적 화면 전환을 마스터해봐요!

 

 

5. React Router로 구현하는 동적 화면 전환 🚀

리액트 앱이 복잡해질수록 페이지 전환은 필수가 되죠.

React Router를 이용하면 SPA 구조를 유지하면서도 화면 전환처럼 보이는 멋진 UI를 만들 수 있어요.

사용자 경험을 부드럽게 만드는 핵심 기술이죠!

🔗 기본 설정: 라우터 구조 만들기

import {
  BrowserRouter as Router,
  Routes,
  Route,
  Link
} from "react-router-dom";

function App() {
  return (
    <Router>
      <nav>
        <Link to="/">홈</Link>
        <Link to="/about">소개</Link>
      </nav>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
      </Routes>
    </Router>
  );
}

 

BrowserRouterRoutes, Route 컴포넌트를 조합해서 각 URL에 해당하는 컴포넌트를 연결하면 페이지처럼 보이는 화면 전환을 구현할 수 있습니다.

🧭 동적 파라미터와 URL 활용

function Profile() {
  const { username } = useParams();
  return <h2>{username}님의 프로필</h2>;
}

// 라우터 설정
<Route path="/user/:username" element={<Profile />} />

 

이렇게 하면 /user/jisu처럼 URL에 따라 내용을 동적으로 렌더링할 수 있어요.

유저 프로필, 상품 상세페이지 등에 딱이죠!

📦 React Router 실무 팁

  • 페이지 이동 후 스크롤 상단 이동은 useEffect + window.scrollTo(0, 0)
  • useNavigate()로 프로그래밍 방식 이동도 가능

🔍 요약 정리

  1. React Router는 SPA에서 가짜 페이지 이동을 구현하는 도구
  2. RouteLink 조합으로 전환 가능
  3. URL 파라미터로 동적 렌더링 처리도 가능

마지막 파트에서는 커스텀 훅을 만들어 동적 UI를 더 효율적으로 제어하는 방법을 알려드릴게요.

자동화와 재사용성까지 챙기는 시간입니다!

 

 

6. 커스텀 훅으로 만든 동적 UI 제어 🧠

동적 UI가 점점 복잡해지면, 공통 로직을 재사용할 일이 많아지죠.

예를 들어 토글 기능, 폼 초기화, 특정 키보드 입력 감지 등은 여러 곳에서 반복됩니다.

이럴 땐 커스텀 훅(Custom Hook)으로 추상화하면 훨씬 효율적이에요!

🔄 useToggle 훅 만들기

import { useState, useCallback } from "react";

function useToggle(initialValue = false) {
  const [state, setState] = useState(initialValue);
  const toggle = useCallback(() => setState((prev) => !prev), []);
  return [state, toggle];
}

 

이 훅은 true/false 상태를 간단히 토글해주는 역할을 합니다.

useCallback으로 성능 최적화까지 고려했어요.

✅ 사용 예시: FAQ 아코디언

function FAQItem() {
  const [open, toggleOpen] = useToggle();

  return (
    <div>
      <h4 onClick={toggleOpen}>Q. 커스텀 훅이 뭐예요?</h4>
      {open && <p>A. 반복되는 훅 로직을 추상화한 함수입니다!</p>}
    </div>
  );
}

 

이처럼 커스텀 훅을 사용하면 코드가 훨씬 깔끔해지고, 여러 곳에서 같은 방식으로 사용할 수 있어 유지보수도 쉬워집니다.

📦 커스텀 훅 팁

  • 이름은 반드시 use로 시작해야 함 (예: useForm, useInput 등)
  • 다른 훅을 내부에서 자유롭게 사용할 수 있음

🎯 요약!

  1. 동일한 훅 로직이 반복된다면 커스텀 훅으로 추상화
  2. 이름은 반드시 use로 시작
  3. 재사용성과 유지보수성 모두 향상됨

자, 이제 리액트 동적 화면 처리를 위한 모든 기초는 끝났어요.

마지막으로 전체 내용을 정리하며 마무리해볼게요. 😊

 

 

🧾 리액트 동적 화면 처리, 이제 어렵지 않죠?

지금까지 useState를 이용한 조건부 렌더링부터 React Router를 활용한 페이지 전환, 그리고 커스텀 훅으로 코드 리팩토링까지!

동적 UI를 만드는 핵심 개념을 하나씩 배워봤습니다.

초보자라면 처음엔 헷갈릴 수도 있지만, 직접 예제 코드를 따라 작성하다 보면 금세 감이 잡힐 거예요.

실제로 서비스를 개발하다 보면 사용자의 인터랙션에 반응해서 화면이 바뀌는 상황은 무수히 많습니다.

이번 포스트가 그런 상황을 어떻게 스마트하게 처리할지에 대한 든든한 실전 가이드가 되었기를 바라요.

 

여러분도 직접 연습하면서, 필요한 기능을 커스텀 훅으로 만들어보거나, React Router로 화면 전환을 구성해보세요. 실전에서 쓸 수 있는 진짜 스킬은 이렇게 쌓이는 거랍니다!

 

반응형
반응형

리액트 이벤트 처리 마스터하기 🎯:
초보자를 위한 실전 가이드

"onClick도 잘 모르겠는데, 이벤트 객체? this 바인딩?" 🤯
리액트 초보가 가장 헷갈리는 바로 그 주제!
이제는 명쾌하게 정리해드릴게요.
반응형

 

https://inf.run/mg1G5

 

Next.js 15: Full-Stack Development| 구멍가게코딩단 - 인프런 강의

전통적인 React 개발 방식과 Next.js를 이용해서 개발하는 방식은 전혀 다릅니다. 이 강의는 최신 Next.js 15의 핵심을 꿰뚫고, '진짜' Next.js다운 개발 방식을 알려드립니다. 취업 포트폴리오부터 실무

www.inflearn.com

 

안녕하세요, 여러분! 😊

리액트를 처음 접하고 나서 가장 많이 헤매는 부분 중 하나가 바로 "이벤트 처리" 아닐까요?

JavaScript에서는 잘 되던 이벤트 등록이, 리액트에선 갑자기 JSX 문법이니 이벤트 객체니 하면서 헷갈리는 경우 많죠.

이번 글에서는 리액트의 이벤트 시스템을 차근차근 정리하고, 자주 실수하는 포인트도 함께 짚어드릴게요.

실습 위주로 따라오시면, 글 다 읽고 나면 자신 있게 "이벤트 처리? 그건 이제 껌이죠!"라고 말하실 수 있을 거예요.

그럼 시작해볼까요? 😊

 

1. 리액트의 기본 이벤트 바인딩 방식 🧷

리액트에서 이벤트를 다루는 방식은 일반 HTML과 다소 다릅니다.

JSX 문법을 따르기 때문에 이벤트 속성을 onclick이 아닌 onClick처럼 카멜케이스(camelCase)로 작성해야 하고, 실행할 함수는 문자열이 아닌 함수 참조로 전달해야 해요.

예제 코드 👇

function ClickButton() {
  function handleClick() {
    alert('버튼이 클릭되었어요!');
  }

  return (
    <button onClick={handleClick}>
      클릭해보세요!
    </button>
  );
}

 

위 코드에서 onClick={handleClick}처럼 함수 이름만 넘기고 괄호는 붙이지 않죠?

괄호를 붙이면 컴포넌트 렌더링 시점에 함수가 실행돼버리니까 꼭 조심해야 해요!

초보자 주의 포인트 📌

  • JSX에서는 이벤트 이름을 반드시 카멜케이스로 작성해야 합니다.
  • 이벤트 핸들러는 함수 참조로 전달해야 하며, 함수 호출문이 들어가면 안 돼요!

잘못된 예와 올바른 예 🆚

잘못된 방식 올바른 방식
onclick="handleClick()" onClick={handleClick}
onClick={handleClick()} onClick={() => handleClick()}

익명 함수로 감싸는 방식은 상황에 따라 사용하면 유용하지만, 함수 내부에서 props나 state를 다뤄야 할 때만 사용하는 게 좋아요!

 

 

2. SyntheticEvent란? 🤖 진짜 이벤트 객체와의 차이

리액트에서 이벤트를 처리할 때 SyntheticEvent라는 개념이 등장해요.

"이게 뭐야?" 싶으실 텐데, 쉽게 말해 브라우저의 native event를 감싼 리액트 전용 래퍼 객체라고 생각하시면 됩니다.

왜 SyntheticEvent를 쓸까?

  • 브라우저 간 이벤트 처리 방식이 달라서, 이를 통합하기 위해 리액트가 만든 표준화된 이벤트 객체예요.
  • stopPropagation()이나 preventDefault() 같은 메서드도 동일하게 사용할 수 있어요.

SyntheticEvent의 특징 🌐

기능 설명
통합 API 브라우저마다 다른 이벤트 처리 방식을 하나의 인터페이스로 제공
자동 메모리 최적화 이벤트 객체는 자동으로 풀(pool)에 반환되어 재사용됨
비동기 코드에서의 문제 setTimeout 등에서 접근하면 이벤트 객체가 null로 초기화되어 있음

Tip! 이벤트 객체를 비동기적으로 사용해야 할 경우 e.persist()를 꼭 호출하세요!

예시 코드 👇

function LogEvent() {
  function handleClick(e) {
    e.persist(); // SyntheticEvent 풀 반환 방지
    setTimeout(() => {
      console.log(e.type); // 'click'
    }, 1000);
  }

  return (
    <button onClick={handleClick}>이벤트 타입 출력</button>
  );
}

 

이처럼 SyntheticEvent는 리액트의 퍼포먼스 최적화를 위한 핵심 설계 중 하나이며, JS 이벤트와 매우 유사하지만 React 내부에서 관리된다는 점을 꼭 기억해 주세요!

 

 

3. 이벤트 객체 다루기 – e.preventDefault(), e.target 등 🧪

리액트 이벤트 핸들러에서 전달받는 e는 앞서 배운 SyntheticEvent 객체입니다.

이 객체를 활용하면 preventDefault()로 기본 동작을 막거나, e.target을 통해 이벤트가 발생한 요소에 접근할 수 있어요.

📌 주요 이벤트 객체 프로퍼티/메서드 요약

속성/메서드 설명
e.preventDefault() 기본 동작(예: 폼 제출, 링크 이동 등)을 막습니다.
e.stopPropagation() 이벤트 버블링(상위 요소로 전파)을 막습니다.
e.target 이벤트가 실제로 발생한 DOM 요소를 가리킵니다.
e.currentTarget 이벤트 핸들러가 바인딩된 요소를 가리킵니다.

🎯 예제 1 – 기본 동작 막기 (preventDefault)

function FormExample() {
  function handleSubmit(e) {
    e.preventDefault();
    alert('폼 제출이 막혔어요!');
  }

  return (
    <form onSubmit={handleSubmit}>
      <input type="text" placeholder="이름 입력" />
      <button type="submit">제출</button>
    </form>
  );
}

 

이 예제는 기본적으로 페이지 리로드를 막고, alert만 출력해요.

만약 e.preventDefault()를 생략하면 폼이 제출되면서 페이지가 새로고침됩니다.

🎯 예제 2 – target과 currentTarget 구분

function TargetExample() {
  function handleClick(e) {
    console.log('target:', e.target.tagName);
    console.log('currentTarget:', e.currentTarget.tagName);
  }

  return (
    <div onClick={handleClick} style={{ padding: '1em', border: '1px solid #ccc' }}>
      <button>버튼 클릭!</button>
    </div>
  );
}

 

이 코드에서 e.target실제 클릭된 버튼이고,

e.currentTargetonClick이 바인딩된 div를 가리킵니다.

DOM 이벤트 흐름을 이해할 때 아주 중요한 개념이에요!

 

💡 정리!

이벤트 객체를 잘 활용하면 복잡한 사용자 상호작용도 깔끔하게 구현할 수 있어요.

특히 targetcurrentTarget의 차이는 반드시 숙지해두세요!

 

 

4. this 바인딩 문제 해결법과 화살표 함수 사용법 🧭

리액트에서 이벤트 핸들러를 작성하다 보면 thisundefined가 되는 문제가 발생하곤 해요.

특히 클래스형 컴포넌트를 사용할 때 this.handleClick처럼 핸들러를 호출하려고 하면 에러가 발생할 수 있죠.

🎯 클래스형 컴포넌트에서의 this 바인딩

class EventExample extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
    this.handleClick = this.handleClick.bind(this); // 바인딩 필수
  }

  handleClick() {
    this.setState({ count: this.state.count + 1 });
  }

  render() {
    return (
      <button onClick={this.handleClick}>
        클릭 횟수: {this.state.count}
      </button>
    );
  }
}

 

this.handleClick을 이벤트에 넘기기 전에 생성자에서 바인딩을 꼭 해줘야 해요.

그렇지 않으면 this가 undefined가 되어 this.setState가 작동하지 않아요!

💡 화살표 함수로 해결하기

class ArrowEventExample extends React.Component {
  state = { count: 0 };

  handleClick = () => {
    this.setState({ count: this.state.count + 1 });
  }

  render() {
    return (
      <button onClick={this.handleClick}>
        클릭 횟수: {this.state.count}
      </button>
    );
  }
}

 

handleClick화살표 함수로 선언하면 생성자에서 바인딩하지 않아도 this가 자동으로 바인딩돼요.

요즘은 이 방식이 더 보편적으로 쓰이고 있어요!

🚀 함수형 컴포넌트에서는?

함수형 컴포넌트에서는 this를 쓰지 않기 때문에 바인딩 문제가 아예 없습니다!

대신 useState, useCallback 등 훅을 적절히 활용해서 상태나 함수를 관리하면 돼요.

function FunctionalEvent() {
  const [count, setCount] = React.useState(0);

  const handleClick = () => {
    setCount(prev => prev + 1);
  };

  return (
    <button onClick={handleClick}>
      클릭 횟수: {count}
    </button>
  );
}

 

🔥 핵심 요약:

클래스형 컴포넌트는 this 바인딩이 필수!

하지만 함수형 컴포넌트에서는 바인딩 걱정 없이 깔끔하게 이벤트를 처리할 수 있어요.

이제 this 때문에 헤맬 일은 없겠죠?

 

 

5. 커스텀 이벤트 핸들러 구조 설계하기 🛠️

리액트에서 이벤트 처리는 단순히 onClick={() => ...}처럼 인라인으로 끝낼 수도 있지만, 규모가 커질수록 이벤트 핸들러의 구조화가 굉장히 중요해집니다.

컴포넌트가 많아지고 이벤트가 늘어나면 로직 재사용유지보수에 큰 차이를 만들어요.

💡 커스텀 핸들러를 분리하는 이유

  • 핸들러 로직이 복잡해질 경우 읽기 어려운 JSX가 되기 쉽습니다.
  • 여러 컴포넌트에서 같은 로직을 재사용할 때 중복을 피할 수 있습니다.

🧩 예제: 입력 값 검증 핸들러

function useInputValidator() {
  const validate = (value) => {
    if (value.length < 3) {
      return "3자 이상 입력해주세요!";
    }
    return "";
  };

  return validate;
}

function InputForm() {
  const [input, setInput] = React.useState("");
  const [error, setError] = React.useState("");
  const validate = useInputValidator();

  const handleChange = (e) => {
    const val = e.target.value;
    setInput(val);
    setError(validate(val));
  };

  return (
    <div>
      <input value={input} onChange={handleChange} />
      {error && <p style={{ color: "red" }}>{error}</p>}
    </div>
  );
}

 

위 코드에서는 useInputValidator라는 커스텀 훅으로 입력값 검증 로직을 분리했어요.

핸들러 함수는 깔끔하게 유지되고, 검증 로직은 재사용 가능합니다.

⚙️ 다양한 상황에 맞는 이벤트 구조 설계 팁

상황 설계 팁
반복적인 입력 검증 커스텀 훅으로 검증 함수 모듈화
복잡한 버튼 액션 핸들러에서 여러 유틸 함수를 조합
다양한 트리거로 같은 동작 공통 로직을 별도 핸들러로 추출

🚀 요약!

리액트 이벤트 처리를 고수처럼 만들고 싶다면, 핸들러를 짧고 간결하게 유지하고, 복잡한 로직은 커스텀 훅이나 함수로 분리해보세요.

유지보수가 10배 쉬워집니다!

 

 

6. 예제로 알아보는 이벤트 처리 베스트 프랙티스 🧠

리액트의 이벤트 시스템을 이론적으로 이해하는 것도 중요하지만, 예제를 통해 감을 잡는 것이 훨씬 효과적입니다.

이번에는 실무에서 자주 등장하는 이벤트 시나리오 3가지와 그에 맞는 처리 방법을 살펴보겠습니다.

🧪 예제 1: 인풋 실시간 반영 + 엔터키 제출

function ChatInput() {
  const [message, setMessage] = React.useState("");

  const handleChange = (e) => setMessage(e.target.value);

  const handleKeyDown = (e) => {
    if (e.key === 'Enter' && !e.shiftKey) {
      e.preventDefault();
      alert(`메시지 전송: ${message}`);
      setMessage("");
    }
  };

  return (
    <textarea
      value={message}
      onChange={handleChange}
      onKeyDown={handleKeyDown}
      placeholder="메시지를 입력하고 Enter를 눌러보세요"
    />
  );
}

 

이벤트를 중첩 조건으로 처리할 수 있고, preventDefault로 기본 동작을 제어할 수 있다는 것을 보여주는 예제예요.

🧪 예제 2: 동적으로 생성된 리스트 항목 클릭

function DynamicList() {
  const items = ["사과", "바나나", "오렌지"];

  const handleClick = (item) => {
    alert(`${item}을 선택하셨습니다!`);
  };

  return (
    <ul>
      {items.map((item, idx) => (
        <li key={idx} onClick={() => handleClick(item)}>
          {item}
        </li>
      ))}
    </ul>
  );
}

 

이벤트 핸들러에 익명 함수를 사용해서 인자를 넘기는 패턴이에요.

많이 쓰이는 패턴이지만 렌더링 성능에 영향을 줄 수 있으니 상황에 따라 useCallback도 고려해보세요!

🧪 예제 3: 부모에서 자식 이벤트 제어

function ParentComponent() {
  const handleChildClick = (msg) => {
    console.log("자식 클릭됨:", msg);
  };

  return <ChildComponent onCustomClick={handleChildClick} />;
}

function ChildComponent({ onCustomClick }) {
  return (
    <button onClick={() => onCustomClick("Hello from Child")}>
      자식 버튼
    </button>
  );
}

 

부모가 자식의 이벤트를 제어할 수 있도록 props로 핸들러를 전달하는 전형적인 패턴입니다.

컴포넌트 간 상호작용을 설계할 때 자주 사용되죠.

✅ 실전에서 꼭 기억할 베스트 프랙티스

  • 이벤트 핸들러는 짧고 명확하게 구성하자.
  • 중복되는 로직은 커스텀 훅이나 외부 함수로 분리하자.
  • props로 이벤트를 넘길 때는 이름을 명확하게 지정하자 (onClick보다는 onItemSelect 등).

🎉 결론!

이벤트 처리를 잘하면 리액트 앱의 UX, 유지보수, 성능까지 모두 향상됩니다.

여기까지 익히셨다면 이제 실전에서도 막힘 없이 이벤트를 자유자재로 다루실 수 있어요!

 

🔚 이벤트, 이제는 두렵지 않다!

여기까지 따라오시느라 정말 수고 많으셨어요!

처음엔 onClick 하나도 헷갈리던 리액트 이벤트 처리 방식, 이제는 꽤나 자신감이 붙지 않으셨나요?

이번 글을 통해 이벤트 객체의 구조부터, this 바인딩 문제, 커스텀 핸들러 설계, 실전 예제까지 모두 다뤄봤습니다.

앞으로는 이벤트 처리 코드를 짤 때,

"이걸 함수로 빼면 더 깔끔할까?",

"preventDefault 꼭 써야 할까?"

같은 고민을 스스로 던지고 답할 수 있게 되실 거예요.

 

다음엔 폼 처리, 상태 동기화 등 더 깊은 리액트 주제로 다시 찾아올게요.

놓치지 않도록 구독이나 북마크 해두시는 거 잊지 마시구요! 😊

반응형
반응형

리액트 훅 완전정복! 초보자를 위한 주요 훅 사용법 가이드

리액트 초보자라면 꼭 알아야 할 필수 훅들!
직접 써보고 '와, 이게 진짜 편하네' 했던
그 기능들만 모았습니다.
반응형

 


https://inf.run/mg1G5

 

Next.js 15: Full-Stack Development| 구멍가게코딩단 - 인프런 강의

전통적인 React 개발 방식과 Next.js를 이용해서 개발하는 방식은 전혀 다릅니다. 이 강의는 최신 Next.js 15의 핵심을 꿰뚫고, '진짜' Next.js다운 개발 방식을 알려드립니다. 취업 포트폴리오부터 실무

www.inflearn.com

 

안녕하세요!

리액트를 공부하다 보면 꼭 만나게 되는 개념 중 하나가 바로 "훅(Hook)"이죠. 함수형 컴포넌트 안에서 상태를 관리하거나 생명주기 효과를 처리할 때 빠질 수 없는 친구인데요.

이번 글에서는 useState, useEffect, useRef, useMemo, useCallback, useContext 등 자주 사용되는 훅들을 초보자 눈높이에 맞춰 쉽게 설명해드릴게요.

직접 써보며 헷갈렸던 부분까지 모두 정리했으니 끝까지 함께 해주세요!

 

1. useState 🧠 상태를 다루는 가장 기본 훅

useState는 리액트 훅 중에서 가장 자주 사용되는 기능 중 하나예요.

컴포넌트 안에서 변하는 값, 예를 들면 버튼 클릭 횟수나 입력 필드의 내용 등을 저장하고 관리하는 데 사용됩니다.

기존의 클래스형 컴포넌트에서 this.statethis.setState()를 사용하던 것을 함수형 컴포넌트에서 더 간단하게 구현할 수 있게 해주는 거죠!

📌 기본 문법

const [상태변수, 상태변경함수] = useState(초기값);

🧪 실습 예제

import { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>현재 카운트: {count}</p>
      <button onClick={() => setCount(count + 1)}>증가</button>
    </div>
  );
}

💡 사용 시 팁

  • 여러 개의 상태가 필요한 경우, useState를 여러 번 선언해도 전혀 문제 없어요!
  • 상태가 바뀌면 해당 컴포넌트는 자동으로 다시 렌더링돼요. 이걸 잘 활용해야 렌더링 최적화에 도움이 돼요.

📋 요약 정리

항목 내용
사용 목적 컴포넌트 내 상태값 관리
초기값 숫자, 문자열, 객체 등 다양한 자료형 가능
업데이트 방식 전용 함수(set함수)를 통해 변경

처음 리액트를 접하면 상태라는 개념이 어렵게 느껴질 수도 있지만, useState만 잘 이해해도 리액트 앱의 절반은 완성한 셈이에요!

 

다음은 useEffect를 통해 컴포넌트의 생명주기를 어떻게 다룰 수 있는지 알아볼게요.

 

 

2. useEffect 🔁 생명주기 대체 훅

리액트 함수형 컴포넌트는 생명주기 메서드가 없죠.

그 대신 useEffect를 사용하면 componentDidMount, componentDidUpdate, componentWillUnmount처럼 동작하게 만들 수 있어요.

 

예를 들어,

컴포넌트가 처음 화면에 나타날 때 데이터를 불러온다거나, 특정 값이 바뀔 때마다 API 요청을 보낸다거나, 또는 컴포넌트가 사라질 때 정리 작업(clean-up)을 해야 할 때 아주 유용합니다.

📌 기본 문법

useEffect(() => {
  // 실행할 코드
  return () => {
    // 정리(clean-up) 코드
  };
}, [의존성 배열]);

🧪 실습 예제

import { useEffect, useState } from 'react';

function Timer() {
  const [seconds, setSeconds] = useState(0);

  useEffect(() => {
    const interval = setInterval(() => {
      setSeconds(prev => prev + 1);
    }, 1000);

    return () => {
      clearInterval(interval);
    };
  }, []);

  return <p>{seconds}초 지났습니다.</p>;
}

💡 동작 방식에 따른 분기

의존성 배열 실행 시점
[] 컴포넌트 최초 렌더링 시 한 번만
[count] count 값이 바뀔 때마다 실행
의존성 없음 모든 렌더링마다 실행

📣 주의할 점

  • 의존성 배열을 제대로 설정하지 않으면 무한 루프에 빠질 수 있어요!
  • 정리(clean-up) 함수는 컴포넌트가 사라질 때뿐만 아니라 다음 실행 전에 호출되므로 유의해야 해요.

이제 useEffect를 통해 비동기 작업이나 외부 이벤트도 자유롭게 처리할 수 있어요.

 

다음으로는, DOM을 직접 제어하거나 변경되지 않는 값을 저장할 수 있는 useRef를 알아보러 가볼까요?

 

 

3. useRef 🔍 DOM 제어와 값 기억에 강력

useRef는 리액트에서 아주 유용하지만, 처음엔 그 쓰임새가 헷갈릴 수 있는 훅이에요.

주로 DOM 요소를 직접 제어하거나 렌더링 사이에도 유지되어야 하는 값을 저장할 때 사용해요.

이 훅의 특징은 값을 변경해도 컴포넌트를 다시 렌더링하지 않는다는 거예요.

그래서 카운트 같은 상태는 useState를 쓰지만, 변경되더라도 화면에 영향을 주지 않아야 하는 값은 useRef를 쓰는 게 더 적절해요.

📌 기본 문법

const myRef = useRef(초기값);

// 접근: myRef.current

🧪 DOM 조작 예제

import { useRef } from 'react';

function FocusInput() {
  const inputRef = useRef();

  const handleClick = () => {
    inputRef.current.focus();
  };

  return (
    <div>
      <input ref={inputRef} type="text" />
      <button onClick={handleClick}>포커스 주기</button>
    </div>
  );
}

🧠 상태 저장 예제 (렌더링 방지)

import { useRef, useState, useEffect } from 'react';

function RenderCount() {
  const [value, setValue] = useState('');
  const count = useRef(1);

  useEffect(() => {
    count.current += 1;
  });

  return (
    <div>
      <input value={value} onChange={(e) => setValue(e.target.value)} />
      <p>렌더링 횟수: {count.current}</p>
    </div>
  );
}

📋 요약 정리

기능 설명
DOM 접근 ref로 지정한 요소에 직접 접근 가능
렌더링 영향 없음 값 변경 시 컴포넌트 재렌더링 발생 안 함
유지되는 값 렌더링 사이에 값을 유지하고 싶을 때 사용

useRef는 보이지 않는 메모장 같아요.

화면에 영향을 주지 않으면서 내부 데이터를 보관할 수 있거든요.

 

이 다음엔 계산이 무거운 작업을 최적화하는 useMemo로 넘어가볼게요!

 

 

4. useMemo 📦 계산 결과를 메모이제이션

리액트에서 어떤 연산이 너무 자주 반복되면, 앱이 느려지는 원인이 될 수 있어요.

useMemo는 이런 상황에서 비용이 많이 드는 계산을 기억해두고, 필요할 때만 다시 계산하게 도와줘요.

쉽게 말해서,

어떤 계산된 값이 매번 바뀌지 않는데도 계속 다시 계산되는 게 비효율적이라면,

useMemo로 감싸서 “얘는 값 안 바뀌면 계산 다시 안 해!”라고 알려주는 거예요.

📌 기본 문법

const memoizedValue = useMemo(() => {
  return 복잡한계산(입력값);
}, [의존성값]);

🧪 실습 예제

import { useState, useMemo } from 'react';

function ExpensiveComponent() {
  const [number, setNumber] = useState(1);
  const [text, setText] = useState('');

  const factorial = (n) => {
    console.log('계산 중...');
    return n <= 1 ? 1 : n * factorial(n - 1);
  };

  const memoizedFactorial = useMemo(() => factorial(number), [number]);

  return (
    <div>
      <p>{number}! = {memoizedFactorial}</p>
      <button onClick={() => setNumber(prev => prev + 1)}>증가</button>
      <input value={text} onChange={(e) => setText(e.target.value)} />
    </div>
  );
}

💡 사용 시 주의할 점

  • 무조건 쓰면 성능이 좋아지진 않아요! 진짜 계산이 무거운 경우에만 쓰는 게 좋아요.
  • 의존성 배열을 잘못 설정하면 오래된 값을 계속 사용하게 될 수도 있어요.

📋 요약 정리

기능 설명
계산 결과 캐싱 의존값이 변하지 않으면 이전 계산 결과 재사용
최적화 대상 복잡하거나 반복되는 계산 작업
렌더링 영향 렌더링마다 계산 방지 가능

정말 계산이 무거운 로직에만 쓰는 게 핵심이에요.

불필요한 useMemo 남용은 오히려 독!

 

다음은 콜백 함수 자체를 메모이제이션해서 불필요한 재렌더링을 막는 useCallback 훅을 알아봅시다!

 

 

5. useCallback 🔄 콜백 함수를 캐싱

useCallbackuseMemo와 비슷하지만, 함수 자체를 기억하는 데 초점을 맞춘 훅이에요.

특히 하위 컴포넌트에 함수를 props로 넘길 때 불필요한 재렌더링을 막기 위해 많이 사용돼요.

리액트에서는 함수도 매번 새로 만들어지는 값이에요.

그래서 의도치 않게 하위 컴포넌트가 렌더링 될 필요가 없어도 재렌더링되곤 해요.

이때 useCallback을 사용하면 함수를 고정시켜서 그런 현상을 막을 수 있어요.

📌 기본 문법

const memoizedCallback = useCallback(() => {
  함수내용
}, [의존성값]);

🧪 실습 예제

import { useState, useCallback } from 'react';

function Parent() {
  const [count, setCount] = useState(0);

  const handleClick = useCallback(() => {
    console.log('버튼 클릭!');
  }, []);

  return (
    <div>
      <p>카운트: {count}</p>
      <button onClick={() => setCount(prev => prev + 1)}>증가</button>
      <Child onClick={handleClick} />
    </div>
  );
}

function Child({ onClick }) {
  console.log('Child 렌더링');
  return <button onClick={onClick}>클릭</button>;
}

💡 언제 써야 할까?

  • 하위 컴포넌트가 React.memo를 사용 중일 때, props로 넘기는 함수가 매번 바뀌면 재렌더링이 발생해요.
  • useCallback을 쓰면 함수 참조가 고정돼서 렌더링 최적화가 가능해져요.

📋 요약 정리

기능 설명
함수 메모이제이션 렌더링 시 동일한 함수 참조 유지
최적화 대상 React.memo와 함께 하위 컴포넌트 렌더링 방지
주의사항 의존성 배열 정확히 지정해야 효과 있음

useCallback은 성능 최적화를 위한 작은 도구예요.

얘를 잘 쓰면 쓸수록 렌더링 횟수를 줄여 앱이 쾌적해집니다.

 

다음은 컴포넌트 간 전역 상태를 쉽게 공유할 수 있는 useContext를 살펴볼 차례예요!

 

 

6. useContext 🌐 전역 상태를 간단하게 공유

리액트 앱이 커지다 보면 여러 컴포넌트에서 공통된 데이터를 사용해야 하는 상황이 많아져요.

예를 들어 사용자 정보, 테마 설정, 로그인 상태 등은 전역 상태로 관리하는 게 편하죠.

이럴 때 사용하는 게 useContext입니다.

useContext는 컴포넌트 트리 전반에 걸쳐 값을 전달하는 React Context를 쉽게 읽을 수 있도록 도와주는 훅이에요.

복잡한 props 전달 없이도 데이터를 쏙쏙! 받아올 수 있죠.

📌 기본 구조

// Context 생성
const MyContext = createContext();

// 상위 컴포넌트에서 Provider로 감싸기
<MyContext.Provider value={공유할값}>
  <하위컴포넌트 />
</MyContext.Provider>

// 하위 컴포넌트에서 사용
const value = useContext(MyContext);

🧪 실습 예제

import React, { createContext, useContext } from 'react';

const ThemeContext = createContext('light');

function App() {
  return (
    <ThemeContext.Provider value="dark">
      <Toolbar />
    </ThemeContext.Provider>
  );
}

function Toolbar() {
  return (
    <div>
      <ThemedButton />
    </div>
  );
}

function ThemedButton() {
  const theme = useContext(ThemeContext);
  return <button>현재 테마: {theme}</button>;
}

💡 언제 쓰면 좋을까?

  • 테마 설정, 언어 설정 등 앱 전체에 영향을 주는 값이 있을 때
  • 로그인 정보처럼 여러 컴포넌트에서 접근이 필요한 상태가 있을 때

📋 요약 정리

기능 설명
전역 상태 공유 props 없이 하위 컴포넌트에서 상태 사용 가능
Context API createContext + useContext 조합으로 구성
성능 유의사항 변경 시 모든 하위 컴포넌트 재렌더링 가능성 있음

 

이제 useContext까지 익혔다면, 리액트의 주요 훅들은 거의 섭렵한 거예요! 💪

 

다음 단계는 지금까지 배운 내용을 정리하고, 각 훅을 실제 프로젝트에서 어떻게 조합해서 사용할 수 있는지 마무리하면서 이야기해볼게요.

지금까지 리액트의 주요 훅들useState, useEffect, useRef, useMemo, useCallback, useContext—을 하나씩 차근차근 살펴봤어요.

처음에는 각 훅이 언제, 왜, 어떻게 쓰는지 헷갈릴 수 있지만, 실제 프로젝트에서 직접 활용해보면 어느 순간 손에 익는 도구처럼 자연스럽게 쓰게 됩니다.

각 훅은 역할과 목적이 명확하기 때문에 상황에 맞춰 조합하는 연습을 많이 해보는 게 좋아요.

예를 들어 useContext로 전역 상태를 만들고, 그 안에서 useMemouseCallback으로 불필요한 렌더링을 줄이는 구조는 실전에서도 많이 사용돼요.

처음 훅을 공부할 땐 “이걸 왜 쓰지?” 싶은 느낌이 들 수 있지만, 리액트를 리액트답게 쓰는 핵심이 바로 이 훅들에 있답니다.

앞으로도 계속해서 다양한 상황에 훅을 적용해보면서 자신만의 개발 노하우를 만들어 보세요! 🚀

반응형

+ Recent posts