스타일드 컴포넌트, styled-components, CSS in JS

라용·2022년 10월 3일
0

위코드 - 스터디로그

목록 보기
70/100

위코드에서 공부하며 정리한 내용입니다.

1. CSS-in-JS

CSS-in-JS 는 자바스크립트 파일 안에서 css 를 작성하는 방법입니다. JS 파일안에서 css 코드를 작성하므로 css 의 변수와 함수를 그대로 사용할 수 있고, css 클래스명을 해시 값으로 자동 생성해 작명에 대한 고민을 덜어줍니다. 컴포넌트와 스타일을 하나의 파일에서 작성하므로 컴포넌트를 기반으로 한 모듈화에 유리합니다. CSS-in-JS 에는 여러 라이브러리가 있는데 최근까지 가장 많이 사용되는 라이브라리는 styled0-components(스타일드 컴포넌트) 입니다.

2. 스타일드 컴포넌트 특징

2-1. 설치

터미널에서 프로젝트를 시작할 폴더에 들어간 후 스타일드 컴포넌트 라이브러리를 설치합니다.

$ npm install styled-components

2-2. 정의

스타일드 컴포넌트로 스타일링을 할 때는 템플릿 리터럴 문법을 사용합니다.

// 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 속성]
`;

3. 스타일 컴포넌트 활용

3-1. props 활용

동적으로 스타일을 적용하기 위해 가장 많이 사용하는 방법입니다. 부모에서 자식컴포넌트에게 값을 넘겨주는 것처럼 스타일 속성 역시 넘겨줄 수 있습니다. <컴포넌트 속성명=값 />의 형태로 선언하면 ${(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;

3-2. 상속 스타일

상속 기능을 사용해 이미 선언한 스타일드 컴포넌트를 활용해 새로운 스타일드 컴포넌트를 만들 수 있습니다.

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;

3-3. 네스팅

스타일드 컴포넌트도 아래와 같이 네스팅을 적용할 순 있지만, 모듈화를 고려한다면 네스팅을 적용하기 보다는 컴포넌트로 분리해서 사용하는 것이 좋습니다.

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;

4. 공통스타일

4-1. GlobalStyle

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>
);

4-2. styled-reset

css 리셋 파일 역시 GlobalStylde.js 파일에 선언해 전역에 적용할 수 있습니다. 방법은 아래와 같습니다.

4-2-1. 설치

스타일드 컴포넌트 라이브러리를 설치합니다.

$ npm install styled-reset

4-2-2. 선언 방법

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;

4-3. ThemeProvider

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>
);

4-3-1. theme

공통된 테마는 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)}
`;

4-3-2. mixin

자주 사용하는 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;

4-4. 글로벌 폰트

폰트를 전역에서 사용하고 싶다면 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;

4-4-1. 웹 폰트

웹 폰트를 사용할 때는 임베드 코들 복사해 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>

4-4-2. 폰트 파일

폰트파일을 사용한다면 [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> 
);

profile
Today I Learned

0개의 댓글