약 2주(?)의 기간 동안 H&M clone coding에 대한 회고록을 작성하려고 한다 :)
월요일이 대체 공휴일이라 기간이 더욱 짧아졌지만, 그만큼 팀원들과 열심히 멘탈도 털려보고 많은 공부(?)도 해보았으니 여러모로 득이 많은 프로젝트면서 애증의 프로젝트이기도 했다. 😎 🤓 🥸
H&M 사이트와 비교해서 보시면 매우 부끄럽습니다.. 🙈🙈🙈🙈
👉 피 땀 눈물이 들어간 H&W 깃허브
👩🏻💻 프로젝트 기간
🔎 팀명
🐥 팀원 소개
FrontEnd :
신혜리(🙋🏻♀️ Me) - Nav, Main, Footer, 페이지 구현
서동혁 - Login, SignUp, 장바구니 페이지 구현
조윤희 - 상품 리스트 페이지 구현
전태양 - 상품 상세페이지, 즐겨찾기 페이지 구현
BackEnd
이다빈 - 상품API, 카테고리API, 장바구니 API
김민호 - 유저API, 즐겨찾기 API
공통 - 데이터 모델링, 데이터추가, 데이터 구축
🛠 적용기술
로그인 / 쇼핑백은 조건부 렌더링을 이용해 hover시 팝업이 나타나게 만들었다!
class Nav extends React.Component {
constructor(props) {
super(props);
this.state = {
categoryList: {},
isUserMenuLogin: false,
isUserMenuShoppingBag: false,
isDepthMenu: false,
};
}
componentDidMount() {
{/* fetch */}
}
userMenuHoverChange = () => {
this.setState({
isUserMenuLogin: !this.state.isUserMenuLogin,
});
};
userMenuHoverShopping = () => {
this.setState({
isUserMenuShoppingBag: !this.state.isUserMenuShoppingBag,
});
};
render() {
const { isUserMenuLogin, isUserMenuShoppingBag } = this.state;
return (
<nav className="navContainer">
<div className="menuServices">
{/* 1차 Depth*/}
<ul className="menuServicesList">
{SERVICES_LIST.map((el, idx) => (
<li key={idx}>{el}</li>
))}
</ul>
{/* 해당 기술 구현 부분 */}
<ul className="userMenu">
{/* 첫번째, 즉 로그인 menu를 onMouseEnter 이벤트 발생 시 조건부 렌더링에 의해
Login 컴포넌트가 나타나거나 사라지게 구현했다.(쇼핑백도 같은 원리이다.)
*/}
<li
onMouseEnter={this.userMenuHoverChange}
onMouseLeave={this.userMenuHoverChange}
>
<i className="fas fa-user" />
로그인
{isUserMenuLogin && <Login />}
</li>
<li>
<Link to="/favorites" className="Link">
<i className="fas fa-heart" />
즐겨찾기
</Link>
</li>
<li
onMouseEnter={this.userMenuHoverShopping}
onMouseLeave={this.userMenuHoverShopping}
>
<Link to="/basket" className="Link">
<i className="fas fa-shopping-bag" />
쇼핑백
</Link>
{isUserMenuShoppingBag && <ShoppingBag />}
</li>
</ul>
</div>
}
우리가 흔히 부르는 슬라이드는 외국에선 캐러셀이라고 한다. nav list 다음으로 너무 까다로워서 두 번째로 넣었다. (밑으로 갈수록 개인적으로 너무x100 어렵다고 생각한 코드 순서..^^...) 개인적으로.. 많이 아쉬움이 남는 기능 구현 중에 하나이다..ㅜㅜ... 조금만 더 시간이 있었더라면...
(신상품 대신 너무 너무 멋있고 예쁘신 멘토님들 슬랙 프로필로 대체!! 초상권은...^^...)
class Main extends React.Component {
constructor(props) {
super(props);
this.state = {
imgSpot: 0,
};
}
imgCaroselBtn = imgSpotNumber => {
{/* 구조분해 할당은 숨쉬듯이!! */}
const { trendList } = this.state;
{/* 이 코드가 매우 아쉬운데, 동적으로 바뀌는게 아닌, 10개 이상의 data가 들어가면 많이 아파한다... */}
if (trendList.length - 5 <= imgSpotNumber) imgSpotNumber = 0;
if (imgSpotNumber < 0) imgSpotNumber = trendList.length - 6;
this.setState({
imgSpot: imgSpotNumber,
});
};
render(){
const { trendList, imgSpot } = this.state;
{/* 상수로 지정된 값을 활용해 넘어가는듯한 착각을 나타내주기 위해 작성 */}
const CONTENT_WIDTH = 120.131;
<div className="trendCarousel">
{/* transform으로 부드럽게 넘어가는듯한 효과를 주기 위함 */}
<ul className="trendImgContainer"
style={{
transform: `translateX(
${imgSpot * -CONTENT_WIDTH}px`,
}}
>
{/* 멘토님들을 mock data로 만든 뒤 컴포넌트화 하여 map 함수를 사용해 구현했다. */}
{trendList.map((trend, idx) => {
return (
<Trend
key={idx}
category={trend.category}
categoryTitle={trend.categoryTitle}
img={trend.img}
/>
);
})}
</ul>
{/* left는 Previous button, right는 next button으로 생각하면된다. */}
<div className="trendBtn">
<i className="fas fa-arrow-left"
onClick={() => this.imgCaroselBtn(imgSpot - 1)}
></i>
<i className="fas fa-arrow-right"
onClick={() => this.imgCaroselBtn(imgSpot + 1)}
></i>
</div>
</div>
}
nav가 다시 나와서 읭? 하시겠지만.. 정말 극강의 난이도를 자랑하는(제 기준..🙄) 기술 구현이다. 바로 3차 Depth(= 카테고리가 3개가 있다고 생각하면 된다.)인데, 3중 map을 돌리는것과 여성의 상의의 셔츠를 들어간다거나 남성의 하의의 데님을 들어가는 멘붕오는 기술 구현이다. 덕분에 react를 포기할까?.. 수천 번 고민했었다..
//1차 Depth
class Depth extends Component {
constructor(props) {
super(props);
this.state = {
categoryList: {},
dropDown: false,
dropDownList: 0,
};
}
{/* 호버시 id값에 따라 나타나게끔 구현해주었다.(즉 id값이 여성은 1, 남성은 2라는 소리) */}
dropDownHover = id => {
this.setState({
dropDown: !this.state.dropDown,
dropDownList: id,
});
};
render() {
const { categoryList, dropDown, dropDownList } = this.state;
return (
<div className="menuContents">
<ul className="menu">
{/* ?.은 옵셔널 체이닝이다(js 개념정리에 쓰여있다 ^^7) */}
{categoryList.category_list?.map(category => {
return (
{/* 2차 depth를 컴포넌트화 하여 props로 값을 넘겨준다. */}
<Category
key={category.id}
idx={category.id}
category={category}
dropDown={dropDown}
dropDownList={dropDownList}
dropDownHover={this.dropDownHover}
/>
);
})}
{/* 여성, 남성, 아동을 제외한 나머지 카테고리는 mock data로 만들어주었다.*/}
{MAIN_MENU_LIST.map((mainMenu, idx) => {
return (
<li key={idx} className="mockDataDepth">
{mainMenu}
</li>
);
})}
</ul>
</div>
);
}
}
//2차 뎁스
class Category extends Component {
render() {
const { category, idx, dropDown, dropDownList, dropDownHover } = this.props;
const { name, main_category } = category;
{/* dropDownList가 idx 값과 완전히 같다면 list를 보이게한다. 즉 여성이면 여성에 관한 카테고리를 보여준다는것 */}
const isDropDownActive = dropDownList === idx;
return (
<li
className="depthMenu"
onMouseEnter={() => dropDownHover(category.id)}
onMouseLeave={() => dropDownHover(category.id)}
>
{name}
{/* isDropDownActive과 twoDepth가 같다면 렌더링 해주는데,
name이 남성이면 두 번째 카테고리를 그것이 아니라면 여성을 나타나게했다. (리팩토링 해야할 부분)
*/}
{isDropDownActive && (
<ul className="twoDepth">
{main_category.map(
subCategory =>
dropDown && (
<SubCategory
key={subCategory.id}
subCategory={subCategory}
gender={name === '남성' ? 2 : 1}
/>
)
)}
</ul>
)}
</li>
);
}
}
//3차 뎁스
class SubCategory extends Component {
render() {
const { subCategory, gender } = this.props;
return (
<li>
{subCategory.name}
<ul className="threeDepth">
{/* 3차 뎁스를 끝으로 3중 map을 돌린다면 아래와 같다. 1,2,3차를 컴포넌트화를 하니 코드가 깨끗해졌다. */}
{subCategory.sub_category.map(threeDepth => {
return (
{/* 동적 라우팅을 담당하는 코드*/}
<li className="depthList">
<Link
to={{
pathname: '/itemlist',
state: {
filterlist: `sub=${threeDepth.id}&sort=ascPrice&gender=${gender}`,
},
}}
key={threeDepth.id}
className="depthLink"
>
{threeDepth.name}
</Link>
</li>
);
})}
</ul>
</li>
);
}
}
첫 번째로는 트렐로 활용이다. 어려운 듯 하면서 쉬운 툴인데, 기능 구현에 바빠 제대로 사용하지 못했던 것이 못내 아쉬웠다.
두 번째로는 '나는 못 해..😥' 라는 생각에 빠져만 있었던 마인드가 너무 아쉬웠다.
세 번째로는 같이 했던 분들과 다시 못한다는 아쉬움이 있다. 한 분 한 분이 너무 좋았고, 팀 분위기도 너무 좋았기 때문에 2차 프로젝트도 같이 하고 싶단 생각이 들었기 때문이다! 😁 (저만 그런거 아니죠..?)
당연히 팀원 분들과의 합이다. 서로 모르는 건 보완해주고 알려주고 적극적으로 도와주는 협동심에 너무 감동하였고, 웃음이 끊이질 않았던 것 같다. 😄 또한 백엔드 분들과 통신을 한 점이다. 넘어오는 데이터를 어떻게 뿌리고 나타내야 하는가? 라는 과정에서 이런 기회를 주신 백엔드 분들께 감사했다.
프로젝트를 하는 내내 내 길이 아닌 건가 생각이 들어서 너무 울고 싶고, 컨플릭트도 많이 나서 한없이 죄송했는데 팀원들과 멘토님들 덕분에 진짜 행복하게 1차 프로젝트를 마무리했습니다. (물론 리팩토링은 필수지만...🥸)
다시 한번 이 자리를 빌어 다들 너무 감사합니당 ~ 2차 프로젝트 분들도 만반잘부~~ 🙇♀️