카페 로케이터

6147 단어 projectproject

프롤로그

이전 SHOP 프로젝트에서는 기술에 대한 장단점 위주로 리뷰 하였다면, 이번에는 프로젝트를 진행하면서 겪은 이슈에 대해서 리뷰해 볼 것이다. 이슈를 겪고 해결했다 할 지라도 정리해서 기록해두는 습관이 더 중요한 것 같다. 앞으로도 이슈를 꼼꼼히 정리하고 더 나은 해법은 없는지 개선해 나가는 자세를 가져야겠다.


1. INDEX

  • INTRODUCTION
  • FEATURES
  • STACKS
  • ISSUE

2. INTRODUCTION

https://cafelocator.herokuapp.com

Cafe Locator는 유저 반경 6km 안 카페를 서치하고, 길을 안내해주며, 실시간 리뷰를 제공하는 소셜 네트워크 지도 서비스 입니다.

3. FEATURES

  • Nearby Search, Place Detail, Directions (구글)
  • 인증, 데이터베이스 (파이어베이스)
  • 모바일 반응형
  • 가상 스크롤
  • 영업시간 요일 정렬
  • 리뷰
  • 바텀 시트 터치 드래그

4. STACKS

- Front-End

  • Typescript
  • React
  • Context API
  • Emotion
  • Google Maps API

- Back-End

  • Firebase (Authentication, Realtime Database)
  • Express JS (Node JS)

- Deployment

  • Heroku

5. ISSUE

API 요청 부분을 Maps Javascript API로 변경하면서 백엔드 서버를 걷어 내었습니다.

#### - CORS

애플리케이션을 완성할때 즈음 모바일 테스트를 시작 했다.
카페를 검색해도 검색이 되질 않아 왜 안되는지 감이 잘 안잡혔는데 결국 이것 저것 삽질 하다 원인은 찾았는데 브라우저에서 CORS 에러를 보내고 있었다.
내 개발 환경 PC에서는 기존 크롬에 깔아 두었던 allow CORS 익스텐션이 차단하고 있었기 때문에 해당 이슈를 알 수 없었지만 모바일은 그러한 익스텐션이 없었기 때문에 테스트를 진행하면서 알게 되었다.

CORS 에러가 났던 이유
서버 사이드 용 구글맵 API를 클라이언트에서 직접 요청했기 때문인데, 해결 방안은 두 가지 정도 있는 것 같다.

해결 방안
1) 클라이언트에서 요청시 구글맵 클라이언트용 라이브러리를 사용
2) 서버를 이용해 우회를 하여 서버와 서버 간으로 통신

고민 끝에 익스프레스도 되짚고 싶어 서버 사이드 용 구글맵 API를 사용해보기로 하였다. 먼저 서버에서 클라이언트 요청을 대비해 CORS 허용을 해주었고, 클라이언트에서 서버로 키워드를 요청하면 서버에서 구글 API로 다시 요청을 보내고, 응답을 서빙했다.

CORS 에러를 대응하면서 느꼈던 점은 디버깅만 잘했어도 개발 시간을 현저히 단축할 수 있었을 것이라고 생각이 들었다.


- 리플로우, 리페인팅 렌더링 최적화

  1. 리플로우가 덜 발생하는 css 속성 사용(transform, fixed)
  2. Bottom Sheets의 리스트를 가상 스크롤로 구현
  3. 이벤트 함수 구독 해제
  4. 컨텍스트 API 관심사 분리

시작은 Bottom Sheets의 위치를 top값으로 조정하여 움직였었다.
그런데 터치를 할때마다 많이 버벅였다. 이번에도 명확한 이유를 모른채 일일히 다 테스트를 해보는 수밖에 없었다.
그래서 또 연이은 삽질 끝에 리플로우, 리페인팅 문제라는 것을 알게 되었다.

애플리케이션 최적화가 안좋았던 이유
리플로우, 리페인팅은 부모 컴포넌트 혹은 자식 컴포넌트의 위치, 크기 등의 css 속성 값이 바뀌었을 때 발생되는 렌더링 프로세스이다.
리플로우는 렌더트리를 구성하는 과정중 css의 width, height, position 등을 다시 계산하는 과정이고, 이 계산하는 과정을 거쳐 그려지는 과정을 리페인팅이라고 한다.
즉 리플로우가 많이 일어날수록 계산 비용이 많아지는거라 최적화를 위해서는 필연적으로 신경을 써줘야하는 부분이다.

해결 방안
첫번째로 리플로우가 발생하지 않는 css 속성들로 최대한 바꿔나가기 시작했다.
완전히 바꿀수는 없고, 최대한 타협을 해야하는 것 같다.
그 중에서도 top값을 조정하여 터치 드래그를 구현했던 것을 리플로우가 발생하지 않는 포지션 fixed와 transform 방식으로 변경하였고, will-change도 사용해보았지만, 해당 기능은 최대한 지양하는게 좋다고해서 테스트해보고 보류 하였다.

두번째로 Bottom Sheets 메뉴의 리스트를 가상 스크롤로 구현하여, 실제 렌더링 된 DOM의 갯수를 실질적으로 줄여서 렌더링 되는 갯수를 최소화하였다. 인스타도 가상 스크롤로 최적화했다는 사실을 처음 알았는데, 흥미로웠다. 결국 렌더링 갯수가 최대한 적은게 최고의 최적화 방법이었다.

세번째로 터치 이벤트를 연결해준 함수들을 실행 후 클린업(clean-up) 함수를 통해 구독 해제를 해주었다.
자바스크립트의 Garbage Collection은 똑똑하지 않다. 참조에 의해서 가비지 컬렉션을 수행할 수 없는 상황이 있기 때문에 더 이상 필요가 없는 메모리의 경우 명시적으로 구독해제를 실행해줘야 메모리 누수를 방지할 수 있다.

useEffect함수는 마운트, 언마운트를 내포하고 있다. 아래와 같이 이펙트/클린업을 구성할 수 있다.
중요한점은 리액트는 새로운 상태값과 렌더링을 완료한 후 클린업 함수가 실행된다는 점이다.

useEffect(() => {
    (이펙트 함수)
    return {
        (클린업 함수)
    };
}, [의존값]);

네번째로 이번 프로젝트에 컨텍스트 API를 처음 도입했는데, 관심사를 왜 명확히 분리하라는지 뼈저리게 느끼게 된 경우였다.
터치 드래그를 할때 맵 컨텍스트와 관심사가 연관되어있던 컨텍스트 때문에 드래그 할때마다 맵까지 리렌더링이 되었었다. 결론적으로 불필요한 리렌더링을 유발하는 컨텍스트의 의존성을 분리해주었다.


- 번들 사이즈 최적화

리액트 애플리케이션들은 웹팩을 통해 하나의 JS 파일로 번들링 되어진다.
그런데 이 과정에서 문득 내 번들 파일은 용량이 어느정도되는지 궁금해졌다.

Before 파이어베이스 마이그레이션
총 번들 사이즈는 약 300kb 나왔다. 번들 파일을 분석하였는데 파이어베이스의 용량이 어마어마했다. 그런데 파이어베이스 9버전 부터는 모듈러 방식이라 번들 사이즈에서 큰 변화가 있었기 때문에 바로 9버전으로 마이그레이션을 진행했다.


After 파이어베이스 마이그레이션
파이어베이스 버전을 올리고 총 번들 사이즈가 200kb 초반대로 대폭 줄은 모습. 애초에 파이어베이스 용량이 어마어마한데, 마이그레이션 후 214.19kb -> 72.81kb 로 대폭 줄었다.

마지막으로 import cost로 확인해 보았다. 눈에 띄게 차이 나는 모습. (위: 마이그레이션 전, 아래: 마이그레이션 후)

기존 네임스페이스 버전의 파이어베이스는 불필요한 모듈을 모두 불러와야 했다. 업데이트 후의 모듈러 버전의 파이어베이스는 필요한 모듈만 불러오기 때문에 효과적으로 번들 최적화가 이루어질 수 있었다.

- CRA 헤로쿠 배포

CRA로 만든 리액트 앱을 헤로쿠에 배포를 했지만 빌드된 파일을 읽지를 못하였다.

CRA 헤로쿠 배포 에러가 났던 이유
헤로쿠에 CRA앱을 배포하면 빌드 파일을 실행을 못한다.
dev 환경에서는 CRA 서버가 실행되지만, 헤로쿠에 배포하고 빌드된 파일에서는 CRA 서버가 제외되기 때문에 Express 서버를 구축해줄 필요가 있었다.

해결방안
간단하게 Express서버를 구축하여, 빌드된 리액트 앱을 읽어주게 하였다.

좋은 웹페이지 즐겨찾기