React 스타일 라이브러리는 항상 Styled components만 사용했었는데 이번 토이 프로젝트를 기회로 다른 스타일 라이브러리인 Emotion을 사용해 보기로 했다.
해당 프로젝트는 React 프레임워크를 사용해 만들어졌기 때문에 이에 권장되는 @emotion/react 패키지를 설치하여 사용했다. 다음은 해당 프로젝트에서 내가 한 번씩 사용해 본 기능들을 정리해보았다.
Emotion에서는 따로 선언된 스타일 블록들을 합쳐서 함께 스타일을 구성할 수 있다. 예를 들어 이렇게 base 라는 객체를 선언한 뒤, jsx에서 스타일을 지정할 때 이를 함께 넣어줄 수 있다.
중요한 것은 원래 css에서도 여러 클래스를 사용하여 스타일을 함께 구성할 수 있지만, 가장 나중에 선언된 스타일이 그 앞선 스타일들을 덮어씌우므로 !important를 쓰거나 필요할 때마다 스타일을 재선언할 수 밖에 없다. (보통 !important의 사용은 기피된다.)
Emotion에서는 Composition을 통해 스타일을 적용하는 순서대로 스타일이 병합되기 때문에 스타일이 선언된 순서를 생각할 필요가 없다. !important를 쓰거나 스타일을 재선언할 필요가 없다.
import { css } from '@emotion/react'
const base = css`
color: hotpink;
`
render(
<div
css={css`
${base};
background-color: #eee;
`}
>
This is hotpink.
</div>
)
실제 사용된 코드. 이렇게 배열 형태로 병합할 수 있다.
const progress = css`
height: 100%;
background: ${theme.palette.blue};
transition-duration: 0.5s;
`;
// 생략
<div css={[progress, { width: `${(100 * (count + 1)) / 11}%` }]} />
Themeing은 @emotion/react 패키지에 포함되어 있다. 위에 Composition을 설명하면서 작성한 코드에 theme.palette.blue를 설명할 차례다.
작은 토이 프로젝트이지만 프로젝트에서 사용되는 색상들을 한 곳에서 관리하고 싶어서 이를 사용하게 되었다.
먼저 styles 폴더 안에 theme.js 파일을 다음과 같이 작성한다.
const palette = {
black: "rbg(75, 85, 99)",
gray: "rgb(107,114,128)",
lightgray: "rgb(209, 213, 219)",
blue: "rgb(42, 77, 208)",
};
const theme = {
palette
};
export default theme;
그리고 프로젝트 최상단에 위치한 root.jsx 파일의 jsx를 ThemeProvider로 감싸고 앞서 작성한 theme.js 파일을 적용시켜준다.
import { ThemeProvider } from "@emotion/react";
import theme from "./styles/theme";
return (
<ThemeProvider theme={theme}>
// ...
<Outlet />
// ...
</ThemeProvider>
);
그러면 하위 파일들은 이 theme을 활용하여 스타일을 적용시킬 수 있게 된다. 하위 파일에서 theme은 useTheme 훅을 사용하여 가져올 수 있다.
const theme = useTheme();
const progress = css`
height: 100%;
background: ${theme.palette.blue};
transition-duration: 0.5s;
`;
Emotion에서 미디어 쿼리를 사용하는 것은 일반 css에서 사용하는 것과 비슷하다. 프로젝트의 최상단에 위치한 root.jsx 파일에 다음과 같이 브레이크 포인트를 설정하고 이를 jsx에 적용시킨다.
const breakpoints = [0, 640, 1140];
const mq = breakpoints.map((bp) => `@media (min-width: ${bp}px)`);
return (
// ...
<div
css={
{
[mq[0]]: { fontSize: "18px" },
[mq[1]]: { fontSize: "22px" },
},
}
>
<Outlet />
// ...
</div>
);
이 때 facepaint 라이브러리를 설치하면 미디어쿼리를 간편하게 쓸 수 있다.
const breakpoints = [0, 640, 1140];
const mq = facepaint(breakpoints.map((bp) => `@media (min-width: ${bp}px)`));
return (
// ...
<div
css={
mq({
fontsize: ["18px", "22px"]
}),
}
>
<Outlet />
// ...
</div>
);
Keyframes는 @emotion/react 패키지에 포함되어 있다. Emotion에서 Keyframes를 사용하는 것은 일반 css에서 사용하는 것과 비슷하다.
loading spinner를 구현하기 위해 다음과 같이 작성하였다.
import { css, keyframes } from "@emotion/react";
function LoadingSpinner() {
const spin = keyframes`
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
`;
const loader = css`
border: 16px solid #f3f3f3;
border-top: 16px solid #3498db;
border-radius: 50%;
width: 50px;
height: 50px;
animation: ${spin} 2s linear infinite;
`;
return <div css={loader} />;
}
return (
<LoadingSpinner />
);