2차 프로젝트 회고 _ KOOTED

Minji Jeong·2021년 10월 31일
1

Project

목록 보기
3/3
post-thumbnail

빠르고도 알찬 2주가 지나 벌써 2차 프로젝트가 끝났다!
두 번째 프로젝트라서 그런지, 1차 프로젝트 때보다 더 완성도 있는 결과물을 만들고 싶어서 더 열정적이었던 것 같다.
특히 원티드는 내가 만들어보고 싶다고 제안했었던 사이트였고, 팀장까지 맡게되어 더 책임감을 가지고 이번 프로젝트를 진행했다.
그럼 지금부터 2주 동안 쌓인 나의 경험을 한 번 정리해보자 🙌

프로젝트 기획

프로젝트 시작 첫째 날에 팀원들과 원티드 사이트를 분석하면서, 어떤 기능을 필수적으로 구현할지 의논하는 시간을 가졌다.

  • 필수: 소셜로그인, 채용 공고 확인 및 필터링, 직군별 연봉 그래프, 이력서 작성 및 지원 기능, 마이페이지

  • 추가: 회사 위치 지도 API, 채용 공고 북마크

이력서를 작성하는 부분은 직접 이력서 입력 폼을 만들어 사용자로부터 입력을 받게 하는 방식을 쓸지, 아니면 파일을 업로드하는 방식을 쓸지 고민을 많이 했었다.
이번에는 입력 폼을 만드는 방식을 썼는데, 다음에 파일 업로드 방식도 꼭 구현해보고싶다.

일정 관리

1차 프로젝트 때 통신을 2주차 부터 시작했더니, 통신을 맞춰보면서 변경되는 사항들이 많아 일정이 밀렸었다. 이번에는 이런 사태를 방지하기 위해!

1주차 때 백엔드에서 구현이 완료된 부분을 우선적으로 통신해보고, 이를 바탕으로 프론트/백에서 수정되어야 할 사항들을 미리 인지해서 합의점을 찾는 작업을 했다.

따라서 1주차 때 레이아웃과 각 페이지 개별 통신이 완료되었고, 2주차 때는 기능 구현에 집중할 수 있었다.
우리팀 Trello이다!
1차 때는 Trello에 신경을 많이 못써서, 이번에는 Trello만 봐도 서로의 진행상황을 알 수 있도록 관리하려고 노력하였다.
레이아웃, 기능, 통신 단위로 세분화해서 카드를 만들었다.
작업을 하나 마무리 할 때마다 DONE으로 옮기는게 너무 기분이 좋았다 🤗

GitHub

저번 프로젝트 때, 기능을 구현하는데만 집중한 나머지
브랜치 안에서 브랜치를 생성하거나 리뷰를 제대로 반영하지 못하는 등,
GitHub의 장점을 제대로 활용하지 못햇던 아쉬움이 있었다.

이번에는 Branch와 각 PR 관리도 깔끔하게 하고 싶었고, 리뷰를 통해 코드 리팩토링도 더 꼼꼼하게 하고싶었다!

PR을 올릴 때 마다 이번 브랜치에서 어떤 기능을 작업했고, 어떤 부분을 수정하였는지, 그리고 어떤 점을 배울 수 있었는지 자세히 작성하려고 노력했다.
PR을 통해 코드 리뷰를 하면서, 내 코드의 문제점들을 발견할 수 있었다.

  1. 무작정 적은 css 코드
    font-size: 16px, flex-direction: row, 블록 요소에 width: 100% 등, 없어도 될 코드들을 적은 것이 꽤 많았다.
  2. 컴포넌트의 최상위 태그 className
    loginWrapper, resumeContainer 이런 방식으로 작명했었다.
    최상위 태그는 css를 nesting 할 때, 다른 컴포넌트에 영향을 미치지 않게 하기 위해 유니크하게 지어주는 것이 좋다. 따라서 ResumeListItem, Main 처럼 해당 컴포넌트의 이름과 동일하게 지었다.
  3. 무작정 <div>
    이부분도 <section>, <footer> 같은 semantic tag를 이용해서,
    tag를 보면 어떤 역할을 하는 부분인지 알 수 있도록 하였다.

이렇게 리뷰를 꼼꼼히 적용하다 보니, push 하기 전에 내 코드를 한 번씩 더 확인하게 되었다.

구현한 기능

메인페이지, 이력서 작성/수정/삭제 페이지, 그리고 마이페이지를 맡게 되었다.

메인페이지

메인페이지는 기능보다는 레이아웃 중심이어서, 로직을 생각하는 시간보다는 JSX와 CSS 코드를 리팩토링 하는 시간이 많았다.

유저가 로그인 하기 전에는 로그인/회원가입 버튼을, 로그인 한 후에는 마이페이지로 가는 버튼을 띄워주기 위해 조건부 렌더링이 필요했는데,
localStorage.getItem("token")으로 토큰 값이 있는지 없는지로 구분을 하였다.

이력서 리스트 페이지

이력서 생성

이력서 삭제


이력서 리스트 페이지 작업을 시작했을 때, 서버에서 GET으로 리스트만 불러와서 띄워주면 되는 간단한 작업이라고 생각했다.
각 이력서를 클릭했을 때, 이력서 작성 페이지로 넘어가는 <Link>를 걸어주는 작업에서 한가지 간과했던 부분이 있었음을 발견했다.

새 이력서를 작성할 때나, 기존 이력서를 수정할 때나 똑같은 Resume 컴포넌트를 사용한다. 그러나 각 기능에 따라서 호출해야하는 API 메서드도 달랐고, 이동해야 하는 URL도 달랐다.

새 이력서 생성
회원 정보 GET, 작성 내용 POST, /resume로 이동

기존 이력서 수정
회원 정보 및 작성했던 내용 GET, 수정한 내용 PUT,
/resume/${resumeID}로 이동

resumeList 컴포넌트에서 resume 작성 컴포넌트로 create인지 update인지를 알 수 있는 state값을 넘겨주면 된다. 그래서 <Link> 대신, props.history.push를 사용하여 다음과 같이 구현하였다.
그리고 Resume 컴포넌트로 넘어왔을 때, location.state의 값이 create이면 POST 메서드를, update이면 PUT 메서드를 사용하여 통신을 하였다.

이력서 작성 페이지 _ 레이아웃

레이아웃을 만들 때, 반복되는 부분 때문에 코드가 정말 난잡해졌다 🤯
가독성도 떨어지고, 반복되는 부분은 컴포넌트를 따로 만드는게 좋아서 분리를 하였다. 그런데, 반복되는 컴포넌트 안에 들어가는 레이아웃들은 모두 달랐기 때문에 이를 어떻게 해야할지 고민을 많이 했다.
아래처럼 ResumeForm이라는 컴포넌트를 만들어서 공통되는 부분을 분리하였다. 그럼 공통 컴포넌트 안에 들어가는, 반복되지 않는 부분은 어떻게 해야할까?
반복되지 않는 부분은, 그 JSX가 들어갈 자리에 {props.children}을 적어주면 된다.
위의 코드를 보면, 공통된 부분은 <ResumeForm>을 호출하여 그에 맞는 내용을 props로 넘겨주고, 바뀌는 부분은 children 자리에 해당 JSX를 적어준 것을 볼 수 있다. 컴포넌트 재사용에 대해서 확실하게 알고 넘어갈 수 있었다.

이력서 작성 페이지 _ 통신

이력서 작성 기능을 만들면서 최대 난관은 수정 기능이었다.

  1. 원래 작성된 내용을 서버에서 가져온다
  2. 이를 input 형식에 맞게 데이터를 가공해서 넣어준다.
  3. input에서 onChange 함수가 발생했을 때
    (사용자가 새로운 내용을 입력했을 때),
    이 값을 e.target.value로 받아와 state값에 set한다.
  4. state값에 저장된 새로운 내용들을 다시 DB 저장 형태에 맞게 가공하여 PUT으로 전송한다.

클라이언트에서 입력하는 형식과 DB에 저장되는 형식이 동일하면 조금 덜 어려웠겠지만,
입사/퇴사 날짜같은 경우 입력받을 때는 월/일 모두 따로 받는데, 서버에 보낼 때는 2020-08-01의 형식으로 보내주어야 되서 아주 대혼란이 왔었다 😫
undefined값이 보내지거나, 아니면 정상적으로 수정은 되는데 onChange가 안먹히거나... 진짜 시간을 많이 투자한 페이지였다.

로직을 모르는 것은 아닌데, 데이터가 왔다 갔다 하면서 복잡해지니까 더 어려웠었던 것 같다. 그래서 코드를 치는 시간보다 노트에 흐름을 정리하고 생각하는 시간이 길었다.

마이페이지

지원하기

북마크

마이페이지는 사용자 정보, 이력서 지원 내역, 북마크 리스트를 서버에서 GET만 해서 띄워주면 되었었다.
이력서 작성 하는 부분에서 통신을 많이 구현하면서 익숙해져서 그런지,
비교적 쉽고 빠르게 끝낼 수 있었던 페이지였다.

북마크를 클릭하면, 해당 채용 공고로 이동할 수 있게 구현하였다.
그래서 북마크 아이템 컴포넌트를 <Link></Link>로 감싸주고, /detail/${공고ID}로 이동할 수 있도록 하였다.

동적 라우팅도 1차 프로젝트 때는 엄청 헷갈렷었는데, 이번 프로젝트에서 많이 사용하다 보니까 익숙해졌다.
그리고 구조분해할당을 할 때, 기존에는 const에 할당하는 방식을 사용했었다.

class BookMarkItem extends React.Component {
  render() {
    const {id, image, title} = this.props;
    return;
  }
}

이번에 React Hook 사용으로 함수형 컴포넌트를 쓰면서, 더 좋은 방법을 사용할 수 있었다. 함수형 컴포넌트는 인자값으로 props가 넘어오는데, 함수의 인자 부분에서 바로 할당을 해 줄 수 있었다.

const BookMarkItem = ({id, image, title}) => {
  return;
}

컴포넌트 상단에서 어떤 값이 넘어오는지 명시적으로 알 수 있었고, 구조분해할당 하는 코드도 줄어들어서 너무 편리했다.

회고의 마무리!

2차 프로젝트는 만들고자 한 기능들을 모두 마무리 할 수 있었다.
또한, 백엔드와 프론트엔드의 원활한 소통 덕분에 서로의 역할에 대해서 더 잘
이해할 수 있었다. 양 쪽이 함께 노력해서 그런지, 프로젝트의 완성도를 높힐 수 있었다.

팀장의 역할도 익숙해진 것 같다.
Daily meeting때 마다 어떤 내용을 공유해야 할 지 매번 헷갈리고 낯설었는데,
이번에는 전체적인 진행상황과 서로의 블로커를 적극적으로 공유하여 팀원 각자가 무슨 일을 해야 하는지 확실하게 알았던 것 같다.

주단위의 스프린트 계획을 지키기 위해서, 각자의 일을 다 끝내면 서로 팀원을 돕기도 했다. 서로 돕다보니 상황을 이해할 수 있고, 좋은 코드가 있으면 공유할 수 있어서 모두 배우는 것이 많았던 것 같다.

프로젝트 시연 영상을 찍는데, 지긋지긋 했던 fetch 에러 없이 프로그램이 잘 돌아가서 너무 뿌듯했다.
팀원 모두가 KOOTED를 위해 2주간 함께 미친듯이 노력했었다 👻
이렇게 다같이 하나의 목표를 위해 몰두한 경험이 너무 좋았고,
앞으로 다른 프로젝트를 할 때도 이 경험이 도움이 많이 될 것 같다.

프로젝트 결과물

KOOTED GitHub
KOOTED 시연 영상

profile
쿼카를 사랑하는 프론트엔드 개발자입니다 :)

0개의 댓글