- Published on
๐ ์ปฌ๋ฌ๋ฆฌ์คํธ ํ๋ก์ ํธ #2 ์ปฌ๋ฌ ์ ํ ๊ธฐ๋ฅ ๊ตฌํ
- ๊ธ์ด์ด
๐ ๋ชฉ์ฐจ
๐๐ป
- ๊ฐ๋ฐ ๋ฐฉ์
- ์ฝ๋ ์ง๊ธฐ ์ ์ค๋น
- ์ฝ๋ ์ง๊ธฐ ์์
- 3.1 ์ ํํ ์นธ ์๊น ์ถ๊ฐ ๐ฅ
- 3.2 ์ ํํ ์นธ ์๊น ์ญ์ (๋ค์ ํ์ดํธ๋ก) ๐ฅ
- 3.3 ์ ํํ ์นธ ์๊น ์ฌ์ ํ - ๋น ์นธ ์์๋๋ก ๐ฅ
1. ๊ฐ๋ฐ ๋ฐฉ์
BDD(Behavior Driven Development) ๐
feat.ํ์ ํ ๋ผ๋
ํ ๋ผ๋์ด ํ์ฌ์์ ํ์๋ BDD๋ฐฉ์์ ์๋ ค์ฃผ์ ์ ์ด๋ฒ ํ๋ก์ ํธ๋ BDD๋ฅผ ์ ์ฉํด๋ณด๊ธฐ๋ก ํ๋ค. BDD๋ ํ๋ ์ฃผ๋ ๊ฐ๋ฐ๋ก ๊ตฌํํด์ผ ํ ์ก์ ๋ค์ ์๋๋ฆฌ์ค๋ก ์์ฑํด์ ์์๋๋ก ๊ฐ๋ฐํ๋ ๋ฐฉ์์ด๋ค. ์ด ๋ฐฉ์์ ์ ํํ ์ด์ ๋ ์ฒ์ ํ ํ๋ก์ ํธ๋ฅผ ํ๋ ๊ณผ์ ์์
- ์ด๋ค ๊ธฐ๋ฅ์ ๊ตฌํํด์ผ ํ๋ ์ง ๊ธฐ์ตํ๊ธฐ ์ฝ๊ณ ,
- ์ ํํ ์ด๋ค ๊ธฐ๋ฅ์ ๊ตฌํํด์ผ ํ๋ ์ง ์ข ๋ ๋ช ํํ ์ ๋ฆฌํ ์ ์๊ณ ,
- ๊ตฌํ ์์๋ฅผ ์ก์ ๋ณ๋ก ๋๋์ด ์ ๋ฆฌํ๊ธฐ ์ข๊ธฐ ๋๋ฌธ์ด๋ค.
๋ ์ด๋ณด์ธ ๋ด๊ฐ ๋ณต์ตํ๊ธฐ์๋ ์ด ๋ฐฉ์์ด ์ฒด๊ณ์ ์ผ๋ก ๊ธฐ๋ก์ ๋ณผ ์ ์์ด ์ฐธ ์ข์๋ค.
2. ์ฝ๋ ์ง๊ธฐ ์ ์ค๋น
- BDD(Behavior Driven Development)๊ฐ๋ฐ์ ํ๊ธฐ์ํด featureํ์ผ์ ๋ง๋ค์๋ค. -> 'App.feture'ํ์ผ ์์ฑ
- featureํ์ผ์ ์ฃผ์์ ๋ฌ๊ธฐ ์ํด Cucumber (Gherkin) Full Support ์ต์คํ ์ ์ ์ค์นํ๋ค. ์๋์ ๊ฐ์ ์ด๋ฏธ์ง์ ์ต์คํ ์ ์ ์ค์นํ๋ฉด ๋๋ค!
- bun์ผ๋ก create-react-app์ ์คํํด ํ๋ก์ ํธ๋ฅผ ์ด๊ธฐํํ๋ค.
bun create react ํ๋ก์ ํธ์ด๋ฆ
- index.html์ head์ pico.css๋ฅผ ์ถ๊ฐ pico.css : classless css์! ๊ธฐ๋ณธ html์ ์์๊ฒ ๋ง๋ค์ด์ค๋ค.
<link rel="stylesheet" href="https://unpkg.com/@picocss/pico@latest/css/pico.min.css" />
3. ์ฝ๋ ์ง๊ธฐ ์์
App.tsx ์์ฑ์ ์์ํ๋ค.
๊ธฐ๋ฅ 0. ์์ ํ๋ ํธ ๋ง๋ค๊ธฐ
์์ ์ฝ๋๋ฅผ hex์ฝ๋๋ก ๋ฐฐ์ด์ ๋ง๋ค์ด map์ผ๋ก ๋ฒํผ์ ๋ฃ์ด์ค๋๋ค.
const WHITE_HEX = '#ffffff'
const pallete = [
'#e6e6e6',
'#d2b48c',
'#800000',
'#7e181e',
'#29f9ff',
'#a8a8f8',
'#5050f1',
'#ffb3ba',
'#ccff00',
'#ff7f50',
'#fa8072',
'#bada55',
]
;<div id="pallete" className="flex flex-row flex-wrap">
{/* 4๋ฒ - pallete๋ ๊ฐ์ด ์ฌ๋ฌ ๊ฐ์ด๋ฏ๋ก map์ ์ฌ์ฉํ๋ค. */}
{pallete.map((hex) => (
<button
onClick={() => addSelected(hex)}
className="roundButton"
style={{ backgroundColor: hex }}
>
{hex}
</button>
))}
</div>
๊ธฐ๋ฅ 1. ์ ํํ ์นธ ์๊น ์ถ๊ฐ ๐ฅ
์๋๋ฆฌ์ค
Scenario: ์ ํ๋ ์๊น ๋ชฉ๋ก์ด ๋ฐ์ค์ ๋ณด์ธ๋ค
# Given - before, ์ฃผ์ด์ง ์ํฉ
Given ์ ํ๋ ์๊น๋ค๋ก ๋ฌธ์ ๋ฅผ ๋ ๋ํ๊ณ
# When - ์ฌ์ฉ์์ ๋์ (ํด๋ฆญ, ํค๋ณด๋, ์คํฌ๋กค, ํญ, ์ง์ฐ๊ธฐ, ๋ณต๋ถ)
# Then - after, ๊ฒฐ๊ณผ
Then ์ ํ๋ ์๊น ๋ชฉ๋ก์ด ๋ณด์ธ๋ค
๋จผ์ ์์์ ์ ํ๋ ์์ ๋ฐฐ์ด์ ๋ง๋ค์ด๋ณด์๋ค.
const colors = ['red', 'green', 'blue']
๊ฐ์ด ์ฌ๋ฌ ๊ฐ์ธ ๋ฐฐ์ด์ map์ ์จ์ element ์ฌ๋ฌ ๊ฐ๋ก ๋ณํํ๋ค..!
{
colors.map((color) => (
<td
style={{
backgroundColor: color,
}}
>
{color}
</td>
))
}
์ฌ๋ฌ ๊ฐ์ ๊ฐ์ background์ ๋ฌธ์์ด๋ก ๋ฃ์ด์ฃผ์ด ์นธ์ ํด๋นํ๋ ์์ด ๋ณด์ด๋๋ก ํ๋ค.
<td
style={{
backgroundColor: color,
}}
>
- ์ ํํ ์๊น ๋ณด์ฌ์ฃผ๊ธฐ ๊ธฐ๋ฅ
function App() {
//1๋ฒ- ์ ํ๋ ์์ด ์ฌ๋ฌ๊ฐ์ธ ๋ฐฐ์ด์ ๋ง๋ ๋ค
const colors = ['red', 'green', 'blue']
return (
<div className="App">
<article>
<table className="selectedContainer flex flex-row">
<tr>
{/* 2๋ฒ - map์ ์จ์ ๋ฐฐ์ด์ ๊ฐ์ ํ๋ฉด์ ๋ฟ๋ ค์ค๋ค. */}
{colors.map((color) => (
<td
style={{
backgroundColor: color,
}}
>
{color}
</td>
))}
</tr>
</table>
<div id="pallete" className="flex flex-row flex-wrap"></div>
</article>
</div>
)
}
- ์๊น ์ ํํ๊ธฐ ๊ธฐ๋ฅ
function App() {
//์ ํ๋ ์์ด ์ฌ๋ฌ๊ฐ์ธ ๋ฐฐ์ด์ ๋ง๋ ๋ค - BOX_COUNT ๊ฐ์ธ ๋ฐฐ์ด์ ๋ง๋ค์ด์ ํฐ์์ผ๋ก ์ฑ์ด๋ค
const [colors, setColors] = React.useState(Array(BOX_COUNT).fill(WHITE_HEX)) //๋ฌธ์์ด
//addSelected ํจ์๋ฅผ ๋ง๋ค์ด
function addSelected(hex: string) {
setColors((old) => {
// findIndex๋ ๋ฐฐ์ด์ ๊ฐ ์ค์์ ์กฐ๊ฑด์ ๋ง๋ ๊ฐ์ด ์๋ ์ฒซ ๋ฒ์งธ index๋ฅผ ์ฐพ๋๋ค!
// hex ์ค์์ hex์ ์์ด white์ธ ์ฒซ๋ฒ์งธ index๋ฅผ ์ฐพ์๋ธ๋ค.
const firstWhiteIndex = old.findIndex((hex) => hex === WHITE_HEX)
if (firstWhiteIndex === -1) {
// ์ธ๋ฑ์ค๋ฅผ ๋ชป ์ฐพ์ผ๋ฉด! -1์ ๋ฐํ
return old // ์๋ colors๋ฅผ ๊ทธ๋๋ก ๋ฐํ... ๋ ์ฑ์ธ ํ์์นธ์ด ์์ผ๋ฏ๋ก!
}
// ์ด๋ฏธ ๋ฐ๋ ์์ old๋๋ก ๋๋๊ณ
const copy = [...old]
// ์ฐพ์๋ธ ์ฒซ ๋ฒ์งธ ์ธ๋ฑ์ค์ hex๊ฐ์ ๋ฃ๋๋ค.
copy[firstWhiteIndex] = hex
return copy // newState !!!
})
}
return (
<div className="App">
<article>
<table className="selectedContainer flex flex-row">
{/* map์ ์จ์ ๋ฐฐ์ด์ ๊ฐ์ ํ๋ฉด์ ๋ฟ๋ ค์ค๋ค. */}
<tr>
{colors.map((color) => (
//์คํ์ผ์ map์ผ๋ก ๋ฐ์์จ color ๋ฌธ์์ด ๊ฐ์ backgroundColor๋ก ์ง์ ํด์ค๋ค.
<td
style={{
backgroundColor: color,
}}
>
{color}
{/* text node */}
</td>
))}
</tr>
</table>
<div id="pallete" className="flex flex-row flex-wrap">
{/* pallete๋ ๊ฐ์ด ์ฌ๋ฌ ๊ฐ์ด๋ฏ๋ก map์ ์ฌ์ฉํ๋ค. */}
{pallete.map((hex) => (
<button
onClick={() => addSelected(hex)}
className="roundButton"
style={{ backgroundColor: hex }}
>
{hex}
</button>
))}
</div>
</article>
</div>
)
}
๊ธฐ๋ฅ 2. ์ ํํ ์นธ ์๊น ์ญ์ (๋ค์ ํ์ดํธ๋ก) ๐ฅ
์๋๋ฆฌ์ค
Scenario: ๋ฐ์ค์ ์นธ์ ํด๋ฆญํ๋ฉด ์ ํํ ์นธ์ด ํฐ์์ผ๋ก ๋ณํ๋ค
Given ์ ํ๋ ์๊น๋ค๋ก ๋ฌธ์ ๋ฅผ ๋ ๋ํ๊ณ
When ๋ฐ์ค์ ์นธ์ ํด๋ฆญํ๋ฉด
Then ์ ํํ ์นธ์ด ํฐ์์ผ๋ก ๋ณํ๋ค
ํน์ ์นธ์ ์ ํํ๋ฉด ๊ทธ ์นธ์ index๋ฅผ ๋ฐ์์ ์ ํ๋์ง ์์ ์นธ์ ์ด์ ์ ๊ฐ์ ๊ทธ๋๋ก ๋ฐ์์ค๊ณ ์ ํ๋ ์นธ์ WHITE_HEX๋ฅผ ๋ฃ์ด์ค๋ค. -> ๊ทธ๋๋ก์ธ ๊ฐ๊ณผ ์๋ก์ด ๊ฐ์ ํฉ์ณ ์ state๋ก ๋ง๋ค์ด์ค๋ค!
function deleteSelected(targetIndex: number) {
setColors((old) => {
const copy = [...old]
// targetIndex์ ๊ฐ์ ํฐ์ ํฅ์ค๋ฅผ ๋ฃ๋๋ค.
copy[targetIndex] = WHITE_HEX
return copy // newState !!!
})
}
๊ธฐ๋ฅ 3. ์ ํํ ์นธ ์๊น ์ฌ์ ํ - ๋น ์นธ ์์๋๋ก ๐ฅ
์๋๋ฆฌ์ค
Scenario: ์ฃผ๋ณด๊ฐ์ ๊ฐ์ ์
๋ ฅํ ์ ์๋ค
Given ๋น์ด์๋ ์นธ์ ๋ ๋ํ๊ณ
When ๊ฐ์ ์
๋ ฅํ๋ฉด
Then ๊ฐ์ด ์
๋ ฅ๋๋ค
์ฒ์์ ์์๋ก ๋ง๋ color๊ฐ์ ๊ฐ์ ๋ง์๋๋ก ๋ณ๊ฒฝํ ์ ์์ผ๋ฏ๋ก, useState๋ฅผ ์ฌ์ฉํด colors๋ฐฐ์ด์ ๋ฐ์ํ(reactive)์ํ๋ก ๋ง๋ ๋ค.
const [colors, setColors] = React.useState(Array(BOX_COUNT).fill(WHITE_HEX)) // ๋ฌธ์์ด
๊ทธ ๋ค์ ํ์์์ผ๋ก ์น ํด์ง ๋น ์นธ ์ค ์ฒซ๋ฒ์งธ์นธ๋ถํฐ ์์๋๋ก ๋ค์ ์์ ๋ฃ์ ์ ์๋๋ก addSelected ํจ์์ ์ฒซ๋ฒ์งธ ํ์์นธ index์ ์ ํ๋ hex๋ฅผ ๋ฃ๋ ์์ ์ถ๊ฐํ๋ค.
function addSelected(hex: string) {
setColors((old) => {
// findIndex๋ ๋ฐฐ์ด์ ๊ฐ ์ค์์ ์กฐ๊ฑด์ ๋ง๋ ๊ฐ์ด ์๋ ์ฒซ ๋ฒ์งธ index๋ฅผ ์ฐพ๋๋ค!
// hex ์ค์์ hex์ ์์ด white์ธ ์ฒซ๋ฒ์งธ index๋ฅผ ์ฐพ์๋ธ๋ค.
const firstWhiteIndex = old.findIndex((hex) => hex === WHITE_HEX)
if (firstWhiteIndex === -1) {
// ์ธ๋ฑ์ค๋ฅผ ๋ชป ์ฐพ์ผ๋ฉด! -1์ ๋ฐํ
return old // ์๋ colors๋ฅผ ๊ทธ๋๋ก ๋ฐํ... ๋ ์ฑ์ธ ํ์์นธ์ด ์์ผ๋ฏ๋ก!
}
// ์ด๋ฏธ ๋ฐ๋ ์์ old๋๋ก ๋๋๊ณ
const copy = [...old]
// ์ฐพ์๋ธ ์ฒซ ๋ฒ์งธ ์ธ๋ฑ์ค์ hex๊ฐ์ ๋ฃ๋๋ค.
copy[firstWhiteIndex] = hex
return copy // newState !!!
})
}