Skip to content

Conversation

@ke-62
Copy link

@ke-62 ke-62 commented Oct 30, 2025

안녕하세요 준수님!! 저번 미션에 이어 또 만나게 되었네요 !! 이번 미션도 잘 부탁드립니다.🙇‍♀️

먼저 제 코드에 대해 설명을 드리자면

model/BuildLine: Line을 실제 '-' 모양으로 보여지도록 만드는 클래스
model/LadderGame: 사다리 게임 진행 과정 (가로선 있으면 이동, 없으면 그냥 내려감)
model/LadderFactory: 사다리를 만들어 주는 곳 (가로선이 겹치지 않도록)
model/LadderSize: 사다리 크기 정보
model/Ladder: (일급컬렉션)Line을 여러개 쌓아둔 만들어진 사다리
model/Line: (일급컬렉션)Point들이 모여 만들어진 가로줄 하나
model/Point: 가로선 하나

입니다!

궁금한점

  1. 이번에는 다른 미션들과 다르게, controller에서 OutputView, InputView만이 아니라 model인 LadderFactory도 Controller 필드(private final~~ = ) 로 선언했습니다. LadderFactory는 사다리를 만드는 도구이지 실제 사다리가 아니므로 재사용해도 괜찮다고 생각했습니다. 따라서 매번 new LadderFactory()를 호출하는 것보다 하나의 인스턴스를 재사용하는 것이 더 적절하다고 판단해서 이렇게 선언했는데 이 판단이 적절한지 궁금합니다.

  2. 현재 제 코드에서는 LadderGame의 메서드를 호출할 때마다 participants와 results를 매개변수로 계속 넘겨주고 있습니다. LadderGame뿐만 아니라 getResult, playAll을 부를때도 매번 같은 데이터를 전달하고 있는데 이게 불필요한 반복이라는 생각이 들었습니다. 그래서 이 데이터들을 밑에 코드처럼 LadderGame의 생성자에서 한 번만 받아서 필드로 저장하면 어떨까 고민했습니다. 그렇게 하면 메서드를 호출할 때 매개변수를 넘기지 않아도 되어 코드가 더욱 간결해질 것 같습니다. 하지만 반대로 LadderGame이 너무 많은 책임을 가져가는 듯한 느낌 또한 들었습니다. 준수님이라면 어떤 방식이 더 좋을 것 같은지 조언 부탁드리겠습니다.!!

public class LadderGame {
    private final Ladder ladder;
    private final List<String> participants;  // 이렇게 필드로 저장
    private final List<String> results;
    
    public LadderGame(Ladder ladder, List<String> participants, List<String> results) {
        this.ladder = ladder;
        this.participants = participants;
        this.results = results;
    }
    
    public String getResult(String name) {
        
    }
    
    public Map<String, String> playAll() {
        
    }
}

항상 리뷰해 주셔서 감사합니다😊

Copy link

@gogo1414 gogo1414 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

안녕하세요, 고은님 😄
말씀하신 것처럼 저번 미션에 이어 또 뵙게 되었네요! 잘 부탁드립니다ㅎㅎ

실행 결과

우선 고은님의 코드를 직접 받아서 실행시켜봤는데, 체크해보면 좋을 부분 3가지 공유드릴게요!

  1. 사다리 게임의 결과를 한번에 확인 가능하다는 안내 문구가 없어서 README.md를 읽지 않고 사다리 게임을 진행하는 사람은 끝나지 않는 사다리게임을 하게 될 것 같아요 😅
  2. 입력 시 " "(띄어쓰기)가 같이 포함되어 "a, b, c, d" 로 입력 시 b, c, d의 결과를 보려면 " b", " c"와 같이 띄어쓰기를 포함해야 결과 확인이 가능해요.
  3. 테스트 코드 실행 시 이미지와 같이 1개의 케이스에서 IllegalArgumentException 예외가 발생하지 않는다고 하네요.
스크린샷 2025-11-02 오후 10 17 46

확인 부탁드릴게요!

궁금한 점에 대한 답변

이번에는 다른 미션들과 다르게, controller에서 OutputView, InputView만이 아니라 model인 LadderFactory도 Controller 필드(private final~~ = ) 로 선언했습니다. LadderFactory는 사다리를 만드는 도구이지 실제 사다리가 아니므로 재사용해도 괜찮다고 생각했습니다. 따라서 매번 new LadderFactory()를 호출하는 것보다 하나의 인스턴스를 재사용하는 것이 더 적절하다고 판단해서 이렇게 선언했는데 이 판단이 적절한지 궁금합니다.

  • Controller 내부에서 new LadderFactory()로 직접 생성하는 방식은 테스트 관점에서 아쉬운 점이 있습니다.
  • 이렇게 내부에서 직접 객체를 만들면, Controller를 테스트할 때 LadderFactory의 동작을 제어하기가 어렵습니다. 예를 들어, 테스트 시에는 무작위가 아닌 고정된 모양의 사다리가 필요한데, LadderFactory를 가짜 객체(Mock)로 교체할 방법이 마땅치 않습니다.
  • 이런 문제를 해결하기 위해 Controller의 생성자에서 LadderFactory를 외부에서 받아오는 방식(의존성 주입, DI)을 많이 사용합니다.
  • 이렇게 구조를 바꾸면, 실제 코드에서는 진짜 LadderFactory를 넣어주고, 테스트 코드에서는 우리가 원하는 대로 동작하는 가짜 Mock 객체를 쉽게 넣어줄 수 있습니다. 이렇게 하면 Controller만 순수하게 테스트하기 훨씬 용이해집니다.
  • 추가로, LadderFactory를 사용하여 사다리를 만드는 방식이 아닌 Ladder 클래스 내부에서 생성하는 방식도 있을텐데, LadderFactory 라는 별도 클래스로 분리하신 의도가 있으실까요??

현재 제 코드에서는 LadderGame의 메서드를 호출할 때마다 participants와 results를 매개변수로 계속 넘겨주고 있습니다. LadderGame뿐만 아니라 getResult, playAll을 부를때도 매번 같은 데이터를 전달하고 있는데 이게 불필요한 반복이라는 생각이 들었습니다. 그래서 이 데이터들을 밑에 코드처럼 LadderGame의 생성자에서 한 번만 받아서 필드로 저장하면 어떨까 고민했습니다. 그렇게 하면 메서드를 호출할 때 매개변수를 넘기지 않아도 되어 코드가 더욱 간결해질 것 같습니다. 하지만 반대로 LadderGame이 너무 많은 책임을 가져가는 듯한 느낌 또한 들었습니다. 준수님이라면 어떤 방식이 더 좋을 것 같은지 조언 부탁드리겠습니다.!!

  • 메서드에 전달해주는 파라미터는 해당 메서드의 네이밍을 토대로 파라미터를 가지고 어떤 동작을 할 것이라는 추측에 많은 도움을 줍니다. 따라서, 고은님이 말씀해주신 것과 같이 클래스 인자로 두어 내부 메서드에서 알아서 사용하는 경우 메서드 안의 로직을 살펴봐야 어떤 변수들을 활용하고 있는지 확인이 가능할 것 같아요. 그래서 개인적으로는 지금과 같이 매개변수로 넘겨주는 방식이 더 좋을 것 같습니다.
  • 현재는 게임을 1회 실행하고 다시 시작하는 구조가 아니지만, 게임을 재시작할 수 있는 구조로 변경할 경우 클래스 인자를 초기화해주는 로직이 필요할텐데 보내주신 코드와 같이 final 키워드가 붙은 불변 상수의 경우 참가자를 초기화해주는 과정이 이상해지지 않을까 싶네요!
  • 이건 별개의 내용이지만 List<String>과 같이 컬렉션 클래스의 경우 final 키워드가 붙어있더라도 내부 값의 변경이 가능하기 때문에 주의가 필요해요. final이 어떤 동작을 하는지 찾아보시면 더 좋을 것 같네요 😄

리뷰를 너무 늦게 드려서 죄송해요! 천천히 리뷰 보시고 답변 남겨주세요!
항상 응원하고 있습니다!! 파이팅!! 👍👍


public void run() {

List<String> participants = inputView.inputParticipants();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ladder 클래스와 같이 참여자도 일급 컬렉션으로 변경하는건 어떨까요?

일급 컬렉션에 대해 참고하시면 좋은 블로그도 같이 공유드릴게요!
https://jojoldu.tistory.com/412

Copy link
Author

@ke-62 ke-62 Nov 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

처음에는 참여자도 일급 컬렉션으로 받으려고 했습니다. 그치만 참여자 관련 비즈니스 로직이 거의 없어서 이번 단계에서는 List의 기본 메서드만으로 충분히 요구사항을 표현할 수 있다고 생각해서 사용하지 않았습니다!!
또한 Controller에서 참여자와 결과를 함께 다루고 있는데 만약 Participants를 일급 컬렉션으로 만들면 참여자는 객체로 감싸고 결과는 그냥 List으로 두게 되어 일관성이 떨어지는 느낌이 들었습니다. 그렇다고 Results까지 만들자니 둘 다 단순한 문자열 리스트인데 과도한 래핑이 될 것 같았습니다.
다만 참여자 이름 중복 금지와 같은 참여자 관련 복잡한 로직 추가와 같은 요구사항이 추가된다면 일급 컬렉션 도입을 고려할 것 같습니다. 현재 단계에서는 단순함을 유지하는 것이 더 적절하다고 판단했습니다!

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

간단한 시스템에서는 고은님이 생각하신 것처럼 핵심이 되는 Ladder 클래스만 일급컬렉션으로 진행하는 것도 좋다고 생각해요!

다만, 특정 개념은 컬렉션 클래스로 내부에서 정의해 다루고 특정 클래스는 일급컬렉션으로 묶는게 일관성이 떨어진다고 생각해요

리뷰어인 제 입장에서는 외부 구조만 봤을 때 어떤 것들이 어떻게 존재하는지 빠르게 파악하는게 어려워요.

결과, 참여자라는 것이 존재하는 것도 실제 코드 내부의 로직을 확인해야 알 수 있기 때문에 이 프로젝트가 사다리 게임인지 한눈에 파악할 수 없어요

또 다른 포인트로는 현재 구조 상으로는 참여자, 결과 등의 데이터를 기록할 수 없어 보여요. 현재는 게임을 데이터로 남겨야 한다는 요구사항이 없지만, 다른 프로젝트를 하게 되시면 데이터를 어떤 형식으로 남길지 그 구조를 설계하시게 될텐데 이번 스터디를 진행하면서 해당 내용까지 같이 공부해보시면 좋지 않을까요?

private final OutputView outputView = new OutputView();
private final InputView inputView = new InputView();

public void run() {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Main 클래스에서 보면 단순히 LadderController.run() 이라는 명령어 실행으로 동작하게 되어있는데, 해당 클래스에서는 어떤 기능이 동작할 지 예측할 수 없는데 보다 더 좋은 네이밍은 없을까요??

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

리뷰어님 말씀대로 사다리 '게임' 이니까 run() 보다는 startGame() 네이밍이 더 적합할 것 같습니다!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

연결해서 playGame도 결과출력과 연관되어 있다고 느껴 gameResult로 수정했습니다.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

run 내부에 있는 로직을 분리해서 작성해보면, run 메서드 자체가 어떤 역할을 하는지 파악하기 쉬울 것 같아요!
오케스트레이션의 역할을 한다고 생각하며 접근해보시면 좋을 것 같네요!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오케스트레이션을 찾아보았는데 명확하게 정리된 블로그나 공식문서를 찾지 못해서요😅 제가 이해한 바로는 현재 run() 메서드가 입력 받기 → 유효성 검증 → 사다리 생성 → 출력 → 게임 진행 같은 전체 흐름을 직접 관리하고 있는데, 각 단계를 메서드로 분리하여 run()은 어떤 순서로 실행되는지만 보여주고 세부 구현은 각 메서드에 위임하는 것이 오케스트레이션 역할을 한다고 생각되는데 맞을까요??

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

헛 오케스트레이션이 아니라 템플릿 메서드 패턴을 찾는게 올바른 방향인데 제가 잘못 설명을 드렸네요..😅 죄송해요

고은님이 적어주신

각 단계를 메서드로 분리하여 run()은 어떤 순서로 실행되는지만 보여주고 세부 구현은 각 메서드에 위임하는 것
이 내용으로 생각해주시면 될 것 같아요!

아래는 두 개념을 간단하게 정리해봤는데 앞으로 혼동을 느끼시지 않도록 간략하게만 적어봤어요!
참고해주시면 좋을 것 같습니다😄

  • 오케스트레이션: 여러 시스템·서비스의 자동화 태스크를 상위 워크플로로 묶어 의존성, 순서, 분기, 상태, 예외, 보상 트랜잭션까지 중앙에서 관리

  • 템플릿 메서드: 상위 메서드가 알고리즘의 골격과 단계 순서(run)를 정의하고, 각 단계 구현은 하위 메서드/클래스가 담당

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵 감사합니다 !!

return lines;
}
}

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

라인이 하나 더 들어갔네요!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

수정했습니다!


public class LadderGame {
private final Ladder ladder;
private final int positions;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

positions는 사용되지 않는 것으로 보여요! 추가해주신 이유가 있을까요??

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

헉 이거는 제가 position 필드와 position 변수를 잠시 혼동했던 것 같습니다..!

맨 처음 코드를 짤 때 position 필드를 추가한 이유는 사다리의 너비 정보를 따로 저장해 주면 범위 체크나 검증 시에 효과적일 것 같아 넣어 두었는데 범위 체크를 moveOnLine 메서드에서 position.size()로 받고 있어 사용되지 않네요. 코드 완성 후에는 position 변수만 보고 사용되구나! 했던 것 같습니다.😅

@ke-62
Copy link
Author

ke-62 commented Nov 4, 2025

궁금한 점들 해결이 잘 되었습니다!! 감사합니다...!

1번 피드백은 InputView에서

결과를 보고 싶은 사람은?--all을 입력하면 전체 결과를 확인하고 종료됩니다

라는 멘트를 넣어 게임 종료 조건을 명확하게 알 수 있도록 수정했습니다.

2번 패드백은 trim(),\ \s* 을 사용하여 쉼표 앞 뒤에 있는 공백을 무시하도록 수정해 보았습니다.
다만 이렇게 수정하면 사용자가 의도적으로 이름 앞,뒤에 공백을 포함시키고 싶을 때와 단순히 입력 편의를 위해 쉼표 뒤에 띄어쓰기를 한 경우를 구분할 수 없다는 문제가 있을 것 같습니다.
현재 구현으로는 두 경우를 구분할 방법이 없는데, 혹시 이를 구분할 수 있는 좋은 방법이 있을까요? 아니면 현재 미션의 요구사항 상 이름에 공백이 들어가는 경우는 고려하지 않아도 될까요?

3번 피드백은 높이 검증 로직이 없어서 예외가 발생 했던 것 같습니다! LadderSize에 유효성 검증 로직 추가했습니다!

이번 피드백도 많이 배워가는 것 같습니다. 항상 리뷰해 주셔서 감사합니다!̆̈ !̆̈ !̆̈

@gogo1414
Copy link

gogo1414 commented Nov 6, 2025

사용자가 의도적으로 이름 앞,뒤에 공백을 포함시키고 싶을 때와 단순히 입력 편의를 위해 쉼표 뒤에 띄어쓰기를 한 경우를 구분할 수 없다는 문제가 있을 것 같습니다.
현재 구현으로는 두 경우를 구분할 방법이 없는데, 혹시 이를 구분할 수 있는 좋은 방법이 있을까요? 아니면 현재 미션의 요구사항 상 이름에 공백이 들어가는 경우는 고려하지 않아도 될까요?

고은님이 고민하고 계신 부분은 사실 정책과 규칙에 가깝다고 생각해요! 사용자가 의도적으로 공백을 포함하는 것이 고은님 입장에서는 어떤가요?

제 경험으로 얘기를 드리자면 데이터를 저장할 때 공백으로 되어있는 데이터를 구분하기 매우 어려워요. 공백으로 되어있기 때문에 데이터를 클릭하여 공백이 얼마나 들어가있는지 확인을 해야 구분이 가능한데, 사용자 100명이 그런식으로 이름을 작성한다고 하면 누가 누군지 파악하기 어렵지 않을까요?

이런 고민이 있을 때 참고하기 좋은 건 다른 서비스에선 어떻게 이름과 아이디를 입력받는지 찾아보는 것도 좋은 방법입니다!

카카오톡, 구글, 네이버 등 각종 서비스를 입력할 때 이름과 닉네임 메일 주소를 입력받는 경우가 많을텐데 이때 어떤식의 규칙 혹은 정책을 사용하는지 한번 살펴보는것도 도움이 많이 됩니다😊

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants