1. 가장 흔한 방식, 일반 CSS
src 폴더에 위치한 App.js, App.css 파일을 통해 스타일을 적용할 수 있다.
CSS 클래스의 이름은 중복되서는 안된다. 그럼으로 컴포넌트이름-클래스형태로 (App-header) 작성하는 것을 권장한다.
CSS Selector를 통해 CSS 클래스가 특정 클래스 내부에서만 적용되게 할 수 있다.
App.js
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
class App extends Component {
render() {
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 {
animation: App-logo-spin infinite 20s linear;
height: 40vmin;
}
/* .App 안에 들어있는 header
header 클래스가 아닌 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);
}
}
.App .logo { ~} 와 같이 CSS 클래스에 작성한다면 App.js 의 ClassName이 logo인 컴포넌트에만 해당 스타일이 적용된다.
즉, 앞단에 클래스 이름 .App를 넣고, .logo 또는 header 와 같은 태그를 작성하여 지정할 수 있다.
2. Sass 사용하기
Sass는 CSS 전처리기로 복작한 작업을 쉽게 해주며, 스타일 코드의 재활용성과 가독성을 높여준다.
Sass의 확장자는 .scss와 .sass가 있으나 .scss 확장자는 기존 CSS 작성방식과 비슷함으로 더 자주 사용된다.
node-sass 라이브러리(Sass를 CSS로 변환해주는 라이브러리)를 설치한다.
> $yarn add node-sass
src 디렉토리에
SassComponent.scss
//변수사용하기
$red: #fa5252;
$orange: #fd7e14;
$yellow: #fcc419;
$green: #40c05;
$blue: #339af0;
$indigo: #5c7cfa;
$violet: #7950f2;
//믹스인 만들기(재사용되는 스타일 블록을 함수처럼 사용할 수 있음)
@mixin square($size) {
$calculated: 32px * $size;
width: $calculated;
height: $calculated;
}
.SassComponent {
display: flex;
.box {
background: red; // 일반 CSS 에선 .SassComponent .box 와 마찬가지
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 SassComponent from './SassComponent';
class App extends Component {
render() {
return (
<div>
<SassComponent></SassComponent>
</div>
);
}
}
export default App;
2-1) utils 함수 분리하기
Sass 변수와 믹스인은 다른 파일로 분리시킬 수 있다.
src 디렉토리에 styles라는 디렉터리를 생성하고 그안에 utils.scss 파일을 만들어 분리시켜보자.
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를 사용해서 다른 scss를 불러올 수 있다.
> @import './styles/utils';
2-2) sass-loader 설정 커스터마이징하기
먼저, yarn eject 명령어를 통해 세부설정을 밖으로 꺼내야 된다. 그 후 생성된 config 디렉터리 내 존재하는
webpack.config.js 를 열어본다.
해당 파일에서 sassRegex를 찾아 use: 에 있는 sass-loader 부분을 지우고 concat을 통해 커스터마이징 된
sass-loader 설정을 넣어주면 된다.
<변경되는 부분>
.concat({
loader: require.resolve('sass-loader'),
options: {
includePaths: [paths.appSrc + '/styles'],
sourceMap: isEnvProduction && shouldUseSourceMap,
data: `@import 'utils';`
}
})
이를 통해 scss 파일의 경로를 매번 다 작성할 필요없이 styles 디렉토리 기준으로 절대 경로를 사용할 수 있다.
> @import './styles/utils'; => @import 'utils.scss'; 로 변경해서 작성 가능
또한, sass-loader의 옵션에 data 필드에 매번 추가되는 코드를 작성함으로써 sass 파일을 불러올때마다
해당 코드가 자동으로 맨 윗부분에 포함시킬 수 있다.
위와 같은 설정들은 sass 사용시 반드시는 아니지만, 해 두면 유용하다.
2-3) node_modules에서 라이브러리 불러오기
물결 문자(~)을 사용하는 방법으로 자동으로 node_modules에서 라이브러리 디렉토리를 탐지해 스타일을
불러올 수 있다.
3. CSS Module
CSS Module은 CSS를 불러와서 사용할 때 클래스 이름을 고유한 값([파일이름]_[클래스이름]_[해시값])을 자동으로
만들어 중첩되는 현상을 방지하는 기술이다.
v2 버전이상부턴 별도의 설정없이 .module.css 확장자로 파일을 저장하기마 하면 CSS Module이 적용된다.
src 디렉토리에
CSSModule.module.css
/* 자동으로 고유해질 것이므로 흔히 사용되는 단어를 클래스 이름으로 마음대로 사용가능*/
.wrapper {
background: black;
padding: 1rem;
color: white;
font-size: 2rem;
}
/* 글로벌 CSS 를 작성하고 싶다면 */
:global {
// :global {} 로 감싸기
.something {
font-weight: 800;
color: aqua;
}
// 여기에 다른 클래스를 만들 수도 있겠죠?
}
src 디렉토리에 CSS Module이 사용될 컴포넌트를 작성해보자
CSSModule.js
import React from 'react';
import styles from './CSSModule.module.scss';
const CSSModule = () => {
return (
<div className={styles.wrapper}>
안녕하세요, 저는 <span className="something">CSS Module!</span>
</div>
);
};
export default CSSModule;
일반 클래스 이름을 사용할 경우엔 className = {styles.[클래스 이름]}으로 작성하고,
전역으로 선언한 클래스를 사용할 경우엔 calssName = "[클래스 이름]"으로 작성하면 된다.
App.js
import React, { Component } from 'react';
import CSSModule from './CSSModule';
class App extends Component {
render() {
return (
<div>
<CSSModule/>
</div>
);
}
}
export default App;
CSS Module 를 두개 이상 적용하고 싶을때
먼저, CSSModule.module.css 에 아래 코드를 추가한다.
.inverted {
// inverted 가 .wrapper 와 함께 사용 됐을 때만 적용
color: black;
background: white;
border: 1px solid black;
}
CSSModule.js 를 아래와 같이 작성한다면 두개의 CSS Module을 적용할 수 있다.
import React from 'react';
import styles from './CSSModule.module.scss';
const CSSModule = () => {
return (
<div className={[styles.wrapper,styles.inverted].join(' ')}>
안녕하세요, 저는 <span className="something">CSS Module!</span>
</div>
);
};
export default CSSModule;
3-1) classnames
classnames은 css클래스를 조건부로 설정할 때 유용한 라이브러리이다.
먼저, classnames 라이브러리를 설치하자.
> yarn add classnames
classnames 사용법
1) classNames('one', 'two');
=> "one two"
2) classNames('one', { two: true});
=> "one two"
3) classNames('one', { two: false});
=> "one"
4) classNames('one', ['two', 'three'])
=> "one two three"
5) const myClass = 'hello'; classNames('one', myclass, { myCondition: true});
=> "one hello myCondition"
/* classNames 라이브러리는 사용한 경우 */
const MyComponent = ({ highlighted, theme}) => (
<div className={classNames('MyComponent', {highlighted}, theme)}>
Hello
</div>
)
/* classNames 라이브러리는 사용 안하는 경우 */
const MyComponent = ({ highlighted, theme}) => (
<div className={'MyComponent ${theme} ${highlighted ? 'highligted': ''}'}>
Hello
</div>
)
classNames을 사용함으로써 가독성을 높일 수 있다.
또한, CSS Module 과 함께 사용한다면 styles.[클래스이름] 형태 대신에 cx('클래스 이름', '클래스이름 2') 형태로 사용할 수 있다.
import React from 'react';
import classNames from 'classnames/bind';
import styles from './CSSModule.module.scss';
const cx = classNames.bind(styles); // 미리 styles 에서 클래스를 받아오도록 설정하고
const CSSModule = () => {
return (
<div className={cx('wrapper', 'inverted')}>
안녕하세요, 저는 <span className="something">CSS Module!</span>
</div>
);
};
3-2) Sass와 함께 사용하기
Sass 파일 뒤에 .module.scss 확장자를 사용해 주면 CSS Module로 사용할 수 있다.
CSSModule.module.scss
/* 자동으로 고유해질 것이므로 흔히 사용되는 단어를 클래스 이름으로 마음대로 사용가능*/
.wrapper {
background: black;
padding: 1rem;
color: white;
font-size: 2rem;
&.inverted {
// inverted 가 .wrapper 와 함께 사용 됐을 때만 적용
color: black;
background: white;
border: 1px solid black;
}}
/* 글로벌 CSS 를 작성하고 싶다면 */
:global {
// :global {} 로 감싸기
.something {
font-weight: 800;
color: aqua;
}
// 여기에 다른 클래스를 만들 수도 있겠죠?
}
CSSModule.js 상단에서 css 대신에 scss로 변경해주면 된다.
4. Styled-components
자바스크립트 파일 안에 스타일을 선언하는 방식 (CSS-in-JS)
Styled-components 라이브러리를 설치해보자
> yarn add Styled-components
src 디렉토리 안에
StyleComponent.js
import React from 'react';
import styled, { css } from 'styled-components';
const Box = styled.div`
/* props 로 넣어준 값을 직접 전달해줄 수 있습니다. */
background: ${props => props.color || 'blue'};
padding: 1rem;
display: flex;
width: 1024px;
`;
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;
App.js
import React, { Component } from 'react';
import StyledComponent from './StyledComponent';
class App extends Component {
render() {
return (
<div>
<StyledComponent />
</div>
);
}
}
export default App;
props 값을 전달해주는 값을 쉽게 스타일에 적용할 수 있는 점이 장점이다.
4-1) Tagged 템플릿 리터럴
'을 사용해서 스타일 정보를 넣어주는 문법을 Tagged 템플릿 리터럴이라고 부른다.
Tagged 템플릿 리터럴을 사용해서 자바 스크립트 객체나 함수의 원본 값 그대로 추출할 수 있어 Styled-componenets에 props를 쉽게 적용할 수 있다.
크롬개발자 도구에서 아래의 코드를 비교하여 내용을 확인 할 수 있다.
1번
'hello ${{foo: 'bar'}} ${() => 'world'}!'
=> 결과 "hello [object object] () => 'world' !"
2번
function tagged(...args) {
console.log(args);
}
tagged'hello ${{foo: 'bar'}} ${() => 'world'}!'
4-2) 스타일링된 엘리먼트 만들기..... 설명 필요...
import React from 'react';
// 문자열로 styled 의 인자로 전달
const MyInput = styled('input')`
background: gray;
`;
// 아예 컴포넌트 형식의 값을 넣어줌
const StyledLink = styled(Link)`
color: blue;
`;
4-3) 스타일에서 props 조회
props를 조회하여 color 값이 없으면 기본값을 blue로 설정할 수 있다.
const Box = styled.div`
/* props 로 넣어준 값을 직접 전달해줄 수 있습니다. */
background: ${props => props.color || 'blue'};
padding: 1rem;
display: flex;
';
<Box color="black">(...)</Box>
4-4) props에 따른 조건부 스타일링
import styled, { css } from 'styled-components';
const Button = styled.button`
...
/* 다음 코드는 inverted 값이 true 일 때 특정 스타일을 부여해줍니다. */
${props =>
props.inverted &&
css`
background: none;
border: 2px solid white;
color: white;
&:hover {
background: white;
color: black;
}
`};
...
`;
조건부 스타일링을 할때 여러 줄의 코드에서 props를 참조한다면 반드시 css로 감싸서 Tagged 템플릿 리터럴을 사용해야 된다.
<Button inverted={true}>테두리만</Button>과 같이 작성함으로써 해당 css를 적용할 수 있다.
4-5) 반응형 디자인
const Box = styled.div`
/* props 로 넣어준 값을 직접 전달해줄 수 있습니다. */
background: ${props => props.color || 'blue'};
padding: 1rem;
display: flex;
width: 1024px;
margin: 0 auto;
@media (max-width: 1024px) {width: 768px;}
@media (max-width: 768px) {width: 100%;}
`;
일반 CSS와 동일하게 media 쿼리를 사용하여 브라우저의 가로 크기에 따라 다른 스타일을 적용할 수 있다.
동일한 작업이 여러 번 발생할 경우 아래와 같이 함수화하여 간편하게 사용할 수 있다.
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%;`};
`;
참고서적 : 리엑트를 다루는 기술 (김민준)
'IT > React' 카테고리의 다른 글
[Leaning-React] 10. 일정 관리 앱 애플리케이션 만들기 (1) | 2020.01.30 |
---|