Project

PROJECT :: bonjuk Review

hooti 2020. 8. 30. 14:22

PROJECT :: bonjuk Review

첫번째 협업 프로젝트,  bonjuk Clone Review

 

 

🎬영상 보러가기

 

 

 클로닝 주제와 팀원, 구현한 기능 

 

 

1. 클로닝 주제

🧐 유명 프렌차이즈 본죽에서 상품 소개 및 판매를 중심으로 제작된 사이트 

HTML과 CSS를 이용한 레이아웃 구축 연습 / React를 이용한 동적 기술 구현 활용 / React 동적 라우팅 활용 / React props, state 활용 / fetch를 이용한 API 호출 활용 / map을 이용한 jsx 리턴 활용 / Back-End와의 API 연결 연습

 

2.팀명과 제작팀원

KiBon (기본에 충실하자!)

Front-End 4명(이효정, 조윤민, 김건우, 손수민) /  Back-End 2명(김해준, 권창식)

 

3. 개발기간

2020.08.18~2020.08.28

 

4. Github & Trello

11-KiBon-frontend / KiBon

 

5. 적용기술

HTML,CSS / JS / React(Router,SASS,fetch,map)

 

6. 구현한 기능 (직접 작업한 내용은 색상을 변경하였습니다)

회원가입 & 로그인: 일정 조건 충족시 회원가입&로그인 성공 또는 실패 / API통신을 통해 받은 token을 localStorage에 저장하여 활용 / token값을 가지고 로그인 상태 유지 & 로그아웃 

 

메인:  JavaScript의 이벤트와 CSS의 애니메이션 효과를 통해 동적인 페이지 구현 / 검색 기능을 통해 원하는 메뉴 필터링 기능 구현

 

메뉴 소개: 메뉴 리스트와 메뉴 상세 페이지 동적 라우팅을 이용하여 연결 / 백엔드 API통신을 통해 필요한 데이터 호출

 

상세 메뉴: 메뉴별 고유의 경로를 가진 상세 페이지 제작 / 백엔드 API통신을 통해 필요한 데이터 호출

 

장바구니:  선택한 제품을 장바구니 추가 / 장바구니 내에서 제품 삭제 기능 구현

 

 

 


 코드 리뷰 

 

 

1. 코드를 더욱 간단하게! 삼항연산자와 className 토글! ( 로그인화면 )

<div className="loginWrap">
  <div className="menu">
    <span
      className={isMember ? "memberOn memberBtn" : "member memberBtn"}
      name="changeMember"
      onClick={() => this.handleChange("changeMember")}
    >
      회원 로그인
    </span>
    <span
      className={
        !isMember ? "memberOn memberBtn" : "member memberBtn"
      }
      name="changeNoMember"
      onClick={this.handleChange}
    >
      비회원 로그인
    </span>
    </div>
    <div>{ismemberShip ? <MemberForm /> : <NoMemberForm />}</div>

본죽의 로그인은 회원 로그인과 비회원 로그인 두 가지가 존재한다. 각 버튼을 누를 때마다 레이아웃이 변경된다. 각 레이아웃을 컴포넌트로 나눠 제작한 후, 현재의 상태 값에 따라 나타나는 조건문을 작성하였다. 조건을 true, false로만 나눈다면 삼항 연산자를 이용하는 것이 깔끔한 코드를 쓸 수 있다.

사실 맨 처음에는 상태 값을 여러 개로 나눠 true, false 값을 조절했었다. 내용을 더 간단하게 줄여야 할까?라는 고민 중에 종택 멘토님께서 짧게 줄여보라는 리뷰를 받고 상태 값을 하나로 적용하는 것을 시도했다. 멘토님의 리뷰 덕분에 코드가 더 예뻐졌다.👏

 

 this.state = {
      ismemberShip: true,
      memberOn: true,
      memberOff: false,
      isMember: true,
    };
  }
  
   hanbleChangeMember = () => {
    this.setState({
      ismemberShip: true,
      memberOn: true,
      memberOff: false,
    });
  handleChange = (e) => {
    if(e ===  "changeMember"){
      this.setState({
        ismemberShip: true,
        isMember: true,
      });
    }else{
      alert(
        "비로그인 시 본아이에프의 회원 혜택(쿠폰, 이벤트 혜택)을 받으실 수 없습니다. 회원가입 하시고 본아이에프 혜택을 놓치지 마세요."
      );
      this.setState({
        ismemberShip: false,
        isMember: false,
      });
    }
  };

처음의 코드는 memberOn, Off를 나눠 적용을 했었다. 처음 코드를 짤 때는 짧게 줄이는 생각보다는 기능이 실행되는 것을 먼저 생각했었다. 작성하고 나니 '이걸 이렇게 불편하게 해야 하나...?' 싶었었다.

 

아니나 다를까, 종택멘토님께서 해당 부분을 지적해주셨다😅

해당 부분의 리뷰를 받고 원리를 정리해보니, 정말로 하나의 상태 값으로 변화가 가능했었다. 역시 코드는 나 혼자만 고민하는 것이 아니라 다 같이 보면서 더 효율적인 방법을 찾는 것이 좋다 생각했다.

this.state = {
      ismemberShip: true,
      isMember: true,
    };
  }

  handleChange = (e) => {
    if (e === "changeMember") {
      this.setState({
        ismemberShip: true,
        isMember: true,
      });
    } else {
      alert(
        "비로그인 시 본아이에프의 회원 혜택(쿠폰, 이벤트 혜택)을 받으실 수 없습니다. 회원가입 하시고 본아이에프 혜택을 놓치지 마세요."
      );
      this.setState({
        ismemberShip: false,
        isMember: false,
      });
    }
  };

조금 더 간결해진 상태 값 코드! 볼 때마다 뿌듯하다. 더욱 짧게 줄일 수 있는 방법이 있을까라는 생각도 들지만 그래도 10줄 정도 줄였다는 것에 기뻤다!

 

 

 

 

2. 정말 재밌는 API 통신! ( 로그인화면 )

locationEvent = () => {
    fetch(`${API}/user/login`, {
      method: "POST",
      body: JSON.stringify({
        identifier: this.state.idValue,
        password: this.state.passwordValue,
      }),
    })
      .then((response) => response.json())
      .then((response) => {
        if (response.token) {
          localStorage.setItem("token", response.token);
          this.props.history.push("/");
        } else {
          alert("아이디와 비밀번호를 확인해주세요");
        }
      });
  };

개인적으로 정말 재밌고 뿌듯했던 코드다! 해당 코드는 로그인의 조건문으로, 아이디와 비밀번호를 입력하였을 때 Back-End와의 API 통신을 통해 가입한 회원이 맞는지 token 값을 가져온다. 해당 값이 없다면 아이디와 비밀번호를 확인하라는 경고 창이 뜨고, 회원 내용과 일치하다면 메인 페이지로 이동한다. 몫 데이터를 이용하지 않고, 정말로 홈페이지처럼 값을 받아오는 것이 너무너무 재밌고 뿌듯했다.

 

 

 

3. 너무 힘들고, 어려웠던 전체동의 체크박스 ( 회원가입 )

 handleAllChecked = () => {
    const { allChecked } = this.state;
    this.setState({
      allChecked: !allChecked,
      checked0: !allChecked,
      checked1: !allChecked,
      checked2: !allChecked,
    });
  };

  handleChecked = (index) => {
    this.setState(
      {
        [`checked${index}`]: !this.state[`checked${index}`],
      },
      () => {
        this.setState({
          allChecked:
            this.state.checked0 && this.state.checked1 && this.state.checked2,
        });
      }
    );

정말... 정말 눈물이 날뻔했었던 부분이다. 회원가입 양식은 금방 끝낼 줄 알았지만, 거대한 산을 만났다. 바로 회원가입을 할 때, 너무나도 당연히 있는 약관 동의 체크박스다. 모든 약관에 동의하는 부분은 정말 쉬웠다. allCheked의 현재 상태 반대 값을 모든 체크박스에 주면 되기 때문이다. 문제는 개별 체크박스였다. allCheked에 준 상태 값 때문에 개별 체크가 실행되지 않았던 것이다. 😂😂 거기다해당 부분은분은 map을 돌렸던 부분이라서 어떻게 해결해야 할지에지에 대한 고민에 빠졌다. 한꺼번에 너무 많은 상황을 생각하니 첫 시작도 하지 못했었다.

 

'이렇게 하면, 되려나? 아니야, 그렇게 하면 그런 상황에선 아무것도 안되잖아, 이건 안 되나? 아 이건 이 상황에서 안될 거야'

 

생각을 공책에 적으면서 엄청난 우울감에 빠졌었다. 나는 지금까지 회원가입을 하면서 이런 체크박스는 정말 간단한 로직이겠지,라고 생각했는데 그게 아니었던 것이다. 다른 사람들은 쉽게 해내는 부분을 나만 못한다는 생각에 빠져 눈물이 나기 시작했었다. 이렇게 우울감에 빠져있다간 아무것도 못한다고 생각했을 때, 나는 로직에 필요한 조건을 숫자로 체크하여 하나씩 쓰기 시작했다. 그리고 한 가지 조건에 맞춰 하나씩 로직을 짜기로 했다. 고민에 빠져있었을 때, 차근히 하나씩 해보라고 조언해 주신 관희 멘토님께 너무 감사했었다.

 

그러나 노력에도 불구하고 결국 파트 멘토님의 도움을 받아 해당 코드를 완성했다. 아직은 혼자서 모든 코드를 작성하지 못한다는 생각이 들어 우울하기도 했지만, 차근히 조건을 적고 있는 나를 보며 예전보단 발전했다는 뿌듯함도 있었다.

 

 

 

3. 더 짧게, 더 보기 좋게!! 유효성 검사(validation)!

handleName = (e) =>{
    this.setState({nameValue:e.target.value});
  };

  handleBirthday = (e) =>{
    this.setState({birthdayValue:e.target.value});
  };

  handleUserId = (e) =>{
    this.setState({userIdValue:e.target.value});
  };

  handlePassword = (e) =>{
    this.setState({passwordValue:e.target.value});
  };

  handlePasswordCheck = (e) =>{
    if({passwordValue:e.target.value} == {passwordCheckValue:e.target.value})
    this.setState({passwordCheckValue:e.target.value});
  };

  handleGender = (e) =>{
    this.setState({genderValue:e.target.value});
  };

  handlePhone = (e) =>{
    this.setState({phoneValue:e.target.value});
  };

  handlEmail = (e) =>{
    this.setState({emailValue:e.target.value});

  };

회원가입양식에서 받아야하는 값이 많아서 코드가 너무 복잡해지고 있는것이 아닌가 싶었다. validation까지도 넣어야하는데 이벤트문이 한없이 길어지니 보기가 정말 싫었었다. 정말 많은 블로그들을 찾아봤지만 내 수준에 맞지 않게 너무 복잡한 코드로 이루어져 았어 어떻게 응용해야할지 고민했었다.

 handleChange = (e) => {
    const {name, value} = e.target;

    if (name === "birthDayValue") {
      this.setState({ errorBoxDay: valDay(value)});
    }

    if (name === "passwordValue") {
      this.setState({ errorBoxPhone: valPassword(value)});
    }

    if (name === "passwordCheckValue") {
      this.setState({ errorBoxPasswordCheck: valpasswordCheck(this.state.passwordValue, value)});
    }

    if (name === "phoneValue") {
      this.setState({ errorBoxPhone: valPhone(value)});
    }

    if (name === "emailValue") {
      this.setState({ errorBoxEmail: valEmail(value)});
    }

    this.setState({[name]: value});
  }

고민을 통해 나왔던 코드다. 각각의 handle 이벤트를 작성하는 것이 아니라, 하나의 handle 이벤트로 모든 값을 관리하는 것이 효율적이다.라는 판단이 생겼기 때문이다. 많은 생각을 하고, 나름대로 응용해서 더 효율적인 코드를 적었지만 그럼에도 이 코드도 마음에 들진 않았다. 조건 문마다 setState를 하기 때문이다.

 handleChange = (e) => {
    const { name, value } = e.target;

    const table = {
      nameValue: value,
      birthDayValue: ["errorBoxDay", valDay(value)],
      passwordValue: ["errorBoxPassword", valPassword(value)],
      userIdValue: value,
      passwordCheckValue: [
        "errorBoxPasswordCheck",
        valpasswordCheck(this.state.passwordValue, value),
      ],
      genderValue: value,
      phoneValue: ["errorBoxPhone", valPhone(value)],
      emailValue: ["errorBoxEmail", valEmail(value)],
    };

    this.setState({
      [name]: value,
      [table[name][0]]: table[name][1],
    });
  };

결국 종택 멘토님의 도움을 받아 위와 같은 코드로 줄이게 되었다. 설명을 해주실 때, 이것보다 더 줄일 수 있는 코드를 말씀해 주셨는데 혼자 작성을 하려니 그 부분까지는 이해하고 적기가 어려웠다. 그래도 처음보다는 많이 발전했다는 것을 느낄 수 있어 코드 리뷰에 꼭 작성하고 싶었다.

 

 

 

 

4. 000님, 회원가입을 축하드립니다! name값 가져오기( 회원가입 )

if (
      nameValue &&
      birthDayValue &&
      userIdValue &&
      passwordValue &&
      phoneValue &&
      emailValue &&
      allChecked
    ) {
      fetch(`${API}/user/signup`, {
        method: "POST",
        body: JSON.stringify({
          name: this.state.nameValue,
          birthdate: this.state.birthDayValue,
          identifier: this.state.userIdValue,
          password: this.state.passwordValue,
          gender: this.state.genderValue,
          phone_number: this.state.phoneValue,
          email: this.state.emailValue,
        }),
      })
        .then((response) => response.json())
        .then((response) => {
          if (response.message === "SUCCESS") {
            localStorage.setItem("name", response.name);
            this.props.history.push("/sign-up/join-complete");
          } else {
            alert("회원가입에 실패하였습니다! 입력창을 확인해주세요.");
          }
        });
    } else {
      alert("필수입력창에 내용을 채워주세요.");
    }
  };

 

회원가입버튼을 눌렀을때, 필수양식의 값이 모두 채워져있고, API통신 값으로 [SUCCESS] 가 온다면 회원가입 완료페이지로 이동한다. 본죽은 가입이 완료되었을때, 로그인상태가 되는것이 아니고 가입했던 회원의 이름이 뜨면서 가입축하 메세지를 화면에 구현한다. 그 값을 저장하기  위해 name이라는 값을 만들어 localStorage에 저장한다. 이 부분을 구현할때, Back-End와의 통신으로 저장해야하는지, 나 혼자서 값을 state에 저장해야하는건지... 정말 많이 헷갈려서 해준님을 어렵게했었는데 답이 너무 간단했었다.😂

복잡하게 생각하는 버릇을 고쳐야겠다고 느꼈던 코드다.

constructor() {
    super();
    this.state = {
      username: "",
    };
  }

  componentDidMount() {
    this.setState({
      username: localStorage.getItem("name"),
    });
  }

<div className="completeBox">
   <p>
     <strong>{this.state.username}</strong> 고객님,
     <br />
     본아이에프 회원이 되신 것을 환영합니다.
   </p>
 </div>

저장했던 name값을 불러오고 state에 저장한다. 별거 아닌 코드같지만 이 부분이 구현되었을때, 정말 홈페이지랑 완벽히 똑같잖아! 하면서 방방 뛰었었다. 

 

 


 

 반성과 칭찬의 시간 

 

 

한줄로 나를 칭찬하고 반성하기

 

칭찬하자🥳 : 로직을 짤 때, 조건을 먼저 생각하고 차근히 시작하는 버릇을 들이기 시작했다. 원했던 기능을 대부분 구현했다. git에 대한 두려움이 많이 사라졌다.

반성하자😰 : 삽질하는 시간이 많아서 더 많은 페이지를 만들지 못했다. 마감에 맞추기 위해 모험을 하지 않았다라는 후회도 든다.

 

 

협업에 대해서 칭찬하고 반성하기

 

칭찬하자🥳 : 개인적으로 팀원들과 정말 재밌게 작업했다. 서로 원했던 기능들을 전부 구현했고, 잡음도 거의 나지 않았다. trello도 모두 잊지 않고활용해 줬고,git 문제도 매번 push, merge 보고를 slack에 하며 충돌을 최소화했다. 미팅도 매일잊지 않고진행하고, 서로 모르는 로직은 머리를 맞대고 고민했다. 정말 이게 바로 협업이라는 것이구나를 느껴주게 해줬던팀원들이었다. 또한 처음 우리팀의 목표를 잘 지킨것이 뿌듯했다. 기본에 충실하자! 라는 팀명과 같이 기능에 너무 압박받지않고, 그동안 배운것을 응용하고 본인이 부족한 부분을 채워가는것을 목표로 삼았는데 팀원 모두가 그 부분을 잘 지켜 발전한 모습을 보여줬다. 조금 어른스럽지 못한 PM을 잘 따라준 팀원들에게 감사를 전하고 싶다. 그리고 역시 나는 발표를 잘한다.(뿌듯)

 

반성하자😰 : PM으로써 계획이 틀어졌을 때,너무불안해했던 것같아 팀원들에게 미안했다.다른 팀에비해 trello, git에 대한 잔소리도 많이했던 것 같다.또 개인의성격 문제로히스테릭을 부린 부분들이 있었다. 나의감정 조절을잘해야 한다고생각했고,그런 부분을잘 받아준팀원들에게 감사했다. 다음에는 조금 더 어른스럽고 긍정적으로 프로젝트에 참여하고 싶다. 또 초기세팅이 부실했던 문제도 있었다. 다음에는 홈페이지를 꼼꼼히 뜯어보고 초기세팅을 꽉차게 하고 싶다.