styled-components 기록용

juno·2022년 8월 30일
0

styled-components

목록 보기
1/1

1. CSS-in-JS

정의

  • CSS-in-JS는 자바스크립트 파일 안에서 css를 작성할 수 있는 방법입니다.

특징

CSS-in-JS는 js 파일 안에서 css 코드를 작성하기 때문에 css의 변수와 함수를 그대로 사용할 수 있습니다. 그리고 css 클래스명을 해시 값으로 자동으로 생성하여 클래스 이름 작명에 대한 고민의 수고가 덜합니다. 또한, 현대 웹은 컴포넌트 기반으로 발전해 나가기 때문에 컴포넌트와 스타일을 하나의 파일에서 작성하는 CSS-in-JS는 모듈화가 수월해집니다.

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

설치

$ npm install styled-components

정의

css, Sass에서는 별도의 css, sass 파일을 생성하여 js파일에 import해서 사용했지만, 스타일드 컴포넌트는 스타일링을 할 때 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;          |  // 3
  color: purple;               |
`;export default App;

1. styled-compoents 를 사용하기 위해서 스타일드 컴포넌트의 styled를 import 해줍니다.

2.UI가 그려지는 return문 안에서 처럼 html 태그와 유사한 형태로 컴포넌트를 만들어서 html 태그처럼 선언합니다. 그래서 선언해준 html태그가 가지고 있는 속성을 스타일드 컴포넌트로 선언한 컴포넌트에도 적용할 수 있습니다.

ex)

<LogoImage src="/images/logo.png" alt="로고" /> 

3. 아래와 같이 styled 객체에 Tagged Templete 문법을 활용해서 css속성을 정의합니다.


const [컴포넌트명] = styled.[html태그]`
  [부여하고자 하는 css속성]
`;

[참고]Tagged Template Literal

3. 스타일드 컴포넌트의 활용

3-1. props 활용

동적으로 스타일을 적용하기 위해서 가장 많이 사용되는 활용 방법입니다. 부모에서 자식컴포넌트에 값을 넘겨줄 때 props를 사용한 것처럼, 이를 활용하여 조건에 따른 스타일링을 할 수 있습니다. <컴포넌트 속성명=값 /> 의 형태로 선언해 줍니다. 그러면, ${(props)⇒props.속성명}으로 값을 유동적으로 사용할 수 있습니다. 예를 들어 <Button color="red" /> 로 선언하면, props.color 의 값은 red가 들어오게 됩니다.

이렇게 받은 props 값은 아래와 같은 방법으로 동적으로 스타일링을 할 수 있습니다.

// App.js

import React, { useState } from 'react';
import styled, { css } from 'styled-components';

const App = () => {
  const [changeColor, setChangeColor] = useState(false);

  const handleChanageColor = () => {
    setChangeColor(!changeColor);
  };

  return (
    <>
      <Button onClick={handleChanageColor} 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;

3-2. 상속 스타일

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

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

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

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

컴포넌트 각각에 적용해주면 좋은 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>
);

4-2. styled-reset

브라우저마다 설정되어있는 css 기본 스타일을 초기화해주기 위해 reset.css, reset.scss 파일을 생성해서 import 해주어 사용했습니다. css 기본 스타일을 초기화해주는 작업을 스타일드 컴포넌트에서는 styled-reset 라이브러리를 설치한 후 GlobalStyle.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를 해서 적용을 시켰습니다. 하지만 매번 import 를 해주고, 참조해야 하는 css파일이 많아지면 의존성을 관리하기도 힘들어집니다. 이런 문제들을 해결하기 위해서 스타일드 컴포넌트에서는 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>
);

ThemeProvider는 위의 예시와 같이 프로젝트의 엔트리 포인트인 index.js의 최초로 렌더링 되는 컴포넌트를 감싸주어서 모든 컴포넌트의 prop에 theme을 부여하여 적용할 수 있습니다.

4-3-1. theme

color, fontSize등 공통된 테마는 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.lightGrey}
`;

전역적으로 사용하기 위한 theme.js 파일을 만들고, 해당 파일을 index.js에서 ThemeProvider를 통해 설정해 주면, 위의 예시처럼 props로 받아와서 사용할 수 있는데 어떤 형태로 들어오는지 console.log 로 같이 확인해 보도록 하겠습니다. 그러면 props는 다음과 같은 객체 형태로 데이터가 들어오게 됩니다. 그래서 props.theme.ligthrgrey로 값을 사용할 수가 있게 됩니다.

// App.js

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

// console.log(props)의 결과

props: {
    children: 'title',
    theme: {
      black: '#000000',
      white: '#FFFFFF',
      lightGrey: '#B0B0B0',
      middleGrey: '#717171',
      deepGrey: '#222222',
      hoverGrey: '#DBDBDB',
    },
  },
  

4-3-2. mixin

자주 사용하는 css 스타일에 대해서는 variables.js 파일을 별도로 생성하여 사용하는 게 좋습니다. variables.js 파일은 theme과 variables를 Theme Provider에 같이 prop으로 합쳐서 전역에서 사용하거나, 사용하고자 하는 파일에서만 import해서 mixin을 사용할 수 있습니다. 아래와 같은 varibales.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;

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()} 
`;  // a

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

export default App;

각 컴포넌트에서 variables.js 파일을 import해서 사용

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

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. 웹 폰트

웹 폰트 사용할 때는 웹 폰트 사이트에서 제공하는 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>

4-4-2. 폰트파일

  • styles 폴더 안에 폰트파일을 저장할 폴더를 생성하여 폰트파일을 넣어줍니다.
src
└── styles
    ├── fonts
    │   └── DoHyeon-Regular.ttf
    ├── GlobalFont.js
    ├── GlobalStyle.js
    └── theme.js
  • 스타일드 컴포넌트의 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
안녕하세요 인터랙션한 웹 개발을 지향하는 프론트엔드 개발자 입니다. https://kimjunho97.tistory.com => 블로그 이전 중

0개의 댓글