최근에 프로젝트를 진행하며 어떤 규칙을 정해놓지 않고 CSS를 작성하거나 여러 사람이 함께 CSS를 작성하다보니 클래스 이름이 제각각이였던 경우가 있었다.
예를들어 같은 의미의 요소인데도 네이밍이 제각각이라 유지보수가 어려웠고, 어디서 스타일이 적용되는지 한눈에 파악하기 힘들었다.
그러면서 "이걸 어떻게 정리하지...?"하는 고민이 커졌다.
이런 문제를 해결하는 방법 중 하나가 바로 BEM(Block, Element, Modifier)방법론이다!
BEM을 사용하면 CSS 코드가 깔끔하게 정리되고, 협업할 때도 모두가 같은 규칙을 따라가서 훨씬 편해진다.
지금부터 BEM이 무엇이고, 어떻게 사용하는지 알아보자!
🔍 CSS 조직 방법론
큰 프로젝트에서는 CSS의 규모를 줄이고, 개발자 간 협업을 원활하게 하며, 코드 유지보수를 쉽게 만들기 위한 여러 방법론이 있다.
대표적인 예시는 다음과 같다.
- OOCSS(Object-Oriented CSS): 컨테이너와 콘텐츠를 분리하는 방식
- SMACSS(Scalable and Modular Architecture for CSS): CSS 규칙을 다섯 가지 범주로 나누는 스타일 가이드
- SUITCSS: 구조화된 클래스 네이밍과 의미 있는 하이픈 사용
- Atomic CSS: 스타일을 원자 단위로 쪼개어 재사용성을 높이는 방식
이 중에서 BEM은 비교적 덜 헷갈리고 직관적인 구조를 제공하기 때문에 많은 개발자들이 선호하는 방법론이다.
🏛️ BEM의 핵심 개념
BEM은 크게 Block(블록), Element(요소), Modifier(수정자) 세 가지 개념으로 구성된다.
1️⃣ 블록(Block)
독립적으로 존재할 수 있는 개별적인 요소
☑️ 예시: header, container, menu, checkbox, input
<div class="menu">...</div>
.menu {
background-color: #eee;
padding: 10px;
}
2️⃣ 요소(Element)
블록 내부에 포함되며, 단독으로는 의미를 가지지 않는 구성요소
☑️ 예시: menu__item, list__item, checkbox__caption, header__title
<ul class="menu">
<li class="menu__item">홈</li>
<li class="menu__item">소개</li>
</ul>
.menu__item {
color: blue;
font-size: 14px;
}
3️⃣ 수정자(Modifier)
블록이나 요소의 상태나 스타일을 변경하는 용도로 사용
☑️ 예시: disabled, highlighted, checked, size-big, color-yellow
<button class="button button--state-success">성공</button>
<button class="button button--state-danger">위험</button>
.button--state-success {
color: #fff;
background: green;
}
.button--state-danger {
color: red;
}
🌟 BEM 방식의 장점
- 모듈화(Modularity): 블록 스타일이 독립적이어서, 다른 페이지에서도 쉽게 재사용할 수 있다.
- 재사용성(Reusability): 여러 블록을 조합해 새로운 UI 요소를 쉽게 만들 수 있다.
- 명확한 구조(Structure): 코드가 일관성 있게 작성돼서, 협업할 때도 가독성이 뛰어나다.
✍️ 네이밍 규칙
올바른 네이밍 규칙을 따르면 개발 속도가 빨라지고, 유지보수가 쉬워진다.
📌 블록
- 소문자, 숫자, 대시(-)만 사용 가능
- CSS 클래스는 .block 형태로 작성
<div class="form">...</div>
.form {
padding: 20px;
}
📌 요소
- 블록 이름 뒤에 두 개의 언더스코어(__)를 붙여 작성
<div class="form">
<input class="form__input" type="text" />
</div>
.form__input {
border: 1px solid #ccc;
}
📌 수정자
- 블록 또는 요소 이름 뒤에 두 개의 대시(--)를 붙여 작성
<button class="button button--primary">버튼</button>
.button--primary {
background-color: blue;
color: white;
}
💡 예제
<form class="form form--theme-dark">
<input class="form__input" type="text" />
<button class="form__submit form__submit--disabled">제출</button>
</form>
.form { }
.form--theme-dark { background-color: #333; color: #fff; }
.form__input { }
.form__submit { }
.form__submit--disabled { opacity: 0.5; }
🙋 BEM에 대한 자주 묻는 질문 (FAQ)
❓왜 BEM을 사용해야 할까요?
CSS를 구조적으로 관리하는 방법은 여러 가지가 있지만, BEM은 재사용성과 유지보수성이 뛰어난 방식이다.
블록 단위로 독립적인 스타일을 적용할 수 있어서, 다른 페이지나 프로젝트에서도 그대로 활용하기 좋다.
❓왜 수정자(Modifier)를 별도의 클래스 형태로 사용하나요?
BEM에서는 .block--mod 형태의 수정자를 사용한다. 이렇게 하면 CSS의 우선순위 문제를 방지하고, 스타일이 의도치 않게 겹치는 걸 막을 수 있다.
❌ 비추천:
<div class="button primary"></div>
✅ 추천:
<div class="button button--primary"></div>
❓블록을 태그명으로 지정하면 안 되나요?
<button class="button"> 대신 <button> 태그만 사용하고 싶을 수도 있다. 하지만 이렇게 하면 스타일이 태그에 종속되기 때문에, 같은 스타일을 다른 태그에서 재사용하기 어려워진다. 따라서 반드시 클래스명으로 블록을 지정하는 것이 좋다.
❓요소 안에 요소가 있을 땐 어떻게 이름을 정하나요?
BEM에서는 요소(Element)를 계속 중첩해서 작성하지 않는다. 예를 들어,
block__elem1__elem2처럼 길게 쓰는 대신, 각 요소를 블록 기준으로만 네이밍하는 게 좋다.
❌ 비추천:
<div class="menu__item__icon"></div>
✅ 추천:
<div class="menu__item">
<span class="menu__icon"></span>
</div>
❓BEM에서는 전역 CSS 리셋을 사용하면 안 되나요?
전역 CSS 리셋을 사용하면 예상치 못한 스타일 충돌이 발생할 수 있다. 대신 각 블록이 필요한 스타일을 직접 정의하는 것이 더 좋은 방법이다.
❌ 비추천 (전역 리셋 사용):
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
✅ 추천 (각 블록별 리셋 적용):
.menu {
margin: 0;
padding: 0;
list-style: none;
}
❓ID와 태그명을 CSS 셀렉터로 사용하면 안 되나요?
ID는 CSS의 우선순위(Specificity)가 너무 높아서 유지보수가 어려워진다. 또, 태그명을 포함하면 특정 요소에 종속되기 때문에 재사용성이 떨어진다.
❌ 비추천:
#header { color: red; } /* ID 사용 */
nav ul { list-style: none; } /* 태그명 사용 */
✅ 추천
.header { color: red; }
.nav__list { list-style: none; }
출처