본문 바로가기
React

[리액트] 리액트에서 스타일 적용하는 5가지 방법 (인라인 스타일, CSS, SCSS, CSS Module, Styled-Components)

by 메이플 🍁 2022. 8. 12.

1. 인라인 스타일

App.jsx

function App() {
  return <button style={{ backgroundColor: 'green' }} />
}

export default App

 

2. CSS

  1. 컴포넌트를 스타일링하는 가장 기본적인 방식
  2. CSS 클래스를 중복되지 않게 만들어야함
  3. CSS 클래스가 중복되는 것을 방지하기 위해 1) 이름을 지을 때 특별한 규칙을 사용하거나 2) CSS selector를 활용한다

CSS 클래스 이름 짓는 2가지 방법

  1. 컴포넌트이름-클래스형태
    • i.e.) App-header
  2. BEM 네이밍: Block__Element-Modifier
    • class="컴포넌트이름__역할—세부특징"
    • i.e.) card__title-primary

컴포넌트 스타일로 css를 사용할시 단점

상위 컴포넌트에서 불러온 css 코드가 하위 컴포넌트에까지 영향을 미치기 때문에 컴포넌트마다 다른 스타일을 개별적으로 적용하기가 힘들어진다. 각 컴포넌트별로 스타일을 관리해주기 위해서 아래에서 소개할 SCSS, CSS Module, Styled-Components를 사용한다.

App.jsx

import Button from './components/Button'

function App() {
  return (
    <>
      <Button size="large" color="tomato" />
      <Button size="medium" color="orange" />
      <Button size="small" color="skyblue" />
    </>
  )
}

export default App

Button.jsx

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

function Button({ size, color }) {
  return (
    <button className={`button ${size} ${color}`}>
      버튼
    </button>
  )
}

export default Button

Button.css

.button {
  outline: none;
  border: none;
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: green;
  border-radius: 10px;
  color: white;
  margin: 10px;
  width: 60px;
  height: 30px;
  font-size: 16px;
}

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

.medium {
  width: 120px;
  height: 60px;
  font-size: 20px;
}

.small {
  width: 100px;
  height: 50px;
  font-size: 16px;
}

.tomato {
  background-color: tomato;
}

.orange {
  background-color: orange;
}

.skyblue {
  background-color: skyblue;
}

 

3. SCSS

  • yarn add sass로 sass라이브러리를 설치해야함
  • 라이브러리는 Sass를 CSS로 변환시켜줌
  • CSS의 전처리기 중 하나
  • CSS를 더 쉽게 작성할 수 있게 해줌
  • 스타일 코드의 재활용성을 높여줌
  • 코드의 가독성을 높여 유지 보수를 더 쉽게 해줌
  • 두가지 확장자 .scss와 .sass를 지원한다

utils 함수 분리하기

@import './styles/utils.scss';

Scss 변수, 믹스인은 utils.scss로 따로 분리하여 작성한 뒤 필요한 곳에서 불러와 사용할 수 있다

sass-loader 설정 커스터마이징하기

@import '../../../styles/utils';

문제

프로젝트 디렉토리를 많이 만들어 구조가 깊어졌다면 해당 파일에 접근할때 상위 폴더로 한참 거슬러 올라가야 한다는 단점이 생긴다.

해결

웹팩에서 Sass를 처리하는 sass-loader의 설정을 커스터마이징하여 해결

sass-loader에 접근하는 방법

create-react-app으로 만든 프로젝트는 프로젝트 구조의 복잡도를 낮추기 위해 웹팩 관련된 세부 설정이 숨겨져 있다

1) yarn eject 명령어로 create-react-app으로 만든 프로젝트의 세부설정을 밖으로 꺼내준다.
2) yarn eject를 하기전에 커밋을 먼저 해주어야 한다 git commit -m "Commit before yarn eject"

3) config > webpack.config.js 파일을 수정한다.

App.jsx

import Button from './components/Button'

function App() {
  return (
    <>
      <Button size="large" color="tomato" />
      <Button size="medium" color="orange" />
      <Button size="small" color="skyblue" />
    </>
  )
}

export default App

Button.jsx

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

function Button({ size, color }) {
  return (
    <button className={`button ${size} ${color}`}>
      버튼
    </button>
  )
}

export default Button

Button.scss

$main-color: green;

.button {
  outline: none;
  border: none;
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: $main-color;
  border-radius: 10px;
  color: white;
  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;
  }

  &.tomato {
    background-color: tomato;
  }

  &.orange {
    background-color: orange;
  }

  &.skyblue {
    background-color: skyblue;
  }
}

 

4. CSS Module

  • 자동으로 클래스 이름을 고유한 값으로 만들어서 컴포넌트 스타일 클래스 이름이 중첩되는 현상을 방지해주는 기술
  • 형식: [파일 이름]_[클래스 이름]_[해시값]

CSS Module을 사용하기 위해 필요한 패키지

CSS Module을 사용하기 위해 css-loader 패키지가 필요하다. 다만 CRA로 리액트 프로젝트를 만들면 기본적으로 설치가 되어있으므로 바로 사용이 가능하다.

CSS Module 사용하기

import style from './파일이름.module.css';

console.log(style)
// {button: 'Button_button__A76z0', large: 'Button_large__sDlIm'}
  • 파일이름.module.css 확장자로 파일을 저장하면 자동으로 CSS Module이 적용된다
  • CSS Module이 적용된 스타일 파일을 불러오면 객체를 하나 전달받는다
  • CSS Module에서 사용한 클래스 이름과 해당 이름을 고유화한 값이 키-값 형태로 들어 있다
  • { className: '[파일 이름]_[클래스 이름]_[해시값]' }

CSS Module로 만든 클래스 이름 사용하기

클래스가 하나일때

className={style.클래스이름}

클래스가 두개 이상일때

1. 템플릿 리터럴 사용

className={`${style.클래스이름} ${style.클래스이름}`}

2. 배열의 메서드 join 사용

className={[styles.클래스이름, styles.클래스이름].join(' ')} 

CSS Module의 장점

CSS Module을 사용하면 컴포넌트마다 다른 고유값이 class 이름에 부여되므로 컴포넌트마다 공유하지 않는 독자적인 스타일 코드를 가질 수 있게 된다.

App.jsx

import Button from './components/Button'

function App() {
  return (
    <>
      <Button size="large" color="tomato" />
      <Button size="medium" color="orange" />
      <Button size="small" color="skyblue" />
    </>
  )
}

export default App

Button.jsx

className으로 연결할 속성이 하나일때는 중괄호 { }안에 바로 넣어준다 

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

function Button({ size, color }) {
  return (
    <button className={ style.button }>
      버튼
    </button>
  )
}

export default Button

className으로 전달해줄 값이 두개 이상인 경우 템플릿 리터럴 형태로 사용

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

function Button({ size, color }) {
  const cssSize = size
  const cssColor = color

  return (
    <button className={ `${style.button} ${style[cssSize]} ${style[cssColor]}` }>
      버튼
    </button>
  )
}

export default Button

className으로 전달해줄 값이 두개 이상인 경우 배열의 메서드 join 사용

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

function Button({ size, color }) {
  const cssSize = size
  const cssColor = color

  return (
    <button
      className={[style.button, style[cssSize], style[cssColor]].join(' ')}
    >
      버튼
    </button>
  )
}

export default Button

Button.module.css

.button {
  outline: none;
  border: none;
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: green;
  border-radius: 10px;
  color: white;
  margin: 10px;
  width: 60px;
  height: 30px;
  font-size: 16px;
}

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

.medium {
  width: 120px;
  height: 60px;
  font-size: 20px;
}

.small {
  width: 100px;
  height: 50px;
  font-size: 16px;
}

.tomato {
  background-color: tomato;
}

.orange {
  background-color: orange;
}

.skyblue {
  background-color: skyblue;
}

 

 

5. Styled-Components

스타일을 CSS나 SCSS처럼 외부 파일로 만드는것이 아닌 자바스크립트 파일에 내장시키는 방식

자바스크립트 파일 안에 스타일을 선언하는 CSS-in-JS 방식

스타일을 작성함과 동시에 해당 스타일이 적용된 컴포넌트를 만들 수 있게 해줌

자체적으로 클래스를 만들고 그 안에 설정한 스타일링이 들어간다

styled-components 라이브러리 설치

yarn add styled-components

vscode-styled-components 설치

styled-components 신텍스 하이라이팅 해주는 extension

styled components의 장점

  1. 자바스크립트 파일 하나에 스타일까지 작성할 수 있기 때문에 스타일 파일을 따로 만들지 않아도 된다
  2. props 값으로 전달해주는 값을 쉽게 스타일에 적용할 수 있다

 

styled components 사용방법

1. 폴더 구조 만들어주기

components 폴더 안에 사용할 컴포넌트명으로 폴더를 만들어주고 그 안에 index.jsx 파일에는 컴포넌트를 생성하고 style.js파일에는 스타일을 적용한다.

2. App.jsx 파일에 Button 컴포넌트 import 해주기

import Button from './components/Button'
// Button 폴더 안에 있는 index.jsx를 찾아 자동으로 import 해주게 된다
// index 파일은 import할때 생략할 수 있다

function App() {
  return (
    <>
      <Button />
    </>
  )
}

export default App

3. index.js에 버튼으로 사용할 컴포넌트 만들기

import React from 'react'

const Button = () => {
  return <button>버튼</button>
}

export default Button

4. style.js에 SCSS 코드 넣기

import styled from 'styled-components'

export const ButtonStyle = styled.button`
  /* SCSS 코드 */
`

5. index.js에 style.js에서 만든 SCSS 코드 import해오기

방법1

style.js

import styled from 'styled-components'

export const ButtonStyle = styled.button`
  /* SCSS 코드 */
`

index.js

import React from 'react'
import { ButtonStyle } from './style.js'

const Button = () => {
  return <ButtonStyle>버튼</ButtonStyle>
}

export default Button

방법2

style.js

import styled from 'styled-components'

export const Button = styled.button`
  /* SCSS 코드 */
`

index.js

스타일 컴포넌트를 S.Button처럼 작성함으로서 Button과 같은 실제 컴포넌트와 차이를 줄 수 있다.

import React from 'react'
import * as S from './style.js'

const Button = () => {
  return <S.Button}>버튼</S.Button>
}

export default Button

 

styled-components를 사용해서 props 값 넘겨주기

1. 기본 구조

index.jsx

import React from 'react'
import * as S from './style.js'

const Button = () => {
  return (
    <>
      <S.Button color="tomato">버튼</S.Button>
      <S.Button color="orange">버튼</S.Button>
      <S.Button color="skyblue">버튼</S.Button>
    </>
  )
}

export default 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: ${(props) => props.color};
  border-radius: 10px;
  color: white;
  margin: 10px;
`

2. 구조분해할당하면서 props 전달

import styled from 'styled-components'

export const Button = styled.button`
  outline: none;
  border: none;
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: ${({color}) => color};
  border-radius: 10px;
  color: white;
  margin: 10px;
`

3. props값이 없을때 default 값 지정

import styled from 'styled-components'

export const Button = styled.button`
  outline: none;
  border: none;
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: ${({ color }) => color || 'green'};
  border-radius: 10px;
  color: white;
  margin: 10px;
`

4. props와 함께 삼항연산자 사용하기

import React from 'react'
import * as S from './style.js'

const Button = () => {
  return (
    <>
      <S.Button color="tomato" isClicked={true}>
        버튼
      </S.Button>
    </>
  )
}

export default Button
import styled from 'styled-components'

export const Button = styled.button`
  outline: none;
  border: none;
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: ${({ isClicked }) => isClicked ? 'green' : 'gray'};
  border-radius: 10px;
  color: white;
  margin: 10px;
`

 

theme에 색상변수 저장해서 사용해주기

theme.js

theme.js 파일에 자주 사용하는 색상 코드 등록해주기

const colors = {
  primary: '#007bff',
  success: '#28a745',
  danger: '#dc3545',
}

const theme = {
  colors,
}

export default theme

App.jsx

theme.js에 등록한 스타일코드를 전역적으로 사용할 수 있도록 ThemeProivder로 전체 컴포넌트 감싸주기

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

function App() {
  return (
    <ThemeProvider theme={theme}>
      <Button size="large" color="tomato" />
      <Button size="medium" color="orange" />
      <Button size="small" color="skyblue" />
    </ThemeProvider>
  )
}

export default App

style.js

theme으로 등록한 색상변수 style.js에서 props 값으로 받아서 사용하기

import styled from 'styled-components'

export const Button = styled.button`
  outline: none;
  border: none;
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: ${({ theme }) => theme.colors.primary};
  border-radius: 10px;
  color: white;
  margin: 10px;
`

 

theme에 자주 사용하는 스타일코드 객체화해서 사용하기

theme.js

theme.js 파일에 자주 사용하는 스타일코드 객체화해서 등록해주기

const colors = {
  primary: '#007bff',
  success: '#28a745',
  danger: '#dc3545',
}

const common = {
  flexCenter: `
    display: flex;
    align-items: center;
    justify-content: center;
  `,
}

const theme = {
  colors,
  common,
}

export default theme

style.js

import styled from 'styled-components'

export const Button = styled.button`
  ${({ theme }) => theme.common.flexCenter}
  outline: none;
  border: none;
  background-color: ${({ theme }) => theme.colors.primary};
  border-radius: 10px;
  color: white;
  margin: 10px;
`

 

keyframes 사용해서 애니메이션 효과주기

style.js

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

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

export const Button = styled.button`
  ${({ theme }) => theme.common.flexCenter}
  outline: none;
  border: none;
  background-color: ${({ theme }) => theme.colors.primary};
  border-radius: 10px;
  color: white;
  margin: 10px;
  animation-name: ${fadeIn};
  animation-duration: 1s;
  animation-timing-function: ease-out;
`

 

globalstyle 사용해서 폰트 추가하기

globalStyle.js

import { createGlobalStyle } from 'styled-components'
import BlackHanSans from '../assets/fonts/BlackHanSans-Regular.ttf'

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

format() 안에 들어가는 부분은 폰트 파일의 확장자에 따라서 변경해주어야한다:

  • otf 파일 → opentype
  • ttf 파일 → truetype
  • woff 파일 → woff

App.js

globalstyle을 import한후 ThemeProvider 밑에 등록해주어 모든 컴포넌트에서 globalstyle에 접근할 수 있도록 해준다.

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

function App() {
  return (
    <ThemeProvider theme={theme}>
      <GlobalStyle />
      <Button size="large" color="tomato" />
      <Button size="medium" color="orange" />
      <Button size="small" color="skyblue" />
    </ThemeProvider>
  )
}

export default App

style.js

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

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

export const Button = styled.button`
  ${({ theme }) => theme.common.flexCenter}
  outline: none;
  border: none;
  background-color: ${({ theme }) => theme.colors.primary};
  border-radius: 10px;
  color: white;
  margin: 10px;
  animation-name: ${fadeIn};
  animation-duration: 1s;
  animation-timing-function: ease-out;
  font-family: 'BlackHanSans';
`

댓글