본문 바로가기
개발

내가 겪은 CSS 스타일링 전략의 변화와 고민들 🤔

by soyooooon 2025. 4. 6.
반응형

 

프론트엔드 개발을 하면서 다양한 프레임워크와 CSS 전략을 선택하고 경험해 왔다. 각 전략은 프론트엔드 CSS의 트렌드, 그리고 특정 시점의 필요나 프로젝트 상황에 따라 선택되었고, 그 안에서 여러 시행착오와 고민이 있었다. 이 글에서는 CSS 경험 과정을 돌아보고, 현재 관심이 있는 CSS와 앞으로의 방향성을 정리해보고자 한다.

 

CSS-in-JS와의 첫 만남: styled-components

처음 React를 사용했을 때는 styled-components를 즐겨 사용했다. 컴포넌트 파일 내에서 함께 선언할 수 있어 구조가 명확했고, 각각의 스타일을 마치 하나의 컴포넌트처럼 다룰 수 있어 직관적이었다. 특히 props를 기반으로 조건부 스타일을 줄 수 있어 유연하게 대응할 수 있었다. 하지만 프로젝트 규모가 커지면서 유지보수, SEO, 성능 측면에서 한계를 느끼기 시작했다.

const Button = styled.button`
  background-color: ${({ primary }) => (primary ? 'blue' : 'gray')};
`;

유지보수

동적으로 CSS를 쉽게 다룰 수 있다는 것은 좋았지만 실제 컴포넌트와 스타일명의 구분이 잘 되지 않기 시작했다. 처음 코드를 봤을 때 이게 스타일인지, 아니면 컴포넌트인지에 대한 파악이 명확하게 되지 않았던 것이 유지보수에 어려움을 주기도 했다.

// Button이라고 되어있다고 가정했을 때 겉보기엔 같지만 두 가지 가능성이 있음
<Button></Button>

// 1. 컴포넌트인 경우
import Button from '@/components/Button'

// 2. 스타일인 경우
const Button = styled.button`
  // button에 대한 style
`

SEO

  • 시멘틱 태그보다는 div를 남용한 경우에도, 컴포넌트에서는 태그가 아닌 스타일명이 보이기 때문에 태그 수정 필요성에 대한 티가 잘 나지 않았다. 관습적인 부분이라 사실 styled.div 대신 styled.section, styled.article 등을 잘만 사용하면 단점이라고 하기 애매할 수는 있지만, 나에게는 한눈에 태그가 들어오지 않는 구조가 된다는 것이 불편하게 생각되었다.
  • styled-components는 런타임에 스타일이 생성된다. 사실 이미 태그나 콘텐츠는 있는 상태이기 때문에, 스타일이 로드되기 전이라고 해서 SEO 직접적으로 영향은 없을 수 있다. 하지만 스타일이 적용되지 않은 상태에서 문맥이 이상한 상태로 읽힌다거나 콘텐츠가 이상한 위치에 있을 경우 SEO 평가 요소에 영향을 미칠 수 있기 때문에 SEO에 간접적인 영향을 줄 수 있다.

성능적인 측면

런타임에 CSS가 생성되기 때문에 초기 렌더링 속도나 번들 사이즈에 영향을 줄 수밖에 없다. 실제로 styled-component가 전체 라이브러리에서 꽤 많은 비중을 차지하는 것을 보고 무거운 느낌을 지울 수 없었다.

 

Vue와 Svelte에서는 SCSS

Vue와 Svelte를 사용할 때는 주로 SCSS를 활용했다. styled-components를 사용하지 않더라도 Vue의 경우 SFC(Single File Component)가 기본이기 때문에 scoped CSS를 적용할 수 있다. 두 가지 모두 style을 컴포넌트 내에 <style> 태그 안에 작성할 수 있기 때문에 별도의 라이브러리를 설치하지 않아도 충분히 쉬운 작성이 가능했다. 때문에 nesting이나 부가적인 기능을 사용할 수 있는 SCSS만으로도 구조적인 스타일 작성이 가능했다. styled-components에서 느꼈던 컴포넌트 단위 스타일링의 편리함과 유사한 느낌을 기본적으로 받을 수 있었다.

클래스 네이밍에 대한 고민

Vue, Svelte에서 SCSS를 사용할 때 가장 고민했던 것은 클래스 네이밍이었다. 초반에는 단순히 kebab-case + nesting을 활용하여 작성했지만, 점점 구조화된 네이밍이 필요하다고 느껴 BEM 방식으로 전환하게 되었다.

.button {
  &--primary {
    background-color: blue;
  }
}

.card__title--highlighted

 

재사용성을 고려하여 매번 네이밍을 고민하는 일이 반복되면서 개발 속도에도 영향을 주었다. 결국 스타일 설계에 들이는 부담이 점점 커졌고, 보다 생산적인 방법에 대한 필요성을 느끼게 되었다.

 

생산성을 끌어올려준 Tailwind CSS

앞선 고민을 하던 중, Tailwind CSS를 접하게 되었다. Tailwind는 이미 정의된 유틸리티 클래스들을 조합하여 사용하는 방식이기 때문에, 스타일 설계에 들이는 시간과 고민을 줄일 수 있었다. 특히 그중에서도 클래스명을 고민할 필요가 없었기 때문에 스타일링에만 집중할 수 있었다.

<button class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
  등록
</button>

커스텀이 용이

유틸리티 클래스가 정해져 있는 경우 커스텀에 대한 걱정도 있었는데, 브랜드 컬러, 폰트, spacing 등을 tailwind.config.js 파일에 설정할 수 있기 때문에, 회사 디자인이 있는 경우에도 사용이 용이했다.

theme: {
  colors: {
    brand: {
      primary: '#1E40AF',
    },
  },
}

Tailwind Component

또한 가끔 백오피스를 개발하거나 초기 프로젝트를 구축하는 경우에는 디자이너 없이 작업하는 경우도 있었는데, 이때 별도의 CSS 프레임워크를 설치하지 않고 Tailwind CSS의 컴포넌트를 통해 빠르게 사이트를 구축한 경험도 있다.

 

Tailwind CSS - Rapidly build modern websites without ever leaving your HTML.

Tailwind CSS is a utility-first CSS framework for rapidly building modern websites without ever leaving your HTML.

tailwindcss.com

가독성과 유지보수에 대한 고민

하지만 내가 느꼈던 가장 큰 단점은 클래스가 지나치게 길어지면서 가독성이 떨어진다는 점이었다. 아무래도 인라인으로 클래스명을 추가하다 보니 만약 반응형까지 고려한 스타일링을 하거나 이 외에도 복잡한 스타일링을 주어야 할 때면 컴포넌트가 너무 길어지는 느낌이었다. 이를 해결하기 위해 @apply를 활용하여 클래스를 묶으려고 시도했지만 몇 가지 걸리는 문제가 있었다. 클래스를 만드는 과정에서 또다시 네이밍에 대한 고민을 해야 하고, @apply로 생성한 클래스와 유틸리티 클래스가 혼용되면 유지보수에 혼란을 줄 것이라는 생각이 들었다. 결국 '컴포넌트를 가능한 한 작게 쪼개자'는 기준을 세우며 문제점을 개선하고자 했다.

 

생산성을 높이려면 Tailwind만으로 충분할까?

Tailwind CSS를 사용하며 설계에 대한 고민을 줄여준 것은 명확했다. 하지만 모든 CSS를 사용할 때 드는 고민인, '모든 걸 처음부터 만들어야 한다'는 부담은 그대로 남아있었다. 혼자 개발하던 상황에서 생산성을 높이기 위한 방법으로 어떤 것들이 있을지 고민했었고, 고민에 대한 해결 방안은 프로젝트의 상황에 따라 약간씩 차이가 있었다.

디자인이 정해진 초기 프로젝트

B2C 프로젝트의 경우 백오피스에 비해 디자인의 중요성이 더 컸기 때문에 디자이너와 함께 협업하는 경우가 많았다. 기존에는 Figma의 dev mode를 활용하여 작업을 했었는데, 색상이나 폰트 크기 등을 제외하면 페이지 내에서 포지션이 적절하지 않은 경우가 많았고, 결국 하나하나 옮겨야 했었다. 때문에 이를 좀 더 개선하고자 디자인을 코드로 변환해 주는 TeleportHQ를 선택했다. 디자이너분과 협의하여 이 툴을 통해 퍼블리싱에 대한 비용은 낮출 수 있었다.

 

Low-code Front-end Design & Development Platform | TeleportHQ

Front-end development platform, with a visual builder and headless content modelling capabilities. Static website creation, and UI development tools.

teleporthq.io

디자인이 정해지지 않은 프로젝트

규모가 작은 프로젝트의 경우 위에서 잠깐 소개한 Tailwind의 컴포넌트로도 어느 정도 해결이 가능했다. 하지만 Tailwind 컴포넌트는 일부를 제외하면 모두 유료로 제공되고 있다. 당시 회사에 프론트엔드는 나 혼자 있던 상황에서 백오피스나 초기 프로젝트를 구현하기 위해 결제 요청을 하기도 애매했기 때문에 큰 프로젝트에서는 이걸 사용하는 것이 애매하다고 생각했다.

shadcn/ui

이런 생각을 하던 중, 최근 shadcn/ui를 알게 되었다. shadcn/ui는 Tailwind 기반으로 작성된 UI 컴포넌트 시스템이다. 각 컴포넌트는 Headless 구조로 되어 있어 커스터마이징이 자유롭고, 접근성도 기본적으로 고려되어 있다. 무엇보다 개발자가 직접 프로젝트로 가져와 로컬에서 관리할 수 있기 때문에 디자인 시스템이 없는 상황에서도 점진적으로 시스템화할 수 있다는 점이 강점이다. 컴포넌트는 미리 스타일링이 되어 있기 때문에 Tailwind만 사용했을 때보다 생산성이 높고, 디자인 일관성도 자연스럽게 따라온다. 새로운 프로젝트나 MVP를 빠르게 구성할 때도 유용하지만 확장성 또한 좋기 때문에 큰 프로젝트로 넘어가는 단계에서도 사용하기 좋다고 생각했다.

 

Build your component library - shadcn/ui

A set of beautifully-designed, accessible components and a code distribution platform. Works with your favorite frameworks. Open Source. Open Code.

ui.shadcn.com

 

앞으로 더 알아보고 싶은 CSS 전략: Zero runtime CSS-in-JS

최근 동료 개발자인 미연님의 블로그 글을 통해 제로 런타임 CSS-in-JS 개념에 대해 접하게 되었다. 제로 런타임 CSS-in-JS를 사용하면 런타임 없이 빌드 타임에 스타일 추출이 가능하여, 동적 스타일링의 장점을 유지하면서도 성능 문제를 해결할 수 있다고 한다. 보통 기술 면접을 가면 그 회사에서 어떤 기술들을 사용하는지 질문하곤 하는데, vanilla-extract와 Panda CSS를 사용한다고 말하는 곳들도 있었다. 실무에서도 도입되는 기술인만큼, 당장 프로젝트에 도입하는 것은 아니더라도 관심을 가지고 살펴보면 좋겠다는 생각이 든다.

vanilla-extract

  • TypeScript 기반 정적 스타일 추출
  • 런타임 비용이 없고, 빌드 타임에 CSS를 생성
 

vanilla-extract — Zero-runtime Stylesheets-in-TypeScript.

Zero-runtime Stylesheets-in-TypeScript.

vanilla-extract.style

 

결론: 프로젝트 특성에 맞는 최적의 선택

스타일링 전략에는 정답이 없다고 생각한다. 팀의 규모, 프로젝트의 성격, 협업 환경에 따라 최적의 방법은 달라진다. 이전에 폴더 구조에 대한 글을 다룰 때도 이야기했지만 적절한 기준을 세우는 것이 가장 중요할 것이다. CSS도 도구 그 자체보다 이 프로젝트에서 왜 그러한 의사결정을 했는가에 대한 명확한 기준을 가지는 것이, 추후 개선을 함에 있어서도 중요한 기준점이 된다고 생각한다. 앞으로도 새로운 스타일링 방식들을 계속해서 실험하고, 그 안에서 나에게 맞는 방식들을 찾아가고자 한다.

반응형