-
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 all 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", | ||
"@react-icons/all-files": "4.1.0", | ||
"classnames": "^2.3.1", | ||
"react": "^18.2.0", | ||
"react-dom": "^18.2.0", | ||
"react-icons": "^4.4.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,24 +1,28 @@ | ||
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'}> | ||
<Router> | ||
<NavBar /> | ||
<Routes> | ||
<Route path="/" element={<Home />} /> | ||
<Route path="/search" element={<Search />} /> | ||
</Routes> | ||
<Suspense | ||
fallback={<div style={{ width: '100vw', height: '100vh' }}>loading...please wait...</div>} | ||
> | ||
<Routes> | ||
<Route path="/" element={<Home />} /> | ||
<Route path="/search" element={<Search />} /> | ||
</Routes> | ||
</Suspense> | ||
<Footer /> | ||
</Router> | ||
); | ||
}; | ||
Comment on lines
12
to
27
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. 혹시...유조? |
||
|
||
export default App; |
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,7 +2,8 @@ import { useRef } from 'react'; | |
import { Link } from 'react-router-dom'; | ||
import classNames from 'classnames/bind'; | ||
|
||
import heroImage from '../../assets/images/hero.png'; | ||
import heroImageWebp from '../../assets/images/hero.png?as=webp'; | ||
import heroImageJpg from '../../assets/images/hero.jpg'; | ||
import trendingGif from '../../assets/images/trending.gif'; | ||
import findGif from '../../assets/images/find.gif'; | ||
import freeGif from '../../assets/images/free.gif'; | ||
|
@@ -21,7 +22,15 @@ const Home = () => { | |
return ( | ||
<> | ||
<section className={styles.heroSection}> | ||
<img className={styles.heroImage} src={heroImage} alt="hero image" /> | ||
<picture> | ||
<source srcSet={heroImageWebp} type="image/webp" /> | ||
<img | ||
className={styles.heroImage} | ||
src={heroImageJpg} | ||
alt="hero image" | ||
fetchPriority="high" | ||
/> | ||
</picture> | ||
<div className={styles.projectTitle}> | ||
<h1 className={styles.title}>Memegle</h1> | ||
<h3 className={styles.subtitle}>gif search engine for you</h3> | ||
|
@@ -44,7 +53,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; | ||
} | ||
|
||
|
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.
preload 적용해 주셨군요!!

혹시 아래 사진처럼 보이면 font를 우선적으로 불러오고 있는 건가요??
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.
오 네트워크탭 활용 멋지네요~ 네 맞습니다! 쑤쑤가 보내준 사진처럼 우선적으로 불러오는 것입니다!