Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[3, 4단계 - 체스] 페드로(류형욱) 미션 제출합니다. #730

Merged
merged 74 commits into from
Mar 30, 2024
Merged
Show file tree
Hide file tree
Changes from 72 commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
b3df817
refactor(continuousMoveStrategy): 불필요한 static 메서드 제거
hw0603 Mar 24, 2024
224a769
refactor(board): 메서드 위치 변경
hw0603 Mar 24, 2024
16d1110
docs(readme): 3단계 추가 기능 요구사항 반영
hw0603 Mar 24, 2024
fd73746
feat(pieceType): 각 기물 타입이 평가치를 가지고 있도록 변경
hw0603 Mar 24, 2024
950a8b4
test(boardTest): Board 의 점수 계산 로직에 대한 테스트 코드 추가
hw0603 Mar 25, 2024
db3cd0f
feat(board): 현재 보드의 팀 별 점수를 계산하는 기능 구현
hw0603 Mar 25, 2024
b88656c
test(boardTest): 폰이 중복된 세로줄에 있는 다양한 경우 추가
hw0603 Mar 25, 2024
cc0a1cc
refactor(board): 중복된 폰의 평가치 패널티를 계산하는 메서드 분리
hw0603 Mar 25, 2024
f68c2b3
test(boardTest): 각 점수별 Board 를 구성하는 Map 을 테스트 픽스처 클래스로 이동
hw0603 Mar 25, 2024
bec3147
docs(readme): ChessGame 요구사항 추가
hw0603 Mar 25, 2024
b47b964
test(chessGameTest): 게임 상태에서의 팀 관리 로직 테스트 추가
hw0603 Mar 25, 2024
c962c8a
feat(gameState): 게임 상태를 표현하는 인터페이스 정의
hw0603 Mar 25, 2024
1b09fd1
feat(moveResponse): 말 이동 후 응답 객체 정의
hw0603 Mar 25, 2024
725e7f8
feat(/state): 게임 상태를 나타내는 구체 클래스 구현
hw0603 Mar 25, 2024
41c7bc8
feat(chessGame): 상태를 가지고 기물을 이동시키는 체스 게임 구현
hw0603 Mar 25, 2024
c2419a2
test(chessGameTest): 킹이 잡혔을 때 게임을 종료시키는 테스트 코드 추가
hw0603 Mar 25, 2024
ac5ef1a
feat(chessGame): 킹이 잡혔을 때 게임을 종료하고 승자를 판단하는 기능 구현
hw0603 Mar 25, 2024
c33282b
test(chessGameTest): 각 팀별 현재 점수를 계산하는 테스트 코드 추가
hw0603 Mar 25, 2024
6a69d5a
feat(chessGame): 팀 별 현재 점수를 계산하는 로직 구현
hw0603 Mar 25, 2024
dfc1a95
refactor(gameEnd): 게임 종료 메시지 상수 포장
hw0603 Mar 25, 2024
8f06743
refactor(gamePlaying): 게임 진행 중 각 말의 차례를 나타내는 상태 객체 추상화
hw0603 Mar 25, 2024
c2bd7bc
style(chessGameTest): 미사용 import 문 삭제
hw0603 Mar 25, 2024
a39a214
remove(turn): 더 이상 사용되지 않는 Turn 클래스와 테스트 코드 삭제
hw0603 Mar 25, 2024
6a2807e
refactor(board): 이동한 위치에 상대편의 기물이 있을 때, 해당 기물의 타입을 반환하도록 변경
hw0603 Mar 25, 2024
801ae77
refactor(boardDto): 정적 팩토리 메서드 인자 타입 변경(Board -> Map<Position, Piece)
hw0603 Mar 25, 2024
9bcc335
feat(chessController): 체스 라운드 진행 기능 구현
hw0603 Mar 25, 2024
8d1c564
feat(outputView): 우승자를 출력하는 기능 구현
hw0603 Mar 26, 2024
c17d59d
feat(outputView): 점수 출력 로직 구현
hw0603 Mar 26, 2024
33665ae
refactor(chessController): 콘솔 출력시 OutputView에서 출력하도록 변경
hw0603 Mar 26, 2024
f471cfb
refactor(chessController): 게임 계속 진행 여부를 판단하는 표현식의 메서드 분리
hw0603 Mar 26, 2024
0b336af
feat(chessController): 게임 종료 후 status 명령을 입력받았을 때 결과를 출력하도록 구현
hw0603 Mar 26, 2024
ad1c927
refactor(chessController): 게임 종료부 메서드 분리
hw0603 Mar 26, 2024
58a5272
refactor(chessController): RequestDt에 따라 분기하는 로직의 메서드 분리
hw0603 Mar 26, 2024
a6cfffc
refactor(/state): 상태 객체를 싱글톤으로 사용
hw0603 Mar 26, 2024
c95705d
refactor(outputView): 점수 출력 시 소수점 아래 1자리까지만 출력하도록 변경
hw0603 Mar 26, 2024
b2d1888
refactor(gameRequest): 컨트롤러가 뷰에서 넘어온 Dto에 의존하지 않고 gameRequest 도메인 객체에…
hw0603 Mar 26, 2024
13dd3fe
test(gameRequestTest): 명령 객체에 대한 테스트 코드 추가
hw0603 Mar 26, 2024
cd69a03
test(pieceDaoTest): Piece 정보를 DB에 저장하기 위한 테스트 코드 추가
hw0603 Mar 26, 2024
6c50a3a
feat(pieceDao): Piece 를 DB에 저장하는 기능 구현
hw0603 Mar 26, 2024
7a1adf3
docs(readme): 4단계 추가 기능 요구사항 작성
hw0603 Mar 26, 2024
3be7b72
feat(pieceDao): List<PieceDto> 를 전달받아 모두 삽입하는 기능 구현
hw0603 Mar 26, 2024
107e671
test(gameDaoTest): 게임 정보를 관리하는 Game 테이블에 대한 테스트 코드 추가
hw0603 Mar 26, 2024
5c98f8b
feat(gameDao): Game 테이블에서 순서를 관리하기 위한 기능 구현
hw0603 Mar 26, 2024
8b790b2
feat(chessGame): 현재 상태를 DB에 저장하는 기능 구현
hw0603 Mar 26, 2024
fb58c54
feat(chessController): 'save' 를 입력하면 현재 상태를 DB에 저장할 수 있도록 구현
hw0603 Mar 26, 2024
aa4db24
refactor(teamColor): 미사용 함수 삭제
hw0603 Mar 26, 2024
52c300c
feat(chessGame): 'load' 를 입력하면 가장 최근에 저장된 게임을 불러오는 기능 구현
hw0603 Mar 26, 2024
b1f25ff
refactor(chessGame): 저장된 게임 ID를 기반으로 이전 게임을 로드할 수 있도록 변경
hw0603 Mar 26, 2024
fb19e82
refactor(/dao): DB 커넥션을 사용하는 객체들을 싱글톤으로 변경
hw0603 Mar 26, 2024
8800be6
refactor(gameDao): DB에 저장된 모든 게임 삭제 시 AUTO_INCREMENT Id 값도 초기화되도록 변경
hw0603 Mar 27, 2024
9a9e956
style: 미사용 import 삭제
hw0603 Mar 27, 2024
0d7fea2
refactor(gameDaoTest): 각 테스트 시작 전과 끝난 후 테이블을 초기화하도록 변경
hw0603 Mar 27, 2024
503576d
refactor(pieceDaoTest): 각 테스트 시작 전과 끝난 후 테이블을 초기화하도록 변경
hw0603 Mar 27, 2024
e097052
add(docker-compose): MySQL 컨테이너를 정의하는 docker-compose 파일 추가
hw0603 Mar 27, 2024
8566ea2
refactor(dbService): DB에 저장된 게임을 로드할 때, DAO 를 서비스 레이어에서 사용하도록 변경
hw0603 Mar 27, 2024
08f66e0
refactor(dbService): 게임 저장 시 DB 접근 로직을 Service 레이어로 분리
hw0603 Mar 27, 2024
8d13e51
feat(outputView): 추가된 명령에 대한 설명 추가
hw0603 Mar 27, 2024
c4e3a1d
refactor(dbService): 게임의 save, load 책임 자체를 서비스 레이어로 위임
hw0603 Mar 27, 2024
fb43fba
refactor(board): Optional 값에 따라 분기하던 로직을 메서드 체이닝으로 변경
hw0603 Mar 28, 2024
fa6eac2
docs(readme): 프로그램 사용 방법 추가
hw0603 Mar 28, 2024
e122c64
refactor(board): 명시적 형변환 대신 제네릭 타입을 명시하도록 변경
hw0603 Mar 28, 2024
5d18908
refactor(dbException): 데이터베이스 관련 로직에서 예외 발생 시 던질 커스텀 예외 클래스 정의
hw0603 Mar 28, 2024
fbce762
refactor(dbConnector): DB 연결 오류 시 null을 반환하지 않고 예외를 던지도록 변경
hw0603 Mar 28, 2024
944fb76
refactor(pieceDao): allAll() 호출 시 배치 쿼리를 통해 bulk insert 가 가능하도록 변경
hw0603 Mar 28, 2024
f2311d3
refactor(chessGame): 기물 이동 시 상태 객체에 위임하지 않고 Board 에 직접 요청하도록 변경
hw0603 Mar 28, 2024
86efdba
refactor(dbService): 스트림으로 데이터 형태를 변환하는 로직의 메서드 분리
hw0603 Mar 28, 2024
24b05c8
refactor(dbService): 게임 저장 시 트랜잭션 처리
hw0603 Mar 29, 2024
c7ed770
refactor(gameDaoTest): GameDao 변경에 따른 테스트 코드 수정
hw0603 Mar 29, 2024
f6ab2ca
refactor(pieceDao): PieceDao 변경에 따른 테스트 코드 수정
hw0603 Mar 29, 2024
d2001d7
fix(pieceDao): DAO 가 아닌 Service 에서 트랜잭션을 관리하도록 수정
hw0603 Mar 29, 2024
d5b904a
fix(/service): 잘못 지정된 service 패키지 경로 수정
hw0603 Mar 29, 2024
9f390a3
test(dbService): 게임 저장/로드 로직 테스트 추가
hw0603 Mar 29, 2024
ea4a08f
add(/resource): 테이블 생성 SQL 추가
hw0603 Mar 29, 2024
45fffe5
docs(readme): 프로그램 실행 방법 보충
hw0603 Mar 29, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,4 @@ out/
.vscode/

### Docker ###
/docker/
/docker/db
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ dependencies {
testImplementation platform('org.assertj:assertj-bom:3.25.1')
testImplementation('org.junit.jupiter:junit-jupiter')
testImplementation('org.assertj:assertj-core')
runtimeOnly("com.mysql:mysql-connector-j:8.3.0")
}

java {
Expand Down
18 changes: 18 additions & 0 deletions docker/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
version: "3.9"
services:
db:
image: mysql:8.0.28
platform: linux/x86_64
restart: always
ports:
- "13306:3306"
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: chess
MYSQL_USER: user
MYSQL_PASSWORD: password
TZ: Asia/Seoul
volumes:
- ./db/mysql/data:/var/lib/mysql
- ./db/mysql/config:/etc/mysql/conf.d
- ./db/mysql/init:/docker-entrypoint-initdb.d
24 changes: 24 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
## 실행 방법
1. 프로그램 실행 후 start 를 입력하여 새로운 게임을 시작하거나 load 를 입력하여 이전에 저장된 게임을 불러온다.
2. 게임이 시작되면 move {source} {target} 을 입력하여 체스 말을 움직인다.
3. 게임 진행 중 save 명령으로 현재 상태를 저장할 수 있다.
4. 한 팀의 킹이 잡히면 게임은 종료되고, 이때 status 명령으로 각 팀의 점수와 승자를 확인할 수 있다.
5. 어디서든 end 명령으로 게임을 종료할 수 있다.

## 기능 흐름

- [x] 사용자로부터 게임 시작 여부 입력
Expand All @@ -21,6 +28,7 @@

### Piece
- [x] 타입이 다른 기물들과 자신을 구별할 수 있는`PieceType`을 가진다.
- [x] `PieceType` 에는 각 기물의 평가치(점수)가 정의되어 있다.
- [x] 이동 가능한 위치인지 여부를 확인한다.
- [x] 모든 말은 체스보드 안에서만 움직일 수 있다.
- [x] 목적지까지 가는 경로에 다른 기물이 존재하면 이동할 수 없다.
Expand All @@ -44,9 +52,25 @@
- [x] 체스의 규칙에 맞게 각 기물들을 시작 위치로 배치한다.
- [x] 기물의 도착지에 현재 이동하는 기물과 같은 색의 기물이 존재하면 이동할 수 없다.
- [x] 도착지에 다른 색의 기물이 존재하는 경우, 해당 기물을 제거하고 배치한다.
- [x] 현재 보드에 남아 있는 기물들을 기반으로 각 팀의 점수를 계산할 수 있다.
- 각 팀의 점수는 남아 있는 팀의 기물 평가치의 합으로 계산한다.
- 같은 세로줄에 같은 색의 폰이 있는 경우, 각 폰들은 원래 평가치의 절반으로 계산된다.

### ChessGame

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

docker compose파일을 잘 올려주시긴했는데 README에 실행법도 간략히 정리되어있으면 더 좋을거같아요

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

반영했습니다!

- [x] `흰 팀 차례`, `검은 팀 차례`, `게임 종료` 의 상태를 가진다.
- [x] 게임이 진행중인 상태에서는 전달받은 출발지와 목적지에 따라 말을 이동시킨다.
- [x] 한 팀의 킹이 잡히면 게임을 종료한다.
- [x] 게임이 종료된 경우 승자를 판단할 수 있다.
- [x] 상태와 무관하게 각 팀별 현재 점수를 계산할 수 있다.
- [ ] 현재 게임 상태를 DB에 저장할 수 있다.
- [ ] DB에 저장된 게임 상태를 다시 불러올 수 있다.

### InputView
- [x] `start` 명령과 `end`, `move` 명령을 입력받는다.
- [x] 게임 종료 후 `status` 명령을 입력받을 수 있다.
- [ ] 현재 게임 상태를 저장하기 위한 `save` 명령을 입력받을 수 있다.
- [ ] 저장된 게임 상태를 불러오기 위한 `load` 명령을 입력받을 수 있다.

### OutputView
- [x] 현재 체스판의 상태를 출력한다.
- [x] 각 진영의 점수를 출력하고 어느 진영이 이겼는지 출력한다.
4 changes: 3 additions & 1 deletion src/main/java/Application.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import controller.ChessController;
import service.DBService;
import view.InputView;
import view.OutputView;

public class Application {
public static void main(String[] args) {
InputView inputView = new InputView();
OutputView outputView = new OutputView();
ChessController chessController = new ChessController(inputView, outputView);
DBService dbService = new DBService();
ChessController chessController = new ChessController(inputView, outputView, dbService);

chessController.run();
}
Expand Down
109 changes: 82 additions & 27 deletions src/main/java/controller/ChessController.java
Original file line number Diff line number Diff line change
@@ -1,29 +1,35 @@
package controller;

import domain.GameCommand;
import domain.game.Board;
import domain.game.BoardInitializer;
import domain.game.Turn;
import domain.game.ChessGame;
import domain.game.GameRequest;
import domain.game.Piece;
import domain.game.TeamColor;
import domain.position.Position;
import service.DBService;
import dto.BoardDto;
import dto.RequestDto;
import java.util.Map;
import java.util.function.Supplier;
import view.InputView;
import view.OutputView;

public class ChessController {
private final InputView inputView;
private final OutputView outputView;
private final DBService dbService;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DB 저장을 위한 데이터는 어느 계층까지 전파되어도 괜찮은가?
DB가 도입되기 전(3단계) 까지는 컨트롤러에서 도메인 데이터를 알 일이 크게 없었는데요, DB 저장을 위해 컨트롤러에서 서비스를 호출하다 보니, 서비스에 전달할(DB에 저장할) 데이터를 컨트롤러 단까지 모두 가져와야 하는 경우가 생겼습니다. 데이터가 있는 도메인에서 DAO를 바로 사용하지 않고 컨트롤러 -> 서비스 -> DAO 순으로 흘러가다 보니 발생한 문제인 것 같은데, 각 계층별 책임을 분리하면서 데이터의 영속성을 유지하기 위한 트레이드오프라고 보면 될까요?

제가 질문을 잘 이해를 못한거같은데, db에 저장할 데이터를 컨트롤러단으로 가져오는게 어느부분이죠?
저장을하려면 당연히 필요한 과정같은데 해당부분에 코멘트를 다시남겨주세요

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// ChessController.java
    private void saveCurrentStatus(ChessGame chessGame) {
        int gameId = dbService.saveGame(chessGame);
        outputView.printSaveResult(gameId);
    }

지금은 Service 레이어가 생기면서 chessGame 자체를 전달하도록 변경하여 컨트롤러에서 직접 게임 내부의 데이터를 가져와서 저장하거나 하는 부분은 없어진 것 같아요.

사실 현재의 미션에만 국한된 질문은 아니였습니다. 처음 저 의문이 들었던 배경 설명을 좀 드리자면 3단계까지 구현했을 때는 (DB에 저장할 일이 없다 보니) 객체 간 메시지 보내기 로 충분히 구현 가능했던 부분이 4단계로 오니 DB에 저장하기 위해서 특정 데이터를 직접 반환하는 게터 느낌의 메서드가 추가되는 경우가 있었어요.

대표적인 예로 Piece에서 TeamColor 값 자체를 꺼내올 일은 없었는데 DB 저장 요구사항이 추가되고 나니 직접 가져와서 저장해야 하는 소요가 생기더라구요.

꼭 체스 미션이 아니라 다른 프로젝트들에서도 이렇게 도메인 로직에는 사용되지 않지만 DB 저장을 위해 필요한 게터들이 생기는 경우가 있을 것 같은데, 말씀하신 대로 저장을 위해 당연히 필요한 과정이라고 생각하고 고민 없이 사용하면 되는 부분일까요?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵 저장에 필요하니 당연히 사용해야죠.

지금 domain과 infra entity가 혼용되어 사용되어 발생하는 의문인데 이걸 나눠서 관리하면 좀 나을거에요. 다만 이것도 중복코드가 많이 발생하기 때문에 트레이드오프가 있어서 프로젝트하실때 직접 경험해보시면 더 좋을거에요.


public ChessController(final InputView inputView, final OutputView outputView) {
public ChessController(final InputView inputView, final OutputView outputView, final DBService dbService) {
this.inputView = inputView;
this.outputView = outputView;
this.dbService = dbService;
}

public void run() {
outputView.printWelcomeMessage();
GameCommand command = readUserInput(inputView::inputGameStart);
if (command.isStart()) {
startGame();
GameRequest gameRequest = readUserInput(inputView::inputGameCommand).asRequest();
while (gameRequest.isStart() || gameRequest.isLoad()) {
startGame(gameRequest);
outputView.printRestartMessage();
gameRequest = readUserInput(inputView::inputGameCommand).asRequest();
}
}

Expand All @@ -32,35 +38,84 @@ private <T> T readUserInput(Supplier<T> inputSupplier) {
try {
return inputSupplier.get();
} catch (IllegalArgumentException e) {
System.out.println(e.getMessage());
outputView.printErrorMessage(e.getMessage());
}
}
}

private void startGame() {
Board board = BoardInitializer.init();
printStatus(board);
private void startGame(GameRequest gameRequest) {
ChessGame chessGame = createGame(gameRequest);
printBoardStatus(chessGame.getPositionsOfPieces());

Turn turn = new Turn();
RequestDto requestDto = readUserInput(inputView::inputGameCommand);
while (requestDto.command().isContinuable()) {
doTurn(board, turn, requestDto);
printStatus(board);
requestDto = readUserInput(inputView::inputGameCommand);
while (shouldProceedGame(gameRequest, chessGame)) {
outputView.printCurrentTurn(chessGame.currentPlayingTeam());
gameRequest = readUserInput(inputView::inputGameCommand).asRequest();
processRequest(gameRequest, chessGame);
}
finishGame(gameRequest, chessGame);
}

private void doTurn(Board board, Turn turn, RequestDto requestDto) {
try {
board.movePiece(turn.current(), requestDto.source(), requestDto.destination());
turn.next();
} catch (IllegalArgumentException e) {
System.out.println("[오류] " + e.getMessage());
private ChessGame createGame(GameRequest gameRequest) {
if (gameRequest.isStart()) {
return new ChessGame();
}
int gameId = readUserInput(inputView::inputGameId);
return dbService.loadGame(gameId);
}

private void printStatus(Board board) {
BoardDto boardDto = BoardDto.from(board);
private void printBoardStatus(Map<Position, Piece> positionOfPieces) {
BoardDto boardDto = BoardDto.from(positionOfPieces);
outputView.printBoard(boardDto);
}

private boolean shouldProceedGame(GameRequest gameRequest, ChessGame chessGame) {
return gameRequest.isContinuable() && !chessGame.isGameEnd();
}

private void processRequest(GameRequest gameRequest, ChessGame chessGame) {
if (gameRequest.isSave()) {
saveCurrentStatus(chessGame);
return;
}
if (gameRequest.isContinuable()) {
playRound(gameRequest, chessGame);
}
}

private void saveCurrentStatus(ChessGame chessGame) {
int gameId = dbService.saveGame(chessGame);
outputView.printSaveResult(gameId);
}

private void playRound(GameRequest gameRequest, ChessGame chessGame) {
try {
chessGame.move(gameRequest.source(), gameRequest.destination());
printBoardStatus(chessGame.getPositionsOfPieces());
} catch (IllegalArgumentException | IllegalStateException e) {
outputView.printErrorMessage(e.getMessage());
}
}

private void finishGame(GameRequest gameRequest, ChessGame chessGame) {
outputView.printGameEndMessage();
if (gameRequest.isEnd()) {
return;
}

outputView.printStatusInputMessage();
gameRequest = readUserInput(inputView::inputGameCommand).asRequest();
if (gameRequest.isStatus()) {
printGameResult(chessGame);
}
}

private void printGameResult(ChessGame chessGame) {
TeamColor winner = chessGame.getWinner();
double whiteScore = chessGame.currentScoreOf(TeamColor.WHITE);
double blackScore = chessGame.currentScoreOf(TeamColor.BLACK);

outputView.printWinner(winner);
outputView.printScore(TeamColor.WHITE, whiteScore);
outputView.printScore(TeamColor.BLACK, blackScore);
}
}
33 changes: 33 additions & 0 deletions src/main/java/dao/DBConnector.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package dao;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class DBConnector {
private static final String SERVER = "localhost:13306";
private static final String DATABASE = "chess";
private static final String OPTION = "?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC";
private static final String USERNAME = "root";
private static final String PASSWORD = "root";

private static DBConnector instance = null;

private DBConnector() {
}

public static DBConnector getInstance() {
if (instance == null) {
instance = new DBConnector();
}
return instance;
}

public Connection getConnection() {
try {
return DriverManager.getConnection("jdbc:mysql://" + SERVER + "/" + DATABASE + OPTION, USERNAME, PASSWORD);
} catch (final SQLException e) {
throw new DBException("DB 연결 오류", e);
}
}
}
17 changes: 17 additions & 0 deletions src/main/java/dao/DBException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package dao;

public class DBException extends RuntimeException {
private static final String DEFAULT_ERROR_MESSAGE = "쿼리 실행 중 오류가 발생했습니다.";

public DBException(String message) {
super(message);
}

public DBException(Throwable cause) {
super(DEFAULT_ERROR_MESSAGE, cause);
}

public DBException(String message, Throwable cause) {
super(message, cause);
}
}
12 changes: 12 additions & 0 deletions src/main/java/dao/GameDao.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package dao;

import domain.game.TeamColor;
import java.sql.Connection;

public interface GameDao {
int addGame(Connection connection);

TeamColor findTurn(Connection connection, int gameId);

void updateTurn(Connection connection, int gameId, TeamColor teamColor);
}
56 changes: 56 additions & 0 deletions src/main/java/dao/GameDaoImpl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package dao;

import domain.game.TeamColor;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class GameDaoImpl implements GameDao {
private static final String TABLE_NAME = "game";

@Override
public int addGame(Connection connection) {
final String query = String.format("INSERT INTO %s(turn) VALUE(?);", TABLE_NAME);
try (PreparedStatement preparedStatement = connection.prepareStatement(query, Statement.RETURN_GENERATED_KEYS)) {
preparedStatement.setString(1, TeamColor.WHITE.name());
preparedStatement.executeUpdate();
ResultSet resultSet = preparedStatement.getGeneratedKeys();
if (resultSet.next()) {
return resultSet.getInt(1);
}
} catch (final SQLException e) {
throw new DBException(e);
}
throw new DBException("게임 생성 중 오류가 발생했습니다.");
}

@Override
public TeamColor findTurn(Connection connection, int gameId) {
final String query = String.format("SELECT turn FROM %s WHERE `gameId` = ?", TABLE_NAME);
try (PreparedStatement preparedStatement = connection.prepareStatement(query)) {
preparedStatement.setInt(1, gameId);
final ResultSet resultSet = preparedStatement.executeQuery();
if (resultSet.next()) {
String turn = resultSet.getString("turn");
return TeamColor.valueOf(turn);
}
} catch (SQLException e) {
throw new DBException(e);
}
throw new DBException(gameId + " 에 해당하는 차례를 찾을 수 없습니다.");
}

@Override
public void updateTurn(Connection connection, int gameId, TeamColor teamColor) {
final var query = String.format("UPDATE %s SET turn = ? WHERE gameId = ?", TABLE_NAME);
try (PreparedStatement preparedStatement = connection.prepareStatement(query)) {
preparedStatement.setString(1, teamColor.name());
preparedStatement.setInt(2, gameId);
preparedStatement.executeUpdate();
} catch (SQLException e) {
throw new DBException(e);
}
}
}
11 changes: 11 additions & 0 deletions src/main/java/dao/PieceDao.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package dao;

import dto.PieceDto;
import java.sql.Connection;
import java.util.List;

public interface PieceDao {
void addAll(Connection connection, List<PieceDto> pieceDtos, int gameId);

List<PieceDto> findAllPieces(Connection connection, int gameId);
}
Loading