⚽️ 목표
내가 생각하는 퍼포먼스의 대한 생각, 접근법
TL;DR
퍼포먼스를 무조건적으로 쫓는 PDD(Performance Driven Development)는 좋지 않은 mindset이다.
•
퍼포먼스 늪에 빠지지 말자.
•
개발 경험(DX), 생산성도 기술 선택에 있어서 아주 중요한 부분으로 작용한다. 퍼포먼스(번들 크기, 속도)가 전부가 아니다.
1. 들어가며
•
항상 드는 생각이지만 퍼포먼스란 단어는 개발자들에게 항상 특별한 힘을 가지고 있는 단어같다. 퍼포먼스란 단어는 개발자들을 예민하게 만드는, 흥분 시키는 단어다.
◦
일단 나란 사람부터 퍼포먼스란 단어만 보면 파블로프의 개 마냥 헥헥헥 거리고 있으니..
◦
잘 개발하고 있다가도 “야 이게 더 퍼포먼스 좋다는데?”하면 고개돌아가는 현실은… 아래 meme이 생각나게 만든다.
•
퍼포먼스에 신경 쓰지 않는 개발은 내가 여기서 말하지 않아도 다들 문제점을 알고 있을 것 이다.
◦
페이지가 늦게 로드 될 경우 또는 이용자 interaction 시 UI 반응속도가 느려질수록 리텐션이 떨어지며 유저 이탈율이 늘어나고… blah blah blah..
•
이 글을 쓰기 몇 주 전까지만해도 “그게 제일 퍼포먼스적으로 좋아?”라며 퍼포먼스만 쫓던 필자가 “그거 퍼포먼스적으로 충분히 좋아?”로 접근법이 바뀐 이유를 공유한다.
2. 사용하려는 기술의 필요성 자각의 필요성
•
이전 글에서도 말했지만 “react 왜 써요?”라는 질문에 답을 가지고 있지 않는 사람이 흔하다. “그러고도 니가 FE 개발자라 할 수 있어?”라는 질타를 하거나 “직업 윤리상 잘못됐다!”라며 따지자는게 아니다.
◦
좀더 본질 적인 질문으로 바꾼다면 “react 왜 써요?” 보다는 “왜 SPA(vue, angular, svelte)로 만들어요?” 라는 질문이 더 좋다 생각한다. 하지만 거기까지 파고들진 말자.
•
일단 필자가 react를 선택한 이유부터 확인해보자.
react 선택 이유
1.
컴포넌트 재사용으로 인한 생산성 향상
•
다른 SPA 라이브러리가 아닌 react 선택의 killing point가 되기에는 충분치 않다.
•
하지만 plain HTML 코드 작성과 비교해서는 확실히 장점이 존재한다. plain HTML 코드해가며 프로젝트 모든 페이지에 존재하는 네비게이션바를 복사/붙여넣기 해본 경험이 있다면 모두 알것이다. → SPA의 장점이지 리액트의 장점은 아니다.
◦
모든 페이지에 대한 개발이 90% 정도 이루어진 상태에서 “헤더에 링크 하나 더 추가됐구요 border-radius 바뀌었어요 다 수정해주세요”에서 그거 전부 다시 페이지 들어가서 헤더를 모두 수정해야하는 슬픈 상황도 존재한다.
◦
하지만 대부분의 SPA를 사용하지 않는 서비스는 정적 HTML 만으로 서비스 하지 않고 PHP나 ASP와 같은 서버 프레임워크에서 컴포넌트를 지원하는 케이스가 존재하긴 한다. 이 부분은 FE의 영역이 아니니 PASS
2.
성능
•
3. 리액트 미신 - 리액트는 빨라요에서 자세한 내용을 확인할 수 있다.
•
결과적으로 이야기 하자면 무조건 성능으로만 따져서는 plain HTML, JS 로직을 이용하여 SPA를 만드는게 맞다.(자신 있으면)
•
그래도 SPA + TS 지원되며 컴포넌트를 활용 할 수 있는 개발을 하기 위해서는 svelte를 사용하는게 맞다. 성능의 이점만을 위해 리액트를 사용한다는건 설득력이 떨어진다. 아니, 틀리다 보는게 맞다.
3.
커뮤니티와 라이브러리 생태계
•
필자에게는 react 사용의 제일 큰 이유다. 이미 많은 이용자들이 사용하고 있으며 이슈가 생겼을때의 해결이 쉬우며 프레임 워크만해도 많은 선택이 존재한다. 이거 라이브러리 없을까? 하는 라이브러리들은 이미 존재하며 이거 괜찮을까?라는 고민은 이미 많은 개발자들이 했고 대부분 개선된 상태다.
4.
채용
•
현실적인 이유다. “이번에 나온 Blazor봤어? 웹어셈블리로 컴파일 해준데!”, “야 svelte가 그리 좋다던데? 엄청 편하고 빠르다 하더라구!”와 같은 말들을 많이 듣지만 현실적으로 FE 개발자 채용의 90%이상은 react다.
•
채용하는 기업 입장에서도 똑같이 적용되는 사항이다. 대다수 개발자들이 사용하지 않는 기술 스택을 선택할 경우 사람구하는게 더 힘들다. 물론 기업에 속한 개발자 또한 갈 수 있는 곳이 흔치 않아 lock-in 현상고통으로 잘 안나가는 경우도 생기지만, 사람일은 모르는것. 갈 사람은 간다. 어떻게 해서든.
5.
배우기 쉽다?
•
사람마다 다른 개인적 편차가 존재하는 부분이다.
•
개인적으로 react, vue와 virtual DOM을 사용하는 라이브러리들 보다 차라리 virtual DOM 없이 개발할 수 있는, 더 직관적인 코드를 가진 svelte가 더 쉽고 편하지 않았나? 하는 생각이든다.
•
본인은 vue 개발을 해본적이 없다. 하지만 똑같은 virtual DOM을 사용하는 vue의 경우에도 react, vue 둘다 개발해본 사람들 말로는 vue가 더 쉬웠다고 한다.
결론
•
아래 3가지 이유 때문에 우리는 리액트를 사용한다.
◦
컴포넌트 재사용으로 인한 생산성 향상
◦
커뮤니티와 라이브러리 생태계
◦
채용
•
“UI와 실제 변화된 데이터의 불일치를 없애고… 생략”과 같은 이유도 react 선택에 해당되는 이유다. 하지만 UI와 실제 변화된 데이터의 불일치를 쉽게 해결하기 위한 다른(같은) 방법들을 가진 다른 라이브러리들인 vue, svelte를 선택하지 않고 react를 선택한 이유에는 해당되지 않는다.
•
필자가 이야기 하고싶은 것은 “리액트는 빨라서, 또는 성능 때문에 쓰는게 아니다”이다.
•
우리는 기술을 사용할때 우리가 기술을 사용하는 이유를 알고 명확한 한계점을 알아야한다. 리액트는 잘짜여진(하드코딩으로 batching로직 잘짜고 Virtual DOM을 사용하지 않는…아 벌써 머리아프다) vanilla JS로 만든 SPA를 퍼포먼스면 에서 더 낫다. 다음장에서 알아보자.
•
번외로 굳이 리액트 사용 및 인기의 가장큰 이유는 채용의 용이성, 취직의 용이성 때문이라 생각한다.
React 성장의 무한 루프 - https://tkdodo.eu/blog/why-react-isnt-dying
3. 리액트는 빠르지 않다 - 벤치마크의 맹점
•
필자는 1 장에서 “리액트는 빠르거나 성능 때문에 사용하지 않는다” 했다. 그 이유를 유명 벤치마크를 통해 설명하겠다.
•
개발자는 기술을 선택하거나 평가할 때 벤치마크를 기반으로 결정한다. 하지만.. 맹목적으로 벤치마크를 기반으로 기술을 평가하는 것이 과연 옳은 선택일까?
vanillajs vs SPA 라이브러리들
•
vanillajs, vue, svelte, react-hooks, balzor-wasm을 크롬 112(글 작성시 최신 벤치마크 결과)기준으로 같은 DOM 조작에 대해 확인한 벤치마크 결과다.
SPA, vanilla js 비교 벤치마크 부분 스크린샷
•
please bare with me - 일단 이 벤치마크가 주는 정보만 나열해보겠다.
◦
vanillajs와 비교했을때 다른 SPA 라이브러리들은 대체적으로 느리다.
◦
blazor wasm은 진짜 엄청 느리다.
◦
vanillajs는 모든 케이스에서 제일 빠르며 다른 SPA 라이브러리와 비교 시 심한 경우 2배이상 빠른 경우도 있다.
React는 빠른가?
•
간단하게 정리하자면 vanillajs보다 느리다.
•
빠를 수 없다.
◦
컴포넌트를 렌더링 후 (여기서 렌더링은 화면에 그리는걸 말하는게 아니다. react.dev 공식문서에서 render and commit 항목을 확인하자.) 실제 DOM과 Virtual DOM의 변경사항을 비교하고 바뀐 사항만 페인트 하는것과 그냥 해당 페인트 하는것과는 다르니…
•
그럼에도 불구하고 특정 항목을 제외하고는 vanillajs와 퍼포먼스적인 면에서 큰 차이가 나지 않는다.
blazor wasm
•
FE 개발자 뿐만아니라 개발자로서 이 벤치마크를 본다면 우리는 “blazor wasm은 못쓰겠는데?”라는 소리가 저절로 나온다. 하지만 실제로 사용하고 있는 서비스들도 있고 blazor로 직접 개발하고 있는 필자의 지인들도 있다. 그렇다는 것은… blazor를 서비스에 사용하는 기업은 “퍼포먼스는 신경 전혀 안쓰는, 유저 친화적이지 않은, C#만 사랑하는 나쁜 기업일까?”
◦
실제 blazor wasm을 사용하여 개발하고 있는 지인 프로덕트에서는 “프로덕트가 너무 느려요”와 같은 퍼포먼스관련 CS는 들어오지 않는다. 아직 단 하나도 없었다고한다.
•
테스트 항목을 확인할 경우 우리가 놓치고 있는 맹점들을 확인할 수 있다.
◦
create rows → creating 1000 rows
◦
partial update → update every 10th row for 1000 rows (16x cpu slowdown)
◦
select row → highlighting a selected row(16x cpu slowdown)
◦
swap rows → swap 2 rows table with 1000 rows. (4x cpu slowdown)
•
1000행 생성, 16배 느린 CPU 속도와 같은 극단적인 케이스 임을 알 수 있다.
극단적인 테스트 항목 - 1000행 생성
•
하나의 이벤트 핸들링 또는 페이지 방문에 1000행 생성되는 케이스는 흔치 않다. 프로덕트마다 다르겠지만 0에 수렴한다 생각한다.
•
극단적인 예시로 만약 1000행을 생성한다 하더라도 이용자 기기의 화면에 1000행의 데이터를 의미있게, 가독성 좋게 보여주는 것은 불가능에 가깝다.
◦
100행 만들고 페이지네이션으로 페이지를 나누거나 스크롤하며 내릴때마다 intersection ovserver를 이용하여 row를 더 생성하는 것 과 더 효율적이고 좋은 방법들이 있다.
•
그럼에도 불구하고, 꼭 한번에 1000행을 만들어야하는 요구사항이 존재한다.(위 벤치마크는 ms기준이다.)제일 퍼포먼스가 안좋은 blazor-wasm의 경우에도 121ms가 소요된다. → 0.121초가 걸린다는 뜻.
•
“과연 유저들은 vanillajs의 0.039초와 blazor wasm의 0.121초의 차이를 느낄 수 있을까?”
◦
0.082초의 차이는 대다수의 이용자가 직접적으로 인지하기는 힘든 시간이다.
◦
구글 web vitals의 INP(Interaction to Next Paint) 기준에서는 200ms이내이다. blazor wasm을 사용하더라도 이용자가 느리다고 자각하기 어려운 response 속도라는 뜻이다.
극단적인 테스트 항목 - 16배 느린 CPU 속도
14인치 맥북 프로 m2 pro geekbench 점수
•
벤치마크는 벤치마크일 뿐이다. 알고 있다. 하지만 절대적인 16배 느린속도 비교에는 제일 합리적인 기준이라 판단했다.
•
위 테스트는 14인치 맥북 프로에서 진행됐다. 23년식 2023 macbook pro 14의 geekbench 싱글코어 점수는 2632이다. → 16배 느린 케이스면 164.5점인 CPU를 찾으면…
◦
2009년에 출시한 intel core 2 Duo(듀얼코어다 듀얼코어)보다 낮은 점수이다. 더 낮은 점수를 찾아보려 했으나.. 그 이전의 데이터는 존재하지 않았다.
◦
모바일의 케이스의 경우 제일 낮은 케이스인 아이패드 미니 4 조차도 기준점인 164.5점의 두배 이상이다.
•
1000행 생성하는 케이스와는 다르게 느린 CPU 속도는 충분히 염려해야할 상황일 수 있지만, 16배나 느린속도는 너무나도 과한 edge case에 대한 염려다. → “생각보다 우리의 단말기의 퍼포먼스는 좋다.“
결과
•
1000행 생성 , 1000행 업데이트, 16배 느린 상황에서 1000행 중 100행 업데이트, 16배 느린 상황에서 선택된 행 하이라이트, 1000행 테이블에서 2행 swap와 같은 테스트 항목은 아주 극단적인 케이스이다.
◦
이런 극단적인 케이스임에도 불구하고 실제 유저들은 차이를 느끼기 어렵다.(몇몇 극극극단적인 케이스 제외)
•
물론 벤치마크는 극단적인 상황에서 비교해야 극단적인 차이를 확인 할 수 있기 때문에 극단적인 벤치마크 항목이 잘못됐다 생각하지 않는다. 오히려 사용하려는 기술에 대한 정확한 파악은 기술 채택 전 필수라고 생각한다. 하지만 우리는 이런 벤치마크에서 정보를 얻을 때 “벤치마크들의 테스트 항목은 극단적이다.”라는 사실을 자각해야한다. 벤치마크를 맹목적으로 믿고 따르지 말고 참조하는 자세로 받아들여야한다.
◦
벤치마크는 비교대상간의 차이점을 보여주기 위해 이런 극단적인 상황을 부여한다. 6ms
32ms 보다 106ms
516ms가 더 와닿지 않는가?
•
“프로덕트에 1000줄의 테이블을 한번에 생성하는 극단적인 요구사항이 존재하는가?”와 같은 질문을 항상 던져보자.
◦
만약 극단적인 요구사항이 존재한다 하더라도 기술적으로 개선할 방법은 너무 많다.
•
일반적인 상황에서 blazor-wasm은 못 써먹을 정도로 느리지 않다.
◦
blazor-wasm을 채택하는 기업들은 속도가 상대적으로 느림에도 채택한 이유가 분명하다.
1.
C# 개발자들이 좀더 쉽게 FE,BE를 개발할 수 있다.
2.
wasm(web assembly)으로 컴파일되는 미래기술을 맛볼수 있다!
3. 리액트 미신 - 리액트는 빨라요
He said…
•
react, redux 오픈소스에 큰 기여를한(아버지라 해도 되지 않을까?) Dan abramov의 트위터다. 사실 이 한마디로 필자의 질문에 대답이 된다고 생각한다.
Reality : it helps create maintainable applications, and is *fast enough* for most use cases.
•
react는 vanillajs를 이용한 DOM 직접 조작 보다 느리다.
◦
vanillajs로 DOM 직접 조작 하는 것 보다 하는일이 더 많으니 당연히 느릴 수 밖에없다.
◦
react는 Virtual DOM이 메모리상에 존재하고 Virtual DOM과 DOM의 변경점을 비교하는 시간도 존재하며 상태값이 변경될 때 마다 리렌더링되니 그럴수 밖에 없다.
•
*fast enough* → 제일 중요한 문장
◦
“대부분의 사용 케이스에서 충분히 빠르다”로 해석 될 수 있다. 그렇다 react는 빨라서 사용하는게 아니라 SPA의 특징 또는 장점(컴포넌트화, 앱과 같은 사용성)들을 가지고 있지만 충분히 빠르기 때문에 사용하는 라이브러리다.
•
2.리액트는 빠르지않다 - 벤치마크의 맹점에서 확인했듯이 벤치마크는 극단적인 테스트 항목을 가지고 있다. 벤치마크가 아닌 실제 프로덕트와 적당한 성능의 단말기를 사용하는 상황이면 vanillajs와 react의 퍼포먼스 차이는 더더욱 줄어든다. 이쯤 되면 아마 유저들이 느끼지 못할 퍼포먼스 차이까지로 좁아진다.
•
마지막으로 필자는 리액트 사용 이유를 아래와 같이 정의한다.
react는 유지관리가 쉬운 코드를 작성할 수 있게 도와주며 SPA의 장점들을 가지고 있는 프로덕트를 만들 수 있도록 도와준다. vanillajs와 비교했을 때 퍼포먼스 적으로 느리긴 하지만 대부분의 use cases에서 충분히 빠른 퍼포먼스를 보여준다.
3-1. 느리면 빠른걸 직접 만들겠다.
•
“react, vue, svelte의 느림을 나는 못견디겠다. 실제로 사용하는 기능도 많이 없는데 vanillajs로 프로덕트를 만들겠다!”라는 생각이 들때…
◦
react나 vue는 vanillajs에 비해 느리다. 어쩔 수 없는 사실이다. 느리다는 이유 하나만으로 온갖 최적화와 DX 개선이 고려된 라이브러리들을 버리고 vanillajs를 이용하여 본인이 직접 빠른 SPA를 만들것인가?
◦
적당한 퍼포먼스를 희생하고 좋은 DX를 가지고 있어 error에 조금더 안전해지고, maintainable한 코드를 작성 할 수 있는 시중에 라이브러리들을 포기하고
•
vanillajs가 리액트에 비해 빠른것도 잘짜여졌을 경우에만 해당된다.
◦
한가지 예로 react, svelte(vue는 해보지 않아서 잘 모르겠음)에선 DOM 업데이트를 자동으로 batching한다. “아마도” 당신이 만든 SPA는 자동 batching 로직까지 직접 만들어야한다. → 아무리 DOM 업데이트 속도가 더 빠르다 해도 batching을 이용하여 업데이트가 수행되는 횟수 자체를 줄이는게 더빠르지 않겠는가?
•
이제 어찌저찌 만들었다
1.
어쩔 수 없이 시중에 존재하는 SPA 라이브러리를 사용했을 때 보다 에러가 더 존재할 것이다. 직접 개인이 만들었기 때문에 수년간 관리되어오던 오픈소스 라이브러리 보다 훨씬 error prone일것이다. 아니, 돌아가게 만드는것 자체가 기적. 직접 만든 SPA 라이브러리의 오류와 함께 추가로 서비스 자체의 오류도 해결해야한다.
2.
결과적으로 내가만든 SPA 라이브러리가 react, vue보다 더 나은 생산성을 보여주는가?
•
SPA라이브러리를 만드느라 걸린 시간 + 프로덕트 개발에 필요한 시간 + 추가되는 요구 사항에 맞춘 SPA 라이브러리 기능 추가 시간까지 전부 합치게 되면 시중의 SPA 라이브러리를 사용했을 때 보다 최소 3배 4배 정도 더 걸린다.
꼭 그래야만..하니?
•
직접 SPA 라이브러리를 만들고 최적화하는 경험이 본인의 커리어에 도움되는건 믿어 의심치 않는다. 좀더 SPA 생태계에 대한 이해도가 높아지고 본인의 포트폴리오 하나가 더 생기는것이니. 개인적인 면에서는 너무 좋은 선택이다. 직접 한번쯤 만들어 보는걸 추천한다.
◦
시중에 있는 SPA 라이브러리들은 최선의 선택일 뿐 정답이 아니다. 당신은 할 수 있다고 믿는다.
•
하지만(しかし) 회사에서 “우리는 회사로 부터 경제적 보상을 받고 일을하는 직원”이다. R&D와 같은 부서가 아닌 이상 우리는 정해진 시간안에 충분히 빠르고 에러로 부터 안전한 프로덕트를 만들어낼 의무가 있다.(심지어 R&D 부서도 due date가 정해져있다.)
•
정해진 시간안에 충분히 빠르고 에러로 부터 안전한 프로덕트를 만들기 위해서는 특이한 요구사항이 없지않는 이상 이미 시중에 존재하는 SPA 라이브러리들을 사용하는것이 옳은 선택이다.
◦
“내가 했던 고민 이미 누군가 했었고 , 내가 마주쳤던 이슈 이미 누군가 마주쳤었다.” → 커뮤니티와 라이브러리 생태계의 중요성이다.
◦
아마 당신이 받은”특이한 요구사항”에 대한 구현도 누군가는 시도해봤을 확률 또한 높다. 사용하려는 기술의 커뮤니티와 라이브러리 생태계는 생각보다 너무나도 중요하다. 결과적으로 본인이 일하는 시간과 깊은 연관이 되어있다.
◦
“내가 찾는 것이 없을 경우 높은 확률로 내가 찾는것이 잘못된것 또는 없는 것 일 수 있다”는걸 잊지말자.
4. 결론
기술 파악
•
우리 모두 알고있고 너무나도 당연한 사실이지만 기술을 선택하고 사용하기 전에 본인이 사용하려는 기술의 장점, 명확한 한계점, 해당 기술로 해결하고자 하는 문제점을 파악해야한다.
◦
물론 프로덕트 개발하면서 일정, 사람에 쫓겨 업무하다보면 제대로된 파악없이 빠르게 선택하고 도입해야할 때가 있다. 이럴땐 사용자가 제일 많은 기술을 사용하자, 만약(아니 당연하게)이슈가 있더라도 나 이전에 누군가가 이미 마주쳤을 것 이다.
UX의 전부는 퍼포먼스가 아니다
“아마도 유저들이 느끼지 못할 퍼포먼스적 이점을 위해 기술 스택을 변경하고 코드를 갈아엎는 것 보다 유지보수가 쉬운 코드를 작성하는것이 DX 향상에 도움이 되며 DX 향상은 결과적으로 UX 향상을 의미한다.”
"performance" === "UX" // false
TypeScript
복사
•
좋은 UX(User Experience)를 결정하는 기준은 프로덕트의 퍼포먼스만이 아니라 프로덕트의 사용성, 접근성, 디자인/이용자 상호작용의 일관성, 보안과 같은 요소들도 포함된다. “당연히 프로덕트의 좋은 퍼포먼스는 더 나은 UX에 더 가까워지지만 좋은 퍼포먼스만으로 UX가 좋은 프로덕트라 할수는 없는것.”(너무나도 당연하게)
•
좋은 퍼포먼스를 가진 프로덕트는 좋은 UX를 가진 프로덕트가 될 가능성이 더 높아지지만 퍼포먼스만 좋은 프로덕트는 좋은 UX를 뜻하지 않는다. 일관적이고 error prone하지 않으며 버그가 적은 UI를 만들기란 어렵고도 복잡한 작업이다. 하지만 “react, css in js와 같이 퍼포먼스 적으로 충분히 빠르지만 DX 향상에 큰 도움을 주는 기술”은 error prone 하지않고 버그가 적은 UI를 쉽게 만들 수 있게 해준다.
◦
아마도 유저가 눈치채지 못할 퍼포먼스 차이에 목메지 말자.
•
우리가 사용하는 React, TypeScript, ESLint, Testing, CI/CD, css in js와 같은 작은 기술들이 모이고 모여 좋은 접근성을 가지고 더 빠르고, 버그 및 에러가 적으며 일관된 방식으로 작동하는 프로덕트를 개발할 있도록 도와준다. → 결과적으로 개발자들이 DX(Developer Experience)에 투자할 수록 이용자들에게 좋은 UX를 제공할 수 있게 된다.