팀프로젝트 | 여행 모임 서비스 offstory (3주차)

noopy·2021년 10월 30일
2

Week 3. 프론트엔드 팀 프로젝트
3주차 ~ 마무리: 21 / 10 / 25 - 21 / 11 / 04

(1) PR 올리는 방법 정하기
(2) 쿠키 > 세션 스토리지
(3) 구현한 기능들
(4) 구현하지 못한 기능들
(4) 마무리와 느낌
(5) 다음 계획
(6) 발표 영상

📌 프로젝트 기간

  • 21 / 10 / 14 (금) ~ 21 / 11 / 03 (수)
    총 21일
  • 팀원
    김영후
    조주영
    김지영

🗂 PR 방법 정하기

짧은 기간안에 프로젝트를 진행하다보니 자체 머지하는 일이 빈번했고, 팀원이 어떤 로직으로 정확히 어떤 일을 했는지 확인하지 않고 넘어가는 상황이 많아졌다. 물론 PR 제목을 통해 대강 뭘 했는지는 알 수 있지만 기능구현에 집중한 나머지 코드 퀄리티나 리뷰는 신경쓰지 않는 느낌이 들었다.
빠르게 기능구현을 해야 하는 것이 맞고, 리뷰에 많은 시간을 쏟는 것도 좋지만 시간이 없기 때문에 PR에 어떤 일을 했는지 무엇이 바뀌었는지 등을 간단히 설명하면 좋겠다고 건의했다. 매일 13시에 스크럼을 진행할 때 PR에 적은 설명을 바탕으로 한 일과 할 일을 설명하는 식으로 진행이 되었다.



자체 머지를 한 후 오류가 발생하면 fix branch를 파서 동기적으로 팀원과 상의 후 오류 수정 결과를 반영했다. 다만 실제 업무에서는 카톡이나 디스코드로 동기적인 커뮤니케이션이 이뤄지지 않고, 자체 머지를 한 후 오류가 생겼을 때 큰 일이 발생할 수 있기 때문에 지금과 같은 짧은 프로젝트(거의 2주)에서만 괜찮은 방법 같다.

🧐 우리 팀은 빠른 기능구현을 목표로 코드 리뷰를 거의 하지 않고 자체 머지 하였지만, 어떤 팀은 코드 리뷰에 정성을 많이 쏟기도 한 것 같다. 궁금한 것은 다른 분의 코드 리뷰가 완료될 때까지 머지하지 않고 다른 작업을 진행해야 할 텐데 다른 작업도 최신 작업 내용이 반영이 안된 상태이므로 애매해질 것 같은데 이 부분을 어떻게 관리하셨는지 궁금하다.
👉🏻 짤이몽땅 서비스를 만드신 팀께 의견을 여쭤본 결과 1️⃣ PR에 우선순위 태그를 둬서(1, 2, 3, 4) 우선순위가 높은 PR을 최대한 빠르게 리뷰해서 반영하려고 노력했고, 2️⃣ 머지가 되지 않은 PR의 코드가 필요할 경우 해당 브랜치를 직접 pull 받아와서 사용하셨다고 한다. 이렇게 되면 충돌이 많이 나고 브랜치가 어지러워질 수 있지만 충돌 경험도 쌓고 리뷰도 진행할 수 있는 좋은 방법 같다 👏

🍪 쿠키 > 세션 스토리지

기존에는 로그인된 유저의 정보를 cookie에 저장했었는데 로그아웃 시 새로고침을 해줘야 쿠키가 삭제되는 부분이나, 클라이언트에 쿠키 정보가 저장되어 보안에 취약하다 판단이 들어 유저의 정보는 세션 스토리지에 저장하기로 했다.

세션 스토리지 부분은 영후님과 주영님이 로직을 짜주셨다.
로그인이 되었을 때 Login.vue에서 스토어에 유저 정보를 저장 후, store에서 userId를 가져와 사용자 정보를 불러온 다음 세션 스토리지에 저장한다. 지금 보니 엄청 비효율적이고 복잡하게 로직을 짰는데 스토어에 저장 후 다시 세션 스토리지에 저장한 이유는 스토어의 데이터가 새로고침 시 초기화되기 때문이다.
vuex refresh 문제점 아티클에서 vuex의 refresh 문제점을 해결할 수 있는 vuex-persistedstate 라이브러리를 소개한다. vuex에 저장되는 데이터들을 웹 브라우저의 localstorage에 저장해 새로고침 시 그 값이 존재하면 localstorage의 값을 가져와 사용하는 원리라 한다! path라는 옵션을 통해 원하는 store 들만 localstorage에 저장되도록 할 수 있다. 현재 코드를 리팩토링할 때 꼭 사용해보고 싶은 라이브러리다.

😉 구현한 기능들

최종적으로 내가 담당한 페이지들은 회원가입 페이지, 포스트 상세 페이지이다. 회원가입은 이전 아티클에서 설명했으니 넘어가고 포스트 상세 페이지에 대해 설명하겠다. 처음으로 vue를 활용한 영화 검색 사이트와는 다르게 코드가 너무 길어지지 않고 기능별로 독립성을 높이기 위해 컴포넌트화 시키려고 노력을 했다.

PostContent 컴포넌트

가장 최상위 컴포넌트인 PostContent에서 post 상세 페이지 api 응답(readPost)을 받아온 다음 하위 컴포넌트들에게 props로 전달해주는 형태로 만들었다. data가 바뀔 경우 vue에서 필요한 부분을 리렌더링 해주기 때문에 편하다.

하위 컴포넌트에서 데이터를 변경시키면 post 상세 페이지 api를 통해 변경된 내용을 반영해주어야 하기 때문에 rerender 이벤트를 최상위 컴포넌트까지 올려준 다음 데이터를 초기화시켜주는 방식으로 구현했다. 다만 컴포넌트 depth가 깊어질 경우 최상위까지 하나하나 이벤트를 올려주는 비효율성이 존재한다.
👉🏻 postContent에서 readPost를 받아오기 때문에 하위 컴포넌트에서 댓글을 달거나, 포스트를 수정, 삭제하는 api를 요청할 경우 변경된 readPost를 다시 요청 받아와야 한다. 이 부분을 provide, inject로 해결할 수도 있을 것 같은데 실제로 구현해봐야 알 것 같다. 조상 요소에서 provide로 initPostdata를 주고 자손 요소에서 inject로 받아온 다음 필요할 때 사용하는 방식으로 구현해봐야겠다.

Post 컴포넌트

🙋‍♀️ 역할

  • 모임 주최자일 경우 편집, 삭제 가능(checkHost)
  • 모임 주최자가 아닐 경우 좋아요 가능
  • 편집 버튼을 누르면 리렌더링 없이 편집 컴포넌트로 전환

코드가 너무 길어지기 때문에 설명할 부분만 이미지로 가져왔다 🙇‍♀️

post 컴포넌트는 포스트를 보여주는 컴포넌트이다. 이 컴포넌트에선 편집 버튼을 누르면 화면이 전환되지 않고 특정 부분만 편집 화면으로 바뀌게 구현하고 싶었다. 이를 위해 중첩된 라우트와 동적 컴포넌트로 구현하고 싶었는데 props나 emit 전달을 어떤 방식으로 할 지 감이 안와 v-if, v-else로 구현하였다.
결과적으로 v-if와 v-else를 남발하여 코드를 짠 것 같아 썩 만족스럽진 않다. 이미지도 완전히 변경된 후에 보이도록 코드를 짜야할 것 같다.

Editor 컴포넌트


Editor 컴포넌트는 v-html을 이용해 \n이 줄바꿈으로 처리되도록 했다.

EditPage 컴포넌트

🙋‍♀️ 역할

  • 편집 취소, 저장
  • 이미지, 제목, 내용 수정

양방향 데이터 바인딩으로 input과 textarea에서 입력한 값들은 data에 바로바로 반영되도록 했다. 저장 버튼을 눌렀을 때 submitInfo가 호출되어 포스트를 업데이트하는 API에 값을 넣는다. 서버의 데이터가 변경되었으므로 최상위 컴포넌트에서 readPost를 불러와 하위 컴포넌트로 업데이트시킨다.
👉🏻 저장할 때 약간의 대기 후에 업데이트가 되는데 낙관적 업데이트로 미리 유저에게 변경 사항을 보여준 후 서버에 업데이트 시켜도 좋을 것 같다. 낙관적 업데이트를 하려면 웹 스토리지나 스토어를 사용해야겠지? 위에서 언급했던 vuex-persistedstate 라이브러리를 사용하면 vuex에서 다 관리할 수 있을 것 같다!! 💕

Comments 컴포넌트

🙋‍♀️ 역할

  • 댓글 업데이트, 삭제

댓글의 로직은 너무 길어서 필요한 부분만 따로따로 설명해보겠다.

  1. API로 댓글을 불러와 순회

    commentId와 commentorId는 리스트마다 각각 갖고 있어야 하기 때문에 li의 속성으로 바로 넣어주었는데 보안 상으로 좋지 못한 코드인 것 같다.

    다른 방법은 store나 state에 배열로 리스트의 갯수만큼 commentId와 commentorId를 넣어두고 li를 클릭했을 때 index를 가지고 배열을 순회해 값을 얻는 방법도 있을 것 같다. 댓글의 개수가 엄청나게 많아지지 않는 한 이 방법이 훨씬 더 보안상으로 안전하지 않을까?

  2. 댓글달기와 로그인해주세요

    유저가 로그인 했을 때와 안했을 때를 구분해 댓글 작성 부분이 다르게 보이도록 구현했다. 이 외에도 빈 공백을 제외하고 문자가 없을 때에는 submit을 막아놓거나, input 이벤트가 발생할 때마다 textarea의 높이를 늘리거나 자잘한 부분들을 구현하느라 시간이 오래 걸렸다.

🥲 적용하지 못한 기능들

최종적으로 구현한 것은 회원가입과 포스트 상세 페이지였지만, 채팅, 참가 승인, 거부, 대기와 같은 로직들을 구현하려고 했었다.

채팅

채팅의 경우 며칠 간 계속 시도해보았지만 시작부터 connect 이벤트가 동작하지 않았고 여러가지 시도를 해본 결과 결과적으로 CORS 정책으로 인해 통신이 안됐던 거였다. 남은 기간이 너무나 빠듯해서 채팅을 구현하는 것은 기술 부채로 미루고 기존의 코드 완성도를 높이자는 의견으로 팀원들과 마무리하였다.

참가 승인, 거부, 대기

주최자의 경우 참가 승인, 거절을 통해 채팅에 참가시킬 수 있다. 이 부분은 서버의 유저 정보 API에 사용하지 않는 username 프로퍼티에 참가 여부 데이터를 저장하기로 했는데 최종적으로는 이 코드도 사용하지 않기로 했다.
원인은 api 테스팅과 검토를 사전에 하지 않아서 발생한 문제였다. 주최자가 참가자의 유저 정보를 가져오려면 상대방의 토큰이 필요한데 본인의 토큰만 가져올 수 있으므로 애초에 불가능한 작업이었다. 사전에 생각하지 않고 로직을 먼저 구현하는 바람에 시간을 쏟았던 코드를 쓰지 못하는 일이 발생한 것이다.
역시... API 검토가 일순위라는 걸 다시 한번 깨닫는 순간이었다 🔥🔥🔥

💬 마무리와 느낌

아쉬웠던 점

최종 마무리 기간 땐 발표를 맡게 되어 두분이서 코드를 정리하고 나는 전체적인 흐름을 정리했다. 전체적으로 흐름을 정리하면서 느낀 건 겉으로는 여러 기능을 많이 구현했지만, 코드 내부는 중복된 코드, 여기저기 각자 쓰는 코드들로 정리가 안되어 있다고 느꼈다 🥲🥲🥲
빠른 기능 구현 VS 최소한의 기능 구현과 코드 퀄리티 사이에서 갈등이 된달까...
둘다 장단점이 있어서 뭐가 더 좋다 나쁘다할 순 없지만 다음에 다른 프로젝트를 진행하게 된다면 완성하지 못하더라도 협업에 초점을 두고 다른 분들의 코드를 많이 배우고 싶다.
또, 다른 곳에서 코드를 가져올 땐 무지성으로 가져오지 말고 스스로 로직을 다 이해하고 사용하고 싶다.

좋았던 점

  1. 필요한 기능을 뚝딱뚝딱 만들어주는 팀원들 덕분에 가져다 쓰기만 하면 돼서 너무 편하고 좋았다.
  2. 또 어쩌다가 충돌없이 dev branch에 머지가 돼서 기존 로직이 덮어씌워지는 일이 발생했는데 이때 git revert를 사용하여 특정 커밋의 내용만 되돌려서 깔끔하게 해결되었다.
  3. 모든 팀원들이 서로의 의견을 충분히 반영해 싸우거나 충돌없이 서로가 원하는 부분을 이해하고자 노력하였다. 목소리를 높이는 게 중요한 게 아니라 최대한 의견을 수렴하여 좋은 방향으로 이끄는 것이 베스트임을 깨달았다.
  4. 낙관적 업데이트를 적용한 팀원의 코드를 보며 좋은 코드란 무엇인지를 계속해서 고민하게 되었다. 최종적으로 적용하지는 못했지만 리팩토링하며 조금씩 적용해보고 싶다.

✔️ 다음 계획

  • vuex-persistedstate 라이브러리로 세션 스토리지 작업들을 store로 옮기기
  • 낙관적 업데이트
  • provide, inject 적용해보기

👉🏻 다음 프로젝트 때 무조건 Vue를 할 수 있을거라 장담할 수 없어서 리액트를 파보기로 했다. 리팩토링은 또다시 다음으로 미뤄져야 할 것 같다 🥲
아니면 간단한 것부터 차근차근 시간이 날 때 리팩토링을 진행할 것 같다!!

profile
💪🏻 아는 걸 설명할 줄 아는 개발자 되기

0개의 댓글