알기 쉬운 BEM 알아보기(CSS)

block__element_modifier모듈 기반의 방법론 (+mix)

공식 문서(https://en.bem.info)에서는 BEM을 이렇게 설명한다.

BEM(Block, Element, Modifier)는 컴포넌트 기반 접근 방식이다. 기본 아이디어는 사용자 인터페이스(UI)를 독립된 블록으로 나누는 것이다. 이를 통해 복잡한 UI도 쉽고 빠르게 개발할 수 있으며, 기존 코드를 재사용할 수 있는 장점이 있다.

BEM은 모듈을 Block, Element, Modifier 단위로 분해하며, 각 앞 글자를 따서 BEM으로 부른다. BEM은 러시아 Yandex사에서 개발한 방법론으로 실제로 이 방법에 기반한 CSS 설계 기법이 널리 사용되고 있지만 BEM은 CSS에만 국한되지는 않는다.

그렇다면 BEM의 공통 규칙과 모듈의 개별 규칙을 알아보자.

CSS style을 위한 BEM 공통 규칙

– ID 셀렉터와 태그 셀렉터를 사용하지 않는다.
– 네스팅된 셀렉터 사용을 최소화한다.
– CSS 클래스 네이밍 컨벤션을 사용하여 이름 충돌을 피하고 셀렉터의 목적을 명확하게 한다.
– Blocks, Elements, Modifiers를 구분하여 사용한다.
– Blocks를 재사용한다.

BEM의 네이밍 규칙

모두 소문자를 사용하며, 두 단어는 하이픈(-)으로 연결한다.

Blockblock, block-two
Elementblock 이름을 상속받아 언더스코어 두 개(__)로 연결block__element, block-two__element-two
ModifierBlock/Element 이름 상속받아 언더스코어 하나(_)로 연결. 키-값 쌍은 스네이크 케이스(_)를 사용.
block_modifier, block__element_modifier, element_key_value
Modifier
(MindBEMding)
Block/Element 이름 상속받아 하이픈 두개(–)로 연결. 키-값에서 키는 생략가능.block–modifier, element–value

모듈의 개별 규칙

논리적, 기능적으로 독립되어 재사용이 가능한 모듈로 정의한다. 클래스 이름은 ‘형태’가 아니라 ‘목적, 용도’를 나타내야 한다.

EX) head, logo, menu (O)
blue-button, small-title (X)

block은 다른 block 내부에서도 사용할 수 있으며, modifier를 통해 위치나 모양을 변경할 수 있다.

그림 – block의 중첩(출처: en.bem.info)

Element

block을 구성하며 block 내부에서만 사용하는 모듈로 정의한다. 예를 들어 menu__item은 menu라는 block을 구성하며 menu 밖에서는 사용할 수 없으므로 element가 된다.

그림 – block을 구성하는 element(출처:en.bem.info)


element 클래스 이름도 block과 동일하게 ‘형태’가 아니라 ‘용도’를 나타내야 한다.
element는 선택적 요소이므로 사용하지 않아도 상관없지만 element 이름을 중첩하여 사용하지 않는다.

EX) block__element__element, menu__button__icon (X)

Modifier

block/element의 모양이나 동작을 정의하는 모듈이다.
modifier는 선택적 요소이므로 사용하지 않아도 상관없지만 단독으로는 사용할 수 없고 block/element와 함께 사용해야 한다.
같은 block이라도 modifier에 따라 모양이 변할 수 있으며, modifier는 런타임이나 block에 따라 바뀔 수 있다.
modifier 클래스 이름은 형태(크기, 색, 상태, 동작)을 나타내야 한다. 형태로 나누면 boolean, key-value 두 가지가 있으며, 각각의 예는 다음과 같다.

– boolean : ex) active, disabled
– key-value : ex) color_red, line_doubled (스네이크 케이스로 표현)

modifier의 수는 제한이 없으므로 여러 개를 붙일 수 있지만 동일한 스타일은 중복하지 않는다.

MIX

mix는 하나의 요소에 역할이 다른 여러 클래스를 사용하는 기법이다.
장점은 코드를 복사하지 않고 기존의 스타일과 조합해서 새로운 모듈로 사용할 수 있다.
block+element, block+block, element+element 등의 mix를 사용할 수 있으며, 샘플 코드를 통해 확인할 수 있다.

<!-- HTML -->
<div class="header">
  <div class="logo header__logo"></div>
</div>

/* CSS */
.logo {
  width:100px;
  height:50px;
}
.header__logo {
  margin: 12px;
}

다른 요소와 관계된 레이아웃과 관련된 부분은 mix, block 내부의 문제는 modifier를 사용하는 것이 좋다.
mix는 독립성과 재사용성이 높은 상태를 유지하는 장점이 있다.


참고
https://en.bem.info
– 다양한 예제로 배우는 CSS 설계 실전 가이드


target으로 부모 요소, 자식 요소 접근하기(자바스크립트,target)

event와 target으로 부모 요소와 자식 요소에 접근하는 방법

자바스크립트는 보통 이벤트와 target을 통해 요소에 접근합니다.

그리고 <div>와 같은 요소를 중첩해서 사용하다 보면 이벤트 발생 시 부모 요소 또는 자식 요소에 접근이 필요한 경우가 있습니다.

예를 들면 다음과 같이 id=2인 div 요소의 이벤트가 발생했을 때 부모 div 요소 id=1에 접근하고 싶은 경우가 있습니다.

const check = (event) => {
    console.log(event.target.id);
}

<body>
  <div id=1>    
        <div id=2 onClick=(check(event))>click
            <div id=3></div>
            <div id=4></div>
        </div>
  </div>
</body>

그렇다면 어떻게 접근을 해야 할까요?

1. 이벤트를 통해 부모 요소 속성 접근하기

현재 이벤트가 발생한 요소를 감싸고 있는 부모 요소에 접근하기 위해서는 target과 parentElement를 사용하여 접근할 수 있습니다.

부모 요소의 id 속성에 접근하는 코드는 다음과 같습니다.

const check = (event) => {
    console.log(event.target.parentElement.id);    // 1
}

<body>
  <div id=1>    
        <div id=2 onClick=(check(event))>click
            <div id=3></div>
            <div id=4></div>
        </div>
  </div>
</body>

실행해보면 콘솔에 1을 프린트합니다.

만약 id가 아닌 다른 속성에 접근하고 싶은 경우에는 id 대신 해당 속성명을 그대로 사용하면 됩니다.

구조를 확인하고 싶은 경우에는 console.log(event.target.parentElement)을 입력하여 확인할 수 있습니다.

console.log(event.target.parentElement)결과

현재 요소를 기점으로 접근을 진행하므로 target을 사용하며, 이벤트가 기점이 되는 경우 currentTarget를 사용합니다.

관련 포스트 : target, currentTarget 차이가 뭘까?


2. 이벤트를 통해 자식 요소 속성 접근하기

위 코드에서 보면 <div id=2>의 자식 요소는 <div id=3>과 <div id=4>입니다.

자식 요소는 target과 children을 통해 접근하는데요.

부모 요소는 하나밖에 없지만 자식 요소는 여럿 존재할 수 있습니다.

따라서 자식 요소는 인덱스를 통해 접근해야 하며, 리액트 코드를 통해 자식 요소의 구조를 살펴보겠습니다.

const accessChildren = () => {

const check = (e) => {
    console.log(e.target.children)
}

    return (
        <div id={1}>    
            <div id={2} onClick={check}>click
                <div id={3}>I'm a first child</div>
                <div id={4}></div>
            </div>
        </div>
    )
}

export default accessChildren;

콘솔에 찍히는 children의 구조는 다음과 같이 두 개의 object를 갖습니다.

따라서 첫 번째 자식 요소의 id에 접근하기 위해서는 다음과 같이 사용하면 됩니다.

const check = (e) => {
    console.log(typeof(e.target.children[0].id)) // 3
}

그렇다면 첫 번째 자식 요소의 텍스트인 I’m a first child는 어떻게 접근할까요?

간단하게 위 children 구조를 열어서 살펴보면 답이 나옵니다.

textContent를 사용해서 접근할 수 있습니다. (innerHTML은 XSS에 취약)

const check = (e) => {
    console.log(e.target.children[0].textContent) //I'm a first child
}


특정 요소가 속한 속성 전체(예를 들면 e.target)를 콘솔에 출력하면 다양한 하위 속성을 확인할 수 있으므로 원하는 속성을 찾거나 에러를 해결할 때 유용하게 사용할 수 있습니다.

target과 currentTarget, 차이가 뭘까?(feat.자바스크립트)

target과 currentTarget을 상황에 맞게 처리하기

자바스크립트에서 이벤트를 다룰 때 targetcurrentTarget의 프로퍼티를 사용해 요소에 접근이 가능합니다.

그럼 targetcurrentTarget의 차이는 무엇일까요?

1. target과 currentTarget

간단하게 설명하면 target은 이벤트가 발생한 바로 그 요소를 직접 가리키고 currentTarget이벤트 리스너(EventListener)를 가진 요소를 가리킵니다.

‘백문불여일코드(百聞不如一code)’이므로 코드를 통해 무엇을 의미하는지 확인해 보겠습니다.

<style>
  .upper{
    background:gold;
    width:80px;
    text-align:center;
  } 
  .lower{
    background:pink;
    width:50px;
  }
</style>

<script>
  function clicked(event){
    alert(event.target.id+" clicked");
  }
</script>

<div class="upper" onClick="clicked(event)" id="div">
  <span class="lower" id="span">                  
    span
  </span>
</div>

위의 코드를 통해 각각이 의미하는 바를 확인해 보겠습니다.

먼저 위 코드를 실행하면 다음 그림과 같은 결과가 나타납니다.

여기서 노랑은 div, 핑크는 span이며, 핑크가 노란색 위에 앉아있는 것이라고 볼 수 있습니다.

그런데 onClick 이벤트는 div에서 설정했지만 노랑을 눌러도, 핑크를 눌러도 모두 이벤트가 발생합니다.

div에만 이벤트를 설정했는데 왜 자식인 span도 이벤트를 상속 받는 것일까요?

이는 이벤트 버블링이벤트 캡쳐와 관련이 있으며, 관련 내용은 아래 포스트에서 확인해 보겠습니다.

이벤트 버블링과 이벤트 캡쳐 포스트

이벤트 발생에 따른 target은 다음과 같습니다.

– 핑크 부분을 클릭
target -> 핑크 (핑크를 눌렀으므로 핑크가 이벤트 발생 시점이 됨)
currentTarget -> 노랑 (onClick 이벤트는 노랑이 갖고 있음)

노란 부분을 클릭
target -> 노랑(노랑을 눌렀으므로 노랑이 이벤트 발생 시점이 됨)
currentTarget -> 노랑(onClick 이벤트는 노랑이 갖고 있음)

따라서 노랑을 누르면 ‘div clicked’, 핑크를 누르면 ‘span clicked’ 알림창을 띄웁니다.

만약 핑크를 눌러도 이벤트를 가진 노랑의 속성에 접근하고 싶다면 currentTarget과 getAttribute를 사용하면 됩니다.

<style>
  .upper{
    background:gold;
    width:80px;
    text-align:center;
  } 
  .lower{
    background:pink;
    width:50px;
  }
</style>

<script>
  function clicked(event){
    alert(event.currentTarget.getAttribute('id')+" clicked");
  }
</script>

<div class="upper" onClick="clicked(event)" id="div">
  <span class="lower" id="span">                  
    span
  </span>
</div>

위 코드는 핑크, 노랑 둘 다 이벤트를 가진 노랑 속성에만 접근합니다.

target은 누른 바로 그 것, currentTarget은 이벤트를 실행하는 바로 그 것으로 이해하면 될 것 같습니다.



targetcurrentTarget이벤트핸들링이벤트 캡쳐와 함께 이벤트 발생 관련하여 중요한 속성과 개념입니다.

백문불여일코드(code)이므로 코드를 통해 확인하고 연습하는 습관을 기르면 어제의 정보가 내일의 나의 무기가 될 수 있을 것이라고 생각합니다.