현록

HTML id와 class 차이

HTML 요소를 구분할 때 idclass를 자주 사용한다.
둘 다 CSS와 JavaScript에서 요소를 찾는 데 쓸 수 있지만, 표현하는 관계가 다르다.

id는 문서 안에서 하나의 요소를 식별한다.
class는 같은 특징이나 역할을 공유하는 여러 요소를 묶는다.

한 요소에 둘을 함께 사용할 수도 있다.
중요한 것은 선택자 문법보다 그 이름이 단일 식별자인지 재사용 가능한 그룹인지다.

id의 역할

id는 HTML 문서 전체에서 특정 요소 하나를 가리키는 식별자다.
같은 문서 안의 id 값은 서로 달라야 한다.

<main id="content">
  <h1>게시글 목록</h1>
</main>

위 문서에서 content는 이 main 요소를 식별한다.
다른 요소에 같은 id="content"를 다시 사용하지 않는다.

id 값에는 ASCII 공백을 넣을 수 없다.
한 요소는 하나의 id 값만 가진다.

<!-- 잘못된 값: 공백이 들어가 있다. -->
<main id="main content"></main>

<!-- 하나의 식별자로 작성한다. -->
<main id="main-content"></main>

브라우저가 공백이 있는 값을 그대로 처리할 수는 있지만 CSS 선택자와 JavaScript 코드에서 다루기 불편해진다.
처음부터 CSS identifier로 쓰기 쉬운 이름을 선택하는 편이 안전하다.

class의 역할

class는 공통 특징을 가진 요소를 그룹으로 묶는다.
같은 class를 여러 요소에 반복해서 사용할 수 있다.

<article class="card">첫 번째 글</article>
<article class="card">두 번째 글</article>
<article class="card">세 번째 글</article>

세 요소는 모두 card라는 같은 역할과 스타일을 공유한다.

한 요소에 여러 class를 공백으로 구분해 넣을 수도 있다.

<button class="button button-primary is-loading" type="button">
  저장 중
</button>

이 버튼에는 button, button-primary, is-loading 세 class가 있다.
기본 버튼 스타일, 강조 변형, 현재 상태를 각각 분리해서 표현할 수 있다.

id와 class의 핵심 차이

두 속성은 다음 기준으로 구분할 수 있다.

  • id는 문서 안에서 유일한 요소를 식별한다.
  • class는 여러 요소가 공유할 수 있는 그룹 이름이다.
  • 한 요소는 하나의 id 값을 가진다.
  • 한 요소는 여러 class 값을 동시에 가질 수 있다.
  • id는 fragment 이동과 다른 HTML 속성의 참조 대상이 될 수 있다.
  • class는 반복되는 스타일과 상태를 묶는 데 자연스럽다.

페이지의 유일한 본문 영역을 가리킨다면 id="content"가 어울린다.
여러 게시글 카드를 같은 모양으로 꾸민다면 class="card"가 어울린다.

CSS에서 id 선택하기

CSS에서는 # 기호로 id를 선택한다.

#content {
  max-width: 720px;
  margin-inline: auto;
}

이 규칙은 id="content"인 요소를 선택한다.

id selector는 class selector보다 specificity가 높다.
나중에 class로 스타일을 덮어쓰려고 할 때 예상보다 강하게 남을 수 있다.

#save-button {
  background: gray;
}

.button-primary {
  background: blue;
}

두 선택자가 같은 요소에 적용되면 id selector의 우선순위가 더 높다.
단순한 작성 순서만으로 .button-primary가 이기지 못할 수 있다.

그래서 재사용하는 컴포넌트 스타일은 보통 class 중심으로 작성하는 편이 관리하기 쉽다.
id는 문서 식별과 참조에 사용하고, 시각적 스타일은 class에 맡기는 방식이 예측 가능하다.

CSS에서 class 선택하기

CSS에서는 . 기호로 class를 선택한다.

.card {
  padding: 20px;
  border: 1px solid #d8dee4;
}

class="card"를 가진 모든 요소에 같은 스타일이 적용된다.

여러 class를 조합해 특정 상태만 선택할 수도 있다.

.button {
  padding: 10px 16px;
}

.button.button-primary {
  background: #1769e0;
  color: #fff;
}

.button.is-loading {
  cursor: wait;
  opacity: 0.6;
}

기본 형태와 변형, 상태를 작은 class로 나누면 같은 HTML 요소에 필요한 조합을 만들 수 있다.

요소의 block과 inline 동작이 헷갈린다면 CSS display block inline inline-block 차이를 함께 보면 좋다.

JavaScript에서 id 찾기

JavaScript에서는 document.getElementById()로 id를 가진 요소를 찾을 수 있다.

const dialog = document.getElementById('confirm-dialog')

dialog?.showModal()

CSS selector 문법을 사용하는 querySelector()도 가능하다.

const dialog = document.querySelector('#confirm-dialog')

getElementById()에는 #을 붙이지 않는다.
querySelector()에는 CSS id selector인 #을 붙인다.

id 값이 window 객체의 property처럼 노출되는 브라우저 동작에 기대는 것은 피하는 편이 좋다.

// 피하는 편이 좋은 방식
console.log(window.content)

// 의도가 분명한 방식
const content = document.getElementById('content')

브라우저에 같은 이름의 API가 추가되거나 전역 변수와 충돌하면 예상하지 못한 결과가 생길 수 있다.

JavaScript에서 class 찾기

같은 class를 가진 여러 요소는 querySelectorAll()로 찾을 수 있다.

const cards = document.querySelectorAll('.card')

for (const card of cards) {
  card.classList.add('is-visible')
}

첫 번째 요소 하나만 필요하면 querySelector()를 사용한다.

const firstCard = document.querySelector('.card')

class를 추가하거나 제거할 때는 문자열 전체를 직접 바꾸기보다 classList를 사용하는 편이 안전하다.

const menu = document.querySelector('.menu')

menu?.classList.toggle('is-open')

기존 class를 보존하면서 상태 class만 변경할 수 있다.

fragment 링크에서 id 사용하기

URL의 # 뒤에 붙는 fragment identifier는 문서 안의 id를 가리킬 수 있다.

<a href="#comments">댓글로 이동</a>

<section id="comments">
  <h2>댓글</h2>
</section>

링크를 선택하면 브라우저는 id="comments"인 요소로 이동한다.
목차, 본문 건너뛰기 링크, 긴 문서의 특정 섹션 공유에 유용하다.

이때 중복 id가 있으면 어느 요소가 목적지인지 문서 구조가 모호해진다.
id를 유일하게 유지해야 하는 이유가 단순한 코딩 스타일에 그치지 않는다.

이동과 동작에 어떤 HTML 요소를 선택할지는 HTML button과 a 태그 차이에서 정리했다.

다른 HTML 속성에서 id 참조하기

id는 다른 요소가 특정 요소를 참조할 때도 사용된다.

<label for="email">이메일</label>
<input id="email" name="email" type="email" />

labelfor 값은 연결할 form control의 id와 같아야 한다.
사용자가 label을 눌렀을 때 input에 초점이 이동하고 보조 기술도 관계를 이해할 수 있다.

ARIA 관계에서도 id를 참조할 수 있다.

<button aria-describedby="delete-help" type="button">삭제</button>
<p id="delete-help">삭제한 항목은 복구할 수 없습니다.</p>

aria-describedby는 설명 요소의 id를 값으로 사용한다.
이런 참조 대상은 같은 문서에서 명확하게 하나여야 한다.

중복 id 문제

반복 목록을 만들면서 같은 id를 복사하면 중복 id가 생기기 쉽다.

<!-- 피해야 하는 구조 -->
<article id="card">첫 번째 글</article>
<article id="card">두 번째 글</article>

스타일은 겉으로 적용되는 것처럼 보일 수 있다.
하지만 fragment 이동, label 연결, JavaScript 탐색, 접근성 tree에서는 관계가 불명확해진다.

반복되는 요소라면 class를 사용한다.

<article class="card">첫 번째 글</article>
<article class="card">두 번째 글</article>

각 항목에 정말 고유한 식별자가 필요하다면 실제 데이터의 고유 값을 포함한다.

<article id="post-101" class="card">첫 번째 글</article>
<article id="post-102" class="card">두 번째 글</article>

class 이름 작성 기준

class 이름은 현재 보이는 색이나 위치보다 요소의 역할을 나타내는 편이 오래 간다.

<!-- 표현에 묶인 이름 -->
<p class="red-text">오류가 발생했습니다.</p>

<!-- 역할을 나타내는 이름 -->
<p class="error-message">오류가 발생했습니다.</p>

디자인이 빨간색에서 다른 표현으로 바뀌어도 error-message라는 이름은 여전히 의미가 있다.

상태는 is-open, is-selected, is-loading처럼 구분하면 읽기 쉽다.

<nav class="menu is-open"></nav>

프로젝트에 이미 BEM, CSS Modules, utility class 같은 규칙이 있다면 그 규칙을 우선한다.
중요한 것은 같은 의미를 같은 방식으로 표현하는 일관성이다.

id를 스타일에 사용하지 않는 기준

id selector가 금지된 것은 아니다.
문서에서 정말 하나뿐인 영역을 빠르게 지정할 수 있다.

다만 컴포넌트 스타일에 id를 많이 사용하면 재사용이 어려워지고 specificity가 높아진다.
같은 컴포넌트를 두 번 렌더링하는 순간 id의 유일성도 깨질 수 있다.

.site-header {
  min-height: 64px;
}
<header id="site-header" class="site-header"></header>

위처럼 id는 fragment나 script 참조에 사용하고 class는 스타일에 사용하는 식으로 역할을 분리할 수 있다.

선택 기준

문서 안에서 단 하나의 요소를 식별하거나 다른 HTML 속성에서 참조해야 한다면 id를 사용한다.

<main id="main-content"></main>

여러 요소가 같은 스타일, 역할, 상태를 공유한다면 class를 사용한다.

<article class="post-card"></article>
<article class="post-card"></article>

한 요소가 고유한 식별자와 재사용 스타일을 모두 가져야 한다면 둘을 함께 사용한다.

<section id="profile" class="panel panel-highlighted"></section>

정리

id는 HTML 문서 전체에서 하나의 요소를 식별한다.
fragment 링크, label 연결, ARIA 참조, JavaScript의 단일 요소 탐색에 사용된다.

class는 여러 요소가 공유할 수 있는 그룹 이름이다.
한 요소에 여러 class를 조합할 수 있고 반복되는 스타일과 상태 표현에 적합하다.

CSS에서는 #id.class로 선택하지만, 선택자 문법만 보고 둘을 고르면 부족하다.
하나를 가리키는 식별자면 id, 반복 가능한 역할이면 class라는 의미부터 정하는 편이 좋다.

참고 자료

관련 포스트
HTTP 상태 코드 정리 thumbnail
HTTP 상태 코드 정리
HTTP 상태 코드의 2xx, 3xx, 4xx, 5xx 의미와 200, 201, 204, 301, 304, 400, 401, 403, 404, 500, 502, 503 차이를 정리합니다.
HTML button과 a 태그 차이 thumbnail
HTML button과 a 태그 차이
HTML button과 a 태그의 차이를 이동과 동작의 의미, href, type 속성, 폼 제출, 키보드 접근성, 잘못된 사용 패턴으로 정리합니다.
HTTP GET과 POST 차이 thumbnail
HTTP GET과 POST 차이
HTTP GET과 POST의 차이를 초보자 기준으로 정리합니다. 조회와 변경, query string과 request body, 캐시, safe, idempotent, form과 fetch 사용 기준을 함께 봅니다.
npm outdated와 npm update 차이 thumbnail
npm outdated와 npm update 차이
npm outdated와 npm update의 차이를 초보자 기준으로 정리합니다. Current, Wanted, Latest의 의미와 semver 범위 안에서 업데이트되는 방식, package-lock.json 변화까지 함께 봅니다.
npm install -g와 npx 차이 thumbnail
npm install -g와 npx 차이
npm install -g와 npx의 차이를 초보자 기준으로 정리합니다. 전역 설치, 로컬 설치, 일회성 실행, 프로젝트 scripts에 넣는 기준까지 함께 봅니다.
package.json에서 ^와 ~ 차이 thumbnail
package.json에서 ^와 ~ 차이
package.json 의존성 버전 앞에 붙는 ^와 ~의 차이를 초보자 기준으로 정리합니다. semantic versioning, 버전 범위, 0.x 예외, package-lock.json과의 관계까지 함께 봅니다.
npx와 npm exec 차이 thumbnail
npx와 npm exec 차이
npx와 npm exec가 어떤 명령어인지 초보자 기준으로 정리합니다. 로컬 패키지 실행, 원격 패키지 임시 실행, --package 옵션, -- 인자 전달 차이까지 함께 봅니다.
npm scripts와 npm run 정리 thumbnail
npm scripts와 npm run 정리
npm scripts란 무엇인지, package.json scripts와 npm run의 관계를 초보자 기준으로 정리합니다. npm run dev, npm start, npm test, node_modules/.bin, -- 인자 전달 방식까지 함께 봅니다.
dependencies와 devDependencies 차이 thumbnail
dependencies와 devDependencies 차이
package.json의 dependencies와 devDependencies 차이를 초보자 기준으로 정리합니다. npm install과 npm install -D, 배포 환경 설치, package-lock.json과의 관계까지 함께 봅니다.
npm ci와 npm install 차이 thumbnail
npm ci와 npm install 차이
npm ci와 npm install의 차이를 초보자 기준으로 정리합니다. clean install의 의미, package-lock.json 조건, CI에서 npm ci를 쓰는 이유, .npmrc 플래그 주의점까지 함께 봅니다.
.npmrc 파일이란? thumbnail
.npmrc 파일이란?
storybook을 사용해보려고 하다 마주한 이슈의 해결법을 알아보다가 등장한 .npmrc라는 파일에 대해 공부해보았다. .npmrc 파일이란? .npmrc 파일은 npm에 대한 config 파일이다. (npm에 대한 rc 파일) 프로젝트별 registry, install 옵션, 인증 토큰처럼 npm CLI가 읽는 설정을 관리할 때 사용한다.
스토리북이란? thumbnail
스토리북이란?
Storybook은 UI 컴포넌트를 독립적으로 개발하고, 문서화하고, 테스트하기 위한 프론트엔드 워크샵입니다. Storybook 10.4 기준 설치 흐름과 stories, 문서화, 테스트 활용 방식을 정리합니다.
TTV와 TTI 차이 thumbnail
TTV와 TTI 차이
TTV와 TTI의 차이를 초보자 기준으로 정리합니다. 사용자가 화면을 보는 시점, 상호작용 가능한 시점, FCP, LCP, INP, Core Web Vitals와의 관계까지 함께 봅니다.
Maria DB 외부 접속 설정하기 thumbnail
Maria DB 외부 접속 설정하기
안녕하세요. 오늘은 Maria DB 초기 세팅 시, 외부에서 접속이 안될 때 매뉴얼을 작성해보겠습니다. dotenv 패키지를 통해서 환경변수로 관리한다면, 별도의 추가 작업을 할 일이 없으실 겁니다.
package-lock.json 파일의 역할 thumbnail
package-lock.json 파일의 역할
안녕하세요. 오늘은 node 환경의 개발자라면 한번쯤 궁금했을만한 package-lock.json의 역할에 대해 알아보겠습니다. 우리는 node 환경에서 개발을 할 때 다양한 패키지들을 설치하여 활용하곤 합니다. 우리가 설치하는 패키지 또한 다른 npm 패키지를 활용하여 만든 패키지들이고 이들 또한 설치를 하게 됩니다. 이렇게 직간접적으로 설치된 패키지들은 대부분 호환성을 "^1.1.5"와 같이 표현하여, 범위로 지정해두고 있습니다.
Linux 환경 배포 자동화 체험해보기 thumbnail
Linux 환경 배포 자동화 체험해보기
안녕하세요. 요즘 포트폴리오를 만들면서 서버 상에 자주 반영할 일이 생겼는데, 매번 명령어들을 타이핑하는 것이 비효율적이라 생각이 들어 배포 자동화를 생각해보게 되었습니다. 현재 레벨에서는 단순히 명령어들만 단축시켜도 효율적이라 생각이 들어 간단한 쉘 스크립트만 작성하였습니다. 정말 간단하니 여러분도 도전해보시길 바랍니다.
협업 필수품. Prettier thumbnail
협업 필수품. Prettier
안녕하세요. 오늘은 Prettier이라는 도구에 대해 알려드리고자 합니다. 개발자는 각자의 코딩스타일이 존재합니다. 그러다보니 같은 프로젝트에서도 작성하는 소스마다 스타일이 제각기 다르기 일쑤입니다. 그럴 때 도입하면 좋은 것이 Prettier입니다. 프로젝트 root 폴더에 .prettierrc 라는 파일을 생성한 뒤, 위 예시와 같이 원하는 옵션을 JSON 형식으로 작성해주면 됩니다.