ewhk9887/korean_code_reviews_from_github huggingface dataset:
@@ -0,0 +1,28 @@
+import DateValidate from './validator/DateValidate.js';
+import MenuValidate from './validator/MenuValidate.js';
+
+class InputValidator {
+  #REGAX;
+
+  constructor() {
+    this.#REGAX = /\s|[!@#$%^&*(),?":{}|<>]|[a-zA-Z]/;
+  }
+
+  async dateValidate(date) {
+    const dateValidator = new DateValidate(this.#REGAX, date);
+    dateValidator.regaxCheck();
+    dateValidator.safeCheck();
+    dateValidator.rangeCheck();
+  }
+
+  async menuValidate(menu) {
+    const menuValidator = new MenuValidate(menu);
+    menuValidator.menuCheck();
+    menuValidator.menuAmountCheck(this.#REGAX);
+    menuValidator.formCheck();
+    menuValidator.duplicateCheck();
+    menuValidator.drinkCheck();
+  }
+}
+
+export default InputValidator;

리뷰: 유효성 검사는 InputValidate 안에서, MenuValidate, DateValidate 의 객체들로 나뉘게 됩니다.
상위에서 정규식을 정의해주고, 정규식을 써야하는 검사 필요시, 하위 객체들에게 내려주는 방식으로 사용했습니다!
@@ -0,0 +1,21 @@
+/* eslint-disable max-lines-per-function */
+import { OUTPUT_MSG } from '../../src/domain/constants/PlannerMsg.js';
+
+describe('배지 테스트', () => {
+  const testCases = [
+    { amount: 1000, expected: '없음' },
+    { amount: 4900, expected: '없음' },
+    { amount: 5000, expected: '별' },
+    { amount: 9900, expected: '별' },
+    { amount: 10000, expected: '트리' },
+    { amount: 19999, expected: '트리' },
+    { amount: 20000, expected: '산타' },
+    { amount: 50000, expected: '산타' },
+  ];
+
+  testCases.forEach((testCase) => {
+    test(`배지 테스트 : ${testCase.amount}`, () => {
+      expect(OUTPUT_MSG.userBadge(testCase.amount)).toBe(testCase.expected);
+    });
+  });
+});

리뷰: 이렇게 처리할 수 있군요!
@@ -0,0 +1,37 @@
+/**
+ * 
+<애피타이저>
+양송이수프(6,000), 타파스(5,500), 시저샐러드(8,000)
+
+<메인>
+티본스테이크(55,000), 바비큐립(54,000), 해산물파스타(35,000), 크리스마스파스타(25,000)
+
+<디저트>
+초코케이크(15,000), 아이스크림(5,000)
+
+<음료>
+제로콜라(3,000), 레드와인(60,000), 샴페인(25,000)
+ */
+
+export const FOOD_MENU = {
+  appetizer: {
+    soup: ['양송이수프', 6000],
+    tapas: ['타파스', 5500],
+    salad: ['시저샐러드', 8000],
+  },
+  main: {
+    stake: ['티본스테이크', 55000],
+    barbeque: ['바비큐립', 54000],
+    seafood: ['해산물파스타', 35000],
+    christmas: ['크리스마스파스타', 25000],
+  },
+  dessert: {
+    cake: ['초코케이크', 15000],
+    iceCream: ['아이스크림', 5000],
+  },
+  drink: {
+    coke: ['제로콜라', 3000],
+    wine: ['레드와인', 60000],
+    champane: ['샴페인', 25000],
+  },
+};

리뷰: FOOD_MENU 객체를 깊은 동결하는 건 어떠신가요?
@@ -0,0 +1,37 @@
+/**
+ * 
+<애피타이저>
+양송이수프(6,000), 타파스(5,500), 시저샐러드(8,000)
+
+<메인>
+티본스테이크(55,000), 바비큐립(54,000), 해산물파스타(35,000), 크리스마스파스타(25,000)
+
+<디저트>
+초코케이크(15,000), 아이스크림(5,000)
+
+<음료>
+제로콜라(3,000), 레드와인(60,000), 샴페인(25,000)
+ */
+
+export const FOOD_MENU = {
+  appetizer: {
+    soup: ['양송이수프', 6000],
+    tapas: ['타파스', 5500],
+    salad: ['시저샐러드', 8000],
+  },
+  main: {
+    stake: ['티본스테이크', 55000],
+    barbeque: ['바비큐립', 54000],
+    seafood: ['해산물파스타', 35000],
+    christmas: ['크리스마스파스타', 25000],
+  },
+  dessert: {
+    cake: ['초코케이크', 15000],
+    iceCream: ['아이스크림', 5000],
+  },
+  drink: {
+    coke: ['제로콜라', 3000],
+    wine: ['레드와인', 60000],
+    champane: ['샴페인', 25000],
+  },
+};

리뷰: 좋은 리뷰 감사합니다! @joywhy pr 링크 남겨주시면 맞리뷰 갈게요!!
@@ -0,0 +1,37 @@
+package christmas.service;
+
+import christmas.domain.Order;
+import christmas.domain.constant.Message;
+import christmas.domain.constant.dish.Appetizer;
+import christmas.domain.constant.dish.Beverage;
+import christmas.domain.constant.dish.Dessert;
+import christmas.domain.constant.dish.MainDish;
+import christmas.domain.constant.dish.Orderable;
+import java.util.Map;
+import java.util.Objects;
+
+public class OrderMaker {
+    public Order make(Map<String, Integer> parsedOrder) {
+        Order order = new Order();
+        parsedOrder.forEach((dishLabel, count) -> order.addMenu(findDish(dishLabel), count));
+        order.validate();
+        return order;
+    }
+
+    private Orderable findDish(String input) {
+        if (Objects.nonNull(Appetizer.valueOfLabel(input))) {
+            return Appetizer.valueOfLabel(input);
+        }
+        if (Objects.nonNull(Beverage.valueOfLabel(input))) {
+            return Beverage.valueOfLabel(input);
+        }
+        if (Objects.nonNull(Dessert.valueOfLabel(input))) {
+            return Dessert.valueOfLabel(input);
+        }
+        if (Objects.nonNull(MainDish.valueOfLabel(input))) {
+            return MainDish.valueOfLabel(input);
+        }
+        throw new IllegalArgumentException(Message.INVALID_ORDER.getContent());
+    }
+
+}

리뷰: 메뉴 enum 타입이 있었으면 한 번에 찾을 수 있을거 같습니다.
@@ -0,0 +1,38 @@
+package christmas.domain.constant.dish;
+
+import java.util.Map;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+public enum MainDish implements Orderable {
+    T_BONE_STEAK("티본스테이크", 55000),
+    BARBEQUE_RIBS("바비큐립", 54000),
+    SEAFOOD_PASTA("해산물파스타", 35000),
+    CHRISTMAS_PASTA("크리스마스파스타", 25000);
+
+    private static final Map<String, MainDish> BY_LABEL =
+            Stream.of(MainDish.values()).collect(Collectors.toMap(MainDish::getLabel, e -> e));
+
+    private final String label;
+    private final int price;
+
+    MainDish(String label, int price) {
+        this.label = label;
+        this.price = price;
+    }
+
+    @Override
+    public String getLabel() {
+        return label;
+    }
+
+    @Override
+    public int getPrice() {
+        return price;
+    }
+
+    public static MainDish valueOfLabel(String label) {
+        return BY_LABEL.get(label);
+    }
+
+}

리뷰: static으로 Map을 생성해둔 거 좋은 거 같습니다. 
그런데 BY_LABEL 이름을 대문자로 하는 것이 컨벤션인지 저도 잘 모르겠더라고요. 
상수는 아닌데 static final이기는 하고 뭐가 맞을까요?
@@ -0,0 +1,31 @@
+package christmas.service;
+
+import christmas.domain.DecemberDate;
+import christmas.domain.constant.Discount;
+import christmas.domain.constant.dish.Orderable;
+import christmas.domain.dto.BenefitDto;
+import christmas.service.discountcalculator.SpecialDiscountCalculator;
+import christmas.service.discountcalculator.WeekDiscountCalculator;
+import christmas.service.discountcalculator.XmasDdayDiscountCalculator;
+import java.util.Map;
+
+public class DiscountManager {
+    public BenefitDto applyDiscount(Map<Orderable, Integer> menus, DecemberDate visitDate) {
+        int totalCost = getTotalCost(menus);
+        BenefitDto benefitDto = new BenefitDto(menus, totalCost);
+        if (totalCost < Discount.DISCOUNT_APPLY_LOWER_BOUND.getValue()) {
+            return benefitDto;
+        }
+        XmasDdayDiscountCalculator.apply(benefitDto, visitDate);
+        WeekDiscountCalculator.apply(menus, benefitDto, visitDate);
+        SpecialDiscountCalculator.apply(benefitDto, visitDate);
+        return benefitDto;
+    }
+
+    private int getTotalCost(Map<Orderable, Integer> menus) {
+        return menus.entrySet().stream()
+                .mapToInt(entry -> entry.getKey().getPrice() * entry.getValue())
+                .sum();
+    }
+
+}

리뷰: menus보다 Order를 인자로 받는 것이 더 객체지향에 맞다고 생각합니다. 그리고 Order 내부에 getTotalCost 메소드가 있는 것이 어떨까요? 그러면 자신의 데이터로 결과를 얻을 수 있습니다.
@@ -0,0 +1,21 @@
+package christmas.service;
+
+import christmas.domain.constant.Benefit;
+import christmas.domain.dto.BenefitDto;
+
+public class PresentationManager {
+    private static final int PRESENTATION_THRESHOLD = 120000;
+    private static final int PRESENTATION_PRICE = 25000;
+
+    private PresentationManager() {
+
+    }
+
+    public static void present(BenefitDto benefitDto) {
+        if (benefitDto.getTotalCost() >= PRESENTATION_THRESHOLD) {
+            benefitDto.addBenefit(Benefit.PRESENTATION, PRESENTATION_PRICE);
+            benefitDto.presentChampagne();
+        }
+    }
+
+}

리뷰: 120_000 처럼 _ 을 붙일 수 있습니다!
@@ -0,0 +1,21 @@
+package christmas.service;
+
+import christmas.domain.constant.Benefit;
+import christmas.domain.dto.BenefitDto;
+
+public class PresentationManager {
+    private static final int PRESENTATION_THRESHOLD = 120000;
+    private static final int PRESENTATION_PRICE = 25000;
+
+    private PresentationManager() {
+
+    }
+
+    public static void present(BenefitDto benefitDto) {
+        if (benefitDto.getTotalCost() >= PRESENTATION_THRESHOLD) {
+            benefitDto.addBenefit(Benefit.PRESENTATION, PRESENTATION_PRICE);
+            benefitDto.presentChampagne();
+        }
+    }
+
+}

리뷰: private으로 생성을 막아준느 것 좋습니다! 다만 내용이 없으면 빈줄도 없는 것이 좋습니다.
@@ -0,0 +1,24 @@
+package christmas.service.discountcalculator;
+
+import static christmas.domain.constant.Discount.DATE_OF_CHRISTMAS;
+import static christmas.domain.constant.Discount.ONE_WEEK;
+import static christmas.domain.constant.Discount.SPECIAL_DISCOUNT_AMOUNT;
+import static christmas.domain.constant.Discount.THREE;
+
+import christmas.domain.DecemberDate;
+import christmas.domain.constant.Benefit;
+import christmas.domain.dto.BenefitDto;
+
+public class SpecialDiscountCalculator {
+    private SpecialDiscountCalculator() {
+
+    }
+
+    public static void apply(BenefitDto benefitDto, DecemberDate visitDate) {
+        if (visitDate.date() == DATE_OF_CHRISTMAS.getValue()
+                || visitDate.date() % ONE_WEEK.getValue() == THREE.getValue()) {
+            benefitDto.addBenefit(Benefit.SPECIAL_DISCOUNT, SPECIAL_DISCOUNT_AMOUNT.getValue());
+        }
+    }
+
+}

리뷰: 긴 조건식은 메소드로 뽑는 것이 가독성에 좋습니다!
@@ -0,0 +1,70 @@
+package christmas.service.discountcalculator;
+
+import static christmas.domain.constant.Discount.ONE;
+import static christmas.domain.constant.Discount.ONE_WEEK;
+import static christmas.domain.constant.Discount.TWO;
+import static christmas.domain.constant.Discount.WEEK_DISCOUNT_UNIT;
+import static christmas.domain.constant.Discount.ZERO;
+
+import christmas.domain.DecemberDate;
+import christmas.domain.constant.Benefit;
+import christmas.domain.constant.dish.Dessert;
+import christmas.domain.constant.dish.MainDish;
+import christmas.domain.constant.dish.Orderable;
+import christmas.domain.dto.BenefitDto;
+import java.util.Map;
+import java.util.Map.Entry;
+
+public class WeekDiscountCalculator {
+    private WeekDiscountCalculator() {
+
+    }
+
+    public static void apply(Map<Orderable, Integer> menus, BenefitDto benefitDto, DecemberDate visitDate) {
+        if (isWeekend(visitDate)) {
+            applyWeekendDiscount(menus, benefitDto);
+            return;
+        }
+        applyWeekdayDiscount(menus, benefitDto);
+    }
+
+    private static void applyWeekendDiscount(Map<Orderable, Integer> menus, BenefitDto benefitDto) {
+        if (getMainDishCount(menus) == ZERO.getValue()) {
+            return;
+        }
+        benefitDto.addBenefit(Benefit.WEEK_END_DISCOUNT, getMainDishCount(menus) * WEEK_DISCOUNT_UNIT.getValue());
+    }
+
+    private static void applyWeekdayDiscount(Map<Orderable, Integer> menus, BenefitDto benefitDto) {
+        if (getDessertCount(menus) == ZERO.getValue()) {
+            return;
+        }
+        benefitDto.addBenefit(Benefit.WEEK_DAY_DISCOUNT, getDessertCount(menus) * WEEK_DISCOUNT_UNIT.getValue());
+    }
+
+    private static int getDessertCount(Map<Orderable, Integer> menus) {
+        return countDishesOfCertainCategory(menus, Dessert.class);
+    }
+
+    private static int getMainDishCount(Map<Orderable, Integer> menus) {
+        return countDishesOfCertainCategory(menus, MainDish.class);
+    }
+
+    private static int countDishesOfCertainCategory(Map<Orderable, Integer> menus,
+                                                    Class<? extends Orderable> category) {
+        int count = ZERO.getValue();
+        for (Entry<Orderable, Integer> entry : menus.entrySet()) {
+            if (entry.getKey().getClass() == category) {
+                count += entry.getValue();
+            }
+        }
+        return count;
+    }
+
+    private static boolean isWeekend(DecemberDate visitDate) {
+        int dateNumber = visitDate.date();
+        return dateNumber % ONE_WEEK.getValue() == ONE.getValue()
+                || dateNumber % ONE_WEEK.getValue() == TWO.getValue();
+    }
+
+}

리뷰: dto에 add하는 것보다 결과값을 반환하면 Dto의 의존성을 떨어뜨리고 다른 Dto에도 전달 할 수 있습니다.
@@ -0,0 +1,8 @@
+public class Car {
+    public String name;
+    public int position;
+
+    public int run() {
+        return this.position++;
+    }
+}
\ No newline at end of file

리뷰: addPosition 도 데이터 변경에 대한 설명이 되는 것 같지만,
run 이라는 행위를 표현하는 단어로 메서드명을 지어보면 어떨까요?

추후에 자동차 기본 이동 조건이 +2 로 바뀐다고 가정 했을 때
addPosition 이면 해당 메서드를 사용하는 입장에서 position+1 일 것 같다는 생각이 드는데
run 으로 추상화 되어있으면 +2 를 하든 +1 을 하든 자동차가 이동한다는 행위를 나타내서 혼동이 없을 것 같습니다 ~
@@ -0,0 +1,8 @@
+public class Car {
+    public String name;
+    public int position;
+
+    public int run() {
+        return this.position++;
+    }
+}
\ No newline at end of file

리뷰: 결과를 보여주는 View 의 역할은 ResultView 에 위임하면 어떨까요?
@@ -0,0 +1,8 @@
+public class Car {
+    public String name;
+    public int position;
+
+    public int run() {
+        return this.position++;
+    }
+}
\ No newline at end of file

리뷰: 추후에 콘솔이 아닌 GUI 프로그램으로 확장된다고 했을 때 Car 이라는 pure 해야하는 모델 클래스가 더럽혀지지 않을까요~?
@@ -0,0 +1,22 @@
+import java.util.ArrayList;
+import java.util.List;
+
+public class GameWinner {
+    public List<String> findWinner(List<Car> cars) {
+        int max = cars.get(0).position;
+        for (int i = 1; i < cars.size(); i++) {
+            max = Math.max(max, cars.get(i).position);
+        }
+        List<String> winnerList = new ArrayList<>();
+        for (Car car : cars) {
+            addWinnerList(car, max, winnerList);
+        }
+        return winnerList;
+    }
+
+    private void addWinnerList(Car car, int max, List<String> winnerList) {
+        if (car.position == max) {
+            winnerList.add(car.name);
+        }
+    }
+}

리뷰: 조슈아 블로크의 effective java 에는 '객체는 인터페이스를 사용해 참조하라.'
라는 부분이 있습니다.
한번 읽어보시고 개선해보시면 좋을 것 같아요 ~
jaehun2841.github.io/2019/03/01/effective-java-item64/#%EC%9C%A0%EC%97%B0%ED%95%9C-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8%EC%9D%84-%EC%83%9D%EC%84%B1%ED%95%98%EB%8A%94-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4-%ED%83%80%EC%9E%85-%EB%B3%80%EC%88%98
@@ -0,0 +1,22 @@
+import java.util.ArrayList;
+import java.util.List;
+
+public class GameWinner {
+    public List<String> findWinner(List<Car> cars) {
+        int max = cars.get(0).position;
+        for (int i = 1; i < cars.size(); i++) {
+            max = Math.max(max, cars.get(i).position);
+        }
+        List<String> winnerList = new ArrayList<>();
+        for (Car car : cars) {
+            addWinnerList(car, max, winnerList);
+        }
+        return winnerList;
+    }
+
+    private void addWinnerList(Car car, int max, List<String> winnerList) {
+        if (car.position == max) {
+            winnerList.add(car.name);
+        }
+    }
+}

리뷰: 배열 사용을 지양하고 List 로 최대한 넘겨받으면 어떨까요?
@@ -0,0 +1,18 @@
+import java.util.List;
+
+public class Main {
+    public static void main(String[] args) {
+        InputView inputView = new InputView();
+        RacingGame racingGame = new RacingGame();
+        GameWinner gameWinner = new GameWinner();
+        ResultView resultView = new ResultView();
+
+        List<Car> cars = inputView.registerCar(inputView.inputCarName());
+        int time = inputView.inputTime();
+        for (int i = 0; i < time; i++) {
+            racingGame.raceByTime(cars);
+            resultView.showResultByTime(cars);
+        }
+        resultView.showFinalResult(gameWinner.findWinner(cars));
+    }
+}

리뷰: Main 에 콘솔 출력하는 코드를 없애보면 어떨까요~?
@@ -0,0 +1,29 @@
+import java.util.Random;
+import java.util.List;
+
+public class RacingGame {
+    private static final int RANDOM_RANGE = 10;
+    private static final int MIN_MOVE_STANDARD = 4;
+
+    public void raceByTime(List<Car> cars) {
+        for (Car car : cars) {
+            int randomValue = giveRandomValue();
+            move(car, randomValue);
+        }
+    }
+
+    private int giveRandomValue() {
+        Random random = new Random();
+        return random.nextInt(RANDOM_RANGE);
+    }
+
+    private void move(Car car, int randomValue) {
+        if (canMove(randomValue)) {
+            car.run();
+        }
+    }
+
+    private boolean canMove(int number) {
+        return number >= MIN_MOVE_STANDARD;
+    }
+}
\ No newline at end of file

리뷰: 접근제어자를 생략하신 이유가 있을까요? 생략하면 어떤 접근제어자가 붙을까요?
@@ -0,0 +1,8 @@
+public class Car {
+    public String name;
+    public int position;
+
+    public int run() {
+        return this.position++;
+    }
+}
\ No newline at end of file

리뷰: 메서드명을 지을 때 조건 변경의 가능성을 꼭 고려하도록 하겠습니다! 단번에 이해되는 좋은 예시 제시해주셔서 감사합니다.
@@ -0,0 +1,8 @@
+public class Car {
+    public String name;
+    public int position;
+
+    public int run() {
+        return this.position++;
+    }
+}
\ No newline at end of file

리뷰: GUI 프로그램으로 확장될 경우도 고려해야 한다는 점 명심하겠습니다!
@@ -0,0 +1,29 @@
+import java.util.Random;
+import java.util.List;
+
+public class RacingGame {
+    private static final int RANDOM_RANGE = 10;
+    private static final int MIN_MOVE_STANDARD = 4;
+
+    public void raceByTime(List<Car> cars) {
+        for (Car car : cars) {
+            int randomValue = giveRandomValue();
+            move(car, randomValue);
+        }
+    }
+
+    private int giveRandomValue() {
+        Random random = new Random();
+        return random.nextInt(RANDOM_RANGE);
+    }
+
+    private void move(Car car, int randomValue) {
+        if (canMove(randomValue)) {
+            car.run();
+        }
+    }
+
+    private boolean canMove(int number) {
+        return number >= MIN_MOVE_STANDARD;
+    }
+}
\ No newline at end of file

리뷰: 메서드의 접근제어자에 몰두한 나머지 변수의 접근제어자는 미처 고려하지 못했던 것 같습니다. 생략할 경우 default 접근제어자가 되어 해당 **패키지** 내에서 접근가능하게 합니다. 여기에서는 해당 클래스 내에서만의 접근이 필요하므로 private를 붙여 수정하겠습니다.
@@ -0,0 +1,22 @@
+import java.util.ArrayList;
+import java.util.List;
+
+public class GameWinner {
+    public List<String> findWinner(List<Car> cars) {
+        int max = cars.get(0).position;
+        for (int i = 1; i < cars.size(); i++) {
+            max = Math.max(max, cars.get(i).position);
+        }
+        List<String> winnerList = new ArrayList<>();
+        for (Car car : cars) {
+            addWinnerList(car, max, winnerList);
+        }
+        return winnerList;
+    }
+
+    private void addWinnerList(Car car, int max, List<String> winnerList) {
+        if (car.position == max) {
+            winnerList.add(car.name);
+        }
+    }
+}

리뷰: 자료 감사합니다👍 List인터페이스 구현해보도록 하겠습니다.
@@ -0,0 +1,8 @@
+public class Car {
+    public String name;
+    public int position;
+
+    public int run() {
+        return this.position++;
+    }
+}
\ No newline at end of file

리뷰: car 의 상태가 외부에서 접근가능한 public 인데요~ 
private 으로 변경해주셔야할 것 같습니다 !
@@ -0,0 +1,22 @@
+import java.util.ArrayList;
+import java.util.List;
+
+public class GameWinner {
+    public List<String> findWinner(List<Car> cars) {
+        int max = cars.get(0).position;
+        for (int i = 1; i < cars.size(); i++) {
+            max = Math.max(max, cars.get(i).position);
+        }
+        List<String> winnerList = new ArrayList<>();
+        for (Car car : cars) {
+            addWinnerList(car, max, winnerList);
+        }
+        return winnerList;
+    }
+
+    private void addWinnerList(Car car, int max, List<String> winnerList) {
+        if (car.position == max) {
+            winnerList.add(car.name);
+        }
+    }
+}

리뷰: Winner 객체에서 들고 있으면 좋을만한 상태(필드 변수)들을 정의하고 책임을 부여해보면 어떨까요?
@@ -0,0 +1,22 @@
+import java.util.ArrayList;
+import java.util.List;
+
+public class GameWinner {
+    public List<String> findWinner(List<Car> cars) {
+        int max = cars.get(0).position;
+        for (int i = 1; i < cars.size(); i++) {
+            max = Math.max(max, cars.get(i).position);
+        }
+        List<String> winnerList = new ArrayList<>();
+        for (Car car : cars) {
+            addWinnerList(car, max, winnerList);
+        }
+        return winnerList;
+    }
+
+    private void addWinnerList(Car car, int max, List<String> winnerList) {
+        if (car.position == max) {
+            winnerList.add(car.name);
+        }
+    }
+}

리뷰: 객체지향에서는 객체의 상태에 직접 접근해서는 안됩니다.
private 으로 변경하시면 getter 로 변경하셔야할 것 같아요~
@@ -0,0 +1,22 @@
+import java.util.ArrayList;
+import java.util.List;
+
+public class GameWinner {
+    public List<String> findWinner(List<Car> cars) {
+        int max = cars.get(0).position;
+        for (int i = 1; i < cars.size(); i++) {
+            max = Math.max(max, cars.get(i).position);
+        }
+        List<String> winnerList = new ArrayList<>();
+        for (Car car : cars) {
+            addWinnerList(car, max, winnerList);
+        }
+        return winnerList;
+    }
+
+    private void addWinnerList(Car car, int max, List<String> winnerList) {
+        if (car.position == max) {
+            winnerList.add(car.name);
+        }
+    }
+}

리뷰: java8 의 stream api 를 적용해보면 어떨까요~?
@@ -0,0 +1,50 @@
+import java.util.Scanner;
+import java.util.List;
+import java.util.ArrayList;
+
+public class InputView {
+    private static final int INITIAL_POSITION = 0;
+    private static final int MIN_NAME_LENGTH = 1;
+    private static final int MAX_NAME_LENGTH = 5;
+
+    public String inputCarName() {
+        Scanner scanner = new Scanner(System.in);
+        System.out.println("경주할 자동차 이름을 입력하세요(이름은 쉼표(,) 기준으로 구분).");
+        return scanner.nextLine();
+    }
+
+    public List<Car> registerCar(String inputName) {
+        String[] names = inputName.split(",");
+        List<Car> cars = new ArrayList<>();
+        for (int i = 0; i < names.length; i++) {
+            checkValidName(names[i]);
+            cars.add(new Car());
+            cars.get(i).name = names[i];
+            cars.get(i).position = INITIAL_POSITION;
+        }
+        return cars;
+    }
+
+    public int inputTime() {
+        Scanner scanner = new Scanner(System.in);
+        System.out.println("시도할 횟수는 몇 회인가요?");
+        return scanner.nextInt();
+    }
+
+    private void checkValidName(String name) {
+        try {
+            checkNameLength(name);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    private void checkNameLength(String name) throws Exception {
+        if (name.length() < MIN_NAME_LENGTH || name.length() > MAX_NAME_LENGTH) {
+            throw new InputNameException(name);
+        }
+        if (name.trim().isEmpty()) {
+            throw new InputNameException();
+        }
+    }
+}

리뷰: InputView 에는 상태값이 없는데요,
util 클래스로 활용될 수 있을 것 같아서 모든 메서드들에 static 을 활용해보시면 어떨까요?
@@ -0,0 +1,29 @@
+import java.util.Random;
+import java.util.List;
+
+public class RacingGame {
+    private static final int RANDOM_RANGE = 10;
+    private static final int MIN_MOVE_STANDARD = 4;
+
+    public void raceByTime(List<Car> cars) {
+        for (Car car : cars) {
+            int randomValue = giveRandomValue();
+            move(car, randomValue);
+        }
+    }
+
+    private int giveRandomValue() {
+        Random random = new Random();
+        return random.nextInt(RANDOM_RANGE);
+    }
+
+    private void move(Car car, int randomValue) {
+        if (canMove(randomValue)) {
+            car.run();
+        }
+    }
+
+    private boolean canMove(int number) {
+        return number >= MIN_MOVE_STANDARD;
+    }
+}
\ No newline at end of file

리뷰: random 으로 인해 게임의 핵심 pulbic 메서드가 테스트 불가능해졌는데
어떻게 개선해 볼 수 있을까요?
@@ -0,0 +1,21 @@
+public class Car {
+    private String name;
+    private int position;
+
+    Car(String name) {
+        this.name = name;
+        this.position = 0;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public int getPosition() {
+        return position;
+    }
+
+    public void goForward() {
+        this.position++;
+    }
+}

리뷰: 규칙 : setter 를 사용하지 않는다 (getter 사용은 지양한다)

객체지향 프로그래밍에서 setter 와 getter 사용을 되도록 지양하라고 하는 이유가 무엇일까요?
@@ -0,0 +1,21 @@
+public class Car {
+    private String name;
+    private int position;
+
+    Car(String name) {
+        this.name = name;
+        this.position = 0;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public int getPosition() {
+        return position;
+    }
+
+    public void goForward() {
+        this.position++;
+    }
+}

리뷰: 랜덤을 통해 특정 조건에서 전진하는 게임의 룰을 자동차라는 모델 클래스가 알고 있을 필요가 있을까요?
예를 들어 게임의 모드가 추가되어서 랜덤 룰이 아닌 다른 게임 룰이 생긴다고 했을 때,
모델이 게임의 룰에 관계없이 pure 하게 짜여져 있으면 모델을 수정하지 않아도 되지 않을까요?
moveForward 가 단순히 car 의 position 을 ++ 하는 로직만 가지고 있으면 어떤지 의견드려봅니다 ~