Skip to content
This page has been auto-translated and may contain errors.View in English

리스트

docs.scrimba.com

웹 페이지의 상당 부분은 사실 리스트입니다: 내비게이션 메뉴, 검색 결과, 상품 그리드, 레시피의 재료 목록, 비밀번호 재설정 절차 같은 것들이요. 콘텐츠가 서로 관련된 항목들의 모음일 때마다 HTML에는 그것을 나타내는 요소가 있으며, 이를 사용하면 브라우저와 보조 기술을 사용해 페이지를 읽는 사람 모두가 그 항목들이 하나로 묶여 있다는 것을 알 수 있습니다.

순서 없는 리스트

항목의 순서가 중요하지 않을 때는 **순서 없는 리스트(unordered list)**를 사용합니다. 장보기 목록을 떠올려 보세요: 우유, 빵, 계란. 어느 것을 먼저 적든 상관없이, 그냥 한데 묶인 여러 가지 물건일 뿐입니다.

이 리스트는 두 개의 태그로 만듭니다. <ul> 태그가 리스트 전체를 감싸고, 각 항목은 자신만의 <li> 태그("list item") 안에 들어갑니다:

html
<ul>
  <li>Milk</li>
  <li>Bread</li>
  <li>Eggs</li>
</ul>

브라우저는 각 항목을 한 줄씩 보여주고 그 앞에 글머리 기호를 붙입니다. 여러분이 직접 글머리 기호나 줄바꿈을 넣은 것이 아니라, 이것이 리스트라고 알려주었기 때문에 브라우저가 그렇게 처리한 것입니다.

순서 없는 리스트는 순서에 아무 의미가 없는 항목들의 모음입니다. <ul> 요소가 컨테이너이고, 그 직속 자식은 모두 항목 하나를 담는 <li> 요소입니다:

html
<ul>
  <li>Free shipping over £40</li>
  <li>Returns within 30 days</li>
  <li>Two-year warranty</li>
</ul>

꼭 지켜야 할 규칙 하나: <ul>의 직속 자식으로 허용되는 것은 <li>뿐입니다. <ul> 안에 <p><div>를 바로 넣지 않습니다. 항목 안에 문단이나 링크를 넣고 싶다면 <li> 안에 넣으면 되고, <li>는 원하는 어떤 콘텐츠든 담을 수 있습니다.

글머리 기호와 들여쓰기는 브라우저의 기본 스타일일 뿐이며 의미와는 무관합니다. CSS를 배우게 되면 이를 자유롭게 바꾸거나 없앨 수 있고, 그래도 그 요소는 여전히 리스트입니다.

순서 없는 리스트는 순서가 중요하지 않은 항목들의 집합을 나타냅니다. <ul>이 컨테이너이고 각 항목은 <li>입니다. <ul>의 직속 자식으로 허용되는 것은 <li> 요소뿐이며(그리고 <template> 같은 스크립트 지원 요소도 허용됩니다), 그래서 <ul>과 그 항목들 사이에 빈 <div>를 끼워 넣는 것은 유효하지 않고 파서도 이를 리스트의 일부로 취급하지 않습니다.

html
<ul>
  <li>Free shipping over £40</li>
  <li>Returns within 30 days</li>
  <li>Two-year warranty</li>
</ul>

글머리 기호와 들여쓰기는 브라우저 내장 스타일시트에서 나오는 표현상의 기본값일 뿐 의미가 아닙니다. list-style: none을 적용하면 요소의 의미는 바뀌지 않은 채 그것들만 사라집니다. 이 구분이 중요한 이유는 의미는 요소 자체에 담겨 있고, 글머리 기호는 그저 겉모습일 뿐이기 때문입니다. 보조 기술(화면을 볼 수 없는 사람에게 페이지를 소리로 읽어주는 스크린 리더 같은 도구들을 통틀어 이르는 말)은 마커가 아니라 구조를 읽습니다. 리스트가 시작된다는 것과 그 안에 몇 개의 항목이 있는지를 알려주는 것입니다. 이 안내가 어떤 이점을 주는지는 중첩을 다루는 Deep Dive에서 다시 살펴보겠습니다.

Juno순서 없는 리스트 순서가 중요하지 않은 리스트에는 <ul>을 사용하고, 각 항목을 자신만의 <li>로 감싸세요. 글머리 기호와 줄바꿈은 브라우저가 알아서 붙여주니 직접 입력할 필요가 없습니다. 저도 처음엔 그걸 믿기 어려워서 자꾸 직접 점을 찍으려 했어요.
Juno순서 없는 리스트<ul>은 컨테이너이고, 그 직속 자식은 오직 <li> 항목뿐입니다. 링크나 문단처럼 더 풍부한 내용은 <li> 안에 넣어야 하고, <ul> 안에 그냥 흘려 넣어서는 안 됩니다. 글머리 기호는 나중에 바꿀 수 있는 기본 스타일이니, 모양이 아니라 의미를 기준으로 요소를 고르세요.
Juno순서 없는 리스트<ul>은 직속 자식으로 오직 <li>만 받으므로, 그 안에 낙오된 <div>가 있으면 잘못된 마크업입니다. 글머리 기호는 브라우저 스타일시트에서 온 것이고 의미와는 무관하므로, list-style: none은 점만 지우고 리스트는 그대로 유지합니다. 이 구분이 있기 때문에 CSS로 마커를 숨긴 뒤에도 스크린 리더가 여전히 항목 수를 셀 수 있는 것입니다.

순서 있는 리스트와 그 속성

가끔은 순서 자체가 핵심일 때가 있습니다. 레시피의 조리 단계, 경주의 순위, 차례대로 따라야 하는 절차 같은 것들이죠. 이런 경우에는 **순서 있는 리스트(ordered list)**를 사용하는데, 이 리스트는 항목에 번호를 자동으로 붙여줍니다.

작동 방식은 순서 없는 리스트와 똑같지만, 컨테이너가 <ul> 대신 <ol>입니다:

html
<ol>
  <li>Preheat the oven to 180°C</li>
  <li>Mix the flour and sugar</li>
  <li>Pour into the tin and bake for 25 minutes</li>
</ol>

이제 브라우저는 항목 앞에 글머리 기호 대신 1, 2, 3을 붙입니다. 나중에 중간에 새 단계를 추가하면 번호가 자동으로 다시 매겨집니다. 번호를 직접 입력할 필요가 없다는 것, 그게 바로 <ol>을 쓰는 이유입니다.

순서 있는 리스트는 순서에 의미가 있는 항목들을 위한 것입니다. 구조는 <ul>과 같고 컨테이너만 <ol>로 바꾸면 되며, 브라우저가 각 <li>에 자동으로 번호를 붙입니다. 번호가 자동으로 생성되므로 항목을 추가하거나 삭제해도 나머지 번호는 저절로 다시 매겨집니다.

<ol>에는 알아두면 유용한 속성이 세 가지 있습니다. start는 리스트가 시작하는 번호를 정합니다:

html
<ol start="4">
  <li>Whisk the eggs</li>
  <li>Fold in the flour</li>
</ol>

이 리스트는 4, 5로 이어집니다. 어떤 다른 콘텐츠가 리스트를 둘로 나눈 뒤 이어지는 경우에 유용합니다.

type은 번호 매기기 스타일을 바꿉니다: 1은 숫자(기본값), Aa는 문자, Ii는 로마 숫자입니다.

html
<ol type="a">
  <li>Gather your evidence</li>
  <li>Write the summary</li>
</ol>

이 리스트는 1, 2 대신 a, b로 이어집니다. 그리고 reversed는 거꾸로 카운트하는데, 카운트다운이나 순위를 아래에서부터 읽는 상위 5개 목록에 적합합니다:

html
<ol reversed>
  <li>Bronze</li>
  <li>Silver</li>
  <li>Gold</li>
</ol>

순서 있는 리스트는 순서가 의미를 갖는 항목들을 나타냅니다. <ol>은 각 <li>에 자동으로 생성된 값으로 번호를 붙이므로, 서수는 여러분이 직접 입력하는 콘텐츠가 아니며 항목이 이동해도 항상 올바르게 유지됩니다.

번호 매기기를 제어하는 속성은 세 가지입니다. start는 표시 스타일과 무관하게 첫 서수를 정수로 지정하므로, 로마 숫자 리스트에 start="4"를 쓰면 iv부터 시작합니다. type은 마커 스타일을 지정합니다: 1, A, a, I, i 중 하나입니다. reversed(불리언 속성으로, 존재 자체가 값이며 별도로 값을 지정하지 않습니다)는 항목 수부터 1까지 거꾸로 셉니다.

html
<ol type="I" start="4" reversed>
  <li>Foundations</li>
  <li>Rollout</li>
  <li>Review</li>
</ol>

스타일이 감추는 구분이 하나 있습니다. type은 마커가 그려지는 방식만 바꿀 뿐, 보조 기술에 전달되고 start가 사용하는 은 항상 내부의 정수입니다. 즉 문자로 스타일을 입힌 리스트라도 그 밑에서는 여전히 1, 2, 3번째 위치입니다. 서수가 콘텐츠 자체의 일부인 경우, 예를 들어 인용을 위해 번호가 붙은 법률 조항 같은 경우에는 텍스트에 번호를 직접 하드코딩하기보다 <ol>이 번호를 관리하게 두는 것이 좋습니다. <li>에 "3."을 직접 하드코딩하면 다시 번호가 매겨지지 않을뿐더러, 스크린 리더가 그것을 두 번 읽게 됩니다. 한 번은 자동으로 생성된 마커로, 또 한 번은 텍스트로요. 하나 더 알아둘 만한 것: 개별 <li>에 있는 value 속성은 그 항목 하나의 번호를 덮어쓰고, 리스트는 그 지점부터 다시 이어서 셉니다.

Juno순서 있는 리스트와 그 속성 순서가 중요한 경우, 예를 들어 레시피의 조리 단계 같은 경우에는 <ol>을 쓰면 브라우저가 항목에 번호를 붙여줍니다. 단계를 추가하거나 삭제해도 번호는 스스로 고쳐지니, 1.이나 2.를 직접 입력할 필요가 없습니다. 항목 자체는 이전처럼 <li>이고, 컨테이너만 다를 뿐입니다.
Juno순서 있는 리스트와 그 속성<ol>에는 유용한 속성이 세 가지 있습니다: 1이 아닌 다른 번호부터 시작하는 start, 숫자 대신 문자나 로마 숫자를 쓰는 type, 거꾸로 세는 reversed입니다. 이 셋 모두 여전히 번호를 브라우저가 자동으로 생성하게 해주며, 그것이 바로 순서 있는 리스트를 쓰는 근본적인 이유입니다.
Juno순서 있는 리스트와 그 속성type은 마커의 모양만 바꿀 뿐이고, 그 뒤에 있는 값, 즉 start가 사용하고 보조 기술이 읽는 값은 항상 정수입니다. 그러니 <li>에 "3."을 직접 입력하지 마세요. 그러면 번호가 다시 매겨지지 않고 스크린 리더가 그 숫자를 두 번 읽게 됩니다. 카운트는 <ol>에 맡기고, 항목 하나를 조정해야 할 때는 개별 <li>value를 쓰세요.

설명 리스트

각 항목이 사실상 한 쌍인 경우를 위한 세 번째 종류의 리스트가 있습니다: 단어와 그 뜻, 라벨과 그 값 같은 것들이요. 용어 사전이 가장 명확한 예로, 각 용어 뒤에 그 뜻이 이어집니다.

이것이 **설명 리스트(description list)**이며, 세 개의 태그를 사용합니다. <dl>이 전체를 감싸고, <dt>가 각 용어를 담고, <dd>가 그 용어에 대한 설명을 담습니다:

html
<dl>
  <dt>HTML</dt>
  <dd>The language that describes the structure of a page.</dd>
  <dt>CSS</dt>
  <dd>The language that describes how the page looks.</dd>
</dl>

브라우저는 각 설명을 해당 용어 아래 들여서 보여주므로, 한눈에 어떤 것과 어떤 것이 짝인지 알 수 있습니다. "dt"는 정의하려는 단어, "dd"는 그 정의라고 생각하면 됩니다.

각 항목이 독립된 것이 아니라 한 쌍을 이룰 때는 <ul>이나 <ol>보다 설명 리스트가 더 잘 맞습니다. 이는 세 요소로 이루어집니다: <dl>이 컨테이너이고, <dt>가 용어이고, <dd>가 그 용어에 연결된 설명입니다.

html
<dl>
  <dt>Author</dt>
  <dd>Priya Anand</dd>
  <dt>Published</dt>
  <dd>14 March 2024</dd>
  <dt>Reading time</dt>
  <dd>6 minutes</dd>
</dl>

이 예시는 용어 사전을 넘어서는 두 번째 큰 용도를 보여줍니다: 키/값 메타데이터, 즉 라벨과 그 값의 모음입니다. 이 그룹화는 유연합니다. 하나의 <dt>가 여러 개의 <dd> 요소로 이어질 수 있는데, 이는 한 용어에 여러 설명이 있을 때 그렇습니다. 그리고 여러 <dt> 요소가 하나의 <dd>를 공유할 수도 있는데, 이는 여러 용어가 같은 것을 의미할 때 그렇습니다. 콘텐츠가 자연스럽게 "이것, 그리고 그 세부사항"인 형태라면 <dl>을 사용하고, 항목들이 각자 독립적으로 존재한다면 <ul>이나 <ol>을 사용하세요.

설명 리스트는 이름과 값을 연결합니다. <dl>이 컨테이너이고, <dt>가 용어(이름)이고, <dd>가 설명(값)입니다. 브라우저와 보조 기술이 이해하는 구조는 그룹들의 연속이며, 하나의 그룹은 하나 이상의 <dt> 요소 다음에 하나 이상의 <dd> 요소가 이어지는 형태입니다.

html
<dl>
  <dt>Storage</dt>
  <dt>Capacity</dt>
  <dd>256 GB SSD</dd>

  <dt>Colour</dt>
  <dd>Graphite</dd>
  <dd>Silver</dd>
</dl>

바로 그 유연함이 핵심입니다: 여러 용어가 하나의 설명을 공유할 수 있고, 하나의 용어가 여러 설명을 가질 수 있습니다. 특히 실용적으로 잘 맞는 경우가 두 가지 있습니다. 첫째는 용어 사전 같은 용어-정의 콘텐츠입니다. 둘째는 명세 표나 게시물 세부 정보 같은 키/값 메타데이터로, 각 라벨이 그 값과 짝을 이루는 경우입니다. <dl>은 "이것들은 이름/값 쌍이다"라는 것을 <div>를 아무리 쌓아도 표현할 수 없는 방식으로 나타냅니다. 이름과 관련해 알아둘 만한 사실이 있습니다: <dt><dd>는 원래 "definition term(정의 용어)"과 "definition description(정의 설명)"을 뜻했지만, 최신 명세에서는 사전적 정의뿐 아니라 모든 이름/값 연관 관계로 그 의미를 의도적으로 넓혔습니다. 콘텐츠가 짝을 이루지 않는 동등한 항목들의 단순한 목록이라면 <dl>은 맞지 않는 도구이고, <ul>이 정직한 선택입니다.

Juno설명 리스트 설명 리스트는 쌍을 위한 것입니다: 용어와 그 뜻이요. 전체를 감싸는 <dl>, 각 용어를 담는 <dt>, 그 뒤에 이어지는 설명을 담는 <dd>를 사용하세요. 용어 사전을 떠올리면 됩니다. 단어, 그다음 정의, 단어, 그다음 정의.
Juno설명 리스트 각 항목이 짝을 이룰 때, 즉 용어와 그 세부사항이나 라벨과 그 값의 관계일 때, 동등한 항목들의 집합이 아닐 때 <dl>을 사용하세요. 그룹화는 상황에 맞게 유연합니다. 하나의 <dt>가 여러 <dd>를 가질 수 있고, 여러 <dt>가 하나의 <dd>를 공유할 수도 있습니다. 작성자나 날짜 같은 키/값 메타데이터가 용어 사전을 넘어 <dl>이 진짜 쓸모를 발휘하는 지점입니다.
Juno설명 리스트<dl>은 이름/값 쌍을 의미하며, 명세에서는 <dt><dd>의 의미를 사전적 정의를 넘어 라벨과 값의 어떤 관계로든 확장했으므로 메타데이터에도 쓸 수 있습니다. 여러 용어가 하나의 값을 공유할 수 있고 하나의 용어가 여러 값을 가질 수도 있는데, 이는 <div>를 아무리 나열해도 표현할 수 없는 것입니다. 항목들이 짝 없이 동등한 관계라면 그것은 <dl>이 아니라 <ul>입니다.

리스트 중첩하기

리스트는 다른 리스트 안에 들어갈 수 있습니다. 메뉴에 "음료"가 있고 그 아래 뜨거운 음료와 차가운 음료 선택지가 있고, 각각에 또 자신만의 선택지가 있을 수 있습니다. 하나의 리스트를 다른 리스트 안에 넣는 것을 **중첩(nesting)**이라고 합니다.

제대로 이해해야 할 핵심은 이것입니다: 안쪽 리스트는 <li> 안에 들어가야 하며, 항목들 사이에 떠 있으면 안 됩니다. 안쪽 리스트 전체(<ul> 또는 <ol>)를 그것이 속한 리스트 항목 안에 넣습니다:

html
<ul>
  <li>Drinks
    <ul>
      <li>Coffee</li>
      <li>Tea</li>
    </ul>
  </li>
  <li>Snacks</li>
</ul>

"Coffee"와 "Tea"는 "Drinks"에 속하므로, 그 작은 리스트는 "Drinks" 항목 안에 들어 있습니다. 브라우저는 안쪽 리스트를 조금 더 들여서 보여주므로, 그것이 위에 있는 항목에 속해 있다는 것을 알 수 있습니다.

어떤 항목들이 다른 항목에 속한다는 것을 보여주려면 리스트 안에 리스트를 중첩합니다. 이를 유효하게 유지하는 규칙은 이렇습니다: 중첩된 리스트는 항상 <li>의 자식이며, 바깥쪽 <ul>이나 <ol>의 직속 자식이 되어서는 절대 안 됩니다. 안쪽 리스트는 그것이 확장하는 항목 안에 자리합니다.

html
<ul>
  <li>Fruit
    <ul>
      <li>Apples</li>
      <li>Bananas</li>
    </ul>
  </li>
  <li>Vegetables
    <ul>
      <li>Carrots</li>
      <li>Spinach</li>
    </ul>
  </li>
</ul>

중첩할 때 종류를 섞을 수도 있습니다. <ul> 안에 <ol>을 넣거나 반대로 넣을 수도 있고, 콘텐츠가 실제로 요구하는 만큼 깊이 중첩할 수도 있습니다. 실제 코드에서 중첩된 리스트를 가장 흔히 만나는 곳은 하위 메뉴가 있는 내비게이션 메뉴입니다: 최상위 섹션 리스트가 있고, 각 섹션의 <li> 안에 그 섹션에 속한 페이지들의 리스트가 들어 있는 형태입니다.

계층 구조는 중첩으로 표현됩니다: 하위 리스트는 그것이 속한 <li> 안에 놓이며, 부모 <ul>이나 <ol>의 직속 자식으로 놓여서는 안 됩니다. 이를 잘못 처리하면(즉 <ul>이 다른 <ul> 안에 직접 놓이면) 유효하지 않은 마크업이 되고, 파서가 어떻게 복구할지는 여러분이 의도한 대로 맞아떨어지리라는 보장이 없습니다.

중첩은 또한 들여쓰기를 눈으로 볼 수 없는 사람들에게 구조가 실제로 도움이 되는 지점이기도 합니다. 스크린 리더는 각 단계에 들어갈 때 리스트, 그 항목 수, 그리고 중첩 깊이를 알려줍니다: "리스트, 세 항목"이라고 말한 뒤, 하위 리스트에서는 "리스트, 두 항목"이라고 말하고, 다시 빠져나올 때도 사용자에게 알려줍니다. 이 실시간 안내는 눈으로 들여쓰기를 공짜로 읽어내는 것에 대응하는 비시각적 버전이며, 이는 중첩이 공백으로 밀어붙인 흉내가 아니라 진짜 마크업일 때만 존재합니다.

이 지점에서 모든 리스트에 적용할 수 있는 정직성 테스트가 나옵니다. 내비게이션 메뉴(방문자를 페이지 사이에서 이동시키는 링크들의 모음으로, 대부분의 사이트 상단에 있는 그 줄)는 실제로 목적지들의 리스트이므로, 의미상 올바른 마크업은 링크들의 <ul>이며, 보통 그 영역이 내비게이션임을 나타내는 <nav> 요소 안에 들어갑니다. 콘텐츠가 서로 관련된 항목들의 집합이라면, 나중에 CSS가 어떻게 보이게 만들든 상관없이 진짜 리스트를 사용하세요. 그렇지 않은 경우에는 리스트를 쓰지 마세요: 서로 관련 없는 블록들 사이에 간격을 만들려고 그것들을 <li>로 감싸서 리스트의 여백을 빌려오면, 사실은 그렇지 않은데도 보조 기술에게 "이 세 가지는 하나의 세 개짜리 그룹이다"라고 말하는 셈이 됩니다. 마커는 숨길 수 있고 레이아웃은 무엇이든 될 수 있지만, <ul>, <ol>, <dl> 중 무엇을 선택할지는 언제나 그 콘텐츠가 정직하게 무엇인지에 답해야 합니다.

Juno리스트 중첩하기 리스트를 리스트 안에 넣으려면, 안쪽 리스트를 그것이 속한 <li> 안에 넣으세요. 항목들 사이에 떠 있게 두면 안 됩니다. 브라우저가 그것을 들여서 보여주므로 위에 있는 항목의 일부임을 알 수 있습니다. 그 "<li> 안에 넣기" 규칙 하나만 기억하면 됩니다.
Juno리스트 중첩하기 중첩된 리스트는 항상 <li>의 자식이며, 바깥쪽 <ul>이나 <ol>의 직속 자식이 되어서는 안 됩니다. <ul> 안에 <ol>을 섞어 넣을 수도 있고, 콘텐츠가 실제로 요구하는 만큼 깊이 들어갈 수도 있습니다. 하위 메뉴가 있는 내비게이션 메뉴에서 이런 상황을 가장 많이 만나게 되니, 지금 익숙해지는 것이 좋습니다.
Juno리스트 중첩하기 하위 리스트는 그것이 속한 <li> 안에 넣으세요. 스크린 리더가 항목 수와 중첩 깊이를 소리로 읽어주는데, 그 음성 구조가 바로 눈으로 볼 수 없는 사람에게 들여쓰기를 대신하는 역할을 합니다. 내비게이션 메뉴는 실제로 링크들의 리스트이므로 그렇게 표시하세요. 관련 없는 블록들을 여백을 빌리려고 <li>로 감싸지 마세요. 요소가 콘텐츠의 정직한 정체를 말하게 하고, 스타일은 그 뒤에 원하는 대로 입히세요.