릴리즈 이력이 전혀 없는 저장소입니다. 태그 0개(git tag / git for-each-ref 로 확인, refs/tags 자체가 없음), GitHub Release 없음, .github/workflows 없음, CHANGELOG.md·docs/RELEASE*.md 등 릴리즈 노트 없음, 릴리즈/패키징 스크립트(scripts/, Makefile, *.sh) 없음. package.json 의 version 은 Vite 스캐폴드 기본값 "0.0.0" 이고 first commit(ab700ba, 2026-09-03) 이후 14개 PR 동안 한 번도 변경된 적이 없으며 private:true 라 레지스트리 배포 대상도 아닙니다. README.md 의 '버전 기록' 표에 있는 유일한 행(0.0.1 / 2025-01-01 / 초기 릴리스)은 저장소 first commit 보다 앞선 날짜의 문서 보일러플레이트로, 실제 릴리즈와 대응하지 않고 갱신된 적도 없습니다. 배포는 .gitlab-ci.yml 이 main 브랜치 push 에 반응해 빌드 후 PVC 로 복사하는 연속 배포 방식이며 태그(CI_COMMIT_TAG)에 반응하는 잡이 전혀 없습니다(docs/08-GitLab-CICD-가이드.md 의 태그 rules 는 문법 예시일 뿐 실제 파이프라인에 없음). 따라서 버전 체계·태그 형식·릴리즈 노트 양식을 새로 정하는 것은 사람의 판단이 필요하므로 아무것도 만들지 않고 건너뜁니다.
src/utils/eventBus.js:1 이 mitt 을 import 하지만 package.json dependencies 에 없고 package-lock 에는 전이 의존성으로만 존재한다. 상위 패키지가 mitt 을 떼면 빌드가 깨진다. 2026-09-10 재확인: 여전히 미해결. dependencies 명시 + lock 갱신만 하면 되는 저위험 작업.
2026-09-10
serviceCode 리터럴 표기 불일치 상수화(myNoteBook / MyNotebook / multiModal)
3/2/S
대기
myNoteBookStorage 는 2026-09-08 에 대소문자 무시 비교로 고쳤지만 PopWorkFlow.vue:118 ('myNoteBook'), MyAgentList.vue:307 ('MyNotebook'), appList.js:192 ('multiModal') 이 각각 정확 일치 비교를 하고 있어 같은 종류의 조회 실패가 남아 있다. constants.js 에 SERVICE_CODE 를 두고 대소문자 무시 매처를 공유하면 정리된다. 서버가 어느 표기를 주는지 확정되지 않아 비교를 느슨하게 하는 방향으로만 접근해야 한다.
2026-09-10
ESLint + Prettier 도입
3/2/M
대기
탭·스페이스 혼재(ocrStatusCheckStore.js·markdown.js·myNoteBookStorage.js·useOpenAppStroage.js 는 스페이스, 나머지 utils/storage 는 탭)로 초기 diff 노이즈가 커서 계속 보류. 도입한다면 포맷 커밋과 규칙 커밋을 분리해야 한다.
2026-09-10
Header.vue 가 OCR 사용 여부와 무관하게 마운트 즉시 3초 폴링 시작
3/3/M
대기
Header.vue:47-50 이 앱 진입 시 무조건 loadStatus() + startPolling(3000) 을 호출해, OCR 을 한 번도 쓰지 않은 사용자도 /tools/ocr/status 를 3초마다 호출한다. 미완료 항목이 있을 때만 폴링을 시작하도록 바꾸면 되지만 배지 표시 회귀 검증이 필요하다.
2026-09-10
globalLoading 참조 카운트 부재로 병렬 요청 중 스피너 조기 종료
3/3/S
대기
src/utils/globalLoading.js 의 isLoading 이 boolean 이라 A/B 두 요청이 겹칠 때 B 가 먼저 stopLoading 하면 A 진행 중에도 스피너가 사라진다. 카운터로 바꾸면 start/stop 짝이 안 맞는 호출부에서 스피너가 영영 안 사라질 위험이 있어 호출부 전수 확인(20곳 이상)이 선행돼야 한다. export 된 timeoutMs(600000) 는 모듈 안에서 아무 데도 쓰이지 않는다.
2026-09-10
빌드 산출물 단일 청크 6.2MB 코드 스플리팅(manualChunks)
3/3/M
대기
2026-09-10 빌드에서도 index-*.js 6,221kB (gzip 1,697kB) 로 동일. mermaid 관련은 이미 동적 청크로 분리돼 있고 monaco-editor/pdfjs-dist/milkdown 이 본체에 남아 있다. 초기 로딩 경로 회귀 위험이 있어 검증 방법이 필요하다.
2026-09-10
mapAppToCard(type='prompt') 결과에 to 가 없어 프롬프트 카드 클릭 무반응
3/3/S
대기
src/utils/common.js 의 prompt 분기는 id/title/desc/text/label 만 만들고 to 를 넣지 않는다. GlobalSearch 프롬프트 카드에 이동 대상이 없는 설계인지 확인 필요.
2026-09-10
parseUtcToKstDate 의 +9h 이중 적용 정리
3/4/S
대기
로컬 타임존이 KST 일 때 +9h 가 이중 적용된다. 운영 표시값에 광범위한 영향이 있어 계속 보류. 서버가 UTC/KST 중 무엇을 주는지 확인이 선행돼야 한다.
2026-09-10
chatTransitionStorage 만료/형식 방어 보강
2/1/S
대기
readChatTransitionData 는 try/catch 로 크래시는 없지만 timestamp 가 없는 레코드는 영구히 만료되지 않고, JSON.parse 결과가 배열이면 그대로 반환해 Chat/Index.vue:717 이 routeSentFiles 폴백 대신 빈 배열을 쓰게 된다. 2026-09-10 재확인: 코드 변동 없음. commonStorage 쪽 정규화 패턴을 옮기면 된다. 실사용 크래시는 확인되지 않아 가치는 낮음.
2026-09-10
buildSettingActions support 모드에서 isSimpleBotBool 미전달 시 액션 중복 노출
2/1/S
대기
ButtonSetting.vue 의 visibleActions 는 show === undefined 를 노출로 처리하지만, support 모드를 쓰는 SupportList/MyAgentList 는 둘 다 ref(false) 로 초기화된 isSimpleBot 을 넘기고 ServiceList/InfoSearchList 는 mode 가 고정이라 support 분기에 닿지 않는다. 현재 실사용 영향 없음. !!isSimpleBotBool 정규화 + 모드별 회귀 테스트는 여전히 유효한 하드닝.
2026-09-10
ChatStorageDetail.copyAnswer 가 복사를 두 번 실행하고 토스트를 두 번 띄운다
2/1/S
대기
src/views/ChatStorage/ChatStorageDetail.vue:678-699 에서 try 블록 안에서 fallbackCopy 로 복사·토스트를 한 뒤, 아래 '폴백' 주석 코드가 같은 fallbackCopy 를 한 번 더 실행해 토스트가 두 번 뜬다(클립보드 API 를 쓰던 시절의 잔재로 보임). 뒤쪽 블록만 지우면 되는 저위험 정리지만 UI 확인이 있으면 좋다.
2026-09-10
getAppInfo 의 'all' 폴백이 SIDEMENU_APP_LIST 대신 APP_LIST 에 캐싱
2/2/S
대기
appList.getAppInfo 는 serviceMenu='all' 일 때 SIDEMENU_APP_LIST 를 읽는데, 사이드메뉴 목록에 없는 앱이라 재조회할 때는 fetchAndCacheAppList(serviceMenu='all', cacheKey='all_{userId}') 가 APP_LIST 쪽에 저장한다. 다음 호출에서 그 캐시를 아무도 읽지 않아 같은 앱에 진입할 때마다 /app/list 재조회가 발생한다. 2026-09-10 발견. 캐시 키를 맞추면 되지만 두 API 의 응답 shape 이 달라(getSideList 는 평탄, appListGet 은 serviceCode 행) 섞어 저장해도 되는지 확인이 필요하다.
2026-09-10
useDateRangeFilter 의 시작일>종료일 보정 누락 및 테스트 공백
2/2/S
대기
src/utils/common.js 의 watch 는 endDate < startDate 인 경우만 보정하고 역방향은 두지만, useDateRangeFilter 를 import 하는 .vue 가 하나도 없어 현재 사용자 영향이 없다. 사용처가 생기면 우선순위 상승.
2026-09-10
열린 앱(openApp/openAppDoc) 저장 레코드 표기 불일치 정규화 및 임베딩 파일명 복원 공용화
4/2/M
완료
useOpenAppStroage 가 { appId, prjId, appName, _raw }(Sidemenu/appUpdateLog) 와 서버 원본({ app_id, ... })(appList.getAppInfo) 두 표기를 섞어 저장해, 원본이 저장되면 Sidemenu.gotoChatMain 의 심플봇 임베딩 재확인이 건너뛰어졌다. normalizeOpenAppRecord/normalizeOpenAppDoc 으로 쓰기·읽기 모두 정규화하고, |__| 파일명 역변환을 common.parseStoredFileName 으로 합쳤다. 테스트 29건 추가(총 382건).
2026-09-10
교훈 (깨졌던 변경)
2026-09-08 demoted — 자율화 단계 release → low-risk: 회귀(reverted) 2026-09-08T00:00:35+09:00
원장 (에이전트가 남긴 기록)
2026-09-03
선택: 공통 유틸 Vitest 단위 테스트 도입 및 파일 확장자 판별 오류 수정 (가치 5 / 위험 2 / 작업량 M)
결과: 성공
요약: 자동화 테스트가 전무했던 저장소에 Vitest + jsdom 환경(vitest.config.js, npm test / npm run test:watch)을 추가하고 common, page, chatFile, favToggle 유틸에 대한 단위 테스트 61건을 작성했다. 테스트 작성 중 발견한 실제 버그 2건(isPreviewablXlsx 가 ['xlsx','xlsx'] 로 중복 비교해 .xls 를 인식하지 못하던 문제, 파일 판별 함수들이 file 이 null 일 때 file.file_name 접근으로 예외를 던지던 문제)을 고치고 회귀 테스트로 고정했다. 검증은 npm test(61건 통과)와 npm run build:dev(빌드 성공)로 수행했다. README 와 docs/09-테스트가이드-총론.md 에 실행 방법을 문서화했다.
보류 아이디어:
markdown.js 의 DOMPurify 설정/onclick 파싱 경로 XSS 하드닝 검토 (가치 4 / 위험 3 / M)
ESLint + Prettier 도입 (탭·스페이스 혼재로 초기 diff 노이즈가 커서 보류) (가치 3 / 위험 2 / M)
parseUtcToKstDate 가 로컬 타임존이 KST 일 때 +9h 를 이중 적용하는 문제 정리 (운영 표시값 영향 커서 보류) (가치 3 / 위험 4 / S)
useFileAccept 의 mode 값 불일치(modeKey 는 ‘ocr’, 제외 확장자 분기는 ‘OCR’) 수정 (가치 3 / 위험 3 / S)
docs/07-개선사항-권장사항.md 의 스토어 패턴 통일(useServiceStore → Pinia defineStore) (가치 3 / 위험 4 / L)
2026-09-03
선택: 파일 업로드 accept/용량 정책 오류 수정 및 단위 테스트 추가 (가치 4 / 위험 2 / 작업량 M)
결과: 성공
요약: useFileAccept 에서 실사용 버그 4건을 고쳤다 — (1) SupportOcr.vue 가 넘기는 "OCR" 이 소문자 키 맵과 매칭되지 않아 ocrFileLimit 정책이 적용되지 않고 항상 기본 50MB 였던 문제, (2) 괄호 표기 확장자("엑셀(xlsx,xls)")를 파싱할 때 "." + [".xlsx"].join() 으로 점을 중복해 ..xlsx 같은 잘못된 accept 토큰을 만들던 문제, (3) OCR 제외 확장자 필터가 괄호 표기 항목과 fallback 목록에는 적용되지 않던 문제, (4) 세 개 화면(SupportOcr, PopSimpleBot, PopSimpleBotUpdate)의 용량 초과 알림이 바이트 값을 “52428800MB” 처럼 MB 로 표기하던 문제. accept/용량 계산을 순수 함수(parseExtensionEntry, buildAcceptString, buildFallbackAccept, resolveMaxSizeMb)로 분리하고 회귀 테스트 18건을 추가했다. 검증은 npm test(총 79건 통과)와 npm run build:dev(빌드 성공)로 수행했다.
보류 아이디어:
markdown.js 의 DOMPurify 설정/onclick 파싱 경로 XSS 하드닝 검토 (가치 4 / 위험 3 / M)
ESLint + Prettier 도입 (탭·스페이스 혼재로 초기 diff 노이즈가 커서 보류) (가치 3 / 위험 2 / M)
parseUtcToKstDate 가 로컬 타임존이 KST 일 때 +9h 를 이중 적용하는 문제 정리 (운영 표시값 영향 커서 보류) (가치 3 / 위험 4 / S)
buttonSettingPolicy.buildSettingActions 모드별 액션 구성 단위 테스트 보강 (가치 2 / 위험 1 / S)
빌드 산출물 단일 청크 6.2MB 문제(manualChunks 코드 스플리팅) 개선 (가치 3 / 위험 3 / M)
2026-09-04
선택: 앱 정보 조회(getAppInfo) 캐시/재조회 경로 오류 수정 (가치 5 / 위험 2 / 작업량 M)
결과: 성공
요약: src/utils/appList.js 의 getAppInfo 에서 실사용 버그 3건을 고쳤다 — (1) 사이드메뉴 캐시 미존재 시 기본값이 {} 라 appList.length === 0 이 항상 false 가 되어 API 재조회를 건너뛰고 {}.find 에서 TypeError 가 나 {} 를 반환하던 문제(딥링크로 /chatmain?appId= 진입 시 앱 정보 유실), (2) serviceMenu === 'all' 재조회가 serviceCode === 'all' 로 필터링돼 항상 빈 목록을 반환, 사이드메뉴 상위 30개에 없는 앱은 절대 찾지 못하던 문제, (3) 재조회로 찾은 앱은 writeOpenApp/simple 모드 후처리를 건너뛰던 경로 불일치. 순수 함수(toAppArray, pickServiceRows, flattenAppList, findAppInfo)로 분리하고 findAppInfo 의 app_id 문자열 비교·빈 키·비배열 입력 방어를 추가했다. 검증은 npm test(총 100건 통과, 신규 21건)와 npm run build:dev(빌드 성공)로 수행했다.
보류 아이디어:
markdown.js 의 DOMPurify 설정/onclick 파싱 경로 XSS 하드닝 검토 (가치 4 / 위험 3 / M)
useFileAttach 의 알림 문구가 옵션(maxFileSize/maxTotalSize)을 무시하고 20MB/100MB 로 하드코딩된 문제 수정 (가치 3 / 위험 1 / S)
useAppList.getFormattedAppList 의 String(app?.knowledge_info?.status) ?? '' 가 문자열 “undefined” 를 만드는 문제 정리 (Sidemenu.vue 에도 동일 코드 중복) (가치 2 / 위험 1 / S)
mitt 이 src/utils/eventBus.js 에서 직접 import 되는데 package.json 직접 의존성에 없어 전이 의존성에 기대고 있는 문제 (가치 3 / 위험 1 / S)
빌드 산출물 단일 청크 6.2MB 문제(manualChunks 코드 스플리팅) 개선 (가치 3 / 위험 3 / M)
2026-09-05
선택: 앱 이동(updateLogAndGo) 쿼리 유실·외부 링크 취약점 수정 (가치 4 / 위험 2 / 작업량 M)
결과: 성공
요약: 모든 앱 카드 클릭이 거치는 공통 네비게이션 경로 src/utils/appUpdateLog.js 에서 5건을 고쳤다 — (1) buildTo 가 to 를 문자열로 받으면 extraQuery 를 그대로 버려 img 모드 앱의 ?mode=img 가 유실되던 문제(문자열도 {path, query, hash} 로 병합하도록 변경), (2) 같은 이유로 nextTo.path 가 undefined 라 /chatmain 판별에 실패해 writeOpenApp 이 호출되지 않고 챗 화면이 앱 컨텍스트를 잃던 문제(toRoutePath 도입), (3) mode === 'link' 의 window.open(urlLink, '_blank') 이 noopener 없이 열려 reverse tabnabbing 이 가능하고 javascript:/data: url_link 가 그대로 실행되던 문제(isSafeExternalLink/openExternalLink 로 http·https·mailto 만 허용, 차단 시 일반 이동으로 폴백), (4) iframe 모드도 동일하게 검증해 위험한 값은 빈 문자열로 넘기도록(ChatFrame 은 빈 값이면 “불러올 메신저가 없습니다” 표시) 처리, (5) url_link 가 문자열이 아닐 때 .replace 로 예외가 나던 문제. 순수 헬퍼(toRoutePath, buildTo, resolveUserIdFromEmail, applyUserIdTemplate, isSafeExternalLink, openExternalLink)로 분리하고 회귀 테스트 30건을 추가했다. 검증은 npm test(총 130건 통과, 신규 30건)와 npm run build:dev(빌드 성공)로 수행했다.
보류 아이디어:
markdown.js 의 DOMPurify 설정/onclick 파싱 경로 XSS 하드닝 검토 (transformAppAnchors 가 < 를 실태그로 되돌림) (가치 4 / 위험 3 / M)
useFileAttach 알림 문구 하드코딩(20MB/100MB) 및 msg.includes('10') 로 서버 오류를 용량초과로 오판하는 문제 수정 (가치 3 / 위험 1 / S)
useTableUtils.sortData 가 모든 값을 getNum 으로 숫자 변환해 텍스트·날짜 컬럼 정렬이 동작하지 않는 문제 (현재 .vue 에서 미사용) (가치 2 / 위험 2 / S)
mapAppToCard(item, idx, 'prompt') 결과에 to 가 없어 GlobalSearch 프롬프트 카드 클릭이 무반응인 문제 (의도 확인 필요) (가치 3 / 위험 3 / S)
빌드 산출물 단일 청크 6.2MB 문제(manualChunks 코드 스플리팅) 개선 (가치 3 / 위험 3 / M)
2026-09-06
선택: 마크다운 렌더링(parseMarkdown) 코드블록/표 래퍼 속성 유실 및 외부 링크 하드닝 (가치 4 / 위험 2 / 작업량 M)
결과: 성공
요약: 챗 본문 렌더링 공통 경로 src/utils/markdown.js 에서 4건을 고쳤다 — (1) transformAppAnchors 의 code 태그 보호가 여는 태그를 버리고 <code>${content}</code> 로 복원해 class="language-xx hljs" 가 사라지고 highlight.js 테마(github-dark)의 .hljs 배경/기본색이 적용되지 않던 문제(매칭 전체를 보관·복원하도록 변경), (2) 불필요한 div 제거 로직이 renderer.table 이 만드는 .table-wrap 래퍼까지 걷어내 컬럼이 많은 표에서 가로 스크롤(overflow-x:auto)이 동작하지 않던 문제(table-wrap 래퍼만 마커로 보호 후 복원), (3) target 이 지정된 a 태그에 rel 이 없어 reverse tabnabbing 이 가능하던 문제(DOMPurify afterSanitizeAttributes 훅으로 rel="noopener noreferrer" 강제), (4) 본문에 MARK_PLACEHOLDER_n__/DATE_PLACEHOLDER_n__ 문자열이 우연히 포함되면 복원 단계에서 undefined 구조분해로 TypeError 가 나거나 “undefined” 가 출력되던 문제. 검증은 npm test(총 155건 통과, 신규 25건 — 수정 전 코드에서 6건 실패함을 확인)와 npm run build:dev(빌드 성공)로 수행했다.
보류 아이디어: useFileAttach 알림 문구 하드코딩(20MB/100MB) 및 msg.includes('10') 로 서버 오류를 용량초과로 오판하는 문제 수정 (가치 3 / 위험 1 / S) · parseUtcToKstDate 가 로컬 타임존이 KST 일 때 +9h 를 이중 적용하는 문제 정리 (가치 3 / 위험 4 / S) · mapAppToCard(item, idx, 'prompt') 결과에 to 가 없어 GlobalSearch 프롬프트 카드 클릭이 무반응인 문제 (가치 3 / 위험 3 / S) · mitt 이 eventBus.js 에서 import 되는데 package.json 직접 의존성에 없어 전이 의존성에 기대는 문제 (가치 3 / 위험 1 / S) · 빌드 산출물 단일 청크 6.2MB 문제(manualChunks 코드 스플리팅) 개선 (가치 3 / 위험 3 / M)
2026-09-06
선택: useFileAttach 용량 안내 문구 옵션 반영 및 업로드 오류 오판 수정 (가치 3 / 위험 1 / 작업량 S)
결과: 성공
요약: src/utils/useFileAttach.js 에서 6건을 고쳤다 — (1) 개당/전체 용량 초과 안내가 maxFileSize/maxTotalSize 옵션을 무시하고 “20MB”/”100MB” 로 하드코딩돼 있어 다른 정책을 주면 잘못된 숫자를 안내하던 문제(formatSizeLimit 로 옵션 값 기반 생성), (2) msg.includes('10') || msg.includes('100') 때문에 “10초 후 다시 시도해 주세요” 같은 무관한 서버 오류까지 용량 초과로 오판하던 문제(HTTP 413 + 용량 키워드 판별 isSizeLimitError 로 교체), (3) err.response.data.message 가 문자열이 아닐 때 .includes 에서 예외가 나던 문제, (4) initialList 를 그대로 ref 에 담아 removeFile 시 호출부 배열까지 변경되던 문제, (5) removeFile 의 인덱스 범위 미검증(음수 인덱스로 끝 항목이 삭제됨), (6) .env 같은 dotfile 을 확장자 “env” 로 잘못 판정하던 문제. 순수 헬퍼(formatBytes, formatSizeLimit, getExt, isSizeLimitError, resolveErrorMessage, normalizeServerFiles)로 분리하고 회귀 테스트 26건을 추가했다. 검증은 npm test(총 156건 통과, 신규 26건)와 npm run build:dev(빌드 성공)로 수행했다.
보류 아이디어: parseUtcToKstDate 가 로컬 타임존이 KST 일 때 +9h 를 이중 적용하는 문제 정리 (가치 3 / 위험 4 / S) · mitt 이 eventBus.js 에서 import 되는데 package.json 직접 의존성에 없어 전이 의존성에 기대는 문제 (가치 3 / 위험 1 / S) · mapAppToCard(item, idx, 'prompt') 결과에 to 가 없어 GlobalSearch 프롬프트 카드 클릭이 무반응인 문제 (가치 3 / 위험 3 / S) · useDateRangeFilter 가 endDate < startDate 만 보정하고 startDate > endDate 는 보정하지 않는 문제 + 미검증 컴포저블 테스트 공백 (가치 3 / 위험 2 / S) · 빌드 산출물 단일 청크 6.2MB 문제(manualChunks 코드 스플리팅) 개선 (가치 3 / 위험 3 / M)
2026-09-06
선택: 토큰 재발급 인터셉터 리프레시 락 누수 수정 (가치 5 / 위험 2 / 작업량 M)
결과: 성공
요약: 모든 API 요청이 거치는 src/api/common/interceptors.js 에서 6건을 고쳤다 — (1) 리프레시 성공 경로가 isRefreshing = false 를 실행하지 않고 곧바로 return instance(originalRequest) 해서, 최초 토큰 재발급 이후 발생하는 모든 401 요청이 if (isRefreshing) 대기열에 들어간 뒤 아무도 깨워주지 않아 영원히 settle 되지 않던 문제(응답 200+error.code===401 경로와 응답 400+code 401/403 경로 양쪽 모두, 화면은 로딩 스피너에 멈춤), (2) retryAllSubscribers 가 newAccessToken 을 즉시 null 로 지워 “다른 API 에서 이미 리프레시됨” 재시도 최적화가 사실상 죽어 있던 문제, (3) 대기열 순회 중 재진입으로 구독이 유실될 수 있던 문제(먼저 비우고 순회), (4) errCode === 401 엄격 비교라 서버가 문자열 "401" 을 주면 재발급을 건너뛰던 문제(같은 파일 400 분기는 == '401' 느슨 비교라 내부 불일치 — isUnauthorizedCode 로 통일), (5) error.config 가 없는 에러에서 error.config.url.includes('/app/simple') 가 TypeError 를 던지던 BZ01 처리 경로, (6) readAuth() 결과와 instance.defaults.headers.common 미존재 시 예외 방어. 리프레시 종료를 finishRefresh(ok) 로 일원화하고 isUnauthorizedCode/extractTokenDataFromResponse 를 export 해 테스트 23건을 추가했다(수정 전 코드에서 6건 실패함을 확인). 검증은 npm test(총 153건 통과)와 npm run build:dev(빌드 성공)로 수행했다.
보류 아이디어: mitt 이 eventBus.js 에서 import 되는데 package.json 직접 의존성에 없어 전이 의존성에 기대는 문제 (가치 3 / 위험 1 / S) · buildSettingActions 의 support 모드에서 isSimpleBotBool 이 undefined 면 ButtonSetting 이 show: undefined 를 노출로 처리해 편집/워크플로우 액션이 동시에 뜨는 문제 (가치 3 / 위험 1 / S) · globalLoading 이 참조 카운트 없이 boolean 이라 병렬 요청 중 하나만 끝나도 스피너가 사라지는 문제 (가치 3 / 위험 3 / S) · parseUtcToKstDate 가 로컬 타임존이 KST 일 때 +9h 를 이중 적용하는 문제 정리 (가치 3 / 위험 4 / S) · 빌드 산출물 단일 청크 6.2MB 문제(manualChunks 코드 스플리팅) 개선 (가치 3 / 위험 3 / M)
2026-09-07
선택: 로그아웃 후 사용자 정보 잔존 및 탭 간 동기화 누락 수정 (가치 4 / 위험 2 / 작업량 M)
결과: 성공
요약: src/storage/userStorage.js 에서 6건을 고쳤다 — (1) readUser() 가 userRef.value 를 반환하는데 clearUser() 는 저장소만 비우고 userRef 를 그대로 둬서 로그아웃(HeaderSetting/redirectToLogin/인터셉터 401) 이후에도 이전 사용자의 이름·이메일·is_admin 이 계속 반환되던 문제, (2) 파일 상단 인라인 if (!window._userStorageSyncInitialized) 블록이 플래그를 먼저 세워서 아래 setupUserStorageSync() 가 항상 조기 반환 → 다른 탭 변경 시 현재 탭의 sessionStorage 가 전혀 갱신되지 않던 문제(다른 탭에서 로그아웃해도 이 탭을 새로고침하면 readFromStorage 가 sessionStorage 를 우선 읽어 로그아웃된 사용자가 되살아남), 리스너를 하나로 통합, (3) storage 이벤트의 JSON.parse(e.newValue) 미보호로 깨진 값에서 리스너가 예외를 던지던 문제, (4) 사용자 변경 통지가 writeUser 에서만 발생해 clearUser·다른 탭 변경이 구독자(Sidemenu)에게 전달되지 않던 문제와 구독자 하나의 예외가 나머지 통지를 끊던 문제, (5) writeUser 저장 형태를 순수 함수 normalizeUserRecord 로 분리, (6) Sidemenu.vue 가 addUserChangeListener 의 해제 함수를 버려 마운트마다 리스너가 누적되던 누수(+ 로그아웃 통지에 반응해 앱 목록을 재조회하지 않도록 readUser() 가드). 검증은 npm test(총 151건 통과, 신규 21건 — 수정 전 코드에서 11건 실패함을 확인)와 npm run build:dev(빌드 성공)로 수행했다.
보류 아이디어: mitt 이 eventBus.js 에서 import 되는데 package.json 직접 의존성에 없어 전이 의존성에 기대는 문제 (가치 3 / 위험 1 / S) · globalLoading 이 참조 카운트 없이 boolean 이라 병렬 요청 중 하나만 끝나도 스피너가 사라지는 문제 (가치 3 / 위험 3 / S) · authStorage.readAuth 가 localStorage 폴백 시 sessionStorage 를 복구하지 않고 clearAuth 도 통지가 없어 userStorage 와 동작이 어긋나는 문제 (가치 3 / 위험 2 / S) · parseUtcToKstDate 가 로컬 타임존이 KST 일 때 +9h 를 이중 적용하는 문제 정리 (가치 3 / 위험 4 / S) · 빌드 산출물 단일 청크 6.2MB 문제(manualChunks 코드 스플리팅) 개선 (가치 3 / 위험 3 / M)
2026-09-08
선택: OCR 사용 횟수 초기화 누락 및 상태 스토어 정합성 수정 (가치 4 / 위험 2 / 작업량 M)
결과: 성공
요약: 미테스트 상태였던 src/storage/ocrStatusCheckStore.js 에서 8건을 고쳤다 — (1) checkOcrStatus 가 if (todayOcrList.length) 가드 때문에 오늘 목록이 비면 useCount 갱신을 건너뛰어 어제 저장된 사용 횟수가 localStorage 에 그대로 남고 다음 날 첫 변환이 “동시 OCR 변환 사용 횟수를 초과했습니다” 로 막히던 문제(항상 재계산 + 저장 값에 savedDate 를 남겨 날짜가 바뀌면 로드 시점에 0 으로 복구), (2) loadStatus() 가 스토어 객체를 통째로 교체하면서 completedItems 키를 빠뜨려 Header/Home 마운트 때마다 undefined 가 되고 SupportOcrList·SupportOcrListCurrent 의 watch 대상이 사라지던 문제, (3) getMaxUseOcr 가 res?.data?.body ?? 5 로 비숫자 응답(객체 등)을 그대로 반환해 useCount < NaN 이 항상 false 가 되어 OCR 이 영구히 막히던 문제, (4) canUseOcr 가 로그인 정보 확인 전에 최대 횟수 API 를 호출하던 문제, (5) startPolling(0) 이 간격 0 의 setInterval 을 만들어 사실상 무한 루프가 되던 문제(최소 1초 하한 — 옛 코드로 테스트를 돌리면 실제로 힙 OOM 발생), (6) calculateUseCount 가 group_id 없는 항목들을 한 그룹으로 묶어 여러 요청을 1회로 세던 문제, (7) checkOcrStatus 가 API 실패 시 문서와 달리 undefined 를 반환하던 문제, (8) todayOcrList/저장 JSON 이 배열·객체가 아닐 때의 방어. 순수 헬퍼(todayKey, toCount, normalizeStoredStatus, calculateUseCount, extractCompletedItems)로 분리하고 테스트 43건을 추가했다(수정 전 코드에서 12건 실패 + 폴링 테스트는 OOM 으로 크래시함을 확인). 검증은 npm test(총 245건 통과)와 npm run build:dev(빌드 성공)로 수행했다.
보류 아이디어: mitt 이 eventBus.js 에서 import 되는데 package.json 직접 의존성에 없어 전이 의존성에 기대는 문제 (가치 3 / 위험 1 / S) · authStorage.readAuth 가 localStorage 폴백 시 sessionStorage 를 복구하지 않고 clearAuth 도 통지가 없어 userStorage 와 동작이 어긋나는 문제 (가치 3 / 위험 2 / S) · Header.vue 가 OCR 사용 여부와 무관하게 마운트 즉시 3초 폴링을 시작해 statusOcr API 를 계속 호출하는 문제 (가치 3 / 위험 3 / M) · globalLoading 이 참조 카운트 없이 boolean 이라 병렬 요청 중 하나만 끝나도 스피너가 사라지는 문제 (가치 3 / 위험 3 / S) · 빌드 산출물 단일 청크 6.2MB 문제(manualChunks 코드 스플리팅) 개선 (가치 3 / 위험 3 / M)
2026-09-08
선택: 노트북/업무도구 목록 캐시 사용자 간 잔존 및 조회 실패 수정 (가치 4 / 위험 2 / 작업량 M)
결과: 성공
요약: 미테스트 상태였던 src/storage/myNoteBookStorage.js 에서 7건을 고쳤다 — (1) getMyNoteBookList 이 serviceCode == "myNoteBook" 정확 일치로 찾는데 서버/다른 화면(MyAgentList.vue:307)은 MyNotebook 표기를 쓰고 있어 API 폴백 경로가 항상 빈 목록을 반환하고 매번 재조회하던 문제(대소문자 무시 비교로 통일), (2) find 로 첫 행만 써서 같은 serviceCode 가 여러 행으로 오면 나머지 appList 를 버리던 문제(flatMap 으로 합침), (3) clearMyList 가 export 만 되고 호출부가 전혀 없어 로그아웃(HeaderSetting.logout / common.js redirectToLogin) 후에도 이전 사용자의 노트북·업무도구 목록이 localStorage 에 남아 다음 사용자의 checkAppExists(Chat/Main.vue:508, Chat/Index.vue:710·1688)가 남의 앱에 true 를 반환하고 SupportImgList 가 남의 app_id 로 이동하던 문제(clearMyNoteBookCache() 추가 후 두 로그아웃 경로에 연결), (4) readMyList/readWorkList 의 JSON.parse 미보호로 캐시가 손상되면 checkAppExists/modeCheckCount 가 예외를 던져 챗 진입 흐름이 끊기던 문제, (5) 저장값이 배열이 아닐 때 .some/.find TypeError 및 body.length 접근 방어, (6) 로그인 정보 미확인 시 쿼리스트링에 문자열 user_id=undefined 가 실리던 문제, (7) item.app_id === appId 엄격 비교라 서버가 숫자 app_id 를 주면 라우트 쿼리(문자열)와 일치하지 않던 문제. 순수 헬퍼(parseStoredList, extractAppList)로 분리하고 SupportImgList.vue 의 getAppInfo.app_id (img 앱이 없으면 undefined 접근 TypeError)도 함께 고쳤다. 검증은 npm test(총 232건 통과, 신규 30건 — 수정 전 코드에 신규 스펙을 돌려 10건 실패함을 확인)와 npm run build:dev(빌드 성공)로 수행했다.
보류 아이디어: mitt 이 eventBus.js 에서 import 되는데 package.json 직접 의존성에 없어 전이 의존성에 기대는 문제 (가치 3 / 위험 1 / S) · authStorage.readAuth 가 localStorage 폴백 시 sessionStorage 를 복구하지 않고 clearAuth 도 통지가 없어 userStorage 와 동작이 어긋나는 문제 (가치 3 / 위험 2 / S) · Header.vue 가 OCR 사용 여부와 무관하게 마운트 즉시 3초 폴링을 시작해 statusOcr API 를 계속 호출하는 문제 (가치 3 / 위험 3 / M) · globalLoading 이 참조 카운트 없이 boolean 이라 병렬 요청 중 하나만 끝나도 스피너가 사라지는 문제 (가치 3 / 위험 3 / S) · 빌드 산출물 단일 청크 6.2MB 문제(manualChunks 코드 스플리팅) 개선 (가치 3 / 위험 3 / M)
2026-09-09
선택: 인증/채팅세션 저장소 방어 및 탭 동기화 플래그 충돌 수정 (가치 3 / 위험 2 / 작업량 M)
결과: 성공
요약: 미테스트 상태였던 src/storage/authStorage.js 와 src/storage/sessionChatStroage.js 에서 7건을 고쳤다 — (1) sessionChatStroage 의 초기화 가드가 window._userStorageSyncInitialized 로 userStorage 와 같은 플래그를 써서, 먼저 로드된 모듈이 플래그를 세우면 나머지 모듈의 storage 리스너가 아예 등록되지 않던 문제(router/index.js 가 MainLayout 을 먼저 import 하므로 실제로는 sessionChat 쪽 탭 동기화가 죽어 있었고, import 순서가 바뀌면 2026-09-07 에 고친 userStorage 탭 동기화가 대신 죽는 잠재 회귀), 전용 플래그 _sessionChatStorageSyncInitialized 로 분리, (2) authStorage 가 sessionStorage/localStorage 접근을 전혀 감싸지 않아 쿠키 차단·시크릿 모드에서 readAuth/getAccessToken 이 예외를 던지던 문제 — 이 경로는 request 인터셉터를 포함한 모든 API 호출이 지나므로 앱 전체가 멈춘다(userStorage 와 동일하게 try/catch 로 방어), (3) safeParse 가 'null'/'"str"'/'[1,2]' 같은 비객체 JSON 을 그대로 돌려줘 interceptors.js 의 readAuth().authority 에서 예외가 나거나 setAccessToken 의 { ...prev } 가 문자열 인덱스 키를 퍼뜨리던 문제(항상 평범한 객체로 정규화), (4) writeAuth 가 객체가 아닌 값을 받으면 토큰 4개를 전부 빈 문자열로 덮어 세션이 조용히 끊기던 문제, (5) readAuth/readSessionChat 이 localStorage 폴백 시 sessionStorage 를 복구하지 않아 매 호출마다 두 저장소를 읽던 문제(모듈 주석의 “sessionStorage 우선” 의도와 불일치), (6) clearAuth/clearSessionChat 의 storage 예외 미방어, (7) 로그아웃 경로(HeaderSetting.logout, common.redirectToLogin)가 clearUser/clearAuth/clearMyNoteBookCache 는 부르면서 clearSessionChat 은 빠뜨려 이전 사용자의 채팅 세션 id 가 localStorage 에 남던 문제(라우터 가드가 비-chat 경로에서만 지우므로 다음 사용자가 chat 딥링크로 진입하면 남의 세션을 불러올 수 있음). 검증은 npm test(총 308건 통과, 신규 33건 — 수정 전 코드에 신규 스펙을 돌려 11건 실패함을 확인)와 npm run build:dev(빌드 성공)로 수행했다.
보류 아이디어: mitt 이 eventBus.js 에서 import 되는데 package.json 직접 의존성에 없어 전이 의존성에 기대는 문제 (가치 3 / 위험 1 / S) · Header.vue 가 OCR 사용 여부와 무관하게 마운트 즉시 3초 폴링을 시작해 statusOcr API 를 계속 호출하는 문제 (가치 3 / 위험 3 / M) · globalLoading 이 참조 카운트 없이 boolean 이라 병렬 요청 중 하나만 끝나도 스피너가 사라지는 문제 (가치 3 / 위험 3 / S) · serviceCode 리터럴 표기 불일치(myNoteBook/MyNotebook/multiModal) 상수화 (가치 3 / 위험 2 / S) · 빌드 산출물 단일 청크 6.2MB 문제(manualChunks 코드 스플리팅) 개선 (가치 3 / 위험 3 / M)
2026-09-09
선택: 공용 저장소(commonStorage) 사용자 캐시 로그아웃 잔존 및 storage 예외 방어 (가치 4 / 위험 2 / 작업량 M)
결과: 성공
요약: 미테스트 상태였던 src/storage/commonStorage.js 에서 6건을 고쳤다 — (1) 로그아웃 경로(HeaderSetting.logout, common.redirectToLogin)가 clearUser/clearAuth/clearMyNoteBookCache/clearSessionChat 은 부르면서 common 저장소는 그대로 둬, 이전 사용자의 기본앱(defaultBot)·서비스 목록(serviceList)·사이드메뉴 앱 목록(sidemenuAppList)·열린 앱/문서 상태(openApp/openAppDoc/docState)·개인 헤더 설정이 남아 다음 사용자에게 그대로 보이던 문제(레이아웃 상태 sidebar_open/headerBar 만 남기는 clearCommonUserData() 추가 후 두 로그아웃 경로에 연결 — 2026-09-08 의 myNoteBook 캐시 잔존과 같은 종류이며 이쪽이 상위 저장소), (2) localStorage 접근을 전혀 감싸지 않아 쿠키 차단·시크릿 모드에서 commonStorage.get 이 예외를 던져 MainLayout setup 과 스토어 초기화가 통째로 실패하던 문제(authStorage 와 동일한 readRaw/writeRaw/removeRaw 방어), (3) safeParse 가 'null'/'"str"'/'[1,2]' 를 그대로 돌려줘 get(key) 가 문자열 인덱스를 반환하거나 remove(key) 의 delete 에서 TypeError 가 나(useSettingStore.clearHeaderSetting·useServiceStore 의 clear 가 예외로 끊김) 문제, (4) set() 이 배열도 통과시켜 인덱스 키를 저장소에 퍼뜨리던 문제, (5) 용량 초과(SIDEMENU_APP_LIST + SERVICE_LIST 가 한 덩어리)·순환 참조 시 setItem 이 호출부로 예외를 던져 Sidemenu 앱 목록 저장에서 렌더가 끊기던 문제, (6) remove() 가 없는 key 에도 저장소를 다시 쓰던 불필요한 write. 검증은 npm test(총 332건 통과, 신규 24건 — 수정 전 코드에 신규 스펙을 돌려 13건 실패함을 확인)와 npm run build:dev(빌드 성공)로 수행했다.
보류 아이디어: mitt 이 eventBus.js 에서 import 되는데 package.json 직접 의존성에 없어 전이 의존성에 기대는 문제 (가치 3 / 위험 1 / S) · Header.vue 가 OCR 사용 여부와 무관하게 마운트 즉시 3초 폴링을 시작해 statusOcr API 를 계속 호출하는 문제 (가치 3 / 위험 3 / M) · globalLoading 이 참조 카운트 없이 boolean 이라 병렬 요청 중 하나만 끝나도 스피너가 사라지는 문제 (가치 3 / 위험 3 / S) · serviceCode 리터럴 표기 불일치(myNoteBook/MyNotebook/multiModal) 상수화 (가치 3 / 위험 2 / S) · appDefaultStorage.getDefaultApp 이 실패 시 빈 문자열을 반환하고 res.data?.body[0] 로 body 미존재 시 TypeError 를 내는 문제 정리 (가치 2 / 위험 1 / S)
2026-09-09
선택: 기본앱 조회(appDefaultStorage) 실패 처리 및 마운트 시 중복 API 호출 제거 (가치 3 / 위험 2 / 작업량 S)
결과: 성공
요약: 미테스트 상태였던 src/storage/appDefaultStorage.js 에서 5건을 고쳤다 — (1) getDefaultApp 이 실패 시 readDefaultApp 의 계약(객체
null)과 다른 빈 문자열을 반환하던 문제를 null 로 정정, (2) res.data?.body[0] 가 body 미존재 시 TypeError 를 던지고(밖의 try/catch 에 삼켜져 “기본앱이 없습니다” 로만 보임) 서버가 배열 대신 객체 하나를 줄 때도 실패하던 문제를 순수 헬퍼 extractDefaultApp 으로 분리, (3) Sidemenu·Chat/Main·Chat/Index 가 같은 시점에 마운트되어 각각 readDefaultApp 을 부르는데 첫 진입에는 전부 캐시 미스라 /app/default 를 동시에 3번 호출하던 문제를 진행 중 요청 재사용(inflight)으로 1회로 합침, (4) 응답 대기 중 로그아웃·사용자 전환이 일어나면 이전 사용자의 기본앱이 common 캐시에 다시 써지던 문제(로그아웃은 clearCommonUserData 로 저장소만 비우고 모듈 메모리의 진행 중 요청은 못 지운다)를 완료 시점 user_id 재확인으로 차단, (5) 캐시에 객체가 아닌 값이 들어 있으면 그대로 반환하던 문제를 정규화 후 재조회로 변경. 함께 Sidemenu.gotoChatMain 이 기본앱이 없을 때 String(undefined) 로 ?appId=undefined 를 붙여 이동하던 버그도 고쳤다. 검증은 npm test(총 347건 통과, 신규 15건 — 수정 전 코드에 신규 스펙을 돌려 10건 실패함을 확인)와 npm run build:dev(빌드 성공)로 수행했다.
보류 아이디어: mitt 이 eventBus.js 에서 import 되는데 package.json 직접 의존성에 없어 전이 의존성에 기대는 문제 (가치 3 / 위험 1 / S) · ChatStorageDetail 의 computed(() => getDefaultBot()) 이 async 함수를 감싸 Promise 를 담고 있어 isGeneralChat 이 항상 false 인 문제 (가치 3 / 위험 2 / S) · Header.vue 가 OCR 사용 여부와 무관하게 마운트 즉시 3초 폴링을 시작해 statusOcr API 를 계속 호출하는 문제 (가치 3 / 위험 3 / M) · globalLoading 이 참조 카운트 없이 boolean 이라 병렬 요청 중 하나만 끝나도 스피너가 사라지는 문제 (가치 3 / 위험 3 / S) · serviceCode 리터럴 표기 불일치(myNoteBook/MyNotebook/multiModal) 상수화 (가치 3 / 위험 2 / S)
2026-09-10
선택: 대화저장 상세(ChatStorageDetail)의 기본앱 비동기 처리 오류 및 봇 식별자 파싱 공용화 (가치 3 / 위험 2 / 작업량 S)
결과: 성공
요약: src/views/ChatStorage/ChatStorageDetail.vue 가 async 함수 readDefaultApp 을 computed(() => getDefaultBot()) 로 감싸 Promise 를 담고 있어, pickBotIds(Promise) 가 항상 빈 식별자를 돌려주고 appId/prjId 의 기본앱 폴백이 죽어 있던 문제를 고쳤다 — 일반챗봇 히스토리(라우트에 appId 쿼리가 없는 진입)에서 pInf.chat.getHistorySession(대화 불러오기)과 inf.chat.feedbackAnswer(피드백 저장)가 app_id/project_id 를 빈 문자열로 보내고, isGeneralChat 이 영원히 false 라 헤더가 항상 “전문가 AI와 대화중” 으로 표시됐다. defaultBot 을 ref 로 바꾸고 initByRoute 가 세션 조회 전에 ensureDefaultBot() 으로 선로딩하도록 했다(readDefaultApp 은 캐시·inflight 합치기가 있어 추가 호출이 생기지 않는다). 함께 Chat/Main·Chat/Index·ChatStorageDetail 에 각각 복사돼 있던 pickBotIds 를 appDefaultStorage.pickBotIds 로 합쳐(app_id/appId/appID·project_id/prj_id/prjId 표기 모두 처리, 비객체·Promise 방어) Main.vue 의 app_id 단일 표기 사본을 제거했다. 검증은 npm test(총 353건 통과, 신규 6건)와 npm run build:dev(빌드 성공, dist 는 커밋 전 삭제)로 수행했다. 다만 이 저장소에는 컴포넌트 테스트 환경이 없어 .vue 수정 자체는 순수 헬퍼 테스트와 코드 리뷰로만 확인했다.
보류 아이디어: mitt 이 package.json 직접 의존성에 없어 전이 의존성에 기대는 문제 (가치 3 / 위험 1 / S) · Header.vue 가 OCR 사용 여부와 무관하게 마운트 즉시 3초 폴링을 시작하는 문제 (가치 3 / 위험 3 / M) · globalLoading 이 참조 카운트 없이 boolean 이라 병렬 요청 중 하나만 끝나도 스피너가 사라지는 문제 (가치 3 / 위험 3 / S) · serviceCode 리터럴 표기 불일치(myNoteBook/MyNotebook/multiModal) 상수화 (가치 3 / 위험 2 / S) · 빌드 산출물 단일 청크 6.2MB 코드 스플리팅(manualChunks) (가치 3 / 위험 3 / M)
2026-09-10
선택: 열린 앱(openApp/openAppDoc) 저장 레코드 표기 불일치 정규화 및 임베딩 파일명 복원 공용화 (가치 4 / 위험 2 / 작업량 M)
결과: 성공
요약: 미테스트 상태였던 src/storage/useOpenAppStroage.js 에서 3건을 고쳤다 — (1) writeOpenApp 이 두 가지 표기를 섞어 저장하던 문제: Sidemenu.toggleApp/gotoChatMain 과 appUpdateLog.updateLogAndGo 는 { appId, prjId, appName, _raw } 로 쓰는데 appList.getAppInfo:96 은 서버 원본({ app_id, project_id, name, mode })을 그대로 저장하고, 읽는 쪽(Sidemenu.gotoChatMain:63, appList.getAppInfo:71)은 항상 appId/_raw 를 꺼내 쓴다. 원본이 한 번 저장되면 openedAppInfo?.appId 가 undefined 라 심플봇 진입 후 로고 클릭 시 임베딩 재확인(clearOpenAppDoc + getUsed + writeOpenAppDoc)이 통째로 건너뛰어져 Chat/Main.resetInfo 가 낡은 임베딩 파일 목록/실패 안내를 보여줬고, getAppInfo 의 캐시 히트 경로도 죽어 있었다 — normalizeOpenAppRecord 로 어느 표기로 들어와도 같은 형태로 저장하고 readOpenApp 도 읽는 시점에 정규화해 이미 원본 표기로 남아 있는 캐시까지 맞췄다(_raw 가 객체가 아니면 null 로 둬 캐시 히트 오인 방지). (2) normalizeOpenAppDoc 으로 items 를 항상 배열, appId 를 항상 문자열로 맞췄다 — 서버가 knowledgeDetail_info 를 주지 않으면 writeOpenAppDoc({...undefined, appId: undefined}) 가 저장되고 라우트에 appId 가 없을 때 undefined == undefined 로 매칭돼 Chat/Main.resetInfo 의 docinfo.items.map 이 TypeError 를 던졌다. (3) makeTimestampedFileName('|__|' 구분자) 의 역변환이 Chat/Main.vue:508 과 PopSimpleBotUpdate.vue:209 에 각각 복사돼 있었는데, 구분자가 없는 파일명이면 slice(0, -1) 로 마지막 글자가 잘리고(보고서.pdf → 보고서.pd.pdf) 확장자가 없으면 slice(-1) 이 타임스탬프 마지막 글자를 확장자로 붙였다 — common.parseStoredFileName 으로 합치고 document/file_name 누락 방어도 함께 넣었다. 검증은 npm test(총 382건 통과, 신규 29건 — 수정 전 코드에 신규 스펙을 돌려 30건 실패함을 확인)와 npm run build:dev(빌드 성공, dist 는 커밋 전 삭제)로 수행했다. 다만 이 저장소에는 컴포넌트 테스트 환경이 없어 .vue 수정 자체는 순수 헬퍼 테스트와 코드 리뷰로만 확인했다.
보류 아이디어: mitt 이 package.json 직접 의존성에 없어 전이 의존성에 기대는 문제 (가치 3 / 위험 1 / S) · getAppInfo 의 ‘all’ 폴백이 SIDEMENU_APP_LIST 가 아닌 APP_LIST 에 캐싱해 매번 재조회하는 문제 (가치 2 / 위험 2 / S) · Header.vue 가 OCR 사용 여부와 무관하게 마운트 즉시 3초 폴링을 시작하는 문제 (가치 3 / 위험 3 / M) · globalLoading 이 참조 카운트 없이 boolean 이라 병렬 요청 중 하나만 끝나도 스피너가 사라지는 문제 (가치 3 / 위험 3 / S) · serviceCode 리터럴 표기 불일치(myNoteBook/MyNotebook/multiModal) 상수화 (가치 3 / 위험 2 / S)