- HTML 목록은 시각적 모양보다 의미와 상호작용 방식으로 골라야 하며, 컨트롤·순서·설명·메뉴·비순서 목록으로 나뉨
- 고정 선택지는 <select>/<option>, 제안 입력은 <datalist>가 맞고, multiple, optgroup, size, value 동작을 구분해야 함
- <ol> 은 순서를 바꾸면 의미가 달라지는 절차·사건·연속체에 쓰며, reversed는 번호만 뒤집고 실제 항목 순서는 바꾸지 않음
- <dl> 은 HTML5에서 정의 목록을 넘어 설명 목록으로 확장됐고, 용어-값, 메타데이터, JSON 디버깅 같은 키-값 쌍에 맞음
- <menu> 는 도구 버튼 같은 명령 목록에 쓰며 nav와 의미·허용 콘텐츠가 다르고, 나머지 일반 목록은 <ul>이 담당함
컨트롤 목록: <select>/<option>과 <datalist>
- 폼에서도 사용자가 상호작용하는 목록을 구성할 수 있음
-
고정된 선택지는 <select>와 <option>
- 사용자가 목록 안의 항목만 고를 수 있어야 하면 <select>와 <option>을 사용함
- 언어 목록 예시는 Select a Language, English, French, Spanish, Portuguese 같은 <option>을 <select name="languages"> 안에 배치함
- 기본 <select>는 정확히 하나의 선택지만 고르게 함
- 여러 항목을 고르게 하려면 multiple 속성을 추가함
- multiple을 사용하면 목록 표시가 달라지고 모든 옵션이 보이며, 사용자는 Shift 또는 Cmd + 클릭으로 여러 항목을 고를 수 있음
- 실제 select와 option을 쓰면 role="listbox"가 붙은 목록 요소에 aria-multiselectable을 직접 붙일 필요 없이 브라우저 기본 시맨틱이 처리함
-
관련 옵션은 <optgroup>으로 묶기
- 언어를 어족별로 묶고 싶으면 <optgroup>으로 옵션 목록을 그룹화함
- 예시는 Germanic, Romance, Celtic이라는 label을 가진 <optgroup>을 만들고, 각 그룹에 English, French, Spanish, Portuguese, Irish, Welsh를 배치함
- 특정 하위 묶음을 선택하지 못하게 하려면 해당 <optgroup>에 disabled 속성을 추가함
- 예시에서는 Celtic 그룹에 disabled를 붙여 Irish, Welsh가 포함된 묶음을 비활성화함
-
기본 HTML 기능으로 개선하기
- 그룹 사이에 시각적 구분이 필요하면 <select> 안에서 허용되는 <hr> 을 사용할 수 있음
- size 속성은 한 번에 표시할 항목 수를 제어하므로 긴 목록에 유용함
- size와 optgroup을 함께 쓰면 그룹 라벨도 표시 공간을 차지함
- 예시는 <select name="languages" size="4" multiple>에 Germanic, Romance, Celtic, Afroasiatic 그룹과 그룹 사이 <hr />를 넣고, Hebrew, Arabic까지 포함함
제안 목록: <datalist>
- 사용자가 반드시 목록에서만 고르는 것이 아니라 목록을 제안하려면 <datalist>를 사용함
- <datalist>는 두 단계로 연결함
- <datalist>를 만들고 id를 부여함
- 대응하는 <input>의 list 속성에 그 id 값을 넣음
- 언어 제안 예시는 <datalist id="languages"> 안에 English, French, Spanish, Portuguese, Irish, Welsh, Hebrew, Arabic 옵션을 두고, <input name="language" list="languages">로 입력 필드와 연결함
-
<option value>의 동작
- <option>의 기본값은 감싼 텍스트이고, value 속성이 있으면 그 값이 기본값을 덮어쓰며 텍스트는 라벨처럼 동작함
- <select>에서는 사용자가 텍스트만 보므로 큰 문제가 아니지만, <datalist>에서는 사용자가 라벨을 보고 선택한 뒤 입력창에는 value가 들어가 혼란스러울 수 있음
- 예시에서 <option value="cy">Welsh</option>를 선택하면 사용자는 Welsh를 보지만 입력에는 cy가 들어감
- <datalist>를 쓸 때는 삽입되는 값이 라벨이 아니라 value 라는 점을 전제로 해야 함
-
여러 입력 타입과 결합
- <datalist>는 텍스트 옵션에만 쓰이지 않고 다른 input 타입과도 함께 쓸 수 있음
- 주 단위 선택 예시는 <input type="week" name="week" id="camp-week" min="2026-W2" max="2026-W51" list="preferred-weeks" />에 <datalist id="preferred-weeks">를 연결함
- 제안 주차는 2026-W22, 2026-W23, 2026-W24, 2026-W25임
-
<input type="range">와 결합
- <datalist>는 문자열 값에만 제한되지 않고 숫자와도 동작하므로, range 입력과 결합해 범위 위에 라벨이 붙은 지점을 만들 수 있음
- 팁 비율 입력 예시는 <input type="range" name="tips" id="tips" min="0" max="50" step="1" list="recommended-tips" />에 <datalist id="recommended-tips">를 연결하고 10%, 18%, 30%, 45% 라벨을 둠
- Chrome 계열에서는 @supports (x: attr(x type(percentage)))로 attr()의 label 값을 읽고, type()으로 값을 퍼센트로 선언한 뒤 옵션을 position: absolute로 배치함
- Firefox용 접근은 @supports not (x: attr(x type(percentage)))를 사용하며, 값은 ::before로 표시됨
- 이 방식은 모든 브라우저가 같은 방식으로 동작하거나 화면에 동일하게 표시된다고 보장하지 않음
순서 목록: <ol>
- 특정 순서로 읽어야 하는 항목 모음은 <ol> 을 사용함
- 항목 옆에 숫자가 보여야 하는지가 아니라, 항목의 순서를 바꾸면 목록의 의미가 달라지는지가 기준임
- <ol>에 적합한 컬렉션은 알고리듬, 사건의 연속, 증가 또는 감소하는 연속체 위의 항목, 레시피, 알파벳순 목록임
- 바나나 빵 레시피 예시는 오븐 예열과 팬 기름칠, 재료 섞기, 반죽 붓기, 60분 굽기 또는 이쑤시개가 깨끗하게 나올 때까지 굽기, 와이어 랙에서 식히기 순서를 <ol>로 표현함
- 알파벳순 재료 목록도 알파벳이라는 연속체를 따르므로 <ol>에 해당하며, baking soda, bananas, brown sugar, butter, eggs, flour, salt 순서로 배치됨
-
순서 목록과 비순서 목록의 중첩
- 잘 구조화된 순서 목록은 브라우저 렌더링을 보지 않아도 어떤 일이 어떤 순서로 일어나야 하는지 읽을 수 있음
- 레시피 예시는 상위 단계에 <ol>을 쓰고, 단계 안에서 순서가 중요하지 않은 항목은 <ul>, 다시 순서가 중요한 하위 단계는 <ol>로 중첩함
- 구조는 Prepare, Mix, Pour, Bake, Cool의 상위 순서를 유지하면서, Prepare와 Bake 안의 병렬 항목은 <ul>로, Mix와 Cool 안의 절차는 <ol>로 표현함
-
reversed
- reversed 속성은 번호 매김 순서를 오름차순에서 내림차순으로 바꿈
- 실제 목록 항목의 순서는 바꾸지 않음
- most to least처럼 많은 것에서 적은 것으로 보여주는 재료와 수량 목록에 사용할 수 있음
- 예시는 <ol reversed>에 eggs (2), flour (2 cups), bananas (2) (mashed), brown sugar (¾ cup), butter (½ cup), baking soda (1 teaspoon), salt (¼ teaspoon)을 넣음
-
JavaScript로 실제 항목 순서 뒤집기
- 목록을 실제로 뒤집으려면 JavaScript로 li 자식들을 역순으로 재배치하고 reversed 속성을 토글할 수 있음
- 예시 함수는 list.querySelectorAll('li') 결과를 배열로 만들고 .reverse()한 뒤, list.innerHTML = ''로 비우고 list.append(...children)으로 다시 붙임
- 마지막에 list.toggleAttribute('reversed')를 호출함
- 예시 이벤트는 orderedList.addEventListener('dblclick', (evt) => { reverseList(orderedList) })로 더블클릭 시 뒤집기를 실행함
-
start
- start 속성은 하나의 거대한 목록 대신 여러 개의 순서 있는 목록으로 나눌 때 번호의 연속성을 유지하는 데 쓰임
- 바나나 브레드 레시피 예시에서 Prepare는 ul로 두고, Mix는 <ol start=2>, Pour는 <ol start=5>, Cool은 <ol start=7>처럼 이어지는 단계 번호를 각 목록에 지정함
- 중간에 6: Bake처럼 순서 없는 목록으로 표현된 섹션이 있어도, 이후 Cool의 ol을 start=7로 시작해 전체 절차의 번호 흐름을 유지할 수 있음
설명 목록: <dl>, <dt>, <dd>
- 설명 목록(description list) 은 모든 내용을 ol이나 ul에 억지로 넣지 않아도 되게 해주는 목록 유형임
-
HTML 4의 정의 목록
- HTML 4에서는 description list가 아니라 정의 목록(definition list) 으로 불렸고, 정의를 제공하는 좁은 용도에 맞춰져 있었음
- 구조는 정의할 용어인 <dt>와 정의 내용인 <dd>로 구성됐으며, 의미적으로 정확하게 쓰려면 정의되는 용어를 <dfn>으로 감쌈
- 예시는 throw, yeet을 같은 정의에 연결하고, no cap, bet에 각각 정의를 붙임
<dl>
<dt><dfn>throw</dfn></dt>
<dt><dfn>yeet</dfn></dt>
<dd>Verb. To discard at a high velocity</dd>
<dt><dfn>no cap</dfn></dt>
<dd>Interjection. Expresses authenticity and truthfulness, sometimes surprise.</dd>
<dt><dfn>bet</dfn></dt>
<dd>Interjection. Expresses agreement and affirmation.</dd>
</dl>
-
HTML5에서 넓어진 의미
- HTML5에서는 정의에만 국한되지 않는 설명 목록이 됐고, “용어와 값의 집합”이 있을 때 쓸 수 있음
- HTML5에서는 관련된 <dt>와 <dd>를 묶기 위해 비의미적 래퍼인 <div>를 허용함
- 브라우저 엔진 예시에서는 Chrome, Opera, Brave, Edge를 Blink-based browsers에 묶고, Firefox, Tor, Librewolf를 Gecko-based browsers에 묶음
<dl>
<div class="dl-item">
<dt>Chrome</dt>
<dt>Opera</dt>
<dt>Brave</dt>
<dt>Edge</dt>
<dd>Blink-based browsers</dd>
</div>
<div class="dl-item">
<dt>Firefox</dt>
<dt>Tor</dt>
<dt>Librewolf</dt>
<dd>Gecko-based browsers</dd>
</div>
</dl>
-
메타데이터와 JSON 디버깅
- 사실과 레이블의 연속이라면 메타데이터 표시에 설명 목록을 쓰는 것이 적합함
- 사용자 프로필도 <dl>에 들어갈 수 있으며, 예시는 First Frank, Last Taylor, Age 44, Job Writer, Handle Paceaux를 <dt>와 <dd> 쌍으로 표현함
- 단일 페이지 애플리케이션에서 JSON 디버깅용으로도 설명 목록을 사용할 수 있음
- DebugJson 예시는 객체의 각 key, value를 Object.entries(obj)로 순회해 키는 <dt><var>...</var></dt>, 값은 <dd><code>...</code></dd>로 렌더링함
- 값이 객체이고 배열이 아니면 DebugJson.createDl(value)를 다시 호출해 중첩된 <dl>을 만들고, 그 외 값은 value.toString()을 <code> 안에 넣음
const debugJson = new DebugJson({foo: 'bar', arr: ['a', 'b'], car: 1}, '.container')
debugJson.render();
메뉴: <menu>
- menu 요소는 명령의 목록을 나타내며, 콘텐츠 렌더링보다 상호작용적인 웹에 더 가까움
- menu는 리치 텍스트 편집기의 텍스트 수정 컨트롤처럼 “도구”에 해당하는 버튼 목록에 맞음
- 리치 텍스트 편집기 예시는 Strong, Emphasize, Strike 버튼을 menu 안의 li로 배치함
<menu>
<li><button onclick="strong()">Strong</button></li>
<li><button onclick="emphasize()">Emphasize</button></li>
<li><button onclick="strike()">Strike</button></li>
</menu>
- HTML 명세상 이는 툴바(toolbar) 용도이며, 상호작용 페이지에서 도구 버튼이 있는 곳은 menu에 들어갈 가능성이 큼
- 비디오 컨트롤 예시도 menu에 들어가며, button에 commandfor="vid-123"과 command="--play", --mute, --fullscreen을 둠
<div class="player player--video">
<video source="whatever.mp4" id="vid-123"></video>
<menu>
<li><button commandfor="vid-123" command="--play">Play</button></li>
<li><button commandfor="vid-123" command="--mute">Mute</button></li>
<li><button commandfor="vid-123" command="--fullscreen">Fullscreen</button></li>
</menu>
</div>
- menu를 쓰면 순서 없는 목록에 aria-role="menu"를 추가할 필요가 없음
-
li가 두 컨테이너에만 들어간다고 가정하지 않기
- 내비게이션의 목록 항목을 일반 목록처럼 보이게 하고 싶지 않다면, menu li 에도 같은 처리가 필요함
- 스타일시트 상단에 nav li뿐 아니라 menu li까지 포함해 list-style-type, text-indent, margin을 초기화함
nav li,
menu li {
list-style-type: none;
text-indent: 0;
margin: 0;
}
-
<nav>와 <menu>는 다름
- nav는 단순히 링크가 들어간 menu가 아니며, 의미와 허용 콘텐츠가 다름
- nav는 사용자에게 “어딘가로 가는 것에 관한 여러 항목”을 제공한다는 의미를 가진 섹션 요소임
- nav는 <p>, <h1-6> 같은 문단과 제목, 그리고 <ul>, <ol>, <menu> 같은 목록을 포함할 수 있음
- menu는 사용자에게 “할 수 있는 것들의 목록”을 제공한다는 의미를 가진 목록 요소이며, 목록 항목인 <li>만 허용함
- menu와 nav는 상호 배타적 선택지가 아니며, menu는 nav 안에 들어갈 수 있지만 nav는 menu 안에 들어갈 수 없음
비순서 목록: <ul>
- ul은 다른 목록 유형과 nav로 해결되지 않는 나머지 목록 요구를 담는 캐치올 목록임
- 예전 HTML에서는 순서 있는 목록과 순서 없는 목록의 차이가 숫자인지 불릿인지 같은 시각적 차이에 가까웠음
- 현재는 접근성, 스크린 리더, 검색 엔진 최적화를 고려하므로 시각적 표시보다 순서가 의미를 갖는지에 집중해야 함
- 밴드 멤버 목록은 순서가 중요하지 않으므로 ul에 해당함
<h3>Beatles</h3>
<ul>
<li>John Lennon</li>
<li>Paul McCartney</li>
<li>Ringo Star</li>
<li>George Harrison</li>
</ul>
- 밴드 이름 목록도 순서 없는 목록으로 표현할 수 있음
<ul>
<li>Beatles</li>
<li>Rolling Stones</li>
<li>Van Halen</li>
<li>Foo Fighters</li>
</ul>