후기 목록이 이렇게 생겼다.

<div className="grid grid-3">
  <div>후기 1</div>
  <div>후기 2</div>
</div>

보이는 건 <ul>/<li> 로 만든 것과 똑같다. 다른 것은 눈에 안 보이는 쪽이다 — 검색 엔진은 「여기 후기가 몇 개 있다」를 못 읽고, 화면 읽기 도구는 「목록, 항목 3개」를 안 알려준다.

안 A   div 로 만들고 필요하면 role 로 알린다
안 B   역할에 맞는 태그가 있으면 그것을 쓴다

먼저: 재보니 SEO 쪽은 잘 되어 있었다

이건 원칙이 아니라, 문제를 잘못 짚을 뻔한 기록이다.

처음엔 헤딩이 문제일 거라 보고, 페이지 하나하나(라우트)마다 h1이 있는지 셌는데 두 번 틀렸다.

1차   h1 이 없는 라우트 6개      →  하위 폴더를 중복으로 세서 틀림
2차   h1 이 없는 라우트 6개      →  grep -c 가 파일 하나일 때 형식이 달라 0으로 셈
3차   13개 라우트 전부 h1 이 있다  →  맞음

세 번째가 맞았다. 그리고 재보니 이쪽은 손댈 것이 없었다.

h1          13개 라우트 전부 정확히 하나      (분기 셋은 로딩·에러·정상이라 실제로는 하나)
헤딩 계층    Hero 가 h1, 각 섹션이 h2 — 순서가 맞다
JSON-LD     lib/structured-data.js 에 LocalBusiness
metadata    14곳에서 내보낸다

여기서 JSON-LD는 검색 엔진이 사람 대신 기계로 읽도록 페이지 안에 적어 둔 구조화 데이터이고, metadata는 페이지 제목·설명을 코드에서 내보내는 Next.js 기능이다. 둘 다 이미 자리를 잡고 있어 손댈 것이 없었다.

「값이 말이 되는지 보고 다음으로 간다」가 두 번 걸렸다. /about 에 제목이 없다는 결과가 이상해서 파일을 열었고, 거기서 측정이 틀린 걸 찾았다.


먼저: 그런데 태그는 거의 전부 div 다

div    202 개
p       83 개
section  23 개 · h2 23 개 · main 20 개 · h1 18 개
article   5 개
ul        2 개 · li  2 개
aside     0 개

ul 이 2개다. 후기 목록도 FAQ 목록도 div 로 되어 있다.

후기 목록   <div className="grid grid-3">
FAQ 목록    <div className="faq-groups">

왜 그렇게 했는지 기록이 없다. 「격자 배치를 하려고」였을 수 있는데 ul 도 display:grid 가 되므로 그 이유는 성립하지 않는다.


원칙 1. 역할에 맞는 태그가 있으면 그것을 쓴다

무엇을 기준으로 태그를 고르는가.

안 A — div 로 만들고 필요하면 role 로 알린다. 태그를 외울 필요가 없고 div 하나로 다 된다. 버린 이유는 role 을 붙이는 것도 기억해야 하는 일이라는 것이다. 실제로 목록에는 role="list" 조차 안 붙어 있다. 태그를 안 외우는 대신 role 을 외워야 하면 아낀 것이 없다.

그리고 role 은 화면 읽기 도구에만 알린다. 검색 엔진은 role 을 구조로 읽지 않는다.

골랐다 — 안 B, 역할에 맞는 태그가 있으면 그것을 쓴다.

이런 것이면 이 태그
여러 개가 나열된다 ul · li (순서가 뜻을 가지면 ol)
용어와 설명이 짝이다 dl · dt · dd
떼어내도 말이 되는 글 하나 article
제목이 있는 구획 section
페이지의 본문 main (페이지당 하나)
길 안내 링크 묶음 nav
본문 옆에 딸린 것 aside
위 어디에도 안 맞는 순수 배치 div

div 는 마지막 칸이다. 「이 덩어리에 이름을 붙일 수 있나」를 먼저 묻고, 붙일 이름이 없을 때만 div 다.

대가는 CSS 를 다시 손봐야 하는 것이다. ul 은 기본 여백과 점(list-style)이 붙어 있어 지워야 한다. 그 몇 줄이 이 표준의 전체 비용이다.


원칙 2. 태그가 없는 것에만 role 을 쓴다

role 자체가 나쁜 것은 아니다. 지금 쓰이는 role 은 전부 올바르다.

role="tablist" · role="tab"    HTML 에 「탭」 태그가 없다
role="dialog"                  커스텀 팝업이다
role="status"                  갱신되는 알림 영역이다

이것들은 네이티브 태그가 없어서 role 로 알리는 것이 정답이다.

판별은 한 줄이다 — 그 역할을 뜻하는 HTML 태그가 있는가.

있다   →  태그를 쓴다.  role 을 붙이지 않는다
없다   →  role 로 알린다

<ul role="list"> 처럼 태그와 role 을 겹쳐 쓰지 않는다. 태그가 이미 그 뜻이라 role 이 하는 일이 없다.


원칙 3. 안 쓰기로 했으면 그 이유를 파일에 적는다

원칙 1에 맞는 태그가 있는데 안 쓰기로 했다면, 왜 그랬는지를 그 자리에 적는다.

지금 후기 목록이 div 인 이유를 아무도 모른다. 판단이 있었는지, 그냥 그렇게 됐는지 알 수 없다. 이유가 안 적힌 판단은 되돌릴 근거도 없어서, 고치려 할 때마다 「혹시 무슨 이유가 있었나」를 처음부터 다시 확인해야 한다.

이건 만들 것인가 가져다 쓸 것인가 표준이 요구하는 것과 같은 기록이다. 거기서는 「왜 가져다 쓰지 않았나」를 적게 했고, 여기서는 「왜 그 태그를 안 썼나」를 적는다.

// ul 을 쓰지 않는 이유: <그 자리에서만 성립하는 사정>
<div className="...">

기본은 태그를 쓰는 쪽이다. 이유가 없으면 원칙 1을 따른다.


판단 기준 정리

질문 답 결론
무엇으로 태그를 고르나 역할에 맞는 태그가 있는가 「이 덩어리에 이름을 붙일 수 있나」
여러 개가 나열된다 ul · li 검색 엔진이 개수를 읽고 읽기 도구가 알린다
div 는 언제 쓰나 붙일 이름이 없을 때 마지막 칸이지 첫 칸이 아니다
role 은 언제 쓰나 태그가 없을 때만 탭·팝업·알림처럼 HTML 에 없는 것
태그와 role 을 같이 안 쓴다 태그가 이미 그 뜻이라 하는 일이 없다
태그를 안 쓰기로 했다 이유를 파일에 적는다 안 적으면 되돌릴 근거가 없다
ul 로 격자 배치가 되나 된다 display:grid 는 어느 요소에나 걸린다

이 표준을 정하기까지

SEO 를 고려해야 한다는 말에서 시작했고, 처음엔 헤딩을 의심했다. 세어 보니 h1 이 없는 라우트가 여섯이라고 나와서 「제목 없는 페이지가 절반」이라고 적을 뻔했다.

그 숫자가 이상해서 파일을 열었고, 측정이 틀린 걸 찾았다. 두 번 틀렸다 — 처음엔 하위 폴더를 중복으로 셌고, 고친 뒤에는 grep -c 가 파일 하나일 때 파일:개수 형식이 아니라 개수만 내놓는 걸 놓쳤다. 세 번째에 재니 13개 라우트 전부 h1 이 정확히 하나였다.

그래서 이 글에 「SEO 쪽은 잘 되어 있었다」를 먼저 적었다. 헤딩 계층도 JSON-LD 도 metadata 도 손댈 것이 없다. 문제를 잘못 짚었으면 안 고쳐도 될 것을 고치게 했을 것이다.

남은 것은 태그였다. div 202개에 ul 2개. 후기 목록도 FAQ 목록도 div 다.

그런데 role 쓰임새를 보니 방향이 하나 정해졌다. role="tablist" · role="dialog" · role="status" 가 쓰이고 있었고 전부 올바른 사용이다 — 탭도 팝업도 알림 영역도 HTML 에 태그가 없다. 접근성을 의식하고는 있었다. 다만 목록처럼 태그가 있는 것까지 div 로 간 것이 문제였다.

그래서 기준이 「div 를 쓰지 마라」가 아니라 「태그가 있으면 태그를 쓴다」가 됐다. div 는 나쁜 태그가 아니라 붙일 이름이 없을 때 쓰는 마지막 칸이다.

왜 div 로 만들었는지 물었더니 기록이 없었다. 「격자 배치 때문」이었을 수 있는데 ul 도 display:grid 가 되므로 그 이유는 성립하지 않는다. 그리고 이유가 안 적힌 판단은 되돌릴 근거도 없다 — 그래서 원칙 3으로 「안 쓰기로 했으면 이유를 적는다」를 넣었다. 앞 표준이 「직접 만들었으면 이유를 적는다」로 정한 것과 같은 자리다.


정리

  • 역할에 맞는 태그가 있으면 그것을 쓴다. 「이 덩어리에 이름을 붙일 수 있나」를 먼저 묻는다.
  • div 는 마지막 칸이다. 붙일 이름이 없을 때만 쓴다.
  • role 은 태그가 없을 때만 쓴다. 탭·팝업·알림은 HTML 에 태그가 없어 role 이 맞다.
  • 태그와 role 을 겹쳐 쓰지 않는다. 태그가 이미 그 뜻이다.
  • 태그를 안 쓰기로 했으면 이유를 파일에 적는다. 안 적으면 되돌릴 근거가 없다.
  • ul 로도 격자 배치가 된다. 기본 여백과 점을 지우는 몇 줄이 이 표준의 전체 비용이다.

자신만의 철학을 만들어가는 중입니다.
최상단으로 이동했습니다!
확대 이미지

댓글남기기