라코스테 클론 프로젝트🐊

PRB·2021년 9월 10일
4

Project

목록 보기
4/13
post-thumbnail

1. 라코스테 프로젝트 소개 🎉

Front-end GitHub 레파지토리
Back-end GitHub 레파지토리

1.팀원 소개🧑🏻‍💻

Front-end : 이송현, 정도영, 최파란별
Back-end : 박지원, 주종민, 하예준

2. 작업기간 📅

21.08.30 ~ 21.09.10 (12일)

3. 기술 스택

Front-end : HTML, SCSS, JS, React
Back-end : Python, Django, MySQL

4. 주요 구현사항 ✅

로그인 페이지, 회원가입 페이지, 상품 상세 페이지, 상품 리스트 페이지, 장바구니 페이지
JWT 토큰을 이용한 회원가입, 로그인, 로그아웃 기능
메인 페이지 슬라이드 구현
상세페이지 슬라이드 구현
상세페이지 알림 창 구현
동적 라우팅을 이용한 각각 다른 상세페이지 이동 구현

5. 각자 역할 👥

Front-end

  • 이송현 : Nav, Footer, Main 페이지
  • 정도영 : 상품리스트, 장바구니 페이지
  • 최파란별 : 회원가입 & 로그인, 상품 상세 페이지

Back-end

  • 박지원 : 로그인, 회원가입 API
  • 주종민 : 제품 리스트 페이지 API
  • 하예준 : 제품 상세 페이지, 장바구니 API

2. 구현 페이지


메인페이지는 송현님이 만들어주셨고
로고는 백엔드이자 프로젝트의 디자이너를 맡고있는 종민님이 제작해주셨다.!!
악어가 정말 귀엽다.


회원가입 페이지가 3가지 페이지로 이루어져 있어 라우터로 각각 페이지 이동을 시켰는데 종택님께서 메뉴 탭 또는 path parameter를 사용한 라우팅을 사용해보라고 하셨는데 메뉴 탭으로 구현을 했다.
메뉴 탭으로 코드를 수정해보니 변경되는 컴포넌트만 갈아끼우게 돼서 코드의 가동성 및 전보다 훨씬 간결해졌다.
그렇게 회원가입을 다 하면

로그인 페이지로 가서 로그인을 하면 된다.
로그인을 하면 백엔드에서 토큰을 주는데 로컬 스토리지에 저장하는 방식으로 구현을 하였다.
또한 로그인을 한 상태에서 마이페이지에 들어가면 회원의 이름이 보였으면 좋겠다고 생각해서 준영님과 예준님께서 의견을 주셨다. 👏
토큰과 같이 회원 이름을 보내주셨고 토큰과 같이 로컬 스토리지에 저장해서 보여주는 방식으로 구현하였다.

다음으로 도영님께서 만들어주신 상품리스트 페이지이다.( 팀원들과 토요일에 카페에서 동적라우팅을 성공시켰다...)

상품 상세페이지이다.
카드 혜택 보기, 컬러, 사이즈, 이름, 가격, 상세 텍스트, 사진 등 많은 데이터들을 백엔드에서 받아온다.
저기있는 상품 왼쪽에 있는 미니사진을 클릭하면 슬라이드기능이 실행된다.

사이즈를 선택하면 쇼핑백에 추가하기버튼이 활성화 된다 😆

3. 아쉬운 부분

1. 코드 단축화 및 리팩토링

코드를 하면서 느낀 게 반복적인 부분도 알면서 하드코딩한다는 점과 상수 데이터로 더 깔끔하게 코드를 작성 못한 게 너무 아쉽다. 아래는 종택님께서 코드 리뷰를 하시면서 남겨주신 말씀이신데 기억에 남아서 가져와봤다.

"프로그램이 잘 작동하는 상황에서 그저 코드가 '지저분하다'는 이유로 불평하는 것은 프로그램의 구조를 너무 미적인 기준으로만 판단하는 건 아닐까?
(...) 하지만 그 코드를 수정하려면 사람이 개입되고, 사람은 코드의 미적 상태에 민감하다. 설계가 나쁜 시스템은 수정하기 어렵다. (...) 무엇을 수정할지 찾기 어렵다면 실수를 저질러서 버그가 생길 가능성도 높아진다.
(...) 프로그램이 새로운 기능을 추가하기에 편한 구조가 아니라면, 먼저 기능을 추가하기 쉬운 형태로 리팩터링하고 나서 원하는 기능을 추가한다." - 마틴 파울러, 리팩터링, 27p

2. 프론트엔드&백엔드 소통

백엔드와 소통이 진짜 정말 중요한 거 같다. 많은 블로그들을 봐도 소통이 정말 중요하다고 하는데 이건 겪어봐야 알 거 같다. 기억에 남는 일로 백엔드 이신 지원님과 회원가입을 맞춰보는 작업이었는데 계속 에러가 나서 현묵님이 키값 맞혀보셨어요?라고 하셨는데 설마 다시 보니 키값이 서로 안 맞았다.🥲
이렇게 사소하지만 작은 부분도안 맞으면 안 된다는 걸걸 몸소 느꼈다.

4. 기억나는 코드 🤔

1. 메뉴탭 기능

위에서도 설명헀지만 정말 기억에 남아서 적어본다.


export default class SignUp  extends Component {
  state = {
	pageIndex: 1,
  };

  goToNextPage = () => {
    this.state.checkList.every(checked => checked.required === checked.status)
      ? this.setState({ pageIndex: this.state.pageIndex + 1 })
      : alert('체크박스는 필수사항입니다.');
  };

  render() {
    const { isUserRulePlusMore, isUserInfoPlusMore, checkList, pageIndex } =
      this.state;
    const MAPPING_SIGNUP = {
      1: (
        <SignUpTermsUse
          isUserRulePlusMore={isUserRulePlusMore}
          isUserInfoPlusMore={isUserInfoPlusMore}
          checkList={checkList}
          toggleUserRulePlus={this.toggleUserRulePlus}
          toggleUserInfoPlus={this.toggleUserInfoPlus}
          goToNextPage={this.goToNextPage}
          handleChange={this.handleChange}
          pageIndex={pageIndex}
        />
      ),
      2: (
        <SignUpCertification
          goToNextPage={this.goToNextPage}
          next={pageIndex}
        />
      ),
      3: <SignUpUserInfo goToLogin={this.goToLogin} />,
    };

    return (
      <div className="signUp">
        <div className="backgroundBanner">{MAPPING_SIGNUP[pageIndex]}</div>
      </div>
    );
  }
}
export default SignUp;

저렇게 맵핑을 해서 state의 값을 변경해서 탭기능을 쓴다는게 정말 신기했다.

2. 슬라이드 기능

<div className="miniPhotoBox">
                {productInfo.images?.map((image, idx) => {
                  return (
                    <div className="miniPhoto" key={productInfo.images.id}>
                      <ProductPhoto
                        id={image.id}
                        imageUrl={image.image_url}
                        imageNum={-684 * idx}
                        changePhoto={this.changePhoto}
                      />
                    </div>
                  );
                })}
              </div>
              <div
                className="slideList"
                style={{
                  transform: `translate3d(
                ${imageNum}px, 0px, 0px`,
                }}
              >
                {productInfo.images?.map(image => {
                  return (
                    <span className="slideContent" key={image.id}>
                      <ProductPhoto imageUrl={image.image_url} />
                    </span>
                  );
                })}
              </div>
            </div>

슬라이드할 사진을 옆으로 쭉 나열하고 상품사진에있는 작은 사진을 누르면
각각 부여한 imageNum에 맞쳐서 나열했던 이미지 리스트가 이동해서 보여지는 방식이다.

class ProductPhoto extends Component {
  render() {
    const { imageUrl, imageNum, changePhoto } = this.props;
    return (
      <img
        alt="productPhoto"
        className="productPhoto"
        src={imageUrl}
        onClick={() => {
          changePhoto(imageNum);
        }}
      />
    );
  }
} 

3. 쇼핑백 버튼 활성화

              <div
                className={`productPick${size ? 'Off' : 'On'}`}
                onClick={() => {
                  size
                    ? this.setState({ isShoppingBag: false }, this.postProduct)
                    : this.setState({ isSize: false });
                }}
              >
                <button>
                  {size ? '쇼핑백에 추가하기' : '사이즈 선택하기'}
                </button>
              </div>

조건부 렌더링을 이용한 방식인데 state에 있는 size의 값이 있다면 쇼핑백에 추가하기 텍스트가 적용되고 그에 맞춰서 로직도 활성화되는 로직으로 구현했다.

5. 프로젝트 후기

사실상 2주도 안되는 시간에서 몇몇 분들과는 처음 얘기도 해봤고 어색한 분위기 속에서 혼자가 아닌 팀으로 만들어가는 프로젝트를 잘 끝낼 수 있을까라는 생각도 들었지만 데일리 미팅을 하며 같이 서로의 이슈에 대해서 고민하는 시간, 점심과 저녁도 같이 먹으면서 서서히 친목을 다졌던 시간, 프론트엔드와 백엔드가 다 합쳐서 통신해봤던 시간 등 기억에 남는 시간들이 더 많다.🥲 프로젝트 과정은 정말 잠도 줄여 가면서 정신적 육체적으로 힘들었지만 팀으로 생활하는 개발자 문화가 더욱 맘에 들었던 시간이었고 막상 프로젝트가 끝나면 좋을 줄 알았지만 이제 같은 목표였던 라코스테 프로젝트 팀원이 이제 없다니 아쉬운 마음이 더 큰 거 같다.😭 항상 친절하게 도와주신 도영님, 송현님, 예준 님, 종민 님, 지원님 감사했습니다. 👏 다음 프로젝트에서도 다 같이 더욱 성장했으면 좋겠습니다.🔥

profile
사용자 입장에서 사용자가 원하는 것을 개발하는 프론트엔드 개발자입니다.

2개의 댓글

comment-user-thumbnail
2021년 9월 11일

일요일에 만나요

답글 달기
comment-user-thumbnail
2021년 9월 11일

수고하셨습니다 파란별님😊

답글 달기