React 공식문서 이해하기 (28)

Syoee·2023년 12월 8일
0

React

목록 보기
28/30
post-thumbnail

Chapter 4. Escape Hatches

#6 이벤트와 Effect 분리하기

학습 목차

1. 이벤트 핸들러와 Effect 중 선택하기
2. 반응형 값 및 반응형 로직
3. Effect에서 비반응형 로직 추출하기


1. 이벤트 핸들러와 Effect 중 선택하기

  • 채팅방 컴포넌트를 구현한다고 상상해보자.
    1. 컴포넌트는 선택한 채팅방에 자동으로 연결되어야 합니다.
    2. “전송” 버튼을 클릭하면, 채팅에 메시지를 전송해야 합니다.
  • 이미 코드를 구현했지만, 이 코드를 어디에 놓아야 할지 확실하지 않다.
    이벤트 핸들러와 Effects 중 어떤 것을 사용해야 할까?
    이 질문에 대답할 때마다, 코드가 실행되어야하는 이유를 생각해야한다.

1-1. 이벤트 핸들러는 특정 상호 작용에 대한 응답으로 실행된다.

  • 사용자 관점에서 메시지를 보내는 것은 특정 “전송” 버튼을 클릭했기 때문에 발생되어야한다.
    메시지가 다른 시간이나 다른 이유로 보내지면 사용자들은 화가 날 것이다.
    이것이 메시지를 보내는 것이 이벤트 핸들러여야하는 이유이다.
    이벤트 핸들러를 사용하면 특정 상호 작용에 대한 처리를 할 수 있다.
function ChatRoom({ roomId }) {
  const [message, setMessage] = useState('');
  // ...
  function handleSendClick() {
    sendMessage(message);
  }
  // ...
  return (
    <>
      <input value={message} onChange={e => setMessage(e.target.value)} />
      <button onClick={handleSendClick}>Send</button>;
    </>
  );
}
  • 이벤트 핸들러를 사용하면 사용자가 버튼을 누를 때만 sendMessage(message) 가 실행된다는 것을 확신할 수 있다.

1-2. Effect는 동기화가 필요할 때마다 실행된다.

  • 코드를 실행해야 하는 이유 는 특정 상호 작용이 아니다.
    사용자가 채팅방 화면으로 이동한 이유나 방법은 중요하지 않다.
    사용자들이 채팅방 화면을 보고 상호작용 할 수 있어야하기 때문에, 컴포넌트는 선택한 채팅 서버에 계속 연결되어 있어야 한다.
    앱의 초기 화면이 채팅방 컴포넌트이고, 사용자가 어떤 상호작용도 수행하지 않았더라도 여전히 채팅 서버에 연결되어야한다. 이것이 바로 Effect인 이유이다.
function ChatRoom({ roomId }) {
  // ...
  useEffect(() => {
    const connection = createConnection(serverUrl, roomId);
    connection.connect();
    return () => {
      connection.disconnect();
    };
  }, [roomId]);
  // ...
}
  • 이 코드를 사용하면, 사용자가 수행한 특정 상호작용과는 무관하게, 항상 현재 선택된 채팅 서버에 대한 활성화된 연결이 있음을 확신할 수 있다.
  • 사용자가 앱을 열었을 때 뿐만 아니라, 다른 채팅방을 선택하거나 다른 화면으로 이동했다가 다시 돌아와도 Effect가 현재 선택된 방과 동기화되어 컴포넌트에 항상 현재 선택된 채팅서버가 연결된 상태가 유지된다. 또한, 필요할 때마다 다시 연결된다.

2. 반응형 값 및 반응형 로직

  • 직관적으로, 이벤트 핸들러는 버튼을 클릭하는 등 항상 “수동”으로 트리거시킨다고 말할 수 있다.
    반면에 Effect는 “자동”으로 동기화 상태를 유지하는 데 필요한 만큼 자주 다시 실행된다.
  • 컴포넌트 본문 내부에 선언된 props, state, 변수를 반응형 값이라고 합니다.
    아래 예제에서 serverUrl반응형 값이 아니지만 roomIdmessage는 반응형 값이다.
const serverUrl = 'https://localhost:1234';

function ChatRoom({ roomId }) {
  const [message, setMessage] = useState('');

  // ...
}
  • 이와 같은 반응형 값은 리렌더링으로 인해 변경될 수 있습니다. 예를 들어, 사용자가 message를 수정하거나 드롭다운에서 다른 roomId를 선택할 수 있습니다. 이벤트 핸들러와 Effect는 변경 사항에 다르게 반응한다.
    • 이벤트 핸들러 내부의 로직은 반응형이 아니다.
      사용자가 동일한 상호작용(예: 클릭)을 다시 수행하지 않는 한 다시 실행되지 않는다.
      이벤트 핸들러는 변경에 “반응”하지 않고 반응형 값을 읽을 수 있다.
    • Effects 내부의 로직은 반응형이다.
      Effect에서 반응형 값을 읽는 경우 의존성으로 지정해야 한다.
      그런 다음 리렌더링으로 인해 해당 값이 변경되면 React는 새 값으로 Effect의 로직을 다시 실행한다.

2-1. 이벤트 핸들러 내부의 로직은 반응형이 아니다.

 // ...
    sendMessage(message);
 // ...
  • 사용자의 관점에서 볼 때 message가 변경되었다고 해서 메시지를 보내겠다는 뜻은 아니다.
    사용자가 입력 중이라는 의미일 뿐이다.
    즉, 메시지를 전송하는 로직은 반응적이어서는 안 된다.
    반응형 값이 변경되었다는 이유만으로 다시 실행되어서는 안 된다.
    이것이 바로 이벤트 핸들러에 속하는 이유이다.
  function handleSendClick() {
    sendMessage(message);
  }
  • 이벤트 핸들러는 반응형이 아니므로 사용자가 보내기 버튼을 클릭할 때만 sendMessage(message)가 실행된다.

2-2. Effect 내부의 로직은 반응형이다.

  // ...
    const connection = createConnection(serverUrl, roomId);
    connection.connect();
  // ...
  • 사용자 입장에서 보면, roomId가 변경되었다는 것은 다른 룸에 연결하고 싶다는 의미이다.
    즉, 방에 연결하기 위한 로직은 반응형이어야한다.
    이러한 코드 라인은 반응형 값을 “따라잡고”, 값이 달라지면 다시 실행되기를 원한다.
    이것이 바로 Effect에 속하는 이유이다.
  useEffect(() => {
    const connection = createConnection(serverUrl, roomId);
    connection.connect();
    return () => {
      connection.disconnect()
    };
  }, [roomId]);
  • Effect는 반응형이므로 createConnection(serverUrl, roomId)connection.connect() 코드는 roomId의 모든 고유값에 대해 실행된다.
    Effect는 현재 선택된 방에 따라 채팅 연결을 동기화한다.

3. Effect에서 비반응형 로직 추출하기

  • 반응형 로직과 비반응형 로직을 함께 사용하려는 경우 상황이 더 까다로워진다.
  • 예를 들어, 사용자가 채팅에 연결할 때 알림을 표시하고 싶다고 가정해 보자.
    props에서 현재 테마(dark or light)를 읽어 올바른 색상으로 알림을 표시한다.
function ChatRoom({ roomId, theme }) {
  useEffect(() => {
    const connection = createConnection(serverUrl, roomId);
    connection.on('connected', () => {
      showNotification('Connected!', theme);
    });
    connection.connect();
// ...
  • 그러나, theme는 반응형 값이며(리렌더링의 결과로 변경될 수 있음), Effect에서 읽는 모든 반응형 값은 의존성으로 선언해야한다.
    이제 theme를 Effect의 의존성으로 지정해야한다.
function ChatRoom({ roomId, theme }) {
  useEffect(() => {
    const connection = createConnection(serverUrl, roomId);
    connection.on('connected', () => {
      showNotification('Connected!', theme);
    });
    connection.connect();
    return () => {
      connection.disconnect()
    };
  }, [roomId, theme]);
  // ...

3-1. Effect Event 선언하기
3-2. Effect Event로 최신 props 및 state 읽기
3-3. Effect Event의 제한사항

  • 위의 3-* 내용들은 아직 안정된 버전의 React로 출시되지 않은 실험적인 API에 대해 설명하고 있어 참고만 하였음.

요약

  • 이벤트 핸들러는 특정 상호 작용에 대한 응답으로 실행된다.
  • Effect는 동기화가 필요할 때마다 실행된다.
  • 이벤트 핸들러 내부의 로직은 반응형이 아니다.
  • Effect 내부의 로직은 반응적이다.
  • 비반응적 로직을 Effect에서 Effect Event로 이동할 수 있다.
  • Effect 내부에서만 Effect Event를 호출하라.
  • Effect Event를 다른 컴포넌트나 Hook에 전달하지 말자.

React 공식 문서

https://react.dev/

React 비공식 번역 문서

https://react-ko.dev/

MDN

https://developer.mozilla.org/ko/

Wikipedia

https://ko.wikipedia.org/wiki/

profile
함께 일하고 싶어지는 동료, 프론트엔드 개발자입니다.

0개의 댓글