CSS in JS 라이브러리를 고민 중이신 여러분께
많은 프론트엔드 개발자분들이 스타일링에 고통받고 있습니다. 크몽의 프론트엔드 팀 또한 오래되고 방대한 코드 베이스를 가지고 있는 탓에, A페이지의 디자인을 수정하였는데, B페이지의 디자인까지 영향을 받는 예측하기 힘든 사이드 이펙트와 이미 꼬여버린 CSS 우선순위 때문에 !important가 남용되고 있었습니다.
하지만 !important가 좋은 해결 방법이 아니라는 것을 우리는 이미 알고 있습니다. 변화가 필요했습니다.
CSS는 간단하지만 쉽지는 않습니다. CSS는 태생적으로 여러 기술적 한계를 가지고 있었고, 웹에서 동작하는 서비스들의 규모가 점점 커지고 개발 패러다임이 바뀌면서 단순한 홈페이지를 만들던 수준의 사용 방식으로는 현대의 웹 서비스 개발 방식을 감당하기 어려웠습니다.
Sass와 Less, Stylus 등의 CSS Preprocessor들의 등장으로 중첩, 상위 선택자 참조(&)등의 편리한 문법과 변수, 함수, 상속 등의 프로그래밍 언어들이 제공해주는 기능을 CSS에도 활용할 수 있게 되었습니다. Less와 Stylus보다 Sass의 사용률이 매우 높으며 격차는 점점 더 벌어지고 있습니다.
처음 등장하였을 때 Sass를 활용한 스타일링은 아주 훌륭해 보였습니다. 하지만 적절한 전략과 모범 사례 없이 사용되었던 Sass는 서비스 규모가 커지면서 우리에게 여전히 해결되지 않은 문제점과 더불어 새로운 문제 또한 안겨 주었습니다.
상황에 따라서 Sass를 사용하는 것은 현재 시점에서도 여전히 좋은 선택 중 하나입니다. CSS만 사용하여 스타일링을 하는 방식과는 비교도 할 수 없이 많은 생산성 향상을 기대할 수 있습니다.
BEM은 Block, Element, Modifier의 약어로 이 세 요소를 사용하여 클래스명을 작성하는 방법론입니다. 러시아의 Yandex에서 정립 후 공개하여 세상에 알려지게 되었습니다.
HTML
CSS
CSS 클래스들을 더 투명하게 하고, 다른 개발자들에게 의미가 잘 전달될 수 있도록 유도합니다. BEM을 그대로 도입하거나 상황에 맞게 적절히 수정하여 도입하는 기업들이 많아졌습니다.
그러나, 또 다른 문제들이 발생했습니다. 마크업이 지나치게 긴 클래스 이름으로 비대해졌고, 마크업이 불필요하게 의미론적이 되었습니다. 하지만 저는 이 방법이 순수 CSS만으로 할 수 있는 최선의 방법이라고 생각합니다.
현대 웹 서비스의 개발에는 React, Vue, Angular 등을 활용한 컴포넌트 기반의 개발 방법을 많이 사용하고 있습니다. Sass와 BEM은 훌륭한 방법이었지만 이 두 방법에는 진정한 캡슐화 개념이 없으므로, 다른 컴포넌트에 영향을 주지 않는 방법은 개발자가 작성한 클래스 이름에 의존해야 한다는 것이었습니다. 이는 진정으로 우리가 원하는 답이 아니었습니다. 가장 좋은 방법은 개발자가 일일이 신경을 쓰지 않아도 되도록 시스템으로 해결하는 방법이라고 생각합니다. 이것이 CSS Module이 한 일입니다.
CSS Module을 사용하면 해당 컴포넌트에서 사용한 클래스명을 시스템에서 알아서 고유한 값으로 변환을 해주기 때문에 다른 컴포넌트에 선언되어 있는 클래스명과 중복이 될 걱정 없이 스타일링을 할 수 있습니다. 그 덕에 개발자가 클래스명을 고민하는 데 드는 시간이 많이 줄었습니다.
React에서는 아래처럼 [파일명].module.css 형태로 작성하여 CSS Module을 활용할 수 있습니다.
Header.module.css
Header.js
Vue에서도 style 태그에 module 속성을 추가하는 것으로 CSS Module을 사용할 수 있습니다.
header.vue
이 방법 또한 많은 인기를 얻었습니다. 하지만 CSS Module만으로는 CSS의 핵심 문제를 해결하지는 못하였고, 우리는 조금 더 우아하고 확실한 방법을 원했습니다.
Facebook의 Front-end Engineer인 vjeux는 이 발표를 통해 아래와 같은 7가지 CSS 관리의 어려움을 JavaScript로 CSS를 작성하는 방법을 통해 해결한 사례를 설명했습니다.
Global namespace
Dependencies
Dead Code Elimination
Minification
Sharing Constants
Non-deterministic Resolution
Isolation
프론트엔드 개발자라면 모두 공감할 문제들입니다. (자세한 내용은 발표 자료를 참고해주세요.)
CSS in JS 라고 불리는 이 개념은 CSS의 태생적 한계를 극복할 수 있는 새로운 방향성을 제시하였습니다. 이 개념에서 영감을 얻은 styled components, emotion, jss 등의 다양한 라이브러리가 개발되었고, 이 중 현재 국내에서는 styled components가 가장 대중적으로 사용되고 있습니다. 크몽도 styled components와 emotion 등 여러 라이브러리 중 어느 라이브러리가 가장 적합한지 많은 고민을 하였습니다.
크몽은 CSS in JS 라이브러리로 emotion을 사용합니다. 그럼 크몽 프론트엔드 팀은 emotion을 어떤 방식으로 활용하고 있는지 소개하겠습니다.
크몽 프론트엔드팀은 이번 2020년에 Vue와 Sass를 메인으로 사용하고 있는 기존 환경에서 Next.js, TypeScript, Storybook, Emotion 등의 기술 스택을 사용하는 React 생태계로의 점진적 환경 전환을 준비하고 있습니다. 이렇게 Vue에서 React로 전환을 결정한 이유에는 여러 가지가 있지만, 그중 CSS in JS 개념이 Vue와는 핏이 잘 맞지 않으며, 관련 라이브러리들의 발전이 React와 비교해 상대적으로 뒤처지고 있다는 상황이 큰 부분을 차지하였습니다.
React로의 전환과 더불어 프로젝트 간 코드 공유 등의 효율적인 개발을 위해서 Monorepo방식으로 코드를 관리합니다.
여러 패키지 중 오늘 이야기하고자 하는 주제와 가장 연관이 있는 것은 design-system 패키지인데요. 서로 다른 여러 프로젝트에서 사용자에 일관된 사용자 경험을 제공하고, 코드 재사용성을 위해서 디자인 시스템을 구축하고 storybook을 통하여 관리하고 있습니다.
UI 컴포넌트를 구현하면서 신경을 많이 쓰고 있는 부분 중 하나는 개발자가 최대한 유연하게 사용할 수 있도록 하는 부분입니다. 하나의 UI 컴포넌트는 디자이너와 함께 논의 후 변경되어선 안 되는 최소한의 상태만 props를 통해 제어할 수 있도록 구현합니다. 예를 들어, 버튼의 색상 같은 경우 색상에 따른 의미와 용도가 정해져 있기 때문에, 개발자가 버튼마다 컬러 코드를 부여하여 컬러를 지정할 필요도, 그렇게 해서도 안 됩니다. 미리 정의해 놓은 color props의 선택지 안에서 선택할 수 있도록 합니다. 그러나 버튼의 margin은 상황에 따라서 유연하게 변경되어야 할 수 있습니다. 그런 상황에 사용할 수 있도록 spacing, elevation 등을 미리 정의해두고 사용합니다.
<Button /> 컴포넌트를 보시면 color prop 아래에 css prop를 확인하실 수 있습니다. 이것은 emotion에서 제공하는 css prop이라는 기능인데요. 이 기능을 통해 기존에 정의한 컴포넌트의 스타일을 쉽고 안전하게 확장할 수 있습니다. 이것이 바로 styled-components 라이브러리와의 차이점이자 크몽에서 스타일링 라이브러리로 emotion을 채택한 이유입니다. @emotion/styled는 styled-components와 거의 동일한 기능을 제공하고 있고, 추가로 @emotion/core를 통해 확장성까지 갖추었습니다.
css prop을 활용하는 방법은 또 한 가지 있습니다. 굳이 styled component를 만들지 않고, 인라인으로 CSS를 작성할 수 있는데요. 아래와 같이 작성할 수 있습니다.
여기까지는 일반적인 인라인 스타일과 비교해 특별히 더 나은 부분이 없습니다. 조금 더 활용해 보겠습니다.
어떠신가요? 일반적인 inline style과는 다른 부분이 보이시나요?
inline style과 비슷해 보이는 css prop이 inline style과 비교해 가지는 장점은 다양합니다. media query 및 pseudo selector, nested selector 등을 사용할 수 있으며 inline style의 한계를 뛰어넘어 인라인 스타일 기법이 이거보다 더 발전할 수 있을까 하는 생각이 들 정도입니다. 간단한 세팅을 통해 className prop을 사용할 수 있는 모든 컴포넌트에서 css prop을 사용 가능하며, 예시에서 보여드린 tagged template literal 방식 외에 Object 형태도 사용할 수 있습니다.
현실 세계에서 개발을 하다 보면 스타일링을 위해 의미를 부여하기 다소 애매한 상황을 자주 만나게 됩니다. 특별한 기능도 없는 특정 UI를 구현하기 위한 컴포넌트나 클래스의 이름을 지어야 하는 상황은 Sass, Bem, CSS Module 등의 훌륭한 방법들로도 도통 쉽게 해결할 수 없었던 문제이었습니다. emotion을 사용하면 이러한 상황에서 더는 고민하지 않고 css prop을 이용해서 인라인으로 스타일을 작성할 수 있습니다. 적절한 네이밍을 고민할 필요가 없으며 해당 컴포넌트에 적용된 스타일 코드를 찾아다닐 필요 없이 바로 확인할 수 있습니다.
emotion은 우리에게 최고의 개발 경험을 제공하고 있습니다. 소개한 내용 외에도 Helper Components, Layout Components 등의 재사용을 극대화한 다양한 활용 방법과 composition, theme, 가벼운 용량과 좋은 성능, TypeScript 지원 등 더 많은 장점이 있습니다. 새로운 프로젝트를 시작할 예정이거나, 기존 스타일링 시스템을 개선하고자 하시는 분들께 이 글이 도움이 되었으면 합니다.
감사합니다.
우리 팀은 크몽과 딱 어울리는 당신을 기다리고 있어요. 우리 함께 크몽을 만들어요!
채용공고 *개발직군 상시 채용중!