ewhk9887/korean_code_reviews_from_github huggingface dataset:
@@ -0,0 +1,75 @@
+package bridge.controller;
+
+import bridge.BridgeRandomNumberGenerator;
+import bridge.constants.GameValue;
+import bridge.domain.Bridge;
+import bridge.domain.BridgeGame;
+import bridge.domain.BridgeMaker;
+import bridge.view.handler.InputHandler;
+import bridge.view.OutputView;
+
+public class BridgeController {
+    private final InputHandler inputHandler;
+    private final OutputView outputView;
+    private final BridgeGame bridgeGame;
+    private boolean status = true;
+
+    public BridgeController(InputHandler inputHandler, OutputView outputView, BridgeGame bridgeGame) {
+        this.inputHandler = inputHandler;
+        this.outputView = outputView;
+        this.bridgeGame = bridgeGame;
+    }
+
+    public void start() {
+        outputView.printStartMessage();
+        Bridge bridge = createBridge();
+        run(bridge);
+    }
+
+    private Bridge createBridge() {
+        outputView.printBridgeSizeInputMessage();
+        BridgeMaker bridgeMaker = new BridgeMaker(new BridgeRandomNumberGenerator());
+        return inputHandler.createValidatedBridge(bridgeMaker);
+    }
+
+    private void run(Bridge bridge) {
+        while (status) {
+            move(bridge);
+            if (isFinished()) {
+                finishGame();
+            }
+            if (!isFinished()) {
+                askRetry();
+            }
+        }
+    }
+
+    private void move(Bridge bridge) {
+        for (String square : bridge.getBridge()) {
+            outputView.printMoveInputMessage();
+            String moveCommand = inputHandler.readMoving();
+            bridgeGame.move(square, moveCommand);
+            outputView.printMap(bridgeGame.getMoveResult());
+        }
+    }
+
+    private boolean isFinished() {
+        return bridgeGame.isSuccessful();
+    }
+
+    private void finishGame() {
+        outputView.printResult(bridgeGame);
+        status = false;
+    }
+
+    private void askRetry() {
+        outputView.printRetryMessage();
+        String retryCommand = inputHandler.readGameCommand();
+        if (retryCommand.equals(GameValue.RETRY_COMMAND)) {
+            bridgeGame.retry();
+        }
+        if (retryCommand.equals(GameValue.QUIT_COMMAND)) {
+            outputView.printResult(bridgeGame);
+        }
+    }
+}

리뷰: 이동했을 때 X가 나오면 재시작이 나와야하는데 
이거 제가 문제를 잘 못이해했네요 ㅋㅋ..
@@ -0,0 +1,75 @@
+package bridge.controller;
+
+import bridge.BridgeRandomNumberGenerator;
+import bridge.constants.GameValue;
+import bridge.domain.Bridge;
+import bridge.domain.BridgeGame;
+import bridge.domain.BridgeMaker;
+import bridge.view.handler.InputHandler;
+import bridge.view.OutputView;
+
+public class BridgeController {
+    private final InputHandler inputHandler;
+    private final OutputView outputView;
+    private final BridgeGame bridgeGame;
+    private boolean status = true;
+
+    public BridgeController(InputHandler inputHandler, OutputView outputView, BridgeGame bridgeGame) {
+        this.inputHandler = inputHandler;
+        this.outputView = outputView;
+        this.bridgeGame = bridgeGame;
+    }
+
+    public void start() {
+        outputView.printStartMessage();
+        Bridge bridge = createBridge();
+        run(bridge);
+    }
+
+    private Bridge createBridge() {
+        outputView.printBridgeSizeInputMessage();
+        BridgeMaker bridgeMaker = new BridgeMaker(new BridgeRandomNumberGenerator());
+        return inputHandler.createValidatedBridge(bridgeMaker);
+    }
+
+    private void run(Bridge bridge) {
+        while (status) {
+            move(bridge);
+            if (isFinished()) {
+                finishGame();
+            }
+            if (!isFinished()) {
+                askRetry();
+            }
+        }
+    }
+
+    private void move(Bridge bridge) {
+        for (String square : bridge.getBridge()) {
+            outputView.printMoveInputMessage();
+            String moveCommand = inputHandler.readMoving();
+            bridgeGame.move(square, moveCommand);
+            outputView.printMap(bridgeGame.getMoveResult());
+        }
+    }
+
+    private boolean isFinished() {
+        return bridgeGame.isSuccessful();
+    }
+
+    private void finishGame() {
+        outputView.printResult(bridgeGame);
+        status = false;
+    }
+
+    private void askRetry() {
+        outputView.printRetryMessage();
+        String retryCommand = inputHandler.readGameCommand();
+        if (retryCommand.equals(GameValue.RETRY_COMMAND)) {
+            bridgeGame.retry();
+        }
+        if (retryCommand.equals(GameValue.QUIT_COMMAND)) {
+            outputView.printResult(bridgeGame);
+        }
+    }
+}

리뷰: 그러네요 분리하기전에는 정상동작해서 리팩토링단계에서 실수했나봅니다..
마지막에 다시 한번 꼼꼼히 체크해야겠네요 😨
@@ -0,0 +1,14 @@
+package bridge.constants;
+
+public class GameValue {
+
+    private GameValue() {
+    }
+
+    public static final String MOVE_UP_COMMAND = "U";
+    public static final String MOVE_DOWN_COMMAND = "D";
+    public static final String RETRY_COMMAND = "R";
+    public static final String QUIT_COMMAND = "Q";
+    public static final String MOVE = "O";
+    public static final String STOP = "X";
+}

리뷰: 관련 있는 상수끼리 Enum 으로 묶어봐도 좋을 것 같습니다 :)
@@ -0,0 +1,17 @@
+package bridge.constants;
+
+import java.util.regex.Pattern;
+
+public enum RegexPattern {
+    ONLY_NUMBER(Pattern.compile("\\d+"));
+
+    private final Pattern pattern;
+
+    RegexPattern(Pattern pattern) {
+        this.pattern = pattern;
+    }
+
+    public boolean matches(String value) {
+        return pattern.matcher(value).matches();
+    }
+}

리뷰: Pattern 자체를 Enum 으로 사용하는 것은 생각해보지 못했는데, 이거 꽤나 재활용성이 높겠군요 👍
@@ -0,0 +1,75 @@
+package bridge.controller;
+
+import bridge.BridgeRandomNumberGenerator;
+import bridge.constants.GameValue;
+import bridge.domain.Bridge;
+import bridge.domain.BridgeGame;
+import bridge.domain.BridgeMaker;
+import bridge.view.handler.InputHandler;
+import bridge.view.OutputView;
+
+public class BridgeController {
+    private final InputHandler inputHandler;
+    private final OutputView outputView;
+    private final BridgeGame bridgeGame;
+    private boolean status = true;
+
+    public BridgeController(InputHandler inputHandler, OutputView outputView, BridgeGame bridgeGame) {
+        this.inputHandler = inputHandler;
+        this.outputView = outputView;
+        this.bridgeGame = bridgeGame;
+    }
+
+    public void start() {
+        outputView.printStartMessage();
+        Bridge bridge = createBridge();
+        run(bridge);
+    }
+
+    private Bridge createBridge() {
+        outputView.printBridgeSizeInputMessage();
+        BridgeMaker bridgeMaker = new BridgeMaker(new BridgeRandomNumberGenerator());
+        return inputHandler.createValidatedBridge(bridgeMaker);
+    }
+
+    private void run(Bridge bridge) {
+        while (status) {
+            move(bridge);
+            if (isFinished()) {
+                finishGame();
+            }
+            if (!isFinished()) {
+                askRetry();
+            }
+        }
+    }
+
+    private void move(Bridge bridge) {
+        for (String square : bridge.getBridge()) {
+            outputView.printMoveInputMessage();
+            String moveCommand = inputHandler.readMoving();
+            bridgeGame.move(square, moveCommand);
+            outputView.printMap(bridgeGame.getMoveResult());
+        }
+    }
+
+    private boolean isFinished() {
+        return bridgeGame.isSuccessful();
+    }
+
+    private void finishGame() {
+        outputView.printResult(bridgeGame);
+        status = false;
+    }
+
+    private void askRetry() {
+        outputView.printRetryMessage();
+        String retryCommand = inputHandler.readGameCommand();
+        if (retryCommand.equals(GameValue.RETRY_COMMAND)) {
+            bridgeGame.retry();
+        }
+        if (retryCommand.equals(GameValue.QUIT_COMMAND)) {
+            outputView.printResult(bridgeGame);
+        }
+    }
+}

리뷰: 이 부분은 인덴트를 1로 리팩토링하는 고통스러운(?) 경험을 해보셔도 좋을 것 같습니다! 
저도 절대 못 줄일 것 같던 인덴트를 줄이려고 이리저리 비틀다보니(?) 다양한 인사이트들이 떠오르더라고요..!
@@ -0,0 +1,82 @@
+package bridge.domain;
+
+import bridge.constants.GameValue;
+
+/**
+ * 다리 건너기 게임을 관리하는 클래스
+ */
+public class BridgeGame {
+
+    private int retryCount;
+    private MoveResult moveResult;
+
+    public BridgeGame(MoveResult moveResult, int retryCount) {
+        this.moveResult = moveResult;
+        this.retryCount = retryCount;
+    }
+
+    /**
+     * 사용자가 칸을 이동할 때 사용하는 메서드
+     */
+    public void move(String square, String moveCommand) {
+        if (moveCommand.equals(GameValue.MOVE_UP_COMMAND)) {
+            moveUp(square, moveCommand);
+        }
+        if (moveCommand.equals(GameValue.MOVE_DOWN_COMMAND)) {
+            moveLower(square, moveCommand);
+        }
+    }
+
+    private boolean isMoveUp(String square, String moveCommand) {
+        return square.equals(moveCommand);
+    }
+
+    private void moveUp(String square, String moveCommand) {
+        if (isMoveUp(square, moveCommand)) {
+            moveResult.moveUp(GameValue.MOVE);
+        }
+        if (!isMoveUp(square, moveCommand)) {
+            moveResult.moveUp(GameValue.STOP);
+        }
+    }
+
+    private boolean isMoveLower(String square, String moveCommand) {
+        return square.equals(moveCommand);
+    }
+
+    private void moveLower(String square, String moveCommand) {
+        if (isMoveLower(square, moveCommand)) {
+            moveResult.moveLowwer(GameValue.MOVE);
+        }
+        if (!isMoveLower(square, moveCommand)) {
+            moveResult.moveLowwer(GameValue.STOP);
+        }
+    }
+
+    public boolean isSuccessful() {
+        return moveResult.isSuccessful();
+    }
+
+    public String getGameResult() {
+        if (isSuccessful()) {
+            return "성공";
+        }
+        return "실패";
+    }
+
+    /**
+     * 사용자가 게임을 다시 시도할 때 사용하는 메서드
+     */
+    public void retry() {
+        retryCount++;
+        moveResult = new MoveResult();
+    }
+
+    public int getRetryCount() {
+        return retryCount;
+    }
+
+    public MoveResult getMoveResult() {
+        return moveResult;
+    }
+}

리뷰: 여기 early return 이 빠진 것 같아요!
@@ -0,0 +1,37 @@
+package bridge.domain;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class MoveResult {
+    private final List<String> upperBridge;
+    private final List<String> lowerBridge;
+
+    public MoveResult() {
+        this.upperBridge = new ArrayList<>();
+        this.lowerBridge = new ArrayList<>();
+    }
+
+    public void moveUp(String moveResult) {
+        upperBridge.add(moveResult);
+        lowerBridge.add(" ");
+    }
+
+    public void moveLowwer(String moveResult) {
+        upperBridge.add(" ");
+        lowerBridge.add(moveResult);
+    }
+
+    public boolean isSuccessful() {
+        if (upperBridge.contains("X") || lowerBridge.contains("X")) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        return "[ " + String.join(" | ", upperBridge) + " ]\n" +
+                "[ " + String.join(" | ", lowerBridge) + " ]";
+    }
+}

리뷰: > 핵심 로직을 구현하는 코드와 UI를 담당하는 로직을 분리해 구현한다.

이 부분은 요구사항이 지켜지지 않은 것 같습니다.
프리코스 기간 동안 가장 많이 요구된 프로그래밍 기법이라, 이 부분은 꼭 리팩토링 해보시면 좋을 것 같습니다!
@@ -0,0 +1,56 @@
+package bridge.view;
+
+import bridge.domain.BridgeGame;
+import bridge.domain.MoveResult;
+
+/**
+ * 사용자에게 게임 진행 상황과 결과를 출력하는 역할을 한다.
+ */
+public class OutputView {
+
+    public void printStartMessage() {
+        println("다리 건너기 게임을 시작합니다.");
+    }
+
+    public void printBridgeSizeInputMessage() {
+        printNewLine();
+        println("다리의 길이를 입력해주세요.");
+    }
+
+    public void printMoveInputMessage() {
+        printNewLine();
+        println("이동할 칸을 선택해주세요. (위: U, 아래: D)");
+    }
+
+    /**
+     * 현재까지 이동한 다리의 상태를 정해진 형식에 맞춰 출력한다.
+     */
+    public void printMap(MoveResult moveResult) {
+        println(moveResult.toString());
+    }
+
+    /**
+     * 게임의 최종 결과를 정해진 형식에 맞춰 출력한다.
+     */
+    public void printResult(BridgeGame bridgeGame) {
+        printNewLine();
+        println("최종 게임 결과");
+        printMap(bridgeGame.getMoveResult());
+        printNewLine();
+        System.out.printf("게임 성공 여부: %s\n", bridgeGame.getGameResult());
+        System.out.printf("총 시도한 횟수: %d\n", bridgeGame.getRetryCount());
+    }
+
+    public void printRetryMessage() {
+        printNewLine();
+        println("게임을 다시 시도할지 여부를 입력해주세요. (재시도: R, 종료: Q)");
+    }
+
+    private void println(String output) {
+        System.out.println(output);
+    }
+
+    private void printNewLine() {
+        System.out.println();
+    }
+}

리뷰: 확실히 출력용 메시지가 재활용되는 경우가 없다보니, 이렇게 하드 코딩해도 꽤나 직관적이잖아? 라는 느낌을 받았습니다 👍
@@ -0,0 +1,42 @@
+package bridge.view.handler;
+
+import bridge.domain.Bridge;
+import bridge.domain.BridgeMaker;
+import bridge.view.ErrorView;
+import bridge.view.InputView;
+import java.util.function.Supplier;
+
+public class InputHandler {
+    private final InputView inputView;
+    private final ErrorView errorView;
+
+    public InputHandler(InputView inputView, ErrorView errorView) {
+        this.inputView = inputView;
+        this.errorView = errorView;
+    }
+
+    public Bridge createValidatedBridge(BridgeMaker bridgeMaker) {
+        return receiveValidatedInput(() -> {
+            int bridgeSize = inputView.readBridgeSize();
+            return Bridge.from(bridgeMaker.makeBridge(bridgeSize));
+        });
+    }
+
+    public String readMoving() {
+        return receiveValidatedInput(inputView::readMoving);
+    }
+
+    public String readGameCommand() {
+        return receiveValidatedInput(inputView::readGameCommand);
+    }
+
+    private <T> T receiveValidatedInput(Supplier<T> inputView) {
+        while (true) {
+            try {
+                return inputView.get();
+            } catch (IllegalArgumentException exception) {
+                errorView.printErrorMessage(exception.getMessage());
+            }
+        }
+    }
+}

리뷰: (궁금해요!)

그동안 현준님과 리뷰해오면서 이런 재시도 로직을 while 로 처리할까, 재귀로 처리할까 고민하다가 저는 재귀를 선택했습니다.
그런데 재귀를 선택해도 호출 뎁스가 깊어지는게 아니고 뎁스가 1로 계속 유지되는 것 같더라고요. (어차피 catch를 하고 에러메시지를 출력을 하므로)

현준님은 재귀 대신 while을 선택하신 이유가 궁금합니다 :)
@@ -0,0 +1,82 @@
+package bridge.domain;
+
+import bridge.constants.GameValue;
+
+/**
+ * 다리 건너기 게임을 관리하는 클래스
+ */
+public class BridgeGame {
+
+    private int retryCount;
+    private MoveResult moveResult;
+
+    public BridgeGame(MoveResult moveResult, int retryCount) {
+        this.moveResult = moveResult;
+        this.retryCount = retryCount;
+    }
+
+    /**
+     * 사용자가 칸을 이동할 때 사용하는 메서드
+     */
+    public void move(String square, String moveCommand) {
+        if (moveCommand.equals(GameValue.MOVE_UP_COMMAND)) {
+            moveUp(square, moveCommand);
+        }
+        if (moveCommand.equals(GameValue.MOVE_DOWN_COMMAND)) {
+            moveLower(square, moveCommand);
+        }
+    }
+
+    private boolean isMoveUp(String square, String moveCommand) {
+        return square.equals(moveCommand);
+    }
+
+    private void moveUp(String square, String moveCommand) {
+        if (isMoveUp(square, moveCommand)) {
+            moveResult.moveUp(GameValue.MOVE);
+        }
+        if (!isMoveUp(square, moveCommand)) {
+            moveResult.moveUp(GameValue.STOP);
+        }
+    }
+
+    private boolean isMoveLower(String square, String moveCommand) {
+        return square.equals(moveCommand);
+    }
+
+    private void moveLower(String square, String moveCommand) {
+        if (isMoveLower(square, moveCommand)) {
+            moveResult.moveLowwer(GameValue.MOVE);
+        }
+        if (!isMoveLower(square, moveCommand)) {
+            moveResult.moveLowwer(GameValue.STOP);
+        }
+    }
+
+    public boolean isSuccessful() {
+        return moveResult.isSuccessful();
+    }
+
+    public String getGameResult() {
+        if (isSuccessful()) {
+            return "성공";
+        }
+        return "실패";
+    }
+
+    /**
+     * 사용자가 게임을 다시 시도할 때 사용하는 메서드
+     */
+    public void retry() {
+        retryCount++;
+        moveResult = new MoveResult();
+    }
+
+    public int getRetryCount() {
+        return retryCount;
+    }
+
+    public MoveResult getMoveResult() {
+        return moveResult;
+    }
+}

리뷰: else문이 아니라서 아무래도 return을 추가해 다음 조건을 확인하지 않도록 하는게 좋겠네요 !
@@ -0,0 +1,37 @@
+package bridge.domain;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class MoveResult {
+    private final List<String> upperBridge;
+    private final List<String> lowerBridge;
+
+    public MoveResult() {
+        this.upperBridge = new ArrayList<>();
+        this.lowerBridge = new ArrayList<>();
+    }
+
+    public void moveUp(String moveResult) {
+        upperBridge.add(moveResult);
+        lowerBridge.add(" ");
+    }
+
+    public void moveLowwer(String moveResult) {
+        upperBridge.add(" ");
+        lowerBridge.add(moveResult);
+    }
+
+    public boolean isSuccessful() {
+        if (upperBridge.contains("X") || lowerBridge.contains("X")) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        return "[ " + String.join(" | ", upperBridge) + " ]\n" +
+                "[ " + String.join(" | ", lowerBridge) + " ]";
+    }
+}

리뷰: 헛 그 부분을 놓쳐버렸네요 ..
이번 5기 최종까지 치면서 느낀건데 5시간이라는 시간에 얽매이지 않고
초반에 기능 요구 사항을 꼼꼼히 검토해야 한다는 것을 뼈저리게 느꼈습니다..
@@ -0,0 +1,42 @@
+package bridge.view.handler;
+
+import bridge.domain.Bridge;
+import bridge.domain.BridgeMaker;
+import bridge.view.ErrorView;
+import bridge.view.InputView;
+import java.util.function.Supplier;
+
+public class InputHandler {
+    private final InputView inputView;
+    private final ErrorView errorView;
+
+    public InputHandler(InputView inputView, ErrorView errorView) {
+        this.inputView = inputView;
+        this.errorView = errorView;
+    }
+
+    public Bridge createValidatedBridge(BridgeMaker bridgeMaker) {
+        return receiveValidatedInput(() -> {
+            int bridgeSize = inputView.readBridgeSize();
+            return Bridge.from(bridgeMaker.makeBridge(bridgeSize));
+        });
+    }
+
+    public String readMoving() {
+        return receiveValidatedInput(inputView::readMoving);
+    }
+
+    public String readGameCommand() {
+        return receiveValidatedInput(inputView::readGameCommand);
+    }
+
+    private <T> T receiveValidatedInput(Supplier<T> inputView) {
+        while (true) {
+            try {
+                return inputView.get();
+            } catch (IllegalArgumentException exception) {
+                errorView.printErrorMessage(exception.getMessage());
+            }
+        }
+    }
+}

리뷰: 재귀 호출로 구현했을 때 코드가 간결해진다는 장점을 가지고 있지만
탐색 깊이가 깊어 질 때 위험할 것이라 생각했습니다.
선호님 말씀대로 1로 유지된다면 이 점은 아무래도 제외해야할것 같네요
하지만 while문을 사용했을 때 반복 횟수를 제어할 수 있다는 장점도 있기에 while문을 사용하였습니다.
예를 들어서 실제 로그인 정책처럼 잘못된 값을 3회 또는 5회 입력 했을 경우 입력을 종료 시킨다던가 등의 상황도 생각해보았습니다!
@@ -0,0 +1,42 @@
+package bridge.view.handler;
+
+import bridge.domain.Bridge;
+import bridge.domain.BridgeMaker;
+import bridge.view.ErrorView;
+import bridge.view.InputView;
+import java.util.function.Supplier;
+
+public class InputHandler {
+    private final InputView inputView;
+    private final ErrorView errorView;
+
+    public InputHandler(InputView inputView, ErrorView errorView) {
+        this.inputView = inputView;
+        this.errorView = errorView;
+    }
+
+    public Bridge createValidatedBridge(BridgeMaker bridgeMaker) {
+        return receiveValidatedInput(() -> {
+            int bridgeSize = inputView.readBridgeSize();
+            return Bridge.from(bridgeMaker.makeBridge(bridgeSize));
+        });
+    }
+
+    public String readMoving() {
+        return receiveValidatedInput(inputView::readMoving);
+    }
+
+    public String readGameCommand() {
+        return receiveValidatedInput(inputView::readGameCommand);
+    }
+
+    private <T> T receiveValidatedInput(Supplier<T> inputView) {
+        while (true) {
+            try {
+                return inputView.get();
+            } catch (IllegalArgumentException exception) {
+                errorView.printErrorMessage(exception.getMessage());
+            }
+        }
+    }
+}

리뷰: 확실히 **3회 까지만 재시도가 가능하다** 라는 로직이 있을 경우에는 while 문으로 처리하는게 유연하게 대응할 수 있겠네요! 
인사이트 받고 갑니다 :D
@@ -1,4 +1,4 @@
-import { EstimationInputDTO, Estimation } from '#estimations/estimation.types.js';
+import { EstimationInputDTO, Estimation, IsActivate } from '#estimations/estimation.types.js';
 import { IEstimationRepository } from '#estimations/interfaces/estimation.repository.interface.js';
 import { PrismaService } from '#global/prisma.service.js';
 import { FindOptions } from '#types/options.type.js';
@@ -88,4 +88,52 @@ export class EstimationRepository implements IEstimationRepository {
       take: pageSize,
     });
   }
+
+  // confirmedForId 값이 null이 아니면은 이사확정 끝난것이다..~!
+  async findReviewable(userId: string, moveInfoIds: string[], page: number, pageSize: number) {
+    const estimations = await this.prisma.estimation.findMany({
+      where: {
+        confirmedForId: { in: moveInfoIds }, // 완료된 이사와 연결된 견적을 조회
+      },
+      include: {
+        driver: true, // 견적과 관련된 드라이버 정보
+        moveInfo: true, // 견적과 관련된 이사 정보
+        reviews: true, // 견적에 작성된 리뷰들
+      },
+      skip: (page - 1) * pageSize,
+      take: pageSize,
+    });
+
+    // 리뷰가 없는 견적만 필터링
+    return estimations.filter(estimation => !estimation.reviews.some(review => review.ownerId === userId));
+  }
+
+  // 유저의 이사 정보 목록 가져오기
+  async getUserMoveInfos(userId: string) {
+    return this.prisma.moveInfo.findMany({
+      where: { ownerId: userId }, // 로그인한 유저의 이사만 조회하기
+      select: { id: true }, // 이사 ID만 선택
+    });
+  }
+
+  // 지정 견적 요청 여부
+  async isDesignatedRequest(estimationId: string): Promise<IsActivate> {
+    const estimation = await this.prisma.estimation.findUnique({
+      where: { id: estimationId },
+      include: {
+        moveInfo: {
+          include: {
+            requests: {
+              where: {
+                status: 'APPLY', //
+              },
+            },
+          },
+        },
+      },
+    });
+
+    // 지정요청이면 'Active', 일반요청이면 'Inactive'
+    return estimation?.moveInfo.requests.length > 0 ? IsActivate.Active : IsActivate.Inactive;
+  }
 }

리뷰: 아마도 javascript 레벨이 아니라 prisma에서 findMany 해오는 단계에서 이 작업을 해서 가져올 수 있을 것 같습니다. 나중에 한 번 찾아보면 좋을 것 같네요.
@@ -1,4 +1,4 @@
-import { EstimationInputDTO, Estimation } from '#estimations/estimation.types.js';
+import { EstimationInputDTO, Estimation, IsActivate } from '#estimations/estimation.types.js';
 import { IEstimationRepository } from '#estimations/interfaces/estimation.repository.interface.js';
 import { PrismaService } from '#global/prisma.service.js';
 import { FindOptions } from '#types/options.type.js';
@@ -88,4 +88,52 @@ export class EstimationRepository implements IEstimationRepository {
       take: pageSize,
     });
   }
+
+  // confirmedForId 값이 null이 아니면은 이사확정 끝난것이다..~!
+  async findReviewable(userId: string, moveInfoIds: string[], page: number, pageSize: number) {
+    const estimations = await this.prisma.estimation.findMany({
+      where: {
+        confirmedForId: { in: moveInfoIds }, // 완료된 이사와 연결된 견적을 조회
+      },
+      include: {
+        driver: true, // 견적과 관련된 드라이버 정보
+        moveInfo: true, // 견적과 관련된 이사 정보
+        reviews: true, // 견적에 작성된 리뷰들
+      },
+      skip: (page - 1) * pageSize,
+      take: pageSize,
+    });
+
+    // 리뷰가 없는 견적만 필터링
+    return estimations.filter(estimation => !estimation.reviews.some(review => review.ownerId === userId));
+  }
+
+  // 유저의 이사 정보 목록 가져오기
+  async getUserMoveInfos(userId: string) {
+    return this.prisma.moveInfo.findMany({
+      where: { ownerId: userId }, // 로그인한 유저의 이사만 조회하기
+      select: { id: true }, // 이사 ID만 선택
+    });
+  }
+
+  // 지정 견적 요청 여부
+  async isDesignatedRequest(estimationId: string): Promise<IsActivate> {
+    const estimation = await this.prisma.estimation.findUnique({
+      where: { id: estimationId },
+      include: {
+        moveInfo: {
+          include: {
+            requests: {
+              where: {
+                status: 'APPLY', //
+              },
+            },
+          },
+        },
+      },
+    });
+
+    // 지정요청이면 'Active', 일반요청이면 'Inactive'
+    return estimation?.moveInfo.requests.length > 0 ? IsActivate.Active : IsActivate.Inactive;
+  }
 }

리뷰: 네!! 찾아보겠습니다 !! 태영님 알려주셔서 감사합니다!!!!