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
Storybook: Frontend workshop for UI development
Storybook is a frontend workshop for building UI components and pages in isolation. Thousands of teams use it for UI development, testing, and documentation. It’s open source and free.
storybook.js.org
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 |