์ฒซ Team Project์ ์์!
๐ ํ ๋ช : Tabom Galagsin (FE - 4 / BE - 2)
- ํ๋ช ์ ํ์๋ค๊ณผ ์์์ ํ๋ฆ๋๋ก ์ง๊ฒ ๋์๋๋ฐ, ์ผ๋จ ์ชผ๋ฆฌ(ใใใ)๊ฐ ์ผ๋ณธ์ด๋ผ ํ๊ตญ์ด๋ก ์ชผ๋ฆฌ๊ฐ ๋ฌด์์ธ์ง ์ฐพ์๋ณด๋ '๊ฐ๋ฝ์ ' ์ด์๊ณ ๋ธ๋ผ์ง ๋ธ๋๋์๊ธฐ ๋๋ฌธ์ ์์ ๋ฐ๋ด(๋ธ๋ผ์ง์ด๋ก estรก bom = it's good)์ ๋ถ์ฌ '๋ฐ๋ด ๊ฐ๋ฝ์ ' ์ด ๋์๋ค. ๐
๐ ํ๋ก์ ํธ ๊ธฐ๊ฐ
2022. Aug. 16 ~ 2022. Aug. 26
(11 days)
๐ ํ๋ก์ ํธ ์ค๋ช
- ์ฌ๋ฆฌํผ๋ก ์ ๋ช ํ Havaianas์ ์น์ฌ์ดํธ๋ฅผ ํด๋ก ์ฝ๋ฉ ํ์๋ค. ์น์ฌ์ดํธ์ ์์์ด ๋ค์ํ๊ณ ํ๋ คํ๋ฉฐ ์๋ ํด ๋ณด๋ฉด ์ฌ๋ฏธ์๋ ๊ธฐ๋ฅ๋ค์ด ๋ง์ด ์๋ค. (ex. Carousel, Dropdown ๋ฑ๋ฑ)
โ๏ธ ํ๋ก์ ํธ๋ฅผ ํตํด React๋ฅผ ๋ฐฐ์ฐ๋ฉฐ ๋๋ ์
- ์ด๊ธฐ์ React๋ฅผ ์ ํ์ ๋,
html
๊ณผ ๋น์ทํ๊ฒ ์๊ฒจ์ ์ข์ํ์ง๋ง ์๋ก์ด ๊ธฐ๋ฅ์ ์ ํ๋ฉด์ ๊ณง vanilla JS๊ฐ ๋ ์ฐ๊ธฐ ํธํ๋ค๊ณ ๋๊ผ๋ค. ํ์ง๋ง ํ๋ก์ ํธ๋ฅผ ์งํํ๋ฉด์ ์ ์ React๋ง ์ฌ์ฉํ๋ ํ์๊ฐ ๋ง์์ง๋ ์์ฐ์ค๋ฝ๊ฒ React์ ์ ์์ด ๋๊ธฐ ์์ํ๊ณ ์ง๊ธ์ React๊ฐ Vanilla JS๋ณด๋ค ์ฌ์ฉํ๊ธฐ ๋ ์ฌ์ฐ๋ฉฐ ๋ฐฐ์ฐ๋ฉด ๋ฐฐ์ธ ์๋ก ๊ตฌํ ํ ์ ์๋ ๊ธฐ๋ฅ์ด ๋ฌด๊ถ๋ฌด์งํ๋ค๊ณ ์๊ฐํ๋ค. ์์ง๋ ๊ฐ ๊ธธ์ ๋ฉ์ง๋ง ๊ทธ๋๋ ์ ์ React์ ์นํด์ง๋ ์ค์ด๋ค. React ๊ณ ์๊ฐ ๋๋ ๊ทธ๋ ๊น์ง.. ๐โโ๏ธ
โ๏ธ ํผ์ ํ ๋์ ํ์ผ๋ก ์งํํ ๋ ๋ค๋ฅธ ์ ๋ฐ ํ์์ ๋ด๊ฐ ๋งก์ ์ญํ
ํ๋ก์ ํธ ์ด์ ์ ํผ์์ Instagram์ clone coding ํ์๋ค. ํผ์์ ํ ๋๋ git์ ํตํด mergeํ ํ์๋ ์๊ณ conflict๋ ์์ด์ ํผ์ ๋ ๊ณ ๋ก ์ฑ์ ์ง๋ ๋๋์ด์๋ค. ํ์ง๋ง ํ์๋ค๊ณผ ํจ๊ป ์งํ์ ํด๋ณด๋ ๋ด๊ฐ ๋งก์ ์ญํ ์ ์ ๊ตฌํํด์ merge ํ์ ๋ ๋ฌธ์ ๊ฐ ์๊ธฐ์ง ์๋๋ก ํด์ผ ํ๋ค. ๋์ ๋ก์ปฌ ์ปดํจํฐ์์๋ ์๋ฌด๋ฐ ๋ฌธ์ ๊ฐ ์์๋ Sass๊ฐ nesting์ด ์๋ชป๋์ด ๋ค๋ฅธ ํ์๋ค ๊ฐ์์ ๋ก์ปฌ๋ก mergeํ์ ๋, ์์๋ผ์ธ ์ก์๋์๊ฒ ์ฌ๊ธฐ์ ๊ธฐ ๋ค ํ์ด๋๊ฐ๊ณ ๋๊ธฐ๋ค์๊ฒ ์ฒดํฌ๋นํ ๋ปํ๋ค.. ๐ ์ด๋ฒ ์ค์๋ฅผ ํตํด์ ์๋ฌด๋ฆฌ ๋ด file์ ๋ฌธ์ ๊ฐ ์์ด๋ ๋ค๋ฅธ ํ์๋ค๊ณผ์ ํ์ ์ ์ํด์๋ nesting์ด ์ ๋ง ์ ๋ง ์ค์ํ๋ค๋ ๊ฑธ ๊นจ๋ฌ์๋ค!
havaianas๋ ๋ด๊ฐ ์ ์ ํ ์น์ฌ์ดํธ์ฌ์ PM์ ์ญํ ์ ๋งก์๋ค. 1์ฐจ sprint์์ ์ด๊ธฐ์ ํ๋ก์ ํธ๋ฅผ ์ด๋ป๊ฒ ์งํํ ๊ฒ์ธ์ง ๊ทธ๋ฆฌ๊ณ ๋๊ฐ ์ด๋ค ticket์ ๋ฐ์๊ฐ ๊ฒ์ธ์ง๋ฅผ ์ ํ๊ณ ๋งค์ผ ์์นจ standup meeting์ ํด์ ์ด์ ํ ์ผ, ์ค๋ ํ ์ผ, ๊ทธ๋ฆฌ๊ณ blocker๋ฅผ ์๋ก ๊ณต์ ํ๋ฉด์ ์งํ์ฌํญ์ ํ์ธํ๋ค. ์ค์ผ์ฅด ๋ฐ ticket๊ด๋ฆฌ๋
Trello
๋ฅผ ์ด์ฉํด ์งํํ์๋ค.
- ํ๋ก ํธ์ํธ ํ ์์์๋ Navbar ๋ฐ ์ ํ ์์ธํ์ด์ง๋ฅผ ๋งก์๋ค.
โ๏ธ๊ธฐ์ตํ๊ณ ์ถ์ ์ฝ๋
- ํ๋ญํ ์ฝ๋ : Dropdown
jsx
<div
onMouseEnter={() => setIsMouseEnter(!isMouseEnter)}
onMouseLeave={() => setIsMouseEnter(!isMouseEnter)}
>
<i className="fa-solid fa-bars navIcon menu" />
{isMouseEnter && <Dropdown />}
</div>
Dropdown ๋ฉ๋ด๋ฅผ ๊ตฌํํ ์ฝ๋! ๋ง์ฐ์ค๊ฐ menu icon์ hoverํ ๋ dropdown์ด ์๊ฒผ์ง๋ง ๋ง์ฐ์ค๊ฐ ๋ ๋์๋ง์ dropdown์ด ์ฌ๋ผ์ก๋๋ฐ ์ด๊ฑธ ์ด๋ป๊ฒ ๊ณ ์น์ง ํ๊ณ ๊ณ ๋ฏผํ๋ค๊ฐ ๋ฐ๊ฒฌํ ๋ฐฉ๋ฒ! Icon๊ณผ dropdown์ ํ
<div>
๋ก ๋ฌถ์ด ์ด div์mouseEnter``mouseLeave
์ด๋ฒคํธ๋ฅผ ์ฃผ๋ฉด ๋๋ค. ์๋ก์ด ๋ฐฉ๋ฒ์ ์๊ฒ ๋์ ํ๋ญํ ์ฝ๋๋ก ์ ์ !
- ์ ๊ธฐํ๋ ์ฝ๋ : useState๋ฅผ ์ด์ฉํ scroll event
jsx
const [scroll, setScroll] = useState(0);
useEffect(() => {
const handleScroll = e => setScroll(e.currentTarget.scrollY);
window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
});
return (
<nav
className="nav"
style={scroll < 32 ? { top: 0 - scroll } : { top: -32 }}
>
Navbar๊ฐ ์ด 2๊ฐ์๋๋ฐ scroll down ํ์ ๋ ์๋์ navbar๋ง ์๋จ์ fix๋๋ ๊ฒ์ ๊ตฌํ. ์ฒ์ ์จ๋ณด๋ scroll event์ฌ์ ์ด๋ป๊ฒ ์ฌ์ฉํด์ผํ๋์ง ์ ๋ชฐ๋์๊ณ ํค๋ฉ์๋ค. ์คํฌ๋กค ์์ฒด๋ฅผ useState์ ๋ฃ์ด์ฃผ์ด ๊ธธ์ด๋ฅผ ๊ธฐ์ค์ผ๋ก postion:fixed ๋ ์๋จ navbar์ top์ ๋ณ๊ฒฝํด ์ฃผ์ด ๋ณด์ด์ง ์๊ฒ ํ๋ ๋ฐฉ๋ฒ์ ์ฌ์ฉํ๋ค.
- ๊ฐ์ฅ ์ด๋ ค์ ๋ ์ฝ๋ : useInterval(custom)์ ์ด์ฉํ carousel ๊ตฌํ
์ฝ๋๋ณด๊ธฐ
jsx
import { useState } from 'react';
import useInterval from '../../../useInterval';
import './NavTopContainer.scss';
function NavTopContainer() {
const [currentPosition, setCurrentPosition] = useState(-2);
const [transitionTime, setTransitionTime] = useState(0.5);
const [resetAuto, setResetAuto] = useState(1);
const currentData = [...CAROUSEL_DATA, ...CAROUSEL_DATA, ...CAROUSEL_DATA];
const clickButton = event => {
if (
event.target.className !== 'fa-solid fa-chevron-left arrow flipflopCursor'
) {
if (currentPosition > 2) {
setTransitionTime(0);
setCurrentPosition(-2);
setTimeout(() => {
setTransitionTime(0.5);
setCurrentPosition(-1);
}, 0);
} else {
setCurrentPosition(currentPosition + 1);
}
} else {
if (currentPosition < -2) {
setTransitionTime(0);
setCurrentPosition(2);
setTimeout(() => {
setTransitionTime(0.5);
setCurrentPosition(1);
}, 0);
} else {
setCurrentPosition(currentPosition - 1);
}
}
};
useInterval(() => {
if (currentPosition > 2) {
setTransitionTime(0);
setCurrentPosition(-2);
setTimeout(() => {
setTransitionTime(() => 0.5);
setCurrentPosition(() => -1);
}, 100);
} else {
setCurrentPosition(prev => prev + 1);
}
}, 3000 + resetAuto);
return (
<div className="navTopContainer">
<div className="carousel">
<div className="leftCover">
<i
className="fa-solid fa-chevron-left arrow flipflopCursor"
onClick={event => {
clickButton(event);
setResetAuto(prev => prev * -1);
}}
/>
</div>
<div className="slider">
<div
className="sliderInner"
style={{
transform: `translate(${(currentPosition * -100) / 15}%)`,
transition: `all ${transitionTime}s`,
}}
>
{currentData.map((data, index) => {
return (
<section className="list" key={index}>
{data.item}
</section>
);
})}
</div>
</div>
<div className="rightCover">
<i
className="fa-solid fa-chevron-right arrow flipflopCursor"
onClick={event => {
clickButton(event);
setResetAuto(prev => prev * -1);
}}
/>
</div>
</div>
</div>
);
}
export default NavTopContainer;
const CAROUSEL_DATA = [
{ item: '์ํ์ธ๋ฃจ์๋ก ํน๊ธ๋ฐฐ์ก' },
{ item: '๋ฐฐ์ก ์ต์
๋ฐ ์๋น์ค๋ฅผ ํ์ธํ๋ ค๋ฉด ์ฐํธ๋ฒํธ๋ฅผ ์
๋ ฅํด ํ์ธํด ๋ณด์ธ์.' },
{ item: '์ํ์ธ๋ฃจ์๋ก ํน๊ธ๋ฐฐ์ก' },
{ item: '๋ฐฐ์ก ์ต์
๋ฐ ์๋น์ค๋ฅผ ํ์ธํ๋ ค๋ฉด ์ฐํธ๋ฒํธ๋ฅผ ์
๋ ฅํด ํ์ธํด ๋ณด์ธ์.' },
{ item: '๋ฐ๋ด ๊ฐ๋ฝ์ ํ์ดํ
!' },
];
์ด ์ฝ๋๋ ์ง์ง ๋๊ณ ๋๊ณ 100% ์ดํดํ ๋ ๊น์ง ๊ณต๋ถํด์ผํ๋ ์ฝ๋! ์ต์๋จ Navbar์ ์ฌ์ฉ๋ carousel์ ์๋ํ๋ ์ด๊ฐ ๋๋ฉด์ ๋์์ ํ์ดํ๋ฅผ ๋๋ฅด๋ฉด ์ด๋ํ๋ ๊ธฐ๋ฅ์ ํ๋ค ( + ๋ฌดํ์ฌ๋ผ์ด๋๊น์ง!) ์ฒ์ carousel์ ๊ฒจ์ฐ๊ฒจ์ฐ ๋ง๋ค์์ง๋ง ํ์ดํ๋ฅผ ํด๋ฆญํ๋ฉด ๋ฒํผ๊ธฐ๋ฅ๊ณผ ์๋ํ๋ ์ด ๊ธฐ๋ฅ์ด ๊ฒน์น๋ฉด์ ๋์นธ์ด ํ๋ฒ์ ๋์ด๊ฐ๋ฒ๋ ธ๋ค. ์ด๋ฅผ ๋ฐฉ์งํ๊ธฐ ์ํด custom์ผ๋ก useInterval์ ์ฌ์ฉํด์ ๋์ด๊ฐ๋ ์๋๊ฐ resetํ๊ฒ ๋ง๋ค์ด ๋ฒํผ๊ธฐ๋ฅ์ ์คํํด๋ ๋์ฅ์ฉ ๋์ด๊ฐ์ง ์๊ฒ ๊ตฌํ!
์๋ก ์๊ฒ ๋ ์ฝ๋ : state ์ด๊ธฐ ๊ฐ ์์ด re-rendering ์์ผ์ฃผ๋ ์ฝ๋. ํ์ํ ๋ ์ฌ์ฉํ๋ฉด ์ ์ฉํ ๊ฒ ๊ฐ๋ค.
const setSelectColour = useState(0)[1];
๐ก ํ๋ก์ ํธ๋ฅผ ๋ง์น๊ณ ๋๋ ์
๊ฐ๋ฐ๊ณต๋ถ๋ฅผ ์์ํ ์ดํ๋ก ํด๋ณด๋ ์ฒซ ํ ํ๋ก์ ํธ!! ๊ธฐ๋๋ฐ ๊ฑฑ์ ๋ฐ์ผ๋ก ์งํํ๋๋ฐ ์ข์ ํ์๋ค๊ณผ ๋ถ์๊ธฐ๋ก ์ผ์ฐ์ผ์ฐ ํ๋ฉฐ ์น์ฌ์ดํธ๋ฅผ ์์ฑ ํด ๋๊ฐ๊ณ ๊ทธ ๊ณผ์ ์์์ ์๋ก ์ํตํ๋ ๋ฐฉ๋ฒ์ ๋ํด ๋ ์๊ฐํด ๋ณด๋ ๊ณ๊ธฐ๊ฐ ๋์๋ค. ๋ด ๋จธ๋ฆฟ์์ ์๋ ์๊ฐ์ ์๋๋ฐฉ์ด ์ดํดํ ์ ์๊ฒ ์ค๋ช ํ๋๊ฑด ์๊ฐ๋ณด๋ค ์ด๋ ค์ ๊ณ ๋ ๋ง์ ์ฐ์ต์ด ํ์ ํ ๊ฒ๊ฐ๋ค.
- ํ์ ๋ถ์๊ธฐ๊ฐ ํ๋ก์ ํธ ์ ๋ฐ์ ์ธ ํ๋ฆ์ ์ฃผ๋ํ๋ ๊ฒ ๊ฐ๋ค. ์๋ฌด๋ฆฌ ํ๋ค๊ณ ์ง์ณ๋ ๊ธ์ ์ ์ผ๋ก ํจ๊ป ์ด๊ฒจ๋ด์๋ผ๋ ๋ง์ธ๋๋ฅผ ๊ฐ์ง๊ณ ์ํ๋ ๊ฒ์ด ์ค์ํ ๊ฒ ๊ฐ๋ค.
- ์ด๊ธฐ์ ๊ณํ์ ์ด๋ป๊ฒ ์ง์ผํ ์ง ๋ชฐ๋ผ์ 1์ฃผ์ฐจ๋ A,B,C๊ธฐ๋ฅ์ ๊ตฌํํ๊ณ 2์ฃผ์ฐจ๋ D,E,F๋ฅผ ๊ตฌํํ์๊ณ ์ ํ๋๋ฐ ์ด๊ฒ ๋ณด๋ค๋ 1์ฃผ์ฐจ๋ถํฐ A,B,C,D,E,F์ ๋ ์ด์์์ ์์ฑ์ํค๊ณ ๊ธฐ๋ฅ์ ํ๋ํ๋ ์ถ๊ฐํ๋ ๋ฐฉ์์ด ๋ ๊ด์ฐฎ์ ๊ฒ ๊ฐ๋ค๋ ์๊ฐ์ด ๋ค์๋ค. ๊ทธ๋ฆฌ๊ณ ๋ณธ๊ฒฉ ํ๋ก์ ํธ๋ฅผ ์์ํ๊ธฐ ์ ์ ํ๋ก ํธ์๋์ ๋ฐฑ์๋๊ฐ ๊ฐ์ด ์ด๋ป๊ฒ ์งํํ ๊ฒ์ธ์ง ํฐ ์ง๋๋ฅผ ๊ทธ๋ ค๋ณด๋ ๊ฒ๋ ํ๋ก์ ํธ๋ฅผ ์งํํ๋๊ฒ์ ์์ด ๊ต์ฅํ ๋์์ด ๋ ๊ฒ ๊ฐ๋ค.
- ๋ค์ ํ๋ก์ ํธ์์๋ ๋ ์ ๊ทน์ ์ผ๋ก ์ํตํ๊ณ ์๊ฐ๊ด๋ฆฌ์ ๊ธฐ๋ก์ ์ ํด์ ๋ ์ฒด๊ณํ๋ ํ๋ก์ ํธ๋ฅผ ์งํํ๊ณ ์ถ๋ค.
๐ฉโ๐ป ์์ผ๋ก ์ด๋ค ๊ฐ๋ฐ์๊ฐ ๋๊ณ ์ถ์์ง์ ๋ํ ์๊ฐ
- ๋๋ ๊พธ์คํ ๊ธฐ๋กํ๋๊ฒ์ ์ฝํ ํธ์ด๋ค. ํ์ง๋ง ๊ณ์ ์ฑ์ฅํ๋ ๊ฐ๋ฐ์๊ฐ ๋๊ธฐ ์ํด์๋ ๊ธฐ๋ก์ ๊พธ์คํ ์ ํด์ผํ ๊ฒ ๊ฐ๋ค. Notion์ด๋ Trello๋ฅผ ์ ํ์ฉํด ๊ธฐ๋ก์ ์ํ๊ณ ๋ ์ค์ค๋ก๋ฅผ ๊ฐ๊ด์ ์ผ๋ก ๋ถ์ํ์ฌ ์ฑ์ฅํ ์ ์๋ ๊ฐ๋ฐ์๊ฐ ๋๊ณ ์ถ๋ค.
๐ฝ ์์ฐ ์์
๐ KPT
Keep : ํ์ ๋ถ์๊ธฐ๊ฐ ์ข์์ ์ฌ๋ฏธ์๊ฒ ํ๋ก์ ํธ๋ฅผ ์งํ ํ๋ค. ๋ค์ ํ๋ก์ ํธ๋ฅผ ์งํ ํ ๋๋ ์ข์ ๋ถ์๊ธฐ๋ฅผ ์ ์งํ ์ ์๋๋ก ๋ ธ๋ ฅํด์ผ๊ฒ ๋ค!
Problem : ๋ง์ ์ฌ๋๋ค์ด ๊ฐ๋ฐ์ ํ ๋ ์ํต์ด ์ ๋ง ์ค์ํ๋ค๊ณ ๊ฐ์กฐํ๋๋ฐ ์ด๋ฒ ํ๋ก์ ํธ๋ฅผ ํ๋ฉด์ ์ ๋ง ์ค์ํ๋ค๋ ๊ฒ์ ๋๋ ์ ์์๋ค. ๋๋ ์ ํ์์ธํ์ด์ง๋ฅผ ๋งก์๋๋ฐ ์ด๊ธฐ ๋ฏธํ ๋, ์ด๋ค ๋ฐ์ดํฐ๊ฐ ๋ค์ด๊ฐ๋์ง ํ์๋ฅผ ํ๊ณ ๋ ์ด์์์ ์งํํ๋ค. ์งํํ๋ค๊ฐ BE์ชฝ์ ํ๋ฒ ์ฒดํฌ ํด ๋ดค๋๋ ์์ํ, ์ฌ๊ณ ๋ฑ ๊ธฐ์กด ๊ฒฐ์ ๋ ์ฌํญ๊ณผ ๋น๊ตํ๋ฉด ์๋ก ๋ค์ด๊ฐ๋ ๋ฐ์ดํฐ๋ค์ด ์์๋ค. ๋ ์ด์์์ ๋ค ๋ง๋ค๊ณ ํ์ธ ํ๋ค๋ฉด ํน์ ์ด๊ธฐ ๋ฏธํ ๋ง ์๊ฐํ๊ณ ์์ ํ์ธํ์ง ์์๋ค๋ฉด double job์ ํด์ผ ํ์ ๊ฒ์ด๋ค. ์๋ก์ด ์ผ์ ์งํ ํ ๋ ๋ง๋ค ํญ์ ํ๋ฒ ๋ ์ฒดํฌํ๋ ์ต๊ด์ ๊ธธ๋ฌ์ผ ๊ฒ ๋ค.
Try : ํ๋ก์ ํธ๋ 2๋ฒ์ sprint๋ก ๋๋์ด์ ธ ์์๊ณ ๊ฐ sprint๋ง๋ค ์ ํด์ง ticket์ด ์์๋๋ฐ ์๋ฌด๋๋ ์ฒ์ํด๋ณด๋ ํ๋ก์ ํธ๋ผ ์ด๋ ค์ด ์ ์ด ์์๊ณ ์ด ์ฌํญ์ ๋นจ๋ฆฌ ํ์๋ค์๊ฒ ๊ณต์ ํ์ฌ ๊ธฐ๊ฐ ๋ด์ ๋งก์ ์ญํ ์ ์์ฑํ ์ ์๊ฒ ํ์๋ค.