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의 네이밍 규칙
모두 소문자를 사용하며, 두 단어는 하이픈(-)으로 연결한다.
Block
–
block, block-two
Element
block 이름을 상속받아 언더스코어 두 개(__)로 연결
block__element, block-two__element-two
Modifier
Block/Element 이름 상속받아 언더스코어 하나(_)로 연결. 키-값 쌍은 스네이크 케이스(_)를 사용.
block/element의 모양이나 동작을 정의하는 모듈이다. modifier는 선택적 요소이므로 사용하지 않아도 상관없지만 단독으로는 사용할 수 없고 block/element와 함께 사용해야 한다. 같은 block이라도 modifier에 따라 모양이 변할 수 있으며, modifier는 런타임이나 block에 따라 바뀔 수 있다. modifier 클래스 이름은 형태(크기, 색, 상태, 동작)을 나타내야 한다. 형태로 나누면 boolean, key-value 두 가지가 있으며, 각각의 예는 다음과 같다.
modifier의 수는 제한이 없으므로 여러 개를 붙일 수 있지만 동일한 스타일은 중복하지 않는다.
MIX
mix는 하나의 요소에 역할이 다른 여러 클래스를 사용하는 기법이다. 장점은 코드를 복사하지 않고 기존의 스타일과 조합해서 새로운 모듈로 사용할 수 있다. block+element, block+block, element+element 등의 mix를 사용할 수 있으며, 샘플 코드를 통해 확인할 수 있다.
CSS의 여러 설계 기법들도 결국에는 다음 여덟 가지 리스트에 속한다고 하니 핵심을 관통하는 포인트라고 볼 수 있다.
1. 특성에 따른 CSS 분류 2. 느슨한 스타일의 결합 3. 적당한 영향 범위 4. 특정 컨텍스트에 대한 적절한 의존도 5. 적절한 명시도(specificity) 6. 영향 범위가 유추 가능한 클래스 네임 7. 형태, 기능, 역할의 유추가 가능한 클래스 네임 8. 확장 용이성
1. 특성에 따른 CSS 분류
첫 번째는 CSS 역할이나 특성에 따라 분류하는 것이다.
베이스 그룹 : 사이트에서 베이스가 되거나 공통으로 적용되는 사항 등 레이아웃 그룹 : 헤더(header), 푸터(footer), 콘텐츠(content) 영역 등 모듈 그룹 : 재사용되는 모듈 등
모듈 자체에는 레이아웃과 관련된 부분은 설정하지 않는 것이 좋다. 모듈은 자신의 레이아웃에는 관여하지 않고 해당 모듈 자체의 역할 및 자녀 요소의 스타일링에만 관심을 가져야 한다. 여기서 레이아웃과 관련된 부분이란 다음과 같다.
두 항목 중 형태나 기능, 역할의 유추가 쉬운 쪽이 어디인지는 분명하다. 각 모듈의 기능이나 역할에 맞춰 이름을 붙이는 것이 매우 중요하다. 특히 작은 규모의 코드라면 title1, title2와 같이 네임을 대충 지정하기 쉽다. 하지만 규모란 언제 어떤 방식으로 커질지 모르므로 항상 습관적으로 이름에서 형태, 기능, 역할을 유추할 수 있고 구체성과 범용성의 균형을 맞추도록 하는 노력이 필요하다.
8. 확장 용이성
확장성이란 결국 기능의 추가나 유지와 연결되는 부분으로, 항상 가능성을 열어두어야 한다.
확장이 쉽도록 클래스를 설계하거나 추가하는 클래스에는 기능, 역할에 따라 적절한 상세도와 영향 범위를 갖도록 한다.
확장이 쉬운 클래스란 멀티 클래스를 사용하는 것으로, 하나의 클래스에는 보편적인 속성, 추가 클래스에는 특정 속성을 추가하여 덮어쓰기를 통해 여러 클래스로 스타일을 지정하는 방식이다.
이를 통해 HTML은 복잡해지지만 CSS는 매우 간단해지는 트레이드오프가 발생하기도 한다. 하지만 멀티 클래스의 이점은 불규칙한 상황에서도 클래스 하나만으로 원하는 작업을 완료할 수 있는 간결함이다.
결국 모든 항목은 별개의 사항이 아니라 다 연결되어 있음을 알 수 있고, 작은 수정 하나가 큰 나비효과를 불러올 수도 있다는 점을 명심해야 한다.
매번 하나하나 체크해가면서 코드를 작성하기보다는 작성 방식이 고민되거나 자율성이 주어졌을 때, 해당 리스트를 떠올리면서 작성하다보면 생각보다도 더 견고한 구조를 만들 수 있을 것이다.
reflow(또는 layout)는 render tree 생성 후 요소의 크기와 위치를 계산하는 단계이다. 초기 렌더링 시 최초로 발생하고 요소의 크기나 위치 변경, 요소의 추가나 삭제 등 레이아웃이 변경될 때마다 reflow가 다시 발생한다.
reflow는 자식과 부모 그리고 요소 자신에게도 영향을 미치므로 성능에 영향을 주며, 레이아웃을 계산하는 reflow 후 가시적인 요소를 생성하는 paint 단계가 발생한다.
reflow가 발생하는 구체적인 상황은 다음과 같다. (https://www.geeksforgeeks.org/what-is-dom-reflow/).
Reflow 발생 원인
– DOM에 요소 삽입, 제거, 업데이트 – 페이지에서 수정 작업 발생(Ex. input 박스 텍스트 수정) – DOM의 요소 이동 – DOM의 요소 애니메이션 효과 – offsetHeight 또는 getComputedStyle()를 사용한 요소 계산 – CSS 스타일 변경 – 요소의 클래스 이름 변경 – 스타일시트 추가 또는 제거 – 윈도우 리사이징 – 웹 페이지 스크롤 – 폰트 변경 – CSS 슈도 클래스(의사 클래스) 활성화(Ex. :hover) – 요소에 style 어트리뷰트 설정
reflow 단계에서 많은 연산이 발생하고 repaint, composite 단계도 이어서 발생하므로 성능에 영향을 줄 수 밖에 없다. 따라서 페이지를 구성하는 방식, 성능과 애니메이션의 타협 등을 통해 reflow를 최소화하는 방법을 찾아야 한다.
기본적으로 reflow를 줄일 수 있는 방법은 다음과 같다.
Reflow 줄이는 방법
– 여러 개의 인라인 스타일을 지정하지 않고 개별 스타일 설정을 최소화하기 – 요소에 클래스명을 사용하고 DOM 트리에서 최대한 하부에서 사용하기 – 새로운 스타일을 추가하기 전에 DOM에서 요소를 제거한 뒤 다시 추가하기 – 스타일을 위한 잦은 연산 피하기 – 애니메이션은 fixed 또는 absolute로 지정하기 – 가능한 테이블 레이아웃보다 블록 레이아웃을 사용하기 – 고정된 크기로 테이블 생성하기 – 클래스를 통해 스타일 변경하기 – 테이블 레이아웃을 fixed로 지정하기
REPAINT
repaint(또는 redraw)는 레이아웃이 아닌, 요소의 가시적인 부분이 변경되면 발생하는 단계이다. 요소의 visibility, background, outline 등이 변경될 때마다 repaint가 발생하지만 해당 속성은 레이아웃에 영향을 주지 않으므로 reflow는 발생하지 않는다.
repaint는 render tree를 레이어별로 나누어 처리하므로, repaint 발생 시 해당 레이어만 수정할 수 있는 장점이 있다.
Reflow와 Repaint는 stacking context와도 관련이 있는데, stacking context는 간단하게 3차원인 z축을 의미하는 것이다.
여러 요소가 하나의 레이어로 묶이게 되면 차지하는 메모리를 줄일 수 있지만 묶인 요소 중 하나의 요소에서 변경이 생기면 해당 레이어에 있는 요소를 모두 다시 그려야 하는 상황이 발생한다.
따라서 변경이 생길 가능성이 있는 요소를 하나의 레이어로 만들어서 관리하면 해당 레이어만 다시 그리면 되기 때문에 효율적으로 리렌더링을 할 수 있다.
조건 중 ‘opacity < 1’이 있는데, opacity 값이 0~0.99인 상황에서만 레이어가 별도로 생성된다는 의미이다.
opacity가 1일 때는 reflow가 발생할 수 있는데 이는 opacity:1인 경우 다른 요소와 하나의 레이어를 이루기 때문에 발생한다고 볼 수 있다.
Composite
상황에 따라 Reflow, Repaint가 발생하지 않는 속성으로 transform, opacity 등이 있는데 이 속성은 렌더링의 마지막 단계인 composite만 실행된다.
해당 속성은 CPU가 아닌 GPU에서 작업을 수행하는 graphic layer를 사용하기 때문인데 composite 단계에서 layer를 병합한다.
GPU는 CPU에 비해 연산이 빠르고 이미지와 애니메이션의 보정 기능도 가지고 있으므로 이미지나 애니메이션 처리에 좋은 대안이 될 수 있다.
GPU를 사용하는 조건은 다음과 같다.
GPU 사용 조건
– 3D transforms 사용(translate3d, translateZ 등) – <video>, <canvas>, <iframe> 요소 사용 – transform, opacity를 사용하는 애니메이션 – position:fixed 요소 – will-change 사용 – filter 사용
GPU의 장점은 빠른 연산이지만 메모리를 많이 차지하는 단점도 있으므로 적정선에서 사용해야 쾌적한 환경을 만들 수 있다.
display는 요소를 보여주고 숨기거나 위치를 설정하기 위한 옵션으로, display만 잘 알아도 레이아웃의 많은 부분을 해결할 수 있습니다.
이번 포스팅에서 display의 속성인 inline, flex, inline-flex, block, inline-block, grid, inline-grid, none의 구현을 통해 각각의 표시 방식을 알아보도록 하겠습니다.
알아보기 편하도록 배경은 블랙, 요소는 각각 레드, 오렌지, 그린으로 표현하겠습니다.
inline : inline으로 표시(width는 요소 크기) block : block으로 표시 inline-block : inline + block으로 표시 flex : 자식 요소를 모두 inline-block으로 설정 inline-flex : inline + flex(전체 width는 자식 width의 합) grid : flex처럼 한 방향이 아닌 가로, 세로로 설정 가능한 레이아웃 inline-grid : inline + grid(전체 width는 자식 width의 합) none : 표시하지 않음(공간을 차지하지 않음)