들어가면서
레벨2 세번째 미션은 지하철 미션이었다. 지하철의 노선과 역 정보를 관리하는 API를 설계하고 구현하는 것이 이번 목표였다. 지하철 노선도라는 복잡한 도메인으로 웹 서비스 API를 설계하는 것이 이번 미션의 취지였다고 한다. 그만큼 도메인을 설계하는 과정에서 고려해야할 사항들이 굉장히 많아서 난이도도 높게 느껴졌고 시간이 많이 부족했다.
이번 미션의 페어는 이리내였다. 이리내는 긴 시간 페어 프로그래밍에도 코드에 대한 집중력을 잃지 않는 크루였다. 덕분에 미션을 진행하면서 고려해야 할 사항들이 너무 많아서 주기적으로 흐름을 놓칠때마다 다시 제자리를 찾아가는데 도움을 많이 받았다.
지난번 장바구니 미션에서 도메인의 설계가 외적인 부분에 영향을 받는 것이 굉장히 맘에 걸렸었다. 그래서 이번에는 반드시 도메인을 레벨1때처럼 TDD로 객체지향을 최대한 지키면서 구현한 이후에 컨트롤러, 서비스, 레포지토리를 설계해야겠다고 다짐했고 이를 수행했다. 그러나 현실적으로 이를 다 지키면서 미션 데드라인을 지키기가 너무 힘들었는데 어떤 부분들이 힘들어서 타협을 할 수 밖에 없었는지 회고해보고자 한다.
공부한 개념들
(포스팅이 작성되는 대로 업로드할 예정입니다.)
셀프 회고
[이번 미션은 왜 어려웠나?]
이번 미션 step1 피드백 시간에 이번 미션을 진행하면서 객체, 테이블, API 등을 어떻게 설계를 진행했는지와 그렇게 설계한 이유를 정리하고 다른 크루들과 얘기할 수 있는 시간을 가졌었다. 페어인 이리내와 정신없이 지나갔던 3일을 다시 되짚어볼 수 있는 시간이었다. 이번 미션의 주된 고민들을 정리할 수 있었기에 해당 내용들을 정리해보고자 한다.
<객체 설계 방법과 그 이유>
구간을 의미하는 Section이라는 객체를 만들었다. Station은 역 하나에 대한 객체이지만, 우리가 다뤄야 하는 것은 역과 역 사이의 관계였다. 그래서 역 간의 관계를 다룰 수 있는 Section이라는 객체를 설계했다. Section 객체에게 역과 역 사이의 거리와 노선 정보를 저장하고 관리할 수 있도록 했다.
그리고 이러한 Section들을 모아서 관리하는 Sections를 설계했다. Sections는 Station을 key로 두고, 이 Station을 포함하는 Section들의 리스트를 value로 관리하는 map을 가지는 일급컬렉션이다. Sections를 통해 전체 Section을 관리하고자 했다. Section의 line 정보를 이용해 원하는 노선의 구성 Station들을 순서대로 정렬해서 반환하는 기능 등을 Sections의 책임으로 부여했다.
<테이블 설계 방법과 그 이유>
Station과 Line은 기본적으로 설계가 되어있던 테이블이었다. 도메인 로직상에서는 이를 값 객체로 사용하였기 때문에 별다른 변경없이 그대로 사용하게 되었다. 새롭게 추가한 테이블로는 Section 도메인에 대한 테이블이 추가되었다. Section 테이블이 구간에 속한 역의 정보들(Station)과, 구간의 거리(Distance), 구간이 속한 노선(Line)에 대한 정보를 갖게 하였다. 우리의 도메인 로직상 Section이 주로 사용되는 최상위 도메인 객체 단위였기 때문에 Section을 통해서 대부분의 로직이 수행 가능했어야 했다. 이러한 이유로 노선을 완벽하게 조립하기 위해 필요한 line_id를 Section 테이블에 추가하게 되었다.
<API 설계 방법과 그 이유>
처음에 API 설계부터 시작을 하였는데, 도메인 로직에 대한 큰 그림이 그려지지 않아서, URI의 계층적 설계나 API의 기능 분리가 어려웠다. 일단은 직관적으로 봤을 때, 노선 안에 역이 속해있기 때문에 lines의 하위 계층으로 stations가 있는 URI를 설계했다. 하지만 도메인을 완성한 후에 다시 돌아왔을 때, 우리가 처음에는 생각하지 못했던 section이라는 도메인이 생겼기 때문에, URI 설계와 API 기능 분리를 다시 해야했다.
'lines/{lineId}/sections' 의 계층 구조를 가지도록 URI를 다시 설계하였다. 이를 통해 노선 별로 속하는 구간들의 계층 구조를 나타내고자 했다. 또한 `/line-stations/{lineId}`의 URI를 통해 노선 별로 구성 역들에 대한 정보를 표현했다. 기존 뼈대코드로 작성되어 있던 'lines/...' 와 'stations/...' URI는 기본적인 노선과 역 정보를 나타내는 것으로 유지했다. 사실 직관적으로는 특정 노선을 구성하는 역들을 조회할 때 사용해야 할 URI의 최상위 계층이 lines가 되어야 할 것이라고 생각했다. 그러나 기본적인 노선에 대한 정보와 노선을 구성하는 역들에 대한 정보를 분리하기 위해 line-stations라는 최상위 URI 계층을
새로 만들어 줄 수 밖에 없었다. 특히나 이부분은 도메인의 로직이 복잡해지면서 더 어려워졌다고 생각이 들었는데 이런 상황에서 URI의 RESTful함을 어떻게 유지할 수 있을지에 대해서 앞으로 고민해봐야할 것 같다.
<설계 과정에서 가장 많이 고민했던 부분>
이번 페어 미션을 진행하면서 최우선순위로 진행했던 부분은 레벨1과 같은 도메인의 설계였다. spring 프레임워크의 기능이 개입되지 않은 순수한 도메인을 설계하고 그 외적인 부분들이 도메인에 맞추도록 하는 방식으로 진행하고 싶었다. 이러한 목적을 가지고 도메인 객체를 설계하고 구현하는 과정은 TDD를 통해 어느정도 잘 진행할 수 있었다.
문제는 어느 정도 도메인을 완성하고 계층형 구조 설계를 고려하기 시작했을 때부터 발생했다. 일단 처음에 설계했던 API의 URI들이 도메인과 전혀 맞지 않았다. 처음 API를 설계할 때는 노선을 나타내는 line을 최상위 URI 계층으로 생각하고 진행했었으나 실제로 도메인 로직을 수정하면서 설계하다보니 많이 변경되어야 했다. 그 이유가 뭔지에 대해 생각해보니 도메인 로직 내부에서 line이 아니라 section을 주된 객체 단위로 사용하다 보니 처음의 설계가 많이 바뀌었던 것 같다. 또한 위에서 언급했던 내용처럼 line에 대한 기본적인 정보를 다루는 URI와 line에 속한 station들의 정보를 다루는 URI를 분리하는 등의 작업으로 인해서도 많이 바뀌었다.
가장 힘들었던 부분은 다음과 같이 서비스에서 비즈니스 로직을 수행하는 순서를 지키고 싶었으나 시간 상 문제로 굉장히 날리면서 개발을 진행하게 되었던 것이다.
- 최대한 서비스에서 Persistence 로직을 통해 기존 도메인 객체들을 불러온다.
- 해당 도메인 객체들을 통해 도메인 로직을 수행한다.
- 도메인 로직을 수행한 뒤의 도메인 객체들의 상태를 Persistence 로직을 통해 저장한다.
실제로 미션을 마무리하는 단계에서 도메인 로직을 수행 완료한 도메인 객체 상태를 다시 DB에 저장하는 과정을 엄격하게 지키기가 어려웠다. 도메인 로직으로 완전하게 수행하는 것보다 DB에서 직접 가져온 데이터로 서비스 계층에서 바로 로직을 수행하는 것이 더 간결한 코드를 짤 수 있었다. 부족한 시간으로 인해 도메인을 설계했던 의도대로 전부 사용하지 못하고 서비스의 간결한 코드로 타협을 볼 수 밖에 없었던 부분들이 가장 아쉬운 부분이었다.
<토론을 했지만 결론이 잘 안났거나 궁금했던 내용>
처음에는 영속성 로직은 생각하지 않고 도메인을 우선적으로 설계하려 했다. 하지만 점점 상위 도메인을 설계하는 과정으로 넘어가면서 영속성 로직에 대한 생각이 개입되지 않을 수 없었다. 도메인을 엄격하게 설계하는 것과 영속성 로직 수행의 효율을 고려하는 것이 트레이드 오프 관계라고 생각이 되었다. 아직 어떤 경우에 이러한 트레이드 오프를 어떻게 조절해야 하는지에 대한 기준이 없는데 앞으로 경험을 쌓아가면서 확립해봐야겠다.
'우아한테크코스 > 회고' 카테고리의 다른 글
[우아한테크코스] 중간 회고 (2) | 2023.07.02 |
---|---|
[우아한테크코스] LV2 - 웹 지하철 미션 Step2, 3 회고 (0) | 2023.05.28 |
[우아한테크코스] LV2 - 웹 장바구니 미션 Step2 회고 (0) | 2023.05.07 |
[우아한테크코스] LV2 - 웹 자동차 경주 미션 회고 (0) | 2023.04.27 |
[우아한테크코스] LV2 - 웹 장바구니 미션 Step1 회고 (0) | 2023.04.27 |