-
Notifications
You must be signed in to change notification settings - Fork 461
양홍주 미션 제출합니다. #161
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
양홍주 미션 제출합니다. #161
Changes from all commits
ad5c897
6d7874f
9ffa133
d7855f3
1a26d7a
00939ad
a56846d
84af92f
30137eb
51eb797
c8b9e13
8387cd1
4075c7e
c3c20c9
95981e3
f7420a4
16604fc
ea9fe48
a01b46c
d3be825
4051f41
464abd8
386b73c
2fa1c6b
f4130e3
a2eac25
22e2c6a
08d1d95
5d223d2
c554794
4a83a2b
26b2dcb
60b14ef
be11956
2a0ac29
10c7a42
284d263
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,121 @@ | ||
| [작성자] | ||
| 이름: 양홍주 | ||
| 메일: [email protected] | ||
|
|
||
|
|
||
| ## 구현할 "기능 목록"을 정리 | ||
|
|
||
| [페어 매칭] | ||
| - 페어 매칭에 필요한 크루들의 이름을 파일 입출력을 통해 불러온다. O | ||
| - src/main/resources/backend-crew.md과 src/main/resources/frontend-crew.md 파일을 이용한다. O | ||
| - 크루들의 이름 목록을 List<String> 형태로 준비한다. O | ||
| - 크루 목록의 순서를 랜덤으로 섞는다. 이 때 `camp.nextstep.edu.missionutils.Randoms`의 shuffle 메서드를 활용한다. O | ||
| - 랜덤으로 섞인 페어 목록에서 페어 매칭을 할 때 앞에서부터 순서대로 두명씩 페어를 맺는다. O | ||
| - 홀수인 경우 마지막 남은 크루는 마지막 페어에 포함시킨다. O | ||
| - 이 경우 한 페어는 3인으로 구성한다. O | ||
| - 같은 레벨에서 이미 페어로 만난적이 있는 크루끼리 다시 페어로 매칭 된다면 크루 목록의 순서를 다시 랜덤으로 섞어서 매칭을 시도한다. O | ||
| - 3회 시도까지 매칭이 되지 않거나 매칭을 할 수 있는 경우의 수가 없으면 에러 메시지를 출력한다. O | ||
|
|
||
|
|
||
| [페어 조회 기능] | ||
| - 과정, 레벨, 미션을 선택하면 해당 미션의 페어 정보를 출력한다. O | ||
| - 매칭 이력이 없으면 매칭 이력이 없다고 안내한다. O | ||
| - 이 때, 에러 메세지는 다음과 같다. "[ERROR] 매칭 이력이 없습니다." O | ||
|
|
||
|
|
||
| [페어 초기화 기능] | ||
| - 페어 매칭 정보를 모두 초기화시킨다. O | ||
|
|
||
| [사전 제공 정보] | ||
| - 과정 O | ||
| - 백엔드 O | ||
| - 프론트엔드 O | ||
| - 레벨 O | ||
| - 레벨1 O | ||
| - 레벨2 O | ||
| - 레벨3 O | ||
| - 레벨4 O | ||
| - 레벨5 O | ||
| - 미션 O | ||
| - 레벨1 O | ||
| - 자동차경주 O | ||
| - 로또 O | ||
| - 숫자야구게임 O | ||
| - 레벨2 O | ||
| - 장바구니 O | ||
| - 결제 O | ||
| - 지하철노선도 O | ||
| - 레벨3 (페어메칭 없음) O | ||
| - 레벨4 O | ||
| - 성능개선 O | ||
| - 배포 O | ||
| - 레벨5 (페어메칭 없음) O | ||
|
|
||
| - 크루 정보는 src/resources 하위에 md 파일로 제공된다. O | ||
| - 백엔드, 프론트엔드 각각 파일이 존재한다. O | ||
|
|
||
|
|
||
| [입력 화면] | ||
| - 사용자가 잘못된 값을 입력하면 IllegalArgumentException와 [ERROR]로 시작하는 에러 메시지를 출력 후 해당 부분부터 다시 입력을 받는다. O | ||
| - 사용자는 "기능 메뉴 선택" 정수 1개를 입력 O | ||
| - 1번은 페어 매칭, 2번은 페어 조회, 3번은 페어 초기화, Q는 종료 O | ||
| - 검증: 만약 없는 메뉴를 입력하면 잘못 입력한 것. O | ||
| - 오타 교정: 앞뒤로 공백을 제거해줌 O | ||
| - 사용자는 "과정, 레벨, 미션"을 ,로 구분하여 한 줄에 입력 O | ||
| - 검증: 없는 과정을 입력하면 잘못된 입력 O | ||
| - 검증: 없는 레벨을 입력하면 잘못된 입력 O | ||
| - 검증: 페어매칭이 없는 과정을 입력하면 잘못된 입력 O | ||
| - 검증: 선택한 과정에 입력한 미션이 없으면 잘못된 입력 O | ||
| - 오타 교정: 입력 항목 사이에 불필요한 ,는 자동으로 제거해줌 O | ||
| - 오타 교정: 전체 입력 문장에서 모든 공백을 제거해줌. O | ||
| - 이미 매칭 정보가 있는 경우 재매칭할지에 대한 정보를 입력받는다. O | ||
| - 사용자는 문자 하나를 입력한다. O | ||
| - 예를 선택할 경우 페어를 재매칭한다. O | ||
| - 아니오를 선택할 경우 코스, 레벨, 미션을 다시 선택한다. O | ||
| - 검증: 예, 아니오가 아닌 값을 입력하면 잘못입력한 것. O | ||
|
|
||
| [출력 화면] | ||
| - 과정와 미션을 출력 O | ||
| ``` | ||
| ############################################# | ||
| 과정: 백엔드 | 프론트엔드 | ||
| 미션: | ||
| - 레벨1: 자동차경주 | 로또 | 숫자야구게임 | ||
| - 레벨2: 장바구니 | 결제 | 지하철노선도 | ||
| - 레벨3: | ||
| - 레벨4: 성능개선 | 배포 | ||
| - 레벨5: | ||
| ############################################ | ||
| ``` | ||
| - 페어 매칭 결과 출력 O | ||
| ``` | ||
| 페어 매칭 결과입니다. | ||
| 이브 : 윌터 | ||
| 보노 : 제키 | ||
| 신디 : 로드 | ||
| 제시 : 린다 | ||
| 시저 : 라라 | ||
| 니콜 : 다비 | ||
| 리사 : 덴버 : 제키 | ||
| ``` | ||
|
|
||
|
|
||
| ## 커밋 컨벤션 | ||
|
|
||
| > 기본 형태 | ||
| ~~~ | ||
| type: subject | ||
|
|
||
| body | ||
| ~~~ | ||
|
|
||
| > type | ||
|
|
||
| feat: 새로운 기능 | ||
| fix: 버그 수정 | ||
| docs: 문서 수정 | ||
| style: 서식 지정, 세미콜론 누락 등 (코드 변경 없음) | ||
| refactor: 코드 리팩토링 | ||
| test: 테스트 코드 추가, 테스트 코드 리팩토링 (생산 코드 변경 없음) | ||
| chore: 빌드, 패키지 매니저 등 업데이트 (생산 코드 변경 없음) | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,19 @@ | ||
| package pairmatching; | ||
|
|
||
| import pairmatching.controller.MatchingController; | ||
| import pairmatching.repository.MatchingResultRepository; | ||
| import pairmatching.service.MatchingService; | ||
| import pairmatching.view.InputView; | ||
| import pairmatching.view.OutputView; | ||
|
|
||
| public class Application { | ||
| public static void main(String[] args) { | ||
| // TODO 구현 진행 | ||
| MatchingController matchingController = new MatchingController( | ||
| new InputView(), | ||
| new OutputView(), | ||
| new MatchingService(new MatchingResultRepository()) | ||
| ); | ||
|
|
||
| matchingController.play(); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,166 @@ | ||
| package pairmatching.controller; | ||
|
|
||
| import static pairmatching.domain.entity.Function.PAIR_CHECK; | ||
| import static pairmatching.domain.entity.Function.PAIR_MATCHING; | ||
| import static pairmatching.domain.entity.Function.PAIR_RESET; | ||
| import static pairmatching.domain.entity.Function.QUIT; | ||
| import static pairmatching.domain.entity.RematchingOption.YES; | ||
| import static pairmatching.messages.ErrorMessages.FAIL_TO_MATCH; | ||
| import static pairmatching.messages.ErrorMessages.INVALID_COURSE_MISSION; | ||
| import static pairmatching.messages.ErrorMessages.INVALID_FUNCTION; | ||
| import static pairmatching.messages.ErrorMessages.INVALID_REMATCHING_SELECT; | ||
| import static pairmatching.messages.ErrorMessages.NOT_EXIST_MATCHING_RESULT; | ||
|
|
||
| import java.util.Optional; | ||
| import pairmatching.domain.dto.MatchingResultMapper; | ||
| import pairmatching.domain.entity.Course; | ||
| import pairmatching.domain.entity.CourseMission; | ||
| import pairmatching.domain.entity.Function; | ||
| import pairmatching.domain.entity.MatchingResult; | ||
| import pairmatching.domain.entity.RematchingOption; | ||
| import pairmatching.service.MatchingService; | ||
| import pairmatching.util.InputUtil; | ||
| import pairmatching.view.InputView; | ||
| import pairmatching.view.OutputView; | ||
|
|
||
| public class MatchingController { | ||
|
|
||
| private final int REMATCHING_COUNT = 3; | ||
|
|
||
| private final InputView inputView; | ||
| private final OutputView outputView; | ||
| private final MatchingService matchingService; | ||
|
|
||
| public MatchingController(InputView inputView, OutputView outputView, MatchingService matchingService) { | ||
| this.inputView = inputView; | ||
| this.outputView = outputView; | ||
| this.matchingService = matchingService; | ||
| } | ||
|
|
||
| public void play() { | ||
|
|
||
| while (true) { | ||
| final Function function = inputValidFunction(); | ||
|
|
||
| if (function.equals(QUIT)) { | ||
| return; | ||
| } | ||
| processFunctionInput(function); | ||
| } | ||
| } | ||
|
|
||
| private void processFunctionInput(Function function) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 여기서 분기처리를 해주는 것은 어떤가요? 이런식으로요! 지금 방식은 모든 메서드에 다 들어가야해서 가독성이 떨어질 것 같습니다! |
||
| processPairMatching(function); | ||
| processPairCheck(function); | ||
| processPairReset(function); | ||
| } | ||
|
|
||
| private void processPairReset(Function function) { | ||
| if (function.equals(PAIR_RESET)) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 취향 차이일수 있는데 저는 |
||
| matchingService.resetMatching(); | ||
| outputView.ouputReset(); | ||
| } | ||
| } | ||
|
|
||
| private void processPairCheck(Function function) { | ||
| if (function.equals(PAIR_CHECK)) { | ||
| outputView.outputCourseMission(); | ||
| final CourseMission courseMission = inputValidCourseMission(); | ||
|
|
||
| checkMatchingResult(courseMission); | ||
| } | ||
| } | ||
|
|
||
| private void checkMatchingResult(CourseMission courseMission) { | ||
| Optional<MatchingResult> matchingResult = matchingService.findMatchingResult(courseMission); | ||
|
|
||
| if (!matchingResult.isPresent()) { | ||
| outputView.outputErrorMessage(NOT_EXIST_MATCHING_RESULT.getMessage()); | ||
| return; | ||
| } | ||
| outputView.outputPairMatchingResult(MatchingResultMapper.from(matchingResult.get())); | ||
| } | ||
|
|
||
| private void processPairMatching(Function function) { | ||
| if (function.equals(PAIR_MATCHING)) { | ||
| outputView.outputCourseMission(); | ||
| final CourseMission courseMission = inputValidCourseMission(); | ||
| Course course = courseMission.getCourse(); | ||
|
|
||
| processExistResult(courseMission, course); | ||
| } | ||
| } | ||
|
|
||
| private void processExistResult(CourseMission courseMission, Course course) { | ||
| Optional<MatchingResult> foundMatchingResult = matchingService.findMatchingResult(courseMission); | ||
|
|
||
| if (foundMatchingResult.isPresent()) { | ||
| RematchingOption rematchingOption = inputValidRematchingOption(); | ||
| processRematching(rematchingOption, course, courseMission); | ||
| return; | ||
| } | ||
| processMatchingResult(course, courseMission); | ||
| } | ||
|
|
||
| private void processRematching(RematchingOption rematchingOption, Course course, CourseMission courseMission) { | ||
| if (rematchingOption.equals(YES)) { | ||
| processMatchingResult(course, courseMission); | ||
| } | ||
| } | ||
|
Comment on lines
+105
to
+109
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 아니오를 선택하면 코스,레벨,미션을 다시 고르도록 해야하는데, |
||
|
|
||
| private void processMatchingResult(Course course, CourseMission courseMission) { | ||
| Optional<MatchingResult> nonDuplicatedResult = createNonDuplicatedResult(courseMission, course); | ||
|
|
||
| if (!nonDuplicatedResult.isPresent()) { | ||
| outputView.outputErrorMessage(FAIL_TO_MATCH.getMessage()); | ||
| } | ||
|
|
||
| processNonDuplicatedResult(courseMission, nonDuplicatedResult); | ||
| } | ||
|
|
||
| private void processNonDuplicatedResult(CourseMission courseMission, Optional<MatchingResult> nonDuplicatedResult) { | ||
| MatchingResult matchingResult = nonDuplicatedResult.get(); | ||
| outputView.outputPairMatchingResult(MatchingResultMapper.from(matchingResult)); | ||
| matchingService.save(courseMission, matchingResult); | ||
| } | ||
|
Comment on lines
+121
to
+125
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Optional 처리를 service나 repository에서 하는 것이 좋을 것 같습니다! 근거로 JPA Repository의 getById 메서드를 들려고 했는데 Deprecated 됐네요... |
||
|
|
||
| private Optional<MatchingResult> createNonDuplicatedResult(CourseMission courseMission, Course course) { | ||
| int count = 0; | ||
|
|
||
| while (count < REMATCHING_COUNT) { | ||
| MatchingResult matchingResult = matchingService.createMatchingResult(course.getCrews()); | ||
| if (!matchingService.isDuplicatedPair(courseMission,matchingResult)) { | ||
| return Optional.ofNullable(matchingResult); | ||
| } | ||
| } | ||
|
|
||
| return Optional.empty(); | ||
| } | ||
|
|
||
| private RematchingOption inputValidRematchingOption() { | ||
| return InputUtil.retryOnInvalidInput(this::inputRematchingOption, | ||
| errorMessage -> outputView.outputErrorMessage(INVALID_REMATCHING_SELECT.getMessage())); | ||
| } | ||
|
|
||
| private RematchingOption inputRematchingOption() { | ||
| return inputView.inputRematchingOption(); | ||
| } | ||
|
|
||
| private CourseMission inputValidCourseMission() { | ||
| return InputUtil.retryOnInvalidInput(this::inputCourseMission, | ||
| errorMessage -> outputView.outputErrorMessage(INVALID_COURSE_MISSION.getMessage())); | ||
| } | ||
|
|
||
| private CourseMission inputCourseMission() { | ||
| return inputView.inputCourseMission(); | ||
| } | ||
|
|
||
| private Function inputValidFunction() { | ||
| return InputUtil.retryOnInvalidInput(this::inputFunction, | ||
| errorMessage -> outputView.outputErrorMessage(INVALID_FUNCTION.getMessage())); | ||
| } | ||
|
|
||
| private Function inputFunction() { | ||
| return inputView.inputFunction(); | ||
| } | ||
|
Comment on lines
+158
to
+165
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 진짜 큰 차이 아니긴 한데 이렇게 수정하는건 어떤가요?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 훨씬 간단해지고 좋네요!! 감사합니다 :) |
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| package pairmatching.domain.dto; | ||
|
|
||
| import java.util.Arrays; | ||
| import java.util.List; | ||
| import java.util.stream.Collectors; | ||
| import pairmatching.domain.entity.Course; | ||
| import pairmatching.domain.entity.CourseMission; | ||
| import pairmatching.domain.entity.Level; | ||
| import pairmatching.domain.entity.Mission; | ||
| import pairmatching.util.StringUtil; | ||
|
|
||
| public class CourseMissionMapper { | ||
|
|
||
| private CourseMissionMapper() { | ||
|
|
||
| } | ||
|
|
||
| public static CourseMission toCourseMission(String input) { | ||
| List<String> separated = seperate(input); | ||
|
|
||
| final String courseName = separated.get(0); | ||
| final String levelName = separated.get(1); | ||
| final String missionName = separated.get(2); | ||
|
|
||
| Course course = toCourse(courseName); | ||
| Level level = toLevel(levelName); | ||
| Mission mission = toMission(level, missionName); | ||
|
|
||
| return CourseMission.create(course, level, mission); | ||
| } | ||
|
|
||
| private static List<String> seperate(String input) { | ||
| String deleteSpaces = StringUtil.removeAllSpaces(input); | ||
| List<String> separated = Arrays.stream(deleteSpaces.split(",")) | ||
| .collect(Collectors.toList()); //실전에선 toList() 사용 | ||
| return separated; | ||
| } | ||
|
|
||
| private static Level toLevel(String levelName) { | ||
| return Level.findLevel(levelName); | ||
| } | ||
|
|
||
| private static Course toCourse(String courseName) { | ||
| return Course.findCourse(courseName); | ||
| } | ||
|
|
||
| private static Mission toMission(Level level, String missionName) { | ||
| return level.findMission(missionName); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| package pairmatching.domain.dto; | ||
|
|
||
| import pairmatching.domain.entity.Function; | ||
| import pairmatching.util.StringUtil; | ||
|
|
||
| public class FunctionMapper { | ||
| private FunctionMapper() { | ||
|
|
||
| } | ||
|
|
||
| public static Function toFunction(String input) { | ||
| final String deleteSpaces = StringUtil.removeAllSpaces(input); | ||
|
|
||
| return Function.findFunction(deleteSpaces); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| package pairmatching.domain.dto; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| public class MatchingResultDto { | ||
|
|
||
| private final List<List<String>> matchingResult; | ||
|
|
||
| public MatchingResultDto(List<List<String>> matchingResult) { | ||
| this.matchingResult = matchingResult; | ||
| } | ||
|
|
||
| public List<List<String>> getMatchingResult() { | ||
| return matchingResult; | ||
| } | ||
| } | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이런 부분에 대해서 저도 고민해봤는데,
3회 체크를 하려면 어쩔수 없이 필드에 값이 존재해야할 것 같았습니다.
하지만 이런 값을을 최대한 배제하려 해서 사용하지 않았는데 이부분에 대해선 어떻게 생각하시나요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
시간이 없어서 분리하지 못했는데..ㅎㅎ 시간 여유가 있었다면 따로 enum에서 관리하는 방법으로 리팩터링 했을 것입니다!