01.11 리액트 컴포넌트 CSS 적용 ( CSS Module, Sass, styled-components )
React.js |
01.11. 컴포넌트 CSS 적용 ( CSS Module, Sass, styled-components )
- CSS Module :
모듈화된 CSS로 CSS 클래스를 만들면 자동으로 고유한 클래스네임을 생성하여 스코프를 지역적으로 제한하는 방법이다. - Sass :
CSS 전처리기 중 하나로, 확장된 CSS 문법을 사용하여 CSS 코드를 더욱 쉽게 작성하는 방식이다. CSS Module 처럼 사용하는 방법도 있다. - styled-components : JS 코드 내부에서 스타일을 정의한다.
01.11.1. CSS Module
CSS Module 은 CSS를 모듈화하여 사용하는 방식으로 CSS 클래스를 만들면 자동으로 고유한 클래스네임을 생성하여 스코프를 지역적으로 제한한다. 모듈화된 CSS를 webpack으로 불러오면 사용자가 정의한 클래스 네임과 고유화된 클래스네임으로 구성된 객체를 반환한다.
{ box: 'src-App__box--mjrNr' }
// 클래스 적용할 때는 아래와 같이 사용한다. className = {styles.box}
새로운 프로젝트를 생성한 후 CSS Module을 활성화한다.
$ create-react-app study-css $ cd study-css $ yarn eject
config/webpack.config.js 파일의 css-loader 설정 부분을 확인한다.
const getStyleLoaders = (cssOptions, preProcessor) => { const loaders = [ isEnvDevelopment && require.resolve('style-loader'), isEnvProduction && { loader: MiniCssExtractPlugin.loader, options: Object.assign( {}, shouldUseRelativeAssetPaths ? { publicPath: '../../' } : undefined ), }, { loader: require.resolve('css-loader'), options: cssOptions, }, { loader: require.resolve('postcss-loader'), options: { ident: 'postcss', plugins: () => [ require('postcss-flexbugs-fixes'), require('postcss-preset-env')({ autoprefixer: { flexbox: 'no-2009', }, stage: 3, }), ], sourceMap: isEnvProduction && shouldUseSourceMap, }, }, ].filter(Boolean); if (preProcessor) { loaders.push({ loader: require.resolve(preProcessor), options: { sourceMap: isEnvProduction && shouldUseSourceMap, }, }); } return loaders; };
style-loader는 스타일을 불러와 웹 페이지에서 활성화하는 역할이다.
css-loader는 css 파일에서 import와 url(...) 문을 webpack의 require 기능으로 처리하는 역할을 한다.
postcss-loader는 모든 웹 브라우저에서 입력한 CSS 구문이 제대로 작동할 수 있도록 자동으로 -webkit, -mos, -ms 등 접두사를 붙여준다.
css-loader는 css 파일에서 import와 url(...) 문을 webpack의 require 기능으로 처리하는 역할을 한다.
postcss-loader는 모든 웹 브라우저에서 입력한 CSS 구문이 제대로 작동할 수 있도록 자동으로 -webkit, -mos, -ms 등 접두사를 붙여준다.
// Adds support for CSS Modules (https://github.com/css-modules/css-modules) // using the extension .module.css { test: cssModuleRegex, use: getStyleLoaders({ importLoaders: 1, sourceMap: isEnvProduction && shouldUseSourceMap, modules: true, getLocalIdent: getCSSModuleLocalIdent, }), },
config/webpack.config.js 파일의 내용에서 .module.css 파일을 사용하면 된다라고 작성되어있다.
/* scr/Test.module.css 파일을 신규 생성한 후 아래 코드 작성 */ .box { display: inline-block; width: 100px; height: 100px; border: 1px solid silver; position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); }
/* src/App.js 코드를 수정한다. */ import React, { Component } from 'react'; import styles from './Test.module.css' class App extends Component { render() { return ( <div className={styles.box}></div> ); } } export default App;
작성 후 프로젝트를 실행한다.
$ yarn start
개발자도구의 Elements 탭을 열어 코드를 살펴보면 클래스네임이 고유하게 설정되어 CSS 클래스가 중복되어 충돌이 일어나지 않는다.
<div class="Test_box__bxmdE"></div>
※ 클래스가 여러개일 경우
<div className={[styles.box, styles.blue].join(' ')}></div>
yarn 에서 classnames 라이브러리 사용
$ yarn add classnames
import React, { Component } from 'react'; import classnames from 'classnames'; import styles from './Test.module.css' class App extends Component { render() { return ( <div className={classnames(styles.box, styles.blue)}></div> ); } } export default App;
classNames의 사용방법은 다음과 같다.
classNames('foo','bar'); // => 'foo bar' classNames('foo',{ bar : true }); // => 'foo bar' 여러 형식을 받아 올 수 있다. classNames({ foo-bar : true }); // => 'foo-bar' classNames({ foo-bar : false }); // => '' false, null, 0, undefined는 무시된다. classNames({ foo : true },{ bar : true }); // => 'foo bar' classNames(['foo', 'bar']); // => 'foo bar' const isBar = true; classNames('foo',{ bar : isBar }); // => 'foo bar'
CSS Module은 고유한 클래스네임을 만들어 스코프를 제한한다. classnames 라이브러리를 사용하면 더욱 편하게 지정할 수 있다.
01.11.2. Sass
Sass(Syntactically awesome style sheets, 문법적으로 매우 멋진 스타일시트)는 CSS에서 사용할 수 있는 문법을 확장하여 중복 코드를 줄여 가독성이 좋은 코드를 작성할 수 있다.
$ yarn add node-sass sass-loader
const getStyleLoaders = (cssOptions, preProcessor) => { const loaders = [ isEnvDevelopment && require.resolve('style-loader'), isEnvProduction && { loader: MiniCssExtractPlugin.loader, options: Object.assign( {}, shouldUseRelativeAssetPaths ? { publicPath: '../../' } : undefined ), }, { loader: require.resolve('css-loader'), options: cssOptions, }, { loader: require.resolve('postcss-loader'), options: { ident: 'postcss', plugins: () => [ require('postcss-flexbugs-fixes'), require('postcss-preset-env')({ autoprefixer: { flexbox: 'no-2009', }, stage: 3, }), ], sourceMap: isEnvProduction && shouldUseSourceMap, }, }, { // sass-loader 추가 loader: require.resolve('sass-loader'), options: { /* 추후 입력 */ }, }, ].filter(Boolean); if (preProcessor) { loaders.push({ loader: require.resolve(preProcessor), options: { sourceMap: isEnvProduction && shouldUseSourceMap, }, }); } return loaders; };
마우스를 올릴때나 클릭할 때 다른 스타일을 적용하려면 다음과 같이 CSS 코드를 작성해야하지만,
.box:hover { background: red; } .box.active { background: yellow; }
Sass 의 현재 선택자 참조 기능으로 작성할 수 있다. & 문자를 아래와 같이 사용한다.
.box { display: inline-block; width: 100px; height: 100px; border: 1px solid silver; position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); &:hover { background: red; } &:active { background: yellow; } }
// 상단에서 작성한 src/Test.css 파일의 확장자를 // src/Test.scss 로 변경하고 App.js 의 import 도 수정한다. import styles from './Test.scss'
import React, { Component } from 'react'; import classnames from 'classnames'; import styles from './Test.scss' class App extends Component { render() { const isBlue = true; return ( <div className={classnames('box', { blue: isBlue})}> { /* div 요소 추가 */ } <div className={classnames('box-inside')}></div> </div> ); } } export default App;
상단의 예시처럼 div 속에 있는 또 다른 div에 CSS 적용하는 방법은 아래와 같다.
/* 기존 CSS 문법 */ .box .box-inside { ... } /* Sass 문법 */ .box { ... .box-inside { ... } }
01.11.2.1. 변수사용
Sass에서 자주 사용하는 값을 변수에 담을 수 있다.
$size: 100px; .box { display: inline-block; /* 상단에서 설정한 $size를 사용 */ width: $size; height: $size; (...) }
01.11.2.2. 믹스인 사용
자주 사용하는 값은 변수에 넣고, 자주 사용하는 구문은 믹스인으로 다시 재사용할 수 있다.
$size: 100px; /* name은 사용자가 입력하는 값 */ @mixin name { top: 50%; left: 50%; transform: translate(-50%, -50%) } .box { display: inline-block; width: $size; height: $size; border: 1px solid silver; position: fixed; // top: 50%; // left: 50%; // transform: translate(-50%, -50%); /* mixin으로 생성하고 include로 불러온다. */ @include name(); (...) }
01.11.2.3. 변수와 믹스인 전역적 사용
스타일만 관리하는 디렉터리를 만들어 전역적으로 쓰는 코드를 따로 분리하고, 컴포넌트 스타일 파일에 불러와 사용할 수 있다.
/* src/styles/utils.scss 파일 내용 */ $size: 100px; /* name은 사용자가 입력하는 값 */ @mixin name { top: 50%; left: 50%; transform: translate(-50%, -50%) }
@import './styles/utils'; .box { display: inline-block; width: $size; height: $size; border: 1px solid silver; position: fixed; @include name(); (...) }
하지만 파일의 디렉터리가 깊어지면 불러올 코드가 길어지고 불편하기 때문에 sass-loader를 설정할 때 includePaths를 설정하여 경로를 간소화할 수 있다.
/* config/paths.js */ module.exports = { (...) /* 스타일 디렉터리 적용 */ styles: resolveApp('src/styles'), };
/* config/webpack.config.js 파일 */ { loader: require.resolve('sass-loader'), options: { includePaths: [paths.styles] }, },
/* Test.scss 의 확장자를 Test.css 로 변경 */ @import 'utils';
앞선 방법보다 훨씬 간단하게 전역코드를 불러올 수 있다.
01.11.3. styled-components
자바스크립트 파일 안에 스타일을 선언하는 방식으로 'CSS in JS' 라고 한다. 관련 라이브러리가 많지만 사용자 수가 많은 styled-components를 설치하여 사용한다.
/* src/components/StyledButton.js 파일 생성 후 아래 코드 작성 */ import React from 'react'; import styled from 'styled-components'; const Wrapper = styled.div` border: 1px solid sliver; display: inline-block; padding: 1rem; border-radius: 3px; &:hover { background: black; color: white; } `; const StyledButton = ({children, ...rest}) => { return ( <Wrapper {...rest}> {children} </Wrapper> ); }; export default StyledButton;
/* App.js 코드 수정 */ import React, { Component } from 'react'; import StyledButton from './components/StyledButton'; class App extends Component { render() { return ( <div> <StyledButton>버튼</StyledButton> </div> ); } } export default App;
styled-components의 최대 장점은 자바스크립트 내부에서 스타일을 정의하기 때문에 자바스크립트와 스타일 사이의 벽이 허물어져 동적 스타일링이 더욱 편해진다는 것이다.
※ StyledButton.js 파일에서 익숙하지 않은 문법을 확인할 수 있다.
` ` - ES6의 Tagged Template Literals 문법으로 backquote(₩) 사이에 ${자바스크립트 표현}이 들어가면 끊어서 함수 인자로 전달한다. 이 문법을 사용한 이유는 CSS 적용을 할 때 props에 접근하기 위해서이다. Tagged Template Literals 문법을 사용하지 않으면 함수가 문자열 자체로 들어가기 때문에 Tagged Template Literals 문
... - ES6 전개 연산자(spread operator)는 표현식(expression)은 2개 이상의 인수arguments(함수 호출 용)나 2개 이상의 요소elements(배열 리터럴 용) 또는 2개 이상의 변수(비구조화 할당 용)가 예상되는 곳에 확장될 수 있도록 한다.
※ StyledButton.js 파일에서 익숙하지 않은 문법을 확인할 수 있다.
` ` - ES6의 Tagged Template Literals 문법으로 backquote(₩) 사이에 ${자바스크립트 표현}이 들어가면 끊어서 함수 인자로 전달한다. 이 문법을 사용한 이유는 CSS 적용을 할 때 props에 접근하기 위해서이다. Tagged Template Literals 문법을 사용하지 않으면 함수가 문자열 자체로 들어가기 때문에 Tagged Template Literals 문
... - ES6 전개 연산자(spread operator)는 표현식(expression)은 2개 이상의 인수arguments(함수 호출 용)나 2개 이상의 요소elements(배열 리터럴 용) 또는 2개 이상의 변수(비구조화 할당 용)가 예상되는 곳에 확장될 수 있도록 한다.
댓글
댓글 쓰기