-
Notifications
You must be signed in to change notification settings - Fork 252
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
[1단계 - 사다리 생성] 망고(고재철) 미션 제출합니다. #129
Conversation
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.
안녕하세요 망고🥭 사다리 미션의 리뷰어 토니에요 👨💻
구현해주신 내용 잘 확인했습니다~! 커밋을 확인해봤는데 TDD를 최대한 적용하려하신 점이 인상적이었어요 👍 우테코에서 학습한 내용을 충실히 수행하고 있음이 느껴지네요!
제가 보기에 개선하면 좋을 점과 망고의 생각을 들어보고 싶은 내용에 대해 코멘트 남겼으니 확인 후 답변도 부탁드려요~! 보시다가 궁금한 사항에 대해서는 편하게 DM 주셔요~~! 화이팅입니다!
@@ -0,0 +1,38 @@ | |||
# 🪜사다리 타기 |
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.
기능 요구사항과 구현할 내용을 잘 적어주셨네요~!
어떤 내용을 구현할지, 구현했는지를 확인하며 개발하면 실수할 일이 줄어들겠죠 👍
import domain.Height; | ||
import domain.Ladder; | ||
import domain.Line; | ||
import domain.User; | ||
import domain.Users; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
import view.InputView; | ||
import view.OutputView; |
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.
MVC 패턴에 맞게 Controller
에서 Model
과 View
객체를 잘 연결해주고 있군요! 좋습니다!
OutputView
에 도메인 객체가 넘어가고 있는데 이부분까지 DTO를 만들어서 넘겨주면 도메인과 View
의 의존관계를 완전하게 끊을 수 있겠어요. 지금의 상황이라면 OutputView
에서 도메인 객체를 조작할 수 있으니까요.
확인해보니 아래에 Model
레이어에 출력에 관한 로직이 많이 들어있어보이네요. DTO에 대해 학습하고 적용하여 개선해봐도 좋고 자바의 원시 타입을 사용해봐도 좋겠어요!
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.
DTO를 만들어서 OutputView에 넘겨주도록 리팩토링 해보았습니다!
DTO를 처음 써보고 아직 DTO에 대해서 잘 몰라서 제대로 한 건지 확신이 들지 않습니다.😅
수정할 부분이 있을 것 같은데, 수정이 필요한 부분이 있다면 피드백 부탁드립니다!
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.
잘 해주셨어요. 아직 익숙하지 않은 개념이라 확신이 들지 않는건 당연해요. DTO를 왜 사용하는지와 같은 부분에 대해서 고민해보면 망고도 망고만의 기준이 생길거에요.
참고 글
지금 수정하면 좋을 부분은, LadderDto
의 getLines
메서드에서 List<Line>
을 반환하며 OutputView
에 도메인(모델) 객체인 Line이 드러났다는 부분이에요. Dto가 필드로 Dto를 가져도 괜찮으니 다음 단계에서 수정해보죠!
그리고 OutputView
에서 사용한 User.MAX_NAME_LENGTH
와 같은 도메인 값이 정말 도메인의 규칙인지 View의 규칙인지 생각해봐도 좋겠네요
private Users initializeUsers() { | ||
try { | ||
List<String> userNames = inputView.inputUserName(); | ||
return new Users(createUsers(userNames)); | ||
} catch (IllegalArgumentException e) { | ||
return initializeUsers(); | ||
} | ||
} | ||
|
||
private List<User> createUsers(List<String> userNames) { | ||
List<User> users = new ArrayList<>(); | ||
for (String userName : userNames) { | ||
users.add(new User(userName)); | ||
} | ||
return users; | ||
} |
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.
initialize => create
로 넘어가는 규칙이 일관되게 적용되어서 좋네요. 다른 코드를 봐도 해당 규칙을 따르겠거니 생각이 드니까요!
outputView.printLadder(ladder); | ||
} | ||
|
||
private Users initializeUsers() { |
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.
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.
initializeUsers() 메서드에서는 exception 캐치 후에 에러 메세지 출력을 안했네요,, 주의하겠습니다!
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.
넵~ 확인했습니다. 항상 사용자를 고려하며 개발하도록 해요.
} | ||
} | ||
|
||
public Ladder createLadder(int personCount, Height height) { |
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.
해당 로직은 Ladder
를 만드는군요! 이 로직을 컨트롤러에 두신 이유가 궁금해요~!
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.
처음에는 단순히 사람 수만큼 반복문을 돌려서 lines 리스트를 만들고 -> 이걸 Ladder 생성자에 넘겨주면 되겠다고 생각했습니다.
하지만 이 과정 또한 생성하는 과정이니까 Ladder 생성자 내부에서 처리해도 되겠다고 생각해서 Ladder 생성자 내부로 옮겨보았습니다!
제가 생각한 방식이 올바른 접근일까요??
그러면 createUsers() 메서드도 Users 생성자 내부로 옮기는 게 맞을까요...?
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.
저는 좋다고 생각해요. Users
는 List<String> names
를 받아서 자신이 스스로를 생성하는거죠~!
@@ -0,0 +1,35 @@ | |||
package validate; | |||
|
|||
public class InputVerifier { |
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.
이 객체도 현재 테스트코드에서만 사용하고 있군요. 이렇게 입력 값에 대한 검증 내용을 객체로 추출하신 이유가 있을까요~?
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.
처음에는 입력 값이 들어오면 값을 검증하고 -> 검증한 값을 생성자로 넘기는 방식
으로 구현했습니다.
그래서 검증하는 부분은 InputVerifier
라는 클래스를 따로 만들어서 해당 클래스에 모아서 처리하도록 하면 가독성이 좋을 것 같다고 생각했습니다!
하지만 입력 값이 들어오면 값을 검증하고 -> 검증한 값을 생성자로 넘기는 방식
보다는 입력 값을 생성자로 넘기고 -> 생성자에서 검증한 후 생성하는 방식
이 더 깔끔할 것 같다고 생각해서 로직을 변경했습니다.
그 과정에서 InputVerifier
클래스를 삭제하기로 했는데, 제대로 리팩토링 되었는지 검사하지 않고 커밋해서 남아있는 것 같습니다. 😢
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.
저도 객체 자체가 검증하는게 좋아요. 리팩터링을 진행하며 빼먹는 부분에 대해서는 어떻게 하면 쉽게 탐지할 수 있을까요?
- 인텔리제이의 전역 검색 (cmd + shift + f) 기능으로 검색한다.
- 해당 코드의 사용처 탐색 (cmd + b) 기능으로 탐색한다.
요정도가 생각나네요!
src/test/java/HeightTest.java
Outdated
void shouldFailHeightWithNotNumber(String heightInput) { | ||
assertThatThrownBy(() -> new Height(heightInput)).isInstanceOf( | ||
IllegalArgumentException.class) | ||
.hasMessageContaining(InputVerifier.HEIGHT_FORMAT_ERROR_MESSAGE); |
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.
실패에 대한 검증을 해주셨군요! 좋아요 👍
어떤 타입의 예외가 던져지는지, 어떤 메시지를 포함하는지를 다 검증하셨어요. 예외만 검증할 때와 메시지도 함께 검증할 때는 어떤 차이점이 있다고 생각하세요~? 망고의 생각을 들어보고 싶습니다!
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.
예외만 검증하게 되면 같은 IllegalArgumentException이 발생했을 때, 그 예외가 해당 테스트에 맞는 예외인지 구분을 못할 거라고 생각해서 메시지까지 함께 검증하도록 했습니다!
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.
오오 생각을 말씀해주셔서 감사해요 😉
말씀해주신대로 예외의 메시지까지 상세하게 검증할 수 있다는 장점이 있죠! 다만 예외 메시지가 변경될 때에 테스트 코드도 바뀌어야한다는 단점도 있구요. 망고는 이를 방지하기 위해 도메인 객체의 메시지 상수를 사용하셨네요~!
src/test/java/HeightTest.java
Outdated
@@ -0,0 +1,53 @@ | |||
import static org.assertj.core.api.Assertions.assertThatThrownBy; |
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.
테스트 파일은 해당 테스트 파일이 테스트하는 객체와 동일한 이름의 패키지에 있어야해요. (지금은 test/java/domain
이겠죠?)
이렇게 위치시켜야하는 이유는 뭘까요? 망고의 생각을 적어주세요!
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.
동일한 이름의 패키지에 있어야 한다는 얘기를 들으니 protected나 default 접근 제어자로 된 메서드를 테스트 하기 위해서
가 이유일 것 같습니다.
또한 프로덕션 코드와 비교할 때 가독성이 좋아진다는 것도 이유가 될 수 있을 것 같구요!
토니가 생각한 이유도 제가 생각한 이유와 같은가요??
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.
네 우선 1:1 매칭이 되어서 코드를 보는 사람이 찾기 쉽다라는 측면도 있구요 말씀해주신 접근 제어자라는 이유도 있어요.
망고가 생각을 잘 적어주셨네요 👍👍
src/test/java/LineTest.java
Outdated
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; | ||
|
||
import domain.Line; | ||
import java.util.List; |
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.
사용하지 않는 import문이 왕왕있네요~!
src/test/java/UserTest.java
Outdated
@DisplayName("변환된 입력값을 통해 올바르게 유저가 생성되는지 확인한다.") | ||
@ParameterizedTest | ||
@ValueSource(strings = {"i", "am", "fun", "dino", "mango"}) | ||
void shouldSuccessMakeUser(String name) { | ||
assertDoesNotThrow(() -> new User(name)); | ||
} | ||
|
||
@DisplayName("생성된 유저 객체가 올바르게 저장되는지 확인한다.") | ||
@ParameterizedTest | ||
@ValueSource(strings = {"i", "am", "fun", "dino", "mango"}) | ||
void shouldSuccessStoreUsers(String name) { | ||
assertDoesNotThrow(() -> new User(name)); | ||
} |
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.
두 테스트는 어떤 차이가 있어요?
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.
프로덕션 코드를 변경하고 테스트 코드도 이에 맞게 변경했는데, 테스트 코드가 중복되게 바뀌었다는 걸 인지하지 못하고 넘어간 것 같아요. 😢
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.
시간이 많이 부족하셨군요..! 다음 미션은 리팩터링시 테스트 코드도 잘 챙겨줍시다!
피드백 반영이 생각보다 오래걸렸네요. 😢
시간에 쫓겨서 작은 실수들을 많이 한 것 같아서 부끄럽네요.. 참고해서 다음부터는 주의하겠습니다. 😅 |
|
||
--- | ||
|
||
## 1단계 리팩토링 요구사항 |
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.
리드미에 제가 추가로 요청드린 내용을 잘 적어주셨네요 굳굳
private final List<Line> lines; | ||
private final Height height; | ||
|
||
public Ladder(int personCount, Height height) { |
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.
Ladder 객체 입장에서 personCount 즉 사람의 명수라는 개념을 알 필요가 있을까요?
요 논의는 다음 단계를 진행하며 요구사항에 따라 답변이 달라지겠네요!
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.
Ladder를 생성할 때 사람 수만큼 너비를 가져야된다고 생각해서 personCount를 넘겨주었습니다.
이렇게 다시 생각하고 보니 personCount라는 이름보다는 width라는 이름으로 넘겨주는 게 더 맞는 것 같네요!
this.lines = lines; | ||
} | ||
|
||
public static LadderDto from(Ladder ladder) { |
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.
정적 팩터리 메서드를 사용하셨군요! from이라는 이름은 어떻게 정하셨어요?
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.
정적 팩터리 메서드에 네이밍 컨벤션이 있는지 찾아보고, 하나의 매개변수를 받아서 객체를 생성
하는 경우에는 from
이라고 네이밍을 하는 걸 보고 이름을 정하게 되었습니다!
import domain.Height; | ||
import domain.Ladder; | ||
import domain.Line; | ||
import domain.User; | ||
import domain.Users; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
import view.InputView; | ||
import view.OutputView; |
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.
잘 해주셨어요. 아직 익숙하지 않은 개념이라 확신이 들지 않는건 당연해요. DTO를 왜 사용하는지와 같은 부분에 대해서 고민해보면 망고도 망고만의 기준이 생길거에요.
참고 글
지금 수정하면 좋을 부분은, LadderDto
의 getLines
메서드에서 List<Line>
을 반환하며 OutputView
에 도메인(모델) 객체인 Line이 드러났다는 부분이에요. Dto가 필드로 Dto를 가져도 괜찮으니 다음 단계에서 수정해보죠!
그리고 OutputView
에서 사용한 User.MAX_NAME_LENGTH
와 같은 도메인 값이 정말 도메인의 규칙인지 View의 규칙인지 생각해봐도 좋겠네요
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.
코멘트가 많았는데 잘 반영해주셨네요 고생 많으셨어요 💯
망고가 남겨주신 답변에 대한 재질문이나 제가 전해드리고 싶은 내용에 대해 답변을 달았으니 확인하여 다음 단계에서 반영해주세요!
2단계는 이번 단계에서 부족하다고 느낀 부분을 개선하여 달려봅시다!!
안녕하세요 토니!
사다리 생성 - 1단계 미션 PR 제출합니다. 🙇♂️
아직 부족하지만 잘 부탁드립니다! :)
감사합니다.