jngmnj's blog

jngmnj's blog Logo

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를 체계적으로 관리하기 위한 도구입니다. 각 방법론마다 장단점이 있으므로, 프로젝트의 특성과 팀의 상황에 맞는 방법론을 선택하는 것이 중요합니다.

가장 중요한 것은 일관성입니다. 선택한 방법론을 팀 전체가 일관되게 사용하고, 정기적으로 리팩토링을 통해 코드 품질을 유지하는 것이 핵심입니다.