당신이 글로벌 서비스에서 겪게 될 폰트 렌더링 문제(FEConf 2019)

Nochi·2025년 3월 1일
2

읽기만 해도 돼

목록 보기
5/6
post-thumbnail

출처 영상
[Track 2-6] 곽희범 - 글로벌 서비스를 하게 되면 겪게 될 폰트 렌더링 문제

예제사이트

FEConf 2019 font examples

폰트, 혹시 쉬우신가요?

: 안에 가려진 것들도 많고, 브라우저마다 특성이 다 다르다.

  • 지구 상의 다양한 문자
  • 유니코드 이전의 인코딩 방식
    • 파편화 된 방식
  • 유니코드 블록
    • utf-8, utf-16과 같이 정리되었다.
  • emoji도 문자
    • 이모지도 폰트로 구성되어 있다.
  • OS 고유의 폰트
    • OS 버전에 따른 코드

⇒ 폰트가 너무 많기 때문에, 모든 문자를 다 폰트가 해결해주지 않아 문제가 발생하기도 한다.

폰트가 달라지면 길이도 높이도 달라진다.

  • (중요)UI가 깨지면 홈페이지에 신뢰도를 떨어뜨린다.

일하면서 겪은 실제 문제들…

사례 1: 문장사이에 낯선 폰트

글자 중 지원하지 폰트를 사용한다면 폰트가 두 가지가 섞여져서 보여질 수 있다.

렌더링 된 폰트를 확인하기 위해서는 개발자 도구(F12) - Elements - Computed - Rendered Fonts를 확인하면 된다.

사례 2: 일본어가 다른 문자보다 더 커보이는 문제

한국에서 사용 시

font-family: "Helvetica Nenu", "Apple SD Gothic Neo", sans-serif

  • 알파벳 + 숫자 + 기호: Helvetica Nenu
  • 한글: Apple SD Gothic Neo
  • 그 외: sans-serif (기본 고딕체)

지원하는 순서대로 보여줌..

일본에서는

font-family: "Helvetica Nenu", "Hiragino Sans", sans-serif

일본어 폰트의 문자가 잘 안맞는 현상이 발생한다. 평균적으로 일본어와 중국어 폰트들이 다른 폰트들에 비해 조금 크다. 그래서 일본어 앞에 숫자들을 붙이면 일본어가 조금 커 보인다.

Sans-serif

: 글꼴 꼬리에 꾸밈이 없는 고딕계열 기본 폰트

sans는 “~이 없는”이라는 전치사 이고, serif는 글꼴, 꼬리를 의미한다. 하여 글꼴 꼬리에 꾸밈이 없는 고딕 계열의 기본 폰트 로 알고 있지만, 자세히 보면 글꼴 꼬리에 꾸밈이 없는 고딕 계열 언어별 설정에 따른 기본 폰트 이다.

-Apple-system, BlinkMacSystemFont

둘 다 애플에서 동작하는 기본 폰트이다.

  • -apple-system은 Safari에서 동작
  • BlinkMacSystemFont는 Chrome에서 동작

이 둘을 합쳐서 system-ui로 분류하였는데,

BootstrapGithub에서는 폰트가 엄청 촌스러워보인다.

지금까지 본 문제들…



그러면 폰트 설정을 어떻게 해야 할까?

다른 글로벌 사이트는 어떻게 되어있을까

facebook


페이스북은 system-ui 폰트를 그대로 사용하고 있다.

Skyscanner


폰트를 OS별로 설정을 다 추가해놓았지만, 동아시아에선 굉장히 불친절한 하다. 동아시아에서는 결국 sans-serif로 설정되기 때문이다.

Trip.com


폰트가 다양하지만 가장 이상적인 방식이다. 그러나 테스트를 해야할 것이 많은 것이 단점이다.

아주 중요한 Attribute는 lang

: 언어를 지정하는 전역 속성이어서 어느 태그에든 붙을 수 있다.

  • 언어별 폰트셋을 사용
    • 적당한 sans-serif를 사용할 수 있게 도와줌
  • 언어별 CSS 스타일 적용
  • 번역툴에게 힌트를 제공
  • 검색엔진에 힌트를 제공
  • 브라우저의 문법 체크 기능
  • 그 외의 각종 파서에서 유용하게 사용 가능

    lang에 따라 폰트가 달라져서 길이가 다 달라진다.

Sans-serif는 쉽게 말해서 Interface이다.

langsans-serif 에 넘겨주면 알맞은 값을 반환해준다는 내용. 함수로 표현하는게 좋은 것 같다고 생각한다.

lang을 통해 언어별로 다른 폰트를 사용할 수 있다.


이렇게 사용하면 kodefault도 다르게 표현할 수 있다.

유연한 디자인

: 폰트 크기가 커져도 문제 없는 디자인

유연한 디자인을 통해 폰트가 불러오는 문제를 해결할 수 있다.

facebook은 대표적으로 sans-serif를 사용하고 있다.

  • 높이가 고정되는 경우, …(ellipsis) 처리를 통해 고정을 시킴
  • 높이가 가변적인 경우, 유연하게 높이가 늘어나도록 적용

이렇게 깨지는 UI가 없는게 특징이다.

웹폰트로 폰트 고정

: 웹폰트 최적화만 잘 시키자.

파편화된 디바이스의 폰트를 고정시킬 수 있다는 장점이 있다.

[A4] 웹폰트의 사용과 최적화 - 이상진

만약, 최적화를 시키지 않으면 최악의 경우.

웹 폰트는 로마자 → 일본어 → 한글 → 중국어 순으로 용량이 커지는 것을 알 수 있다. 그리하여 다운로드 다 받을 때까지 화면에 안나올 수 있다.

정리

  • lang attribute는 선택이 아니라 필수이다.
  • 디자이너와의 협업하면서 마크업을 유연하게 하는 것
  • 웹폰트도 방법하는 것도 장점이다.

폰트로 인해 깨지는 UI를 미리 탐지해 볼 순 없을까?

E2E테스트로 찾아낼 수 있을까? cypress.io

  • 크롬브라우저 위에서 DOM 기반으로 진행하는 테스트
  • 유저의 실제 행위에 대한 테스트 가능 (클릭, 스크롤 등)
  • 에러 발생 시, 스크린샷을 제공
  • 동영상 녹화 기능 제공

문제 1) overflow: hidden


일본어 메뉴의 경우, 폰트의 크기로 인해 로그아웃 버튼이 사라져버렸다면?

cy.get('.section).then($el => {
	const parentNode = $el[0];
	for (let child of parentNode.children) {
		expect(Cypress.dom.isHidden(child).to.be.false
	}
})

problem) overflow clientRect


언어 변경에 따라 글자가 네모 영역을 벗어난 경우에 ElementoffsetWidth, scrollWidth, offsetHeight, scrollHeight로 알 수 있다.

cy.get(".section").then($el => {
	const parentNode = $el[0];
	for (let child of parentNode.children) {
		const {
			offsetWidth, scrollWidht,
			offsetHeight, scrollHeight
		} = child;

		expect(offsetWidth > scrollWidht).to.be.true;
		expect(offsetHeight > scrollHeight).to.be.true;
	}
})

offsetWidth은 border를 포함한 보여지는 크기를 가진다. scrollWidht은 content영역에 보이지 않는 부분을 포함한다.

이렇게 잘려 있는 것을 알 수 있다.

problem) scrollable


일본어 쪽에 약간 커져서 스크롤이 생기는 문제가 있었다. Cypress.dom.isScrollable($element)로 스크롤이 생겼는지 알 수 있다.

cy.get(".section").then($el => {
	expect(Cypress.dom.isScrollable($el)).to.be.false
	}
})

problem) scalable layout

UI 상으로 깨진 부분이 없어서 찾기 어려웠다. 그래서 기본 언어의 화면을 기록하고 나머지 언어와의 화면을 비교하였다.

const traning = () => {
	const elementSection = Cypress.$(".section");
	const { children } = elementSection[0];
	const childrenTop = [];
	for (let i = 0; i < children.length; i++) {
		const child = children[i];
		childrenTop.push(child.getBoundingClientRect().top)
	}
	
	// 학습한 값
	baseValue = {
		width: elementSection.width();
		height: elementSection.height();
		childrenTop
	}
}

예제 상태와 비교했을 때, childrenTop 이 다르면 검출이 될 것이다.

cy.get(".section").then($el => {
	const parentNode = $el[0];
	for (let i = 0; i < parentNode.children.length; i++ ) {
		const child = children[i];
		const { top } = child.getBoundingClientReact();
		expect(top === baseValue.childrenTop[i]).to.be.true;
	}
})

글로벌 서비스를 테스트할 때, 스타트업에서는 이러한 다양한 환경에 대해 익숙하지 않거나 충분히 이해하지 못하는 경우가 많다. 현재 회사에서도 글로벌 서비스를 진행하고 있지만, 대부분의 테스트가 한국에서만 이루어지고 있어 각국의 브라우저와 OS에서의 UI 차이를 미리 파악하지 못하고 있다는 걸 깨달았다. 이를 해결하려면 테스트 범위를 확장하고, Cypress와 같은 도구를 적극 활용하여 다양한 환경에서의 UI 동작을 체크하고 신뢰를 유지하는 것이 중요해 보인다. 글로벌 서비스를 안정적으로 제공할 수 있도록 앞으로도 지속적으로 관리 해야겠다.

0개의 댓글