1차 프로젝트 회고록(H&M)

Jetom·2021년 10월 18일
0

잡담

목록 보기
8/10
post-thumbnail

약 2주(?)의 기간 동안 H&M clone coding에 대한 회고록을 작성하려고 한다 :)

월요일이 대체 공휴일이라 기간이 더욱 짧아졌지만, 그만큼 팀원들과 열심히 멘탈도 털려보고 많은 공부(?)도 해보았으니 여러모로 득이 많은 프로젝트면서 애증의 프로젝트이기도 했다. 😎 🤓 🥸

H&M 사이트와 비교해서 보시면 매우 부끄럽습니다.. 🙈🙈🙈🙈

👉 피 땀 눈물이 들어간 H&W 깃허브

🙇‍♀️  팀소개 🙇‍♂️

👩🏻‍💻 프로젝트 기간

  • 2021.10.05 ~ 2021.10.15

🔎   팀명

  • HandWash
    H&M과 비슷한 느낌을 찾던중 HandWash라는 동혁님의 아이디어로 탄생한 팀명

🐥   팀원 소개

  • FrontEnd :
    신혜리(🙋🏻‍♀️  Me) - Nav, Main, Footer, 페이지 구현
    서동혁 - Login, SignUp, 장바구니 페이지 구현
    조윤희 - 상품 리스트 페이지 구현
    전태양 - 상품 상세페이지, 즐겨찾기 페이지 구현

  • BackEnd
    이다빈 - 상품API, 카테고리API, 장바구니 API
    김민호 - 유저API, 즐겨찾기 API
    공통 - 데이터 모델링, 데이터추가, 데이터 구축

🛠   적용기술

  • FrontEnd : React, Html5, Css3
  • BackEnd : Python@3.8.11, Django@3.2.8, PostgreSQL@14.0, JWT, bcrypt
  • Communication : Git, GitHub, Trello, Slack, Notion

🙏   제가 쓴 모든 코드는 리팩토링이 필요합니다. 너그럽게 봐주세요 🙏

기능 구현 및 코드 공유

🧭 Nav (상단바)

로그인 / 쇼핑백은 조건부 렌더링을 이용해 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 list

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차 프로젝트도 같이 하고 싶단 생각이 들었기 때문이다! 😁 (저만 그런거 아니죠..?)


좋았던 점

당연히 팀원 분들과의 합이다. 서로 모르는 건 보완해주고 알려주고 적극적으로 도와주는 협동심에 너무 감동하였고, 웃음이 끊이질 않았던 것 같다. 😄 또한 백엔드 분들과 통신을 한 점이다. 넘어오는 데이터를 어떻게 뿌리고 나타내야 하는가? 라는 과정에서 이런 기회를 주신 백엔드 분들께 감사했다.


마지막으로..

  • 스스로 생각할 힘을 길러주신 연욱님께 감사합니다 :)
  • 컴포넌트화에 대한 관점을 길러주시고, 숨 쉬듯 구조 할당을 알려주신 종택님께 감사합니다 :)
  • 숨은 팀원 래영님께 정말 감사합니다.. (너무 감사하고 죄송한 1004 래영님.. 흑흑 🥲)
  • 어떤 상황에서도 웃으면서 서로를 이해하면서 같이 노력한 우리 H&W 팀 너무 감사합니다 🙇‍♀️

프로젝트를 하는 내내 내 길이 아닌 건가 생각이 들어서 너무 울고 싶고, 컨플릭트도 많이 나서 한없이 죄송했는데 팀원들과 멘토님들 덕분에 진짜 행복하게 1차 프로젝트를 마무리했습니다. (물론 리팩토링은 필수지만...🥸)

다시 한번 이 자리를 빌어 다들 너무 감사합니당 ~ 2차 프로젝트 분들도 만반잘부~~ 🙇‍♀️

profile
사람이 좋은 인간 리트리버 신혜리입니다🐶

0개의 댓글