Frontend 05

HTML class와 id, 이름표 이상의 의미

class와 id를 둘 다 이름 붙이는 용도로만 생각해서 CSS나 JavaScript에서 왜 다르게 쓰는지 헷갈린다.

근거 · 교안 p15-p17

HTML class와 id, 이름표 이상의 의미 대표 이미지

1장: HTML 속성, 이름표 그 이상: 태그에 정보 더하기

솔라의 손끝이 노트북 화면 위를 불안하게 맴돌았다. 이제 막 만들기 시작한 작은 게시판 페이지의 코드였다. 눈으로는 분명 <div><p> 같은 익숙한 태그들을 보고 있었지만, 머릿속은 깔끔하게 정리되지 않았다. 솔라는 잠시 키보드에서 손을 떼고, 옆에 놓인 노트에 무언가를 끄적였다.

class="post" …이름표? id="latest-post" …이것도 이름표?

같은 태그에 붙은 두 개의 다른 이름표. 솔라는 고개를 갸웃거리며 혼잣말을 중얼거렸다.

“어차피 이름 붙이는 거면 하나만 쓰면 되지 않나…?”

그때, 거실에서 조용히 책을 읽던 루나가 솔라의 중얼거림을 들었는지 시선을 돌렸다. 루나는 솔라의 화면과 노트를 번갈아 보더니, 말없이 솔라의 코드 편집기를 가리켰다.

“솔라, 거기 말고 이 태그는 어때?”

루나의 손가락이 가리킨 곳은 링크를 거는 <a> 태그였다. <a href="https://example.com">...</a>. 솔라는 잠시 그 코드를 들여다봤다.

“이건… 링크 주소를 알려주는 href 속성이잖아.”

“그럼 이건?”

루나는 이번엔 이미지를 보여주는 <img> 태그를 짚었다. <img src="image.jpg" alt="귀여운 고양이">.

“이미지 파일 경로를 알려주는 src랑, 이미지가 안 보일 때 대신 나올 글자를 알려주는 alt 속성이고.” 솔라는 기계적으로 대답하다가 문득 멈칫했다. 아. classid만 ‘속성’이 아니었다. href, src, alt도 모두 태그에 뭔가를 더해주는 속성이었다.

“잠깐만… 그럼 얘네도 다 이름표 같은 건가?” 솔라가 스스로에게 묻듯 말했다. 하지만 말이 끝나기도 전에 스스로 고개를 저었다. 아니, 그건 아니었다. href는 ‘이곳으로 이동해’라는 분명한 목적을 가진 정보다. src는 ‘이 그림을 가져와’라는 지시였다. 단순한 이름표가 아니었다.

“아니네. 이건 그냥 이름이 아니야. 태그에 기능을 더해주고 있잖아.”

“그럼 우리 아주 간단한 것부터 한번 확인해볼까?” 루나가 제안했다. 루나는 솔라의 코드 편집기에서 새 파일을 열었다. 텅 빈 하얀 화면이었다.

“여기에 상자 하나만 만들어봐. div 태그로.”

솔라는 익숙하게 <div></div>를 입력했다. 물론 화면에는 아무 변화도 없었다. 뼈대만 있을 뿐, 아무런 정보도 없으니 당연했다.

“좋아. 이제 그 상자가 눈에 보이게 만들어보자. style이라는 속성을 써서.”

솔라는 루나의 말에 따라 div 태그 안에 style 속성을 추가했다.

<div style="width: 100px; height: 100px; background-color: skyblue;"></div>

코드를 저장하자마자, 텅 비어있던 미리보기 화면에 하늘색 정사각형이 나타났다. 솔라는 자기도 모르게 ‘오’ 하는 작은 감탄사를 내뱉었다.

div 태그는 그대로인데… style이라는 속성을 추가하니까 갑자기 상자가 생겼네.”

“정확히는, ‘상자’라는 형태를 정의하는 태그에 ‘어떻게 보여줄지’에 대한 추가 정보를 준 거지.” 루나가 조용히 덧붙였다.

솔라는 고개를 끄덕였다. 머릿속에 흩어져 있던 조각들이 맞춰지는 느낌이었다. href, src, 그리고 방금 써본 style까지. 이 모든 속성들은 단순한 이름표가 아니었다. 각자의 역할에 맞는 ‘추가 정보’를 태그에 부여해서, 태그가 원래는 할 수 없었던 구체적인 일을 하도록 만들고 있었다.

솔라는 다시 아까 자신이 고민하던 코드로 돌아갔다. classid가 적힌 줄을 뚫어져라 쳐다봤다.

<p class="post-content" id="post-123">...</p>

이제 이 코드 줄이 완전히 다르게 보였다. post-contentpost-123은 더 이상 그냥 이름표가 아니었다. 태그에 무언가 정보를 더하고 있는 것이 분명했다.

“알겠다! 그럼 classid도 이름표가 아니었어. hrefstyle처럼 이 태그에 어떤 ‘정보’를 추가하는 거였구나. ‘이 문단은 게시글 내용 그룹에 속해’라는 정보랑, ‘이건 123번 게시글이야’라는 고유한 정보를 미리 붙여두는 거였어.”

솔라는 마치 중요한 단서를 발견한 탐정처럼 눈을 빛냈다. 이름표는 이름표인데, 목적을 가진 이름표. 그냥 부르기 위한 이름이 아니라, 나중에 무언가를 하기 위해 미리 새겨두는 정보였던 것이다. HTML 태그에 붙는 모든 것은, 그저 이름이 아니라 역할과 의미를 가진다는 사실을 깨달았다.

“그래. 둘 다 태그에 추가 정보를 준다는 건 이제 확실히 알겠어.” 솔라는 확신에 찬 목소리로 말했다. 하지만 이내 새로운 궁금증이 고개를 들었다.

“그런데 언니, 그럼 왜 굳이 classid 두 개로 나눈 거지? 어차피 정보를 주는 거면 하나로 통일해도 되잖아. 얘네 둘은 서로 다른 종류의 정보를 주는 거야?”

2장: class와 id의 고유 규칙: ‘여러 개’ vs ‘하나’

솔라는 자신의 게시판 코드 위에 새로운 실험용 파일을 하나 더 열었다. 이전의 궁금증, 즉 classid가 둘 다 정보를 추가하는 것이라면 왜 굳이 두 개가 필요한지에 대한 답을 직접 찾아보기 위해서였다. 화면에는 똑같이 생긴 <div> 태그 세 개가 나란히 놓여 있었다. 솔라는 잠시 고민하다가, 세 개의 태그에 모두 똑같은 id를 부여했다.

id="important-notice"

솔라의 생각은 간단했다. 만약 idclass가 그저 다른 이름일 뿐이라면, 이렇게 해도 아무 문제가 없을 것이고, 그러면 둘을 구분할 필요가 없다는 자신의 생각이 증명되는 셈이었다. 하지만 코드를 작성하는 손끝에서부터 미묘한 위화감이 느껴졌다. 브라우저 미리보기 화면에는 아무런 오류 표시도, 변화도 없었다. 겉보기엔 멀쩡했다.

“이상하네… 아무 일도 안 일어나잖아. 그럼 정말 똑같은 건가?”

솔라는 고개를 갸웃거리며 중얼거렸다. 어쩌면 id를 여러 개 써도 괜찮은데, 그냥 사람들이 쓰지 말라고 약속만 한 걸지도 모른다고 생각했다. 그때, 솔라의 혼잣말을 들은 루나가 다가와 화면을 들여다봤다. 루나는 솔라가 작성한 세 줄의 코드를 잠시 보더니, 아무 말 없이 키보드를 가져가 새 파일을 열었다. 그리고는 HTML이 아닌, 스타일을 정의하는 <style> 태그를 먼저 작성하기 시작했다.

#main-title {
  background-color: lightgoldenrodyellow;
}

.post-item {
  background-color: aliceblue;
}

솔라는 루나가 작성한 코드를 유심히 지켜봤다. 하나는 이름 앞에 #(해시) 기호가, 다른 하나는 .(점) 기호가 붙어 있었다.

“자, 이제 이 두 가지 이름표를 사용할 상자들을 만들어 보자.” 루나가 말했다. “main-title이라는 이름의 상자 하나, 그리고 post-item이라는 이름의 상자 세 개.”

솔라는 루나의 의도를 파악하고 직접 코드를 작성하기 시작했다. 먼저 #main-title 스타일을 적용하기 위해 id 속성을 사용했다.

<div id="main-title">게시판 제목</div>

코드를 입력하자마자 미리보기 화면에 옅은 노란색 배경을 가진 상자가 나타났다. ‘게시판 제목’이라는 글자가 선명하게 보였다.

“좋아. 그럼 이제 post-item 상자 세 개를 만들어봐.”

솔라는 이번엔 . 기호로 정의된 스타일을 떠올리며 class 속성을 사용해 세 개의 상자를 만들었다.

<div class="post-item">1번 게시글</div>
<div class="post-item">2번 게시글</div>
<div class="post-item">3번 게시글</div>

결과는 즉각적이었다. 화면에 하늘색 배경을 가진 상자 세 개가 나란히 나타났다. 솔라는 잠시 두 결과를 번갈아 쳐다봤다. 옅은 노란색 상자 하나와 하늘색 상자 세 개.

“아…!”

짧은 탄성과 함께 솔라의 머릿속에서 무언가 번쩍했다. #으로 시작한 이름은 오직 하나의 요소에만 적용되었고, .으로 시작한 이름은 여러 개의 요소에 한꺼번에 적용되었다. 자신이 아까 id를 세 번이나 썼을 때 아무 일도 일어나지 않았던 것은, 브라우저가 그저 첫 번째 id만 ‘진짜’라고 인정하고 나머지는 암묵적으로 무시했기 때문일지도 모른다는 생각이 들었다.

“잠깐만. 그럼 class는 ‘같은 종류’라는 걸 표시하는 그룹 이름 같은 거고, id는 이 문서에서 딱 하나만 있어야 하는 ‘고유한 이름’인 거네?”

솔라는 마치 운동 경기에서 선수들의 유니폼과 등번호를 구분하듯 말했다.

class는 ‘우리 팀 유니폼’ 같은 거구나. 여러 명이 같은 유니폼을 입을 수 있으니까. 하지만 id는 선수 각자의 등번호 같은 거야. 같은 팀에 똑같은 등번호를 가진 선수는 두 명 있을 수 없으니까.”

루나는 솔라의 비유에 조용히 고개를 끄덕였다.

솔라는 자신이 방금 깨달은 규칙을 확인해보고 싶었다. main-title이라는 id를 가진 상자를 하나 더 만들어 보았다.

<div id="main-title">두 번째 제목?</div>

그러자 두 번째 상자에는 노란색 배경이 적용되지 않았다. 브라우저는 main-title이라는 id를 가진 첫 번째 상자만을 인정하고 있었다. 반면, post-item이라는 class를 가진 상자를 하나 더 만들자, 그 상자는 어김없이 다른 post-item 상자들과 똑같은 하늘색 배경을 갖게 되었다.

결과는 명확했다. class는 여러 개에 적용할 수 있는 이름표, id는 단 하나에만 부여할 수 있는 이름표였다. 둘은 이름표라는 점은 같았지만, ‘하나’와 ‘여럿’을 다루는 규칙이 근본적으로 달랐다. 이름의 생김새가 아니라, 그 이름이 허용하는 개수가 달랐던 것이다.

“이제 알겠다. class는 반복 사용이 가능하고, id는 유일해야 한다는 규칙이 있었구나. 그냥 이름이 두 종류인 게 아니었어.”

솔라는 방금 전까지의 혼란이 말끔히 정리되는 것을 느꼈다. 하지만 그와 동시에 새로운 의문이 꼬리를 물었다. 규칙은 알겠다. 하지만 왜 이런 규칙이 필요한 걸까?

“언니, 근데 왜 굳이 ‘유일한 이름’이 필요해? 그냥 class="main-title" 이라고 하고 하나만 쓰면 되잖아. class로도 충분히 하나만 있는 요소를 가리킬 수 있는데, 왜 꼭 id라는 특별한 규칙까지 만들어서 구별하는 거지? 이 ‘유일함’이 꼭 필요한 이유가 있는 거야?”

3장: CSS/JS의 선택: class와 id의 목적 있는 활용

솔라의 질문이 남긴 침묵 속에서, 루나는 말없이 솔라의 게시판 코드 파일을 복사해 새 파일을 만들었다. 그리고는 방금 전 실험에 사용했던 스타일과 상자들을 모두 지워버리고, 게시판의 뼈대가 될 법한 간단한 구조만 남겼다.

<div>게시판 제목</div>

<div>
  <div>1번 게시글</div>
  <div>2번 게시글</div>
  <div>3번 게시글</div>
</div>

<button>글쓰기</button>

화면에는 아무런 스타일도 없는, 밋밋한 글자들만 덩그러니 놓여 있었다. 루나는 솔라에게 키보드를 넘겨주며 말했다.

“여기에 이름표를 붙여보자. 솔라 네가 생각하는 대로. class만 써서.”

솔라는 잠시 망설였다. ‘유일한 이름’이 왜 필요한지 여전히 의문이었지만, 언니의 제안은 자신의 생각을 시험해볼 기회였다. 어차피 class로도 하나만 있는 요소를 가리킬 수 있으니, 굳이 id를 쓸 필요가 없다는 것을 보여주면 될 터였다. 솔라는 자신 있게 코드를 수정하기 시작했다.

<div class="title">게시판 제목</div>

<div class="post-list">
  <div class="post-item">1번 게시글</div>
  <div class="post-item">2번 게시글</div>
  <div class="post-item">3번 게시글</div>
</div>

<button class="write-button">글쓰기</button>

“됐어. 제목은 title, 목록은 post-list, 각 항목은 post-item, 버튼은 write-button. 전부 class로 했어. 어차피 title이나 write-button은 하나씩밖에 없으니까, 이렇게 해도 문제없잖아?”

솔라의 목소리에는 자신의 논리에 대한 확신이 묻어 있었다. 루나는 솔라가 작성한 코드를 잠시 들여다보더니, 고개를 끄덕였다.

“좋아. 그럼 이제 이 이름표들을 사용해서 두 가지 일을 해볼 거야.”

루나는 먼저 스타일 시트를 열고 코드를 추가했다.

“첫 번째, 모든 게시글 항목의 배경색을 연한 회색으로 만들어줘.”

이건 쉬웠다. 솔라는 방금 전 실험을 떠올리며 .post-item 클래스를 선택해 배경색을 지정했다.

.post-item {
  background-color: whitesmoke;
  padding: 10px;
  margin-bottom: 5px;
}

결과는 즉각적이었다. 3개의 게시글 항목에 모두 옅은 회색 배경과 여백이 생기며 보기 좋게 정돈되었다. ‘역시, class는 여러 개를 한 번에 꾸밀 때 정말 편하단 말이야.’ 솔라는 만족스러운 미소를 지었다.

“이제 두 번째.” 루나가 말했다. “저 ‘글쓰기’ 버튼을 누르면, ‘새 글을 작성합니다’라는 알림창이 뜨게 만들어보자. 이건 JavaScript의 도움이 필요해.”

루나는 <script> 태그를 추가하고, 버튼을 선택해서 클릭 이벤트를 연결하는 코드를 보여주었다.

const writeBtn = document.getElementsByClassName('write-button')[0];
writeBtn.addEventListener('click', function() {
  alert('새 글을 작성합니다.');
});

코드는 정상적으로 작동했다. 글쓰기 버튼을 누르자 약속된 알림창이 떴다. 솔라는 의기양양한 표정으로 루나를 보았다. “봐, 언니. class로도 충분히 되잖아. id는 필요 없다니까.”

“정말 그럴까?” 루나는 솔라가 작성한 JavaScript 코드의 한 부분을 가리켰다. getElementsByClassName('write-button')[0].

“여기를 자세히 봐. getElementsByClassName은 ‘write-button’이라는 클래스를 가진 요소들을 ‘전부’ 찾아오라는 명령이야. 그래서 결과가 배열(목록) 형태로 나오지. 지금은 버튼이 하나뿐이니까, 그 목록의 첫 번째([0]) 요소를 가져와서 쓰는 거고.”

솔라는 그제야 코드의 의미를 제대로 파악했다. 컴퓨터는 ‘write-button’ 클래스를 가진 요소가 몇 개인지 모르니, 일단 전부 찾아서 목록으로 만든 다음, 개발자가 그중 하나를 다시 지정해줘야 했던 것이다.

“만약 다른 곳에 write-button이라는 클래스를 가진 요소가 또 있다면?” 루나가 물었다. “그땐 이 코드가 우리가 원하는 버튼을 정확히 가리킨다고 보장할 수 있을까?”

솔라의 표정이 굳어졌다. 그럴 수 없었다. 만약 목록의 순서가 바뀌거나 다른 요소가 추가되면, 코드는 엉뚱한 대상을 클릭 이벤트의 주인으로 삼을 수도 있었다. class는 특정 대상을 ‘콕 집어서’ 부르기엔 불안정한 방법이었다.

“그럼… id를 쓰면 뭐가 다른데?”

루나는 말없이 솔라가 썼던 HTML과 JavaScript 코드를 수정했다.

<button id="write-btn">글쓰기</button>
const writeBtn = document.getElementById('write-btn'); // [0]이 사라졌다!
writeBtn.addEventListener('click', function() {
  alert('새 글을 작성합니다.');
});

차이는 명확했다. document.getElementById('write-btn'). ‘write-btn이라는 id를 가진 유일한 요소 하나만 찾아와.’ 명령어 자체가 달랐다. 불필요한 목록도, [0]과 같은 순서 지정도 없었다. 마치 여러 사람에게 ‘거기 안경 쓴 분!’하고 부르는 대신, ‘솔라야!’ 하고 이름을 직접 부르는 것과 같았다. id는 문서 전체에서 유일하다는 규칙이 있었기에 가능한, 가장 확실하고 빠른 호출 방식이었다.

“아…!”

솔라의 입에서 작은 탄성이 터져 나왔다. classid의 차이, 그리고 id의 ‘유일성’ 규칙이 왜 필요한지에 대한 마지막 퍼즐 조각이 맞춰지는 순간이었다.

class는 CSS로 스타일을 입히기 위해 ‘같은 종류의 그룹’을 묶는 거였어. 선수들한테 똑같은 유니폼을 입히는 것처럼. 하지만 id는 JavaScript 같은 걸로 어떤 특별한 기능을 실행시키기 위해, ‘바로 그 녀석’을 정확하게 찾아내기 위한 거였구나. 등번호처럼, 절대 중복되면 안 되는 고유한 식별 번호!”

규칙은 목적을 위해 존재했다. ‘여러 개’에 적용할 수 있는 class는 여러 요소를 한 번에 꾸미는 CSS에 최적화된 도구였고, ‘단 하나’만 허용되는 id는 특정 요소를 정밀하게 제어해야 하는 JavaScript에 가장 적합한 도구였던 것이다. 둘은 이름표의 종류가 다른 게 아니라, 애초에 사용하려는 도구와 목적이 달랐다.

깨달음을 얻은 솔라는 아까 만들었던 텅 빈 게시판 설계도로 다시 돌아갔다. 이제는 더 이상 망설이지 않았다. 그녀의 손가락은 각 태그의 ‘역할’과 ‘목적’을 생각하며 자신 있게 움직였다.

전체 게시판을 감싸는 단 하나의 컨테이너에는 <div id="board-wrapper">. 페이지의 유일한 제목에는 <h1 id="main-title">. 여러 게시글을 담을 목록에는 <ul class="post-list">. 반복되는 각 게시글 항목에는 <li class="post-item">. 그리고 사용자가 클릭해서 특별한 동작을 실행시킬 단 하나의 버튼에는 <button id="create-post-btn">.

솔라는 자신이 완성한 구조를 바라보았다. 이제 이 이름표들은 더 이상 혼란의 대상이 아니었다. 앞으로 어떤 스타일을 입히고 어떤 기능을 부여할지 명확하게 알려주는, 잘 짜인 청사진이었다. 이름표에 올바른 목적을 부여하는 순간, 코드는 비로소 질서를 찾았다.