2022.06.30(Thurs)
[TIL] Day47
[SEB FE] Day46
โฐย ๋ถํ ๋จ์๋ก UI ์ปดํฌ๋ํธ๋ฅผ ๋ง๋ค์ด ๋๊ฐ๋ ๊ฐ๋ฐ ์งํ
โฐย ์ฌ์ฌ์ฉ ๊ฐ๋ฅํ ์ปดํฌ๋ํธ ๊ฐ๋ฐ ๊ฐ๋ฅ
โฐย ์ปดํฌ๋ํธ ๋จ์๋ก ๋ง๋ค์ด ํ์ด์ง๋ฅผ ์กฐ๋ฆฝํ๋ ๊ฐ๋ฐ ๋ฐฉ์ โ ์ํฅ์ ๊ฐ๋ฐ
๐ย CSS โ SASS โ BEM โ CSS Modules โ Styled Components
โฐย ๊ตฌ์กฐํ๋ CSS ํ์์ฑ์ด ๋๋๋ ๋ฐฐ๊ฒฝ
: CSS๊ฐ ๊ตฌ์กฐ์ ์ผ๋ก ์์ฑ๋ ์ ์๊ฒ ๋์์ ์ฃผ๋ ๋๊ตฌ
โย ๊ฐ CSS ์ ์ฒ๋ฆฌ๊ธฐ์ ๋ง๋ Compiler๋ฅผ ์ฌ์ฉํด์ผ ํจ โ ์ปดํ์ผ ํ, CSS ๋ฌธ์๋ก ๋ณํ
Syntactically Awesome Style Sheets
๐นย CSS๋ฅผ ํ์ฅํด ์ฃผ๋ Scripting ์ธ์ด
๐นย ํน์ ์์ฑ ๊ฐ์ ๋ณ์๋ก ์ ์ธ โ ํ์ํ ๊ณณ์ ์ ์ธ๋ ๋ณ์ ์ ์ฉ
โ ๋ฐ๋ณต๋๋ ์ฝ๋๋ฅผ ์ ์ธ 1๋ฒ์ผ๋ก ์ฌ๋ฌ ๊ณณ์์ ์ฌ์ฌ์ฉํ ์ ์๋๋ก ํด ์ฃผ๋ ๊ธฐ๋ฅ ์กด์ฌ
๐ย Read SCSS Code โ ์ ์ฒ๋ฆฌ โ ์ปดํ์ผ โ ์ ์ญ CSS ๋ฒ๋ค ํ์ผ์ ์์ฑํด์ฃผ๋ ์ ์ฒ๋ฆฌ๊ธฐ ์ญํ
โย ์คํ์ผ ๊ฒน์น๋ ๋ฌธ์ ํด๊ฒฐ์ ์ํด ๊ณ์ธต ๊ตฌ์กฐ๋ฅผ ๋ง๋ค์ด CSS ์ฉ๋ ์ด๋ง์ด๋งํ๊ฒ ์ปค์ง๊ฒ ๋จ โฌ๏ธ
โฐย ์งํฅ์ (BEM, OOCSS, SMACSS)
: Block
, Element
, Modifier
๋ก ๊ตฌ๋ถํ์ฌ ํด๋์ค๋ช
์ ์์ฑํ๋ ๋ฐฉ๋ฒ (โ-
, __
์ผ๋ก ๊ตฌ๋ถ)
.header__navigationโ-navi-text {
color: red;
}
{/* Block: header */}
{/* Element: navigation */}
{/* Modifier: navi-text */}
Block
: ์ ์ฒด๋ฅผ ๊ฐ์ธ๊ณ ์๋ ๋ธ๋ญ ์์Element
: ๋ธ๋ญ์ด ํฌํจํ๊ณ ์๋ ํ ์กฐ๊ฐModifier
: ๋ธ๋ญ ๋๋ ์์์ ์์ฑ๐ย ํด๋์ค๋ช ์ ํ์ ์ฅํฉ โ ๋ถํ์ํ ๋งํฌ์ โฌ๏ธย โ ์ฌ์ฌ์ฉํ ๋๋ง๋ค ๋ชจ๋ UI ์ปดํฌ๋ํธ ๋ช ์์ ํ์ฅ ํ์
๐ย ๋ก์ง ์ ์ง์ ํ ์บก์ํ โย โ ๊ฐ๋ฐ์๋ค์ ์ ์ผํ ํด๋์ค๋ช ์ ํ์ ์์กดํ ์ ๋ฐ์โฆ
โ ์บก์ํ(encapsulation; ๊ฐ์ฒด ์์ฑ&ํ์๋ฅผ ํ๋๋ก ๋ฌถ๊ณ ์ค์ ๊ตฌํ ๋ด์ฉ ์ผ๋ถ๋ฅผ ์ธ๋ถ์ ๊ฐ์ถฐ ์๋ํ๋ ๊ฐ๋ )
CSS๋ฅผ ์ปดํฌ๋ํธ ์์ญ์ผ๋ก ๋ถ๋ฌ๋ค์ด๊ธฐ ์ํด ํ์
ex. Styled-Component
ํน์ง | ๐ | ๐ | |
---|---|---|---|
CSS | ๊ธฐ๋ณธ์ ์คํ์ผ๋ง ๋ฐฉ๋ฒ | - | - ์ผ๊ด๋ ํจํด ๊ฐ๊ธฐ ์ด๋ ค์ - !important ๋จ์ฉ |
SASS | ํ๋ก๊ทธ๋๋ฐ ๋ฐฉ๋ฒ๋ก ๋์ โ ์ปดํ์ผ๋ CSS ์์ฑ ์ ์ฒ๋ฆฌ๊ธฐ | - ๋ณ์/ํจ์/์์ ๊ฐ๋
ํ์ฉ โ ์ฌ์ฌ์ฉ - CSS ๊ตฌ์กฐํ | - ์ ์ฒ๋ฆฌ ๊ณผ์ ํ์ - ๋๋ฒ๊น ์ด๋ ค์ - ์ปดํ์ผํ CSS ํ์ผ ๊ฑฐ๋ํด์ง |
BEM | CSS ํด๋์ค๋ช ์์ฑ์ ์ผ๊ด๋ ํจํด์ ๊ฐ์ ํ๋ ๋ฐฉ๋ฒ๋ก | - ๋ค์ด๋ฐ์ผ๋ก ๋ฌธ์ ํด๊ฒฐ - ์ ์ฒ๋ฆฌ ๊ณผ์ ๋ถํ์ โ | - ์ ํ์ ์ด๋ฆ ์ฅํฉ - ํด๋์ค ๋ชฉ๋ก ๋ง์์ง |
Styled-Component | ์ปดํฌ๋ํธ ๊ธฐ๋ฐ์ผ๋ก CSS๋ฅผ ์์ฑํ ์ ์๊ฒ ๋์์ฃผ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ | - CSS๋ฅผ ์ปดํฌ๋ํธ ์์ผ๋ก ์บก์ํ - ๋ค์ด๋ฐ/์ต์ ํ ์ ๊ฒฝ ์ธ ํ์ โ | - ๋น ๋ฅธ ํ์ด์ง ๋ก๋ ๋ถ๋ฆฌ |
โฐย ๊ธฐ๋ฅ์ / ์ํ๋ฅผ ๊ฐ์ง ์ปดํฌ๋ํธ๋ค๋ก๋ถํฐ UI๋ฅผ ์์ ํ ๋ถ๋ฆฌํ์ฌ ์ฌ์ฉํ ์ ์๋ ๋จ์ํ ํจํด
โฐย HTML + JS + CSS๊น์ง ๋ฌถ์ด์ ํ๋์ JSํ์ผ ์์์ ์ปดํฌ๋ํธ ๋จ์๋ก ๊ฐ๋ฐํ ์ ์์
# install
# with npm
$ npm install --save styled-components
# with yarn
$ yarn add styled-components
{/* package.json์ ์ฝ๋ ์ถ๊ฐ ๊ถ์ฅ -> ์ฌ๋ฌ ๋ฒ์ Styled Components ์ค์น๋จ */}
{
"resolutions": {
"styled-components": "^5"
}
}
// Styled Components๋ฅผ ์ฌ์ฉํ ํ์ผ๋ก ๋ถ๋ฌ์ค๊ธฐ
import styled from 'styled-components'
styled.ํ๊ทธ์ข
๋ฅ
ํ ๋น โ ๋ฐฑํฑ ์์ ๊ธฐ์กด CSS ๋ฌธ๋ฒ๊ณผ ๋๊ฐ์ด ์คํ์ผ ์์ฑ ์์ฑconst ์ปดํฌ๋ํธ์ด๋ฆ = styled.ํ๊ทธ์ข
๋ฅ`
CSS์์ฑ1: ์์ฑ๊ฐ;
CSS์์ฑ2: ์์ฑ๊ฐ;
`;
const BlueButton = styled.button`
background-color: blue;
color: white;
// hover ์์ฑ์ &:hover์ผ๋ก ์ฒ๋ฆฌ
&:hover {
background: cornflowerblue;
color: white;
transition: 0.5s;
}
`;
const ์ปดํฌ๋ํธ์ด๋ฆ = styled(์ฌํ์ฉํ ์ปดํฌ๋ํธ)`
์ถ๊ฐํ CSS์์ฑ1: ์์ฑ๊ฐ;
์ถ๊ฐํ CSS์์ฑ2: ์์ฑ๊ฐ;
`;
const BigBlueButton = styled(BlueButton)`
padding: 10px;
margin-top: 10px;
`
const ์ปดํฌ๋ํธ์ด๋ฆ = styled.ํ๊ทธ์ข
๋ฅ`
CSS์์ฑ: ${(props) => ํจ์์ฝ๋}
`;
// 1) Props๋ก ์กฐ๊ฑด๋ถ ๋ ๋๋ง
const Button1 = styled.button`
background: ${(props) => (props.skyblue ? "skyblue" : "white")};
`;
// 2) Props ๊ฐ์ผ๋ก ๋ ๋๋ง
const Button2 = styled.button`
background: ${(props) => (props.color ? props.color : "white")};
`;
const Button3 = styled.button`
background: ${(props) => props.color || "white"};
`;
import { createGlobalStyle } from "styled-components";
const GlobalStyle = createGlobalStyle`
button {
padding: 5px;
margin: 2px;
border-radius: 5px;
}
`;
export default GlobalStyle;
import "./App.css";
import styled from "styled-components";
import GlobalStyle from "./GlobalStyle";
// ... ์์์ ์ ์ธ ๋์ฒด
function App() {
return (
<>
<BlueButton>Blue Button</BlueButton>
<br />
<BigBlueButton>Big Blue Button</BigBlueButton>
{/* ์ ์ญ ์คํ์ผ์ธ <GlobalStyle/> ์ปดํฌ๋ํธ๋ฅผ ์ต์์ ์ปดํฌ๋ํธ์์ ์ฌ์ฉ */}
<GlobalStyle />
<Button1>Button1</Button1>
<Button1 skyblue>Button1</Button1>
<br />
<Button2>Button2</Button2>
<Button2 color="orange">Button2</Button2>
<Button2 color="tomato">Button2</Button2>
<br />
<Button3>Button3</Button3>
<Button3 color="pink">Button3</Button3>
<Button3 color="turquoise">Button3</Button3>
</>
);
}
export default App;
CDD๋ฅผ ์ง์ํ๋ ๋๊ตฌ ์ค ํ๋๋ก
Component Explorer
(์ปดํฌ๋ํธ ํ์๊ธฐ) ๋ฑ์ฅ
์ปดํฌ๋ํธ ํ์๊ธฐ์ ๋ง์ UI ๊ฐ๋ฐ ๋๊ตฌ๊ฐ ์๋๋ฐ ๊ทธ ์ค ํ๋๊ฐStorybook
โ UI ๊ฐ๋ฐ ์ฆ, CDD๋ฅผ ํ๊ธฐ ์ํ ๋๊ตฌ
- ๊ฐ๊ฐ์ ์ปดํฌ๋ํธ๋ค์ ๋ฐ๋ก ๊ตฌ์ฑ โ ํ๋ฒ์ ํ๋์ ์ปดํฌ๋ํธ์์ ์์ ๊ฐ๋ฅ
- ์ฌ์ฌ์ฉ์ฑ ํ๋๋ฅผ ์ํด ์ปดํฌ๋ํธ๋ฅผ ๋ฌธ์ํํ๊ณ ,
์๋์ผ๋ก ์ปดํฌ๋ํธ๋ฅผ ์๊ฐํํ์ฌ ์๋ฎฌ๋ ์ด์ ํ ์ ์๋ ๋ค์ํ ํ ์คํธ ์ํ ํ์ธ ๊ฐ๋ฅ
๐ย ๋ฒ๊ทธ ์ฌ์ ๋ฐฉ์ง
๐ย ํ
์คํธ ๋ฐ ๊ฐ๋ฐ ์๋ ํฅ์
๐ย ์์กด์ฑ ๊ฑฑ์ ํ์ง ์๊ณ ์ ํ๋ฆฌ์ผ์ด์
๋น๋ ๊ฐ๋ฅ
โฐย Whyย UI๊ฐ๋ฐ๋๊ตฌ(Storybook) ์ฌ์ฉ?
: Storybook์ ๊ธฐ๋ณธ์ ์ผ๋ก ๋
๋ฆฝ์ ์ธ ๊ฐ๋ฐ ํ๊ฒฝ์์ ์คํ
โ ๊ฐ๋ฐ์๋ ์ ํ๋ฆฌ์ผ์ด์
์ ๋ค์ํ ์ํฉ์ ๊ตฌ์ ๋ฐ์ง ์๊ณ UI ์ปดํฌ๋ํธ๋ฅผ ์ง์ค์ ์ผ๋ก ๊ฐ๋ฐ ๊ฐ๋ฅ
โย ์ด๋ฒคํธ๋ฅผ ํตํด ์ํ๋ฅผ ๋ณ๊ฒฝํ๋ ๊ณผ์ ์ ๊ฑฐ์น์ง ์์๋ ์ํ ๋ณํ์ ๋ฐ๋ฅธ ์ปดํฌ๋ํธ ๋ณํ ํ์ธ ๊ฐ๋ฅ
# install
# package.json์ ๋ณด๊ณ ์ฌ์ฉ์ค์ธ FE ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋ง๋ Storybook ์ฌ์ฉํ๊ฒฝ์ ์์์ ๋ง๋ค์ด์ค
npx storybook init
# Storybook ์คํ
npm run storybook
// src/Title.js
import React from "react";
const Title = ({ title, textColor }) => (
<h1 style={{ color: textColor }}>{title}</h1>
);
export default Title;
// src/Title.stories.js
import Title from "./Title";
export default {
title: "Practice/Title", // ์ปดํฌ๋ํธ ์ด๋ฆ์ผ๋ก, Storybook์์ ์นดํ
๊ณ ๋ฆฌ๋ก ์ ์ฉ
component: Title, // ์ด๋ค ์ปดํฌ๋ํธ๋ฅผ ๊ฐ์ ธ์์ Story๋ก ๋ง๋ค ๊ฒ์ธ์ง ๋ช
์
argTypes: { // ์ปดํฌ๋ํธ์ ํ์ํ ์ ๋ฌ์ธ์ ์ข
๋ฅ&ํ์
title: { control: "text" },
textColor: { control: "text" },
},
};
// ํ
ํ๋ฆฟ ์์ฑ
// Title ์ปดํฌ๋ํธ๊ฐ args๋ฅผ ์ ๋ฌ๋ฐ์ props๋ก ๋ด๋ ค์ค
const Template = (args) => <Title {...args} />;
// Storybook์์ ํ์ธํ๊ณ ์ถ์ ์ปดํฌ๋ํธ ์์ฑ
// ํ
ํ๋ฆฟ์ ์ฌ์ฉํด์ Storybook์ ๋ฃ์ด์ค story๋ฅผ ๋ง๋ค์ด์ค
export const RedTitle = Template.bind({});
// ๋ง๋ค์ด์ค story์ ์ ๋ฌ์ธ์ ์์ฑ
RedTitle.args = {
title: "Red Title",
textColor: "red",
};
// Controls ์ง์ ์ค์ ๊ฐ๋ฅํ Story
export const StorybookTitle = (args) => {
return <Title {...args} />;
};
// ๋ ๋ค๋ฅธ Button ์์
// 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 }) => (
<StyledButton color={color} size={size}>
{text}
</StyledButton>
);
export default Button;
// src/Button.stores.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>;
DOM element ์ฃผ์๊ฐ์ ํ์ฉํด์ผ ํ๋ ๊ฒฝ์ฐ, DOM์ ๊ฑด๋๋ ค์ผ ํ ๋๊ฐ ์๊ธธ ์ ์์
์ด๋ฌํ ์์ธ์ ์ธ ์ํฉ์์useRef
๋ก DOM ๋ ธ๋, element, React ์ปดํฌ๋ํธ ์ฃผ์๊ฐ์ ์ฐธ์กฐํ ์ ์์.
// ์ด๋ค ์ฃผ์๊ฐ์ด๋ ๋ด์ ์ ์์.
const ์ฃผ์๊ฐ_๋ด๋_๊ทธ๋ฆ = useRef(์ฐธ์กฐ์๋ฃํ);
return(
<div>
<input ref={์ฃผ์๊ฐ_๋ด๋_๊ทธ๋ฆ} type="text" />
{/* ์ฃผ์๊ฐ_๋ด๋_๊ทธ๋ฆ ๋ณ์์ input DOM element ์ฃผ์๊ฐ ๋ด๊น */}
{/* ๋ค๋ฅธ ์ปดํฌ๋ํธ์์ input DOM element ํ์ฉ ๊ฐ๋ฅ */}
</div>
);
โย ์ด๋ฌํ ์ฃผ์๊ฐ์ ์ปดํฌ๋ํธ๊ฐ re-render๋๋ ๋ฐ๋์ง ์์.
// useRef ํ์ฉ ์์
function InputFocusButton() {
const inputEl = useRef(null);
const onButtonClick = () => {
inputEl.current.focus();
};
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}
์ค๋๋ ๋ฐ์์ ๋ฐฅ ๋จน๊ณ ์ค๋ ๋ฐ๋์ ์ ๋ ๊ณต๋ถ ์กฐ๊ธ ๋ฆ๊ฒ ์์..
ํ ๊ณต๋ถ๊ฐ ๋ ๋ง๋ค. ๋งจ๋ ๊ณํ๋ง ์ฅํฉํ๊ฒ ์ธ์๋๊ณ ๋ค ์งํค์ง๋ ๋ชปํจ;๐ซ
โ๏ธ ํ๋ก๊ทธ๋๋จธ์ค Lv.1๊ฐ์ ์ซ์๋ ์ซ์ด ๋ฌธ์ ํ๊ธฐ
โ๏ธ ํ์ ๋ฆฌ์กํธ JS ์์ฉ ํํธ ๊ฐ์ ๋ฃ๊ธฐ
โ๏ธ Coplit ๋ฐฐ์ด ๋ฌธ์ ๋ณต์ต
โ๏ธ Udemy ์๊ณ ๋ฆฌ์ฆ&์๋ฃ๊ตฌ์กฐ๋น ์คํ๊ธฐ๋ฒ ๊ฐ์ ๋ฃ๊ธฐ
โ๏ธ Udemy React_Section 1 ๊ฐ์ ๋ฃ๊ธฐ