티스토리 뷰

아래는 내가 한국 지하철 개찰구의 작동 원리를 분석해서 최초로 그렸던 클래스 다이어그램과 시퀀스 다이어그램이다.

 

이렇게 draw.io 로 얼기설기 그려서 구글 제미나이에게 주면서 이 다이어그램의 문제를 찾아보라고 프롬프트했더니

그는 나를 순살치킨으로 만들었다.

하나씩 읽어보면서... 모르는 건 찾아보면서 검토해보았다.

 

  • 보안성 문제: MobileApp이 TicketGate에 userId만 달랑 넘기는데, 이거 중간에 가로채서 복제하면 부정 승차 막을 방법 있어?
    • 하지만 실습 제출을 위해 가장 단순한 설계를 했으니까 이건 잠시 넘어가주지 않으련...
    • 일회용 토큰과 암호화된 코드를 사용하지 않을까?
    • 갑자기 부정승차와 같은 우회로를 점검해봐야겠다는 생각을 했다. 추가로 글을 작성해서 정리해볼 것이다.
  • 오프라인 모드: 만약 지하철역 네트워크가 끊기면 모든 게이트가 먹통이 되는 설계인데, 비상시를 위한 로컬 승인 로직은 왜 없어?
    • 이 친구가 내가 보안 질문을 많이 했더니 이걸 두번째 질문으로 주네
    • 게이트에 로컬 캐시, 그러니까 Local DB를 둬서 네트워크 단절 시 일단 승인한 뒤에 나중에 다시 네트워크 연결되면 서버로 데이터를 전송하는 'Store and Forward' 방식이 있을 것 같다. 
  • SRP(단일 책임 원칙) 위반: SeoulMetro 클래스가 요금 계산도 하고 정산 요청도 하네? 요금 정책이 바뀌면 이 클래스 전체를 수정해야 하는데, 객체지향 설계 맞아?
    • SeoulMetro 클래스를 분리해서 요금 계산용 FareCalculator 등 클래스를 만드는 편이 좋아보인다.
  • 예외 처리 부재: CardPayment에서 정산하다가 네트워크 오류로 DB 저장이 안 되면 어떻게 돼? 시퀀스 다이어그램에 실패 시나리오가 왜 하나도 없어?
    • 시퀀스 다이어그램에 alt를 추가해서 결제 실패 케이스도 그려야겠다.
  • 하드코딩 위험: TicketGate가 stationName을 직접 필드로 가지고 있는데, 역 이름이 바뀌거나 게이트 위치가 바뀌면 매번 코드를 수정할 거야?
    • 확장성이 나쁜 것을 지적했구나
    • stationName 말고 stationId로 표현한 뒤 역 정보는 별도의 Station 관리 서버나 DB에서 동적으로 불러오는 편이 낫다.
  • 시간 동기화: tagTime을 게이트에서 생성해서 넘기는데, 게이트 시계랑 서버 시계가 안 맞으면 요금 계산 오류 날 텐데 이건 고려 안 했어?
    • 모든 시스템이 NTP를 통해 표준시를 동기화
    • 아니면 게이트 시간이 아닌 메인 서버 수신 시간을 기준으로 정산 시간을 확정한다.
  • 결합도 문제: MobileApp이 TicketGate 객체를 직접 매개변수로 받는 tagGate(gate) 방식은 게이트 종류가 바뀌면 앱도 수정해야 하는 거 아냐?
    • 앱이 특정 게이트 객체를 아는 게 아니라 인터페이스를 통해 소통하게 해서 결합도를 낮춘다.
  • 환승 로직 누락: 이 설계로는 지하철에서 버스로 갈아타는 환승 할인을 전혀 처리할 수 없는데, 확장성은 고려한 거야?
    • 미안 내가 실습하느라 급했다.
    • 사용자의 최근 하차 정보를 저장하는 TripSession 테이블을 추가한다.
    • 태그 시점에 마지막 하차 시간으로부터 30분 이내인지 확인하는 로직을 FareCaculator에 추가한다.
  • 동시성 제어: 수천 명이 동시에 개찰구에 태그를 할 때, SeoulMetro 서버가 이 요청들을 순서대로 안전하게 처리할 수 있는 구조야?SeoulMetro가 모든 정산 요청의 컨트롤 타워가 되면, 사용자 수가 늘어날 때 서버 터지는 거 아냐? 분산 처리 계획은? 
    • 병목 현상과 관련해서 질문하는 걸로 파악했다.
    • 서버 앞에 L4 스위치(로드밸런서)를 두고, 요청을 메시지 큐에 담아 비동기로 처리하면 버틸 수 있을 것이다.
  • 상태 관리: TicketGate는 지금 이 사용자가 들어온 상태인지 나가는 상태인지 어떻게 알아? 게이트 자체가 '상태'를 저장하지 않는데 로직이 돌아가?
    • DB에 사용자의 현재 상태(IN_PROGRESS, COMPLETED)를 저장하는 Status 필드를 추가한다.
    • 탑승할 때 IN_PROGRESS로 바꾸고, 하차 시에만 정산 후 COMPLETED로 변경한다.
  • 비용 발생: 하차할 때마다 CardPayment에 정산을 요청하면 API 호출 비용이나 부하가 엄청날 텐데, 모아서 처리하는 배치 방식은 왜 고려 안 했어?
    • 어...
    • 하루치 이용 내역을 다음 날 이용자가 적은 시간에 한꺼번에 정산해볼까?
  • 데이터 정합성: DB에 저장하는 processTime과 SeoulMetro가 보낸 requestTime이 다를 경우, 어떤 시간을 기준으로 매출을 정산할 거야?
    • 요청 시각과 실제 처리 시각을 둘다 저장할까?
  • 가시성(Visibility): SeoulMetro의 요금 계산 메서드(calculateFee)가 private인데, 만약 고객 센터 앱에서 예상 요금을 조회해야 한다면 어떻게 접근해?
    • calculateFee를 public이나 protected로 전환해서 외부 조회용 앱에서도 접근이 가능하게 한다.
  • 상속의 부재: 카드 결제 말고 현금 결제나 정기권 결제가 추가되면 클래스 구조를 다 갈아엎어야 하는 거 아냐? 상속이나 인터페이스 활용이 부족해 보이는데?
    • PaymentMethod라는 인터페이스를 정의하고 CreditCard, T-Money, SamsungPay 같은 구현을 추가한다. 새로운 결제 수단이 추가 되어도 기존 코드를 해치지 않는다.
  • 매개변수 일관성: 시퀀스 다이어그램에서 하차 시에는 요금 계산을 요청한다고 했는데, 왜 메서드 인자에 '요금 계산 방식'에 대한 전략은 안 보여?
    • 이거... 모르겠어서 제미나이 다시 질문해봤는데 handleExit 호출 시에 아예 FareStrategy 객체를 넘겨서 거리별, 구간별, 정액제 등 다양한 계산 방식 중에서 유연하게 선택할 수 있는게 좋겠다고 해줬다.
  • 로그 기록: 장애가 발생했을 때 어디서 문제가 생겼는지 추적할 수 있는 '로그 저장' 기능은 설계의 어느 부분에 포함되어 있어?
    • 로그 추적이 또 중요하거든요
    • 모든 트랜잭션을 단계마다 로그를 남겨놓는 편이 좋을 것 같다.
  • 사용자 경험(UX): 정산 완료 후 DB 저장까지 마쳐야 문이 열리는 구조면, 결제 속도가 느릴 때 사용자가 게이트 앞에서 계속 기다려야 해?
    • 찾아보니 선 승인 후 결제 방식을 사용한다고 한다.
    • 유효한 사용자인게 확인되면 문을 열고 실제 결제 처리는 백그라운드에서 비동기로 진행한다고...
  • 캡슐화: CardPayment에서 saveToDB를 굳이 분리한 이유는 뭐야? processSettlement 안에서 다 처리하면 안 되는 이유가 있어?
    • 결제는 성공했는데 DB 기록만 실패하는 경우를 막으려고. 재시도 메커니즘을 위해서 존재하는거얌.
  • 다중도(Multiplicity): 클래스 다이어그램에서 User와 SamsungPayApp이 1:1 관계인데, 사용자가 폰을 두 개 쓰거나 앱을 여러 개 쓰면 이 설계 깨지는 거 아냐?
    • User와 App의 관계를 1:N을 변경
    • 혹은 여러 기기를 하나로 묶는 UserAccount 개념을 추가할 수도 있다.

 

그래 너 잘났다

수정한 내용을 담아서,,, 이번엔 mermaid로 코드까지 만들어달라고 해서 새로 그려보았다.

 

최근에 올라온 글
최근에 달린 댓글
«   2026/06   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30