[Day 45] Styling(BEM, CSS Modules, React UI Framework, Sass, StoryBook)

CSS 방법론

BEM (Block Element Modifier)

정의

클래스 이름을 짓는 방법 (.header__navigation--secondary 같은 형식으로 쓴다.)

BEM에서는 클래스 이름으로 어떤 요소의 명확한 역할과 책임만 명시해준다.

공유되는 부분은 scss 문법의 @mixin을 이용해서 표현한다.

Block

문단 전체에 적용된 엘리먼트 또는 엘리먼트를 담고 있는 컨테이너를 말한다.

/* 리액트에서는 컴포넌트의 이름을 블록 이름으로 쓴다. */
.PostList {...}
.PostDetail {...}

Element

엘리먼트는 블록 안에서 특정 기능을 수행하는 컴포넌트를 말한다.

/* 블록이름__엘리먼트이름 구조로 네이밍한다. */
.header__logo {...}
.header__menu {...}
.header__search {...}
.header__login {...}

/* 블록이름이나 엘리먼트 이름이 긴 경우 '-' 으로 연결한다. */
.block-name__element-name

Modifiers

모디파이어는 블록 또는 엘리먼트의 속성(상태)이다. —> 이 속성은 블록 또는 엘리먼트의 외관이나 상태를 변화시킨다.

/* Class 명에 '-'를 추가해서 모디파이어를 추가한다. */
.block--modifier {...}
.block__element--modifier {...}

CSS Modules

Create React App - Adding a CSS Modules Stylesheet

CSS Modules는 Create React App ver2.0+ 에서 새로 추가된 기능이다.

클래스 이름을 유일하고 식별 가능하게 자동으로 만들어준다. —> 즉, BEM을 자동으로 해준다.

(장점) 여러 파일에서 같은 클래스 이름을 사용할 수 있게 해준다.

import React, { Component } from 'react';
import styles from './Button.module.css'; // Import css modules stylesheet as styles
import './another-stylesheet.css'; // Import regular stylesheet

class Button extends Component {
  render() {
    // reference as a js object
    return <button className={styles.error}>Error Button</button>;
  }
}

/* 
CSS의 클래스 이름이 규칙에 맞춰서 변환되고, import 해오는 styles 객체에 매핑된다. ---> 객체의 속성이름에는 내가 정한 클래스명이 사용되고, 속성값에는 변환된 클래스명이 저장된다.
*/

React UI Framework

기능을 컴포넌트 단위로 미리 만들어 놓은 것.

  1. Semantic UI React (Semantic UI의 React 버전)
  2. ReactStrap (BootStrap의 React 버전)

Sass

Variables

$font-stack:    Helvetica, sans-serif;
$primary-color: #333;

body {
  font: 100% $font-stack;
  color: $primary-color;
}

Nesting

nav {
  ul {
    margin: 0;
    padding: 0;
    list-style: none;
  }

  li { display: inline-block; }

  a {
    display: block;
    padding: 6px 12px;
    text-decoration: none;
  }
}

Import

// base.scss

@import 'reset';

body {
  font: 100% Helvetica, sans-serif;
  background-color: #efefef;
}

파일을 불러와서 합쳐준다. —> 합쳐진 파일만 제공하면 된다.

여러 파일에서 공유해야 하는 변수, 코드를 다른 파일에다 작성해두고 @import 해오는 방식으로 사용하면 된다.

MixIns

공유되어야 하는 코드 뭉치를 @mixin으로 묶어서 함수처럼 재사용할 수 있다.

// mixin은 일종의 함수와 같다.
@mixin transform($property) {
  -webkit-transform: $property;
  -ms-transform: $property;
  transform: $property;
}

// mixin을 불러올 때는 @include
.box { @include transform(rotate(30deg)); }

(중요!!) MixIn을 활용한 Media Query 작성

// commons.scss
@mixin desktop {
  @media screen and (min-width: 768px) {
    // @include에서 받아온 내용을 @content에 매핑해준다.
    @content
  }
}

// index.scss
&__list {
    @include common-border;
    @include desktop {
      // 아래의 코드가 @content에 매핑된다.
      border-color: yellow;
    }
  }

Operators

CSS 에서의 calc() 함수와 비슷하게 계산을 해준다.

.container {
  width: 100%;
}

article[role="main"] {
  float: left;
  width: 600px / 960px * 100%;
}

aside[role="complementary"] {
  float: right;
  width: 300px / 960px * 100%;
}

UI Develop Utils

StoryBook

UI 개발할 때 각각의 페이지를 하나하나 스토리북으로 만들어서 테스트하기 편하게 해주는 도구 —>

// 설치는 아래와 같이 한다.
npx -p @storybook/cli sb init

// 시작은 아래와 같이 한다.
npm run storybook

설치하고 난 뒤 project root directory에 생긴 .storybook 폴더에 들어있는 config.js 파일을 수정해준다. —> Storybook의 Default Setting은 src/stories 에서 각각의 컴포넌트를 확인할 수 있는 구조로 되어 있는데, 이를 src/components 에서 확인할 수 있는 구조로 바꿔준다.

// .storybook/config.js
import { configure } from '@storybook/react';

const req = require.context('../src/components', true, /\.stories\.js$/);

function loadStories() {
  req.keys().forEach(filename => req(filename));
}

configure(loadStories, module);

컴포넌트를 Storybook 에서 확인하려면 따로 파일을 만들어야 한다.

// 파일명은 보통 ComponentName.stories.js 형태로 만든다.
// 이 파일의 코드에 스토리북에 넣을 컴포넌트를 매핑해주면 된다.

// ex. PostForm.stories.js
import React from 'react';

import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import { linkTo } from '@storybook/addon-links';

import { Button, Welcome } from '@storybook/react/demo';
import PostForm from './PostForm'

// .add('title', () => <ComponentName />) 형태로 넣어준다.
// props가 들어오는 상황에 따라 각기 다른 페이지로 만들어줄 수 있다.
storiesOf('PostForm', module)
  .add('default', () => <PostForm />)
  .add('editing', () => <PostForm editing = {true} />)

스토리북은 통신이나 상태변화 같은 부작용을 일으키는 코드가 없는(외부세계와 연동되지 않은) 컴포넌트에서만 사용해야 한다.

—> 부작용이 있는 컴포넌트에서는 테스트하기가 어렵기 때문에 컴포넌트를 만들 때 역할과 책임을 명확하게 정해줘야 한다. (화면을 그리기만 하는 컴포넌트 vs 부작용을 일으키는 컴포넌트)

—> 컴포넌트를 부작용을 담당하는 컴포넌트(Container Component)화면 그리기만 담당하는 컴포넌트(Presentational Component)로 나눠서 작성하는 것이 관례(Best Practice)다.

Docs - Presentational and Container Components

태그: , ,

업데이트:

댓글남기기