[나만무] React.memo로 소켓 재연결 방지하기

맹쥐·2025년 7월 6일
3
post-thumbnail

우리 프로젝트에서는 사용자마다 한 소켓을 사용한다.
소켓으로 주고받는 데이터는 크게 두 카테고리로 분류할 수 있다.

  • 픽셀
  • 채팅

캔버스 데이터는 항상 주고받아야하기 때문에,
캔버스가 렌더링 되는 순간 소켓을 연결한다.

하지만 채팅은 그렇게되면 쓸데없는 데이터양이 늘어나기때문에
채팅 버튼을 클릭했을 때 채팅 소켓을 연결하도록 설계했다.

[ 좌측 하단 파란색 버튼이 채팅버튼]


문제 상황: 불필요한 채팅 소켓 연결 시도

export const useChatSocket = (
  onMessageReceived: (message: ChatMessage) => void,
  onChatError: (error: ChatError) => void,
  group_id: string,
  user_id: string
) => {
  useEffect(() => {
    // 유효하지 않은 group_id이면 소켓 연결 안 함
    if (!group_id || group_id === '0' || !user_id) {
      console.log('소켓 연결 스킵: 유효하지 않은 group_id 또는 user_id');
      return;
    }

    // 채팅 이벤트 리스너 등록
    socketService.onChatMessage(onMessageReceived);
    socketService.onChatError(onChatError);

    // 채팅방 참여
    socketService.joinChat({ group_id });
    console.log(`채팅방 참여: group_id=${group_id}`);

    return () => {
      // 클린업 시 이벤트 리스너 제거
      socketService.offChatMessage(onMessageReceived);
      socketService.offChatError(onChatError);
    };
  }, [group_id, user_id, onMessageReceived, onChatError]);

그런데 이상하게도 마이페이지, 그룹 등 다른 메뉴 버튼을 클릭할 때마다 콘솔에 이런 로그가 계속 찍혔다.

채팅 소켓을 계속해서 시도하고 있는 것이다.


원인 분석

Chat 컴포넌트가 App.tsx에서 항상 렌더링되고 있었기 때문이었다.
마이페이지든 뭐든 열릴 때마다 App 전체가 리렌더링 → Chat도 매번 실행 → useEffect

// App.tsx
return (
  <main>
    <PixelCanvas />
    <Modal isOpen={isMyPageModalOpen}>  {/* 마이페이지 모달 */}
      <MyPageModalContent />
    </Modal>
    <Chat />  {/* ← 항상 렌더링! 마이페이지와 상관없이 */}
  </main>
);

Chat 컴포넌트를 조건부 렌더링 하거나, 메모이제이션 으로 불필요한 리렌더링을 방지해야 한다.
소켓이 계속 재연결되어 불필요한 네트워크 트래픽이 발생할 수 있다.


처음 배운 개념: React.memo

React.memo란?

React.memo는 컴포넌트를 메모이제이션(memoization) 해서, props가 바뀌지 않으면 리렌더링을 생략해주는 기능이다.

// 일반 컴포넌트
function Chat() { ... }

// 메모이제이션 적용
const Chat = React.memo(function Chat() { ... });
                                      

동작 원리

부모 컴포넌트가 리렌더링돼도,
자식 컴포넌트(Chat)의 props가 이전과 동일하다면,
React는 불필요한 리렌더링을 하지 않음

즉, 마이페이지를 열어도 Chat은 리렌더링되지 않게 된다!


해결 방법

방법 1: React.memo 적용

const Chat = React.memo(function Chat() {
  ...
});

React.memo는 props가 바뀌지 않으면 컴포넌트를 리렌더링하지 않도록 막아준다.
덕분에 App이 리렌더링되더라도 Chat 컴포넌트는 props가 같다면 재실행되지 않는다.

방법 2: 조건부 렌더링

{isChatOpen && <Chat />}

이번 경험을 통해 리액트 성능 최적화의 핵심은 "불필요한 리렌더링을 줄이는 것"이라는 걸 체감했다.
React.memo는 간단하지만 매우 강력한 도구인듯하다.
앞으로 적극적으로 활용하게 될 것 같다.

리액트를 왜 쓰는지 조금씩 알아가는 중 .. !


참고

React 공식 문서 - React.memo

profile
이유민

1개의 댓글

comment-user-thumbnail
2025년 7월 6일

frontend 솜씨가 좋으시네요

답글 달기