-
Notifications
You must be signed in to change notification settings - Fork 166
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[개인 미션 - 성능 오답노트] 해리(최현웅) 미션 제출합니다. #151
Changes from 10 commits
0ccfd1e
1939747
80e846c
b08b3cc
6a70c42
45df67d
135d4f4
2539760
a921117
ceb859d
c543650
64f4449
3d3b433
c8a5c31
405fcad
6666981
f6b4a19
77ef0c2
5497ed7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,22 +9,23 @@ | |
"scripts": { | ||
"build:dev": "webpack --mode=development", | ||
"build:prod": "webpack --mode=production --node-env=production", | ||
"prod": "webpack serve --mode=production", | ||
"dev": "webpack serve --mode=development", | ||
"watch": "webpack --watch", | ||
"serve": "webpack serve --mode=development", | ||
"prettier": "prettier --write .", | ||
"deploy": "npm run build:prod && npx gh-pages -d dist" | ||
}, | ||
"keywords": [], | ||
"author": "woowacourse", | ||
"homepage": "https://{username}.github.io/perf-basecamp", | ||
"homepage": "https://hwinkr.github.io/perf-basecamp", | ||
"license": "MIT", | ||
"dependencies": { | ||
"@giphy/js-fetch-api": "^4.1.1", | ||
"@giphy/js-types": "^4.2.1", | ||
"classnames": "^2.3.1", | ||
"react": "^18.2.0", | ||
"react-dom": "^18.2.0", | ||
"react-icons": "^4.4.0", | ||
"react-icons": "5.3.0", | ||
"react-router-dom": "^6.3.0" | ||
}, | ||
"devDependencies": { | ||
|
@@ -41,6 +42,7 @@ | |
"babel-loader": "^8.2.2", | ||
"copy-webpack-plugin": "^9.0.1", | ||
"css-loader": "^6.2.0", | ||
"css-minimizer-webpack-plugin": "7.0.0", | ||
"dotenv-webpack": "^7.0.3", | ||
"eslint": "^8.0.1", | ||
"eslint-config-prettier": "^8.5.0", | ||
|
@@ -53,18 +55,28 @@ | |
"file-loader": "^6.2.0", | ||
"html-loader": "^2.1.2", | ||
"html-webpack-plugin": "^5.3.2", | ||
"image-minimizer-webpack-plugin": "4.1.0", | ||
"mini-css-extract-plugin": "2.9.1", | ||
"prettier": "^2.3.2", | ||
"sharp": "0.33.5", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 오?? 어디에 쓰이는 친구인가요? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 저는 이미지 파일 확장자나 크기를 직접 변경하는 방법을 선택하지 않고, 웹팩에서 제공해 주는 플러그인을 활용했습니다. 해당 문서에서 제시하는 방법 중, sharp를 사용하게 되었습니다. optimization: {
minimizer: [
`...`,
new CssMinimizerPlugin(),
new ImageMinimizerPlugin({
minimizer: {
implementation: ImageMinimizerPlugin.sharpMinify,
options: {
encodeOptions: {
webp: {
quality: 80
}
}
}
},
generator: [
{
preset: 'webp',
implementation: ImageMinimizerPlugin.sharpGenerate,
options: {
encodeOptions: {
webp: {
quality: 80,
lossless: false
}
}
}
}
]
})
], 그래서 설치하게 되었습니다. |
||
"style-loader": "^3.2.1", | ||
"ts-loader": "^9.3.1", | ||
"typescript": "^4.8.2", | ||
"webpack": "^5.88.2", | ||
"webpack-cli": "^4.7.2", | ||
"webpack-bundle-analyzer": "4.10.2", | ||
"webpack-cli": "4.10.0", | ||
"webpack-dev-server": "^3.11.2" | ||
}, | ||
"sideEffects": [ | ||
"*.css" | ||
], | ||
Comment on lines
+70
to
+72
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이 친구를 추가해서 어떤 변화가 있는 건가요?? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
"sideEffects": false, 위와 같이 설정하니 스타일이 깨지는 문제가 있어 css 파일은 tree shaking이 적용되지 않도록 했어요! |
||
"babel": { | ||
"presets": [ | ||
"@babel/preset-env", | ||
"@babel/preset-react" | ||
"@babel/preset-react", | ||
{ | ||
"modules": "false" | ||
} | ||
], | ||
"plugins": [ | ||
"@babel/plugin-transform-runtime" | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,22 +1,25 @@ | ||
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom'; | ||
|
||
import Home from './pages/Home/Home'; | ||
import Search from './pages/Search/Search'; | ||
const Home = lazy(() => import('./pages/Home/Home')); | ||
const Search = lazy(() => import('./pages/Search/Search')); | ||
|
||
import NavBar from './components/NavBar/NavBar'; | ||
import Footer from './components/Footer/Footer'; | ||
|
||
import './App.css'; | ||
import { lazy, Suspense } from 'react'; | ||
|
||
const App = () => { | ||
return ( | ||
<Router basename={'/perf-basecamp'}> | ||
<NavBar /> | ||
<Routes> | ||
<Route path="/" element={<Home />} /> | ||
<Route path="/search" element={<Search />} /> | ||
</Routes> | ||
<Footer /> | ||
<Router> | ||
<Suspense fallback={<div>loading...please wait...</div>}> | ||
<NavBar /> | ||
<Routes> | ||
<Route path="/" element={<Home />} /> | ||
<Route path="/search" element={<Search />} /> | ||
</Routes> | ||
<Footer /> | ||
</Suspense> | ||
</Router> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. NavBar와 Footer도 Suspense로 감싼 이유가 궁금합니다! NavBar와 Footer는 Home이나 Search보다 빠르게 로드되니까 Suspense로 감싸게 되면 오히려 렌더링이 늦어지는 단점이 존재할 것 같은데 해리는 어떻게 생각하시나요🤔 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 혹시 Layout Shift 때문에 NavBar와 Footer도 Suspense로 감싸신 거라면 Layout Shift가 일어나지 않게 로딩 화면이 화면 전체를 차지하게 만들면 일어나지 않아요. 다만 이러면 Home 페이지에서도 lazy를 적용하니까 첫 페이지(Home)에 들어갔을 때, 로딩 시간이 걸린다는 단점이 있을 수 있겠네요😭 const App = () => {
return (
<Router>
<NavBar />
<Suspense fallback={<div style={{ width: '100vw', height: '100vh' }}>Loading...</div>}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/search" element={<Search />} />
</Routes>
</Suspense>
<Footer />
</Router>
);
}; There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 쑤쑤 정말 좋은 피드백 감사합니다! 😆
저도 그럴 것 같다고 생각합니다! Suspense는 UI를 그릴 준비가 완료될 때까지 fallback UI를 보여주므로 현재 저와 같이
로딩 시간이 걸린다는 단점이 있을 것이라고는 생각하지 않습니다. 사용자가 제일 처음 memegle 사이트에 방문하게 되면 Home 페이지를 그리기 위한 리소스들을 다운로드 받을 것이고, 그 시간 동안 fallback UI를 보여주게 됩니다. 네트워크 환경에 따라서 로딩 시간의 차이는 있겠지만 lazy를 적용하더라도 바로 리소스들을 다운로드 받을 것이라고 예상되어 lazy를 적용하기 때문에 로딩 시간이 더 걸릴 것이라고 생각하지는 않아요! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
흠.... 저는 단순하게 사람들이 대부분 Home 페이지로 접근을 하니까 Home에도 lazy가 걸려 있으면 초기 로딩 지연이 존재할 것 같았어요. 그래서 사용자 입장에서 좋진 않을 것 같다?고 생각해서 Home에 lazy를 주지 않는 방식도 생각했어요. 그런데 그렇게 하게 되면 Home 페이지 js 파일 분리가 안 되겠네요... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
저도 이 방법을 생각했지만, 서비스에 익숙한 사용자라면 바로 url을 통해서 Search 페이지에 접근할 수도 있다고 생각했어요. (제가 딱 그렇거든요,,,자주 쓰는 서비스면 그냥 url을 외우고 입력해서 바로 들어갑니다.) 그렇다면 사용자는 Search 페이지에 있지만 Home 페이지를 그리기 위한 리소스도 함께 다운받게 되겠죠. Home 페이지에 |
||
); | ||
}; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,7 +2,7 @@ import { GifsResult } from '@giphy/js-fetch-api'; | |
import { IGif } from '@giphy/js-types'; | ||
|
||
import { GifImageModel } from '../models/image/gifImage'; | ||
import { apiClient, ApiError } from '../utils/apiClient'; | ||
import { apiClient, apiClientWithCache, ApiError } from '../utils/apiClient'; | ||
|
||
const API_KEY = process.env.GIPHY_API_KEY; | ||
if (!API_KEY) { | ||
|
@@ -43,7 +43,7 @@ export const gifAPIService = { | |
* @returns {Promise<GifImageModel[]>} | ||
* @ref https://developers.giphy.com/docs/api/endpoint#!/gifs/trending | ||
*/ | ||
getTrending: async (): Promise<GifImageModel[]> => { | ||
getTrending(): Promise<GifImageModel[]> { | ||
const url = apiClient.appendSearchParams(new URL(`${BASE_URL}/trending`), { | ||
api_key: API_KEY, | ||
limit: `${DEFAULT_FETCH_COUNT}`, | ||
|
@@ -52,6 +52,14 @@ export const gifAPIService = { | |
|
||
return fetchGifs(url); | ||
}, | ||
|
||
getTrendingWithCache: async () => { | ||
return await apiClientWithCache({ | ||
queryKey: 'trending-gifs', | ||
staleTime: 1000 * 60 * 5, | ||
queryFn: gifAPIService.getTrending | ||
}); | ||
}, | ||
Comment on lines
+56
to
+62
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 세션 스토리지, 캐시 스토리지 사용법에 익숙하지 않기도 했고 캐싱 역할을 가지는 모듈을 직접 구현해 보고 싶기도 해서 |
||
/** | ||
* 검색어에 맞는 gif 목록을 가져옵니다. | ||
* @param {string} keyword | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,10 +2,10 @@ import { useRef } from 'react'; | |
import { Link } from 'react-router-dom'; | ||
import classNames from 'classnames/bind'; | ||
|
||
import heroImage from '../../assets/images/hero.png'; | ||
import trendingGif from '../../assets/images/trending.gif'; | ||
import findGif from '../../assets/images/find.gif'; | ||
import freeGif from '../../assets/images/free.gif'; | ||
import heroImage from '../../assets/images/hero.png?as=webp'; | ||
import trendingGif from '../../assets/images/trending.gif?as=webp'; | ||
import findGif from '../../assets/images/find.gif?as=webp'; | ||
import freeGif from '../../assets/images/free.gif?as=webp'; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. png, gif 모두 webp로 변환하셨는데 최신 브라우저에서는 webp를 모두 지원하지만 사파리 옛날 버전과 같이 webp를 지원하지 않는 경우가 은근히 있더라고요. https://caniuse.com/?search=webp ![]() There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. gif를 webm으로 변환할 지 고민하셨다고 했는데 최종적으로 webp로 변경하신 이유가 있으실까요? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ![]() ![]() 오호.. 쑤쑤 덕분에 잊고 살았던 Can i Use 웹 사이트를 다시 방문해서 jpeg, webp 확장자를 지원하는 브라우저 버젼을 비교해 보는 시간을 가졌습니다. Safari, FrieFox를 제외하면 webp가 더 많은 브라우저 버젼을 지원해 주는 것으로 확인되는데요. 그래도 훨씬 더 많은 브라우저 버젼을 지원해 주기 위해서 jpeg 확장자도 포함을 해야겠네요! 왜 png가 아닌 jepg를 사용하는지 추가로 궁금해하실 수도 있을 것 같은데요. ![]() 위 내용들을 참고해서, 이미지 최적화에는 Jpeg를 추천하고, 손실 압축을 사용하기 때문에 크기를 더 작게 만들 수 있고, 투명도가 필요한 이미지가 아니기 때문에 Jpeg를 사용하기로 했어요. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
어떤 파일 확장자를 사용할지에 대해서 정말 많은 고민을 했었는데요. gif도 webp로 변환할 수가 있고, 용량도 줄여주기 때문에 사용했었습니다. webpack 파일에 설정을 해두고 png, gif를 webp로 한 번에 변환하는 편함도 느끼기 위한것도 의사 결졍 요소 중 하나였습니다. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
이 부분이 상당히 납득이 되네요👍 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 바로 그거에요 쑤코톡! |
||
|
||
import FeatureItem from './components/FeatureItem/FeatureItem'; | ||
import CustomCursor from './components/CustomCursor/CustomCursor'; | ||
|
@@ -23,7 +23,7 @@ const Home = () => { | |
<section className={styles.heroSection}> | ||
<img className={styles.heroImage} src={heroImage} alt="hero image" /> | ||
<div className={styles.projectTitle}> | ||
<h1 className={styles.title}>Memegle</h1> | ||
<h1 className={styles.title}>memegle</h1> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 오잉? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. cloudfront 캐싱을 적용한 후 배포 파일이 변경되었을 때, 브라우저가 새로운 js 파일을 받아오는지 테스트 하기 위해서 약간의 변화를 줬었는데 다시 수정한다는 것을 깜빡했네요 ㅎ |
||
<h3 className={styles.subtitle}>gif search engine for you</h3> | ||
</div> | ||
<Link to="/search"> | ||
|
@@ -44,7 +44,7 @@ const Home = () => { | |
</Link> | ||
</div> | ||
</section> | ||
<CustomCursor text="memegle" /> | ||
<CustomCursor text="쑤쑤나는해리야" /> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
</> | ||
); | ||
}; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,8 +14,7 @@ const CustomCursor = ({ text = '' }: CustomCursorProps) => { | |
|
||
useEffect(() => { | ||
if (cursorRef.current) { | ||
cursorRef.current.style.top = `${mousePosition.pageY}px`; | ||
cursorRef.current.style.left = `${mousePosition.pageX}px`; | ||
cursorRef.current.style.transform = `translate(${mousePosition.pageX}px, ${mousePosition.pageY}px)`; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 쑤쑤의 질문 타임~
단순히 reflow를 일으키지 않아서. 안됩니다. 코린이도 이해하기 쉽게 설명해주세요^^ There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 하하 꼭 제 조카가 물어보는 것 같군요^^ 코린이도 이해할 수 있도록 정말 low level에서 설명해 보자면,
translate는 서울 주민이기 때문입니다. translate 속성을 사용하면 브라우저는 translate로 결정되는 요소들의 위치, 크기를 reflow가 발생하는 레이어(부산)이 아닌 새로운 레이어(서울)에서 처리하게 됩니다. 그래서 reflow가 발생하지 않게 됩니다. traslate 속성은 CPU가 아닌 GPU를 사용하게 되며 GPU는 브라우저의 메인 스레드와 별개로 동작하기 때문에 성능에도 좋다고 합니다. 제 설명이 부족했을수도 있지만, z-index의 동작 방식을 완전히 파악하고 있는 쑤쑤라면 제 설명을 충분히 이해할 수 있을 것이라고 믿어요!!! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 10점 만점의 8.7점 드리겠습니다.There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 1.3점이 깎인 부분을 설명해 주세요 |
||
} | ||
}, [mousePosition]); | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,17 +1,31 @@ | ||
import { useEffect } from 'react'; | ||
import { useEffect, useRef } from 'react'; | ||
|
||
type ScrollHandler = () => void; | ||
|
||
const useScrollEvent = (onScroll: ScrollHandler) => { | ||
const requestAnimationFrameRef = useRef<number | null>(null); | ||
|
||
useEffect(() => { | ||
const handleScroll = (event: Event) => { | ||
onScroll(); | ||
const handleScroll = () => { | ||
if (requestAnimationFrameRef.current) { | ||
cancelAnimationFrame(requestAnimationFrameRef.current); | ||
return; | ||
} | ||
|
||
requestAnimationFrameRef.current = requestAnimationFrame(() => { | ||
onScroll(); | ||
requestAnimationFrameRef.current = null; | ||
}); | ||
}; | ||
|
||
window.addEventListener('scroll', handleScroll); | ||
|
||
return () => { | ||
window.removeEventListener('scroll', handleScroll); | ||
|
||
if (requestAnimationFrameRef.current) { | ||
cancelAnimationFrame(requestAnimationFrameRef.current); | ||
} | ||
}; | ||
}, [onScroll]); | ||
}; | ||
Comment on lines
+1
to
31
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Partially Presented Frame을 최소로 발생시키기 위해 requestAnimationFrame을 활용하셨군요. 해결하지 못해서 고민하고 있었는데 한번 사용해 봐야겠네요👍 다만 크게 개선되지 않는 것 같아서 고민이네요....😅 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 요구사항 자체가 Partially Presented Frame 현상을 최소화하는 것이라, 아예 없애는 것은 불가능할 것 같다고 판단해서 Request Animation Frame API를 활용해서 최소화해 보는 것으로 만족했습니다! |
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,7 +10,7 @@ | |
|
||
.selectedItemContainer { | ||
position: fixed; | ||
right: -320px; | ||
right: 0; | ||
top: 0; | ||
width: 320px; | ||
height: 100%; | ||
|
@@ -19,12 +19,14 @@ | |
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1); | ||
backdrop-filter: blur(5px); | ||
-webkit-backdrop-filter: blur(5px); | ||
transform: translateX(100%); | ||
opacity: 0; | ||
transition: 0.5s all cubic-bezier(0.82, 0.085, 0.395, 0.895); | ||
will-change: transform, opacity; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 오! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 네넹 해당 속성을 사용하면 브라우저가 위 벨로그 글에 |
||
} | ||
|
||
.selectedItemContainer.showSheet { | ||
right: 0; | ||
transform: translateX(0); | ||
opacity: 1; | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
interface CacheItem<T> { | ||
data: T; | ||
expiredTime: number; | ||
} | ||
|
||
class Cache { | ||
private cache: Record<string, CacheItem<any>> = {}; | ||
|
||
isValidCache(key: string): boolean { | ||
const item = this.cache[key]; | ||
if (!item) return false; | ||
|
||
return Date.now() <= item.expiredTime; | ||
} | ||
|
||
get<T>(key: string): T | null { | ||
if (!this.cache[key]) return null; | ||
|
||
return this.cache[key].data; | ||
} | ||
|
||
set<T>(key: string, data: T, ttl: number): void { | ||
const expiredTime = Date.now() + ttl; | ||
this.cache[key] = { data, expiredTime }; | ||
} | ||
} | ||
|
||
const cache = new Cache(); | ||
|
||
export default cache; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
usedExports
옵션을 각각true/false
로 주고 빌드해보면 사이즈 차이가 꽤나 커요.react-icons
에서도tree shaking
이 적용이 되고 있다는 얘기겠죠.적용은 되고 있지만 완벽하게 되는지? 확실히 모르겠지만 사용되는 아이콘 뿐만 아니라 모든 아이콘을 불러오는 것 같아요. 관련 이슈 참고 바랄게요.
React Icons Imports everything even when included 2 or 3 icons #154
The chunk is very big. #289
기존

react-icons
대신react-icons/all-files
를 이용하니 Gzipped size 기준으로 0.45 KB가 줄었네요.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
쑤쑤 정성 가득한 피드백 정말 감사합니다~ 미션을 진행하면서도
@react-icons/all-files 를 설치할 필요가 있을까? 에 대해서 정말 많이 고민을 했었습니다. 번들 사이즈 관점에서 큰 차이가 없는데 굳이 사용할 필요가 없을 것 같다고 판단해서 사용하지 않았었는데요. 쑤쑤의 피드백을 통해서 다시 한 번 도입 필요성에 대해서 생각해 보게 되었어요. 그 후, 생각을 바꾸고
@react-icons/all-files
라이브러리를 설치하기로 했습니다.그 이유는 아래와 같아요.
1. 불안정성
쑤쑤가 첨부해 준 링크에 들어가서 이슈와 코멘트 내용들을 확인해 보니, 빌드 시 트리 쉐이킹 관련해서 문제가 100% 해결되지 않은 것을 알 수 있었어요.
2. 프로젝트 규모가 커져 아이콘을 많이 사용할 경우
프로젝트 규모가 커지고 라이브러리에서 사용하는 아이콘도 점점 많아질수록 1KB 이내의 차이라도 사용자에게 조금이라도 적은 번들 사이즈를 다운받도록 하는 것이 사용자 경험 측면에서 더 좋을 것 같았어요.
그래서 설치하기로 변경했답니다^^
1.72KB
1.28KB
0.44KB
정도 줄어들었네요.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
단순히 번들 사이즈가 줄어서 설치하는게 아닌 구체적인 이유를 함께 적어주셔서 좋네요👍