-
문제
- 채팅 페이지 인풋에 텍스트 입력 시 매번 서버로 GET요청을 보낸다.
- 메세지 send 시 또는 스크롤을 최상단으로 올릴 때 새로운 websocket을 생성한다.
-
원인
문제 a
- 타이핑 시 발생하는 Request URL 의 형태: base_url/info?t={랜덤숫자} 는 현재 설계된 API에 포함되지 않은 API (https://hhboard.xyz/ws/info?t=1714358503912)
- 확인 결과 SockJS 라이브러리로 웹소켓 생성 시 항상 GET /info 요청을 서버로 보내며, 웹소켓 연결과 관련한 서버의 정보를 응답으로 받는 것을 알 수 있었다.
- GET요청 자체에는 문제가 없으나, Input의 value를 useState로 관리하면서, value가 업데이트 될 때 마다 리렌더링에 의해 계속해서 새로운 웹소켓이 연결된다고 파악했다.
문제 b
- 메세지 send 시 스크롤을 최하단으로 변경하는 함수를 호출한다.
- 스크롤을 최상단으로 올릴 때 이전 메세지의 다음 페이지를 받아오는 함수를 호출한다.
- GET요청으로 이전 메세지를 받아오는 코드와 유저의 채팅 페이지 입장 후 생성되는 메세지를 주고 받기 위한 웹소켓이 하나의 컴포넌트에서 관리되면서, 파악하기 어려운 사이드이펙트가 발생해 웹소켓이 특정 상황에서 새로 생성된다고 추측했다.
근본 원인
- 위와 같이 파악한 문제를 해결하는 과정에서, 발생한 문제의 원인이 되는 정확한 코드를 찾을 수 있었다.
- new SockJS(웹소켓 URL) 객체 호출 시, 항상 새로운 웹소켓 객체를 생성한다.
- 이 코드를 컴포넌트 최상위에서 선언했기 때문에 어떤 이유로 컴포넌트가 렌더링 될 때 마다 새로운 웹소켓이 생성되고 있었다.
-
해결
문제 a
- useState 대신 useRef를 사용: 입력받은 텍스트를 onClick 시 참조해 서버로 전송하는 방식으로 input을 관리함으로써 컴포넌트의 리렌더링을 최소화했다.
문제 b
- 컴포넌트 분리: 웹소켓으로 메세지를 주고 받는 컴포넌트와, 이전 채팅 내역을 불러오는 컴포넌트를 분리해 각 컴포넌트의 역할을 나누었다.
원인 해결
- new SockJS 객체를 웹소켓 연결을 위한 useEffect 내부에서 호출하고, 해당 useEffect를 페이지 진입 시 한 번만 호출되도록 했다.