ewhk9887/korean_code_reviews_from_github huggingface dataset:
@@ -0,0 +1,26 @@
+package lotto.domain;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class LottoRepository {
+    private final List<Lotto> lottos = new ArrayList<>();
+
+    public void issueLottos(int purchaseQuantity) {
+        for (int i = 0; i < purchaseQuantity; i++) {
+            Lotto lotto = new Lotto(Number.generateNumbers());
+            lottos.add(lotto);
+        }
+    }
+
+    public List<WinningResult> checkWinningResults(WinningLotto winningLotto) {
+        return lottos.stream()
+                .map(lotto -> lotto.checkWinningResult(winningLotto))
+                .collect(Collectors.toList());
+    }
+
+    public List<Lotto> getLottos() {
+        return lottos;
+    }
+}

리뷰: 전역변수를 이렇게 new로 생성하는 것이랑 생성자로 객체가 생성될 떄 this로 지정해줘서 작성하는 것에 대해 어떤 방식이 나은 표현인지 의견을 나눠보고 싶습니다.
@@ -0,0 +1,57 @@
+package lotto.view;
+
+import java.util.Arrays;
+import java.util.Map;
+import lotto.domain.Lotto;
+import lotto.domain.LottoRepository;
+import lotto.domain.Rank;
+import lotto.domain.WinningStats;
+
+public class OutputView {
+    private static final String PURCHASE_QUANTITY_FORMAT = "%n%d개를 구매했습니다.%n";
+    private static final String WINNING_STATS = "\n당첨 통계\n---";
+    private static final String WINNING_STATS_FORMAT = "%d개 일치%s (%,d원) - %d개%n";
+    private static final String HAS_BONUS_NUMBER = ", 보너스 볼 일치";
+    private static final String HAS_NOT_BONUS_NUMBER = "";
+    private static final String TOTAL_PROFIT_RATE_FORMAT = "총 수익률은 %.1f%%입니다.";
+
+    public static void printError(Exception e) {
+        System.out.println(e.getMessage());
+    }
+
+    public static void printPurchaseQuantity(int purchaseQuantity) {
+        System.out.printf(PURCHASE_QUANTITY_FORMAT, purchaseQuantity);
+    }
+
+    public static void printLottos(LottoRepository lottoRepository) {
+        for (Lotto lotto : lottoRepository.getLottos()) {
+            System.out.println(lotto.getNumbers()
+                    .stream()
+                    .sorted()
+                    .toList());
+        }
+    }
+
+    public static void printWinningStats(WinningStats winningStats) {
+        Map<Rank, Integer> rankCounts = winningStats.getWinningStats();
+        System.out.println(WINNING_STATS);
+        Arrays.stream(Rank.values())
+                .skip(1)
+                .forEach(rank -> System.out.printf(WINNING_STATS_FORMAT
+                        , rank.getWinningCount()
+                        , checkBonusNumber(rank)
+                        , rank.getPrize()
+                        , rankCounts.get(rank)));
+    }
+
+    private static String checkBonusNumber(Rank rank) {
+        if (rank.hasBonusNumber()) {
+            return HAS_BONUS_NUMBER;
+        }
+        return HAS_NOT_BONUS_NUMBER;
+    }
+
+    public static void printTotalProfitRate(double totalProfitRate) {
+        System.out.printf(TOTAL_PROFIT_RATE_FORMAT, totalProfitRate);
+    }
+}

리뷰: stream을 굉장히 잘 사용하시는 것 같습니다.  배워갑니다.
@@ -0,0 +1,58 @@
+package lotto.domain;
+
+import static lotto.global.Error.DUPLICATED;
+import static lotto.global.Error.INVALID_SIZE;
+
+import java.util.Collections;
+import java.util.List;
+
+public class Lotto {
+    private final List<Integer> numbers;
+
+    public Lotto(List<Integer> numbers) {
+        validateSize(numbers);
+        validateDuplicated(numbers);
+        validateNumberRange(numbers);
+        this.numbers = numbers;
+    }
+
+    private void validateSize(List<Integer> numbers) {
+        if (numbers.size() != 6) {
+            throw new IllegalArgumentException(INVALID_SIZE.getMessage());
+        }
+    }
+
+    private void validateDuplicated(List<Integer> numbers) {
+        if (numbers.stream()
+                .distinct().count() != numbers.size()) {
+            throw new IllegalArgumentException(DUPLICATED.getMessage());
+        }
+    }
+
+    private void validateNumberRange(List<Integer> numbers) {
+        numbers.forEach(Number::new);
+    }
+
+    // TODO: 추가 기능 구현
+    public WinningResult checkWinningResult(WinningLotto winningLotto) {
+        int winningCount = checkWinningCount(winningLotto);
+        boolean hasBonusNumber = hasBonusNumber(winningLotto);
+        return new WinningResult(winningCount, hasBonusNumber);
+    }
+
+    private int checkWinningCount(WinningLotto winningLotto) {
+        return (int) numbers.stream()
+                .filter(number -> winningLotto
+                        .getWinningNumbers()
+                        .contains(number))
+                .count();
+    }
+
+    private boolean hasBonusNumber(WinningLotto winningLotto) {
+        return numbers.contains(winningLotto.getBonusNumber());
+    }
+
+    public List<Integer> getNumbers() {
+        return Collections.unmodifiableList(numbers);
+    }
+}

리뷰: 오 .. 그런게 있었군요 개인적으로 형변환하는게 지저분하게 보이고 강제하는거 같아서 별로 선호하지 않았었는데 새로 알게됐네요 (메모)
@@ -0,0 +1,26 @@
+package lotto.domain;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class LottoRepository {
+    private final List<Lotto> lottos = new ArrayList<>();
+
+    public void issueLottos(int purchaseQuantity) {
+        for (int i = 0; i < purchaseQuantity; i++) {
+            Lotto lotto = new Lotto(Number.generateNumbers());
+            lottos.add(lotto);
+        }
+    }
+
+    public List<WinningResult> checkWinningResults(WinningLotto winningLotto) {
+        return lottos.stream()
+                .map(lotto -> lotto.checkWinningResult(winningLotto))
+                .collect(Collectors.toList());
+    }
+
+    public List<Lotto> getLottos() {
+        return lottos;
+    }
+}

리뷰: lottos 변수는 전역변수는 아닌거 같고 초기화를 진행해줘서 헷갈리신거 같아요 !!! 아래 코드보면 lottos.add 하더라구요 저는 개인적으로 this 표현을 하지 않으면 정말 알아보기 힘든 경우가 아니면 잘 사용하진 않습니다🤔
@@ -0,0 +1,30 @@
+package lotto.domain;
+
+import java.util.Collections;
+import java.util.List;
+import lotto.global.Error;
+
+public class WinningLotto {
+    private final List<Integer> winningNumbers;
+    private final int bonusNumber;
+
+    public WinningLotto(List<Integer> winningNumbers, int bonusNumber) {
+        validateBonusNumber(winningNumbers, bonusNumber);
+        this.winningNumbers = winningNumbers;
+        this.bonusNumber = bonusNumber;
+    }
+
+    private void validateBonusNumber(List<Integer> winningNumbers, int bonusNumber) {
+        if (winningNumbers.contains(bonusNumber)) {
+            throw new IllegalArgumentException(Error.INVALID_BONUS_NUMBER.getMessage());
+        }
+    }
+
+    public List<Integer> getWinningNumbers() {
+        return Collections.unmodifiableList(winningNumbers);
+    }
+
+    public int getBonusNumber() {
+        return bonusNumber;
+    }
+}

리뷰: winningNumbers  를 List<Integer>대신 Lotto형을 사용하는 것에 대해 어떻게 생각하시나요 ?!?! 저는 사실 winningLotto의 경우 Lotto1개를 가지되 보너스 번호도 추가로 가지고 있다는 형태로 코드를 작성했었거든요 객체상태가 숫자 6개를 가지는 것도 동일하구요 !!! 어떻게 생각하는지 궁금합니다
@@ -0,0 +1,38 @@
+package lotto.domain;
+
+import static lotto.domain.Rank.NONE;
+
+import java.util.Arrays;
+
+public class WinningResult {
+    private static final int SECOND_OR_THIRD = 5;
+    private final int winningCount;
+    private final boolean hasBonusNumber;
+
+    public WinningResult(int winningCount, boolean matchingBonusNumber) {
+        this.winningCount = winningCount;
+        this.hasBonusNumber = matchingBonusNumber;
+    }
+
+    public Rank toRank() {
+        if (winningCount == SECOND_OR_THIRD) {
+            return Arrays.stream(Rank.values())
+                    .filter(rank -> rank.getWinningCount() == winningCount)
+                    .filter(rank -> rank.hasBonusNumber() == hasBonusNumber)
+                    .findAny()
+                    .orElse(NONE);
+        }
+        return Arrays.stream(Rank.values())
+                .filter(rank -> rank.getWinningCount() == winningCount)
+                .findAny()
+                .orElse(NONE);
+    }
+
+    public int getWinningCount() {
+        return winningCount;
+    }
+
+    public boolean hasBonusNumber() {
+        return hasBonusNumber;
+    }
+}

리뷰: 제 개인적인 의견으로 저는 enum이 선언된 순서로 인덱스가 부여되지만 enum의 순서는 언제든 변경될 수 있고, 다른 항목이 추가되면서 enum 순서가 엉킬수 있게 된다는 점에서 인덱스 순서로 코드를 개발하는 방식은 지양하는 편입니다 !! 근데 코멘트 보고 생각해보니 저도 enum 인덱스대로 print 찍도록 코드를 작성한게 있었던거 같은데 .......🤔🥲
@@ -0,0 +1,21 @@
+package lotto.global;
+
+public enum Error {
+    INVALID_SIZE("로또는 6개의 번호로 구성되어야 합니다."),
+    DUPLICATED("로또 번호는 서로 중복될 수 없습니다."),
+    INVALID_TYPE("숫자만 입력 가능합니다."),
+    INVALID_UNIT("1,000원 단위로만 입력 가능합니다."),
+    INVALID_AMOUNT_RANGE("구매금액은 1,000원 이상 100,000원 이하여야 합니다."),
+    INVALID_NUMBER_RANGE("로또 번호는 1-45 사이의 숫자만 입력 가능합니다."),
+    INVALID_BONUS_NUMBER("보너스 번호는 로또번호와 중복될 수 없습니다.");
+    private static final String ERROR = "[ERROR] ";
+    private final String message;
+
+    Error(String message) {
+        this.message = ERROR + message;
+    }
+
+    public String getMessage() {
+        return message;
+    }
+}

리뷰: 에러 메세지 앞에 붙는 [ERROR]를 상수 처리 해주셨네요!
저는 enum클래스 안에서는 상수처리할 생각을 못했는데 배워갑니다
@@ -0,0 +1,58 @@
+package lotto.domain;
+
+import java.util.Collections;
+import java.util.EnumMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+public enum Rank {
+    NONE(0, false, 0),
+    FIFTH(3, false, 5_000),
+    FOURTH(4, false, 50_000),
+    THIRD(5, false, 1_500_000),
+    SECOND(5, true, 30_000_000),
+    FIRST(6, false, 2_000_000_000);
+
+    private final int winningCount;
+    private final boolean hasBonusNumber;
+    private final int prize;
+
+    Rank(int winningCount, boolean hasBonusNumber, int prize) {
+        this.winningCount = winningCount;
+        this.hasBonusNumber = hasBonusNumber;
+        this.prize = prize;
+    }
+
+    public static WinningStats countRanks(List<WinningResult> winningResults) {
+        Map<Rank, Integer> winningStats = new EnumMap<>(Rank.class);
+        List<Rank> ranks = convertToRanks(winningResults);
+        for (Rank rank : values()) {
+            int count = Collections.frequency(ranks, rank);
+            winningStats.put(rank, count);
+        }
+        return new WinningStats(winningStats);
+    }
+
+    private static List<Rank> convertToRanks(List<WinningResult> winningResults) {
+        return winningResults.stream()
+                .map(WinningResult::toRank)
+                .collect(Collectors.toList());
+    }
+
+    public long calculateProfit(int count) {
+        return (long) prize * count;
+    }
+
+    public int getWinningCount() {
+        return winningCount;
+    }
+
+    public boolean hasBonusNumber() {
+        return hasBonusNumber;
+    }
+
+    public int getPrize() {
+        return prize;
+    }
+}

리뷰: ```java
public enum BonusMatchStatus {
    MATCHED, NOT_MATCHED, IRRELEVANT;
}
```
```java
FIRST(6, BonusMatchStatus.IRRELEVANT, 2_000_000_000),
    SECOND(5, BonusMatchStatus.MATCHED, 30_000_000),
    THIRD(5, BonusMatchStatus.NOT_MATCHED, 1_500_000),
    FOURTH(4, BonusMatchStatus.IRRELEVANT, 50_000),
    FIFTH(3, BonusMatchStatus.IRRELEVANT, 5_000),
    LOSING(0, BonusMatchStatus.IRRELEVANT, 0);
```
enum안에서 boolean 타입을 이용하여 2등과 3등을 구분해주셨네요!
저는 if로 하나하나 다구분해줬는데 더 효율적인 방법인것 같습니다.

그래서 저도 이 부분에 대해서 다른 리뷰를 보면서 공부하다가 알게 된 사실인데
보너스볼의 일치여부를 true/false로 관리하게 된다면 서로 false값에 대해 서로 다른 관점이 있을 수 있다고 합니다.
false라는 값이 "일치하면 안된다는 것", "상관없다" 두개로 나뉠 수 있기 때문에 
2등(일치해야한다), 3등(일치하면 안된다),  나머지등수(상관없다) 3가지 케이스가 나온다고 합니다
그래서 이렇게 enum응 활용하는 방법도 있다고 합니다!
@@ -0,0 +1,47 @@
+package lotto.view;
+
+import static lotto.global.Error.INVALID_TYPE;
+
+import camp.nextstep.edu.missionutils.Console;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class InputView {
+    private static final String ASK_PURCHASE_AMOUNT = "구입금액을 입력해 주세요.";
+    private static final String ASK_WINNING_NUMBERS = "\n당첨 번호를 입력해 주세요.";
+    private static final String ASK_BONUS_NUMBER = "\n보너스 번호를 입력해 주세요.";
+
+
+    public static int askPurchaseAmount() {
+        System.out.println(ASK_PURCHASE_AMOUNT);
+        String input = Console.readLine();
+        validateType(input);
+        return Integer.parseInt(input);
+    }
+
+    public static List<Integer> askWinningNumbers() {
+        System.out.println(ASK_WINNING_NUMBERS);
+        String input = Console.readLine();
+        return Arrays.stream(input.trim()
+                        .split(","))
+                .peek(number -> validateType(number))
+                .mapToInt(number -> Integer.parseInt(number))
+                .boxed()
+                .collect(Collectors.toList());
+    }
+
+    public static int askBonusNumber() {
+        System.out.println(ASK_BONUS_NUMBER);
+        String input = Console.readLine();
+        validateType(input);
+        return Integer.parseInt(input);
+    }
+
+    private static void validateType(String input) {
+        if (!input.chars()
+                .allMatch(Character::isDigit)) {
+            throw new IllegalArgumentException(INVALID_TYPE.getMessage());
+        }
+    }
+}

리뷰: 해당 부분에서 아무런 입력없이 엔터를 눌렀을시 문자로 잡히지 않고
For input string: "" 오류로 빠지게 됩니다. 저도 그 이유가 궁금하여 구글링을 해봤습니다.

**isDigit**
public static boolean isDigit(int codePoint)
지정된 문자 (Unicode 코드 포인트)가 숫자인가 어떤가를 판정합니다.
[getType(codePoint)](http://cris.joongbu.ac.kr/course/java/api/java/lang/Character.html#getType(int)) 에 의해 나타나는 범용 카테고리형이 **DECIMAL_DIGIT_NUMBER**인 경우, 숫자가 됩니다.

**DECIMAL_DIGIT_NUMBER**
public static final byte DECIMAL_DIGIT_NUMBER
Unicode 사양의 범용 카테고리 「Nd」

이러한 이유 때문에 아무것도 입력받지 않은 "" 즉 Nd 일때 숫자로 통과 하였다가 나중에 NumberFormatException 오류가 뜨는 듯 합니다. 

(isDigit 클래스)
http://cris.joongbu.ac.kr/course/java/api/java/lang/Character.html#isDigit(int)
@@ -0,0 +1,58 @@
+package lotto.domain;
+
+import java.util.Collections;
+import java.util.EnumMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+public enum Rank {
+    NONE(0, false, 0),
+    FIFTH(3, false, 5_000),
+    FOURTH(4, false, 50_000),
+    THIRD(5, false, 1_500_000),
+    SECOND(5, true, 30_000_000),
+    FIRST(6, false, 2_000_000_000);
+
+    private final int winningCount;
+    private final boolean hasBonusNumber;
+    private final int prize;
+
+    Rank(int winningCount, boolean hasBonusNumber, int prize) {
+        this.winningCount = winningCount;
+        this.hasBonusNumber = hasBonusNumber;
+        this.prize = prize;
+    }
+
+    public static WinningStats countRanks(List<WinningResult> winningResults) {
+        Map<Rank, Integer> winningStats = new EnumMap<>(Rank.class);
+        List<Rank> ranks = convertToRanks(winningResults);
+        for (Rank rank : values()) {
+            int count = Collections.frequency(ranks, rank);
+            winningStats.put(rank, count);
+        }
+        return new WinningStats(winningStats);
+    }
+
+    private static List<Rank> convertToRanks(List<WinningResult> winningResults) {
+        return winningResults.stream()
+                .map(WinningResult::toRank)
+                .collect(Collectors.toList());
+    }
+
+    public long calculateProfit(int count) {
+        return (long) prize * count;
+    }
+
+    public int getWinningCount() {
+        return winningCount;
+    }
+
+    public boolean hasBonusNumber() {
+        return hasBonusNumber;
+    }
+
+    public int getPrize() {
+        return prize;
+    }
+}

리뷰: 이번에 큰 수의 자료형을 찾아보다가 BigDecimal을 알게 되었는데 
만약 그럴일 없겠지만 long범위도 벗어난다면 쓰면 좋을것 같아요
@@ -0,0 +1,30 @@
+package christmas.controller;
+
+import christmas.model.UserOrder;
+import christmas.service.ChristmasEventService;
+
+public class ChristmasEventController {
+
+    private static final String EVENT_START_MESSAGE = "안녕하세요! 우테코 식당 12월 이벤트 플래너입니다.";
+
+    private final ChristmasEventService christmasEventService;
+
+    public ChristmasEventController() {
+        System.out.println(EVENT_START_MESSAGE);
+        christmasEventService = new ChristmasEventService();
+    }
+
+    public void run() {
+        UserOrder userOrder = requestUserInput();
+
+        requestUserBenefit(userOrder);
+    }
+
+    public UserOrder requestUserInput() {
+        return christmasEventService.requestUserInput();
+    }
+
+    public void requestUserBenefit(UserOrder userOrder) {
+        christmasEventService.requestUserBenefit(userOrder);
+    }
+}

리뷰: 혹시 view가 아닌 controller에 따로 출력 메세지를 두신 이유가 있으신지 궁금합니당
@@ -0,0 +1,22 @@
+package christmas.constant;
+
+public class Constants {
+
+    public static final String DEFAULT_CONDITION = "없음";
+    public static final String STAR_BADGE = "별";
+    public static final String TREE_BADGE = "트리";
+    public static final String SANTA_BADGE = "산타";
+    public static final int MAX_ORDER_COUNT = 20;
+    public static final int MIN_INPUT_DATE = 1;
+    public static final int MAX_INPUT_DATE = 31;
+    public static final int D_DAY_SALE_DEFAULT = 1000;
+    public static final int D_DAY_SALE_DATE = 25;
+    public static final int D_DAY_INCREASE_PRICE = 100;
+    public static final int SPECIAL_SALE_DEFAULT = 1000;
+    public static final int WEEKDAY_SALE_DEFAULT = 2023;
+    public static final int WEEKEND_SALE_DEFAULT = 2023;
+    public static final int PRESENT_CHAMPAGNE_PRICE = 25000;
+    public static final int MIN_BENEFITABLE_PRICE = 10000;
+    public static final int MIN_PRESENTABLE_PRICE = 120000;
+
+}

리뷰: 다음에는 enum으로 분리해서 사용해 보시는걸 추천합니다
@@ -0,0 +1,40 @@
+package racingcar.view;
+
+import camp.nextstep.edu.missionutils.Console;
+import java.util.Arrays;
+import java.util.List;
+import racingcar.util.validator.AttemptsNumberValidator;
+import racingcar.util.validator.CarNamesValidator;
+import racingcar.util.Constant;
+import racingcar.util.validator.Validator;
+
+public class InputView {
+
+    private final Validator namesValidator;
+    private final Validator attempsValidator;
+
+    private InputView() {
+        this.namesValidator = new CarNamesValidator();
+        this.attempsValidator = new AttemptsNumberValidator();
+    }
+
+    public static InputView getInstance() {
+        return new InputView();
+    }
+
+    public List<String> readCarNames() {
+        String input = input();
+        namesValidator.validate(input);
+        return Arrays.asList(input.split(Constant.DELIMITER_COMMA));
+    }
+
+    public int readAttemptsNumber() {
+        String input = input();
+        attempsValidator.validate(input);
+        return Integer.parseInt(input);
+    }
+
+    private String input() {
+        return Console.readLine();
+    }
+}

리뷰: 싱글톤 패턴을 사용하시려고 한것 같은데 필드 상에 InputView instance = new InputView() 를 선언해놓은게 아니면 getInstance를 할때마다 새로운 객체가 생성되어서 싱글톤 패턴을 사용하려고 하신 의미가 없지 않나요??? 궁금해서 여쭤봅니다!
@@ -0,0 +1,31 @@
+package racingcar.domain.car;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class Cars {
+
+    private final List<Car> cars = new ArrayList<>();
+
+    public void addCar(Car car) {
+        cars.add(car);
+    }
+
+    public List<String> getWinners() {
+        return cars.stream()
+                .filter(car -> car.isWinner(getMaxPosition()))
+                .map(Car::getName)
+                .collect(Collectors.toList());
+    }
+
+    private int getMaxPosition() {
+        return Collections.max(cars.stream()
+                .map(Car::getPosition).toList());
+    }
+
+    public List<Car> getCars() {
+        return Collections.unmodifiableList(cars);
+    }
+}

리뷰: 데이터를 가공할때 getter를 지양하는 방향으로 설계를 원하신다면 해당 글을 추천드립니다!
https://tecoble.techcourse.co.kr/post/2020-04-28-ask-instead-of-getter/
@@ -0,0 +1,44 @@
+package racingcar.util.validator;
+
+import java.util.regex.Pattern;
+
+public class AttemptsNumberValidator extends Validator {
+
+    private static final int ATTEMPTS_MIN_RANGE = 1;
+    private static final int ATTEMPTS_MAX_RANGE = 20;
+    private final Pattern numberValidatePattern = Pattern.compile("^[0-9]{1,2}$");
+
+    @Override
+    public void validate(String input) {
+        validateNumber(input);
+        validateNumberRange(input);
+    }
+
+    private void validateNumberRange(String input) {
+        int number = Integer.parseInt(input);
+        if (number < ATTEMPTS_MIN_RANGE || number > ATTEMPTS_MAX_RANGE) {
+            throw new IllegalArgumentException(ErrorMessage.INVALID_NUMBER.getMessage());
+        }
+    }
+
+    private void validateNumber(String input) {
+        if (!numberValidatePattern.matcher(input).matches()) {
+            throw new IllegalArgumentException(ErrorMessage.INVALID_NUMBER.getMessage());
+        }
+    }
+
+    private enum ErrorMessage {
+        INVALID_NUMBER("1 이상 20 이하의 숫자만 입력해주세요.");
+
+        private final String Message;
+
+        ErrorMessage(String message) {
+            Message = message;
+        }
+
+        public String getMessage() {
+            return Message;
+        }
+    }
+
+}

리뷰: 해당 enum에 에러메시지가 1개라서 Validator에 상수로 선언해주셔도 될거 같습니다!
@@ -0,0 +1,53 @@
+package racingcar.util.validator;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.regex.Pattern;
+import racingcar.util.Constant;
+
+public class CarNamesValidator extends Validator {
+
+    private static final int CAR_NAMES_MIN_RANGE = 2;
+    private static final int CAR_NAMES_MAX_RANGE = 20;
+    private final Pattern nameValidatePattern = Pattern.compile("^[a-zA-Z가-힣0-9]{1,5}$");
+
+    @Override
+    public void validate(String input) {
+        List<String> carNames = Arrays.asList(input.split(Constant.DELIMITER_COMMA));
+        validateIterator(carNames);
+        validateCarNamesNumbers(carNames);
+    }
+
+    private void validateIterator(List<String> carNames) {
+        for (String carName : carNames) {
+            validateNamePattern(carName);
+        }
+    }
+
+    private void validateNamePattern(String carName) {
+        if (!nameValidatePattern.matcher(carName).matches()) {
+            throw new IllegalArgumentException(ErrorMessage.INVALID_NAMES.getMessage());
+        }
+    }
+
+    private void validateCarNamesNumbers(List<String> carNames) {
+        if (carNames.size() < CAR_NAMES_MIN_RANGE || carNames.size() > CAR_NAMES_MAX_RANGE) {
+            throw new IllegalArgumentException(ErrorMessage.INVALID_NAMES_NUMBERS.getMessage());
+        }
+    }
+
+    private enum ErrorMessage {
+        INVALID_NAMES("특수문자를 제외한 5자 이하의 이름을 입력해주세요."),
+        INVALID_NAMES_NUMBERS("2이상 20개 이하의 이름을 작성해 주세요.");
+
+        private final String Message;
+
+        ErrorMessage(String message) {
+            Message = message;
+        }
+
+        public String getMessage() {
+            return Message;
+        }
+    }
+}

리뷰: 정규식 표현 여기서도 배워갑니다!