위코드에서 공부하며 정리한 내용입니다.
CSS-in-JS 는 자바스크립트 파일 안에서 css 를 작성하는 방법입니다. JS 파일안에서 css 코드를 작성하므로 css 의 변수와 함수를 그대로 사용할 수 있고, css 클래스명을 해시 값으로 자동 생성해 작명에 대한 고민을 덜어줍니다. 컴포넌트와 스타일을 하나의 파일에서 작성하므로 컴포넌트를 기반으로 한 모듈화에 유리합니다. CSS-in-JS 에는 여러 라이브러리가 있는데 최근까지 가장 많이 사용되는 라이브라리는 styled0-components(스타일드 컴포넌트) 입니다.
터미널에서 프로젝트를 시작할 폴더에 들어간 후 스타일드 컴포넌트 라이브러리를 설치합니다.
$ npm install styled-components
스타일드 컴포넌트로 스타일링을 할 때는 템플릿 리터럴 문법을 사용합니다.
// App.js
import React from 'react';
import styled from 'styled-components'; // 스타일트 컴포넌트 임포트
const App = () => {
return <Title>styled-components!!</Title>;
// ui 를 그리는 리턴문 안에서 html 태그와 같은 형식으로 컴포넌트 만들어서 선언
// html 태그가 가지는 속성을 스타일드 컴포넌트로 선언한 컴포넌트에 적용 가능
}
const Title = styled.h1` // 테그 템플릿 리터럴 문법으로 css 속성 정의
font-size: 32px;
text-align: center;
color: purple;
`
태그 템플릿 문법은 아래와 같은 양식으로 작성합니다.
const [컴포넌트] = styled.[html태그]`
[부여할 css 속성]
`;
동적으로 스타일을 적용하기 위해 가장 많이 사용하는 방법입니다. 부모에서 자식컴포넌트에게 값을 넘겨주는 것처럼 스타일 속성 역시 넘겨줄 수 있습니다. <컴포넌트 속성명=값 />의 형태로 선언하면 ${(props) => props.속성명}으로 값을 유동적으로 사용할 수 있습니다. 로 선언하면 props.color 의 값은 red 가 됩니다.
// App.js
import React, { useState } from 'react';
import styled, { css } from 'styled-components';
const App = () => {
const [changeColor, setChangeColor] = useState(false);
const handleChangeColor = () => {
setChangeColor(!changeColor);
}
return (
<>
<Button onClick={handleChangeColor} primary={changeColor}>
Button
</Button>
</>
);
}
const Button = styled.button`
padding: 20px;
margin: 10px;
border: none;
background-color: ${(props) => (props.primary ? "purple" : "pink")};
// 버튼 컴포넌트에 전달한 props 속성 값으로 css 스타일을 변경
`
export default App;
상속 기능을 사용해 이미 선언한 스타일드 컴포넌트를 활용해 새로운 스타일드 컴포넌트를 만들 수 있습니다.
const [새로운 컴포넌트명] = styled(기존 컴포넌트명)`
[부여하고자 하는 css 속성]
`;
기존의 버튼 컴포넌트를 사용해 새로운 버튼 컴포넌트를 만드는 방법 예시는 아래와 같습니다.
// App.js
const App = () => {
return (
<>
<Button>Button</Button>
<NewButton>New Button</NewButton>
</>
);
};
const Button = styled.button`
margin: 20px;
padding: 20px;
border: none;
background-color: yellow;
font-size: 20px;
`;
const NewButton = styled(Button)` // 기존 스타일드 컴포넌트에서 컬러만 추가 수정
color: white;
background-color: purple;
`;
아래 처럼 import 한 외부 라이브러리 컴포넌트도 스타일을 확장해 사용할 수 있습니다.
// App.js
import { Link } from 'react-router-dom';
import styled from 'styled-components';
const App = () => {
return <CartLink to="/cart">장바구니</CartLink>;
};
const CartLink = styled(Link)` // Link 컴포넌트에 추가 스타일 적용
color: red;
`;
export default App;
스타일드 컴포넌트도 아래와 같이 네스팅을 적용할 순 있지만, 모듈화를 고려한다면 네스팅을 적용하기 보다는 컴포넌트로 분리해서 사용하는 것이 좋습니다.
import React from 'react';
import styled from 'styled-components';
const Main = () => {
return (
<List>
<li>
메뉴<a href="http://list">클릭</a>
</li>
</List>
);
};
const List = styled.ul`
padding: 0;
li {
padding: 10px 0;
color: red;
font-size: 30px;
a {
color: green;
}
}
`
export default Main;
하지만 react-slick 과 같은 외부 라이브러리를 사용해 정해진 스타일을 변경한다면 네스팅을 사용해야 합니다.
import React from 'react';
import styled from 'styled-components';
import Slider from 'react-slick';
import 'slick-carousel/slick/slick.css';
import 'slick-carousel/slick/slick-theme.css';
const Main = () => {
return (
<Carousel>
// slick 코드
</Carousel>
);
};
const Carousel = styled(Slider)`
.slick-slide {
// slick 커스텀 스타일
}
`;
export default Main;
common.scss 처럼 전역에 공통으로 적용할 스타일은 createGlobalStyle 함수를 통해 만들어 사용할 수 있습니다.
// src/styles/GlobalStyle.js
import React from 'react'
import { createGlobalStyle } from 'styled-components'
const GlobalStyle = createGlobalStyle`
* {
box-sizing: boder-box;
font-family: 'Noto Sans KR', sans-serif;
}
`
export default GlobalStyle;
그리고 아래처럼 GlobalStyle 컴포넌트를 전역 스타일로 적용하고 싶은 컴포넌트 상위에 적용해주면 하위 컴포넌트에 전역스타일이 적용됩니다.
// index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import { ThemeProvider } from 'styled-components';
import Router from './Router';
import GlobalStyle from './styles/GlobalStyle';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<ThemeProvider>
<GlobalStyle />
<Router />
</ThemeProvider>
);
css 리셋 파일 역시 GlobalStylde.js 파일에 선언해 전역에 적용할 수 있습니다. 방법은 아래와 같습니다.
스타일드 컴포넌트 라이브러리를 설치합니다.
$ npm install styled-reset
createGlobalStyle안에 ${reset} 을 선언합니다.
// src/styles/GlobalStyle.js
import React from 'react';
import { createGlobalStyle } from 'styled-components';
import reset from 'styled-reset';
const GlobalStyle = createGlobalStyle`
${reset}
*{
box-sizing: border-box;
font-family: 'Do Hyeon', sans-serif;
}
`
export default GlobalStyle;
sass 에서 변수와 mixin 등 공통으로 사용할 스타일을 모아둔 파일을 만들고 사용할 scss 파일에 import 해서 사용했습니다. 이럴 경우 매번 참조를 해야 하므로 의존성을 관리하기 힘듭니다. 이를 해결하기 위해 ThemeProvider 를 통해 전역으로 테마, JS 변수를 공유해 사용합니다.
// index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import { ThemeProvider } from 'styled-components';
import GlobalStyle from './styles/GlobalStyle';
import theme from './styles/theme';
import Router from './Router';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<ThemeProvider theme={theme}>
<GlobalStyle />
<Router />
</ThemeProvider>
);
공통된 테마는 theme.js 에 선언하고 각 컴포넌트에서 props 로 받아 스타일을 적용할 수 있습니다. ThemeProvider 의 속성을 넘겨주면 전역에서 사용할 수 있습니다.
// theme.js
const theme = {
black: '#000000',
white: '#FFFFFF',
lightGrey: '#B0B0B0',
middleGrey: '#717171',
deepGrey: '#222222',
hoverGrey: '#DBDBDB',
};
export default theme;
// App.js
import { Link } from 'react-router-dom';
import styled from 'styled-components';
const App = () => {
return <Container>title</Container>;
};
const Container = styled.div`
background-color: ${props => props.theme.lightGray}
`;
// 콘솔을 찍어서 들어오는 값을 확인할 수 있음
const Container = styled.div`
background-color: ${props => console.log(props)}
`;
자주 사용하는 css 스타일은 variables.js 파일을 별도로 생성해서 사용하면 좋습니다. theme 과 variables를 Theme Provider에 같이 prop 으로 합쳐서 전역에 사용하거나 사용할 파일에만 import 해서 mixin 을 사용합니다. 아래과 같이 variableds.js 파일을 만들고,
// variableds.js
import { css } from 'styled-components';
const variables = {
flexSet: (direction = 'row', justify = 'center', align = 'center') => `
display: flex;
flex-direction: ${direction};
justify-contest: ${justify};
align-items: ${align};
`,
absoluteCenter: css`
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
`,
};
export default variables;
ThemeProvider 에 prop 으로 합쳐서 사용합니다.
// index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import { ThemeProvider } from 'styled-components';
import GlobalStyle from './styles/GlobalStyle';
import theme from './styles/theme';
import variables from './styles/variables'
import Router from './Router';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<ThemeProvider theme={{ style: theme, variables }}>
<GlobalStyle />
<Router />
</ThemeProvider>
);
사용할 때는 아래와 같이 적용합니다.
// App.js
const App = () => {
return (
<Container>
<Button>첫번째 버튼</Button>
<Button>두번째 버튼</Button>
</Container>
);
};
const Container = styled.div`
${(props) => props.theme.variables.flexSet()}
`;
const Button = styled.button`
background-color: ${(props) => props.theme.style.lightGray};
`;
export default App;
variables.js 파일에서 기본으로 설정한 값을 사용할 때는 flexSet() 을 써줍니다. 이때 적용하고 싶은 인자를 넘겨줄 수 있고, 없다면 기본 세팅값이 들어값니다.
ex) ${(props) => props.theme.variables.flexSet('', 'space-between', 'center')}
실제 적용한 코드 예시를 살펴보면
// App.js
import styled from 'styled-components';
import variables from './styles/variables';
const App = () => {
return (
<Wrapper>
<Button primary="pink">Pink Button</Button>
<Button primary="Yellow">Yellow Button</Button>
</Wrapper>
);
};
const Wrapper = styled.div`
${variables.flexSet()}
`;
const Button = styled.button`
margin: 20px;
padding: 20px;
border: none;
background-color: ${(props) => props.primary};
font-size: 20px;
`;
export default App;
폰트를 전역에서 사용하고 싶다면 GlobalStyle.js 의 body 안에 font-family 를 웹 폰트 명으로 지정해야 합니다. 이때 웹 폰트인지 폰트파일을 사용할 것인지에 따라 성정방법이 다릅니다.
// GlobalStyle.js
import { createGlobalStyle } from 'styled-components';
import reset from 'styled-reset';
const GlobalStyle = createGlobalStyle`
${ reset }
body {
font-family: 'Do Hyeon', sans-serif;
}
` ;
export default GlobalStyle;
웹 폰트를 사용할 때는 임베드 코들 복사해 html 파일에 코드를 추가합니다
// index.html
<head>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Do+Hyeon&display=swap" rel="stylesheet" />
</head>
폰트파일을 사용한다면 [src] - [styles] - [fonts] 폴더 안에 폴더 파일을 넣어주고, 스타일드 컴포넌트의 createGlobalStyle을 통해 글로벌 스타일을 생성합니다. 이때 @font-face 를 사용합니다.
// src/styles/GlobalFont.js
import { createGlobalStyle } from 'styled-components';
import DoHyeon from './fonts/DoHyeon-Regular.ttf';
const GlobalFont = createGlobalStyle`
@font-face {
font-family: 'Do Hyeon';
src: url(${DoHyeon}) format('woff');
}
`;
export default GlobalFont;
전역에서 사용할 때는 아래처럼 적용합니다.
// index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import { ThemeProvider } from 'styled-components';
import GlobalStyle from './styles/GlobalStyle';
import GlobalFont from './styles/GlobalFont';
import color from './styles/theme';
import Router from './Router';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<ThemeProvider theme={theme}>
<GlobalStyle />
<GlobalFont />
<Router />
</ThemeProvider>
);