본문 바로가기
개발/React

[React, Typescript] 채팅 프론트엔드 구현 (3편)

by 세크레투스 2024. 8. 1.
반응형
SMALL

이미지 가로 싱크가 안맞는데, 기존에 개발한 프로젝트에선 프로필 사진이 보이는 채팅창을 구현했는데, 보안상 잘라냈기 때문이다. (기존 프로젝트 실행화면임)

 

오늘은 마지막 단계인,

5. 채팅 데이터에서 채팅 내용이 개행됐을 경우, <br/>처리를 통해 화면에서도 개행 처리 해줌.
6. 조합문자(아직 완성되지 않은 문자, 예를 들어 한글, 중국어, 일본어 등)을 사용할 경우, 채팅 입력 시 마지막 글자가 중복 처리되는 것을 막기 위해, 조합상태를 감지하는 로직 구현.

부분을 구현하는 과정을 포스팅하려고 한다.

 

5. 채팅 데이터에서 채팅 내용이 개행됐을 경우, <br/> 처리를 통해 화면에서도 개행 처리 해줌.

messages 상태변수의 채팅 데이터에서 채팅 내용이 개행됐을 때, '\n'을 <br/> 태그로 변경처리하여 실제 채팅 컴포넌트 화면에서도 개행처리가 될 수 있도록 설정해줄 것이다.

ChattingComponent.tsx 파일에 formatMessage라는 메세지 포멧팅 함수를 정의한다.

import React, {useState, useRef, useEffect} from "react";

function ChattingComponent({nickname, chatting, time, isMe}: any) { //props로 받아올 데이터
  ...
  
  //메세지 포맷팅 함수 정의 (개행 문자를 <br/>로 변환)
  const formatMessage = (text: string) => {
    return text.split("\n").map((line, index) => (
      <React.Fragment key={index}>
        {line}
        <br />
      </React.Fragment>
    ));
  };
  
  return ( ... );
}

export default ChattingComponent;

 

이제, return 구간에 chatting props를 방금 만든 formatMessage() 함수 안에 넣어서 실제 화면에서 개행처리 될 수 있게 처리해준다.

import React, {useState, useRef, useEffect} from "react";

function ChattingComponent({nickname, chatting, time, isMe}: any) { //props로 받아올 데이터
  ...
  
  //메세지 포맷팅 함수 정의 (개행 문자를 <br/>로 변환)
  const formatMessage = (text: string) => {
    return text.split("\n").map((line, index) => (
      <React.Fragment key={index}>
        {line}
        <br />
      </React.Fragment>
    ));
  };
  
  return (
  	<>
    	{/* 채팅 입력한 사람이 나(본인)인 경우 : 채팅 입력한 사람이 타인인 경우 */}
        {isMe ? (
        {/* Tailwind CSS에서 justify-end는 오른쪽 정렬을 뜻함. */}
        <div id="me" className="flex justify-end my-5">
          <div id="time" className="flex flex-col justify-end mr-3">
            <p className="text-xs text-black">{time}</p>
          </div>
          <div className="mr-3">
            <div id="nickname" className="flex justify-end">
              <p className="text-xs text-white">{chatNick}</p>
            </div>
            <div id="chatting" className="p-3 bg-white rounded-md mt-2">
              <span className="text-xs text-black">
                {formatMessage(chatting)}
              </span>
            </div>
          </div>
        </div>
      ) : (
      	{/* CSS 기본정렬은 왼쪽이므로, justify-end 삭제. */}
        <div id="you" className="flex my-5">
          <div className="ml-3">
            <p id="nickname" className="text-xs text-white">
              {chatNick}
            </p>
            <div id="chatting" className="p-3 bg-white rounded-md mt-2">
              <span className="text-xs text-black">
                {formatMessage(chatting)}
              </span>
            </div>
          </div>
          <div id="time" className="flex flex-col justify-end ml-3">
            <p className="text-xs text-black">{time}</p>
          </div>
        </div>
      )}
    </>
  );
}

export default ChattingComponent;

완성이다.

 

6. 조합문자(아직 완성되지 않은 문자, 예를 들어 한글, 중국어, 일본어 등)을 사용할 경우, 채팅 입력 시 마지막 글자가 중복 처리되는 것을 막기 위해, 조합상태를 감지하는 로직 구현.

우선, 조합상태감지 로직을 구현하기 전, 조합문자가 무엇인지 설명이 필요할 것이다.

조합문자란, 아직 완성되지 않은 문자로, 여러 키 입력이 조합되어 최종문자가 만들어지는 과정을 뜻한다.

조합문자의 대표적인 예로는 한글, 중국어, 일본어가 있다.

 

조합상태감지 로직이 필요한 이유는, 영어와 같은 알파벳 문자는 받침이 없기 때문에 알파벳 입력 시 바로 문장이 마무리 되지만,

조합문자의 경우 받침이 있기 때문에, 예를 들어서 한글을 입력했을 때 문장이 완전히 끝났다고 판단하지 않기 때문이다.

 

그래서, 알파벳을 입력했을 때완 다르게, 조합문자를 입력 후 엔터를 누르면 맨 마지막 글자가 한번 더 중복되는 현상을 확인할 수 있다.

이를 위해 조합상태감지 로직을 구현해줘야 한다.

 

Home.tsx 파일에 조합상태감지를 위한 상태변수와 함수를 useState로 정의하고, 이를 각각 handleCompositionStart 함수와 handleCompositionEnd 함수에 적용시켜준다.

그리고 이 두 함수를 textarea의 onCompositionStart 이벤트와 onCompositionEnd 이벤트에 각각 적용시켜준다.

// Home.tsx
function Home() {
	...
    const [isComposing, setIsComposing] = useState<boolean>(false);
    ...
    //입력 구성 시작 핸들러
    const handleCompositionStart = () => {
      setIsComposing(true);
    };

    //입력 구성 끝 핸들러
    const handleCompositionEnd = (
      e: React.CompositionEvent<HTMLTextAreaElement>
    ) => {
      setIsComposing(false);
      setInput(e.currentTarget.value);
    };
    
    return (
    	...
            <textarea
              className="w-full h-12 mx-3 focus:outline-none resize-none flex-1"
              style={{ resize: "none" }}
              placeholder="메세지를 입력해주세요."
              onKeyDown={handleKeyDown}
              onKeyUp={handleKeyUp}
              onCompositionStart={handleCompositionStart}
              onCompositionEnd={handleCompositionEnd}
            />
            <button onClick={handleSendMessage}>
              입력
            </button>
        ...
    )
}​

 

여기까지 마쳤으면 우리가 원하는 완벽한 형태의 채팅창을 구현할 수 있다.

 

채팅창 포스팅은 여기서 마치도록 하겠다.

끝..!

반응형
LIST