혜화동에 차린 나의 극장.
시설 투자를 잘 했기 때문일까?
내 극장에서 공연을 올리고 싶어하는 극단이 많다.
극단 요청에 따라 공연 정보를 저장하고, 삭제하는 서비스가 있었으면 좋겠다.
또, 관객들은 올려진 공연을 보기 위해 티켓을 구매하는 서비스도 있었으면 좋겠다.
이번 개인 프로젝트 목표는 짜임새 있는 클래스 설계였습니다.
그동안 경험, 습득, 상상했던 클래스 설계를 처음부터 녹여보고 싶었습니다.
1. 요구사항을 기능별로 분리한다.
- 영속성 관련 로직을 시점으로 기능을 분리했습니다.
- 영속성 관련 로직이 가장 덜어낼 것이 없는 최소성을 가진 로직으로 만들고 싶었습니다.
2. 필요한 모든 개념을 그림으로 그린다.
- performance(공연), ticket(티켓), ticket order(티켓주문) 세 개의 개념이 필요했습니다.
- 주문이 포괄적인 개념이라고 생각해, `티켓` 이라는 비즈니스 개념에 포함된 `주문` 으로 설정했습니다.
3. 개념 간의 관계를 그림으로 그린다.
- ERD 보다는 도메인 클래스 간 연결관계를 명확하게 만들고 싶었습니다.
- 구현 순서를 명확하게 정하고 싶었고, 구현 영역 중 어떤 부분이 다른 개념에 영향을 줄 수 있는지 예상하고 싶었습니다.
필요한 모든 개념을 더 이상 쪼갤 수 없는 개념으로 잘게 쪼개고 싶었습니다.
그동안 사양변경에 영향이 많은 코드를 작성하면서 느꼈던 분노와 울분을 극복하는 좋은 방법이라고 생각했기 때문입니다.
`주문` 이라는 개념이 포괄적이라고 생각했습니다.
`주문` 안에 `티켓` 이 있는 구조라면, `티켓`이외의 개념이 추가할 수록 거대한 클래스가 될 거라고 생각했기 때문입니다.
그래서 `티켓` 이라는 개념에 속한 `티켓 주문` 이라는 클래스로 만들어 새로운 개념이 추가되어도 기존의 로직은 변경하지 않도록 설계했습니다.
클래스 안에서도 최소성이 유지되었으면 좋겠다고 생각해 원시값 포장을 적극적으로 채용했습니다.
- 도메인 클래스 필드를 모두 원시값으로 포장해 사용했습니다.
- 도메인 클래스에서 모든 필드에 대한 유효성 검사를 하지 않고, 개별 필드가 자신의 유효성을 판단하고 잘못된 인스턴스 생성을 방지하기 때문에
낮은 결합, 높은 응집을 만족할 수 있었습니다.
- 특정 필드에 대한 디버깅이나, 타입변경에도 수정할 곳이 적어, 개발 실수를 미연에 방지하는 좋은 장치인 것 같습니다.
설계 당시에는 bottom-up 방식을 채택했습니다.
작은 개념부터 큰 개념 순으로 구현하는 것입니다.
체감했던 장점이 있습니다.
프로그램 근간이 되는 작은 부분만 고려하여 프로그램을 작성하니,
나중에 프로그램 규모가 커진 시점에서도 원소 부분에 대한 믿음을 가지고 구현할 수 있었습니다.
단점도 있었습니다.
설계 단계에서 미처 예상하지 못한 부분이 있어 구조 변경이 필요한 경우,
이전까지 구현했던 내용의 대부분이 의미가 없어져 제거하고 새로 작성해야 하는 경우가 있었습니다.
다음부터는 도메인 개념을 먼저 만들고,
컨트롤러 로직을 만들면서 필요한 기능이 설계에서 충분히 고려했는지 판단한 후에
레포지토리 레벨 구현을 하는 방식으로 작업하려고 합니다.
이전 과제를 하면서 테스트 관련 피드백에 항상 있던 내용이 있었습니다.
'이 테스트가 꼭 필요한가요?'
테스트 대상을 고려하지 않고 테스트 작성을 했다고 이해했습니다.
이번 테스트에서는 `로직`과 `데이터 전달` 책임을 인식해 테스트를 작성했습니다.
컨트롤러는 `데이터 전달`
서비스와 레포지토리는 `로직` 으로 구분하고, 로직 관련 테스트에 집중했습니다.
또한 서비스에서 필요한 로직을 구현할 수 있도록 하고 싶었기 때문에, mock 객체를 사용하지 않고,
의존성을 주입받아 테스트했습니다.
이전까지 RuntimeError 를 상속하는 커스텀 예외를 만들어 사용했었습니다.
프로그램에서 발생하는 모든 예외를 대처할 수 있는 Common 예외였습니다.
이렇게 하니, unchecked exception 까지 굳이 붙잡아서 커스텀 예외로 다시 던지는게 되었습니다.
불필요한 과정이기도 하고, 자바에서 기본적으로 제공하는 예외를 사용하는게 더 범용성 있을거라 판단해,
이번 프로젝트에서는 사용하지 않으려고 했습니다.
하지만, 직접 RuntimeException을 코드에서 던지는 것보다 커스텀 예외를 던져서 처리하는 것이 더 바람직하다는 IDE 조언에 따라,
이번 프로젝트에서도 커스텀 예외를 만들어 사용했습니다.
이번 프로젝트에서는 지난번 커스텀 예외처럼 Common 개념의 유일한 예외가 아닌, 유형별로 나눠 만들었습니다.
이름이 같은 예외 클래스를 인자로 넘겨주는 ErrorMessage에 따라서 다른 예외로 처리하고 싶지 않았기 때문입니다.