CSS 방법론 완전 정리: BEM, OOCSS, SMACSS

지정민
대규모 웹 프로젝트에서 CSS를 관리하는 것은 쉽지 않은 일입니다. 코드가 복잡해지고 유지보수가 어려워지는 문제를 해결하기 위해 여러 CSS 방법론이 등장했습니다. 이번 글에서는 가장 널리 사용되는 세 가지 CSS 방법론을 자세히 살펴보겠습니다.
CSS 방법론이 필요한 이유
기존 CSS의 문제점
/* 문제가 있는 CSS */
.header {
background: blue;
padding: 10px;
}
.title {
color: red;
font-size: 20px;
}
.content {
background: blue; /* 중복 */
padding: 10px; /* 중복 */
}- 중복 코드: 같은 스타일이 여러 곳에 반복
- 특이성 문제: CSS 우선순위로 인한 예측 불가능한 스타일 적용
- 유지보수 어려움: 수정 시 여러 파일을 찾아야 함
- 네이밍 충돌: 클래스명이 겹치는 문제
1. BEM (Block Element Modifier)
BEM은 가장 인기 있는 CSS 방법론 중 하나입니다. 명명 규칙을 통해 CSS의 구조를 명확하게 만듭니다.
BEM의 핵심 개념
- Block: 독립적인 컴포넌트
- Element: Block의 구성 요소
- Modifier: Block이나 Element의 변형
명명 규칙
/* Block */
.button {
}
/* Element */
.button__text {
}
.button__icon {
}
/* Modifier */
.button--primary {
}
.button--large {
}
.button--disabled {
}
/* Element + Modifier */
.button__text--highlighted {
}실전 예제
<!-- HTML -->
<button class="button button--primary button--large">
<span class="button__text">Click me</span>
<i class="button__icon">→</i>
</button>
<button class="button button--secondary button--small">
<span class="button__text">Cancel</span>
</button>/* CSS */
.button {
display: inline-flex;
align-items: center;
border: none;
border-radius: 4px;
cursor: pointer;
font-family: inherit;
transition: all 0.2s;
}
.button__text {
font-weight: 500;
}
.button__icon {
margin-left: 8px;
}
/* Modifiers */
.button--primary {
background: #007bff;
color: white;
}
.button--primary:hover {
background: #0056b3;
}
.button--secondary {
background: #6c757d;
color: white;
}
.button--large {
padding: 12px 24px;
font-size: 16px;
}
.button--small {
padding: 6px 12px;
font-size: 14px;
}
.button--disabled {
opacity: 0.6;
cursor: not-allowed;
}BEM의 장점
- 명확한 구조: 클래스명만 봐도 HTML 구조를 파악 가능
- 재사용성: Block 단위로 컴포넌트 재사용
- 특이성 문제 해결: 단일 클래스명으로 특이성 통일
- 팀 협업: 일관된 명명 규칙으로 팀원 간 소통 원활
BEM의 단점
- 긴 클래스명:
button__text--highlighted같은 긴 이름 - HTML 복잡성: 많은 클래스명으로 인한 HTML 가독성 저하
- 유연성 부족: 엄격한 규칙으로 인한 제약
2. OOCSS (Object-Oriented CSS)
OOCSS는 객체지향 프로그래밍의 개념을 CSS에 적용한 방법론입니다.
OOCSS의 핵심 원칙
1. 구조와 스킨 분리
/* 구조 (Structure) */
.button {
display: inline-block;
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
}
/* 스킨 (Skin) */
.button-primary {
background: #007bff;
color: white;
}
.button-secondary {
background: #6c757d;
color: white;
}2. 컨테이너와 콘텐츠 분리
/* 컨테이너 */
.media {
display: flex;
align-items: flex-start;
}
/* 콘텐츠 */
.media__object {
margin-right: 10px;
}
.media__body {
flex: 1;
}실전 예제
<!-- HTML -->
<div class="media">
<img class="media__object" src="avatar.jpg" alt="Avatar" />
<div class="media__body">
<h3 class="media__title">John Doe</h3>
<p class="media__text">This is a sample text.</p>
</div>
</div>
<button class="button button-primary">Primary Button</button>
<button class="button button-secondary">Secondary Button</button>/* CSS */
/* Button Object */
.button {
display: inline-block;
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
font-family: inherit;
text-decoration: none;
transition: all 0.2s;
}
/* Button Skins */
.button-primary {
background: #007bff;
color: white;
}
.button-secondary {
background: #6c757d;
color: white;
}
/* Media Object */
.media {
display: flex;
align-items: flex-start;
}
.media__object {
margin-right: 10px;
}
.media__body {
flex: 1;
}
.media__title {
margin: 0 0 5px 0;
font-size: 16px;
font-weight: bold;
}
.media__text {
margin: 0;
color: #666;
}OOCSS의 장점
- 재사용성: 작은 객체들을 조합하여 다양한 컴포넌트 생성
- 유지보수: 구조와 스타일이 분리되어 수정이 용이
- 성능: 작은 CSS 파일들로 인한 빠른 로딩
- 확장성: 새로운 스킨을 쉽게 추가 가능
OOCSS의 단점
- 학습 곡선: 객체지향 개념 이해 필요
- 복잡성: 많은 작은 클래스들로 인한 관리 복잡성
- 일관성: 팀원 간 일관된 사용법 유지 어려움
3. SMACSS (Scalable and Modular Architecture for CSS)
SMACSS는 CSS를 5가지 카테고리로 분류하여 체계적으로 관리하는 방법론입니다.
SMACSS의 5가지 카테고리
1. Base (기본)
/* Base */
body {
font-family: Arial, sans-serif;
line-height: 1.6;
color: #333;
}
a {
color: #007bff;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}2. Layout (레이아웃)
/* Layout */
.l-header {
background: #fff;
border-bottom: 1px solid #ddd;
}
.l-main {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.l-sidebar {
width: 250px;
float: left;
}
.l-content {
margin-left: 270px;
}3. Module (모듈)
/* Module */
.button {
display: inline-block;
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
}
.button-primary {
background: #007bff;
color: white;
}
.button-secondary {
background: #6c757d;
color: white;
}4. State (상태)
/* State */
.is-hidden {
display: none;
}
.is-active {
background: #007bff;
color: white;
}
.is-disabled {
opacity: 0.6;
cursor: not-allowed;
}5. Theme (테마)
/* Theme */
.theme-dark {
background: #333;
color: #fff;
}
.theme-dark .button-primary {
background: #0056b3;
}실전 예제
<!-- HTML -->
<div class="l-header">
<nav class="nav">
<a href="#" class="nav__link is-active">Home</a>
<a href="#" class="nav__link">About</a>
<a href="#" class="nav__link">Contact</a>
</nav>
</div>
<div class="l-main">
<div class="l-sidebar">
<div class="widget">
<h3 class="widget__title">Sidebar</h3>
<p class="widget__content">Content here</p>
</div>
</div>
<div class="l-content">
<button class="button button-primary">Primary</button>
<button class="button button-secondary is-disabled">Secondary</button>
</div>
</div>/* CSS */
/* Base */
body {
font-family: Arial, sans-serif;
line-height: 1.6;
color: #333;
margin: 0;
}
/* Layout */
.l-header {
background: #fff;
border-bottom: 1px solid #ddd;
padding: 10px 0;
}
.l-main {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.l-sidebar {
width: 250px;
float: left;
}
.l-content {
margin-left: 270px;
}
/* Module */
.nav {
display: flex;
gap: 20px;
}
.nav__link {
padding: 10px 15px;
color: #333;
text-decoration: none;
border-radius: 4px;
}
.widget {
background: #f8f9fa;
padding: 20px;
border-radius: 8px;
}
.widget__title {
margin: 0 0 10px 0;
font-size: 18px;
}
.widget__content {
margin: 0;
color: #666;
}
.button {
display: inline-block;
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
margin-right: 10px;
}
.button-primary {
background: #007bff;
color: white;
}
.button-secondary {
background: #6c757d;
color: white;
}
/* State */
.is-active {
background: #007bff;
color: white;
}
.is-disabled {
opacity: 0.6;
cursor: not-allowed;
}SMACSS의 장점
- 체계적 구조: 명확한 카테고리 분류로 코드 조직화
- 확장성: 모듈 단위로 기능 추가 및 수정 용이
- 유지보수: 각 카테고리별로 독립적인 관리
- 팀 협업: 일관된 구조로 팀원 간 소통 원활
SMACSS의 단점
- 복잡성: 5가지 카테고리로 인한 초기 설정 복잡
- 학습 곡선: 각 카테고리의 역할 이해 필요
- 유연성: 엄격한 구조로 인한 제약
방법론 비교
| 특징 | BEM | OOCSS | SMACSS |
|---|---|---|---|
| 복잡도 | 중간 | 낮음 | 높음 |
| 학습 곡선 | 낮음 | 중간 | 높음 |
| 유연성 | 중간 | 높음 | 중간 |
| 팀 협업 | 높음 | 중간 | 높음 |
| 확장성 | 중간 | 높음 | 높음 |
| 성능 | 중간 | 높음 | 중간 |
실무에서의 선택 기준
BEM을 선택하는 경우
- 팀원이 CSS 방법론에 익숙하지 않은 경우
- 빠른 프로토타이핑이 필요한 경우
- 명확한 명명 규칙이 필요한 경우
OOCSS를 선택하는 경우
- 높은 재사용성이 필요한 경우
- 작은 컴포넌트들을 조합하는 경우
- 성능 최적화가 중요한 경우
SMACSS를 선택하는 경우
- 대규모 프로젝트인 경우
- 체계적인 구조가 필요한 경우
- 장기적인 유지보수를 고려하는 경우
하이브리드 접근법
실무에서는 여러 방법론을 조합하여 사용하는 것이 일반적입니다.
/* BEM + OOCSS 조합 */
.button {
/* OOCSS: 구조 */
display: inline-block;
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
}
.button--primary {
/* OOCSS: 스킨 */
background: #007bff;
color: white;
}
.button__text {
/* BEM: Element */
font-weight: 500;
}
.button__text--highlighted {
/* BEM: Element + Modifier */
color: #ffc107;
}마무리
CSS 방법론은 대규모 프로젝트에서 CSS를 체계적으로 관리하기 위한 도구입니다. 각 방법론마다 장단점이 있으므로, 프로젝트의 특성과 팀의 상황에 맞는 방법론을 선택하는 것이 중요합니다.
가장 중요한 것은 일관성입니다. 선택한 방법론을 팀 전체가 일관되게 사용하고, 정기적으로 리팩토링을 통해 코드 품질을 유지하는 것이 핵심입니다.