# SCSS를 사용하다가 알게된 BEM (Block Element Modifier)

회사 사내에서 `Vue`  기술스택을 많이 사용하다보니, 자연스레 `SCSS` 를 많이 사용하게 된다. `React` 의 `Emotion` 등의 스타일, 그리고 최근 많이 사용하고 있는 `Tailwind` 의 경우에는 자연스럽게 내부 난수와 같은 값으로 클래스 명을 유니크하게 지정해주면서 스타일을 지정해준다.

`SCSS` 는 스타일은 서비스 도메인의 바닥부터 함께 스타일 구조들을 천천히 쌓아나가며, 직접 이름을 짓고 붙여주는 과정을 진행하게 되는데 이를 사용해보았다면 CSS 클래스 이름을 어떻게 지어야 할지 고민을 했던 적이 있을 수 있다.

처음 CSS를 배웠을 때, 클래스 이름은 그냥 의미 있어 보이면 된다고 생각했다. `.header`, `.menu`, `.button`처럼. 회사 업무를 진행하면서 막상 생각치도 못한 곳에서 문제가 생겼다. 누군가 `.button`을 이미 쓰고 있다면, 거기에 내가 `.button`을 또 정의하면서 스타일이 충돌하게 된다는 것인데, 만약 스타일시트에 모듈과 같은 스코프가 정의되어있지 않다면 CSS의 특성상, 이건 단순한 실수가 아니라 구조적인 문제였다. 그때부터 "이름을 어떻게 지어야 하는가"가 진짜 문제처럼 느껴졌다. 

그러다 `—` , `__`  클래스 네임에 프리픽스가 붙는 클래스 네임들을 발견하게 되었고, 이게 뭔가 찾아보다가 포스팅을 시작하게 되었다 ^,^ .. 바로 BEM(**Block Element Modifier)**이라는 개념인데, CSS 클래스 네이밍 방법론 중 가장 오래되고, 가장 널리 쓰이는 방식이다.

## BEM은 왜 생겼을까?

BEM은 러시아 인터넷 기업 **Yandex**에서 2005년경 내부적으로 사용하기 시작한 방법론이다. Yandex는 대규모 웹 서비스를 수십 명의 개발자가 함께 유지보수해야 했고, 스타일 충돌과 유지보수 어려움이 반복됐다.

2010년대 초, CSS 생태계에는 마땅한 기준이 없었다. 클래스 이름은 개발자마다 달랐고, 프로젝트가 커질수록 스타일 파일은 누구도 건드리기 싫은 영역이 됐다. OOCSS(Object-Oriented CSS)나 SMACSS 같은 개념들이 있었지만, 구체적인 네이밍 규칙까지 제시한 건 BEM이 처음이었다.

Yandex가 2012년에 BEM을 공개하면서 빠르게 퍼졌다. 특히 컴포넌트 기반 UI가 주류가 된 이후, BEM의 구조적 사고방식이 React나 Vue의 컴포넌트 개념과 잘 맞아떨어져서 더 주목받았다.

## BEM의 핵심 구조

BEM의 기본 구조는 세 가지로 이뤄진다.

```javascript
Block__Element--Modifier
  ↑       ↑        ↑
 블록    엘리먼트    수정자
```

### Block — 독립적인 UI 단위

**Block**은 그 자체로 독립적으로 존재할 수 있는 UI 구성 요소다. 예를 들어 `nav`, `card`, `button`, `form` 같은 것들이다.

```javascript
.card { }         /* 카드 블록 */
.nav { }          /* 내비게이션 블록 */
.button { }       /* 버튼 블록 */
```

Block은 **위치에 의존하지 않는다**는 점이 중요하다. 어디에 놓여도 같은 모양이어야 한다. `margin`, `position`처럼 외부 레이아웃을 정의하는 속성은 Block 자체에 넣지 않는 것이 원칙이다.

### Element — Block에 종속된 구성 요소

**Element**는 Block 내부에서만 의미를 가지는 구성 요소다. `__` (언더스코어 두 개)로 Block과 연결한다.

```javascript
.card__title { }      /* 카드 안의 제목 */
.card__image { }      /* 카드 안의 이미지 */
.card__footer { }     /* 카드 안의 푸터 */
.nav__item { }        /* 내비게이션의 항목 */
.nav__link { }        /* 내비게이션 항목 안의 링크 */
```

Element는 Block 없이는 존재할 수 없다. `.card__title`은 `.card` 없이는 의미가 없다. 이게 BEM이 말하는 "종속"이다. Element는 **중첩하지 않는다**. `.card__footer__button`처럼 쓰면 안 된다. 카드 푸터 안에 있는 버튼이라도 클래스 이름은 `.card__button`이 맞다. 구조는 HTML로 표현하고, 클래스 이름은 깊이를 반영하지 않아야한다.

여기서 내가 잘못 알고 있었던 점은, 무조건 엘리먼트 클래스 네임 명명 규칙은 상위 엘리먼트를 따라가야한다고 생각했다.

```javascript
<!-- 이렇게 하면 안 된다 -->
<div class="card">
  <div class="card__footer">
    <button class="card__footer__button">확인</button>  ← 중첩 금지
  </div>
</div>

<!-- BEM에서 맞는 방식 -->
<div class="card">
  <div class="card__footer">
    <button class="card__button">확인</button>  ← Block 기준으로 평탄화
  </div>
</div>
```

### Modifier — 상태나 변형을 나타내는 수정자

**Modifier**는 Block이나 Element의 다른 상태, 다른 변형을 나타낸다. `--` (하이픈 두 개)로 연결한다.

```javascript
.button--primary { }      /* 주요 액션 버튼 */
.button--disabled { }     /* 비활성화된 버튼 */
.button--large { }        /* 큰 버튼 */
.card--highlighted { }    /* 강조된 카드 */
.nav__item--active { }    /* 활성화된 내비게이션 항목 */
```

Modifier는 **독립적으로 사용하지 않는다**. `.button--primary`는 `.button`과 함께 쓴다.

```javascript
<!-- 맞는 방식 -->
<button class="button button--primary">저장</button>

<!-- 틀린 방식 — Modifier만 단독 사용 -->
<button class="button--primary">저장</button>
```

---

## 런타임에서 BEM이 작동하는 방식

BEM은 런타임 메커니즘이 아니라 **명명 규칙**이다. 브라우저는 BEM을 전혀 모른다. 브라우저 입장에서 `.card__title`은 그냥 클래스 이름일 뿐이다.

그러나 바로 이 점이 BEM의 강점이다. **특이도(specificity)를 낮게 유지한다**.

CSS 특이도 계산 방식을 떠올려보면:

```javascript
인라인 스타일 > ID > 클래스 > 태그

specificity: (0, 0, 0, 0)
              ↑  ↑  ↑  ↑
           인라인 ID 클래스 태그
```

BEM은 클래스 하나만으로 모든 스타일을 표현한다. ID를 쓰지 않고, 태그 선택자도 최소화한다. 결과적으로 특이도가 항상 `(0, 0, 1, 0)`으로 균일하게 유지된다.

```javascript
/* BEM 방식 — 특이도: (0,0,1,0) */
.card__title { color: black; }

/* 비BEM 방식 — 특이도: (0,0,1,1) */
.card h2 { color: black; }

/* 비BEM 방식 — 특이도: (0,1,0,0) */
#card-title { color: black; }
```

특이도가 균일하면, 나중에 스타일을 덮어쓸 때 `!important`나 더 복잡한 선택자 없이도 가능하다. Modifier가 Base 스타일을 덮어쓰는 구조가 깔끔하게 작동하는 이유다.

```javascript
.button {
  background: gray;
  color: white;
  padding: 8px 16px;
}

.button--primary {
  background: blue;    /* 같은 특이도, 뒤에 선언돼서 덮어씀 */
}

.button--large {
  padding: 12px 24px;  /* 같은 특이도, 정상적으로 덮어씀 */
}
```

브라우저는 CSS 파일을 위에서 아래로 파싱하고, 같은 특이도일 경우 나중에 선언된 규칙이 이긴다. BEM은 이 캐스케이딩 규칙을 예측 가능하게 만든다.

## 실제 HTML 구조와 BEM 적용 예시

실제로 카드 컴포넌트를 만들면 이렇게 된다.

```javascript
<article class="card card--featured">         <!-- ← Block + Modifier -->
  <img class="card__image" src="..." />       <!-- ← Element -->
  <div class="card__body">                    <!-- ← Element -->
    <h2 class="card__title">제목</h2>         <!-- ← Element -->
    <p class="card__description">설명</p>     <!-- ← Element -->
  </div>
  <footer class="card__footer">              <!-- ← Element -->
    <button class="button button--primary">  <!-- ← 별도 Block + Modifier -->
      자세히 보기
    </button>
  </footer>
</article>
```

여기서 주목할 점은 `button`이다. 카드 안에 있지만 `.card__button`이 아니라 `.button`이다. 버튼은 **독립적인 Block**으로 재사용되기 때문이다. 카드 안에서만 쓰이는 요소라면 `.card__button`으로 만들겠지만, 여러 곳에서 쓰이는 버튼이라면 별도 Block으로 두는 것이 맞다.

> **BEM이 유독 좋은 것만은 아니다?**

실제로 BEM 구조로 엘리먼트를 사용하게 되면, CSS 스타일을 적용할 때는 정말 가독성이 좋다고 느껴진다.

이를 테면 SCSS의 중첩 구조에서 `&__` , `&—`를 통해 중첩 구조에서 어떤 엘리먼트를 가르키는지 명확하게 알 수 있었다. `&` 를 표현해주기 위한 블록의 `prefix` 를 계속해서 붙여줘야한다는 특징을 가지고 있다.

다만 엘리먼트에서는 `&` 를 표현해주기 위한 블록의 `prefix` 를 계속해서 붙여줘야한다는 특징을 가지고 있다.

```javascript
// 계속 `prefix` 가 붙는 구조

<div class="card">
  <h2 class="card__title">제목</h2>      <!-- card__ 계속 반복 -->
  <img class="card__image" />            <!-- card__ 계속 반복 -->
  <p class="card__description">설명</p>  <!-- card__ 계속 반복 -->
</div>
```

```javascript
// 구조적인 SCSS 중첩 구조

.card {
  background: white;
   
  &__title {        // ← 컴파일하면 .card__title
    font-size: 20px;
  }

  &__image {        // ← .card__image
    width: 100%;
  }

  &--featured {     // ← .card--featured (Modifier)
    border: 2px solid blue;
  }

  &--featured &__title {  // ← .card--featured .card__title
    color: blue;
  }
}
```

## BEM과 다른 방법론 비교

| 방법론 | 핵심 아이디어 | 네이밍 예시 | 특이도 관리 | 학습 곡선 |
| --- | --- | --- | --- | --- |
| **BEM** | Block–Element–Modifier 구조화 | `.card__title--bold` | 균일하게 낮음 | 중간 |
| **OOCSS** | 구조와 스킨 분리 | `.btn .btn-skin-blue` | 보통 | 낮음 |
| **SMACSS** | 카테고리별 분류 (Base, Layout, Module...) | `.l-grid`, `.is-active` | 카테고리마다 다름 | 높음 |
| **CSS Modules** | 로컬 스코프로 충돌 방지 | `styles.card` (컴파일 후 해시) | 자동 격리 | 낮음 |
| **Utility-first (Tailwind)** | 작은 유틸리티 클래스 조합 | `flex gap-4 p-4 rounded` | 거의 없음 | 낮음 |

BEM의 가장 큰 장점은 **클래스 이름만 보고도 구조를 파악할 수 있다는 점**이다. 반면 클래스 이름이 길어지는 단점도 있다.

CSS Modules나 Tailwind가 대세가 된 지금도 BEM이 여전히 쓰이는 이유는, 빌드 도구 없이도 동작하고 팀 전체가 쉽게 합의할 수 있는 기준이기 때문이다.

## 언제 BEM을 선택하면 좋을까?

### BEM이 잘 맞는 경우

**디자인 시스템이나 UI 라이브러리를 만들 때.** 컴포넌트가 여러 프로젝트에서 재사용된다면, 스타일 충돌 없이 예측 가능한 클래스 이름이 필요하다. BEM은 이 요구를 잘 충족한다.

```javascript
/* 라이브러리 수준에서 명확한 네임스페이스 */
.ds-button { }           /* ds = design system */
.ds-button--primary { }
.ds-card__title { }
```

**빌드 도구 없이 순수 CSS를 써야 할 때.** CSS Modules나 styled-components처럼 컴파일 과정이 필요한 방식은 쓸 수 없는 환경이 있다. 그때 BEM은 충돌 없는 네이밍을 보장하는 실용적인 선택이 될 수 있다. **팀 내 CSS 컨벤션을 통일해야 할 때.** 경험이 다른 팀원들이 함께 CSS를 작성할 때, BEM은 명확한 규칙을 제공한다. 코드 리뷰에서 "이 클래스 이름 왜 이렇게 지었어요?" 라는 질문이 줄어든다. (현재 팀 내에 가장 도입이 되어야하는 이유 중에 하나이기도 하다, 일부 프로젝트에서는 BEM으로 디자인 시스템 규격이 되어있는데, 일부 백오피스를 비롯한 프로젝트에서는 명명 규칙이 따로 존재하지 않는다)

### BEM이 불편한 경우

글의 시작말에서 언급했던 것처럼 **React, Vue처럼 컴포넌트 스코프가 기본으로 제공되는 환경**에서는 이미 CSS Modules나 Vue의 `scoped` 스타일이 충돌을 막아준다면 BEM의 주요 이점이 줄어든다. 

**Tailwind 같은 유틸리티 퍼스트 방식을 쓸 때.** 클래스 이름 자체를 최소화하는 방식과 BEM은 철학이 다르다. 섞어 쓰면 혼란이 생길 수 있다.

**클래스 이름 길이가 큰 부담이 될 때가 있다. **이게 사실 BEM을 도입하면서 가장 우려가 되었던 부분인데, `.some-component__inner-wrapper--with-special-state`처럼 길어지면 HTML이 읽기 어려워진다. 이럴 땐 Block 분리를 다시 검토하거나, CSS Modules를 도입하는 쪽이 낫다.

## 어떻게 하면 BEM을 더 잘 쓸 수 있을까?

### Mix — 여러 Block을 한 요소에

한 HTML 요소가 두 가지 역할을 할 때, BEM은 **Mix**를 허용한다.

```javascript
<!-- nav__item이면서 동시에 독립적인 link Block -->
<a class="link nav__item" href="...">메뉴</a>
```

`nav__item`은 `nav` 블록 안에서의 역할을 담당하고, `link`는 링크로서의 스타일을 담당한다. 각 클래스의 책임이 분리된다.

### Modifier 값 표현

Boolean modifier(`--active`, `--disabled`)와 Key-Value modifier(`--size-large`, `--theme-dark`)를 구분하면 더 명확하다.

```javascript
/* Boolean Modifier */
.button--disabled { }

/* Key-Value Modifier */
.button--size-large { }    /* size: large */
.button--theme-dark { }    /* theme: dark */
```

### 네임스페이스 prefix 추가

대규모 프로젝트에서는 Block 이름 앞에 prefix를 붙여서 역할을 명확히 한다.

```javascript
.c-card { }        /* c = component */
.l-header { }      /* l = layout */
.u-visually-hidden { }  /* u = utility */
.js-modal-trigger { }   /* js = JavaScript hook, 스타일 없음 */
```

`js-`로 시작하는 클래스는 CSS 스타일링에 쓰지 않고 JavaScript에서만 쓰는 규칙이다. 스타일과 동작의 결합을 방지한다.

실제로 써보면 BEM은 처음에는 클래스 이름이 너무 길다는 느낌이 든다. 근데 시간이 지나면 HTML을 보는 것만으로도 구조가 눈에 들어오는 경험을 할 수 있다. (처음부터 BEM으로 명명 규칙을 설정하고 진행해볼 것이라는 생각을 한다. 무지성으로 `__` 을 넣곤했는데 ^,^.. ) 아직 명명 규칙을 찾고있는 팀이라면 클래스 이름들을 BEM 기준으로 한번 다시 살펴보는 것만으로도 구조적으로 무엇이 Block이고 무엇이 Element인지 프로젝트에서 명확해지는 경험을 할 수 있을 것이라 생각이 든다.

For the site tree, see the [root Markdown](https://slashpage.com/timmy.md).
