프론트엔드 개발자는 요구사항에 부합하는 기능을 구현하는 것만큼이나 CSS 스타일링 능력도 중요하다. 이는 설계, 구현, 디자인 등과 같은 기본적인 능력들을 뒷받침하고 있다. 이 페이지에서는 CSS 스타일링과 최근 주목 받는 TailwindCSS에 대해 학습할 것이다. 프로젝트에 효과적인 CSS 스타일링을 적용하면 유지 보수 및 사용자 경험을 향상시키고, 웹 페이지의 성능과 접근성을 개선할 수 있다.
Node.js 패키지 방식으로 아이콘 사용학
@import
방식으로 폰트 혹은 아이콘을 가져오는 방식에는 문제점이 존재한다. 다른 사이트에 호스팅된 외부 CSS 파일을 가져오므로 네트워크 속도에 영향을 받을 수 있다. 따라서 대부분의 웹 애플리케이션은 Node.js 패키지 형태로 구현된 CSS 프레임워크를 내장하는 형태로 배포한다.
웹 안전 글꼴과 fontsource
@import
규칙은 웹에 안전한 글꼴, 즉 웹 안전 글꼴(Web-Safe Fonts)을 사용해야 한다는 제약이 있다. 웹 안전 글꼴이란 데스크탑, 모바일 등 모든 디바이스에서 동작하는 모든 브라우저에 적용할 수 있는 글꼴이다. 사용자 컴퓨터에 설치되지 않은 때에도 웹 페이지에 항상 올바르게 표시되는 글꼴을 의미한다.
구글이 제공하는 모든 글꼴은 웹 안전 글꼴이므로 @import
규칙을 적용할 수 있다. fontsource는 구글 글꼴과 같은 오픈소스 웹 안전 글꼴을 패키지 형태로 설치해 준다.
yarn add @fontsource/스네이크-표기법-글꼴명
yarn add @fontsource/material-icons
Icon 컴포넌트 구현하기
Icon 컴포넌트는 Material에서 제공하는 아이콘을 렌더링하는 재사용 가능한 컴포넌트이다. name
은 아이콘의 이름을, style
은 스타일을 지정하는 데 사용된다. { …props }
를 사용하여 전달받은 모든 props
를 전달할 수 있는데, 이로 인해 style={style}
형태의 코드를 생략했다.
import { CSSProperties } from 'react';
export type IconProps = { name: string; style?: CSSProperties };
export const Icon = ({ name, ...props }: IconProps) => {
return (
<span {...props} className="material-icons">
{name}
</span>
);
};
아마 Icon을 사용하는 부모 컴포넌트는 이렇게 사용될 것이다.
<Icon name="home" style={{ color: 'red' }} />
<Icon name="check_circle_outline" style={{ color: 'blue' }} />
그런데, { …props }
방식은 편리하지만 단점이 있다. 예를 들어, 아래 코드처럼 글로벌 스타일에 적용된 .text-blue: { color: blue; }
같은 클래스 선택자를 사용하고 싶다면?
<Icon name="home" className="text-blue" />
<Icon name="check_circle_outline" className="text-red" style={{fontSize: '50px'}}/>
Icon 컴포넌트에 className
prop
을 전달하는 방법이 떠오를 것이다. 앞서 말했듯이 { …props }
를 사용하면 className
속성이 <span>
요소에 자동으로 전달된다. 이로 인해 className
속성이 중복되거나 충돌할 수 있으며, 이는 의도하지 않은 스타일이 적용되는 문제를 초래할 수 있다.
- 우선순위 충돌: 두 개 이상의 CSS 클래스 또는 스타일이 동일한 요소에 적용될 때, 우선순위 충돌이 발생할 수 있다. 이는 CSS 스타일 규칙에 따라 결정된다. 예를 들어, 더 구체적인 선택자가 더 높은 우선순위를 갖는다.
- 의도하지 않은 스타일 적용: 부모 컴포넌트에서 Icon 컴포넌트에 전달된
className
이나 스타일이 아이콘 컴포넌트에 이미 적용된 스타일과 충돌할 경우, 의도하지 않은 스타일이 적용될 수 있다. 이는 컴포넌트 간의 결합도가 높아져 유지 보수가 어려워질 수 있다. - 스타일 오버라이딩:
className
이나style
prop
을 통해 전달된 스타일이 기존에 적용된 스타일을 덮어쓰는 경우가 있을 수 있다. 이는 의도하지 않은 결과를 초래할 수 있으며, 디자인의 일관성을 해치는 요소가 될 수 있다.
이를 방지하기 위해서는 CSS 클래스를 조합하여 사용하거나, CSS 모듈을 사용하여 스타일을 지정하는 것이 좋은 해결책이 될 수 있다.
리액트가 제공하는 DetailedHTMLProps와 HTMLAttributes 타입
리액트 프레임워크는 한꺼번에 특정 HTML 요소의 속성을 추가할 수 있게 해주는 DetailedHTMLProps와 HTMLAttributes 타입을 제공한다. 즉, 아래 코드는 <span>
요소의 모든 속성을 표현하는 타입이다!
React.DetailedHTMLProps<React.HTMLAttributes<HTMLSpanElement>,HTMLSpanElement>;
TypeScript의 교집합 타입 구문
이제 IconProps 타입은 ReactSpanProps 타입이면서 동시에 { name: string }
인 타입이다. 완성된 Icon 컴포넌트를 살펴보자.
type ReactSpanProps = React.DetailedHTMLProps<
React.HTMLAttributes<HTMLSpanElement>,
HTMLSpanElement
>;
export type IconProps = ReactSpanProps & { name: string };
export const Icon = ({ name, className: newClassName, ...props }: IconProps) => {
const className = ['material-icons', newClassName].join(' ');
return (
<span {...props} className={className}>
{name}
</span>
);
};
- ReactSpanProps: HTMLSpanElement에 대한 HTML 속성을 상세하게 정의하는 타입이다. 이는 리액트에서 제공하는 DetailedHTMLProps를 사용하여
<span>
요소에 대한 모든 속성을 포함한다. - IconProps 타입:
IconProps
는ReactSpanProps
와name
속성을 포함하는 타입이다. 이는<span>
요소에 대한 모든 HTML 속성과 아이콘의 이름을 포함한다. className: newClassName
을 비구조화 할당하여 새로운 이름으로className
을 받아들인다. 이를 통해 부모 컴포넌트에서 전달된className
을 사용할 수 있다.['material-icons', newClassName].join(' ')
을 사용하여 기존의 클래스에 'material-icons' 클래스를 추가한다. 이를 통해 아이콘 폰트를 적용할 수 있다.
CSS
Cascading Style Sheets. 이미 알고 있듯이 HTML이나 XML 같은 마크업 언어로 작성된 문서의 스타일을 정의하는 언어이다. JavaScript 같은 프로그래밍 언어들과 마찬가지로 CSS 또한 편하게 작성하기 위한 새로운 문법 역시 지속적으로 제시되어 왔다. 그 중 Sass/SCSS
와 PostCSS
에 대해 먼저 알아본 후, 본격적으로 TailwindCSS
를 배워볼 것이다.
CSS 전, 후 처리기
CSS 작업을 보다 효율적으로 만들어주는 도구들이다. 이를테면 중복 제거, 유지 보수 등 말이다.
전처리기
전처리기는 스타일시트를 작성하기 전에 사용자가 작성하기 쉬운 확장된 문법을 사용하여 스타일시트를 작성할 수 있게 해준다. 이 작업은 개발자가 사용자 정의 함수나 변수, mixin, 중첩 규칙 등을 사용하여 보다 유연하고 효율적으로 CSS를 작성할 수 있도록 한다. 이렇게 작성된 전처리기 코드는 일반적으로 컴파일러를 통해 기본 CSS로 변환되어 브라우저에 전달되어 렌더링 된다.
후처리기
후처리기는 CSS를 생성한 후에 추가적인 처리를 적용하여 스타일시트를 변경한다. 이는 보통 브라우저에서 스타일시트가 이미 렌더링된 후에 적용되는 것이다. 후처리기는 일반적으로 기본 CSS 파일을 작성하고, 이 파일에 추가적인 변환 및 처리를 적용하여 새로운 CSS 파일을 생성한다. 이러한 처리는 주로 웹사이트의 성능 최적화, 크로스 브라우저 호환성을 위한 접두사(prefix) 추가, 코드 압축 등의 목적으로 사용된다.
따라서, 전처리기는 스타일시트를 작성하기 전에 코드를 변경하고, 후처리기는 이미 렌더링된 스타일시트에 대해 추가적인 변경을 적용한다.
Sass/SCSS
Sass(Syntactically Awesome Style Sheets)
CSS의 확장된 문법을 제공하여 스타일시트를 더 효율적으로 작성할 수 있도록 도와준다. Sass
는 변수, 중첩 규칙, mixin 등과 같은 기능을 제공하여 코드의 반복을 줄이고 가독성을 향상시킨다.
SCSS(Sassy CSS)
Sass
의 최신 버전으로, CSS와 거의 동일한 문법을 사용한다. Sass
의 기능을 그대로 포함하고 있으며, 기존 CSS 코드도 쉽게 포팅할 수 있도록 한다.
PostCSS
우선, PostCSS
는 후처리기이다. JavaScript를 사용하여 CSS를 변경하고 확장하는 도구이며, Sass
와 달리 CSS 표준 문법을 사용한다. PostCSS
는 플러그인 기반으로 동작하여 사용자가 필요한 기능을 선택적으로 추가할 수 있다. Autoprefixer
는 그 중 하나로, 자동으로 웹 브라우저 호환성을 위한 접두사(prefix)를 추가해준다. 특징은 다음과 같다.
- 플러그인 기반 아키텍처:
PostCSS
는 플러그인 기반으로 동작하며, 사용자가 필요에 따라 원하는 기능을 선택하여 사용할 수 있다. 이는 유연성과 확장성을 제공한다. - JavaScript 사용:
PostCSS
는 JavaScript로 작성되어 있으며, 이는 JavaScript의 강력한 기능과 생태계를 활용할 수 있음을 의미한다. - 성능:
PostCSS
는 높은 성능을 보장한다. 플러그인들은 효율적으로 작동하며, 변환된 결과물도 빠르게 생성된다. - 모듈화된 구조:
PostCSS
는 모듈화된 구조를 가지고 있어, 필요한 기능을 가진 플러그인을 간편하게 추가하고 구성할 수 있다.
TailwindCSS
TailwindCSS
는 앞서 배운 전처리기와 PostCSS
의 장점을 살려 더욱 효율적으로 CSS를 작성할 수 있게 해준다. 현재 CSS를 작성하기 위한 가장 선호도가 높은 프레임워크로서, UI 디자인을 빠르고 효율적으로 구축할 수 있도록 도와준다.
TailwindCSS 사용하기
autoprefixer
CSS 관점에서 브라우저 호환성 문제는 -webkit, -moz, -ms 등으로 대표되는 벤더 접두사 문제이다. 즉, CSS 표준은 linear-gradient지만, 구글 크롬이나 애플 사파리 브라주어에서는 -webkit-linear-gradient를, 마이크로소프트 브라우저에서는 -ms-leanear-gradient와 같은 이름으로 사용해야 하는 문제이다.
autoprefixer
는 대표적인 PostCSS
플러그인으로 이러한 벤더 접두사 문제를 해결해 주는 역할이다. 사용자 CSS가 벤더 접두사를 붙이지 않더라도 후처리 과정에서 자동으로 벤더 접두사가 붙은 CSS를 생성해 준다. 그리고 이런 auto prefixer 기능을 사용하려면 PostCSS
도 함께 설치해야 한다.
yarn add -D postcss autoprefixer tailwindcss
구성 파일 만들기
TailwindCSS
는 PostCSS
의 플러그인 형태로 동작하며 PostCSS
가 TailwindCSS
를 플러그인으로 동작시키려면 postcss.config.js
파일에 TailwindCSS
를 등록해야 한다. 그리고 TailwindCSS
는 PostCSS
와는 별도로 자신만의 구성 파일 tailwind.config.js
이 있어야 한다.
npx tailwindcss init -p
위 커맨드를 실행하면 postcss.config.js
파일과 tailwind.config.js
파일이 생성된다. 프로젝트에서 사용할 PostCSS
플러그인 및 구성을 지정할 수 있고, TailwindCSS
를 사용할 때 사용자 정의 등 프로젝트에 맞게 조정하는 데 사용된다.
TailwindCSS 기능 반영하기
TailwindCSS
를 사용하려면 css 파일에 아래처럼 추가해야 한다.
@tailwind base;
@tailwind components;
@tailwind utilities;
이제 TailwindCSS
를 사용할 준비가 끝났다. 공식 홈페이지는 색상, 텍스트 스타일링, 위치 설정, 레이아웃 등 다양한 기능에 대한 클래스 및 사용 방법을 제공한다.
예를 들어, 색상 클래스는 black과 white 색상의 무채색은 아래와 같은 규칙으로 클래스를 제공한다. 여기서 ‘/불투명도’ 는 생략할 수 있다. 접두사는 bg, text, border 등을 사용할 수 있다.
접두사-색상명/불투명도
bg-black/70
반면에 빨강, 파랑 등 유채색은 다음과 같은 규칙으로 클래스를 제공한다. 물론 여기서도 ‘/불투명도’는 생략할 수 있다.
접두사-색상_이름-채도/불투명도
text-gray-50
TailwindCSS
의 클래스들은 토큰으로 이루어져 있고, CSS 속성이나 값을 나타낸다. 처음에는 단위나 표현 방법을 외워야 하기 때문에 어려웠다. 역시 예제나 프로젝트 작업을 하면서 익숙해졌고 나름 재미있었다. 토큰들을 잘 사용할 줄 알면 원하는 스타일 적용하는 데 간편하게 할 수 있다.