[WIL] 210816 ~ 210822 - 항해 11주차

이번 주도 살아남았다.

😇 또 일요일이다.

시간이 정말 빠르다.
팀이 꾸려진 것이 엊그제 같은데 실전프로젝트 4주차가 끝났다.
만든 것들을 보면 4주가 됐으니까 이 정도구나 싶긴 한데 그래도 시간이 너무 빠르다.

내 이번 주 개발은 둘로 나눌 수 있겠다.
기술적 도전과 짜잘이(?)

기술적 도전

1. socket.io를 이용한 채팅

중간 평가 때 채팅을 가져갈 수 있을 줄 알았는데 다른 기능들에 치여서 이번주에 구현했다.
프론트도 서버도 socket을 사용하는 것은 처음이라 생각보다 시간이 오래걸렸고, 예상치 못한 문제들도 있었다.


1. 방마다 채팅이 따로 존재해야 했다.
우리 협업돼지의 주요 개념 중 하나가 '방'이다. 여러개의 협업 방을 만들 수 있고, 그 방마다 대시보드, 문서, 칸반, 타임라인(캘린더)를 제공한다.
당연히 채팅도 방마다 따로 제공되어야 했다.

다행히도 socket.io에서 room이라는 개념이 있어, 어떤 협업 방에 들어갈 때마다 roomId, userId와 같은 정보를 서버로 보내서 해당 roomId에 user가 참여하는 방식으로 구현할 수 있었다.


2. 코드를 어디다 놓아야 하는 걸까
api는 쓰는 곳에서 사용하면 된다.
그래서 socket 코드도 처음에는 협업 방 코드 안에 두었다.

그랬더니 방에 들어갈 때마다 connect를 하고 나올 때마다 disconnect를 했다.
이건 아니라는 생각이 들어서 connect, disconnect는 가장 상위 컴포넌트인 App.js로 옮겼다.

그리고 각 room에 join하고 leave하는 이벤트를 명확하게 설정했더니 일단 socket은 이어졌다.

그 다음은 방에 왔다갔다 하는 만큼 배수로 event가 발생했다는 것이다.

재현해보면 다음과 같다.

  1. 아무 방에 다섯 번 들어갔다 나왔다를 반복한다. (매번 방이 바뀌어도 상관 없다.)
  2. 여섯 번째로 아무 방에 들어가서 채팅창을 연다.
  3. '안녕하세요'라고 입력하고 제출한다.
  4. '안녕하세요'가 여섯 번 나타난다.
  5. DB에 '안녕하세요'는 한 번만 있다.

이해를 할 수가 없었다.
대체 왜...?

이유는 room에 join하는 횟수만큼 메시지를 받아보고 있었기 때문이다.

export const subscribeToChat = (cb) => {
  if (!socket) return;
  socket.on("message", (data) => {
    return cb(null, data);
  });
};

이런 함수가 있다.
서버에서 "message"라는 key로 나에게 정보를 보내주므로 그걸 받아보기 위한 함수다.

이 key는 사실 하나의 room에 종속되는 것이 아니라 socket 전역적으로 약속한 key다.

그런데 message를 구독하는 함수를 room 안에 들어갈 때마다 실행하도록 코드를 만들었으니, 누가 메세지 하나라도 치면 n번 보이는 것이 당연한 일이었다.

이 역시 App.js로 옮겨서 딱 한 번만 구독하도록 만들어서 해결했다.


3. 근데 왜 정보를 자꾸 새로 불러올까
방 목록에서 방으로 들어가면 해당 socket room으로 연결됨과 동시에 그 room의 기존 메시지들을 받아오도록 되어있다.

근데 왜 이 값을 자꾸 두 번씩 가져오는 걸까?
기존 메시지들이 n번 렌더되는 이유가 뭘까

아마 부모 컴포넌트에서 렌더가 여러번 되어서가 아닐까 싶었다.

하지만 이 복잡한 워크스페이스 안에서 무엇을 해야 이 채팅창에 렌더를 덜 할 수 있을지 도무지 생각이 나지 않았다.

그래서 돌렸다. 비둘기 목.

  useEffect(() => {
    getMessages((err, data) => {
      if (err) {
        console.log(err);
        return;
      }
      if (prevRoomId !== roomId) {
        dispatch(setPrevRoomId(roomId));
        dispatch(loadMessages(data));
        return;
      }
    });
  }, [prevRoomId, roomId, dispatch]);

roomId가 기존 roomId와 같을 때는 제발 더 불러오지 말라고 설정했다.

추가적으로 리덕스 모듈에서도 로드된 기존 채팅들을 배열에 추가하지말고 바꿔넣으라는 코드로 변경했다.

[LOAD_MESSAGES]: (state, action) =>
  produce(state, (draft) => {
    draft.messages = action.payload.messages;
  }),

사실 이 점은 채팅에 무한 스크롤을 적용한다면 다시 바꿔야 해서 고민이 많다.

이전 정보들을 스크롤 올릴 때마다 가져올텐데 배열을 바꿔치기 할 수는 없으니까...

이렇게 글을 쓰다보니 더 상위에서 메시지를 가져와볼까 하는 생각이 들기는 한다.
하던 것 마무리하고 테스트해봐야겠다.

4. 어쨌든 돌아간다.

처음 네트워크탭에서 이걸 봤을 때의 희열이 잊히지 않는다.
더 이상 서버에서 뭘 가져오는 것에 놀랍지도 신기하지도 않았는데, socket은 또 다른 영역이었다.
내가 request를 보내지 않아도 서버가 말을 걸어 준다.
얼마나 성공적인지는 모르겠지만 일단은 아주 새로운 것을 해냈다는 기쁨이 크다.
조금 행복😆

2. resize 되는 메뉴

이번에는 디자이너 분들이랑도 함께하는 프로젝트다 보니 피그마를 적극 사용하고 있다.
단순히 디자인 공유가 아니라 디자이너와 개발자 간 Q&A도 피그마에서 하고 있다.

그런데 지난주 쯤 피그마에 이런 질문이 올라왔다.

전혀 해 본 적이 없고 감도 오지 않았지만, 내가 사용자라도 저 기능이 있다면 좋겠다는 생각이 강하게 왔다.
그래서 도전하기로 했다.

막상 검색해보니 꽤 그럴듯한 패키지가 많았다.

  • react-resize-panel
  • react-resize-observer(react-resize-detector)
  • react-resizable (위클리 다운로드가 무려 27만이다)
  • react-rnd

등등

하나씩 테스트 해봐는데 나에게 가장 알맞는 패키지는 react-rnd였다.

딱 필요한 부분만 사용하고 나머지는 disable 시키는 것이 매우 간단했다.

// ResizeWidth.jsx

......

// initialSize = {width: "260px", height: "100%"}
// drag : "right" or "left"
// option : min·max Width/Height
// storeSaveFunc : dispatch할 actionCreator
const ResizeWidth = ({
  size,
  handleSize,
  drag,
  option,
  storeSaveFunc,
  children,
}) => {
  const dispatch = useDispatch();

  const onResize = (event, dir, refToElement, delta, position) => {
    handleSize(refToElement.style.width.split("px")[0]);
  };

  const onResizeStop = (event) => {
    dispatch(storeSaveFunc(size.width));
  };

  return (
    <Rnd
      default={{
        x: drag === "right" ? 0 : null, 
        y: 0,
        width: size.width,
        height: size.height,
      }}
      resizeGrid={[1, 1]}
      disableDragging={true}
      bounds="window"
      enableResizing={{
        top: false,
        bottom: false,
        right: drag === "right" ? true : false,
        left: drag === "left",
      }}
      onResize={onResize}
      onResizeStop={onResizeStop}
      style={{ zIndex: 25 }} // var가 안됨
      {...option}
    >
      {children}
    </Rnd>
  );
};

이렇게 컴포넌트를 만들어서 더 많이 사용해보려고 했는데 사실 딱 디자이너님이 말씀하신 곳에만 썼다.
다른 곳을 하기엔 CSS 바꾸는데 시간이...😇

✨결과물✨

내가 해놓고도 신기해서 보는 사람마다 자랑하고 있다.
사실 채팅이 훨씬 어려웠는데 뭔가 이게 더 자랑하고 싶다🙄

이 경험으로 얻은 것은 두 가지다.
1. 생각보다 별별 패키지가 다 있다. 무슨 기능을 만들 때 일단 검색하자.
2. 사용자를 위해 조금 더 생각해보자. 모양새와 편리함 두 가지를 잡는 방법이 아마 있을 것이다🙂

짜잘이(?)

우리 프론트 팀원 분 중 한명 曰,

만들어놓은 기능들을 하나하나 디테일을 넣다보니 이 얘기가 나왔다.

새로운 기능을 만들 때는 뭔가 코드도 길고 몇 시간이 지나보면 눈에 띄는 변화가 있었다면,
지금은 몇 시간을 들여서 정말 많은 것을 수정했는데도 티가 손톱만큼도 나지 않는다.

하지만 막상 사용자가 볼 때는 그 자잘한 요소들이 제대로 안되어 있는 것을 가장 거슬려 하는 것 같다.
그래서 프론트엔드 개발자는 꼼꼼해야 한다는 얘기를 짜잘이로 하게 된 것이다🥴

이번주는 이런 짜잘이들의 연속이었다.
이런 것들은 사실 하루 종일 보고 있으면 거슬리지도 않기 때문에 '나는 이 사이트를 처음 들어온다. 나는 유저다.'는 암시를 걸면서 한다.

아직도 진행중이지만 조금 지치긴 한다.
가끔 대충 넘어가고 싶기도 하다.

그래도 내가 유저라면 분명 불만일테니까 해야지.. 운명이다..

🚢 총 회고

어느 새 항해도 끝이 보인다.
99일이 언뜻 짧아보이지만 어쨌든 내 취업준비의 시작을 3개월 이상 늦춰야 한다는 것에 엄청난 불안에 잠을 못자기도 했고, 취소를 고민도 했다.
물론 항해 시작 전 이야기다.

항해를 시작하고서는 그렇게 생각할 새가 없었다.
휘몰아치듯 과제를 하나 끝내면 다음 과제가 내 눈 앞에 있었다.

생각할 여유가 다시 조금 생겼지만 불안하지는 않다.

항해에 오기 전 강한 불안감을 조성한 것은 크게 네 가지였던 것 같다.
1. 팀 프로젝트 경험이 없다.
2. 스스로 개발자로 일할 수 있다는 확신이 없다.
3. 세상에 나 혼자 떨어진 것마냥 아는 개발자가 없다.
4. 지식이 자꾸 휘발된다.

솔직하게 모든 사람에게 항해를 추천할 수는 없을 것 같다.

우리나라 교과과정도 누군가에게는 적합하지만 누구한테는 지옥과 같은 시간이듯, 모두에게 딱 맞는 과정이라는 것은 없을 것이다.

하지만 1. 서울에 살지 않고, 2. 혼자 공부해 본 경험이 있으며, 3. 아는 개발자가 없어서 개발 얘기를 할 사람이 없는, 4. 협업을 해보고 싶은 예비 개발자라면 일단은 괜찮지 않을까 생각한다.

어지간한 오프라인 부트캠프는 서울에 있어서 오고가는 것만 생각해도 머리가 아팠다.

항해의 서포트를 강하게 느껴본 적은 없지만
그냥 이 흐름이 나에게 꽤 잘 맞았고, 운 좋게 좋은 사람들을 많이 만났던 것이 내가 지난 70여 일을 후회하지 않는 이유다.

모든 것은 운이다.
첫주차 프로젝트에서 팀장이 되었던 것 마저 지금은 좋은 기회였다고 생각한다.

남은 날들도 하루하루 좋은 기회였다고 생각되도록 더 열심히 잘 해내고 싶다.

좋은 웹페이지 즐겨찾기