[1차프로젝트] 회고 및 내 코드리뷰

박성수·2022년 10월 29일
1

1차프로젝트 시연영상

https://youtu.be/wxar1wj1cNQ

개인회고

프로젝트를 시작하며

내가 웹개발을 시작하고 처음으로 진행했던 프로젝트였다. 프로젝트에 들어가기 전 주말부터 어떤 팀을 만날지에 대한 기대와 ‘내가 잘 할 수 있을까?’ 라는 걱정감이 컸다.

프로젝트 팀이 발표되고 미팅을 진행하고 역할을 분담하는 과정에서 팀원들이 서로 소극적이어서 진행이 더뎠다. 조금 더 많은 역할을 맡아서 많이 성장하고 싶은 욕심도 있었지만 최대한 공정하게 분배를 하려고 했고 그렇게 됐던 것 같다.

진행과정과 마주했던 벽

  • 회원가입페이지

나는 회원가입 페이지 아이템 리스트페이지 장바구니페이지를 담당했다. 회원가입 페이지에서는 크게 어려웠던 부분은 없었다. 백엔드와 첫 소통을 회원가입 페이지에서 했고, 첫 시도만에 계속 성공하지는 않았지만 순조롭게 오류를 잡아가면서 회원가입 기능까지 완료했다.

  • searchParams

처음 만났던 벽은 아이템리스트페이지 였다. 상품을 한번에 받아오는 건 쉬웠지만, 페이지네이션이나 필터링을 구현하는데 2~3일 정도 구글링도 많이해보고 useSearchParams사용 방법에 대해서도 많이 고민했다. 멘토분들께도 물어봤지만 원하는 답변은 아니었고, 내 나름대로의 방식을 찾아서 적용시켰던 것 같다.

  • 모달창 닫기

다음 만났던 벽은 모달창이었다 모달창의 외부를 클릭하면 닫혀야하는데, 그걸 구현하는게 어려웠다. useRef로도 적용해보았고 결국은 closest이벤트로 가장 가까운 클래스네임을 찾아 다르다면 모달창이 닫히도록 했다.

전체 페이지에서 이벤트를 부여해야하는데 이런 방식으로는 map으로 반복되는 컴포넌트에 적용하는 것은 불가능했다. (동시에 열리고 동시에 닫히는 현상, 혹은 컴포넌트 내부의 다른곳을 클릭해야만 닫히고 컴포넌트 외부에서는 동작하지 않음) 이는 Ref로 돔에 직접 접근해서 닫히게끔 이벤트를 부여해야하는데,, 2차프로젝트때는 더 공부해서 적용시켜봐야겠다.

회고

나는 이번프로젝트때 내가 스스로 문제를 해결하려고 노력하고 많은 시간을 쏟아 부었다고 생각한다. 이 과정에서 절망감도 많이 느끼고, 성취감도 많이 느꼈다. 아직 개발문화를 다 느낀건 아니겠지만 ‘이런게 개발자의 삶이구나’ 라는 생각도 들고 ‘이런걸 평생 할 수 있을까?’라는 생각도 들었다.

첫 주에는 첫 프로젝트라는 긴장감과 설렘에 더 많은 작업을 하고 미리 끝내 놓으려고 스프린트를 진행했지만, 두 번째 주에는 이미 많은 것을 끝내놓은 뒤라 조금 루즈해지는 모습을 보였다. 만약 이때 조금더 긴장감을 갖고 오류를 더 수정하거나, 완성도를 더 높이는 작업을 했으면 어땠을까? 라는 반성을 한다.

팀원들은 프로젝트를 진행하며 나에게 든든함을 느꼈다고 한다. 내가 회의 마치고 노션도 정리하고 이것저것 먼저하려고 하긴했지만 이렇게 느껴주는 팀원들에게 감사했다.

조금 더 다른 팀원들 신경을 썼다면 어땠을까 라는 아쉬움도 있었다고한다.

다른 사람을 먼저 도와주려는 성향은 있지만, 상대방이 싫어하거나 부담스러워할 수도 있기에 먼저다가가지 않았던 것 같다. 다음부터는 조금 더 도움을 주려고 다가가봐야겠다.

2차프로젝트를 앞두고 다짐

조금은 한번에 스프린트를 하지말고 이주에 맞는 계획을 나눠서 실행한다면 끝까지 힘이 빠지지 않고 스프린트를 진행할 수 있지 않을까.. 1주차에 너무 많은 힘을 빼지말자!

조금 진행속도가 느린 팀원들이 있으면 조금은 적극적으로 나서서 도와주려는 모션을 취해서 다같이 성장하는 분위기를 이끌어야겠다.

코드리뷰

1) 셀렉트 박스(div로 구현한)에서 숫자를 변경하면 장바구니의 수량이 변경되는 PATCH메서드

기존의 코드의 문제점 > state에서 관리하던 numberOfShoe가 비동기적으로 실행되며, 선택 값을 변경했을 때 이전의 값으로 업데이트 되는 현상.

내가 접근한 해결법 : 선택한 값을 저장하는 변수를 선언한 뒤, 그 변수를 fetch함수에 인자로 넘겨주고 그 변수를 보내는 방법

const numberOfShoeClick = e => {
    const count = e.target.value;
    setNumberOfShoe(e.target.value);
    setIsSelect(false);
    changeStock(count);
  };

const changeStock = count => {
    fetch(
      `${api.carts}?cartId=${data.cartId}&count=${count}&stock=${data.stock}`,
      {
        method: 'PATCH',
        headers: {
          'Content-Type': 'application/json;charset=utf-8',
          authorization: localStorage.getItem('token'),
        },
      }
    )
      .then(res => res.json())
      .then(data => {
        if (data.message === 'FAILED') {
          alert('재고가 없습니다.');
        }
        window.location.reload();
      });
  };

멘토님의 방법 : fetch함수를 함수에 담지 않고 useEffect에 담고 numberOfShoe를 의존성 배열에 추가한다!

//유진멘토님의 조언

const numberOfShoeClick = e => {
    const count = e.target.value;
    setNumberOfShoe(e.target.value);
    setIsSelect(false);
  };

useEffect(() => {
    fetch(
      `${api.carts}?cartId=${data.cartId}&count=${numberOfShoe}&stock=${data.stock}`,
      {
        method: 'PATCH',
        headers: {
          'Content-Type': 'application/json;charset=utf-8',
          authorization: localStorage.getItem('token'),
        },
      }
    )
      .then(res => res.json())
      .then(data => {
        if (data.message == 'FAILED') {
          alert('재고가 없습니다.');
        }
        window.location.reload();
      });
  }, [data.cartId, data.stock, numberOfShoe]);

2) 정규표현식

const emailRegex =
    /^[0-9a-zA-Z]([-_\.]?[0-9a-zA-Z])*@[0-9a-zA-z]([-_\.]?[0-9a-zA-Z])*\.[a-zA-Z]{2,3}$/;
const passwordRegex =
    /^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#\$%\^&\*])(?=.{8,})/;

//각각 email, password정규식 
//email > @포함 . 뒤에 2자이상
//password > 숫자, 영자, 대문자, 특수문자 8자 이상

emailRegex.test('입력된 값') 
//다음과 같은 식으로 입력된 값이 해당 정규식을 통과하는 값인지 판단할 수 있음 true or false반환

const priceToString = price => {
    return price.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
  };

//숫자가 안에 들어오면 천의 자리마다 ','를 찍어주는 함수

priceToStiring(1000000)  //1,000,000반환

3) searchParams사용

const [searchParams, setSearchParams] = useSearchParams();
const offset = searchParams.get('offset');
const limit = searchParams.get('limit');
const sort = searchParams.get('sort');

useEffect(() => {
    fetch(
      `${api.products}/${params.gender}/${params.category}?sort=${sort}&offset=${offset}&limit=${limit}`,
      {
        method: 'GET',
        headers: { 'content-type': 'application/json' },
      }
    )
      .then(res => res.json())
      .then(res => setShoesData(res.data));
  }, [params.gender, params.category, limit, offset, sort]);

//의존성 배열이 바뀔 때 마다 Back에서 데이터를 요청함

const pagination = pagingNum => {
    searchParams.set('offset', (pagingNum - 1) * EIGHT);
    searchParams.set('limit', EIGHT);
    setSearchParams(searchParams);
  };
//페이지 버튼을 누르면 몇 번째 버튼을 눌렀는지 확인 후 그 이후 8개 아이템을 가져옴

const sortReset = () => {
    setSearchParams({ sort: '', offset: 0, limit: EIGHT });
  };
//모두지우기 버튼을 누르면 sort를 비우고 0번째부터 8번째 아이템을 가져옴

const clickSort = isSelected => {
    setSearchParams({ sort: isSelected, offset: 0, limit: EIGHT });
  };
//isSelected인자를 받고 sort를 isSelected로 받음
//isSelected인자의 이름은 new, high, low 중 하나
//필터링하는 부분

const [isSelected, setIsSelected] = useState({
    new: false,
    low: false,
    high: false,
  });
//isSelected의 초기값 셋 다 false

const clickReset = () => {
    setIsSelected({
      new: false,
      low: false,
      high: false,
    });
    sortReset();
  };
// 모두지우기 버튼을 누르면 세 값 모두 false로 바꾼 후 searchParams의 값도 초기로 세팅해 줌

const handleSelected = event => {
    const { name } = event.target;
    setIsSelected({
      new: false,
      low: false,
      high: false,
      [name]: !isSelected[name],
    });
    clickSort([name]);
  };

// 버튼을 누르면 해당 버튼의 이름을 받아와 그값을 반대로 바꿈 
// (바꾸기 전에 모두 false로 바꿈) 세 버튼 중 하나만 선택되어야 하기 떄문
// clickSort()에 [name]을 받아 인자로 넣어줌 그러면 버튼 클릭시 각각 new, low, high가 인자로 전달
// clickSort에 의해 sort의 값으로 new, low, high가 들어가고 그에 해당하는 데이터를 요청함

다음과 같이 개선 가능…유진멘토님의 조언…

profile
Front-end Developer

0개의 댓글