지하철 미션 3단계 진행하면서 노선별 거리당 추가 요금 정책을 어떻게 해결할 것인가에 대한 문제를 생각해보는 글이다.
거리당 추가 요금이 고정이라면 편하겠지만 노선이 계속 추가되거나 수정이 필요할 경우에 데이터베이스를 활용하지 않으면 서버를 내렸다가 켜야하는 상황이 발생한다.
이러한 문제를 해결하고자 노선별 거리당 추가 요금 정책을 데이터베이스에 저장하기로 했다.
그리고 문제를 해결하려고하니 오류가 마구 터지기 시작했다. 🥲
먼저 비용관련해서 고려해야할 부분은 3가지인데 "노선별 거리당 추가 요금 정책" 관련 이야기만 하려고 한다.
- 기본 요금 1,250
- 노선별 거리당 추가 요금 정책 ("추가 요금 정책" 이라고 부르겠다.)
- 승객별 요금 할인
🟧 01. 현재 지하철 애플리케이션 플로우
문제 상황을 더 확실하게 알기 위해서는 먼저 애플리케이션 플로우를 간단히 알아야한다.
현재 구조가 잘못되었다는 이야기를 하려고하는게 아니다. 단순하게 흐름만 알아보자.
RouteFinder
- 사용자가 출발역, 도착역, 승객나이에 대한 정보를 넘겨줬을 때 RouteFinder가 최단 경로를 찾는다.
CalculateFareService
- RouteFinder가 찾은 최단 경로를 받아 ExpenseComposite과 DiscountComposite에게 계산을 요청한다.
ExpenserComposite
- 이번에 문제가 발생하는 부분이다.
- 기본 요금 정책을 가지고 있다.
- LineExpensePolicy라는 노선별 거리당 추가 요금 정책을 가지고 있다.
LineExpensePolicy
- 노선별 거리당 추가 요금 정책이다.
- 정책 정보는 데이터베이스에 저장되어 있다.
- 해당 정책을 사용하기 위해서는 데이터베이스에 접근해야 한다.
DiscountComposite
- 승객별 할인 정책을 가지고 있다.
🟧 02. 추가 요금 정책, 데이터베이스에서 조회해서 사용하기
그냥 계산하면 된다.
- 사용자가 최단 경로에 대한 금액을 요청한다면 데이터베이스에 접근하여 "추가 요금 정책"을 조회해서 ExpenseComposite에 넣어서 계산하면 된다.
DefaultExpensePolicy
- 기존에 ExpenseComposite과 함께 빈으로 등록한 기본 요금 정책이다.
ExpenseComposite과 DefaultExpensePolicy는 애플리케이션 실행 시점에 빈으로 등록시켰다.
이제 데이터베이스에서 "추가 비용 정책"을 조회하여 ExpenseComposite의 필드에 주입시켜주면 된다.
이제 ExpenseComposite 빈에는 데이터베이스에서 가져온 "추가 비용 정책(LineExpensePolicy)"가 잘 들어와있다.
사용자가 요청하면 이제 아래와 같이 잘 나올거다 ! so easy ~
계산 끝 감사합니다. 😌
도대체 무슨 일이 있었기에.. 이렇게 손님을 화내게 만들 수 있었을까 ?
사실 IoC 컨테이너에 등록한 ExpenseCompote이 싱글톤 빈이라는 것을 생각하면 너무 당연한 이야기였다.
🟧 03. 싱글톤 빈이 상태를 가질 때 문제
사용자 두 명이 각각 최단 경로와 요금을 요청했을 때 다음과 같은 일이 발생한다.
첫 번째 사용자 (#1 Request)
- "추가 비용 정책"을 계산해야한다.
- 데이터베이스에서 "추가 비용 정책"을 가져온다.
- "추가 비용 정책"을 ExpenseComposite에 등록한다.
- 생각한 요금과 같은 값이 나온다. 👍
두 번째 사용자 (#2 Request)
- "추가 비용 정책"을 계산해야한다.
- 데이터베이스에서 "추가 비용 정책"을 가져온다.
- "추가 비용 정책"을 ExpenseComposite에 등록한다.
- 이전 사용자에 비해 생각보다 더 비싼 요금이 나온다. 😡
이유는 당연하게도 IoC 컨테이너에 싱글톤 빈으로 등록된 ExpenseComposite은 모든 사용자가 같이 사용하게 된다.
사용자가 계속해서 요금 계산을 요청할 수록 공통으로 사용하는 ExpenseComposite에 "추가 비용 정책"이 중복으로 추가 되기 때문이다.
결국
첫 번째 사용자는 ExpenseComposite을 처음으로 이용했기 때문에 정상적으로 요금이 나왔지만
두 번째 사용자는 이미 ExpenseComposite에 "추가 비용 정책"이 들어있지만 한 번 더 추가하여 더 많은 요금을 지불하게 되었다.
🟧 04. 싱글톤 빈 말고 프로토타입 빈 이용해서 해결하기
이 문제는 ExpenseComposite이 단 하나만 존재하기 때문에 중복해서 "추가 비용 정책"이 들어가게 된 것이었다.
반대로 ExpenseComposite이 단 하나만 존재하지 않으면 된다.
싱글톤 빈을 이용하면 IoC 컨테이너가 ExpenseComposite을 계속해서 관리하게 되는데
프로토타입 빈을 이용하면 IoC 컨테이너가 생성까지만 해주고 이후에 관리를 하지 않게 된다.
즉, 사용자마다 각각의 ExpenseComposite을 이용할 수 있는 상태가 만들어진다.
프로토타입 빈이 된 ExpenseComposite을 믿고 다시 한번 사용자 요청을 받아보자.
수많은 사용자가 요금 계산을 요청해도 사용자마자 ExpenseComposite을 가지고 있는 것을 볼 수 있다.
기본 비용 정책(DefaultExpensePolicy)는 싱글톤 빈으로 등록해놨기 때문에 모두 다 같은 객체를 가지고 있다.
하지만 프로토타입 빈으로 만든 ExpenseComposte은 사용자 모두가 다른 객체를 가지고 있게 만들어주었고
"추가 비용 정책"이 중복해서 들어가게 되는 일을 해결할 수 있었다. 😄
중간에 "추가 비용 정책"이 바뀌는 상황이다. 전혀 문제 없는 상황이다.
"추가 비용 정책"이 바뀌기 전/후로 사용자마다 비용이 다르게 계산되는 생각한 상황에도 잘 맞아떨어진다.
🟧 05. 문제는 해결됐지만 쿼리는 해결하지 못했다.
성능이 처참하다. 쿼리가 사용자마다 하나씩 무조건 날라가도록 되어있다.
또한 사용자 각각이 가지고 있는 ExpenseComposite는 모두 같은 내용이다.
즉, ExpenseComposite을 싱글톤 빈으로 사용한다면 훨씬 더 효율적이고 맞는 방법이라 느낀다.
프로토타입 빈을 이용하여 중복해서 만들어지는 "추가 비용 정책" 문제 해결을 좋게 접근하지 못했다고 생각한다.
🟧 06. 다시 원점으로 돌아와서 문제 해결하기
기존 문제는 다음과 같았다.
- "추가 비용 정책"이 중복으로 가지게 된다.
- 요금 계산 요청마다 데이터베이스에 접근해서 "추가 비용 정책"을 가져오기 때문이다.
아.. 요청마다 데이터베이스에 접근 안하면 되잖아...🙈🙈🙈
어떤 조건이 만족했을 때 싱글톤 빈으로 등록된 ExpenseComposite에 접근하여 상태를 변경해주면 된다.
이제 아래 그림과 같아졌다.
요금 계산 요청이 올 때마다 데이터베이스를 조회하지 않고 등록되어 있는 ExpenseComposite을 이용하게 되었다.
다만 아직 생각하는 부분이 있다.
- 싱글톤 빈이 상태를 가지고 있어도 될까?
- 상태를 조작하는 기능을 만들어도 될까?
결국 "관리만 잘하면 해결되는 일 아닌가 !?"라고 생각하지만 다른 문제가 발생할수도 있지 않을까 고민이 된다.
아니면 다른 방식으로 해결할 수 있을거 같지만 열심히 삽질했기에 이만 포스팅을 마치겠다 !
👊 정리하자면
- 프로토타입 빈을 이용해서 사용자마다 다른 객체를 가질 수 있게 할 수 있다.
- 생각보다 정답은 가까운데 있다.
'👨🚀 우아한테크코스 5기' 카테고리의 다른 글
[20230703] 우아한테크코스 5기 LEVEL3 - 바톤 프로젝트 1주차 (0) | 2023.07.03 |
---|---|
[20230607] 우아한테크코스 5기 LEVEL2 - 레벨2 및 인터뷰 회고 (0) | 2023.06.23 |
[20230508] 우아한테크코스 5기 LEVEL 2 - 장바구니 미션 2단계 (0) | 2023.05.08 |
[20230427] 우아한테크코스 5기 LEVEL 2 - 장바구니 1단계 (4) | 2023.05.02 |
[20230424] 우아한테크코스 5기 LEVEL 2 - 웹 자동차 경주 1단계 & 2단계 회고 (3) | 2023.04.16 |