[스터디]리액트를 다루는 기술-컴포넌트 스타일링
스타일링 방식
- 일반 CSS : 컴포넌트를 스타일링 하는 기본적인 방식
- Sass : CSS 전처리기 중 하나로 확장된 CSS문법을 사용
- CSS Module : CSS 클래스의 고유한 이름을 자동으로 생성해주는 옵션
- styled-components : 스타일을 자바스크립트에 내장시키는 방식
일반 CSS
- 이름 짓는 규칙 : “컴포넌트 이름”-“클래스” 형태로 생성
// App.js import React from 'react'; import logo from './logo.svg'; import './App.css'; function App() { return ( <div className="App"> <header> <img src={logo} className="logo" alt="logo" /> <p> Edit <code>src/App.js</code> and save to reload. </p> <a href="https://reactjs.org" target="_blank" rel="noopener noreferrer" > Learn React </a> </header> </div> ); } export default App; // App.css .App { text-align: center; } /* App 안에 들어있는 .logo */ .App .logo { height: 40vmin; pointer-events: none; animation: App-logo-spin infinite 20s linear; } /* App안에 있는 header에 스타일에 적용함 */ .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안에 있는 a 태그에 스타일에 적용함 */ .App a { color: #61dafb; } @keyframes App-logo-spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
Sass 사용하기
Sass(Synatically Awesome Style Sheets, 문법적으로 매우 놀라운 스타일시트) : CSS 전처리기로 복잡한 작업을 쉽게 해주고, 스타일 코드의 재활용성, 코드의 가독성을 높여줌
// 설치 $ yarn add node-sass
// SassComponent.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 { display: flex; .box { // 일반 CSS 에선 .SassComponent .box 와 마찬가지 background: red; 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 React, {Component} from 'react'; import './App.css'; import SassComponent from "./SassComponent"; class App extends Component { render() { return ( <div> <SassComponent/> </div> ) } } export default App;
자주 사용하는 믹스인(재사용가능한 스타일 블록)을 utils.scss로 따로 분리한 모습
// ./styles/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'; .SassComponent { display: flex; .box { // 일반 CSS 에선 .SassComponent .box 와 마찬가지 background: red; 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; } } }
sass-loader 설정 커스터마이징하기
프로젝트의 디렉토리 깊이가 깊어져, 필요한 파일을 @import 하기 쉽지 않을 때, sass-loader의 설정을 커스터마징하여 해결 할 수 있다. 다만, create-react-app으로 만들어진 프로젝트는 기본적으로 세부설정이 숨겨져 있어 이를 꺼내기 위한 작업이 아래와 같이 필요한다. 단, 사전에 commit 모두 되어 있어야 한다.
$ yarn eject yarn run v1.21.1 $ react-scripts eject NOTE: Create React App 2+ supports TypeScript, Sass, CSS Modules and more without ejecting: https://reactjs.org/blog/2018/10/01/create-react-app-v2.html ? Are you sure you want to eject? This action is permanent. (y/N)
위 명령이 잘 실행되면 config 디렉토리가 생성되며, webpack.config.js 를 아래와 같이 수정한 뒤, 서버를 재시작한다. 만약 서버가 재시작 되지 않으면, node_modules 디렉토리를 삭제 후 $yarn install 하여 모듈을 재설치한다.
// Opt-in support for SASS (using .scss or .sass extensions). // By default we support SASS Modules with the // extensions .module.scss or .module.sass // src/styles 기준 절대경로를 사용하여 필요한 파일을 임포트한다. // 교재의 옛 버전에서 설정은 오류가 발생하니 아래와 같이 수정하도록하자. { test: sassRegex, exclude: sassModuleRegex, use: getStyleLoaders( { importLoaders: 3, sourceMap: isEnvProduction && shouldUseSourceMap, }).concat({ loader: require.resolve('sass-loader'), options: { // prependData:`@import 'utils';`, sassOptions: { includePaths: [paths.appSrc + '/styles'], sourceMap: isEnvProduction && shouldUseSourceMap, } } }),
상단 prependData:@import 'utils';
의 주석을 제거하게 되면, .scss 파일을 불러올 때마다, @import ‘utils’가 자동으로 수행된다. 모든 파일에 추가할 때 사용하면 편리한 기능이다.
node_modules에서 라이브러리 불러오기
우선 학습을 위해 2가지 라이브러리를 아래와 같이 설치한다.
$ yarn add open-color include-media
utils.scss 에 아래와 같이 라이브러리를 넣어 사용한다. import 할 때, node_module 경로는 ~ 으로 치환할 수 있다.
// utils.scss @import '~include-media/dist/include-media'; @import '~open-color/open-color'; // 변수 사용하기 $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 .SassComponent { display: flex; background: $oc-gray-2; @include media('<768px') { // 가로크기가 768px 이하이면 배경을 검정색으로 바꿔준다. background: $oc-gray-9; } .box { // 일반 CSS 에선 .SassComponent .box 와 마찬가지 background: $oc-gray-2; 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; } } }
CSS Module
CSS Module은 CSS를 불러와 사용할 때, 클래스 이름을 고유한 값(파일이름_클래스이름_해시값)으로 자동으로 만들어준다.
.module.css 확장자로 파일을 저장하면, 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 import React from "react"; import styles from './CSSModule.module.css'; const CSSModule = () => { return ( <div className={`${styles.wrapper} ${styles.inverted}`}> 안녕하세요, 저는 <span className="something">CSS Module!</span> </div> ); }; export default CSSModule;
// App.js import React, {Component} from 'react'; import './App.css'; import CSSModule from "./CSSModule"; class App extends Component { render() { return ( <div> <CSSModule/> </div> ) } } export default App;
classnames
CSS 클래스를 조건부로 설정할 때 유용한 라이브러리
$ yarn add classnames
import classNames from 'classnames'; classNames('one', 'two'); // = 'one two' classNames('one', { two : true}); // = 'one two' classNames('one', { two : false}); // = 'one' classNames('one', ['two', 'three']); // = 'one two three'
// CSSModule.js import React from "react"; import classNames from 'classnames/bind'; import styles from './CSSModule.module.css'; const cx = classNames.bind(styles); const CSSModule = () => { return ( <div className={cx('wrapper', 'inverted')}> 안녕하세요, 저는 <span className="something">CSS Module!</span> </div> ); }; export default CSSModule;
Sass와 CSSModule 함께 사용하기
// CSSModule.module.scss /* 자동으로 고유해질 것이므로 흔히 사용되는 단어를 클래스 이름으로 마음대로 사용가능*/ .wrapper { background: black; padding: 1rem; color: white; font-size: 2rem; &.inverted { // invert가 .wrapper과 같이 사용될 때만 적용 color: black; background: white; border: 1px solid black; } } /* 글로벌 CSS 를 작성하고 싶다면 */ :global { .something { font-weight: 800; color: aqua; } }
styled-components
자바스크립트 파일 안에 스타일을 선언하는 방식. ‘CSS-in-JS’라고 부른다. styled-components, emotion 등의 라이브러리가 많이 쓰인다. 여기서는 styled-components를 알아본다.
$ yarn add styled-components
// StyledComponents.js import React from 'react'; import styled, { css } from 'styled-components'; // 여러줄의 스타일 구문을 조건부로 설정해야 하는 경우 css를 불러와야함 const Box = styled.div` /* props 로 넣어준 값을 직접 전달해줄 수 있습니다. */ background: ${props => props.color || 'blue'}; padding: 1rem; display: flex; `; 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;
intellij에서 위 코드가 하이라이팅이 제대로 되지 않는 경우 플러그인 “Styled Components & Styled JSX”을 설치하면 제대로 나온다.
// App.js import React, {Component} from 'react'; import './App.css'; import StyledComponent from "./StyledComponents"; class App extends Component { render() { return ( <div> <StyledComponent/> </div> ) } } export default App;
Tagged 템플릿 리터럴
스타일을 작성할 때 `을 사용하여 만들었는 데, 이를 Tagged 템플릿 리터럴이라고 한다. 이 것은 일반 템플릿 리터럴과 달리 템플릿 안에 자바스크립트 객체나 함수를 온전히 추출할 수 있다는 점이다.
// 브라우저에서 테스트 해본다. `hello ${{foo: 'bar'}} ${() => 'world'}!` => "hello [object Object] () => 'world'!" function tagged(...args) { console.log(args); } tagged`hello ${{foo: 'bar'}} ${() => 'world'}!` => (3) [Array(3), {…}, ƒ] 0: (3) ["hello ", " ", "!", raw: Array(3)] 1: {foo: "bar"} 2: () => 'world'
스타일링된 엘리먼트 만들기
import styled from 'styled-components'; // 폰트 크기가 2rem인 div가 생성된다. const Mycomponent = stled.div` font-size: 2rem; `; // 태그 타입을 styled 함수 인자로 전달 const MyInput = styled('input')` background : gray; ` // 컴포넌트 자체를 전달 const StyledLink = styled(Link)` color : blue; `
반응형 디자인
// 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;
최신 댓글