CSS 글꼴(Font) 이야기

CSS 글꼴

우리는 하루에도 수많은 글을 봅니다. 스마트폰으로 웹서핑을 하거나 책을 볼 때, 우연히 길에서 스쳐간 간판까지, 눈을 뜨고 있다면 의식적으로든, 무의식적으로든 글을 읽는 걸 피할 수 없죠.

글은 기본적으로 정보를 전달하지만 같은 글이라도 다양한 서체로 표현될 수 있습니다. 사람마다 글씨체가 모두 다른 것처럼 말이죠. 이에 따라 세상에는 굉장히 많은 글꼴이 개발되어 사용되고 있습니다. 애플의 스티브 잡스가 글꼴을 굉장히 중요하게 다루었다는건 굉장히 유명한 이야기입니다.

프로그래머인 저에게도 글꼴을 다룰일은 굉장히 많습니다. 개발하는 서비스에 따라 디자이너가 원하는 글꼴이 다르기 때문에 디자인 팀에서 선정한 글꼴을 적용해야합니다. 또한 개발환경을 세팅할 때 좋아하는 글꼴을 설치하고 이를 IDE에 적용하는 것은 마치 신성한 의식과도 같은 일입니다.

이번 글에서는 프로그래머로서 글꼴을 다룰 때 자주 마주치는 몇 가지 개념들에 대해 다루도록 하겠습니다.

세리프(Serif)와 산 세리프(Sans-Serif)

출처 https://www.w3schools.com

세리프(Serif)

세리프는 ‘장식’을 의미합니다. 위에 이미지에서 세리프 글꼴을 보면 선의 끝에 굴림 처리가 되어있고, 선의 굵기도 다른 특징을 가집니다. 한글로 치면 궁서체가, 영어로는 타임즈 뉴 로만(Times New Roman)이 대표적이죠. 보통 인쇄물에 많이 사용됩니다.

산 세리프(Sans-Serif)

Sans는 ‘없음’을 나타냅니다. 즉 세리프가 없다라는 의미가 되니 장식이 없다라고 받아들이면 되겠죠? 위의 이미지에서 산 쉐리프 서체를 보면 선의 굵기가 동일하고 장식이 없습니다. 한글 글꼴에서는 고딕 서체들을 떠올리면 됩니다. 주로 모니터 화면에서 보는 서체에 많이 사용됩니다.

참고로 디지털 세상에서 산 세리프 글꼴이 자주 쓰이게 된건 과거에는 디스플레이 기능의 제약상 곡선을 매끄럽게 표현하기 어려워 세리프 글꼴을 표현하기 어려웠던 이유가 있었다고 하네요.

세리프와 산 세리프의 차이

둘의 차이를 위에서 간단히 살펴 보았습니다. 표면적으로는 글꼴에 ‘장식’이 있는지 없는지가 핵심이지만 조금만 더 이 둘의 차이를 알아보도록 하겠습니다.

먼저 세리프와 산 세리프의 차이는 가독성(readability)과 판독성(legibility)을 가지고 볼 수 있습니다.

가독성은 많은 양의 글을 볼 때의 읽기 쉬운 정도를 나타내는데 일반적으로 신문과 같은 인쇄물에서는 쉐리프가, 모니터 화면 상에서는 산세리프가 가독성이 높다고 봅니다. 다만 이건 사람에 따라, 익숙한 정도에 따라 달라지는 주관적인 부분입니다.

판독성은 각각의 글자가 정확히 어떤 글자를 나타내는지를 인식할 수 있는 정도를 나타냅니다. 예를 들어 i와 l은 글꼴에 따라 매우 비슷하게 보일 수 있습니다. 프로그래머가 코딩을 할 때 가장 먼저 프로그래밍 하기 좋은 글꼴을 설정하는데 그 이유가 여기에 있습니다.

프로그래머가 사용하는 글꼴들은 대부분 이 판독성이 좋은 것들이 많습니다. 아래의 예는 네이버에서 만든 D2Coding 이라는 글꼴입니다. 이미지를 보시면 헷갈리기 쉬운 글자들이 구분되기 쉽도록 신경을 많이 쓴 것이 느껴 지시나요? 그리고 이런 노력들이 앞에서 이야기한 판독성을 높여줍니다.

d2coding

모노스케이프(Monoscape)

앞에서 세리프와 산 세리프에 대해 알아보았습니다. 그럼 두 번째. 글꼴을 이야기 할 때 모노스케이프는 무엇을 의미하는 걸까요?

답은 간단합니다. 모노스케이프 글꼴을 모든 글자의 가로길이가 같은 글꼴을 이야기합니다. 모노스케이프란 단어 자체가 고정너비라는 뜻이기도 합니다. 그럼 모노스케이프 글꼴은 언제 주로 사용할까요? 바로 프로그래밍을 할 때 가장 많이 선택 합니다. 글꼴이 고정너비여야만 코드가 동일한 간격으로 정렬되어 편집도 쉽고 보기도 좋기 때문이죠. 엄밀히 말하면 보통 모노스케이프이면서 산 세리프인 글꼴을 많이 사용합니다.

참고로 저는 코딩을 할 때 D2Coding이나 나눔고딕코딩 글꼴을 주로 사용하는데요. 이 두 글꼴은 한국에서 개발된 글꼴이라 한글도 잘 지원이 되고 무료이기까지 합니다.

아무래도 우린 한국 사람이니 코드를 짜다보면 주석이나, 코드 자체에도 한글이 들어갈 일이 생깁니다. 이럴 때 외국에서 만든 글꼴을 쓰면 한글이 예쁘게 나오지 않는 경우가 많은데(폰트에 한글 자체가 아예 안들어가 있기 때문에) D2Coding이나 나눔고딕코딩을 쓰면 이런 문제가 깨끗이 해결됩니다. 참고로 이 두 글꼴은 네이버에서 만든 글꼴입니다. 한때 제가 몸담았던 회사라 괜히 더 애정이 가네요. :)

d2coding

폰트 패밀리(Font Families)

글꼴은 우리가 글을 사용하며 함께 발전해 왔습니다. 어떤 운영체제를 사용하든 글꼴 설정에 들어가서 내가 사용할 수 있는 글꼴을 확인해 보면 그 숫자에 아마 놀라실겁니다.

이렇게 많은 글꼴중에 원하는 글꼴을 좀 더 쉽게 적용하려면 어떻게 해야할까요? 그리고 내가 사용하려고 하는 글꼴이 시스템에 설치가 되어있지 않다면 어떻게 해야할까요? 폰트 패밀리는 이런 문제를 해결해 줍니다.

아래는 CSS에서 폰트 패밀리를 설정하는 예제입니다. 아래 코드는 이렇게 읽을 수 있습니다.

시스템에 Verdana 글꼴이 있으면 Verdana를, 없으면 Arial을, Arial도 없으면 산 세리프 글꼴 중에 하나를 사용해주세요.

1
font-family: Verdana, Arial, sans-serif;

즉 글꼴이 없는 경우를 대비해서 좌측에서부터 순서대로 예비 글꼴을 지정할 수 있는 것이죠. 이때 특정 글꼴을 지정하거나, 앞서 우리가 배웠던 글꼴의 형태를 지정해 줄 수 있습니다. 위의 예에서는 산 세리프(sans-serif) 부분이 이에 해당합니다.

마무리

이번 글에서는 프로그래머로서 글꼴을 다룰 때 알아야할 아래의 세 가지 주요 개념에 대해 알아보았습니다.

  1. 세리프와 산 세리프
  2. 모노스케이프
  3. 폰트 패밀리

세리프와 산 세리프, 모노스케이프는 글꼴에 대한 전반적인 개념이고 폰트 패밀리는 CSS에서 글꼴을 지정하는 방식입니다. 폰트 패밀리는 CSS에서 대표적으로 사용되지만 다른 언어나, 툴 등에서도 자주 사용됩니다. 다만 사용되는 곳에 따라 구체적인 사용법은 조금 다를 수 있습니다. 하지만 우리는 개념을 알고 있으니 문서를 보면 금방 이해하고 적용할 수 있겠죠?

웹링크 미리보기 화면 보여주기(OGTag)

웹서핑을 하다보면 재미있는 글이나 자료들을 URL 링크 형태로 공유하는 경우가 많이 있습니다. 하지만 단순히 URL만 보게되면 보는 사람 입장에서는 해당 링크가 어떤 웹페이지인지 알기가 어렵죠. 그래서 많은 앱과 서비스에서 아래와 같이 해당 페이지에 대한 미리보기(제목, 설명, 이미지)를 제공합니다.

OGTag

이런 화면을 보여주기 위해 사용되는 가장 기본적인 방법은 OGTag(Open Graph Meta Tag)를 이용하는 방법입니다. OGTag는 웹페이지에 대한 간단한 정보를 html에 입력해두고 이 페이지를 보여주는 앱이나 서비스에서 이를 이용해 위의 스크린 샷과 같이 페이지에 대한 간략한 정보를 보기 쉽게 보여줄 수 있도록 하는 것입니다. 아래는 html에 OGTag를 입력하는 예입니다.

1
2
3
4
5
6
7
8
9
10
11
<head>
......
<meta property="og:title" content="test"/>
<meta property="og:type" content="article"/>
<meta property="og:url" content="https://rootree.net"/>
<meta property="og:description" content="This is a description."/>
<meta property="og:image" content="https://img.jpg"/>
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="627" />
......
</head>

보시면 property가 og:xxx인 메타 태그들이 있는데요. 이것들이 바로 OGTag 입니다. 메타 태그는 본래 해당 페이지를 설명하는 정보들을 넣기위한 태그입니다. 검색엔진이 웹페이지를 검색하거나 웹브라우저가 해당 페이지를 정확하게 보여주기 위해 도움이 되는 정보 등으로 사용됩니다. 이중에서도 Open Graph Meta Tag는 사용자가 웹사이트를 페이스북에 공유하기 좋게 하기 위한 목적으로 만들어졌습니다. 이와 유사한 걸로는 트위터의 card 태그가 있습니다.

위와 같이 og:xxx 태그를 입력해둔 웹페이지를 페이스북에 공유하면 페이스북에서는 이 태그들을 이용해서 해당 페이지의 링크를 마치 페이스북의 오브젝트처럼 다루어서 보다 예쁘게 보여줍니다. 아래 이미지는 페이스북에 링크를 공유했을때 보여지는 화면인데요. 이걸 보시면 바로 이해가 가실겁니다. 제가 공유한건 단순히 https://naver.com 이란 URL인데 아래와 같이 페이스북에서 자체적으로 예쁘게 보여주죠. 지금은 페이스북이 아니라 밴드, 라인, 카톡등 다른 서비스에서도 이를 이용해서 비슷하게 미리보기를 해주고 있습니다.

OGTag_Facebook

Open Graph Meta Tag는 페이스북의 Open Graph Protocol에 정의가 되어있습니다. 이에 대한 자세한 설명은 아래 링크에서 확인 가능합니다. 지금까지 소개에선 타이틀, 설명, 썸네일 이미지 정도의 정보를 보여줄 수 있다는 정도였지만 실제로는 OGTag를 이용해 동영상, 음악 같은 것들도 표현할 수 있습니다. 또한 아래의 Open Graph Protocol 페이지에 가면 언어별 파서 및 플러그인, 개발 도구 등도 보실 수 있습니다.

그럼 안드로이드 앱에서 OGTag를 이용하려면 어떻게 하면 될까요? 아주 간단합니다.

  1. 링크를 입력 받는다.
  2. 해당 링크의 html을 가져와 OGTag를 파싱한다.
  3. 2에서 파싱된 OGTag 정보를 가지고 원하는 UI로 예쁘게 보여준다.

위의 과정을 간단히 코드로 구현해 보았습니다. Jsoup를 통해 html을 가져오고 여기서 OGTag를 파싱하는 코드입니다. 간단히 위의 로직만 옮긴 코드라서 슈도코드 정도 느낌으로 참고만 하면 될 것 같습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
private void getOGTag(String url, final OGTag ret) {
// 입력받은 url에 해당하는 html을 요청하고 OGTag들을 가져온다.
try {
Connection con = Jsoup.connect(url);
Document doc = con.get();
Elements ogTags = doc.select("meta[property^=og:]");
if (ogTags.size() <= 0) {
return;
}

// 필요한 OGTag를 추려낸다
for (int i = 0; i < ogTags.size(); i++) {
Element tag = ogTags.get(i);

String text = tag.attr("property");
if ("og:url".equals(text)) {
ret.setOgUrl(tag.attr("content"));
} else if ("og:image".equals(text)) {
ret.setOgImageUrl(tag.attr("content"));
} else if ("og:description".equals(text)) {
ret.setOgDescription(tag.attr("content"));
} else if ("og:title".equals(text)) {
ret.setOgTitle(tag.attr("content"));
}
}
} catch (IOException e) {
return;
}
}

조금 더 생각해볼 것들

서비스 적용하기

위의 예는 아주 간단한 구현이지만 실제로 서비스에 적용하려면 성능과 네트워크 사용에 대한 부분도 생각해봐야합니다. 링크가 PC 페이지인 경우 html 자체도 상당한 크기를 가질 수 있기때문에 이 자체도 부담이 될 수 있습니다. 따라서 매번 URL을 통해 OGTag를 파싱하는 건 쓸데없이 네트워크 트래픽 및 성능 상 낭비가 될 수 있습니다. 이를 피하기 위해서는 중간에 서버를 두거나, 캐싱을 사용하는 건데요. 서버를 두는건 OGTag를 통해 얻을 수 있는 장점에 비해 너무 큰 리소스가 들어가는 문제가 있습니다.

또한 서버를 두든 앱에서 로컬 캐싱을 하든 웹페이지의 OGTag를 실시간으로 반영하기 어렵다는 이슈가 있습니다. 따라서 서비스에 따라 상황에 맞춰 적절히 적용하는 센스가 필요합니다. 앱개발자 입장에서는 며칠 단위의 로컬 캐시를 만들어서 서버없이 간단히 구현하는 방식도 좋을것 같습니다.

OGTag가 없는 경우

OGTag를 사용자가 넣어두지 않은 경우에도 URL미리보기를 적용하고 싶다면 html내에서 적당한 이미지와 타이틀, 설명을 뽑아서 사용하면 됩니다. 가령 타이틀은 메타 태그의 title을, 설명은 description을 사용한다든지, 아니면 본문의 텍스트 중 앞부분에서 적당히 잘라서 쓴다든지 하는 것이죠. 이미지 역시 div 또는 body안에서 첫번째 이미지를 찾아서 가져 올 수 있겠죠. 이런 부분은 정답이 없기에 성능이나 정책을 적당히 고려해서 적용하면 될 것 같습니다.

페이스북의 OGTag 처리

참고로 페이스북은 서버단에서 OGTag를 처리하고 자체적으로 캐싱도 해주고 있습니다. 사용자의 포스팅을 피드 형식으로 보여주니 당연한거 같기도 합니다. 사용자가 포스팅에 URL을 첨부하면 페이스북 크롤러가 해당 페이지의 OGTag를 캐싱해서 피드에 내려주는 방식입니다. 성능 이슈 때문인지 캐싱된 OGTag는 크롤링 될 때의 스냅샷이 저장되고 개발자 콘솔에서 캐시를 초기화 해주지 않으면 OGTag가 수정되어도 반영이 되지 않습니다. 폴라에는 각 엔드나 프로필등의 주요화면에 URL 공유하기 기능이 있는데요. 위와 같은 이슈때문에 공유하기 URL을 생성할때 앱에서 URL 뒤에 타임스탬프를 붙여서 이를 회피하고 있습니다(캐싱이 되지 않도록).

그리고 OGTag가 없더라도 타이틀, 설명, 이미지를 적당히 가져와서 보여줍니다. 페이스북 크롤러가 미리보기에 사용할 값을들 파싱할때 OGTag가 있으면 OGTag를, 없으면 다른 곳에서 데이터를 가져오도록 구현되어 있는듯 하네요. http://develope.android.com 페이지를 가지고 테스트 해봤더니(ogtag가 없는 페이지) 타이틀은 title, 설명은 description을 가져옵니다. 이미지는 첫번째 이미지가 아니고 중간에 있는걸 가져오는데 뭘 기준으로 고르는지는 잘 모르겠네요.

마무리

폴라에서 공유하기 기능을 개발하면서 단순히 URL링크만 공유했는데 어떻게 다른 서비스에서는 저렇게 링크를 보기좋게 보여줄까 하고 궁금했습니다. 다른 분께도 이 글이 도움이 되면 좋겠습니다