개발인생/Frontend

[React] 필수템 정리 - Component, JSX, Props, State, useEffect

forri 2025. 3. 6. 15:53

1. 컴포넌트(Component)

리액트는 "컴포넌트" 기반 라이브러리이다. ui를 작은 조각들(컴포넌트)로 나눠서 재사용할 수 있음

 

🐢예제 코드(컴포넌트 만들기)

function Greeting() {
	return <h1>안녕 나는 리액트 컴포넌트야</h1>;
}

export default Greeting;
  • return 안에는 JSX문법을 사용해 화면에 표시할 내용을 작성

2.JSX(JavaScript XML)

JSX는 리액트에서 HTML을 JavaScript 안에서 사용할 수 있도록 도와주는 문법

 

🐢예제 코드(JSX 사용)

function App() {
  const name = "포리";
  return <h1>{name}야, 안녕!</h1>;
}

export default App;
  • {name} 처럼 중괄호를 사용하면 JavaScipt 변수를 JSX 안에서 사용할 수 있음

3. Props(속성)

컴포넌트 간 데이터를 전달할때 props를 사용

 

🐢예제 코드(Props 전달하기)

function Welcome(props) { //props는 Welcome 컴포넌트가 받을 데이터를 담고 있는 객체
	return <h1>안녕, {props.name}!</h1>;
}

function App() {
	return <Welcome name = "포리" />; //return으로 Welcome컴포넌트를 찾아서 실행(함수 실행과 비슷함) + Welcome함수를 실행하면서 {name:"포리"}라는 객체를 props로 넘겨줌
}

export default App;
  • Welcome 컴포넌트가 props.name을 받아서 "포리" 라는 이름을 출력

4. State(상태)

state는 컴포넌트 내부에서 관리하는 데이터임. useState라는 Hook을 사용해서 변경가능

 

🐢예제 코드(State 사용하기)

import { useState } from "react";

function Counter() {
  const [count, setCount] = useState(0); // count 초기값을 0으로 설정

  return (
    <div>
      <h1>카운트: {count}</h1>
      <button onClick={() => setCount(count + 1)}>+</button>
      <button onClick={() => setCount(count - 1)}>-</button>
    </div>
  );
}

export default Counter;
  • 버튼을 클릭할 때마다, count 값이 바뀌고 화면에 업데이트 됨

5. 이펙트(useEffect)

컴포넌트가 화면에 나타날때(API호출, 이벤트 등록 등) useEffect를 사용

 

🐢예제 코드(useEffect 사용하기)

import {useEffect, useState} from "react";

functoin Timer() {
	const [seconds, setSeconds] = useState(0);
    
    useEffect(()=> {
    	const interval = setInterval(()=> {
        	setSeconds((prev) => prev + 1);
        }, 1000);
        
        return () => clearInterval(interval); //컴포넌트가 사라질때 정리
    },[]);

	return <h1>경과시간: {second}초</h1>;
}

export default Timer;
  • useEffect를 사용해서 1초마다 seconds 값 증가
👉 useEffect안에 있는 return은 unmount 함수
function ClickTracker() {
useEffect(() => {
console.log("컴포넌트 마운트됨");
return () => {
console.log("컴포넌트 언마운트됨!");
};
}, [ ]);

간단예제

사용자 목록을 API에서 불러와서 화면에 표시하고, 필터링하는 기능🫡

 

1️⃣ 부모 컴포넌트 (UserList)

import { useState, useEffect } from "react";
import UserCard from "./UserCard"; // 자식 컴포넌트 임포트

function UserList() {
  const [users, setUsers] = useState([]); // API에서 가져온 사용자 데이터를 저장하는 상태(State)
  const [searchTerm, setSearchTerm] = useState(""); // 검색 입력창에 입력한 값을 저장하는 상태(State)

  // ✅ useEffect를 사용하여 API에서 데이터 가져오기
  useEffect(() => {
    console.log("데이터 가져오는 중...");
    fetch("https://jsonplaceholder.typicode.com/users") // API 호출
      .then((response) => response.json())
      .then((data) => {
        console.log("데이터 로드 완료!");
        setUsers(data);
      })
      .catch((error) => console.error("데이터 로드 실패!", error));
  }, []); // 빈 배열 → 최초 1회 실행

  //검색어에 따라 사용자 필터링
  const filteredUsers = users.filter((user) =>
    user.name.toLowerCase().includes(searchTerm.toLowerCase())
  );

  return (
    <div>
      <h1>사용자 목록</h1>
      {/* 검색 입력창 */}
      <input
        type="text"
        placeholder="이름 검색"
        value={searchTerm} //상태를 입력 필드와 연결(양방향 데이터 바인딩)
        onChange={(e) => setSearchTerm(e.target.value)} //입력할 때마다 상태 업데이트
      />

      {/* 필터링된 사용자 목록을 UserCard 자식 컴포넌트로 전달 */}
      {filteredUsers.length > 0 ? (
        filteredUsers.map((user) => <UserCard key={user.id} user={user} />) //props 사용
      ) : (
        <p>검색 결과 없음</p>
      )}
    </div>
  );
}

export default UserList;

 

✔️입력값을 실시간으로 반영하는 방식

더보기
더보기

1️⃣ value 속성: 상태(state)와 입력 필드 연결

<input type="text" value={searchTerm} />

설명

  • value={searchTerm} → 입력 필드의 값(value)을 searchTerm 상태와 동기화.
  • 즉, searchTerm이 변경되면 입력 필드에도 자동으로 반영됨.

📌 만약 value 속성이 없으면?

  • 사용자가 입력한 값이 UI에 반영되지만, 상태(searchTerm)와 연결되지 않음.

📌 value만 설정하고 onChange가 없으면?

  • 입력창이 읽기 전용(read-only) 상태가 되어 아무것도 입력할 수 없음.

 

2️⃣ onChange 이벤트 핸들러: 상태 업데이트

onChange={(e) => setSearchTerm(e.target.value)}

설명

  • 사용자가 입력할 때마다 e.target.value(입력한 값)를 가져와서 setSearchTerm()으로 상태를 업데이트.
  • 이렇게 하면 searchTerm이 최신 값으로 변경되고, UI도 자동으로 갱신됨.

 

3️⃣ 양방향 데이터 바인딩 (Two-Way Data Binding)

🔵 단방향 데이터 흐름 (One-Way Data Binding)

  • 리액트는 기본적으로 단방향 데이터 바인딩을 사용함.
  • 즉, state → UI (UI를 상태에 따라 업데이트)
const [text, setText] = useState("초기값");

return <input type="text" value={text} />;

📌 문제점

  • value에 text를 넣었지만, 사용자가 입력해도 값이 변하지 않음.
  • 읽기 전용(Read-Only) 상태가 되어 입력할 수 없음

 

🟠 양방향 데이터 바인딩 (Two-Way Data Binding)

const [text, setText] = useState("초기값");

return (
  <input
    type="text"
    value={text}
    onChange={(e) => setText(e.target.value)} // 입력할 때마다 상태 업데이트
  />
);

📌 이제 정상 동작함
✅ 사용자가 입력한 값이 즉시 text 상태로 업데이트
✅ text 상태가 변경되면서, 입력 필드에도 즉시 반영

 

📌 양방향 바인딩을 활용하면?

  • 입력 필드와 상태(state)가 서로 영향을 주고받는 구조가 됨.
  • 사용자 입력이 즉시 반영되고, 상태가 변경되면 UI도 즉시 갱신됨.
  • 예: 검색 입력창, 폼(Form) 입력, 실시간 필터링 기능 등에 필수적

 

2️⃣ 자식 컴포넌트 (UserCard)

function UserCard({ user }) {
  return (
    <div style={{ border: "1px solid #ccc", padding: "10px", margin: "5px" }}>
      <h2>{user.name}</h2>
      <p>Email: {user.email}</p>
      <p>Address: {user.address.city}</p>
    </div>
  );
}

export default UserCard;

 

👉 화면 출력 결과

처음 실행 시 "Leanne"을 검색하면 "XXXX"을 검색하면 결과 없음
사용자 목록
[ 🔍 이름 검색 입력창 ]
Leanne Graham
Email: Sincere@april.biz
Ervin Howell
Email: Shanna@melissa.tv
Clementine Bauch
Email: Nathan@yesenia.net
...
사용자 목록
[ 🔍 이름 검색 입력창: "Leanne" ]
Leanne Graham
Email: Sincere@april.biz
사용자 목록
[ 🔍 이름 검색 입력창: "XXXX" ]
검색 결과 없음