새로운 프로젝트를 만들자.
yarn create react-app styling-react
CSS
CSS 클래스가 중복되지 않게 만들기 위해서 1 ) 이름을 지을 때 특별한 규칙을 사용하여 짓거나 2 ) CSS Selector를 활용할 수 있다. 그 중 BEM Naming은 해당 클래스가 어디에서 어떤 용도로 사용되는지 명확하게 작성하는 방식이다. css Selector를 사용하면 CSS 클래스가 특정 클래스 내부에 있는 경우에만 스타일을 적용할 수 있다. 예를들어 .App .logo{ }는 App의 logo에만 스타일이 적용된다.
Sass(Syntactically Awesome Style Sheets)
CSS 전처리기로 복잡하 작업을 쉽게 할 수 있도록 해주고, 스타일 코드의 재활용성을 높여주고, 코드의 가독성을 높여서 유지 보수를 쉽게 해준다. 확장자는 .scss 와 .sass 두 가지가 있는데 문법이 꽤 다른편이다. 주요 차이점은 .sass는 중괄호와 세미콜론을 사용하지 않는다. .scss는 기존 CSS를 작성하는 방식과 비슷하다.
- sass 설치
yarn add sass
- SassComponent.scss
* transition: all 0.3s ease-in : 요소에 접근할 때 스르르~ 가는 느낌
* @import : 다른 스타일 시트에서 스타일 규칙을 가져올 때 사용된다. 조건부 사용을 제한된다.
* :hover: 사용자 커서가 요소 위에 올라가 있을 때 선택된다.
$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 {
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 './App.css';
import SassComponent from './SassComponent.js';
function App() {
return (
<div>
<SassComponent />
</div>
);
}
export default App;
Utils 함수 분리하기
자주 사용하는 Utils를 분리하기 위해 src 폴더 안에 styles/utils.scss 를 생성하고 기존의 SassComponent.scss 파일 안에 있던 부분을 잘라내자. 그리고 SassComponent.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 './styles/utils.scss';
.SassComponent { ... }
위와 결과는 동일하다.
sass-loader customizing
만약 프로젝트의 directory가 깊어져 style을 쉽게 사용하기 어렵다면 sass-loader 설정을 커스터마이징할 수 있다. 이를 위해서는 프로젝트 디렉터리에서 yarn eject 명령어를 통해 세부 설정을 밖으로 꺼내 주어야한다.
yarn eject
프로젝트 디렉터리에 config 디렉터리가 생성된다. 이 안에서 webpack.config.js 파일의 "saRegex"를 보면 두 번째 결과에서 use:에 있는 'sass-loader'부분을 지우고 concat을 통해 커스터마이징 된 sassloader 설정을 넣어주자. 서버를 다시 껐다 켜면 이제 styles 디렉터리 기준 절대 경로를 사용하여 불러올 수 있다.
- webpack.config.js - sassRegex 2번째 결과 부분 아래와 같이 수정
{
test: sassRegex,
exclude: sassModuleRegex,
use: getStyleLoaders(
{
importLoaders: 3,
sourceMap: isEnvProduction
? shouldUseSourceMap
: isEnvDevelopment,
modules: {
mode: 'icss',
},
},
).concat({
loader: require.resolve("sass-loader"),
options:{
sassOptions: {
includePaths: [paths.appSrc + "/styles"],
},
},
}),
// Don't consider CSS imports dead code even if the
// containing package claims to have no side effects.
// Remove this when webpack adds a warning or an error for this.
// See https://github.com/webpack/webpack/issues/6571
sideEffects: true,
},
- SassComponent.js에서 이제 @import 'utils.scss' 만으로 사용할 수 있다.
만약 모든 곳에서 사용한다면 일일이 입력하기 번거롭기 때문에 위의 파일에서 additionalData 를 입력하면 된다.
- webpack.config.js - sassRegex 2번째 결과 부분
.concat({
loader: require.resolve("sass-loader"),
options:{
sassOptions: {
includePaths: [paths.appSrc + "/styles"],
},
additionalData: "@import 'utils';",
},
그러면 파일에서 import를 입력하지 않아도 된다.
node_modules에서 라이브러리 불러오기
두가지 유용한 sass 라이브러리를 설치하고 사용해보자.
yarn add open-color include-media
- utils.scss
@import '~include-media/dist/include-media';
@import '~open-color/open-color';
- SassComponent.scss
.SassComponent {
display: flex;
background: $oc-gray-2;
@include media('<768px') {
background: $oc-gray-9;
}
.box{...}
}
CSS Module 만들기
CSS Module은 css를 불러와서 사용할 때 클래스 이름을 고유한 값, [파일이름]_[클래스이름]_[해시값] 형태로 자동으로 만들어서 컴포넌트 스타일 클래스 이름이 중첩되는 현상을 방지해주는 기술이다.
- CSSModule.module.css
/* 자동으로 고유해질 것이므로 흔히 사용되는 단어를 클래스 이름으로 마음대로 사용가능*/
.wrapper {
background: black;
padding: 1rem;
color: white;
font-size: 2rem;
}
/* 글로벌 CSS 를 작성하고 싶다면 */
:global .something {
font-weight: 800;
color: aqua;
}
- 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;
- App.js
import './App.css';
import CSSModule from './CSSModule';
const App = () => {
return (
<div>
<CSSModule />
</div>
);
}
export default App;
만약 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
<div className={`${styles.wrapper} ${styles.inverted}`}>
안녕하세요, 저는 <span className="something">CSS Module!</span>
</div>
classnames
다음 라이브러리로 class를 편리하게 적용할 수 있다.
yarn add classnames
- CSSModule.js
import React from 'react';
import styles from './CssModule.module.css';
import classNames from 'classnames/bind'
const cx = classNames.bind(styles); // 미리 style에서 class를 받아옴
const CSSModule = () => {
return (
<div className={cx('wrapper', 'inverted')}>
안녕하세요, 저는 <span className="something">CSS Module!</span>
</div>
);
};
export default CSSModule;
Styled-components
컴포넌트 스타일링 중 자바스크립트 파일 안에 스타일을 선언하는 방식(CSS-in-JS)이 있다.
다음을 설치해보자.
yarn add styled-components
- 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;
- App.js
import './App.css';
import StyledComponent from './StyledComponent';
const App = () => {
return (
<div>
<StyledComponent />
</div>
);
}
export default App;
개인적으로는 코드를 따로 분리하는게 더 편리한 것 같다는 느낌이 든다..
'Programming > React.js' 카테고리의 다른 글
10. immer사용하여 불변성 유지하기 (0) | 2022.08.31 |
---|---|
9. 컴포넌트 성능 최적화 (0) | 2022.08.24 |
7. Hooks (0) | 2022.03.06 |
6. component의 lifecycle method (0) | 2022.03.06 |
5. 반복적으로 Component 사용하기 (0) | 2022.03.05 |