BEM은 .card__title--active 처럼 클래스명으로 부모-자식 관계와 상태를 명확히 나타내는 CSS 네이밍 컨벤션입니다. 하지만 순수 CSS에서는 클래스명이 너무 길어져 타이핑하기 번거롭습니다.
SCSS의 부모 참조 연산자 &를 BEM과 결합하면, 중첩(Nesting)의 깊이를 1단계로 유지하면서도 BEM 클래스명을 자동으로 생성할 수 있어 코드의 가독성과 유지보수성이 극대화됩니다.
| 구성요소 | 구분자 | 설명 및 예시 |
|---|---|---|
| Block (블록) | 없음 | 독립적으로 의미를 가지는 최상위 컴포넌트 (예: .card, .nav) |
| Element (요소) | __ (언더스코어 2개) | 블록 내부에 종속된 하위 요소 (예: .card__title, .nav__item) |
| Modifier (상태) | -- (하이픈 2개) | 블록이나 요소의 모양/상태 변경 (예: .card--dark, .nav__item--active) |
& 연산자를 사용하여 부모 선택자를 참조하면, 컴파일 시 부모 이름이 문자열로 치환되며 BEM 클래스명이 완성됩니다.
<!-- BEM 방식으로 작성된 제품 카드 -->
<article class="product-card">
<div class="product-card__content">
<h2 class="product-card__title">최신형 노트북</h2>
<p class="product-card__price product-card__price--discount">1,200,000원</p>
</div>
<button class="product-card__button">구매하기</button>
</article>
/* SCSS 작성법 (& 기호가 .product-card 로 치환됨) */
.product-card {
border: 1px solid #ddd;
border-radius: 8px;
max-width: 300px;
/* Element */
&__content {
padding: 20px;
}
&__title {
font-size: 1.25rem;
font-weight: bold;
}
&__price {
color: #333;
/* Modifier: 상태나 변형은 &-- 로 연결 */
&--discount {
color: #ef4444;
font-weight: bold;
text-decoration: underline;
}
}
/* 가상 클래스(Pseudo-class)도 &로 연결 */
&__button {
background: #3b82f6;
color: white;
&:hover {
background: #2563eb;
}
}
}
실무에서는 &__나 &--를 매번 타이핑하는 대신, Mixin과 문자열 보간(Interpolation, #{})을 활용해 BEM 구조를 함수처럼 강제하는 아키텍처를 많이 사용합니다. 이 방법을 사용하면 팀원들이 실수로 잘못된 BEM 컨벤션을 사용하는 것을 원천 차단할 수 있습니다.
/* BEM 제어용 믹스인 정의 */
@mixin e($element) {
&__#{$element} {
@content;
}
}
@mixin m($modifier) {
&--#{$modifier} {
@content;
}
}
/* 믹스인 활용 예시 */
.product-card {
/* @include e('content')는 결국 &__content 와 동일합니다. */
@include e('content') {
padding: 20px;
}
@include e('price') {
color: #333;
@include m('discount') {
color: #ef4444;
}
}
}
초보자들이 가장 많이 하는 실수는 HTML 구조와 똑같이 4단계, 5단계 이상 중첩하는 것입니다. (예: .nav { ul { li { a { span { ... } } } } }) 이렇게 하면 컴파일된 CSS의 구체성(Specificity)이 불필요하게 높아져 스타일 덮어쓰기가 매우 힘들어집니다. 중첩은 최대 3단계를 넘지 않는 것이 좋으며, BEM을 사용하면 1단계로 평탄화할 수 있습니다.