BEM CSS 방법론 실전 가이드 - 예제로 배우는 네이밍 규칙

BEM CSS 방법론 실전 가이드 - 예제로 배우는 네이밍 규칙

“왜 내 CSS는 점점 스파게티 코드가 되어가는 걸까?”

.button 클래스를 수정했더니 전혀 다른 페이지의 버튼이 깨졌어요…”

이런 경험이 있다면, BEM(Block, Element, Modifier) 방법론이 답이 될 수 있습니다.

BEM은 CSS 클래스 이름을 체계적으로 작성하는 방법론으로, 2009년 러시아의 Yandex에서 개발되어 현재 전 세계적으로 사용되고 있습니다.

이번 포스트에서는 BEM의 세 가지 핵심 개념인 Block, Element, Modifier를 실제 예제와 함께 완벽하게 마스터해보겠습니다.

BEM 방법론 구조 다이어그램
BEM 방법론 구조 다이어그램


BEM이란 무엇인가?

BEMBlock, Element, Modifier의 약자로, CSS 클래스 이름을 짓는 규칙입니다.

/* BEM 구조 */
.block {
}
.block__element {
}
.block--modifier {
}
.block__element--modifier {
}css

왜 BEM을 사용해야 할까?

문제 상황BEM이 제공하는 해결책
클래스 이름 충돌네임스페이스로 완전히 분리
스타일 덮어쓰기독립적인 블록으로 격리
코드 이해 어려움명확한 관계 표현
재사용 불가능컴포넌트 단위 설계

1. Block: 독립적인 컴포넌트

Block이란?

Block은 그 자체로 의미를 가지는 독립적인 컴포넌트입니다. 페이지 어디에든 재사용할 수 있는 기능적 단위입니다.

BEM Block 예시
BEM Block 예시

Block 작명 규칙

/* ✅ 좋은 예: 명확한 의미 */
.header {
}
.menu {
}
.search-form {
}
.user-profile {
}

/* ❌ 나쁜 예: 모호하거나 일반적인 이름 */
.content {
} /* 너무 일반적 */
.big-block {
} /* 스타일 기반 이름 */
.block1 {
} /* 의미 없는 이름 */css

Block 실전 예제

카드 컴포넌트를 Block으로 만들기:

<!-- Block: card -->
<div class="card">
  <img src="product.jpg" alt="상품" />
  <h3>상품 이름</h3>
  <p>상품 설명</p>
  <button>구매하기</button>
</div>html
.card {
  border: 1px solid #ddd;
  padding: 20px;
  border-radius: 8px;
  background: white;
}css

Block의 특징

  1. 독립성: 다른 블록에 의존하지 않음
  2. 재사용성: 어디서든 사용 가능
  3. 이동 가능: 위치가 바뀌어도 동작
  4. 중첩 가능: 블록 안에 다른 블록 포함 가능
<!-- 블록 안에 블록 중첩 -->
<div class="page">
  <header class="header">
    <!-- header 블록 -->
    <form class="search-form">
      <!-- search-form 블록 -->
      <!-- ... -->
    </form>
  </header>
</div>html

2. Element: Block의 구성 요소

Element란?

ElementBlock의 일부분으로, Block 안에서만 의미를 가지는 구성 요소입니다. BlockElement__ (더블 언더스코어)로 연결합니다.

BEM Element 구조
BEM Element 구조

Element 작명 규칙

/* Block__Element 형식 */
.card__image {
}
.card__title {
}
.card__description {
}
.card__button {
}

.menu__item {
}
.menu__link {
}

.search-form__input {
}
.search-form__button {
}css

Element 실전 예제

카드 컴포넌트에 Element 적용:

<div class="card">
  <img class="card__image" src="product.jpg" alt="상품" />
  <h3 class="card__title">상품 이름</h3>
  <p class="card__description">상품 설명</p>
  <button class="card__button">구매하기</button>
</div>html
.card {
  border: 1px solid #ddd;
  padding: 20px;
  border-radius: 8px;
}

.card__image {
  width: 100%;
  height: 200px;
  object-fit: cover;
}

.card__title {
  font-size: 18px;
  font-weight: bold;
  margin: 10px 0;
}

.card__description {
  color: #666;
  line-height: 1.5;
}

.card__button {
  background: #007bff;
  color: white;
  padding: 10px 20px;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}css

Element 주의사항

❌ 잘못된 사용: Element의 Element

/* 절대 이렇게 사용하지 마세요! */
.card__header__title {
} /* ❌ Element의 Element */

/* 대신 이렇게 사용하세요 */
.card__header {
}
.card__title {
} /* ✅ 모두 card의 직접적인 Element */css

✅ 올바른 HTML 구조와 BEM

<div class="card">
  <div class="card__header">
    <h3 class="card__title">제목</h3>
    <!-- card__header__title이 아님! -->
    <span class="card__badge">NEW</span>
  </div>
  <div class="card__body">
    <p class="card__text">내용</p>
    <!-- card__body__text가 아님! -->
  </div>
</div>html

3. Modifier: 상태와 테마 변형

Modifier란?

ModifierBlock이나 Element모양, 상태, 동작을 변경합니다. -- (더블 대시)로 연결합니다.

BEM Modifier 예시
BEM Modifier 예시

Modifier 작명 규칙

/* Block--Modifier */
.card--featured {
}
.button--large {
}
.menu--vertical {
}

/* Block__Element--Modifier */
.card__button--disabled {
}
.form__input--error {
}
.menu__item--active {
}css

Modifier 실전 예제

버튼의 다양한 상태 표현

<!-- 기본 버튼 -->
<button class="button">기본 버튼</button>

<!-- 큰 버튼 -->
<button class="button button--large">큰 버튼</button>

<!-- 주요 버튼 -->
<button class="button button--primary">주요 버튼</button>

<!-- 비활성 버튼 -->
<button class="button button--disabled">비활성 버튼</button>

<!-- 복합: 크고 주요한 버튼 -->
<button class="button button--large button--primary">크고 중요한 버튼</button>html
/* 기본 버튼 스타일 */
.button {
  padding: 8px 16px;
  border: 1px solid #ddd;
  background: white;
  border-radius: 4px;
  font-size: 14px;
  cursor: pointer;
}

/* Modifier: 크기 변형 */
.button--large {
  padding: 12px 24px;
  font-size: 16px;
}

/* Modifier: 스타일 변형 */
.button--primary {
  background: #007bff;
  color: white;
  border-color: #007bff;
}

/* Modifier: 상태 변형 */
.button--disabled {
  opacity: 0.5;
  cursor: not-allowed;
}css

Modifier 활용 패턴

1. Boolean Modifier (상태)

/* 활성/비활성 같은 on/off 상태 */
.menu__item--active {
}
.form__input--disabled {
}
.card--hidden {
}css

2. Key-Value Modifier (속성-값)

/* 특정 값을 가지는 수정자 */
.button--size-large {
}
.button--color-red {
}
.card--theme-dark {
}css

4. 실전: 완전한 카드 컴포넌트 만들기

이제 Block, Element, Modifier를 모두 활용해 실제 사용 가능한 카드 컴포넌트를 만들어보겠습니다.

완전한 BEM 카드 컴포넌트
완전한 BEM 카드 컴포넌트

HTML 구조

기본 카드 구조

<article class="product-card">
  <!-- 이미지 영역 -->
  <div class="product-card__image-wrapper">
    <img class="product-card__image" src="laptop.jpg" alt="노트북" />
    <span class="product-card__badge">SALE</span>
  </div>

  <!-- 콘텐츠 영역 -->
  <div class="product-card__content">
    <h3 class="product-card__title">최신형 노트북</h3>
    <p class="product-card__price">
      <span class="product-card__price-original">1,500,000원</span>
      <span class="product-card__price-sale">1,200,000원</span>
    </p>
    <p class="product-card__description">고성능 프로세서와 긴 배터리 수명</p>
  </div>

  <!-- 액션 버튼 영역 -->
  <div class="product-card__actions">
    <button class="product-card__button product-card__button--primary">
      구매하기
    </button>
    <button class="product-card__button product-card__button--secondary">
      장바구니
    </button>
  </div>
</article>html

Modifier 적용 예시

<!-- 추천 상품 (featured modifier) -->
<article class="product-card product-card--featured">
  <div class="product-card__image-wrapper">
    <img class="product-card__image" src="phone.jpg" alt="스마트폰" />
    <span class="product-card__badge product-card__badge--hot">HOT</span>
  </div>
  <!-- 동일한 구조 유지 -->
</article>

<!-- 품절 상품 (sold-out modifier) -->
<article class="product-card product-card--sold-out">
  <div class="product-card__image-wrapper">
    <img class="product-card__image" src="tablet.jpg" alt="태블릿" />
    <span class="product-card__badge product-card__badge--sold-out">
      SOLD OUT
    </span>
  </div>
  <div class="product-card__actions">
    <button class="product-card__button product-card__button--disabled">
      품절
    </button>
  </div>
</article>html

CSS 스타일

Block 스타일

/* 기본 카드 블록 */
.product-card {
  border: 1px solid #e0e0e0;
  border-radius: 8px;
  overflow: hidden;
  background: white;
  transition: transform 0.2s;
}

.product-card:hover {
  transform: translateY(-4px);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}css

Element 스타일

/* 이미지 영역 */
.product-card__image-wrapper {
  position: relative;
  height: 200px;
  overflow: hidden;
}

.product-card__image {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.product-card__badge {
  position: absolute;
  top: 10px;
  right: 10px;
  padding: 4px 8px;
  background: #ff5252;
  color: white;
  font-size: 12px;
  font-weight: bold;
  border-radius: 4px;
}

/* 콘텐츠 영역 */
.product-card__content {
  padding: 16px;
}

.product-card__title {
  font-size: 18px;
  font-weight: bold;
  margin: 0 0 8px 0;
}

.product-card__price-original {
  color: #999;
  text-decoration: line-through;
  margin-right: 8px;
}

.product-card__price-sale {
  color: #ff5252;
  font-size: 20px;
  font-weight: bold;
}

/* 버튼 영역 */
.product-card__actions {
  padding: 16px;
  border-top: 1px solid #e0e0e0;
  display: flex;
  gap: 8px;
}

.product-card__button {
  flex: 1;
  padding: 10px;
  border: none;
  border-radius: 4px;
  font-size: 14px;
  font-weight: bold;
  cursor: pointer;
}css

Modifier 스타일

/* Block Modifiers */
.product-card--featured {
  border: 2px solid #ffd700;
  background: linear-gradient(135deg, #fffef5 0%, #fff 100%);
}

.product-card--sold-out {
  opacity: 0.6;
  pointer-events: none;
}

/* Element Modifiers */
.product-card__badge--hot {
  background: #ff9800;
}

.product-card__badge--sold-out {
  background: #757575;
}

.product-card__button--primary {
  background: #007bff;
  color: white;
}

.product-card__button--secondary {
  background: white;
  color: #007bff;
  border: 1px solid #007bff;
}

.product-card__button--disabled {
  background: #e0e0e0;
  color: #999;
  cursor: not-allowed;
}css

5. Modifier 재사용 패턴

기본 스타일 + Modifier 조합

BEM의 핵심은 기본 스타일을 유지하면서 Modifier로 변형하는 것입니다.

<!-- 기본 카드 버튼 -->
<button class="card__button">기본 버튼</button>

<!-- submit 버튼: 기본 + modifier -->
<button class="card__button card__button--submit">제출</button>

<!-- cancel 버튼: 기본 + modifier -->
<button class="card__button card__button--cancel">취소</button>html
/* 기본 버튼 스타일 (공통) */
.card__button {
  padding: 10px 20px;
  border-radius: 4px;
  font-size: 14px;
  font-weight: bold;
  cursor: pointer;
  transition: all 0.3s;
  border: none;
}

/* Modifier: submit 버튼만의 스타일 */
.card__button--submit {
  background: #28a745;
  color: white;
}

.card__button--submit:hover {
  background: #218838;
}

/* Modifier: cancel 버튼만의 스타일 */
.card__button--cancel {
  background: #dc3545;
  color: white;
}

.card__button--cancel:hover {
  background: #c82333;
}css

Modifier 재사용 패턴
Modifier 재사용 패턴

Modifier 조합하기

여러 Modifier를 동시에 적용할 수도 있습니다:

<!-- 크고 주요한 버튼 -->
<button class="button button--large button--primary">중요한 액션</button>

<!-- 작고 비활성화된 버튼 -->
<button class="button button--small button--disabled">사용 불가</button>html

6. BEM 사용 시 주의사항

자주 하는 실수와 해결책

1. Element의 Element 만들기

/* ❌ 잘못된 예 */
.card__header__title {
}

/* ✅ 올바른 예 */
.card__header {
}
.card__title {
}css

2. Modifier만 단독 사용

<!-- ❌ 잘못된 예 -->
<button class="--primary">버튼</button>

<!-- ✅ 올바른 예 -->
<button class="button button--primary">버튼</button>html

3. 의미 없는 이름 사용

/* ❌ 피해야 할 이름 */
.card__div {
}
.card__span {
}
.card__element1 {
}

/* ✅ 의미 있는 이름 */
.card__wrapper {
}
.card__label {
}
.card__content {
}css

BEM과 CSS 전처리기

SASS/SCSS를 사용하면 BEM을 더 효율적으로 작성할 수 있습니다:

.card {
  border: 1px solid #ddd;

  &__title {
    font-size: 18px;
  }

  &__button {
    padding: 10px;

    &--primary {
      background: blue;
    }

    &--secondary {
      background: gray;
    }
  }

  &--featured {
    border-color: gold;
  }
}scss

BEM 네이밍 컨벤션 변형

프로젝트에 따라 구분자를 다르게 사용하기도 합니다:

/* 클래식 BEM */
.block__element--modifier {
}

/* 카멜케이스 변형 */
.blockName__elementName--modifierName {
}

/* 싱글 대시 변형 */
.block__element-modifier {
}css

7. BEM 적용 전후 비교

Before: BEM 없이 작성한 CSS

/* 충돌 위험이 높은 일반적인 이름들 */
.header {
  background: blue;
}
.title {
  font-size: 24px;
}
.button {
  padding: 10px;
}

/* 점점 복잡해지는 선택자 */
.page .header .nav .menu .item .link {
  color: white;
}
.sidebar .widget .title {
  font-size: 16px;
} /* title 충돌! */

/* 어디에 쓰이는지 알 수 없는 클래스 */
.large {
  font-size: 20px;
}
.red {
  color: red;
}
.mt-10 {
  margin-top: 10px;
}css

After: BEM으로 작성한 CSS

/* 명확한 네임스페이스 */
.site-header {
  background: blue;
}
.site-header__title {
  font-size: 24px;
}
.site-header__button {
  padding: 10px;
}

/* 단순하고 명확한 선택자 */
.navigation__link {
  color: white;
}
.sidebar-widget__title {
  font-size: 16px;
} /* 충돌 없음! */

/* 용도가 명확한 Modifier */
.button--large {
  font-size: 20px;
}
.text--danger {
  color: red;
}
.card--spacing-medium {
  margin-top: 10px;
}css

8. BEM 도입 전후의 변화

BEM 도입 전의 고통

프로젝트가 커질수록 CSS 관리는 악몽이 되어갔습니다:

  • “이 클래스 지워도 되나?” - 어디서 쓰이는지 확신할 수 없어 방치
  • “왜 스타일이 안 먹지?” - 다른 곳의 더 구체적인 선택자에 덮어써짐
  • “이름이 겹치네…” - .title, .button 같은 이름이 수십 개
  • CSS 파일 크기만 계속 증가 - 지우기 무서워서 계속 추가만 함

BEM 도입 후의 변화

BEM을 적용하고 나서 달라진 점들:

  • 클래스명만 봐도 구조 파악 - .card__title은 card 컴포넌트의 제목
  • 안심하고 삭제 가능 - 영향 범위가 명확해서 리팩토링이 쉬워짐
  • 새 개발자도 바로 이해 - 일관된 규칙으로 누구나 코드 파악 가능
  • CSS 충돌 제로 - 더 이상 !important 남발하지 않음

코드 비교

/* BEM 전: 3개월 후 아무도 건드리지 못하는 코드 */
.container .box .title {
} /* 이게 어느 페이지의 title? */
.active {
} /* 뭐가 active? */
.btn-2 {
} /* btn-1은 어디에? */

/* BEM 후: 1년 후에도 명확한 코드 */
.product-card__title {
} /* 상품 카드의 제목 */
.navigation__item--active {
} /* 네비게이션의 활성 아이템 */
.button--size-large {
} /* 큰 사이즈 버튼 */css

정리

BEM은 CSS 클래스 이름을 체계적으로 작성하는 강력한 방법론입니다.

핵심 개념:

  • Block: 독립적인 컴포넌트 (.card)
  • Element: Block의 구성 요소 (.card__title)
  • Modifier: 상태나 테마 변형 (.card--featured)

BEM의 가치:

  1. 클래스 이름 충돌 완전 방지
  2. 코드만 봐도 구조와 관계 파악 가능
  3. 안전한 리팩토링과 유지보수
  4. 팀 협업 시 일관된 코드 스타일

명확한 것이 간결한 것보다 낫다”는 BEM의 철학을 기억하세요.

AI 시대, BEM이 더 중요하다고 생각하는 개인적인 이유

실무에서 AI 도구들과 함께 개발하면서, 예전에 학습했던 BEM 방법론을 다시 돌아보게 되었습니다.

처음 BEM을 배울 때는 솔직히 .card__button--primary 같은 긴 클래스명이 불필요하게 복잡해 보였습니다. “그냥 .btn으로 써도 되는데…” 라고 생각했었죠.

그런데 최근 ClaudeCursor 같은 AI 도구를 활용해 개발하면서 깨달은 점이 있습니다. 체계적인 규칙이 AI와의 협업을 얼마나 효율적으로 만드는지 말이죠.

/* AI에게 "card 컴포넌트의 버튼 스타일 수정해줘" 라고 요청할 때 */

/* BEM 없는 코드: AI가 어떤 버튼인지 모호함 */
.btn {
} /* 이게 어느 버튼? */
.button {
} /* 이것도 버튼? */

/* BEM 적용 코드: AI가 정확히 찾아서 수정 */
.card__button {
} /* 명확하게 card의 버튼 */
.modal__button {
} /* 명확하게 modal의 버튼 */css

실무에서 AI 도구를 사용하며 느낀 건, AI도 결국 명확한 컨텍스트가 있을 때 가장 정확한 결과를 낸다는 것입니다.

BEM으로 작성된 코드는 구조와 관계가 명확해서, AI가 제 의도를 훨씬 잘 이해하고 정확한 수정을 해줍니다.

이제 와서 생각해보니, 예전에 배웠던 BEM 같은 좋은 규칙 하나하나가 결국 AI를 더 잘 활용할 수 있는 기반이 되더라구요.

특히 이제 막 개발을 시작하는 분들이라면, 처음부터 이런 체계적인 방법론을 익히는 것이 AI 시대에 더 경쟁력 있는 개발자가 되는 지름길이 아닐까 싶습니다.

“명확한 코드는 인간뿐만 아니라 AI와의 소통도 원활하게 만든다” - 이것이 제가 실무에서 AI와 함께 일하며 얻은 깨달음입니다.


참고 자료


여러분도 BEM을 사용하면서 느낀 점이 있거나, AI 도구와 함께 개발하며 겪은 경험이 있다면 댓글로 나눠주세요.

함께 더 좋은 개발 문화를 만들어가면 좋겠습니다!