BEM CSS 방법론 실전 가이드 - 예제로 배우는 네이밍 규칙
“왜 내 CSS는 점점 스파게티 코드가 되어가는 걸까?”
“.button
클래스를 수정했더니 전혀 다른 페이지의 버튼이 깨졌어요…”
이런 경험이 있다면, BEM(Block, Element, Modifier)
방법론이 답이 될 수 있습니다.
BEM
은 CSS 클래스 이름을 체계적으로 작성하는 방법론으로, 2009년 러시아의 Yandex에서 개발되어 현재 전 세계적으로 사용되고 있습니다.
이번 포스트에서는 BEM
의 세 가지 핵심 개념인 Block
, Element
, Modifier
를 실제 예제와 함께 완벽하게 마스터해보겠습니다.
BEM이란 무엇인가?
BEM
은 Block
, Element
, Modifier
의 약자로, CSS 클래스 이름을 짓는 규칙입니다.
/* BEM 구조 */
.block {
}
.block__element {
}
.block--modifier {
}
.block__element--modifier {
}
css
왜 BEM을 사용해야 할까?
문제 상황 | BEM 이 제공하는 해결책 |
---|---|
클래스 이름 충돌 | 네임스페이스로 완전히 분리 |
스타일 덮어쓰기 | 독립적인 블록으로 격리 |
코드 이해 어려움 | 명확한 관계 표현 |
재사용 불가능 | 컴포넌트 단위 설계 |
1. Block: 독립적인 컴포넌트
Block이란?
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의 특징
- 독립성: 다른 블록에 의존하지 않음
- 재사용성: 어디서든 사용 가능
- 이동 가능: 위치가 바뀌어도 동작
- 중첩 가능: 블록 안에 다른 블록 포함 가능
<!-- 블록 안에 블록 중첩 -->
<div class="page">
<header class="header">
<!-- header 블록 -->
<form class="search-form">
<!-- search-form 블록 -->
<!-- ... -->
</form>
</header>
</div>
html
2. Element: Block의 구성 요소
Element란?
Element
는 Block
의 일부분으로, Block 안에서만 의미를 가지는 구성 요소입니다. Block
과 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란?
Modifier
는 Block
이나 Element
의 모양, 상태, 동작을 변경합니다. --
(더블 대시)로 연결합니다.
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
를 모두 활용해 실제 사용 가능한 카드 컴포넌트를 만들어보겠습니다.
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를 동시에 적용할 수도 있습니다:
<!-- 크고 주요한 버튼 -->
<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
의 가치:
- 클래스 이름 충돌 완전 방지
- 코드만 봐도 구조와 관계 파악 가능
- 안전한 리팩토링과 유지보수
- 팀 협업 시 일관된 코드 스타일
“명확한 것이 간결한 것보다 낫다”는 BEM
의 철학을 기억하세요.
AI 시대, BEM이 더 중요하다고 생각하는 개인적인 이유
실무에서 AI 도구들과 함께 개발하면서, 예전에 학습했던 BEM
방법론을 다시 돌아보게 되었습니다.
처음 BEM
을 배울 때는 솔직히 .card__button--primary
같은 긴 클래스명이 불필요하게 복잡해 보였습니다. “그냥 .btn
으로 써도 되는데…” 라고 생각했었죠.
그런데 최근 Claude
나 Cursor
같은 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 도구와 함께 개발하며 겪은 경험이 있다면 댓글로 나눠주세요.
함께 더 좋은 개발 문화를 만들어가면 좋겠습니다!