React

Styled-components

soheedev 2023. 3. 21. 21:28

React에 스타일 적용하기

전체 컴포넌트에 공통으로 적용하기 위한 css와 특정 컴포넌트에만 적용하기 위한 별도의 css로 작성할 필요가 있고 또 컴포넌트 내부의 값 (props)에 따라 스타일을 다르게 하기 위해서 css module 혹은 styled components가 필요하다.

 

1. SCSS를 사용한 다양한 사이즈 버튼 만들기

  • Button 이름의 scss파일을 만든다.(large, mediun, small 사이즈의 스타일을 가진다.)
  • 컴포넌트에서 Button.scss를 사용하도록 불러오고 버튼의 사이즈를 받을 수 있도록 props를 사용한다.
  • App.jsx에서 3가지 사이즈를 넘겨준다.

 

/styles/Button.scss

$main-color: #3d3dff;

// 자주 사용되는 스타일은 그룹으로 선언
@mixin flex-center {
  display: flex;
  align-items: center;
  justify-content: center;
}

.Button {
  // 버튼의 기본 속성 제거
  outline: none;
  border: none;

  // mixin 사용
  @include flex-center;

  background-color: $main-color;
  border-radius: 10px;
  color: white;
  width: 100px;
  margin: 10px;

  // &연산자 사용
  &.large {
    width: 140px;
    height: 70px;
    font-size: 24px;
  }
  &.medium {
    width: 120px;
    height: 60px;
    font-size: 20px;
  }
  &.small {
    width: 100px;
    height: 50px;
    font-size: 16px;
  }
}

/components/Button.jsx

import React from 'react';
import '../styles/Button.scss';

function Button({ size }) {
  return (
    <button className={`Button ${size}`}>
      <p>button</p>
    </button>
  );
}

export default Button;

/App.jsx

import Button from './components/Button';

function App() {
  return (
    <div className='App'>
      <Button size='large' />
      <Button size='medium' />
      <Button size='small' />
    </div>
  );
}

export default App;

프로젝트에 필요한 버튼을 하나씩 새로 만드는 것보다 컴포넌트 형태로 만들어 놓고 사용한다면 훨씬 효율적으로 개발이 가능하다.

props 활용하기

단순한 스타일뿐만 아니라 이벤트등 여러 가지의 다양한 props를 추가로 넘겨줄 수 도 있기 때문에 rest 연산자를 활용한다. (예제에서는 color와 핸들러 함수를 받을 수 있도록 추가하였다.)

 

/components/Button.jsx

import React from 'react';
import '../styles/Button.scss';

function Button({ size, color, ...rest }) {
  return (
    <button className={`Button ${size} ${color}`} {...rest}>
      <p>button</p>
    </button>
  );
}

export default Button;

/App.jsx

import Button from './components/Button';

function App() {
  return (
    <div className='App'>
      <Button size='large' color='red' onClick={() => alert('Hi')} />
      <Button size='medium' color='blue'/>
      <Button size='small'/>
    </div>
  );
}

export default App;

 

2. css module 사용

  • css module의 장점은 클래스 명마다 고유값을 부여해서 클래스가 중복되는 것을 방지한다. 
  • 사용하려면 css-loader가 필요하나 vite로 프로젝트를 만들면 기본적으로 설정이 되어있다.
  • css module의 경우 scss문법은 사용할 수 없고 기본 css문법을 사용해야 한다.
  • 파일의 확장자 명으로 *.module.css를 사용한다.
  • className에 작성할 때는 불러온 이름. 클래스이름의 형태로 지정한다.

 

/styles/Button.module.css

.Button {
  outline: none;
  border: none;

  display: flex;
  align-items: center;
  justify-content: center;

  background-color: #3d3dff;
  border-radius: 10px;
  color: white;
  width: 100px;

  margin: 10px;
}

.large {
  width: 140px;
  height: 70px;
  font-size: 24px;
}

/components/Button.jsx

import React from 'react'
import style from '../styles/Button.module.css'

function Button() {
  return (
    <button className={`${style.Button} ${style.large}`}>
      <p>버튼</p>
    </button>
  )
}

export default Button

실제 클래스 이름에 고유값이 붙은 것을 확인할 수 있다.

클래스 이름이 알아서 고유값으로 붙었음

 

3. styled-components 사용

스타일이 적용된 컴포넌트를 만들 수 있게 해주는 모듈을 말한다. 스타일이 적용된 컴포넌트들을 조합해서 하나의 컴포넌트를 완성하게 되며 다음과 같은 기능을 지원하고 있다.

  • 테마 설정
  • 글로벌 스타일 생성
  • props 활용

 

설치

yarn add styled-components

기본 문법

import styled from 'styled-components' // 사용위해 import

const (변수명) = styled.(태그명)` // 변수로 선언하되 백틱 안에 css내용이 들어간다.

`

구조는 style폴더가 아닌 컴포넌트 명으로 폴더를 만들고 해당 폴더 안에 index.jsx와 style.js 파일을 생성한다.

 

/components/Button/style.js

import styled from 'styled-components';

export const Button = styled.button`
  outline: none;
  border: none;

  display: flex;
  align-items: center;
  justify-content: center;

  background-color: #3d3dff;
  border-radius: 10px;
  color: white;
  width: 100px;

  margin: 10px;
  &:hover {
    opacity: 0.5;
  }
`;

아래처럼 템플릿 리터럴 문법을 활용하면 props를 활용하여 색상이나 이벤트 적용이 가능하다.

/* props를 통해 배경색을 지정한다. */
background-color: ${(props) => props.color || 'blue'}
/* 클릭에 따라 표시되는 스타일을 변경한다. */
background-color: ${({ isClicked }) => (isClicked ? 'gray' : 'blue')};

 

/components/Button/index.jsx

import React from 'react';
import * as S from './style'; // style파일 내부 변수들을 S라는 이름으로 접근 및 사용

function Button() {
  return (
    <S.Button>
      <p>버튼</p>
    </S.Button>
  );
}

export default Button;

color를 props로 넘겨주어 색상을 지정하거나 클릭 여부에 따라 배경색을 다르게 표시할 수 있다.

<S.Button color="#fff">
import React, { useState } from 'react';
import * as S from './style';

function Button() {
  const [isClicked, setIsClicked] = useState(false);

  return (
    <S.Button isClicked={isClicked} onClick={() => setIsClicked(!isClicked)}>
      <p>버튼</p>
    </S.Button>
  );
}

export default Button;

themeprovider 활용하기

themeprovider는 앱 전체에 공통된 스타일 값을 사용할 수 있도록 도와주는 역할을 담당한다.

 

/styles/theme.js

// 우리 앱의 테마 색 등을 선언
const palette = {
  orange: '#FFA500',
  black: '#000000',
};

// 자주 사용하는 스타일 그룹은 미리 선언
const common = {
  flexCenter: `
      display: flex;
      justify-content: center;
      align-items: center;
  `,
};

// 개발자 간의 폰트 크기 통일을 위해 미리 선언
const fontSizes = {
  title: '2rem',
  subtitle: '1.5rem',
  paragraph: '1rem',
};

const theme = {
  palette,
  common,
  fontSizes,
};

export default theme;

/App.jsx

import { ThemeProvider } from 'styled-components';
import theme from './styles/theme';
import Button from './components/Button';

function App() {
  return (
    <div>
      <ThemeProvider theme={theme}>
        <Button />
      </ThemeProvider>
    </div>
  );
}

export default App;

위 처럼 themeprovider로 감싸서, 앱 전체에 theme에서 정의한 값을 가져오도록 한다. themeprovider가 있기 때문에 Button에 직접 props를 넘겨주지 않아도 Button 컴포넌트에서 바로 theme를 가져와 사용할 수가 있다.

 

/components/Button/style.js

import styled from 'styled-components'

export const Button = styled.button`
  outline: none;
  border: none;

  ${({ theme }) => theme.common.flexCenter}

  background-color: ${({ theme }) => theme.palette.orange};
  border-radius: 10px;
  color: white;
  width: 100px;

  margin: 10px;
  &:hover {
    opacity: 0.5;
  }
`

keyframes 활용하기

애니메이션을 만들고자 한다면, keyframes 함수를 불러와서 사용하면 된다.

import styled, { keyframes } from 'styled-components' //keyframes 추가

const fadeIn = keyframes`
  from {
    opacity: 0
  }
  to {
    opacity: 1
  }
`

export const Button = styled.button`
  outline: none;
  border: none;

  ${({ theme }) => theme.common.flexCenter}

  background-color: ${({ theme }) => theme.palette.orange};

  border-radius: 10px;
  color: white;
  width: 100px;

  margin: 10px;
  &:hover {
    opacity: 0.5;
  }

  animation-name: ${fadeIn};
  animation-duration: 1s;
  animation-timing-function: ease-out;
`

createGlobalStyle 활용하기

앱 전체에 적용하고자 하는 속성이 있다면 createGlobalStyle을 통해 컴포넌트를 만들고 전체에 적용한다.

보통 앱 전체에 폰트를 적용할 때 사용한다.

 

폰트 파일(.otf)을 다운받아 assets 폴더 안에 fonts 폴더에 넣어주고 styles 폴더 아래 globalStyle.js 파일을 생성한다.

https://fonts.google.com/noto/specimen/Noto+Sans+KR 

 

/styles/globalStyle.js

import { createGlobalStyle } from 'styled-components'
import NotoSansBold from '../assets/fonts/NotoSansKR-Bold.otf'

export default createGlobalStyle`
    @font-face {
        font-family: "NotoSansBold";
        src: url(${NotoSansBold}) format('opentype');
    }
`

format: otf → opentype, ttf →truetype, woff → woff (파일의 확장자에 따라 다름)

 

/App.jsx

import { ThemeProvider } from 'styled-components'
import theme from './styles/theme'
import Button from './components/Button'
import GlobalStyle from './styles/globalStyle'

function App() {
  return (
    <ThemeProvider theme={theme}>
      <GlobalStyle />
      <Button />
    </ThemeProvider>
  )
}

export default App

 

Button 스타일에 font-family를 설정하면 적용이 된다.

font-family: 'NotoSansBold';

 

예제 구현해보기

  • isClicked라는 state를 만든다.
  • Button이라는 styled-components를 만들고 클릭에 따라 배경색이 변하도록 한다.
  • 버튼을 클릭하면 isClicked의 값이 바뀌도록 onClick 함수를 지정한다.

 

/components/Button/index.jsx

import React, { useState } from 'react'
import * as S from './style'

function Button() {
  const [isClicked, setIsClicked] = useState(false)

  return (
    <S.Button isClicked={isClicked} onClick={() => setIsClicked(!isClicked)}>
      {isClicked ? <p>Good!</p> : <p>Nice!</p>}
    </S.Button>
  )
}

export default Button

/components/Button/style.js

import styled, { keyframes } from 'styled-components'

const fadeIn = keyframes`
  from {
    opacity: 0
  }
  to {
    opacity: 1
  }
`

export const Button = styled.button`
  animation-name: ${fadeIn};
  animation-duration: 1s;
  animation-timing-function: ease-out;

  outline: none;
  border: none;

  ${({ theme }) => theme.common.flexCenter}

  background-color: ${({ isClicked }) => (isClicked ? 'orange' : 'violet')};
  border-radius: 10px;
  color: white;
  width: 100px;

  margin: 10px;

  &:hover {
    opacity: 0.5;
  }
  cursor: pointer;
`

 

 

 

20230306 패스트캠퍼스 FE4기 수업자료를 참고하였습니다.

'React' 카테고리의 다른 글

TodoList 만들기(2)  (0) 2023.04.06
TodoList 만들기(1)  (0) 2023.04.05
useRef  (0) 2023.03.20
이벤트핸들러  (0) 2023.03.15
useEffect  (0) 2023.03.09