-
Notifications
You must be signed in to change notification settings - Fork 464
[4단계 - 체스] 미아(이종미) 미션 제출합니다. #778
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
Conversation
jinyoungchoi95
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
안녕하세요 미아 😄
4단계 잘 구현해주셨네요. 몇가지 코멘트 남겨두었으니 확인부탁드려요.
궁금하거나 고민이 되는 부분이 있으시다면 언제든 pr 코멘트 또는 dm으로 요청 부탁드립니다.
감사합니다 🙇♂️
| 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") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
runtimeOnly는 어떤 기능으로 동작할까요? implementation, compileOnly, api와 무슨 차이가 있을까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
compileOnly은 컴파일에 의존하는 모듈들을 컴파일패스에 설정하고, runtimeOnly는 런타임에 의존하는 모듈들을 런타임패스에 설정하는 것이 좋습니다. 따라서 runtimeOnly는 DB 의존성에 사용하는 것이 좋습니다.
implementation과 api의 차이는 잘 몰라서 추가로 찾아보았습니다!
implementation과 api는 컴파일패스, 런타임패스에 모두 저장하는데, api는 전이 의존성을 허용하고 implementation은 허용하지 않습니다. implementation이 컴파일이 빠르고 재컴파일 횟수가 줄어들어 더 권장됩니다. 또한 전이 종속성에 실수로 빠지지 않습니다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
전이 종속성에 실수로 빠지지 않습니다
api의 경우 해당 문제때문에 의존성의 방향이 한단계 밑으로 내려가서 사용을 지양하는 편이긴해요. 대부분의 예제가 그런 이유로 implementation을 예제로 뿌려주기도하구요.
docker/db/mysql/init/ddl.sql
Outdated
|
|
||
| CREATE TABLE chess_board | ||
| ( | ||
| chess_board_id BIGINT NOT NULL AUTO_INCREMENT, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| chess_board_id BIGINT NOT NULL AUTO_INCREMENT, | |
| id BIGINT NOT NULL AUTO_INCREMENT, |
chess_board의 id인데 굳이 명시할 필요 없이 id로도 충분하지 않을까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
id로 충분할 것 같아 변경하였습니다!
docker/db/mysql/init/ddl.sql
Outdated
| CREATE DATABASE IF NOT EXISTS `chess` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci; | ||
| CREATE DATABASE IF NOT EXISTS `chess_test` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
테스트용 database 세팅값은 같은 파일내에 있을 경우 가독성을 떨어뜨리는 등 혼란을 주기 때문에 별개의 파일로 분리하는것이 좋습니다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
분리하였습니다!
docker/db/mysql/init/ddl.sql
Outdated
| PRIMARY KEY (file, `rank`, chess_board_id) | ||
| ); | ||
|
|
||
| CREATE TABLE turn |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
turn정보는 chess_board에 들어가도 되었을텐데 테이블로 분리할 필요가 있을까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
제가 도메인 설계를 할 때 ChessBoard가 Turn을 가지고 있지 않아서 따로 분리하였습니다. Java 어플리케이션의 도메인 구조와 DB 테이블 관계가 다른것이 어색하다고 느꼈습니다.
하지만 추후 GameResult를 도입하면서 다른 도메인 클래스(GameResult와 ChessBoard)여도 함께 저장하고, DTO를 사용하여 조회하였습니다. 어차피 일대일 대응 관계이므로 함께 조회하는게 더 효율적인 것 같았기 때문입니다! 그래서 chess_board에 turn 정보도 넣도록 수정하였습니다 :)
추가로 질문은, 실제로 오리가 현업에서 개발할 때 서로 다른 도메인 클래스이고, Composition 과 같은 관계도 없는데 같은 테이블로 저장하는 경우가 종종 있으신가요?! 저는 처음에 어색하다고 느껴서 질문드려요! 한편으로는 제가 도메인 설계를 잘못한 것 같기도 합니다 🥲
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
사실 데이터베이스에 저장되는 매커니즘과 객체지향의 매커니즘이 다르기때문에 그 둘의 괴리감이 발생하는 경우는 이상하지 않다고 생각해요. 데이터베이스는 연관관계의 주인이 fk로 한쪽이 가지고 있고, 상속도 사용할 수 없는데 반해 객체지향에서는 서로가 서로의 참조값을 가질 수 있고, 상속도 사용할 수 있으니까요.
설계할때는 "편하게"하기 위해서 데이터베이스를 먼저 설계하고 그게 맞게 domain 객체를 매핑만 하는 형태의 소위 데이터베이스 중심적인 설계를 하는데 말씀하신 케이스는 도메인에서 문제를 풀어나갈때 발생하기도 하는 케이스인 것 같아요. 너무 그 둘의 괴리감이 있다고 한쪽에 딱맞추어야 정답이다를 고민하지 않으셔도 좋을 것 같습니다 :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
그렇군요. 객체 설계와 DB 설계를 잘 맞춰나가는게 어렵네요 🥲
| @@ -0,0 +1,31 @@ | |||
| package chess.repository.utility; | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
자바에서는 관습적으로 utility라는 패키지명이 아닌 util, utils 패키지네임을 쓰는 편이에요.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
util 관련 관습은 처음 알았네요..! 반영하였습니다 👍
| @BeforeEach | ||
| void setUp() { | ||
| dataBaseCleaner.truncateTables(); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
beforeEach를 하면 현재 테스트되는 메소드에 영향이 안가는 것은 맞지만 테스트가 다 끝나고나서는 데이터가 남아있을 것 같네요. afterEach가 맞지 않을까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
그렇군요 afterEach로 변경하였습니다!
|
|
||
| public ChessBoard createOrGetInitialChessBoard() { | ||
| Optional<ChessBoard> chessBoard = chessBoardDao.findLatest(); | ||
| return chessBoard.orElseGet(this::createInitialChessBoard); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
orElseGet 👍
| turnDao.delete(chessBoardId); | ||
| turnDao.save(chessBoardId, nextTurn); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
update를 해도되지 않나요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
맞습니다..! 리팩토링하면서 turn을 chess_board에 저장하며 update 쿼리로 변경하였습니다.
| () -> assertThat(chessBoard).isNotEmpty(), | ||
| () -> chessBoard.ifPresent(actual -> assertThat(actual.getBoard()).hasSize(64)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(반영하지 않으셔도 됩니다.) 개인적으로는 when에서 Optional을 반환하지 않고 get()으로 꺼내어 사용하는 편이긴 합니다 :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
다른 테스트에서는 get()을 사용했어서 이 테스트도 get()을 사용하도록 변경하였습니다 ! 위에서 empty를 확인해주어서 ifPresent가 필요 없어 보이기도 하네요 ..!
| import java.sql.SQLException; | ||
| import java.util.List; | ||
|
|
||
| public class DataBaseCleaner { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
truncate를 사용한 이유가 무엇일까요? 단점은 없을까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
테스트를 할 때마다 데이터를 지우는 방법으로 drop과 trncate를 생각했는데, 매번 테이블을 드랍하고 생성하는 것보다 truncate가 간편하여 사용하였습니다..! 지금 방식의 단점은 쿼리를 문자열로 저장해놓아서 테이블에 변경 사항이 있다면 함께 변경해주어야 하는 것입니다. truncate 자체의 단점은 떠오르지 않는데, 오리가 생각하는 다른 단점이 있을까요 ??
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
말씀해주신 내용들이 맞아요. rollback이 안되는 문제가 있는데 현재 케이스에선 큰 문제가 없을 것 같아요 :)
|
안녕하세요 오리 ! 마감 직전에 다시 리뷰 요청 드리네요 🥲 몇 가지 질문과 답변을 코멘트에 남겨놓았습니다! |
jinyoungchoi95
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
안녕하세요 미아 😄
피드백 잘 반영해주셨네요. 4단계에 필요한 학습과 구현을 충분히 진행하신 것으로 보여 머지하려고하는데요. 남겨둔 코멘트에 궁금하신 것이 있을까하여 request change드려요.
궁금하거나 고민이 되는 부분이 있으시다면 언제든 pr 코멘트 또는 dm으로 요청 부탁드립니다.
감사합니다 🙇♂️
| CREATE TABLE piece | ||
| ( | ||
| file VARCHAR(10) NOT NULL, | ||
| `rank` INT NOT NULL, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(반영하지 않으셔도 됩니다.) 예약어는 현재처럼(`를 붙여야하는) 제약사항이 존재하기 때문에 가능하면 db에서 네이밍은 예약어를 피하려는 편입니다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
level2에서 db를 많이 활용할 때 예약어를 피해서 설계해보도록 하겠습니다! 감사합니다 😊
docker/db/mysql/init/ddl_test.sql
Outdated
| @@ -0,0 +1,26 @@ | |||
| GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost'; | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
root보다는 user에게 권한을 주고 user를 사용하는 것이 좋았을 것 같네요 :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
db connection과 ddl 실행을 root 권한이 아닌 user권한으로 실행하도록 변경하였습니다!
| @@ -0,0 +1,147 @@ | |||
| package chess.repository.dao; | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
다음단계에서 진행할 내용이지만, 엄밀히 따지면 repository 내부에 dao가 있는 구조 현재는 어색할 수 있어요. repository 클래스가 존재하는 것도 아니기 때문에 repository라는 네이밍이 어디서왔고 왜쓰였느냐가 명확하지 않은 것 같아요. 해당 내용은 2레벨때 천천히 배우셔도 좋을 것 같아 링크만 드립니다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DAO is an abstraction of data persistence. However, a repository is an abstraction of a collection of objects
dao와 repository가 비슷한 개념인 줄 알았는데 잘못사용했네요 🥲 패키지명은 dao로 변경하였습니다! 감사합니다 🙇🏻♀️
|
안녕하세요 오리! 남겨주신 리뷰를 바탕으로 약간의 수정을 진행했습니다. 추가적으로 궁금한 것은 없습니다 :) |
jinyoungchoi95
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
안녕하세요 미아 😄
1레벨동안 고생많으셨습니다. 다음 레벨도 화이팅하세요 :)
감사합니다 🙇♂️
안녕하세요 오리! 4단계도 잘 부탁드립니다 🙇🏻♀️
추가된 기능과 게임 실행 방법은 리드미에 적어놓았습니다. 또한
docker/db/mysql/init/ddl.sql에 DDL을 첨부해놓았습니다. (도커를 실행하면 따로 DDL을 실행하지 않아도 됩니다.)DB 다이어그램
추가 구현 기능
ChessGameService를 생성하였습니다.TIE)를 다뤄야 했는데, 실제 게임 규칙을 모두 적용하기엔 시간이 부족하여 플레이어의 합의 무승부만 다루었습니다. 유저가 무승부를 입력하면 무승부로 저장됩니다.리팩토링된 부분
ChessBoardGenerator삭제저번 리뷰를 바탕으로 '추상화의 목적이 불분명하고, 불필요한 의존도가 생겼다'라고 생각하여 삭제하였습니다.
GameState구현체 추가기능이 늘어나면서
Run이 맡는 역할이 커져서Move와Summarize를 추가하였습니다.