컴포넌트를 스타일링할 때는 다양한 방식을 사용할 수 있지만, 이번에 다룰 스타일링 방식은 다음과 같다.
css를 작성할 때 가장 중요한 점은 CSS 클래스를 중복되지 않게 만드는 것이다. 이를 위해 2가지 방식이 있는데, 그중 하나는 이름을 지을 때 특별한 규칙을 사용하여 짓는 것이고, 또 다른 하나는 CSS Selector를 활용하는 것이다.
클래스 이름이 컴포넌트 이름-클래스 형태로 지어져 있다(예: App-header).
클래스 이름에 컴포넌트 이름을 포함시킴으로써 다른 컴포넌트에서 실수로 중복되는 클래스를 만들어 사용하는 것을 방지할 수 있다.
CSS Selector를 사용하면 CSS 클래스가 특정 클래스 내부에 있는 경우에만 스타일 적용이 가능하다. 예를 들어 .App 안에 들어 있는 .logo에 스타일을 적용하고 싶다면 다음과 같이 작성한다.
.App .logo {
animation: App-logo-spin infinite 20s linear;
height: 40vmin;
}
SaSS(SyntaclicalIy Awesome Style Shccn)(문법적으로 매우 멋진 스타일시트)는 CSS 전처리기로 복잡한 작업을 쉽게 할 수 있도록 해 주고, 스타일 코드의 재활용성을 높여 줄 뿐만 아니라 코드의
가독성을 높여서 유지 보수를 더욱 쉽게 해 준다.
$font-stack: Helvetica, sans-serif
$primary-color : #333;
body
font: 100% $font-stack
color: $primary-color
$font-stack: Helvetica, sans-serif;
$primary-color : #333;
body{
font: 100% $font-stack;
color: $primary-color;
}
여러 파일에서 사용될 수 있는 Sass 변수 및 믹스인은 다른 파일로 따로 분리하여 작성한 뒤 필요
한 곳에서 쉽게 불러와 사용할 수 있다. 다른 SCSS 파일을 불러올 때는 @import 구문을 사용하면 된다.
이 작업은 Sass를 사용할 때 반드시 해야 하는 것은 아니지만, 해 두면 유용한 작업이다.
{
test: sassRegex,
exclude : sassModuleRegex,
use: getStyleLoaders(
{
importLoaders:2,
sourceMap : isEnvProduction && shouldUseSourceMap,
**}).concat({
loader:require.resolve('sass-loader'),
options:{
sassOptions:{
includePaths:[paths.appSrc + '/styles']
},
sourceMap: isEnvProduction && shouldUseSourceMap,
}
}),**
sideEffects:true,
},
import 구문을 @import 'utils.scss'; 로 수정하여 앞으로 utils.scss를 사용하는 컴포넌트를 편하게 사용할 수 있다.
하지만 새 파일을 생성할 때마다 utils.scss를 매번 이렇게 포함시키는 것도 귀찮을 수 있을 것이다. 그렇다면 sassToader의 additionalData 옵션을 설정하면 된다.
{
test: sassRegex,
exclude : sassModuleRegex,
use: getStyleLoaders(
{
importLoaders:2,
sourceMap : isEnvProduction && shouldUseSourceMap,
}).concat({
loader:require.resolve('sass-loader'),
options:{
sassOptions:{
includePaths:[paths.appSrc + '/styles']
},
sourceMap: isEnvProduction && shouldUseSourceMap,
> prependData : `@import 'utils';` <
}
}),
sideEffects:true,
},
이후 import구문을 지워도 utils.scss를 자동으로 불러와 정상적으로 작동하는 것을 볼 수 있다.
Sass의 장점 중 하나는 라이브러리를 쉽게 불러와서 사용할 수 있다는 점이다. yarn을 통해 설치한 라이브러리를 사용하는 가장 기본적인 방법은 @import'../../../node_modules/library/styles'; 를 이용하여 불러오는 방법이다. ../를 반복적으로 작성하는 것이 번거로우니, 위 문장을 간단하게 바꿔 보겠다.
@import '~library/styles'; 이와 같이 물결 문자, ~를 사용하여 불러오는 방법이다.
물결 문자를 사용하면 자동으로 node_modules에서 라이브러리 디렉터리를 탐지하여 스타일을 불러올 수 있다.
CSS Module은 CSS를 불러와서 사용할 때 클래스 이름을 고유한 값인, [파일 이름]_[클래스 이름]__[해시값] 형태로 자동으로 만들어, 클래스 이름이 중복되는 현상을 방지해 주는 기술이다.
이때, 만들어진 클래스는 우리가 만든 스타일을 직접 불러온 컴포넌트 내부에서만 작동한다.
특정 클래스가 웹 페이지에서 전역적으로 사용되는 경우라면 :global을 앞에 입력하여 글로벌 CSS임을 명시해 줄 수 있다.
import React from 'react';
import styles from './CSSModule.module.css';
const CSSModule = () => {
return(
<div className={styles.wrapper}>
안녕하세요, 저는 <span className="something">CSS Module!</span>
</div>
);
};
export default CSSModule;
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'
const myClass = 'hello';
classNames('one', myClass, {myCondition: true}); //'one hello myCondition'
위와 같이 여러 종류의 파라미터를 조합하여 CSS 클래스를 설정할 수 있어, 컴포넌트에서 조건부로 클래스를 설정할 때 매우 편리하다.
Sass를 사용할 때도 파일 이름 뒤에 .module.scss 확장자를 사용해 주면 CSS Modules로 사용할 수 있다.
CSS Module에서 글로벌 클래스를 정의할 때 :global을 사용한 것처럼, .css/.scss 파일에서도 :local을 사용하여 CSS Module을 사용할 수 있다.
CSS-in-JS : 자바스크립트 파일 안에 스타일을 선언하는 방식으로, 이와 관련된 라이브러리의 종류는 https://github.com/MicheleBertoli/css-in-js 에서 확인할 수 있다. CSS-in-JS 라이브러리 중에서 개발자들이 가장 선호하는 styled-components를 알아보겠다.
import React from 'react';
import styled, {css} from 'styled-components';
const Box = styled.div`
/*props로 넣어 준 값을 직접 전달해 줄 수 있습니다*/
background: ${props => props.color || 'blue'};
padding:1rem;
display:flex;
`;
const Button = styled.button`
bakcground: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;
스타일을 작성할 때 `을 사용하여 만든 문자열에, 스타일 정보를 넣어 주는 방식이다. 일반 템플릿과 다른 점은 템플릿 안에 자바스크립트 객체나 함수를 전달할 때, 온전히 추출할 수 있다는 점이다.
'hello ${{foo: 'bar' }) ${() => 'world'}!'
// 결과: "hello [object Object] () => 'world'!"
템플릿에 객체나 함수를 넣으면 객체는 "[object Object]"로, 함수는 함수 내용이 문자열화로 원래의 형태를 잃어버리게 됩니다. 이럴때 Tagged 템플릿을 이용해 템플릿 안에 넣은 값을 온전히 추출할 수 있다.
function tagged(...args) {
console.log(args);
}
tagged`hello ${{foo:'bar'}} ${() => 'world'}!`
styled-components를 사용하여 스타일링된 엘리먼트를 만들 때는 컴포넌트 상단에서 styled를 불러오고, styled.태그명을 사용하여 구현한다.
import styled from 'styled-components';
const MyComponent = styled.div`
font-size: 2rem;
`;
하지만 사용해야할 태그명이 유동적이거나 특정 컴포넌트 자체에 스타일링해 주고 싶다면 다음과 같은 형태로 구현해 준다.
//태그의 타입을 styled 함수의 인자로 전달
const MyInput = styled('input')`
background: gray;
`
//아예 컴포넌트 형식의 값을 넣어 줌
const StyledLink = styled(Link)`
color:blue;
styled-components를 사용하면 스타일쪽에서 컴포넌트에게 전달된 props 값을 참조할 수 있다.
const Box = styled.div`
background:${props => props.color || 'blue'};
padding:1rem;
display:flex;
`;
위와 같이 만들어진 코드는 JSX에서 사용할 때 color 값을 props로 넣어 줄 수 있다.
<Box color="black"> (...) </Box>)
일반 CSS 클래스를 사용하여 조건부 스타일링을 할 때는 className을 사용했지만, styled-components에서는 props로도 간단하게 처리할 수 있다.
/* StyledComponents.js - Button 컴포넌트 */
import styled, { css } from 'styled-components';
/* 단순 변수 형태가 아니라 여러 줄의 스타일 구문을 조건부로 설정해야 하는 경우 css를 불러와야 합니다. */
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;
}
`;
브라우저의 가로 크기에 따라 다른 스타일을 적용하기 위해서는 일반 CSS처럼 media 쿼리를 사용하면 된다.
const Box = styled.div`
/* props로 넣어 준 값을 직접 전달해 줄 수 있습니다. */
background: ${props => props.color || 'blue'};
padding: 1rem;
display:flex;
/* 기본적으로는 가로 1024px에 가운데 정렬, 가로 크기가 작아지면 크기를 줄이고 768px 미만이 되면 꽉 채웁니다. */
width:1024px;
margin:0 auto;
@media (max-width:1024px) {
width:768px;
}
@media (max-width:768px) {
width:100%;
}
`;
일반 CSS에서 할 때와는 큰 차이가 없지만, 여러 컴포넌트에서 반복해야 한다면 귀찮을 수 있다. 그럴 땐 이 작업을 함수화하여 간편하게 사용할 수 있다.
import React from 'react';
import styled, {css} from 'styled-components';
const sizes = {
desktop:1024,
tablet:768
};
// 상단의 size 객체에 따라 자동으로 media 쿼리 함수를 만들어 줍니다.
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`
background: ${props => props.color || 'blue'};
padding: 1rem;
display:flex;
width:1024px;
margin:0 auto;
${media.desktop`width: 768px;`}
${media.tablet`width:100%;`};
Quiz
1. CSS를 작성할 때 가장 중요한 점은 ()을 중복되지 않게 만드는 것이다.
2. Sass는()과 () 2가지 확장자를 지원한다.
3. ()이란 함수처럼 재사용할 수 있는 스타일 코드 블록을 말한다.
4. scss 파일에서 다른 scss 파일을 불러올 때는 () 구문을 사용한다.
5. 파일을 임포트할 때 () 를 사용하면 자동으로 node_modules에서 라이브러리를 탐지해 준다.
6. Tagged 템플릿은 자바스크립트 ()나 ()를 전달할 때 온전히 추출할 수 있다.
7. CSS Module이 아닌 일반 .css/.scss 파일에서도 ()을 사용하여 CSS Module을 사용할 수 있다.
const Box = styled.div`
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%;
}
`;
$font-stack: Helvetica, sans-serif
$primary-color : #333;
body
font: 100% $font-stack
color: $primary-color
정답.
1. 클래스 이름
2. .sass .scss
3. 믹스인
4. import
5. 물결문자
6. 객체 함수
7. :local
8.
import React from 'react';
import styled, {css} from 'styled-components';
const sizes = {
desktop:1024,
tablet:768
};
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`
background: ${props => props.color || 'blue'};
padding: 1rem;
display:flex;
width:1024px;
margin:0 auto;
${media.desktop`width: 768px;`}
${media.tablet`width:100%;`};
$font-stack: Helvetica, sans-serif;
$primary-color : #333;
body{
font: 100% $font-stack;
color: $primary-color;
}
[리액트스타터2] 11장. 컴포넌트 성능 최적화 (0) | 2022.12.29 |
---|---|
[리액트스타터2] 10장. 일정 관리 웹 애플리케이션 만들기 (0) | 2022.12.22 |
[리액트스타터2] 8장. Hooks (0) | 2022.11.24 |
[리액트스타터2] 7장. 컴포넌트의 라이프사이클 메서드 (0) | 2022.11.17 |
[리액트스타터2] 6장. 컴포넌트 반복 (0) | 2022.11.10 |