Skip to content

Sass 믹스인 중복 배제하기

CSS 전처리기 Sass의 강력한 기능 가운데 하나는 믹스인mixin이다. 공통 패턴을 의미적이고 재사용 가능한 덩어리로 추상화한 것이다. 버튼 스타일을 고려하면서도 모든 속성이 무엇인지 기억해야 하는 대신 선택자가 버튼 스타일을 포함한다고 해보자. 버튼 스타일이 한 곳에서 유지되므로 업데이트와 일관성을 유지하기가 쉬워진다.

그러나 믹스인은 속성을 너무 자주 복제하기 때문에 출력 CSS의 실패 지점을 여럿 만들고 파일 크기를 부풀린다. 하지만 Sass를 사용하고 있다면 여러분은 이미 일반적인 프로그래머보다 더 적은 CSS를 만드는 방식을 따르고 있는 것이다. 나처럼 성능에 집착한다면 부풀리기는 결코 받아들일 수 없는 일이다. 이 글에서는 Sass 믹스인을 아주 최소한의 CSS 출력으로 얻는 방법을 설명하겠다.

 

Sass의 몇 가지 배경

Sass는 작성된 스타일시트와 브라우저에서 해석할 .css 파일 사이의 계층이다. Sass에는 CSS를 더 쉽게 작성하고 유지할 수 있도록 돕는 멋진 기능이 많이 추가되었다. Sass가 스타일시트 작성자에게 하는 일 가운데 하나는 이들의 CSS를 DRY하게 해 유지관리가 쉽게 만드는 것이다. DRY또는 “don’t repeat yourself(반복되는 코드를 만들지 말라)”는 《실용주의 프로그래머The Pragmatic Programmer》에서 만든 프로그래밍 원칙이다.

시스템 안의 모든 지식은 권위 있고 명확한 단 하나의 표현만을 가져야 한다.

CSS는 DRY에 잘 맞지 않다. 공통 속성 집합은 항상 불가피하게 복제된다(버튼을 생각해보라). 3가지 형식을 갖는 버튼 스타일을 만들려면 스타일을 각 버튼 형식마다 반복하거나 그 버튼을 만드는 속성을 여러 선택자로 분할해야 한다. 이런 특징은 작성자를 곤란한 상황에 빠뜨린다. 각 형식에 해당하는 속성을 다시 작성하는 것은 수많은 CSS의 복사·붙여넣기(파일 크기 증가)와 여러 실패 지점, 고유 원천의 부재를 초래한다. 이는 결국 아주 취약한 구성 요소가 된다는 것을 뜻한다. 반면에 속성을 여러 선택자로 나누는 것은 시스템에서 버튼의 모든 형식에 대해 권위 있는 단 하나의 표현이 없다는 것을 의미한다. 그 대신 둘 이상의 선택자에 흩어져 있음을 뜻한다. 그럼으로써 속성이 모호하게 정의되기 때문에 이것이 구성 요소에 취약성을 더한다.

우리가 원하는 이상적인 방법은 한 곳에서 핵심 스타일을 정의하고(중복 없이) 선택자 하나로 스타일을 적용하는 것이다.

 

왜 DRY인가?

왜 이런 문제를 다뤄야 할까? 간단히 말하면 DRY CSS가 사이트 성능을 향상시킬 수 있기 때문이다. 사이트 아키텍처를 설계할 때 성능은 우리가 선택하는 이미지 형식에서 CSS 선택자를 작성하는 방법에 이르기까지 다양한 부분에서 중요하다. 모바일에 관해 논의할 때는 특히 그렇다. HTTP 요청처럼 웹의 기본인 어떤 것이 성능 문제를 일으킬 수 있다. 대규모 웹 성능에서 CSS 파일 크기는 문제가 아니라는 오랜 가정은 모바일 사용자가 총 공유 웹사이트 캐시를 100메가바이트 미만으로 직면했을 때는 사실이 아니다. 확보할 수 있는 약간의 공간도 모두 캐시 카운트를 벗어나게 된다.

목표는 Sass와 HTML에서 유지보수하기 좋은 선택자를 만든 뒤 CSS 표현에서 가능한 한 군더더기를 빼내어 캐시 메모리 공간을 줄이는 것이다.

 

믹스인과 상속 : 두 개의 하프 솔루션

Sass 믹스인은 이런 문제 가운데 하나에 해답을 제공한다. 스타일시트 작성자가 믹스인을 사용하면 한 곳에서 핵심 스타일을 정의하고 참조할 수 있다. 믹스인은 인자를 받아 한 믹스인 호출에서 또 다른 믹스인 호출로 약간의 변경을 가능하게 하고 동일한 패턴의 다른 형식을 만들 수 있게 한다. 하지만 믹스인 활용에는 한 가지 문제가 있다. 기회가 되면 믹스인은 호출될 때마다 속성을 작성해 출력 CSS를 부풀린다. 믹스인은 Sass 선언 중복 배제(DRY-ing)의 권위 있는 단일 표현을 해결하지만 종종 출력 CSS에서 중복 속성을 남겨두기도 한다.

Sass는 CSS를 DRY하는 데 도움을 줄 수 있는 또 다른 개념인 상속extend을 소개했다. @extend 지시문을 통해 상속을 사용하면 스타일시트 작성자에게 “선택자 A를 선택자 B와 같은 스타일로 지정하기 원한다”라고 말할 수 있다. 이때 Sass가 수행하는 일은 선택자 A와 선택자 B를 쉼표로 분리해 선택자 B의 속성을 공유할 수 있게 하고 나머지 속성을 평소처럼 작성하게 하는 것이다. 믹스인과 달리 상속은 인자를 줄 수 없다. 모 아니면 도다.



 

상속은 출력 CSS에서 중복되는 속성과 단일 선택자 문제를 해결한다. 하지만 스타일시트 작성자는 여전히 Sass에서 두 개의 별도 스타일 집합을 유지해야 한다. 시작할 때 두 개의 선택자를 작성한 것처럼 각 구성 요소의 형식에 어떤 속성이 추가되어야 하는지 기억해야 한다.

믹스인과 상속은 각 문제의 절반만 해결한다. 믹스인과 상속을 창의적인 아키텍처와 Sass의 몇 가지 흥미로운 기능과 결합하면, 패턴이 Sass에서 사용되고 유지 관리되는 방식과 구성 요소의 스타일이 출력 CSS에서 적용되고 표현되는 방식, 이 두 반쪽을 하나의 명확하고 권위 있는 표현으로 결합하는 진정한 DRY 믹스인을 만들 수 있다.

 

DRY 빌딩 블록

Sass의 4가지 기능인 플레이스홀더(혹은 자리표시자) 선택자placeholder selector, 맵 데이터 형식, @at-root 지시문, unique-id() 함수는 DRY 믹스인의 주춧돌을 맡고 있다.

 

플레이스홀더 선택자

플레이스홀더 선택자는 Sass의 @extend 지시문을 사용하기 위한 고유 선택자 유형이다. 클래스처럼 작성했으나 .으로 시작하는 대신 %로 시작할 경우 스타일시트가 출력되지 못하는 것을 제외하면 일반적인 상속처럼 동작한다. 따라서 선택자는 스타일시트의 플레이스홀더가 선언된 곳에 배치된다.



 

Sass 3.3에서 맵은 자바스크립트에서 객체와 비슷한 방식으로 동작하는 데이터 형식(숫자형, 문자열, 목록 등과 같은 데이터)이다. 맵은 키와 값의 쌍으로 구성되는데 키와 값은 Sass의 모든 데이터 형식(맵 자체 포함)이 될 수 있다. 키는 항상 고유하고 이름으로 조회할 수 있어서 고유하게 저장하고 검색하는 데 이상적이다.


 

AT-ROOT

Sass 3.3의 새로운 기능@at-root 지시문은 현재 중첩에 상관없이 스타일시트 루트에 포함된 정의를 배치한다.

 

UNIQUE ID

Sass 3.3의 unique-id() 함수는 현재 Sass의 실행에 고유성을 보장하는 CSS ID를 반환한다.

기본 믹스인 만들기

패턴을 믹스인으로 전환하려면 패턴을 구성하는 스타일의 핵심을 살펴보고 공유할 것과 사용자 입력을 확인해야 한다. 이를 위해 기본 버튼을 예로 들어보자.


 

이 패턴을 믹스인으로 전환하려면 사용자 제어 속성(동적)과 아닌 속성(정적)을 선택해야 한다. 동적 속성은 믹스인으로 전달된 인자로 제어하고 정적 속성은 간단히 작성한다. 버튼이 색상만 동적이기를 원한다면 인자와 함께 믹스인을 호출할 수 있고 CSS는 예상대로 출력될 것이다.


 

이 코드는 잘 동작하지만 중복 속성을 많이 만들어낸다. 새로운 색의 버튼을 만들고 싶다면 Sass(믹스인 정의 포함하지 않음)와 출력 CSS는 다음과 같다.



 

앞의 코드는 중복 속성이 많아 출력 CSS를 부풀린다. 이것은 원하는 바가 아니다. 여기가 플레이스홀더 선택자를 창의적으로 사용할 곳이다.

 

믹스인 중복 배제하기

믹스인 중복 배제하기는 정적 및 동적 부분으로 나누는 것을 의미한다. 동적 믹스인은 사용자가 호출하고 정적 믹스인은 그렇지 않은 경우에 복제될 부문만 포함한다.


 

이제 믹스인을 두 부분으로 나눴으므로 중복을 방지하기 위해 button-static에서 해당 항목을 상속하고자 한다. 믹스인 대신 플레이스홀더 선택자를 사용해 이 작업을 수행할 수 있지만 그것은 선택자가 스타일시트로 이동된다는 뜻이다. 그 대신 선택자가 필요할 때 처음으로 만들어져서 예상하는 소스 순서를 유지하도록 플레이스홀더를 적절하게 동적으로 만들고자 한다. 이렇게 하려면 전역 변수를 만들어 동적 선택자의 이름을 저장하는 것이 첫 번째 단계가 된다.


 

그 다음 button-static에서 선택자에 해당하는 키가 있는지 확인한다. 이 키를 지금은 “button”이라고 부른다. map-get 함수를 사용해 키값을 얻거나 키가 없으면 null을 반환한다. 키가 존재하지 않으면 map-merge를 사용해 키를 고유한 ID값으로 설정한다. 전역 변수에 쓰고자 하기 때문에 !global 플래그를 사용한다.


 

플레이스홀더 ID가 이미 있는지 확인한 후 플레이스홀더를 만들어야 한다. @at-root 지시문과 보간 #{}으로 고유 ID 이름을 갖는 디렉터리 루트에 플레이스홀더 선택자를 만든다. 플레이스홀더 선택자의 콘텐츠는 정적 믹스인(재귀 믹스인)을 호출한다. 그 다음 동일한 플레이스홀더 선택자를 상속해 활성화하고 CSS에 속성을 작성한다.

여기서는 클래스처럼 전체 선택자를 상속하지 않고 플레이스홀더 선택자를 사용함으로써 해당 선택자가 상속된 경우에만 콘텐츠를 포함하므로 출력 CSS가 줄어든다. 속성을 작성하는 대신 확장을 사용해 속성 중복도 피한다. 결국 출력 CSS의 취약성이 감소된다. 믹스인이 호출될 때마다 이렇게 공유되는 속성은 CSS 전처리 단계를 통해 대략적으로 결합되지 않고 실제 출력 CSS에서 공유된다.


 

아직 완전히 끝나지 않았다. 여전히 중복된 결과를 얻게 될 것고 이는 우리가 원한 결과가 아니다(선택자 확장도 원한 것이 아니다). 원하지 않는 결과를 방지하기 위해 button-static에 인자를 추가해 상속 프로세스 실행 여부를 지시한다. 역시 동적 믹스인에 추가하고 정적 믹스인에 전달한다. 믹스인은 다음과 같다.


 

이런 노력을 거쳐 Sass에서 스타일을 쉽게 유지·관리하고 HTML에 단일 선택자를 제공하며, 전체 CSS양을 최소로 유지하는 방법을 만들었다. 버튼 믹스인을 여러 번 포함해도 정적 속성은 결코 중복되지 않는다.

처음 믹스인을 사용할 때 믹스인이 호출된 CSS에서 스타일이 만들어져 의도한 캐스케이드를 유지하고 취약성을 줄이게 될 것이다. 같은 믹스인을 여러 번 호출할 수 있으므로 Sass와 HTML 모두에서 변형을 쉽게 만들고 유지할 수 있다.

원래 예제 믹스인은 이제 다음과 같은 CSS를 만들어낸다.



정적 속성은 선언했던 곳에서 쉼표로 구분되어 디버깅하기 쉽게 만들고 소스 순서를 유지하며, 출력 CSS 파일의 크기를 줄인다(바뀌는 속성만 새로운 선택자를 얻는다). 멋진 DRY 믹스인이다!

 

더 나아가기

동일한 패턴을 각 믹스인에 다시 작성하는 것은 전혀 DRY가 아니다. WETwrite everything twice(프로그래머는 바보처럼 행동할 때가 있다)이다. 우리는 이를 원하지 않는다. 플레이스홀더를 생성하기 위해 믹스인 만들기를 고려하고 이를 대신 호출하자. 또는 툴키트Toolkit Sass 확장 플러그인(보어Bower 패키지 매니저나 컴퍼스Compass 확장 프레임워크를 통해)을 사용해 동적 확장 믹스인으로 동적 플레이스홀더를 찾아서 만들고 확장할 수 있다. “button”처럼 검색할 문자열을 간단히 전달한다.


 

지금까지 살펴본 것을 통해 DRY 믹스인 패턴의 중복을 막을 수 있다. 입력 Sass 파일뿐만 아니라 출력 CSS 파일의 양을 줄이고 최고의 메타 프로그래밍을 보장하자.

 

도서 소개


댄 시더홈의 개정판 『웹디자이너를 위한 SASS』
웹 개발에서 CSS의 비중이 커짐에 따라 코드는 반복되고 스타일시트는 길고 복잡해지며 용량도 커지고 있다. 여기저기 흩어져 있는 코드를 찾고 수정하는 데 많은 시간을 소요하지만 코드를 관리하는 것은 점점 어려워지고 시간이 지날수록 효율성도 떨어진다. 이런 문제를 해결해주는 것이 바로 SASS다!

저작권 정보이 글은 A List Apart 기사를 번역한 것입니다. 저작권자의 정당한 허락을 받은 저작물로 한국어판 저작권은 웹액츄얼리에 있습니다. 웹액츄얼리의 서면 동의 없이 무단 전재, 복제를 금합니다. 원본은 DRY-ing Out Your Sass Mixins에서 확인할 수 있습니다.
참여를 기다립니다!웹액츄얼리에서 웹디자인 관련 영문 번역자를 찾습니다. 웹 콘텐츠 번역에 관심 있는 분은 메일로 간략한 본인 소개와 번역 이력을 보내주시면 연락드리겠습니다.
books@webactually.com


맨위로