MsystechandClaude Opus 5 ddf2bdaee8 같은 서식 중복 열기 차단 — 출처를 가리지 않는다 (+ 3단계 판단용 실측 진단)
■ 중복 열기로 편집분이 통째로 사라지던 문제

중복 차단이 d.IsFromDb 를 요구했다. 그래서 XML 파일로 이미 열어 둔 서식을 목록에서
더블클릭해 DB 로 또 열 수 있었고, 두 탭에서 교대로 저장하면 낙관적 충돌 검출이
양쪽 어디에도 없어 마지막 저장이 앞선 편집을 통째로 덮었다.

레거시 MultiSheetFormAllow 는 탭 제목의 [서식코드]만 보고 출처와 무관하게 막는다
(frmSheetDesigner.vb:242-252). 같은 규칙으로 맞췄다:
  · 판정을 FindOpenSheet(서식코드) 하나로 모으고 IsFromDb 조건을 뺐다.
  · 열기 경로 3곳(DB / XML 파일 / JSON 가져오기)이 전부 이 지점을 지난다.
    종전에는 DB 경로에만 검사가 있었다.
  · 이력 탭은 계속 제외한다 — 현재본을 열려는 요청이 과거 스냅샷을 활성화하면 안 된다.
  · 빈 서식코드는 서로 다른 문서로 본다. 묶으면 '새 서식'을 두 번 만들 수 없다.
  · 막을 때 어느 탭으로 갔는지 상태줄에 적는다. 아무 일도 안 일어난 것처럼 보이면 사용자는 다시 누른다.

비교용으로 나란히 열어야 하는 경우를 위해 탈출구를 남겼다 —
DataConfig.AllowMultiSheetForm(기본 false, 레거시 AllowMultiSheetForm 기본 "N" 과 동일).

■ 함께 고친 것 — 이력본을 저장해도 탭이 '이력'으로 남던 문제

DB 저장 후 HistorySdgKey 를 null 로 되돌리지 않아 탭 제목이 '(이력 N)' 으로 남고,
종료 확인이 '저장할 수 없는 탭' 분기로 들어가 저장 선택지 없이
'변경을 버리고 종료할까요?' 만 물었다 — 저장 뒤의 편집분이 조용히 버려진다.
저장한 순간 그 탭은 스냅샷이 아니라 활성 디자인이므로 표식을 지운다.

이 수정이 만드는 경계가 하나 있다: 이력 탭을 되살려 저장하면 같은 서식의 다른 탭이
이제 옛 내용을 들고 있게 된다. 그 탭에서 저장하면 방금 저장분을 덮으므로,
어느 탭이 낡았는지 알리는 경고를 띄운다(자동으로 닫지는 않는다 — 미저장 편집분이 있을 수 있다).

■ 3단계 판단용 실측(--db-phase3)

사이트 확인을 기다리는 동안 DB 로 알 수 있는 것부터 쟀다:
  · ShtCneYon='Y' 40건, 그중 현역 19건 — TK_MODIFY(제자리 정정) 부재의 실제 영향 범위.
  · ShtDepCls 는 418건(24%)에 실제 분류가 들어 있다(산업재해·물리치료실·병동출력물…).
    비어 있지 않으므로 진료과 2단계 그룹 미이식은 목록 구성을 실제로 바꾼다.
  · ShtDspSeq 는 1,714건(98.7%)이 1 이상이다. 표시 순번은 사실상 전 서식에 채워져 있어,
    정렬 미이식은 거의 모든 서식의 목록 순서를 레거시와 다르게 만든다.
  · ShtTyp='S'(스캔형) 108건, 현역 105건. 사이트 조건 이식이 걸리는 모집단이 실재한다.
  · EMRShtSortTyp 는 확인하지 못했다 — 코드 마스터(DtlCod/DtlCodVal) 테이블이
    이 접속 스키마에 없다. 운영 접속에서 재측정이 필요하다.

편집 스모크 5건 추가(출처 무관 판정 / 대소문자 / 이력 제외 / 무관 서식 / 빈 코드).
회귀: 테스트 142/142, 편집 스모크 192건 실패 0,
DB 왕복 1,271건 diff 0/예외 0, 종이 렌더 P062 바이트 동일.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 13:27:00 +09:00

SheetMe 서식생성기 (C#/.NET 10 WPF)

MsystechHIS 레거시 서식생성기([003]EMR\[001]SheetDesigner, VB.NET WinForms)를 대체하는 신규 WPF 서식 디자이너. 레거시 XML 포맷을 읽기·쓰기 모두 무손실 호환하여, 여기서 만든 서식을 기존 EMR 런타임(EMRLoader)이 그대로 렌더링한다.

프로젝트 구성

프로젝트 TFM 역할
src/SheetMe.Core net10.0 포맷 중립 모델(FormDocument) · LegacyXmlSerializer(읽기+쓰기, PropBag 무손실 왕복) · FormJsonSerializer(부 포맷) · ControlRegistry(팔레트/속성 스키마)
src/SheetMe.Data net10.0 Oracle 저장소 — OracleLegacyFormStore(E_ShtMst/E_SdgMst/E_SctMst, 레거시 동일 저장 의미론) · SctMstXmlWalker · EmrDataContext(M.MW.Data.EMR)
src/SheetMe.Designer net10.0-windows WPF MVVM 디자이너(M.Framework.WPF) — 캔버스/팔레트/인스펙터/페이지/레이어
tests/SheetMe.Core.Tests net10.0 MSTest 44건 — 실서식 XML 왕복 의미론 diff 0 게이트 포함

실행

dotnet build
dotnet run --project src/SheetMe.Designer
dotnet test

진단 모드 (자동 검증)

# 첫 페이지 오프스크린 렌더 → PNG (레거시 시각 대조용)
SheetMe.Designer.exe --render-smoke <서식.xml> <출력.png>
# 편집 코어 자동 검증(선택/이동/리사이즈/마퀴/복붙/Undo/왕복) → 리포트
SheetMe.Designer.exe --edit-smoke <리포트.txt>

주요 기능 (v1)

  • 캔버스 편집: 클릭/Ctrl/Shift/마퀴 선택, 드래그 이동(스냅: 그리드+정렬 가이드, Alt=자유), 8핸들 리사이즈(다중선택 비례), 키보드 넛지, z-order, 정렬 6종
  • Undo/Redo: 문서 스냅샷 메멘토 — 드래그=1스텝, 넛지 400ms 코얼레스
  • 팔레트 14종: Label·TextBox·MaskedTextBox·CheckBox·RadioButton·ComboBox·ListBox·CheckList·DateTimePicker·Panel·GroupBox·Line·PictureBox·CalcBox — 미지원 레거시 컨트롤(Spread/MSign 등)은 자리표시로 무손실 보존(이동/리사이즈 가능, 저장 시 원본 속성 전부 유지)
  • 인스펙터: 스키마 기반 속성 편집(다중선택 교집합), 폰트/색은 레거시 invariant 문자열 정합
  • 페이지/레이어: 페이지 추가/삭제/용지 크기, 레이어 목록(잠금/숨김)
  • 그룹: Ctrl+G/Ctrl+Shift+G — 그룹 멤버 클릭 시 그룹 전체가 한 단위로 선택/이동
  • 컨테이너 자식 편집: Panel/GroupBox 내부 컨트롤을 캔버스에서 직접 클릭·이동·리사이즈·삭제(상대좌표 유지)
  • 미리보기/인쇄: 편집 크롬 없는 렌더(캔버스와 동일 템플릿), FixedPage 1:1(DIP) 인쇄
  • 파일: 레거시 .xml 열기/저장(TK_ExportXML 호환) + JSON 가져오기/내보내기(부 포맷)
  • DB: E_ShtMst 검색 → 활성 디자인 열기 · DB 저장(레거시 동일: ShtCneYon='Y'→제자리 갱신 / 그 외 SdgDelYon 버저닝 + E_SctMst 재생성, 시퀀스→MAX+1 채번, 단일 트랜잭션) · 신규 서식 등록(미등록 코드 저장 시 자동 안내 — ShtTyp='D' 관행 기본값)

설정 (src/SheetMe.Designer/appsettings.json)

{
  "ConnectionStrings": { "His": "Oracle 접속 문자열" },
  "FormStore": { "SaveMode": "File" }   // DB 저장은 "Db" 로 명시 변경 시에만 활성(안전 게이트)
}

레거시 호환 규약 (정합성 기준: bzDesignSheetLoader.LoadObject 가 오류 없이 복원)

  • XML: <Sheet name="SheetDesignInfo"> 루트에 페이지당 <Object>(MDesignerHost) N개, 자식 Object 먼저 → Property 나중
  • 타입: 레거시 AQN 원문 보존, 신규는 M.EMR.UserControl.{Cls}, M.EMR.UserControl, Version=1.0.0.0, ...
  • 속성값: WinForms TypeConverter invariant 포맷("120, 88", "굴림, 11.25pt, style=Bold", "224, 224, 224")
  • LocationOnBase 는 저장 시 재계산(부모 절대좌표 누적), <Binary> 는 verbatim 보존(신규 생성 안 함 — .NET 10 BinaryFormatter 제거)
  • z-order: XML 첫 Object = 최상위(WinForms Controls 규약) ↔ WPF 캔버스는 역순 렌더

실DB 검증 결과 (2026-07-16, 테스트DB 192.168.100.222/MSYSTECHHIS)

  • 읽기: 전체 디자인 서식 1,271건 일괄 왕복(Read→Write) 의미론 diff 0 / 예외 0 — 미지원 컨트롤 보존 서식 242건 포함
  • 쓰기: S999 무변경 재저장 — 신규 버전 생성(SdgKey 52241→52380), 재저장본 diff 0, E_SctMst 36행 워커 기대치 일치, 이전 버전 SdgDelYon='Y' 이력 보존 확인
  • 호환성 세부: 텍스트 내 CR 은 NewLineHandling.Entitize( 보존), 레거시 데이터의 제어문자(0x05)는 CheckCharacters=false 관용 파싱(구식 XmlTextReader 동등)

계획 범위(P0~P5) 전체 완료 — 후속 아이디어

  • 레거시 SheetDesigner/EMR 미리보기로 S999 육안 확인 (권장)
  • v2 후보: 미지원 컨트롤 네이티브 지원(Spread 표, MSign 서명), 계산박스 수식 평가, 컨트롤 간 자식 드래그 재배치(reparent), 자 눈금(룰러), REST API 경유 모드

설계 계획서: C:\Users\Msystech\.claude\plans\c-net-wpf-flickering-gizmo.md

S
Description
No description provided
Readme
3.5 MiB
Languages
C# 99.8%
PowerShell 0.2%