1. Component Driven Development (CDD)
조립해나갈 수 있는 부품 단위로 UI 컴포넌트를 만들어나가는 개발 방식. BBC나 UN 등이 이 방법을 활용하고 있다.
재사용할 수 있는 컴포넌트를 개발할 수 있고, 상향식 개발에 적합한 방식이다.
1) 구조적인 CSS 작성 방법의 발전
구조화된 CSS가 필요해진 이유
- 프로젝트의 규모나 복잡도가 커짐
- 함께 작업해야할 팀원 수가 늘어남
- 모바일, 태블릿 등 다양한 디바이스의 디스플레이를 커버해야함
=> 효율적으로 CSS를 작업하기 위해 구조화된 CSS가 필요해졌다!
(1) CSS 전처리기(CSS Preprocessor)
CSS가 구조적으로 작성될 수 있게 도움을 주는 도구. CSS를 작성할때 발생하는 반복적인 작업들을 변수와 함수, 상속 등의 프로그래밍 개념을 활용하여 해결할 수 있다. 그러나 CSS 전처리기 자체는 웹서버가 인지하지 못하기 때문에 각 전처리기에 맞는 Compiler를 사용해야 하고, 컴파일을 완료한 후에 실제로 사용되는 CSS 문서로 변환이 된다.
SASS(Syntactically Awesome Style Sheets)
CSS 전처리기들 중 가장 유명한 언어. JS 처럼 특정 속성의 값을 변수로 선언하여 필요한 곳에 적용하거나 반복되는 코드를 한번의 선언으로 여러곳에서 재사용할 수 있도록 해주는 등의 기능을 가졌다. SCSS 코드를 읽어서 전처리한다음 컴파일 하고 전역 CSS 번들 파일을 만들어주는 전처리기의 역할을 한다. 그러나 CSS를 구조화를 해결하는 장점보다 다른 문제들을 더 많이 만들게 되고, CSS 전처리기의 문제를 보완하기 위한 여러 CSS 방법론이 대두되었다.
(2) CSS 방법론
CSS 방법론들의 공통적인 지향점
- 코드의 재사용
- 코드의 간결화(유지보수 용이)
- 코드의 확장성
- 코드의 예측성(클래스명으로 의미를 예측)
BEM 방법론
Block, Element, Modifier로 구분하여 클래스 명을 작성하는 방법. 각각은 --와 __로 구분한다.
클래스명은 BEM 방식의 이름을 여러번 반복하여 재사용할 수 있도록하며 HTML / CSS / SASS 파일에서도 일관된 코딩 구조를 만들어준다.
그러나 클래스명 선택자가 장황해지고, 긴 클래스명 때문에 마크업이 불필요하게 커지고, 재사용하려고 할때마다 모든 UI 컴포넌트를 명시적으로 확장해야만 한다.
애플리케이션으로 개발 방향이 진화하면서 캡슐화의 중요성이 커지게 되었고, CSS를 컴포넌트 영역으로 불러들이기 위해서 CSS-in-JS가 탄생했다.
대표적인 CSS-in-JS에는 Styled-Component가 있다.
✅ 각 CSS 방법론의 특징 및 장단점
특징 | 장점 | 단점 | |
CSS | 기본적인 스타일링 방법 | - | 일관된 패턴을 갖기 어려움. !important의 남용 |
SASS (preprocessor) |
프로그래밍 방법론을 도입하여 컴파일된 CSS를 만들어내는 전처리기 | 변수/함수/상속 개념을 활용하여 재사용 가능 CSS의 구조화 |
전처리 과정이 필요함. 디버깅의 어려움이 있음 컴파일한 CSS 파일이 거대해짐 |
BEM | CSS 클래스명 작성에 일관된 패턴을 강제하는 방법론 | 네이밍으로 문제 해결. 전처리 과정 불필요 |
선택자의 이름이 너무 장황해짐. 클래스 목록이 너무 많아짐. |
Styled-Component (CSS-in-JS) |
컴포넌트 기반으로 CSS를 작성할 수 있게 도와주는 라이브러리 | CSS를 컴포넌트 안으로 캡슐화, 네이밍이나 최적화를 신경쓸 필요가 없다 | 빠른 페이지 로드에 불리함 |
2. CDD 개발 도구
CSS in JS 라이브러리를 사용하면 CSS도 쉽게 JavaScript 안에 넣어줄 수 있으므로, HTML + JS + CSS까지 묶어서 하나의 JS 파일 안에서 컴포넌트 단위로 개발할 수 있다.
1) Styled Compoenents
(1) 기본 사용방법
설치하기
// npm
npm install --save styled-components
// yarn
yarn add styled-components
설치 후 package.json에 코드 추가 (권장)
여러 버전의 styled-compoenents가 설치되는 것을 방지한다
{
"resolutions": {
"styled-components": "^5"
}
}
사용할 파일에 import
import styled from "styled-components"
(2) Styled Components 문법
컴포넌트 만들기
const 컴포넌트이름 = styled.태그종류`
css속성:속성값;
`;
// 예시
const BlueButton = styled.button`
background-color:blue;
color:#fff;
`;
Styled Components는 ES6의 템플릿 리터럴을 사용한다. 즉, 따옴표가 아니라 백틱(`)문자를 사용하는 것에 주의해야한다. 이렇게 생성한 컴포넌트를 React 컴포넌트를 사용하는 것처럼 리턴문 안에 작성하면 스타일이 적용된 컴포넌트가 렌더링된다.
import styled from "styled-components";
//Styled Components로 컴포넌트 생성
const BlueButton = styled.button`
background-color: blue;
color: white;
`;
const OrangeButton = styled.button`
background-color: orange;
color: black;
`;
export default function App() {
// React 컴포넌트를 사용하듯이 사용
return (
<>
<BlueButton>Blue Button</BlueButton>
<OrangeButton>Orange Button</OrangeButton>
</>
);
}
컴포넌트 재활용하기
이미 만들어진 컴포넌트를 사용해서 새 컴포넌트를 만드는 방법이다.
const 컴포넌트이름 = styled(재활용할컴포넌트)`
추가할 CSS 속성 : 속성값;
`;
// 예시
const BigBlueButton = styled(BlueButton)`
padding: 10px;
margin-top:10px;
`;
import styled from "styled-components";
const BlueButton = styled.button`
background-color: blue;
color: white;
`;
//만들어진 컴포넌트를 재활용해 컴포넌트를 만들 수 있습니다.
const BigBlueButton = styled(BlueButton)`
padding: 10px;
margin-top: 10px;
`;
//재활용한 컴포넌트를 재활용할 수도 있습니다.
const BigRedButton = styled(BigBlueButton)`
background-color: red;
`;
export default function App() {
return (
<>
<BlueButton>Blue Button</BlueButton>
<br />
<BigBlueButton>Big Blue Button</BigBlueButton>
<br />
<BigRedButton>Big Red Button</BigRedButton>
</>
);
}
Props를 활용하기
Styled Component로 만든 컴포넌트도 props를 내려줄수있다. 내려준 props 값에 따라서 컴포넌트를 렌더링 할 수 도 있다. 템플릿 리터럴 문법을 사용해 JS 코드를 사용하고, props를 받아올때는 props를 인자로 받는 함수를 만들어 사용한다.
const 컴포넌트이름 = styled.태그종류`
css속성 : ${(props)=>함수코드}
`;
- props로 조건부 렌더링
const Button = styled.button`
// 삼항연산자를 사용. props에 skyblue라는 props가 있다면 특정 배경색 지정
background: ${(props) => props.skyblue ? 'skyblue' : 'white'}
`;
return (
<>
<GlobalStyle />
<Button1>Button1</Button1>
<Button1 skyblue>Button1</Button1>
</>
);
- props 값으로 렌더링하기
props의 값을 통째로 사용해서 컴포넌트 렌더링에 활용하는 방법.
// props값으로 렌더링 - 삼항연산자
const Button1 = styled.button`
background: ${(props) => (props.color ? props.color : "white")};
`;
// props값으로 렌더링 - || 연산자 사용
const Button2 = styled.button`
background: ${(props) => props.color || "white"};
`;
export default function App() {
return (
<>
<GlobalStyle />
<Button1>Button1</Button1>
<Button1 color="orange">Button1</Button1>
<Button1 color="tomato">Button1</Button1>
<br />
<Button2>Button2</Button2>
<Button2 color="pink">Button2</Button2>
<Button2 color="turquoise">Button2</Button2>
</>
);
}
전역 스타일 설정하기
createGlobalStyle 함수 import
import { createGlobalStyle } from "styled-components";
필요한 전역 스타일 작성
const GlobalStyle = createGlobalStyle`
button {
padding : 5px;
margin : 2px;
border-radius : 5px;
}
`
해당 컴포넌트를 최상위 컴포넌트에서 사용
function App() {
return (
<>
<GlobalStyle />
<Button>전역 스타일 적용하기</Button>
</>
);
}
hover 사용방법
import styled from "styled-components";
import { createGlobalStyle } from 'styled-components'
const GlobalStyle = createGlobalStyle`
* {
margin: 0.5rem;
}
`;
const Practice = styled.button`
padding: 1rem;
font-size: 2rem;
background: powderblue;
border-radius: 1rem;
transition: 0.5s;
&:hover {
background: cornflowerblue;
color: #fff;
cursor: pointer;
}
`;
export default function App() {
return (
<>
<GlobalStyle/>
<Practice>Practice!</Practice>
</>
);
}
2) Storybook
Component Driven Development가 트렌드로 자리잡게 되면서 이를 지원하는 도구 중 하나인 Component Explore(컴포넌트 탐색기)가 등장했다. Storybook은 Component Explorer의 많은 UI 개발 도구 중의 하나이다.
Storybook은 각각의 컴포넌트들을 따로 볼 수 있게 구성해주어 한번에 하나의 컴포넌트에서 작업할 수 있다. 복잡한 개발 스택을 시작하거나, 특정 데이터를 데이터베이스로 강제 이동하거나, 애플리케이션을 탐색할 필요 없이 전체 UI를 한눈에 보고 개발할 수 있다.
또한 재사용성을 확대하기 위해 컴포넌트를 문서화 하고, 자동으로 컴포넌트를 시각화하여 시뮬레이션 할 수 있는 다양한 테스트 상태를 확인할 수 있고, 이를 통해 버그를 사전에 방지할 수 있도록 도와준다. 그 외에도 테스트 및 개발 속도를 향상시키는 장점이 있고 애플리케이션 또한 의존성을 걱정하지 않고 빌드할 수 있다.
Storybook의 주요 기능
- UI 컴포넌트 카탈로그화
- 컴포넌트 변화를 Stories로 저장
- 핫 모듈 재 로딩과 같은 개발 툴 경험을 제공
- 리액트를 포함한 다양한 뷰 레이어 지원
(1) 기본 사용 방법
설치하기
npx storybook init
npm i @testing-library/dom
설치가 완료되면 /.storybook 폴더(Storybook 관련 설정파일)와 /src/stories 폴더(예시파일)가 생성되어있다.
실행하기
npm run storybook
실행하게 되면 이렇게 로컬 주소가 터미널에 나타난다. 해당 주소로 들어가면 스토리북을 확인할 수 있다.
이렇게 스토리북을 사용하면 꼭 애플리케이션 실행 후 이벤트를 통해 상태를 변경하는 과정을 거치지 않아도 상태변화에 따른 컴포넌트 변화를 확인할 수 있다.
(2) 작성 방법 예시
src/Title.js
import React from "react";
const Title = ({ title, textColor }) => (
<h1 style={{ color: textColor }}>{title}</h1>
);
export default Title;
props로 title, textColor를 받을 컴포넌트를 작성한다.
작성한 컴포넌트를 스토리로 만들기 위해 src/Title.stories.js 파일을 생성하고 내용을 작성한다.
import Title from "./Title"; // 작성한 컴포넌트 불러오기
export default {
title: "Practice/Title", // 컴포넌트 이름. /를 통해 카테고리화 할수있음
component: Title, // 어떤 컴포넌트를 가져와서 스토리로 만들건지
argTypes: {
// 컴포넌트에 필요한 전달 인자의 종류와 타입
title: { control: "text" },
textColor: { control: "text" },
},
};
// 템플릿을 만들어줌.
// Title 컴포넌트가 args를 전달받아 props로 내려준다.
const Template = (args) => <Title {...args} />;
// storybook에서 확인하고 싶은 컴포넌트 = export const 로 작성
// Template.bind({}) 는 정해진 문법이라고 생각하면 됨
export const RedTitle = Template.bind({});
// 만들어준 스토리의 전달인자 작성
RedTitle.args = {
title: "Red Title",
textColor: "red",
};
// 스토리 추가생성
export const BlueTitle = Template.bind({});
// 추가한 스토리의 전달인자 작성
BlueTitle.args = {
title: "Blue Title",
textColor: "blue",
};
// 템플릿 활용 없이 전달인자를 직접 받는 스토리
export const StorybookTitle = (args) => {
return <Title {...args} />;
};
styled-component로 만든 컴포넌트를 스토리로 만드는 예시
src/Button.js
import React from "react";
import styled from "styled-components";
const StyledButton = styled.button`
// props.color가 있으면 props.color, 없으면 흰색을 배경색으로
background: ${(props) => props.color || "white"};
// props.size가 'big'이면 200px, 아니면 100px을 너비로 사용
width: ${(props) => (props.size === "big" ? "200px" : "100px")};
// props.size가 'big'이면 80px, 아니면 40px을 너비로 사용
height: ${(props) => (props.size === "big" ? "80px" : "40px")};
`;
const Button = ({ color, size, text }) => (
// 위에서 작성한 props + text를 받아 textContent로 사용한다
<StyledButton color={color} size={size}>
{text}
</StyledButton>
);
export default Button;
src/Button.stories.js
import Button from "./Button";
export default {
title: "Practice/Button",
component: Button,
argTypes: {
color: { control: "color" },
size: { control: { type: "radio", options: ["big", "small"] } },
text: { control: "text" },
},
};
export const StorybookButton = (args) => <Button {...args}></Button>;
3. useRef
useRef는 DOM 노드, 엘리먼트, React 컴포넌트 주소값을 참조할 수 있는 React hook이다.
이 주소값은 컴포넌트가 리렌더링 되더라도 바뀌지 않는다. 또한, 이러한 특징 때문에 컴포넌트 안에서 조회 및 수정할 수 있는 변수를 관리할때에도 사용할 수 있다. useRef로 관리하는 변수는 값이 바뀐다고 해서 컴포넌트가 리렌더링 되지 않는다.
그러나 useRef가 남용되는 경우 React의 특징이자 장점인 선언형 프로그래밍 원칙에 위배되기 때문에 조심해서 사용해야 한다.
useRef는 아래와 같은 상황에서 사용될 수 있다.
- focus
- text selection
- media playback
- 애니메이션 적용
- d3.js, greensock 등 DOM 기반 라이브러리 사용
- setTimeout, setInterval 등을 통해 만들어진 id
- 외부 라이브러리를 사용하여 생성된 인스턴스
- scroll 위치
useRef의 예시
const 주소값을_담는_그릇 = useRef(참조자료형)
// 이제 주소값을_담는_그릇 변수에 어떤 주소값이든 담을 수 있습니다.
return (
<div>
<input ref={주소값을_담는_그릇} type="text" />
{/* React에서 사용 가능한 ref라는 속성에 주소값을_담는_그릇을 값으로 할당하면*/}
{/* 주소값을_담는_그릇 변수에는 input DOM 엘리먼트의 주소가 담깁니다. */}
{/* .current를 사용해야 정확하게 input DOM 엘리먼트를 가리킨다 */}
</div>);
// 예시
function TextInputWithFocusButton() {
const inputEl = useRef(null);
const onButtonClick = () => {
// button이 클릭되면 inputEL(=input)에 focus 된다
inputEl.current.focus();
};
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>);
}
'study > TIL' 카테고리의 다른 글
23.02.24 - Redux (0) | 2023.02.24 |
---|---|
23.02.23 - 상태 관리 (0) | 2023.02.23 |
221228 - CLI, Git 기초 (0) | 2023.02.19 |
23.02.16 - Figma (0) | 2023.02.16 |
230215 - UI/UX (0) | 2023.02.15 |