ewhk9887/korean_code_reviews_from_github huggingface dataset:
@@ -1,7 +1,18 @@
 package store;
 
+import java.io.IOException;
+import store.config.ApplicationConfig;
+import store.controller.StoreController;
+
 public class Application {
     public static void main(String[] args) {
-        // TODO: 프로그램 구현
+        try {
+            ApplicationConfig config = new ApplicationConfig();
+            StoreController storeController = config.storeController();
+
+            storeController.run();
+        } catch (IOException e) {
+            System.out.println("resources 파일 오류 발생: " + e.getMessage());
+        }
     }
 }

리뷰: 요구사항이 [Error]로 시작하여 오류 메세지를 띄워야한다는 것이 있어 이를 추가하면 좋을 것 같습니다.
@@ -1,7 +1,18 @@
 package store;
 
+import java.io.IOException;
+import store.config.ApplicationConfig;
+import store.controller.StoreController;
+
 public class Application {
     public static void main(String[] args) {
-        // TODO: 프로그램 구현
+        try {
+            ApplicationConfig config = new ApplicationConfig();
+            StoreController storeController = config.storeController();
+
+            storeController.run();
+        } catch (IOException e) {
+            System.out.println("resources 파일 오류 발생: " + e.getMessage());
+        }
     }
 }

리뷰: 파일을 읽어오는 부분에서 오류가 발생할때를 위한 try catch문이라고 생각되는데 이를 파일을 읽어오는 로직에서 따로 처리하는 것이 더 좋을 것 같습니다
@@ -0,0 +1,56 @@
+package store.config;
+
+import java.io.IOException;
+import store.controller.StoreController;
+import store.util.LocalDateGenerator;
+import store.util.RealLocalDateGenerator;
+import store.domain.StoreManager;
+import store.repository.ProductRepository;
+import store.repository.PromotionRepository;
+import store.service.StoreService;
+import store.util.ResourceFileReader;
+import store.view.InputView;
+import store.view.OutputView;
+
+public class ApplicationConfig {
+    private static final String PRODUCT_FILE_PATH = "src/main/resources/products.md";
+    private static final String PROMOTION_FILE_PATH = "src/main/resources/promotions.md";
+
+    private final ResourceFileReader resourceFileReader;
+
+    public ApplicationConfig() throws IOException {
+        this.resourceFileReader = new ResourceFileReader(PRODUCT_FILE_PATH, PROMOTION_FILE_PATH);
+    }
+
+    public StoreController storeController() throws IOException {
+        return new StoreController(inputView(), outputView(), storeService());
+    }
+
+    public StoreService storeService() throws IOException {
+        return new StoreService(storeManager(), realLocalTimeGenerator());
+    }
+
+    public InputView inputView() {
+        return new InputView();
+    }
+
+    public OutputView outputView() {
+        return new OutputView();
+    }
+
+    public StoreManager storeManager() throws IOException {
+        return new StoreManager(productRepository(), promotionRepository());
+    }
+
+    public ProductRepository productRepository() throws IOException {
+        return new ProductRepository(resourceFileReader.readProducts());
+    }
+
+    public PromotionRepository promotionRepository() throws IOException {
+        return new PromotionRepository(resourceFileReader.readPromotions());
+    }
+
+    public LocalDateGenerator realLocalTimeGenerator() {
+        return new RealLocalDateGenerator();
+    }
+}

리뷰: private으로 사용해도 문제가 없을 것이라고 생각되는데 public으로 만드신 이유가 따로 있나요??
@@ -0,0 +1,56 @@
+package store.config;
+
+import java.io.IOException;
+import store.controller.StoreController;
+import store.util.LocalDateGenerator;
+import store.util.RealLocalDateGenerator;
+import store.domain.StoreManager;
+import store.repository.ProductRepository;
+import store.repository.PromotionRepository;
+import store.service.StoreService;
+import store.util.ResourceFileReader;
+import store.view.InputView;
+import store.view.OutputView;
+
+public class ApplicationConfig {
+    private static final String PRODUCT_FILE_PATH = "src/main/resources/products.md";
+    private static final String PROMOTION_FILE_PATH = "src/main/resources/promotions.md";
+
+    private final ResourceFileReader resourceFileReader;
+
+    public ApplicationConfig() throws IOException {
+        this.resourceFileReader = new ResourceFileReader(PRODUCT_FILE_PATH, PROMOTION_FILE_PATH);
+    }
+
+    public StoreController storeController() throws IOException {
+        return new StoreController(inputView(), outputView(), storeService());
+    }
+
+    public StoreService storeService() throws IOException {
+        return new StoreService(storeManager(), realLocalTimeGenerator());
+    }
+
+    public InputView inputView() {
+        return new InputView();
+    }
+
+    public OutputView outputView() {
+        return new OutputView();
+    }
+
+    public StoreManager storeManager() throws IOException {
+        return new StoreManager(productRepository(), promotionRepository());
+    }
+
+    public ProductRepository productRepository() throws IOException {
+        return new ProductRepository(resourceFileReader.readProducts());
+    }
+
+    public PromotionRepository promotionRepository() throws IOException {
+        return new PromotionRepository(resourceFileReader.readPromotions());
+    }
+
+    public LocalDateGenerator realLocalTimeGenerator() {
+        return new RealLocalDateGenerator();
+    }
+}

리뷰: throws를 사용한 이유가 있을까요? file을 제외하고 크게 throw를 던질 이유가 존재하는지 만약 에러가 file을 읽어오는 과정에서만 나타난다면 file을 읽어오는 과정에서 예외를 바로 던져주지 않는지 궁금합니다.
@@ -0,0 +1,56 @@
+package store.config;
+
+import java.io.IOException;
+import store.controller.StoreController;
+import store.util.LocalDateGenerator;
+import store.util.RealLocalDateGenerator;
+import store.domain.StoreManager;
+import store.repository.ProductRepository;
+import store.repository.PromotionRepository;
+import store.service.StoreService;
+import store.util.ResourceFileReader;
+import store.view.InputView;
+import store.view.OutputView;
+
+public class ApplicationConfig {
+    private static final String PRODUCT_FILE_PATH = "src/main/resources/products.md";
+    private static final String PROMOTION_FILE_PATH = "src/main/resources/promotions.md";
+
+    private final ResourceFileReader resourceFileReader;
+
+    public ApplicationConfig() throws IOException {
+        this.resourceFileReader = new ResourceFileReader(PRODUCT_FILE_PATH, PROMOTION_FILE_PATH);
+    }
+
+    public StoreController storeController() throws IOException {
+        return new StoreController(inputView(), outputView(), storeService());
+    }
+
+    public StoreService storeService() throws IOException {
+        return new StoreService(storeManager(), realLocalTimeGenerator());
+    }
+
+    public InputView inputView() {
+        return new InputView();
+    }
+
+    public OutputView outputView() {
+        return new OutputView();
+    }
+
+    public StoreManager storeManager() throws IOException {
+        return new StoreManager(productRepository(), promotionRepository());
+    }
+
+    public ProductRepository productRepository() throws IOException {
+        return new ProductRepository(resourceFileReader.readProducts());
+    }
+
+    public PromotionRepository promotionRepository() throws IOException {
+        return new PromotionRepository(resourceFileReader.readPromotions());
+    }
+
+    public LocalDateGenerator realLocalTimeGenerator() {
+        return new RealLocalDateGenerator();
+    }
+}

리뷰: 저의 경우에는 throws를 사용하여 해당 메서드가 어떤 에러를 일으키는지 정리하기위해서도 많이 사용하였는데 이 경우에는 다른 이유인 것 같아 궁금합니다.
@@ -0,0 +1,11 @@
+package store.constant;
+
+public class ErrorMessages {
+    public static final String INVALID_FORMAT_MESSAGE = "올바르지 않은 형식으로 입력했습니다. 다시 입력해 주세요.";
+    public static final String INVALID_INPUT_MESSAGE = "잘못된 입력입니다. 다시 입력해 주세요.";
+    public static final String NOT_EXIST_PRODUCT_MESSAGE = "존재하지 않는 상품입니다. 다시 입력해 주세요.";
+    public static final String QUANTITY_EXCEED_MESSAGE = "재고 수량을 초과하여 구매할 수 없습니다. 다시 입력해 주세요.";
+
+    private ErrorMessages() {
+    }
+}

리뷰: enum클래스로 만들어 사용하는 것이 더 좋을 것같다는 생각이 드는데 어떻게 생각하시나요?
@@ -0,0 +1,7 @@
+package store.constant;
+
+public enum OrderStatus {
+    NOT_APPLICABLE,
+    PROMOTION_AVAILABLE_ADDITIONAL_PRODUCT,
+    PROMOTION_STOCK_INSUFFICIENT,
+}

리뷰: 저의 경우에는 단순히 상태를 나타내는 것이 아닌 해당 상태일때 처리하는 함수도 넣었었습니다. 당장의 코드는 깔끔해졌었지만 나중에 유지보수를 생각하면 이렇게 단순히 상태를 나타내는 것이 더 좋다는 생각이 드네요
@@ -0,0 +1,31 @@
+package store.domain;
+
+import java.time.LocalDate;
+
+public class Order {
+    private final String name;
+    private int quantity;
+    private final LocalDate creationDate;
+
+    public Order(String name, int quantity, LocalDate creationDate) {
+        this.name = name;
+        this.quantity = quantity;
+        this.creationDate = creationDate;
+    }
+
+    public void updateQuantity(int quantityDelta) {
+        quantity += quantityDelta;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public int getQuantity() {
+        return quantity;
+    }
+
+    public LocalDate getCreationDate() {
+        return creationDate;
+    }
+}

리뷰: update라고 하면 수정에 가까운 느낌이라 addQuantity가 더 적합한 메서드 이름인 것 같습니다.
@@ -0,0 +1,69 @@
+package store.dto;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class Receipt {
+    private List<ProductReceipt> products;
+    private List<ProductReceipt> freeProducts;
+    private ProductReceipt totalOriginInfo;
+    private int totalFreePrice;
+    private int membershipPrice;
+    private int finalPayment;
+
+    public Receipt() {
+        this.products = new ArrayList<>();
+        this.freeProducts = new ArrayList<>();
+        this.totalFreePrice = 0;
+        this.membershipPrice = 0;
+        this.finalPayment = 0;
+    }
+
+    public List<ProductReceipt> getProducts() {
+        return products;
+    }
+
+    public List<ProductReceipt> getFreeProducts() {
+        return freeProducts;
+    }
+
+    public ProductReceipt getTotalOriginInfo() {
+        return totalOriginInfo;
+    }
+
+    public int getTotalFreePrice() {
+        return totalFreePrice;
+    }
+
+    public int getMembershipPrice() {
+        return membershipPrice;
+    }
+
+    public int getFinalPayment() {
+        return finalPayment;
+    }
+
+    public void setProducts(List<ProductReceipt> products) {
+        this.products = products;
+    }
+
+    public void setFreeProducts(List<ProductReceipt> freeProducts) {
+        this.freeProducts = freeProducts;
+    }
+
+    public void setTotalOriginInfo(ProductReceipt totalOriginInfo) {
+        this.totalOriginInfo = totalOriginInfo;
+    }
+
+    public void setTotalFreePrice(int totalFreePrice) {
+        this.totalFreePrice = totalFreePrice;
+    }
+
+    public void setMembershipPrice(int membershipPrice) {
+        this.membershipPrice = membershipPrice;
+    }
+
+    public void setFinalPayment(int finalPayment) {
+        this.finalPayment = finalPayment;
+    }
+}

리뷰: Convertor에서 있었던 일들이 Receipt에 addOrder를 사용해서 주문을 추가하는 것도 좋을 것 같습니다.
@@ -0,0 +1,47 @@
+package store.repository;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import store.domain.Product;
+
+public class ProductRepository {
+    private static final String EMPTY_STRING = "";
+
+    private final List<Product> products;
+
+    public ProductRepository(List<Product> products) {
+        this.products = new ArrayList<>(products);
+        setUp();
+    }
+
+    public List<Product> findAll() {
+        return new ArrayList<>(products);
+    }
+
+    public Optional<Product> findPromotionalProduct(String name) {
+        return products.stream()
+                .filter(product -> product.getName().equals(name) && !product.getPromotion().isEmpty())
+                .findFirst();
+    }
+
+    public Optional<Product> findRegularProduct(String name) {
+        return products.stream()
+                .filter(product -> product.getName().equals(name) && product.getPromotion().isEmpty())
+                .findFirst();
+    }
+
+    public Optional<Product> findProduct(String name) {
+        return findPromotionalProduct(name).or(() -> findRegularProduct(name));
+    }
+
+    private void setUp() {
+        for (int i = 0; i < products.size(); i++) {
+            Product product = products.get(i);
+
+            if (!product.getPromotion().isEmpty() && findRegularProduct(product.getName()).isEmpty()) {
+                products.add(++i, new Product(product.getName(), product.getPrice(), 0, EMPTY_STRING));
+            }
+        }
+    }
+}

리뷰: LinkedHashMap을 사용하면 탐색이 몹시 편해질 것 같습니다.
@@ -0,0 +1,87 @@
+package store.service;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import store.constant.OrderStatus;
+import store.util.LocalDateGenerator;
+import store.domain.Order;
+import store.domain.ReceiptConverter;
+import store.domain.StoreManager;
+import store.dto.OrderNotice;
+import store.domain.OrderResult;
+import store.dto.ProductDto;
+import store.dto.PurchaseInfo;
+import store.dto.Receipt;
+
+public class StoreService {
+    private final StoreManager storeManager;
+    private final LocalDateGenerator timeGenerator;
+    private final List<Order> orders;
+    private Iterator<Order> orderIterator;
+    private Order currentOrder;
+
+    public StoreService(StoreManager storeManager, LocalDateGenerator timeGenerator) {
+        this.storeManager = storeManager;
+        this.timeGenerator = timeGenerator;
+        this.orders = new ArrayList<>();
+    }
+
+    public List<ProductDto> getProducts() {
+        return storeManager.getProductDtos();
+    }
+
+    public void resetOrders() {
+        orders.clear();
+    }
+
+    public void applyPurchaseInfo(List<PurchaseInfo> purchases) {
+        for (PurchaseInfo purchaseInfo : purchases) {
+            storeManager.validatePurchaseInfo(purchaseInfo);
+            orders.add(new Order(
+                    purchaseInfo.name(),
+                    purchaseInfo.quantity(),
+                    timeGenerator.today()
+            ));
+        }
+        orderIterator = orders.iterator();
+    }
+
+    public boolean hasNextOrder() {
+        return orderIterator.hasNext();
+    }
+
+    public OrderNotice checkOrder() {
+        currentOrder = orderIterator.next();
+        return determineOrderNotice(currentOrder);
+    }
+
+    public void modifyOrder(int quantityDelta) {
+        currentOrder.updateQuantity(quantityDelta);
+    }
+
+    public Receipt calculateOrders(boolean isMembershipDiscount) {
+        List<OrderResult> orderResults = new ArrayList<>();
+        ReceiptConverter converter = new ReceiptConverter();
+
+        for (Order order : orders) {
+            List<Integer> result = storeManager.calculateOrder(order);
+            orderResults.add(new OrderResult(order.getName(), result.getFirst(),
+                    result.get(1), result.get(2), result.getLast()));
+        }
+        return converter.convertToReceipt(orderResults, isMembershipDiscount);
+    }
+
+    private OrderNotice determineOrderNotice(Order order) {
+        if (storeManager.isValidForAdditionalProduct(order) != 0) {
+            return new OrderNotice(OrderStatus.PROMOTION_AVAILABLE_ADDITIONAL_PRODUCT, order.getName(),
+                    storeManager.isValidForAdditionalProduct(order));
+        }
+        if (storeManager.isStockInsufficient(order) > 0) {
+            return new OrderNotice(OrderStatus.PROMOTION_STOCK_INSUFFICIENT, order.getName(),
+                    storeManager.isStockInsufficient(order));
+        }
+        return new OrderNotice(OrderStatus.NOT_APPLICABLE, order.getName(),
+                storeManager.isStockInsufficient(order));
+    }
+}

리뷰: 직접 Iterator를 보내지 않고 메서드와 연결해서 사용하신 모습이 생각하지 못한 부분이라 좋은 것 같습니다.
@@ -1,7 +1,18 @@
 package store;
 
+import java.io.IOException;
+import store.config.ApplicationConfig;
+import store.controller.StoreController;
+
 public class Application {
     public static void main(String[] args) {
-        // TODO: 프로그램 구현
+        try {
+            ApplicationConfig config = new ApplicationConfig();
+            StoreController storeController = config.storeController();
+
+            storeController.run();
+        } catch (IOException e) {
+            System.out.println("resources 파일 오류 발생: " + e.getMessage());
+        }
     }
 }

리뷰: 초기 설정값인 상품, 프로모션에서 생긴 오류는 요구사항에 해당하지 않는다고 생각해 다르게 출력했습니다.
다시 생각해보면 일관된 포멧으로 오류 메시지를 출력하는게 더 깔끔할 것 같네요..!

try catch를 통해 초기 설정 오류의 경우 프로그램 자체가 종료되도록 구현하다 보니 main문에 작성하게 되었습니다!
@@ -0,0 +1,56 @@
+package store.config;
+
+import java.io.IOException;
+import store.controller.StoreController;
+import store.util.LocalDateGenerator;
+import store.util.RealLocalDateGenerator;
+import store.domain.StoreManager;
+import store.repository.ProductRepository;
+import store.repository.PromotionRepository;
+import store.service.StoreService;
+import store.util.ResourceFileReader;
+import store.view.InputView;
+import store.view.OutputView;
+
+public class ApplicationConfig {
+    private static final String PRODUCT_FILE_PATH = "src/main/resources/products.md";
+    private static final String PROMOTION_FILE_PATH = "src/main/resources/promotions.md";
+
+    private final ResourceFileReader resourceFileReader;
+
+    public ApplicationConfig() throws IOException {
+        this.resourceFileReader = new ResourceFileReader(PRODUCT_FILE_PATH, PROMOTION_FILE_PATH);
+    }
+
+    public StoreController storeController() throws IOException {
+        return new StoreController(inputView(), outputView(), storeService());
+    }
+
+    public StoreService storeService() throws IOException {
+        return new StoreService(storeManager(), realLocalTimeGenerator());
+    }
+
+    public InputView inputView() {
+        return new InputView();
+    }
+
+    public OutputView outputView() {
+        return new OutputView();
+    }
+
+    public StoreManager storeManager() throws IOException {
+        return new StoreManager(productRepository(), promotionRepository());
+    }
+
+    public ProductRepository productRepository() throws IOException {
+        return new ProductRepository(resourceFileReader.readProducts());
+    }
+
+    public PromotionRepository promotionRepository() throws IOException {
+        return new PromotionRepository(resourceFileReader.readPromotions());
+    }
+
+    public LocalDateGenerator realLocalTimeGenerator() {
+        return new RealLocalDateGenerator();
+    }
+}

리뷰: 놓친 부분이 있었네요. 지적해주셔서 감사합니다!
상위 메서드 호출로 전체적인 설정을 해주는 클래스라서 말씀해주신대로 private으로 사용하는 것이 더 적절한 것 같아요.
@@ -0,0 +1,56 @@
+package store.config;
+
+import java.io.IOException;
+import store.controller.StoreController;
+import store.util.LocalDateGenerator;
+import store.util.RealLocalDateGenerator;
+import store.domain.StoreManager;
+import store.repository.ProductRepository;
+import store.repository.PromotionRepository;
+import store.service.StoreService;
+import store.util.ResourceFileReader;
+import store.view.InputView;
+import store.view.OutputView;
+
+public class ApplicationConfig {
+    private static final String PRODUCT_FILE_PATH = "src/main/resources/products.md";
+    private static final String PROMOTION_FILE_PATH = "src/main/resources/promotions.md";
+
+    private final ResourceFileReader resourceFileReader;
+
+    public ApplicationConfig() throws IOException {
+        this.resourceFileReader = new ResourceFileReader(PRODUCT_FILE_PATH, PROMOTION_FILE_PATH);
+    }
+
+    public StoreController storeController() throws IOException {
+        return new StoreController(inputView(), outputView(), storeService());
+    }
+
+    public StoreService storeService() throws IOException {
+        return new StoreService(storeManager(), realLocalTimeGenerator());
+    }
+
+    public InputView inputView() {
+        return new InputView();
+    }
+
+    public OutputView outputView() {
+        return new OutputView();
+    }
+
+    public StoreManager storeManager() throws IOException {
+        return new StoreManager(productRepository(), promotionRepository());
+    }
+
+    public ProductRepository productRepository() throws IOException {
+        return new ProductRepository(resourceFileReader.readProducts());
+    }
+
+    public PromotionRepository promotionRepository() throws IOException {
+        return new PromotionRepository(resourceFileReader.readPromotions());
+    }
+
+    public LocalDateGenerator realLocalTimeGenerator() {
+        return new RealLocalDateGenerator();
+    }
+}

리뷰: 자바 컴파일러가 throws를 사용하지 않는 경우 컴파일 에러를 띄워 명시적으로 사용했습니다!
다시 생각해보니 파일 입력에 관한 것을 main에서 따로 처리하면 덕지덕지 throws를 사용하지 않았어도 될 문제였던 것 같아요.
@@ -0,0 +1,50 @@
+package baseball.controller;
+
+import baseball.config.Score;
+import baseball.dto.SubmitAnswerDto.SubmitAnswerInputDto;
+import baseball.dto.SubmitAnswerDto.SubmitAnswerOutputDto;
+import baseball.service.GameService;
+import baseball.view.OutputView;
+import java.util.List;
+import java.util.Map;
+
+public class GameController {
+    private final GameService gameService;
+
+    public GameController(GameService gameService) {
+        this.gameService = gameService;
+    }
+
+    public void run() {
+        do {
+            this.startGame();
+        } while (RetryInputUtil.getCommand() != 2);
+    }
+
+    private void startGame() {
+        OutputView.printStartMessage();
+        while (true) {
+            if (this.startTurn()) {
+                break;
+            }
+        }
+        OutputView.printEndMessage();
+    }
+
+    private boolean startTurn() {
+        try {
+            List<Integer> userNumbers = RetryInputUtil.getUserNumbers();
+            SubmitAnswerOutputDto submitAnswerOutputDto = this.gameService.submitAnswer(
+                    new SubmitAnswerInputDto(userNumbers));
+
+            Map<Score, Integer> score = submitAnswerOutputDto.score();
+            OutputView.printResult(score);
+
+            return score.get(Score.STRIKE) == 3;
+
+        } catch (IllegalArgumentException error) {
+            OutputView.printError(error.getMessage());
+            return false;
+        }
+    }
+}
\ No newline at end of file

리뷰: 의미가 있는 숫자이니 상수로 관리해도 좋을 것 같습니다.
@@ -0,0 +1,50 @@
+package baseball.controller;
+
+import baseball.config.Score;
+import baseball.dto.SubmitAnswerDto.SubmitAnswerInputDto;
+import baseball.dto.SubmitAnswerDto.SubmitAnswerOutputDto;
+import baseball.service.GameService;
+import baseball.view.OutputView;
+import java.util.List;
+import java.util.Map;
+
+public class GameController {
+    private final GameService gameService;
+
+    public GameController(GameService gameService) {
+        this.gameService = gameService;
+    }
+
+    public void run() {
+        do {
+            this.startGame();
+        } while (RetryInputUtil.getCommand() != 2);
+    }
+
+    private void startGame() {
+        OutputView.printStartMessage();
+        while (true) {
+            if (this.startTurn()) {
+                break;
+            }
+        }
+        OutputView.printEndMessage();
+    }
+
+    private boolean startTurn() {
+        try {
+            List<Integer> userNumbers = RetryInputUtil.getUserNumbers();
+            SubmitAnswerOutputDto submitAnswerOutputDto = this.gameService.submitAnswer(
+                    new SubmitAnswerInputDto(userNumbers));
+
+            Map<Score, Integer> score = submitAnswerOutputDto.score();
+            OutputView.printResult(score);
+
+            return score.get(Score.STRIKE) == 3;
+
+        } catch (IllegalArgumentException error) {
+            OutputView.printError(error.getMessage());
+            return false;
+        }
+    }
+}
\ No newline at end of file

리뷰: 반복 로직을 잘 짜시는 것 같습니다 👍
@@ -0,0 +1,21 @@
+package baseball.config;
+
+public enum Config {
+    MIN_RANDOM_NUMBER(1),
+    MAX_RANDOM_NUMBER(9),
+    NUMBER_OF_RANDOM_NUMBER(3);
+
+    private final Object value;
+
+    Config(Object value) {
+        this.value = value;
+    }
+
+    public int getInt() {
+        return (int) value;
+    }
+
+    public String getString() {
+        return (String) value;
+    }
+}

리뷰: Object 대신 제네릭을 사용하면 안정성을 더욱 높일 수 있을 것 같습니다.