ewhk9887/korean_code_reviews_from_github huggingface dataset:
@@ -0,0 +1,20 @@
+package store.util.reader;
+
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+public class RepeatableReader {
+    public static <T> T handle(Supplier<String> viewMethod,
+                               Function<String, T> converter,
+                               Consumer<String> errorHandler) {
+        while (true) {
+            try {
+                String userInput = viewMethod.get();
+                return converter.apply(userInput);
+            } catch (IllegalArgumentException e) {
+                errorHandler.accept(e.getMessage());
+            }
+        }
+    }
+}

리뷰: 함수형 인터페이스를 너무 잘 사용하신 거 같아요!
@@ -0,0 +1,44 @@
+package store.util.reader;
+
+import static store.exception.reader.FileReadErrorStatus.INVALID_FILE_PATH;
+import static store.exception.reader.FileReadErrorStatus.UNEXPECTED_IO_ERROR;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import store.exception.reader.FileReadException;
+
+public class StoreFileReader {
+    private static final String DELIMITER = ",";
+
+    public static List<List<String>> readFile(String fileName) {
+        validateFileReadable(fileName);
+        Path path = Paths.get(fileName);
+        try (BufferedReader reader = Files.newBufferedReader(path, StandardCharsets.UTF_8)) {
+            return reader.lines().skip(1)
+                    .filter(line -> line != null && !line.trim().isEmpty())
+                    .map(StoreFileReader::parseLine)
+                    .toList();
+        } catch (IOException e) {
+            throw new FileReadException(UNEXPECTED_IO_ERROR);
+        }
+    }
+
+    private static List<String> parseLine(String line) {
+        return List.of(line.split(DELIMITER));
+    }
+
+    public static void validateFileReadable(String fileName) {
+        if (fileName == null || fileName.trim().isEmpty()) {
+            throw new FileReadException(INVALID_FILE_PATH);
+        }
+        Path path = Paths.get(fileName);
+        if (!(Files.exists(path) && Files.isReadable(path))) {
+            throw new FileReadException(INVALID_FILE_PATH);
+        }
+    }
+}

리뷰: 좋은 의견 감사합니다!
@@ -0,0 +1,20 @@
+package store.util.reader;
+
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+public class RepeatableReader {
+    public static <T> T handle(Supplier<String> viewMethod,
+                               Function<String, T> converter,
+                               Consumer<String> errorHandler) {
+        while (true) {
+            try {
+                String userInput = viewMethod.get();
+                return converter.apply(userInput);
+            } catch (IllegalArgumentException e) {
+                errorHandler.accept(e.getMessage());
+            }
+        }
+    }
+}

리뷰: 저도 저번 미션 코드리뷰 다니면서 봤는데 좋다고 생각해서 이번 미션에 적용해보았어요!
@@ -0,0 +1,29 @@
+package store.exception.store;
+
+public enum StoreErrorStatus {
+
+    INSUFFICIENT_STOCK("재고 수량을 초과하여 구매할 수 없습니다. 다시 입력해 주세요."),
+    NON_EXIST_PRODUCT("존재하지 않는 상품입니다. 다시 입력해 주세요."),
+    DUPLICATE_GENERAL_PRODUCT("상품이 중복되었습니다."),
+    DUPLICATE_PROMOTION_PRODUCT("하나의 상품에 두개의 프로모션이 적용될 수 없습니다."),
+
+    INVALID_PROMOTION_BUY_AMOUNT("구매 개수는 0 이하가 될 수 없습니다."),
+    INVALID_PROMOTION_GIVE_AMOUNT("추가 증정은 하나만 가능합니다."),
+    NON_EXIST_PROMOTION("존재하지 않는 프로모션이 있습니다."),
+    DUPLICATE_PROMOTION_NAME("프로모션의 이름은 중복될 수 없습니다."),
+
+    INVALID_PRODUCT_PRICE("가격은 0 이하가 될 수 없습니다."),
+    INVALID_PRODUCT_STOCK("재고는 음수가 될 수 없습니다."),
+
+    PRODUCT_PRICE_INCONSISTENT("일반 상품과 프로모션 상품의 가격은 동일해야합니다.");
+
+    private final String message;
+
+    StoreErrorStatus(String message) {
+        this.message = message;
+    }
+
+    public String getMessage() {
+        return message;
+    }
+}

리뷰: 감사합니다 👍
@@ -0,0 +1,94 @@
+package store.model.store.product;
+
+import static store.exception.store.StoreErrorStatus.INVALID_PRODUCT_PRICE;
+import static store.exception.store.StoreErrorStatus.INVALID_PRODUCT_STOCK;
+
+import java.time.LocalDateTime;
+import store.exception.store.StoreException;
+import store.model.store.promotion.Promotion;
+
+public class Product {
+    private final String name;
+    private final int price;
+    private int stock;
+    private final Promotion promotion;
+
+    private Product(String name, int price, int stock, Promotion promotion) {
+        validatePrice(price);
+        validateStock(stock);
+        this.name = name;
+        this.price = price;
+        this.stock = stock;
+        this.promotion = promotion;
+    }
+
+    public static Product of(String name, int price, int stock, Promotion promotion) {
+        return new Product(name, price, stock, promotion);
+    }
+
+    public static Product createEmptyGeneralProduct(Product product) {
+        return new Product(product.name, product.price, 0, null);
+    }
+
+    public void decreaseStock(int amount) {
+        stock -= amount;
+    }
+
+    public boolean isNameEquals(Product product) {
+        return this.name.equals(product.name);
+    }
+
+    public boolean isNameEquals(String name) {
+        return this.name.equals(name);
+    }
+
+    public boolean isPromotionPeriod(LocalDateTime now) {
+        if (this.promotion == null) {
+            return false;
+        }
+        return promotion.isPeriod(now);
+    }
+
+    public boolean isGeneralProduct() {
+        return promotion == null;
+    }
+
+    public boolean isPromotionProduct() {
+        return promotion != null;
+    }
+
+    private void validatePrice(int price) {
+        if (price <= 0) {
+            throw new StoreException(INVALID_PRODUCT_PRICE);
+        }
+    }
+
+    private void validateStock(int stock) {
+        if (stock < 0) {
+            throw new StoreException(INVALID_PRODUCT_STOCK);
+        }
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public int getPrice() {
+        return price;
+    }
+
+    public int getStock() {
+        return stock;
+    }
+
+    public String getPromotionName() {
+        if (promotion == null) {
+            return "";
+        }
+        return promotion.getName();
+    }
+
+    public int getPromotionUnit() {
+        return promotion.getUnit();
+    }
+}

리뷰: Product가 이름과 가격만을 가지고, Stock이라는 모델을 만들어 `Product product`, `int stock` 을 가지게 할까 생각했지만, 일단 빠르게 구현하는게 목표기도 했고 큰 문제가 없을 것 같아서 위와 같이 설계했습니다.

Service 레이어와 Repository 레이어를 도입없이 MVC 패턴만으로 설계해서 모델이 재고 수량을 들고 있는 것에 문제는 없다고 생각합니다.
@@ -0,0 +1,61 @@
+package store.model.store.promotion;
+
+import static store.exception.store.StoreErrorStatus.INVALID_PROMOTION_BUY_AMOUNT;
+import static store.exception.store.StoreErrorStatus.INVALID_PROMOTION_GIVE_AMOUNT;
+
+import java.time.LocalDateTime;
+import store.exception.store.StoreException;
+
+public class Promotion {
+    private final String name;
+    private final int buyAmount;
+    private final int giveAmount;
+    private final LocalDateTime startAt;
+    private final LocalDateTime endAt;
+
+    private Promotion(String name, int buyAmount, int giveAmount, LocalDateTime startAt, LocalDateTime endAt) {
+        validateBuyAmount(buyAmount);
+        validateGiveAmount(giveAmount);
+        this.name = name;
+        this.buyAmount = buyAmount;
+        this.giveAmount = giveAmount;
+        this.startAt = startAt;
+        this.endAt = endAt;
+    }
+
+    public static Promotion of(String name,
+                               int buyAmount,
+                               int giveAmount,
+                               LocalDateTime startAt,
+                               LocalDateTime endAt) {
+        return new Promotion(name, buyAmount, giveAmount, startAt, endAt);
+    }
+
+    public boolean isNameEquals(String name) {
+        return this.name.equals(name);
+    }
+
+    public boolean isPeriod(LocalDateTime now) {
+        return startAt.isBefore(now) && endAt.isAfter(now);
+    }
+
+    private void validateBuyAmount(int buyAmount) {
+        if (buyAmount < 1) {
+            throw new StoreException(INVALID_PROMOTION_BUY_AMOUNT);
+        }
+    }
+
+    private void validateGiveAmount(int giveAmount) {
+        if (giveAmount != 1) {
+            throw new StoreException(INVALID_PROMOTION_GIVE_AMOUNT);
+        }
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public int getUnit() {
+        return buyAmount + giveAmount;
+    }
+}

리뷰: 우테코에서 제공하는 라이브러리의 `DateTimes` 클래스의 `now()` 메서드의 반환값이 LocalDateTime이여서 LocalDate로 변환하지 않고 바로 비교할 수 있게 위와 같이 사용했습니다.
@@ -0,0 +1,20 @@
+package store.util.reader;
+
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+public class RepeatableReader {
+    public static <T> T handle(Supplier<String> viewMethod,
+                               Function<String, T> converter,
+                               Consumer<String> errorHandler) {
+        while (true) {
+            try {
+                String userInput = viewMethod.get();
+                return converter.apply(userInput);
+            } catch (IllegalArgumentException e) {
+                errorHandler.accept(e.getMessage());
+            }
+        }
+    }
+}

리뷰: 감사합니다 😄
@@ -0,0 +1,94 @@
+package store.model.store.product;
+
+import static store.exception.store.StoreErrorStatus.INVALID_PRODUCT_PRICE;
+import static store.exception.store.StoreErrorStatus.INVALID_PRODUCT_STOCK;
+
+import java.time.LocalDateTime;
+import store.exception.store.StoreException;
+import store.model.store.promotion.Promotion;
+
+public class Product {
+    private final String name;
+    private final int price;
+    private int stock;
+    private final Promotion promotion;
+
+    private Product(String name, int price, int stock, Promotion promotion) {
+        validatePrice(price);
+        validateStock(stock);
+        this.name = name;
+        this.price = price;
+        this.stock = stock;
+        this.promotion = promotion;
+    }
+
+    public static Product of(String name, int price, int stock, Promotion promotion) {
+        return new Product(name, price, stock, promotion);
+    }
+
+    public static Product createEmptyGeneralProduct(Product product) {
+        return new Product(product.name, product.price, 0, null);
+    }
+
+    public void decreaseStock(int amount) {
+        stock -= amount;
+    }
+
+    public boolean isNameEquals(Product product) {
+        return this.name.equals(product.name);
+    }
+
+    public boolean isNameEquals(String name) {
+        return this.name.equals(name);
+    }
+
+    public boolean isPromotionPeriod(LocalDateTime now) {
+        if (this.promotion == null) {
+            return false;
+        }
+        return promotion.isPeriod(now);
+    }
+
+    public boolean isGeneralProduct() {
+        return promotion == null;
+    }
+
+    public boolean isPromotionProduct() {
+        return promotion != null;
+    }
+
+    private void validatePrice(int price) {
+        if (price <= 0) {
+            throw new StoreException(INVALID_PRODUCT_PRICE);
+        }
+    }
+
+    private void validateStock(int stock) {
+        if (stock < 0) {
+            throw new StoreException(INVALID_PRODUCT_STOCK);
+        }
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public int getPrice() {
+        return price;
+    }
+
+    public int getStock() {
+        return stock;
+    }
+
+    public String getPromotionName() {
+        if (promotion == null) {
+            return "";
+        }
+        return promotion.getName();
+    }
+
+    public int getPromotionUnit() {
+        return promotion.getUnit();
+    }
+}

리뷰: 맞습니다. 코드를 보면서 불편함이 전혀 없었습니다!
@@ -0,0 +1,94 @@
+package store.model.store.product;
+
+import static store.exception.store.StoreErrorStatus.INVALID_PRODUCT_PRICE;
+import static store.exception.store.StoreErrorStatus.INVALID_PRODUCT_STOCK;
+
+import java.time.LocalDateTime;
+import store.exception.store.StoreException;
+import store.model.store.promotion.Promotion;
+
+public class Product {
+    private final String name;
+    private final int price;
+    private int stock;
+    private final Promotion promotion;
+
+    private Product(String name, int price, int stock, Promotion promotion) {
+        validatePrice(price);
+        validateStock(stock);
+        this.name = name;
+        this.price = price;
+        this.stock = stock;
+        this.promotion = promotion;
+    }
+
+    public static Product of(String name, int price, int stock, Promotion promotion) {
+        return new Product(name, price, stock, promotion);
+    }
+
+    public static Product createEmptyGeneralProduct(Product product) {
+        return new Product(product.name, product.price, 0, null);
+    }
+
+    public void decreaseStock(int amount) {
+        stock -= amount;
+    }
+
+    public boolean isNameEquals(Product product) {
+        return this.name.equals(product.name);
+    }
+
+    public boolean isNameEquals(String name) {
+        return this.name.equals(name);
+    }
+
+    public boolean isPromotionPeriod(LocalDateTime now) {
+        if (this.promotion == null) {
+            return false;
+        }
+        return promotion.isPeriod(now);
+    }
+
+    public boolean isGeneralProduct() {
+        return promotion == null;
+    }
+
+    public boolean isPromotionProduct() {
+        return promotion != null;
+    }
+
+    private void validatePrice(int price) {
+        if (price <= 0) {
+            throw new StoreException(INVALID_PRODUCT_PRICE);
+        }
+    }
+
+    private void validateStock(int stock) {
+        if (stock < 0) {
+            throw new StoreException(INVALID_PRODUCT_STOCK);
+        }
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public int getPrice() {
+        return price;
+    }
+
+    public int getStock() {
+        return stock;
+    }
+
+    public String getPromotionName() {
+        if (promotion == null) {
+            return "";
+        }
+        return promotion.getName();
+    }
+
+    public int getPromotionUnit() {
+        return promotion.getUnit();
+    }
+}

리뷰: 그렇게 말씀해주시니 감사합니다 🙏
@@ -0,0 +1,16 @@
+package store.view;
+
+import store.model.order.Payment;
+import store.model.store.StoreRoom;
+import store.view.formatter.OutputFormatter;
+
+public class OutputView {
+
+    public void printProducts(StoreRoom storeRoom) {
+        System.out.println(OutputFormatter.buildStoreStock(storeRoom));
+    }
+
+    public void printReceipt(Payment payment) {
+        System.out.println(OutputFormatter.buildReceipt(payment));
+    }
+}

리뷰: `MVC` 관점에서 뷰가 모델을 참조하는 것이 안되는 것은 아니지만 `뷰는 모델과 컨트롤러를 알 필요가 없다`라는 말이 있는 거처럼 되도록 모르는 것이 좋은 것으로 알고 있습니다!
그런 측면에서 `StoreRoom`을 인자로 받아서 처리하는 것 보다 메세지 포맷을 위한 데이터만 받아서 처리하는 방식은 어떨까요?
@@ -0,0 +1,35 @@
+package calculator.controller.validation
+
+class UserInputValidator {
+    fun validateUserInput(numbers: List<String>): List<Int> {
+        val newNumbers = checkIsEmpty(numbers)
+        checkIsInteger(newNumbers)
+        val allNumbers = changeInteger(newNumbers)
+        changeNegativeNumber(allNumbers)
+        return allNumbers
+    }
+
+    private fun checkIsEmpty(numbers: List<String>): List<String> {
+        return numbers.map { it.ifEmpty { "0" } }
+    }
+
+    private fun checkIsInteger(newNumbers: List<String>) {
+        newNumbers.map {
+            if (it.toIntOrNull() == null) {
+                throw IllegalArgumentException(UserInputErrorType.NOT_INTEGER.errorMessage)
+            }
+        }
+    }
+
+    private fun changeInteger(newNumbers: List<String>): List<Int> {
+        return newNumbers.map { it.toInt() }
+    }
+
+    private fun changeNegativeNumber(allNumbers: List<Int>) {
+        allNumbers.forEach {
+            if (it < 0) {
+                throw IllegalArgumentException(UserInputErrorType.NEGATIVE_NUMBER.errorMessage)
+            }
+        }
+    }
+}
\ No newline at end of file

리뷰: 유효성 검사를 하는 함수인지라 chane라는 네이밍은 조금 어색한것 같아요 ! ㅎㅎ

any를 사용하면 함수를 조금 더 줄일 수 있을 것 같습니다 !
```
private fun changeNegativeNumber(allNumbers: List<Int>) {
    if (allNumbers.any { it < 0 }) {
        throw IllegalArgumentException(UserInputErrorType.NEGATIVE_NUMBER.errorMessage)
    }
}

```
@@ -0,0 +1,45 @@
+package calculator.controller.domain
+
+import calculator.constants.Delimiter.CLONE
+import calculator.constants.Delimiter.COMMA
+import calculator.constants.Delimiter.CUSTOM_DELIMITER_PREFIX
+import calculator.constants.Delimiter.CUSTOM_DELIMITER_SUFFIX
+
+class DelimiterController(
+    private val input: String
+) {
+    fun checkDelimiter(): List<String> {
+        if (hasCustomDelimiter()) {
+            val customDelimiter = getCustomDelimiter()
+            val noCustomDelimiter = deleteCustomDelimiter()
+            val numbers = splitCustomDelimiter(noCustomDelimiter, customDelimiter)
+            return numbers
+        }
+        val numbers = splitDelimiter()
+        return numbers
+    }
+
+    private fun hasCustomDelimiter(): Boolean {
+        return input.startsWith(CUSTOM_DELIMITER_PREFIX.value) && input.indexOf(CUSTOM_DELIMITER_SUFFIX.value) == 3
+    }
+
+    private fun getCustomDelimiter(): String {
+        val customDelimiter = input.substring(2, 3)
+        return customDelimiter
+    }
+
+    private fun deleteCustomDelimiter(): String {
+        val noCustomDelimiter = input.substring(5)
+        return noCustomDelimiter
+    }
+
+    private fun splitDelimiter(): List<String> {
+        val numbers = input.split(COMMA.value, CLONE.value)
+        return numbers
+    }
+
+    private fun splitCustomDelimiter(noCustomDelimiter: String, customDelimiter: String): List<String> {
+        val numbers = noCustomDelimiter.split(COMMA.value, CLONE.value, customDelimiter)
+        return numbers
+    }
+}
\ No newline at end of file

리뷰: 채채님! 저도 첫 주차 때 커스텀 구분자를 요런식으로 추출했었는데요
다른 분들 구현한걸 보니까 두 글자 이상일 때 도 구분자로 사용할 수 있도록 만드셨더라구요
그래서 이렇게 하면 커스텀 구분자가 2글자 이상일 경우 예상과는 다른 오류를 보여주는 것 같아요
물론 ! 문제 요구 사항에 없었기 때문에 배제하고 구현해도 전혀 문제 없지만 저는 
이런 부분을 첫 주차 때 놓쳐서 이번 리팩토링에선 적용해봤는데 혹시 일부러 구현을 안하신건지 궁금합니다 ㅎㅎ
<img width="926" alt="image" src="https://github.com/user-attachments/assets/ec40ef9b-ae02-4dc5-8dac-6420d69ea0c5">
@@ -0,0 +1,45 @@
+package calculator.controller.domain
+
+import calculator.constants.Delimiter.CLONE
+import calculator.constants.Delimiter.COMMA
+import calculator.constants.Delimiter.CUSTOM_DELIMITER_PREFIX
+import calculator.constants.Delimiter.CUSTOM_DELIMITER_SUFFIX
+
+class DelimiterController(
+    private val input: String
+) {
+    fun checkDelimiter(): List<String> {
+        if (hasCustomDelimiter()) {
+            val customDelimiter = getCustomDelimiter()
+            val noCustomDelimiter = deleteCustomDelimiter()
+            val numbers = splitCustomDelimiter(noCustomDelimiter, customDelimiter)
+            return numbers
+        }
+        val numbers = splitDelimiter()
+        return numbers
+    }
+
+    private fun hasCustomDelimiter(): Boolean {
+        return input.startsWith(CUSTOM_DELIMITER_PREFIX.value) && input.indexOf(CUSTOM_DELIMITER_SUFFIX.value) == 3
+    }
+
+    private fun getCustomDelimiter(): String {
+        val customDelimiter = input.substring(2, 3)
+        return customDelimiter
+    }
+
+    private fun deleteCustomDelimiter(): String {
+        val noCustomDelimiter = input.substring(5)
+        return noCustomDelimiter
+    }
+
+    private fun splitDelimiter(): List<String> {
+        val numbers = input.split(COMMA.value, CLONE.value)
+        return numbers
+    }
+
+    private fun splitCustomDelimiter(noCustomDelimiter: String, customDelimiter: String): List<String> {
+        val numbers = noCustomDelimiter.split(COMMA.value, CLONE.value, customDelimiter)
+        return numbers
+    }
+}
\ No newline at end of file

리뷰: 요 5라는 상수도 이름을 붙혀주는건 어떨까요 ?
@@ -0,0 +1,45 @@
+package calculator.controller.domain
+
+import calculator.constants.Delimiter.CLONE
+import calculator.constants.Delimiter.COMMA
+import calculator.constants.Delimiter.CUSTOM_DELIMITER_PREFIX
+import calculator.constants.Delimiter.CUSTOM_DELIMITER_SUFFIX
+
+class DelimiterController(
+    private val input: String
+) {
+    fun checkDelimiter(): List<String> {
+        if (hasCustomDelimiter()) {
+            val customDelimiter = getCustomDelimiter()
+            val noCustomDelimiter = deleteCustomDelimiter()
+            val numbers = splitCustomDelimiter(noCustomDelimiter, customDelimiter)
+            return numbers
+        }
+        val numbers = splitDelimiter()
+        return numbers
+    }
+
+    private fun hasCustomDelimiter(): Boolean {
+        return input.startsWith(CUSTOM_DELIMITER_PREFIX.value) && input.indexOf(CUSTOM_DELIMITER_SUFFIX.value) == 3
+    }
+
+    private fun getCustomDelimiter(): String {
+        val customDelimiter = input.substring(2, 3)
+        return customDelimiter
+    }
+
+    private fun deleteCustomDelimiter(): String {
+        val noCustomDelimiter = input.substring(5)
+        return noCustomDelimiter
+    }
+
+    private fun splitDelimiter(): List<String> {
+        val numbers = input.split(COMMA.value, CLONE.value)
+        return numbers
+    }
+
+    private fun splitCustomDelimiter(noCustomDelimiter: String, customDelimiter: String): List<String> {
+        val numbers = noCustomDelimiter.split(COMMA.value, CLONE.value, customDelimiter)
+        return numbers
+    }
+}
\ No newline at end of file

리뷰: 혹시 여기서 3이 의미하는게 뭔지 궁금하네요..!
@@ -0,0 +1,35 @@
+package calculator.controller.validation
+
+class UserInputValidator {
+    fun validateUserInput(numbers: List<String>): List<Int> {
+        val newNumbers = checkIsEmpty(numbers)
+        checkIsInteger(newNumbers)
+        val allNumbers = changeInteger(newNumbers)
+        changeNegativeNumber(allNumbers)
+        return allNumbers
+    }
+
+    private fun checkIsEmpty(numbers: List<String>): List<String> {
+        return numbers.map { it.ifEmpty { "0" } }
+    }
+
+    private fun checkIsInteger(newNumbers: List<String>) {
+        newNumbers.map {
+            if (it.toIntOrNull() == null) {
+                throw IllegalArgumentException(UserInputErrorType.NOT_INTEGER.errorMessage)
+            }
+        }
+    }
+
+    private fun changeInteger(newNumbers: List<String>): List<Int> {
+        return newNumbers.map { it.toInt() }
+    }
+
+    private fun changeNegativeNumber(allNumbers: List<Int>) {
+        allNumbers.forEach {
+            if (it < 0) {
+                throw IllegalArgumentException(UserInputErrorType.NEGATIVE_NUMBER.errorMessage)
+            }
+        }
+    }
+}
\ No newline at end of file

리뷰: check를 하고 새로운 숫자들을 반환해볼 생각은 못했는데, 저도 써봐야겠습니다ㅎㅎ
그런데 새로운 숫자들을 반환한다면 밑 함수처럼 이름을 change로 해도 좋을 것 같습니당