#0 개요

    구현 과정은 다음과 같다.

    1. react-hook-form을 적용하여, 채팅을 입력하지 않고 제출하는 경우에 대한 block 만들기.

    2. 정상적으로 제출시 message state에 저장되고 채팅이 출력되는 로컬 로직부터 구현

    << 구현 방식을 변경했다.

    채팅 UI 변경은 socket.on으로 서버로부터 데이터를 받을 때만. 이유는 아래와 같다

     

    3. socket emit 이벤트로 작성한 채팅을 보내는 함수만 작성

    4. Shadcn 의 Sooner를 사용해서 빈 채팅창 입력에 대한 오류 출력 UI 작성하기

    https://sonner.emilkowal.ski/

     

    Sonner

    An opinionated toast component for React.

    sonner.emilkowal.ski

    5. 메세지 전송시 해당 시간을 저장해두고, 다음 메세지를 전송하는 순간 시간을 비교해서 2초 이내이면 막는 로직 만들기

    6. 이것도 Sooner로 출력

     

    #1 react-hook-form으로 입력 컴포넌트 작성 및 Sonner 적용

    import { faPaperPlane } from "@fortawesome/free-solid-svg-icons";
    import { IconDefinition } from "@fortawesome/fontawesome-svg-core";
    import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
    import { useForm, FieldErrors } from "react-hook-form";
    import { toast } from "sonner";
    import { Socket } from "socket.io-client";
    
    interface IFormProps {
      playerNum: number;
      socket: Socket; // 부모에서 전달받을 소켓 인스턴스
    }
    interface IForm {
      chat: string;
    }
    
    function ChatForm({ playerNum, socket }: IFormProps) {
      const paperPlane: IconDefinition = faPaperPlane;
      const {
        register,
        handleSubmit,
        resetField,
        formState: { errors },
      } = useForm<IForm>();
    
      const onValidSubmit = async (data: IForm, e?: React.BaseSyntheticEvent) => {
        //local storage에서 토큰 가져오기, player num 가져오기기
        //axios api 작성성
        e?.preventDefault();
    
        try {
          // 1) 서버로 메시지 전송
          // 서버에서도 "chat" 이벤트를 수신하여 모든 클라이언트에게 broadcast하도록 구현
          const newMessage = {
            id: Date.now(), // 간단히 임시 ID
            player: playerNum,
            text: data.chat,
          };
          socket.emit("chat", newMessage);
    
          // 메시지 전송 로직 성공 시 input 비우기
          resetField("chat");
        } catch (error) {
          // 에러 처리
          console.error(error);
        }
      };
    
      const onInvalidSubmit = (errors: FieldErrors<IForm>) => {
        if (errors.chat?.type === "required") {
          // "빈 채팅을 입력할 수 없습니다" 에러 발생 시, 원하는 custom function 실행
          toast.error(errors.chat.message);
        }
      };
    
      return (
        <form
          className="w-full h-full bg-[#07070a4d] focus-within:bg-neutral-900 rounded-lg shadow-lg border-2 border-solid border-[#555555] px-4 flex items-center text-sm"
          onSubmit={handleSubmit(onValidSubmit, onInvalidSubmit)}
          action=""
        >
          <input
            {...register("chat", {
              required: "빈 채팅을 입력할 수 없습니다.",
              maxLength: {
                value: 200,
                message: "최대 200자리까지 입력할 수 있습니다다",
              },
            })}
            className="bg-transparent text-[#848484] focus:text-[#dddddd] w-full"
            placeholder="채팅 입력하기"
            type="text"
            autoComplete="off"
          />
          <div className="bg-[#848484] w-[1px] h-6"></div>
          <button className="w-6 h-6 ml-2">
            <FontAwesomeIcon icon={paperPlane} className="text-[#848484]" />
          </button>
        </form>
      );
    }
    
    export default ChatForm;

     

    onInvalid 함수로 toast를 발생시켜 UI를 띄운다.

     

    #2 Socket을 연결하기

    const socket: Socket = io("http://localhost:3000");
    
    //컴포넌트 내부
    useEffect(() => {
        socket.on("chat", (data: { id: number; player: number; text: string }) => {
          setMessages((prev) => [
            ...prev,
            {
              id: data.id,
              player: data.player,
              text: data.text,
              // 보낸 playerNum과 현재 사용자의 playerNum이 같으면 isMine = true
              isMine: data.player === currentPlayerNum,
            },
          ]);
        });
    
        return () => {
          socket.off("chat");
        };
      }, [currentPlayerNum]);

    다음과 같은 형태로 socket에서 받는 형태로 진행한다.

     

    #3 개발 완료된 UI

    1. 채팅방 입장

     채팅방 입장시 chat:room:emit 이벤트를 클라이언트 쪽에서 발생하게 했다.

    그럼 백엔드에서 해당 유저를 소켓 룸에 넣어주고, chat:room:send 이벤트를 해당 룸에 있는 사람들에게 모두 보내준다.

    chat:room:send 에는 해당 게임 방에 대한 방 번호, 제목, 유저들 등의 정보가 전달되며,

    그 정보를 바탕으로 UI를 업데이트하여 실시간으로 유저들이 방에 들어온 정보를 업데이트한다.

     

    2. 채팅 입력 로직

    채팅 입력시 chat:emit 이벤트가 발생해서 사용자가 입력한 chat string을 소켓 통신으로 보낸다.

    그럼 서버에서 채팅방 유저들에게 chat:send 이벤트를 통해 채팅 정보들을 보내준다.

    • 네이버 블러그 공유하기
    • 네이버 밴드에 공유하기
    • 페이스북 공유하기
    • 카카오스토리 공유하기