React - Styled Component

miaยท2023๋…„ 1์›” 21์ผ
0
post-custom-banner

๐Ÿ–Œ๏ธ Styled Component...?

๋ง๊ทธ๋Œ€๋กœ style์ด ์žˆ๋Š” component์ด๋‹ค.

Sass์™€ ๊ฐ™์€ css ์ „์ฒ˜๋ฆฌ๊ธฐ (์ž์‹ ๋งŒ์˜ ํŠน๋ณ„ํ•œ syntax๋ฅผ ๊ฐ€์ง€๊ณ  css๋ฅผ ์ƒ์„ฑํ•˜๋„๋ก ํ•˜๋Š” ํ”„๋กœ๊ทธ๋žจ)์ด ๋“ฑ์žฅํ–ˆ์Œ์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ  ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์˜ ์ƒํƒœ ๊ฐ’์„ ๊ณต์œ ํ•  ์ˆ˜ ์—†์–ด์„œ ๋™์ ์œผ๋กœ ์Šคํƒ€์ผ๋ง ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์ธ๋ผ์ธ ์Šคํƒ€์ผ์„ ์ด์šฉํ•˜๊ฑฐ๋‚˜ css ํด๋ž˜์Šค๋ช…์œผ๋กœ ์กฐ๊ฑด๋ถ€ ์Šคํƒ€์ผ๋ง์„ ์ด์šฉํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ์‚ฌ์šฉํ–ˆ๋‹ค.

ํ•˜์ง€๋งŒ ์ธ๋ผ์ธ ์Šคํƒ€์ผ์„ ์„ ์–ธํ•˜๋ฉด ์Šคํƒ€์ผ ์ ์šฉ ์ˆœ์œ„๊ฐ€ ๋†’์•„์ง€๋ฉฐ ์Šคํƒ€์ผ์„ ์žฌ์‚ฌ์šฉ ํ•  ์ˆ˜ ์—†๋Š” ๋ฌธ์ œ๊ฐ€ ์žˆ์—ˆ๊ณ , ํด๋ž˜์Šค ์ด๋ฆ„์„ ์‚ฌ์šฉํ–ˆ์„ ๋•Œ๋Š” css ํด๋ž˜์Šค ์ฆ๊ฐ€์— ๋”ฐ๋ฅธ ํด๋ž˜์Šค ์ด๋ฆ„์— ๋Œ€ํ•œ ๊ณ ๋ฏผ์ด ์žˆ์—ˆ๋‹ค.
์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋“ฑ์žฅํ•œ ํŒจ๋Ÿฌ๋‹ค์ž„์ด CSS-in-JS์ด๋‹ค.

ํŠน์ง•

CSS-in-JS๋Š” js ํŒŒ์ผ ์•ˆ์—์„œ css ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ธฐ ๋•Œ๋ฌธ์— css์˜ ๋ณ€์ˆ˜์™€ ํ•จ์ˆ˜๋ฅผ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  css ํด๋ž˜์Šค๋ช…์„ ํ•ด์‹œ ๊ฐ’์œผ๋กœ ์ž๋™ ์ƒ์„ฑํ•˜์—ฌ ํด๋ž˜์Šค ์ด๋ฆ„ ์ž‘๋ช…์— ๋Œ€ํ•œ ๊ณ ๋ฏผ์˜ ์ˆ˜๊ณ ๊ฐ€ ๋œํ•˜๋‹ค. ๋˜ํ•œ ๋ชจ๋“ˆํ™”(์œ ๋‹›๋ณ„๋กœ ๋‚˜๋ˆ„์–ด ์žฌ์กฐ๋ฆฝํ•˜๋Š” ๋ฐฉ์‹)์—๋„ ์œ ์šฉํ•˜๋‹ค.

์„ค์น˜

npm install styled-components

์ •์˜

์Šคํƒ€์ผ๋“œ ์ปดํฌ๋„ŒํŠธ๋Š” ์Šคํƒ€์ผ๋ง์„ ํ•  ๋•Œ ES6์˜ Tagged Templeate Literal ๋ฌธ๋ฒ•์„ ์ด์šฉํ•˜์—ฌ jsํŒŒ์ผ ์•ˆ์—์„œ ์„ ์–ธํ•ด์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

// App.js

import React from 'react';
import Styled from 'styled-components'; // 1

const App = () => {
	return <Title>styled-components!!</Title>; //2
};

const Title = styled.h1`
	font-size: 32px;
	text-align: center;
	color: purple;
`

export default App; //3
  1. styled-components๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์Šคํƒ€์ผ๋“œ ์ปดํฌ๋„ŒํŠธ์˜ styled๋ฅผ import ํ•ด์ฃผ์–ด์•ผ ํ•œ๋‹ค.
  2. UI๊ฐ€ ๊ทธ๋ ค์ง€๋Š” return๋ฌธ ์•ˆ์—์„œ html ํƒœ๊ทธ์™€ ์œ ์‚ฌํ•œ ํ˜•ํƒœ๋กœ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งŒ๋“ค์–ด์„œ html ํƒœ๊ทธ์ฒ˜๋Ÿผ ์„ ์–ธํ•œ๋‹ค. ๊ทธ๋ž˜์„œ ์„ ์–ธํ•ด์ค€ htmlํƒœ๊ทธ๊ฐ€ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ์†์„ฑ์„ ์Šคํƒ€์ผ๋“œ ์ปดํฌ๋„ŒํŠธ๋กœ ์„ ์–ธํ•œ ์ปดํฌ๋„ŒํŠธ์—๋„ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
ex) <LogoImage src ="/images/logo.png" alt="๋กœ๊ณ " />

(ํƒœ๊ทธ์ฒ˜๋Ÿผ ์‚ฌ์šฉํ•˜์ง€๋งŒ, PascalCase๋กœ ์ปดํฌ๋„ŒํŠธ ์ด๋ฆ„์„ ์ž‘์„ฑํ•ด์ฃผ์–ด์•ผ ํ•œ๋‹ค.)
3. ์•„๋ž˜์™€ ๊ฐ™์ด styled ๊ฐ์ฒด์— Tagged Templete ๋ฌธ๋ฒ•์„ ํ™œ์šฉํ•ด์„œ css ์†์„ฑ์„ ์ •์˜ํ•œ๋‹ค.

 const [์ปดํฌ๋„ŒํŠธ๋ช…] = styled.[htmlํƒœ๊ทธ]`
	[๋ถ€์—ฌํ•˜๊ณ ์ž ํ•˜๋Š” css์†์„ฑ]
`;

์Šคํƒ€์ผ๋“œ ์ปดํฌ๋„ŒํŠธ์˜ ํ™œ์šฉ

props ํ™œ์šฉ

๋™์ ์œผ๋กœ ์Šคํƒ€์ผ์„ ์ ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ ๊ฐ€์žฅ ๋งŽ์ด ์‚ฌ์šฉ๋˜๋Š” ํ™œ์šฉ ๋ฐฉ๋ฒ•์ด๋‹ค. ๋ถ€๋ชจ์—์„œ ์ž์‹์ปดํฌ๋„ŒํŠธ์— ๊ฐ’์„ ๋„˜๊ฒจ์ค„ ๋•Œ props๋ฅผ ์‚ฌ์šฉํ•œ ๊ฒƒ์ฒ˜๋Ÿผ, ์ด๋ฅผ ํ™œ์šฉํ•˜์—ฌ ์กฐ๊ฑด์— ๋”ฐ๋ฅธ ์Šคํƒ€์ผ๋ง์„ ํ•  ์ˆ˜ ์žˆ๋‹ค. <์ปดํฌ๋„ŒํŠธ ์†์„ฑ๋ช…=๊ฐ’ /> ์˜ ํ˜•ํƒœ๋กœ ์„ ์–ธํ•ด์ค€๋‹ค. ๊ทธ๋Ÿฌ๋ฉด, ${(props) => props.์†์„ฑ๋ช…} ์œผ๋กœ ๊ฐ’์„ ์œ ๋™์ ์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

 ex) <Button color="red" /> --> 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")}; //1
`;

export default App;

์ƒ์† ์Šคํƒ€์ผ

์Šคํƒ€์ผ๋“œ ์ปดํฌ๋„ŒํŠธ์˜ ์ƒ์† ๊ธฐ๋Šฅ์„ ์ด์šฉํ•œ๋‹ค๋ฉด, ์ด๋ฏธ ์„ ์–ธ๋œ ์Šคํƒ€์ผ๋“œ ์ปดํฌ๋„ŒํŠธ๋ฅผ ํ™œ์šฉํ•ด์„œ ์ƒˆ๋กœ์šด ์Šคํƒ€์ผ๋“œ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งŒ๋“ค ์ˆ˜๋„ ์žˆ๋‹ค.

 const [์ƒˆ๋กœ์šด ์ปดํฌ๋„ŒํŠธ๋ช…] = styled(๊ธฐ์กด ์ปดํฌ๋„ŒํŠธ๋ช…)`
 	[๋ถ€์—ฌํ•˜๊ณ ์ž ํ•˜๋Š” css์†์„ฑ]
 `;

Button ์ปดํฌ๋„ŒํŠธ๋ฅผ ํ™œ์šฉํ•˜์—ฌ NewButton ์ปดํฌ๋„ŒํŠธ ๊ตฌ์„ฑํ•˜๋Š” ์˜ˆ์‹œ

//  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;
`;

export default App;

ํฐํŠธ ์ปฌ๋Ÿฌ์™€ ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์ปฌ๋Ÿฌ๋งŒ ๋‹ค๋ฅธ ๋‘ ๊ฐ€์ง€์˜ ๋ฒ„ํŠผ์„ ๋งŒ๋“ ๋‹ค๊ณ  ๊ฐ€์ •ํ–ˆ์„ ๋•Œ Button ์ปดํฌ๋„ŒํŠธ์˜ ๊ธฐ๋ณธ์ ์ธ ์Šคํƒ€์ผ์„ ์ ์šฉ์‹œํ‚ค๊ธฐ ์œ„ํ•ด์„œ

const NewButton = styled(Button)` [์Šคํƒ€์ผ]`; 

์™€ ๊ฐ™์ด ์ƒ์†ํ•˜์—ฌ ์ ์šฉํ•œ๋‹ค. NewButton ์ปดํฌ๋„ŒํŠธ์—๋Š” Button ์ปดํฌ๋„ŒํŠธ์— ์ ์šฉํ•œ ์Šคํƒ€์ผ์ด ๊ทธ๋Œ€๋กœ ์ƒ์†๋˜์–ด NewButton์ปดํฌ๋„ŒํŠธ์—์„œ ์ถ”๊ฐ€๋กœ ์ ์šฉํ•œ ํฐํŠธ ์ปฌ๋Ÿฌ, ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์ปฌ๋Ÿฌ ์ด์™ธ์—๋Š” ๊ฐ™์€ ์Šคํƒ€์ผ์ด ์ ์šฉ๋œ๋‹ค.
๊ฐœ๋ฐœ์ž ๋„๊ตฌ์˜ Elements ํƒญ์„ ํ™•์ธํ•ด๋ณด๋ฉด, ์•„๋ž˜์™€ ๊ฐ™์ด ์Šคํƒ€์ผ์ด ๋ถ€์—ฌ๋œ๋‹ค.

// Elements

<button class="sc-bczRLJ jgCBVB">Button</button> // 1
<button class="sc-bczRLJ sc-gsnTZi jgCBVB hXQPTY">New Button</button> //2
  1. Button ์ปดํฌ๋„ŒํŠธ์˜ class๊ฐ€ jsCBVB๋กœ Button ์ปดํฌ๋„ŒํŠธ์—์„œ ์„ ์–ธํ•œ ์Šคํƒ€์ผ์ด๋‹ค.
  2. NewButton ์ปดํฌ๋„ŒํŠธ์˜ class๋Š” jgCBVB hXQPTY๋กœ Button ์ปดํฌ๋„ŒํŠธ์˜ ์Šคํƒ€์ผ์ด ์ ์šฉ๋˜์—ˆ๋‹ค.

์œ„์˜ ์˜ˆ์‹œ์ฒ˜๋Ÿผ ๊ฐ™์€ ํŒŒ์ผ์—์„œ ์„ ์–ธํ•œ ์Šคํƒ€์ผ๋“œ ์ปดํฌ๋„ŒํŠธ๋ฟ ์•„๋‹ˆ๋ผ, Link, ์•„์ด์ฝ˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋“ฑ ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ์ปดํฌ๋„ŒํŠธ๋„ 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)`
	color: red;
`;

export default App;

// Elements

<a class="sc-eCYdqJ gubPie" href="/cart">์žฅ๋ฐ”๊ตฌ๋‹ˆ</a>

๋„ค์ŠคํŒ…

์Šคํƒ€์ผ๋“œ ์ปดํฌ๋„ŒํŠธ์—์„œ๋„ ์•„๋ž˜์™€ ๊ฐ™์ด ๋„ค์ŠคํŒ…ํ•ด์„œ ์Šคํƒ€์ผ์„ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ํ•˜์ง€๋งŒ ์Šคํƒ€์ผ ์ปดํฌ๋„ŒํŠธ๋Š” ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ถ„๋ฆฌํ•ด์„œ ๋ชจ๋“ˆํ™”ํ•˜๊ธฐ ์œ„ํ•ด ์ตœ์ ํ™”๋˜์–ด์žˆ๊ธฐ ๋•Œ๋ฌธ์—, ๋„ค์ŠคํŒ…์„ ์ ์šฉํ•ด์„œ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค ํ•˜๋‚˜์˜ ์ปดํฌ๋„ŒํŠธ๋กœ ๋ถ„๋ฆฌํ•ด์„œ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๋” ์ข‹๋‹ค.

	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;

๊ณตํ†ต์Šคํƒ€์ผ

GlobalStyle

์ปดํฌ๋„ŒํŠธ ๊ฐ๊ฐ์— ์ ์šฉํ•ด์ฃผ๋ฉด ์ข‹์€ css ์Šคํƒ€์ผ์ด ์žˆ์ง€๋งŒ common.css, common.scss ์ฒ˜๋Ÿผ ์ „์—ญ์— ๊ณตํ†ต์œผ๋กœ css ์Šคํƒ€์ผ์„ ์ ์šฉํ•ด์ฃผ๋Š” ๋•Œ๋„ ์žˆ๋‹ค. ์Šคํƒ€์ผ๋“œ ์ปดํฌ๋„ŒํŠธ์—์„œ๋Š” createGlobalStyle ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด ์ „์—ญ์— ์ ์šฉํ•˜๊ธฐ ์œ„ํ•œ ์Šคํƒ€์ผ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.

// src/styles/GlobalStyle.js

import React from 'react'
import { createGlobalStyle } from 'styled-components'

const GlobalStyle = createGlobalStyle`
	* {
    	box-sizing: border-box;
        font-family: 'Noto Sans KR', sans-serif;
    }
`

export default GlobalStyle;

๋˜ํ•œ, ์•„๋ž˜์˜ ์˜ˆ์‹œ์ฒ˜๋Ÿผ createGlobalStyle ํ•จ์ˆ˜๋กœ ์ƒ์„ฑํ•œ 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>
);

styled-reset

๋ธŒ๋ผ์šฐ์ €๋งˆ๋‹ค ์„ค์ •๋˜์–ด์žˆ๋Š” css ๊ธฐ๋ณธ ์Šคํƒ€์ผ์„ ์ดˆ๊ธฐํ™”ํ•ด์ฃผ๊ธฐ ์œ„ํ•ด reset.css, reset.scss ํŒŒ์ผ์„ ์ƒ์„ฑํ•ด์„œ import ํ•ด์ฃผ์–ด ์‚ฌ์šฉํ–ˆ๋‹ค. css ๊ธฐ๋ณธ ์Šคํƒ€์ผ์„ ์ดˆ๊ธฐํ™”ํ•ด์ฃผ๋Š” ์ž‘์—…์„ ์Šคํƒ€์ผ๋“œ ์ปดํฌ๋„ŒํŠธ์—์„œ๋Š” styled-reset ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์„ค์น˜ํ•œ ํ›„ GlobalStyle.js ํŒŒ์ผ์— ์„ ์–ธํ•˜์—ฌ ์ „์—ญ์— ์ ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

์„ค์น˜

npm insall styled-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;

ThemeProvider

sass์—์„œ๋Š” ๋ณ€์ˆ˜์™€ mixin ๋“ฑ ๊ณตํ†ต์œผ๋กœ ์‚ฌ์šฉํ•  ์Šคํƒ€์ผ์— ๋Œ€ํ•ด์„œ ํŒŒ์ผ์„ ๋งŒ๋“ค๊ณ , ์‚ฌ์šฉํ•˜๊ณ ์ž ํ•˜๋Š” scssํŒŒ์ผ์— import๋ฅผ ํ•ด์„œ ์ ์šฉ์„ ์‹œ์ผฐ๋‹ค. ํ•˜์ง€๋งŒ ๋งค๋ฒˆ import๋ฅผ ํ•ด์ฃผ๊ณ , ์ฐธ์กฐํ•ด์•ผ ํ•˜๋Š” css ํŒŒ์ผ์ด ๋งŽ์•„์ง€๋ฉด ์˜์กด์„ฑ์„ ๊ด€๋ฆฌํ•˜๊ธฐ๋„ ํž˜๋“ค์–ด์ง„๋‹ค. ์ด๋Ÿฐ ๋ฌธ์ œ๋“ค์„ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด์„œ ์Šคํƒ€์ผ๋“œ ์ปดํฌ๋„ŒํŠธ์—์„œ๋Š” ThemeProvider๋ฅผ ํ†ตํ•ด ์ „์—ญ์œผ๋กœ ํ…Œ๋งˆ, JS๋ณ€์ˆ˜ ๋“ฑ์„ ๊ณต์œ ํ•˜์—ฌ ์‚ฌ์šฉํ•œ๋‹ค. ThemeProvider๋Š” ํ•˜์œ„ ํƒœ๊ทธ์—๋„ themeํŒŒ์ผ์„ ์ฐพ์„ ์ˆ˜ ์žˆ๋„๋ก ์ „๋‹ฌํ•ด์ฃผ๋Š” ์—ญํ• ์„ ํ•œ๋‹ค.

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

ThemeProvider๋Š” ์œ„์˜ ์˜ˆ์‹œ์™€ ๊ฐ™์ด ํ”„๋กœ์ ํŠธ์˜ ์—”ํŠธ๋ฆฌ ํฌ์ธํŠธ์ธ index.js์˜ ์ตœ์ดˆ๋กœ ๋ Œ๋”๋ง ๋˜๋Š” ์ปดํฌ๋„ŒํŠธ๋ฅผ ๊ฐ์‹ธ์ฃผ์–ด์„œ ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ์˜ prop์— theme์„ ๋ถ€์—ฌํ•˜์—ฌ ์ ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

theme

color, fontSize๋“ฑ ๊ณตํ†ต๋œ ํ…Œ๋งˆ๋Š” theme.js์—์„œ ์„ ์–ธํ•˜์—ฌ ๊ฐ ์ปดํฌ๋„ŒํŠธ์—์„œ props๋กœ ๋ฐ›์•„ ์Šคํƒ€์ผ์„ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ThemeProvider์˜ ์†์„ฑ์œผ๋กœ ๋„˜๊ฒจ์ฃผ๋ฉด ์ „์—ญ์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

// theme.js

const theme = {
	black: '#000000',
    white: '#FFFFFF',
    lightGray: '#B0B0B0',
    middleGray: '#717171',
    deepGray: '#222222',
    hoverGray: '#DBDBDB',
};

export default theme;
// App.js

import { Link } from 'react-router-dom';
import styled 'styled-components';

const App = () => {
	return <Container>title</Container>;
};

const Container = styled.div`
	background-color: ${props => props.theme.lightGray}
`;

์ „์—ญ์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•œ theme.js ํŒŒ์ผ์„ ๋งŒ๋“ค๊ณ , ํ•ด๋‹น ํŒŒ์ผ์„ index.js์—์„œ ThemeProvider๋ฅผ ํ†ตํ•ด ์„ค์ •ํ•ด์ฃผ๋ฉด, ์œ„์˜ ์˜ˆ์‹œ์ฒ˜๋Ÿผ props๋กœ ๋ฐ›์•„์™€์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ ์–ด๋–ค ํ˜•ํƒœ๋กœ ๋“ค์–ด์˜ค๋Š”์ง€ console.log๋กœ ํ™•์ธํ•˜๋ฉด props๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ฐ์ฒด ํ˜•ํƒœ๋กœ ๋ฐ์ดํ„ฐ๊ฐ€ ๋“ค์–ด์˜จ๋‹ค. ๊ทธ๋ž˜์„œ props.theme.lightgray๋กœ ๊ฐ’์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

// App.js

const Container = styled.div`
	background-color: ${props => console.log(props)}
`;

// console.log(props)์˜ ๊ฒฐ๊ณผ

props: {
	children: 'title',
    theme: {
    	black: '#000000',
        white: '#FFFFFF',
        lightGray: '#B0B0B0',
        middleGray: '#717171',
        deepGray: '#222222',
        hoverGray: '#DBDBDB',
    },
},

mixin

์ž์ฃผ ์‚ฌ์šฉํ•˜๋Š” css ์Šคํƒ€์ผ์— ๋Œ€ํ•ด์„œ๋Š” variables.js ํŒŒ์ผ์„ ๋ณ„๋„๋กœ ์ƒ์„ฑํ•˜์—ฌ ์‚ฌ์šฉํ•˜๋Š” ๊ฒŒ ์ข‹๋‹ค. variables.js ํŒŒ์ผ์€ theme๊ณผ variables๋ฅผ Theme Provider์— ๊ฐ™์ด prop์œผ๋กœ ํ•ฉ์ณ์„œ ์ „์—ญ์—์„œ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜, ์‚ฌ์šฉํ•˜๊ณ ์ž ํ•˜๋Š” ํŒŒ์ผ์—์„œ๋งŒ import ํ•ด์„œ mixin์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ์•„๋ž˜์™€ ๊ฐ™์€ variables.js ํŒŒ์ผ์„ ์ƒ์„ฑํ–ˆ์„ ๋•Œ ์–ด๋–ค ๋ฐฉ์‹์œผ๋กœ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ํ™•์ธํ•ด๋ณด์ž.

// variables.js

import { css } from 'styled-components';

const variables = {
	flex: (direction = 'row', justify = 'center', align = 'center') => `
    	display: flex;
        flex-direction: ${direction};
        justify-content: ${justify};
        align-items: ${align};
    `,
    
    absoluteCenter: css`
    	position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
    `,
};

export default variables;
  1. ThemeProvider ์—์„œ props์œผ๋กœ ํ•ฉ์ณ์„œ ์‚ฌ์šฉ
// index.js

import React from 'react';
import ReactDOM from 'react-dom/client';
import { ThemeProvider } from './styles/GlobalStyle';
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.flex()}
`; // a

const Button = styled.button`
	background-color: ${(props) => props.theme.style.lightGray};
`;

export default App;

variables.js ํŒŒ์ผ์—์„œ ๊ธฐ๋ณธ์œผ๋กœ ์„ค์ •ํ•œ ๊ฐ’์„ ์‚ฌ์šฉํ•  ๋•Œ์—๋Š” flex()๋ฅผ ์จ์ฃผ๊ณ , ๋‹ค๋ฅธ ์ธ์ž๋ฅผ ๋„˜๊ฒจ์ค„๋•Œ๋Š”

${(props) => props.theme.variables.flex('', 'space-between', 'center')}

์ฒ˜๋Ÿผ ์ ์šฉํ•˜๊ณ  ์‹ถ์€ ์ธ์ž๋ฅผ ๋„˜๊ฒจ์ค„ ์ˆ˜ ์žˆ๋‹ค.

  1. ๊ฐ ์ปดํฌ๋„ŒํŠธ์—์„œ variables.js ํŒŒ์ผ์„ importํ•ด์„œ ์‚ฌ์šฉ.(๋‹คํฌ๋ชจ๋“œ X)
// 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.flex()}
`;

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;

์›น ํฐํŠธ

์›น ํฐํŠธ ์‚ฌ์šฉํ•  ๋•Œ๋Š” ์›น ํฐํŠธ ์‚ฌ์ดํŠธ์—์„œ ์ œ๊ณตํ•˜๋Š” embedding(์ž„๋ฒ ๋“œ) ์ฝ”๋“œ๋ฅผ ๋ณต์‚ฌํ•˜์—ฌ html ํŒŒ์ผ์— ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•ด์•ผ ํ•œ๋‹ค. index.html์˜ head์•ˆ์— link ํƒœ๊ทธ๋ฅผ ์ด์šฉํ•ด์„œ ๊ธ€๋กœ๋ฒŒ ํฐํŠธ๋กœ ์ง€์ •ํ•ด์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

// 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>

ํฐํŠธํŒŒ์ผ

  1. styles ํด๋” ์•ˆ์— ํฐํŠธํŒŒ์ผ์„ ์ €์žฅํ•  ํด๋”๋ฅผ ์ƒ์„ฑํ•˜์—ฌ ํฐํŠธํŒŒ์ผ์„ ๋„ฃ์–ด์ค€๋‹ค.
src
โ””โ”€โ”€ styles
    โ”œโ”€โ”€ fonts
    โ”‚   โ””โ”€โ”€ DoHyeon-Regular.ttf
    โ”œโ”€โ”€ GlobalFont.js
    โ”œโ”€โ”€ GlobalStyle.js
    โ””โ”€โ”€ theme.js
  1. ์Šคํƒ€์ผ๋“œ ์ปดํฌ๋„ŒํŠธ์˜ createGlobalStyle์„ ํ†ตํ•ด ๊ธ€๋กœ๋ฒŒ ์Šคํƒ€์ผ์„ ์ƒ์„ฑํ•ด์ค€๋‹ค. ์ด ๋•Œ @font-face๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์›น ํŽ˜์ด์ง€์— ์ ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
// src/styles/GlobalFont.js

import { createGlobalStyle } from 'styled-components';
import DoHyon from './fonts/DoHyeon-Regualr.ttf';

const GlobalFont = createGlobalStyle`
	@font-face {
    	font-family: 'Do Hyeon';
        src: url(${DoHyeon}) format('woff');
    }
`;

export default GlobalFont;
  1. ์ „์—ญ์—์„œ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ์ ์šฉํ•ด์„œ ์‚ฌ์šฉํ•œ๋‹ค.
// index.js

import React from 'react';
import ReactDOM from 'react-dom/client';
import { ThemeProvider } from 'styled-componenets';
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
๋…ธ ํฌ๊ธฐ ํ‚พ๊ณ ์ž‰
post-custom-banner

0๊ฐœ์˜ ๋Œ“๊ธ€