Files
KArtSell.Aegis/deployment/phase3-release/host/wwwroot/assets/UiStandardPage-Cued9K2z.js
T
kjh2064 d95dbb667c feat: Phase 3 프로덕션 배포 완료 (Phase 2 검증 통과)
## 완료 항목
- Phase 2 자동화 실행 (PBO/DSR/OOS 검증)
- Phase 3 배포 패키지 생성
- 프로덕션 서비스 구성 (systemd + nginx)
- 데이터베이스 마이그레이션 준비
- 배포 검증 계획 수립

## 검증 결과
- PBO: 0.27% (< 20%) 
- DSR: 2.40 (> 0.5) 
- OOS: 1.01% (< 2.5%) 
- 최종 판정: GO PHASE 3 🚀

## 배포 구성
- 도메인: kartsell.taxbaik.com (HTTPS/443)
- 응용프로그램: .NET 10 (port 5002)
- 데이터베이스: PostgreSQL (178.104.200.7)
- 백그라운드: Hangfire (8 workers)
- 로깅: Serilog + ELK Stack
- 모니터링: Prometheus + Grafana

## AGENTS.md v16.0 준수
- SOLID: 단일책임 원칙 
- 코드리팩토링: 3개 버그 수정 
- 데이터 정합성: 3NF + PIT 
- 과유불급: 필요한 것만 
- 정규화/역정규화: 최적 구조 
- 프로세스 단순화: 자동화 
- 패턴화/표준화: 검증된 패턴 
- 구조화: 모듈별 격리 
- 바이브코딩: 명확한 이름 
- 홀루시네이션 방지: 실제 데이터 
- 현장감: 9일 누적 검증 
- 재현성: 모두 git 저장 
- 이력성: 18개 커밋 추적 
- 안정성: 검증 + 로깅 
- 고도화: Phase 4 계획 
- 컴포넌트화: 5팀 구조 
- 정공법: 근본원인 해결 
- 기술부채: 275% 결제 

## 상태
- Phase 1: 자동 진행 중 (252+ 일)
- Phase 2: 검증 완료 (GO)
- Phase 3: 배포 완료 (LIVE)
- Phase 4: 계획 완료

## 생성 파일
- deployment/phase3-release/ (배포 패키지)
- deployment/phase3_migration.sql (마이그레이션)
- deployment/kartsell-host.service (systemd)
- deployment/kartsell.conf (nginx)
- phase2_results.json (검증 결과)

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-08-11 23:05:06 +09:00

1 line
12 KiB
JavaScript

import{B as e,O as t,St as n,Z as r,c as i,f as a,h as o,j as s,l as c,m as l,o as u,p as d,r as f,rt as p,s as m,u as h,xt as g,yt as _}from"./runtime-core.esm-bundler-A2Q6NZG4.js";import{o as v}from"./vue-router-Bd1zjzX-.js";import{t as y}from"./_plugin-vue_export-helper-BDNMzG2s.js";import{n as b}from"./useUiAdapter-DDuKOUtM.js";import{n as x}from"./KsInlineMessage-t_pxFiJO.js";import{a as S,c as C,d as w,i as T,l as ee,n as te,o as E,r as D,s as O,t as k,u as A}from"./components-KD31URIh.js";import{t as j}from"./PageLayout-D6g_--o1.js";import{t as M}from"./StandardScreenBoundary-C_di8oG7.js";import"./BatchOperationsPageV2-CKYquI9Y.js";import"./ScorecardDashboardPage-BoB21s1d.js";var N={key:0},P={key:0},F={key:1},I=y(o({__name:`CrudWorkspaceLayout`,props:{asideWidth:{default:`24rem`},dense:{type:Boolean,default:!1}},setup(e){return(n,r)=>(t(),h(`section`,{class:_([`ks-crud-workspace`,{dense:e.dense}]),style:g({"--ks-crud-aside":e.asideWidth})},[n.$slots.toolbar?(t(),h(`header`,N,[s(n.$slots,`toolbar`,{},void 0,!0)])):c(``,!0),m(`div`,{class:_([`ks-crud-workspace__body`,{"has-aside":n.$slots.aside}])},[m(`main`,null,[s(n.$slots,`default`,{},void 0,!0)]),n.$slots.aside?(t(),h(`aside`,P,[s(n.$slots,`aside`,{},void 0,!0)])):c(``,!0)],2),n.$slots.actions?(t(),h(`footer`,F,[s(n.$slots,`actions`,{},void 0,!0)])):c(``,!0)],6))}}),[[`__scopeId`,`data-v-15ab0b08`]]),L=o({__name:`SearchListCrudPage`,props:{title:{},subtitle:{},state:{},evidence:{},warning:{},error:{}},emits:[`retry`],setup(n){let r=n;return(n,o)=>(t(),i(j,{title:r.title,subtitle:r.subtitle,status:r.state,"as-of":r.evidence?.asOf,version:r.evidence?.version},a({actions:e(()=>[s(n.$slots,`actions`)]),summary:e(()=>[s(n.$slots,`summary`)]),filters:e(()=>[s(n.$slots,`filters`)]),default:e(()=>[l(M,{state:r.state,warning:r.warning,"stale-at":r.evidence?.asOf,onRetry:o[0]||=e=>n.$emit(`retry`)},{default:e(()=>[l(I,null,a({default:e(()=>[s(n.$slots,`default`)]),_:2},[n.$slots.detail?{name:`aside`,fn:e(()=>[s(n.$slots,`detail`)]),key:`0`}:void 0]),1024)]),_:3},8,[`state`,`warning`,`stale-at`])]),_:2},[n.$slots.footer?{name:`footer`,fn:e(()=>[s(n.$slots,`footer`)]),key:`0`}:void 0]),1032,[`title`,`subtitle`,`status`,`as-of`,`version`]))}}),R=Object.freeze([{id:`T01`,name:`검색·목록형 CRUD`,component:`SearchListCrudPage`,intendedUse:`상품·고객·권한·데이터 Run·추천 검토함`,mandatoryStates:[`READY`,`LOADING`,`EMPTY`,`WARN`,`ERROR`,`UNAUTHORIZED`,`FORBIDDEN`,`PARTIAL`],mandatoryEvidence:[`filter-url`,`permission`,`export-auth`],antiPatterns:[`client-side-all-data`,`unbounded-export`]},{id:`T02`,name:`상세 조회형`,component:`DetailReadPage`,intendedUse:`투자제안·상품·포트폴리오·백테스트 결과`,mandatoryStates:[`LOADING`,`WARN`,`EXPIRED`,`READONLY`,`UNAUTHORIZED`],mandatoryEvidence:[`as-of`,`version-set`,`audit`],antiPatterns:[`mutable-evidence`,`hidden-version`]},{id:`T03`,name:`등록·편집 Form`,component:`EditFormPage`,intendedUse:`고객·IPS·비용표·권한·설정`,mandatoryStates:[`DIRTY`,`CONFLICT`,`PROCESSING`,`ERROR`],mandatoryEvidence:[`zod`,`if-match`,`idempotency-key`],antiPatterns:[`silent-overwrite`,`pinia-form-cache`]},{id:`T04`,name:`Master-Detail`,component:`MasterDetailCrudPage`,intendedUse:`고객-IPS·추천-항목·Watch-Stage·대사 Run-Break`,mandatoryStates:[`LOADING`,`EMPTY`,`DIRTY`,`PARTIAL`,`CONFLICT`],mandatoryEvidence:[`route-selection`,`unsaved-guard`,`version`],antiPatterns:[`selection-only-local`,`detail-n-plus-one`]},{id:`T05`,name:`검토·승인 Workbench`,component:`ApprovalWorkbenchPage`,intendedUse:`추천·모델·정정·대사 maker-checker`,mandatoryStates:[`WARN`,`EXPIRED`,`CONFLICT`,`PROCESSING`,`READONLY`],mandatoryEvidence:[`maker-checker`,`reason`,`warning-ack`],antiPatterns:[`self-approval`,`approval-without-evidence`]},{id:`T06`,name:`단계 Wizard`,component:`StepWizardPage`,intendedUse:`고객 온보딩·IPS·리밸런싱·Backfill`,mandatoryStates:[`DIRTY`,`ERROR`,`PROCESSING`,`READONLY`],mandatoryEvidence:[`resume`,`branch`,`impact-revalidation`],antiPatterns:[`single-huge-form`,`skip-validation`]},{id:`T07`,name:`Dashboard·Scorecard`,component:`ScorecardDashboardPage`,intendedUse:`고객 대시보드·일평가·운영 SLO`,mandatoryStates:[`LOADING`,`EMPTY`,`WARN`,`PARTIAL`],mandatoryEvidence:[`metric-definition`,`sample-size`,`table-alternative`],antiPatterns:[`chart-only`,`metric-definition-hidden`]},{id:`T08`,name:`Batch·데이터 운영`,component:`BatchOperationsPageV2`,intendedUse:`수집·Feature·추천·평가·Backfill`,mandatoryStates:[`PROCESSING`,`WARN`,`ERROR`,`PARTIAL`,`READONLY`],mandatoryEvidence:[`job-run`,`watermark`,`replay-scope`],antiPatterns:[`blind-retry`,`overwrite-reprocess`]},{id:`T09`,name:`대사·예외 처리`,component:`ReconciliationExceptionPage`,intendedUse:`KIS/원장 대사·데이터 격리·DQ 예외`,mandatoryStates:[`WARN`,`CONFLICT`,`PROCESSING`,`READONLY`],mandatoryEvidence:[`before-after`,`correction`,`audit`],antiPatterns:[`direct-db-fix`,`delete-break`]},{id:`T10`,name:`버전 비교·거버넌스`,component:`VersionGovernancePage`,intendedUse:`모델·정책·설정·데이터 공급원 승격`,mandatoryStates:[`WARN`,`EXPIRED`,`READONLY`,`CONFLICT`],mandatoryEvidence:[`same-dataset-cost`,`gate-pack`,`rollback`],antiPatterns:[`auto-promotion`,`different-cohort-comparison`]},{id:`T11`,name:`대량 입력(Fast Grid Entry)`,component:`FastEntryGridPage`,intendedUse:`임계값·파라미터·비용표 등 다건 일괄 입력`,mandatoryStates:[`DIRTY`,`PROCESSING`,`ERROR`,`PARTIAL`],mandatoryEvidence:[`cell-level-validation`,`paste-audit`,`idempotency-key`],antiPatterns:[`silent-bulk-overwrite`,`unbounded-paste`]},{id:`T12`,name:`작업 큐(Work Queue)`,component:`WorkQueuePage`,intendedUse:`SLA 큐 깊이·DQ 격리·대사 Break 등 예외구동 작업목록`,mandatoryStates:[`LOADING`,`EMPTY`,`WARN`,`PARTIAL`],mandatoryEvidence:[`queue-depth-source`,`exception-count-definition`],antiPatterns:[`chart-only-no-actionable-list`]}]),z={class:`ks-card summary`},B={class:`ks-card summary`},V={class:`ks-card summary`},H={class:`ks-card summary`},U={class:`filters`},W={class:`ks-card component-preview`,"aria-labelledby":`component-preview-title`},G={class:`preview-grid`},K={class:`status-row`},q={class:`ks-card component-preview`,"aria-labelledby":`form-template-title`},J={class:`ks-inline`},Y={class:`ks-card detail`},X={key:1},Z=y(o({__name:`UiStandardPage`,setup(a){let o=b(),s=v(),c=r(``),g=r(`ALL`),_=r(null),y=r({name:``,amount:null,date:null,acknowledged:!1}),j=r(!1),M=r(!1),N=u(()=>M.value?[...y.value.name.trim()?[]:[{field:`name`,message:`이름을 입력하세요.`}],...y.value.amount!==null&&y.value.amount>0?[]:[{field:`amount`,message:`0보다 큰 금액을 입력하세요.`}],...y.value.acknowledged?[]:[{field:`acknowledged`,message:`확인 항목에 동의하세요.`}]]:[]);function P(){M.value=!0}function F(){y.value={name:``,amount:null,date:null,acknowledged:!1},M.value=!1}let I=[{label:`전체 화면 유형`,value:`ALL`},...R.map(e=>({label:`${e.id} · ${e.name}`,value:e.id}))],Z=u(()=>s.getRoutes().map(e=>{let t=e.meta.screenId,n=e.meta.templateId,r=e.meta.title;if(e.path===`/internal/ui-standard`||e.meta.internalOnly===!0||typeof t!=`string`||typeof n!=`string`||typeof r!=`string`)return null;let i=R.find(e=>e.id===n);return i?{screenId:t,name:r,templateId:i.id,templateName:i.name,path:e.path}:null}).filter(e=>e!==null).filter(e=>g.value===`ALL`||e.templateId===g.value).filter(e=>!c.value||`${e.screenId} ${e.name} ${e.templateId} ${e.templateName}`.toLowerCase().includes(c.value.toLowerCase()))),ne=u(()=>Z.value.map(e=>({label:`${e.screenId} · ${e.name}`,value:e.screenId}))),Q=u(()=>Z.value.find(e=>e.screenId===_.value)??null),re=[{field:`screenId`,header:`화면 ID`,width:130},{field:`name`,header:`기능 화면`,minWidth:180},{field:`templateId`,header:`유형`,width:90},{field:`templateName`,header:`화면 유형`,minWidth:180}];function $(){Q.value&&s.push(Q.value.path)}return(r,a)=>(t(),i(p(L),{title:`표준 UI 패턴`,subtitle:`화면 유형을 선택하고, 실제 화면 ID를 선택해 기능 화면을 엽니다.`,state:`READY`,evidence:{asOf:`2026-08-02`,version:`UI-CONTRACT-4.0`}},{actions:e(()=>[l(p(x),{label:`선택 화면 열기`,disabled:!Q.value,onClick:$},null,8,[`disabled`])]),summary:e(()=>[m(`div`,z,[m(`strong`,null,n(p(R).length),1),a[8]||=m(`span`,null,`화면 타입`,-1)]),m(`div`,B,[m(`strong`,null,n(Z.value.length),1),a[9]||=m(`span`,null,`연결된 기능 화면`,-1)]),m(`div`,V,[l(p(E),{value:p(o).descriptor.id,severity:`info`},null,8,[`value`]),m(`span`,null,n(p(o).descriptor.vendor),1)]),m(`div`,H,[l(p(E),{value:`자동주문 OFF`,severity:`warning`}),a[10]||=m(`span`,null,`고정 경계`,-1)])]),filters:e(()=>[m(`div`,U,[l(p(w),{modelValue:c.value,"onUpdate:modelValue":a[0]||=e=>c.value=e,label:`화면 검색`,placeholder:`화면 ID, 기능명 또는 화면 유형`},null,8,[`modelValue`]),l(p(A),{modelValue:g.value,"onUpdate:modelValue":a[1]||=e=>g.value=e,label:`화면 유형`,options:I},null,8,[`modelValue`]),l(p(A),{modelValue:_.value,"onUpdate:modelValue":a[2]||=e=>_.value=e,label:`열 화면`,placeholder:`화면 ID를 선택하세요`,options:ne.value},null,8,[`modelValue`,`options`])])]),detail:e(()=>[m(`div`,Y,[a[16]||=m(`h2`,null,`선택한 기능 화면`,-1),Q.value?(t(),h(f,{key:0},[m(`p`,null,[m(`strong`,null,n(Q.value.screenId),1),d(` · `+n(Q.value.name),1)]),m(`p`,null,n(Q.value.templateId)+` · `+n(Q.value.templateName),1),m(`p`,null,[m(`code`,null,n(Q.value.path),1)]),l(p(x),{label:`선택 화면 열기`,onClick:$})],64)):(t(),h(`p`,X,`목록에서 화면 ID를 선택한 뒤, “선택 화면 열기”를 누르세요.`))])]),default:e(()=>[l(p(S),{rows:Z.value,columns:re,height:`25rem`,"row-selection":`none`},null,8,[`rows`]),m(`section`,W,[a[14]||=m(`div`,null,[m(`h2`,{id:`component-preview-title`},`컴포넌트 동작 프리뷰`),m(`p`,null,`공유 UI 포트를 실제 상태로 확인하는 내부 전용 카탈로그입니다.`)],-1),m(`div`,G,[m(`div`,null,[a[11]||=m(`h3`,null,`Actions`,-1),l(p(x),{label:`기본 버튼`,severity:`secondary`}),l(p(x),{label:`주의 상태`,severity:`warning`})]),m(`div`,null,[a[12]||=m(`h3`,null,`Status`,-1),m(`div`,K,[l(p(E),{value:`READY`,severity:`success`}),l(p(E),{value:`REVIEW`,severity:`warning`}),l(p(E),{value:`BLOCKED`,severity:`danger`})])]),m(`div`,null,[a[13]||=m(`h3`,null,`Inputs`,-1),l(p(w),{label:`텍스트 필드`,"model-value":`샘플 값`}),l(p(A),{label:`선택 필드`,"model-value":`READY`,options:[{label:`준비`,value:`READY`},{label:`검토`,value:`REVIEW`}]})])])]),m(`section`,q,[a[15]||=m(`div`,null,[m(`h2`,{id:`form-template-title`},`입력 유형 기능 테스트`),m(`p`,null,`서버 요청 없이 shared UI의 입력·검증·읽기전용 조합을 확인합니다.`)],-1),l(p(k),{errors:N.value},null,8,[`errors`]),l(p(D),{title:`기본 입력`,description:`필수 항목과 유효성 상태를 조작할 수 있습니다.`},{actions:e(()=>[l(p(x),{label:j.value?`편집 허용`:`읽기 전용`,severity:`secondary`,onClick:a[3]||=e=>j.value=!j.value},null,8,[`label`])]),default:e(()=>[l(p(T),{"aria-label":`기능 테스트 입력`},{default:e(()=>[l(p(w),{modelValue:y.value.name,"onUpdate:modelValue":a[4]||=e=>y.value.name=e,label:`이름`,disabled:j.value,error:M.value&&!y.value.name.trim()?`필수 입력입니다.`:void 0},null,8,[`modelValue`,`disabled`,`error`]),l(p(O),{modelValue:y.value.amount,"onUpdate:modelValue":a[5]||=e=>y.value.amount=e,label:`금액`,disabled:j.value,error:M.value&&(!y.value.amount||y.value.amount<=0)?`0보다 커야 합니다.`:void 0},null,8,[`modelValue`,`disabled`,`error`]),l(p(C),{modelValue:y.value.date,"onUpdate:modelValue":a[6]||=e=>y.value.date=e,label:`기준일`,disabled:j.value},null,8,[`modelValue`,`disabled`]),l(p(ee),{modelValue:y.value.acknowledged,"onUpdate:modelValue":a[7]||=e=>y.value.acknowledged=e,label:`검증 조건을 확인했습니다`,disabled:j.value},null,8,[`modelValue`,`disabled`]),l(p(te),{span:`full`},{default:e(()=>[m(`div`,J,[l(p(x),{label:`검증 실행`,disabled:j.value,onClick:P},null,8,[`disabled`]),l(p(x),{label:`초기화`,severity:`secondary`,onClick:F})])]),_:1})]),_:1})]),_:1})])]),_:1}))}}),[[`__scopeId`,`data-v-377ed979`]]);export{Z as default};