기록하는 개발자

CSS 방법론 - BEM, OOCSS, SMACSS 본문

Web

CSS 방법론 - BEM, OOCSS, SMACSS

밍맹030 2024. 3. 3. 17:58
728x90

내가 지금까지 무심코 써왔던 CSS 스타일링 방법이 사실은 CSS 방법론이라는 이름하에 여러 개로 나눠지고 있었다.

가장 유명한 BEM, OOCSS, SCACSS 세 가지 중 무엇을 쓰는 것이 효율적인가에 대해 많은 이야기가 있는데

알고 보니 나는 이 3가지를 모두 섞어서 쓰고 있었다🫢

더 놀라운 점은 실제로 한 가지만 쓰는 것보다는 두 가지 이상을 섞어 쓰면서 개별적인 단점을 보완하는 것이 더 유리하다는 것이다🫢🫢🫢

그렇다면 세 가지 방법론에 대해 알아보자


1. BEM

블록(Block), 요소(Element), 상태(Modifier)로 구분하여 클래스 이름을 작성하는 방법론이다. 각 부분을 __와 --로 구분하여 클래스명을 짓게 된다. Ex) Block__Element—Modifier

 

1. Block

- block의 속성을 가지고 재사용할 수 있는 독립적인 영역에 적용한다.

- block은 여백, 위치, 색상 등 상태에 대한 내용은 담지 않는다.

- 클래스명이 길어질 경우엔 하이픈(-) 으로 구분한다.

<!-- right way -->
<div class="header"></div>
<div class="btn-container"></div>

<!-- wrong way -->
<!-- fixed, white 같이 상태에 대한 내용은 클래스로 담지 않는다. -->
<div class="header-fixed"></div>
<div class="btn-container-white"></div>

 

2. Element

- Block 안에 적용되는 내용들로 클래스명도 블록과 맥락이 이어져야한다.

- 클래스명은 언더바 2개(__) 로 작성한다.

<div class="nav">
  <div class="nav__logo">
  	<img class="nav__logo--small" src="/logo"></img>
  </div>
  
  <div class="btn-container">
  	<button class="btn-container__btn"></button>
  	<span class="btn-container__btn-text"></span>
  </div>

</div>

 

3. Modifiers

- block 혹은 element의 모양(color, size), 상태(position, disabled, checked)를 정의한다.

- 클래스명은 하이픈 2개(--)로 작성한다.

<div class="nav">
  <div class="nav__logo">
  	<img class="nav__logo--small" src="/logo"></img>
  </div>
</div>

 

장점

직관적인 클래스 사용을 통해 여러사람이 함께 작업을 진행하더라도 클래스를 알아보기 쉽게한다.

- 코드의 재사용성이 용이하다.

 

단점

- 직관적인 클래스명을 사용하다보니 클래스명이 지나치게 길어져 복잡해지는 경우가 있다.

 


2. OOCSS(Object Oriented Css)

CSS를 모듈 방식으로 작성하여 중복을 줄이는 방식의 방법론으로, 실제로 가장 많이 쓰인다. 

 

특징

- css를 모듈 방식으로 적용해서 중복을 최소화 하는 것을 추구한다.

- id 선택자를 사용하지 않는다.

- 각 단어별 하이픈(-)으로 구분한다.

 

1. 구조와 외형을 분리

반복되는 기본 구조 및 외형과 관련된 내용을 공통 스타일화 하여 정의한다.

<style>
   /* 구조 스타일 */
  .button{
    width : 80px;
    height : 40px;
    text-align : center;
  }
  
  /* 외형 스타일 */
  .button-black{
    background : black;
    color : white;
  }
</style>

<span class="button button-black">click</span>

 

2. 컨텐츠와 컨테이너를 분리

- 어떤 요소(element)에 적용되어 있는게 중요한 것이 아니라 클래스를 적용한 컨텐츠 자체에 의미를 부여하고 사용한다.

- 태그가 변경되어도 적용한 클래스가 같다면 css를 바꿀 필요가 없다.

- 태그에 영향없이 클래스 자체에 스타일이 부여되기 때문에 재사용이 용이하다.

<h2 class="title"></h2>
<p class="title"></p>

 

장점 : 공통된 부분을 찾아 어디서나 재활용 할 수 있다.

단점 : 다중 클래스 사용으로 유지보수의 어려움과 가독성 저하의 가능

 


3. SMACSS(Scalable and Modular Architecture for CSS)

CSS를 범주화(Categorization)로 패턴화 하고자 하는 방법론

작성할 CSS를 비슷한 종류끼리 모아 5가지 스타일로 나누고 각 유형에 맞는 선택자와 작명법, 코딩 기법을 제시한다. 기본(base), 레이아웃(layout), 모듈(module), 상태(state), 테마(theme) 다섯 가지의 범주를 제시한다.

 

1. Base (기본)

각 브라우저의 스타일 초기화를 위해 reset 등 페이지 전체에 기본 스타일을 적용하며 !important는 사용하지 않는다.

 

2. Layout (레이아웃)

- 페이지 큰 틀을 잡아주는 레이아웃과 관련된 스타일을 정의한다.

- header, footer, container, aside 등 주요 컴포넌트들과 그 하위 컴포넌트에 규칙을 적용한다.(nav, form...)

- 주요 요소(id)와 하위 요소(class)로 구분하고 접두사를 사용한다.

- 특정 상태에 따른 레이아웃을 변경하는 내용을 다루는 css 작성시 l- 혹은 layout- 클래스를 접두사로 붙인다.

#header{
    width:100%
}

#header .nav{
    overflow:hidden;
}

/* ex) fixed: 고정된 크기의 #header를 명시 */
.l-fixed #header{
    width:1200px;
}

/* ex) flipped: floating된 .nav를 명시 */
.l-flipped #header .nav{
    float:left
}

 

3. Module (모듈)

- 재사용성이 높은 구성 요소를 정의한다.

- 재사용을 위해서 id 선택자, 태그(element) 선택자를 사용하지 않는다.

(단순 태그 선택자를 사용하게 된다면 .side_button > a 와 같이 '>' 직계자손 선택자를 사용한다.)

<style>
  .buttons{absolute; top:0; right:0}
  .buttons > a{display:block; border:1px solid blue;}
</style>

<div class="buttons">
  <a href="#">버튼</a>
</div>

 

4. State (상태 규칙)

- 요소의 상태 변화를 표현하며, 주로 접두사 .is- 클래스를 사용한다(ex) .is-disabled, .is-checked).

- 클래스 적용은 javascript를 통해(element.classList.add) 특정 이벤트에 따라 적용한다.

.is-error { ... }
.is-hidden { ... }
.is-disabled { ... }

 

5. Theme (테마)

- 사용자가 선택 가능하도록 스타일을 재선언하여 사용한다.

- 주의 할 점은 메인 css 뒤에 테마용 css를 선언해야 한다는 점이다.(main이 나중에 오면 theme이 덮어씌워짐)

/* main.css */
.button{background:black;}

/* theme.css */
.button{background:white;}

 

SMACSS 사용 시 유의사항

  1. 파생된 CSS 선택자(후손 선택자, 자식 선택자, 필터 선택자, 형제 선택자 등)사용금지
  2. Id 선택자 사용금지
  3. !Important 사용금지
  4. 다른 개발자들이 이해할 수 있도록 class 이름을 의미 있게 지어야 한다.

 


 

내가 사용하는 방식을 소개해볼까 한다🤭

우선 나는 css가 아닌 scss를 사용한지는 꽤 됐다. scss는 css 전처리기로 아래와 같은 장점이 있다.

css 전처리기(preprocessor) 장점
재사용성 - 공통 요소 또는 반복적인 항목을 변수 또는 함수로 대체
시간적 비용 감소 - 임의 함수 및 Built-in 함수로 개발 시간적 비용 절약
유지 관리 - 중첩, 상속 등의 기능으로 구조화된 코드로 유지 및 관리 용이

Sass(Scss)의 장점(특징)
- 변수 사용 가능  / - 수학 연산자(+ - / * % == !=)를 사용할 수 있다.
- nesting 기능 : 중첩해서 선언 가능. 상위 요소 참조 시 문자 & 사용
- import를 통해 다른 scss 파일을 import 가능
- extend를 통해 특정 선택자의 속성을 상속받기 가능
- mixin을 통해 공통적으로 쓰이는 css 속성을 묶어 선언 후 @include를 통해 재사용 가능
- 커스텀 함수를 사용 가능. @function으로 정의, @return이 리턴값
- @if, @else, @for, @each, @while을 통해 흐름을 제어할 수 있다.

 

글의 시작에서 알고 보니 내가 방법론 3가지를 모두 섞어서 쓰고 있었다고 했다. 세 가지를 엄격하게 적용한 것은 아니고, 어떻게 보면 편리하게 필요한 부분만 가져와서 사용한 것 같다. 가장 최근 프로젝트에서는 아래와 같은 방법으로 스타일링을 적용했다.

 

0. 전역으로 사용하는 색상은 변수로, 재사용 되는 스타일(헤더나 본문폰트, 버튼 스타일 등)은 mixin으로 지정해두고 필요할 때 include 해서 사용한다. 아래는 실제 내가 프로젝트에서 작성한 variable.scss 파일의 일부이다.

// variable.scss

// 전역으로 사용하는 색상
$color-disabled: #bdc8d6;
$color-error: #EE0D0D;
$color-focus: #2a82f0;
$color-toast-background: rgba(0, 0, 0, 0.8);

$color-text-white: #fff;
$color-text-black: #000;
$color-text-gray: #a0a0a0;
$color-text-darkgray: #646464;
$color-placeholder: rgba(0, 0, 0, 0.3);

// 전역으로 사용하는 폰트 스타일
@mixin text-h1 {
    font-weight: 400;
    font-size: 6rem;
    line-height: 6.5rem;
}

@mixin text-body1 {
    font-size: 1rem;
    line-height: 1.5rem;
    letter-spacing: 0.5px;
}

@mixin button-white {
    color : $color-text-white;
    border : $border-button;         
    border-radius: $border-radius-button;
    background-color: $color-surface-white;
}

@mixin button-hover-black {
    background-color: $color-hover;
    color:$color-text-white;
    border: $border-button-hover;
    box-shadow: $shadow-button-hover;
}

// 전역으로 사용하는 그림자 스타일
$shadow-bottom: 0 4px 4px rgba(0, 0, 0, 0.25);
$shadow-center: 0 0 5px rgba(0, 0, 0, 0.25);
$shadow-dropdown: 1px 2px 4px rgba(0, 0, 0, 0.25);
$shadow-button: 1px 3px 3px rgba(0, 0, 0, 1);
$shadow-button-hover: 1px 3px 3px rgba(204, 207, 209, 1);

// 전역으로 사용하는 버튼 스타일
$border-button: 3px solid #000;
$border-button-hover: 3px solid #e5e6e8;
$border-radius-button: 1.25rem;

 

1. SMACSS 방식으로 기본 html base를 reset한다. 아래는 내가 사용한 reset.scss 파일이다.

  * Reset 하는 이유

    - 각 브라우저마다 설정 되어있는 기본 스타일이 다 다르기 때문에, CSS를 작성할 때 초기화를 시킨다. 즉, 기본적으로 태그가 가지고 있는 간격, border, 색상 등을 통일 시키는 작업이다.

// reset.scss 
@import "./mixin.scss";

*{
    margin: 0;
    padding: 0;
    border: 0;
}
/* HTML5 display-role reset for older browsers */
article, aside, details,figcaption,figure,footer,header,hgroup,menu,nav,section {
  display: block;
}

ol, ul {
  list-style-type: none;
}

button{
    cursor: pointer;
}

a {
  text-decoration: none; 
  outline: none;
}

 

2. scss(sass)를 사용해 클래스 이름은 BEM 방식으로 짓는다.

 - 기존 BEM 방식은 modifier에 block 혹은 element의 모양(color, size), 상태(position, disabled, checked)를 정의 하는데, 이러한 부분은 취하지 않았다. 단지 이름을 지을 때 '__'와 '--"로 구분하는 방식만 가져와서 사용했다.

ex) 최상위-블럭__블럭-내-요소--개별요소

@import "../../../base/variable.scss";

.MyReview{
    min-width : 550px;
    max-height: 40rem;
    @include overflow-y-scroll();
}
.MyReview-card__container{...}

.MyReview-card__header{           
    .MyReview-card__header--col1{...}
    .MyReview-card__header--col2{
        .MyReview-card__header--row1{...}
    }
}

.MyReview-card__content{
    .MyReview-card__rating{...}
}

 

3. OOCSS를 기반으로 스타일을 적용한다.

- 아래와 같이, 두 container 내 button은 모두 동일한 mixin 'button-white'를 사용하지만 필요한 스타일링을 유동적으로 추가해서 사용한다.

.ReviewModal-btn__container{
    .ReviewModal-submitBtn{
        @include button-white;
        padding : 7px 20px;
        
        &:hover{
            @include button-hover-gray;
        }
    }
}

.userInfo-btn__container{
    button{
        @include button-white(); 
        cursor: pointer;  
        padding: 5px 0;
        width : 110px;
        font-size: 15px;
        margin : 5px;
        
        &:hover {
            @include button-hover-black();
        }
    }
}

 

 

참고

https://mine-it-record.tistory.com/655

https://abcdqbbq.tistory.com/75

728x90