[React] 컴포넌트 스타일링
자료의 출처는 '엘리스 AI 트랙 2기 (https://aitrack.elice.io/)' 'React 심화', 및 '리액트를 다루는 기술'입니다.
React 스타일링
React 앱에서의 스타일링
좋은 앱이란 무엇인가
- 번들 사이즈에 대한 고려
=> CSS 코드가 차지하는 사이즈는 무척 중요한 요소. - 앱성능에대한고려
=> animation, transition 등 유저와의 상호작용에서 스타일 코드의 성능이 중요 요소 - 사용자에게 유리한 UI/UX를 고려
=> 스타일링에 대한 지식으로, 고급 테크닉을 적용하여 더 나은 UI/UX를 반영 - 자바스크립트를 이용한 다양한 스타일 기법
=> UI 토글링, 애니메이션, 다크모드, 복잡한 UI 컴포넌트 등은 자바스크립트에 대한 지식만으로 구현하기 힘듦. - 유지보수가 용이하고 확장 가능한 코드를 작성
=> 스타일에 관련된 코드를 어떻게 작성하고 관리하는가에 대한 지식이 필요.
좋은 앱을 만들기위한 다양한 방식이 있지만 중요한 요소 중 하나는 앱에 대한 스타일링이다.
스타일링 방식 및 종류
- 일반 CSS: 컴포넌트 스타일링의 가장 기본적인 방식
- Sass: 자주 사용되는 CSS 전처리기(pre-processor) 중 하나로 확장된 CSS 문법을 사용하여 CSS 코드를 더욱 쉽게 작성할 수 있도록 해줌.
- CSS Module: 스타일을 작성할 때 CSS 클래스가 다른 CSS 캘르스의 이름과 절대 충돌하지 않도록 파일마다 고유한 이름을 자동으로 생성해주는 옵션
- styled-componenets: 스타일을 자바스크립트 파일에 내장시키는 방식으로 스타일을 작성함과 동시에 해당 스타일이 적용된 컴포넌트를 만들 수 있게 해 줌.
가장 일반적인 CSS
이름 짓는 규칙
CSS 작성 시 가장 중요한 점은 CSS 클래스를 중복되지 않게 만드는 것
1. 컴포넌트-이름 클래스 형태
ex) App.css
.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;
}
클래스 이름에 컴포넌트 이름을 포함시켜 다른 컴포넌트에서 실수로 중복되는 캘래스를 만들어 사용하는 것을 방지
2. BEM 네이밍(BEM Naming)
CSS 방법론 중 하나로, 이름을 지을 때 일종의 규칙을 준수하여 해당 클래스가 어디에서 어떤 용도로 사용되는지 명확히 작성
ex) .card__title-primary
CSS Selector
CSS Selector를 사용하여 CSS 클래스가 특정 클래스 내부에 있는 경우에만 스타일 적용 가능
/* .App 안에 들어 있는 .logo */
.App .logo {
height: 40vmin;
pointer-events: none;
}
CSS Import
button.jsx
import 'button.css';
function Button({ children }) {
return (
<button className="button"> {children}
</button>
);}
button.css
.button {
background-color: orangered;
color: white;
width: 140px;
height: 40px;
}
App.jsx
import Button from './Button';
function App() {
return (
<div>
<Button>Submit</Button>
</div>
)}
WEB
import 'title.css';
방식으로 해당 js에 import하고,
element에
<element className="selector" />
방식으로 class를 사용하여 css를 적용한다.
CSS import - 장/단점
- 단순히 CSS 파일만을 import 하여 사용할 수 있어 편리.
- 컴포넌트가 많지 않을 경우, 하나의 CSS 파일에 코드를 관리하는 것도 가능함.
- CSS 파일은 분리할 수 있으나, namespace를 나눌 수 없음.
- 만일 스타일이 겹칠 경우 cascading rule에 따라, 마지막에 나온 룰이 덮어씌워짐.
Sass
Sass(Syntactically Awesome Style Sheets-문법적으로 매우 멋진 스타일시트)는 CSS 전처리기로
복잡한 작업을 쉽게 할 수 있도록 해주고, 스타일 코드의 재활용성을 높여 줄 뿐만 아니라 코드이 가독성을 높여 유지 보수에 더욱 용이
Sass에서는 두 가지 확장자 .scss와 .sass를 지원 ( 두 확장자의 문법은 다름)
ex) .sass
$font-stack: Helvetica, sans-erif
$primary-color: #333
body
font: 100% $font-stack
color: $primary-color
ex) .scss
$font-stack: Helvetica, sans-erif;
$primary-color: #333;
body {
font: 100% $font-stack
color: $primary-color
}
주요 차이점: .sass 확장자는 중괄호({})와 세미콜론(;)을 사용하지 않는다.
.scss는 기존 CSS 작성방식과 유사
설치방법
$ yarn add node-sass@4.14.1
(sass 내용은 이후 추가 예정)
Sass 참고
CSS Module
CSS module은 css를 불러와서 사용할 때 클래스 이름을 고유한 값,
[파일 이름]_[클래스 이름]__[해시값] 형태로 자동으로 만들어서 컴포넌트 스타일 클래스 이름이 중첩되는 현상을 방지해주는 기술
특징
- 클래스 이름을 지을 때 고유성에 대해 고민하지 않아도 됨
=> 해당 클래스는 우리가 방금 만든 스타일을 직접 불러온 컴포넌트 내부에서만 작동하기 때문
특정 클래스가 웹 페이지에서 전역적으로 사용되는 경우, :global을 앞에 입력하여 글로벌 CSS임을 명시해 줄 수 있음 - 하나의 CSS module 파일 안에 작성한 스타일은 하나의 파일 namespace로 관리.
- class name 뒤에 겹치지 않는 hash를 붙임.
- 스타일이 겹치는 상황을 해결.
- 두 단어 이상의 경우, class 명을 camelCase로 이름을 지음.
import CSS module.js
import styles from './CSSModule.module.css';
파일명 뒤에 확장자를 위해 module.css를 기입한다.
ex)
import styles from "./input-with-button.module.css";
export function InputWithButton() {
return (
<div className={styles.container}>
<input type="text" name="text" className={styles.input} />
<button className={styles.button}>Submit</button>
</div>
); }
CSS Module이 적용된 스타일 파일을 불러오면 객체를 하나 전달받게 되는데, CSS Module에서 사용한 이름과 해당 이름을 고유화한 키-값 형태로 들어있다.
사용법
/* JSX element */
className = {styles.[클래스 이름]} 형태로 전달
CSS Module을 사용한 클래스 이름을 두 개 이상 적용할 때
ex) CSS Module.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;
}
ex) 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;
classnames
설치
$ yarn add classnames
CSS 클래스를 조건부로 설정할 때 매우 유용한 라이브러리
CSS Module 사용 시 이 라이브러리를 사용하면 여러 클래스를 적용할 때 매우 편리
classnames 사용법 참고
Sass와 함께 사용하기
Sass를 사용할 때 파일 이름 뒤에 .module.scss 확장자를 사용해 주면 CSS Module로 사용
ex) CSSModule.module.scss
/* 자동으로 고유해질 것이므로 흔히 사용되는 단어를 클래스 이름으로 마음대로 사용 가능 */
.wrapper {
background: black;
padding: 1rem;
color: white;
font-size: 2rem;
&.inverted {
color: black;
background: white;
border: 1px solid black;
}
}
/* 글로벌 CSS를 작성하고 싶다면 */
:global {
// :global {}로 감싸기
.something {
font-weight: 800;
color: aqua;
}
}
import styles from './CSSModule.module.css';
CSS Module이 아닌 파일에서 CSS Module 사용하기
CSS Module이 아닌 일반 .css/.scss 파일에서도 :local을 사용하여 CSS Module을 사용할 수 있다
:local .wrapper {
/* 스타일 */
}
:local {
.wrapper {
/*스타일*/
}
}
styled-components
자바스크립트 파일 안에 스타일을 선언하는 방식으로 'CSS-in-JS'라고도 부름
관련 라이브러리 참고
https://github.com/MicheleBertoli/css-in-js
- 별도의 CSS 파일을 만들지 않고 하나의 컴포넌트 파일 안에서 스타일을 작성
- 자바스크립트 문법을 그대로 활용하여 코드를 작성
- React 컴포넌트를 사용하는 것처럼 사용
- Sass 문법 활용 가능
* 다양한 라이브러리 중 styled-componenets를 가장 선호하며 해당 내용을 소개함
설치
$ yarn add styled-components
작성 방법
import React from "react";
import styled from "styled-components";
const {className} = styled.{element}` /*백틱*/
/* 스타일 */
`;
const StyledComponent = () => {
return (
<className />
)
}
ex) styledComponent.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;
margin: 0 auto;
`;
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;
*
styled-componenets와 일반 classNames를 사용하는 CSS/Sass를 비교 시, 가장 큰 장점은
props 값으로 전달해 주는 값을 쉽게 스타일에 적용할 수 있다는 점
Tagged 템플릿 리터럴
스타일 작성 시 `(백틱)을 사용하여 만든 문자열에 스타일 정보를 넣어 주었다.
여기서 사용한 문법 => Tagged 템플릿 리터럴
CSS Module의 일반 템플릿 리터럴과 다른 점은 템플릿 안에 자바스크립트 객체나 함수를 전달할 때 온전히 추출할 수 있는 것
`hello ${{foo: 'bar'}} ${() => 'world'}!`
// 결과: "hello [objec Object] () => `world`!"
템플릿에 객체를 넣거나 함수를 넣으면 형태를 잃어버림
↓
다음과 같은 함수를 작성하고 나서 해당 함수 뒤에 템플릿 리터럴을 넣어 준다면, 템플릿 안에 값을 온전히 추출할 수 있음
function tagged(...args) {
console.log(args);
}
tagged`hello ${{foo: 'bar'}} ${() => 'world'}!`
=> console을 통한 결과
Tagged 템플릿 리터럴을 사용하면 템플릿 사이사이에 들어가는 자바스크립트 객체나 함수의 원본 값을 그대로 추출 할 수 있다.
styled-components는 이러한 속성을 사용해 styled-components로 만든 컴포넌트의 props를 스타일 쪽에서 쉽게 조회할 수 있도록 해준다.
정리
리액트 컴포넌트 스타일링의 여러 방식들은 현재 모두 사용되는 쓸모있는 기술들이며, 본인이 원하는, 또는 업무에 필요한 방법들을 익혀 능숙하게 사용하도록 하자.