[React] Emotion.js 소개 및 사용법
01. Emotion이란?
Emotion은 분리된 CSS 파일을 만드는 것 대신에 자바스크립트로 모든 스타일을 작성할 수 있도록 하는 JavaScript 라이브러리이다.
스타일의 범위 지정, 조건부 및 동적 스타일, 재사용 가능성 및 유지 관리와 같은 기능을 제공한다.
JS로 작성된 스타일은 결과적으로 통합되어 통합된 CSS파일을 생성한다.
사용하는 이유?
웹 컴포넌트의 UI를 구성할 때 styled component를 사용하는 경우도 있다. 하지만 아래와 같은 Emotion의 특징으로 인해 현재는 Emotion.js의 사용률이 styled component를 능가한 상황이다.
- props, 조건 등에 따라 스타일을 지정할 수 있다.
- 반응형을 쉽고 빠르게 적용할 수 있다.
- CSS Props 기능을 제공한다.
- Server Side Rendering시 세팅이 간편하다.
02. 사용법
설치
Emotion을 설치하기 위해 다음 패키지를 설치한다.
yarn add @emotion/react @emotion/styled
style
폴더에 스타일 파일을 저장해두고 사용하며, 자주 사용하는 컴포넌트와 스타일을 글로벌 UI로 하여 각 요소에서 중복되는 코드를 최대한 줄인다.
style
내부에 추가 폴더를 생성하여 스타일의 용도와 범위에 따라 각 스타일 파일을 정리해두면 유지보수에 용이할 것이다.
ex. component, theme, layout, libs
css 이용
import { css } from "@emotion/react";
export const calcRem = (px: number) => `${px / 16}rem`;
export const typo = {
Header_28: css`
font-family: "Pretendard";
font-size: ${calcRem(28)};
line-height: 150%;
font-weight: 700;
`,
Header_24: css`
font-family: "Pretendard";
font-size: ${calcRem(24)};
line-height: 150%;
font-weight: 700;
`,
Header_20: css`
font-family: "Pretendard";
font-size: ${calcRem(20)};
line-height: 150%;
font-weight: 700;
`,
};
Emotions에서 css prop을 불러와 이용한다. css 선언은 객체 형식으로 넘겨도 되고, 문자열 형식으로 넘겨도 된다. 위의 typo는 문자열 형식으로 넘긴 것이며 아래는 객체 형식으로 넘긴 것이다. 다만, Emotion 공식 문서상에서 가급적 스타일을 객체로 선언하는 것을 권장한다.
객체형으로 넘길 경우 css() 함수의 호출을 생략하고 컴포넌트 css prop에 객체를 바로 넘길 수 있다. 또한 TypeScript 사용 시 type chenking을 통해 오타를 방지할 수 있다.
/** @jsxImportSource @emotion/react */
// JSX pragma : React의 jsx() 함수를 사용하지 말고, Emotion의 jsx() 함수를 대신 사용하라고 알려주기 위함
// css prop에 넘어간 스타일이 제대로 반영이 되지 않을테니 주의
import { css } from "@emotion/react";
function MyComponent() {
return (
<div
css={css({
backgroundColor: "yellow",
})}
>
노란색 영역
</div>
);
}
여기서 주의해야 할 점은 /** @jsxImportSource @emotion/react */
부분인데, 이는 JSX pragma이다. Babel을 사용하는 경우 트랜스파일러한테 JSX 코드를 변환할 때, React의 jsx() 함수를 사용하지 말고, Emotion의 jsx() 함수를 사용하라고 알려주기 위해 사용하는 것이다. 해당 내용을 작성하지 않으면 css 함수에 prop으로 넘겨준 것이 제대로 적용되지 않는다.
styled component 이용
Emotion은 styled component 또한 지원한다. 이 또한 마찬가지로 객체 혹은 문자열 형식으로 넘길 수 있다.
import styled from "@emotion/styled";
export const Heading = styled.h1`
background-color: ${(props) => props.bg};
color: ${(props) => props.fg};
`;
이렇게 생성한 Heading을 사용할 때 Heading에 색상값을 전달해 줄 수 있다.
<Heading bg="#008f68" fg="#fae042">
Heading with a green background and yellow text.
</Heading>
또한 문자열 대신 객체를 넘길 수도 있다.
export const Quote = styled("blockquote")((props) => ({
fontSize: props.size,
}));
이는 기본 스타일의 개체도 포함한다.
const Cite = styled("cite")(
{
fontWeight: 100,
},
(props) => ({
fontWeight: props.weight,
})
);
${}: 부모-자식간 타게팅 가능
import styled from "@emotion/styled";
export const Child = styled.div`
color: red;
`;
export const Parent = styled.div`
${Child} {
color: green;
}
`;
return (
<div>
<Parent>
<Child>Green because I am inside a Parent</Child>
</Parent>
<Child>Red because I am not inside a Parent</Child>
</div>
);
혹은 이런 식으로 객체형으로 사용될 수도 있다.
const Child = styled.div({
color: "red",
});
const Parent = styled.div({
[Child]: {
color: "green",
},
});
&: nest selector를 사용할 수 있다.
import styled from '@emotion/styled'
const Example = styled.('span')`
color: lightgreen;
& > strong {
color: hotpink;
}
`
return (
<Example>
This is <strong>nested</strong>
</Example>
)
Composition
- Emotion의 가장 강력하고 유용한 패턴 중 하나이다.
- 일반 CSS를 사용할 경우 여러 클래스 이름을 사용하여 style을 함께 구성할 수 있지만 정의되는 순서가 곧 스타일이 적용되는 순서이기 때문에 제한적이다.
- Composition을 사용하면 style이 사용되는 순서대로 적용된다.
import {css} from '@emotion/react'
const danger = css`
color: red;
`
const base = css`
background-color: darkgreen;
color: turquoise;
`
return (
<div>
<div css={base}>This will be turquoise</div>
<div css={[dange, base]}>This will be also be turquoise since the base styled overwrite the danger styles</div>
</div>
<div css={[base, danger]}>This will be red</div>
</div>
)
예시
/** @jsxImportSource @emotion/react */
import { css } from "@emotion/react";
import styled from "@emotion/styled";
const Heading = styled("h1")`
background-color: ${(props) => props.bg};
color: ${(props) => props.fg};
`;
const Subheading = Heading.withComponent("h2");
const Quote = styled("blockquote")((props) => ({
fontSize: props.size,
}));
const Cite = styled("cite")(
{
fontWeight: 100,
},
(props) => ({
fontWeight: props.weight,
})
);
const Footer = styled("footer")`
border-top: 1px solid #ccc;
color: #ccc;
margin-top: 50px !important;
padding-top: 20px;
`;
function App() {
return (
<div
css={css`
background: #ddd;
`}
>
{" "}
// 문자형
<div css={css({ padding: 10 })}>
{" "}
// 객체형
<Heading bg="#008f68" fg="#fae042">
Quotations
</Heading>
<Subheading fg="#6db65b">For React Developers</Subheading>
<Quote size={28}>
I built this with <code>`emotion/react`</code> and{" "}
<code>`emotion/styled`</code>!
</Quote>
<Cite weight={700}>Sammy</Cite>
<Footer>Shark Facts</Footer>
</div>
</div>
);
}
export default App;
References
Eugene - [커비샵 개발일지 #2] emotion과 global ui 세팅
DigitalOcean - How To Use Emotion for Styling in React
댓글남기기