ewhk9887/korean_code_reviews_from_github huggingface dataset:
@@ -0,0 +1,68 @@
+package christmas.domain;
+
+import christmas.view.OutputView;
+import java.util.Map;
+
+public class Price {
+
+    int totalPriceBeforeDiscount;
+    EventPrices eventPrices;
+
+
+    public Price(int totalPrice) {
+        this.totalPriceBeforeDiscount = totalPrice;
+        this.eventPrices = new EventPrices();
+
+    }
+
+    public void printTotalPrice(){
+
+        System.out.println(totalPriceBeforeDiscount + "원");
+    }
+
+    public void printChampagneEvent(){
+        if(120000 <= totalPriceBeforeDiscount){
+            OutputView.printChampagne();
+            eventPrices.addChampagneBenefit(25000);
+        } else if (totalPriceBeforeDiscount < 120000) {
+            OutputView.printNone();
+        }
+    }
+
+    public void saleChristmasEvent(int christmasEventDiscount){
+        if (christmasEventDiscount != 0){
+            eventPrices.addChristBenefit(christmasEventDiscount);
+        }
+    }
+
+    public void saleStarEvent(boolean starBoolean){
+        if(starBoolean){
+            eventPrices.addStarBenefit(1000);
+        }
+    }
+
+    public void saleWeekdayAndWeekendEvent(Map<String, Integer> weeklyEvent){
+        if(weeklyEvent.values() != null){
+            eventPrices.addWeekdayAndWeekendBenefit(weeklyEvent);
+        }
+    }
+
+    public void printBenefitListAndPrice(){
+        eventPrices.calculateBenefitList();
+    }
+
+    public void printBenefitTotalPrice(){
+        eventPrices.printTotalprice();
+    }
+
+    public void printDiscountedTotalPrice(){
+        eventPrices.printDiscountedPrice(totalPriceBeforeDiscount);
+    }
+
+    public void checkDecemberBadge(){
+        eventPrices.printDecemberBadge();
+    }
+
+
+
+}

리뷰: P5: 이 부분도, 뷰는 분리하는게 좋을 것 같아요!
다른 부분에도 코멘트 남겼지만, 이 곳은 아예 System.out을 사용하고 있어서 한 번 더 남겨보아요..! 😇 

그리고 이 부분 금액 포맷팅도 추가되면 더 좋겠네용..! (다른 부분은 되어있는데 여기만 빠져있어서요)
@@ -0,0 +1,68 @@
+package christmas.domain;
+
+import christmas.view.OutputView;
+import java.util.Map;
+
+public class Price {
+
+    int totalPriceBeforeDiscount;
+    EventPrices eventPrices;
+
+
+    public Price(int totalPrice) {
+        this.totalPriceBeforeDiscount = totalPrice;
+        this.eventPrices = new EventPrices();
+
+    }
+
+    public void printTotalPrice(){
+
+        System.out.println(totalPriceBeforeDiscount + "원");
+    }
+
+    public void printChampagneEvent(){
+        if(120000 <= totalPriceBeforeDiscount){
+            OutputView.printChampagne();
+            eventPrices.addChampagneBenefit(25000);
+        } else if (totalPriceBeforeDiscount < 120000) {
+            OutputView.printNone();
+        }
+    }
+
+    public void saleChristmasEvent(int christmasEventDiscount){
+        if (christmasEventDiscount != 0){
+            eventPrices.addChristBenefit(christmasEventDiscount);
+        }
+    }
+
+    public void saleStarEvent(boolean starBoolean){
+        if(starBoolean){
+            eventPrices.addStarBenefit(1000);
+        }
+    }
+
+    public void saleWeekdayAndWeekendEvent(Map<String, Integer> weeklyEvent){
+        if(weeklyEvent.values() != null){
+            eventPrices.addWeekdayAndWeekendBenefit(weeklyEvent);
+        }
+    }
+
+    public void printBenefitListAndPrice(){
+        eventPrices.calculateBenefitList();
+    }
+
+    public void printBenefitTotalPrice(){
+        eventPrices.printTotalprice();
+    }
+
+    public void printDiscountedTotalPrice(){
+        eventPrices.printDiscountedPrice(totalPriceBeforeDiscount);
+    }
+
+    public void checkDecemberBadge(){
+        eventPrices.printDecemberBadge();
+    }
+
+
+
+}

리뷰: P3: 여기의 if-else로직이... 뭔가 이상한게 숨어있는 것 같은데요 🕵️‍♂️  (사용하지 않기로 했던 if-else가 있는 것을 제외하고서라도...)
@@ -0,0 +1,90 @@
+package christmas.domain;
+
+import christmas.constant.BenefitList;
+import christmas.view.OutputView;
+import java.text.DecimalFormat;
+import java.util.HashMap;
+import java.util.Map;
+
+public class EventPrices {
+    Map<String,Integer> benefitList;
+    int totalPrice;
+
+    public EventPrices() {
+
+        this.benefitList = new HashMap<>();
+        this.totalPrice = 0;
+    }
+
+    public void addChampagneBenefit(int discountPrice){
+        benefitList.put("GIFT",discountPrice);
+    }
+//
+    public void addChristBenefit(int discountPrice){
+        benefitList.put("CHRISTMAS", discountPrice);
+    }
+
+    public void addStarBenefit(int discountPrice){
+        benefitList.put("STAR",discountPrice);
+    }
+
+    public void addWeekdayAndWeekendBenefit(Map<String, Integer> weeklyEvent){
+        if(weeklyEvent.containsKey("DESSERT") && weeklyEvent.get("DESSERT") != 0){
+            benefitList.put("DESSERT",weeklyEvent.get("DESSERT"));
+        } else if (weeklyEvent.containsKey("MAIN") && weeklyEvent.get("MAIN") != 0) {
+            benefitList.put("MAIN",weeklyEvent.get("MAIN"));
+        }
+    }
+
+    public void calculateBenefitList(){
+        if(benefitList.isEmpty()){
+            OutputView.printNone();
+            return;
+
+        } else if (benefitList != null) {
+
+            for (Map.Entry<String, Integer> entry : benefitList.entrySet()){
+                addTotalPrice(entry.getValue());
+                printBenefits(entry.getKey(),entry.getValue());
+            }
+        }
+    }
+
+    private void printBenefits(String benefitName, int discountPrice){
+        DecimalFormat format = new DecimalFormat("-###,###원");
+        String benefitPrint = BenefitList.getEventNameByValue(benefitName);
+        benefitPrint += format.format(discountPrice);
+        System.out.println(benefitPrint);
+    }
+
+    private void addTotalPrice(int price){
+        this.totalPrice += price;
+    }
+
+    public void printTotalprice(){
+        DecimalFormat format = new DecimalFormat("-###,###원");
+        System.out.println(format.format(totalPrice));
+    }
+
+    public void printDiscountedPrice(int totalPriceBeforeDiscount){
+        DecimalFormat format = new DecimalFormat("-###,###원");
+        int discountedPrice = totalPriceBeforeDiscount - totalPrice;
+        System.out.println(format.format(discountedPrice));
+    }
+
+    public void printDecemberBadge(){
+        if(totalPrice < 5000){
+            OutputView.printNone();
+            return;
+        }else if(totalPrice < 10000) {
+            OutputView.printStarBadge();
+            return;
+        }else if(totalPrice < 20000){
+            OutputView.printTreeBadge();
+            return;
+        }else if(20000 <= totalPrice){
+            OutputView.printSantaBadge();
+            return;
+        }
+    }
+}

리뷰: P2: if-else 문에는 return이 필요하지 않아요..! 더군다나 반환타입이 void인 상황에서는요!

물론 명시적인 return을 써서 헷갈릴만한 코드나 사이드이펙트를 막는 경우도 있는데, 지금은 해당되지 않는 코드 같아서 말씀드려보았습니다 😇
@@ -0,0 +1,25 @@
+package christmas.controller;
+
+import christmas.service.EventService;
+
+public class EventController {
+    private final EventService eventService;
+
+    public EventController(EventService eventRepository) {
+        this.eventService = eventRepository;
+    }
+
+    public void requestInfo() {
+        eventService.saveInfo();
+    }
+
+    public void startPrintingResult(){
+        eventService.printFristLineAndMenus();
+    }
+
+    public void runDecemberEvent() {
+        eventService.startCalculateAndPrint();
+    }
+
+
+}

리뷰: EventPlanner EventController EventService로 구조를 가져가신 이유가 궁금합니다. 어떤 이점이 있다 생각이 들어서 이렇게 구조를 가져가신걸까요?

또한 뷰로직이 도메인과 강하게 결합된다 생각하는데 어떻게 생각하실까요?
@@ -0,0 +1,33 @@
+public class Calculator {
+    public double calculate(String expression) {
+        String[] tokens = expression.split(" ");
+        double currentResult = Double.parseDouble(tokens[0]);
+
+        for (int i = 1; i < tokens.length; i += 2) {
+            String operator = tokens[i];
+            double number = Double.parseDouble(tokens[i + 1]);
+
+            switch (operator) {
+                case "+":
+                    currentResult += number;
+                    break;
+                case "-":
+                    currentResult -= number;
+                    break;
+                case "*":
+                    currentResult *= number;
+                    break;
+                case "/":
+                    if (number == 0) {
+                        throw new ArithmeticException("0으로 나눌 수 없습니다.");
+                    }
+                    currentResult /= number;
+                    break;
+                default:
+                    System.out.println("지원하지 않는 연산자입니다: " + operator);
+                    return 0;
+            }
+        }
+        return currentResult;
+    }
+}

리뷰: Enum을 사용해서 switch문을 사용하지 않는 방향으로 코드를 작성해봅시다
@@ -0,0 +1,33 @@
+public class Calculator {
+    public double calculate(String expression) {
+        String[] tokens = expression.split(" ");
+        double currentResult = Double.parseDouble(tokens[0]);
+
+        for (int i = 1; i < tokens.length; i += 2) {
+            String operator = tokens[i];
+            double number = Double.parseDouble(tokens[i + 1]);
+
+            switch (operator) {
+                case "+":
+                    currentResult += number;
+                    break;
+                case "-":
+                    currentResult -= number;
+                    break;
+                case "*":
+                    currentResult *= number;
+                    break;
+                case "/":
+                    if (number == 0) {
+                        throw new ArithmeticException("0으로 나눌 수 없습니다.");
+                    }
+                    currentResult /= number;
+                    break;
+                default:
+                    System.out.println("지원하지 않는 연산자입니다: " + operator);
+                    return 0;
+            }
+        }
+        return currentResult;
+    }
+}

리뷰: Calculator 클래스의 책임은 무엇인가요.
한 클래스에 여러 개의 책임이 있는 것 같습니다.
SCP에 대해서 공부해보고 코드를 작성해주세요
@@ -0,0 +1,34 @@
+public class Validator {
+    public boolean isValid(String expression) {
+        String[] tokens = expression.split(" ");
+
+        if (tokens.length % 2 == 0) {
+            return false;
+        }
+
+        if (!isNumeric(tokens[0])) {
+            return false;
+        }
+
+        for (int i = 1; i < tokens.length; i += 2) {
+            if (!isOperator(tokens[i]) || !isNumeric(tokens[i + 1])) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    private boolean isNumeric(String str) {
+        try {
+            Double.parseDouble(str);
+            return true;
+        } catch (NumberFormatException e) {
+            return false;
+        }
+    }
+
+    private boolean isOperator(String str) {
+        return str.equals("+") || str.equals("-") || str.equals("*") || str.equals("/");
+    }
+}

리뷰: 한 메소드가 많은 역할을 해주는 것 같습니다.
각각의 if문을 여러 메소드로 나누어보세요.
@@ -1,5 +1,22 @@
+import java.util.Scanner;
+
 public class Main {
     public static void main(String[] args) {
-        System.out.println("1 + 1 = 2");
+        Scanner scanner = new Scanner(System.in);
+        System.out.println("계산할 수식을 입력하세요 (예: 30 + 20 / 2 * 4):");
+        String expression = scanner.nextLine();
+
+        Validator validator = new Validator();
+        if (validator.isValid(expression)) {
+            Calculator calculator = new Calculator();
+            try {
+                double result = calculator.calculate(expression);
+                System.out.println("결과: " + result);
+            } catch (ArithmeticException e) {
+                System.out.println("계산 중 오류가 발생했습니다: " + e.getMessage());
+            }
+        } else {
+            System.out.println("잘못된 수식 형식입니다. 다시 입력해주세요.");
+        }
     }
 }

리뷰: Main 클래스에 너무 많은 책임이 있습니다.
SCP에 대해서 공부 해보고 코드를 작성해주세요
* 콘솔에 프린트해주는 것도 하나의 책임으로 봅니다
@@ -11,6 +11,7 @@
 
 import java.io.IOException;
 
+// (Q) Custom deserializer를 구현한 이유가 무엇인가요? Jackson 등의 serializer를 사용하기 어려운 부분이 있나요?
 public class StoreDeserializer extends StdDeserializer {
 
     public StoreDeserializer(){

리뷰: > 네 사실 Menu 데이터와는 다르게 Store 데이터는 Point라는 spring에서 제공하는 객체를 property로 가지고 있습니다. 그런데 objectMapper의 readValue()로는 Store.class를 바로 역직렬화시키지 못하는데 그건 바로 Point를 objectMapper가 해석하지 못하기 때문이더라고요. JSONObject.get()을 사용해서 하나하나 까서 역직렬화해도 됐지만, convertStoreData() 메서드가 길어지는 게 보기 싫어서 Custom deserializer를 사용해서 한방에 처리되게끔 보이도록(?) 했습니다. 사실 Deserializer를 어떻게 해야하는지 아직 감을 잘 못 잡았습니다. 어떻게 하는게 좋을까요

point 타입의 객체 또한 spring boot default serializer(jackson)로도 충분히 serialize/deserialize 가능할 것 같습니다. 변수명을 동일하게 처리해보셨을까요?
@@ -5,27 +5,32 @@
 import org.springframework.web.bind.annotation.*;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.EnumSet;
 import java.util.List;
+import java.util.stream.Collectors;
 
 @RestController
-@RequestMapping("/customer")
+@RequestMapping("/customer") // (R) HomeController에서 url prefix가 customer로 되어있는 것이 조금 이상한 것 같습니다.
 public class HomeController {
 
     @GetMapping
     @ResponseStatus(HttpStatus.OK)
-    public String home(){
+    public String home() {
         return "Customer server is activated successfully";
-    }
+    } // (Q) 어떤 용도의 메서드인가요?
 
     @GetMapping("/main")
     @ResponseStatus(HttpStatus.OK)
-    public List<String> foods() {
-        List<String> foodKindList = new ArrayList<>();
-        for (FoodKind foodKind : EnumSet.allOf(FoodKind.class)){
-            foodKindList.add(foodKind.toString());
-        }
-        return foodKindList;
+    public List<FoodKind> foods() {
+        /*
+            (C) 여기도 Stream API를 잘 사용해보시면 좋을 것 같습니다.
+            단순히 음식 종류를 반환하는 것이라면, 아래와 같이 사용해보세요.
+            (enum type을 return 하더라도 string으로 반환하는 것으로 알고 있는데, 한번 확인해주세요~!)
+
+            + API에서 set을 반환하면 무작위 순서로 반환하기 때문에, 순서가 무관한 경우에만 set을 사용하는 것이 좋을 것 같습니다.
+        */
+        return Arrays.stream(FoodKind.values()).toList();
     }
 
 }

리뷰: >HomeController의 home()은 특별한 이유 없이 초기에 서버가 잘 돌아가는지 테스트 하려고 넣어놓은 테스트 용도의 코드입니다. Customer, Restaurant, Rider 세가지 서버를 띄우다보니 White Error 페이지로는 헷갈려서 임시로 작성했던 코드인데, 테스트 용도기 때문에 없어도 문제가 없습니다. 해당 코드에는 주석을 넣어야 했었네요.

> customer, restaurant, rider 서버 모두 home controller를 가지고 있는데, 사실 주 용도는 서버가 돌아가는지 보려고 만들어둔 것입니다. url prefix도 컨트롤러명과 일치하지 않는 게 그것 때문입니다. 테스트 용도로 작성했기 때문이에요. 해당 클래스는 지우는 게 나을까요?

흔히 이러한 용도의 controller를 health check controller라고 지칭합니다. 운영 환경에서 서버가 동작하는지 주기적으로 해주어야 하기 때문에, 네이밍만 health-check 의미가 드러나게 해주시면 좋을 것 같습니다!
@@ -11,6 +11,7 @@
 
 import java.io.IOException;
 
+// (Q) Custom deserializer를 구현한 이유가 무엇인가요? Jackson 등의 serializer를 사용하기 어려운 부분이 있나요?
 public class StoreDeserializer extends StdDeserializer {
 
     public StoreDeserializer(){

리뷰: 네 동일한 변수명으로 처리했지만, custom deserializer를 사용하지 않으면 다음과 같은 에러가 발생합니다. 
_Cannot construct instance of `org.springframework.data.geo.Point` (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator)_

Point라는 객체를 objectMapper가 모르기 때문에 발생한 일이라고 하더군요. 아래는 chat GPT를 통해 얻은 답변입니다.
https://chat.openai.com/share/fe6258ed-93d6-446b-a39b-6b9c9d1ac0b1

다만 제가 했던 것처럼 StoreSqsDto의 모든 property를 custom deserialize 할 필요 없이 Point만 custom으로 등록하면 되겠군요.
@@ -11,6 +11,7 @@
 
 import java.io.IOException;
 
+// (Q) Custom deserializer를 구현한 이유가 무엇인가요? Jackson 등의 serializer를 사용하기 어려운 부분이 있나요?
 public class StoreDeserializer extends StdDeserializer {
 
     public StoreDeserializer(){

리뷰: @millwheel 
아 Point가 기본 생성자가 없군요.
제가 알고있는 일반적인 serializer의 deserialize 방식은, 먼저 기본 생성자로 객체를 생성하고 setter를 통해 객체 필드를 setting하는 방식으로 알고 있습니다.

그런데 기본 생성자가 없다보니 객체를 생성하는 과정에서 에러가 나는 것 같네요~

사실 point 클래스와 관련해서도 리뷰 드리려고 했는데, 이후 관련 리뷰에서 더 얘기해보겠습니다~
@@ -0,0 +1,45 @@
+package calculator.model;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.BiFunction;
+
+public enum OperatorType {
+    ADDITION("+", (a, b) -> a + b),
+    SUBTRACTION("-", (a, b) -> a - b),
+    MULTIPLICATION("*", (a, b) -> a * b),
+    DIVISION("/", (a, b) -> {
+        if (b == 0) {
+            throw new ArithmeticException("0으로 나눌 수 없습니다.");
+        }
+        return a / b;
+    });
+
+    private static final Map<String, OperatorType> SYMBOL_MAP = new HashMap<>();
+
+    static {
+        for (OperatorType type : values()) {
+            SYMBOL_MAP.put(type.symbol, type);
+        }
+    }
+
+    private final String symbol;
+    private final BiFunction<Integer, Integer, Integer> operation;
+
+    OperatorType(String symbol, BiFunction<Integer, Integer, Integer> operation) {
+        this.symbol = symbol;
+        this.operation = operation;
+    }
+
+    public static OperatorType fromString(String symbol) {
+        OperatorType type = SYMBOL_MAP.get(symbol);
+        if (type == null) {
+            throw new IllegalArgumentException("잘못된 연산자입니다.");
+        }
+        return type;
+    }
+
+    public int apply(int a, int b) {
+        return operation.apply(a, b);
+    }
+}
\ No newline at end of file

리뷰: 이렇게 문자열을 리턴해줄 필요가 있을까요?
@@ -0,0 +1,17 @@
+package calculator.model;
+
+public class Operator {
+
+    public int calculate(String[] values) {
+        int result = Integer.parseInt(values[0]);
+
+        for (int i = 1; i < values.length; i += 2) {
+            String symbol = values[i];
+            int nextNumber = Integer.parseInt(values[i + 1]);
+            OperatorType operatorType = OperatorType.fromString(symbol);
+            result = operatorType.apply(result, nextNumber);
+        }
+
+        return result;
+    }
+}

리뷰: Enum 사용법을 다시 공부해서 적용해봅시다. Switch문을 사용하지 않는 방식으로요.
@@ -0,0 +1,11 @@
+package calculator.view;
+
+import java.util.Scanner;
+
+public class InputView {
+    public String formulaInput() {
+        Scanner scanner = new Scanner(System.in);
+        System.out.print("수식을 입력하세요 : ");
+        return scanner.nextLine();
+    }
+}
\ No newline at end of file

리뷰: 클래스에 역할이 두 가지 존재합니다

1. 문자열을 받는다
2. 문자열을 나눈다

클래스의 책임과 역할에 대해서 생각해봅시다
@@ -0,0 +1,27 @@
+package calculator.controller;
+
+import calculator.model.Operator;
+import calculator.util.FormulaParser;
+import calculator.view.InputView;
+import calculator.view.OutputView;
+
+public class CalculatorController {
+    private final Operator operator;
+    private final InputView inputView;
+    private final OutputView outputView;
+    private final FormulaParser formulaParser;
+
+    public CalculatorController(Operator operator, InputView inputView, OutputView outputView, FormulaParser formulaParser) {
+        this.operator = operator;
+        this.inputView = inputView;
+        this.outputView = outputView;
+        this.formulaParser = formulaParser;
+    }
+
+    public void calculatorRun() {
+        String formula = inputView.formulaInput();
+        String[] values = formulaParser.parse(formula);
+        int result = operator.calculate(values);
+        outputView.resultOutput(result);
+    }
+}

리뷰: 이 변수가 어떤 역할을 하는 변수인지 변수명만 보고 알 수 있게 만들어주세요
@@ -0,0 +1,17 @@
+package calculator.model;
+
+public class Operator {
+
+    public int calculate(String[] values) {
+        int result = Integer.parseInt(values[0]);
+
+        for (int i = 1; i < values.length; i += 2) {
+            String symbol = values[i];
+            int nextNumber = Integer.parseInt(values[i + 1]);
+            OperatorType operatorType = OperatorType.fromString(symbol);
+            result = operatorType.apply(result, nextNumber);
+        }
+
+        return result;
+    }
+}

리뷰: 이렇게 해줄 거면 필드에서 직접적으로 선언해주는 것이 더 깔끔합니다
@@ -0,0 +1,45 @@
+package calculator.model;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.BiFunction;
+
+public enum OperatorType {
+    ADDITION("+", (a, b) -> a + b),
+    SUBTRACTION("-", (a, b) -> a - b),
+    MULTIPLICATION("*", (a, b) -> a * b),
+    DIVISION("/", (a, b) -> {
+        if (b == 0) {
+            throw new ArithmeticException("0으로 나눌 수 없습니다.");
+        }
+        return a / b;
+    });
+
+    private static final Map<String, OperatorType> SYMBOL_MAP = new HashMap<>();
+
+    static {
+        for (OperatorType type : values()) {
+            SYMBOL_MAP.put(type.symbol, type);
+        }
+    }
+
+    private final String symbol;
+    private final BiFunction<Integer, Integer, Integer> operation;
+
+    OperatorType(String symbol, BiFunction<Integer, Integer, Integer> operation) {
+        this.symbol = symbol;
+        this.operation = operation;
+    }
+
+    public static OperatorType fromString(String symbol) {
+        OperatorType type = SYMBOL_MAP.get(symbol);
+        if (type == null) {
+            throw new IllegalArgumentException("잘못된 연산자입니다.");
+        }
+        return type;
+    }
+
+    public int apply(int a, int b) {
+        return operation.apply(a, b);
+    }
+}
\ No newline at end of file

리뷰: 이 메소드가 static 메소드여야만 하는 이유가 있나요?
@@ -0,0 +1,11 @@
+package calculator.view;
+
+import java.util.Scanner;
+
+public class InputView {
+    public String formulaInput() {
+        Scanner scanner = new Scanner(System.in);
+        System.out.print("수식을 입력하세요 : ");
+        return scanner.nextLine();
+    }
+}
\ No newline at end of file

리뷰: FormulaParser 클래스에 문자열을 나누는 역할을 분할했습니다.
@@ -0,0 +1,27 @@
+package calculator.controller;
+
+import calculator.model.Operator;
+import calculator.util.FormulaParser;
+import calculator.view.InputView;
+import calculator.view.OutputView;
+
+public class CalculatorController {
+    private final Operator operator;
+    private final InputView inputView;
+    private final OutputView outputView;
+    private final FormulaParser formulaParser;
+
+    public CalculatorController(Operator operator, InputView inputView, OutputView outputView, FormulaParser formulaParser) {
+        this.operator = operator;
+        this.inputView = inputView;
+        this.outputView = outputView;
+        this.formulaParser = formulaParser;
+    }
+
+    public void calculatorRun() {
+        String formula = inputView.formulaInput();
+        String[] values = formulaParser.parse(formula);
+        int result = operator.calculate(values);
+        outputView.resultOutput(result);
+    }
+}

리뷰: calculatorRun으로 수정했습니다.
@@ -0,0 +1,45 @@
+package calculator.model;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.BiFunction;
+
+public enum OperatorType {
+    ADDITION("+", (a, b) -> a + b),
+    SUBTRACTION("-", (a, b) -> a - b),
+    MULTIPLICATION("*", (a, b) -> a * b),
+    DIVISION("/", (a, b) -> {
+        if (b == 0) {
+            throw new ArithmeticException("0으로 나눌 수 없습니다.");
+        }
+        return a / b;
+    });
+
+    private static final Map<String, OperatorType> SYMBOL_MAP = new HashMap<>();
+
+    static {
+        for (OperatorType type : values()) {
+            SYMBOL_MAP.put(type.symbol, type);
+        }
+    }
+
+    private final String symbol;
+    private final BiFunction<Integer, Integer, Integer> operation;
+
+    OperatorType(String symbol, BiFunction<Integer, Integer, Integer> operation) {
+        this.symbol = symbol;
+        this.operation = operation;
+    }
+
+    public static OperatorType fromString(String symbol) {
+        OperatorType type = SYMBOL_MAP.get(symbol);
+        if (type == null) {
+            throw new IllegalArgumentException("잘못된 연산자입니다.");
+        }
+        return type;
+    }
+
+    public int apply(int a, int b) {
+        return operation.apply(a, b);
+    }
+}
\ No newline at end of file

리뷰: 지정된 연산자를 제외한 다른 기호를 입력했을 경우에 출력하도록 했지만
계산기의 경우 따로 없어도 될 것 같습니다.
@@ -0,0 +1,17 @@
+package calculator.model;
+
+public class Operator {
+
+    public int calculate(String[] values) {
+        int result = Integer.parseInt(values[0]);
+
+        for (int i = 1; i < values.length; i += 2) {
+            String symbol = values[i];
+            int nextNumber = Integer.parseInt(values[i + 1]);
+            OperatorType operatorType = OperatorType.fromString(symbol);
+            result = operatorType.apply(result, nextNumber);
+        }
+
+        return result;
+    }
+}

리뷰: OperatorType의 기능을 수정하면서 Operator의 기능과 중복되기에 삭제했습니다.