EEltaCrew
블로그

한 페이지에 한 언어: IP 국가 판정과 한/영 병기를 버리고 브라우저 언어 + hreflang 으로 바꾼 이유

게시 2026-09-16실측 기준 2026-08-28읽는 시간 약 3분elta · EltaCrew

웹 서비스 6개의 초기 언어를 정하는 방식이 제각각이었습니다. IP 국가로 정하던 곳은 구글봇에게만 영어가 나가 검색 결과 언어를 통제할 수 없었고, 한/영을 한 페이지에 병기하던 랜딩은 구글이 기대하는 "페이지 하나에 언어 하나" 와 어긋났습니다. 전 웹을 한 규칙으로 통일한 과정을 씁니다.

제각각이던 초기 언어

웹 서비스 6개가 각자 다른 방식으로 첫 언어를 정하고 있었습니다.

각각 이유는 있었지만 전부 문제가 있었습니다.

IP 국가 판정의 문제

영어 브라우저를 쓰는 한국 거주자가 한국어를 받습니다. 그건 참을 수 있습니다. 진짜 문제는 구글봇입니다. 구글봇은 미국에서 오므로 항상 영어 페이지를 받았습니다. 한국어 검색 결과에 한국어 제목이 나갈 방법이 없었습니다. 검색 결과에 어떤 언어가 나갈지를 서버가 통제하지 못하는 구조였습니다. langFromCountry() 함수를 지웠습니다.

타임존 판정도 같은 이유로 버렸습니다. 클라이언트에서 JS 로 판정하면 크롤러가 보는 첫 HTML 은 판정 전 상태입니다.

병기의 문제

루트 랜딩은 한 페이지에 한국어 문단과 영어 문단을 나란히 두고 있었습니다. 처음 광고 심사에서 "본문이 짧다"는 오진을 하고 분량을 늘리려다 그렇게 된 것으로 추정합니다. 그런데 구글은 페이지 하나에 언어 하나를 기대합니다. 병기된 페이지는 어느 언어 결과에 넣을지 애매하고, lang 속성도 하나만 줄 수 있습니다.

통일한 규칙

2026년 8월 11일에 전 웹 공통 표준으로 정하고, 8월 28일에 예외였던 루트까지 맞췄습니다.

  1. 초기 언어는 브라우저 언어. Accept-Languageko* 면 한국어, 그 외는 영어. 서버 렌더링(SSR) 사이트는 헤더로, 정적 사이트는 첫 로드 JS 로 판정하되 어느 쪽이든 각 언어가 자기 URL 을 가집니다.
  2. 언어마다 URL 을 따로 둡니다. / = 한국어(lang="ko"), /en/ = 영어(lang="en"). 변환기 웹도 /mdhtml//mdhtml/en/ 로 나눴습니다.
  3. 양쪽 페이지에 hreflang 을 서로 걸고 x-default 는 한국어로, 각자 self canonical 과 og:locale 을 둡니다.
  4. 헤더에 언어 전환 링크를 둡니다. 자동 판정이 틀렸을 때 사용자가 한 번에 바꿀 수 있어야 합니다.
  5. 사이트맵에 두 URL 을 모두 넣습니다.

루트는 병기본을 원본으로 두고, 카드 22장(앱 16 + 웹 6)의 한국어·영어 문장을 분리해 두 페이지를 생성하는 스크립트로 만들었습니다. 병기본은 백업으로만 남깁니다.

폰트도 같이 통일

같은 날 폰트도 하나로 정리했습니다. Pretendard 가변 폰트의 동적 서브셋 CSS 하나만 씁니다. 포트폴리오의 Geist, 쇼케이스 제목의 Clash Display, 디렉토리의 Inter + Space Grotesk, 변환기의 Roboto + Noto Sans KR 을 전부 걷어냈습니다. 페이지마다 폰트가 다르면 한 사이트로 보이지 않습니다.

예외는 코드용 고정폭 폰트입니다. Pretendard 는 가변폭이라 코드 정렬이 깨지고, 웹 SSH 는 xterm.js 터미널이라 고정폭이 아니면 화면이 망가집니다. JetBrains Mono 는 유지합니다.

확인

라이브에서 curl -H "Accept-Language: en"ko 로 각각 받아 <html lang> 과 첫 문단 언어를 확인했고, 두 페이지의 hreflang 이 서로를 가리키는지, 사이트맵에 둘 다 있는지 봤습니다. 검색 결과 반영은 재크롤 뒤의 일이라 기다립니다.

정리

SEO다국어hreflang

함께 읽기

301 은 되돌릴 수 없다: 서브도메인 이전 43분의 실수가 5일 뒤 404 로 돌아온 이야기, 그리고 Cloudflare 엣지 캐시웹 서비스 두 개를 서브도메인으로 옮기던 날, 잘못된 301 이 43분간 나갔습니다. 서버는 바로 고쳤지만 브라우저는 5일 뒤에도 그 301 을 따라가 404 에 떨어졌습니다. permanent 리다이렉트를 내보내기 전에 해야 할 일, 이미 나간 301 을 구제하는 유일한 방법, 그리고 배포했는데 화면이 안 바뀌는 Cloudflare 엣지 캐시 사고를 씁니다.앱 안에서 언어를 바꿨는데 가끔 안 바뀌는 이유: setApplicationLocales 는 떠 있는 액티비티만 다시 만든다"한국어에서 English 로 바꿔도 그대로다, 껐다 켜야 바뀐다" 는 제보의 원인은 AppCompat 이 그 순간 화면에 떠 있는 액티비티만 재생성하기 때문이었습니다. 뒤에 멈춰 있던 화면은 옛 언어로 돌아옵니다. 앱 8개에 적용한 해결책과, 같이 걸린 함정 5가지를 씁니다.