styled-components, emotion 비교
프로젝트를 구축하기 전 CSS-in-JS로 유명한 두개 라이브러리 styled-components와 emotion을 비교하고
우리 환경에 맞는 라이브러리를 택하기 위해서 비교를 하게 되었다.
styled-components와 emotion 문법만 조금 다를 뿐 동일한 기능을 가지고 있어서 더 선택하기 힘들었던 것 같다.
트렌드
트렌드를 확인해 보면 비슷하게 사용되고 있는것을 볼 수 있다.
@emotion/core 10버전 -> @emotion/react 11버전
크기
styled-components가 emotion에 비해 크기가 크지만 유의미한 차이는 아닌 것같다.
styled-components@5.3.6
https://bundlephobia.com/package/styled-components@5.3.6
@emotion/react@11.10.6
https://bundlephobia.com/package/@emotion/react@11.10.6
기본 문법
styled-components
컴포넌트 형식으로 사용되어 보기가 좋고, 의미를 부여할 수 있어서 컨텐츠를 확인하기도 수월하다
import styled from 'styled-components';
const Wrapper = styled.div`
border: 1px solid red;
`;
const Box = styled.div`
background-color: blue;
width: 100px;
height: 100px;
`;
const App = () => {
return (
<Wrapper>
<Box>
박스입니다.
</Box>
</Wrapper>
)
}
export default App;
emotion
/** @jsxImportSource @emotion/react */
import { css } from '@emotion/react'
const wrapper = css`
border: 1px solid red;
`;
const box = css`
background-color: blue;
width: 100px;
height: 100px;
`;
const App = () => {
return (
<div css={wrapper}>
<div css={box}>
박스입니다.
</div>
</div>
)
}
export default App;
emotion에서는 styled문법도 제공한다.
/** @jsxImportSource @emotion/react */
import styled from '@emotion/styled';
const Wrapper = styled.div`
border: 1px solid red;
`;
const Box = styled.div`
background-color: blue;
width: 100px;
height: 100px;
`;
const App = () => {
return (
<Wrapper>
<Box>
박스입니다.
</Box>
</Wrapper>
)
}
export default App;
inline css
emotion는 인라인으로 css작성한 스타일을 class로 변경해 준다.
기존 inline으로 선언하지 못했던 media query, pseudo selector(가상 선택자), nested selector을 사용할 수 있다.
/** @jsxImportSource @emotion/react */
import { css } from '@emotion/react'
const wrapper = css`
border: 1px solid red;
`;
const App = () => {
return (
<div css={wrapper}>
// 인라인으로 작성한 emotion css코드
<div css={css`
background-color: blue;
width: 100px;
height: 100px;
`}>
박스입니다.
</div>
</div>
)
}
export default App;
브라우저 렌더링 화면
class가 적용되고 해당 클래스에 스타일이 적용되어있다.
hash 클래스 라벨링
CSS-in-JS 라이브러리는 자동으로 class에 hash를 붙임으로 충돌나지 않도록 해준다.
emotion, styled-components 모두 hash 라벨링을 제공한다.
emotion
https://emotion.sh/docs/labels
라벨링 방식을 제공하고 있다. css선언에 label속성을 넣으면 적용된다.
/** @jsxImportSource @emotion/react */
import { css } from '@emotion/react'
const wrapper = css`
border: 1px solid red;
label: test-one;
`;
const box = css`
background-color: blue;
width: 100px;
height: 100px;
label: some-two;
`;
const App = () => {
return (
<div css={wrapper}>
<div css={box}>
박스입니다.
</div>
</div>
)
}
export default App;
선언한 label속성값이 class 뒤에 적용된 걸 확인할 수 있다.
위에 라벨링을 제공하고 있지만 모든 스타일에 라벨링을 기재해야하는 번거로움과 컨벤션도 있어야하는 단점이 있을수있다.
babel 설정에서 labelFormat에 클래스명 규칙을 정하면 자동으로 생성하는 방법이 있다.
emotion labelFormat
labelFormat에서 제공하는 세가지 value
- [local] - the name of the variable the result of the css or styled expression is assigned to. // 스타일 변수 명
- [filename] - name of the file (without extension) where css or styled expression is located. //파일명
- [dirname] - name of the directory containing the file where css or styled expression is located. // 파일경로
// babel.js
{
"plugins": [
[
"@emotion",
{
"autoLabel": "dev-only",
"labelFormat": "myapp-[dirname]-[local]-[filename]",
}
]
]
}
labelFormat 적용된 클래스명
styled-components
아무 설정도 안하고 확인하면 해시값만 있는 클래스명을 확인할 수 있다.
styled-components의 Babel Macro를 사용하여 파일명_스타일명이 붙은 클래스명으로 확인 가능하다.
import styled, { createGlobalStyle } from 'styled-components/macro'
jsx pragma
프리셋을 사용하거나 프라그마를 설정하면, 컴파일되는 jsx 코드는 react.createElement 대신에
emotion에서 제공하는 jsx 함수를 사용하게 된다.
Emotion은 jsx 파일에 JSX Pragma를 작성해줘야하는데
/** @jsxImportSource @emotion/react */
이부분이 번거롭다면 babel preset 세팅을 해주면 된다.
cra프로젝트라면 .babelrc 설정을 cra에서 막아두어서 eject를 진행하고 해야한다.
// .babelrc
{
"presets": [
"@emotion/babel-preset-css-prop",
]
}
composition
css cascading을 더 편리하게 해줄 수 있는 기능이다.
emotion
https://emotion.sh/docs/composition
아래 코드에서 className으로 나중에 선언 된 danger이 적용이 될 것 같지만
className 선언순서로 적용이되는것이 아닌 style태그 내 css선언 순서로 적용이 된다.
const App = () => {
return (
<div>
<style>
{`
.danger {
color: red;
}
.base {
background-color: lightgray;
color: turquoise;
}
`}
</style>
<div className="base danger">
박스입니다.
</div>
</div>
)
}
export default App;
emotion에서 제공하는 composition을 사용해서 우선순위를 정할 수 있습니다.
const danger = css`
color: red;
`;
const base = css`
background-color: darkgreen;
color: turquoise;
`;
const App = () => {
return (
<div>
<div css={[base, danger]}>
텍스트1
</div>
<div css={[danger, base]}>
텍스트2
</div>
</div>
)
}
SSR
emotion
V10이전인 경우에만 설정이 필요하고 이상인경우는 SSR설정이 따로 필요없다고 나와있다.
styled-components
따로 설정이 필요하다.
styled components SSR
css extends
emotion, styled-components 라이브러리 모두 지원한다.
emotion
const btn = css`
font-size: 20px;
margin: 0 10px;
border: 1px solid red;
`;
const blackBtn = css`
${btn}
background-color: #000;
color: #fff;
`
const App = () => {
return (
<div>
<button css={btn}>버튼1</button>
<button css={blackBtn}>버튼2</button>
</div>
)
}
styled-components
import styled from 'styled-components';
const Btn = styled.button`
border: 1px solid red;
margin: 0 10px;
font-size: 20px;
background: gray;
`;
const WhiteBtn = styled(Btn)`
background-color: #fff;
color: #000;
`;
const BlackBtn = styled(Btn)`
background: #000;
color: #fff;
`;
const App = () => {
return (
<div>
<WhiteBtn>화이트 버튼</WhiteBtn>
<BlackBtn>블랙 버튼</BlackBtn>
</div>
)
}
export default App;
media query
emotion, styled-components 거의 동일하다.
emotion
const breakpoints = [320,768,1024,1440];
const media = breakpoints.map(
bp => `@media (min-width: ${bp}px)`
)
const btn = css`
font-size: 20px;
margin: 0 10px;
border: 1px solid red;
font-size: 30px;
${media[0]} { //@media (min-width: 320px)
font-size: 40px;
}
${media[1]} { //@media (min-width: 768px)
font-size: 50px;
}
`;
styled-components
const breakpoints = [320,768,1024,1440];
const media = breakpoints.map(
bp => `@media (min-width: ${bp}px)`
)
const Btn = styled.button`
font-size: 20px;
margin: 0 10px;
border: 1px solid red;
font-size: 30px;
${media[0]} { //@media (min-width: 320px)
font-size: 40px;
}
${media[1]} { //@media (min-width: 768px)
font-size: 50px;
}
`;
props
emotion에서 props로 값을 내려서 처리하기가 조금 더 귀찮다.
styled-components가 편해보인다.
emotion
const btn = (props = {}) => css`
font-size: 20px;
margin: 0 10px;
border: 1px solid red;
font-size: 30px;
background-color: ${props.bgColor ?? 'blue'};
`;
const blackBtn = css`
${btn()}
background-color: #000;
color: #fff;
`
const App = () => {
return (
<div>
<button css={btn({bgColor: 'red'})}>버튼1</button>
<button css={blackBtn}>버튼2</button>
</div>
)
}
styled-components
const Btn = styled.button`
font-size: 20px;
margin: 0 10px;
border: 1px solid red;
font-size: 30px;
color: #fff;
background-color: ${(props) => props.bgColor ?? 'blue'};
`;
const BlackBtn = styled.button`
background-color: #000;
color: #fff;
`
const App = () => {
return (
<div>
<Btn bgColor="red">버튼1</Btn>
<BlackBtn>버튼2</BlackBtn>
</div>
)
}
결론
두 라이브러리 모두 너무 편리해서 둘 중 하나를 고르긴 힘들었지만! emotion으로 결정했다.
emotion을 선택한 이유는 SSR설정없이 사용할 수 있다는 점과 composition을 더 편리하게 사용 할 수있다는 점이 큰 장점으로 보였다.
참고한 페이지
https://styled-components.com/
https://emotion.sh/docs/introduction
https://ideveloper2.dev/blog/2019-05-05--thinking-about-emotion-js-vs-styled-component/
https://80000coding.oopy.io/7ad296c7-8832-4951-9cf7-074a196d42ea
'TECH' 카테고리의 다른 글
styled-components로 theme적용하기 with Typescript (0) | 2023.02.22 |
---|---|
styled-components 기본 문법 (0) | 2023.02.22 |
redux-toolkit (0) | 2023.02.20 |
redux개념 정리 (0) | 2023.02.16 |
tsconfig.json 기본세팅, d.ts, JSDoc (0) | 2023.02.14 |