본문 바로가기
Programming/React.js

8. Component styling

by _S0_H2_ 2022. 3. 8.
728x90
반응형

새로운 프로젝트를 만들자.

yarn create react-app styling-react

 

CSS

CSS 클래스가 중복되지 않게 만들기 위해서 1 ) 이름을 지을 때 특별한 규칙을 사용하여 짓거나 2 ) CSS Selector를 활용할 수 있다. 그 중 BEM Naming은 해당 클래스가 어디에서 어떤 용도로 사용되는지 명확하게 작성하는 방식이다. css Selector를 사용하면 CSS 클래스가 특정 클래스 내부에 있는 경우에만 스타일을 적용할 수 있다. 예를들어 .App .logo{ }는 App의 logo에만 스타일이 적용된다. 

 

Sass(Syntactically Awesome Style Sheets)

CSS 전처리기로 복잡하 작업을 쉽게 할 수 있도록 해주고, 스타일 코드의 재활용성을 높여주고, 코드의 가독성을 높여서 유지 보수를 쉽게 해준다. 확장자는 .scss 와 .sass 두 가지가 있는데 문법이 꽤 다른편이다. 주요 차이점은 .sass는 중괄호와 세미콜론을 사용하지 않는다. .scss는 기존 CSS를 작성하는 방식과 비슷하다.

- sass 설치

yarn add sass

- SassComponent.scss

* transition: all 0.3s ease-in : 요소에 접근할 때 스르르~ 가는 느낌

* @import : 다른 스타일 시트에서 스타일 규칙을 가져올 때 사용된다. 조건부 사용을 제한된다.

* :hover: 사용자 커서가 요소 위에 올라가 있을 때 선택된다.

$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 {
      background: red; // 일반 CSS 에선 .SassComponent .box 와 마찬가지
      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);
      }
      &.violet {
        background: $violet;
        @include square(7);
      }
      &:hover {
        // .box 에 마우스 올렸을 때
        background: black;
      }
    }
  }

- SassComponent.js

import React from 'react';
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.js';

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

export default App;

Utils 함수 분리하기

자주 사용하는 Utils를 분리하기 위해 src 폴더 안에 styles/utils.scss 를 생성하고 기존의 SassComponent.scss 파일 안에 있던 부분을 잘라내자. 그리고 SassComponent.scss 는 다음을 추가해준다.

- 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 { ... }

위와 결과는 동일하다.

 

sass-loader customizing

만약 프로젝트의 directory가 깊어져 style을 쉽게 사용하기 어렵다면 sass-loader 설정을 커스터마이징할 수 있다. 이를 위해서는 프로젝트 디렉터리에서 yarn eject 명령어를 통해 세부 설정을 밖으로 꺼내 주어야한다. 

yarn eject

프로젝트 디렉터리에 config 디렉터리가 생성된다. 이 안에서 webpack.config.js 파일의 "saRegex"를 보면 두 번째 결과에서 use:에 있는 'sass-loader'부분을 지우고 concat을 통해 커스터마이징 된 sassloader 설정을 넣어주자. 서버를 다시 껐다 켜면 이제 styles 디렉터리 기준 절대 경로를 사용하여 불러올 수 있다.

 

- webpack.config.js - sassRegex 2번째 결과 부분 아래와 같이 수정

{
              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"],
					},
				  },
			  }),
              // Don't consider CSS imports dead code even if the
              // containing package claims to have no side effects.
              // Remove this when webpack adds a warning or an error for this.
              // See https://github.com/webpack/webpack/issues/6571
              sideEffects: true,
            },

 

- SassComponent.js에서 이제 @import 'utils.scss' 만으로 사용할 수 있다.

 

만약 모든 곳에서 사용한다면 일일이 입력하기 번거롭기 때문에 위의 파일에서 additionalData 를 입력하면 된다.

- webpack.config.js - sassRegex 2번째 결과 부분 

.concat({
  loader: require.resolve("sass-loader"),
  options:{
    sassOptions: {
        includePaths: [paths.appSrc + "/styles"],
    },
    additionalData: "@import 'utils';",
  },

그러면 파일에서 import를 입력하지 않아도 된다.

 

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

두가지 유용한 sass 라이브러리를 설치하고 사용해보자.

yarn add open-color include-media

- utils.scss

@import '~include-media/dist/include-media';
@import '~open-color/open-color';

- SassComponent.scss

.SassComponent {
    display: flex;
    background: $oc-gray-2;
    @include media('<768px') {
        background: $oc-gray-9;
    }
    .box{...}
}

 

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

- CSSModule.js

import React from 'react';
import styles from './CSSModule.module.scss';

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

export default CSSModule;

- App.js

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

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

export default App;

 

만약 CSS.Module을 사용한 클래스 이름을 두 개 이상 적용하고 싶다면 다음과 같이 사용할 수 있다.

- CSSModule.module.css

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

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

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

- CSSModule.js

<div className={`${styles.wrapper} ${styles.inverted}`}>
  안녕하세요, 저는 <span className="something">CSS Module!</span>
</div>

classnames

다음 라이브러리로 class를 편리하게 적용할 수 있다.

yarn add classnames

- CSSModule.js

import React from 'react';
import styles from './CssModule.module.css';
import classNames from 'classnames/bind'

const cx = classNames.bind(styles); // 미리 style에서 class를 받아옴

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

export default CSSModule;

 

Styled-components

컴포넌트 스타일링 중 자바스크립트 파일 안에 스타일을 선언하는 방식(CSS-in-JS)이 있다. 

다음을 설치해보자.

yarn add styled-components

- StyledComponent.js

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

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

// 위에있는 size 객체에 따라 자동으로 media 쿼리 함수를 만들어줍니다.
// 참고: https://www.styled-components.com/docs/advanced#media-templates
const media = Object.keys(sizes).reduce((acc, label) => {
  acc[label] = (...args) => css`
    @media (max-width: ${sizes[label] / 16}em) {
      ${css(...args)};
    }
  `;

  return acc;
}, {});

const Box = styled.div`
  /* props 로 넣어준 값을 직접 전달해줄 수 있습니다. */
  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;
  /* & 문자를 사용하여 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;

- App.js

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

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

export default App;

개인적으로는 코드를 따로 분리하는게 더 편리한 것 같다는 느낌이 든다..

728x90
반응형

'Programming > React.js' 카테고리의 다른 글

10. immer사용하여 불변성 유지하기  (0) 2022.08.31
9. 컴포넌트 성능 최적화  (0) 2022.08.24
7. Hooks  (0) 2022.03.06
6. component의 lifecycle method  (0) 2022.03.06
5. 반복적으로 Component 사용하기  (0) 2022.03.05