[Project 배민서바이벌] 3,4,5일차

임채현·2022년 2월 3일
0

Daily StandUp Meeting

멘토님 앞에서 우리가 그간 어떻게 미팅을 진행해왔는지 보여주고 그에 대한 피드백을 가지는 시간을 가졌다.

  • Why StandUP?
    • 오래 서있으면 다리가 아프지 않은가??
    • 즉 standup 미팅을 하는 이유는 최대한 불필요한 미팅요소를 줄여서 시간을 최소화하여 미팅을 진행하겠다는 것이다. 짧은 기간동안 필요한 말들만 하여 마치고 빠르게 본업에 집중한다는 것!
  • 피드백 내용
    • 트렐로 티켓보면서 회의
    • 촤대한 불필요한 말 줄이기
    • 서기랑 팀장(PM) 필요
    • 같은 화면 공유(시각자료 활용)
    • 구글스프레드시트로 명세서(프로젝트 데이터) 공유
    • 회의 전 티켓 최신화
      피드백을 수용하여 트렐로를 적극적으로 활용하기 시작했고 서기는 최희택님이 PM은 한신웅님이 하기로 결정하였다.

My Part

로그인 레이아웃과 기능구현을 일단 대강 맞추고 나는 회원가입페이지를 만들기 시작했다.
회원가입페이지는 여러개의 입력칸이 존재한다.
다음은 배민문방구의 회원가입란이다.

우리 팀은 여기서 아이디, 비밀번호, 닉네임(이름), 이메일, 휴대폰번호를 회원가입에 필요한 정보로 받기로 했다.
백엔드도 이 데이터 요건에 맞추어 API 기능정의서를 작성하였다.

Component

하나의 input을 Component화하여 재사용하는게 좋을 것 같았다.
3개의 Component가 완성되었다.

  • SignUpInput
  • SignUpInputList
  • SignUp

SignUpInput

  • SignUpInput은 위에서 말했듯이 하나의 input을 component화 한것이다.
  • SignUpInputList의 자식 컴포넌트이며 props를 통해 부모의 데이터를 받아온다.
  • props는 주로 input의 attribute값이며 input값들의 state을 handle할 onChange함수를 선언하였다.
import React from 'react';
import './SignUpInput.scss';

function SignUpInput({
  type,
  signupInfo,
  value,
  text,
  placeholder,
  setSignupInfo,
  name,
}) {
  const onChange = e => {
    setSignupInfo({
      ...signupInfo,
      [e.target.name]: e.target.value,
    });
  };
  return (
    <div className="SignUpInput">
      <div className="inputTextBox">
        <p className="inputText">{`* ${text}`}</p>
      </div>
      <div className="inputWrapper">
        <input
          className="signUpInput"
          name={name}
          onChange={onChange}
          type={type}
          placeholder={placeholder}
          value={value}
        />
      </div>
    </div>
  );
}

export default SignUpInput;

SignUpInputList

  • SignUp의 자식 컴포넌트이다.
  • props로 signUpInfo와 setSignUpinfo를 가진다.
    이 props를 통해 signUp의 signUpInfo와 setSignUpInfo를 가져오고 이 가져온 함수와 state값으로 자식 컴포넌트인 signUpInput의 onChange 함수에 데이터를 전달하여 작동시킨다.
  • Const Data를 활용하여 object mapping을 진행한다.
import React from 'react';
import './SignUpInput.scss';

function SignUpInput({
  type,
  signupInfo,
  value,
  text,
  placeholder,
  setSignupInfo,
  name,
}) {
  const onChange = e => {
    setSignupInfo({
      ...signupInfo,
      [e.target.name]: e.target.value,
    });
  };
  return (
    <div className="SignUpInput">
      <div className="inputTextBox">
        <p className="inputText">{`* ${text}`}</p>
      </div>
      <div className="inputWrapper">
        <input
          className="signUpInput"
          name={name}
          onChange={onChange}
          type={type}
          placeholder={placeholder}
          value={value}
        />
      </div>
    </div>
  );
}

export default SignUpInput;

SignUp

  • 가장 부모컴포넌트이자 useState을 통해 컴포넌트에 활용할 state을 만들고 회원가입 유효성 검사를 실행할 함수를 선언한다.
  • 다중 state을 한번에 관리하기 위하여 state을 객체화하여 관리한다.
  • 정규식을 활용하여 각 input에 해당하는 유효성 검사 함수를 만든다.
  • useEffect와 fetch를 활용하여 벡엔드 데이터를 가져오고 입력받은 회원정보를 보내준다.
  • 백엔드 데이터 response의 message를 활용하여 중복 검사를 체크한다.
import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import SignUpInputList from './SignUpInputList';
import './SignUp.scss';

function SignUp() {
  const [signUpInfo, setSignUpInfo] = useState({
    signUpId: '',
    signUpPw: '',
    signUpPwCheck: '',
    signUpEmail: '',
    signUpNick: '',
    signUpPhone: '',
  });

  const {
    signUpId,
    signUpPw,
    signUpPwCheck,
    signUpEmail,
    signUpNick,
    signUpPhone,
  } = signUpInfo;

  const navigate = useNavigate();

  const goToLogin = () => {
    navigate('/login');
  };

  const isValidPw = value => {
    let pwRegex =
      /^(?=.*[a-zA-z])(?=.*[0-9])(?=.*[$`~!@$!%*#^?&\\(\\)\-_=+]).{8,16}$/;
    return pwRegex.test(value);
  };
  const isPwSame = signUpPw !== signUpPwCheck;

  const isValidEmail = value => {
    let emailRegex =
      /([\w-.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([\w-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$/;
    return emailRegex.test(value);
  };

  const isValidPhone = value => {
    let phoneRegex = /^[0-9\b -]{0,13}$/;
    return phoneRegex.test(value);
  };

  const isFilledMandatory =
    !signUpId ||
    !signUpPw ||
    !signUpPwCheck ||
    !signUpEmail ||
    !signUpNick ||
    !signUpPhone;

  const signUpValidation = e => {
    e.preventDefault();
    if (isFilledMandatory) {
      alert('필수항목을 입력해주세요!');
    } else if (!isValidPw(signUpPw)) {
      alert(
        '사용불가! 영문자,숫자,특수문자 조합으로 8-16글자 범위로 입력해주세요! '
      );
    } else if (isPwSame) {
      alert('비밀번호와 비밀번호확인은 같아야 합니다.');
    } else if (!isValidEmail(signUpEmail)) {
      alert('올바른 이메일 형식이 아닙니다.');
    } else if (!isValidPhone(signUpPhone)) {
      alert('올바른 전화번호 형식이 아닙니다.');
    }
  };

  const signUpRegister = () => {
    fetch('http://10.58.5.43/users/signup', {
      method: 'POST',
      body: JSON.stringify({
        account: signUpId,
        password: signUpPw,
        email: signUpEmail,
        phone: signUpPhone,
        nickname: signUpNick,
      }),
    })
      .then(res => res.json())
      .then(res => {
        if (res.message === 'ACCOUNT ALREADY EXISTS') {
          alert('중복된 아이디입니다!');
        } else if (res.message === 'NICKNAME ALREADY EXISTS') {
          alert('중복된 닉네임입니다!');
        } else if (res.message === 'E-MAIL ALREADY EXISTS') {
          alert('중복된 이메일입니다!');
        } else if (res.message === 'PHONE-NUMBER ALREADY EXISTS') {
          alert('중복된 휴대폰번호입니다!');
        }
      });
  };

  return (
    <div className="signUp">
      <form className="signUpForm" onSubmit={signUpValidation}>
        <header className="signUpHeader">
          <h1 className="info">기본정보</h1>
          <h4 className="sideInfo">
            *표시는 반드시 입력하셔야 하는 항목입니다.
          </h4>
        </header>
        <SignUpInputList
          setSignupInfo={setSignUpInfo}
          signupInfo={signUpInfo}
        />
        <div className="signUpButtonWrapper">
          <button className="signUpCancelBtn" onClick={goToLogin}>
            취소
          </button>
          <button className="signUpBtn" onClick={signUpRegister}>
            회원가입
          </button>
        </div>
      </form>
    </div>
  );
}

export default SignUp;

🌈 회고

😵 BLOCKER

Input State.........

몬스터과제를 하면서 너무 monster 양식의 컴포넌트화에 익숙해져 그대로 프로젝트에 도입하려던게 문제점을 제공했다.
몬스터과제 역시 컴포넌트를 가장 기본 컴포넌트, 기본 컴포넌트를 나열하는 리스트 컴포넌트, 그리고 그것을 화면에 렌더링해줄 컴포넌트로 구성하였는데 뭔가 이 방법을 직접 프로젝트와 같은 실습에도 적용을 해봐야 할것 같다는 생각에 회원가입페이지를 같은 방식으로 진행하였다.
그러다보니까 state관리가 매우 힘들어졌다. ㅠㅠ
사실 그냥 html로 input을 다 작성해줄 수 있다. 6개밖에 안되기 때문에 그러면 state관리가 쉽겠지만 후에 유지보수를 한다 했을 때 const data와 component를 사용한 코드가 더 좋은 측면이 많기 때문에 내가 쓴 코드를 유지하기로 결심하였다.(멘토님의 의견도 반영)
결국 props를 여러개 만들고 state이름을 상수데이터에 inputName으로 넣는 식으로 어떻게든 해결하였다.(멘토님과 동기의 도움을 많이 받았다.)
나중에 다른 동기분의 코드를 보니까 컴포넌트를 2개만 활용해서(List컴포넌트 제외)나와 같은 방법으로 간결하게 한 것을 보고 추후 리팩토링할 때 다시한번 해볼 생각이다.

유효성 검사

회원가입에 대한 유효성 검사는 조건을 만족하지 않았을 때 input 밑에 경고문이 생겨나는 식으로 기능 구현을 하고 싶었는데 생각보다 잘 안되었다.
회의 결과 세세한 기능구현에 포커싱을 하기보다는 전체적으로 모든 페이지를 1차적으로 완성하고 우리 팀의 첫번째 목표였던
루프(회원가입 => 로그인 => 메인페이지 => 상세페이지 => 장바구니 => 결제 => 마이페이지에서 확인)
를 구현하는데 집중하자는 팀원들의 의견을 반영하여 일단 유효성 검사에 대한 경고문을 alert창을 뜨게하는 것으로 대체하였다.
추후 시간이 남으면 다시 구현해 볼 생각이다.

🧐 FELT

  • 팀원들과 미팅을 하면서 전체적인 프로젝트 진행방향에서 우선순위가 무엇인지 확실하게 파악할 수 있었다. 이는 후에 1차뿐만 아니라 2차에서도 나의 능률을 많이 올려줄 것 같다.
  • Git 사용과 trello 사용이 점점 익숙해지고 있다!!! 프로젝트하면서 필수적인 tool들이고 후에 현업에 나가서도 쓸 확률이 높아 잘된것 같다.
  • 무에서 유를 창조하는 기분이다. 내가 배운게 생각보다 많이 좁은 것 같다. 배운 것 만으로 프로젝트를 진행하기엔 많이 어렵다. 고도의 구글링 실력이 필요하다.
profile
열심히 살고 싶은 임채현입니다.

0개의 댓글