FRONT-END/REACTJS

컴포넌트 스타일링

JINGMONG 2022. 2. 19. 22:14

일반 css

일반 CSS를 작성할 때 중요한 것은 CSS 클래스를 중복되지 않게 만드는 것이다. CSS 클래스 중복을 방지하는 방법은 이름을 지을 때 특별한 규칙을 사용하는 것, CSS selector를 활용하는 것이다.

이름 짓는 규칙

클래스 이름을 컴포넌트 이름-클래스 형태로 지어 실수로 중복되는 클래스를 방지한다. 또는 이름을 지을 때 일종의 규칙을 준수하여 해당 클래스가 어디에서 어떤 용도로 사용되는지 명확하게 작성하는 BEM 네이밍을 사용한다.

.App-header {} /* 컴포넌트 이름-클래스 형태 */
.card_title-primary {} /* BEM 네이밍 */

CSS selector

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

.App .logo {} /* App클래스 안에 logo클래스 */

Sass

Sass는 CSS 전처리기로 복잡한 작업을 쉽게 할 수 있도록 해 주고, 스타일 코드의 재활용성을 높여 줄 뿐만 아니라 코드의 가독성을 높여서 유지 보수를 쉽게 해준다.

Sass에서는 .scss와 .sass를 지원한다.

Sass를 사용하려면 Sass라이브러리를 설치해야한다.

$ yarn add sass
$red: #fa5252;
$orenge: #fd7e14;
$yellow: #fcc419;
$blue: #339af0;
$indigo: #5c7cfa;
$violet: #7950f2;

//믹스인 만들기(재사용되는 스타일 블록을 함수처럼 사용)
@mixin square($size) {
  $calculated: 32px * $size;
  width: $calculated;
  height: $calculated;
}

.SassComponent {
  display: flex;
  .box {
    background: red;
    cursor: pointer;
    transition: all 0.3s ease-in;
    &.red {
      background: $red;
      @include square(1);
    }
    &.orenge {
      background: $orenge;
      @include square(2);
    }
    &.yellow {
      background: $yellow;
      @include square(3);
    }
    &.blue {
      background: $blue;
      @include square(4);
    }
    &.indigo {
      background: $indigo;
      @include square(5);
    }
    &.violet {
      background: $violet;
      @include square(6);
    }
    &:hover {
      background: black;
    }
  }
}
import React from "react";
import "../Sass/SassComponent.scss";

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

export default SassComponent;

utils 함수 분리

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

분리된 scss파일은 @import 구문을 사용하여 불러온다.

sass-loader 설정 커스터마이징

프로젝트가 커지고 디렉터리가 복잡해지면 경로를 입력할 때 불편함이 있다.
이 문제를 웹팩에서 Sass를 처리하는 sass-loader의 설정을 커스터마이징하여 해결할 수 있다.

먼저 react-app을 git에 커밋하고 yarn eject 명령어를 실행한다.

$ git add.
$ git commit -m "Commit before yarn eject"
$ yarn eject

yarn eject 하고나면 config 디렉터리에 webpack.config.js파일안에 sassRegex를 검색하고 다음과 같이 설정한다.

{
  test: sassRegex,
  exclude: sassModuleRegex,
  use: getStyleLoaders({
    importLoaders: 3,
    sourceMap: isEnvProduction
      ? shouldUseSourceMap
      : isEnvDevelopment,
    modules: {
      mode: "icss",
    },
  }).concat({
    loader: require.resolve("sass-loader"),
    options: {
      sassOptions: {
        includePaths: [paths.appSrc + "/styles"],
      },
      additionalData: "@import 'utils';",
    },
  }),
  sideEffects: true,
},

node_modules에서 라이브러리 불러오기

Sass의 장점 중 하나는 라이브러리를 쉽게 불러와서 사용할 수 있다는 것이다.

~을 사용하면 자동으로 node_modules에서 라이브러리 디렉터리를 탐지하여 스타일을 불러올 수 있다.

$ yarn add open-color include-media

open-color: 색상 팔레트 라이브러리

include-media: 반응형 디자인을 쉽게 만들어주는 라이브러리

CSS Module

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

.module.css 확장자로 파일을 저장하면 CSS Module이 적용된다.

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

:global .something {
  font-weight: 800;
  color: aqua;
}
import React from "react";
import styles from "../CSSModule.module.css";

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

export default CSSModule;

CSS Module을 사용한 클래스 이름을 두 개 이상 적용하고 싶을 때는 ES6 문법 템플릿 리터럴을 사용하여 문자열을 합해 준다.

<div className={`${styles.wrapper} ${styles.inverted}`}>

문법 템플릿 리터럴을 사용하고 싶지 않다면

<div className={[styles.wrapper, styles.inverted].join(" ")}>

처럼 쓸 수 도 있다.

classnames

classnames는 CSS 클래스를 조건부로 설정할 때 매우 유용한 라이브러리이다.

$ yarn add classnames

CSSModule + classnames

import React from "react";
import styles from "../CSSModule.module.css";
import classNames from "classnames/bind";

const cx = classNames.bind(styles);

const CSSModule = () => {
  return (
    <div className={cx("wrapper", "inverted")}>
      안녕하세요, 저는 <span className="something">CSS Module!</span>
    </div>
  );
};

export default CSSModule;

styled-component

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

styled-compoents를 사용하면 자바스크립트 파일 하나에 스타일까지 작성할 수 있기 때문에 .css 또는 .scss 확장자를 가진 스타일 파일을 따로 만들지 않아도 된다는 장점이 있다.

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

const Box = styled.div`
  background: ${(props) => props.color || "blue"};
  padding: 1rem;
  display: flex;
`;

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

  &:hover {
    background: Range(255, 255, 255, 0.9);
  }

  ${(props) =>
    props.inverted &&
    css`
      background: none;
      border: 2px solid white;
      color: white;
      &:hober {
        backgrond: white;
        color: black;
      }
    `};
  & + button {
    margin-left: 1rem;
  }
`;

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

export default StyledComponent;

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

Tagged 템플릿 리터럴

안에 스타일에 관련된 코드를 작성하였는데 이를 Tagged 템프럴 리터럴 문법이라고 한다.

템플릿에 객체를 넣거나 함수를 넣으면 형태를 잃어 버리게 된다. Tagged 템플릿 리터럴을 사용하면 템플릿 사이사이에 들어가는 자바스크립트 객체나 함수의 원본 값을 그대로 추출할 수 있다. styled-components는 이러한 속성을 사용하여 styled-components로 만든 컴포넌트의 props를 스타일 쪽에서 쉽게 조회할 수 있도록 해준다.

스타일링된 엘리먼트

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

import styled from 'styled-components';

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

사용할 태그명이 유동적이거나 특정 컴포넌트 자체에 스타일링 해주고 싶다면

const MyInput = styled('input')`
  background: gray;
`;

const StyledLink = styled(Link)`
  color: blue;
`;

처럼 구현할 수 있다.

스타일에서 props 조회

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

const Box = styled.div`
  background: ${(props) => props.color || "blue"};
  padding: 1rem;
  display: flex;
`;
...
    <Box color="black">
...

조건부 스타일링

  ${(props) =>
        props.inverted &&
        css`
          background: none;
          border: 2px solid white;
          color: white;
          &:hober {
            backgrond: white;
            color: black;
          }
        `};
      & + button {
        margin-left: 1rem;
      }
    `;
...
      <Button inverted={true}>테두리만</Button>
...

처럼 props 값에 따라서 서로 다른 스타일을 적용할 수 있다.

반응형 디자인

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

const sizes = {
  desktop: 1024,
  tablet: 768
}

const media = Object.keys(sizes).reduce((acc, lable) => {
  acc[lable] = (...args) => css`
    @media (max-width: ${sizes[lable] / 16}em) {
      ${css(...args)};
    }
  `;

  return acc;
}, {});

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

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;

  &:hover {
    background: Range(255, 255, 255, 0.9);
  }

  ${(props) =>
    props.inverted &&
    css`
      background: none;
      border: 2px solid white;
      color: white;
      &:hober {
        backgrond: white;
        color: black;
      }
    `};
  & + button {
    margin-left: 1rem;
  }
`;

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

export default StyledComponent;

https://styled-components.com/docs/advanced#media-templates

 

styled-components: Advanced Usage

Theming, refs, Security, Existing CSS, Tagged Template Literals, Server-Side Rendering and Style Objects

styled-components.com

 

'FRONT-END > REACTJS' 카테고리의 다른 글

react 비동기 작업  (0) 2022.02.19
react route로 SPA 개발  (0) 2022.02.19
Hooks  (0) 2022.01.03
라이프사이클 메서드  (0) 2021.12.28
컴포넌트의 라이프사이클  (0) 2021.12.28