Skip to content

Conversation

nonactress
Copy link

@nonactress nonactress commented Sep 18, 2025

안녕하세요 정상희 리뷰어님!!
3,4 단계를 리뷰어님이랑 같이 리뷰할 수 있는 기회가 있어서 좋습니다!

일단 제 코드에 관한 설명부터 하겠습니다!
Controller의 역할 클래스

  • Application : main 메소드가 들어간 클래스로 시작할 때 쓰이는 클래스
  • RacingController : 전반적이 레이싱 게임의 흐름(?)을 담당하고 게임의 실질적인 controller역할

view의 역할 클래스

  • OutputView : 출력 담당
  • InputView : 입력 담당

model의 역할 클래스

  • Car : 각 자동차의 필요한 name,position
  • Cars : 자동차들의 배열이며, 자동차이 레이싱에 필요한 메소드를 정리
  • GenerateRandom : cars에 Random을 인자로 가지고 있는 것이 맞지 않는다고 생각했고, 테스트시 변경이 용이하기 위해 인터페이스로 제작 ( GenerateCarRandom : 게임 메소드 , TestGenerateRand : 테스트 메소드)

궁금한 거

  1. mvc 패턴을 적용하며 코딩 할 때 어떤 거 부터 생성하나요?
    예를 들어 저의 경우 model부터 생성 후에 어떤 입력 값과 출력이 필요하겠구나 하고 view를 만들고 마지막에 controller를 만들었습니다!
  2. 명명에 관하여
    혹시 메소드나 클래스 명명과 관련된 규칙을 어디서 찾아볼 수 있는지 궁금합니다! 특히 이름을 생각하는데 시간을 많이 쓰는 거 같아 질문 드립니다!!
  3. 모든 로직에 단위 테스트를 구현한다.
    지금은 랜덤 값만 단위테스트가 가능하게 구현 되어있습니다! 하지만 다른 파트에서 단위테스트를 진행하는 것이 어떻게 가능한지 잘 모르겠습니다. 예를 들어 입력 값을 받는 부분을 단위 테스트를 한다고 하면 잘못된 입력값이 input 되면 그냥 프로그램이 작동이 안되는게 아닌가라고 생각합니다!!

@nonactress nonactress changed the base branch from main to nonactress September 18, 2025 10:06
Copy link

@SANGHEEJEONG SANGHEEJEONG left a comment

Choose a reason for hiding this comment

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

안녕하세요, 현진님!
저도 리뷰어-리뷰이로 만나뵙게 되어 반갑습니다 :)
함께 열심히 티키타카 해봐요!

질문에 대한 답변

mvc 패턴을 적용하며 코딩 할 때 어떤 거 부터 생성하나요?예를 들어 저의 경우 model부터 생성 후에 어떤 입력 값과 출력이 필요하겠구나 하고 view를 만들고 마지막에 controller를 만들었습니다!

제가 이전에 자동차 미션하던 걸 다시 보고 왔는데 저도 현진님과 같은 순서로 커밋이 찍혀있네요. Model→View→Controller 순서로 만들었습니다. 아마 저도 비슷한 이유에서 저런 순서로 작성한 것 같은데요, MVC 에서 컨트롤러는 뷰와 모델을 이어주는 역할을 하니까 자연스럽게 모델과 뷰를 작성하고 이를 조립시키는 쪽으로 생각의 흐름이 흘러가서 마지막에 컨트롤러를 작성하게 된 것 같습니다.

현재 클래스 별로 현진님이 3가지를 각각 분리시켜주셨는데, 조금 더 눈에 보이게 패키지를 활용해서 나눠보면 어떨까요? 저는 패키지를 사용하여 분리하면 상하위 분류가 한 번에 보여 편하다고 느꼈습니다! 참고로, 패키지 생성하는 법은 파일 생성하는 법과 비슷합니다. 상위 패키지에서 New→Pakage를 선택해주시면 되며, 패키지 명명은 아래 답변을 참고하여 지으시면 좋을 것 같아요!

명명에 관하여혹시 메소드나 클래스 명명과 관련된 규칙을 어디서 찾아볼 수 있는지 궁금합니다! 특히 이름을 생각하는데 시간을 많이 쓰는 거 같아 질문 드립니다!!

구글 자바 네이밍 가이드 정리

웹 서칭 해보시면 책이나 표준 가이드를 정리해놓은 글이 많습니다. 저는 처음에는 대략적인 틀(ex. 클래스 이름은 대부분 명사 + UpperCamelCase, 메서드 이름은 대부분 동사 + lowerCamelCase 등등)만 익히고 익숙해진 이후에는 AI의 도움을 받고 있어요. 무작정 지어달라고 하진 않고 대충 어떤 책임을 가진 친구인지 설명해주고 범용적인 이름으로 후보 한 5개 정도 달라고 하면 굉장히 빠르게 지어주거든요. 그 중에서 가장 직관적이라고 생각하는 이름을 채택합니다.

만약 AI 써도 맘에 안들면 깃허브에 {짓고 싶은 이름}으로 검색한 후 왼쪽 Code를 클릭하면 얼마나 쓰였는지 보여주는데 대충 많이 쓰인 게 좀 더 범용적이라고 생각해서 그걸로 짓는 편이에요. 똑똑한 팀원에게 받은 팁입니다! 주의할 점은 위에서 말했듯 대략적인 규칙은 숙지하신 후에 사용하시는 편이 좋습니다. 예시로 현진님이 GenerateRandom을 인터페이스 이름으로 명명하셨는데, 803K번 등장하지만 클래스 명보다는 메서드명으로 더욱 많이 쓰이는 것을 확인할 수 있습니다..! 예시로 든 김에 명사나 형용사로 변경해보는 것은 어떨까요?

  • 아래와 같이 사용하시면 됩니다!
image

모든 로직에 단위 테스트를 구현한다.지금은 랜덤 값만 단위테스트가 가능하게 구현 되어있습니다! 하지만 다른 파트에서 단위테스트를 진행하는 것이 어떻게 가능한지 잘 모르겠습니다. 예를 들어 입력 값을 받는 부분을 단위 테스트를 한다고 하면 잘못된 입력값이 input 되면 그냥 프로그램이 작동이 안되는게 아닌가라고 생각합니다!!

현재 GenerateRandomGenerateCarRandomTestGenerateRandom이 implement 받아서 사용하고 있는 구조로 보이는데요! 그런데 테스트를 위한 TestGenerateRandom가 main→java 패키지에 위치하는 것 같아요! 보통 테스트 파일은 test→java 패키지에 넣으며 이렇게도 단축키를 통해 생성할 수 있습니다! 직접 파일 생성하셔도 되구요.

주석을 통해 테스트 하고 있던 걸로 보이는데 맞을까요?

또한 test→java 파일을 통해 파일 생성을 하셨다면 아래 학습테스트를 통해 테스트가 무엇인지 학습한 후 테스트코드를 구현해주시면 좋을 것 같아요! 시간이 부족하다면 다른 사람들의 pr을 참고하여 테스트 코드에 대한 감을 익혀도 좋구요!

image

전체 리뷰

코드 일관성

각 파일마다 코드정렬 해주시면 좋을 것 같습니다. (현재 안 되어있는 클래스 : Car, GenerateRandom, InputView)
또한 불규칙한 개행이 나타나는 곳이 보여요..!
이는 가독성 저하로 이어질 수 있으므로 수정하는 것이 어떨까요?

윈도우 : Crlt + Alt + L
맥 : Command + Option + L


OutputView.printWinners(cars.findWinners());
}
}

Choose a reason for hiding this comment

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

현진님은 컨트롤러가 어떤 역할을 한다고 생각하시나요?

현재 현진님의 컨트롤러는 크게 아래와 같은 역할을 모두 수행하는 것 같습니다!

  1. InputView로부터 자동차 이름과 시도 횟수를 받아옵니다.
  2. 입력받은 이름으로 Cars 객체를 생성합니다.
  3. OutputView의 각종 출력 메서드를 호출합니다.
  4. for 반복문을 통해 전체 레이싱 게임의 흐름을 직접 관리합니다.

저는 컨트롤러는 뷰를 통해 입력을 받은 후 모델에 전달하는 역할을 한다고 생각합니다. 현재는 제어의 역할을 넘어 전체 게임의 진행과 흐름을 모두 책임지고 있는데요, 게임 진행 책임은 컨트롤러에서 분리시키는 것이 어떨까요?

MVC 패턴에서 컨트롤러의 역할이 어디까지라고 생각하시는지 현진님의 생각을 들려주시면 감사하겠습니다!

Copy link
Author

Choose a reason for hiding this comment

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

저는 컨트롤러 안에서 말씀하신대로 controller안에서 게임이 흐름이 보여야 한다고 생각해서 위와 같은 방식으로 코드를 구성했습니다!
위의 피드백 수용해서 'RacingService'를 만들었습니다!

Choose a reason for hiding this comment

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

RacingService로 로직을 분리해주셨군요!

사실 제가 처음 리뷰를 드릴 때 의도했던 바를 조금 더 명확하게 설명드리고 싶어서 추가 의견을 남깁니다. 제가 착각해서 조금 헷갈리게 말을 작성했던 것 같아요. 죄송해요 🥲

제가 처음에 컨트롤러의 책임을 언급하며 혼동했던 이유는 게임의 전체적인 흐름과 상태를 관리하는 모델이 없다 보니, 컨트롤러가 직접 Cars를 제어하면서 게임 진행까지 책임지는 것처럼 보였기 때문인데요, 이전 코멘트의 원래 의도는 레이싱 게임의 상태와 행위를 함께 관리하는 새로운 모델 객체를 만드는 것이었습니다.

현재는 Cars가 자동차 목록을 관리하는 동시에 우승자를 찾는 역할(findWinners)까지 수행하고 있는데요, 여기서 한 걸음 더 나아가 Cars는 자동차들의 일급 컬렉션 역할에만 집중하게 하고, 새로 만든 게임 담당 객체가 Cars를 소유하며 게임 진행 자체를 책임지게 하는 구조는 어떨까요?

이렇게 구조를 바꾸면 좋은 점은...

  • 객체별 책임이 명확해집니다
    • Car: 자동차 하나의 상태를 책임집니다.
    • Cars: 자동차 '목록'을 관리하는 책임에만 집중합니다.
    • Racing: 게임의 규칙과 상태를 책임집니다.
  • 응집도가 높아집니다.
    • 게임과 관련된 로직이 RacingGame 하나에 모여있어 나중에 게임 규칙이 바뀌더라도 RacingGame 클래스만 수정하면 됩니다.
  • 컨트롤러는 정말 지휘만 하게 됩니다.
    • 컨트롤러는 game.startRound() 처럼 더 높은 수준의 명령만 내리면 됩니다.

지금 제안 드린 구조에 대해서는 어떻게 생각하시는지 편하게 의견 남겨주세요!

}

private static void printCarPosition(Car car) {
for (int i = 0; i < car.getPosition(); i++) {

Choose a reason for hiding this comment

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

참고) 이런 방법도 있답니다!

private static void printCarPosition(Car car) {
    System.out.print("-".repeat(car.getPosition()));
}

Copy link
Author

Choose a reason for hiding this comment

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

훨씬 깔끔하네요! 수정하겠습니다!

}
}
return true;
}

Choose a reason for hiding this comment

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

현재 이 함수가 안 쓰이는 것으로 보이는데 맞나요?
✚ 추가로 depth가 깊어 보입니다!

Copy link
Author

Choose a reason for hiding this comment

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

아직 그 단위 테스트 할 때 써보려고 만든 함수인데 미완이라 수정해보겠습니다!

return winners;
}

private void isFindingFirstPizes(List<String> winners, Car car, int maxPosition) {
Copy link

@SANGHEEJEONG SANGHEEJEONG Sep 18, 2025

Choose a reason for hiding this comment

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

is로 메서드명을 지으면 보통 boolean을 반환한다고 오해하기 쉬운데요, 어떤 동작을 하는지가 더 명확히 드러나는 이름은 어떨까요? 추가로 Pizes 오타난 것 같습니다!

Copy link
Author

Choose a reason for hiding this comment

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

isFindingFirstPrizes => checkAndAddWinner 로 수정했습니다!



public Car() {
}

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.

@nonactress
Copy link
Author

nonactress commented Sep 20, 2025

@SANGHEEJEONG

  1. isValid
    위 메소드가 지금 깊이가 2이상인데 어떻게 수정할 지 고민해보다가 isEmpty 메소드 생성으로 해결해 보려 했지만 결국 for - if 형식이 되어 다시 깊이가 유지되고 있습니다.
    [2831966]
  2. RandomMoveStrategyTest
    위 단위테스트 코드를 고정된 random 값을 가져도 cars가 잘 작동하는지를 테스트 해보려고 하였는데 어떻게 로직을 구성해야하는지 잘 모르겠습니다!
    [b160676]

Copy link

@SANGHEEJEONG SANGHEEJEONG left a comment

Choose a reason for hiding this comment

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

안녕하세요, 현진님!

열심히 반영해주신 것 같아 감사합니다 :)

질문에 대한 답변

isValid위 메소드가 지금 깊이가 2이상인데 어떻게 수정할 지 고민해보다가 isEmpty 메소드 생성으로 해결해 보려 했지만 결국 for - if 형식이 되어 다시 깊이가 유지되고 있습니다.[[2831966](https://github.com/next-step/java-racingcar-simple-playground/commit/2831966d5a59a412dda7b5935fd866c649b1a73c)]

2가지 정도 방법이 생각나는데요, 우선 현재는 inputView에서 바로 검증을 진행하고 있어서 스트림 같은 문법을 쓰지 않는 이상 depth 2를 피할 순 없을 것 같습니다! 그렇다면 자동차에 이름을 부여한 뒤에 각각의 자동차에서 이름을 검증하게 만들면 어떨까요?

요구사항 중에 이름은 5자 이하만 가능하다. 이것도 있고 공백 검증도 있는 것으로 보아 Car 클래스가 무거워 질 수도 있을 것 같아요! 만약 이 부분에서 고민이 생기신다면, 다음 미션 키워드인 원시값 포장이라는 키워드를 미리 학습해보셔도 좋을 것 같아요! CarName이라는 값객체로 분리해보는 방법입니다.

RandomMoveStrategyTest

위 단위테스트 코드를 고정된 random 값을 가져도 cars가 잘 작동하는지를 테스트 해보려고 하였는데 어떻게 로직을 구성해야하는지 잘 모르겠습니다!

현진님의 현재 코드는 Cars에서 아래와 같이 직접 의존 객체를 생성하고 있습니다.

RandomGenerator generateRandom = new RandomMoveStrategy();

이렇게 하면 Cars 객체는 RandomMoveStrategy라는 특정 구현체와 아주 강하게 결합되어, 외부에서 이 move 전략을 테스트용 전략(TestGenerateRandom)으로 갈아 끼우는 것이 불가능해집니다. 현진님께서 주석으로 남겨주신 코드가 이 증거라고 할 수 있죠!

이 문제를 해결하기 위한 핵심 키워드는 의존성 주입입니다. Cars가 직접 RandomMoveStrategy를 만드는 것이 아니라, 외부에서 생성된 RandomGenerator 구현체를 생성자를 통해 주입받는 방식으로 바꾸는 것입니다.

이렇게 변경하면 Cars를 테스트할 때 아래처럼 사용할 수 있게 됩니다!

@Test
void 자동차들을_일괄적으로_움직일_수_있다() {
    // given
    // pobi와 jun은 움직이고(true), woni는 멈추는(false) 전략을 각각 주입합니다.
    final List<Car> carList = List.of(
            new Car("pobi", new TestRandomGenerator(true)),
            new Car("woni", new TestRandomGenerator(false)),
            new Car("jun", new TestRandomGenerator(true))
    );
    final Cars racingCars = new Cars(carList);
    
    ...
}

질문

이건 그냥 단지 호기심에 하는 질문인데 커밋 메시지를 영어로 쓰게된 계기가 있나요??

// then: 자동차의 위치는 5가 되어야 한다.
assertEquals(moveCount, car.getPosition());
}
}

Choose a reason for hiding this comment

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

give-when-then으로 테스트 잘 작성해주셨네요 👍
@DisplayName 대신 한국어로 테스트 이름을 바로 지을 수도 있어요!

void move_메서드를_호출하면_위치가_1_증가한다() 

@@ -0,0 +1,4 @@
package controller;

public class RacingControllerTest {

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.

넵 수정하겠습니다!

return false;

}
}

Choose a reason for hiding this comment

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

코드를 보니 RandomMoveStrategygenerate() 메서드가 boolean 값을 반환하도록 설계하셨는데요, 혹시 이렇게 구현하신 이유가 있을까요? random.nextInt(10)으로 생성한 숫자를 바로 반환하지 않고, 내부에서 MOVE_CONDITION과 직접 비교해서 truefalse로 만들어주신 의도가 궁금합니다!

저는 Random 관련 객체들의 역할을 "랜덤 숫자 생성"에만 딱 집중시키는 건 어떨까 제안하고 싶어요. RandomMoveStrategy는 주사위 같은 역할이라고 생각했는데 1에서 6 사이의 숫자를 사용자에게 보여주는 것까지가 주사위의 역할인 것 같아요! “주사위 숫자가 4 이상이니 전진하세요"라고 말하는 것은 주사위가 아니라, 규칙을 아는 플레이어의 역할이라고 생각합니다.

현재는 약간의 코드 중복(if문)도 있어서, 만약 판단 비즈니스 요구사항이 4보다 작은 경우로 바뀐다면 모든 전략을 수정해줘야하는 번거로움이 생길 수도 있을 것 같아요!

Copy link
Author

Choose a reason for hiding this comment

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

  1. RandomMoveStrategy
    리뷰어님이 말씀하신 내용처럼 위 객체를 생성할 때 return 값에 대해 많이 고민해보았습니다! 하지만 저는 위 객체를 주사위 같은 역할의 객체로 생각하기 보단 전진 가능한 조건에 초점을 맞추고 만든 객체인 만큼 플레이어한테 어떤 랜덤한 값이 나왔는지 알려줄 이유가 없다고 생각했습니다!
  2. RandomMoveStrategy의 유지보수
    위 객체는 'RandomGenerator' 인터페이스로 구현되어 있어서 안에 static final int MOVE_CONDITION = 4; 에서 정수 값만 변경 한다면 충분히 조건변경이 용이하다고 생각했습니다!!!

// given: "pobi"라는 이름을 가진 자동차를 준비하고
String carName = "pobi";
FixedNumberMoveStub fixedNumberMoveStub = new FixedNumberMoveStub();

Choose a reason for hiding this comment

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

이 테스트가 어떤 걸 테스트하고 싶은지 잘 모르겠습니다! 실제로 랜덤한 값이 나오는 것을 테스트하고 싶으신 건가요?? 만약 그렇다면, 그건 저희가 직접 테스트하기 아주 어려운 영역이라고 생각합니다. '진짜 랜덤'은 테스트를 실행할 때마다 결과가 계속 바뀌기 때문에, 결과를 예측하고 검증하는 자동화된 테스트를 만들기 거의 불가능하기 때문입니다. 이건 저희 코드의 로직보다는 Java의 Random 클래스 자체의 기능을 테스트하는 셈이라 테스트하지 않아도 되는 영역 같습니다!

Copy link
Author

Choose a reason for hiding this comment

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

아아 위 코드를 작성한 이유는 만약에 제가 RandomGenerator를 만들지 않았다면 랜덤한 값이 나오는 메소드인지 확인해보지 않을까?? 하는 생각에서 작성한 코드 였습니다!
만약 지금은 프로젝트의 단위가 작지만 커진다면 위와 같이 자신이 작성하지 않은 코드를 사용할 때 랜덤값이 아니여도 확인해야하는 경우도 있는지 궁금합니다!

Choose a reason for hiding this comment

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

결론부터 말씀드리면, 프로젝트 규모가 아무리 커져도 java.util.Random과 같은 외부 표준 라이브러리가 정말 랜덤한 값을 생성하는지 직접 테스트하는 경우는 거의 없는 것 같습니다.

이유를 크게 두 가지로 생각해봤는데요,

  1. 신뢰와 책임의 범위
    Java의 표준 라이브러리처럼 엄청 많은 개발자가 사용하는 도구는 이미 오랜 시간에 걸쳐 검증되었다고 생각합니다! 만약 저희가 자동차 회사라고 생각해봤을 때, 저희의 책임은 타이어나 이외의 자동차 부품들을 검증하는 것이 아니라, 이 부품들을 가져와서 우리가 만든 자동차가 잘 달리게 하는 것이 아닐까요? 그래서 저희는 직접 작성한 코드의 로직을 테스트하는 데 집중하는 것이 더 효율적이라고 생각합니다.
  2. 테스트의 목적
    저희가 진짜 테스트하고 싶은 것은 랜덤 값 그 자체가 아니라, '랜덤 값을 사용하는 우리 코드가 예상대로 잘 동작하는가' 일 것 같아요!

저희는 외부 라이브러리의 기능 자체를 검증하기보다는 '그 라이브러리를 사용하는 우리 코드가 생각대로 잘 동작하는가'에 집중하는 것이 일반적인 테스트 전략인 것 같아요!

덕분에 테스트의 역할과 범위에 대해 다시 한 번 생각해볼 수 있었네요. 좋은 질문 감사합니다! 😊


OutputView.printWinners(cars.findWinners());
}
}

Choose a reason for hiding this comment

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

RacingService로 로직을 분리해주셨군요!

사실 제가 처음 리뷰를 드릴 때 의도했던 바를 조금 더 명확하게 설명드리고 싶어서 추가 의견을 남깁니다. 제가 착각해서 조금 헷갈리게 말을 작성했던 것 같아요. 죄송해요 🥲

제가 처음에 컨트롤러의 책임을 언급하며 혼동했던 이유는 게임의 전체적인 흐름과 상태를 관리하는 모델이 없다 보니, 컨트롤러가 직접 Cars를 제어하면서 게임 진행까지 책임지는 것처럼 보였기 때문인데요, 이전 코멘트의 원래 의도는 레이싱 게임의 상태와 행위를 함께 관리하는 새로운 모델 객체를 만드는 것이었습니다.

현재는 Cars가 자동차 목록을 관리하는 동시에 우승자를 찾는 역할(findWinners)까지 수행하고 있는데요, 여기서 한 걸음 더 나아가 Cars는 자동차들의 일급 컬렉션 역할에만 집중하게 하고, 새로 만든 게임 담당 객체가 Cars를 소유하며 게임 진행 자체를 책임지게 하는 구조는 어떨까요?

이렇게 구조를 바꾸면 좋은 점은...

  • 객체별 책임이 명확해집니다
    • Car: 자동차 하나의 상태를 책임집니다.
    • Cars: 자동차 '목록'을 관리하는 책임에만 집중합니다.
    • Racing: 게임의 규칙과 상태를 책임집니다.
  • 응집도가 높아집니다.
    • 게임과 관련된 로직이 RacingGame 하나에 모여있어 나중에 게임 규칙이 바뀌더라도 RacingGame 클래스만 수정하면 됩니다.
  • 컨트롤러는 정말 지휘만 하게 됩니다.
    • 컨트롤러는 game.startRound() 처럼 더 높은 수준의 명령만 내리면 됩니다.

지금 제안 드린 구조에 대해서는 어떻게 생각하시는지 편하게 의견 남겨주세요!

{
String[] splits = getStrings(splitCar);
if (splits != null) return splits;
splitCar = scanner.nextLine().split(",");

Choose a reason for hiding this comment

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

depth를 논의해보기 전에 먼저

  1. 스페이스 + 쉼표의 경우 에러 메시지 출력 O
    image

  2. 쉼표 + 쉼표의 경우 에러 메시지 출력 X
    image

<2번 상황 원인>
Java의 기본 split() 메서드는 결과 배열의 맨 뒤에 오는 모든 공백 문자열을 자동으로 제거해버립니다.

  • "a,b,,".split(",")["a", "b"] (길이 2)
  • ",,,".split(",")[] (길이가 0인 빈 배열)
    따라서 ,,,를 입력하면, carNames는 아무 요소도 없는, length0인 배열이 됩니다.
String[] carNames = scanner.nextLine().split(",", -1);

→ 이렇게 하면 해결 가능하다고 하네요!

Copy link
Author

Choose a reason for hiding this comment

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

메소드를 만들고 입력값으로 ,,,을 테스트를 안해봤었네요 ㅠㅠ
감사합니다!!


public static boolean isValid(String [] cars){
if(cars.length == 1 ){
System.out.println("한 개 이상의 자동차를 입력해주세요!");

Choose a reason for hiding this comment

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

➕ 현재는 메시지를 콘솔에 출력하고 있는데 자바 예외처리 문법을 학습해보시면 좋을 것 같습니다.

if (cars.length <= 1) {
        // boolean을 반환하는 대신, 예외를 생성하고 던집니다.
        throw new IllegalArgumentException("[ERROR] 두 개 이상의 자동차를 입력해주세요!");
    }
while (true) {
        try {
            System.out.println("경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)");
            String[] carNames = scanner.nextLine().split(",", -1);

            // 예외가 발생할 수 있는 검증 메서드를 호출
            validateCarNames(carNames);
						
						... // 이 외 로직
        } catch (IllegalArgumentException e) {
            // validateCarNames에서 예외를 던지면 이 코드가 실행됨
            // 예외에 담긴 메시지를 출력하고, while 루프의 처음으로 돌아감
            System.out.println(e.getMessage());
        }
    }

Copy link
Author

Choose a reason for hiding this comment

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

[f6c6e25]
적용해봤습니다!

Copy link
Author

Choose a reason for hiding this comment

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

throw 문법은 어떻게 적용할지 잘 모르겠어서 더 공부해보겠습니다!

@nonactress
Copy link
Author

nonactress commented Sep 22, 2025

@SANGHEEJEONG

  1. CarsWinner 생성
    앞에서 말씀해주신 Cars가 우승자도 찾는 메소드도 가지고 있는 것보단 위 CarsWinner로 클래스를 분리해서 만들어보았습니다! 말씀하신 규칙과 관련된 값들이 하나의 객체로 모여있다고 생각합니다!!
    [43190b6]

Copy link

@SANGHEEJEONG SANGHEEJEONG left a comment

Choose a reason for hiding this comment

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

안녕하세요, 현진님
내일 뵙기 전에 질문에 대한 답변 하나만 남겨뒀어요 :)
같이 페어프로그래밍 재밌게 해봐요!!

// given: "pobi"라는 이름을 가진 자동차를 준비하고
String carName = "pobi";
FixedNumberMoveStub fixedNumberMoveStub = new FixedNumberMoveStub();

Choose a reason for hiding this comment

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

결론부터 말씀드리면, 프로젝트 규모가 아무리 커져도 java.util.Random과 같은 외부 표준 라이브러리가 정말 랜덤한 값을 생성하는지 직접 테스트하는 경우는 거의 없는 것 같습니다.

이유를 크게 두 가지로 생각해봤는데요,

  1. 신뢰와 책임의 범위
    Java의 표준 라이브러리처럼 엄청 많은 개발자가 사용하는 도구는 이미 오랜 시간에 걸쳐 검증되었다고 생각합니다! 만약 저희가 자동차 회사라고 생각해봤을 때, 저희의 책임은 타이어나 이외의 자동차 부품들을 검증하는 것이 아니라, 이 부품들을 가져와서 우리가 만든 자동차가 잘 달리게 하는 것이 아닐까요? 그래서 저희는 직접 작성한 코드의 로직을 테스트하는 데 집중하는 것이 더 효율적이라고 생각합니다.
  2. 테스트의 목적
    저희가 진짜 테스트하고 싶은 것은 랜덤 값 그 자체가 아니라, '랜덤 값을 사용하는 우리 코드가 예상대로 잘 동작하는가' 일 것 같아요!

저희는 외부 라이브러리의 기능 자체를 검증하기보다는 '그 라이브러리를 사용하는 우리 코드가 생각대로 잘 동작하는가'에 집중하는 것이 일반적인 테스트 전략인 것 같아요!

덕분에 테스트의 역할과 범위에 대해 다시 한 번 생각해볼 수 있었네요. 좋은 질문 감사합니다! 😊

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