classnames
classnames(https://www.npmjs.com/package/classnames)는 각 태그에 대해서, 혹은 컴포넌트에 대해서 클래스 명칭을 적용하기 위해서 사용하는 className을 더욱더 가독성 있고, 쉽게 작성할 수 있게 도와줍니다. classnames function은 string일수도 있고, object일수도 있는 여러 개의 아규먼트를 받아서 해당 부분을 여러 개의 클래스 명칭을 적용하도록 돕습니다. 또한 bind version을 사용하는 것을 통해서 CSS-Module의 스타일 적용을 돕습니다. 클래스 명칭을 dynamic allocation 하고 싶다면 다음과 같이 작성될 수 있습니다.
const [isRed, setIsRed] = useState<boolean>(false);
const changeColor = () => {
if(isRed) {
setIsRed(true);
} else {
setIsRed(false);
}
}
return (
<div className={'wrapper ' + isRed ? 'red' : 'green'}>
/* this is the same as <div className={`wrapper ${isRed}` ? 'red' : 'green'}> */
/* but, don't edit like this
{isRed ? <div className={'wrapper red'}> : <div className={'wrapper green'}>}
*/
<button onclick={changeColor}>
myButton
</button>
</div>
);
해당 방식이 잘못된 것은 아니나 multi-class를 적용하기 위해서 강제로 space(' ')를 wrapper뒤에 붙여주는 것이 필요하고, 만약에 적용 자체가 없는 경우 ('green'이 아니라 ''을 사용하고 싶다면)에는 사실 빈칸이 하나 존재하는 셈입니다. 그러면 'green' 대신에 ' green'을 사용하면 문제가 해결되는 것일까요? 그보다는 classnames를 적용하는 것이 더 가독성 있는 코드로 다가올 수 있습니다.
import classNames from 'classnames';
...
return (
<div className={classNames(wrapper, { red: isRed, green: !isRed })}>
...
해당 방식은 value에 의해서 key를 제어하는 것을 의미하며, key를 바꾸는 것도 아래 방식처럼 제공합니다.
let buttonType = 'primary';
classNames({ [`btn-${buttonType}`]: true });
CSS-Module에서도 효과적으로 적용할 수 있습니다. 보통 styles로 css module을 사용하는데, 작성된 module.css에서 적용할 클래스 명칭이 hyphen(-)을 가지고 있다면 곤란한 상황이 연출됩니다.
import styles from './App.module.scss';
// 1) syntax error
<div className={styles.wrapper + ' ' + styles.yellow-green}/>
// 2) bypass the error
<div className={styles.wrapper + ' ' + styles['yellow-green']}/>
// 3) code convention but bad
<div className={`${styles['wrapper']} ${styles['yellow-green']}`}/>
그나마 3번 방식이 소스 구성을 할 때 통일성을 주장할 수 있는 방식일 듯합니다. 클래스 명칭을 적용하기 위해서 작성된 CSS에 prefix로 styles를 붙이는 듯한 작성은 클래스 명칭이 길어지고 적용해야 하는 부분이 많아진다면 소스가 난잡해지게 됩니다. 클래스 명칭이 길어지는 것은 근본적으로 해결할 수 없는 문제지만, prefix처럼 붙는 styles를 classnames/bind를 사용하는 것으로 해소할 수 있습니다.
import classNames from 'classnames/bind';
import styles from './App.module.scss';
const cx = classNames.bind(styles);
<div className={cx('wrapper', 'yellow-green')}>
CRA에 SASS-Module 적용하기
// 프로젝트를 생성할 디렉토리(가칭 : my-directory)로 이동
cd my-directory
// 생성할 프로젝트 폴더(가칭 : my-app-css-module)
npx create-react-app my-app-css-module
// + scss를 사용하기 위해서 https://ansrlm.tistory.com/20에서 설명했던 sass를 설치해야 합니다.
npm install sass
# via npm
npm install classnames
# via Bower
bower install classnames
# or Yarn (note that it will automatically save the package to your `dependencies` in `package.json`)
yarn add classnames
디렉토리 이동과 리파지토리 생성을 한 이후에 classnames 패키지를 설치합니다.
App.jsx에 CSS-Module을 적용하기 위해서, 그리고 SASS를 함께 사용하기 위해서 우선 css파일의 이름을 바꾸고, SCSS문법으로 작성하게 됩니다. 파일 명칭을 App.css -> App.module.scss로 변경하는 것을 수반합니다.
// App.module.scss
.App {
text-align: center;
div {
color: greenyellow;
}
}
.App-logo {
height: 40vmin;
pointer-events: none;
}
.MyClass {
color: greenyellow;
}
@media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-spin infinite 20s linear;
}
}
.App-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;
}
.App-link {
color: #61dafb;
}
@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
App.module.scss을 사용하기 위해서 App.jsx도 다음과 같이 변경합니다.
// App.jsx
import React from 'react';
import classNames from 'classnames/bind';
import logo from './logo.svg';
import styles from './App.module.scss';
const cx = classNames.bind(styles);
const App = () => (
<div className={cx('App', 'MyClass')}>
<header className={styles['App-header']}>
<img src={logo} className={styles['App-logo']} alt="logo" />
<div>
{styles.App}
</div>
<p className={`${styles['App-red']} ${styles['App-bold']}`}>
Edit
{' '}
<code>src/App.js</code>
{' '}
and save to reload.
</p>
<a
className={styles['App-link']}
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>
);
export default App;
classnames가 적용된 방법, 적용되지 않은 방법, styles를 적용할 때 object key position을 사용하는 방법, object dot operator를 사용한 방법이 섞여 있으니 참고하시면 되겠습니다. (적용 안 되는 class인 App-red / App-bold도 있습니다.)
Bundler output
webpack의 결과물은 build/static/{css, js}에 존재합니다.
`${file_name}_${class_name}__${hash}`로 구성되어 있는 것을 확인하실 수 있습니다.
또한 앞서 작성했었던 App.jsx에는 있지만 App.module.scss에는 존재하지 않는 App-red / App-blod는 undefined로 할당되는 것을 확인할 수 있습니다.
※ 잘못된 부분이나 궁금한 점 댓글로 작성해 주시면 최대한 답변하겠습니다. 읽어주셔서 감사합니다.
※ CSS #5 - CSS in JS (1)에서 CSS in JS 중 하나인 styled-component를 소개하고 inline style와의 차이점 비교를 진행할 예정입니다.
'React > CSS' 카테고리의 다른 글
CSS #6 - CSS-in-JS (2) Styled-Component with Storybook (0) | 2021.06.27 |
---|---|
CSS #5 - CSS-in-JS (1) Pros and Cons (2) | 2021.06.20 |
CSS #3 - SASS (2) with CSS Module (0) | 2021.05.01 |
CSS #2 - SASS (1) (0) | 2021.04.19 |
CSS #1 - CSS priority (1) | 2021.04.18 |