22.12.4

커피 내리는 그냥 사람·2022년 12월 4일
0

항해99

목록 보기
75/108

이번주 기술적으로 막혔던 부분

TIL 및 깃허브를 참고해주시면 감사하겠습니다. TIL 모음집이라 생각하고 작성하겠습니다.

1. stomp를 활용한 실시간 채팅

초기 단계

  • 실시간 채팅 기능 구현 완료(채팅 기능 확인 : 사파리 * 크롬 다른 아이디로 1개의 채팅방 임의 설정 후 실행)
  • 구현 스크린샷(핑퐁식으로 주고 받아지는 것 잘 되는지 확인)

  • 좌 사피리 우 크롬
  • 이후 작업 : 입력 후 input창 비워지는 것까지 확인(input에 value값 준 뒤 useState 값 "")
  • 주요 코드
(chatting.jsx : 채팅 주요 로직)
const sock = new SockJS("http://3.38.228.74:8080/ws/chat");
  const ws = webstomp.over(sock);
  const dispatch = useDispatch();
  
  const listReducer = useSelector((state) => state.chatting.chatList);
  console.log(listReducer);
  // let postId = Number(id) : 임의값이라 일단 주석처리

  useEffect(() => {
    dispatch(__getinitialChatList({
      postId:10,
      roomId:1
    }));
    // 임의의 유저와 룸넘버

    return () => {
      onbeforeunloda()
      
    };
  
  }, []);
  
  useEffect(() => {
  
  
    wsConnectSubscribe()
    
    
    // return () => {
    //   console.log("???????")
    //   onbeforeunloda()
      
    // };
 
  }, [listReducer.id]);
  
  const [chatBody, setChatBody] = useState("");

  const content = {
    sender: "보내는이",
    message:chatBody
    // 임의의 센더와 useState값의 챗바디
    };

  let headers = { 
    Access_Token: localStorage.getItem('Access_Token')
  };
// 토큰값이 있어야 채팅이 가능하게 함

  function wsConnectSubscribe() {
    try {
      ws.connect(
        headers,(frame) => {
          ws.subscribe(
            `/sub/1`,
            // 데스티네이션
            (response) => {
              console.log("어떻게 나오는지",response)
              let data = JSON.parse(response.body)
              dispatch(chatList(data))
// 값이 어떻게 넘어오는지 보는 로직
            }
            );
        },
      );
    } catch (error) {
    }

  }
  

  function waitForConnection(ws, callback) {
    setTimeout(
        function () {
            // 연결되었을 때 콜백함수 실행
            
            if (ws.ws.readyState === 1) {
                callback();

                // 연결이 안 되었으면 재호출
            } else {
                waitForConnection(ws, callback);
            }
        },
        1 // 밀리초 간격으로 실행
    );
}
//stomp 메시지 에러 waitForConnection함수로 해결


const onbeforeunloda = () =>{

  try {
    ws.disconnect(
      ()=>{
        ws.unsubscribe("sub-0");
        clearTimeout(
          waitForConnection
          )
      },
    
    {Access_Token: localStorage.getItem('Access_Token')}
)
    }catch (e){
      console.log("연결구독해체 에러",e)
  }
}

  const inputHandler = (e) =>{
  setChatBody(e.target.value)
}
  // 연결 해제 시 로직

  //onSubmitHandler
const onSubmitHandler = (event) =>{
  //event.preventDefault()
  // if (chatBody=== "" || chatBody === " ") {
  //   return alert("내용을 입력해주세요.");
  //   }
    waitForConnection(ws,function() {   
  ws.send(
    `/pub/1`,
    JSON.stringify(content),
            {
              Access_Token: localStorage.getItem("Access_Token")
            },
          )})
  setChatBody("")
}
const appKeyPress = (e) => {
  
  if (e.key === 'Enter') {
    onSubmitHandler()
    setChatBody("")
  }
}
//enter시 메시지 보냄
const scrollRef= useRef();

useEffect(() => {
  if (scrollRef) {
    scrollRef.current.scrollIntoView({
      behavior: "smooth",
      block: "end",
      inline: "nearest",
    });
  }
}, [listReducer]);

중반 단계

1. 채팅방 트러블슈팅 :

  • 데스티네이션과 실제 만든 새로운 채팅방 불일치(이로 인해 실시간 채팅도 불가)
    ⇒ (해결방안) 로컬스토리지에 값을 저장하여 그 값을 url로 임포트 해오는 방식으로 해결
(postDetail.jsx)
// 채팅방 개설

  useEffect(()=>{
    localStorage.setItem("roomId", postChat)
  }, [postChat])
// postChat 선언한 것이 변할 때마다 셋아이템 유즈이펙트

const onClickChatting = () =>{
  dispatch(__CreateRoom({
    postId:post.postId,
    // 페이로드로 넘겨주는 건 postId만
  }));
  setTimeout(
    function () {
        // 만들어진 채팅방으로 이동하는 로직 => localStorage 활용한 방법 이용
      	// localStorage에 저장해서 데스티네이션, url 등을 맞춰서 방 개설하는 방법 활용
        // 연결되었을 때 콜백함수 실행
        navigate(`/chatting/${localStorage.getItem("roomId")}`);
    },
    300 // 밀리초 간격으로 실행
  );
}

2. 채팅방 CSS

트러블슈팅 : 각종 아이콘(svg) 이동, 말풍선 좌우 정렬 이슈가 있었다.
1) 말풍선 좌우 정렬 : 우측으로 본인 글 옮기는 로직

(Chatting.jsx)
{listReducer.joinUserNickname === item.sender ?
                (<ChatStyle key={uuidv4()} style={{textAlign:"right"}}>
                  {/* 인라인 요소로 바로 우측 정렬 해결 */}
                  <TimeDiv>
                  <TimeSpan>{item.sendDate}</TimeSpan>
                <JoinUserNickname>{item.message}</JoinUserNickname>
                </TimeDiv>
                </ChatStyle>) :
                 
                (<ChatStyle key={uuidv4()}>
                   <TimeDiv>
                    <PostUserNickname>{item.message}</PostUserNickname>
                    <TimeSpan>{item.sendDate}</TimeSpan></TimeDiv>
                </ChatStyle>)
          }

2) 종이비행기 아이콘 정렬하기

<InputDiv>
          <Input value={chatBody} onChange={inputHandler} onKeyPress={appKeyPress} placeholder="댓글을 입력하세요"/>
                    {/* value를 줘야 사라진다 */}
          <Send onClick={onSubmitHandler} style={{position:"fixed", left:"calc(50% + 140px)", transform:"translateX(-50%)", bottom:25}}/>
                    {/* calc로 계산해서 반응형에 맞춰도 움직이지 않는 그림 만든다. svg는 컴포넌트처럼 임포트가 가능하다 */}
        </InputDiv>

후반 단계

- ***트러블슈팅 : 채팅방을 새로 개설한 다음 아무 말도 안 치고 나갔을 때 채팅방 리스트 전체가 보이지 않는 현상

⇒ 조건부 렌더링으로 length > 0일 때 보이게 하고 아닐 때(채팅방이 정상적이지 않을 때) 아예 null 처리를 해주는 방식으로 설정***```javascript

(chatList.jsx)
// 채팅방 list
<Div onClick={(e) => e.stopPropagation()}>
        {Room !== undefined &&
          Room !== [] &&
          Room.map((item, i) => {
            return (
              <div key={i}>
                {item.chatList.length > 0 ? (<>
                // 조건이 0보다 커야만, 즉 채팅이 있어야만 채팅방이 개설되는 로직
                <RoomList onClick={() => onClickChatting(item)}>
                  <Profile>
                <div><img src={item.postUserAvatarUrl} style={{ width: 44, height: 44, borderRadius:"50%"}}/></div>
               <div>
                
               <div>{item.postUserNickname} <Time>{item.chatList[item.chatList.length-1].sendDate}</Time></div>
                <Message>
                {item.chatList[item.chatList.length-1].message}
                // 스몰 이슈: 
                {/* 대화의 맨 마지막만 가지고 오는 로직이면 위와 같이 진행하면 됨 */}
                </Message>                
                </div>
                </Profile>
                <SellImg>
                {item.image !== undefined && (
                <>
                <img src={item.image.imgUrl} style={{ width: 44, height: 44}} />
                </>)}
                </SellImg>
                </RoomList>
                </>): null}
              </div>
            );
          })}

      </Div>

2. iphone에서 게시물이 업로드 되지 않았던 현상

트러블슈팅 : iphone에서 글이 업로드가 안 되는 현상

  • 원인 : 아이폰 & 맥북 사파리 연결 통해 로그 살펴보니 postList(이슈는 objectionList)에서 undefined에서 객체를 뽑아오려고 했음
  • 해결 1 : 먼저 옵셔널 체이닝으로 문제된 줄만 처리 => 실패(전역적으로 관리가 필요했다.)
  • 해결 2 : 먼저 posts라고 useSelector로 가져온 요소를 useEffect를 줘서 log를 찍어봄 => 빈 배열이라 posts.length > 0 && 처리 => 실패(내부에 map 돌린 데이터 중 images라는 배열이 또 있었음)
  • 해결 3 : 일단 전체 조건부 렌더링은 두고 이미지를 둘러싼 조건부 렌더링을 거둔 다음 옵셔널 체이닝 => 실패(아무래도 전체를 다 옵셔널 체이닝을 걸어야겠다 싶었음)
  • 해결 4: post라고 map 돌린 모든 부분 옵셔널 체이닝 => 게시물이 써지긴 하지만 새로고침을 하지 않으면 랜더링이 안 되는 현상 발생, 부분 성공
  • 최종 성공 : props로 navigate하고 있던 부분을 extrareducer에서 window.locate.replace 처리 해줘서 성공

(부분 코드)

(PostList.jsx)
{posts.length > 0 &&
  // 전체 조건부 렌더링
          posts.map((post, index) => {
            return (
              <div
                className="bg-white flex p-[18px] border-b-[0.5px] border-D9"
                key={index}
                onClick={() => {
                  onClickHandler(post.postId);
                }}
              >
               
                  <img
                    className="object-cover  min-w-[84px] w-[84px] h-[84px]  rounded"
                    src={post?.images[0]?.imgUrl}
                  />
					// 요소별 옵셔널 체이닝

                <div className=" w-full flex-col  ml-3 font-medium">
                  <div>
                    <label>{post?.title}</label>
                  </div>
                  <div className=" mt-1 text-base font-semibold">
                    <label>{post?.userPrice.toLocaleString("ko-KR")}</label>
                  </div>
                  <div className="flex justify-between mt-4  text-xs font-normal">
                    <div>{post?.createdAt}</div>
                    <div className="flex">
                      <img src={mainHeart} />
                      <div>{post?.likeCnt}</div>
                    </div>
                  </div>
                </div>
              </div>
            );
          })}
(PostsSlice.js)
//__addPost
    [__addPost.pending]: (state) => {
      state.isLoading = true;
    },
    [__addPost.fulfilled]: (state, action) => {
      state.isLoading = false;
      state.posts.push(action.payload);
      window.location.replace("/postread/all")
      // replace 활용한 새로고침 및 렌더링
    },
    [__addPost.rejected]: (state, action) => {
      state.isLoading = false;
      state.error = action.payload;
    },
profile
커피 내리고 향 맡는거 좋아해요. 이것 저것 공부합니다.

0개의 댓글