실전 프로젝트 최종 점검(2)

이수진·2023년 1월 5일
0

항해

목록 보기
14/15

(8주차 ~ 11주차)

배포가 눈 앞에...!

끝나지 않을 것 같던 항해의 마지막 실전프로젝트도 끝을 향해 달려가고 있다. 확실히 디자이너 분들과 협업을 진행하다보니 디자인을 위한 디자인에 의한 기능들을 빼놓을 수가 없었다. 가끔 이렇게 까지 해야할까라는 생각이 들때가 있지만, 프론트 개발자라면 이 모든 과정도 사용자를 위한 배려이자 서비스 정신이겠다는 결론이다. 기본적인 MVP 기능을 완성한 뒤 디자이너분들과 피드백을 주고받으며 기능을 추가하거나 디버깅을 진행하고 있다.

10주) 디버깅 및 채팅 기능 확장

피드백을 진행하면서 정말 다양한 에러와 마주했다. 예외처리를 하는 일도 정말 손이 많이가는 일이라는 걸 느꼈다. 그러면서 에러 바운더리에 관한 내용을 접하게 되었는데, 최근 에러 처리에서 중요한 개념이라는 것을 알게되었다. 이번 프로젝트에서 적용을 하기엔 이미 짜여진 에러처리 과정이 있어서, 이후 프로젝트를 진행하게 된다면 적용해 볼 예정이다. 디버깅 내용으로는 타입에러 혹은 리턴 값을 제대로 주지않아서 생긴 간단한 것부터 훅과 관련한 에러까지 다양했다. 그 중 가장 고생했던 두 상황은 다음과 같다.

1. 스윗 알럿 적용기

스윗 알럿 라이브러리 적용 이유 및 배경

일반 window 알럿으로는 감당이 되지 않았던 알럿 디자인을 해결하기 위해 스윗알럿을 적용하게 되었다. 자체적으로 모달을 만들어서 사용하기에는 코드량이나 재사용성을 고려하였을 때 무리가 있다고 판단을 하였고 다운로드 수, 브라우저간 호환성, 지속적인 모니터링, 자유로운 커스텀 등의 기준으로 봤을 때 스윗알럿2가 가장 우리 프로젝트에 적합하다고 판단 되어 적용하게 되었다.

첫 난관 - axios 요청하기

  1. 문제의 발단
    우리 서비스 내에는 채팅 추가 기능에서와 같이 confirm 확인 이후 서버와 통신을 해야하는 파트가 있었다. 라이브러리 문서 내에서 ajax 콜을 할 수 있다는 내용은 포함되어 있었지만 그것을 바로 우리 프로젝트에 적용하기 힘들다는 판단이 들었고, 기존 post 모듈 내에 작성되었던 미들웨어에 디스패치로 접근하는 것이 그 동안의 패턴이었기에 알럿에서도 디스패치를 당연히 써야한다고 생각했다.

  2. 문제의 원인
    하지만 스윗 알럿을 작성해둔 모듈은 함수형 컴포넌트가 아니었고 react hook을 사용할 수 있는 구조가 아니었다. 이때 정말 다양한 에러와 마주하며 hook rules break와 관련한 에러 메세지를 접했다. hook은 함수형 컴포넌트일때, 최상위에서만 호출 된다는 규칙에서 자꾸 에러가 발생했다.

  3. 해결 과정

  • 처음 시도 했던 것은 디스패치를 부를 수 있는 환경을 만드는 것이었다.
    훅을 호출 할 수 있는 조건을 만들기 위해 스윗알럿을 함수형 컴포넌트로 만들어보려고 시도해보기도하고 미들웨어의 모양을 따서 디스패치를 불러보려고도 시도해보았다. 하지만 이러한 시도들은 리액트의 규칙이나 컴포넌트 모듈 작성에 관한 정확한 이해없이 들이받는 심정으로 작성한 코드들이었기에 당연히 잘 될리가 없었다. hook rules break에러가 전혀 해결되지 않았다. 그렇다고 모듈에서 커스텀 할 스윗알럿을 하나하나 만들어서 사용하기엔 재사용성이 너무 좋지 않았고 코드가 엄청나게 길어지는 문제가 있었기에 재사용성과 디스패치를 모두 해결할 방법을 찾아야했다. 결국 스윗알럿 엘리먼츠 내에서 디스패치를 부르려고 하는 것 부터가 잘못된 접근이라는 결론을 내리게되었다.
  • 다음으로 시도한 방법은 ajax 를 썼듯이 axios 요청을 스윗알럿 내에서 바로 적용하는 것이었다.
    이때만해도 axios 요청의 응답을 리덕스 내에 데이터로 저장해야하는 기능이 아니었기에 디스패치할 상황을 일단은 배제하고 스윗알럿 내에서 바로 axios 요청을 하는 것으로 방향을 바꾸게 되었다. 물론 이 접근도 한번에 성공한 것은 아니다. 스윗 알럿 공식 문서 예제는 confirm 이전 비동기 처리로 ajax를 호출하는 방식이었기에 똑같은 방식으로 우리 프로젝트에 적용하기엔 무리가있었다. 우리 프로젝트에서는 confirm 이후 axios 요청을 해야했기에 관련 로직을 따라 여러 방향으로 코드를 작성해 보았고 기대한 결과를 얻을 수 있었다.

두 번째 난관 - 재사용성 및 리덕스 데이터 관리

  1. 문제의 발단
    앞선 난관은 스윗 알럿 내에서 axios 요청을 하며 일정부분 문제가 해결 된 것 처럼 보였다. 하지만 얼마 가지 않아서 모듈에서 모듈별 미들웨어로 관리되어야할 axios요청들이 무분별하게 스윗알럿 컴포넌트에서 불려지고 있었고, 요청 이후 리덕스로 관리할 데이터가 생기자 처리가 되지 않는 문제가 발생했다. 결국 코드의 재사용성과 데이터 관리 부분만 보아도 이전에 시도한 방법이 best practice가 아니었다는 것을 알 수 있는 대목이었다.

  2. 문제의 원인
    재사용성이 불가능하게 된 원인은 confirm 이후의 과정을 제대로 핸들링하지 못했다는 것이었다. 결국 사용자가 확인, 취소, miss를 행동을 했을 때, 그 이후 행동을 다룰 방법을 찾지 못했다. 임시방편으로 하다보니 특정 상황에 맞는 특정 알럿을 모두 만들어야하는 상황까지 간 것이었다. 따라서 스윗 알럿을 호출한 뒤 이후 행동을 이어나갈 수 있는 방법을 찾아야했다.

  3. 해결 과정
    그러다 문득 임포트해서 사용하고 있던 스윗 알럿이 true, false를 반환 한다면 이 값을 이용해 이후 동작을 결정 지을 수 있겠다는 생각이 들었다.

  • 첫 시도
    confirm이후 return 값으로 true를 그 이외의 상황에는 false를 주고 스윗 알럿을 조건문으로 감싼 뒤 비동기 작업을 시도했다. 그러자 깔끔하게 에러가 발생했는데 그 내용을 살펴보니 타입에러와 같아 보였다. 그래서 스윗알럿을 콘솔로 확인해 보았더니 프로토타입 Promise가 찍혀나왔다.
  • 두번째 시도
    처음에는 그저 에러가 발생하기에 포기해야하나 싶던 찰나 Promise가 눈을 사로잡았다. 비동기 작업이 가능하다면 then, catch를 사용할 수 있겠다는 생각이 들었고, 이번에는 알럿을 감싸는 것이 아닌 알럿 이후 비동기 과정에서 콘솔을 찍어보니 설정해 둔 불린 값이 제대로 나오는 것을 확인할 수 있었다.
    이것을 이용하여 그동안 알럿 컴포넌트에서 상황마다 모두 다르게 작성했던 길고 긴 코드를 불린 값을 반환하는 함수로 통일 할 수있었고 비동기적 작업이 가능해지면서 모듈내에서 서버와 통신하여 데이터를 리덕스에서 관리하는 것 까지 해결 할 수있었다.

2. 채팅 기능 확장하기

채팅에 기능을 추가하게 된 배경은 효과적으로 트롤러를 관리하기 위해서이다. 단순 채팅 기능 이외에 추가하게 된 기능은 다음과 같다.

  • 채팅방 입장 승인 요청하기
  • 요청 대기 취소하기
  • 승인 요청 거절, 수락하기
  • 단순 참여자 채팅방 나가기
  • 방장이 채팅방 나가기 (채팅 삭제)
  • 채팅 참여자 강퇴시키기

채팅 기능을 추가하는 과정에서도 역시 다양한 버그에 부딪혔지만 그 중 가장 기억에 남는 이슈 두 가지는 실시간 강퇴 기능 구현과 웹소켓 간헐적 끊김 현상이었다.

첫 난관 - 실시간으로 강퇴시키기

  1. 강퇴 기능 구현배경
    승인 거절, 수락과 채팅방 내 방장에게 강퇴 권한을 부여하여 강퇴를 시킬 수 있게 한 이유는 악성 유저를 사용자들 스스로 필터링할 수 있게 하여 사용자들이 조금이라도 더 안전한 환경에서 서비스를 이용할 수 있게하기 위해서이다. 이때 실시간으로 강퇴를 시켜야 했던 이유는 악성 유저가 채팅에 참여 중이지 않을 수 있지만, 실시간으로 채팅에 참여 중인 상황이라면 방장이 강퇴를 한 그 순간에 강퇴 당한 사용자가 채팅방 밖으로 퇴장당해야만 해당 기능이 의미가 있기 때문이다.

  2. 문제의 원인
    처음 이 기능이 까다롭다고 느낀 것은 한 가지 기능에 총 3가지의 유형이 있어 구조가 복잡했기 때문이다. 먼저 방장은 참여자를 내보낼 수 있어야했고, 강퇴를 당한 유저는 채팅방 밖으로, 남아있는 유저와 팀장의 채팅방 참여 인원에서는 해당 유저를 삭제해줘야 했다. 그러기 위해서는 각각의 아이디 정보를 구분해야 했고 관련 데이터를 관계를 웹소켓 상에서 정리하는 것이 어려웠다.

  3. 해결 과정

  • 처음에는 문제에 접근하는 방식을 가늠하는 것 부터 어려웠다. 그 이유는 웹소켓이 실시간 채팅이라는 기능에 초점이 맞추어져있어서 메세지를 주고받는 것 이외의 행동을 하게 할 수 있다는 생각을 하지 못했기 때문이다. 이후 실시간이라는 개념에 초점을 두고 생각을 발전시켜보니 스톰프 센드를 할 때, 유저의 정보를 보내주고 받는 메세지의 모듈에서 메세지로 넘어온 아이디와 접속 중인 유저 본인의 아이디를 비교하게 되면 실시간으로 어떤 행동을 이끌어 낼 수 있지 않을까하는 접근을 하게되었다.

  • 이 과정에서 임머 불면성에 관련한 에러도 접하게 되었는데, 대략적으로 설명하자면 값을 리턴하던지 draft 변경을 하던지 둘 중 하나만 하라는 내용이었다. 실시간으로 행동을 이끌어 내려다보니 하나의 리듀서에 너무 많은 조건을 주게 되면서 코드가 엉켰고 그 때문에 발생한 에러라고 생각된다. 그러면서 리듀서의 순수 함수와 예측가능한 상태라는 개념에 관해 생각하게 되었는데 아직은 완벽히 이해 되지는 않지만 앞으로도 계속해서 고민해 볼 부분같다.

    참고 자료
    리덕스의 리듀서가 순수 함수여야만 하는 이유

  • 이러한 고민과 접근 이후에 완벽하지는 못해도 원하는 기능을 구현 할 수 있었고, 스톰프 메세지를 주고 받는 개념을 이용하여 채팅 메세지를 보내는 함수 이외에 강퇴, 방폭을 할 수 있는 함수를 작성하여 기능을 추가 하게 되었다.

두번째 난관 - 간헐적 웹소켓 끊어짐 현상 해결

  1. 문제 현상
  • 첫 번째 문제는 채팅 기능의 가장 큰 문제는 1분 내외로 채팅이 끊어진다는 것이었다. 좀 더 자세히 이야기하자면 여러명이든 혼자든 상관없이 일정 속도 이상으로 채팅을 치거나 채팅 양이 많아지면 소켓이 1분, 2분을 견디지 못하고 메세지가 보내내지 않는 것이었다. 완전히 disconnect이 되는 것도 아니고 상대가 보내는 메세지가 보이기도 하지만 나는 메세지를 보낼 수 없었다.

    > 에러 메세지 내용

  • 두 번째 문제는 끊어짐 이전에 단순 채팅을 보내기 위해 input에 타이핑하는 것 마저도 매우 느리고 밀리는 현상이 발생한 것이었다.
  1. 문제 접근 및 해결과정
  • 우선 두번째 현상에 초점을 맞추고 문제에 접근했다. 프로파일러 확인을 통해 과도한 리렌더링이 발생하는 것이 문제라고 판단하였다.
  • 이것을 해결하기 위해 useRef와 throttle 방식을 시도해보았다.
    1. useRef 시도
      • onChange 이벤트 발생마다 빈번한 setState와 Action 발생으로 리렌더링이 일어나 input 입력 시 느려진다고 생각하여, useRef로 돔 요소 접근을 시도했다.
      • 그 결과 공백을 시작으로 메세지가 하나씩 밀리는 현상이 발생하였다.
    2. throttle 시도
      • onChange 이벤트 발생 빈도의 개선이 있었지만, throttle 타이밍 특성 상 마지막 값 송신이 불가했다.
  • 두가지 접근 모두 문제가 있었고, 이후 이벤트마다 Action이 발생한다는 것에 초점을 맞추어 해당 로직을 빼고, 보내기 버튼을 클릭할 때, 실제 메세지가 담겨지도록 수정했다.
  1. 결과 및 소감
  • useState Hook을 통해 state값이 변하거나 Action을 실행시키는 dispatch가 동작할 때 렌더링이 일어나는 사실에 대한 정확한 이해가 부족하여, 과도한 리렌더링으로 인한 주요 채팅 기능의 오작동을 야기했다.

  • 현재는 onChange 마다 실행되던 dispatch 와 액션 부분을 삭제하였고, 그 대신 useRef의 defalutValue값을 소켓 메세지 함수의 파라미터로 사용하고 있다.

  • 웹 소켓 끊어짐 현상이 해결되었고, 채팅 입력시 input의 text 밀림 현상 및 채팅 속도가 개선 되었다.

세번째 난관 - 이미지 업로드 속도 최적화

  1. 문제 발견 과정
    사이즈가 큰 이미지의 경우 프로필 수정 이후 이미지 렌더링 되지 않는 현상 발생
    프로필 수정 api 요청의 응답이 페이지 렌더링 속도에 미치지 못하였다.
    api 요청 전 페이지 이동이 되면서 수정 액션이 실행되지 않았음을 확인 하였다. (api 요청 응답 없음)

  2. 해결 과정
    비동기 처리 순서에 문제가 있다고 생각해서, api 요청이 완료되기 전까지 로딩 처리를 하였고, 로딩이 완료 된 후 확인 알럿 처리를 하였다.
    그럼에도 불구하고 로딩이 너무 오래 걸리는 현상이 발생하여 서버에 올려주는 이미지의 용량이 매우 큰 것이 문제라고 판단하였다.
    이미지 리사이징 라이브러리를 통해 서버로 api 요청 전 리사이징 된 이미지를 넘겨주었다.

  3. 결과 및 소감
    큰 용량의 이미지를 업로드 해도 최대 사이즈가 300px을 넘지 않게 되었고, 처리 속도가 향상 되었다.
    이전까지 서버에서 이미지 리사이징을 해왔지만 이것을 계기로 프론트 단의 처리가 필요함을 느꼈다.

11주) 프론트 최적화 및 PWA 적용

profile
# 인생은 못 먹어도 GO # 오늘의 과제에 최선을 다하는 열심 인간

0개의 댓글