-
Notifications
You must be signed in to change notification settings - Fork 122
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
[1단계 - 웹 자동차 경주] 히이로(문제웅) 미션 제출합니다. #53
Conversation
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.
안녕하세요 히이로 👋
구현 잘 해주셨습니다.
몇 가지 코멘트 남겼으니 다음 단계 진행하면서 확인 부탁드려요~
|
||
import racingcar.dto.RacingGameResultDto; | ||
|
||
public interface RacingCarRepository { |
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.
전반적으로 모든 계층에 인터페이스가 적용되어있는데 이렇게 구현해주신 이유가 있을까요?
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.
이번 미션을 진행하면서 기존에 구현했던 console 기반 프로그램을 제거하는 것이 아니라 그대로 놔둬야 한다는 것을 알게되었습니다. 이후 console과 web 두 플랫폼을 모두 지원하는 방향으로 프로그램을 구현하게 될 수 있을 가능성이 있다고 판단했습니다. 그래서 어떤 플랫폼이든 service 레이어에서 비즈니스 로직을 수행하고 repository 레이어에서 DB에 데이터를 저장하거나 조회하는 기능을 수행할 것이라고 생각해서 인터페이스화를 미리 해뒀습니다.
그런데 다시 2단계 요구사항을 읽어보니 콘솔 기반 프로그램에서 DB를 따로 연동하지는 않더라구요... 😭 결과적으로 의미 없는 인터페이스화가 적용된 것 같습니다. 해당 부분은 2단계 진행하면서 따로 필요성이 생기지 않는 한 삭제하도록 하겠습니다!
public class WebRacingCarRepository implements RacingCarRepository { | ||
|
||
private final RacingGameDao racingGameDao; | ||
private final CarDao carDao; |
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.
Service와 Repository 사이에서 주고 받는 데이터를 관리하는 책임을 지는 객체를 Dto로 설계하는 것이 적합할지, 만약 적합하다면 그 네이밍은 어떻게 해서 Controller ~ Service 간 사용되는 Dto와 분리시킬 수 있을지 질문드립니다.
Repo가 Request, Response를 알 필요는 없겠죠.
먼저 Repo랑 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는 뚜렷한 책임을 지지 않고 Dao의 기능만을 수행하는 계층이 되어버렸습니다.
왜 이렇게 되었는지에 대해서 생각하기 이전에 repository와 Dao의 차이에 대해서 학습을 진행했습니다.
- Dao는 Data Access Object라는 이름을 가지는 만큼 DB에 접근하여 데이터를 저장하고 조회하는 기능을 수행합니다.
- DB에 날릴 쿼리를 작성하고 실제로 쿼리를 날려서 작업을 수행하는 것등의 역할을 수행합니다.
- 일반적으로 DB 테이블 하나 당 하나의 Dao가 1:1 맵핑되어 해당 테이블에 대한 DB 작업을 Dao가 수행합니다.
- repository는 service 레이어에서 비즈니스 로직을 수행하는데 필요한 데이터(도메인)를 찾아서 반환하는 작업을 수행합니다.
- 여기에서 데이터를 찾는 작업은 반드시 DB를 통해서 이뤄져야 하는 것은 아니라는 것을 이번 학습을 통해 알게 되었습니다.
- 그래서 repository는 Dao를 이용해서 DB에 접근해 service 레이어가 필요로 하는 데이터를 넘겨줄 수 있지만 그 외의 방법으로도 데이터를 저장하거나 조회할 수 있습니다.
- 또한 하나의 반환 데이터, 즉 도메인 객체를 반환하기 위해 여러 Dao를 사용할 수 있습니다.
- 중요한 것은 repository가 service에서 사용하는 도메인 객체를 어디에서 어떻게 가져오고 저장하는지는 캡슐화가 되어야 한다는 것입니다.
- 결과적으로 repository에서 service로 데이터를 반환할 때에는 비즈니스 로직을 바로 수행할 수 있는 도메인 객체가 생성되어 반환되어야 합니다.
- 이러한 차이 이전에 repository와 Dao 모두 계층형 아키텍처에서 Persistence Layer에 속한다는 것을 알게 되었습니다.
- Persistence 즉 영속성은 데이터들이 영구저장될 수 있는 특징을 갖고 있는지를 나타내는 성질입니다.
여기까지 학습하고 나니 repository의 개념 자체를 Persistence 레이어와 혼동하고 있었다는 것을 알게 되었습니다.
- 그래서 repository 내부에 Dao를 위치시키고 내부에서 기능을 수행하도록 구현했던 것 같습니다. 또한 이번 미션 1단계에서 도메인 로직은 굉장히 간단했습니다. 그러다 보니 Dao와 Repository가 수행하는 기능의 차이가 거의 없어지게 되었습니다.
- 이런 경우 굳이 repository를 두지 않고 바로 Dao를 통해 데이터를 저장하고 조회할 수 있도록 할 수 있을 것 같습니다.
하지만 2단계 미션 요구사항 중 이력조회 기능을 수행해야 할 때에는 두 개 이상의 Dao를 이용하여 데이터를 조회해야 할 일이 생길 것으로 예상됩니다.
- 이런 경우에는 repository 객체를 두어 여러 Dao를 통해 Service 계층이 원하는 데이터를 완성시켜 넘겨줄 수 있는 역할을 수행하게 할 수 있을 것 같습니다.
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.
이러한 관점에서 다시 코드를 리팩토링한다면 Dto가 아니라 Service 계층과 Repository 계층 간에는 domain 객체 혹은 데이터를 주고 받고록 구현하고, Repository는 Dao에게 필요한 값을 꺼내서 넣어주는 책임을 수행하도록 구현할 것 같습니다.
@Repository | ||
public class H2RacingGameDao implements RacingGameDao { | ||
|
||
@Override | ||
public int save(int count) { | ||
String sql = "INSERT INTO RACING_GAME(count) VALUES (?)"; | ||
|
||
try (Connection connection = ConnectionProvider.getConnection(); |
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.
스프링의 기능를 활용해서 구현해보시면 좋을 것 같아요.
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단계 미션을 먼저 진행했었어서 JdbcTemplate 기능을 활용하지 못했네요... 2단계 때 수정해보도록 하겠습니다!
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
public class CarTest { |
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.
기존에 Domain을 미리 구현해둔 상태였기 때문에 새로운 테스트 코드를 작성한다고 하면 어떤 부분에 대해 작성해야 하는지 감이 잡히질 않았습니다.
억지로 테스트를 구현할 필요는 없습니다. 이번 미션은 TDD가 필수도 아니었구요.
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
public class ConsoleRacingCarController { |
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.
Controller, Service, Repository 등 Layered Architecture 구조를 사용하게 되면서 각 계층에 대해 테스트를 어떻게 진행해야 하는지 큰 방향에 대한 감을 잡고 싶습니다.
그리고 테스트에 DB의 영향을 우회하기 위해 테스트 객체들을 Mocking해야 할 것 같은데 이런 부분도 어떤 방식으로 진행하면 좋을지 여쭤보고 싶습니다.
�이 글을 읽어보시면 좋을 것 같아요. 이 글에서 키워드를 잡고 하나씩 공부해보시면 좋을 것 같네요. 다만, 아마 다음 미션 쯤에 다룰 내용도 있을 거라 이번 미션에서는 그냥 가볍게 알아두시면 좋을 것 같습니다.
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.
레퍼런스 감사합니다 범블비! 덕분에 아주 약간은 감을 잡은 것 같기도 하네요 ㅎㅎ... 일단 이번 미션의 주된 내용은 아니니 우선순위를 조금 낮춰서 가져가 보겠습니다!
public class H2CarDao implements CarDao { | ||
|
||
@Override | ||
public void save(int gameId, List<CarDto> carDtos) { |
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에 도메인을 넘겨도 되는가?
저는 View에 도메인을 넘겨도 되는가와 이 질문은 동일하다고 생각해요.
단순히 도메인에서 데이터만을 뽑아내는 거면 도메인을 넘겨도 괜찮다고 생각합니다.
도메인에서 로직을 호출한 결과값이 필요하거나 여러 값들이 합쳐진다면 DTO를 만드는 편이 낫겠죠.
DB를 다루는 코드와 domain은 분리해야 각종 변경으로부터 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.
다만 현재 구현 방식이 이 철학을 잘 구현했는지는 조금 의문이 들기도 하네요. 레이어를 여러 층으로 나누는 건 분명한 이유가 필요합니다. 나눠야하는 분명한 이유를 들지 못한다면 합치는 게 더 나은 방식이라는 생각을 해보시면 좋을 것 같아요.
위에서 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.
2단계에서 해당 부분들이 잘 해결된 것 같네요 👍
안녕하세요 범블비, 히이로입니다!
스프링에 대한 기본적인 배경이 아예 없는 상태에서 진행하다 보니 미션을 마무리하고 나서도 굉장히 찝찝한 기분이 드네요... 😂
이번 미션에서는 기존 콘솔 도메인 로직 수행에 필요한 값들을 웹 페이지에서 받아오거나, 결과값을 외부로 반환하는 과정의 코드를 구현하는데 집중했습니다. 해당 과정을 진행하면서 들었던 몇 가지 의문들이 있었는데 아래에 같이 정리해서 보내드립니다.
리뷰 잘 부탁드립니다! 🙇