posting: fout현상해결도전기 - #75
Conversation
|
@gemini review |
There was a problem hiding this comment.
Code Review
This pull request adds a new blog post detailing strategies to resolve web font FOUT issues, including WOFF2 conversion, font-display settings, and font subsetting. The review feedback identifies invalid CSS syntax in code examples, suggests improving the safety of the font subsetting script by avoiding source file overwrites, and recommends consistent reporting of font size measurements throughout the article.
| async function run() { | ||
| const chars = "보돌코스코어드선수관리"; | ||
|
|
||
| const boldBuffer = fs.readFileSync("public/fonts/Shilla_CultureB-Bold.woff2"); | ||
| const boldSubset = await subsetFont(boldBuffer, chars, { | ||
| targetFormat: "woff2", | ||
| }); | ||
| fs.writeFileSync("public/fonts/Shilla_CultureB-Bold.woff2", boldSubset); | ||
|
|
||
| const mediumBuffer = fs.readFileSync( | ||
| "public/fonts/Shilla_CultureM-Medium.woff2", | ||
| ); | ||
| const mediumSubset = await subsetFont(mediumBuffer, chars, { | ||
| targetFormat: "woff2", | ||
| }); | ||
| fs.writeFileSync("public/fonts/Shilla_CultureM-Medium.woff2", mediumSubset); | ||
| } |
There was a problem hiding this comment.
서브셋 생성 스크립트가 원본 폰트 파일을 직접 덮어쓰고 있습니다. 스크립트 실행 중 오류가 발생하거나 원본 파일이 다시 필요할 경우를 대비하여, 결과물을 별도의 파일명(예: ...-subset.woff2)으로 저장하는 것이 더 안전합니다.
| async function run() { | |
| const chars = "보돌코스코어드선수관리"; | |
| const boldBuffer = fs.readFileSync("public/fonts/Shilla_CultureB-Bold.woff2"); | |
| const boldSubset = await subsetFont(boldBuffer, chars, { | |
| targetFormat: "woff2", | |
| }); | |
| fs.writeFileSync("public/fonts/Shilla_CultureB-Bold.woff2", boldSubset); | |
| const mediumBuffer = fs.readFileSync( | |
| "public/fonts/Shilla_CultureM-Medium.woff2", | |
| ); | |
| const mediumSubset = await subsetFont(mediumBuffer, chars, { | |
| targetFormat: "woff2", | |
| }); | |
| fs.writeFileSync("public/fonts/Shilla_CultureM-Medium.woff2", mediumSubset); | |
| } | |
| async function run() { | |
| const chars = "보돌코스코어드선수관리"; | |
| const boldBuffer = fs.readFileSync("public/fonts/Shilla_CultureB-Bold.woff2"); | |
| const boldSubset = await subsetFont(boldBuffer, chars, { | |
| targetFormat: "woff2", | |
| }); | |
| fs.writeFileSync("public/fonts/Shilla_CultureB-Bold-subset.woff2", boldSubset); | |
| const mediumBuffer = fs.readFileSync( | |
| "public/fonts/Shilla_CultureM-Medium.woff2", | |
| ); | |
| const mediumSubset = await subsetFont(mediumBuffer, chars, { | |
| targetFormat: "woff2", | |
| }); | |
| fs.writeFileSync("public/fonts/Shilla_CultureM-Medium-subset.woff2", mediumSubset); | |
| } |
There was a problem hiding this comment.
Code Review
This pull request adds a blog post detailing the optimization of web fonts to resolve FOUT issues, covering techniques like font-display: swap, WOFF2 conversion, and font subsetting. Feedback was provided regarding a syntax error in a CSS code block, a discrepancy in the character count for the subset script, inconsistent file size reporting throughout the text, and a suggestion to avoid overwriting original font files in the provided script.
|
|
||
| ```css | ||
| @font-face { | ||
| font-family: "Shilla\*Culture"; |
|
|
||
| ## 해결 시도 4: 서브셋 — 근본 원인인 폰트 크기줄이기 | ||
|
|
||
| 여러 방법을 시도하면서 결국 중요한 건, **폰트 파일 크기**였습니다. FOUT가 안 보이는 WantedSans는 split 서브셋으로 개당 18~45KB인 반면, 신라문화체는 한글 2,574자가 통째로 들어있어 2,405KB나 됐습니다. 실제로 프로젝트에서 사용하는 글자는 총 9글자이기때문에 나머지 글자는 다운할 필요가 없어서 서브셋처리를 통해 최적화를 해보기로 했습니다. |
There was a problem hiding this comment.
본문에는 사용 중인 글자가 '총 9글자'라고 설명되어 있으나, 아래 코드의 chars 변수에 정의된 고유 글자 수는 10개("보, 돌, 코, 스, 어, 드, 선, 수, 관, 리")입니다. 설명과 코드의 수치를 일치시키는 것이 좋습니다.
| 여러 방법을 시도하면서 결국 중요한 건, **폰트 파일 크기**였습니다. FOUT가 안 보이는 WantedSans는 split 서브셋으로 개당 18~45KB인 반면, 신라문화체는 한글 2,574자가 통째로 들어있어 2,405KB나 됐습니다. 실제로 프로젝트에서 사용하는 글자는 총 9글자이기때문에 나머지 글자는 다운할 필요가 없어서 서브셋처리를 통해 최적화를 해보기로 했습니다. | |
| 여러 방법을 시도하면서 결국 중요한 건, **폰트 파일 크기**였습니다. FOUT가 안 보이는 WantedSans는 split 서브셋으로 개당 18~45KB인 반면, 신라문화체는 한글 2,574자가 통째로 들어있어 2,405KB나 됐습니다. 실제로 프로젝트에서 사용하는 글자는 총 10글자이기때문에 나머지 글자는 다운할 필요가 없어서 서브셋처리를 통해 최적화를 해보기로 했습니다. |
| const boldSubset = await subsetFont(boldBuffer, chars, { | ||
| targetFormat: "woff2", | ||
| }); | ||
| fs.writeFileSync("public/fonts/Shilla_CultureB-Bold.woff2", boldSubset); |
There was a problem hiding this comment.
| | Bold | 2,404 KB | 11.6 KB | 99.5% | | ||
| | Medium | 2,317 KB | 10.2 KB | 99.5% | | ||
|
|
||
| **2,405 kB → 약 11KB.** WantedSans의 split 파일(18~45KB)보다도 작아졌습니다. | ||
| (WantedSans는 단순히 파일이 작은 것이 아니라, unicode-range 기반으로 폰트가 여러 개의 subset 파일로 분리되어 있음) | ||
| 실제로 빠른 4G 네트워크 기준으로 비교해보니 차이가 확연했습니다. | ||
|
|
||
|  | ||
|
|
||
| | | 파일 크기 | 로딩 시간 (빠른 4G) | | ||
| | --------- | --------- | ------------------- | | ||
| | 서브셋 전 | 2,406 KB | 7.51초 | |
PR전 코드 퀄리티 체크하기
작업내용
🔍 가독성 (Readability) CHECK
명명 규칙
const ANIMATION_DELAY_MS = 300형태로 의미 있는 이름 사용const isValidUser = user.age >= 18 && user.isVerifieduserData→authenticatedUser,list→activeUserList구조 및 구성
추상화 및 분리
AuthGuard컴포넌트ViewerSubmitButton,AdminSubmitButton로 역할별 분리🎯 예측 가능성 (Predictability) CHECK
반환 타입 일관성
UseQueryResult<T, Error>일관 사용{ ok: boolean; reason?: string }형태 일관 사용단일 책임 원칙
fetchBalance()가 로깅 등 부수효과 없이 balance만 반환명확한 명명
http.get()→httpService.getWithAuth()useModal()→useConfirmationModal()🔗 응집도 (Cohesion) CHECK
도메인별 구성
domains/user/,domains/product/폼 응집도
⚡ 결합도 (Coupling) CHECK
상태 관리 범위
useCardIdQueryParam()같은 focused hook 사용Props Drilling 제거
추상화 수준
📋 추가 CHECK
성능 고려사항
useCallback,useMemo적절 사용타입 안정성
테스트 가능성
문서화
🎨 코드 스타일 CHECK