React에 스타일 적용하기
전체 컴포넌트에 공통으로 적용하기 위한 css와 특정 컴포넌트에만 적용하기 위한 별도의 css로 작성할 필요가 있고 또 컴포넌트 내부의 값 (props)에 따라 스타일을 다르게 하기 위해서 css module 혹은 styled components가 필요하다.
1. SCSS를 사용한 다양한 사이즈 버튼 만들기
- Button 이름의 scss파일을 만든다.(large, mediun, small 사이즈의 스타일을 가진다.)
- 컴포넌트에서 Button.scss를 사용하도록 불러오고 버튼의 사이즈를 받을 수 있도록 props를 사용한다.
- App.jsx에서 3가지 사이즈를 넘겨준다.
/styles/Button.scss
$main-color: #3d3dff;
// 자주 사용되는 스타일은 그룹으로 선언
@mixin flex-center {
display: flex;
align-items: center;
justify-content: center;
}
.Button {
// 버튼의 기본 속성 제거
outline: none;
border: none;
// mixin 사용
@include flex-center;
background-color: $main-color;
border-radius: 10px;
color: white;
width: 100px;
margin: 10px;
// &연산자 사용
&.large {
width: 140px;
height: 70px;
font-size: 24px;
}
&.medium {
width: 120px;
height: 60px;
font-size: 20px;
}
&.small {
width: 100px;
height: 50px;
font-size: 16px;
}
}
/components/Button.jsx
import React from 'react';
import '../styles/Button.scss';
function Button({ size }) {
return (
<button className={`Button ${size}`}>
<p>button</p>
</button>
);
}
export default Button;
/App.jsx
import Button from './components/Button';
function App() {
return (
<div className='App'>
<Button size='large' />
<Button size='medium' />
<Button size='small' />
</div>
);
}
export default App;
프로젝트에 필요한 버튼을 하나씩 새로 만드는 것보다 컴포넌트 형태로 만들어 놓고 사용한다면 훨씬 효율적으로 개발이 가능하다.
props 활용하기
단순한 스타일뿐만 아니라 이벤트등 여러 가지의 다양한 props를 추가로 넘겨줄 수 도 있기 때문에 rest 연산자를 활용한다. (예제에서는 color와 핸들러 함수를 받을 수 있도록 추가하였다.)
/components/Button.jsx
import React from 'react';
import '../styles/Button.scss';
function Button({ size, color, ...rest }) {
return (
<button className={`Button ${size} ${color}`} {...rest}>
<p>button</p>
</button>
);
}
export default Button;
/App.jsx
import Button from './components/Button';
function App() {
return (
<div className='App'>
<Button size='large' color='red' onClick={() => alert('Hi')} />
<Button size='medium' color='blue'/>
<Button size='small'/>
</div>
);
}
export default App;
2. css module 사용
- css module의 장점은 클래스 명마다 고유값을 부여해서 클래스가 중복되는 것을 방지한다.
- 사용하려면 css-loader가 필요하나 vite로 프로젝트를 만들면 기본적으로 설정이 되어있다.
- css module의 경우 scss문법은 사용할 수 없고 기본 css문법을 사용해야 한다.
- 파일의 확장자 명으로 *.module.css를 사용한다.
- className에 작성할 때는 불러온 이름. 클래스이름의 형태로 지정한다.
/styles/Button.module.css
.Button {
outline: none;
border: none;
display: flex;
align-items: center;
justify-content: center;
background-color: #3d3dff;
border-radius: 10px;
color: white;
width: 100px;
margin: 10px;
}
.large {
width: 140px;
height: 70px;
font-size: 24px;
}
/components/Button.jsx
import React from 'react'
import style from '../styles/Button.module.css'
function Button() {
return (
<button className={`${style.Button} ${style.large}`}>
<p>버튼</p>
</button>
)
}
export default Button
실제 클래스 이름에 고유값이 붙은 것을 확인할 수 있다.
3. styled-components 사용
스타일이 적용된 컴포넌트를 만들 수 있게 해주는 모듈을 말한다. 스타일이 적용된 컴포넌트들을 조합해서 하나의 컴포넌트를 완성하게 되며 다음과 같은 기능을 지원하고 있다.
- 테마 설정
- 글로벌 스타일 생성
- props 활용
설치
yarn add styled-components
기본 문법
import styled from 'styled-components' // 사용위해 import
const (변수명) = styled.(태그명)` // 변수로 선언하되 백틱 안에 css내용이 들어간다.
`
구조는 style폴더가 아닌 컴포넌트 명으로 폴더를 만들고 해당 폴더 안에 index.jsx와 style.js 파일을 생성한다.
/components/Button/style.js
import styled from 'styled-components';
export const Button = styled.button`
outline: none;
border: none;
display: flex;
align-items: center;
justify-content: center;
background-color: #3d3dff;
border-radius: 10px;
color: white;
width: 100px;
margin: 10px;
&:hover {
opacity: 0.5;
}
`;
아래처럼 템플릿 리터럴 문법을 활용하면 props를 활용하여 색상이나 이벤트 적용이 가능하다.
/* props를 통해 배경색을 지정한다. */
background-color: ${(props) => props.color || 'blue'}
/* 클릭에 따라 표시되는 스타일을 변경한다. */
background-color: ${({ isClicked }) => (isClicked ? 'gray' : 'blue')};
/components/Button/index.jsx
import React from 'react';
import * as S from './style'; // style파일 내부 변수들을 S라는 이름으로 접근 및 사용
function Button() {
return (
<S.Button>
<p>버튼</p>
</S.Button>
);
}
export default Button;
color를 props로 넘겨주어 색상을 지정하거나 클릭 여부에 따라 배경색을 다르게 표시할 수 있다.
<S.Button color="#fff">
import React, { useState } from 'react';
import * as S from './style';
function Button() {
const [isClicked, setIsClicked] = useState(false);
return (
<S.Button isClicked={isClicked} onClick={() => setIsClicked(!isClicked)}>
<p>버튼</p>
</S.Button>
);
}
export default Button;
themeprovider 활용하기
themeprovider는 앱 전체에 공통된 스타일 값을 사용할 수 있도록 도와주는 역할을 담당한다.
/styles/theme.js
// 우리 앱의 테마 색 등을 선언
const palette = {
orange: '#FFA500',
black: '#000000',
};
// 자주 사용하는 스타일 그룹은 미리 선언
const common = {
flexCenter: `
display: flex;
justify-content: center;
align-items: center;
`,
};
// 개발자 간의 폰트 크기 통일을 위해 미리 선언
const fontSizes = {
title: '2rem',
subtitle: '1.5rem',
paragraph: '1rem',
};
const theme = {
palette,
common,
fontSizes,
};
export default theme;
/App.jsx
import { ThemeProvider } from 'styled-components';
import theme from './styles/theme';
import Button from './components/Button';
function App() {
return (
<div>
<ThemeProvider theme={theme}>
<Button />
</ThemeProvider>
</div>
);
}
export default App;
위 처럼 themeprovider로 감싸서, 앱 전체에 theme에서 정의한 값을 가져오도록 한다. themeprovider가 있기 때문에 Button에 직접 props를 넘겨주지 않아도 Button 컴포넌트에서 바로 theme를 가져와 사용할 수가 있다.
/components/Button/style.js
import styled from 'styled-components'
export const Button = styled.button`
outline: none;
border: none;
${({ theme }) => theme.common.flexCenter}
background-color: ${({ theme }) => theme.palette.orange};
border-radius: 10px;
color: white;
width: 100px;
margin: 10px;
&:hover {
opacity: 0.5;
}
`
keyframes 활용하기
애니메이션을 만들고자 한다면, keyframes 함수를 불러와서 사용하면 된다.
import styled, { keyframes } from 'styled-components' //keyframes 추가
const fadeIn = keyframes`
from {
opacity: 0
}
to {
opacity: 1
}
`
export const Button = styled.button`
outline: none;
border: none;
${({ theme }) => theme.common.flexCenter}
background-color: ${({ theme }) => theme.palette.orange};
border-radius: 10px;
color: white;
width: 100px;
margin: 10px;
&:hover {
opacity: 0.5;
}
animation-name: ${fadeIn};
animation-duration: 1s;
animation-timing-function: ease-out;
`
createGlobalStyle 활용하기
앱 전체에 적용하고자 하는 속성이 있다면 createGlobalStyle을 통해 컴포넌트를 만들고 전체에 적용한다.
보통 앱 전체에 폰트를 적용할 때 사용한다.
폰트 파일(.otf)을 다운받아 assets 폴더 안에 fonts 폴더에 넣어주고 styles 폴더 아래 globalStyle.js 파일을 생성한다.
https://fonts.google.com/noto/specimen/Noto+Sans+KR
/styles/globalStyle.js
import { createGlobalStyle } from 'styled-components'
import NotoSansBold from '../assets/fonts/NotoSansKR-Bold.otf'
export default createGlobalStyle`
@font-face {
font-family: "NotoSansBold";
src: url(${NotoSansBold}) format('opentype');
}
`
format: otf → opentype, ttf →truetype, woff → woff (파일의 확장자에 따라 다름)
/App.jsx
import { ThemeProvider } from 'styled-components'
import theme from './styles/theme'
import Button from './components/Button'
import GlobalStyle from './styles/globalStyle'
function App() {
return (
<ThemeProvider theme={theme}>
<GlobalStyle />
<Button />
</ThemeProvider>
)
}
export default App
Button 스타일에 font-family를 설정하면 적용이 된다.
font-family: 'NotoSansBold';
예제 구현해보기
- isClicked라는 state를 만든다.
- Button이라는 styled-components를 만들고 클릭에 따라 배경색이 변하도록 한다.
- 버튼을 클릭하면 isClicked의 값이 바뀌도록 onClick 함수를 지정한다.
/components/Button/index.jsx
import React, { useState } from 'react'
import * as S from './style'
function Button() {
const [isClicked, setIsClicked] = useState(false)
return (
<S.Button isClicked={isClicked} onClick={() => setIsClicked(!isClicked)}>
{isClicked ? <p>Good!</p> : <p>Nice!</p>}
</S.Button>
)
}
export default Button
/components/Button/style.js
import styled, { keyframes } from 'styled-components'
const fadeIn = keyframes`
from {
opacity: 0
}
to {
opacity: 1
}
`
export const Button = styled.button`
animation-name: ${fadeIn};
animation-duration: 1s;
animation-timing-function: ease-out;
outline: none;
border: none;
${({ theme }) => theme.common.flexCenter}
background-color: ${({ isClicked }) => (isClicked ? 'orange' : 'violet')};
border-radius: 10px;
color: white;
width: 100px;
margin: 10px;
&:hover {
opacity: 0.5;
}
cursor: pointer;
`
20230306 패스트캠퍼스 FE4기 수업자료를 참고하였습니다.
'React' 카테고리의 다른 글
TodoList 만들기(2) (0) | 2023.04.06 |
---|---|
TodoList 만들기(1) (0) | 2023.04.05 |
useRef (0) | 2023.03.20 |
이벤트핸들러 (0) | 2023.03.15 |
useEffect (0) | 2023.03.09 |