Web/HTML&CSS

[BEM] BEM method0logy

은서파 2022. 9. 22. 17:52

SCSS를 정리하면서 CSS 코드를 보다보니 클래스 이름을 매우 번거롭게 _를 두개씩 써가면서 작성해놨길래 이상하다 싶어서 찾아봤는데 BEM이라는 방법론에 입각한 것이었다! 

이번 포스트에서는 BEM 방법론에 대해서 살펴보자.

 

CSS에도 바람이 많이 분다~

 

CSS 개발을 위한 방법론

몇년 전이지만 ITWORLD라는 곳에서 프로그래머가 가장 힘들어하는 일을 개발자 대상으로 조사한 적이 있다. 그 결과를 보면 실소를 금할 수 없지만 정말 공감이 간다. 무려 "이름짓기"다.

출처: ITWORLD(https://www.itworld.co.kr/tags/3926/코드/85296)

CSS에서도 마찬가지이다. 요소들을 구분하기 위해 id와 class를 남발하다보면 어디에 무엇이 있었는지 알기 너무 힘들어진다. 다른 사람과의 작업 결과물을 합치다 보면 그냥 또다른 쓰레기를 새로 만드는게 훨씬 빠르겠다는 생각이 들고 어느덧 이를 실행에 옮기고 있는 나를 발견한다.

그래서 CSS에서도 이를 위한 여러가지 방법론이 등장했다. OOCSS, SMACSS, SUITCSS, Atomic 등 종류도 다양하다.이 와중에 BEM이라는 녀석도 있는데 이들은 다 CSS를 구조화하려고 노력하고 있다.

여기서는 BEM에 대해서 살펴보자.

 

BEM

https://en.bem.info/methodology/

 

Methodology / BEM

Methodology

en.bem.info

BEM은 Block, Element, Modifier의 약자로 웹 페이지를 만들기 위한 컴포넌트 기반의 접근법이다. 

https://www.smashingmagazine.com/2012/04/a-new-front-end-methodology-bem/

 

BEM: A New Front-End Methodology — Smashing Magazine

_This article is the sixth in our new series that introduces the latest, useful and freely available tools and techniques, developed and released by active members of the Web design community. The first article covered [PrefixFree](https://www.smashingmaga

www.smashingmagazine.com

위 사이트는 차근차근 적용하는 방법을 더 잘 보여준다. 

BEM을 도입하면 사용자 인터페이스를 재사용 가능한 독립된 블록으로 나눠서 복잡한 UI를 쉽고 빠르게 개발할 수 있게 함으로써 기존의 copy-paste로 일관하던 CSS 작업을 개선할 수 있다.

 

어떻게?

컴포넌트 기반으로 개발한다고 이야기 했지만 어차피 CSS는 자바같은 언어가 아니기 때문에 거창한 방법이 있는것은 아니고 일정한 명명 규칙을 이용해서 컴포넌트를 구분하는 형태로 작성하면 된다.

BEM의 기본 명명 규칙은 다음과 같다.

  • 화면 요소를 지칭하기 위해 ID를 사용하지 않고 class만을 사용한다. 따라서 아래 이름들은 모두 class 이름이다.
  • "어떻게 보일것인가?" 보다는 의미론적으로 "어떤 목적인가?"에 따라 명명한다.
  • 개별 요소의 이름은 kebab case를 사용한다.(search-form)
  • B, E, M을 구성하기 위해서는 언드스코어 2개(__) 또는 1개(_)를 사용한다.

그럼 Block, Element, Modifier의 의미와 명명 규칙을 이용해 어떻게 B,E,M을 나타내는지 살펴보자.

 

Block

 

정의

Block은 기능적으로 독립된 페이지 컴포넌트로 필요한 곳에서 재사용이 가능하다. 

한눈에 봐도 어떤 것이 블록인지 알수 있을것 같다. 중요한 점은 block은 내부에만 신경써야지 외부에 영향을 주지 말아야 한다. 외부에 영향을 주는 요소는 margin, position 속성들이 있다.

명명 규칙

block의 이름은 상태(빨간색)이 아닌 목적(에러표시)을 나타내야 한다. 즉 의미를 담아야 한다. 다음은 두 개의 block이 있는데 하나는 error로 어떤 경우에 사용되는지를 표현하고 다른 하나는 단순히 빨간색을 의미한다. 화면에 보이는 것은 동일하겠지만 의미가 있고 없고의 차이가 있다.

<!-- 오류가 있는 블록: 의미 전달 -->
<div class="error"></div>

<!-- 빨간 블록: 강조? 단순 색상? -->
<div class="red"></div>
.red{  color: red;}

.error{  color: red;}

block의 이름은 하나의 프로젝트 내에서 유일해야 한다.

 

block의 중첩

block은 간단히 하나의 요소로만 작성될 수도 있고 다른 block을 중첩해서 가질 수도 있다.

<header class="header">
  <div class="logo"></div>
  <form class="search-form"></form>
</header>

 

Element

 

정의

Element는 Block을 구성하는 부품들로 block에서 분리돼서 단독적으로 사용되지는 않는다.

 

참고로 모든 block이 element를 가져야 하는 것은 아니다. 간단한 block은 충분히 element없이 사용될 수 있다.

 

명명 규칙

element의 이름 역시 block과 마찬가지로 상태를 나타내지 않고 목적을 나타내야 한다. 그리고 element는 반드시 특정 block과 연결되어야 하는데 이때 under score 2개를 사용한다. 즉 block-name__element-name의 형태가 된다. element의 이름은 하나의 block 내에서 unique 해야 한다.

<form class="search-form">
  <input class="search-form__input">
  <button class="search-form__button">Search</button>
</form>

위의 코드는 search-form이라는 block 내부에 search-form__input, search-form__button 2개의 element를 작성한 예이다.

element의 이름을 구성할 때 block의 이름은 element의 namespace 역할이 된다. 만약 아래의 좌측과 같이 class 이름을 준다면 우측의 형태로 css를 작업하게 될 것이다.

<div class="block">
  <div class="block__elem1">
    <div class="block__elem2">
      <div class="block__elem3"></div>
    </div>
  </div>
</div>
.block {}

.block__elem1 {}

.block__elem2 {}

.block__elem3 {}
이렇게 작성된 코드를 보니 SCSS로 css 작업을 해야겠다는 생각이 팍! 들긴 하다.

이렇게 코드를 작성하게 된다면 아래와 같이 DOM 구조가 변경된다고 하더라도 CSS 부분의 변경이 전혀 발생하지 않는다.

<div class="block">
  <div class="block__elem1">
    <div class="block__elem2"></div>
  </div>
  <div class="block__elem3"></div>
</div>

 

기타 주의 사항

 

첫 번째 주의 사항은 중첩 규칙이다.

element 역시 block과 마찬가지로 중첩될 수 있지만 element 끼리 계층을 가지면 안된다. 즉 block-name__element1__element2는 부적절하다. 아래는 search-form의 content라는 element 내부에 input, button이라는 element를 중첩한 좋은 예이다.

<form class="search-form">
  <div class="search-form__content">
    <input class="search-form__input">
    <button class="search-form__button">Search</button>
  </div>
</form>

하지만 아래처럼 element의 이름을 계속 덧붙여 작성하면 안된다.

<form class="search-form">
  <div class="search-form__content">
    <input class="search-form__content__input">
    <button class="search-form__content__button">Search</button>
  </div>
</form>

 

두 번째 주의 사항은 소속 규칙이다.

element는 block의 부품이기 때문에 block에서 분리돼서 사용하면 안된다.  즉 아래처럼 search-form 내부에 input, button을 정의했다면 그들은 search-form만의 부품이다.

<form class="search-form">
  <input class="search-form__input">
  <button class="search-form__button">Search</button>
</form>

다음처럼 search-form을 벗어나서 사용하면 안된다.

<form class="search-form">  </form>
<!-- search-form 소속인데 외부에 분리되어 있음 -->
<input class="search-form__input">
<!-- search-form 소속인데 외부에 분리되어 있음 -->
<button class="search-form__button">Search</button>

 

 

Modifier

 

정의

modifier는 block이나 modifier의 모양, 상태, 동작을 나타내며 반드시 block 이나 element에 연결되어 사용된다. 

  • 어떤 모양인가? size나 theme 지정 등
  • 상태가 어떤가? disabled, focused, checked 등 상태
  • 동작이 어떤가? direction이 top-left로 이동

 

명명 규칙

modifier를 block, element와 연결할 때는 underscore 1개를 사용하는데 값의 타입에 따라서 두 가지 형태로 사용된다.

  • boolean 타입: disabled나 checked 처럼 속성의 존재 유무만 중요하고 값이 무관한 경우
    • block-name_modifier-name
    • block-name__element-name_modifier-name
  • map(key-value) 타입: modifier의 값이 중요한 경우로 modifier 이름 뒤에 다시 underscore를 이용해 value를 추가한다.
    • block-name_modifier-name_modifier-value
    • block-name__element-name_modifier-name_modifier-value

 

또한 하나의 요소에 동시에 동일한 modifier에 여러 값을 설정하면 안된다.

<!-- `search-form` 블록 중에서 `theme` 수식어의 값이 `islands` 인 경우 -->
<form class="search-form 
             search-form_theme_islands">
  <input class="search-form__input" />
  <!-- The `button` element has the `size` modifier with the value `m` -->
  <button class="search-form__button 
                 search-form__button_size_m">Search</button>
  <!-- 동시에 동일한 modifier에 여러 값을 설정 불가 -->
  <button class="search-form__button 
                 search-form__button_size_s 
                 search-form__button_size_m">Search</button>
</form>

위의 경우처럼 만약 search-form block에 size modifier가 한번은 s로 또 한번은 m으로 설정된다면 혼돈만 야기할 뿐이다.

 

Mix

 

앞서 block의 정의에 대해서 설명하면서 어디든 배치될 수 있어야 하기 때문에 block에는 margin이나 position 속성을 주지 말라고 했는데 그럼 어떻게 이런 속성을 조절해야할까?

당연히 또는 다행하게도 하나의 DOM 노드에 서로 다른 BEM entity를 적용할 수 있다. 아래의 예를 살펴보자.

<!-- 전체적으로는 `header` 블록 -->
<div class="header">
  <!-- 다음의 div는 `search-form` 블록과 `header` 블록의 `search-form` 엘리먼트가 혼용됨 -->
  <div class="search-form 
              header__search-form">
  </div>
</div>

header block 내부의 div는 search-form과 header__search-form을 가지고 있다. 하나는 block이고 하나는 element이다. 이렇게 block과 element를 mix 해서 구성하면 header__search-form에 margin, position 속성을 부여하고 search-form에는 부여하지 않는다.

그럼 search-form블록은 외부에 영향을 주는 요소가 없으므로 독립적으로 동작할 수 있게 된다.

 

 

그래도 좀 막막한 면이 있는데 좋은 워크샵을 올리신 분이 있어서 참조하면 좋을듯 하다.

https://nykim.work/15

 

[CSS 방법론] BEM 방식

오늘은 CSS 방법론을 다뤄보겠습니다 ;-) 말이 거창하긴 한데 쉽게 풀어쓰면 'CSS 클래스네임을 어떻게 지으면 좋을지' 고민해보는 거죠. 방법론에는 여러 가지가 있는데, 최근 BEM을 실무에 도입하

nykim.work