[인스타그램 클론코딩] 12. 메신저 기능 구현(Front-End)

2023. 2. 16. 15:30·Web/인스타 클론 코딩

프론트는 갈수록 점점 개판이 되어간다.

프론트에서 stomp로 구독후 채팅내용을 수정하면 계속 서버로 info정보를 요청하던데 왜그런지 문제를 찾지 못해서 오래걸렸다.

 

1. ChatRoomList 작성

const ChatRoomList = () => {

    const [chatRoomList, setChatRoomList] = useState();
    const [chatRoom, setChatRoom] = useState();
    const [targetEmail, setTargetEmail] = useState();
    const [clickState, setClickState] = useState();

    const onChatRoom = (idx) => {
        setChatRoom(chatRoomList[idx].chatRoomId);
    }
    const onClickChatRoom = (idx) => {
        withJwtAxios.get("/chats/create", {params: {target: chatRoomList[idx].email}})
            .then((res) => {
                let list = res.data.chatRoomList;

                setChatRoomList(list);
                setChatRoom(list[idx].chatRoomId)
                setTargetEmail(list[idx].email)
            })

        let len = clickState.length;
        let temp = new Array(len).fill(false);
        temp[idx] = true;
        setClickState(temp);
    }

    useEffect(() => {
        withJwtAxios.get("/chats/user-list")
            .then((res) => {
                setChatRoomList(res.data.chatRoomList);
                setClickState(new Array(res.data.chatRoomList.length).fill(false));
            });
    }, [])

    return (
        <div className={style.container}>
            <div className={style.chat_room_list_container}>
                {chatRoomList != undefined ?
                    chatRoomList.map((chat, idx) => {
                        return(
                            <div key={idx} className={style.list_container} onClick={() => onClickChatRoom(idx)} style={clickState[idx] ? {backgroundColor: '#dadada'} : {backgroundColor: 'white'}}>
                                <img src={Static_Base_Url + chat.profileUrl} className={style.thumbnail}/>
                                <div className={style.content}>
                                    <div className={style.nickname}>{chat.nickname}</div>
                                    {clickState[idx] ? <></> : <div className={style.last_message}>{chat.lastMessage}</div>}
                                </div>
                            </div>
                        )
                    })
                    :
                    <div></div>
                }
            </div>
            <div className={style.chat_room_container}>
                <ChatRoom chatRoomList={chatRoomList} chatRoom={chatRoom} targetEmail={targetEmail}/>
            </div>
        </div>

    );
};

export default ChatRoomList;

 

- 이 컴포넌트는 채팅 유저 목록을 랜더링해주는 함수이다.

- 특정 채팅방을 선택하면 채팅방 Id와 채팅유저를 props로 넘겨준다.

 

2. ChatRoom.js 작성

const ChatRoom = (props) => {

    const chatRoomId = props.chatRoom;
    const targetEmail = props.targetEmail;
    const [inputs, setInputs] = useState("");
    const [chatting, setChatting] = useState();
    const [chatList, setChatList] = useState();
    const userDetail = useSelector(state => state.userDetail);
    const stomp = useRef({});
    const sock = new SockJs("http://localhost:8080/api/socket");

    stomp.current = Stomp.over(sock);
    const headers = {
        'Authorization': localStorage.getItem("accessToken")
    }

    const onSubmit = () => {
        let value = JSON.stringify(inputs);
        value = value.slice(1, value.length-1)

        if(value) {
            const data = {
                chatRoomId: chatRoomId,
                message: inputs
            }
            withJwtAxios.post("/chats/chat", data)
                .then((res) => {
                    setChatList(res.data.chatList);
                });

            stomp.current.send("/publish/messages", headers, JSON.stringify({
                message: inputs,
                senderEmail: userDetail.email,
                receiverEmail: targetEmail,
                chatRoomId: chatRoomId
            }), (res) => {
                console.log(res.data.body);
            })

            setInputs("");
        }
    }

    const onKeyPress = (e) => {
        if(e.key == 'Enter') {
            onSubmit();
        }
    }

    useEffect(() => {
        if(chatRoomId != undefined) {
            withJwtAxios.get("/chats/chat-list", {params: {chatRoomId: chatRoomId}})
                .then((res) => {
                    setChatList(res.data.chatList);
                })

            stomp.current.connect(headers, () => {
                stomp.current.subscribe('/subscribe/rooms/' + chatRoomId,  (res) => {
                    const obj = JSON.parse(res.body)
                    // console.log(obj)
                    setChatting({
                        email: obj.email,
                        nickname: obj.nickname,
                        profileUrl: obj.profileUrl,
                        content: obj.content
                    })
                }, headers)
            });
        }
    }, [chatRoomId])

    useEffect(() => {
        if(chatList != undefined) {
            let tmp = [...chatList];
            tmp.push(chatting);
            setChatList(tmp);
        }
    }, [chatting])

    const onChange = (e) => {
        setInputs(e.target.value);
    }

    return (
        <>
            <div className={style.chat_body_container}>
                <div>
                    {chatList != undefined ? chatList.map((chat, idx) => {
                        if(chat.email === userDetail.email) {
                            return(
                                <div key={idx} className={style.my_chat}>
                                    <div className={style.my_chat_box}>
                                        {chat.content}
                                    </div>
                                </div>
                            )
                        } else {
                            if(idx > 0) {
                                return(
                                    <div key={idx} className={style.oppo_chat}>
                                        {chatList[idx-1].email !== chat.email ?
                                            <img className={style.thumbnail} src={Static_Base_Url + chat.profileUrl} /> :
                                            <div className={style.thumbnail_blank}></div>}
                                        <div className={style.oppo_chat_box}>
                                            {chat.content}
                                        </div>
                                    </div>
                                )
                            } else {
                                return(
                                    <div key={idx} className={style.oppo_chat}>
                                        <img className={style.thumbnail} src={Static_Base_Url + chat.profileUrl} />
                                        <div className={style.oppo_chat_box}>
                                            {chat.content}
                                        </div>
                                    </div>
                                    )
                            }

                        }


                        }) :
                    <div></div>
                    }
                </div>
            </div>
            <div className={style.input_container}>
                <input className={style.input} value={inputs} onChange={onChange} onKeyPress={onKeyPress}/>
                <button className='btn btn-outline-primary' onClick={onSubmit}>보내기</button>
            </div>

        </>
    );
};

export default ChatRoom;

 

- 랜더링 되면, SockJs로 백엔드 서버와 소켓 연결을 한 후 채팅방 ID정보를 토대로 구독을 진행한다.

- 그리고 stomp의 send메서드로 내용을 보내고, subscribe에 구독 후 메세지 응답이 발생 했을 때의 로직을 설정해준다.

 

3. 동작모습

코드는 간단한데, 전혀 처음 접하는 개념이라 좀 이상할 수도 있다.

 

 

저작자표시 비영리 변경금지 (새창열림)

'Web > 인스타 클론 코딩' 카테고리의 다른 글

[인스타그램 클론코딩] 13. 게시글 검색 기능 구현(Front-End)  (0) 2023.02.16
[인스타그램 클론코딩] 13. 게시글 검색 기능 구현(Back-End)  (0) 2023.02.16
[인스타그램 클론코딩] 12. 메신저 기능 구현(Back-End)  (0) 2023.02.16
[인스타그램 클론코딩] 11. 프로필 수정 기능 구현(Front-End)  (0) 2023.02.07
[인스타그램 클론코딩] 11. 프로필 수정 기능 구현(Back-End)  (0) 2023.02.07
'Web/인스타 클론 코딩' 카테고리의 다른 글
  • [인스타그램 클론코딩] 13. 게시글 검색 기능 구현(Front-End)
  • [인스타그램 클론코딩] 13. 게시글 검색 기능 구현(Back-End)
  • [인스타그램 클론코딩] 12. 메신저 기능 구현(Back-End)
  • [인스타그램 클론코딩] 11. 프로필 수정 기능 구현(Front-End)
뚝딱뚝딱2
뚝딱뚝딱2
  • 뚝딱뚝딱2
    개발도상국
    뚝딱뚝딱2
  • 전체
    오늘
    어제
    • 분류 전체보기
      • 공부
        • Java
        • Spring Boot
        • LORA
      • Web
        • 인스타 클론 코딩
        • GPT 응답 API 서버
        • Spring Boot 예외 처리
        • 코테 준비용 서비스 만들기
      • DevOps
        • 쿠버네티스
        • 서버 만들기
      • 코딩테스트
        • 알고리즘
      • 교육
        • 스파르타코딩클럽 - 내일배움단
        • 혼자 공부하는 컴퓨터 구조 운영체제
      • 잡다한것
  • 블로그 메뉴

    • 홈
  • 링크

    • GITHUB
  • 공지사항

  • 인기 글

  • 태그

    예외
    티스토리챌린지
    MSA
    chat GPT
    스프링부트
    오블완
    Java
    REST API
    OpenAI API
    mapstruct
    클론코딩
    쿠버네티스
    react
    인스타그램
    클러스터
    스프링 부트
    Entity
    백준
    spring boot
    리액트
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.1
뚝딱뚝딱2
[인스타그램 클론코딩] 12. 메신저 기능 구현(Front-End)
상단으로

티스토리툴바