[우아한테크코스 4기] 최종 코딩테스트 리팩토링

최종 코딩테스트를 마치고, 마저 기능 개발 - 리팩토링을 하는 과정을 기록하고자 합니다.

크루 관리 - 리팩토링

프론트엔드 - 백엔드 크루원을 추가하는 함수의 중복

아래 함수는 controller 에서 crew원들을 추가하는 로직을 담당하는 함수이다. frontend, backend를 구분하여 함수를 두 개 작성하였는데, 이 경우

  • 수정 사항을 두 함수에게 반영해야한다.
triggerAddFrontendCrew(input) {
    const frontendCrewList = this.$crewModel.getFrontendCrewList();
    const newCrew = this.makeNewCrew(input, frontendCrewList);
    if (newCrew) {
      this.mutateModelWithNewFrontendCrew(newCrew);
      this.renderViewWithFrontendCrew();
    }
  }

  triggerAddBackendCrew(input) {
    const backendCrewList = this.$crewModel.getBackendCrewList();
    const newCrew = this.makeNewCrew(input, backendCrewList);
    if (newCrew) {
      this.mutateModelWithNewBackendCrew(newCrew);
      this.renderViewWithBackendCrew();
    }
  }

이를 하나의 함수로도 동작하게 리팩토링해보자.

1. Crew 모델에서의 중복 제거

❌ 변경 전

 getFrontendCrewList() {
    return this.getDataByKey(CREW_KEYS.FRONTEND);
  }

  getBackendCrewList() {
    return this.getDataByKey(CREW_KEYS.BACKEND);
  }

✅ 변경 후

 getCrewListByCourse(course) {
   // course : Proptypes.oneOf([CREW_KEYS.FRONTEND,CREW_KEYS.BACKEND])
    return this.getDataByKey(course);
  }

변경 전 코드는 함수 이름으로 어떤 크루의 리스트를 가져오는 지 알 수 있어 좋다. 코드가 짧으니 굳이 변경하지 않아도 될 것 같다는 생각이든다.

... 수정중

팀 매칭

이 부분은 코스와 미션을 선택하고 뷰를 띄우는 부분까지 기능 개발을 완료하였었습니다.

랜덤으로 팀을 만들어내는 함수를 작성하는데 시간이 걸리기도 하였고, 소감문을 작성하는데 계획해 둔 시간이 있어 코딩테스트 당시 전부 기능 개발을 하지 못해 다시 기능 개발에 들어가 보았습니다.

팀 매칭 - 기능 구현

코딩테스트에선 팀매칭 기능 구현을 하지 못했다. 크게 팀매칭의 두 가지 기능이 있는데, 이를 구현해보았습니다.

1. 팀을 랜덤하게 매칭한다.

// team 인원 수가 입력되면 실행되는 핸들러 함수이다.
onSubmitTeamMemberCountForm(e) {
    e.preventDefault();
  
  
    const crewList = this.$crewModel.getAllCrewList();
  
    const { count } = this.$inputsModel.getTeamMemberCountInput();
  
  
    try {
      if (isValidCount(Number(count))) {
        
        // 올바른 count가 입력되면
        
        // 새로운 팀을 만든다.
        const team = this.$teamModel.makeTeam(crewList, Number(count));
        // 새로운 팀으로 모델을 변형한다.
        this.mutateModelWithNewTeam(team);
        
        // 업데이트 된 모델로 뷰를 업데이트한다.
        this.renderViewWithNewTeam();
      }
    } catch (error) {
      alert(error);
    }
  }

  mutateModelWithNewTeam(team) {
    
    const { course, mission } = this.$inputsModel.getCourseAndMissionInput();
    this.$teamModel.setTeam(course, mission, team);
  }

  renderViewWithNewTeam() {
    const { course, mission } = this.$inputsModel.getCourseAndMissionInput();
    const team = this.$teamModel.getTeam(course, mission);
    this.renderViewMatchedTeam(course, mission, team);
  }

2. 매칭되어 있는 경우, 재매칭 버튼을 통해 재매칭 한다.

onClickRematchButton(e) {
    // console.log(e.target);
    // const {target: {}}
    const { course, mission } = this.$inputsModel.getCourseAndMissionInput();

 	
    // 네이밍 어떻게 하면 좋을까
    // 모델을 변형한다.
    this.mutateModelByRematch();
    // 업데이트된 모델을 바탕으로 뷰를 업데이트한다.
    this.renderViewNotMatchedTeam(course, mission);
  }

  mutateModelByRematch() {
    const { course, mission } = this.$inputsModel.getCourseAndMissionInput();
    this.$teamModel.clearTeam(course, mission);
  }

  renderViewNotMatchedTeam(course, mission) {
    const crewList = this.$crewModel.getAllCrewList();
    this.$view.renderNotMatchedTeam(course, mission, [...crewList]);
    this.bindNotMatchedTeamHandler();
  }

정리
팀 매칭 기능 구현을 완료하는데, 20분도 채 걸리지 않아 시간분배가 잘못되었음을 깨닫게 되었습니다.

  • 명확히 모델을 정의하고 넘어가는 것이 필요하다고 생각하게 되었습니다.( 개발 이후에 모델을 변경하고자 하니, 의외로 수정해야하는 부분이 많아 시간이 더 걸리게 되었습니다.)

  • 시간이 촉박하다보니, 하드 코딩을 하게 되었고 이 부분에서 문제가 발생하여 오히려 시간이 더 걸리게 되었습니다.

팀 매칭 - 리팩토링

1. Team 모델에서의 하드코딩을 지양하자

당시에는 빠르게 기능을 개발해야할 것 같아, 하드코딩을 수행하였습니다. 다음은 Team 모델의 defaultValue를 만들어내는 함수입니다.


generateDefaultValue() {
    return {
      [`${COURSE_SELECT_MAP.프론트엔드}-${MISSION_SELECT_MAP.결제}`]: null,
      [`${COURSE_SELECT_MAP.프론트엔드}-${MISSION_SELECT_MAP.베이스볼}`]: null,
      [`${COURSE_SELECT_MAP.프론트엔드}-${MISSION_SELECT_MAP.레이싱카}`]: null,
      [`${COURSE_SELECT_MAP.프론트엔드}-${MISSION_SELECT_MAP.로또}`]: null,
      [`${COURSE_SELECT_MAP.프론트엔드}-${MISSION_SELECT_MAP.장바구니}`]: null,
      [`${COURSE_SELECT_MAP.프론트엔드}-${MISSION_SELECT_MAP.지하철노선도}`]: null,
      [`${COURSE_SELECT_MAP.프론트엔드}-${MISSION_SELECT_MAP.퍼포먼스}`]: null,
      [`${COURSE_SELECT_MAP.백엔드}-${MISSION_SELECT_MAP.결제}`]: null,
      [`${COURSE_SELECT_MAP.백엔드}-${MISSION_SELECT_MAP.베이스볼}`]: null,
      [`${COURSE_SELECT_MAP.백엔드}-${MISSION_SELECT_MAP.레이싱카}`]: null,
      [`${COURSE_SELECT_MAP.백엔드}-${MISSION_SELECT_MAP.로또}`]: null,
      [`${COURSE_SELECT_MAP.백엔드}-${MISSION_SELECT_MAP.장바구니}`]: null,
      [`${COURSE_SELECT_MAP.백엔드}-${MISSION_SELECT_MAP.지하철노선도}`]: null,
      [`${COURSE_SELECT_MAP.백엔드}-${MISSION_SELECT_MAP.퍼포먼스}`]: null,
    };
  }

위와 같이 기본 값을 하드코딩 하게되니 다음과 같은 문제가 발생하였습니다.

1. 생성되지 않은 키가 존재 할 수 있습니다. 개발자가 모든 키를 직접 입력하다보니, 빠지는 키가 생길 수 있습니다.(예기치 못한 에러가 발생할 수 있습니다)

2. COURSE_SELECT_MAP, MISSION_SELECT_MAP에 변경을 가하게 되면, 위 함수도 같이 수정해야합니다. (유지보수 비용이 너무 커지게됩니다)

위와 같은 문제들을 방지하고자 다음과 같이 코드를 수정해보았습니다. Object.keys 메소드를 활용하여, 좀 더 선언적으로 코드를 변경해보았습니다. (COURSE_SELECT_MAP, MISSION_SELECT_MAP 객체에 따라 기본값이 생성되므로)

  generateDefaultValue() {
    const result = {};
    Object.keys(COURSE_SELECT_MAP).forEach((courseKey) => {
      Object.keys(MISSION_SELECT_MAP).forEach((missionKey) => {
        result[`${courseKey}-${missionKey}`] = null;
      });
    });
    return { ...result };
  }

다음과 같은 이점을 얻을 수 있었습니다.

1. 가독성이 올라간다. (객체의 키값으로 기본값의 키를 생성하는구나)

2. COURSE_SELECT_MAP, MISSION_SELECT_MAP 의 값을 변경하여도, 코드를 수정하지 않아도됩니다.

좋은 웹페이지 즐겨찾기