-
Notifications
You must be signed in to change notification settings - Fork 122
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[1단계 - 웹 자동차 경주] 테오(최우성) 미션 제출합니다. (#63)
* chore: 자동차 경주 미션 코드 가져오기 * docs: 페어 프로그래밍 룰 작성 * docs: 추가로 구현할 기능 목록 작성 * chore: 도메인 테스트 코드 가져오기 * feat: 자동차 경주 진행 요청을 보낼 시 응답한다. * feat: 자동차 경주 진행에 대한 웹 요청을 받을 수 있다. * feat: 사용자 입력이 잘못되었을 때 예외를 반환한다. * feat: 자동차 경주 진행 결과에 대한 웹 응답을 전달할 수 있다. * feat: 참여자들의 정보를 이동 거리 기준 내림차순으로 정렬한다. * docs: 기능 구현 목록 추가 * feat: H2 의존성 추가 및 테이블 스키마 구현 * chore: 패키지 분리 * feat: 자동차 경주 게임 플레이 이력을 DB에 저장한다. * test: 테스트 패키지 구조 변경 * refactor: 모든 Spring component에 @Autowired 애너테이션 명시 * chore: 개행 일관성 유지 * refactor: 변경 가능성이 없는 클래스, 파라미터에 final 키워드 부여 * fix: Repository 클래스를 확장 가능하게 변경 * refactor: DAO에서 쿼리 별 메소드 분리 * refactor: Service가 추상적인 DAO에 의존하도록 변경 * test: 테스트 코드의 웹 환경 설정 통일 * refactor: 자동차 위치정보의 불필요한 형변환 제거
- Loading branch information
1 parent
07ad425
commit 2d18c57
Showing
33 changed files
with
1,087 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,90 @@ | ||
# jwp-racingcar | ||
|
||
# 페어 프로그래밍 룰 | ||
|
||
## 스위치 기준 | ||
|
||
- [x] 스위치 시간은 20분으로 한다. | ||
- [x] 내비게이터는 전자기기에 손을 대지 않는다. | ||
|
||
## 깃 컨벤션 | ||
|
||
- [x] 기능 목록에 있는 기능 단위로 커밋한다. | ||
- [x] 작동할 수 있는 기능 단위로 커밋한다. | ||
- [x] 커밋 메세지는 아래 키워드를 사용해 기능 목록 그대로 작성한다. | ||
- feat: 기능 구현을 완료했을 때 | ||
- refactor: 기능의 변화 없이 코드를 변경했을 때 | ||
- test: 테스트 코드만 작성했을 때 | ||
- chore: 패키지 변경 등 사소한 수정사항이 생겼을 때 | ||
- fix: 프로그램의 결함을 수정할 때 | ||
- docs: 문서를 수정할 때 | ||
|
||
## 코드 컨벤션 | ||
|
||
- [x] 모든 클래스는 `final` 혹은 `abstract` 이어야 한다. | ||
- [x] 모든 파라미터에 `final` 키워드를 붙인다. | ||
|
||
## 구현 계획 | ||
|
||
- [x] 구현은 다음과 같은 순서로 진행된다. | ||
1. Spring MVC 학습 | ||
2. 자동차 경주 미션 - 웹 요청/응답 구현하기 | ||
3. Spring JDBC 학습 | ||
4. 자동차 경주 미션 - DB 연동하기 | ||
5. 리팩토링 | ||
|
||
## 기타 룰 | ||
|
||
- [x] 미션 진행 2일차(수) 오후 4:00에 중간 회고를 진행한다. | ||
- [x] 최소한 2시간에 한 번은 쉬어야 한다. | ||
- [x] 커피챗을 최소 1회 진행한다. | ||
- [x] 집중이 안된다면 페어에게 솔직하게 이야기한다. | ||
|
||
--- | ||
|
||
# 추가로 구현할 기능 목록 | ||
|
||
## 웹 애플리케이션 구동 | ||
|
||
- [x] 자동차 경주 진행에 대한 웹 API를 구현한다. | ||
- [x] 자동차 경주 진행에 대한 웹 요청을 받을 수 있다. | ||
- [x] JSON 형태로 입력을 받는다. | ||
- [x] 참여자들의 이름을 입력받는다. | ||
- [x] 시도 횟수를 입력받는다. | ||
- [x] `/plays`로 `POST` 요청을 보낼 시 응답한다. | ||
- [x] 자동차 경주 진행 결과에 대한 웹 응답을 전달할 수 있다. | ||
- [x] JSON 형태로 전달한다. | ||
- [x] 우승자들의 이름을 전달한다. | ||
- [x] 참여자들의 정보를 전달한다. | ||
- [x] 모든 참여자들의 이름을 전달한다. | ||
- [x] 모든 참여자들의 이동 거리를 전달한다. | ||
- [x] 이동 거리의 내림차순으로 정렬 후 전달한다. | ||
- [x] 성공 시 STATUS CODE `200`를 반환한다. | ||
- [x] 실패 시 다음과 같은 STATUS CODE를 반환한다. | ||
- [x] 사용자 입력이 잘못되었을 때는 `400`을 반환한다. | ||
- [x] 정의되지 않은 경로로 요청하는 경우 `404`를 반환한다. | ||
- [x] 정의되지 않은 HTTP 메서드를 호출했을 때는 `405`를 반환한다. | ||
- [x] 서버 내부에서 에러가 발생했을 때는 `500`을 반환한다. | ||
|
||
## DB 연동 | ||
|
||
- [x] 자동차 경주 게임 플레이 이력을 DB에 저장한다. | ||
- [x] H2 Database에 저장된다. | ||
- [x] 저장되는 정보는 다음과 같다. | ||
- [x] 플레이 횟수 | ||
- [x] 플레이어 별 최종 이동 거리(이름, 최종 위치) | ||
- [x] 우승자 | ||
- [x] 플레이한 날짜/시간 | ||
|
||
# TO-STUDY | ||
|
||
- [ ] `@Transactional` 학습, 어떻게 사용할 수 있는지 | ||
- [ ] Mock 테스트의 테스트 범위와 원리 학습하기 | ||
- [ ] RequestBody + Model 같이 사용할 수 있는지 | ||
- [ ] RequestBody의 required가 어떻게 동작하는지 | ||
|
||
# 고민사항 | ||
|
||
- [ ] 이너클래스로 `CarDTO`를 둘지, 외부 객체로 뺼지 | ||
- [ ] 어느 기준으로 DTO를 생성해야 할지 | ||
- [ ] Controller - Service, Service - Dao 사이마다 DTO를 새로 정의해야 하는지(필드가 새롭게 추가되는 경우) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,20 +1,25 @@ | ||
plugins { | ||
id 'java' | ||
id 'org.springframework.boot' version '2.7.9' | ||
id 'io.spring.dependency-management' version '1.0.15.RELEASE' | ||
id 'java' | ||
id 'org.springframework.boot' version '2.7.9' | ||
id 'io.spring.dependency-management' version '1.0.15.RELEASE' | ||
} | ||
|
||
sourceCompatibility = '11' | ||
|
||
repositories { | ||
mavenCentral() | ||
mavenCentral() | ||
} | ||
|
||
dependencies { | ||
implementation 'org.springframework.boot:spring-boot-starter-web' | ||
testImplementation 'org.springframework.boot:spring-boot-starter-test' | ||
implementation 'org.springframework.boot:spring-boot-starter-web' | ||
implementation 'org.springframework.boot:spring-boot-starter-validation' | ||
testImplementation 'org.springframework.boot:spring-boot-starter-test' | ||
testImplementation 'io.rest-assured:rest-assured:4.4.0' | ||
|
||
implementation 'org.springframework.boot:spring-boot-starter-jdbc' | ||
runtimeOnly 'com.h2database:h2' | ||
} | ||
|
||
tasks.named('test') { | ||
useJUnitPlatform() | ||
useJUnitPlatform() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
package racingcar.controller; | ||
|
||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.stereotype.Controller; | ||
import org.springframework.web.bind.annotation.PostMapping; | ||
import org.springframework.web.bind.annotation.RequestBody; | ||
import org.springframework.web.bind.annotation.ResponseBody; | ||
import racingcar.dto.GameResultDto; | ||
import racingcar.dto.RacingGameRequestDto; | ||
import racingcar.service.RacingGameService; | ||
|
||
import javax.validation.Valid; | ||
|
||
@Controller | ||
public final class RacingController { | ||
|
||
private final RacingGameService racingGameService; | ||
|
||
@Autowired | ||
public RacingController(final RacingGameService racingGameService) { | ||
this.racingGameService = racingGameService; | ||
} | ||
|
||
@PostMapping(path = "/plays") | ||
@ResponseBody | ||
public GameResultDto playRacingGame(@Valid @RequestBody final RacingGameRequestDto racingGameRequestDto) { | ||
return racingGameService.playRacingGame(racingGameRequestDto); | ||
} | ||
} |
41 changes: 41 additions & 0 deletions
41
src/main/java/racingcar/dao/JdbcTemplateRacingGameDao.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
package racingcar.dao; | ||
|
||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.jdbc.core.JdbcTemplate; | ||
import org.springframework.jdbc.support.GeneratedKeyHolder; | ||
import org.springframework.jdbc.support.KeyHolder; | ||
import org.springframework.stereotype.Repository; | ||
import racingcar.dto.CarDto; | ||
|
||
import java.sql.PreparedStatement; | ||
import java.util.List; | ||
|
||
@Repository | ||
public class JdbcTemplateRacingGameDao implements RacingGameDao { | ||
|
||
private final JdbcTemplate jdbcTemplate; | ||
|
||
@Autowired | ||
public JdbcTemplateRacingGameDao(final JdbcTemplate jdbcTemplate) { | ||
this.jdbcTemplate = jdbcTemplate; | ||
} | ||
|
||
public Number saveGameResult(final String winners, final int trialCount) { | ||
final String sqlToInsertGameResult = "INSERT INTO GAME_RESULT (winners, trial_count) values (?, ?)"; | ||
KeyHolder keyHolder = new GeneratedKeyHolder(); | ||
jdbcTemplate.update(connection -> { | ||
PreparedStatement preparedStatement = connection.prepareStatement(sqlToInsertGameResult, new String[]{"id"}); | ||
preparedStatement.setString(1, winners); | ||
preparedStatement.setInt(2, trialCount); | ||
return preparedStatement; | ||
}, keyHolder); | ||
return keyHolder.getKey(); | ||
} | ||
|
||
public void savePlayerResults(final List<CarDto> racingCars, final Number gameResultKey) { | ||
for (CarDto carDto : racingCars) { | ||
String sqlToInsertPlayerResult = "INSERT INTO PLAYER_RESULT (name, position, game_result_id) values (?, ?, ?)"; | ||
jdbcTemplate.update(sqlToInsertPlayerResult, carDto.getName(), carDto.getPosition(), gameResultKey); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package racingcar.dao; | ||
|
||
import racingcar.dto.CarDto; | ||
|
||
import java.util.List; | ||
|
||
public interface RacingGameDao { | ||
|
||
Number saveGameResult(final String winners, final int trialCount); | ||
|
||
void savePlayerResults(final List<CarDto> racingCars, final Number gameResultKey); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package racingcar.domain; | ||
|
||
public class Car { | ||
|
||
private final CarName carName; | ||
private final Position position; | ||
|
||
public Car(String name, int position) { | ||
this.carName = new CarName(name); | ||
this.position = new Position(position); | ||
} | ||
|
||
public void move() { | ||
position.moveForward(); | ||
} | ||
|
||
public int comparePosition(Car otherCar) { | ||
return this.getPosition() - otherCar.getPosition(); | ||
} | ||
|
||
public boolean hasSamePositionWith(Car otherCar) { | ||
return comparePosition(otherCar) == 0; | ||
} | ||
|
||
public int getPosition() { | ||
return position.getPosition(); | ||
} | ||
|
||
public String getCarName() { | ||
return carName.getCarName(); | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package racingcar.domain; | ||
|
||
import racingcar.exception.CarNameBlankException; | ||
import racingcar.exception.CarNameLengthException; | ||
|
||
public class CarName { | ||
|
||
private static final int MAX_CAR_NAME_LENGTH = 5; | ||
|
||
private final String carName; | ||
|
||
public CarName(String carName) { | ||
validateCarName(carName); | ||
this.carName = carName; | ||
} | ||
|
||
private void validateCarName(String carName) { | ||
validateCarNameIsNotEmpty(carName); | ||
validateCarNameLength(carName); | ||
} | ||
|
||
private void validateCarNameIsNotEmpty(String carName) { | ||
if (carName.isBlank()) { | ||
throw new CarNameBlankException(); | ||
} | ||
} | ||
|
||
private void validateCarNameLength(String carName) { | ||
if (carName.length() > MAX_CAR_NAME_LENGTH) { | ||
throw new CarNameLengthException(); | ||
} | ||
} | ||
|
||
public String getCarName() { | ||
return carName; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package racingcar.domain; | ||
|
||
public class Coin { | ||
|
||
private int remaining; | ||
|
||
public Coin(int remaining) { | ||
this.remaining = remaining; | ||
} | ||
|
||
public void use() { | ||
if (isLeft()) { | ||
remaining--; | ||
} | ||
} | ||
|
||
public boolean isLeft() { | ||
return remaining > 0; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
package racingcar.domain; | ||
|
||
public interface NumberGenerator { | ||
|
||
int makeDigit(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package racingcar.domain; | ||
|
||
import racingcar.exception.PositionInvalidException; | ||
|
||
public class Position { | ||
|
||
private int position; | ||
|
||
public Position(int position) { | ||
validatePosition(position); | ||
this.position = position; | ||
} | ||
|
||
private void validatePosition(int position) { | ||
validatePositionIsNotNegative(position); | ||
} | ||
|
||
private void validatePositionIsNotNegative(int position) { | ||
if (position < 0) { | ||
throw new PositionInvalidException(); | ||
} | ||
} | ||
|
||
public void moveForward() { | ||
position++; | ||
} | ||
|
||
public int getPosition() { | ||
return position; | ||
} | ||
} |
Oops, something went wrong.