이전 프로젝트에서 사용해본 방식인데 편리함이 진짜 넘사수준으로 좋아서 제일 만족했던 기술이었기 때문에 다음 프로젝트에서는 조금 더 공부를 해보고 응용해보려고 한다. 이전에는 공부 하나 없이 바로 사용했던터라 기초부터 심화까지 제대로 공부해보고자 한다.
React, Vue, Angular 등과 같은 프레임워크, 라이브러리들이 인기를 끌면서 웹 페이지를 여러 개의 컴포넌트로 분리하고, 각 컴포넌트에 HTML, CSS, 자바스크립트를 종합하는 패턴으로 구성하는 컴포넌트 기반 개발 방법을 주류가 됨.
그에 따라 CSS 방식에서도 Styled Component를 사용하여 자바스크립트 파일 안에 삽입하는 CSS-in-JS 방식이 트렌드가 됨.
CSS-in-JS는 이 글에서 정리했었다.
import Dashboard from './Dashboard';
import App.css;
function App() {
return (
<div className="container">
<Dashboard> ... </Dashboard>
</div>
);
}
export default App
아래는 App.css의 일부이다.
.container {
background-color: #000000;
}
import Dashboard.css;
function Dashboard({ children }) {
return (
<div className="container">
{children}
</div>
);
}
export default Dashboard;
아래는 Dashboard.css의 일부이다.
.container {
font-size: 16px;
}
이 경우 Dashboard의 container에서는 font-size: 16px만 적용되도록 구현했지만, App.css의 background-color까지 적용되게 된다.
이는 사용된 클래스 이름이 전역적인 특성을 가지기 때문이다.
즉, 한 컴포넌트에서 사용한 클래스 이름을 다른 모든 컴포넌트에서도 사용할 수 있게 된다.
프로젝트 규모가 커질수록 클래스 이름을 중복시키지 않고 작성하는 것이 어렵다.
const StyledApp = styled.div`
background-color: #000000;
`;
const Dashboard = styled.div`
font-size: 16px;
`;
function App() {
return (
<StyledApp>
<Dashboard> ... </Dashboard>
</StyledApp>
);
}
Styled Components는 다음과 같이 사용하여 클래스 이름을 사용하지 않도록 만들었다.
import { shadow20 } from '../shadows';
const Card = styled.div`
${shadow20}
...(다른 CSS 코드)
`;
export default Card;
Styled Components에서는 스타일 재사용이 필요한 상황에서 클래스가 아니라 JavaScript 변수를 만듦. CSS 코드는 VSCode 같은 코드 에디터에서 추적하기 어렵기 때문에 직접 텍스트로 하나하나 검색을 해야 하는데 JavaScript라서 언제 어디서 쓰고 있는지 에디터를 통해 확인하기 쉽고, 이름을 바꾸거나 삭제를 하는 것도 코드 에디터를 통해 쉽게 할 수 있다.
const 컴포넌트명 = styled.태그명 ` 속성: 값; `
다음과 같은 형태로 작성해주면 된다.
import styled from 'styled-components';
const Button = styled.button`
background-color: #41C9E2;
border: none;
color: #ffffff;
padding: 16px;
`;
위처럼 코드를 작성해주면,
아래와 같이 컴포넌트를 사용할 수 있다.
<Button>Hello!</Button>
const Button = styled.button`
color: white;
background-color: red;
border: none;
border-radius: 50%;
`;
다음과 같은 버튼 컴포넌트가 있고, a 태그에서 Button의 스타일을 그대로 가져오고 싶을 때 다음과 같이 사용할 수 있다.
<Button as="a">log out</Button>
attrs를 통해 고정되는 Props나 다이나믹한 Props, 기본 Tag의 props 등을 전달.
const Input = styled.input`
background-color: red;
height: 10px;
`;
function App() {
return (
<div>
<Input required />
<Input required />
<Input required />
<Input required />
<Input required />
<Input required />
<Input required />
</div>
);
위 코드에서 모든 Input이 필수로 입력되어야 하는 상황일 때 다음과 같이 모든 컴포넌트에 required를 작성해주어야 하는 불편함이 있다.
이때 아래와 같이 attrs를 사용해주면 속성이 추가되게 된다.
const Input = styled.input.attrs({ required: true })`
background-color: red;
height: 10px;
`;
Styled Components에서 Prop에 따라 컴포넌트의 스타일을 다르게 보여줄 수 있음.
const Button = styled.button`
background-color: #6750a4;
border: none;
border-radius: ${({ round }) => round ? `9999px` : `3px`};
color: #ffffff;
font-size: ${({ size }) => SIZES[size] ?? SIZES['medium']}px;
padding: 16px;
&:hover,
&:active {
background-color: #463770;
}
`;
function App() {
return (
<div>
<Button size="small" round>
round small
</Button>
<Button size="medium" round>
round medium
</Button>
<Button size="large" round>
round large
</Button>
</div>
);
}
논리 연산자나 삼항 연산자를 사용할 수 있다.
const Button = styled.button`
...
${({ round }) => round && `
border-radius: 9999px;
`}
`;
border-radius: ${({ round }) => round ? `9999px` : `3px`};
Nesting은 CSS 규칙 안에서 CSS 규칙을 만드는 것이고, & 선택자와 컴포넌트 선택자 두 가지 방법이 있다.
.Button {
background-color: #6750a4;
border: none;
color: #ffffff;
padding: 16px;
}
.Button:hover,
.Button:active {
background-color: #463770;
}
다음과 같은 코드를 Styled Components에서는 아래처럼 쓰게 되는 것이다.
const Button = styled.button`
background-color: #6750a4;
border: none;
color: #ffffff;
padding: 16px;
&:hover,
&:active {
background-color: #463770;
}
`;
.StyledButton {
...
}
.StyledButton .Icon {
margin-right: 4px;
}
다음과 같은 코드를 Styled Components에서는 아래처럼 쓰게 되는 것이다.
const StyledButton = styled.button`
background-color: #6750a4;
border: none;
color: #ffffff;
padding: 16px;
${Icon} {
margin-right: 4px;
}
&:hover,
&:active {
background-color: #463770;
}
`;
Styled Components로 만들어진 컴포넌트를 상속하려면 styled() 함수를 사용한다.
아래 코드에서는 SubmitButton이 Button의 스타일을 상속받게 된다.
const Button = styled.button`
background-color: #6750a4;
border: none;
color: #ffffff;
font-size: ${({ size }) => SIZES[size] ?? SIZES['medium']}px;
padding: 16px;
${({ round }) =>
round
? `
border-radius: 9999px;
`
: `
border-radius: 3px;
`}
&:hover,
&:active {
background-color: #463770;
}
`;
const SubmitButton = styled(Button)`
background-color: #de117d;
display: block;
margin: 0 auto;
width: 200px;
&:hover {
background-color: #f5070f;
}
`;
function Hello() {
return (
<div>
<h1>안녕하세요.</h1>
<h2>반갑습니다.</h2>
</div>
);
}
export default Hello;
import styled from 'styled-components';
import Hello from './Hello';
const StyledHello = styled(Hello)`
background-color: #ededed;
border-radius: 8px;
padding: 16px;
margin: 40px auto;
width: 400px;
`;
다음과 같은 코드는 스타일이 적용되지 않는다.
Styled Components는 내부적으로 className을 따로 생성한다. 자체적으로 생성된 className이 있는 부분에 styled() 함수의 스타일이 입혀지게 된다.
JSX 문법으로 직접 만든 컴포넌트는 styled() 함수가 적용될 className에 대한 정보가 없기 때문에 styled() 함수에서 스타일이 적용되지 않는다.
즉, 직접 만든 컴포넌트는 className 값을 Prop으로 따로 내려줘야 styled() 함수를 사용할 수 있다.
function Hello({ className }) {
return (
<div className={className}>
...
</div>
);
}
div 태그에 className을 내려줬기 때문에 styled(Hello)에서 작성한 코드는 Hello 안에 있는 div 태그에 적용된다.
반복되는 코드는 한 곳에서 지정하고 여러 군데서 활용하기 위해 사용.
const Button = styled.button`
...
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
`;
const Input = styled.input`
...
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
`;
위와 같이 box-shadow가 반복되는 경우 아래와 같이 작성할 수 있다.
import styled, { css } from 'styled-components';
const boxShadow = css`
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
`;
const Button = styled.button`
...
${boxShadow}
`;
const Input = styled.input`
...
${boxShadow}
`;
글로벌 스타일 컴포넌트를 최상위 컴포넌트에서 렌더링 하면 글로벌 스타일이 항상 적용된 상태가 되도록 할 수 있음.
import { createGlobalStyle } from 'styled-components';
const GlobalStyle = createGlobalStyle`
* {
box-sizing: border-box;
}
body {
font-family: 'Noto Sans KR', sans-serif;
}
`;
function App() {
return (
<>
<GlobalStyle />
<div>글로벌 스타일</div>
</>
);
}
애니메이션을 적용하려면 keyframes를 import 해야한다. 이후 사용법은 기본적인 CSS와 같다.
import styled, { keyframes } from 'styled-components';
const rotate360 = keyframes`
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
`;
const Rotate = styled.div`
display: inline-block;
animations: ${rotate360} 2s linear infinite;
`;
keyframes를 정의하고 이를 styled-components에 전달하여 사용한다.
themeProvider를 사용해서 색상을 객체 형식으로 모아둔 theme을 props로 하위 컴포넌트들에게 넘겨줄 수 있음.
theme만 바꿔주면 되므로 라이트 테마, 다크 테마를 구현할 때 유용하게 사용.
import { ThemeProvider } from "styled-components";
import Button from "./Button";
function App() {
const theme = {
primaryColor: '#1da1f2',
};
return (
<ThemeProvider theme={theme}>
<Button>확인</Button>
</ThemeProvider>
);
}
export default App;
const Button = styled.button`
background-color: ${({ theme }) => theme.primaryColor};
/* ... */
`;