: ๋ถํ ๋จ์๋ก UI ์ปดํฌ๋ํธ๋ฅผ ๋ง๋ค์ด ๋๊ฐ๋ ๊ฐ๋ฐ ๋ฐฉ๋ฒ
( ์ฌ์ฌ์ฉ ํ ์ ์๋ UI ์ปดํฌ๋ํธ์ ํ์์ฑ์ผ๋ก ์ธํด ๋ง๋ค์ด์ก๋ค. )
+ CDD ๋ฐฉ๋ฒ์ ํ์ฉํ์ฌ UI๋ฅผ ๊ตฌ์ถํ๋ ์ฌ์ดํธ : BBC , UN
๊ตฌ์กฐํ๋ CSS๊ฐ ํ์ํ๊ฒ ๋ ์ด์
CSS ์ ์ฒ๋ฆฌ๊ธฐ (CSS Preprocessor) : CSS๊ฐ ๊ตฌ์กฐ์ ์ผ๋ก ์์ฑ๋ ์ ์๊ฒ ๋์์ ์ฃผ๋ ๋๊ตฌ
( SASS๊ฐ CSS ์ ์ฒ๋ฆฌ๊ธฐ ์ค ๊ฐ์ฅ ์ ๋ช
)
CSS ๋ฐฉ๋ฒ๋ก - BEM
: Block, Element, Modifier๋ก ๊ตฌ๋ถํ์ฌ ํด๋์ค๋ช
์ ์์ฑํ๋ ๋ฐฉ๋ฒ
Styled-Component (CSS-in-JS)
: JavaScript์์ CSS๋ฅผ ์์ฑํ๋ ๋ฐฉ์ -> CSS๋ฅผ ์ปดํฌ๋ํธ์ ์์ญ์ผ๋ก ๋ถ๋ฌ๋ค์ธ CSS ์์ฑ ๋ฐฉ๋ฒ
: JavaScript์์ CSS๋ฅผ ์์ฑํ๋ ๋ฐฉ์ -> CSS๋ฅผ ์ปดํฌ๋ํธ์ ์์ญ์ผ๋ก ๋ถ๋ฌ๋ค์ธ CSS ์์ฑ ๋ฐฉ๋ฒ
CSS in JS ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋ฉด CSS๋ฅผ JavaScript๋ก ๋ฃ์ด์ค์,
HTML + JS + CSS ๋ฅผ ๋ฌถ์ด์ ํ๋์ JSํ์ผ ์์์ ์ปดํฌ๋ํธ ๋จ์๋ก ๊ฐ๋ฐํ ์ ์๋ค.
๊ทธ ์ค ๊ฐ์ฅ ์ธ๊ธฐ ์๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ => Styled Components
- ํฐ๋ฏธ๋์ ์๋ ๋ช ๋ น์ด๋ฅผ ์ ๋ ฅํ์ฌ Styled Components ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ค์น
npm install --save styled-components # npm
yarn add styled-components # yarn
- package.json์ ์๋ ์ฝ๋๋ฅผ ์ถ๊ฐํ์ฌ ์ฌ๋ฌ ๋ฒ์ ์ Styled Components๊ฐ ์ค์น๋์ด ๋ฐ์ํ๋ ๋ฌธ์ ๋ฅผ ์ค์ฌ์ค๋ค.
{
"resolutions": {
"styled-components": "^5"
}
}
- Styled Components๋ฅผ ์ฌ์ฉํ ํ์ผ์์ ์๋ ์ฝ๋๋ฅผ ์์ฑํด ๋ถ๋ฌ์จ๋ค.
import styled from "styled-components"
1. ์ปดํฌ๋ํธ ๋ง๋ค๊ธฐ
Styled Components๋ ES6์ Templete Literals ๋ฌธ๋ฒ์ ์ฌ์ฉํ๋ค. => ๋ฐ์ดํ๊ฐ ์๋ ๋ฐฑํฑ( ` ) ์ฌ์ฉ
import styled from "styled-components";
// Styled Components๋ก ์ปดํฌ๋ํธ๋ฅผ ๋ง๋ค๊ณ
const SkyBlueButton = styled.button`
background-color: skyblue;
`;
export default function App() {
// React ์ปดํฌ๋ํธ๋ฅผ ์ฌ์ฉํ๋ฏ์ด ์ฌ์ฉ
return <SkyBlueButton>SkyBlue Button</SkyBlueButton>;
}
์ ์ฝ๋ ์ถ๋ ฅ ํ๋ฉด
2. ์ปดํฌ๋ํธ๋ฅผ ์ฌํ์ฉํด์ ์๋ก์ด ์ปดํฌ๋ํธ ๋ง๋ค๊ธฐ
import styled from "styled-components";
const SkyBlueButton = styled.button`
background-color: Skyblue;
`;
// ๋ง๋ค์ด์ง ์ปดํฌ๋ํธ๋ฅผ ์ฌํ์ฉํด ์ปดํฌ๋ํธ๋ฅผ ๋ง๋ค ์ ์๋ค.
const BigSkyBlueButton = styled(SkyBlueButton)`
padding: 10px;
margin-top: 10px;
`;
// ์ฌํ์ฉํ ์ปดํฌ๋ํธ๋ฅผ ์ฌํ์ฉํ ์๋ ์๋ค.
const BigRedButton = styled(BigSkyBlueButton)`
background-color: blue;
color: white;
`;
export default function App() {
return (
<>
<SkyBlueButton>Skyblue Button</SkyBlueButton>
<br />
<BigSkyBlueButton>Big Skyblue Button</BigSkyBlueButton>
<br />
<BigRedButton>Big blue Button</BigRedButton>
</>
);
}
์ ์ฝ๋ ์ถ๋ ฅ ํ๋ฉด
3. Props ํ์ฉํ๊ธฐ
- Styled Component๋ก ๋ง๋ ์ปดํฌ๋ํธ๋ React ์ปดํฌ๋ํธ์ฒ๋ผ props๋ฅผ ๋ด๋ ค์ค ์ ์๊ณ , ๋ด๋ ค์ค props ๊ฐ์ ๋ฐ๋ผ์ ์ปดํฌ๋ํธ๋ฅผ ๋ ๋๋งํ๋ ๊ฒ๋ ๊ฐ๋ฅํ๋ค.
- Styled Components๋ ํ
ํ๋ฆฟ ๋ฆฌํฐ๋ด ๋ฌธ๋ฒ( ${ }
)์ ์ฌ์ฉํ์ฌ JavaScript ์ฝ๋๋ฅผ ์ฌ์ฉํ ์ ์๋ค.
- props๋ฅผ ๋ฐ์์ค๋ ค๋ฉด props๋ฅผ ์ธ์๋ก ๋ฐ๋ ํจ์๋ฅผ ๋ง๋ค์ด ์ฌ์ฉํ๋ฉด ๋๋ค.
1) Props๋ก ์กฐ๊ฑด๋ถ ๋ ๋๋งํ๊ธฐ
import styled from "styled-components";
// ๋ฐ์์จ prop์ ๋ฐ๋ผ ์กฐ๊ฑด๋ถ ๋ ๋๋ง์ด ๊ฐ๋ฅํ๋ค. (์ผํญ์ฐ์ฐ์ ์ฌ์ฉ)
const Button1 = styled.button`
background: ${(props) => (props.skyblue ? "skyblue" : "white")};
`;
export default function App() {
return (
<>
<Button1>Button1</Button1>
<Button1 skyblue>Button2</Button1>
</>
);
}
์ ์ฝ๋ ์ถ๋ ฅ ํ๋ฉด
2) Props ๊ฐ์ผ๋ก ๋ ๋๋งํ๊ธฐ
props์ ๊ฐ์ ํต์งธ๋ก ํ์ฉํด์ ์ปดํฌ๋ํธ ๋ ๋๋ง์ ํ์ฉํ ์ ์๋ค.
import styled from "styled-components";
// ๋ฐ์์จ prop ๊ฐ์ ๊ทธ๋๋ก ์ด์ฉํด ๋ ๋๋งํ ์ ์๋ค.
const Button1 = styled.button`
background: ${(props) => (props.color ? props.color : "white")};
`;
// ๋ค์๊ณผ ๊ฐ์ ํ์์ผ๋ก๋ ํ์ฉํ ์ ์๋ค.
const Button2 = styled.button`
background: ${(props) => props.color || "white"};
`;
export default function App() {
return (
<>
<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>
</>
);
}
4. ์ ์ญ ์คํ์ผ ์ค์ ํ๊ธฐ
- ์ ์ญ ์คํ์ผ์ ์ค์ ํ๊ธฐ ์ํด Styled Components์์ createGlobalStyle
ํจ์๋ฅผ ๋ถ๋ฌ์จ๋ค.
import { createGlobalStyle } from "styled-components";
- ์ด ํจ์๋ฅผ ์ฌ์ฉํด CSS ํ์ผ์์ ์์ฑํ๋ฏ ์ค์ ํด์ฃผ๊ณ ์ถ์ ์คํ์ผ์ ์์ฑํ๋ค.
const GlobalStyle = createGlobalStyle`
button {
padding : 5px;
margin : 2px;
border-radius : 3px;
}
`
- <GlobalStyle>
์ปดํฌ๋ํธ๋ฅผ ์ต์์ ์ปดํฌ๋ํธ์์ ์ฌ์ฉํด์ฃผ๋ฉด ์ ์ญ์ <GlobalStyle>
์ปดํฌ๋ํธ์ ์คํ์ผ์ด ์ ์ฉ๋๋ค.
function App() {
return (
<>
<GlobalStyle />
<Button>์ ์ญ ์คํ์ผ ์ ์ฉ</Button>
</>
);
}
: UI ๊ฐ๋ฐ ๋๊ตฌ, Component Driven Development๋ฅผ ํ๊ธฐ ์ํ ๋๊ตฌ (์ปดํฌ๋ํธ ์๊ฐํ & ๋ฌธ์ํ)
Storybook์ ๋ ๋ฆฝ์ ์ธ ๊ฐ๋ฐ ํ๊ฒฝ์์ ์คํ๋์ด์ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ค์ํ ์ํฉ์ ๊ตฌ์ ๋ฐ์ง ์๊ณ UI ์ปดํฌ๋ํธ๋ฅผ ์ง์ค์ ์ผ๋ก ๊ฐ๋ฐํ ์ ์๋ค.
Storybook ์ฃผ์ ๊ธฐ๋ฅ
- Storybook ์ค์น
npx storybook init
Storybook ์ค์น๊ฐ ์๋ฃ๋๋ฉด, /.storybook ํด๋์ /src/stories ํด๋๊ฐ ์์ฑ๋๋ค.
/.storybook ํด๋
=> Storybook ๊ด๋ จ ์ค์ ํ์ผ, /src/stories ํด๋
=> Storybook ์์ ํ์ผ
- Storybook ์คํ
npm run storybook
localhost:6006์ผ๋ก ์ ๊ทผํ์ฌ Storybook์ ์คํํ๋ค.
Storybook์ ์ฌ์ฉํ๋ฉด ์ ํ๋ฆฌ์ผ์ด์ ์ ์คํํ๊ณ ์ด๋ฒคํธ๋ฅผ ํตํด ์ํ๋ฅผ ๋ณ๊ฒฝํ๋ ๊ณผ์ ์ ๊ฑฐ์น์ง ์์๋ ์ํ ๋ณํ์ ๋ฐ๋ฅธ ์ปดํฌ๋ํธ์ ๋ณํ๋ฅผ ํ์ธํ ์ ์๋ค.
- ๊ฐ๋จํ ์คํ ๋ฆฌ ์์ฑ
src ํด๋ > Title.js ํ์ผ ์์ฑ, ์๋์ ๊ฐ์ด React ์ปดํฌ๋ํธ๋ฅผ ํ๋ ๋ง๋ค์ด export ํด์ค๋ค.
// Title.js
import React from "react";
// title์ h1 ์์์ textContent, textColor์ ๊ธ์์์ด ๋๋ props
const Title = ({title, textColor}) => (
<h1 style={{color: textColor}}>{title}</h1>
);
export default Title;
๊ฐ์ ์์น์ธ src ํด๋ > Title.stories.js ํ์ผ ์์ฑ
.storybook ์์ ์๋ Storybook ์ค์ ํ์ผ์ ์ํด ์ปดํฌ๋ํธ ํ์ผ๊ณผ ๋๊ฐ์ ํ์ผ ์ด๋ฆ์ .stories๋ฅผ ๋ถ์ฌ ํ์ผ์ ๋ง๋ค๋ฉด ์์์ ์คํ ๋ฆฌ๋ก ์ธ์ํ๋ค.
// Title.stories.js
// ์์์ ์์ฑํ ์ปดํฌ๋ํธ๋ฅผ ๋ถ๋ฌ์จ๋ค.
import Title from "../Title";
// title : ์ปดํฌ๋ํธ ์ด๋ฆ์ผ๋ก, '/'๋ฅผ ๋ฃ์ด ์นดํ
๊ณ ๋ฆฌํ ํ ์ ์๋ค.
// component : ์ด๋ค ์ปดํฌ๋ํธ๋ฅผ ๊ฐ์ ธ์์ ์คํ ๋ฆฌ๋ก ๋ง๋ค ๊ฒ์ธ์ง ๋ช
์ํ๋ค.
// argTypes : ์ปดํฌ๋ํธ์ ํ์ํ ์ ๋ฌ์ธ์์ ์ข
๋ฅ์ ํ์
์ ์ ํด์ค๋ค.
// ์ง๊ธ์ title, textColor์ด๋ผ๋ ์ ๋ฌ์ธ์์ text ํ์
์ด ํ์ํจ์ ์๋ฏธํ๋ค.
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๋ก ์์ฑ
// ํ
ํ๋ฆฟ์ ์ฌ์ฉํ์ฌ Storybook์ ๋ฃ์ด์ค ์คํ ๋ฆฌ๋ฅผ ํ๋ ๋ง๋ค์ด์ค๋ค.
// Template.bins({}); ๋ ์ ํด์ง ๋ฌธ๋ฒ์ด๋ผ๊ณ ์๊ฐํ๊ณ ์ฌ์ฉ
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} />;
};
: useRef๋ฅผ ํ์ฉํ๋ฉด DOM ๋ ธ๋, ์๋ฆฌ๋จผํธ, React ์ปดํฌ๋ํธ ์ฃผ์๊ฐ์ ์ฐธ์กฐํ ์ ์๋ค.
( React : DOM ์ง์ ์กฐ์ํ๋ ๊ฒ ์ง์ -> document.querySelector ๋ฑ ์ฌ์ฉ x )
DOM ์๋ฆฌ๋จผํธ์ ์ฃผ์๊ฐ์ ํ์ฉํด์ผ ํ๋ ๊ฒฝ์ฐ
=> ์์ ๊ฐ์ DOM ๊ฐ์ฒด ์ฃผ์ ํ์ํ ์ํฉ์ด ๊ณ์ ๋ฐ์ํด useRef ๋ฉ์๋๊ฐ ์์ฑ๋์๋ค.
const ์ฃผ์๊ฐ์_๋ด๋_๊ทธ๋ฆ = useRef(์ฐธ์กฐ์๋ฃํ)
// ์ฃผ์๊ฐ์_๋ด๋_๊ทธ๋ฆ ๋ณ์์ ์ด๋ค ์ฃผ์๊ฐ์ด๋ ๋ด์ ์ ์๋ค.
return (
<div>
<input ref={์ฃผ์๊ฐ์_๋ด๋_๊ทธ๋ฆ} type="text" />
{/* React์์ ์ฌ์ฉ ๊ฐ๋ฅํ ref๋ผ๋ ์์ฑ์ ์ฃผ์๊ฐ์_๋ด๋_๊ทธ๋ฆ์ ๊ฐ์ผ๋ก ํ ๋นํ๋ฉด*/}
{/* ์ฃผ์๊ฐ์_๋ด๋_๊ทธ๋ฆ ๋ณ์์๋ input DOM ์๋ฆฌ๋จผํธ์ ์ฃผ์๊ฐ ๋ด๊ธด๋ค. */}
{/* ์ดํ ๋ค๋ฅธ ์ปดํฌ๋ํธ์์ input DOM ์๋ฆฌ๋จผํธ๋ฅผ ํ์ฉํ ์ ์๋ค. */}
</div>;
=> ์ฃผ์๊ฐ์ ์ปดํฌ๋ํธ๊ฐ re-render ๋๋๋ผ๋ ๋ฐ๋์ง ์๋๋ค.
useRef๋ฅผ ๋จ์ฉํ๋ ๊ฒ์ React์ ์ ์ธํ ํ๋ก๊ทธ๋๋ฐ ์์น๊ณผ ๋ฐฐ์น๋๊ธฐ ๋๋ฌธ์ ์กฐ์ฌํด์ ์ฌ์ฉํด์ผ ํ๋ค.
์์ 1 : focus
https://codesandbox.io/s/patient-worker-3kzhd?from-embed
์์ 2 : media playback
https://codesandbox.io/s/priceless-sanderson-kx77s?from-embed
+ Event.stopPropagation()
: ํ์ฌ ์ด๋ฒคํธ๊ฐ ์บก์ฒ๋ง/๋ฒ๋ธ๋ง ๋จ๊ณ์์ ๋ ์ด์ ์ ํ๋์ง ์๋๋ก ๋ฐฉ์งํ๋ค.