[project] wesely 프로젝트 회고록

dal·2022년 8월 28일
0

project

목록 보기
1/1
post-thumbnail

깃허브 : https://github.com/wecode-bootcamp-korea/36-1st-wesely-frontend


  • 프로젝트
    wesely: 생활용품 사이트

  • 개발 기간
    2022년 8월 16일 ~ 2022년 8월 25일 10일

  • 기술스택
    프론트엔드 : JavaScript, React.js, Sass
    백엔드 : Node.js, Express, MySQL 8.0, Postman
    협업 툴 : Notion, Slack, Gather

  • 사용 라이브러리
    React.js
    CRA
    react-router-dom
    scss
    eslint
    prerttier

프로젝트 전체 페이지 :

  • 메인 페이지
  • 상품 리스트 페이지
  • 상세 페이지
  • 장바구니 페이지
  • 로그인 페이지
  • 회원가입 페이지

내가 맡은 페이지:

  • 장바구니 페이지
  • 로그인 페이지
  • 회원가입 페이지

✨장바구니 페이지

구현 기능

  • 정기 구독 기능
    👉 미선택시 일반 구매
    👉 선택 시, 해당 구독 주기로 저장
    👉 선택된 주기 재 선택 시, state 초기화

  • 상품 삭제
    👉 삭제 때 마다 통신을 하여, 페이지에서 뒤로 간 후 다시 되돌아왔을 때 상품이 남아있는 것을 방지

  • 상품 수량 조절
    👉 조절 때 마다 통신을 하여, 페이지에서 뒤로 간 후 다시 되돌아왔을 때 상품의 수량이 변하지 않는것을 방지

  • 실시간 재고 파악
    👉 상품을 더 담을 때 재고 부족일 경우, 재고 부족 알림과 함께 수량 늘리기 방지

  • 유저 보유 포인트
    👉 현재 포인트 계산 출력
    👉 계산 후 차감 포인트 출력

나의 성장 포인트

  1.  삭제, 수량 조절에서 map과 filter를 사용하는데, 과연 이러한 방식이 맞는지 고민하였습니다.
     왜냐하면 장바구니의 아이템이 100개, 200개, 1000개 일 경우에도, 배열 순회를 한다면 비효율이니까요.
     그러나, 결국 받아오고 사용하게 될 데이터는 json형식이므로 다른 적절한 방법이 없는 것을 멘토님께 여쭤보아서 알게 되었고, 생각해보니 상품의 수가 많은 페이지에서는 offset, limit등을 활용해 한번에 받아오는 데이터들의 수가 적절할테고, 논외로 장바구니 페이지의 경우엔 상식적으로 200, 300개일 경우가 희박할 것입니다.
     결론적으로 백에게서 적절한 데이터의 수를 받아옴으로써 이러한 문제가 일어나지 않을 것이라는 생각이 들었습니다.

  2. const parcel = totalPrice < 30000 ? 2500 : 0;
    위는 3만원 아래의 경우 택배비 2500원, 아닐 경우 0원인 변수지만, 결국 boolean type이므로 isDeliveryFree 와 같은 직관적인 네이밍을 사용하는 것이 좋다는 것을 배웠습니다. 변수의 이름을 더욱 직관적으로 설정하도록 노력해야겠습니다.

  3. onClick={() =>setProducts(products.filter(product => product.id !== id))}
    위와 같이, 한줄로 끝나는 로직이라도 리턴 위에서 따로 함수로 만드는 것이 가독성와 유지보수에 용이하다는것을 배웠습니다.

  4. mixin 활용하기
    display: flex;
    align-items: center;
    justify-content: flex-end;
    scss를 보면, 가장 많이 보이는 함수들입니다. mixin을 사용하여 단 한줄로 코드를 줄이니, 가독성에도 좋고 관리에도 쉬웠습니다.

  5. 상수 리스트는 배열안의 객체로 관리하기
export default ProductsCart;
const periods = ['4주', '8주', '12주', '16주'];

구독 주기를 만들기 위해 선언한 상수데이터였습니다. 그러나 기본적으로 상수라면 사용해야하는 UPPER_SNAKE_CASE 컨벤션을 사용하지 않았었고, map을 사용하여 버튼을 만들 때, key값을 인덱스로 사용하였습니다.

const PERIOD_LIST = [
  { id: 1, period: '4주' },
  { id: 2, period: '8주' },
  { id: 3, period: '12주' },
  { id: 4, period: '16주' },
];

위와 같이 UPPER_SNAKE_CASE 컨벤션으로 상수 데이터임을 명시하고, 배열 안의 객체 형태로 만들어야 key값을 해당 주기의 고유한 아이디로 사용하였습니다.
mpa과 같은 배열 순회 메서드를 사용할 때, 인덱스로 키 값을 주는 것은, 나중에 데이터의 수가 변경되었을 때, 관리하기 어렵기 때문에 key값을 인덱스로 주지 않는 것을 항상 생각해야겠습니다.

로그인 페이지

구현 기능

  • 이메일 입력 시 유효성 검사
    👉 텍스트입력 창에 변화가 일어날 때 마다 정규식을 통한 유효성 검사
    👉 검사에 따라 문구 출력
    👉 에러 문구가 없을시, 체크 아이콘 표시

  • 비밀번호 입력창
    👉 우측 토글을 이용하여 입력한 비밀번호 확인 가능
    👉 로그인 실패의 경우 에러 문구 출력

  • 이메일 확인 검사
    👉 존재하는 이메일의 경우 비밀번호 창 출력
    👉 존재하지 않으면 회원 가입 페이지 이동
    👉 회원가입 페이지로 이동 시, 입력한 이메일을 navigate에 state를 담아 전달하여 회원가입 페이지에서 useLocation를 활용하여 이메일 인풋창에 작성한 이메일을 출력
    👉 회원가입 페이지로 이동 시, 버튼 문구를 '다음'에서 '로그인'으로 변경

  • 사용자 환영 메시지
    👉 존재하는 이메일의 경우 이름 데이터를 받아, 마스킹 처리 후 사용자 이름을 출력
    👉 아직 이름을 받아오지 않았을 경우, 로그인 및 회원가입안내 출력

  • 이메일 유효성 검사 재시작
    👉 비밀번호 창이 출력 되었을 때, 이메일 창 클릭 시 다시 이메일 유효성 검사 시작
    👉 기존 state값들 초기화

  • 로그인 시 토큰 발급
    👉 로그인에 성공할 경우, 기간이 정해진 토큰을 발급해주어 사용자의 인증이 필요한 페이지마다, 토큰을 통해 인증이 가능하도록 구현

  • 기타 스타일
    👉 지정한 유효성 검사에 따라 입력창의 보더, 버튼 스타일 등 변경

나의 성장 포인트

1. 렌더링 중의 업데이트 오류
로그인 컴퍼넌트의 렌더링 중, 이메일박스 컴퍼넌트에서 스테이트를 업데이트하는 구조에서 문제가 생겼습니다. 상위 컴퍼넌트의 렌더링 중, 하위 컴퍼넌트의 상태값이 업데이트 되는 문제였기에, 참조하는 부분을 useEffect에 의존성 배열에 담아 이 문제를 해결하였습니다.


2. 쉽게 생각하지 말기
기존 구현하던 로그인 페이지와 달라서, 생각보다 시간이 오래걸렸습니다. 이메일 검사, 문구 출력, 여러 이벤트들이 있었는데, 단순히 생각하지말고 구현할 때, 여러 부분들을 꼼꼼히 확인해야 한다는것을 알았습니다.


3. 유효성 검사는 같이하자!
유효성 검사는 혼자하는 것이 아님을 알게되었습니다. 최대한 기존 사이트의 기능들을 똑같이 하려했는데, 기존 사이트의 이메일 검사가 까다롭다는것을 알았습니다. 따라서 정규식을 기존 사이트에 맞게 작성한 후, 사용하였는데 중요한건 백에게 이 사실을 전달하지 못했다는 것입니다. 저의 유효성 검사는 까다롭지만 작성 후 백에게 전달 되는 이메일의 유효성 검사는 느슨하다는것이 말이 안되었습니다. 따라서 백엔드와의 소통을 통해 서로 같은 정규식을 사용하는 것으로 해결하였습니다.


4. 마스킹은 백에게
존재하는 이메일로 로그인 시, 회원의 이름 정보를 받아 온후 마스킹 처리하여 환영메시지를 출력하도록 구현하였습니다. 그러나, 이러한 절차는 백이 먼저 마스킹 처리를 한 후 보내주어야 보안상으로 안전하다는 것을 알았습니다. 이미 구현이 완료된 이후에 이 사실을 알았기에, 나중 로그인 페이지를 구현할 시, 이 점을 꼭 염두해두어야겠습니다.

회원가입 페이지

구현 기능

  • 각 input마다의 유효성 검사
    👉 입력 창에 변화가 일어날 때 마다 각각 정규식을 통한 유효성 검사를 하여 해당하는 문구 추가
    👉 입력 시, 바로 에러 문구가 뜨는 것을 방지하기 위해, state를 사용하여 onBlur시 작동하도록 설정
    👉 유효성 검사 통과 시, 에러 문구 미 출력 및 체크 아이콘 출력

  • 비밀번호 입력창
    👉 우측 토글을 이용하여 입력한 비밀번호 확인 가능

  • 이메일 인풋
    👉 로그인 페이지에서 입력한 이메일을 전달 받아, 인풋창에 출력
    👉 본 페이지와 동일하게 인풋 클릭 시, 바로 수정이 아닌 로그인 페이지로 이동
    👉 이때, 클릭하여 로그인 페이지로 이동 시, 로그인 페이지의 인풋창에 이메일이 그대로 뜨도록 다시 전달해줌

  • 휴대번호 입력시 자동 하이픈
    👉 입력 때 마다, 작성한 로직에 따라, 하이픈이 추가된 상태로 state를 업데이트
    👉 휴대번호 입력창 출력단 하이픈 실시간 출력
    ex) 010 , 010-1, 010-17 ....
    👉 백엔드에게 데이터 전송시에는 정규식으로 하이픈 제거

  • 컴퍼넌트화 및 함수 통합
    👉 여러 인풋 컴퍼넌트에서 하나의 함수를 연결하여 각 인풋컴퍼넌트에서 에러메시지와 정규식을 관리하여 유지보수에 용이하도록 구현

  • 회원가입
    👉 회원가입 완료 시, 로그인 창으로 이동

나의 성장 포인트

1. 여러 input 관리하기
input들이 여러개 있어서, 하나의 인풋 컴퍼넌트에서 관리를 할 수 있을지 고민해봤습니다. 직접 작성을 해본 결과는 가독성, 유지보수, 효율성 모두 떨어지는것으로 판단해 다시 작성한 코드들을 되돌렸습니다.
그 이유로는

  const typeDatas = {
    password: {
      name: inputType,
      type: viewPassword ? 'text' : 'password',
      korean: '비밀번호',
      invalid: '6자리 이상의 비밀번호를 설정해 주세요.',
      toggleEvent: true,
      placeholder: '비밀번호 (6자 이상)',
      validReg: PASS_REG,
      maxLength: 20,
    },
    phoneNumber: {
      name: inputType,
      type: 'text',
      korean: '휴대폰번호',
      invalid: '휴대폰번호를 올바르게 입력해주세요.',
      placeholder: `휴대폰 번호 (' - ')제외`,
      validReg: PHONE_REG,
      maxLength: 13,
    },
  };

위는 도전해본 코드의 일부입니다. 취소한 이유는 아래와 같습니다.
😂 각 단에 출력될 인풋들이 모두 달라서 결국 미리 모두 선언해주어야 했고 그렇다면 각 컴퍼넌트에서 관리하는것이 더 편리하지 않을까란 생각을 했습니다.
😂 return부분에서도 각 인풋에 따라 검사해야할 조건들이 너무 많았습니다.
각각의 인풋에 해당하는 함수와 state들이 있기에 (ex. password는 토글, 전화번호는 하이픈 등...) 각각의 컴퍼넌트에서 관리하는 것이 편하다고 생각하였습니다.
😂 결론적으로 가독성, 유지보수, 효율성 모두 떨어졌고, input들이 더욱 늘어난다면......... 😱


따라서 각각의 input들과 그 input에 해당하는 이벤트, 에러메시지, 유효성검사 정규식들을 각각의 컴퍼넌트에서 관리하도록 구현하였습니다.
또한 input들에서 공통적으로 사용하는 함수들(input 업데이트, blur확인, 유효성 검사 후 에러 메시지 출력 함수 등...)을 상위에 하나로 놓아 하나의 함수로 여러 input컴퍼넌트들을 관리하도록 구현하였습니다.

ex) 유효성 검사 후 에러메시지 출력 함수
->
  function inputVaildMsg(type, validReg, emptyMsg, errorMsg) {
    if (validStartList[type] === true) {
      return signUpinput[type] === ''
        ? emptyMsg
        : !validReg.test(signUpinput[type])
        ? errorMsg
        : (validManagement[type] = true);
    }
  }

이러한 구조로 구현후 되돌아보며, InputList 컴퍼넌트를 만들어 여러 input들과 공통 함수를 포함시키게 하였으면 좋았을텐데라는 아쉬움이 남았습니다. 프로젝트의 기한이 충분했다면, 더 고민해보고 더 나은 코딩을 하였을텐데요.


프로젝트를 하며, 좋았던 점

  • 혼자가 아닌, 팀이기에 배울 수 있던것들이 있었습니다.
    같은 결과물도 사람에 따라 여러 로직으로 구현되게 됩니다. 내가 맡은 페이지가 아니더라도, 팀원들이 구현한 기능 및 페이지들을 보며, 그들의 구현 방향을 볼 수 있고, 로직 및 컨벤션, 구조를 파악할 기회가 있었습니다. '나라면 어떻게 구현했을까' 라는 생각을 가지고 다른 사람의 코딩을 이해하고 배울점들을 찾고, 내것으로 만들기 위해 노력하였습니다. 결국 팀의 성장은 저의 성장으로 이어진다는것을 깨달았습니다.

  • 통신에 대해서 알게되었습니다.
    지금까지는 혼자 구현을 하였고, 통신에 대해서는 간략히만 알고있었습니다. 그러나 이번 프로젝트를 통해, 어떻게 백에게 데이터를 주는지, 그리고 어떻게 받아오는지, 토큰은 어떻게 로컬스토리지에 저장하며 사용하는지, 우리가 사용하게 될 데이터는 어떠한 구조인지에 대해 깨닫게 되었습니다. 처음 통신을 테스트하였을 때의 그 걱정과 설렘은 이제 자신감으로 바뀌어, 다음 프로젝트때 더욱 유연하고 완성도 높은 통신을 할 수 있을거라는 기대감이 생겼습니다.

  • 더 광범위한 생각을 할 수 있었습니다.
    백에게 데이터를 받아 와서, 구현할 수 있는것은 많았습니다. 상품의 데이터 수를 받아와, 상품을 더 담을 경우 재고 부족을 할 수도 있었고, 다른 페이지의 경우 재고가 없을 경우 일시품절의 이미지로 변경하는 등 여러 기능을 추가로 구현할 수 있었습니다. 따라서, 다음 프로젝트 때는 데이터를 더욱 유연하게 가공하여 어떠한 상황을 연출할 수 있고, 어떠한 기능들을 추가로 구현할 수 있는지에 대해 생각의 범위를 넓힐 수 있는 기회를 가지게 되었습니다.

  • 더욱 나은 개발자가 될 수 있는 기회였습니다.
    여러 페이지들을 구현하며, 나의 코드에서 불필요한 점이 많았고, 이러한 점들을 다시 되돌아 보며, 더욱 나은 코딩을 할 수 있도록 받아들이고 개선하였습니다. 기본적인 컨벤션부터 구조까지 저의 코드 하나 하나를 더욱 신경쓰며 똑같은 개발을 하더라도, 남들 보다 더욱 완성도 높은 코딩을 할 수 있도록 노력할것입니다.

  • 소통과 협력에 대해서 알게되었습니다.
    이번 프로젝트로 개발은 혼자가 아닌 팀으로 이루어지며, 프론트와 백이 따로가 서로 긴밀이 소통하고 협력하여 이루어진다는것을 배웠습니다. 서로 어떠한 데이터를 주고 받을 지에 대한 소통, 컨벤션과 유효성검사에 대한 통일, 전체적인 데이터 구조 등 끊임없이 소통하였고 협력하였습니다. 앞으로 있을 프로젝트와 협업, 그리고 평생의 개발동안 소통과 협력은 매우 중요하고, 계속해서 커뮤니케이션 능력을 키워나가야 한다는것을 깨달았습니다.

프로젝트를 하며, 아쉬웠던 점

  • 짧은 프로젝트 기간
    프로젝트의 기한이 짧아, 추가로 구현해야할 페이지들을 구현하지 못했고, 리팩토링에 많은 시간을 투자하지 못했습니다. 프로젝트를 하며 '일주일만 더 있었더라면, 아니 하루만 더 있었더라면'라는 생각을 많이 했습니다. 그래서 팀원의 대다수가 밤을 샌 적도 있었고, 더욱 여유가 없어진 모습이 있었습니다. 그래도 모두 최대의 노력으로 만족스러운 결과물을 내었고, 나중에도 언제든 짧은 기한의 프로젝트를 받을 수 있기에, 다음 프로젝트부터는 더 신속히, 더 열정 가득히 코딩을 하고 대신 더 클린하고 정확히 코딩을 하여 프로젝트의 기한과 완성도 두 마리의 토끼를 잡을 수 있도록 노력해야겠습니다.

  • 백과의 소통 늘리기
    백과 소통할 기회가 더 많이 있었다면 좋겠다는 생각이 들었습니다. 백이 전해주는 데이터의 컨벤션이 바뀌거나, 혹은 내가 데이터가 추가로 필요 할때, 추가로 구현할 기능들이 생겼을 때 바로바로 소통하지 못하거나, 초반의 소통이 부족했다는 생각이 들었습니다. 이러한 점을 해결하기 위해서, 다음 프로젝트 때는 처음 설계때 정확히 기능들을 파악해 필요한 데이터들이 어떤것들인지, 그리고 어떠한 상황에서 어떠한 내용들을 전달해주자는 상의를 해, 작업의 효율성을 늘리도록 해야겠습니다.

  • 객관적으로 보기
    처음 페이지들을 맡았을 때, '어? 이건 이렇게 하면 되고, 저건 저렇게 하면 되겠는데? 금방 끝내겠는데?'라는 생각을 하였는데, 큰 오산이였습니다. 디테일한 기능들도 있었고, 백과 통신할 시간들도 필요했고, 초기 세팅 및 구조에 대해서 상의할 시간들도 필요했습니다. 그리고 생각과 달리, 구현이 쉽지 않은 부분도 있었기에, 결론적으로 사이트, 그리고 저 자신을 객관적으로 보지 못했습니다. 다음 프로젝트때는 어떠한 기능들이 있는지, 어떠한 데이터들이 필요한지, 얼마나 시간이 걸리고 어떻게 구현을 할지에 대해서 깊게 생각해보고 sprint 및 티켓도 무리하지 않고, 더 정확하게 관리하여 저의 객관성을 키우도록 노력하겠습니다.

전체적인 페이지 영상과 함께 마무리


2주란 시간이 매우 빠르게 지나갔습니다. 이번 프로젝트때 배운 모든 것들이 저의 밑거름이 되어, 더욱 성장할 수 있도록, 더욱 좋은 개발자가 될 수 있도록 거듭나겠습니다!

0개의 댓글