우당탕탕 기술블로그
[React] input에 입력할 때 글자 깜빡임 현상, 웹폰트 성능 최적화 본문
✏️ 문제 상황

useState 혹은 useRef를 사용해도 input 창에 텍스트를 빠르게 타이핑하면 깜빡임 현상이 발생했다.
🧐 문제 원인
타이핑할 때마다 폰트를 요청하고 있어서 발생한 문제였다.
@import url(//fonts.googleapis.com/earlyaccess/notosanskr.css);
*,
body {
font-family: 'Noto Sans KR', sans-serif;
}
😊 해결 방안

src/assets/fonts 폴더를 만들어서 사용할 폰트(woff2, woff, otf)를 직접 다운받아서 적용했다.
/* src/assets/font.module.scss */
@font-face {
font-family: 'Noto Sans KR';
font-style: normal;
font-weight: 100;
src: local('Noto Sans KR'),
url('./NotoSansKR-Thin.woff2') format('woff2'),
url('./NotoSansKR-Thin.woff') format('woff'),
url('./NotoSansKR-Thin.otf') format('opentype');
}
@font-face {
font-family: 'Noto Sans KR';
font-style: normal;
font-weight: 300;
src: local('Noto Sans KR'),
url('./NotoSansKR-Light.woff2') format('woff2'),
url('./NotoSansKR-Light.woff') format('woff'),
url('./NotoSansKR-Light.otf') format('opentype');
}
...
/* src/index.module.scss */
import 'src/assets/font.module.scss'
*,
body {
font-family: 'Noto Sans KR',
...
}
브라우저(IE 제외)에서 지원하는 웹 폰트 중 가장 용량이 적은 woff2를 가장 우선으로 사용하도록 했다.
woff2의 장점은 아래와 같다
- 파일 크기 감소: woff2는 웹 폰트를 압축하는 데 사용되는 형식으로, 폰트 파일의 크기를 크게 줄일 수 있다. 압축률이 높아짐에 따라 다운로드 시간이 단축되어 페이지 로딩 속도가 향상된다. 작은 파일 크기는 특히 모바일 장치와 저속 인터넷 연결을 사용하는 사용자에게 이점을 제공한다. 또한 기존 woff 보다 30~50% 정도의 압축률을 보여준다.
- 브라우저 호환성: woff2는 현재 대부분의 최신 브라우저에서 지원되는 웹 폰트 형식이다. woff2를 사용하면 대부분의 사용자가 폰트를 올바르게 렌더링할 수 있으므로 광범위한 브라우저 호환성을 확보할 수 있다.

또한 local 문법을 사용했다.
- local 문법을 사용 안 하면 시스템에 폰트 유무와 관계없이 무조건 다운로드하게 된다. 불필요한 요청을 지속적으로 하게 된다.
- local 문법을 앞에 선언해 주면 시스템에 설치되어 있다면 리소스를 요청하지 않는다. 즉, 사용자가 해당하는 폰트를 가지고 있다면 리소스 요청을 하지 않는 것이다.
그 외 성능 최적화 방법
1. 서브셋 폰트
- 서브셋 폰트(subset font)는 폰트 파일에서 불필요한 글자를 제거하고 사용할 글자만 남겨둔 폰트다. 예를 들어, 갉, 뷁, 챫 등의 불필요한 글자를 제거하고 자주 사용하는 폰트만 남겨두고 용량을 줄일 수 있는 방법이다.
- 영어는 대소문자 포함 72자 밖에 되지 않지만 한글의 경우 자음, 모음의 조합으로 11,172자가 필요해서 이 방식을 이용해서 폰트 파일 용량을 줄일 수 있다.
- 최근에 출시되는 한글 웹 폰트의 대부분은 2,350자의 서브셋 폰트다. 서브셋 폰트가 아니라면 직접 서브셋 폰트를 만들어 사용할 수 있고 서브셋 폰트 메이커, fontTools 라이브러리를 통해 만들 수 있다.
2. 텍스트가 항상 보이게 하기(CSS의 font-display 속성을 이용한 최적화)
- 우선 브라우저 마다 렌더링 차단 처리 방식이 다르다.
- Chrome, Firefox 등의 브라우저는 FOUT 방식(Flash Of Unstyled Text)으로 렌더링 차단을 처리
- 웹 폰트가 로딩될 때까지 우선 폴백 폰트로 텍스트를 렌더링한다. 웹 폰트 로딩이 완료되면 텍스트를 웹 폰트로 전환한다. 이 방식은 웹 폰트 로딩 여부에 관계없이 텍스트가 항상 보이는 장점이 있다. 하지만 글꼴의 자간, 높이 등 서식이 달라 웹 폰트 적용 전과 후에 레이아웃이 변경될 수 있다.
- IE 계열의 브라우저는 FOIT 방식(Flash Of Invisible Text)으로 렌더링 차단을 처리
- 웹 폰트가 로딩되기 전까지 해당 텍스트를 화면에 보여 주지 않는다. 웹 폰트 로딩이 완료되면 텍스트를 웹 폰트로 전환한다. 한 번에 웹 폰트를 보여줄 수 있다는 장점이 있다. 웹 폰트의 로딩이 늦으면 빈 텍스트가 노출되는 문제점이 있다.

- 따라서 모든 브라우저에 의도적으로 FOUT 방식으로 작동하게 한다면 텍스트를 항상 보이게 할 수 있다.
- CSS의 font-display 속성을 이용한 최적화
- 텍스트가 항상 보이게 하려면 FOUT와 동일하게 작동하는 swap 옵션을 사용한다. fallback 옵션과 optional 옵션을 사용하면 100ms 동안 텍스트가 보이지 않지만 매우 짧은 시간이어서 이 옵션들도 최적화에 사용할 수 있다.
- swap: 우선 폴백 폰트로 글자를 렌더링하고, 웹 폰트 로딩이 완료되면 웹 폰트를 적용한다. 웹 폰트 로딩 여부와 관계없이 항상 텍스트가 보인다.
- fallback: 우선 100ms 동안 텍스트가 보이지 않고, 그 후 폴백 폰트로 렌더링한다. 특이한 점은 약 2초의 전환(swap) 시간이 있다는 점이다. 이 시간 안에 로딩이 완료되면 웹 폰트로 전환한다. 하지만 이 시간이 지나면 웹 폰트 다운로드가 완료되어도 웹 폰트로 전환하지 않고 폴백 폰트를 유지한다. 전환 시간 이후에 다운로드된 웹 폰트는 웹 페이지에 적용되지는 않지만 캐시에는 저장된다. 그래서 추후에 사용자가 다시 방문했을 때 바로 웹 폰트가 적용된다는 장점이 있다.
- optional: fallback 옵션과 비슷하지만 다르게 작동하는 옵션이다. 우선 100ms 동안 텍스트가 보이지 않고 그 후 폴백 폰트로 전환한다. 웹 폰트를 다운로드하지만 브라우저가 네트워크 상태를 파악해 웹 폰트 전환 여부를 결정한다는 점이 이 옵션의 특이한 점이다. 예를 들어 네트워크의 연결 상태가 안 좋으면 웹 폰트의 다운로드가 완료되어도 캐시에 저장만 하고 전환은 하지 않는다.
- block: 위에 적힌 세 속성과 다르게 FOIT 방식으로 작동하는 옵션이다. 웹 폰트가 로딩되지 않았을 때는 텍스트를 렌더링하지 않는다(최대 3초). 웹 폰트 로딩이 완료되면 웹 폰트를 적용한다.
- font-display 속성은 Internet Explorer 계열 브라우저를 제외한 브라우저가 지원한다. 텍스트가 보이지 않는 문제가 발생하는 브라우저에서는 문제없이 최적화에 font-display 속성을 사용할 수 있다
- 텍스트가 항상 보이게 하려면 FOUT와 동일하게 작동하는 swap 옵션을 사용한다. fallback 옵션과 optional 옵션을 사용하면 100ms 동안 텍스트가 보이지 않지만 매우 짧은 시간이어서 이 옵션들도 최적화에 사용할 수 있다.
출처
https://d2.naver.com/helloworld/4969726
CSS Web Fonts
W3Schools offers free online tutorials, references and exercises in all the major languages of the web. Covering popular subjects like HTML, CSS, JavaScript, Python, SQL, Java, and many, many more.
www.w3schools.com
'Web' 카테고리의 다른 글
[React] 이미지 등록 후 서버 요청 시 413 에러 발생, 이미지 압축하기 (0) | 2023.07.11 |
---|---|
[React] npm i node-sass 에러 (0) | 2023.06.04 |
[React] 카카오 애드핏 에러 (useEffect, componentDidMount, useLayoutEffect) (0) | 2023.06.03 |
[HTML, JS] form을 이용한 데이터 POST 요청 시 Not Found 404 오류 (0) | 2023.05.29 |
[CSS] input password 안 보이는 현상 (0) | 2023.05.26 |