ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 데이터 모델링
    Projects/약품고 (스마트 의약품냉장고) 2026. 2. 10. 15:52

    처음 설계한 것과, 과감히 버린 것들

    시스템 구조가 잡히고 나면 자연스럽게 이 질문에 도착한다.

    “그래서… 이걸 DB에는 어떻게 담지?”

    데이터 모델링은 늘 그렇듯
    가장 논리적으로 보여야 하지만, 실제로는 가장 많이 흔들리는 단계였다.


    DB 설계의 첫 기준

    “이 테이블 하나만 봐도
    ‘왜 이 데이터가 생겼는지’가 보이는가?”

     

    정규화나 성능 이전에,
    맥락이 사라지지 않는 구조가 목표였다.

    지금 돌아보면 ERD를 먼저 그리지 않고
    ‘질문’을 먼저 적는게 도움이 될 것 같다.

    가장 먼저 나눈 기준: 마스터 vs 이력

    초기 설계에서 가장 먼저 분리한 건 이 두 가지였다.

    • 변하지 않는 것
    • 계속 쌓이는 것

    그래서 자연스럽게 구조가 갈렸다.

    • 약품 마스터 (Item Master)
    • 입고 / 출고 / 폐기 이력

    약품 이름, 분류, 보관 조건은 바뀌지 않지만
    입고와 출고는 사건(Event) 이기 때문이다.

    처음엔 재고 테이블 하나로 다 해결하려 했는데,
    이력과 상태를 섞는 순간부터 복잡도가 급격히 올라갔다.

    재고는 ‘상태’지만, 우리는 테이블로 관리했다

    개념적으로 재고는 입고·출고 이력의 **결과(상태)**다.

    초기에는 이력만으로 재고를 계산하는 방식도 고려했다.

    하지만 재고 조회는 가장 자주 쓰이는 화면이고,
    유효기한·개봉 여부·잔량 필터가 많아 즉시 응답이 필요한 영역이었다.

    그래서 우리는 재고를


    “상태로 정의하되, 현재 상태를 담는 inventory 테이블로 관리”하기로 했다.

    • 이력의 진실은 inbound_history / outbound_history
    • inventory는 빠른 조회를 위한 현재 스냅샷
    • instance_id를 공유해 언제든 이력 추적 가능
    inboud_history에 데이터가 생기는 순간 invetoyr에도 반드시 생기는 트랜잭션 처리가 정말 중요한데 그부분에 대해선 꼼꼼하게 검수하진 못한것 같다.

    인스턴스 개념을 도입한 이유

    약품 하나는 실제로는 여러 개다.

    • 같은 약
    • 다른 LOT
    • 다른 유효기한
    • 다른 개봉 상태

    그래서 약품을 인스턴스 단위로 다루기로 했다.

    • 입고 1건 = 인스턴스 1개
    • 출고는 인스턴스를 소모
    • 폐기는 인스턴스 상태 변경

    이 구조는 귀찮지만,
    의약품 관리에서는 거의 필수에 가깝다.

    나중에 “유효기한 임박 우선 출고” 같은 기능을 넣을 수 있었던 것도
    이 인스턴스 개념 덕분이었다.

    세션 ID를 거의 모든 테이블에 붙인 이유

    처음에는 좀 과해 보였다.

    • 입고 이력
    • 출고 이력
    • 온도 이벤트
    • 조치 기록

    전부 세션 ID를 들고 있다.

    하지만 이유는 명확했다.

    “이 데이터가 어떤 업무 흐름에서 생겼는지
    다시 묶을 수 있어야 한다.”

     

    세션 ID는
    로그와 DB를 다시 하나로 꿰는 실 같은 역할을 한다.

    지금 다시 설계한다면,
    “세션은 어디서 시작되고, 어디까지 보장되는가”를
    UX·API·DB 레벨에서 처음부터 명확히 정의했을 것 같다.

    현재 구조에서 세션은 업무의 생명주기를 관리하는 개념이라기보다는, 로그를 묶기 위한 ID를 하나 발급해 키처럼 사용하는 수준에 가깝다.

    그래서 브라우저 비정상 종료, 새로고침, 탭 이동처럼 사용자 흐름이 꼬이는 상황에서는 세션이 자연스럽게 종료되거나 정리되지 못하고 의도치 않게 이어질 위험이 있다.

    온도 데이터는 완전히 다른 취급을 했다

    온도 데이터는 성격이 완전히 달랐다.

    • 매우 자주 쌓이고
    • 대부분은 의미 없고
    • 일부만 사건이 된다

    그래서 구조를 나눴다.

    • 온도 로그: 전부 저장
    • 온도 이벤트: 이상 구간만 요약
    • 조치 기록: 사람이 개입한 흔적

    이렇게 나누니
    “데이터는 많지만, 의미는 명확한” 구조가 됐다.

    모든 데이터를 ‘중요하게’ 다루려고 하면
    결국 아무 것도 중요하지 않게 된다.
    어떤 데이터를 보려하는지 미리 정의해놓는게 좋아보인다.

    처음에 설계했다가 버린 것들

    솔직히 말하면, 이런 것들도 있었다.

    • 재고 변경 통합 테이블
    • 상태 플래그가 잔뜩 붙은 만능 테이블
    • 입고/출고를 하나로 합친 히스토리

    버리는 기준은 정규화에 있었다.
    우선은 정규화과 된 erd 구조를 설계하고 그다음,
    필요에 의해 비정규화된 테이블이나 뷰를 만드는게 좋다고 생각했다.
    그래서 단순 통계, 조회, 비정규화 테이블은 우선 만들지 않고, 쿼리문으로 구현했다.


    이 단계에서 남기는 요약 회고

    이 DB 설계에서 배운 점

    • 어떤 데이터가 필요한지 문장으로 정의하기
    • erd를 설계할땐 우선 정규화된 erd를 설계하고, 그 다음 필요에 의해 병목 예상 지점에 비정규화 테이블 설계하기.

    'Projects > 약품고 (스마트 의약품냉장고)' 카테고리의 다른 글

    설계 회고  (0) 2026.02.10
    API 설계  (0) 2026.02.10
    시스템 설계  (0) 2026.02.10
    Ux 흐름 설계  (0) 2026.02.10
    MVP  (0) 2026.02.10
Designed by Tistory.