dentOne 환자·주문 관리 서비스
- JavaScript
- PHP
- Team Size:3인 (프론트엔드 1 · 백엔드 1 · 디자이너 1)
- Duration:2026.05 ~ 2026.07
Trouble Shooting
# 멀티 윈도우 환경의 인증 기반 화면 잠금
[문제]환자 정보 보호를 위해 15분 무활동 시 자동 로그아웃됐지만, 다시 로그인하면 작업 상태가 사라져 환자 조회부터 다시 해야 했습니다. 또한 서버 요청 발생 여부만으로는 실제 사용자 활동을 판단하기 어려웠고, 메인 창과 팝업 창이 함께 열리는 구조여서 창마다 잠금 상태가 달라질 수 있었습니다.
[해결]마우스·키보드·스크롤·클릭 등 실제 입력 이벤트로 15분 무활동을 감지하고, 로그아웃 대신 작업 화면 위에 비밀번호 재인증 잠금 화면을 띄웠습니다. 데이터 접근 차단은 서버에서 처리했습니다. 잠금 시점에 access·refresh token을 무효화하고 서버 세션에 잠금 플래그를 세워, 잠금 UI를 제거하거나 새로고침해도 환자 데이터 조회와 토큰 재발급이 차단됩니다. MutationObserver는 잠금 UI가 비정상 제거됐을 때 화면을 복구하는 UI 보조 장치로만 사용했습니다. 여러 창의 잠금 상태는 localStorage 이벤트로 동기화하고, 창 생존 신호(heartbeat)로 비밀번호 입력 창을 한 곳만 선출했습니다. 재인증에 성공하면 기존 작업 화면을 그대로 이어서 사용합니다.
# 대용량 파일의 안정적인 분할 업로드
[문제]치과 주문 과정에서 대용량 파일을 업로드해야 했으며, 하나의 요청으로 전체 파일을 전송하면 네트워크 상태에 따라 업로드 실패 가능성이 커지고 진행 상태를 사용자에게 안내하기 어려웠습니다.
[해결]자체 업로드 API(init / part-url / complete / abort)로 S3 Multipart 세션을 생성하고, 서버가 지정한 파트 크기로 파일을 나눠 파트별 presigned URL로 S3에 직접 PUT 업로드했습니다. 각 파트의 ETag를 수집해 complete 단계에서 하나로 조립하고, 중간 실패 시 abort로 남은 세션을 정리했습니다. 진행률은 업로드된 파트 수 기준으로 표시하되 조립 완료 전에는 100%가 되지 않도록 상한을 뒀고, 소용량 파일은 단일 PUT으로 분기했습니다. 파일은 임시 경로에 먼저 올린 뒤 주문 이력 생성 후 최종 경로로 옮겨, 이동과 DB 확정이 모두 성공한 시점에만 화면에 노출되도록 했습니다.
# 비동기 재결제의 Client-Server 상태 동기화
[문제]결제 실패(past due) 상태에서 카드를 변경하면 Paddle 재결제가 비동기로 처리되어, 클라이언트는 재결제가 시작됐지만 서버에는 기존 failed가 남는 불일치 구간이 생겼습니다. 서버 상태만으로 UI를 결정하면 재결제 중에도 결제 실패로 표시되어, 사용자가 카드 변경을 반복할 수 있었습니다.
[해결]서버의 failed를 임의로 바꾸지 않고, 재결제 요청의 pending 상태를 클라이언트에서 별도로 정의했습니다. 여러 창을 오가는 결제 흐름을 고려해 sessionStorage 대신 localStorage를 쓰고 subscription ID별로 상태를 분리했습니다. 카드 변경이 완료되면 pending 플래그를 저장하고, 클라이언트 pending과 서버 failed를 조합한 파생 상태로 "결제 처리 중" UI를 렌더링했습니다. 해제 기준은 클라이언트 타이머가 아니라 서버의 failed 해제로 삼아, 정상화를 확인한 뒤 플래그를 제거해 Client-Server 상태를 재동기화했습니다. Paddle Sandbox에서 카드 변경부터 재결제 대기, 서버 상태 정상화까지 검증했습니다. (Paddle 심사 이슈로 운영 PG 적용 전, Sandbox 검증 범위)