리액트에서 컴포넌트를 스타일링할 때는 다양한 방식을 사용할 수 있다. 스타일링 방식은 다음과 같은 것들이 있다.
일반 CSS : 컴포넌트를 스타일링하는 가장 기본적인 방식이다.
Sass : 자주 사용되는 CSS 전처리기(pre-processor) 중 하나로 확장된 CSS 문법을 사용하여 CSS 코드를 더욱 쉽게 작성할 수 있도록 해 준다.
CSS Module : 스타일을 작성할 때 CSS 클래스가 다른 CSS 클래스의 이름과 절대 충돌하지 않도록 파일마다 고유한 이름을 자동적으로 생성해 주는 옵션이다.
styled-componenets : 스타일을 자바스크립트 파일에 내장시키는 방식으로 스타일을 작성함과 동시에 해당 스타일이 적용된 컴포넌트를 만들 수 있게 해 준다.
프로젝트는 일반 CSS 방식으로 만들어져 있다. 기존의 CSS 스타일링이 딱히 불편하지 않고 새로운 기술을 배울 필요가 없다고 생각되면, 일반 CSS를 계속 사용해도 상관없다.
실제로도 소규모 프로젝트를 개발하고 있다면 새로운 스타일링 시스템을 적용하는 것이 불필요할 수 있다. 그런 상황에서는 프로젝트에 이미 적용되어 있는 기본 CSS 시스템을 사용하는 것만으로도 충분하다.
CSS를 작성할 때 가장 중요한 점은 CSS 클래스를 중복되지 않게 만드는 것이다. CSS 클래스가 중복되는 것을 방지하는 여러 가지 방식이 있는데, 그중 하나는 이름을 지을 때 특별한 규칙을 사용하여 짓는 것이고, 또 다른 하나는 CSS Selector를 활용하는 것이다.
클래스 이름을 컴포넌트 이름 - 클래스 형태로 지으면, 다른 컴포넌트에서 실수로 중복되는 클래스를 만들어 사용하는 것을 방지할 수 있다. 비슷한 방식으로 BEM 네이밍이라는 방식도 있는데, 이름을 지을 때 일종의 규칙을 준수하여 해당 클래스가 어디에서 어떤 용도로 사용되는지 명확하게 작성하는 방식이다.
CSS Selector를 사용하면 CSS 클래스가 특정 클래스 내부에 있는 경우에만 스타일을 적용할 수 있다. 예를 들어 .App 안에 들어 있는 .logo에 스타일을 적용하고 싶다면 다음과 같이 작성하면 된다.
.App .logo {
animation: App-logo-spin infinite 20s linear;
height: 40vmin;
}
Sass(Syntactically Awesome Style Sheets)(문법적으로 매우 멋진 스타일시트)는 CSS 전처리기로 복잡한 작업을 쉽게 할 수 있도록 해 주고, 스타일 코드의 재할용성을 높여 줄 뿐만 아니라 코드의 가독성을 높여서 유지 보수를 더욱 쉽게 해준다.
Sass에서는 두 가지 확장자 .scss와 .sass를 지원한다. .scss와 .sass의 주요 차이점을 살펴보면, .sass 확장자는 중괄호({})와 세미콜론(;)을 사용하지 않는다. 반면, .scss 확장자는 기존 CSS를 작성하는 방식과 비교해서 문법이 크게 다르지 않다.
// 변수 사용하기
$red: #fa5252;
$orange: #fd7e14;
$yellow: #fcc419;
$green: #40c057;
$blue: #339af0;
$indigo: #5c7cfa;
$violet: #7950f2;
//믹스인 만들기(재사용되는 스타일 블록을 함수처럼 사용할 수 있음)
@mixin square($size) {
$calculated: 32px * $size;
width: $calculated;
height: $calculated;
}
.SassComponent {
display: flex;
.box {
background: red;
cursor: pointer;
transition: all 0.3s ease-in;
&.red {
// .red 크래스가 .box와 함께 사용되었을 때
background: $red;
@include square(1);
}
&.orange {
background: $orange;
@include square(2);
}
&.yellow {
background: $yellow;
@include square(3);
}
&.green {
background: $green;
@include square(4);
}
&.blue {
background: $blue;
@include square(5);
}
&.indigo {
background: $indigo;
@include square(6);
}
&.violet {
background: $violet;
@include square(7);
}
&:hover {
// .box에 마우스를 올렸을 때
background: black;
}
}
}
여러 파일에서 사용될 수 있는 Sass 변수 및 믹스인은 다른 파일로 따로 분리하여 작성한 뒤 필요한 곳에서 쉽게 불러와 사용할 수 있다.
Sass의 장점 중 하나는 라이브러리를 쉽게 불러와서 사용할 수 있다는 점이다. 물결 문자를 사용하면 자동으로 node_modules에서 라이브러리 디렉터리를 탐지하여 스타일을 불러올 수 있다.
CSS Module은 CSS를 불러와서 사용할 때 클래스 이름을 고유한 값, 즉 [파일 이름]_[클래스 이름]_[해시값]
형태로 자동으로 만들어서 컴포넌트 스타일 클래스 이름이 중첩되는 현상을 방지해 주는 기술이다.
CSS Module을 사용하면 클래스 이름을 지을 때 그 고유성에 대해 고민하지 않아도 된다. 해당 클래스는 스타일을 직접 불러온 컴포넌트 내부에서만 작동하기 때문에, 흔히 사용하는 단어로 이름을 짓는다고 해도 전혀 문제가 되지 않는다.
만약 특정 클래스가 웹 페이지에서 전역적으로 사용되는 경우라면 :global을 앞에 입력하여 글로벌 CSS임을 명시해 줄 수 있다.
CSS Module을 사용한 클래스 이름을 두 개 이상 적용할 때는 다음과 같이 코드를 작성하면 된다.
위 코드에서는 ES6 문법 템플릿 리터럴(Template Literal)을 사용하여 문자열을 합해 주었다. 이 문법을 사용하면 문자열 안에 자바스크립트 레퍼런스를 쉽게 넣어 줄 수 있다.
const name = '리액트'
const message = `제 이름은 ${name}입니다.`;
컴포넌트 스타일링의 또 다른 패러다임은 자바스크립트 파일 안에 스타일을 선언하는 방식이다. 이 방식을 'CSS-in-JS'라고 부르는데, 이와 관련된 라이브러리는 정말 많다. 여기서는 CSS-in-JS 라이브러리 중에서 개발자들이 가장 선호하는 styled-components를 알아보자.
import React from "react";
import styled, { css } from "styled-components";
const Box = styled.div`
/* props로 넣어 준 값을 직접 전달해 줄 수 있다. */
background: ${(props) => props.color || "blue"};
padding: 1rem;
display: flex;
`;
const Button = styled.button`
background: white;
color: black;
border-radius: 4px;
padding: 0.5rem;
display: flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
font-size: 1rem;
font-weight: 600;
/* & 문자를 사용하여 Sass처럼 자기 자신 선택 가능 */
&: hover {
background: rgba(255, 255, 255, 0.9);
}
/* 다음 코드는 inverted 값이 true일 때 특정 스타일을 부여해 준다. */
${(props) =>
props.inverted &&
css`
background: none;
border: 2px solid white;
color: white;
&: hover {
background: white;
color: black;
}
`};
& + button {
margin-left: 1rem;
}
`;
const StyledComponents = () => (
<Box color="black">
<Button>안녕하세요</Button>
<Button inverted={true}>테두리만</Button>
</Box>
);
export default StyledComponents;
앞에서 작성한 코드를 확인해 보면, 스타일을 작성할 때 백틱을 사용하여 만든 문자열에 스타일 정보를 넣어 주었다. 여기서 사용한 문법을 Tagged 템플릿 리터럴이라고 부른다. CSS Module을 배울 때 나온 일반 템플릿 리터럴과 다른 점은 템플릿 안에 자바스크립트 객체나 함수를 전달할 때 온전히 추출할 수 있다는 것이다.
`hello ${{foo: 'bar'}} ${() => 'world'}!`
// 결과: "hello [object Object] () => 'world' !"
템플릿에 객체를 넣거나 함수를 넣으면 형태를 잃어 버리게 된다. 객체는 "[object Object]"로 변환되고, 함수는 함수 내용이 그대로 문자열화되어 나타난다.
만약 다음과 같은 함수를 작성하고 나서 해당 함수 뒤에 템플릿 리터럴을 넣어 준다면, 템플릿 안에 넣은 값을 온전히 추출할 수 있다.
functionm tagged(...args) {
console.log(args);
}
tagged`hello ${{foo: 'bar' }} ${() => 'world'}!`
styled-components를 사용하여 스타일링된 엘리먼트를 만들 때는 컴포넌트 파일의 상단에서 styled를 불러오고 styled. 태그명을 사용하여 구현한다.
import styled from 'styled-components';
const MyComponent = styled.div`
font-size: 2rem;
`;
이렇게 styled.div 뒤에 Tagged 템플릿 리터럴 문법을 통해 스타일을 넣어 주면, 해당 스타일이 적용된 div로 이루어진 리액트 컴포넌트가 생성된다. 그래서 나중에 <MyComponent>Hello</MyComponent>
와 같은 형태로 사용할 수 있다.
div가 아닌 button이나 input에 스타일링을 하고 싶다면 styled.button 혹은 styled.input 같은 형태로 뒤에 태그명을 넣어 주면 된다.
하지만 사용해야 할 태그명이 유동적이거나 특정 컴포넌트 자체에 스타일링해 주고 싶다면 다음과 같은 형태로 구현할 수 있다.
// 태그의 타입을 styled 함수의 인자로 전달
const MyInput = styled('input')`
background: gray;
`
// 아예 컴포넌트 형식의 값을 넣어 줌
const StyledLink = styled(Link)`
color: blue;
`
styled-components를 사용하면 스타일 쪽에서 컴포넌트에게 전달된 props 값을 참조할 수 있다.
const Box = styled.div`
/* props로 넣어 준 값을 직접 전달해 줄 수 있다. */
background: ${props => props.color || 'blue'};
padding: 1rem;
display: flex;
`;
이 코드를 보면 background 값에 props를 조회해서 props.color의 값을 사용하게 했다. 그리고 color 값이 주어지지 않았을 때는 blue를 기본 색상으로 설정했다.
이렇게 만들어진 코드는 JSX에서 사용될 때 다음과 같이 color값을 props로 넣어 줄 수 있다.
<Box color="black">(...)</Box>
일반 CSS 클래스를 사용하여 조건부 스타일링을 해야 할 때는 className을 사용하여 조건부 스타일링을 해왔지만, styled-components에서는 조건부 스타일링을 간단하게 props로도 처리할 수 있다.
/* 다음 코드는 inverted 값이 true일 때 특정 스타일을 부여해 준다. */
${(props) =>
props.inverted &&
css`
background: none;
border: 2px solid white;
color: white;
&: hover {
background: white;
color: black;
}
`};
이렇게 만든 컴포넌트는 다음과 같이 props를 사용하여 서로 다른 스타일을 적용할 수 있다.
<Button>안녕하세요</Button>
<Button inverted={true}>테두리만</Button>
스타일 코드 여러 줄을 props에 따라 넣어 주어야 할 때는 CSS를 styled-components에서 불러와야 한다. CSS를 사용하지 않고 바로 문자열을 넣어도 작동하기는 하지만, 이렇게 했을 때는 해당 내용이 그저 문자열로만 취급되기 때문에 VS Code에서 신택스 하이라이팅이 제대로 이루어지지 않고, Tagged 템플릿 리터럴이 아니기 때문에 함수를 받아 사용하지 못해 해당 부분에서는 props 값을 사용하지 못한다.
그렇기 때문에 props를 참조한다면 반드시 CSS로 감싸 주어서 Tagged 템플릿 리터럴을 사용해 주어야 한다.
이번에는 styled-components를 사용할 때 반응형 디자인을 어떻게 하는지 한번 알아보자. 브라우저의 가로 크기에 따라 다른 스타일을 적용하기 위해서는 일반 CSS를 사용할 때와 똑같이 media 쿼리(query)를 사용하면 된다.
이러한 방법은 일반 CSS에서 할 때와 큰 차이가 없지만, 여러 컴포넌트에서 반복해야 한다면 조금 귀찮을 수도 있다. 그럴 때는 이 작업을 함수화하여 간편하게 사용할 수 있다.