학습/React

리액트 기초 - 강의 정리 2

김 무무 2025. 2. 25. 00:31

1. children prop과 컴포넌트 합성

`children`은 특별한 내장 prop으로, 컴포넌트의 시작 태그와 종료 태그 사이에 있는 내용을 가리킨다.

이를 통해 컴포넌트 내부에 다른 요소나 컴포넌트를 배치할 수 있다.

ex)

function Card({ children }) {
  return <div className="card">{children}</div>;
}

// 사용
<Card>
  <h2>제목</h2>
  <p>내용</p>
</Card>

 

 

children vs 일반 props

- children은 컴포넌트 사이에 JSX를 배치할 때 유용하다.

- 일반 props는 구체적인 데이터나 구성 옵션을 전달할 때 좋다.

 

선택은 개발자의 선호와 특정 상황에 따라 달라질 수 있으며, 중요한 것은 일관성 있게 사용하는 것이다.

 

 

2. 이벤트 처리

리액트에서는 HTML과 유사한 방식으로 이벤트를 처리하지만, 몇 가지 차이점이 있다.

 

- 이벤트 이름은 camelCase를 사용 (onClick, onChange 등)

- JSX에서는 함수를 이벤트 핸들러로 전달한다

- 커스텀 컴포넌트에도 함수를 prop으로 전달하여 이벤트 핸들러로 사용할 수 있다.

 

ex)

function CustomButton({ onButtonClick }) {
  return <button onClick={onButtonClick}>Click me</button>;
}

// 사용
function App() {
  const handleClick = () => {
    alert('Button clicked!');
  };

  return <CustomButton onButtonClick={handleClick} />;
}

 

 

2.1. 리액트에서의 함수

리액트에서 함수는 일급 객체(first-class objects)로 취급된다.

이는 함수가 변수에 할당되거나, 다른 함수에 인자로 전달되거나, 함수에서 반환될 수 있음을 의미한다.

 

JavaScript에서 함수명은 실제 함수 구현을 가리키는 포인터와 같다.

즉, handleClick이라는 함수명을 사용할 때, 우리는 함수의 내용(구현)이 저장된 메모리 위치를 참조하고 있는 것이다. 

 

ex)

// 함수 선언
function handleClick() {
  console.log('Button clicked!');
}

// handleClick은 함수 구현체를 가리키는 포인터
const myFunction = handleClick; // 함수 참조를 다른 변수에 할당

 

 

2.2. 함수 전달 방식의 차이

함수를 props로 전달할 때 세 가지 방식이 있으며, 각각 다른 결과를 가져온다.

 

1) 함수명 :

함수 자체를 참조하여 전달한다.

이벤트 발생 시 함수가 호출된다.

렌더링마다 새 함수를 생성하지 않고 동일한 함수 참조를 사용한다.

이벤트 핸들러가 매개변수 없이 호출될 때 가장 좋은 방식이다.

   <button onClick={handleClick}>Click</button>

 

 

2) 함수() :

렌더링 시점에 함수를 즉시 호출한다.

이벤트 발생과 관계없이 렌더링될 때 실행되며 함수 실행 결과를 속성에 넣는다.

대부분의 경우에서 의도하지 않은 동작이며, 피해야 할 방식이다.

   <button onClick={handleClick()}>Click</button> // 잘못된 사용법!

 

 

3) () => 함수() :

함수를 새로운 함수로 감싸서 전달한다.

이벤트 발생 시 익명 함수가 실행되고, 그 안에서 원래 함수를 호출한다.

렌더링마다 새로운 함수 인스턴스가 생성되므로, 하위 컴포넌트가 React.memo로 최적화되어 있다면 불필요한 리렌더링이 발생할 수 있다.

매개변수를 전달해야 할 때 유용하다.

   <button onClick={() => handleClick()}>Click</button>

 

 

3. 상태 관리 (useState Hook)

상태는 컴포넌트 내에서 관리되는 데이터로, 시간에 따라 변경될 수 있으며 UI에 영향을 미친다. 

일반 변수와 달리, 상태가 변경되면 리액트는 컴포넌트를 자동으로 다시 렌더링한다.

3.1. useState Hook

useState는 리액트에서 제공하는 Hook으로, 함수형 컴포넌트에서 상태를 사용할 수 있게 해준다.

useState()는 배열을 반환하며, 첫 번째 요소는 현재 상태 값, 두 번째 요소는 상태를 업데이트하는 함수이다.

 

ex)

import { useState } from 'react';

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

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
}

 

 

3.2. 상태 업데이트와 리렌더링

상태 업데이트 함수를 호출하면 리액트에게 컴포넌트를 다시 렌더링하도록 요청한다.

이때 중요한 점은 상태 업데이트는 비동기적으로 처리된다는 것이다.

 

상태 업데이트 함수를 호출해도 현재 실행 중인 코드 블록에서는 상태 값이 바로 변경되지 않는다.

대신, 다음 렌더링 사이클에서 새로운 상태 값이 적용된다.

 

 

상태 업데이트 시 이전 상태 활용 (함수형 업데이트)

이전 상태에 기반하여 상태를 업데이트할 때는 함수형 업데이트를 사용하는 것이 안전하다.

 

ex)

// 이 방식은 최신 상태를 사용한다고 보장할 수 없다
setCount(count + 1);

// 함수형 업데이트 - 항상 최신 상태를 기반으로 업데이트한다
setCount(prevCount => prevCount + 1);

 

 

3.4. 상태 업데이트 함수 사용 패턴

useState Hook을 사용할 때는 몇 가지 규칙을 따라야 한다.

 

- 컴포넌트의 최상위 레벨에서만 호출해야 함

- 조건문, 반복문, 중첩 함수 내에서 호출할 수 없음

- 리액트 컴포넌트 또는 커스텀 Hook 내에서만 사용할 수 있음

 

 

4. 조건부 렌더링

4.1. 삼항 연산자

JSX 내에서 삼항 연산자를 사용하여 조건에 따라 다른 요소를 렌더링할 수 있다.

 

ex)

function UserGreeting({ isLoggedIn }) {
  return (
    <div>
      {isLoggedIn ? <h1>Welcome back!</h1> : <h1>Please sign in.</h1>}
    </div>
  );
}

 

 

 

4.2. 논리 연산자(&&)를 활용한 조건부 렌더링

&& 연산자를 사용하여 조건이 참일 때만 요소를 렌더링할 수 있다.

ex)

function Notifications({ messages }) {
  return (
    <div>
      {messages.length > 0 && (
        <h2>You have {messages.length} unread messages.</h2>
      )}
    </div>
  );
}

 

 

4.3. 변수와 if문을 통한 조건부 JSX 할당

더 복잡한 조건부 렌더링이 필요한 경우, JSX를 변수에 할당하고 일반 JavaScript의 if 문을 사용할 수 있다.

 

ex)

function LoginStatus({ isLoggedIn, user }) {
  let content;

  if (isLoggedIn) {
    content = <h1>Welcome {user.name}!</h1>;
  } else {
    content = <h1>Please log in</h1>;
  }

  return <div>{content}</div>;
}

 

 

5. 리스트 렌더링

5.1. map() 메서드를 사용한 리스트 생성

JavaScript의 map() 메서드를 사용하여 데이터 배열을 JSX 요소 배열로 변환할 수 있다.

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

 

 

5.2. key prop

리스트 아이템을 렌더링할 때는 각 아이템에 고유한 key prop을 제공해야 한다.

이 키는 리액트가 리스트 아이템을 식별하는 데 사용되며, 렌더링간에 요소를 추적할 수 있도록 한다.

 

만약 key가 없으면 리액트는 배열 위치만으로 요소를 식별한다.

이 경우 첫번째 항목만 추가되거나 제거되어도 리액트가 모든 항목이 변경되었다고 인식해 모든 항목을 재생성 할 수도 있다.


- 가능하면 데이터의 고유 ID를 키로 사용
- 인덱스를 키로 사용하는 것은 최후의 수단 (항목 순서가 변경될 수 있는 경우 문제가 발생할 수 있음)

 

ex)

// 좋은 예
{users.map(user => <ListItem key={user.id} user={user} />)}

// 피해야 할 예 (더 나은 대안이 없을 때만 사용)
{users.map((user, index) => <ListItem key={index} user={user} />)}