개발하다 생긴 일

perfect dish | 이거 만들어볼까? (6) - 메뉴 보드와 책임 분리

로그관리자 2024. 1. 29. 11:52
728x90

 

 

🍽 Perfect dish 개발 일지

이 글은 개발 여정을 기록한 것으로 틀린 내용이 나올 수 있습니다. 점점 고쳐나가기 때문이죠 

 

 

 

💡 메뉴와 메뉴 보드, 그들의 관계 (응집도와 결합도)

 

메뉴 파트를 다 만들고 메뉴 보드라는 도메인을 만드는 중에 이런 고민이 생겼다. 

메뉴를 모아서 메뉴 보드를 만드는 건데 굳이 별도로 만들어 관리하는 거보다 그냥 메뉴 안에 ArrayList로 만드는 게 낫지 않을까...? 

 

이런 생각을 한 이유는

지금 시점에서 메뉴 보드에 들어갈 필드가 List<Menu> menus; 하나였기 때문이다. 그리고 dto의 역할을 하는 request와 response 도 메뉴 리스트 하나를 넘겨줄 뿐인데, 그냥 ArrayList 필드 하나 넣고 createMenus 메서드를 Menu 클래스에 두는 게 더 빨리 구현할 수 있지 않을까? 였기 때문이다. 

다음 날 일어나자마자 다시 생각을 해봤다. 

처음에 우리가(나) 클래스를 두 개로 나눈 이유가 뭐였을까. 메뉴는 메뉴를 관리하는 거고 (메뉴 등록, 변경, 삭제) 메뉴 보드는 메뉴리스트를 관리(메뉴리스트에 메뉴를 등록, 변경)이다. 그런데 이 두기능을 하나로 관리하게 된다면? 단일 책임 원칙이 무너진다. 두 기능이 함께 있기에 책임이 모호해진다. 는 응집도가 낮아진다는 거다. (우논시절통순기...)  그리고 분명 코드가 복잡해지기 때문에 유지보수 할 때 어려움이 있다. 그리고 그것도 고려해야 한다. 확장성. 지금은 필드 하나만 있지만 추후에 추가될 수도~ 있으니 

 일단 여기까지 응집도는 충족시켰으니 결합도 측면에서 보자. 

 

낮은 결합도. 클래스 간의 의존성을 낮추다.

메뉴를 메뉴 보드에 등록시켰는데 만약 메뉴가 변경되거나 삭제된다면? 연쇄 작용으로 문제가 생기겠지. 그렇다면 이 메뉴가 지금 가능한 메뉴인지 확인하는 작업이 필요할 거 같다. 필드에 isAvailable를 넣고 메뉴 보드에 리스트를 생성하는 시점에 가능? ㅇㅇ 를 확인해야 한다. 근데 만약 메뉴 보드의 메뉴리스트를 생성하는 시점에는 available이 true였는데 중간에 false로 된다? 면 메뉴 변경 여부를 정기적으로 확인해야겠져 

옵저버패턴 - (정처기에서 나와서 내용은 알 고 있으나; 한 객체의 상태가 바뀌면 그 객체에 의존하는 다른 객체들에 연락이 가고 자동으로 내용이 갱신) 활용하려면 시간이 조금 걸림. 그런데 이 패턴은 실시간으로 확인을 하기 때문에 자원 소모가 크다고 한다.

스케줄러를 활용해 매일 0시에 메뉴의 상태가 가능한 애들만 업데이트시키자.  

 

+ 이후에 메뉴 보드에서 그냥 메뉴 리스트, 할인 정책이 들어간 오늘의 메뉴 리스트를 추가함으로써
확장성을 고려한 메뉴 보드 분리가 옳은 선택이었음을 확인했다. 

 

 

 

💡 독립적으로 할인 정책 넣고 싶은데요 

 

메뉴 보드에 메뉴를 담은 리스트가 있다. 그런데 오늘의 메뉴 주문 시 10% 할인하는 정책을 넣은 또 다른 리스트를 넣으려 한다. 

이는 추후 추가될 order 파트에서 menuBoard의 discountMenus에 대한 애들만 뽑아서 할인 정책 적용할 것이기에,,,
menuBoard에서부터 분리하여 설계한다. 

null 값이 있을 수도 있는 객체를 다룰 때 NPE를 방지하고자 Optional로 잘 포장해왔다.(Wrapper 클래스) 
(if~ == null 은 가독성이 떨어지기도 하고..)

그렇다면 컬렉션 자체, 그러니까 객체를 생성할 때부터 값이 없을 수도 있는 건 어떻게 처리하면 좋을까? 

Optional의 정적 팩토리 메서드를 살펴보자

// 빈 optional을 반환
Optional.empty();

// optional이지만 값이 있어야 함 (null값 전달 시 NPE 발생)
Optional.of(value);

// null이 될 수도 있는 값도 가능 (null값 전달해도 NPE 발생하지 않음)
Optional.ofNullable(value);

 

 

 

Request에서도 일반 메뉴와 할인메뉴의 로직을 따로 두었다.
discountMenus는 Optional 이기 때문에 필드에 Optional.empty()로 기본 설정을 했다. 

메뉴가 있으면 temp리스트로 만들어 담는데 이때 리스트를 만들 때는. add 해서 담고, 그걸 Optional에 담을 때는(값이 있는 상태. null이 아님) Optional 중에서 무조건 값이 있을 때 사용하는 정적 팩토리 메서드인. of()를 적용하였다. 

근데 내가 간과한 게 있다. 만약 discountMenus가 이미 메뉴가 들어있다고 한다면? 
저 필드를 Optional.empty() 로 했기 때문에 discountMenus 객체가 새로 생성될 때마다 무조건 비어있는 리스트를 새로 만들어 메뉴를 추가하게 될거라구

(우리는 리스트를 맨 처음에만 새로 만들고! 다음부터는 거기에 추가 추가 추가 할 거니까요)

그러므로 addDiscountMenu메서드에서 -> 물론 이 메서드는 repository로 옮길 예정 
1. 메뉴가 null이 아닌지 확인
2. discountMenus가 빈 리스트인지 확인 
3. 비어있다? 새 temp리스트 만들고 거기에 메뉴 추가 후 discountMenus에 Optional.of로 추가 
4. 비어있지 않다면? discountMenus에 그냥 메뉴 추가



그리하여 repository에서 각 메뉴리스트 별 추가하는 메서드를 만들고 

잉 근데 어차피 findAll 해서 모든 리스트가 반환되는 로직이 있는데 굳이 '생성'하는 메서드에 조회하여 '반환'하는 역할까지 주는 게 맞나? 이거 단일 책임 원칙에 위배...?라는 생각이 들어 void로 생성! 의 책임만 선물하였다. 

 

 

그리고 지금에서야 하는 얘기지만 그냥 바로 DB 연결하고 할 걸 내가 저장소가 되려다 보니(? - 애플리케이션 내부에서 메모리를 관리)
뭔가 더 많은 시간을 잡아먹는 거 같다. 근데 이렇게 해봄으로써 RDBMS나 ORM이 왜 필요한지 알게 되었자너 🤣

 

 

항 재밌다 

728x90