컴포넌트 스타일링

ho_vi·2023년 9월 19일

React

목록 보기
9/19
post-thumbnail

리액트에서 컴포넌트를 스타일링 할 때는 다양한 방식을 사용할 수 있습니다.

회사마다 요구하는 방식이 다르고 개발자마다 각자 취향에 따라 선택해서 사용할 수 있습니다.

자주 사용되는 스타일링 방식

  • 일반 CSS : 가장 기본적인 방식
  • Sass : 자주 사용되는 CSS 전처리기 중 하나로 확장된 CSS 문법을 사용하여 CSS 코드를 더욱 쉽게 작성 할 수 있도록 해 줍니다.
  • CSS Module : 스타일을 작성 할 때 CSS 클래스가 다른 CSS 클래스의 이름과 절대 충돌하지 않도록 파일마다 고유한 이름을 자동으로 생성해 주는 옵션 입니다.
  • styled-components : 스타일을 자바스크립트 파일에 내장시키는 방식으로 스타일을 작성함과 동시에 해당 스타일이 적용된 컴포넌트를 만들 수 있게 해줍니다.

새로운 프로젝트 생성

$ yarn create react-app kh-styling-ex

$ cd kh-styling-ex

$ yarn start

가장 흔한 방식, 일반 CSS

App.css와 App.js에 적용된 방식을 확인하면 됩니다.

이름 짓는 규칙

프로젝트에서 자동 생성된 App.css를 읽어 보면 클래스 이름이 컴포넌트 이름-클래스 형태로 지어져 있습니다 (App-header). 클래스 이름에 컴포넌트 이름을 포함시킴으로써 다른 컴포넌트에서 실수로 중복된 클래스를 만들어 사용하는 것을 방지 할 수 있습니다.

CSS Selector

CSS Selecore를 사용하면 CSS 클래스가 특정 클래스 내부에 있는 경우에만 스타일을 적용할 수 있습니다.

.App .logo {
  animation: App-logo-spin infinite 20s linear;
}

Sass 사용하기

Sass(Syntactically Awesome Style Sheets)는 CSS를 보완하기 위한 스타일 시트 언어로서, 중첩된 규칙(Nested Rules), 변수(Variables), 믹스인(Mixins) 등의 기능을 제공합니다. Sass는 기본적으로 .sass 확장자를 가지며, 중괄호({}) 대신 들여쓰기를 사용합니다.

SCSS(Sassy CSS)는 Sass의 3버전 이후 추가된 문법으로서, CSS와의 호환성이 더 높습니다. SCSS는 기본적으로 .scss 확장자를 가지며, 중괄호({})와 세미콜론(;)을 사용합니다.

리액트에서 Sass나 SCSS를 사용하려면, 관련 라이브러리를 설치하고, 웹팩(Webpack)이나 Parcel 등의 번들러에서 로더(Loader)를 설정해주어야 합니다. 설정이 완료된 후에는, import 구문을 사용하여 Sass/SCSS 파일을 불러와서 사용할 수 있습니다.

주요 차이점은 .sass 확장자는 중괄호({})와 세미콜론(;)을 사용하지 않습니다.

반면 .scss 확장자는 기존 CSS를 작성하는 방식과 비교해서 문법이 크게 다르지 않습니다.

.scss 문법이 더 자주 사용 됩니다.

import React from 'react';
import './styles.scss'; // Sass(SCSS) 파일 불러오기

function MyComponent() {
  return (
    <div className="my-component">
      <h1 className="title">Hello, world!</h1>
    </div>
  );
}
// styles.scss 파일
$primary-color: #007bff;

.my-component {
  background-color: $primary-color;
}

.title {
  font-size: 2rem;
  color: white;
}
// styles.sass 파일
$primary-color: #007bff

.my-component
  background-color: $primary-color

.title
  font-size: 2rem
  color: white

sass 라이브러리 설치

$ yarn add sass

SassComponent.scss

$red: #fa5252;
$orange: #fd7e14;
$yellow: #fcc419;
$green: #40c057;
$blue: #339af0;
$indigo: #5c7cfa;
$violet: #7950f2;
// 믹스인 만들기(재사용되는 스타일 블록을 함수처럼 사용할 수 있음)
@mixin square($size) {
	$calculated:32px * $size;
	width: $calculated;
	height: $calculated;
}

.SassComponent {
	display: flex;
	.box { // 일반 CSS에서는 .SassComponent .box와 마찬가지
		background: red;
		cursor:pointer;
		transition: all 0.3s ease-in;
		&.red { // .red 클래스가 .box와 함께 사용되었을 때
			background: $red;
			@include square(1);
		}
		&.orange {
			background: $orange;
			@include square(2);
		}
		&.yellow {
			background: $orange;
			@include square(3);
		}
		&.green {
			background: $orange;
			@include square(4);
		}
		&.blue {
			background: $orange;
			@include square(5);
		}
		&.indigo {
			background: $orange;
			@include square(6);
		}
		&:hover {
			// .box에 마우스를 올렸을 때
			background: black;
		}
	}
}

SassComponent.js

import './SassComponent.scss';

const SassComponent = () => {
    return (
        <div className='SassComponent'>
            <div className='box red' />
            <div className='box orange' />
            <div className='box yellow' />
            <div className='box green' />
            <div className='box blue' />
            <div className='box indigo' />
            <div className='box violet' />
        </div>
    );
};

export default SassComponent;

App.js

import './App.css';
import SassComponent from './SassComponent';

function App() {
  return (
    <div>
      <SassComponent />
    </div>
  );
}

export default App;

utils 함수 분리하기

여러 파일에서 사용될 있는 Sass 변수 및 믹스인은 다른 파일로 따로 분리하여 작성한 뒤 필요한 곳에서 쉽게 불러와 사용할 수 있습니다.

src/styles/utils.scss

$red: #fa5252;
$orange: #fd7e14;
$yellow: #fcc419;
$green: #40c057;
$blue: #339af0;
$indigo: #5c7cfa;
$violet: #7950f2;
// 믹스인 만들기(재사용되는 스타일 블록을 함수처럼 사용할 수 있음)
@mixin square($size) {
	$calculated:32px * $size;
	width: $calculated;
	height: $calculated;
}

SassComponent.scss

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

.SassComponent {
	display: flex;
	.box { // 일반 CSS에서는 .SassComponent .box와 마찬가지
		background: red;
		cursor:pointer;
		transition: all 0.3s ease-in;
		&.red { // .red 클래스가 .box와 함께 사용되었을 때
			background: $red;
			@include square(1);
		}
		&.orange {
			background: $orange;
			@include square(2);
		}
		&.yellow {
			background: $yellow;
			@include square(3);
		}
		&.green {
			background: $green;
			@include square(4);
		}
		&.blue {
			background: $blue;
			@include square(5);
		}
		&.indigo {
			background: $indigo;
			@include square(6);
		}
		&:hover {
			// .box에 마우스를 올렸을 때
			background: black;      
		}
	}
}

CSS Module

CSS Module은 CSS를 불러와서 사용 할 때 클래스 이름을 고유한 값, 즉 [파일 이름]{[클래스 이름]__[해시값] 형태로 자동으로 만들어서 컴포넌트 스타일 클래스 이름이 중첩되는 현상을 방지해 주는 기술 입니다.

CSSModule.module.css

/* 자동으로 고유해질 것이므로 흔히 사용되는 단어를 클래스 이름으로 마음대로 사용 가능 */

.wrapper {
    background: black;
    padding: 1rem;
    color: white;
    font-size: 2rem;
}

/* 글로벌 CSS를 작성하고 싶다면 */
:global .something {
    font-weight: 800;
    color: aqua;
}

CSS Module을 사용하면 클래스 이름을 지을 때 그 고유성에 대해 고민하지 않아도 됩니다.

만약 특정 클래스가 웹페이지에서 전역적으로 사용되는 경우라면 :global을 앞에 입력하여 글로벌 CSS임을 명시해 줄 수 있습니다.

App.js

import './App.css';
import CSSModule from './CSSModule';

function App() {
  return (
    <div>
      <CSSModule />
    </div>
  );
}

export default App;

결과 확인

CSS Module을 사용한 클래스 이름을 두 개 이상 적용할 때는 다음과 같이 코드를 작성하면 됩니다.

CSSModule.module.css

CSS Module을 사용한 클래스 이름을 두 개 이상 적용할 때는 다음과 같이 코드를 작성하면 됩니다.

/* 자동으로 고유해질 것이므로 흔히 사용되는 단어를 클래스 이름으로 마음대로 사용 가능 */

.wrapper {
    background: black;
    padding: 1rem;
    color: white;
    font-size: 2rem;
}

.inverted {
    color: block;
    background: white;
    border: 1px solid black;
}

/* 글로벌 CSS를 작성하고 싶다면 */
:global .something {
    font-weight: 800;
    color: aqua;
}

CSSModule.js

import styles from './CSSModule.module.css';

const CSSModule = () => {
    return (
        <div className={`${styles.wrapper} ${styles.inverted}`}>
            안녕하세요. 저는 <span className='something'>CSS Module!</span>
        </div>
    );
};

export default CSSModule;

styled-components

컴포넌트 스타일링의 또 다른 패러다임은 자바스크립트 파일 안에 스타일을 선언하는 방식 입니다.

이 방식을 CSS-in-JS 라고 부릅니다.

CSS-in-JS 라이브러리 중에서 개발자들이 가장 많이 사용하는 styled-components를 사용해 보겠습니다.

$ yarn add styled-components

styled-components와 일반 className를 사용하는 CSS/Sass를 비교했을 때, 가장 큰 장점은 props값으로 전달해 주는 값을 쉽게 스타일에 적용할 수 있다는 것입니다.

👉 **vscode-styled-components** : VS Code에서 styled-components를 작성할 때 도움이 되는 플러그인 👉 **`css`**는 **`styled-components`**패키지에서 제공하는 함수로, 스타일을 재사용하기 위해 사용됩니다. **`css`**함수는 스타일을 정의하는 데 사용되는 템플릿 리터럴을 반환합니다.

StyledComponent.js

import styled, { css } from 'styled-components';

const Box = styled.div`
    background: ${props => props.color || 'blue'};
    padding: 1rem;
    display: flex;
    width: 1024px;
    margin: 0 auto;
    @media (max-width: 1024px) {
        width: 768px;
    }
    @media (max-width: 768px) {
        width: 90%;
    }
`;
const Button = styled.button`
  background: white;
  color: black;
  border-radius: 4px;
  padding: .5rem;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  font-size: 1rem;
  font-weight: 600;
  &:hover {
    background: rgba(255,255,255,0.5);
  }
  ${(props => props.inverted && css`
    background: none;
    border: 2px solid white;
    color: white;
    &:hover {
        background: white;
        color: black;
    }
  `)};
  & + button {
    margin-left : 1rem
  }
`;

const StyledComponent = () => {
    return(
        <Box color="orangered">
            <Button>안녕하세요.</Button>
            <Button inverted={true}>테두리만</Button>
        </Box>
    );
};
export default StyledComponent;

App.js

import './App.css';
import StyledComponent from './StylesComponent';

function App() {
  return (
    <div>
      <StyledComponent />
    </div>
  );
}

export default App;

스타일링된 엘리먼트 만들기

styled-components를 사용하여 스타일링된 엘리먼트를 만들 때는 컴포넌트 파일의 상단에서 styled를 불러오고, styled.태그명을 사용하여 구현 합니다.

import styled from 'styled-components';

const MyComponent = styled.div`
	font-size: 2rem;
`;

스타일에서 props 조회하기

styled-components를 사용하면 스타일 쪽에서 컴포넌트에게 전달된 props 값을 참조할 수 있습니다.

  • background 값에 props를 조회해서 props.color의 값을 사용 합니다. 값이 없으면 blue를 기본 색상으로 설정
const Box = styled.div`
    background: ${props => props.color || 'blue'};
    padding: 1rem;
    display: flex;
`;

props에 따른 조건부 스타일링

일반 CSS 클래스를 사용하여 조건부 스타일링을 해야 할 때는 className을 사용하여 조건부 스타일링을 합니다.

styled-componenets에서는 조건부 스타일링을 간단하게 props로도 처리 할 수 있습니다.

const Button = styled.button`
  background: white;
  color: black;
  border-radius: 4px;
  padding: 0.5rem;
  display: flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  font-size: 1rem;
  font-weight: 600;

  /* & 문자를 사용하여 Sass 처럼 자기 자신 선택 가능 */
  &:hover {
    background: rgba(255, 255, 255, 0.9);
  }

  /* 다음 코드는 inverted 값이 true 일 때 특정 스타일을 부여해줍니다. */
  ${(props) =>
    props.inverted &&
    css`
      background: none;
      border: 2px solid white;
      color: white;
      &:hover {
        background: white;
        color: black;
      }
    `};
  & + button {
    margin-left: 1rem;
  }
`;

반응형 디자인

일반 CSS를 사용할 때와 똑같이 media 쿼리를 사용하면 됩니다.

기본적으로 가로 크기 1024px에 가운데 정렬을 하고 가로 크기가 작아짐에 따라 크기를 줄이고 768px미만이 되면 꽉 채웁니다.

import styled, { css } from 'styled-components';

const Box = styled.div`
    background: ${props => props.color || 'blue'};
    padding: 1rem;
    display: flex;
    width: 1024px;
    margin: 0 auto;
    @media (max-width: 1024px) {
        width: 768px;
    }
    @media (max-width: 768px) {
        width: 100px
    }
`;

const Button = styled.button`
  background: white;
  color: black;
  border-radius: 4px;
  padding: 0.5rem;
  display: flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  font-size: 1rem;
  font-weight: 600;

  /* & 문자를 사용하여 Sass 처럼 자기 자신 선택 가능 */
  &:hover {
    background: rgba(255, 255, 255, 0.9);
  }

  /* 다음 코드는 inverted 값이 true 일 때 특정 스타일을 부여해줍니다. */
  ${(props) =>
    props.inverted &&
    css`
      background: none;
      border: 2px solid white;
      color: white;
      &:hover {
        background: white;
        color: black;
      }
    `};
  & + button {
    margin-left: 1rem;
  }
`;

const StyledComponent = () => (
  <Box color="black">
    <Button>안녕하세요</Button>
    <Button inverted={true}>테두리만</Button>
  </Box>
);
  
  export default StyledComponent;

& + button은 스타일드 컴포넌트에서 &를 사용하여 부모 요소를 참조하고, +를 사용하여 바로 뒤에 나오는 button요소를 선택하는 CSS 선택자입니다.

이 선택자는 "부모 요소 바로 뒤에 나오는 button요소"에 스타일을 적용하게 됩니다.

이때 +를 사용하는 것은 인접 형제 선택자(Adjacent Sibling Selector)라고 불리며, 이 선택자를 사용하면 두 요소가 형제 요소이면서도 바로 이웃 관계(바로 옆에 위치하는 관계)일 때만 스타일이 적용됩니다.

profile
FE 개발자🌱

0개의 댓글