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

[Spring 체스 - 3단계] 다니(이다은) 미션 제출합니다. #299

Merged
merged 26 commits into from
May 2, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
5f3ad77
refactor(dao): DBConnection 삭제
da-nyee Apr 23, 2021
b7c61ef
refactor(dao): @Autowired 삭제
da-nyee Apr 23, 2021
eb7622c
refactor(dao): try-catch 삭제
da-nyee Apr 23, 2021
e11a219
docs(readme): 요구사항 정리
da-nyee Apr 23, 2021
1aeb2c0
docs(readme): DB DDL 수정
da-nyee Apr 25, 2021
95c5b86
feat(room): 체스방 목록 조회 기능 추가
da-nyee Apr 25, 2021
5a02315
docs(readme): DB DDL 수정
da-nyee Apr 25, 2021
10a6bfc
feat(room): 체스방 생성 기능 추가
da-nyee Apr 25, 2021
0fd737e
feat(room): 체스방 참여 기능 추가
da-nyee Apr 25, 2021
046c090
feat(resources): 홈 화면 버튼 추가
da-nyee Apr 25, 2021
0339eb5
fix(chess): 서버를 재시작했을 때 이전 게임이 진행되지 않던 버그 해결
da-nyee Apr 25, 2021
d3452e6
fix(chess): King을 잡아도 게임이 종료되지 않던 버그 해결
da-nyee Apr 25, 2021
0401795
style(dao): 코드 포맷 변경
da-nyee Apr 28, 2021
08ce4a1
refactor(player): 사용하지 않는 메소드 삭제
da-nyee Apr 28, 2021
dde125f
refactor(controller): 게임 시작(방 생성) 요청 및 응답 DTO 생성
da-nyee Apr 28, 2021
41c447b
fix(chess): 객체 직렬화 버그 해결
da-nyee Apr 28, 2021
cfcb45a
test(api-controller): 테스트 작성
da-nyee Apr 29, 2021
d5431c3
refactor(chess): View-Controller 사이 DTO 범위 수정
da-nyee Apr 29, 2021
fede792
refactor(chess): Repository-DAO 사이 DTO 범위 수정
da-nyee Apr 29, 2021
cd90bec
refactor(chess): Service 내 DTO 삭제
da-nyee Apr 29, 2021
eece951
feat(resources): schema.sql 추가
da-nyee Apr 29, 2021
13b02f1
refactor(chess): 메소드명, 변수명, 파라미터명 변경
da-nyee Apr 30, 2021
d222c24
refactor(chess): 메소드명 변경
da-nyee Apr 30, 2021
d1e6aac
refactor(chess): url-pattern 방식 변경
da-nyee Apr 30, 2021
5bc1ba5
refactor(js): window.location.href 주소 수정
da-nyee Apr 30, 2021
7db4006
refactor(dto): 클래스명 변경
da-nyee Apr 30, 2021
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
5 changes: 4 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,11 @@ dependencies {
implementation 'net.rakugakibox.spring.boot:logback-access-spring-boot-starter:2.7.1'
implementation 'pl.allegro.tech.boot:handlebars-spring-boot-starter:0.3.1'

implementation 'io.rest-assured:rest-assured:4.3.2'
implementation 'io.rest-assured:json-path:4.3.2'
compile group: 'com.sun.xml.bind', name: 'jaxb-osgi', version: '3.0.0-M5'

testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'io.rest-assured:rest-assured:3.3.0'

runtimeOnly 'mysql:mysql-connector-java'
runtimeOnly 'com.h2database:h2'
Expand Down
48 changes: 35 additions & 13 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,54 @@
- Spring Framework를 활용하여 애플리케이션을 구동한다.
- Spark를 대체하여 Spring MVC를 활용하여 요청을 받고 응답을 한다.
- Spring JDBC를 활용하여 DB 접근하던 기존 로직을 대체한다.
<br/>

## step1 프로그래밍 요구사항
- 스프링 애플리케이션으로 체스가 실행 가능 해야한다.
- @Controller나 @RestController를 활용하여 요청을 받아야 한다.
- Spring JDBC에서 제공하는 JdbcTemplate를 이용하여 Connection을 직접 만들어 주는 로직을 대체한다.
- JdbcTemplate는 매번 새로 생성하지 않고 빈 주입을 받아서 사용한다.
<br/>

## step2 요구사항
- 체스 게임을 진행할 수 있는 방을 만들어서 동시에 여러 게임이 가능하도록 하기
<br/>

## step2 프로그래밍 요구사항
### 체스방 만들기
- localhost:8080 요청 시 노출되는 페이지에 체스방을 만들 수 있는 버튼이 있다.
- 체스방 만들기 버튼을 누르면 새로운 체스판이 초기화된다.
- 체스방에는 고유 식별값이 부여된다. (이 고유 식별값은 체스방 주소에서 사용됨)
### 체스방 목록 조회하기
- localhost:8080 요청 시 체스방 목록을 조회할 수 있다.
- 체스방 목록에는 체스방 제목이 표시된다.
### 체스방 참여하기
- localhost:8080 요청 시 체스방 목록에서 체스방을 클릭하면 체스 게임을 이어서 진행할 수 있다.
<br/>

## DB DDL
```sql
-- -----------------------------------------------------
-- Table `mydb`.`piece`
-- Table `mychess`.`room`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `mydb`.`piece` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`piece_name` VARCHAR(45) NOT NULL,
`piece_position` VARCHAR(45) NOT NULL,
PRIMARY KEY (`id`))
ENGINE = InnoDB;
CREATE TABLE IF NOT EXISTS `mychess`.`room` (
`room_id` BIGINT NOT NULL AUTO_INCREMENT,
`room_name` VARCHAR(45) NOT NULL,
`current_turn` VARCHAR(45) NOT NULL,
PRIMARY KEY (`room_id`)
) CHARSET = utf8, ENGINE = InnoDB;


-- -----------------------------------------------------
-- Table `mydb`.`turn`
-- Table `mychess`.`piece`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `mydb`.`turn` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`current_turn` VARCHAR(45) NOT NULL,
PRIMARY KEY (`id`))
ENGINE = InnoDB;
CREATE TABLE IF NOT EXISTS `mychess`.`piece` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`piece_name` VARCHAR(45) NOT NULL,
`piece_position` VARCHAR(45) NOT NULL,
`room_id` BIGINT NOT NULL,
PRIMARY KEY (`id`),
FOREIGN KEY (`room_id`)
REFERENCES room(`room_id`)
) CHARSET = utf8, ENGINE = InnoDB;
```
37 changes: 28 additions & 9 deletions src/main/java/chess/controller/ChessApiController.java
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
package chess.controller;

import chess.domain.piece.Piece;
import chess.domain.position.Position;
import chess.dto.ChessBoardDto;
import chess.dto.request.MoveRequestDto;
import chess.dto.request.RoomNameRequestDto;
import chess.dto.request.TurnChangeRequestDto;
import chess.dto.response.MoveResponseDto;
import chess.dto.response.RoomIdResponseDto;
import chess.service.ChessService;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;

import java.util.Map;

@RestController
public class ChessApiController {
Expand All @@ -19,14 +24,28 @@ public ChessApiController(final ChessService chessService) {
this.chessService = chessService;
}

@PostMapping(value = "/move", produces = MediaType.APPLICATION_JSON_VALUE)
public MoveResponseDto move(@RequestBody MoveRequestDto moveRequestDto) {
return chessService.move(moveRequestDto);
@PostMapping(value = "/api/room")
public RoomIdResponseDto start(@RequestBody final RoomNameRequestDto roomNameRequestDto) {
return new RoomIdResponseDto(chessService.addRoom(roomNameRequestDto.getRoomName()));
}

@GetMapping("/api/chess/{roomId}")
public ChessBoardDto chess(@PathVariable final Long roomId) {
Map<Position, Piece> board = chessService.board(roomId);
String currentTurn = chessService.currentTurn(roomId);
return new ChessBoardDto(board, currentTurn);
}

@PostMapping(value = "/api/move", produces = MediaType.APPLICATION_JSON_VALUE)
public MoveResponseDto move(@RequestBody final MoveRequestDto moveRequestDto) {
boolean isMovable = chessService.move(
moveRequestDto.getSource(), moveRequestDto.getTarget(), moveRequestDto.getRoomId());
return new MoveResponseDto(isMovable);
}

@PostMapping(value = "/turn", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Void> turn(@RequestBody TurnChangeRequestDto turnChangeRequestDto) {
chessService.changeTurn(turnChangeRequestDto);
@PostMapping(value = "/api/turn", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Void> turn(@RequestBody final TurnChangeRequestDto turnChangeRequestDto) {
chessService.changeTurn(turnChangeRequestDto.getNextTurn(), turnChangeRequestDto.getRoomId());
return new ResponseEntity<>(HttpStatus.OK);
}
}
49 changes: 21 additions & 28 deletions src/main/java/chess/controller/ChessController.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
package chess.controller;

import chess.dto.ChessBoardDto;
import chess.dto.StringChessBoardDto;
import chess.dto.RoomsDto;
import chess.dto.response.ScoreResponseDto;
import chess.service.ChessService;
import com.fasterxml.jackson.core.JsonProcessingException;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Controller
public class ChessController {
Expand All @@ -17,34 +20,24 @@ public ChessController(final ChessService chessService) {
this.chessService = chessService;
}

@GetMapping("/start")
public String start() {
chessService.makeRound();
return makeNewGame();
}

@GetMapping("/reset")
public String reset() {
chessService.resetRound();
return makeNewGame();
@GetMapping("/rooms")
public String rooms(final Model model) {
List<RoomsDto> roomsDto = new ArrayList<>();
Map<Long, String> rooms = chessService.rooms();
for (Map.Entry<Long, String> roomEntry : rooms.entrySet()) {
roomsDto.add(new RoomsDto(roomEntry.getKey(), roomEntry.getValue()));
}
model.addAttribute("rooms", roomsDto);
return "rooms";
}

@GetMapping("/chess")
public String chess(final Model model) throws JsonProcessingException {
ChessBoardDto chessBoard = chessService.chessBoardFromDB();
String jsonFormatChessBoard = chessService.jsonFormatChessBoard(chessBoard);
model.addAttribute("jsonFormatChessBoard", jsonFormatChessBoard);
String currentTurn = chessService.currentTurn();
model.addAttribute("currentTurn", currentTurn);
chessService.updateRound(chessBoard, currentTurn);

ScoreResponseDto scoreResponseDto = chessService.scoreResponseDto();
@GetMapping("/chess/{roomId}")
public String chess(@PathVariable final Long roomId, final Model model) {
double whiteScore = chessService.whiteScore(roomId);
double blackScore = chessService.blackScore(roomId);
ScoreResponseDto scoreResponseDto = new ScoreResponseDto(whiteScore, blackScore);
Comment on lines +36 to +38

Choose a reason for hiding this comment

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

컨트롤러에서 서비스 호출이 처음보다 줄긴 했지만 요런 부분도 줄여볼 수 있을 것 같아요
서비스 메서드에 roomId를 넘기면 ScoreResponseDto를 반환해주는 느낌으로요 :)
(왠지 DTO때문에 이렇게 하신 것 같네요ㅎㅎ)

model.addAttribute("roomId", roomId);
model.addAttribute("score", scoreResponseDto);
return "chess";
}

private String makeNewGame() {
chessService.initialize();
return "redirect:/chess";
}
}
72 changes: 21 additions & 51 deletions src/main/java/chess/dao/PieceDao.java
Original file line number Diff line number Diff line change
@@ -1,75 +1,45 @@
package chess.dao;

import chess.dao.setting.DBConnection;
import chess.dto.request.MoveRequestDto;
import chess.dto.response.ChessResponseDto;
import org.springframework.beans.factory.annotation.Autowired;
import chess.dao.dto.response.ChessResponseDto;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

import javax.sql.DataSource;
import java.util.ArrayList;
import java.util.List;

@Repository
public class PieceDao extends DBConnection {
@Autowired
public class PieceDao {
private final JdbcTemplate jdbcTemplate;

public PieceDao(DataSource dataSource) {
public PieceDao(final DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}

public void initializePieceStatus(final String pieceName, final String piecePosition) {
String query = "INSERT INTO piece (piece_name, piece_position) VALUE (?, ?)";
try {
jdbcTemplate.update(query, pieceName, piecePosition);
} catch (Exception e) {
e.printStackTrace();
}
public void initializePieceStatus(final String pieceName, final String piecePosition,
final Long roomId) {
String query = "INSERT INTO piece (piece_name, piece_position, room_id) VALUE (?, ?, ?)";

Choose a reason for hiding this comment

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

시간이 되신다면 batch insert도 학습해보시면 도움되실 것 같아요
(적용하실 필요는 없습니다!!)

jdbcTemplate.update(query, pieceName, piecePosition, roomId);
}

public List<ChessResponseDto> showAllPieces() {
List<ChessResponseDto> pieces = new ArrayList<>();
String query = "SELECT * FROM piece";

try {
pieces = jdbcTemplate.query(
query, (rs, rowNum) -> new ChessResponseDto(
rs.getLong("id"),
rs.getString("piece_name"),
rs.getString("piece_position"))
);
} catch (Exception e) {
e.printStackTrace();
}
return pieces;
public List<ChessResponseDto> findAllPieces(final Long roomId) {
String query = "SELECT * FROM piece WHERE room_id=?";
return jdbcTemplate.query(
query,
(rs, rowNum) -> new ChessResponseDto(
rs.getLong("id"),
rs.getString("piece_name"),
rs.getString("piece_position")),
roomId
);
}

public void movePiece(final MoveRequestDto moveRequestDto) {
public void movePiece(final String source, final String target) {
String query = "UPDATE piece SET piece_position=? WHERE piece_position=?";
try {
jdbcTemplate.update(query, moveRequestDto.getTarget(), moveRequestDto.getSource());
} catch (Exception e) {
e.printStackTrace();
}
}

public void removeAllPieces() {
String query = "DELETE FROM piece";
try {
jdbcTemplate.update(query);
} catch (Exception e) {
e.printStackTrace();
}
jdbcTemplate.update(query, target, source);
}

public void removePiece(final MoveRequestDto moveRequestDto) {
public void removePiece(final String target) {
String query = "DELETE FROM piece WHERE piece_position=?";
try {
jdbcTemplate.update(query, moveRequestDto.getTarget());
} catch (Exception e) {
e.printStackTrace();
}
jdbcTemplate.update(query, target);
}
}
56 changes: 56 additions & 0 deletions src/main/java/chess/dao/RoomDao.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package chess.dao;

import chess.dao.dto.response.RoomResponseDto;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.stereotype.Repository;

import javax.sql.DataSource;
import java.sql.PreparedStatement;
import java.util.List;
import java.util.Objects;

@Repository
public class RoomDao {
private final JdbcTemplate jdbcTemplate;

public RoomDao(final DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}

public Long addRoom(final String roomName) {
KeyHolder keyHolder = new GeneratedKeyHolder();
String query = "INSERT INTO room (room_name, current_turn) VALUES (?, ?)";
jdbcTemplate.update(connection -> {
PreparedStatement ps = connection.prepareStatement(query, new String[]{"room_id"});
ps.setString(1, roomName);
ps.setString(2, "white");
return ps;
}, keyHolder);
return Objects.requireNonNull(keyHolder.getKey()).longValue();
}

public List<RoomResponseDto> findAllRooms() {
List<RoomResponseDto> rooms;
String query = "SELECT * FROM room";
rooms = jdbcTemplate.query(
query,
(rs, rowName) -> new RoomResponseDto(
rs.getLong("room_id"),
rs.getString("room_name"),
rs.getString("current_turn"))
);
return rooms;
}

public String findCurrentTurn(final Long roomId) {
String query = "SELECT current_turn FROM room WHERE room_id=?";
return jdbcTemplate.queryForObject(query, String.class, roomId);
}

public void changeTurn(final String nextTurn, final Long roomId) {
String query = "UPDATE room SET current_turn=? WHERE room_id=?";
jdbcTemplate.update(query, nextTurn, roomId);
}
}
Loading