React에서 상태 끌어 올리기가 필요한 이유는 형제 컴포넌트끼리 직접적으로 상태값을 전달할 수 없습니다.
이유는 React의 단방향 데이터 흐름(one-way data flow) 구조 때문입니다.React에서는 부모 컴포넌트에서 자식 컴포넌트로 props를 전달하고, 자식 컴포넌트에서는 해당 props를 사용하여 렌더링합니다. 그리고 자식 컴포넌트에서 발생한 이벤트나 사용자 입력에 대한 상태 업데이트는 부모 컴포넌트에서 전달받은 콜백 함수를 호출하여 부모 컴포넌트의 상태를 업데이트하는 방식으로 구현됩니다.따라서 형제 컴포넌트 간에는 직접적으로 props나 상태를 전달할 수 없으며, 부모 컴포넌트를 통해 간접적으로 상태나 props를 전달해야 합니다. 이러한 구조는 컴포넌트 간의 의존성을 줄이고, 코드의 유지보수성을 높이는데 도움을 줍니다.

React에서 상태(state)를 끌어올리지 않으면 다음과 같은 문제점이 발생할 수 있습니다.

  1. 중복된 상태: 하위 컴포넌트에서 동일한 상태를 사용할 경우, 중복된 상태가 발생할 수 있습니다. 이는 상태 업데이트를 관리하기 어렵게 만들고, 코드의 복잡성을 증가시킵니다.
  2. 상태 업데이트의 불일치: 하위 컴포넌트에서 상태를 업데이트할 때, 상위 컴포넌트의 상태와 일치하지 않을 수 있습니다. 이는 애플리케이션의 예상치 못한 동작을 초래할 수 있습니다.
  3. 데이터의 일관성 문제: 여러 하위 컴포넌트에서 동일한 데이터를 사용하는 경우, 데이터의 일관성 문제가 발생할 수 있습니다. 이는 데이터를 관리하기 어렵게 만들고, 예상치 못한 동작을 초래할 수 있습니다.
  4. 성능 저하: 하위 컴포넌트에서 상태를 업데이트할 때, 해당 상태를 사용하는 모든 컴포넌트가 다시 렌더링됩니다. 이는 불필요한 렌더링을 유발하여 성능 저하를 초래할 수 있습니다.
  5. 코드 복잡성: 상태를 여러 하위 컴포넌트에서 사용할 경우, 코드의 복잡성이 증가합니다. 이는 유지보수성을 떨어뜨리고, 코드의 가독성을 저하시킵니다.

따라서 React에서는 상태를 상위 컴포넌트에서 관리하고, 상태 끌어올리기를 통해 상태 업데이트를 효율적으로 관리함으로써 위와 같은 문제점을 해결할 수 있습니다.

React에서 App.js 파일에서 버튼 컴포넌트와 유저 컴포넌트를 import하고 상태(state)를 끌어올리는 코드는 다음과 같습니다.

import React, { useState } from 'react';
import Button from './Button';
import User from './User';

const App = () => {
  const [count, setCount] = useState(0);
  const [user, setUser] = useState({ name: 'John', age: 30 });

  const incrementCount = () => {
    setCount(count + 1);
  };

  const updateName = (newName) => {
    setUser({ ...user, name: newName });
  };

  return (
    <div>
      <Button onClick={incrementCount} />
      <User user={user} onUpdateName={updateName} />
    </div>
  );
};

export default App;

위 코드에서, ButtonUser 컴포넌트는 다른 파일에 정의되어 있으며, useState Hook을 사용하여 countuser 상태를 생성합니다. incrementCount 함수는 setCount 함수를 호출하여 count 값을 업데이트합니다. updateName 함수는 setUser 함수를 호출하여 user 객체의 name 값을 업데이트합니다.

버튼 컴포넌트 예시:

import React from 'react';

const Button = (props) => {
  return (
    <button onClick={props.onClick}>
      Click me
    </button>
  );
};

export default Button;

위 예시에서 Button 컴포넌트는 onClick prop을 받아서 버튼이 클릭되었을 때 호출됩니다.

유저 컴포넌트 예시:

import React from 'react';

const User = (props) => {
  const { user, onUpdateName } = props;
  const handleNameChange = (event) => {
    onUpdateName(event.target.value);
  };
  return (
    <div>
      <div>Name: {user.name}</div>
      <div>Age: {user.age}</div>
      <input type="text" value={user.name} onChange={handleNameChange} />
    </div>
  );
};

export default User;

위 예시에서 User 컴포넌트는 user prop을 받아서 이름과 나이를 보여주고, onUpdateName prop을 받아서 이름이 변경될 때 호출됩니다. 이름을 변경할 때는 handleNameChange 함수가 호출되며, 이 함수에서는 onUpdateName 함수를 호출하여 이름을 업데이트합니다.