React
20210607 React05 : Style Loders, CSS, SASS, CSS module, SASS module, classNames 라이브러리(객체 key module.css로 만들기), 객체 value 생략표현
React 05
React Component Styling
Style Loders
- Style을 적용하기 위한 파일들이 동작하기 위함 배포용 파일로 변화시키기 위한 설정들로 4가지 방식이 있음
- CSS, CSS Module, Sass, Sass Module
- 위의 4개의 type에 webpack에서 loader를 붙이고 설정하여 사용하게 됨
// webpack.config.js
// style files regexes
const cssRegex = /\.css$/;
const cssModuleRegex = /\.module\.css$/;
const sassRegex = /\.(scss|sass)$/;
const sassModuleRegex = /\.module\.(scss|sass)$/;
CSS, SASS
- 기본적으로 react의 스타일링 체계의 경우 컴포넌트에 귀속적으로 스타일링이 되는 것이 아니고 전역적으로 스타일링을 필요로 함
- react가 다른 css체계에 의존하지 않고 독립적인 스타일링 하는 기능을 제공하고 있지 않음 (컴포넌트 별로 스코핑이 되지 않음)
- head에 style 태그에 순서대로 쌓이는 구조임
CSS Styling
- 스타일을 줄려고 하는 class의 이름들이 전역적으로 오염되는 것을 주의해야 함
- 방법01 : 이런 오염들을 막기 위해 CSS 방법론(Naming)이 생겨남 -> BEM (Block, Element, Modifier)
- CRA 기본 app.css 의 className은 BEM으로 기본설정 되어 있음
- 방법02 : 물론, css에서 제공하고 있는 위계질서 (일치, 후손, 자식, 등등..)을 사용하면 됨
- 방법01 : 이런 오염들을 막기 위해 CSS 방법론(Naming)이 생겨남 -> BEM (Block, Element, Modifier)
SASS Styling
- css 스타일링의 한계점을 개선하기 위해 다른 문법을 사용해서 css로 변환해서 사용하는 방식이 있음
- 대표적으로 SCSS인데, 스타일링 문법에 포함 구조를 넣어 위계 질서를 더 편하게 줄 수 있고, 그외의 변수 및 여러 기능들을 제공하여 편함
- webpack loader에는 SCSS 변환을 연결해 놓았지만, dev server에서는 확인할 수 없기 때문에 따로 SCSS 모듈을 설치해서 확인 해야함 (
npm i sass
, 패키지 이름이 scss가 아님을 주의!) - CRA에서는 scss 설정이 모두 되어 있기 때문에 scss 만 설치하면 쉽고 간편하게 scss 방식을 사용 할 수 있음
// App.css 를 App.scss 형식으로 구조를 바꾸어 설정
.App {
text-align: center;
.logo {
height: 40vmin;
pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) {
.logo {
animation: App-logo-spin infinite 20s linear;
}
}
.header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}
.link {
color: #61dafb;
}
@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
}
- App.js에 scss 파일 연결 (물론 sass가 설치된 상황이어야 함)
// App.js
import logo from "./logo.svg";
// import './App.css';
import "./App.scss";
function App() {
return (
<div className="App">
<header className="header">
<img src={logo} className="logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>
);
}
export default App;
CSS module, SASS module
CSS, SASS Moudule
- CRA : CSS Module
- 기존의 CSS, SASS 파일을 import하는 경우 전역적으로 style이 순서만 다르게 들어가기 때문에 스코프가 오염될 수 있는 단점이 있음
- CSS Module은 그런 오염을 막을 수 있게 자동으로 클래스 명을 바꾸어 사용할수 있게 도와줌
- 일반적인 형식의 후손, 일치 선택자 등을 사용한 위계 질서를 통해서 작성된 CSS를 파일명이
module.css
로 끝나게 파일을 하나 만들고 사용될 JS파일에서 import 시켜서 styles 객체를 꺼내와 사용하면 됨 - 이 모듈의 경우 우리가 작성한 className을
[filename]_[classname]__[hash]
형식으로 바꾸어 줌
- SASS Module도 이미 다 CRA에서 설정이 되어 있기 때문에, 그냥
module.scss
로 scss 파일 만들고 사용할 컴포넌트 JSX파일에 import만 시켜주면됨
import styles from "./App.module.css";
console.log(styles);
// {
// App: "App_App__2asuU"
// App-logo-spin: "App_App-logo-spin__3qVcD"
// header: "App_header__3G0EC"
// link: "App_link__1KD3u"
// logo: "App_logo__3Ycvw"
// }
- 범용적인 이름을 css class명으로 사용하여 작성했지만, JSX에서 className 붙일때 module을 통해서 자동으로 hash 값을 부여하도록 하기 때문에 겹치지 않게 class 명을 만들어줌
import styles from "./App.module.css";
function App() {
return (
<div className={styles["App"]}>
<header className={styles["header"]}>
<img src={logo} className={styles["logo"]} alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className={styles["link"]}
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>
);
}
export default App;
- 모듈에서 자동으로 만들어준 key-value 구조로 JSX에서는 className으로 모듈의 key를 통해 값을 불러오게 연결하여 사용하면 됨
styles['key']
형태로 사용하여 할당
컴포넌트 전용 css, sass moudule 관리
컴포넌트 JSX 파일에 해당 JSX파일의 CSS module 파일을 연결함으로써 전역적인 관리가 아닌 독립적인 관리가 가능해진다.
state가 없는 상황, function component 사용시
// 해당 컴포넌트에 맞는 css를 연결
import styles from "./Button.module.css";
const Button = (props) => <button className={styles["button"]} {...props} />;
export default Button;
.button {
background-color: transparent;
border-radius: 3px;
border: 2px solid palevioletred;
color: palevioletred;
margin: 0 1em;
padding: 0.25em 1em;
font-size: 20px;
}
- state를 사용하여, class component 사용시
- 버튼을 누르면 state의 loading 값이 true 그리고 1초뒤 false로 변함
- className에 loading 값을 조건으로 삼항 연산자를 통해서 className을 제어함
- button만 아니면 button, loading 둘다.
import React from "react";
import styles from "./Button.module.css";
class Button extends React.Component {
state = {
loading: false,
};
render() {
return (
<button
onClick={this.startLoading}
className={
this.state.loading
? `${styles["button"]} ${styles["loading"]}`
: styles["button"]
}
{...this.props}
/>
);
}
startLoading = () => {
this.setState({
loading: true,
});
setTimeout(() => {
this.setState({
loading: false,
});
}, 1000);
};
}
export default Button;
.button {
background-color: transparent;
border-radius: 3px;
border: 2px solid palevioletred;
color: palevioletred;
margin: 0 1em;
padding: 0.25em 1em;
font-size: 20px;
}
.loading {
border: 2px solid grey;
color: grey;
}
- 이렇게 하면 간단한 기능이지만 생각보다 코드가 많이지고 복잡해 져서 비효율적으로 보이기 때문에 라이브러리를 통해서 개선할 수 있다.
ClassNames 라이브러리
- className을 쉽게 사용할 수 있게 해줌
npm i classnames
- classNames로 함수를 받아 사용함
- classNames의 경우 인자를 넣으면 해당 인자를 스페이스로 띄워 반환시켜줌
- true, false값을 가진 key-value의 객체
{key: true or false}
를 인자로 넣으면 해당 인자의 값이 true 이면, 정상적으로 내보내고, false이면 내보내지 않음 - 정확히 true, false가 아닌 Falsy, Truthy 값도 인지하여 값을 선택함
null
,false
,undefined
,0
,{ bar: null }
,''
-> Falsy'bar'
,1
-> Truthy
import styles from "./Button.module.css";
import classNames from "classnames";
console.log(classNames("foo", "bar")); // foo bar
console.log(classNames("foo", "bar", "baz")); // foo bar baz
console.log(classNames({ foo: true }, { bar: false })); // foo
console.log(classNames(null, false, "bar", undefined, 0, 1, { bar: null }, "")); // bar 1
console.log(classNames(styles["button"], styles["loading"]));
// Button_button__2Px41 Button_loading__2Fgtc
- 객체 형태로 값을 가지게 하고 싶은데 key값은
[]
또는.
의 형태로 지정 못함
import React from "react";
import styles from "./Button.module.css";
import classNames from "classnames";
class Button extends React.Component {
state = {
loading: false,
};
render() {
return (
<button
onClick={this.startLoading}
className={classNames(styles["button"], {
loading: this.state.loading,
// loading이라는 class이름이 있냐 없냐가 됨 (이 방법으론 module형을 표현 불가함)
// styles['loading']: this.state.loading 표현 불가
})}
{...this.props}
/>
);
}
startLoading = () => {
this.setState({
loading: true,
});
setTimeout(() => {
this.setState({
loading: false,
});
}, 1000);
};
}
export default Button;
classNames 라이브러리 : 객체 key값 module.css 화 시키는 방법
- 이러한 문제점을 해결하기위해서 classNames 라이브러리에서 제공하고 있는
bind
함수를 사용하면 됨 classNames.bind(styles)
를 받아 기존의 classNames 함수 사용하는 것 처럼 사용하면됨
import styles from './Button.module.css';
import classNames from 'classnames/bind';
const cx = classNames.bind(styles);
console.log(cx('button', 'loading')); // styles 모듈을 거친 표현으로 className이 바뀜
render() {
return (
<button
onClick={this.startLoading}
className={cx('button', {loading: this.state.loading})}
{...this.props}
/>
);
}
객체 생략 표현 사용하여 더 간단하게 만들기
- 미리 state 값을 객체 key와 같은 이름의 변수로 받아와 사용하여 연결하면, 객체의 key-value 생략 표현을 사용할 수 있음
- 객체의 key이름과 값의 변수 명이 같으면 생략 표현 가능
- {keyName : valueVariableName} -> keyName === valueVariableName ->
{ keyName }
import styles from './Button.module.css';
import classNames from 'classnames/bind';
const cx = classNames.bind(styles);
render() {
// 값의 변수명을 같게 만들기도 하고, state의 loading key를 새로 받아(얕은 복사) 사용
const { loading } = this.state;
return (
<button
onClick={this.startLoading}
className={cx('button', { loading })}
{...this.props}
/>
);
}