GitHub 저장소
STEP 2 목표
변경 사항
개선할 점
배운 점
궁금한 점
나가며
📆기간 : 2023.05.12 ~ 2023.05 22
들어가며
1단계를 우가, 쥬니 랑 함께 3인 페어로 급하게 끝낸 이후 2단계를 시작하였다. 2단계는 경로 조회 기능이었다. 외부 라이브러리를 사용하여 역과 역 사이의 최단 거리를 조회하고 이에 따라 요금을 계산하는 기능을 구현하였다.
GitHub 저장소
[2단계 - 경로 조회 기능] 오션(김동해) 미션 제출합니다. by donghae-kim · Pull Request #117 · woowacourse/jw
안녕하세요 배럴 ! 오션입니다!🌊 1단계 머지 이후로 원래 코드를 더 리팩토링하고 2단계 기능 구현을 해보았습니다! 리뷰 반영 과거 코멘트에서 map을 돌려 여러번 db조회를 하는 것은 좋지 않다
github.com
STEP 2 목표
- 데이터 베이스 분리
- 외부 라이브러리를 잘 사용하기
변경사항
여러번의 DB 쿼리문 삭제
sections.stream()
.map(it -> {
Station startStation = it.getStartStation();
Station endStation = it.getEndStation();
Long startStationId = stationDao.findIdByName(startStation.getName());
Long endStationId = stationDao.findIdByName(endStation.getName());
return new SectionEntity(lineId, startStationId, endStationId, it.getDistance().getDistance());
})
.collect(Collectors.toList());
나는 다음과 같이 sections List를 돌면서 map에서 쿼리를 모두 날려서 값을 가져오는 방식으로 구현했었다. 이렇게 구현했던 이유는 사실 조금 급했기도 했었고, 전체 값을 가져와서 조립하는 것보다 각각의 값을 가져오는 것이 조금 더 깔끔해 보였다. 하지만 map을 돌려 여러번 db조회를 해오는 방법은 그 횟수가 한번에 조회해오는 것보다 몇십배는 더 많다는 것을 깨닫고 수정하게 되었다.
Map<String, Long> stations = stationDao.findAll().stream()
.collect(Collectors.toMap(StationEntity::getName, StationEntity::getId));
이와 같은 방식으로 한번에 조회를 해와서 사용하였다.
확실히 그 횟수가 줄어든 것을 볼 수 있었다.
변환로직 분리
나는 서비스에서 entity <-> domain간 변환 로직이 많았다. 이 변환 로직이 비즈니스로직과 섞여 계속해서 더러움을 주고 있었다. 나는 이 점이 너무 불편했다.
처음에 생각했던 것은 각 객체간 변환을 domain에서 하는 것은 어떨까 ? 였다. 하지만 이는 domain이 결국 entity를 알아야 한다는 점에서 기각하였다.
두번째 생각은 mapper클래스를 두어 변환을 위한 클래스를 사용하면 어떨까? 였다. 이는 좋았으나 domain에 id식별값을 사용하면서 domain과 entity를 합치고 entity를 제거하면서 기각하였다.
entity 삭제
나는 지금까지 entity를 영속 계층간 dto로써, table과 1:1매핑되는 객체로써 사용을 해왔다. 이를 분리해서 가져가는 것에 대한 장점으로는 도메인 변경이 유연하여 확장하기에 용이한점, db의 변화가 entity에 그친다는 점, 계층간 분리가 잘 되는 점이 있어 사용하였다. 하지만 단점으로는 변환로직이 너무 많아서 불편한 단점이 많았던 것 같다. 그래서 복잡하고 domain-entity간 추가적인 코드 작성이 많았던 것 같다. 그래서 domain에 id식별값을 사용하면서 entity를 제거해 보았다. 이
렇게 하다보니 dao쪽에도 domain객체가 넘어갔는데 이를 repository를 이용하면 좋을 것 같다. 그래서 domain과 repository는 같은 계층으로 관리하고, dao에는 직접 db와 매핑되는 객체를 두어 관리하면 좋을 것 같다.
이는 domain 계층에 Repository 인터페이스를 두고 영속성 계층에 Repository 구현체를 두면서 계층간 분리를 하는 방식을 의미한다.
도메인에 id 값 추가
이는 고민했던 것에 대해 새롭게 글을 작성해 보았다.
Prototype 사용 했었다.
Graph가
private final WeightedMultigraph<String, DefaultWeightedEdge> graph = new WeightedMultigraph<>(DefaultWeightedEdge.class);
라는 필드값을 가지고 있어서 @Scope("prototype) 로 설정을 하였다.
scope를 다르게 가져간 이유로는 @component 클래스가 필드값을 가지고 있기에 싱글턴으로 하면 안된다 라고 생각하였기 때문이다. 그래서 prototype을 사용하였고 그렇기에 상태를 가지고 있어도 되지 않나? 라는 생각을 하게 되었다. 그리고 Configuration bean대신 component를 사용한 이유는 component를 활용해서 클래스를 빈 등록하면 더 편하게 사용할 수 있을 것 같아서 였다.
하지만 prototype bean은 제대로 관리되지 않으면 종료되지 않고 생성만 계속되는 객체만 많아져서 오히려 prototype으로 설정하는 것보다 기존에 객체로 만들어 주는 방향이 편리하다고 생각하게 되었다 . .
개선할 점
- 코드를 작성함에 있어서 급하게 한 것이 조금 많은 것 같다. 여유를 가지고 천천히 진행해도 좋을 것 같다.
- 도메인을 먼저 짜고 설계를 하다보니 레벨1을 생각하게 되어 사용하지 않는 도메인이 발생했다.
- DB에 맞춰서 도메인을 구성할 필요는 없지만 어느정도 DB에 필요한 값에 따라서 약간은 변형될 수 있다고 생각도 해야할 것 같다.
배운 점
- 도메인 중심 설계
- DB 쿼리 비용
- 스프링을 사용한 테스트
- Auto Configuration
- 스프링에서 외부 라이브러리 사용
궁금한 점
이번 미션에서는 2단계에서 궁금한 점들 보다는 1단계에 궁금한 것들이 많은 것 같다. 1단계에 정리하였다.
나가며
이번 지하철 미션은 spring에 도메인을 추가하면서 어려움이 최고를 달했다. 레벨 2 에서 레벨 1의 지식을 함께 사용하려고 하다 보니 너무 복잡해졌다. 도메인의 난이도 자체는 어렵진 않았으나 이를 사용함에 있어서 많은 어려움이 있었던 것 같다. 잘 해보고 싶었는데 점점 코드가 복잡해지는 것을 느꼈다. 최대한 깔끔하게 짜고 싶었는데 어려웠지만 잘해냈다.
'우아한테크코스' 카테고리의 다른 글
[우아한테크코스 5기] 레벨2 레벨인터뷰 회고 (0) | 2023.06.13 |
---|---|
[우아한테크코스 5기] 장바구니 협업 학습로그 (0) | 2023.06.13 |
[우아한테크코스 5기] 지하철 1단계 학습로그 (1) | 2023.05.21 |
[우아한테크코스 5기] 장바구니 2단계 학습 로그 (1) | 2023.05.07 |
[우아한테크코스 5기] 장바구니 1단계 학습 (2) | 2023.04.30 |