원문은 ‘Can CSS Be Too Modular?’다. 각주는 모두 역자가 단 것이다.


나는 “CSS가 지나치게 모듈화될 수도 있나요?” 하는 식의 질문을 자주 받는다. 최근에 이런 류의 이메일을 받았다. 아래를 보자.

해리,

우선 inuitcss를 만들어 줘서 고맙다는 이야기를 하고 싶습니다. 내가 가르치는 객체지향 CSS 교실에서 inuitcss에 대해 이야기를 하곤 합니다. 나는 아주 철학적인 질문이 있습니다. 경험상, 내가 만드는 웹사이트는 지나치게 추상화됩니다. 그리고 심심찮게 코드가 중복됩니다. 이건 분명 객체가 추상화돼 그런 것입니다.

예컨대, 나는 padding을 조절하려고 .box라는 간단한 추상 객체를 만들었습니다. 그리고 나서 나는 알림 메시지와 인라인 레이블과 인풋 들을 만들고 크기에 관한 추상 요소들이 같다는 것을 알았습니다. 때문에, 나는 자식 요소를 가리키거나 해당 객체를 특정(SPECIFIC1)하기 전까지는 추상화를 피하는 쪽으로 바뀌었습니다. 아톰(atom2)들과 일하게 됐죠. 그래서 이런 것들이,

.box { }
.box--xs { padding: 6px; }
.box--s { padding: 12px; }
.box--m { padding: 24px; }
.box--l { padding: 36px; }
.box--xl { padding: 48px; }
  
.alert { }
.alert--xs { padding: 6px; }
.alert--s { padding: 12px; }
.alert--m { padding: 24px; }
.alert--l { padding: 36px; }
.alert--xl { padding: 48px; }
  
.label { }
.label--xs { padding: 6px; }
.label--s { padding: 12px; }
.label--m { padding: 24px; }
.label--l { padding: 36px; }
.label--xl { padding: 48px; }

pa(padding-all)라 부르는 atom이 됐습니다.

.pa--xs { padding: 6px; }
.pa--s { padding: 12px; }
.pa--m { padding: 24px; }
.pa-l { padding: 36px; }
.pa--xl { padding: 48px; }

이제 이건 어떤 요소에도 적용할 수 있고, 어떤 주요 객체에도 종속되지 않습니다.

나는 스매싱 매거진에서 이 테크닉을 다루는 멋진 글을 봤습니다. 야후가 이걸 구현했고요. 아톰들은 정말이지 멋지게 캐시를 구현할 수 있는 저레벨 도구입니다. 나는 추상 객체들을 구현하는 때는 아래와 같은 경우뿐이라고 봅니다.

  1. 추상 객체가 아톰으로 구현할 수 없는 특정 객체를 가리킬 때.
  2. 추상 객체를 구현하기 위해 너무 많은 아톰이 필요할 때(당신이 만든 .btn클래스 같은).
  3. 추상화는 객체를 특정(SPECIPIC)하는데 아톰은 그렇지 않을 때.

이것은 모듈화한 디자인을 대체하는 것이라기 보다는 완성하는 것입니다. 모두가 헬퍼 클래스를 사용합니다(inuitcss, Bootstrap, SUIT). 하지만 아톰 모음을 만드는 것은 유연한 프레임워크로 진화하기 위한 다음 단계로 보입니다.

그 글은 여기서 볼 수 있습니다(필자의 font-size 네이밍 컨벤션은 별로입니다. 바뀔 경우를 대비해서 .fz-s, .fz-m, .fz-l 같은 것으로 하는 게 나았을 겁니다): http://www.smashingmagazine.com/2013/10/21/challenging-css-best-practices-atomic-approach/

이에 대한 당신의 생각이 궁금합니다. 내 생각이 잘못된 것인지요. 나는 이에 관해 말할 때는 학생들에게 우선 경고를 합니다. 작은 사이트(제 사이트)에 적용한 것만 봤을 뿐이라고요.

안녕히.
[이름]

이에 대해 아래처럼 답장했다:

안녕하세요!

메일 감사합니다. 이것은 흥미로운 주제고, 지난 2년 간 저 자신이 한 작업에 대립되는 것이죠.

문제는, 제 생각엔, 우리가 어떤 새 도구나 테크닉을 익히면, 그것의 최대 극단까지 사용을 해 본다는 것입니다. 그러고 나면, 그렇게 한 결과 뭔가 잘못됐다는 것을 알게 되고, 고치기 시작합니다. 제가 봐 온 대부분의 개발 문제/해법은 진자운동처럼 보입니다: 한쪽 극단, 극단적 과잉교정, 적정선 발견.

이런 식으로 보면, 제 생각엔 구식 CSS(클래스 없는, 아주 꼬인 선택자들… 등등)는 진자의 한쪽 극단이었고, 아토믹 CSS 깉은 것들(또는 인라인 스타일을 모방/모사하기 시작한 모든 것)은 반대편 극단입니다. 바로잡으려는 게 여러 모로 지나쳐서 수용 가능한 영역을 벗어나 너무 멀리 가 버린 것이죠.3 저는 우리가 적정선(sweet spot)을 찾아야 한다고 봅니다.

아토믹 CSS로 우리는 정말 정말 정말 DRY4한 CSS를 작성할 수 있을 것입니다. 하지만 어떤 DOM5은 이렇게 될 겁니다(이것은 가상의 예제입니다. 어떤 아토믹 CSS도 실제 이런 식으로 이름을 쓰진 않을 거예요).

<div class="d-bk p-md bg-w">
    <a href="#" class="p-sm fs-m d-bk c-gn">Log in</a>
</div>

저는 이 HTML에 대해 매우 조금밖에 알 수 없습니다. 물론, 어떤 선언이 어떻게 작동하는지를 말할 수 있습니다(이것은 거의 인라인 스타일입니다). 하지만 다음과 같은 것들은 말할 수 없을 것입니다:

  • 이 DOM이 시작하고 끝나는 섹션 ‘범위(scope)’는 어디인가?
  • diva는 같은 UI 구성요소(component)인가?
  • diva가 다른 하나 없이 존재할 수 있는가?
  • 어떤 클래스들이 테마상 연관돼 있는가?
  • 이 클래스들은 순전히 우연적으로 같이 있는 것뿐인가?
  • 이 HTML을 재사용할 때 손상시키지 않으려면 모든 클래스들이 필요한가?
  • 모든 클래스들이 결합됐을 때 특정 UI 구성요소가 만들어지는가?6
  • 없어도 되는 클래스는 무엇인가?
  • 이 DOM 조각에서 클래스 하나가 변하면, 다른 모든 비슷한 DOM 조각도 모두 변할 필요가 있는가?

물론, 이것은 아주 작은 CSS입니다. 하지만 가독성을 완전히 상실했으며, 문서로서의 독립성(self-document)을 잃었습니다. 너무 극단적입니다.

우리는 HTML 출력 전에 작동하는 메타 템플릿 언어 같은 것들을 사용해서 이를 해결할 수 있을 것입니다. 이런 식으로 말이죠(다시 말하지만, 가상의 코드입니다):

<% ui-component('promo-button') %>

이것은 아마 결합돼 UI 조각을 만드는 아토믹 클래스들을 뱉어낼 것입니다. 하지만 그러고 나서 우리는 처음 만들 때 생긴 문제를 풀기 위해 곧장 시스템 안쪽을 만지게 될 것입니다. 더 복잡하게 말입니다. 이것은 꽤 비생산적으로 보입니다.

(관련해서, 우리는 아토믹 클래스들을 Sass로 감쌀 수 있을 것입니다. 이렇게:

.btn-promo { @extend .p-sm; @extend .fs-m; @extend .d-bk; @extend .c-gn; }

… 하지만 이것도 앞선 것처럼 같은 가독성 문제를 가져옵니다. 완전 오버킬(overkill)이기도 하고요. 오직 @extend만 사용해 각 선언들을 확장하면 프로젝트가 아주 이상해질 것입니다. 서로간 의존성이 아주 강해질 것이고요.)

… 코드가 꽤 중복됐습니다.

DRY함(DRYness)이라는 주제로 돌아가 보죠. 제가 한 말을 인용해 보겠습니다. ‘컴파일 결과물에서 중복이 있는 것은 나쁜 게 아니다. 소스가 중복되는 게 나쁜 것이다.’

이렇게 말할 수 있을 것입니다: padding: 6px;이 CSS에서 50번 등장하는 것은 별 문제가 아니다. 성능 문제도 없다. 뭐 Gzip이 구겨 버릴 테니까.

그러나 수동으로 padding: 6px;을 Sass(혹은 LESS나 다른 것)에 타자쳐 넣는다면 문제입니다. DRY함은 Single Source of Truth7를 추구합니다. Single Source of Truth란 모든 핵심 데이터는 코드에서 한 번만 등장해야 한다는 것입니다. 그러면 일관성(값이 갱신되면 전체 프로젝트에 적용되므로)과, 더 중요하게는 유지보수 용이성을 보장할 수 있습니다. DRY는 소스 내 반복에 관한 것이지 일반적 반복에 관한 것이 아닙니다.

결국, 언급한 예시들 (6px, 12px, 24px 등)이 한 번 작성된 뒤 전체 프로젝트에서 재활용되면 되면 된다는 것입니다. 이것이 DRY한 것(다른 말로 Single Source of Truth)이고, HTML에서 더 나은 가독성과 맥락/정보를 제공하는 것입니다.

아주 간단한 개념 콘셉트를 봅시다: sassmeister.com/…/87b2da6cbd80d3f1f993. 여기에는 전체에 걸쳐 한 번만 사용되는 숫자가 있습니다. 그래서 우리는 Single Source of Truth를 얻을 수 있습니다. 이것은 아주 쉽게 다양한 사이즈로 재사용될 수 있습니다.

그로면 HTML은 이런 것을 좀더 닮게 될 것입니다:

<div class="box box--promo">
    <a href="#" class="btn btn--small btn--positive">Log in</a>
</div>

이러면 와우 DOM에 대해 더 많이 알 수 있습니다. 이 클래스들이 같은 테마로 연관돼있다는 걸 알 수 있군요. 어떤 조각이 필수가 아닌지도 알 수 있고, 바꿔도 되는 게 어떤 건지도 알 수 있습니다(예를 들면, --가 붙은 것들).

유틸리티 클래스를 사용하지 않을 이유는 전혀 없습니다. 저도 사용하니까요. 하지만 유틸리티 클래스는 규칙이 아니라 예외입니다. 전체 영역을 유틸리티 클래스로 구축하면 안 됩니다. 아토믹 CSS만큼 추상화한 클래스들(뿐만 아니라 비슷한 접근법은 전부 — 이것은 아토믹 CSS만 겨냥한 것이 아닙니다)은 파일 용량을 줄인 대가로 맥락, 가독성, 그리고 유지보수 용이성을 희생할 것입니다. 저는 이런 접근법이 오버킬이라고 생각합니다. 특히 gzip을 생각하면 파일 사이즈에서조차 별 이득이 없습니다. 우리는 적정선을 찾아야 합니다. 그리고 제 생각에 아토믹 CSS(와 그 비슷한 접근법들)는 적정선을 벗어났습니다.

와우, 아주 긴 답변이 됐네요. 도움이 됐기를 바랍니다.

Best, Harry

  1. 대문자로 쓴 것으로 보아 CSS 선택자 점수(특정도)를 뜻하는 말로 쓴 것 같고, 이 경우는 문맥상 선택자 점수를 높이 줘야 하는 경우를 말하는 듯하다. 

  2. atom은 atomic css의 기초 단위들을 말한다. 그래서 atom이라고 썼다. 

  3. vast overcorrection to take us as far from the other option as possible. 

  4. DRY 원칙. ”Do not Repeat Yourself”의 약어. 코드 중복을 피하라는 뜻이다. 

  5. Document Object Model. 간단히 말해 HTML 문서를 말한다고 보면 된다. 

  6. Do all classes when combined create a specific UI component? 

  7. 번역어가 마땅치 않아 원문 그대로 썼는데, 유일 소스 원칙 정도로 번역할 수도 있을 것 같다.