들어가며
이번에 다룰 얘기는 웹의 뼈대이자 근본이라 할 수 있는 HTML에 관련된 내용입니다. 우리 그룹은 기본적으로 기초적인 웹 접근성과 표준 문법을 준수하는 방향으로 개발이 이루어져야 한다는 것을 기본 정책으로 삼고 있는데요, 놓치기 쉬운 문서의 논리적 흐름에 관해서 얘기해 볼까 합니다.
개발 단위의 파편화
개발 시간을 효율적으로 활용하기 위해서 보통은 먼저 화면을 이루는 구성요소들을 파악하고 재사용성에 따라서 컴포넌트 단위로 분리한 뒤 UI 개발 작업을 진행합니다. 컴포넌트 단위로 분리된 구성요소를 먼저 작업한 뒤에 화면에 맞추어 조립되는 방식으로 작업이 된 것인데 개발 초기에는 문제점을 인지하기 어렵지만 화면이 어느 정도 윤곽이 잡히고 나면 다음과 같은 문제점들이 보이기도 합니다.
비효율적으로 개발되는 DOM
습관적으로 컴포넌트 전체를 무의미한 div로 감싸는 경우입니다. 페이지 전체적으로 봤을 때 역할이 미미하고 DOM 트리만 더 복잡해지는 결과를 가져옵니다.
// 불필요한 래퍼 요소로 감싸진 스타일드 컴포넌트
const StyledComponent = () => (
<Container>
<Title>제목</Title>
<Content>내용</Content>
</Container>
);
const Container = styled.div``;
const Title = styled.h1`
font-size: 20px;
font-weight: 600;
`;
const Content = styled.p`
margin-top: 12px;
`;
이러한 경우 컴포넌트 자체에서는 필요한 마크업만 제공하고, 필요한 경우에만 부모 컴포넌트에 감싸기 용도의 요소를 추가해가며 화면을 완성하는 순서로 개발이 진행되어야 합니다.
// 불필요한 요소를 제거한 스타일드 컴포넌트
const StyledComponent = () => (
<>
<Title>제목</Title>
<Content>내용</Content>
</>
);
const Title = styled.h1`
font-size: 20px;
font-weight: 600;
`;
const Content = styled.p`
margin-top: 12px;
`;
논리적이지 못한 마크업
css 스타일링만을 위해서 무의미한 <div>
가 사용되는 경우도 있습니다.
flex 정렬의 경우 컨테이너 역할을 하는 부모 요소의 존재가 필수인데 이 경우에는 수직 정렬을 위해서 무의미하게 사용되고 있습니다.
const StyledComponent = () => (
<Container>
<Item />
<Item />
<Item />
</Container>
);
const Container = styled.div`
display: flex;
flex-direction: column;
row-gap: 8px;
`;
const Item = styled.div`
...
`;
상단 마진만으로도 동일한 결과물을 얻을 수 있으므로 부모 컨테이너를 제거하여 DOM 트리를 좀 더 정리할 수 있습니다.
const StyledComponent = () => (
<>
<Item />
<Item />
<Item />
</>
);
const Item = styled.div`
margin-top: 8px;
&:first-child {
margin-top: 0;
}
`;
정확하지 않은 제목 요소 순서
조립 단계에서 선언된 제목 요소의 순서를 정확하게 파악하지 못한 채로 작업된 경우입니다.
☑️ chrome 라이트하우스에서도 제목이 올바르게 선언되지 않는 경우 접근성 저해 요소로 판단하여 에러로 표시합니다.
컴포넌트 내부에서 제목 요소를 정하기보다는 화면 전체 단위에서 위계를 판단하여 내려줄 수 있는 방법을 시도하는 것이 좋습니다.
보통 이런 문제들은 논리적 구조 설계와 연관되어 있습니다.
웹페이지는 ‘문서’다
HTML 파일의 시작은 doctype 선언부터 시작됩니다. doctype은 HTML의 버전을 선정하는 기능이지만 좀 더 확대해석해 보자면 표준 모드가 무엇인지 알려줌으로써 웹 브라우저가 해당 표준 명세를 가지고 웹페이지를 분석, 판단하도록 합니다. 즉, 본질적으로 이 웹페이지는 일종의 문서라고 볼 수 있으며 이는 구조적이고 논리적으로 전달하고자 하는 정보를 설계해야 함을 의미합니다.
콘텐츠가 잘 설계되었는지는 웹페이지에 적용된 css를 모두 비활성화해보면 알 수 있습니다. 이렇게 하면 웹페이지가 문서편집기와 시각적으로 유사한 브라우저의 기본 스타일로 되돌아갑니다. 적절한 시맨틱 마크업이 이루어졌는지 시각적으로 잘 알아볼 수 있으며 문서의 전반적인 논리적 흐름에 대해서도 쉽게 점검해 볼 수 있게 됩니다.
☑️ 포털사이트 daum의 모든 css를 비활성화한 상태. 콘텐츠가 논리적으로 구성되어 있으며 건너뛰기도 충실히 제공되고 있음을 알 수 있다.
다들 아시다시피 잘 설계된 구조는 웹 접근성 및 사용자 경험과도 연관되죠. HTML5의 섹션화 콘텐츠와 아웃라인 알고리즘을 눈여겨봐야 하는 이유입니다.
섹션화 콘텐츠와 제목
HTML5의 모든 요소들은 각각 역할이 부여되어 있으며 콘텐츠 카테고리에 의해 분류됩니다. 어떠한 요소들은 중복으로 부여된 역할이 있기도 합니다. 지금 살펴볼 요소들은 대표적인 섹션화 콘텐츠 요소들입니다.
<header>
: 페이지 섹션에 대한 소개 콘텐츠 또는 탐색 링크 세트를 정의합니다. 제목, 작성자 이름 및 기타 소개 정보가 포함된 섹션이나 기사의 시작 부분에 자주 사용됩니다.<footer>
: 일반적으로 작성자, 저작권 데이터 또는 관련 링크에 대한 정보를 포함하는 섹션의 바닥글 또는 문서 자체를 나타냅니다.<nav>
: 탐색 링크용으로 지정된 이 요소는 다른 페이지나 페이지 내의 부분으로 연결되는 페이지 섹션을 포함하기 위한 것입니다. 이는 페이지의 모든 링크에 대한 것이 아니라 주요 탐색 링크 블록에 대한 것입니다.<article>
: 독립적으로 배포하거나 재사용할 수 있는 문서, 페이지 또는 사이트(예: 잡지 또는 신문 기사)의 독립적인 구성에 사용됩니다.<section>
: 특정 주제와 관련된 문서의 독립형 섹션을 나타내며 내용이 문서 전체와 관련이 있을 때 사용해야 합니다. 일반적으로 제목이 포함됩니다.<aside>
:<aside>
요소 주변의 콘텐츠와 접선적으로 관련된 콘텐츠를 표시합니다. 이러한 섹션은 사이드바나 설명 상자로 표시되는 경우가 많습니다.<h1>
~<h6>
: 이 태그는 섹션 제목의 6가지 수준을 나타냅니다.<h1>
은 가장 높은(또는 기본) 수준이고<h6>
은 가장 낮은 수준입니다. 섹션의 제목이나 콘텐츠의 하위 섹션을 정의하는 데 사용됩니다.<main>
: 문서의 주요 내용을 지정합니다. 문서에는<main>
요소가 하나만 있어야 합니다.<main>
태그 내부의 콘텐츠는 문서에 고유해야 하며 페이지 전체에서 반복되어서는 안 됩니다(예: 사이트 전체 사이드바 또는 바닥글).
대부분의 마크업 개발자들은 <header>
, <footer>
등의 태그는 이미 익숙히 알고 있으며 그 역할 또한 잘 이해하고 있으나 이 태그들이 페이지 곳곳에서 여러번 쓰일 수 있다는 점을 알고 있는 사람은 많지 않습니다.
HTML5의 아웃라인 알고리즘
아웃라인 알고리즘이란 HTML5 표준 명세에서 문서의 의미적인 구조 분리를 개선하기 위해서 도입된 개념입니다. 문서를 논리적, 의미론적으로 구분할 수 있도록 하며 스크린리더 같은 보조 기기에서는 제목 사이사이를 건너뛰며 탐색하는 방법을 제공할 수 있도록 하여 보다 효율적으로 웹페이지를 이용할 수 있도록 하는 것이 취지입니다.
아웃라인 알고리즘에서는 제목 요소를 사용함으로써 암묵적인 아웃라인이 형성됩니다. 제목 요소의 위계에 따라서 논리적인 구조를 형성하는 것은 그동안 우리가 자주 사용했던 논리구조 확보 방법이죠. 그렇기 때문에 암묵적인 제목 선언은 논리적인 순서대로 이루어져야 하고 주제별로 잘 그룹화되어야 합니다.
☑️ 포털사이트 daum의 제목 아웃라인. 주제별로 위계가 확실하게 선언되어 있어 논리적인 흐름도 매우 매끄럽다.
그런데 <section>
, <article>
, <nav>
, <aside>
요소는 명시적으로 아웃라인을 형성해 줄 수 있습니다. (<header>
와 <footer>
는 아웃라인을 형성하지 않습니다.) 이렇게 명시적으로 형성된 아웃라인에서도 제목은 선언해 주어야 하지만 (제목이 없는 무명 섹션은 권장되지 않습니다) 암묵적인 아웃라인보다는 유연한 제목 요소의 사용이 가능해집니다.
가령 가장 최고 레벨인 <h1>
을 사용하여 제목을 선언했어도 하위에 아웃라인이 잘 형성되어 있다면 <h1>
을 다시 선언해도 웹브라우저는 아웃라인 알고리즘에 의거해서 문서의 구조를 해석합니다. 문서 최상단에서 선언된 <h1>
요소는 문서의 대 제목이 되는 것이고 특정 섹션 안에서 선언된 <h1>
요소는 해당 섹션의 대 제목이 되는 것이죠.
아래 간단한 예시가 있습니다.
<body>
<h1>문서 대표 제목</h1>
<div>
<h1>섹션1의 제목1</h1>
<h2>섹션1의 제목2</h2>
</div>
<div>
<h1>섹션2의 제목1</h1>
</div>
</body>
위 코드의 아웃라인을 살펴보면 다음과 같습니다. <div>
는 섹션화 콘텐츠가 아니기 때문에 명시적인 아웃라인을 형성해 줄 수 없었고, 제목 요소가 형성하는 암묵적 아웃라인으로 인해 논리적 흐름이 깨진 상태입니다.
위의 예시에서 섹션화 요소를 잘 사용하면 문서의 논리적 흐름을 올바르게 잡아줄 수 있게 됩니다.
<body>
<h1>문서 대표 제목</h1>
<section>
<h1>섹션1의 제목1</h1>
<h2>섹션1의 제목2</h2>
</section>
<section>
<h1>섹션2의 제목1</h1>
</section>
</body>
수정된 코드의 아웃라인을 살펴보면 다음과 같이 표현되는 것을 알 수 있습니다.
이렇게 잘 섹션화된 구조에서는 분리된 섹션의 제목 순서를 h1부터 사용하게끔 유도하여 어느 정도 개발자의 실수로 인하여 중간 레벨의 제목이 누락되는 것을 방지할 수 있게 돕습니다.
마치며
아주 간단한 예시를 들긴 했지만 신경 쓰지 않으면 그냥 지나칠 수 있을 웹문서의 논리적인 구조 확보에 대한 내용에 대해 한번 생각해 보았습니다.
HTML5 요소들의 의미론에 대한 부분은 사실 개발자들 사이에서도 의견이 다양합니다. 하나의 디자인을 어떻게 해석하는지에 따라서 전혀 다른 마크업 구조를 가지게 되는 경우도 많고 제목의 사용 빈도에 대해서도 의견이 분분합니다. 이렇듯 HTML은 웹의 가장 근본이며 진입장벽이 매우 낮은 언어에 속하지만 심화적인 부분으로 진입하게 되면 가장 난해한 언어이기도 한 것 같습니다.
한편, 프런트 엔드 개발 기술은 변화가 급격하고 그 생명주기가 빨라졌습니다. 어느샌가 새로운 기술이 대세로 떠오르기도 하고 불과 10년 전 너도 나도 사용하던 기술은 급격하게 도태되기도 합니다. 프런트 엔드 개발자들은 이런 폭풍과도 같은 변화에 빠르게 적응해야 하는 상황에 놓여 있습니다. 환경이 그래서일까요? 새로운 기술을 익히는 데만 치중하고 그 근본인 HTML(물론, CSS도 포함이겠죠)은 등한시하는 안타까운 모습이 종종 목격되기도 합니다. 하지만, 탄탄한 기본이 밑받침되어야 그 위에 세워지는 프런트 기술도 굳건하지 않을까 합니다. 어차피 어떤 기술을 사용하여 만들건 웹문서를 이루는 구성은 결국 HTML, CSS, javascript이기 때문입니다.
우리가 HTML과 CSS에 대한 공부를 게을리하면 안 되는 이유들이라고 생각합니다. 여러분의 생각은 어떠신가요?
읽을 거리 : https://html.spec.whatwg.org/multipage/sections.html#headings-and-outlines