좋은 개발자가 되기 전에 좋은 사람이 먼저 되고 싶어요

프론트엔드

프론트엔드 성능 개선과 컨벤션 수립

개발하는 이정민 2024. 4. 25. 14:01

최근에 성능 개선 관련으로 작업을 진행했다. 다른 포스팅에서도 확인할 수 있듯이 최근에 나는 이직을 했다. 그리고 현재 온보딩 과정을 보내고 있는데 빠르게 만들어보고 얼른 일하고 싶었지만 딱히 프로젝트 투입을 바로 할 수 있는 여건(기획or디자인)이 없던 상황.... 그래서 기존 코드를 파악을 하고 있었고 초기에 외주로 시작했던 서비스여서 너무 많은 사람들의 손을 거쳐갔다 보니 생각보다 B2C 서비스에 레거시 코드가 너무 많아 정리가 필요해 보였기에 동료 프론트엔드 개발자 분과 팀장님께 개인적으로 말씀드려 단독으로 뜯어보기 시작하였고 이전 회사에서 느꼈던 경험과 공부를 통해 재밌게 작업하여 포스팅을 남겨 본다

(작업 도중에 다른 프로젝트에 투입되게 되어 급하게 마무리 지었던게 아쉽다)

 

내가 만났던 B2C 프로젝트는 아래와 같이 구성되어 있었다

React(cra) + (context + reducer) + redux + styled component + RESTAPI

 

그리고 가장 먼저 눈에 들어왔던 건 사이트 접속 속도였다. 아무리 SPA라고는 하지만 속도가 많이 느렸다 일단 라이트 하우스 체크를 해보도록 하자, 아직 작업했던 성능 개선 관련 코드들을 한 번에 실 서버에 배포하기에는 부담이 되어 분할하여 배포하기로 했고 성능 체크 before after 또한 stage 서버 기준이며 인터넷 성능, 컴퓨터 성능에 따라 다르게 나올 수는 있다.

 

lighthouse

 

chrome performance

 

먼저 라이트 하우스를 보면 performance 점수가 2점으로 처참하고, 세부 진단 결과를 보면 다양하고 은근히 라이트하우스는 친절하다. FCP를 지연시키는 원인들을 알려주고 이미지의 크기를 컨트롤이 필요하다든지 불필요한 css 코드 및 무거운 Js 파일 용량 혹은 외부 스타일이나 폰트 등을 사용하기 위한 link를 거는 것들을 다 체크해 준다.

 

그럼 어떻게 시작할까 ?


아래와 같은 순서로 진행하려고 했다.

1. 필요하지 않는 스타일 코드, 다운로드 받고 있는 폰트 및 외부 스타일 프레임 워크 정리, 사용하지 않는 dependency 정리

2. 코드 스플리팅 및 이미지 최적화

3. 스타일 코드 작성 시에 mixins 사용 및 컨벤션 수립

4. 불 필요한 최적화 코드 걷어내기

 

1번을 작업하기 위해 필요하지 않는 스타일 코드들은 정리를 시작했다. 사실 이 부분은 노가다 작업이며 세부 컴포넌트까지 추적해서 정리할 수는 없으니 페이지 단위의 구성되어 있는 부모 wrapper들을 시작으로 체크하여 삭제해 줬다. 그다음엔 다운로드하고 있는 폰트 및 외부 스타일 프레임 워크를 살펴보니 여러 사람들의 손을 타서 font를 다양한 걸 다운로드하고 있었고 디자이너와 상의하여 폰트를 통일시키게 되었다. 또한 css 파일에 직접적으로 import 하여 사용하는 것은 지양해야 하기에 그러한 부분들은 link로 다운을 받는 것으로 통일시켰다.

 

은근히 영향력을 행세하는 녀석들..

 

또한 처음 해당 서비스를 개발할 때에 antd, bootstrap 등을 사용하고 있었고 antd은 크게 사용처가 없는 듯하여 스타일 코드 작성으로 변경하고 삭제 처리를 했고 bootstrap은 className으로 사용하고 있는 경우가 있어서 이런 부분은 눈에 보이는 것만 삭제한 뒤에 추후에 일정을 잡아 처리하기로 동료 개발자와 이야기하였다. 이러한 외부 스타일 프레임워크 다운로드는 많은 위의 이미지와 마찬가지로 꽤나 영향을 차지한다 그리고 사용하지 않는 dependency 정리의 경우 depcheck라는 라이브러리로 unused dependency 삭제 처리를 진행하여 node_modules의 크기를 (351Mb > 290Mb)로 줄였다

 

2번은 먼저 위에 올렸던 두 번째 사진을 확인해 보면 main js 파일의 경우 통으로 받아오고 있어서 코드 스플리팅의 필요성을 느꼈고 무작정 모든 페이지를 코드 스플리팅으로 받아 오기에는 부담이 있어 중요와 비중요 그리고 로그인과 비로그인 기준으로 라우팅 페이지를 나눠 비중요와 비로그인 기준으로 먼저 코드 스플리팅을 하여 받기로 정하였으며 해당 프로젝트는 cra로 구성되어 있었기에 일단 번들 크기 측정을 위하여 cra-bundle-analyzer를 사용하여 측정을 했고 코드 스플리팅의 경우 react-lazy를 사용하려고 하다가 suspense를 다 설정해줘야 하는 필요성이 있었기에 그렇지 않은 @lodable-component를 사용하여 스플리팅을 진행했다. before와 after는 아래와 같다

 

before 번들 쪼개기 전

 

before 그냥 받아와 지고 있는 페이지들

 

after : 이야.. 많이도 쪼개졌다

 

after: lodable 사용으로 코드 스플리팅

 

after: 목적 별 페이지 묶음

 

위 이미지를 보면 알 수 있듯이 코드 스플리팅한 컴포넌트들의 route 설정을 목적별로 묶어줘 관리를 하였고 pathname 또한 상수로 정리하여 무지성으로 작성하는 경우를 줄였다. 또한 번들 측정 결과를 보면 페이지의 main size 번들 감소 및 많은 chunk 분할 들이 일어나서 앞으로 개발하면서 어떤 chunk 파일에서 번들 사이즈를 많이 잡아먹는지 체크하여 개선할 수 있을 것 같다.

 

3번은 자주 사용하는 코드인 flex or absolute or ellipsis or media query 관련 설정들은 따로 style 폴더 안에 mixins 파일을 만들어 묶음 처리를 해놔 사용하기 용이하게 하였다 또한 해당 서비스의 방문 접속의 80퍼센트 이상은 모바일로 접근을 하지만 그에 따른 코드 작성들은 데스크탑 기준으로 스타일 작성을 하고 있었기에 스타일 코드 작성시에 모바일을 우선으로 하여 데스크탑을 작업하게끔 하고 스타일 코드 작성 순서 또한 위에서 아래로 position > display > background > font > animaiton 등으로 수립하여 노션 문서로 정리를 했다

(스타일 코드 작성시에 모바일 우선과 데스크탑 우선에 따른 장단점 유무는 아래의 링크를 확인을 하자)

https://ujeon.medium.com/css-모바일-우선-vs-데스크탑-우선breakpoint-3ed09d033c49

 

window.matchMedia 적극 활용

 

그리고 미디어 쿼리를 기존에도 잘 사용하고 있었지만 react-device-detect라는 라이브러리를 사용해 반응형 사이트인데도 컴포넌트 분기처리를 그렇게 사용을 하고 있었다 react-device-detect는 라이브러리의 본질은 이름 그대로 "디바이스" 기준으로 하기에 첫 접속 시 user agent의 디바이스를 가지고 판단하기에 리사이징에서는 적용이 안되어 지금 사용하는 방식과는 맞지 않은 라이브러리였다 오로지 라이브러리는 그 본질 그대로 사용하여야 한다고 생각한다. (불 필요할 시에 사용 X) 그래서 위 사진과 같은 window.match를 사용하여 윈도우 자체 크기에 따라 우리가 정한 media query가 맞는지를 판단해 주는 hook을 만들어 사용하게 되어 이제는 리사이징도 잘 반영이 되어 불 필요한 device-detect 사용을 줄일 수가 있게 되었다.

 

4번은 앞으로도 그래야 하지만 무분별한 useMemo와 useCallback 사용을 막는 것이다. react는 부모의 state 변화가 일어나면 자식의 리렌더링은 당연한 흐름이다 게다가 useMemo와 useCallback은 공짜가 아니다, 분명히 최적화를 해준다면 내부에는 그 로직을 가능케 해주는 또 다른 코드가 있을 터이니 꼭 필요한 곳에만 쓰고 모를 때는 성능을 측정해 보자 (아래는 자바스크립트와 리액트 분야에서 유명 인사인 Kent C. Dodds가 작성한 최적화 관련된 글이 있으니 참고해 보자)

https://ideveloper2.dev/blog/2019-06-14--when-to-use-memo-and-use-callback/

 

서버에서 webp 처리한 것에 대한 사이즈 커스텀

 

또한 이미지의 경우 기존에 사용하는 방법은 고정으로 사용할 이미지의 경우 aws에 대부분 올려 받아와서 쓰려고 하고 있었고 그 이외는 mock data로 public/images에 관리되고 있었다 하지만 확장자는 png와 jpeg 등을 주로 사용하여 이미지 크기에 대한 최적화 진행이 되어 있지 않았기에 webp를 사용하여 최적화를 진행해주려고 했다. (webp에 대한건 아래 링크를 확인해 보자)

https://medium.com/@tarah_s/webp-images-to-improve-performance-a052fb27623a

 

그 이외에 돌아다니면서 사용하지 않는 코드들을 정리해 줬고 상태관리를 context+reducer 사용과 redux를 같이 사용하고 있었기에 context-reducer를 정리하여 redux만 사용하게끔 처리를 해놨다

 

위 작업에 따른 결과는 어떨가... 

아직도 performance가 6점..
FCP가 거의 4초이상 줄었다.

 

FCP의 감소와 main js 파일의 분할 처리가 되어 있는 모습을 보면 뭔가 큰 변화(?)가 있어 보이기는 하지만 사실 결국엔 자바스크립트 코드의 스플리팅은 조삼모사이며 FCP 또한 빠르면 좋지만 그게 정답은 아니다 여러 가지 요소가 작용하여 성능 개선이 일어나게 되고 라이트하우스 결과를 보면 6점인 것 또한 위의 4가지 사항들을 수립해 놓고 모든 부분을 다 작업해놓지 않았기에 큰 변화는 없었다ㅠ

 

이유인즉슨.. 갑자기 다른 프로젝트에 참여하게 되었다..

 

하지만 여러 사람의 코드가 거쳐간 프로젝트를 개선하기 위해 성능을 측정하고, 컨벤션을 수립하고 정리하는 것에 있어서 굉장히 재미있게 했다. 또한 어떤걸 먼저 우선순위로 둬야하는지 핸들링 방법에 대해서도 많이 배운 것 같다. 이전에는 이미 다 개선이 잘 되고 있었고 컨벤션 또한 까다롭게 지켜지던 코드만 만지다가 이러한 야생의 코드는 처음이랄까,,

 

무튼 요번에도 이러한 작업들 관련으로 공부하다 보니 스스로 많은 성장을 한 것 같다.

 

 


해당 블로그는 공부한 내용의 기록으로 중점이 맞춰져있습니다.

스스로 어떻게 받아들이고 있는지에 대한 주관적인 견해가 많이 들어가 있습니다.
그렇기에 다소 설명이 부족하고 다른 방향으로 해석할 수 있는 여지가 있습니다.

우연찮게 지나가다가 발견하신 글이라면 다양한 의견 감사하게 받아들이겠습니다.

'프론트엔드' 카테고리의 다른 글

Unit Test and Jest  (0) 2024.03.19
Web Animation  (0) 2024.03.13
SSOT 그리고 제어 컴포넌트와 비제어 컴포넌트  (0) 2024.02.16