본문 바로가기
2024/프로젝트

[My Little Rosemary] Next.js에서 어떤 도구로 스타일링 해야 될까?

by ye-jji 2024. 7. 17.

프로젝트를 시작해서 기본적인 레이아웃을 잡으면서 고민이 시작되었다. 원래 react에서 사용했던 건 Styled Components였는데 이번 프로젝트에서 사용하려고 하니 생각해봐야 하는 부분이 있었다. 생각보다 번들 크기가 커진다는 것, 그리고 SSR방식이기 때문에 반짝임 현상이 발생하는 것 등등이었다. 그래서 이 부분에 대해 정리가 필요하겠다는 생각이 들었고 사용해봤거나 사용할 수 있는 것들을 모아서 비교해보기로 했다.

 

1. CSS Modules

https://nextjs.org/docs/app/building-your-application/styling/css-modules

CSS Modules는 CSS를 모듈화하여 작성할 수 있게 해주는 기술이다. CSS 파일에서 클래스나 ID를 정의할 때, 전역으로 적용되는 문제가 있는데 CSS Modules를 사용하면, 각 CSS 파일의 클래스와 ID가 로컬로 스코프(scope)되기 때문에 다른 CSS 파일과 충돌하지 않는다. 주로 JavaScript 모듈 번들러(예: Webpack)를 통해 사용되고, CSS 파일을 컴포넌트 단위로 로드하고, 클래스 이름이 고유하게 생성되도록 만든다. 결과적으로, CSS의 전역 네임스페이스 문제를 해결하고, 컴포넌트 기반의 스타일링을 가능하게 한다.

 

장점

CSS Modules는 로컬 스코프 기능을 통해 클래스와 ID를 각 컴포넌트에 국한시켜서 전역 네임스페이스 충돌을 방지한다. 그래서 동일한 이름의 클래스를 사용해도 충돌이 없고 스타일링의 일관성을 유지하기 편하다. 또한, 스타일을 컴포넌트 단위로 모듈화해 관리하기 때문에 코드의 가독성이 높고 유지보수가 쉽다. 기존의 CSS 문법을 그대로 사용할 수 있어 러닝커브가 낮고, JavaScript 코드에서 CSS 클래스를 쉽게 가져와 사용할 수 있어 개발 속도가 빨라진다. 각 컴포넌트의 스타일이 해당 컴포넌트에만 영향을 미치기 때문에, 스타일 변경이 다른 컴포넌트에 미치는 영향을 최소화할 수 있다는 장점이 있다.

 

단점

CSS Modules를 사용하려면 Webpack과 같은 번들러를 설정해야 한다. 그리고 클래스 이름이 변환될 때 길어지기 때문에 가독성이 좋지는 않다. 또한, CSS Modules는 JavaScript 환경에서만 동작하기 때문에 순수 CSS만을 사용하는 프로젝트에서는 적용하기 어렵고, CSS 파일을 JavaScript 모듈로 취급해야 하는 점에서 추가적인 설정이 필요하다. 사실 단점이 엄청 크다는 느낌은 들지 않는다.

 

2. Styled Components

https://styled-components.com

Styled Components는 JavaScript와 CSS를 결합하여 컴포넌트 단위로 스타일을 관리할 수 있게 해주는 라이브러리이다. React 환경에서 주로 사용하고, 템플릿 리터럴을 사용하여 스타일을 정의한다. 컴포넌트와 스타일을 하나의 파일에 결합할 수 있어, 스타일이 로컬로 스코프되어 다른 컴포넌트와의 스타일 충돌을 방지할 수 있다. 또한, JavaScript의 동적 기능을 활용할 수 있어서, 조건부 스타일링이나 테마 적용이 편하다.

 

장점

가장 큰 장점은 스타일을 컴포넌트와 함께 관리할 수 있다는 점이다. 스타일이 특정 컴포넌트에만 적용되도록 보장하기 때문에, 글로벌 스타일 충돌 문제를 해결할 수 있다. 스타일이 컴포넌트와 동일한 파일에 존재하기 때문에, 코드 가독성이 좋고 유지보수하기 수월하다. 또한, CSS 문법을 그대로 사용할 수 있고, JavaScript의 모든 기능을 사용할 수 있어 조건부 스타일링, 애니메이션, 테마 적용 등이 매우 간편하다. 동적 스타일링이 필요한 프로젝트에서 특히 유용하다.

 

단점

스타일 컴포넌트는 런타임에 스타일을 생성하기 때문에, 성능 오버헤드가 발생할 수 있다. 특히 대규모 애플리케이션에서 문제가 될 수 있다. 그리고 모든 스타일이 JavaScript 파일 내에 포함되어 있기 때문에 JavaScript 번들의 크기가 커질 수 있다. 이렇게 되면 초기 로드 시간이 길어지고, 번들링 과정이 더 복잡해진다. 또한 React와 템플릿 리터럴에 대한 러닝커브가 존재한다. 하지만 나는 이미 익숙해서 그건 괜찮다.

 

3. Tailwind CSS

https://tailwindcss.com/

Tailwind CSS는 유틸리티-우선(Utility-First) CSS 프레임워크로, 미리 정의된 클래스를 사용해 HTML 요소를 스타일링하는 방식이다. 다른 CSS 프레임워크와 달리, 레이아웃, 색상, 타이포그래피, 여백 등을 제어할 수 있는 유틸리티 클래스를 제공한다. 그래서 CSS를 작성하는 대신 HTML에 직접 클래스를 적용하여 스타일을 정의할 수 있다.

 

장점

Tailwind CSS의 가장 큰 장점은 빠르고 효율적인 스타일링을 가능하게 한다는 점이다. 유틸리티 클래스를 사용해 CSS를 작성할 필요 없이 HTML 요소에 스타일을 바로 적용할 수 있어 개발 속도가 빨라진다. 또한, 미리 정의된 클래스들은 일관된 디자인 시스템을 쉽게 구축할 수 있도록 도와주고, Tailwind의 설정 파일을 통해 쉽게 커스터마이징할 수 있다. 이렇게 하면 디자인 일관성을 유지하면서도 필요에 따라 유연하게 조정이 가능해진다. 또한, 불필요한 CSS를 제거하는 PurgeCSS와 같은 도구와 함께 사용하면, 최종 빌드에서 사용되지 않는 스타일을 제거하여 파일 크기를 최소화할 수 있다.

 

단점

Tailwind CSS의 단점 중 내가 가장 크게 느끼는 것은 HTML 코드가 유틸리티 클래스로 인해 지저분해진다는 점이다. 많은 클래스를 사용해 스타일을 정의하기 때문에, HTML이 복잡해지고 가독성이 떨어진다. 또한, 모든 스타일을 유틸리티 클래스로 정의해야 해서 기존의 CSS 작성 방식에 익숙한 나에게는 초기 러닝커브가 존재한다. 프로젝트 초기 설정이 다소 복잡하고, 설정 파일을 잘못 구성하면 예상치 못한 스타일 충돌이 발생할 수 있다.

 

4. Vanilla Extract

https://vanilla-extract.style/documentation/getting-started

Vanilla Extract는 스타일을 TypeScript 파일 내에서 작성할 수 있게 해주는 CSS-in-TypeScript 라이브러리다. CSS-in-JS를 사용하는 다른 라이브러리들과 비슷하지만, 런타임에 스타일을 생성하는 대신 빌드 시점에 CSS 파일로 추출하여 최종 번들에 포함시킨다는 차이가 있다. Vanilla Extract는 TypeScript의 타입 안전성을 활용해 스타일을 정의할 수 있고, CSS의 모듈화와 동적 스타일링을 지원한다.

 

장점

Vanilla Extract의 가장 큰 장점은 타입 안전성을 제공한다는 점이다. 스타일 작성 시 타입 검사를 받을 수 있어, 오류를 사전에 방지할 수 있다. 또한, 빌드 시점에 스타일을 추출해 런타임 오버헤드가 없고, 최종 번들 크기를 최소화할 수 있다.그리고 CSS Modules와 유사하게 스타일을 모듈화할 수 있어, 각 컴포넌트의 스타일이 로컬로 스코프된다. 사용방식은 기존과 동일하게 작성할 수 있어 러닝커브가 낮은편이다.

 

단점

하지만 비교적 새로운 라이브러리로, 다른 CSS-in-JS 라이브러리나 솔루션에 비해 커뮤니티와 리소스가 적다는 단점이 있다. 또한, TypeScript에 의존하기 때문에 TypeScript를 사용하지 않는 프로젝트에서는 적용하기 어렵다. 마지막으로 초기 설정이 조금 복잡하다는 단점이 있다.

 

5. 비교

성능, 코드 양, 가독성을 비교해보기로 했다. 동일한 UI를 각각의 방식으로 코딩해서 비교해보면 좋겠지만 그건 나중의 나에게 미루고 일단 할수있는 비교만 해보기로 했다.

 

성능

실제로 구현하지 않으면 디테일한 차이를 알기 어려울 수도 있지만 확실한건 Styled Components이 제일 안좋을거 같다. 런타임 오버해드 발생가능성이 있기 때문에 그렇게 생각했다. 그리고 그 다음은 CSS Modules이라고 생각했다. 아무래도 js에 의존하다보니 그렇지 않은 Tailwind CSS가 좀 더 좋을 것 같다고 생각했기 때문이다. 그리고 Tailwind CSS는 스타일을 정적으로 적용하기 때문에 성능이 좀 더 좋을 것 같다. 가장 좋을 것 같다고 생각한 건 Vanilla Extract인데 그 이유는 스타일이 빌드 시점에 추출되고, 최종 번들에 css와 js파일이 포함되지 않고, 런타임 오버헤드가 전혀 없는 점들을 고려하면 제일 좋을 것 같다는 생각이 들었기 때문이다.

 

코드

코드 작성하는 양을 비교해보면 UI의 복잡도에 따라 차이가 있겠지만 Tailwind CSS가 가장 적을 것으로 예상된다. 파일 생성을 따로 하지 않고 클래스만 작성하기 때문에 그렇게 생각했다. 그 다음은 Styled Components라고 생각했다. 컴포넌트 단위로 같은 파일에 바로 작성이 가능하기 때문이다. 그렇지만 세부적인 항목의 값을 다 적어야 하니까 코드를 좀 더 작성해야 할 것이라고 생각했다. 그 다음은 CSS Modules이다. 일단 해당하는 파일별로 css파일이 새로 생기고 약간 중복되는 내용도 경험상 많았기 때문에 전부 글로벌로 묶으면 처음 로딩이 오래걸리거나 충돌이 있을 수도 있어서 코드가 좀 많아지는 것 같다. 마지막은 Vanilla Extract이다. 예시를 살펴보니 import해서 적용할 때 className={styles.searchBox} 이런 식으로 전부 들어간다.

 

가독성

Tailwind CSS가 제일 좋지 않다고 생각한다. 클래스 이름에 기능이 붙어있지 않고 많은 유틸리티 클래스가 HTML에 포함되어 있기 때문에 유지보수에도 어려움이 있다고 생각한다. 그 다음은 엇비슷 할 거 같은데 아무래도 Styled Components가 제일 가독성은 좋을 것 같다. 컴포넌트와 같은 파일에 있기도 하고 유지보수 하기도 편하고 해서 그렇게 생각했다.

 

결론

제일 중요하게 생각하는 것이 예전에는 가독성이었는데 인턴 경험을 통해 성능이 비용과 직결되는 것을 보면서 성능을 먼저 챙기고 그 뒤에 가독성이 따라 오는게 좋겠다는 생각을 하게 되었다. 그렇게 생각하고 보니 Vanilla Extract이 제일 적합하다는 생각을 하게 되었다. 물론 처음 사용해보는 거라서 러닝커브가 있겠지만 이번 기회에 배워 보는 걸로!