ewhk9887/korean_code_reviews_from_github huggingface dataset:
@@ -0,0 +1,58 @@
+package com.codesquad.dust4.api;
+
+import com.codesquad.dust4.domain.DustForecast;
+import com.codesquad.dust4.dto.DustInfoByStationDto;
+import com.codesquad.dust4.dto.ForecastResponseDto;
+import com.codesquad.dust4.dto.LocationReturnDto;
+import com.codesquad.dust4.service.DustStatusPublicApi;
+import com.codesquad.dust4.service.ForecastPublicApi;
+import com.codesquad.dust4.utils.DustMockDataUtil;
+import com.codesquad.dust4.utils.LocationConverterUtil;
+
+import java.net.URISyntaxException;
+import java.util.concurrent.ExecutionException;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.io.IOException;
+
+@RestController
+public class DustAPIController {
+
+  private static final Logger logger = LoggerFactory.getLogger(DustAPIController.class);
+
+  @GetMapping("/dust-status")
+
+  public ResponseEntity<DustInfoByStationDto> getDustInfo(@RequestParam("latitude") String EPSGlatitude, @RequestParam("longitude") String EPSGlongitude)
+      throws IOException, ExecutionException, InterruptedException {
+    logger.info("longitude: {}, latitude: {}", EPSGlongitude, EPSGlatitude);
+
+    LocationReturnDto locationReturnDto = LocationConverterUtil.locationConverter(EPSGlongitude, EPSGlatitude);
+
+    String tmX = locationReturnDto.getLongitude();
+    String tmY = locationReturnDto.getLatitude();
+
+    DustInfoByStationDto dustInfoByStationDto = DustStatusPublicApi.dustStatus(tmX, tmY);
+
+    return new ResponseEntity<>(dustInfoByStationDto, HttpStatus.OK);
+  }
+
+  @GetMapping("/forecast")
+  public ResponseEntity<ForecastResponseDto> enlistDustForecast() throws URISyntaxException, JsonProcessingException {
+
+    logger.info("dust forecast");
+
+    DustForecast forecast = ForecastPublicApi.forecast();
+    ForecastResponseDto responseDto = new ForecastResponseDto();
+    responseDto.setContent(forecast);
+
+    return new ResponseEntity<>(responseDto, HttpStatus.OK);
+  }
+}

리뷰: 파라미터 변수명 첫 글자에 대문자 쓰지 않도록 바꿔 주시고요. 
아마 `EPSG` 가 무언가의 약어인 모양입니다. 그럴땐 아래와 같은 네이밍이면 될 것 같습니다.
 `String epsgLatitude, String epsgLongitude`
@@ -0,0 +1,49 @@
+package com.codesquad.dust4.domain;
+
+public class DustStatusQuo {
+
+  private String dataTime; //측정일
+  private String pm10Value; //미세먼지(PM10) 농도
+  private String pm10Grade; //미세먼지(PM10) 24시간 등급
+
+  public DustStatusQuo() {}
+
+  public DustStatusQuo(String dataTime, String pm10Value, String pm10Grade) {
+    this.dataTime = dataTime;
+    this.pm10Value = pm10Value;
+    this.pm10Grade = pm10Grade;
+  }
+
+  public String getDataTime() {
+    return dataTime;
+  }
+
+  public void setDataTime(String dataTime) {
+    this.dataTime = dataTime;
+  }
+
+  public String getPm10Value() {
+    return pm10Value;
+  }
+
+  public void setPm10Value(String pm10Value) {
+    this.pm10Value = pm10Value;
+  }
+
+  public String getPm10Grade() {
+    return pm10Grade;
+  }
+
+  public void setPm10Grade(String pm10Grade) {
+    this.pm10Grade = pm10Grade;
+  }
+
+  @Override
+  public String toString() {
+    return "DustStatusQuo{" +
+            "dataTime='" + dataTime + '\'' +
+            ", pm10Value='" + pm10Value + '\'' +
+            ", pm10Grade='" + pm10Grade + '\'' +
+            '}';
+  }
+}

리뷰: 음....이게 저도 항상 고민되는 부분인데요. 
현재 상태라는 의미에서 `StatusQuo` 라고 하셨겠지만 사실 이게 협업의 관점에서는...흔히 사용되지 않는 단어이긴 해서... 
`CurrentStatus` 정도면 좋지 않을까 싶긴 한데요. status quo는 사실 좀 어려운 단어라서요. 
클래스명이나 변수명 작명에 사용되는 영단어는 항상 중학교 영어 단어집 정도 수준을 벗어나지 않는 것이 바람직해봉요. 그리고 가급적 굉장히 쉽게 써야 하고요. 
`HyunjaeSangTae` 같은 작명만 아니라면 말이에요.
@@ -0,0 +1,30 @@
+package com.codesquad.dust4.utils;
+
+import com.codesquad.dust4.api.DustAPIController;
+import java.util.concurrent.ExecutionException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.client.RestTemplate;
+
+public class AccessTokenUtil {
+
+  public static final String CONSUMER_KEY_LOCATION = "6e8fc57906324e20805a";
+  public static final String CONSUMER_SECRET_LOCATION = "aaa5becbdfcd4b5bb349";
+  private static final Logger logger = LoggerFactory.getLogger(DustAPIController.class);
+
+  public static String getLocationConversionAPIToken()
+      throws ExecutionException, InterruptedException {
+    String URL = "https://sgisapi.kostat.go.kr/OpenAPI3/auth/authentication.json";
+    String accessTokenURL =
+        URL + "?consumer_key=" + AccessTokenUtil.CONSUMER_KEY_LOCATION + "&consumer_secret="
+            + AccessTokenUtil.CONSUMER_SECRET_LOCATION;
+
+    RestTemplate restTemplate = new RestTemplate();
+
+    String receivedToken = restTemplate.getForObject(accessTokenURL, String.class);
+    String accessToken = ParseJSONDataUtil.parseAccessTokenJson(receivedToken);
+    logger.info("accessToken: {} ", accessToken);
+
+    return accessToken;
+  }
+}

리뷰: 요청이 들어올때마다 API call을 발생시켜 토큰 정보를 얻어오게 되겠네요. 
정말 그래야 할 필요가 있나요? 토큰이 매 요청시마다 변경되는 건가요. 
외부 API 호출 횟수는 최소화하는 것이 좋은데요, 만약 이 API의 응답 속도가 늦다거나 하면, 서비스 속도의 지연으로 바로 이어질 거고, 이런 식으로 연쇄적인 지연을 겪다 보면 결국 서비스 전면 장애로도 이어질 수 있어요. 
만약 access token이 계속 동적으로 바뀌어야만 하는 것이 아니라면, properties 파일에서 정적 값으로 관리해주시고, 
예를 들어... 매 시간마다 바뀌어야 한다면 `@Scheduled` 적극적으로 활용해서 내부 cache 사용하는 쪽으로 수정되면 좋겠네요.
@@ -0,0 +1,30 @@
+package com.codesquad.dust4.utils;
+
+import com.codesquad.dust4.api.DustAPIController;
+import java.util.concurrent.ExecutionException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.client.RestTemplate;
+
+public class AccessTokenUtil {
+
+  public static final String CONSUMER_KEY_LOCATION = "6e8fc57906324e20805a";
+  public static final String CONSUMER_SECRET_LOCATION = "aaa5becbdfcd4b5bb349";
+  private static final Logger logger = LoggerFactory.getLogger(DustAPIController.class);
+
+  public static String getLocationConversionAPIToken()
+      throws ExecutionException, InterruptedException {
+    String URL = "https://sgisapi.kostat.go.kr/OpenAPI3/auth/authentication.json";
+    String accessTokenURL =
+        URL + "?consumer_key=" + AccessTokenUtil.CONSUMER_KEY_LOCATION + "&consumer_secret="
+            + AccessTokenUtil.CONSUMER_SECRET_LOCATION;
+
+    RestTemplate restTemplate = new RestTemplate();
+
+    String receivedToken = restTemplate.getForObject(accessTokenURL, String.class);
+    String accessToken = ParseJSONDataUtil.parseAccessTokenJson(receivedToken);
+    logger.info("accessToken: {} ", accessToken);
+
+    return accessToken;
+  }
+}

리뷰: 이 클래스는 `@Component` 여도 되겠는데요.
@@ -0,0 +1,30 @@
+package com.codesquad.dust4.utils;
+
+import com.codesquad.dust4.api.DustAPIController;
+import java.util.concurrent.ExecutionException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.client.RestTemplate;
+
+public class AccessTokenUtil {
+
+  public static final String CONSUMER_KEY_LOCATION = "6e8fc57906324e20805a";
+  public static final String CONSUMER_SECRET_LOCATION = "aaa5becbdfcd4b5bb349";
+  private static final Logger logger = LoggerFactory.getLogger(DustAPIController.class);
+
+  public static String getLocationConversionAPIToken()
+      throws ExecutionException, InterruptedException {
+    String URL = "https://sgisapi.kostat.go.kr/OpenAPI3/auth/authentication.json";
+    String accessTokenURL =
+        URL + "?consumer_key=" + AccessTokenUtil.CONSUMER_KEY_LOCATION + "&consumer_secret="
+            + AccessTokenUtil.CONSUMER_SECRET_LOCATION;
+
+    RestTemplate restTemplate = new RestTemplate();
+
+    String receivedToken = restTemplate.getForObject(accessTokenURL, String.class);
+    String accessToken = ParseJSONDataUtil.parseAccessTokenJson(receivedToken);
+    logger.info("accessToken: {} ", accessToken);
+
+    return accessToken;
+  }
+}

리뷰: 클래스를 `@Component` 로 관리하면 이 값도 `@Value` 로 주입받을 수 있겠네요.
@@ -0,0 +1,15 @@
+package com.codesquad.dust4.config;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.CorsRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+@Configuration
+public class WebCorsConfig implements WebMvcConfigurer {
+
+  @Override
+  public void addCorsMappings(CorsRegistry registry) {
+    registry.addMapping("/**")
+        .allowedOrigins("*");
+  }
+}
\ No newline at end of file

리뷰: `implements WebMvcConfigurer` 보단 `extends WebMvcConfigurationSupport` 가 더 낫다고 생각되네요. 

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.html
@@ -0,0 +1,8 @@
+package com.codesquad.dust4.exception;
+
+import org.springframework.web.bind.annotation.ControllerAdvice;
+
+@ControllerAdvice
+public class CustomControllerAdvice {
+
+}

리뷰: 아마 OAuth2 요구사항을 저 때 구현하지 못했던 것이 아닌가 생각듭니다 ㅋㅋ
@@ -0,0 +1,49 @@
+package com.codesquad.dust4.domain;
+
+public class DustStatusQuo {
+
+  private String dataTime; //측정일
+  private String pm10Value; //미세먼지(PM10) 농도
+  private String pm10Grade; //미세먼지(PM10) 24시간 등급
+
+  public DustStatusQuo() {}
+
+  public DustStatusQuo(String dataTime, String pm10Value, String pm10Grade) {
+    this.dataTime = dataTime;
+    this.pm10Value = pm10Value;
+    this.pm10Grade = pm10Grade;
+  }
+
+  public String getDataTime() {
+    return dataTime;
+  }
+
+  public void setDataTime(String dataTime) {
+    this.dataTime = dataTime;
+  }
+
+  public String getPm10Value() {
+    return pm10Value;
+  }
+
+  public void setPm10Value(String pm10Value) {
+    this.pm10Value = pm10Value;
+  }
+
+  public String getPm10Grade() {
+    return pm10Grade;
+  }
+
+  public void setPm10Grade(String pm10Grade) {
+    this.pm10Grade = pm10Grade;
+  }
+
+  @Override
+  public String toString() {
+    return "DustStatusQuo{" +
+            "dataTime='" + dataTime + '\'' +
+            ", pm10Value='" + pm10Value + '\'' +
+            ", pm10Grade='" + pm10Grade + '\'' +
+            '}';
+  }
+}

리뷰: 네, 그렇네요. 사실 Status만 있으면 되지 Quo는 괜히 쓸데없는 접미사 같은^^ 조금 더 명확한 단어명 사용에 노력해보겠습니다.
@@ -0,0 +1,67 @@
+package christmas.controller;
+
+import christmas.domain.Menu;
+import christmas.domain.Today;
+import christmas.domain.dao.OrderDAO;
+import christmas.service.EventDatable;
+import christmas.service.EventDateService;
+import christmas.service.EventPrice;
+import christmas.view.InputView;
+import christmas.view.OutputView;
+
+public class Planner {
+    private final EventDatable eventDatable = new EventDateService();
+    private final OrderDAO orderDAO = OrderDAO.getInstance();
+    private Today today;
+    private EventPrice eventPrice;
+
+    public void run() {
+        InputView.printStartMessage();
+        dateRun();
+        menuRun();
+        eventPrice = new EventPrice(today);
+        InputView.printResultMessage();
+        discountDetailsRun();
+    }
+
+    private void menuRun() {
+        while (true) {
+            try {
+                orderDAO.addOrderDetail(InputView.readOrderAccept());
+                break;
+            } catch (IllegalArgumentException e) {
+                System.out.println(e.getMessage());
+            }
+        }
+    }
+
+    private void dateRun() {
+        while (true) {
+            try {
+                String input = InputView.readVisitDate();
+                Validator.validateDateToNumber(input);
+                today = new Today(Integer.parseInt(input), eventDatable);
+                break;
+            } catch (IllegalArgumentException e) {
+                System.out.println(e.getMessage());
+            }
+        }
+    }
+
+    private String giveaway() {
+        if (eventPrice.isGiveawayLimit()) {
+            return Menu.CHAMPAGNE.getName() + " 1개";
+        }
+        return null;
+    }
+
+    private void discountDetailsRun() {
+        OutputView.printMenu(orderDAO.getOrder());
+        OutputView.printTotalOrderPrice(eventPrice.getTotalPrice());
+        OutputView.printGiveAway(giveaway());
+        OutputView.printBenefitDetails(eventPrice.getDiscountDetails());
+        OutputView.printTotalBenefit(eventPrice.totalDiscount());
+        OutputView.printResultPayment(eventPrice.totalDiscountExcludingGiveaway());
+        OutputView.printEventBadge(eventPrice.getEventBadge());
+    }
+}

리뷰: Planner 보다는 ~~~Controller라는 이름으로 바꾸면 좋을 것 같습니다.
처음에 도메인인 줄 알았어요.
@@ -0,0 +1,67 @@
+package christmas.controller;
+
+import christmas.domain.Menu;
+import christmas.domain.Today;
+import christmas.domain.dao.OrderDAO;
+import christmas.service.EventDatable;
+import christmas.service.EventDateService;
+import christmas.service.EventPrice;
+import christmas.view.InputView;
+import christmas.view.OutputView;
+
+public class Planner {
+    private final EventDatable eventDatable = new EventDateService();
+    private final OrderDAO orderDAO = OrderDAO.getInstance();
+    private Today today;
+    private EventPrice eventPrice;
+
+    public void run() {
+        InputView.printStartMessage();
+        dateRun();
+        menuRun();
+        eventPrice = new EventPrice(today);
+        InputView.printResultMessage();
+        discountDetailsRun();
+    }
+
+    private void menuRun() {
+        while (true) {
+            try {
+                orderDAO.addOrderDetail(InputView.readOrderAccept());
+                break;
+            } catch (IllegalArgumentException e) {
+                System.out.println(e.getMessage());
+            }
+        }
+    }
+
+    private void dateRun() {
+        while (true) {
+            try {
+                String input = InputView.readVisitDate();
+                Validator.validateDateToNumber(input);
+                today = new Today(Integer.parseInt(input), eventDatable);
+                break;
+            } catch (IllegalArgumentException e) {
+                System.out.println(e.getMessage());
+            }
+        }
+    }
+
+    private String giveaway() {
+        if (eventPrice.isGiveawayLimit()) {
+            return Menu.CHAMPAGNE.getName() + " 1개";
+        }
+        return null;
+    }
+
+    private void discountDetailsRun() {
+        OutputView.printMenu(orderDAO.getOrder());
+        OutputView.printTotalOrderPrice(eventPrice.getTotalPrice());
+        OutputView.printGiveAway(giveaway());
+        OutputView.printBenefitDetails(eventPrice.getDiscountDetails());
+        OutputView.printTotalBenefit(eventPrice.totalDiscount());
+        OutputView.printResultPayment(eventPrice.totalDiscountExcludingGiveaway());
+        OutputView.printEventBadge(eventPrice.getEventBadge());
+    }
+}

리뷰: 컨트롤러는 상태를 가지지 않는 게 좋습니다. Today와 EventPrice를 메소드 내 지역변수로 활용해주세요.
유지보수 시, 값을 잘못 할당하거나 실행 순서가 꼬여서 의도하지 않은 버그를 발생시킬 수 있어요.
@@ -0,0 +1,67 @@
+package christmas.controller;
+
+import christmas.domain.Menu;
+import christmas.domain.Today;
+import christmas.domain.dao.OrderDAO;
+import christmas.service.EventDatable;
+import christmas.service.EventDateService;
+import christmas.service.EventPrice;
+import christmas.view.InputView;
+import christmas.view.OutputView;
+
+public class Planner {
+    private final EventDatable eventDatable = new EventDateService();
+    private final OrderDAO orderDAO = OrderDAO.getInstance();
+    private Today today;
+    private EventPrice eventPrice;
+
+    public void run() {
+        InputView.printStartMessage();
+        dateRun();
+        menuRun();
+        eventPrice = new EventPrice(today);
+        InputView.printResultMessage();
+        discountDetailsRun();
+    }
+
+    private void menuRun() {
+        while (true) {
+            try {
+                orderDAO.addOrderDetail(InputView.readOrderAccept());
+                break;
+            } catch (IllegalArgumentException e) {
+                System.out.println(e.getMessage());
+            }
+        }
+    }
+
+    private void dateRun() {
+        while (true) {
+            try {
+                String input = InputView.readVisitDate();
+                Validator.validateDateToNumber(input);
+                today = new Today(Integer.parseInt(input), eventDatable);
+                break;
+            } catch (IllegalArgumentException e) {
+                System.out.println(e.getMessage());
+            }
+        }
+    }
+
+    private String giveaway() {
+        if (eventPrice.isGiveawayLimit()) {
+            return Menu.CHAMPAGNE.getName() + " 1개";
+        }
+        return null;
+    }
+
+    private void discountDetailsRun() {
+        OutputView.printMenu(orderDAO.getOrder());
+        OutputView.printTotalOrderPrice(eventPrice.getTotalPrice());
+        OutputView.printGiveAway(giveaway());
+        OutputView.printBenefitDetails(eventPrice.getDiscountDetails());
+        OutputView.printTotalBenefit(eventPrice.totalDiscount());
+        OutputView.printResultPayment(eventPrice.totalDiscountExcludingGiveaway());
+        OutputView.printEventBadge(eventPrice.getEventBadge());
+    }
+}

리뷰: eventPrice의 로직이 외부로 노출되어 있어요. 로직을 eventPrice 내부로 옮겨주세요.
```suggestion
    private String giveaway() {
        return eventPrice.getGiveawayMessage();
    }
```
@@ -0,0 +1,11 @@
+package christmas.controller;
+
+public class Validator {
+    public static void validateDateToNumber(String number) {
+        try {
+            Integer.parseInt(number);
+        } catch (NumberFormatException e) {
+            throw new IllegalArgumentException("[ERROR] 유효하지 않은 날짜입니다. 다시 입력해 주세요.");
+        }
+    }
+}

리뷰: Validator를 validator 패키지로 옮겨주면 좋을 것 같습니다.
이름도 DateValidator로 바꾸면 좋을 것 같아요.
@@ -0,0 +1,75 @@
+package christmas.domain;
+
+import java.util.Map;
+import java.util.AbstractMap;
+
+public enum Menu {
+    APPETIZER(0, 0, "애피타이저"),
+    MAIN_DISH(1, 2023, "메인"),
+    DESSERT(2, 2023, "디저트"),
+    DRINK(3, 0, "음료"),
+    MUSHROOM_SOUP(0, 6000, "양송이수프"),
+    TAPAS(0, 5500, "타파스"),
+    CAESAR_SALAD(0, 8000, "시저샐러드"),
+    T_BONE_STEAK(1, 55000, "티본스테이크"),
+    BBQ_RIBS(1, 54000, "바비큐립"),
+    SEAFOOD_PASTA(1, 35000, "해산물파스타"),
+    CHRISTMAS_PASTA(1, 25000, "크리스마스파스타"),
+    CHOCO_CAKE(2, 15000, "초코케이크"),
+    ICE_CREAM(2, 5000, "아이스크림"),
+    ZERO_COKE(3, 3000, "제로콜라"),
+    RED_WINE(3, 60000, "레드와인"),
+    CHAMPAGNE(3, 25000, "샴페인");
+
+    private final int type;
+    private final int price;
+    private final String name;
+
+    Menu(int type, int price, String name) {
+        this.type = type;
+        this.price = price;
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public static Menu matchingMenu(String inputMenuName) {
+        for (Menu menu : Menu.values()) {
+            if (menu.name.equalsIgnoreCase(inputMenuName)) {
+                return menu;
+            }
+        }
+        throw new IllegalArgumentException("[ERROR] 입력하신 메뉴는 없는 메뉴입니다.");
+    }
+
+    public static AbstractMap.SimpleEntry<String, Integer> giveawayChampagne() {
+        return new AbstractMap.SimpleEntry<>(CHAMPAGNE.name, CHAMPAGNE.price);
+    }
+
+    public static boolean isAllDrinks(Map<Menu, Integer> orderDetail) {
+        return orderDetail.keySet().stream()
+                .allMatch(item -> item.type == DRINK.type);
+    }
+
+    public static int calculateTotalPrice(Map<Menu, Integer> orderDetail) {
+        return orderDetail.entrySet().stream()
+                .mapToInt(entry -> entry.getKey().price * entry.getValue())
+                .sum();
+    }
+
+    public static int calculateMainDishDiscount(Map<Menu, Integer> orderDetail) {
+        return orderDetail.entrySet().stream()
+                .filter(entry -> entry.getKey().type == MAIN_DISH.type)
+                .mapToInt(entry -> MAIN_DISH.price * entry.getValue())
+                .sum();
+    }
+
+    public static int calculateDessertDiscount(Map<Menu, Integer> orderDetail) {
+        return orderDetail.entrySet().stream()
+                .filter(entry -> entry.getKey().type == DESSERT.type)
+                .mapToInt(entry -> DESSERT.price * entry.getValue())
+                .sum();
+    }
+}

리뷰: type을 숫자로 구별하지 말고 새 enum을 파서 구별하면 좋을 것 같아요.
저는 Course enum에서 APPETIZER, MAIN, DESSERT, DRINK를 만들어서 구별했어요.
@@ -0,0 +1,48 @@
+package christmas.domain;
+
+import christmas.service.EventDatable;
+
+public class Today {
+    private final DayOfWeek startDayOfMonth = DayOfWeek.FRIDAY;
+    private final EventDatable eventDatable;
+    private final int INDEX_ALIGN = 1;
+    private final int WEEK_LENGTH = 7;
+    private DayOfWeek todayOfWeek;
+    private int todayDate;
+
+    enum DayOfWeek {
+        SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY
+    }
+
+    public Today(int inputDate, EventDatable eventDatable) {
+        this.eventDatable = eventDatable;
+        setDate(inputDate);
+    }
+
+    public void setDate(int inputDate) {
+        eventDatable.validateDecemberDate(inputDate);
+        this.todayDate = inputDate;
+        this.todayOfWeek = getDayOfWeek(inputDate, startDayOfMonth);
+    }
+
+    public int getChristmasEventToday() {
+        if (eventDatable.isChristmasEventDay(todayDate)) {
+            return (todayDate * 100) + 900;
+        }
+        return 0;
+    }
+
+    public boolean isWeekend() {
+        return todayOfWeek == DayOfWeek.FRIDAY || todayOfWeek == DayOfWeek.SATURDAY;
+    }
+
+    public boolean isSpecialDay() {
+        return todayOfWeek == DayOfWeek.SUNDAY || eventDatable.isChristmasDDay(todayDate);
+    }
+
+    private DayOfWeek getDayOfWeek(int inputDate, DayOfWeek startDate) {
+        int startDayIndex = startDate.ordinal();
+        int dayOfWeekIndex = (startDayIndex + inputDate - INDEX_ALIGN) % WEEK_LENGTH;
+        return DayOfWeek.values()[dayOfWeekIndex];
+    }
+}

리뷰: setter는 지양해주세요. 내부적으로만 사용되고 있다면, private로 설정해주세요.
@@ -0,0 +1,30 @@
+package christmas.domain.dao;
+
+import christmas.domain.Menu;
+import christmas.domain.parser.OrderParsable;
+import christmas.domain.parser.OrderParser;
+
+import java.util.Map;
+import java.util.Collections;
+
+public class OrderDAO {
+    private final OrderParsable orderParsable;
+    private static final OrderDAO ORDER_DAO = new OrderDAO();
+    private static Map<Menu, Integer> orderDetail;
+
+    private OrderDAO() {
+        this.orderParsable = new OrderParser();
+    }
+
+    public static OrderDAO getInstance() {
+        return ORDER_DAO;
+    }
+
+    public Map<Menu, Integer> getOrder() {
+        return Collections.unmodifiableMap(orderDetail);
+    }
+
+    public void addOrderDetail(String inputOrder) {
+        orderDetail = orderParsable.getOrderDetail(inputOrder);
+    }
+}

리뷰: 버그가 일어나기 정말 쉬운 설계에요. private static 변수를 조작하는 add 메소드가 존재하고 있고, getOrder에서 초기화되지도 않아요.
static을 모두 없애고 해당 클래스를 인스턴스로 생성해서 활용하세요.