@@ -0,0 +1,161 @@
+package christmas.service;
+
+import static christmas.domain.booking.MenuSearch.findMenuItem;
+import static christmas.domain.booking.constants.Constant.AMOUNT_INDEX;
+import static christmas.domain.booking.constants.Constant.AMOUNT_MAX;
+import static christmas.domain.booking.constants.Constant.FIRST_DAY;
+import static christmas.domain.booking.constants.Constant.LAST_DAY;
+import static christmas.domain.booking.constants.Constant.MENU_AMOUNT_DELIMITER;
+import static christmas.domain.booking.constants.Constant.MENU_INDEX;
+import static christmas.domain.booking.constants.Constant.MENU_TYPE_DELIMITER;
+import static christmas.domain.booking.constants.Constant.SEPARATE_TWO;
+import static christmas.exception.ErrorMessage.AMOUNT_OUT_OF_RANGE;
+import static christmas.exception.ErrorMessage.DATE_OUT_OF_RANGE;
+import static christmas.exception.ErrorMessage.REQUEST_INVALID_DATE;
+import static christmas.exception.ErrorMessage.REQUEST_INVALID_MENU;
+
+import christmas.domain.booking.MenuSearch;
+import christmas.domain.booking.dto.MenuItem;
+import christmas.domain.booking.dto.MenuType;
+import christmas.exception.PlannerException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+
+public class Parser {
+
+    public static int parseInt(String input) {
+        try {
+            validateOutOfRange(input);
+            return Integer.parseInt(input);
+        } catch (NumberFormatException exception) {
+            throw PlannerException.of(REQUEST_INVALID_DATE, exception);
+        }
+    }
+
+    private static void validateOutOfRange(String input) {
+        int day = Integer.parseInt(input);
+        if (FIRST_DAY <= day && day <= LAST_DAY) {
+            return;
+        }
+        throw PlannerException.from(DATE_OUT_OF_RANGE);
+    }
+
+    public static Map<MenuItem, Integer> splitMenuAndAmount(String input) {
+        Map<MenuItem, Integer> menuAndAmountMap = new HashMap<>();
+        try {
+            validateOmittedArgument(input);
+            validateDuplicateMenu(input);
+            validateFood(input);
+            String[] menuAndAmount = input.split(MENU_TYPE_DELIMITER);
+            Arrays.stream(menuAndAmount).forEach(entry -> processMenuEntry(entry, menuAndAmountMap));
+            validateEmpty(menuAndAmountMap);
+            validateAmount(sumAmount(menuAndAmountMap));
+        } catch (PlannerException exception) {
+            throw PlannerException.of(REQUEST_INVALID_MENU, exception);
+        }
+        return menuAndAmountMap;
+    }
+
+
+    private static void processMenuEntry(String entry, Map<MenuItem, Integer> menuAndAmountMap) {
+        String[] parts = entry.split(MENU_AMOUNT_DELIMITER);
+        Integer amount = 0;
+        try {
+            amount = Integer.parseInt(parts[AMOUNT_INDEX].trim());
+        } catch (NumberFormatException exception) {
+            throw PlannerException.of(REQUEST_INVALID_MENU, exception);
+        }
+        if (parts.length == SEPARATE_TWO) {
+            int eachAmount = Integer.parseInt(parts[AMOUNT_INDEX].trim());
+            validateAmount(eachAmount);
+            String menu = parts[MENU_INDEX].trim();
+            Optional<MenuItem> item = MenuSearch.findMenuItem(menu);
+            Integer finalAmount = amount;
+            item.ifPresent(menuItem -> menuAndAmountMap.put(menuItem, finalAmount));
+        }
+    }
+
+    private static int sumAmount(Map<MenuItem, Integer> menuAndAmountMap) {
+        return menuAndAmountMap.values()
+                .stream()
+                .mapToInt(Integer::intValue)
+                .sum();
+    }
+
+    private static void validateFood(String input) {
+        try {
+            onlyBeverage(input);
+            certainFoods(input);
+        } catch (PlannerException exception) {
+            throw PlannerException.of(REQUEST_INVALID_MENU, exception);
+        }
+    }
+
+    private static void certainFoods(String input) {
+        String[] menuAndAmount = input.split(MENU_TYPE_DELIMITER);
+        boolean allCertains = Arrays.stream(menuAndAmount)
+                .map(entry -> entry.split(MENU_AMOUNT_DELIMITER)[MENU_INDEX])
+                .allMatch(Parser::isCertainFood);
+        if (!allCertains) {
+            throw PlannerException.from(REQUEST_INVALID_MENU);
+        }
+    }
+
+    private static boolean isCertainFood(String menu) {
+        return findMenuItem(menu)
+                .map(menuItem -> Arrays.asList(MenuType.values()).contains(menuItem.type()))
+                .orElse(false);
+    }
+
+    private static void onlyBeverage(String input) {
+        String[] menuAndAmount = input.split(MENU_TYPE_DELIMITER);
+        boolean allMatch = Arrays.stream(menuAndAmount)
+                .map(entry -> entry.split(MENU_AMOUNT_DELIMITER)[MENU_INDEX])
+                .allMatch(menu -> findMenuItem(menu)
+                        .map(menuItem -> menuItem.type() == MenuType.BEVERAGE)
+                        .orElse(false)
+                );
+        if (allMatch) {
+            throw PlannerException.from(REQUEST_INVALID_MENU);
+        }
+    }
+
+    private static void validateDuplicateMenu(String input) {
+        String[] menuAndAmount = input.split(MENU_TYPE_DELIMITER);
+        String[] menus = Arrays.stream(menuAndAmount)
+                .map(entry -> entry.split(MENU_AMOUNT_DELIMITER)[MENU_INDEX])
+                .toArray(String[]::new);
+        if (Arrays.stream(menus).distinct().count() != menus.length) {
+            throw PlannerException.from(REQUEST_INVALID_MENU);
+        }
+    }
+
+
+    private static void validateAmount(int amount) {
+        if (AMOUNT_INDEX <= amount && amount <= AMOUNT_MAX) {
+            return;
+        }
+        throw PlannerException.from(AMOUNT_OUT_OF_RANGE);
+    }
+
+    private static void validateEmpty(Map<MenuItem, Integer> menuAndAmountMap) {
+        if (menuAndAmountMap.isEmpty()) {
+            throw PlannerException.from(REQUEST_INVALID_MENU);
+        }
+    }
+
+    private static void validateOmittedArgument(String input) {
+        validateWhiteSpace(input);
+        if (input.endsWith(MENU_AMOUNT_DELIMITER) || input.endsWith(MENU_TYPE_DELIMITER)) {
+            throw PlannerException.from(REQUEST_INVALID_MENU);
+        }
+    }
+
+    private static void validateWhiteSpace(String input) {
+        if (input.contains(" ")) {
+            throw PlannerException.from(REQUEST_INVALID_MENU);
+        }
+    }
+}

리뷰: 참고 : [블로그 글](https://velog.io/@kasania/Java-Static-import%EC%97%90-%EB%8C%80%ED%95%9C-%EA%B4%80%EC%B0%B0#static-import%EA%B0%80-%EC%9E%98%EB%AA%BB-%EC%82%AC%EC%9A%A9%EB%90%98%EB%8A%94-%EA%B2%BD%EC%9A%B0)

> static import는 정말 자주 사용하는 클래스의 "이름만 보아도 어디에 속하는지 알 수 있는," "정적 멤버" 를 사용하는 데만 쓰도록 하자.

전반적인 코드 구조를 모르는 사람은  `AMOUNT_INDEX`만 보더라도 이게 `domain.booking.constants.Constant.java` 파일에 있다고 예측하기는 힘들 것 같습니다.