ewhk9887/korean_code_reviews_from_github huggingface dataset:
@@ -0,0 +1,82 @@
+package bossmonster.domain.dto;
+
+public class GameHistoryDto {
+
+    private int turnCount;
+    private String playerName;
+    private String playerAttackType;
+    private int playerAttackDamage;
+    private int monsterAttackDamage;
+    private int playerMaxHP;
+    private int playerCurrentHP;
+    private int playerMaxMP;
+    private int playerCurrentMP;
+    private int monsterMaxHP;
+    private int monsterCurrentHP;
+    private boolean gameStatus;
+
+    public GameHistoryDto(int turnCount, String playerName, String playerAttackType, int playerAttackDamage,
+                          int monsterAttackDamage, int playerMaxHP, int playerCurrentHP, int playerMaxMP,
+                          int playerCurrentMP, int monsterMaxHP, int monsterCurrentHP, boolean gameStatus) {
+        this.turnCount = turnCount;
+        this.playerName = playerName;
+        this.playerAttackType = playerAttackType;
+        this.playerAttackDamage = playerAttackDamage;
+        this.monsterAttackDamage = monsterAttackDamage;
+        this.playerMaxHP = playerMaxHP;
+        this.playerCurrentHP = playerCurrentHP;
+        this.playerMaxMP = playerMaxMP;
+        this.playerCurrentMP = playerCurrentMP;
+        this.monsterMaxHP = monsterMaxHP;
+        this.monsterCurrentHP = monsterCurrentHP;
+        this.gameStatus = gameStatus;
+    }
+
+    public int getTurnCount() {
+        return turnCount;
+    }
+
+    public String getPlayerName() {
+        return playerName;
+    }
+
+    public String getPlayerAttackType() {
+        return playerAttackType;
+    }
+
+    public int getPlayerAttackDamage() {
+        return playerAttackDamage;
+    }
+
+    public int getMonsterAttackDamage() {
+        return monsterAttackDamage;
+    }
+
+    public int getPlayerMaxHP() {
+        return playerMaxHP;
+    }
+
+    public int getPlayerCurrentHP() {
+        return playerCurrentHP;
+    }
+
+    public int getPlayerMaxMP() {
+        return playerMaxMP;
+    }
+
+    public int getPlayerCurrentMP() {
+        return playerCurrentMP;
+    }
+
+    public int getMonsterMaxHP() {
+        return monsterMaxHP;
+    }
+
+    public int getMonsterCurrentHP() {
+        return monsterCurrentHP;
+    }
+
+    public boolean isGameStatus() {
+        return gameStatus;
+    }
+}

리뷰: `isFinish()`나 `isPlaying()`와 같은 것은 어떨까요?
@@ -0,0 +1,73 @@
+package bossmonster.domain;
+
+import bossmonster.domain.dto.GameHistoryDto;
+
+public class RaidGame {
+
+    private static final int DEFAULT_VALUE = 0;
+
+    private BossMonster bossMonster;
+    private Player player;
+    private GameHistory gameHistory;
+    private boolean status;
+    private int turns;
+
+    public RaidGame(BossMonster bossMonster, Player player) {
+        this.bossMonster = bossMonster;
+        this.player = player;
+        this.gameHistory = new GameHistory();
+        this.status = true;
+        this.turns = DEFAULT_VALUE;
+
+        gameHistory.addHistory(turns, status, player, bossMonster);
+    }
+
+    public void executeTurn(AttackType attackType) {
+        if (isGameProgress()) {
+            increaseTurn();
+            int playerAttackDamage = DEFAULT_VALUE;
+            int monsterAttackDamage = DEFAULT_VALUE;
+                playerAttackDamage = executePlayerTurn(attackType);
+            if (bossMonster.isAlive()) {
+                monsterAttackDamage = executeBossMonsterTurn();
+            }
+            setStatus();
+            createTurnHistory(attackType, playerAttackDamage, monsterAttackDamage);
+        }
+    }
+
+    public GameHistoryDto getGameHistory() {
+        return gameHistory.requestRaidHistory();
+    }
+
+    private boolean isGameProgress() {
+        setStatus();
+        return status;
+    }
+
+    private void setStatus() {
+        status = player.isAlive() && bossMonster.isAlive();
+    }
+
+    private void increaseTurn() {
+        turns ++;
+    }
+
+    private int executePlayerTurn(AttackType attackType) {
+        int playerAttackDamage = player.attack(attackType);
+        bossMonster.takeDamage(playerAttackDamage);
+        return playerAttackDamage;
+    }
+
+    private int executeBossMonsterTurn() {
+        int monsterAttackDamage = bossMonster.attack();
+        player.takeDamage(monsterAttackDamage);
+        return monsterAttackDamage;
+    }
+
+    private void createTurnHistory(AttackType playerAttackType, int playerAttackDamage, int monsterAttackDamage) {
+        gameHistory.addHistory(turns, playerAttackType.getName(), playerAttackDamage, monsterAttackDamage, status,
+                player,
+                bossMonster);
+    }
+}

리뷰: 컨트롤러와 도메인을 정말 잘 분리하신 것 같습니다!
@@ -0,0 +1,61 @@
+package bossmonster.domain;
+
+import bossmonster.exception.Validator;
+
+public class Player {
+
+    private String name;
+    private PlayerStatus status;
+
+    public Player(String name, int maxHP, int maxMP) {
+        this.name = Validator.validatePlayerName(name);
+        Validator.validatePlayerStatus(maxHP, maxMP);
+        this.status = new PlayerStatus(maxHP, maxMP);
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public boolean isAlive() {
+        return status.isAlive();
+    }
+
+    public int getMaxHP() {
+        return status.getMaxHP();
+    }
+
+    public int getCurrentHP() {
+        return status.getCurrentHP();
+    }
+
+    public int getMaxMP() {
+        return status.getMaxMP();
+    }
+
+    public int getCurrentMP() {
+        return status.getCurrentMP();
+    }
+
+    public int attack(AttackType attackType) {
+        if (attackType.getName().equals(AttackType.ATTACK_TYPE_NORMAL.getName())) {
+            recoverMP(attackType.getMpRecover());
+        }
+        if (attackType.getName().equals(AttackType.ATTACK_TYPE_MAGIC.getName())) {
+            consumeMP(attackType.getMpUsage());
+        }
+        return attackType.getDamage();
+    }
+
+    public void takeDamage(int damage) {
+        status.setCurrentHP(status.getCurrentHP() - damage);
+    }
+
+    private void recoverMP(int mp) {
+        status.setCurrentMP(status.getCurrentMP() + mp);
+    }
+
+    private void consumeMP(int mp) {
+        status.setCurrentMP(status.getCurrentMP() - mp);
+    }
+}

리뷰: 이 부분 어려우셨을텐데 역할분배 잘하신 것 같아요!
@@ -0,0 +1,63 @@
+package bossmonster.view.constants;
+
+public enum Message {
+
+    /* Input Request */
+    INPUT_MONSTER_HP("보스 몬스터의 HP를 입력해주세요."),
+    INPUT_PLAYER_NAME("\n" + "플레이어의 이름을 입력해주세요"),
+    INPUT_PLAYER_HP_MP("\n" + "플레이어의 HP와 MP를 입력해주세요.(,로 구분)"),
+    INPUT_ATTACK_TYPE_SELECT("\n" + "어떤 공격을 하시겠습니까?"),
+    INPUT_ATTACK_TYPE("%d. %s"),
+
+    /* Output Message */
+    OUTPUT_START_RAID("\n" + "보스 레이드를 시작합니다!"),
+    OUTPUT_LINE_ONE("============================"),
+    OUTPUT_BOSS_STATUS("BOSS HP [%d/%d]"),
+    OUTPUT_LINE_TWO("____________________________"),
+    OUTPUT_BOSS_DEFAULT("   ^-^" + "\n" +
+                            " / 0 0 \\" + "\n" +
+                            "(   \"   )" + "\n" +
+                            " \\  -  /" + "\n" +
+                            "  - ^ -"),
+    OUTPUT_BOSS_DAMAGED("   ^-^" + "\n" +
+                        " / x x \\" + "\n" +
+                        "(   \"\\  )" + "\n" +
+                        " \\  ^  /" + "\n" +
+                        "  - ^ -"),
+    OUTPUT_BOSS_WIN("   ^-^" + "\n" +
+                    " / ^ ^ \\" + "\n" +
+                    "(   \"   )" + "\n" +
+                    " \\  3  /" + "\n" +
+                    "  - ^ -"),
+    OUTPUT_PLAYER_STATUS("\n" + "%s HP [%d/%d] MP [%d/%d]"),
+    OUTPUT_TURN_RESULT_PLAYER("\n" + "%s을 했습니다. (입힌 데미지: %d)"),
+    OUTPUT_TURN_RESULT_BOSS("보스가 공격 했습니다. (입힌 데미지: %d)"),
+    OUTPUT_GAME_OVER_PLAYER_WIN("\n" + "%s 님이 %d번의 전투 끝에 보스 몬스터를 잡았습니다."),
+    OUTPUT_GAME_OVER_PLAYER_LOSE("\n" + "%s의 HP가 %d이 되었습니다." + "\n" + "보스 레이드에 실패했습니다."),
+
+    /* Error Message */
+    ERROR_EMPTY("입력값이 없습니다. 다시 입력해 주세요."),
+    ERROR_NOT_INTEGER("숫자만 입력 가능합니다. 다시 입력해 주세요."),
+    ERROR_NOT_COMMA("콤마(',')를 통해 구분해서 다시 입력해 주세요."),
+    ERROR_ARRAY_SIZE("입력된 항목이 많습니다. 다시 입력해 주세요."),
+    ERROR_BOSS_MONSTER_HP("보스몬스터의 HP는 100 이상 300 이하만  설정 가능합니다. 다시 입력해 주세요."),
+    ERROR_PLAYER_NAME("플레이어의 이름은 5자 이하만 가능합니다. 다시 입력해 주세요."),
+    ERROR_PLAYER_STATUS("플레이어의 HP와 MP의 합은 200으로만 설정 가능합니다. 다시 입력해 주세요."),
+    ERROR_PLAYER_ATTACK_TYPE("존재하지 않는 공격 방식입니다. 다시 입력해 주세요."),
+    ERROR_PLAYER_MANA_SHORTAGE("마나가 부족합니다. 다시 입력해 주세요.");
+
+    public static final String ERROR_PREFIX = "[ERROR] ";
+    private final String message;
+
+    Message(String message) {
+        this.message = message;
+    }
+
+    public String getMessage() {
+        return message;
+    }
+
+    public String getErrorMessage() {
+        return ERROR_PREFIX + message;
+    }
+}

리뷰: 저도 상수를 한 파일로 관리했었는데요.
상수를 한 파일로 관리하게 되면 팀 프로젝트를 진행할 때 상수 파일에서 충돌이 많이 일어나더라구요.
이런 문제를 어떻게 해결하면 좋을까요?
@@ -0,0 +1,63 @@
+package bossmonster.view.constants;
+
+public enum Message {
+
+    /* Input Request */
+    INPUT_MONSTER_HP("보스 몬스터의 HP를 입력해주세요."),
+    INPUT_PLAYER_NAME("\n" + "플레이어의 이름을 입력해주세요"),
+    INPUT_PLAYER_HP_MP("\n" + "플레이어의 HP와 MP를 입력해주세요.(,로 구분)"),
+    INPUT_ATTACK_TYPE_SELECT("\n" + "어떤 공격을 하시겠습니까?"),
+    INPUT_ATTACK_TYPE("%d. %s"),
+
+    /* Output Message */
+    OUTPUT_START_RAID("\n" + "보스 레이드를 시작합니다!"),
+    OUTPUT_LINE_ONE("============================"),
+    OUTPUT_BOSS_STATUS("BOSS HP [%d/%d]"),
+    OUTPUT_LINE_TWO("____________________________"),
+    OUTPUT_BOSS_DEFAULT("   ^-^" + "\n" +
+                            " / 0 0 \\" + "\n" +
+                            "(   \"   )" + "\n" +
+                            " \\  -  /" + "\n" +
+                            "  - ^ -"),
+    OUTPUT_BOSS_DAMAGED("   ^-^" + "\n" +
+                        " / x x \\" + "\n" +
+                        "(   \"\\  )" + "\n" +
+                        " \\  ^  /" + "\n" +
+                        "  - ^ -"),
+    OUTPUT_BOSS_WIN("   ^-^" + "\n" +
+                    " / ^ ^ \\" + "\n" +
+                    "(   \"   )" + "\n" +
+                    " \\  3  /" + "\n" +
+                    "  - ^ -"),
+    OUTPUT_PLAYER_STATUS("\n" + "%s HP [%d/%d] MP [%d/%d]"),
+    OUTPUT_TURN_RESULT_PLAYER("\n" + "%s을 했습니다. (입힌 데미지: %d)"),
+    OUTPUT_TURN_RESULT_BOSS("보스가 공격 했습니다. (입힌 데미지: %d)"),
+    OUTPUT_GAME_OVER_PLAYER_WIN("\n" + "%s 님이 %d번의 전투 끝에 보스 몬스터를 잡았습니다."),
+    OUTPUT_GAME_OVER_PLAYER_LOSE("\n" + "%s의 HP가 %d이 되었습니다." + "\n" + "보스 레이드에 실패했습니다."),
+
+    /* Error Message */
+    ERROR_EMPTY("입력값이 없습니다. 다시 입력해 주세요."),
+    ERROR_NOT_INTEGER("숫자만 입력 가능합니다. 다시 입력해 주세요."),
+    ERROR_NOT_COMMA("콤마(',')를 통해 구분해서 다시 입력해 주세요."),
+    ERROR_ARRAY_SIZE("입력된 항목이 많습니다. 다시 입력해 주세요."),
+    ERROR_BOSS_MONSTER_HP("보스몬스터의 HP는 100 이상 300 이하만  설정 가능합니다. 다시 입력해 주세요."),
+    ERROR_PLAYER_NAME("플레이어의 이름은 5자 이하만 가능합니다. 다시 입력해 주세요."),
+    ERROR_PLAYER_STATUS("플레이어의 HP와 MP의 합은 200으로만 설정 가능합니다. 다시 입력해 주세요."),
+    ERROR_PLAYER_ATTACK_TYPE("존재하지 않는 공격 방식입니다. 다시 입력해 주세요."),
+    ERROR_PLAYER_MANA_SHORTAGE("마나가 부족합니다. 다시 입력해 주세요.");
+
+    public static final String ERROR_PREFIX = "[ERROR] ";
+    private final String message;
+
+    Message(String message) {
+        this.message = message;
+    }
+
+    public String getMessage() {
+        return message;
+    }
+
+    public String getErrorMessage() {
+        return ERROR_PREFIX + message;
+    }
+}

리뷰: 입출력에 사용되는 메시지들을 InputView와 OutputView에서 각각 사용하다 보니 메시지가 많은 경우에는 자리를 많이 차지하는 것 같아서 한 파일로 관리하고 가져다 쓰는 형태로 해야겠다고 생각했습니다. 제가 공부한지 얼마 되지 않아 팀 프로젝트를 경험해본 적이 아직 없어 어떤 부분에서 충돌이 일어나는지는 잘 모르겠습니다. 분명 상수를 한 파일로 관리하다보면 문제가 생길 수도 있을 것 같은데요. 이 부분은 여러 프로젝트를 진행하면서 조금 더 고민해 봐야 할 부분인 것 같습니다. 의견 감사합니다!
@@ -0,0 +1,78 @@
+package bossmonster.exception;
+
+import bossmonster.view.constants.Message;
+
+public class Validator {
+
+    public static String validateInputOfNumber(String inputValue) {
+        validateEmpty(inputValue);
+        validateInteger(inputValue);
+        return inputValue;
+    }
+
+    public static String validatePlayerName(String inputValue) {
+        validateEmpty(inputValue);
+        validateRangeOfPlayerName(inputValue);
+        return inputValue;
+    }
+
+    public static String[] validateInputOfNumbers(String inputValue) {
+        validateSeparated(inputValue);
+        String[] separatedValue = inputValue.split(",");
+        validateArray(separatedValue);
+        for (String value : separatedValue) {
+            validateEmpty(value);
+            validateInteger(value);
+        }
+        return separatedValue;
+    }
+
+    public static int validateBossMonster(int hp) {
+        return validateRangeOfMonster(hp);
+    }
+
+    public static void validatePlayerStatus(int hp, int mp) {
+        if (hp + mp != 200) {
+            throw new IllegalArgumentException(Message.ERROR_PLAYER_STATUS.getErrorMessage());
+        }
+    }
+
+    private static void validateEmpty(String inputValue) {
+        if (inputValue.isEmpty()) {
+            throw new IllegalArgumentException(Message.ERROR_EMPTY.getErrorMessage());
+        }
+    }
+
+    private static void validateInteger(String inputValue) {
+        try {
+            Integer.parseInt(inputValue);
+        } catch (NumberFormatException e) {
+            throw new IllegalArgumentException(Message.ERROR_NOT_INTEGER.getErrorMessage());
+        }
+    }
+
+    private static void validateSeparated(String inputValue) {
+        if (!inputValue.contains(",")) {
+            throw new IllegalArgumentException(Message.ERROR_NOT_COMMA.getErrorMessage());
+        }
+    }
+
+    private static void validateArray(String[] inputValue) {
+        if (inputValue.length != 2) {
+            throw new IllegalArgumentException(Message.ERROR_ARRAY_SIZE.getErrorMessage());
+        }
+    }
+
+    private static int validateRangeOfMonster(int hp) {
+        if (hp < 100 || hp > 300) {
+            throw new IllegalArgumentException(Message.ERROR_BOSS_MONSTER_HP.getErrorMessage());
+        }
+        return hp;
+    }
+
+    private static void validateRangeOfPlayerName(String name) {
+        if (name.length() > 5) {
+            throw new IllegalArgumentException(Message.ERROR_PLAYER_NAME.getErrorMessage());
+        }
+    }
+}

리뷰: 처음에는 입력 단계에서 검증하는 부분만 따로 넣고, 도메인 부분에서 검증할 로직은 각 도메인 클래스에서 진행하는 것으로 했었는데요. 하다보니 겹치는 부분이 있는 것 같아서 잘 활용하지 못했습니다. 위에서 의견을 주신 것 처럼 DTO를 활용하면 더 깔끔하게 검증 로직을 분배할 수 있을 것 같습니다.
@@ -0,0 +1,17 @@
+package bossmonster.exception;
+
+import bossmonster.view.OutputView;
+import java.util.function.Supplier;
+
+public class ExceptionHandler {
+
+    public static <T> T retryInput(Supplier<T> supplier) {
+        while (true) {
+            try {
+                return supplier.get();
+            } catch (Exception exception) {
+                OutputView.printError(exception);
+            }
+        }
+    }
+}

리뷰: 저도 에러 발생 시, 재입력을 받아야 하는 부분에 대해서 고민을 많이 하다가.. 다른 분들의 코드 리뷰를 보고 여러가지 찾아보다 이런 방법도 있다는 것을 알게 되었습니다. 하나 만들어 두면 활용성이 좋은 것 같아요. 네이밍에 대해서는 말씀 주신 부분처럼 가독성이 안 좋아 질 수 있기 때문에 더 고민이 필요할 것 같습니다. 막상 네이밍에 대해서는 많이 신경을 쓰지 못했던 것 같네요. 

클래스명에 관해 의견 주신 부분에 대해서도 감사합니다. 스프링에 대한 개념이 아직 없어서 스프링의 ExceptionHandler가 어떻게 사용되는지 처음 알았네요.. 저는 단순히 예외를 관리한다는 부분에서 저렇게 사용했는데 다른 분들이 읽기에는 헷갈릴 수 있을 것 같습니다. 이 부분도 참고하겠습니다. 감사합니다!
@@ -0,0 +1,82 @@
+package bossmonster.domain.dto;
+
+public class GameHistoryDto {
+
+    private int turnCount;
+    private String playerName;
+    private String playerAttackType;
+    private int playerAttackDamage;
+    private int monsterAttackDamage;
+    private int playerMaxHP;
+    private int playerCurrentHP;
+    private int playerMaxMP;
+    private int playerCurrentMP;
+    private int monsterMaxHP;
+    private int monsterCurrentHP;
+    private boolean gameStatus;
+
+    public GameHistoryDto(int turnCount, String playerName, String playerAttackType, int playerAttackDamage,
+                          int monsterAttackDamage, int playerMaxHP, int playerCurrentHP, int playerMaxMP,
+                          int playerCurrentMP, int monsterMaxHP, int monsterCurrentHP, boolean gameStatus) {
+        this.turnCount = turnCount;
+        this.playerName = playerName;
+        this.playerAttackType = playerAttackType;
+        this.playerAttackDamage = playerAttackDamage;
+        this.monsterAttackDamage = monsterAttackDamage;
+        this.playerMaxHP = playerMaxHP;
+        this.playerCurrentHP = playerCurrentHP;
+        this.playerMaxMP = playerMaxMP;
+        this.playerCurrentMP = playerCurrentMP;
+        this.monsterMaxHP = monsterMaxHP;
+        this.monsterCurrentHP = monsterCurrentHP;
+        this.gameStatus = gameStatus;
+    }
+
+    public int getTurnCount() {
+        return turnCount;
+    }
+
+    public String getPlayerName() {
+        return playerName;
+    }
+
+    public String getPlayerAttackType() {
+        return playerAttackType;
+    }
+
+    public int getPlayerAttackDamage() {
+        return playerAttackDamage;
+    }
+
+    public int getMonsterAttackDamage() {
+        return monsterAttackDamage;
+    }
+
+    public int getPlayerMaxHP() {
+        return playerMaxHP;
+    }
+
+    public int getPlayerCurrentHP() {
+        return playerCurrentHP;
+    }
+
+    public int getPlayerMaxMP() {
+        return playerMaxMP;
+    }
+
+    public int getPlayerCurrentMP() {
+        return playerCurrentMP;
+    }
+
+    public int getMonsterMaxHP() {
+        return monsterMaxHP;
+    }
+
+    public int getMonsterCurrentHP() {
+        return monsterCurrentHP;
+    }
+
+    public boolean isGameStatus() {
+        return gameStatus;
+    }
+}

리뷰: 단순히 현재 게임 상태를 묻기 위해 썼던 것 같은데 의견 주신 메서드명이 반환되는 값을 이해하기에 더 직관적이네요!
@@ -0,0 +1,73 @@
+package bossmonster.domain;
+
+import bossmonster.domain.dto.GameHistoryDto;
+
+public class RaidGame {
+
+    private static final int DEFAULT_VALUE = 0;
+
+    private BossMonster bossMonster;
+    private Player player;
+    private GameHistory gameHistory;
+    private boolean status;
+    private int turns;
+
+    public RaidGame(BossMonster bossMonster, Player player) {
+        this.bossMonster = bossMonster;
+        this.player = player;
+        this.gameHistory = new GameHistory();
+        this.status = true;
+        this.turns = DEFAULT_VALUE;
+
+        gameHistory.addHistory(turns, status, player, bossMonster);
+    }
+
+    public void executeTurn(AttackType attackType) {
+        if (isGameProgress()) {
+            increaseTurn();
+            int playerAttackDamage = DEFAULT_VALUE;
+            int monsterAttackDamage = DEFAULT_VALUE;
+                playerAttackDamage = executePlayerTurn(attackType);
+            if (bossMonster.isAlive()) {
+                monsterAttackDamage = executeBossMonsterTurn();
+            }
+            setStatus();
+            createTurnHistory(attackType, playerAttackDamage, monsterAttackDamage);
+        }
+    }
+
+    public GameHistoryDto getGameHistory() {
+        return gameHistory.requestRaidHistory();
+    }
+
+    private boolean isGameProgress() {
+        setStatus();
+        return status;
+    }
+
+    private void setStatus() {
+        status = player.isAlive() && bossMonster.isAlive();
+    }
+
+    private void increaseTurn() {
+        turns ++;
+    }
+
+    private int executePlayerTurn(AttackType attackType) {
+        int playerAttackDamage = player.attack(attackType);
+        bossMonster.takeDamage(playerAttackDamage);
+        return playerAttackDamage;
+    }
+
+    private int executeBossMonsterTurn() {
+        int monsterAttackDamage = bossMonster.attack();
+        player.takeDamage(monsterAttackDamage);
+        return monsterAttackDamage;
+    }
+
+    private void createTurnHistory(AttackType playerAttackType, int playerAttackDamage, int monsterAttackDamage) {
+        gameHistory.addHistory(turns, playerAttackType.getName(), playerAttackDamage, monsterAttackDamage, status,
+                player,
+                bossMonster);
+    }
+}

리뷰: 감사합니다. 객체지향을 공부하면서 MVC 패턴에 구애받지 않고 도메인 로직은 도메인 로직에서만으로도 돌아갈 수 있도록 구현하고자 연습하고 있습니다. 컨트롤러가 없어도 도메인 내에서는 도메인 로직이 잘 돌아가야 한다고 생각하면서 고민했던 것 같아요!
@@ -0,0 +1,61 @@
+package bossmonster.domain;
+
+import bossmonster.exception.Validator;
+
+public class Player {
+
+    private String name;
+    private PlayerStatus status;
+
+    public Player(String name, int maxHP, int maxMP) {
+        this.name = Validator.validatePlayerName(name);
+        Validator.validatePlayerStatus(maxHP, maxMP);
+        this.status = new PlayerStatus(maxHP, maxMP);
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public boolean isAlive() {
+        return status.isAlive();
+    }
+
+    public int getMaxHP() {
+        return status.getMaxHP();
+    }
+
+    public int getCurrentHP() {
+        return status.getCurrentHP();
+    }
+
+    public int getMaxMP() {
+        return status.getMaxMP();
+    }
+
+    public int getCurrentMP() {
+        return status.getCurrentMP();
+    }
+
+    public int attack(AttackType attackType) {
+        if (attackType.getName().equals(AttackType.ATTACK_TYPE_NORMAL.getName())) {
+            recoverMP(attackType.getMpRecover());
+        }
+        if (attackType.getName().equals(AttackType.ATTACK_TYPE_MAGIC.getName())) {
+            consumeMP(attackType.getMpUsage());
+        }
+        return attackType.getDamage();
+    }
+
+    public void takeDamage(int damage) {
+        status.setCurrentHP(status.getCurrentHP() - damage);
+    }
+
+    private void recoverMP(int mp) {
+        status.setCurrentMP(status.getCurrentMP() + mp);
+    }
+
+    private void consumeMP(int mp) {
+        status.setCurrentMP(status.getCurrentMP() - mp);
+    }
+}

리뷰: 잘 되었다니 다행이네요 좋게 봐주셔서 감사합니다!
@@ -0,0 +1,61 @@
+package bossmonster.domain;
+
+import bossmonster.exception.Validator;
+
+public class Player {
+
+    private String name;
+    private PlayerStatus status;
+
+    public Player(String name, int maxHP, int maxMP) {
+        this.name = Validator.validatePlayerName(name);
+        Validator.validatePlayerStatus(maxHP, maxMP);
+        this.status = new PlayerStatus(maxHP, maxMP);
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public boolean isAlive() {
+        return status.isAlive();
+    }
+
+    public int getMaxHP() {
+        return status.getMaxHP();
+    }
+
+    public int getCurrentHP() {
+        return status.getCurrentHP();
+    }
+
+    public int getMaxMP() {
+        return status.getMaxMP();
+    }
+
+    public int getCurrentMP() {
+        return status.getCurrentMP();
+    }
+
+    public int attack(AttackType attackType) {
+        if (attackType.getName().equals(AttackType.ATTACK_TYPE_NORMAL.getName())) {
+            recoverMP(attackType.getMpRecover());
+        }
+        if (attackType.getName().equals(AttackType.ATTACK_TYPE_MAGIC.getName())) {
+            consumeMP(attackType.getMpUsage());
+        }
+        return attackType.getDamage();
+    }
+
+    public void takeDamage(int damage) {
+        status.setCurrentHP(status.getCurrentHP() - damage);
+    }
+
+    private void recoverMP(int mp) {
+        status.setCurrentMP(status.getCurrentMP() + mp);
+    }
+
+    private void consumeMP(int mp) {
+        status.setCurrentMP(status.getCurrentMP() - mp);
+    }
+}

리뷰: 밑에 Validator의 사용에 대해 주신 의견과 동일한 문제네요.. 입출력에 관련된 검증과 도메인 부분의 검증은 분리해서 사용하는게 맞을 것 같습니다. 감사합니다!