ewhk9887/korean_code_reviews_from_github huggingface dataset:
@@ -0,0 +1,57 @@
+package racingcar.view;
+
+import racingcar.model.Car;
+import racingcar.model.CarName;
+import racingcar.model.Cars;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class OutputView {
+
+    private static final String NEW_LINE = "\n";
+    private static final String SCORE_UNIT = "-";
+    private static final String SCORE_BOARD = "%s : %s";
+    private static final String WINNER_SEPARATOR = ", ";
+    private static final String WINNERS_ARE = "최종 우승자 : ";
+    private static final String ERROR_PREFIX = "[ERROR] ";
+    private static final String RESULT_INTRO = "실행 결과";
+
+    public void printResult(Cars cars) {
+        System.out.println(getScore(cars));
+    }
+
+    private String getScore(Cars cars) {
+        return cars.get().stream()
+                .map(this::toScoreBoard)
+                .collect(Collectors.joining(NEW_LINE));
+    }
+
+    private String toScoreBoard(Car car) {
+        return String.format(SCORE_BOARD, car.getName(), convertToScore(car.getPosition()));
+    }
+
+    private String convertToScore(int position) {
+        return SCORE_UNIT.repeat(position);
+    }
+
+    public void printWinners(List<CarName> winners) {
+        System.out.print(WINNERS_ARE);
+        System.out.println(getWinnerNames(winners));
+    }
+
+    private String getWinnerNames(List<CarName> winners) {
+        return winners.stream()
+                .map(CarName::get)
+                .collect(Collectors.joining(WINNER_SEPARATOR));
+    }
+
+    public void printError(IllegalArgumentException error) {
+        System.out.print(ERROR_PREFIX);
+        System.out.println(error.getMessage());
+    }
+
+    public void printRaceIntro() {
+        System.out.println(RESULT_INTRO);
+    }
+}

리뷰: 상수처리가 정말 깔끔하네요! 
특히 SCORE_BOARD 부분 배워갑니다.
@@ -0,0 +1,57 @@
+package racingcar.view;
+
+import racingcar.model.Car;
+import racingcar.model.CarName;
+import racingcar.model.Cars;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class OutputView {
+
+    private static final String NEW_LINE = "\n";
+    private static final String SCORE_UNIT = "-";
+    private static final String SCORE_BOARD = "%s : %s";
+    private static final String WINNER_SEPARATOR = ", ";
+    private static final String WINNERS_ARE = "최종 우승자 : ";
+    private static final String ERROR_PREFIX = "[ERROR] ";
+    private static final String RESULT_INTRO = "실행 결과";
+
+    public void printResult(Cars cars) {
+        System.out.println(getScore(cars));
+    }
+
+    private String getScore(Cars cars) {
+        return cars.get().stream()
+                .map(this::toScoreBoard)
+                .collect(Collectors.joining(NEW_LINE));
+    }
+
+    private String toScoreBoard(Car car) {
+        return String.format(SCORE_BOARD, car.getName(), convertToScore(car.getPosition()));
+    }
+
+    private String convertToScore(int position) {
+        return SCORE_UNIT.repeat(position);
+    }
+
+    public void printWinners(List<CarName> winners) {
+        System.out.print(WINNERS_ARE);
+        System.out.println(getWinnerNames(winners));
+    }
+
+    private String getWinnerNames(List<CarName> winners) {
+        return winners.stream()
+                .map(CarName::get)
+                .collect(Collectors.joining(WINNER_SEPARATOR));
+    }
+
+    public void printError(IllegalArgumentException error) {
+        System.out.print(ERROR_PREFIX);
+        System.out.println(error.getMessage());
+    }
+
+    public void printRaceIntro() {
+        System.out.println(RESULT_INTRO);
+    }
+}

리뷰: 저도 @khj1 님과 같은 이유로 getter를 사용했는데요,

개인적으로는 `출력을 위한 getter는 캡슐화를 위배하는 게 아니다` 라고 생각합니다.
물론 의도와 다르게 사용되는 경우 캡슐화를 저해시킬 수 있습니다. 이 부분은 어쩔 수 없는 단점이라고 생각해요. 

개인적으로 프리코스 기간 동안 '객체의 자율성을 얼마나 허용해줘야 하는지' 를 계속 고민했는데, 제가 다다른 결론은 '객체는 UI 로직 외의 경우에서 자율성을 보장해줘야 한다' 였습니다.

객체지향을 사용하는 이유는 결국 '자율성'보다는 '유연성'이니까요! UI 로직의 변경이 도메인 영역을 침범하는 것은 안된다고 판단했습니다. 

저는 이렇게 생각했는데,

@JerryK026 님 말씀 들어보니 이 부분은 스타일의 차이일 수도 있겠네요!
업무 환경이나 팀의 코드 스타일에 따라 달라질 것 같기도 합니다. 확실히 이 분야에 정답은 없는 것 같네요.. ㅎㅎ
@@ -0,0 +1,57 @@
+package racingcar.view;
+
+import racingcar.model.Car;
+import racingcar.model.CarName;
+import racingcar.model.Cars;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class OutputView {
+
+    private static final String NEW_LINE = "\n";
+    private static final String SCORE_UNIT = "-";
+    private static final String SCORE_BOARD = "%s : %s";
+    private static final String WINNER_SEPARATOR = ", ";
+    private static final String WINNERS_ARE = "최종 우승자 : ";
+    private static final String ERROR_PREFIX = "[ERROR] ";
+    private static final String RESULT_INTRO = "실행 결과";
+
+    public void printResult(Cars cars) {
+        System.out.println(getScore(cars));
+    }
+
+    private String getScore(Cars cars) {
+        return cars.get().stream()
+                .map(this::toScoreBoard)
+                .collect(Collectors.joining(NEW_LINE));
+    }
+
+    private String toScoreBoard(Car car) {
+        return String.format(SCORE_BOARD, car.getName(), convertToScore(car.getPosition()));
+    }
+
+    private String convertToScore(int position) {
+        return SCORE_UNIT.repeat(position);
+    }
+
+    public void printWinners(List<CarName> winners) {
+        System.out.print(WINNERS_ARE);
+        System.out.println(getWinnerNames(winners));
+    }
+
+    private String getWinnerNames(List<CarName> winners) {
+        return winners.stream()
+                .map(CarName::get)
+                .collect(Collectors.joining(WINNER_SEPARATOR));
+    }
+
+    public void printError(IllegalArgumentException error) {
+        System.out.print(ERROR_PREFIX);
+        System.out.println(error.getMessage());
+    }
+
+    public void printRaceIntro() {
+        System.out.println(RESULT_INTRO);
+    }
+}

리뷰: 저는 개인적으로 MVC 에서 View는 컨트롤러로부터 동적인 정보만 받아야 한다고 생각하는데요! 혹시 이런 접근 방법은 어떠실까요?

제가 이렇게 생각하는 이유는, 관심사가 다르기 때문입니다.
에러 처리에 관한 출력은 해당 try-catch 블록에 책임이 있다고 생각해요.

사실 정답은 없는 것 같으나, 제가 고수하는 방법은 어떤지 제안드려 봅니다.
@@ -0,0 +1,47 @@
+package racingcar.model;
+
+public class Car implements Comparable<Car> {
+    private static final int MOVABLE_LOWER_BOUND = 4;
+
+    private final CarName name;
+    private final Position position = Position.init();
+
+    private Car(String name) {
+        this.name = CarName.from(name);
+    }
+
+    public static Car from(String name) {
+        return new Car(name);
+    }
+
+    public void move(int command) {
+        if (isMovable(command)) {
+            position.increase();
+        }
+    }
+
+    private static boolean isMovable(int command) {
+        return command >= MOVABLE_LOWER_BOUND;
+    }
+
+    public boolean isAt(Position expectedPosition) {
+        return position.equals(expectedPosition);
+    }
+
+    public int getPosition() {
+        return position.getPosition();
+    }
+
+    public CarName getName() {
+        return name;
+    }
+
+    public boolean isSamePosition(Car other) {
+        return this.position.equals(other.position);
+    }
+
+    @Override
+    public int compareTo(Car other) {
+        return this.position.compareTo(other.position);
+    }
+}

리뷰: 저도 배워갑니다!! 이런 식으로 비교도 가능하군요..
@@ -0,0 +1,52 @@
+package racingcar.model;
+
+import java.util.Objects;
+
+public class CarName {
+
+    private static final String INVALID_NAME_LENGTH = "자동차 이름은 5자를 초과할 수 없습니다.";
+    private static final int NAME_LENGTH_UPPER_BOUND = 5;
+
+    private final String name;
+
+    public CarName(String name) {
+        validate(name);
+        this.name = name;
+    }
+
+    private void validate(String name) {
+        if (isInvalidLength(name)) {
+            throw new IllegalArgumentException(INVALID_NAME_LENGTH);
+        }
+    }
+
+    private boolean isInvalidLength(String name) {
+        return name.length() > NAME_LENGTH_UPPER_BOUND;
+    }
+
+    public static CarName from(String name) {
+        return new CarName(name);
+    }
+
+    public String get() {
+        return name;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        CarName carName = (CarName) o;
+        return Objects.equals(name, carName.name);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(name);
+    }
+
+    @Override
+    public String toString() {
+        return name;
+    }
+}

리뷰: hashCode가 사용되지 않는 것 같아, 따로 오버라이딩 하신 이유가 궁금합니다!
@@ -0,0 +1,52 @@
+package racingcar.model;
+
+import java.util.Objects;
+
+public class CarName {
+
+    private static final String INVALID_NAME_LENGTH = "자동차 이름은 5자를 초과할 수 없습니다.";
+    private static final int NAME_LENGTH_UPPER_BOUND = 5;
+
+    private final String name;
+
+    public CarName(String name) {
+        validate(name);
+        this.name = name;
+    }
+
+    private void validate(String name) {
+        if (isInvalidLength(name)) {
+            throw new IllegalArgumentException(INVALID_NAME_LENGTH);
+        }
+    }
+
+    private boolean isInvalidLength(String name) {
+        return name.length() > NAME_LENGTH_UPPER_BOUND;
+    }
+
+    public static CarName from(String name) {
+        return new CarName(name);
+    }
+
+    public String get() {
+        return name;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        CarName carName = (CarName) o;
+        return Objects.equals(name, carName.name);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(name);
+    }
+
+    @Override
+    public String toString() {
+        return name;
+    }
+}

리뷰: get 메소드가 toString과 중복되는 것 같습니다!
@@ -0,0 +1,63 @@
+package racingcar.cotroller;
+
+import racingcar.RandomRacingNumberGenerator;
+import racingcar.model.AttemptCount;
+import racingcar.model.Cars;
+import racingcar.view.InputView;
+import racingcar.view.OutputView;
+
+import java.util.List;
+import java.util.function.Supplier;
+
+public class RacingCarController {
+
+    private final InputView inputView;
+    private final OutputView outputView;
+    private final Cars cars;
+
+    public RacingCarController() {
+        inputView = new InputView();
+        outputView = new OutputView();
+        cars = checkError(this::getCars);
+    }
+
+    public void run() {
+        progressGame();
+        printWinners();
+    }
+
+    private void progressGame() {
+        AttemptCount attemptCount = checkError(inputView::readAttemptCount);
+
+        outputView.printRaceIntro();
+        while (attemptCount.isPlayable()) {
+            attempt();
+            attemptCount.decrease();
+        }
+    }
+
+    private void printWinners() {
+        outputView.printWinners(cars.findWinners());
+    }
+
+    private void attempt() {
+        RandomRacingNumberGenerator numberGenerator = new RandomRacingNumberGenerator();
+        cars.race(numberGenerator);
+
+        outputView.printResult(cars);
+    }
+
+    private Cars getCars() {
+        List<String> names = inputView.readNames();
+        return Cars.from(names);
+    }
+
+    private <T> T checkError(Supplier<T> reader) {
+        try {
+            return reader.get();
+        } catch (IllegalArgumentException error) {
+            outputView.printError(error);
+            return checkError(reader);
+        }
+    }
+}

리뷰: 따로 컨트롤러에서 RandomRacingNumberGenerator를 주입해주신 이유가 궁금합니다.

제 생각에는, RandomRacingNumberGenerator를 race 메소드 내에서 선언하면 컨트롤러에서 알아야 할 관심사가 하나 줄어드는 이점이 있을 것 같아서요!
@@ -1,29 +1,51 @@
 package lotto.domain;
 
-import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
+import java.util.stream.Collectors;
 
 public class Lotto {
-    private List<Integer> lotto;
+    private List<LottoNumber> lotto;
 
-    public Lotto(List<Integer> list) {
-        lotto = new ArrayList<>();
-        lotto = list;
+    public Lotto(List<LottoNumber> lotto) {
+        Collections.sort(lotto);
+        this.lotto = lotto;
+    }
+
+    public Lotto() {
     }
 
-    public List<Integer> getLotto() {
+    public List<LottoNumber> getLotto() {
         return lotto;
     }
 
-    public void setLotto(List<Integer> lotto) {
+    public void setLotto(List<LottoNumber> lotto) {
         this.lotto = lotto;
     }
 
-    public boolean isContain(int number) {
-        return lotto.contains(number);
+    public int countMatch(List<LottoNumber> winningNumber) {
+        int count = 0;
+        for (LottoNumber lottoNumber : winningNumber) {
+            count = increaseCount(lottoNumber, count);
+        }
+        return count;
+    }
+
+    public int increaseCount(LottoNumber lottoNumber, int count) {
+        if (this.hasThisNumber(lottoNumber)) {
+            count++;
+        }
+        return count;
+    }
+
+    public boolean hasThisNumber(LottoNumber number) {
+        return lotto.stream()
+                .filter(lottoNo -> lottoNo.equals(number)).count() != 0;
     }
 
     public String toString() {
-        return lotto.toString();
+        return lotto.stream()
+                .map(LottoNumber::toString)
+                .collect(Collectors.joining(", ","[","]"));
     }
-}
+}
\ No newline at end of file

리뷰: 뭘 포함하고 있다는거죠?
@@ -1,29 +1,51 @@
 package lotto.domain;
 
-import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
+import java.util.stream.Collectors;
 
 public class Lotto {
-    private List<Integer> lotto;
+    private List<LottoNumber> lotto;
 
-    public Lotto(List<Integer> list) {
-        lotto = new ArrayList<>();
-        lotto = list;
+    public Lotto(List<LottoNumber> lotto) {
+        Collections.sort(lotto);
+        this.lotto = lotto;
+    }
+
+    public Lotto() {
     }
 
-    public List<Integer> getLotto() {
+    public List<LottoNumber> getLotto() {
         return lotto;
     }
 
-    public void setLotto(List<Integer> lotto) {
+    public void setLotto(List<LottoNumber> lotto) {
         this.lotto = lotto;
     }
 
-    public boolean isContain(int number) {
-        return lotto.contains(number);
+    public int countMatch(List<LottoNumber> winningNumber) {
+        int count = 0;
+        for (LottoNumber lottoNumber : winningNumber) {
+            count = increaseCount(lottoNumber, count);
+        }
+        return count;
+    }
+
+    public int increaseCount(LottoNumber lottoNumber, int count) {
+        if (this.hasThisNumber(lottoNumber)) {
+            count++;
+        }
+        return count;
+    }
+
+    public boolean hasThisNumber(LottoNumber number) {
+        return lotto.stream()
+                .filter(lottoNo -> lottoNo.equals(number)).count() != 0;
     }
 
     public String toString() {
-        return lotto.toString();
+        return lotto.stream()
+                .map(LottoNumber::toString)
+                .collect(Collectors.joining(", ","[","]"));
     }
-}
+}
\ No newline at end of file

리뷰: Builder 패턴과 메소드 체이닝 공부하시고 stringBuilder 다시 써보세요
@@ -1,29 +1,51 @@
 package lotto.domain;
 
-import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
+import java.util.stream.Collectors;
 
 public class Lotto {
-    private List<Integer> lotto;
+    private List<LottoNumber> lotto;
 
-    public Lotto(List<Integer> list) {
-        lotto = new ArrayList<>();
-        lotto = list;
+    public Lotto(List<LottoNumber> lotto) {
+        Collections.sort(lotto);
+        this.lotto = lotto;
+    }
+
+    public Lotto() {
     }
 
-    public List<Integer> getLotto() {
+    public List<LottoNumber> getLotto() {
         return lotto;
     }
 
-    public void setLotto(List<Integer> lotto) {
+    public void setLotto(List<LottoNumber> lotto) {
         this.lotto = lotto;
     }
 
-    public boolean isContain(int number) {
-        return lotto.contains(number);
+    public int countMatch(List<LottoNumber> winningNumber) {
+        int count = 0;
+        for (LottoNumber lottoNumber : winningNumber) {
+            count = increaseCount(lottoNumber, count);
+        }
+        return count;
+    }
+
+    public int increaseCount(LottoNumber lottoNumber, int count) {
+        if (this.hasThisNumber(lottoNumber)) {
+            count++;
+        }
+        return count;
+    }
+
+    public boolean hasThisNumber(LottoNumber number) {
+        return lotto.stream()
+                .filter(lottoNo -> lottoNo.equals(number)).count() != 0;
     }
 
     public String toString() {
-        return lotto.toString();
+        return lotto.stream()
+                .map(LottoNumber::toString)
+                .collect(Collectors.joining(", ","[","]"));
     }
-}
+}
\ No newline at end of file

리뷰: 그리고 정말 toString을 이렇게 구현하시겠습니까?
@@ -0,0 +1,67 @@
+package lotto.domain;
+
+import java.util.Arrays;
+
+public enum Rank {
+    FIRST(6, 2000000000),
+    SECOND(5, 30000000),
+    THIRD(5, 1500000),
+    FOURTH(4, 50000),
+    FIFTH(3, 5000),
+    MISS(0, 0);
+
+    private static final int PRIZE_MIN_NUMBER = 3;
+
+    private int matchCount;
+    private int winningPrice;
+
+    private Rank(int matchCount, int winningPrice) {
+        this.matchCount = matchCount;
+        this.winningPrice = winningPrice;
+    }
+
+    public int getMatchCount() {
+        return matchCount;
+    }
+
+    public int getWinningPrice() {
+        return winningPrice;
+    }
+
+    public static Rank lookUpRank(int matchCount, boolean hasBonusNumber) {
+        if (matchCount < PRIZE_MIN_NUMBER) {
+            return MISS;
+        }
+
+        if (SECOND.isRightCount(matchCount) && hasBonusNumber) {
+            return SECOND;
+        }
+
+        if (THIRD.isRightCount(matchCount)) {
+            return THIRD;
+        }
+
+        return (Rank) Arrays.stream(values()).filter(rank -> rank.isRightCount(matchCount)).toArray()[0];
+    }
+
+    public boolean isRightCount(int matchCount) {
+        return this.matchCount == matchCount;
+    }
+
+    public int plusReward(int income) {
+        return income + winningPrice;
+    }
+
+    public String toString() {
+        StringBuilder stringBuilder = new StringBuilder();
+        stringBuilder.append(this.matchCount)
+                .append("개 일치");
+        if (this.winningPrice == Rank.SECOND.winningPrice) {
+            stringBuilder.append(", 보너스 볼 일치");
+        }
+        stringBuilder.append("(")
+                .append(this.winningPrice)
+                .append(")-");
+        return stringBuilder.toString();
+    }
+}
\ No newline at end of file

리뷰: 좀 더 좋은 메소드 네이밍
@@ -0,0 +1,67 @@
+package lotto.domain;
+
+import java.util.Arrays;
+
+public enum Rank {
+    FIRST(6, 2000000000),
+    SECOND(5, 30000000),
+    THIRD(5, 1500000),
+    FOURTH(4, 50000),
+    FIFTH(3, 5000),
+    MISS(0, 0);
+
+    private static final int PRIZE_MIN_NUMBER = 3;
+
+    private int matchCount;
+    private int winningPrice;
+
+    private Rank(int matchCount, int winningPrice) {
+        this.matchCount = matchCount;
+        this.winningPrice = winningPrice;
+    }
+
+    public int getMatchCount() {
+        return matchCount;
+    }
+
+    public int getWinningPrice() {
+        return winningPrice;
+    }
+
+    public static Rank lookUpRank(int matchCount, boolean hasBonusNumber) {
+        if (matchCount < PRIZE_MIN_NUMBER) {
+            return MISS;
+        }
+
+        if (SECOND.isRightCount(matchCount) && hasBonusNumber) {
+            return SECOND;
+        }
+
+        if (THIRD.isRightCount(matchCount)) {
+            return THIRD;
+        }
+
+        return (Rank) Arrays.stream(values()).filter(rank -> rank.isRightCount(matchCount)).toArray()[0];
+    }
+
+    public boolean isRightCount(int matchCount) {
+        return this.matchCount == matchCount;
+    }
+
+    public int plusReward(int income) {
+        return income + winningPrice;
+    }
+
+    public String toString() {
+        StringBuilder stringBuilder = new StringBuilder();
+        stringBuilder.append(this.matchCount)
+                .append("개 일치");
+        if (this.winningPrice == Rank.SECOND.winningPrice) {
+            stringBuilder.append(", 보너스 볼 일치");
+        }
+        stringBuilder.append("(")
+                .append(this.winningPrice)
+                .append(")-");
+        return stringBuilder.toString();
+    }
+}
\ No newline at end of file

리뷰: set이라는 명칭은 클래스 필드들에 값을 세팅할때 사용하니까 다른 명칭이 좋을 것 같습니다
@@ -0,0 +1,17 @@
+package lotto.domain;
+
+public class WinningLotto extends Lotto {
+    private Lotto lotto;
+    private int matchCount;
+    private boolean hasbonusNumber;
+
+    public WinningLotto(Lotto lotto, int matchCount, LottoNumber bonusNumber) {
+        this.hasbonusNumber = lotto.hasThisNumber(bonusNumber);
+        this.lotto = lotto;
+        this.matchCount = matchCount;
+    }
+
+    public Rank findRank() {
+        return Rank.lookUpRank(matchCount, hasbonusNumber);
+    }
+}
\ No newline at end of file

리뷰: get set 지워보고 생각해보십쇼 (객체지향 생활체조 규칙 get set 사용을 자제한다)
@@ -0,0 +1,32 @@
+package lotto.domain;
+
+import lotto.utils.Splitter;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class LottoGenerator {
+    List<LottoNumber> randomNumbers;
+
+    public LottoGenerator() {
+        randomNumbers = new ArrayList<>();
+    }
+
+    public Lotto manual(String continuousNumber) {
+        randomNumbers = Splitter.splitNumber(continuousNumber);
+        return new Lotto(randomNumbers);
+    }
+
+    public Lotto auto() {
+        return new Lotto(generateRandomNumbers());
+    }
+
+    public List<LottoNumber> generateRandomNumbers() {
+        for (int number = 1; number <= 45; number++) {
+            randomNumbers.add(new LottoNumber(number));
+        }
+        Collections.shuffle(randomNumbers);
+        return randomNumbers.subList(0, 6);
+    }
+}
\ No newline at end of file

리뷰: generateAuto() 가 낫지 않을까요
@@ -0,0 +1,32 @@
+package lotto.domain;
+
+import lotto.utils.Splitter;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class LottoGenerator {
+    List<LottoNumber> randomNumbers;
+
+    public LottoGenerator() {
+        randomNumbers = new ArrayList<>();
+    }
+
+    public Lotto manual(String continuousNumber) {
+        randomNumbers = Splitter.splitNumber(continuousNumber);
+        return new Lotto(randomNumbers);
+    }
+
+    public Lotto auto() {
+        return new Lotto(generateRandomNumbers());
+    }
+
+    public List<LottoNumber> generateRandomNumbers() {
+        for (int number = 1; number <= 45; number++) {
+            randomNumbers.add(new LottoNumber(number));
+        }
+        Collections.shuffle(randomNumbers);
+        return randomNumbers.subList(0, 6);
+    }
+}
\ No newline at end of file

리뷰: generateMenual 이 낫지 않을까요. menual인지 manual인지 통일 시켜달라니까 무시당했네요