13.VBA 소스 — 복사·다운로드·소스 분석(심화)
BX-v7.xlam 에 든 VBA 소스 48개 모듈(15,337줄)을 리본 기능별로 묶었습니다. 내려받은 xlam 의 VBA 편집기(Alt+F11)에서 보는 코드와 같습니다.
방법 1 · 코드 복사해서 붙여넣기
- 모듈의 [코드 복사]를 누릅니다.
- Excel 에서 Alt+F11 → [삽입] → [모듈](clsXXX 는 [클래스 모듈])을 만들고, 속성 창(F4)의 (이름)을 모듈 이름(예:
modMain)으로 바꾼 뒤 붙여 넣습니다(Ctrl+V).
방법 2 · .bas / .cls 파일 가져오기
- [.bas 다운로드](클래스는 .cls)로 받습니다. 모두 받으려면 전체 소스 zip(188 KB).
- VBA 편집기 [파일] → [파일 가져오기](Ctrl+M)에서 고릅니다. 파일은 VBA 편집기 내보내기 형식(
Attribute VB_Name포함, CRLF 줄바꿈, CP949 인코딩)이라 가져오면 모듈 이름과 한글이 그대로 살아 있습니다. 메모장 등에서 UTF-8 로 다시 저장하면 한글이 깨지니 받은 파일을 그대로 가져오세요.
대부분의 기능은 공통 모듈(modUtil · modRibbonUI · modUITheme · frmChoice …)을 함께 부르므로 한 모듈만 가져오면 '정의되지 않았습니다' 오류가 날 수 있습니다. 폼(frmChoice · frmInput)은 화면 배치가 xlam 안 .frx 에 있어 코드만 제공하고, 리본 버튼도 xlam 안 XML 에 있습니다. 바로 쓰실 때는 완성본 BX-v7.xlam 을, 공부·수정용으로는 이 소스를 쓰세요.
심화과정 — 기능마다 [심화과정: 소스 분석]을 펼치면 리본 버튼에서 시작하는 호출 흐름, 핵심 VBA 객체, 고쳐 쓰는 방법이 나옵니다. 줄 번호를 누르면 해당 모듈 소스의 그 줄로 이동합니다.
01UBIS예산확인 UBIS
세출예산요구서·102-B 장표·팀원 명단을 불러와 예산보드(팀·세부사업·사업명 슬라이서)·세부산출내역·담당자별 집행 현황 대시보드를 만듭니다. 슬라이서로 팀을 고르면 빈 행은 자동으로 숨습니다(clsBoardEvents).
리본 진입점(UB_Ribbon → UB_Run)과 태그별 기능 분기, 보드 통합문서 확보(EnsureBoard)·설치, 전체 새로고침 순서(새로고침)를 담당하는 오케스트레이션 모듈입니다.
소스 보기 (171줄)
세출예산요구서와 장표(102-A/102-B) 파일을 읽기 전용으로 열어 숨김 원본 시트로 값만 복사합니다. 머리글로 장표 종류를 판별하고 예산소관부서명으로 우리 부서 행만 걸러 냅니다.
소스 보기 (238줄)
부서명단 파일(xlsx·docx·hwpx·hwp)에서 성명·팀·직위 열을 찾아 부서담당자 시트 5행부터 기록합니다. hwpx 는 압축을 풀어 정규식으로 표를 읽고, hwp 는 한글 자동화로 hwpx 로 바꾼 뒤 같은 경로로 읽습니다.
소스 보기 (227줄)
부서담당자·단위사업카드 시트를 만들고, 사용자가 노란 칸에 입력한 담당자·과제카드·장표 직접지정 값을 키 기준으로 읽어 모델에 덮어씁니다(ReadCardOverrides, ReadStaffTeams).
소스 보기 (321줄)
요구서(사업항목 1레벨)와 장표(세목 말단행 합산)를 '세부사업|사업항목' 정규화 키로 매칭해 gItems 모델을 만듭니다. 실집행액 계산, 담당자→팀 배정, 장표 순서 정렬도 여기서 합니다.
소스 보기 (364줄)
요구서의 산출기초 행을 그대로 펼친 세부산출내역 표를 만들고, 메모 열을 키 기준으로 보존하며 산출내역·메모 키워드 찾기(자동 필터)와 슬라이서를 붙입니다.
소스 보기 (265줄)
메인 시트의 도형 버튼 배치(SetupMainSheet)와 사업항목별 집행 표·KPI·슬라이서(BuildMain), 인쇄, 상태 글자색 지정을 담당합니다.
소스 보기 (232줄)
숨김 '차트자료' 시트에 세목 단위 원본 표와 팀·세목·사업 TOP·담당자 집계 수식 블록을 만들고, 예산보드 시트에 KPI 카드와 격자 표를 수식 참조로 그려 슬라이서와 연동합니다.
소스 보기 (651줄)
장표를 사업항목→세출세목으로 집계(ItemSubtotals)해 '사업총액' 행과 '세목' 행을 번갈아 놓은 세부현황 표를 만듭니다. 이 집계 함수는 예산보드에서도 함께 씁니다.
소스 보기 (215줄)
여러 모듈이 함께 쓰는 공통 유틸리티 모듈입니다. 시트 이름 상수, 보드 통합문서 찾기(BW), 머리글 탐색(FindHeaderRow·HeaderMap·HCol), 문자열 정규화(CleanText·NormKey·MakeKey), 설정 시트 읽기/쓰기(CfgGet·CfgSet), 버튼·표·슬라이서·KPI 카드 생성, 속도 모드(SpeedOn·SpeedOff), 색상 함수를 제공합니다.
소스 보기 (659줄)
사업항목 1건을 담는 데이터 클래스입니다. 요구액·예산현액·집행액 등 필드와, 잔액·집행률·상태·담당자(수정값 우선)를 계산해 돌려주는 Property Get 을 가집니다.
소스 보기 (46줄)
WithEvents 로 Application 의 SheetCalculate 이벤트를 받아, 이름이 BX_HIDE_ 로 시작하는 범위에서 값이 빈 행을 자동으로 숨깁니다(슬라이서 선택 → 재계산 → 빈 줄 숨김).
소스 보기 (40줄)
심화과정: 소스 분석
리본 버튼("UBIS예산확인")의 태그를 UB_Run 이 받아 보드 통합문서를 확보한 뒤, 기관 예산시스템에서 내려받은 세출예산요구서와 집행 장표(102-A/102-B)를 숨김 원본 시트로 값만 복사합니다. 새로고침 때마다 두 원본을 Scripting.Dictionary 기반 모델(gItems, 항목 = clsProject)로 합치고, 그 모델로 메인 보드·세부현황·예산보드·세부산출내역 시트를 엑셀 표(ListObject)와 슬라이서, SUBTOTAL 수식으로 다시 그립니다. 슬라이서로 필터하면 '보이는 행만' 합산하는 수식 덕분에 KPI와 표가 즉시 따라 바뀝니다.
모듈 구성
modMain— 리본 진입점(UB_Ribbon → UB_Run)과 태그별 기능 분기, 보드 통합문서 확보(EnsureBoard)·설치, 전체 새로고침 순서(새로고침)를 담당하는 오케스트레이션 모듈입니다.modImport— 세출예산요구서와 장표(102-A/102-B) 파일을 읽기 전용으로 열어 숨김 원본 시트로 값만 복사합니다. 머리글로 장표 종류를 판별하고 예산소관부서명으로 우리 부서 행만 걸러 냅니다.modStaffImport— 부서명단 파일(xlsx·docx·hwpx·hwp)에서 성명·팀·직위 열을 찾아 부서담당자 시트 5행부터 기록합니다. hwpx 는 압축을 풀어 정규식으로 표를 읽고, hwp 는 한글 자동화로 hwpx 로 바꾼 뒤 같은 경로로 읽습니다.modMapping— 부서담당자·단위사업카드 시트를 만들고, 사용자가 노란 칸에 입력한 담당자·과제카드·장표 직접지정 값을 키 기준으로 읽어 모델에 덮어씁니다(ReadCardOverrides, ReadStaffTeams).modModel— 요구서(사업항목 1레벨)와 장표(세목 말단행 합산)를 '세부사업|사업항목' 정규화 키로 매칭해 gItems 모델을 만듭니다. 실집행액 계산, 담당자→팀 배정, 장표 순서 정렬도 여기서 합니다.modCalc— 요구서의 산출기초 행을 그대로 펼친 세부산출내역 표를 만들고, 메모 열을 키 기준으로 보존하며 산출내역·메모 키워드 찾기(자동 필터)와 슬라이서를 붙입니다.modBoard— 메인 시트의 도형 버튼 배치(SetupMainSheet)와 사업항목별 집행 표·KPI·슬라이서(BuildMain), 인쇄, 상태 글자색 지정을 담당합니다.modChart— 숨김 '차트자료' 시트에 세목 단위 원본 표와 팀·세목·사업 TOP·담당자 집계 수식 블록을 만들고, 예산보드 시트에 KPI 카드와 격자 표를 수식 참조로 그려 슬라이서와 연동합니다.modDetail— 장표를 사업항목→세출세목으로 집계(ItemSubtotals)해 '사업총액' 행과 '세목' 행을 번갈아 놓은 세부현황 표를 만듭니다. 이 집계 함수는 예산보드에서도 함께 씁니다.modUtil— 여러 모듈이 함께 쓰는 공통 유틸리티 모듈입니다. 시트 이름 상수, 보드 통합문서 찾기(BW), 머리글 탐색(FindHeaderRow·HeaderMap·HCol), 문자열 정규화(CleanText·NormKey·MakeKey), 설정 시트 읽기/쓰기(CfgGet·CfgSet), 버튼·표·슬라이서·KPI 카드 생성, 속도 모드(SpeedOn·SpeedOff), 색상 함수를 제공합니다.clsProject— 사업항목 1건을 담는 데이터 클래스입니다. 요구액·예산현액·집행액 등 필드와, 잔액·집행률·상태·담당자(수정값 우선)를 계산해 돌려주는 Property Get 을 가집니다.clsBoardEvents— WithEvents 로 Application 의 SheetCalculate 이벤트를 받아, 이름이 BX_HIDE_ 로 시작하는 범위에서 값이 빈 행을 자동으로 숨깁니다(슬라이서 선택 → 재계산 → 빈 줄 숨김).
코드 흐름 (줄 번호를 누르면 소스의 그 부분으로 이동)
modMain39~72줄(UB_Ribbon, UB_Run)① 리본 태그로 기능 분기리본 버튼의 콜백 UB_Ribbon 은control.tag문자열만 UB_Run 에 넘깁니다. UB_Run 은 먼저 EnsureBoard 로 보드 통합문서를 확보하고, 태그가UB_새보드이면 새 통합문서를 만들어 설치만 하고 끝냅니다. 그 밖의 태그는Select Case로 요구서선택·장표선택·새로고침버튼·예산보드 등 같은 이름의 프로시저를 호출하며, 오류는 EH 레이블에서 메시지 상자로 알려 줍니다.modMain75~88줄(EnsureBoard)② 보드 통합문서 확보와 설치추가 기능(ThisWorkbook.IsAddin)으로 실행 중이면 활성 통합문서에 '메인' 또는 '설정' 시트가 있는지 IsBoardWB(modUtil)로 확인하고, 없으면Workbooks.Add로 새 통합문서를 만들어설치(9-19줄)를 부릅니다. 설치는 CfgSheet 로 숨김 설정 시트를, modBoard 의 SetupMainSheet 로 ①~③·인쇄·예산보드 등 도형 버튼을 만들고 빈 기본 시트를 지웁니다. 이후 모든 모듈은 modUtil 의BW()로 이 보드 통합문서를 참조합니다.modImport39~96줄(장표선택)③ 장표 가져오기와 부서 필터GetOpenFilename의 다중 선택으로 장표 파일들을 받아Workbooks.Open(ReadOnly:=True, UpdateLinks:=0)으로 엽니다. '회계연도' 머리글 행을 찾아사업항목명1열이 있으면 102-B,사업항목1레벨이 있으면 102-A 로 판별하고, 부서명이 없으면 PickDept 로 부서 목록을 보여 고르게 합니다. CopyValues(109-148줄)는 UsedRange 를 배열로 읽어 예산소관부서명에 부서명이 포함된 행과 머리글 위쪽 행만 원본 시트에 한 번에 씁니다. 요구서선택(7-36줄)도 같은 방식으로 요구서를 복사하고 '부서)명 :' 뒤 글자를 부서명으로 저장합니다.modStaffImport147~198줄(ReadHwpxRows, ParseHwpxTables)④ 부서명단 파일 읽기(선택 기능)부서명단파일선택은 확장자별로 읽기 함수를 고릅니다. hwpx 는 파일을 src.zip 으로 복사한 뒤Shell.Application의 CopyHere 로 임시 폴더에 풀고, Contents 폴더의 section*.xml 을 ADODB.Stream(UTF-8)으로 읽습니다. ParseHwpxTables 는VBScript.RegExp로hp:tr→hp:tc→hp:t순서로 행·셀·글자를 뽑아 행 배열 Collection 을 만듭니다. WriteStaffRows(38-93줄)가 머리글에서 성명·팀·직위 열을 찾아 부서담당자 시트에 기록하고 [적용] 여부를 묻습니다.modMain98~129줄(새로고침)⑤ 전체 새로고침 순서원본 시트가 하나도 없으면 안내 후 끝내고,gAutoBusy플래그로 중복 실행을 막은 뒤 SpeedOn 으로 화면 갱신·계산을 끕니다. 단계마다gStep에 이름을 넣고 CleanupLegacySheets → BuildModel → (있으면) RefreshStaffSheet·RefreshCardSheet(modMapping) → BuildMain → BuildGrouped → BuildChartBoard → BuildCalc 순서로 호출합니다. 오류가 나면 EH 에서gStep을 함께 보여 주므로 어느 단계에서 실패했는지 바로 알 수 있습니다.modModel209~258줄(MatchAll)⑥ 요구서와 장표 매칭으로 모델 구성BuildModel(15-31줄)은 ParseRequest 로 요구서의 '1. 사업명' 행을 clsProject 로 만들고(금액은 천원 단위라* 1000), AggregateFromSheet 로 세목코드가 있는 장표 말단행을 같은 키로 합산합니다. MatchAll 은 요구서 항목마다 장표 키를 '직접지정 → 완전일치 → 같은 세부사업 안 포함관계(가장 긴 이름)' 순으로 찾고, 장표 순서를 기준으로 새 gItems 를 만들어 요구서의 담당자·요구액을 붙입니다. 장표가 없으면 요구액을 예산현액으로 대신 쓰며, 마지막에 ApplyOverride 와 AssignTeams 로 사용자 수정값과 팀을 반영합니다.modBoard64~187줄(BuildMain)⑦ 메인 보드 표·KPI·슬라이서ClearRowsKeepTables 로 표 머리글과 슬라이서는 남긴 채 5행 아래를 비우고, SortedKeys 순서로 clsProject 의 예산현액·집행액·잔액·집행률·상태를 2차원 배열에 담아 한 번에 씁니다. MakeTable 로 'tbl메인' 표를 만들거나 크기만 바꾸고, 합계 행·BarByRate 데이터 막대·ColorStatus 글자색을 적용합니다. KpiRow 는SUBTOTAL(109,tbl메인[예산현액])같은 수식 카드라 슬라이서로 거른 행만 합산합니다. 이어 modDetail 의 BuildGrouped 가 ItemSubtotals 결과로 '사업총액'/'세목' 행을 번갈아 놓은 세부현황 표를 같은 방식으로 만듭니다.modChart351~479줄(BuildChartBoard)⑧ 예산보드: 수식 블록과 빈 행 자동 숨김BuildChartData(101-265줄)가 숨김 '차트자료' 시트에 세목 단위 원본 표 'tbl차트원본'을 만들고, 팀·세목·사업 TOP·담당자·KPI 블록에 VisF 가 만든SUMPRODUCT(...SUBTOTAL(109,OFFSET(...)))수식을 넣습니다. SortedBlock 은 LARGE·INDEX·MATCH 로 필터 결과를 다시 정렬하고, BuildChartBoard 는 예산보드 시트에 DrawKpi·GridRow 로 이 셀들을 참조하는 카드와 격자 표를 그린 뒤 오른쪽에 슬라이서 4개를 붙입니다. BX_SetHideName 으로 BX_HIDE_ 이름 범위를 등록하고 BX_EnsureBoardEvents 로 clsBoardEvents 를 연결하면, 슬라이서 선택 → 재계산 → App_SheetCalculate 가 빈 행을 숨깁니다.modCalc69~248줄(BuildCalc)⑨ 세부산출내역 표와 메모 보존세부산출내역 시트가 이미 있으면 'tbl세부산출' 표의 키 열(세부사업|사업명|산출내역|세목코드|순서)과 메모 열을 Dictionary 에 먼저 담아 둡니다. 요구서 원본을 다시 훑어 소계·합계 행을 빼고 1레벨이면 번호를 떼어 내며, gItems 에서 팀·담당자·과제카드를 찾아 붙이고 같은 키의 메모를 되살립니다. 조건부 서식으로 레벨 1 행을 연파랑으로 칠하고, LeafSum 수식으로 세목코드가 있는 행만 합계를 내며, E7·G7 칸과 [찾기] 버튼(메모찾기)으로 자동 필터를 겁니다.
핵심 VBA 객체·함수
Application.GetOpenFilename(..., MultiSelect)— 파일 열기 대화상자를 띄워 경로를 받습니다. 취소하면 Boolean False 가 오므로VarType(fp) = vbBoolean으로 확인합니다.Workbooks.Open(fileName, ReadOnly:=True, UpdateLinks:=0)— 원본 파일을 읽기 전용·외부 링크 갱신 없이 열어 값만 복사한 뒤 저장하지 않고 닫습니다.Range.Value2 (2차원 배열 읽기/쓰기)— UsedRange 나 표 영역을 배열로 한 번에 읽고, 결과 배열을Resize(n, m).Value2 = arr로 한 번에 써서 셀 단위 반복보다 훨씬 빠릅니다.CreateObject("Scripting.Dictionary")— 사업항목 키 → clsProject, 세목 → 금액 배열 등 키 기반 집계에 쓰는 사전 객체입니다. Exists 로 있는지 확인 후 누적합니다.ListObjects.Add(xlSrcRange, rng, , xlYes)— 범위를 엑셀 표로 만듭니다. MakeTable 은 같은 이름의 표가 있으면 Resize 로 크기만 바꿔 슬라이서 캐시를 유지합니다.SlicerCaches.Add2(표, 필드, 이름) / Slicers.Add— 표 열에 연결된 슬라이서를 만듭니다. 이미 있으면 위치·크기만 다시 지정하고, 새로고침 때는 ClearManualFilter 로 필터만 해제합니다.SUBTOTAL(109, OFFSET(...)) + SUMPRODUCT— 필터로 숨겨진 행을 빼고 조건에 맞는 보이는 행만 합산하는 수식 패턴입니다. KPI·팀·세목 집계가 슬라이서에 즉시 반응하는 핵심입니다.FormatConditions.AddDatabar / AddColorScale— 집행률 셀에 0~1 범위 데이터 막대(구간별 색)와 3색 스케일을 조건부 서식으로 넣습니다.
부르는 공통 함수
- SpeedOn / SpeedOff (modUtil) — 화면 갱신·이벤트·자동 계산·경고를 끄고 상태 표시줄에 진행 문구 표시, 끝나면 복구
- BW / IsBoardWB (modUtil) — 추가 기능으로 실행할 때 작업 대상 보드 통합문서(메인 또는 설정 시트 보유)를 찾거나 새로 만듦
- GetSheet / SheetExists / ClearRowsKeepTables (modUtil) — 시트 확보·존재 확인, 표 머리글과 슬라이서는 남기고 내용만 비우기
- FindHeaderRow / HeaderMap / HCol (modUtil) — 머리글 행 찾기, 머리글 이름→열 번호 사전, 필수 열이 없으면 오류 발생
- CleanText / NormKey / MakeKey (modUtil) — 줄바꿈·특수 공백 제거, 공백·괄호 보충어를 없앤 매칭 키 '세부사업|사업항목' 생성
- CfgGet / CfgSet (modUtil) — 숨김 '설정' 시트의 키/값(부서명·기준일·마지막갱신 등) 읽기·쓰기
- AddButton / AddSlicerTo / DeleteOrClearSlicers (modUtil) — 매크로 연결 둥근 도형 버튼, 표 슬라이서 생성·재배치, 접두어별 슬라이서 삭제 또는 필터 해제
- KpiRow / DrawKpi / StatusText / rate (modUtil) — 수식 KPI 카드 배치, 집행률 구간별 상태 문구, 0으로 나누기 방지 나눗셈
이렇게 고쳐 쓰세요
- '완료' 상태 기준을 95%에서 90%로 낮추기 — modUtil 486줄
ElseIf rate >= 0.95 Then의0.95를0.9로 바꿉니다. '정상' 기준은 488줄0.5입니다. 상태 글자는 BuildMain·BuildGrouped 가 새로고침 때 다시 계산하므로 [새로고침]만 누르면 반영됩니다. - 집행률 막대 색 구간(80%/60%) 바꾸기 — modChart 609줄
If rate >= 0.8 Then(파랑)과 611줄ElseIf rate >= 0.6 Then(초록)의 숫자를 원하는 값으로 고칩니다. 메인·세부현황·예산보드가 모두 이 BarByRate 를 쓰므로 한 곳만 바꾸면 됩니다. - 예산보드의 사업항목 TOP 10 을 TOP 15 로 — modChart 16줄
Private Const TOPN As Long = 10의10을15로 바꿉니다. 담당자 표 최대 행 수는 18줄OWN_N, 세목 표는 17줄SEM_N입니다. - 강조 파랑을 조금 어둡게 — modUtil 58줄
ClrBlue = RGB(0, 122, 255)를RGB(0, 102, 204)처럼 바꿉니다. VBA 의RGB(빨, 초, 파)는 사람이 읽는 순서로 쓰지만 결과 Long 값은 BGR 순서(&H00BBGGRR)라서 RGB(0,122,255) 는&HFF7A00입니다. 16진수로 직접 쓸 때는 파랑과 빨강 자리를 바꿔 써야 합니다.
고칠 때 주의
- modMapping 298줄·309줄의
SpeedOn: BuildModel: SpeedOff에서 줄 맨 앞SpeedOn:은 VBA 가 '줄 레이블'로 해석하므로 SpeedOn 이 실제로 호출되지 않습니다. 속도 모드를 켜려면SpeedOn "...": BuildModel: SpeedOff처럼 인수를 붙여 고쳐야 합니다. - 원본 열은 머리글 이름으로 찾습니다(HCol). 기관 예산시스템 내려받기 양식의 머리글(예:
예산현액,사업항목명1)이 바뀌면 '헤더 ... 열을 찾지 못했습니다' 오류가 나므로, 해당 HCol 호출의 이름 문자열을 함께 고쳐야 합니다. - 금액 단위가 시트마다 다릅니다. ParseRequest 는 요구서 금액에
* 1000을 곱해 원 단위로 맞추지만, 세부산출내역(modCalc)은 천원 단위 그대로 둡니다. 나눗셈은 0/0 오류를 피하도록 modUtil 의rate()를 쓰세요.
02키워드값 가져오기 정보가져오기
폴더 안 hwp·hwpx·pdf·이미지(Windows OCR)에서 성명·계좌번호 등 키워드 값을 찾아 파일별 한 행으로 정리합니다.
기능 전체를 담은 단일 모듈입니다. 시트·버튼 생성, 폴더 선택, 파일 형식별 문서 구조 읽기(hwpx/hwp/pdf/이미지 OCR), 키워드 값 판별 규칙, 결과 서식까지 1225줄 안에서 처리하며, 도형 버튼 서식만 공용 모듈(modRibbonUI)의 UI_StyleButton 을 빌려 씁니다.
소스 보기 (1,225줄)
심화과정: 소스 분석
리본 버튼이 HK_Start 를 불러 '키워드값' 시트와 [1 폴더선택]·[2 키워드값 가져오기] 도형 버튼을 만들고, 사용자는 3행 B~J 칸에 찾을 키워드를 적습니다. HK_Extract 는 폴더 안의 hwpx(압축 해제 후 MSXML 로 XML 파싱)·hwp(한글 자동화로 hwpx 임시 변환)·pdf(Word 로 열기)·이미지(PowerShell 로 Windows 내장 OCR) 파일을 모두 '표 + 문단' 블록 구조로 바꾼 뒤, 같은 규칙(같은 칸 '키워드: 값' → 오른쪽 칸 → 아래 칸 → 문단 뒤/다음 줄)으로 값을 찾아 파일당 한 행씩 기록합니다.
모듈 구성
modHwpKeywordSheet— 기능 전체를 담은 단일 모듈입니다. 시트·버튼 생성, 폴더 선택, 파일 형식별 문서 구조 읽기(hwpx/hwp/pdf/이미지 OCR), 키워드 값 판별 규칙, 결과 서식까지 1225줄 안에서 처리하며, 도형 버튼 서식만 공용 모듈(modRibbonUI)의 UI_StyleButton 을 빌려 씁니다.
코드 흐름 (줄 번호를 누르면 소스의 그 부분으로 이동)
modHwpKeywordSheet49~111줄(HK_Start, HK_NewSheet, HK_CreateButtons)① 입력 시트와 버튼 만들기HK_Start 는 리본 콜백으로, HK_NewSheet 가 '키워드값'(이미 있으면 '키워드값_1', '_2' …) 시트를 맨 뒤에 추가합니다. HK_CreateButtons 는Shapes.AddShape(msoShapeRoundedRectangle, ...)로 둥근 도형 2개를 만들고 공용 UI_StyleButton 으로 HK_PickFolder·HK_Extract 를 연결합니다. 3행 B~J 에는 회색(RGB(150, 150, 150)) '키워드1~9' 자리표시자를 넣고, 5~6행에 사용 순서 안내를 적습니다.modHwpKeywordSheet116~135줄(HK_PickFolder)② 대상 폴더 선택Application.FileDialog(msoFileDialogFolderPicker)로 폴더를 고르게 하고, FileSystemObject 로 HK_IsSupported(hwp·hwpx·pdf·이미지,~$임시 파일 제외)에 맞는 파일 수를 셉니다. A2 에 '대상폴더: 경로' 하이퍼링크를 달고 B2 에 대상 파일 개수를 표시합니다. 나중에 HK_FolderOf(250-266줄)가 이 링크 주소(또는 예전 방식의 B2)를 다시 읽어 폴더를 알아냅니다.modHwpKeywordSheet140~248줄(HK_Extract)③ 파일별 반복과 형식 분기HK_ReadKeywords 가 3행에서 회색 자리표시자가 아닌 칸만 키워드로 모으고, 4행 아래 이전 결과를 지웁니다. 파일마다 확장자에 따라 HK_LoadHwpx / HK_LoadHwpViaHwpx / HK_LoadPdfViaWord / HK_LoadImageViaOcr 를 부르며, 한글·Word 객체는 처음 필요할 때 한 번만 만들어 재사용합니다. 읽기에 성공하면 키워드마다 HK_FindValue 결과를 텍스트 서식(@)으로 쓰고, A열에 값을 찾은 쪽 번호를 붙인 파일 링크, 12열(L)에 처리 상태를 남깁니다. 중간 오류는 HK_Fail 에서 한글·Word 를 종료한 뒤 문제 파일 이름을 알려 줍니다.modHwpKeywordSheet626~729줄(HK_LoadHwpx, HK_WalkPara, HK_ParseTable)④ hwpx: XML 을 표·문단 블록으로HK_Unzip(753-788줄)이 파일을 .zip 으로 복사해 PowerShellExpand-Archive로 풀고(실패 시 Shell.Application CopyHere 예비), Contents\section0.xml 부터 차례로MSXML2.DOMDocument.6.0에 읽습니다. HK_WalkPara 는hp:run안의hp:t글자를 한 줄로 모으다가hp:tbl을 만나면 HK_ParseTable 로 넘겨 문서 순서를 유지합니다. HK_ParseTable 은hp:cellAddr의 rowAddr/colAddr 와hp:cellSpan을 읽어 병합 범위 전체에 같은 글자와 같은 셀 번호(cid)를 채우므로, 뒤에서 병합 칸을 건너뛰고 '진짜 오른쪽 칸'을 찾을 수 있습니다.modHwpKeywordSheet793~880줄(HK_CreateHwp, HK_LoadHwpViaHwpx, HK_HwpPageOf)⑤ hwp: 한글로 hwpx 임시 변환과 쪽 번호CreateObject("HWPFrame.HwpObject")로 한글을 띄우고 보안 모듈 등록 후 창을 숨깁니다. 원본을hwp.Open(path, "HWP", "forceopen:true")로 열어SaveAs로 임시 hwpx 를 만든 뒤 ④의 HK_LoadHwpx 로 읽고, 실패하면GetTextFile("UNICODE")로 글자만 줄 단위로 읽는 예비 경로(HK_LoadHwpText)를 씁니다. 값을 찾으면 HK_HwpPageOf 가HAction.Execute("RepeatFind", HFindReplace.HSet)로 키워드를 찾고KeyIndicator의 쪽 번호를 받아 결과 파일명 옆에 '(p.3)' 처럼 표시합니다.modHwpKeywordSheet896~935줄(HK_LoadPdfViaWord)⑥ pdf: Word 로 열어 표 읽기Word 는 pdf 를 열 때 편집 가능한 문서로 변환하므로,Documents.Open(path, False, True, False)(읽기 전용)로 연 뒤 문단을 차례로 봅니다.Range.Information(12)가 참(표 안)이면 그 표를 시작 위치로 한 번만 등록해 셀마다 RowIndex·ColumnIndex 에 글자를 넣고, 아니면 문단 글자를 줄로 추가합니다.Information(3)으로 얻은 쪽 번호를 표·줄에 함께 저장합니다.modHwpKeywordSheet944~1112줄(HK_LoadImageViaOcr, HK_BuildOcrTable)⑦ 이미지: Windows OCR 단어 좌표로 표 흉내HK_OcrScript(1143-1181줄)가 만든 PowerShell 스크립트를 UTF-8 로 임시 폴더에 쓰고WScript.Shell.Exec로 실행해 최대 60초 기다립니다. 스크립트는Windows.Media.Ocr.OcrEngine으로 이미지를 1.4·1.2·1.0·1.7·2.0 배로 확대해 각각 인식하고, 단어마다 글자·x·y·폭·높이를 탭으로 구분해 돌려줍니다. HK_BuildOcrTable 은 단어를 세로 중심 차이(글자 높이의 0.6배 이내)로 행을 묶고, 글자 폭의 2.2배보다 넓은 빈틈으로 칸을 나눈 뒤, 칸 왼쪽 x 가 가까운 것끼리 같은 열 번호를 주어 하나의 표로 등록합니다. OCR 문서는hkFuzzy = True로 키워드 한 글자 오독을 허용합니다.modHwpKeywordSheet328~397줄(HK_FindValue, HK_SearchTable)⑧ 값 판별 규칙HK_FindValue 는 blocks 배열(양수 = 문단, 음수 = 표)을 문서 순서대로 훑어 처음 값이 나온 곳에서 멈추고 그 쪽 번호를 돌려줍니다. HK_SearchTable 은 키워드 칸을 찾으면 먼저 같은 칸의 '키워드: 값'을 보고, cid 가 다른 첫 오른쪽 칸(vR)과 아래 칸(vB)을 구합니다. 같은 행의 다른 키워드 수가 같은 열보다 많으면 머리글 표로 보고 아래 칸을 우선하되, 오른쪽 칸이 라벨이 아닌 값이고 머리글 행이 아니면 다시 오른쪽을 우선합니다. 문단은 HK_SearchLine(428-455줄)이 '키워드 : 값' 또는 키워드만 있는 줄의 다음 문단을 값으로 씁니다.modHwpKeywordSheet477~586줄(HK_IsKeywordCell ~ HK_Clean)⑨ 키워드 비교·라벨 판별·값 정리HK_Norm 은 공백·콜론·괄호·기호를 지우고 소문자로 바꿔 비교 기준을 맞춥니다. HK_AfterKeyword 는 키워드 바로 뒤가 한글·영문·숫자면('학교명', '성명란') 더 긴 라벨로 보고 버리며, 앞쪽 구분 기호와 '(한글)' 같은 괄호 보충어를 지웁니다. HK_LooksLabel 은 숫자 없는 6자 이하이면서 '명·번호·일자·주소…'로 끝나는 글을 값이 아닌 다음 항목명으로 판단하고, HK_Clean 은 값 뒤에 다른 키워드 라벨이 이어지면 그 앞에서 자르고 200자로 제한합니다. OCR 보정 HK_FuzzyFix(1185-1207줄)는 3자 이상 키워드와 길이가 같고 한 글자만 다른 조각을 키워드로 바꾸되 숫자 자리는 보정하지 않습니다.
핵심 VBA 객체·함수
Application.FileDialog(msoFileDialogFolderPicker)— 폴더 선택 대화상자입니다..Show가 -1 이면.SelectedItems(1)에 고른 폴더 경로가 들어 있습니다.CreateObject("MSXML2.DOMDocument.6.0").Load— hwpx 의 section XML 을 DOM 으로 읽어 childNodes·nodeName·getAttribute("rowAddr") 등으로 표·문단 구조를 순서대로 탐색합니다.WScript.Shell .Run / .Exec (powershell.exe)— Run 은 Expand-Archive 압축 해제를 끝날 때까지 기다리며(세 번째 인수 True) 실행하고, Exec 는 OCR 스크립트를 실행한 뒤status를 확인하며 기다립니다.HWPFrame.HwpObject: Open / SaveAs(tmp, "HWPX") / GetTextFile— 한글 자동화로 hwp 를 열어 hwpx 사본으로 저장하거나, 실패 시 문서 글자 전체를 유니코드 텍스트로 가져옵니다.hwp.HAction.Execute("RepeatFind", HParameterSet.HFindReplace.HSet) + KeyIndicator— GetDefault 로 찾기 매개변수를 채운 뒤 키워드를 앞으로 찾고, KeyIndicator 의 세 번째 출력값(인쇄 쪽 번호)을 읽어 값이 있는 쪽을 알아냅니다.Word.Application: Documents.Open + Range.Information(3 / 12)— pdf 를 Word 로 열어 문단마다 쪽 번호(3 = wdActiveEndPageNumber)와 표 안 여부(12 = wdWithInTable)를 확인합니다.Windows.Media.Ocr.OcrEngine.RecognizeAsync (PowerShell 안)— Windows 내장 OCR 로 이미지의 줄·단어와 단어별 BoundingRect 좌표를 얻습니다. 한국어 OCR 언어팩이 있어야 합니다.ADODB.Stream (Charset = "utf-8")— PowerShell 스크립트를 UTF-8 로 저장하고, OCR 결과 텍스트 파일을 UTF-8 로 읽어 한글이 깨지지 않게 합니다.
부르는 공통 함수
- UI_StyleButton (modRibbonUI) — 도형 버튼의 색·글꼴·여백을 꾸미고 OnAction 을 UI_ShapeClick 으로, AlternativeText 에 실행할 프로시저 이름(HK_PickFolder·HK_Extract)을 저장
- UI_ShapeClick (modRibbonUI) — 클릭된 도형(Application.Caller)의 AlternativeText 를 읽어 UI_Run 으로 해당 프로시저를 실행
- UI_Macro (modRibbonUI) — OnAction 에 넣을 매크로 이름 문자열을 돌려주는 도우미
이렇게 고쳐 쓰세요
- 긴 값이 200자에서 잘리지 않게 — 25줄
Private Const MAX_VALUE_LEN As Long = 200의200을500으로 바꿉니다. HK_Clean(584줄)이 이 길이를 넘으면 잘라 '…'를 붙입니다. - 키워드 칸을 9개(B~J)에서 13개(B~N)로 늘리기 — 23줄
KW_LAST_COL As Long = 10을14로 바꿉니다. 이때 처리 상태 열이 12열(L)로 고정돼 있어 키워드 칸과 겹치므로, 147줄ws.cells(3, 12)와 217줄ws.cells(r, 12)의12를16으로 함께 바꿔야 합니다. - 이미지 OCR 속도 높이기(배율 줄이기) — 1160줄 PowerShell 문자열
@(1.4, 1.2, 1.0, 1.7, 2.0)을@(1.4, 1.0)으로 줄이면 인식 횟수가 5번에서 2번으로 줄어듭니다. 인식률이 떨어질 수 있으므로 시험 후 정합니다. 기다리는 최대 시간은 960줄Timer - T > 60의60(초)입니다. - 값으로 잘못 잡히는 항목명 추가 — 530줄
tails = Array("명", "번호", ...)목록 끝에, "직렬"처럼 항목명 끝 글자를 추가하면, 숫자 없는 6자 이하이면서 그 글자로 끝나는 칸을 값이 아닌 다음 라벨로 보고 건너뜁니다.
고칠 때 주의
- 한글 자동화의 Open·SaveAs 는 Boolean 대신 숫자 1 을 돌려줄 수 있어
Not ok가 엉뚱하게 참이 됩니다. 810·820줄처럼(... <> 0)으로 정규화하는 부분은 지우지 마세요. - 자리표시자 판별은 글자색
RGB(150, 150, 150)에 의존합니다(57줄에서 지정, 273줄에서 비교). 한쪽 색만 바꾸면 '키워드1' 같은 안내 글자가 실제 키워드로 검색됩니다. - hwpx 압축 해제와 OCR 은
powershell.exe -ExecutionPolicy Bypass로 실행합니다. 보안 정책으로 PowerShell 실행이 막힌 PC 에서는 hwpx 는 Shell.Application 예비 경로로 넘어가지만, 이미지 OCR 은 '읽기 실패'로 남습니다.
03OFFICE파일합치기 OFFICE파일합치기
선택 폼 6항목: 한글→HWPX 하나로 / 한글→PDF / PDF 합치기(목차·링크) / Office 전부→PDF / 엑셀 파일들 합치기 / 시트들→하나로.
리본 버튼(tag OfficeMergeMenu)의 진입점. 1차·2차 선택 카드로 작업과 방식을 고르고 해당 모듈의 실행 프로시저에 모드 번호를 넘깁니다.
소스 보기 (52줄)
폴더 안 HWP·HWPX 를 한글 자동화 개체로 새 문서에 InsertFile 로 차례로 삽입해 '병합_날짜.hwpx' 로 저장하고 '한글병합' 시트에 목록을 남깁니다. 한글 개체 생성·정렬·저장 같은 보조 함수도 들어 있습니다.
소스 보기 (378줄)
폴더 안 HWP·HWPX 를 한 개씩 열어 SaveAs("PDF")로 변환하고 '변환 결과' 시트에 성공/실패를 기록합니다. 하나로 합치는 모드에서는 임시 폴더에 변환한 뒤 modMergePdf 의 MP_MergeFiles 에 넘깁니다.
소스 보기 (197줄)
PDF 목록을 Word 문서에 InsertFile 로 붙이고 파일마다 책갈피(BM_001…)를 달아, 선택 시 PAGEREF 필드와 내부 하이퍼링크로 된 목차 페이지를 맨 앞에 만든 뒤 PDF 로 내보냅니다.
소스 보기 (494줄)
한글·PDF·Word·Excel·PowerPoint·이미지를 이름순으로 Word 문서 하나에 넣어 'office병합_날짜.pdf' 로 내보냅니다. 형식마다 한글→PDF, 엑셀→PDF, 슬라이드→PNG 같은 중간 변환을 거칩니다.
소스 보기 (278줄)
폴더 안 엑셀 파일을 읽기 전용으로 열어 시트 내용을 새 통합 문서의 'Combined' 한 시트 또는 시트 이름별 시트로 모으고 '작업요약' 시트에 파일 목록을 남깁니다.
소스 보기 (423줄)
현재 통합 문서에서 선택한 시트(1개만 선택하면 보이는 전체 시트)를 '시트모음' 시트 하나에 '시트명:' 제목과 함께 위에서 아래로 이어 붙입니다.
소스 보기 (64줄)
심화과정: 소스 분석
리본 진입점 OfficeMergeMenu 가 선택 카드(UI_Choose)로 여섯 가지 작업 중 하나를 고르게 한 뒤, 각 모듈의 실행 프로시저(MH_Run·HP_Run·MP_Run·OfficeMergeToPdf·MX_Run·MergeSheetsToCollection)를 부릅니다. 한글 파일은 COM 개체 HWPFrame.HwpObject 로 열어 InsertFile 액션으로 이어 붙이거나 SaveAs("PDF")로 변환하고, PDF·Office 파일은 숨겨 둔 Word 문서에 차례로 삽입한 뒤 ExportAsFixedFormat 으로 PDF 하나를 만듭니다. 엑셀 파일·시트 합치기는 Excel 개체 모델만으로 UsedRange 를 복사해 새 통합 문서나 '시트모음' 시트에 이어 붙입니다.
모듈 구성
modOfficeMenu— 리본 버튼(tag OfficeMergeMenu)의 진입점. 1차·2차 선택 카드로 작업과 방식을 고르고 해당 모듈의 실행 프로시저에 모드 번호를 넘깁니다.modMergeHwp— 폴더 안 HWP·HWPX 를 한글 자동화 개체로 새 문서에 InsertFile 로 차례로 삽입해 '병합_날짜.hwpx' 로 저장하고 '한글병합' 시트에 목록을 남깁니다. 한글 개체 생성·정렬·저장 같은 보조 함수도 들어 있습니다.modHwpToPdf— 폴더 안 HWP·HWPX 를 한 개씩 열어 SaveAs("PDF")로 변환하고 '변환 결과' 시트에 성공/실패를 기록합니다. 하나로 합치는 모드에서는 임시 폴더에 변환한 뒤 modMergePdf 의 MP_MergeFiles 에 넘깁니다.modMergePdf— PDF 목록을 Word 문서에 InsertFile 로 붙이고 파일마다 책갈피(BM_001…)를 달아, 선택 시 PAGEREF 필드와 내부 하이퍼링크로 된 목차 페이지를 맨 앞에 만든 뒤 PDF 로 내보냅니다.modOfficeMerge— 한글·PDF·Word·Excel·PowerPoint·이미지를 이름순으로 Word 문서 하나에 넣어 'office병합_날짜.pdf' 로 내보냅니다. 형식마다 한글→PDF, 엑셀→PDF, 슬라이드→PNG 같은 중간 변환을 거칩니다.modMergeExcelFiles— 폴더 안 엑셀 파일을 읽기 전용으로 열어 시트 내용을 새 통합 문서의 'Combined' 한 시트 또는 시트 이름별 시트로 모으고 '작업요약' 시트에 파일 목록을 남깁니다.modMergeSheets— 현재 통합 문서에서 선택한 시트(1개만 선택하면 보이는 전체 시트)를 '시트모음' 시트 하나에 '시트명:' 제목과 함께 위에서 아래로 이어 붙입니다.
코드 흐름 (줄 번호를 누르면 소스의 그 부분으로 이동)
modOfficeMenu11~52줄(OfficeMergeMenu)① 리본 메뉴에서 작업 고르기13~21줄에서UI_Choose로 여섯 개 카드를 보여 주고, 돌려받은 번호를 22줄Select Case choice로 나눕니다. 1·2·3·5번은 한 번 더UI_Choose로 방식(서식 통합/구역 구분, 각각/목차/파일만, 이름별/한 시트)을 물어sub1에 담고, 0(취소)이면 바로 끝냅니다. 그 번호를MH_Run sub1,HP_Run sub1,MP_Run sub1,MX_Run sub1처럼 각 모듈에 그대로 넘기고, 4·6번은OfficeMergeToPdf Nothing,MergeSheetsToCollection Nothing으로 리본 인자 없이 호출합니다.modMergeHwp18~114줄(MH_Run)② 한글 파일들을 HWPX 하나로27~29줄에서 모드에 따라keepSec·keepFmt·asHwpx값을 정합니다(1=구역·서식 유지 안 함, 2=유지).GetHwpObject로 한글 개체를 만들고 창 숨김(42줄)과RegisterModule "FilePathCheckDLL"(47줄)로 파일 접근 승인창을 막은 뒤FileNew로 빈 문서를 엽니다.ListHwpFiles로 모은 경로를QuickSortStrings로 이름순 정렬하고, 65~77줄 반복문에서 파일마다InsertFileToHwp를 불러 실패한 이름만 모읍니다. 모드 1이면 78줄MH_TrimTrailingEmpty로 끝의 빈 문단을 지운 뒤 '병합_yyyymmdd_hhnnss.hwpx' 로 저장하고 '한글병합' 시트에 기록합니다.modMergeHwp292~338줄(InsertFileToHwp, SaveAsHwp)③ InsertFile 액션과 HWPX 저장294줄HAction.GetDefault "InsertFile"로 파라미터 셋을 기본값으로 채우고 파일 이름과KeepSection·KeepCharshape·KeepParashape·KeepStyle을 넣은 다음 303줄HAction.Execute로 실행합니다. 이 네 값이 0이면 삽입 문서가 현재 문서(첫 문서) 서식을 따르고, 1이면 원래 구역·글자·문단 모양을 유지합니다. 저장은 316줄hwp.SaveAs(savePath, "HWPX", "")를 먼저 시도하고, 안 되면FileSaveAs_S액션에Format = "HWPX"를 지정하는 방식으로 한 번 더 시도합니다. 성공 여부는Dir(savePath)로 실제 파일이 생겼는지로 판단합니다.modHwpToPdf13~80줄, 96-197 (HP_Run, HP_ConvertFolder)④ 한글 → PDF 일괄 변환HP_Run은 모드 1이면 원본 폴더에, 2·3이면 '_pdf임시_시각' 하위 폴더에 PDF 를 만들도록 출력 폴더를 정합니다.HP_ConvertFolder는 104~111줄에서 hwp/hwpx(임시파일 '~$', 이전 결과 '병합_' 제외)를 모아 버블 정렬하고, 한글 개체를 한 번만 만든 뒤 파일마다 163줄objHwp.Open(filePath, "", "forceopen:true"), 167줄objHwp.SaveAs(pdfPath, "PDF", "")를 실행합니다. 결과는 C열에 '성공'(초록RGB(0, 176, 80)) 또는 실패 사유(빨강)로 쓰고, 성공한 경로만pdfPaths배열에 담습니다. 모드 2·3이면 63줄에서MP_MergeFiles로 합친 뒤 임시 폴더를 지웁니다.modMergePdf106~242줄(MP_MergeFiles)⑤ Word 로 PDF 이어 붙이기이미 실행 중인 Word 가 있으면GetObject로 빌려 쓰고, 없으면CreateObject("Word.Application")로 숨겨서 띄웁니다. 속도를 위해ScreenUpdating과Options.Pagination을 끄고, 150~188줄에서 파일마다 쪽 나누기 → 책갈피BM_001추가 →rng.InsertFile순서로 넣습니다(실패하면 문서로 열어 복사·붙여넣기). 마지막에 페이지 계산을 다시 켜고Repaginate·필드 갱신 후 207줄ExportAsFixedFormat(CreateBookmarks:=2)로 PDF 를 저장하며, 원래 Word 가 열려 있지 않았을 때만 종료합니다.modMergePdf305~447줄(BuildTOCPage)⑥ 목차 페이지와 내부 링크Selection.HomeKey로 문서 맨 앞에 가서 '목 차' 제목, 구분선, 항목을TypeText로 입력합니다. 항목마다 탭 뒤에Fields.Add ... "PAGEREF " & bmNames(i)필드를 넣어 쪽 번호가 자동으로 계산되게 하고, 오른쪽 탭(점선 리더)으로 번호를 끝에 맞춥니다. 목차 뒤 쪽 나누기 다음 위치로 첫 책갈피를 다시 지정(403줄)해 첫 항목이 목차 쪽을 가리키지 않게 하고, 3번째 문단부터 탭 앞 글자에Hyperlinks.Add SubAddress:=bmNames(i)로 내부 링크를 겁니다.modOfficeMerge16~102줄, 139-204 (OfficeMergeToPdf, OM_InsertOne)⑦ 여러 형식을 PDF 하나로OM_Collect가 확장자를OM_Kind로 doc/img/pdf/hwp/xls/ppt 로 분류하고 이름순 정렬합니다. Word 새 문서(여백 1.5cm)에 파일마다 쪽 나누기를 넣고OM_InsertOne을 부르는데, doc 은InsertFile, 이미지는InlineShapes.AddPicture로 쪽 폭에 맞춰 넣습니다. hwp 는 한글로 임시 PDF 를 만들고, xls 는wb.ExportAsFixedFormat xlTypePDF로 PDF 를 만든 뒤OM_InsertPdf로 넣으며, ppt 는Slides(k).Export png, "PNG", 1600으로 슬라이드를 그림으로 바꿔 넣습니다. 끝나면 'office병합_날짜.pdf' 로 내보내고OM_Cleanup이 임시 폴더와 한글·PowerPoint 개체를 정리합니다.modMergeExcelFiles26~32줄, 39-345 (MX_Run, 시트합치기)⑧ 엑셀 파일들 합치기MX_Run은 메뉴 번호를 모듈 변수mxPresetMode에 넣고시트합치기를 부릅니다(1→vbNo=이름별, 2→vbYes=Combined). 폴더의 xlsx/xls/xlsm/xlsb 를 모은 뒤 먼저ProtectContents로 보호 시트가 있는 파일을 알려 주고,Workbooks.Add로 새 통합 문서와 '작업요약' 시트를 만듭니다. 파일마다MX_OpenOrGet으로 열어(이미 열린 파일은 그대로 사용) 각 시트의UsedRange를 C열부터 붙이고, A열에 파일 번호, B열에 파일 링크를 답니다. 보호된 시트는 서식 없이.Value만 옮기고, 이름별 모드에서는Scripting.Dictionary에 시트 이름과 다음 붙일 행(이름_p)을 함께 보관합니다.modMergeSheets1~64줄(MergeSheetsToCollection, MergeCollectionCore, CopySheetContent)⑨ 현재 문서의 시트들 합치기선택한 시트가 1개면 확인 후 이름이 '시트모음'으로 시작하지 않고 보이는 시트 전부를, 여러 개면 선택한 워크시트만Collection에 담습니다.MergeCollectionCore는 '시트모음', '시트모음_1' … 중 비어 있는 이름으로 새 시트를 맨 뒤에 만듭니다.CopySheetContent는Cells.Find("*", SearchDirection:=xlPrevious)를 행·열 방향으로 두 번 써서 실제 마지막 행·열을 구하고, '시트명: …' 제목 한 줄 아래에rng.Copy로 붙인 뒤 두 줄을 띄웁니다.
핵심 VBA 객체·함수
hwp.HAction.GetDefault "InsertFile", hwp.HParameterSet.HInsertFile.HSet— 한글 '파일 끼워 넣기' 액션의 파라미터를 기본값으로 채웁니다. 이어서 FileName·KeepSection·KeepCharshape 등을 바꾸고 HAction.Execute 로 실행합니다.hwp.RegisterModule "FilePathCheckDLL", "FilePathCheckerModule"— 한글 자동화에서 파일을 열거나 저장할 때 뜨는 접근 승인 창을 띄우지 않도록 보안 모듈을 등록합니다(해당 모듈이 PC 에 등록되어 있어야 효과가 있습니다).hwp.Open(path, "", "forceopen:true") / hwp.SaveAs(path, "PDF", "")— 한글 문서를 열고 PDF 형식으로 다른 이름 저장합니다. 반환값이 0이 아니면 성공으로 처리합니다.Range.InsertFile FileName:=p, ConfirmConversions:=False (Word)— Word 문서의 지정 위치에 다른 파일 내용을 끼워 넣습니다. PDF 는 Word 의 PDF 변환 기능을 거쳐 들어갑니다.wdDoc.Fields.Add Range, -1, "PAGEREF BM_001"— 책갈피가 있는 쪽 번호를 표시하는 필드를 만듭니다. 목차의 쪽 번호가 이 필드로 자동 계산됩니다.wdDoc.ExportAsFixedFormat OutputFileName, 17, CreateBookmarks:=2— Word 문서를 PDF(17)로 내보내며, 문서의 책갈피를 PDF 책갈피로 함께 만듭니다.Workbook.ExportAsFixedFormat Type:=xlTypePDF— 엑셀 통합 문서를 PDF 로 저장합니다. Office 합치기에서 엑셀 파일을 Word 에 넣기 전 중간 PDF 를 만들 때 씁니다.Worksheet.UsedRange.Copy Destination— 시트에서 사용 중인 범위를 서식과 함께 다른 시트의 지정 셀로 복사합니다. 보호된 시트는 대신 .Value 를 대입해 값만 옮깁니다.
부르는 공통 함수
- UI_Choose (modRibbonUI) — 숫자 입력 대신 카드 형태 선택 폼을 띄우고 고른 번호(취소=0)를 돌려줌
- UI_ChoiceToMsg (modRibbonUI) — 선택 번호 1/2/그 외를 vbYes/vbNo/vbCancel 로 바꿔 기존 MsgBox 방식 코드와 연결
- BX_Trace (modRibbonUI 공용 변수) — 긴 작업의 현재 단계를 적어 두었다가 오류 메시지에 '@단계'로 붙임
- MP_MergeFiles (modMergePdf) — PDF 목록을 Word 로 합치는 공용 코어. 한글→PDF 하나로 합치기(HP_Run)에서도 재사용
이렇게 고쳐 쓰세요
- PDF 합치기 결과의 여백을 2cm 에서 1.5cm 로 — modMergePdf 141~144줄의
wdApp.CentimetersToPoints(2)네 곳을wdApp.CentimetersToPoints(1.5)로 바꿉니다. 목차 탭 위치는 313줄에서 여백을 빼서 계산하므로 따로 고칠 필요가 없습니다. - 목차 링크 글자색을 진파랑에서 진빨강으로 — modMergePdf 441줄
RGB(0, 51, 153)을RGB(192, 0, 0)으로 바꿉니다. VBA 의RGB(빨강, 초록, 파랑)함수는 사람이 읽는 순서로 쓰지만, 결과 Long 값은 내부적으로 0x00BBGGRR(BGR) 순서이므로 숫자로 직접 쓸 때는&H0000C0처럼 빨강이 맨 뒤 두 자리에 옵니다. - PowerPoint 슬라이드 그림을 더 선명하게 — modOfficeMerge 185줄
pres.Slides(k).Export png, "PNG", 1600의1600(내보내는 그림 가로 픽셀)을2400으로 올립니다. 그림은OM_AddPicture가 쪽 폭에 맞춰 줄이므로 크기는 같고 해상도만 올라가며, 결과 PDF 용량은 커집니다. - 한글 합치기 결과를 HWPX 대신 HWP 로 저장(모드 1) — modMergeHwp 27줄
Case 1: keepSec = 0: keepFmt = 0: asHwpx = True의True를False로 바꾸면 84줄 경로('.hwp')와SaveAsHwp의 FileSaveAs_S 경로를 탑니다.
고칠 때 주의
- 결과 파일 이름 앞머리 '병합_' 은 다음 실행 때 결과물이 다시 합쳐지지 않도록 수집 단계에서 제외하는 표시이기도 합니다(modMergeHwp 238·246줄, modHwpToPdf 106줄, modMergePdf 260·270줄, modOfficeMerge 109줄). 접두어를 바꾸면 이 비교들도 함께 바꿔야 합니다.
- 목차 링크는 '1문단=제목, 2문단=구분선, 3문단부터 항목'이라는 가정(modMergePdf 411줄
tocStartPara = 3)으로 문단 번호를 계산합니다. 목차 위에 문단을 추가하면 이 값도 늘려야 링크가 맞는 줄에 걸립니다. - 엑셀 합치기의 모드 번호는 메뉴(1=이름별, 2=한 시트)와 내부 값(
vbNo=이름별,vbYes=Combined)이 반대로 대응됩니다(modMergeExcelFiles 75~76줄). 선택지 순서를 바꿀 때 두 곳을 함께 확인하세요.
04폴더/파일 이름변경 폴더정리
파일 이름을 단어로 나눠 지정 단어만 조합해 일괄 변경, 중복 단어 목록에서 O 표시 단어만 삭제.
목록 시트 생성, 단어 추출·조합, 중복 단어 정리, 실제 이름 변경까지 이름변경 기능 전체를 담은 모듈입니다. 버튼으로 불리는 Public 프로시저(p001~p005)와 시트 서식·이름 찾기용 Private 보조 함수로 구성됩니다.
소스 보기 (707줄)
심화과정: 소스 분석
리본 진입점 p001_폴더선택 이 고른 폴더의 하위 폴더·파일 이름을 '폴더'/'파일'/'폴더및파일' 시트에 나열하고, 1행에 단계별 도형 버튼 6개를 만들어 이후 작업을 버튼 매크로로 이어 갑니다. 이름은 구분 문자(_-.,() 등)를 빈칸으로 바꿔 단어로 나눈 뒤, 입력한 단어가 든 조각을 뽑아 조합하거나 여러 이름에 공통으로 든 단어를 지워 D열 '이름 변경'에 새 이름을 만듭니다. 마지막 단계에서 금지 문자·중복 이름을 검사하고 VBA 의 Name 원본 As 새경로 문으로 실제 이름을 바꾼 결과를 E열에 표시합니다.
모듈 구성
modRenameFiles— 목록 시트 생성, 단어 추출·조합, 중복 단어 정리, 실제 이름 변경까지 이름변경 기능 전체를 담은 모듈입니다. 버튼으로 불리는 Public 프로시저(p001~p005)와 시트 서식·이름 찾기용 Private 보조 함수로 구성됩니다.
코드 흐름 (줄 번호를 누르면 소스의 그 부분으로 이동)
modRenameFiles10~96줄(p001_폴더선택)① 대상 고르기와 목록 시트 만들기24줄UI_Choose로 '폴더/파일/폴더 및 파일' 중 하나를 고르고, 폴더 선택 대화상자로 기준 폴더를 받습니다. 선택에 따라 시트 이름을 정한 뒤 48~52줄에서 같은 이름 시트를 경고 없이 지우고 새로 만듭니다.Scripting.FileSystemObject의SubFolders(숨김 속성vbHidden제외)와Files('.tmp'·'~$' 제외)를 돌며 B열에 앞 공백 2칸을 붙인 이름, C열에 '폴더' 또는 확장자를 4행부터 적습니다. B2에는 기준 폴더 경로를 하이퍼링크로 넣어 이후 단계가 이 경로를 다시 읽습니다.modRenameFiles609~648줄(CreateMacroButtons, SetupHeaders)② 단계 버튼과 표 머리CreateMacroButtons는 이름·글자·매크로 배열 세 개를 같은 순서로 두고 1행에 둥근 사각형 6개를 116pt 간격으로 만든 뒤UI_StyleButton으로 버튼 모양을 꾸밉니다. 이때 실행할 프로시저 이름은 도형의 대체 텍스트(AlternativeText)에 저장되고, 클릭하면 공통 처리기UI_ShapeClick이 그 이름을 읽어 실행합니다. 마지막 '6 이름 변경'만i = 5조건으로 강조 버튼이 됩니다.SetupHeaders는 D~Z열을 텍스트 서식("@")으로 바꿔 '0926' 같은 숫자 이름이 숫자로 바뀌지 않게 하고, 3행에 No.·이름·정보·이름 변경·결과, 2행 F~J열에 '단어입력1~5' 머리글을 씁니다.modRenameFiles105~156줄, 670-698 (p002_이름가져오기, UpdateCombinedName, ReplaceCharsWithSpace)③ 단어 추출과 이름 조합3행 F열부터 입력된 단어(키워드)를 열마다 읽고, 각 행의 B열 이름을ReplaceCharsWithSpace로 정리해Split(…, " ")로 나눕니다. 조각 중 키워드를 포함하는(InStr … vbTextCompare, 대소문자 무시) 첫 조각을 그 열에 넣고,UpdateCombinedName이 F열부터 마지막 열까지 값이 있는 칸을 빈칸으로 이어 D열 '이름 변경'에 씁니다.ReplaceCharsWithSpace는 689줄 문자 목록을 빈칸으로 바꾸고 연속 빈칸을 하나로 줄입니다. 사용한 키워드 칸은 연두색RGB(144, 238, 144)로 표시됩니다.modRenameFiles161~224줄(p_단어입력추가, p003_이름새로설정)④ 입력 열 추가와 수동 조합'3 입력 추가' 버튼은 2행 마지막 머리글 다음 열에 '단어입력N' 을 만들고 텍스트 서식을 적용합니다. '4 이름 조합' 버튼은 3행에 키워드가 있는데 아래가 비어 있는 열을 찾아 4행~마지막 행에 그 키워드를 통째로 채웁니다(213줄). 그다음 모든 행에UpdateCombinedName을 다시 불러, 사용자가 F열 이후 칸을 직접 고친 내용까지 D열 새 이름에 반영합니다.modRenameFiles378~461줄, 550-562 (p005_중복단어삭제 1회차, SortDictKeysByValue)⑤ 공통 단어 목록 만들기391줄 조건(P3='중복 단어', P4 값 있음, P2 가 '적용됨' 아님)에 맞으면 2회차로 넘어가고, 아니면 1회차를 실행합니다. 1회차는 이름마다seen사전으로 같은 이름 안의 중복을 한 번만 세고, 전체 사전dict에 '그 단어가 든 이름 수'를 누적합니다(두 사전 모두CompareMode = vbTextCompare).SortDictKeysByValue로 많이 나온 순으로 정렬해 2개 이상 이름에 나온 단어만 P열(단어)·Q열(이름 수)·R열('O')에 쓰고, 표 위에 'O 삭제 항목만 삭제' 버튼을 만듭니다.modRenameFiles466~547줄(p005_중복단어적용, DupApply, CreateDupApplyButton)⑥ 표시한 단어 지우기DupApply는 R열이 'O'인 P열 단어만 사전에 담고, 각 이름을 다시 단어로 나눠 그 단어를 뺀 나머지를 빈칸으로 이어 D열에 씁니다.onlyMarked가 False(5번 버튼 두 번째 클릭)면seen사전으로 한 이름 안에서 반복된 단어도 하나만 남깁니다. 모두 지워져 빈 이름이 되면 원래 이름을 유지하고(515줄), 바뀐 행은 E열에 '이름 변경 대기'를 연한 노랑으로 표시하며 P2 에 '적용됨'을 남깁니다.modRenameFiles229~369줄(p004_이름변경)⑦ 검사 후 실제 이름 바꾸기B2 하이퍼링크 주소(없으면 셀 값)로 기준 폴더를 읽고UI_Choose로 한 번 더 확인합니다. 행마다 새 이름 비어 있음 → 금지 문자\/:*?"<>|→ 원본 찾기(FindActualFileName) →UI_ValidFolderName→ 같은 이름이면 건너뜀 → 대상 이름이 이미 있음 순서로 검사하고, 실패하면 E열에 사유와 연한 빨강을 쓰고GoTo NextItem으로 넘어갑니다. 파일은 C열 확장자를 다시 붙이고(310줄), 351줄Name oldPath As newPath로 이름을 바꾼 뒤 성공하면 B열도 새 이름으로 고칩니다.modRenameFiles570~607줄(FindActualFileName)⑧ 시트 이름과 실제 파일 맞추기시트의 B열 이름은 사용자가 고쳤거나 구분 문자가 바뀌었을 수 있으므로, 먼저 폴더(또는 같은 확장자 파일)에서 이름이 정확히 같은 항목을 찾습니다. 없으면 두 번째 반복에서 양쪽을ReplaceCharsWithSpace로 정리해 비교하고, 맞는 항목이 정확히 1개일 때만 실제 이름(확장자 포함)을 돌려줍니다. 2개 이상이면 빈 문자열을 돌려줘 엉뚱한 파일이 바뀌지 않게 합니다.
핵심 VBA 객체·함수
CreateObject("Scripting.FileSystemObject").GetFolder(path).SubFolders / .Files— 폴더 안 하위 폴더와 파일 컬렉션을 돌려줍니다. 각 항목의 Name·Path·Attributes 로 이름과 숨김 여부를 확인합니다.fso.GetBaseName / fso.GetExtensionName— 경로에서 확장자를 뺀 이름과 확장자만 따로 뽑습니다. 목록 시트의 B열(이름)과 C열(확장자)을 채울 때 씁니다.Name oldPath As newPath— VBA 내장 문으로 파일이나 폴더 이름을 바꿉니다(같은 드라이브 안에서는 이동도 됨). 실패하면 오류가 나므로 On Error Resume Next 후 Err.Number 로 결과를 판단합니다.CreateObject("Scripting.Dictionary") + CompareMode = vbTextCompare— 단어별 개수를 세는 사전입니다. 텍스트 비교 모드라 대소문자가 달라도 같은 단어로 셉니다.ws.Shapes.AddShape(msoShapeRoundedRectangle, x, y, w, h)— 시트 위에 둥근 사각형 도형을 만듭니다. UI_StyleButton 이 여기에 글자와 연결 매크로를 넣어 단계 버튼으로 씁니다.Range.DataSeries Rowcol:=xlColumns, Type:=xlLinear, Step:=1— A4 의 1부터 아래로 1씩 증가하는 번호를 채웁니다. 항목이 1개일 때 오류가 나므로 665줄에서 lastRow > 4 일 때만 실행합니다.
부르는 공통 함수
- UI_Choose (modRibbonUI) — 대상 종류 선택과 이름 변경 실행 확인 카드
- UI_StyleButton (modRibbonUI) — 도형 색·글자를 꾸미고 실행할 프로시저 이름을 AlternativeText 에 저장(클릭 시 UI_ShapeClick 이 실행)
- UI_ValidFolderName (modRibbonUI) — 빈 이름, 끝의 점·공백, 제어 문자, 금지 문자, CON·PRN 등 예약 이름을 걸러 냄
이렇게 고쳐 쓰세요
- 물결(~)과 작은따옴표도 단어 구분 문자로 처리 — 689줄
specialChars = "_-.,()[]{}!@#$%^&+="의 따옴표 안 끝에~'를 덧붙입니다. 이 함수는 단어 추출, 중복 단어 정리, 원본 파일 찾기(FindActualFileName)에서 모두 쓰이므로 세 기능에 함께 적용됩니다. - 3개 이상 이름에 나온 단어만 중복 단어로 표시 — 420줄
If dict(keys(i)) >= 2 Then의2를3으로 바꿉니다. 452줄 안내 문구의 '2개 이상'도 함께 고쳐 두면 헷갈리지 않습니다. - 이름 변경 성공 표시색을 연두에서 하늘색으로 — 354줄
RGB(200, 255, 200)를RGB(204, 236, 255)로 바꿉니다.RGB()함수 인자는 빨강·초록·파랑 순서지만, 셀Interior.Color에 숫자를 직접 넣을 때는 0x00BBGGRR(BGR) 순서라&HFFECCC가 같은 색입니다. - 처음 만들어지는 단어입력 열을 5개에서 3개로 — SetupHeaders 의 632~633줄(
I2·J2머리글)을 지우고 641·647줄 범위A3:J3·F3:J3를A3:H3·F3:H3으로 줄입니다. 124·206줄의lastCol = 10은 머리글이 없을 때의 기본값이므로8로 맞춥니다.
고칠 때 주의
- 목록을 다시 만들면 48~52줄에서 같은 이름의 '폴더'/'파일'/'폴더및파일' 시트를 경고 없이 삭제합니다. 작업 내용을 남기려면 시트 이름을 바꿔 두거나 이 부분에 확인 창을 넣으세요.
- B열 이름에는 정렬용 앞 공백 2칸이 붙어 있고 코드가
Trim으로 지워 비교합니다. 다른 곳에서 B열을 읽도록 고칠 때도Trim을 빼먹으면 원본을 찾지 못합니다. Name … As …로 바뀐 이름은 되돌리기(Ctrl+Z)가 되지 않습니다. 시험할 때는 복사본 폴더에서 실행하세요.
05폴더/파일 정리 폴더정리
이름 속 키워드로 폴더를 추천해 기준 폴더로 자동 이동(분류_결과 시트에서 확인 후 이동, 폴더 다시 선택 버튼).
폴더 분석, 단어 빈도 기반 분류 추천, 기준폴더 키워드 표, 이동·날짜 이름 정리·이름 변경까지 폴더/파일 정리 기능 전체를 담은 모듈입니다(1876줄). 진입점과 시트 버튼용 Public 프로시저, 분석·매칭·서식용 Private 함수로 나뉩니다.
소스 보기 (1,876줄)
심화과정: 소스 분석
리본 진입점 폴더분석정리 가 '기준폴더 설정' 또는 폴더/파일/폴더및파일 분석 중 하나를 고르게 하고, 분석은 FC_RunAnalysis 가 FileSystemObject 로 항목을 나열해 '분류_결과' 시트에 표로 씁니다. 이름을 정규식으로 단어로 나눠 Scripting.Dictionary 로 빈도를 세고, 날짜·숫자류 단어를 뺀 뒤 각 항목 이름에 든 가장 흔한 단어를 E열 '이동 폴더'로 추천합니다(기준폴더가 설정돼 있으면 그 하위 폴더별 키워드 표와 대조). 사용자가 E·F열을 확인·수정한 뒤 1행 버튼을 누르면 MoveFile/MoveFolder 로 실제 이동·이름 변경을 하고 결과를 G열에 남깁니다.
모듈 구성
modFolderClassify— 폴더 분석, 단어 빈도 기반 분류 추천, 기준폴더 키워드 표, 이동·날짜 이름 정리·이름 변경까지 폴더/파일 정리 기능 전체를 담은 모듈입니다(1876줄). 진입점과 시트 버튼용 Public 프로시저, 분석·매칭·서식용 Private 함수로 나뉩니다.
코드 흐름 (줄 번호를 누르면 소스의 그 부분으로 이동)
modFolderClassify34~62줄(폴더분석정리)① 메뉴와 기준폴더 상태 표시먼저FC_BasePath()로 현재 통합 문서의 '기준폴더' 시트 B2(하이퍼링크 주소 우선)를 읽어 안내 문구를 만들고,UI_Choose로 네 가지 메뉴를 보여 줍니다. 1번을 고르면FC_SetBasePathDialog로 기준폴더 시트만 만들고 끝나며, 분석은 그 시트 1행 버튼(FC_분석_폴더등)에서 이어집니다. 2~4번은 분석 유형 문자열('폴더'·'파일'·'폴더및파일')로 바꿔FC_RunAnalysis를 부릅니다. 기준폴더 경로를 레지스트리가 아니라 시트에 두므로, 그 파일을 저장해 두면 다음에도 같은 설정을 씁니다.modFolderClassify251~381줄(FC_SetBasePathDialog, FC_BuildBaseSheet)② 기준폴더 시트: 폴더별 키워드 표FC_BuildBaseSheet는 기준폴더의 하위 폴더마다 새 사전을 만들고, 폴더 이름을FC_CollectWordFreq에 두 번 넣어(329~330줄, 가중치 2) 안의 파일 이름 단어와 함께 빈도를 셉니다.FC_TopKeywords로 상위 15개(KW_MAX)를 골라 C열부터 '키워드1~15' 칸에 쓰고, 마지막 열에 파일 수를 적습니다. 키워드 칸은 연한 노랑RGB(255, 251, 235)로 '수정 가능'을 표시하며, 사용자가 고친 키워드는 나중에FC_ReadBaseMapping이 그대로 읽어 씁니다. 1행에는 초록 분석 버튼 3개와 '기준폴더 다시 읽기' 버튼을 만듭니다.modFolderClassify76~134줄, 1416-1452 (FC_RunAnalysis 앞부분, FC_CreateMoveButton)③ 분석 준비와 결과 시트 틀분류 키워드 배열kwArr는 빈 값 하나로 고정하고(81~82줄) 폴더 선택 대화상자로 분석 폴더를 받습니다. 화면 갱신과 자동 계산을 끈 뒤FC_GetOrCreateSheet("분류_결과")를 비우고,FC_CreateMoveButton이 1행에 이동·기준폴더·날짜 형식·이름 변경 버튼 10개와 설명 도형을 배치합니다. 2행 B2 에 분석 폴더를 하이퍼링크로 넣고, 3행에 NO·폴더명·파일명·확장자·이동 폴더·이름 변경(안)·이동결과·분석 추천·추천 근거(A~I열) 머리글을 씁니다.modFolderClassify865~1046줄, 828-840 (FC_AnalyzeFolders/Files/FoldersAndFiles, FC_SkipItem)④ 항목 나열과 단어 빈도 수집분석 유형에 따라 하위 폴더만, 최상위 파일만, 또는 최상위 파일 → 하위 폴더 → 그 안의 파일 순서로 한 행씩 씁니다.FC_SkipItem은 '~$'·'$' 로 시작하는 이름, desktop.ini·thumbs.db, 숨김 속성(폴더는And 2, 파일은 숨김+시스템And 6)을 건너뜁니다. 각 이름은FC_CollectWordFreq로 전역 사전globalFreq에 누적되고, 진행률은Application.StatusBar에 백분율로 표시됩니다. 파일 내부 텍스트 검색(FC_ExtractFileText)은 키워드가 있을 때만 실행되도록FC_HasKeywords로 막혀 있습니다.modFolderClassify1214~1263줄(FC_IsNumberLike, FC_CollectWordFreq, FC_CleanText)⑤ 정규식으로 단어 다듬기FC_CleanText는VBScript.RegExp로 한글·영문·숫자 이외 문자를 빈칸으로 바꾸고([^가-힣a-zA-Z0-9]), 숫자만으로 된 단어와 연속 공백을 정리합니다.FC_CollectWordFreq는 2글자 이상이면서FC_IsNumberLike가 아닌 단어만 사전에 셉니다.FC_IsNumberLike는 숫자만 있는 단어와, 1219줄 정규식^\d+(년도|년|월|일|차|회|…)$에 맞는 '2026년'·'3차'·'2학기' 같은 단어를 걸러 냅니다.modFolderClassify1266~1319줄, 476-512 (FC_FillRecommendations, FC_WriteKeywordTable)⑥ E열 이동 폴더 자동 추천전체 빈도 상위 5개(2회 이상)를 F2 에 '자동 추천 키워드'로 적고, E열이 빈 행마다 그 이름의 단어 중 전체 빈도가 가장 높은 단어(bestN = 1에서 시작하므로 2회 이상)를 고릅니다. 고른 단어가UI_ValidFolderName을 통과하면 E열에 쓰고 I열에 '이름에 'OO' 포함 (전체 N회)' 근거를 남기며, 없으면 '미분류'로 둡니다. 이어FC_WriteKeywordTable이 K열부터 3열(순위·키워드·빈도)에 분석 키워드 상위 15개 표를 만듭니다.modFolderClassify538~604줄, 414-432, 810-824 (FC_ApplyBaseKeywords, FC_MatchBaseFolder, FC_ResolveDest)⑦ 기준폴더 키워드와 대조기준폴더가 설정돼 있으면 O열부터 기준 키워드 표를 쓰고, '기준폴더' 시트 표를 읽어(경로가 다르면 시트를 새로 만듦) 행마다FC_MatchBaseFolder를 부릅니다. 이 함수는 폴더별 키워드 중 이름에 들어 있는 개수를 세어 가장 많이 맞는 폴더(동점이면 위쪽 행)를 돌려줍니다. 표에서 못 찾으면 상위 키워드 단순 포함 검사 후FC_ResolveDest로 키워드를 포함한 기존 하위 폴더 중 이름이 가장 짧은 것을 고릅니다. 원래 추천은 H열 '분석 추천(참고)'에 보관하고, 맞는 폴더가 없으면 E열을 '미분류'로 바꿉니다.modFolderClassify658~807줄, 1363-1410 (FC_MoveAll, FC_GetUniquePath)⑧ 미리보기 후 실제 이동먼저 G열이 '성공'이 아닌 행을 훑어 E열 폴더별 이동 개수를 사전에 모으고,UI_Choose로 '폴더별 이동 항목 수' 미리보기를 보여 승인받습니다. 승인되면 행마다 원본·대상 경로를 만들고, 폴더를 자기 자신이나 하위 폴더로 옮기는 경우와 이미 그 폴더에 있는 파일은 건너뜁니다. 대상 폴더가 없으면CreateFolder로 만들고,FC_GetUniquePath가 같은 이름이 있을 때 '_1', '_2' 를 붙인 경로를 찾은 뒤MoveFolder/MoveFile로 옮깁니다. 폴더째 옮긴 경우 그 안 파일 행은 '폴더와 함께 이동됨'으로 처리합니다.modFolderClassify1458~1502줄, 1558-1634, 1693-1766 (FC_NormalizeDate, FC_CycleNameFormat, FC_ApplyRenames)⑨ 날짜 정규화와 이름 변경FC_NormalizeDate는 정규식 네 개를 순서대로 시도해 '2026.09.26'·'2026.3.5'·'260926'·'0926(올해)' 같은 날짜를 YYYYMMDD 로 바꾸고, 찾은 날짜를found로 돌려줍니다.FC_CycleNameFormat은 버튼을 누를 때마다 H2/I2 셀에 저장한 순번을 1→4로 돌리며 '키워드 월일', '키워드-YYYYMMDD' 같은 형식의 새 이름을 F열 '이름 변경(안)'에 씁니다.FC_ApplyRenames는 F열 값으로MoveFile/MoveFolder를 써서 같은 폴더 안에서 이름을 바꾸고, 폴더 이름이 바뀌면 그 폴더에 속한 파일 행의 B열도 함께 고칩니다.
핵심 VBA 객체·함수
CreateObject("VBScript.RegExp") — .Pattern / .Test / .Execute(…)(0).SubMatches— 정규식으로 특수문자 정리, 날짜·숫자 단어 판별, 날짜 토큰 추출을 합니다. SubMatches(n) 으로 괄호 그룹(연·월·일)을 꺼냅니다.CreateObject("Scripting.Dictionary") + CompareMode = vbTextCompare— 단어 → 빈도, 폴더명 → 이동 개수를 담는 사전입니다. Exists 로 있는지 확인한 뒤 값을 1씩 늘립니다.fso.MoveFile / fso.MoveFolder / fso.CreateFolder— 파일·폴더를 다른 경로로 옮기거나(같은 폴더 안이면 이름 변경) 대상 폴더를 만듭니다. 대상에 같은 이름이 있으면 오류가 나므로 FC_GetUniquePath 로 먼저 피합니다.Folder.SubFolders / Folder.Files / item.Attributes— 하위 폴더·파일을 나열하고, 속성 비트(2=숨김, 4=시스템)로 분석 대상에서 뺄 항목을 판단합니다.Application.FileDialog(msoFileDialogFolderPicker)— 폴더 선택 대화상자를 띄웁니다. 기준폴더 설정에서는 .InitialFileName 으로 이전 기준폴더를 시작 위치로 줍니다.ws.Shapes.AddShape(msoShapeRoundedRectangle, …).TextFrame2— 1행 버튼과 설명 도형을 만듭니다. FC_AddNote 는 TextFrame2 로 여백·줄바꿈·글꼴을 지정해 안내 상자를 그립니다.ws.Hyperlinks.Add Anchor, Address, TextToDisplay— B2 분석 폴더, 기준폴더 경로, 하위 폴더 이름에 탐색기로 열리는 링크를 겁니다. 이후 단계는 이 하이퍼링크 주소를 경로로 다시 읽습니다.
부르는 공통 함수
- UI_Choose (modRibbonUI) — 첫 메뉴, 이동 미리보기·이름 변경 실행 확인 카드
- UI_StyleButton (modRibbonUI) — 1행 도형 버튼의 색·글자 지정과 실행할 프로시저 연결(AlternativeText)
- UI_ValidFolderName (modRibbonUI) — 추천 키워드·E열 폴더명·F열 새 이름이 Windows 이름 규칙에 맞는지 검사
이렇게 고쳐 쓰세요
- 키워드 표를 15개에서 10개로 줄이기 — 25줄
Private Const KW_MAX As Long = 15의15를10으로 바꿉니다. 기준폴더 시트의 '키워드1~N' 열, K열·O열 키워드 표, 표 읽기 범위가 모두 이 상수를 쓰므로 한 곳만 고치면 됩니다. 186줄 완료 메시지의 '키워드 15개' 문구는 따로 고칩니다. - 세 글자 이상 단어만 분류 키워드로 쓰기 — 1233줄
If Len(W) >= 2 And …와 1300줄If Len(W) >= 2 And …의2를 모두3으로 바꿉니다. 앞쪽은 빈도 수집, 뒤쪽은 항목별 추천 단계라 둘 다 바꿔야 결과가 일치합니다. - '주차'·'번째' 같은 단위가 붙은 숫자도 키워드에서 빼기 — 1219줄 정규식 괄호 안 목록에
|번째처럼 단위를 덧붙입니다(예:…|차년도|기준|번째)$). '주차'는 이미 들어 있으니 겹치지 않게 확인합니다. - 주황 버튼 색을 파랑으로 — 29줄
FC_ORANGE As Long = 39167을16742912로 바꿉니다.Const에는RGB()함수를 쓸 수 없어 숫자로 적는데, 값은 빨강 + 초록×256 + 파랑×65536(0x00BBGGRR, BGR 순서)이므로RGB(0, 122, 255)= 0 + 122×256 + 255×65536 = 16742912 입니다. 참고로 현재 39167 은RGB(255, 152, 0)입니다.
고칠 때 주의
- 주석에는 분석 키워드가 I~K열, 기준 키워드가 M~O열이라고 되어 있지만 실제 코드는 K열(161줄
"K")과 O열(543줄"O")부터 씁니다. 기준폴더 이동은 622줄에서 P2(기준 키워드 표의 경로 칸)를 확인하므로, 표 위치를 옮기면 이 셀 주소도 함께 바꿔야 합니다. - 분석 직후 170줄
FC_ApplyFormatting이 A4:I 범위의 배경을 지우고(1858줄) 줄무늬를 다시 칠하므로, 추천 단계에서 E열에 넣은 연두·노랑 표시는 처음 분석 결과에는 남지 않습니다. 색을 남기려면 서식 적용 뒤에 색칠하도록 순서를 바꿔야 합니다. - 파일 이동·이름 변경은 자동 백업이 없고 되돌릴 수 없습니다. 또 파일 내부 텍스트 검색 코드(1083~1206줄)는
kwArr가 항상 비어 있어 지금은 실행되지 않으며, 살리면 Word 를 파일마다 새로 띄우므로 많이 느려질 수 있습니다.
06셀간격정리 셀정리하기
①모든 글자 1행 ②A4 가로 ③A4 세로 ④되돌리기 — 열 너비·행 높이 정리(#### 방지).
셀간격정리 기능 전체(진입점, 대상 범위 결정, 한 줄 정리, A4 가로·세로 인쇄용 열 너비 배분, 백업 시트 저장·복원)를 담은 단일 모듈입니다.
소스 보기 (448줄)
심화과정: 소스 분석
리본 버튼이 셀자동맞춤2 를 부르면 대상 범위(2셀 이상 선택 범위 또는 사용 영역)를 정하고 선택 폼으로 네 가지 방식 중 하나를 고릅니다. 정리 전에 열 너비·행 높이·줄바꿈·셀에 맞춤·페이지 설정을 매우 숨김(VeryHidden) 백업 시트의 한 행에 문자열로 저장해 두고, '되돌리기' 를 고르면 그 문자열을 다시 풀어 원래 상태로 복원합니다. A4 인쇄용 정리는 열마다 한 줄 기준 너비와 숫자가 깨지지 않는 최소 너비를 측정한 뒤 인쇄 가능 폭 안에서 비례 배분합니다.
모듈 구성
modAutoFit— 셀간격정리 기능 전체(진입점, 대상 범위 결정, 한 줄 정리, A4 가로·세로 인쇄용 열 너비 배분, 백업 시트 저장·복원)를 담은 단일 모듈입니다.
코드 흐름 (줄 번호를 누르면 소스의 그 부분으로 이동)
modAutoFit29~48줄(셀자동맞춤2)① 리본 진입과 방식 선택리본 버튼은 공통 허브UI_Ribbon→UI_Run을 거쳐셀자동맞춤2로 들어옵니다. 대상 범위를 구한 뒤UI_Choose로 4개 항목(한 줄로 / A4 가로 / A4 세로 / 되돌리기)을 보여 주고, 반환 번호에 따라Select Case로 각 처리 프로시저를 부릅니다. 취소하면 0이 돌아와 아무 것도 하지 않습니다.modAutoFit51~65줄(AF_TargetRange)② 대상 범위 결정선택이 Range 이고 셀이 2개 이상이면 선택 범위를, 아니면UsedRange를 씁니다. 여러 영역을 선택했으면 첫 영역만 쓰고, 열·행 전체를 선택한 경우에는 사용 영역과의Intersect로 줄입니다.CountA가 0이면 빈 범위로 보고 Nothing 을 돌려줍니다.modAutoFit289~348줄(AF_Snapshot, AF_PutLong)③ 실행 전 상태 저장정리 전에 숨김 백업 시트(없으면 만들고xlSheetVeryHidden으로 숨김)에 시트 하나당 한 행을 기록합니다. A열 시트명, B열 저장 시각, C열 범위, D~H열 페이지 설정(방향·용지·Zoom·가로/세로 쪽수)을 넣고, I열부터 열 너비열:너비;, 행 높이행:높이;, 줄바꿈 셀 주소, 셀에 맞춤 셀 주소를 이어 씁니다. 셀 하나에 30000자(CHUNK)까지만 넣고 넘치면 다음 셀로 이어 쓰며, 각 묶음 끝에|셀을 둡니다. 같은 시트 기록이 이미 있으면 덮어쓰지 않아 '첫 실행 전' 상태가 유지됩니다.modAutoFit68~88줄(AF_OneLine)④ 모든 글자 1행으로범위의WrapText·ShrinkToFit을 끄고Columns.AutoFit으로 열 너비를 내용에 맞춥니다. 너비가MAX_COL_W(250)를 넘는 열은 250으로 자르고 개수를 세어 결과 메시지에 보여 준 뒤,Rows.AutoFit으로 행 높이를 맞춥니다.modAutoFit91~131줄(AF_FitPage)⑤ A4 인쇄용: 페이지 설정과 열 너비 측정용지를xlPaperA4, 방향을 인수orient로 바꾸고, 가로면 A4 높이(841.9pt)·세로면 A4 폭(595.3pt)에서 좌우 여백을 빼 인쇄 가능 폭을 구합니다.AF_ApplyCellFormat으로 글자 셀은 자동 줄바꿈, 숫자·날짜 셀은 셀에 맞춤으로 바꾼 다음, 측정을 위해 줄바꿈을 잠시 끄고 AutoFit 해 열마다 자연 너비natW와 숫자가 깨지지 않는 최소 너비minW(AF_MinWidth)를 배열에 담습니다.modAutoFit132~180줄(AF_FitPage)⑥ 인쇄 폭 안에서 열 너비 배분AF_MeasureUnit이 마지막 열의 너비를 10만큼 바꿔 보며 '너비 1단위 = 몇 pt' 와 열당 고정 여백을 실측하고, 이를 이용해 인쇄 가능 폭을 너비 단위로 환산합니다. 자연 너비 합이 들어가면 그대로, 최소 너비 합조차 넘치면 최소 너비를 비율 축소(최소 2), 그 사이면 최소 너비를 보장하고 남는 폭을 (자연−최소)에 비례해 나눕니다. 끝으로 행 높이를 AutoFit 하고FitToPagesWide = 1로 가로 1쪽 맞춤을 안전장치로 겁니다.modAutoFit362~448줄(AF_Restore, AF_GetLong)⑦ 되돌리기백업 시트에서 현재 시트 이름의 행을 찾고,AF_GetLong으로|가 나올 때까지 셀을 이어 붙여 네 문자열을 복원합니다. 범위의 줄바꿈·셀에 맞춤을 모두 끈 뒤 기록된 셀만 다시 켜고,Split으로열:너비쌍을 풀어 너비와 숨김(h접미사)을 되돌리며 행 높이와 페이지 설정도 복원합니다. 사용한 기록 행은 지우고, 백업 시트가 비면 시트도 삭제합니다.
핵심 VBA 객체·함수
Range.Columns.AutoFit / Range.Rows.AutoFit— 열 너비와 행 높이를 셀 내용에 맞게 자동 조정합니다. 한 줄 정리와 자연 너비 측정에 쓰입니다.Range.WrapText / Range.ShrinkToFit— 자동 줄바꿈과 '셀에 맞춤'(글자 축소) 서식 속성입니다. 글자 셀과 숫자 셀에 서로 다르게 적용됩니다.Worksheet.PageSetup (.PaperSize, .Orientation, .Zoom, .FitToPagesWide, .FitToPagesTall)— 용지 크기·방향과 '한 페이지에 맞춤' 인쇄 설정을 바꾸고, 백업 시트에 원래 값을 기록해 복원합니다.Range.ColumnWidth / Range.Width— ColumnWidth 는 문자 단위 너비, Width 는 pt 단위 실제 폭입니다. 두 값을 비교해 단위 환산 계수를 실측합니다.Worksheet.Visible = xlSheetVeryHidden— 백업 시트를 시트 숨기기 취소 메뉴에도 나오지 않게 숨깁니다.Intersect(rng, ws.UsedRange)— 열·행 전체를 선택했을 때 실제 데이터가 있는 영역만 남깁니다.VarType(c.Value2)— 셀 값이 숫자·날짜·논리·오류 형식인지 판정해 숫자형(셀에 맞춤)과 글자형(줄바꿈)을 구분합니다.
부르는 공통 함수
- UI_Choose (modRibbonUI) — 선택 폼(frmChoice)을 띄워 1~4번 정리 방식을 고르게 하고 번호를 반환
- UI_Ribbon / UI_Run (modRibbonUI) — 리본 버튼 tag "셀자동맞춤2" 를 받아 이 프로시저를 호출하고 Excel 설정을 보관·복원
이렇게 고쳐 쓰세요
- 한 줄 정리 때 열 너비 상한(현재 250) 바꾸기 — 16줄
Private Const MAX_COL_W As Single = 250의250을 원하는 값(예:100)으로 바꿉니다. 한 줄 정리(79줄)와 A4 정리의 자연 너비(125줄)에 함께 적용됩니다. - 글자 열의 최소 너비를 넓히기 — 15줄
MIN_TEXT_W As Single = 6의6을8등으로 키웁니다.AF_MinWidth(217줄)가 이 값을 최소 너비의 출발점으로 씁니다. - 인쇄 시 세로도 1쪽에 맞추기 — 169줄
.FitToPagesTall = False를.FitToPagesTall = 1로 바꿉니다. 행이 많으면 글자가 매우 작게 인쇄되니 주의합니다. - 숫자 셀 최소 너비 여유를 늘리기 — 223줄
L = Len(T) * (c.Font.Size / base) * 1.05 + 1.5의1.5를2.5로 늘리면 숫자 열이 조금 더 넓어집니다.
고칠 때 주의
- 백업 시트 이름 상수(11줄)를 바꾸면 이미 저장된 되돌리기 기록을 찾지 못합니다.
- 되돌리기 기록은 시트별 1개이며 첫 실행 전 상태만 보관합니다(301줄). 두 번 정리한 뒤 되돌리면 첫 정리 이전으로 돌아갑니다.
- 통합 문서 구조가 보호되어 있으면 백업 시트를 만들 수 없어 되돌리기 없이 진행됩니다(295-298줄).
07병합효과 셀정리하기
①적용: 실제 병합 없이 같은 값 구간의 가운데 한 칸만 표시 ②취소.
리본 tag "FakeMergeMenu" 의 진입점으로, 전·후 미리보기가 있는 선택 폼을 띄워 적용(Fake병합) 또는 취소(병합취소)로 보냅니다.
소스 보기 (20줄)
병합효과 적용 본체(Fake병합)와 숨김 셀 표시형식 복원(FM_RestoreHidden), 배경색 선택 대화상자(PickBgColor)를 담고 있습니다.
소스 보기 (145줄)
심화과정: 소스 분석
셀을 실제로 병합하지 않고, 열마다 같은 값이 이어지는 구간을 찾아 가운데 한 칸만 보이게 하고 나머지 셀은 표시형식 ;;; 로 글자만 숨깁니다. 구간 사이의 가로 테두리는 R1C1 수식 조건부서식으로 지우므로 값은 셀마다 그대로 남아 정렬·필터·계산식이 계속 동작합니다. 숨기기 전 원래 표시형식은 셀의 ID 속성에 보관해 두었다가 취소 때 복원합니다.
모듈 구성
modFakeMergeMenu— 리본 tag "FakeMergeMenu" 의 진입점으로, 전·후 미리보기가 있는 선택 폼을 띄워 적용(Fake병합) 또는 취소(병합취소)로 보냅니다.modFakeMerge— 병합효과 적용 본체(Fake병합)와 숨김 셀 표시형식 복원(FM_RestoreHidden), 배경색 선택 대화상자(PickBgColor)를 담고 있습니다.modFakeMergeUndo— 선택 범위에서 숨김 표시형식·조건부서식·배경색을 걷어내 병합효과를 취소합니다.
코드 흐름 (줄 번호를 누르면 소스의 그 부분으로 이동)
modFakeMergeMenu8~20줄(FakeMergeMenu)① 메뉴 폼에서 적용/취소 선택UI_ChooseEx에 항목 2개와 미리보기 사양merge:after,merge:before를 넘겨 전·후 모양을 그림으로 보여 줍니다. 체크 항목은 없으므로Empty와 더미 변수를 넘기고, 1이면Fake병합, 2면병합취소를 부릅니다.modFakeMerge7~30줄(Fake병합)② 범위 확인과 배경색 결정선택이 Range 가 아니거나 셀이 2개 미만이면 안내 후 끝냅니다.UI_Choose로 '색상 직접 선택' 과 '기본 흰색' 중 하나를 고르게 하고, 직접 선택이면PickBgColor가 Excel 색 편집 대화상자를 띄웁니다. 흰색은RGB(255, 255, 255)입니다.modFakeMerge32~47줄(Fake병합)③ 초기화와 테두리 숨김 조건부서식먼저FM_RestoreHidden으로 이전 병합효과의 숨김을 되돌리고, 기존 조건부서식을 지운 뒤 전체 테두리·가운데 정렬을 적용합니다. 이어서=AND(RC<>"",RC=R[-1]C)(위 셀과 같으면 위쪽 선 없음)과=AND(RC<>"",RC=R[1]C)(아래 셀과 같으면 아래쪽 선 없음) 두 규칙을 추가해 같은 값 구간이 한 칸처럼 보이게 합니다.modFakeMerge49~72줄(Fake병합)④ 열별 같은 값 구간 찾기열 j 마다 위에서 아래로 내려가며 현재 셀과 다음 셀의 비교 문자열(FM_Val)이 같으면countSame을 늘립니다. 구간이 끝났을 때 길이가 2 이상이면 가운데 위치(countSame - 1) \ 2를 구해 구간에 배경색을 칠하고FM_HideExcept로 가운데 셀 외의 글자를 숨깁니다. 오류값은 주소를 붙여 서로 다르게 취급하므로 묶이지 않습니다.modFakeMerge90~128줄(FM_HideExcept, FM_RestoreHidden)⑤ 표시형식 보관과 복원숨길 셀은 원래NumberFormat을c.ID에 적어 두고;;;(양수;음수;0;텍스트 모두 빈칸)로 바꿉니다. 복원할 때는 ID 에 보관한 형식을 쓰고, 없으면 위아래 50칸 안에서 같은 값이면서 숨겨지지 않은 이웃 셀의 형식을, 그것도 없으면General을 씁니다.modFakeMergeUndo5~30줄(병합취소)⑥ 병합효과 취소한 셀만 선택했으면CurrentRegion으로 범위를 넓힙니다. 숨김 셀 수를 센 다음FM_RestoreHidden으로 표시형식을 되돌리고, 조건부서식 삭제, 전체 테두리 복원,Interior.ColorIndex = xlNone으로 배경색을 없앱니다.
핵심 VBA 객체·함수
Range.FormatConditions.Add(Type:=xlExpression, Formula1:="=AND(RC<>"""",RC=R[-1]C)")— 수식형 조건부서식을 추가합니다. R1C1 표기라 범위의 모든 셀에 '바로 위 셀과 같으면' 조건이 상대적으로 적용됩니다.FormatCondition.Borders(xlTop).LineStyle = xlNone— 조건이 참인 셀의 위쪽(또는 아래쪽) 테두리를 지워 같은 값 구간을 한 칸처럼 보이게 합니다.Range.NumberFormat = ";;;"— 값은 그대로 두고 화면·인쇄에서 글자만 보이지 않게 하는 사용자 지정 표시형식입니다.Range.ID— 셀에 붙일 수 있는 문자열 속성으로, 숨기기 전 원래 표시형식을 임시 보관하는 데 씁니다.Application.Dialogs(xlDialogEditColor).Show(10, 255, 255, 255)— 통합 문서 팔레트 10번 색을 편집하는 색 대화상자를 띄웁니다. 고른 색을 읽은 뒤 팔레트를 원래 색으로 되돌립니다.Range.CurrentRegion— 빈 행·열로 둘러싸인 연속 데이터 영역입니다. 취소 때 한 셀만 선택한 경우 범위를 넓히는 데 씁니다.
부르는 공통 함수
- UI_ChooseEx (modRibbonUI) — 미리보기(merge:after / merge:before)가 있는 선택 폼
- UI_Choose (modRibbonUI) — 배경색 방식 선택 폼
- UI_DrawPreview (modUITheme) — frmChoice 가 미리보기 사양 문자열을 그림으로 그릴 때 사용
이렇게 고쳐 쓰세요
- 기본 배경색을 흰색 대신 연한 회색으로 — modFakeMerge 29줄
bgColor = RGB(255, 255, 255)를RGB(242, 242, 242)로 바꿉니다. VBA 의RGB(빨,초,파)함수는 내부적으로 BGR 순서 Long 값을 만들어 주므로 인수는 평소 RGB 순서대로 쓰면 됩니다. - 구간에서 가운데 대신 맨 위 셀만 보이게 — modFakeMerge 60줄
midOffset = (countSame - 1) \ 2를midOffset = 0으로 바꿉니다. - 빈 셀도 같은 값으로 묶기 — modFakeMerge 56줄
If cur <> "" And cur = nxt Then에서cur <> "" And를 지우고, 42·45줄 수식의RC<>"""",조건도 함께 빼야 테두리와 숨김이 일치합니다.
고칠 때 주의
- 적용과 취소 모두 범위의 기존 조건부서식을 전부 삭제합니다(36줄, Undo 20줄). 다른 조건부서식이 있는 범위에는 주의합니다.
- 취소는 배경색을 xlNone 으로 지우므로 병합효과 전부터 있던 배경색도 사라집니다.
- 같은 값 비교는 열 방향(세로)만 합니다. 가로 방향 병합효과는 지원하지 않습니다.
08셀색표시 셀정리하기
①조건에 맞는 행만 색 ②색 지우기 ③~⑥ 교차형 파랑·노랑·핑크·연한녹색(폼 미리보기).
리본 tag "CellColorMenu" 의 진입점(메뉴 폼)과 '색 지우기'(CC_ClearColors)를 담당합니다.
소스 보기 (56줄)
조건부서식으로 조건에 맞는 행에 색을 표시하는 셀색표시 본체와, 텍스트 조건 수식 생성·좌상단 셀 찾기·셀 서식 대화상자 색 선택 보조 함수를 담고 있습니다.
소스 보기 (283줄)
교차행 색칠 공용 루틴 AR_Stripe(짝수 행에 지정 색)와 예전 버튼용 진입점 짝수행연한파란색을 담고 있습니다.
소스 보기 (33줄)
심화과정: 소스 분석
리본의 CellColorMenu 가 미리보기가 있는 선택 폼으로 여섯 가지 작업(조건부 색 표시, 색 지우기, 교차형 4색)을 고르게 합니다. 조건부 색 표시는 기준열 주소를 '열 고정·행 상대'($B2 꼴)로 만들어 수식형 조건부서식을 추가하므로 조건에 맞는 행 전체가 칠해지고, 교차형은 짝수 번째 행의 Interior.Color 를 직접 칠합니다. 색 지우기는 범위의 채우기색과 조건부서식을 함께 제거합니다.
모듈 구성
modCellColorMenu— 리본 tag "CellColorMenu" 의 진입점(메뉴 폼)과 '색 지우기'(CC_ClearColors)를 담당합니다.modCellColorMark— 조건부서식으로 조건에 맞는 행에 색을 표시하는 셀색표시 본체와, 텍스트 조건 수식 생성·좌상단 셀 찾기·셀 서식 대화상자 색 선택 보조 함수를 담고 있습니다.modAltRowBlue— 교차행 색칠 공용 루틴 AR_Stripe(짝수 행에 지정 색)와 예전 버튼용 진입점 짝수행연한파란색을 담고 있습니다.modAltRowYellow— 예전 버튼용 진입점 짝수행연한노란색 하나뿐이며, AR_Stripe 에 연한 노랑을 넘겨 호출합니다.
코드 흐름 (줄 번호를 누르면 소스의 그 부분으로 이동)
modCellColorMenu10~29줄(CellColorMenu)① 메뉴 폼과 분기UI_ChooseEx에 6개 항목과 미리보기 사양 배열을 넘깁니다. 교차형 항목에는stripe:DDEBF7처럼 RRGGBB 16진 색을 주어 폼 오른쪽에 줄무늬 미리보기가 그려집니다. 선택 번호에 따라셀색표시,CC_ClearColors, 또는AR_Stripe RGB(...)를 부릅니다.modCellColorMenu32~56줄(CC_ClearColors)② 색 지우기한 셀만 선택했으면CurrentRegion, 열·행 전체를 선택했으면 사용 영역과의Intersect로 범위를 정합니다. 확인 폼에서 '지우기' 를 고르면 조건부서식 수를 센 뒤FormatConditions.Delete와Interior.ColorIndex = xlNone으로 지웁니다. 글꼴·테두리·값은 건드리지 않습니다.modAltRowBlue9~33줄(AR_Stripe)③ 교차형 색칠범위 결정 방식은 색 지우기와 같습니다.For i = 2 To rng.Rows.Count Step 2로 범위 안의 짝수 번째 행만 골라Interior.Color를 칠하고 칠한 행 수를 알려 줍니다. 조건부서식이 아닌 직접 채우기라 정렬하면 색이 행을 따라 움직입니다.modCellColorMark3~56줄(셀색표시)④ 조건부 색 표시: 처리 방식·범위·기준열먼저 기존 조건부서식을 지울지, 새 규칙을 1순위로 추가할지 고르게 하고UI_ChoiceToMsg로 vbYes/vbNo/vbCancel 로 바꿉니다. 적용 범위는CurrentRegion자동 선택 또는Application.InputBox(Type:=8)로 직접 드래그하게 하고, 기준열 셀도 같은 방식으로 받습니다. 기준 주소는Address(RowAbsolute:=False, ColumnAbsolute:=True)로 적용 범위 첫 행 기준$열행형태로 만듭니다.modCellColorMark58~94줄(셀색표시)⑤ 조건 수식 만들기1·2번은=ISODD(기준),=NOT(ISODD(기준))입니다. 3번은UI_Input으로 받은 문자열이=로 시작하면 그대로 수식으로,<·>로 시작하면 기준 주소 뒤에 붙여=$B2<10처럼 만듭니다. 4번은BuildTextConditionFormula가 '기준 셀 값과 같음'(절대 주소 비교) 또는 '텍스트 포함'(IFERROR(ISNUMBER(SEARCH(...)),FALSE)) 수식을 만듭니다.modCellColorMark96~139줄(셀색표시)⑥ 색 선택과 규칙 추가연한 노랑RGB(255, 242, 204)등 4색 중 고르거나, 5번이면 셀 서식 '패턴' 대화상자에서 직접 고릅니다. 처리 방식이 '모두 삭제' 면 기존 규칙을 지운 뒤FormatConditions.Add(Type:=xlExpression)로 규칙을 넣고.Interior.Color를 지정하며, '추가' 면.SetFirstPriority로 최우선 순위로 올립니다. 끝나면 범위 좌상단 셀로 화면을 되돌립니다.modCellColorMark222~283줄(PickColorFromFormatCellsDialog_NoJump)⑦ 대화상자로 색 고르기(화면 점프 방지)시트 맨 끝 셀(XFD1048576)을 임시로 선택한 뒤 스크롤 위치를 되돌려 화면이 튀지 않게 하고,Application.Dialogs(xlDialogPatterns).Show로 셀 서식 대화상자를 띄웁니다. 그 셀에 칠해진 색을 읽어 반환한 뒤 원래 값·채우기를 복원하고 범위 좌상단으로 이동합니다.
핵심 VBA 객체·함수
Range.FormatConditions.Add(Type:=xlExpression, Formula1:=formula1)— 수식이 TRUE 인 셀에 서식을 적용하는 조건부서식 규칙을 추가합니다. 기준열만$로 고정해 행 전체에 같은 판단이 적용됩니다.FormatCondition.SetFirstPriority— 새로 추가한 규칙을 조건부서식 우선순위 1번으로 올립니다.Range.Address(RowAbsolute:=False, ColumnAbsolute:=True)— 열만 절대 참조인 주소(예:$B2)를 만들어 조건부서식 수식의 기준으로 씁니다.Application.InputBox(Prompt, Title, Type:=8)— 사용자가 시트에서 마우스로 범위를 드래그해 Range 를 돌려받는 입력 상자입니다.Application.Dialogs(xlDialogPatterns).Show— Excel 의 셀 서식 '채우기(패턴)' 대화상자를 띄웁니다. 확인하면 선택한 셀에 색이 적용됩니다.Range.Interior.Color = RGB(221, 235, 247)— 셀 채우기 색을 직접 지정합니다. RGB 함수가 BGR 순서 Long 값을 만들어 줍니다.
부르는 공통 함수
- UI_ChooseEx (modRibbonUI) — stripe:RRGGBB 미리보기가 있는 메뉴 폼
- UI_Choose / UI_ChoiceToMsg (modRibbonUI) — 단계별 선택 폼과 선택 번호→vbYes/vbNo/vbCancel 변환
- UI_Input (modRibbonUI) — 사용자 조건·포함 텍스트 입력 폼(frmInput)
이렇게 고쳐 쓰세요
- 교차형 색을 짝수 행이 아니라 홀수 행에 칠하기 — modAltRowBlue 26줄
For i = 2 To rng.rows.Count Step 2의 시작값2를1로 바꿉니다. - 교차형 파랑 색을 조금 더 진하게 — modCellColorMenu 24줄
RGB(221, 235, 247)를RGB(189, 215, 238)로 바꾸고, 미리보기가 맞도록 20줄stripe:DDEBF7도stripe:BDD7EE로 고칩니다. 미리보기 문자열은 RRGGBB(빨-초-파) 순서의 16진수이고,RGB()함수는 인수를 빨·초·파 순서로 받습니다(내부 Long 값은 BGR 순서). - 조건부 색 표시의 기본 노랑을 다른 색으로 — modCellColorMark 98줄
pickedColor = RGB(255, 242, 204)의 세 숫자를 원하는 RGB 값으로 바꾸고, 97줄 항목 이름 "연한 노랑" 도 함께 고칩니다.
고칠 때 주의
- 사용자 조건(3번)에서
=로 시작하는 수식은 그대로 쓰이므로 기준 주소의$·행 번호를 적용 범위 첫 행에 맞춰야 행마다 올바르게 판단됩니다. - 교차형 색은 직접 채우기라 '색 지우기' 로만 지워지고, 행을 정렬·삽입하면 줄무늬가 어긋납니다.
- '조건부서식 모두 삭제' 를 고르면 적용 범위의 다른 규칙도 모두 사라집니다.
09테두리 스타일 셀정리하기
선 모양·굵기를 미리보기로 고르고 바깥·안쪽 테두리 적용.
리본 tag "테두리선스타일" 진입점 하나로 범위 결정, 선택 폼, 조합 보정, 테두리 적용을 모두 처리합니다.
소스 보기 (77줄)
심화과정: 소스 분석
선택 범위(한 셀이면 연결 영역)에 대해 선 모양 미리보기가 있는 선택 폼 한 장으로 선 종류 6가지와 굵기 3단계를 함께 고르게 합니다. Excel 이 지원하지 않는 선 종류×굵기 조합은 자동으로 보정하고, 바깥 네 변과 안쪽 세로·가로선의 Borders 에 같은 LineStyle·Weight 를 적용합니다. 1열 또는 1행 범위에서는 안쪽 선을 건너뛰어 오류를 막습니다.
모듈 구성
modBorderStyle— 리본 tag "테두리선스타일" 진입점 하나로 범위 결정, 선택 폼, 조합 보정, 테두리 적용을 모두 처리합니다.
코드 흐름 (줄 번호를 누르면 소스의 그 부분으로 이동)
modBorderStyle6~13줄(테두리선스타일)① 대상 범위 결정리본 버튼은UI_Ribbon→UI_Run을 거쳐 이 프로시저로 들어옵니다. 선택이 Range 가 아니면 안내 후 끝내고, 셀 하나만 선택했으면CurrentRegion으로 표 전체를 대상으로 삼습니다.modBorderStyle15~29줄(테두리선스타일)② 선 종류·굵기 선택 폼UI_ChooseEx에 선 종류 6개, 미리보기 사양line:solid~line:none, 체크 항목얇게/중간/굵게(기본 1번)를 넘깁니다. 폼은 선택 번호를 반환하고 체크한 굵기 번호는 ByRef 인수wt로 돌려줍니다. 선택 번호를xlContinuous·xlDot·xlDash·xlDashDot·xlDouble·xlLineStyleNone상수로 바꿉니다.modBorderStyle31~37줄(테두리선스타일)③ 테두리 지우기'테두리 없음' 을 고르면rng.Borders.LineStyle = xlLineStyleNone한 줄로 바깥·안쪽 선을 모두 지우고 끝냅니다.modBorderStyle39~54줄(테두리선스타일)④ 굵기 변환과 조합 보정굵기 번호 2·3을xlMedium·xlThick로, 그 밖은xlThin으로 바꿉니다. 이중선은 굵기를xlThick으로 고정하고, 점선은 얇게만, 짧은 점선·점-실선은 굵게일 때 중간으로 낮추며, 보정했을 때는 안내 문구fixMsg를 남깁니다.modBorderStyle56~77줄(테두리선스타일)⑤ 바깥·안쪽 선 적용Array(xlEdgeLeft, xlEdgeTop, xlEdgeBottom, xlEdgeRight)를 돌며 네 변에 선 종류·굵기·자동 색(xlAutomatic)을 넣습니다. 열이 2개 이상일 때만xlInsideVertical, 행이 2개 이상일 때만xlInsideHorizontal을 적용하고, 결과와 보정 안내를 메시지로 보여 줍니다.
핵심 VBA 객체·함수
Range.Borders(xlEdgeLeft | xlEdgeTop | xlEdgeBottom | xlEdgeRight)— 범위 바깥 네 변의 테두리 개체입니다.Range.Borders(xlInsideVertical) / Range.Borders(xlInsideHorizontal)— 범위 안쪽 세로선·가로선입니다. 1열·1행 범위에서는 존재하지 않아 조건문으로 건너뜁니다.Border.LineStyle = xlContinuous / xlDot / xlDash / xlDashDot / xlDouble— 선 종류를 지정합니다.xlLineStyleNone은 선을 지웁니다.Border.Weight = xlThin / xlMedium / xlThick— 선 굵기를 지정합니다. 선 종류에 따라 허용되는 굵기가 다릅니다.Border.ColorIndex = xlAutomatic— 선 색을 자동(보통 검정)으로 맞춥니다.
부르는 공통 함수
- UI_ChooseEx (modRibbonUI) — 선 모양 미리보기(line:…)와 굵기 체크 항목이 있는 선택 폼, 체크 값은 ByRef 로 반환
- UI_DrawPreview (modUITheme) — line:solid 같은 사양 문자열로 폼 안에 선 모양을 그림
이렇게 고쳐 쓰세요
- 테두리 색을 검정 대신 회색으로 — 61·66·71줄의
.ColorIndex = xlAutomatic을.Color = RGB(128, 128, 128)로 바꿉니다.RGB()는 빨·초·파 순서로 받아 BGR 순서 Long 값을 만듭니다. - 폼의 기본 굵기를 '중간' 으로 — 20줄
Array("얇게", "중간", "굵게"), 1, wt)의1을2로 바꿉니다. - 바깥선만 적용하고 안쪽선은 그대로 두기 — 64-73줄의 두
If ... Borders(xlInside...)블록을 삭제하거나 주석(') 처리합니다.
고칠 때 주의
- 선 종류 목록(18줄)과 미리보기 배열(19줄), Select Case 번호(22-29줄)는 순서가 같아야 합니다. 항목을 추가하면 세 곳을 함께 고칩니다.
- '테두리 없음' 은 범위의 기존 테두리를 모두 지웁니다.
10빈칸삭제 셀정리하기
선택 열의 빈 셀 행 삭제(자동 백업).
심화과정: 소스 분석
사용자가 고른 기준 셀의 열에서, 그 셀 행부터 열의 마지막 데이터 행까지 아래에서 위로 거꾸로 훑으며 기준 열이 비어 있는 행을 삭제합니다. 행 전체가 비어 있으면 바로 지우고, 기준 열만 비고 다른 열에 데이터가 있으면 처음 한 번만 '그래도 삭제 / 유지' 를 묻고 그 답을 끝까지 적용합니다. 실행 직전에는 공통 허브(UI_Run)가 활성 시트를 숨김 백업 시트로 복사해 둡니다.
모듈 구성
modDeleteBlankRows— 리본 tag "b빈칸삭제" 진입점 하나로 기준 셀 입력, 역순 반복 삭제, 결과 안내를 처리합니다.
코드 흐름 (줄 번호를 누르면 소스의 그 부분으로 이동)
modRibbonUI 54 (UI_Run) → modDeleteBlankRows 1-22 (b빈칸삭제)① 자동 백업 후 기준 셀 받기UI_Run은 이 기능을 '삭제 계열' 로 분류해(UI_IsDestructive) 실행 전에 활성 시트를 숨김 백업 시트로 복사합니다. 이어서Application.InputBox(Type:=8)로 기준 셀을 마우스로 고르게 하고, 취소하면On Error Resume Next덕분에 변수가 Nothing 으로 남아 안내 후 끝냅니다.modDeleteBlankRows24~36줄(b빈칸삭제)② 기준 열과 검사 구간 결정여러 셀을 골라도Cells(1, 1)첫 셀만 기준으로 삼습니다. 그 셀의 열 번호와 행 번호를 시작점으로,Cells(Rows.Count, 열).End(xlUp).Row로 그 열의 마지막 데이터 행을 끝점으로 정합니다. 끝점이 시작점보다 위면 할 일이 없으므로 끝냅니다.modDeleteBlankRows38~69줄(b빈칸삭제)③ 아래에서 위로 빈 행 삭제For i = lastRow To startRow Step -1로 역순 반복해 행을 지워도 아직 검사하지 않은 행 번호가 밀리지 않게 합니다. 기준 셀이 오류값이 아니고Trim후 길이가 0이면 빈칸으로 봅니다.CountA(ws.Rows(i))가 0이면 바로 삭제하고, 다른 데이터가 있으면 처음 한 번UI_Choose로 물어 vbYes(삭제)/vbNo(유지)/vbCancel(중단)을 정합니다.modDeleteBlankRows71~82줄(b빈칸삭제)④ 마무리와 결과 안내화면 갱신을 다시 켜고, 중단하지 않았다면 삭제한 행 수와 유지한 행 수를 보여 주며 리본의 '백업 시트 복원' 으로 되돌릴 수 있음을 안내합니다.
핵심 VBA 객체·함수
Application.InputBox(prompt, title, Type:=8)— 시트에서 셀을 마우스로 선택해 Range 로 받는 입력 상자입니다. 취소하면 False 가 돌아와 Set 이 실패합니다.Cells(Rows.Count, col).End(xlUp).Row— 열의 맨 아래에서 위로 올라가 마지막으로 데이터가 있는 행 번호를 구합니다.Application.WorksheetFunction.CountA(ws.Rows(i))— 행 전체에서 비어 있지 않은 셀 수를 셉니다. 0이면 완전히 빈 행입니다.ws.Rows(i).Delete— 행 전체를 삭제하고 아래 행을 위로 올립니다.IsError(value)— #N/A 같은 오류값을 빈칸으로 잘못 판단하거나 Trim 에서 형식 오류가 나는 것을 막습니다.
부르는 공통 함수
- UI_Run / UI_MakeBackup (modRibbonUI) — 실행 직전 활성 시트를 '백업_' 숨김 시트로 복사(최근 3개 보관)
- UI_Choose / UI_ChoiceToMsg (modRibbonUI) — 다른 데이터가 있는 행 처리 방법 선택과 vbYes/vbNo/vbCancel 변환
- UI_RestoreBackup (modRibbonUI) — 결과 메시지가 안내하는 되돌리기 기능
이렇게 고쳐 쓰세요
- 공백만 있는 셀은 빈칸으로 보지 않기 — 46줄
Len(Trim$(CStr(ws.cells(i, targetColNum).value))) = 0에서Trim$(...)를 빼고Len(CStr(ws.cells(i, targetColNum).value)) = 0으로 바꿉니다. - 묻지 않고 항상 '완전히 빈 행만' 삭제 — 50-53줄의 질문 블록을 지우고 40줄 아래에
userChoice = vbNo한 줄을 추가합니다. 76줄의 완료 메시지 조건은 그대로 둬도 됩니다. - 기준 셀 행은 검사에서 빼기(제목 행 보호) — 43줄
For i = lastRow To startRow Step -1의startRow를startRow + 1로 바꿉니다.
고칠 때 주의
- 삭제는 Excel 실행 취소(Ctrl+Z)로 되돌릴 수 없습니다. 되돌리기는 자동 백업 시트에 의존하므로 리본 버튼으로 실행해야 백업이 만들어집니다.
- 검사 구간의 끝은 기준 열의 마지막 데이터 행이라, 기준 열이 비어 있는 맨 아래 행들은 검사하지 않습니다.
- 첫 질문의 답이 이후 모든 행에 적용됩니다.
11중복셀값제거 셀정리하기
선택 열에서 반복되는 제목 줄 등 중복 값 행 삭제(자동 백업).
리본 tag "c중복셀값제거" 진입점 하나로 기준 셀 입력, 확인, 시트 보호·필터 해제, 역순 삭제를 처리합니다.
소스 보기 (107줄)
심화과정: 소스 분석
사용자가 고른 기준 셀의 값과 같은 값을 가진 같은 열의 다른 행을 모두 삭제하고, 기준 셀의 행 하나만 남깁니다. 비교는 앞뒤 공백을 뺀 문자열로 하며, 행 번호가 밀리지 않도록 열의 마지막 데이터 행부터 1행까지 거꾸로 훑습니다. 실행 직전에는 공통 허브(UI_Run)가 활성 시트를 숨김 백업 시트로 복사합니다.
모듈 구성
modRemoveDupRows— 리본 tag "c중복셀값제거" 진입점 하나로 기준 셀 입력, 확인, 시트 보호·필터 해제, 역순 삭제를 처리합니다.
코드 흐름 (줄 번호를 누르면 소스의 그 부분으로 이동)
modRibbonUI 54 (UI_Run) → modRemoveDupRows 1-36 (c중복셀값제거)① 자동 백업 후 기준 셀 받기UI_Run이 이 기능을 삭제 계열로 보고 활성 시트를 숨김 백업 시트로 먼저 복사합니다.Application.InputBox(Type:=8)로 기준 셀을 받고, 여러 셀을 골랐으면 첫 셀만 씁니다. 기준 셀이 오류값이거나 비어 있으면 안내 후 끝냅니다. 비교에 쓸 값keyword와 남길 셀 주소selectedAddress를 저장합니다.modRemoveDupRows38~54줄(c중복셀값제거)② 확인과 실행 준비UI_Choose로 '삭제 실행 / 취소' 를 묻습니다. 실행하면 화면 갱신을 끄고 계산을 수동(xlCalculationManual)으로 바꾼 뒤, 비밀번호 없는 시트 보호는Unprotect로 풀고 자동 필터가 켜져 있으면 끕니다(필터가 걸린 상태에서는 숨은 행 삭제가 꼬일 수 있기 때문입니다).modRemoveDupRows58~89줄(c중복셀값제거)③ 아래에서 위로 같은 값 행 삭제For i = lastRow To 1 Step -1로 기준 열을 거꾸로 훑습니다. 현재 셀 주소가 기준 셀과 다르고, 오류값이 아니며,Trim(CStr(...))로 비교한 값이 같으면EntireRow.Delete로 행을 지웁니다. 삭제 오류는Err를 확인해 직접 실행 창에 기록하고 건너뜁니다.modRemoveDupRows91~107줄(c중복셀값제거)④ 마무리와 결과 안내계산을 자동으로 되돌리고 화면 갱신을 켭니다. 시트 재보호(ws.Protect)는 주석으로 남아 있어 실행되지 않습니다. 삭제한 행 수와 '백업 시트 복원' 안내를 보여 주거나, 지운 행이 없으면 그 사실을 알립니다.
핵심 VBA 객체·함수
Application.InputBox(prompt, title, Type:=8)— 시트에서 마우스로 기준 셀을 골라 Range 로 받습니다.Application.Calculation = xlCalculationManual— 행을 지울 때마다 수식이 다시 계산되지 않게 잠시 수동 계산으로 바꿉니다.ws.Unprotect / ws.AutoFilterMode = False— 비밀번호 없는 시트 보호를 풀고 자동 필터를 해제해 행 삭제가 막히지 않게 합니다.ws.Rows(i).EntireRow.Delete— 행 전체를 삭제합니다.Range.Address— 기준 셀 주소와 현재 셀 주소를 비교해 기준 행을 삭제 대상에서 제외합니다.
부르는 공통 함수
- UI_Run / UI_MakeBackup (modRibbonUI) — 실행 직전 활성 시트를 숨김 백업 시트로 복사
- UI_Choose (modRibbonUI) — 삭제 확인 폼
- UI_RestoreBackup (modRibbonUI) — 결과 메시지가 안내하는 되돌리기 기능
이렇게 고쳐 쓰세요
- 대소문자를 구분하지 않고 비교하기 — 72줄
If Trim(CStr(currentCell.value)) = Trim(CStr(keyword)) Then의 양쪽을UCase(Trim(CStr(...)))로 감쌉니다. - 기준 셀 아래쪽 행만 삭제(위쪽은 유지) — 64줄
For i = lastRow To 1 Step -1의1을targetCell.row + 1로 바꿉니다. - 작업 후 시트 다시 보호하기 — 97줄
' ws.Protect앞의 작은따옴표를 지웁니다. 원래 보호되지 않았던 시트도 보호되므로 필요할 때만 씁니다.
고칠 때 주의
- 비교 범위는 기준 열 1행부터 마지막 데이터 행까지 전체라 표 위쪽의 같은 값(예: 제목)도 지워질 수 있습니다.
- 중간에 오류가 나면 계산 방식이 수동으로 남을 수 있지만, 리본 허브 UI_Run 이 종료 시 원래 계산 방식을 복원합니다.
- 행 삭제는 Ctrl+Z 로 되돌릴 수 없어 자동 백업 시트에 의존합니다.
12한글(HWPX) 계산확인 HWPX활용기능
①계산 내용 확인: 한글 산출 근거 글을 줄마다 계산해 합계·예산액 대조 ②표 계산 확인: 한글 표를 복사하면 계산식·금액 열을 자동 인식.
리본 tag "HwpCalcMenu" 진입점(한글계산확인)과 산출 근거 글 계산(한글계산1차), 클립보드 읽기, 금액·단위 정규식 파싱 보조 함수를 담고 있습니다.
소스 보기 (709줄)
한글 표를 붙여 넣어 계산식 열·금액 열을 자동 인식하고(TC_DetectColumns), 두 줄 칸을 합친 뒤(TC_NormalizeRows) 행별 재계산·차이·비율·부분합·총합계를 만드는 표계산 본체입니다.
소스 보기 (715줄)
심화과정: 소스 분석
한글 문서에서 복사(Ctrl+C)한 내용을 클립보드에서 읽어 새 통합 문서에서 다시 계산하고, 원문에 적힌 금액과 다르면 색과 비고로 표시합니다. '계산 내용 확인' 은 산출 근거 글을 줄 단위로 정규식 파싱해 Application.Evaluate 로 곱셈·나눗셈을 계산하고 들여쓰기로 상·하위 항목 합계를 대조하며, '표 계산 확인' 은 붙여 넣은 표에서 계산식 열과 금액 열을 자동 인식해 재배치한 뒤 행별 재계산·부분합·총합계를 만듭니다. 끝으로 결과 범위를 한글에 바로 붙일 수 있는 HTML 표 형식으로 클립보드에 올립니다.
모듈 구성
modHwpCalcClipboard— 리본 tag "HwpCalcMenu" 진입점(한글계산확인)과 산출 근거 글 계산(한글계산1차), 클립보드 읽기, 금액·단위 정규식 파싱 보조 함수를 담고 있습니다.modTableCalc— 한글 표를 붙여 넣어 계산식 열·금액 열을 자동 인식하고(TC_DetectColumns), 두 줄 칸을 합친 뒤(TC_NormalizeRows) 행별 재계산·차이·비율·부분합·총합계를 만드는 표계산 본체입니다.
코드 흐름 (줄 번호를 누르면 소스의 그 부분으로 이동)
modHwpCalcClipboard11~50줄(한글계산확인)① 메뉴 선택, 새 문서, 결과 복사UI_Choose로 '계산 내용 확인' 과 '표 계산 확인' 중 하나를 고르면Workbooks.Add로 새 통합 문서를 만들고, 개별 완료 메시지를 끄는HCM_Quiet를 켠 채한글계산1차또는표계산을 부릅니다. 끝나면 결과 시트의 A2:D(또는 A1:G) 범위를HC_CopyRangeAsTable(rng, nR, nC, True, 10)으로 색 유지·10pt 표 형태로 클립보드에 올립니다.modHwpCalcClipboard62~130줄, 427-438 (한글계산1차, GetClipboardText)② 클립보드 글 읽기와 결과 시트 준비CreateObject("htmlfile")의clipboardData.GetData("Text")로 클립보드 글을 읽고, 막힌 환경이면 MSForms DataObject 로 다시 읽습니다. 비어 있으면 시트를 건드리지 않고 끝냅니다. '한글계산' 시트를 재사용하거나 만든 뒤 계산을 수동으로 바꾸고, 머리글(No./원문/계산값/비고)을 하늘색RGB(199, 230, 255)으로 꾸밉니다. 줄바꿈을vbLf로 통일해 줄 배열로 나눕니다.modHwpCalcClipboard132~223줄(한글계산1차)③ 줄마다 금액 계산과 원문 대조제외 키워드 줄은 건너뛰고StripPrefixMarkers가 '1)', '가.' 같은 앞 번호를 정규식으로 지웁니다.=기준으로 왼쪽(계산식)과 오른쪽(원문 결과)을 나눠, 곱하기·나누기 기호가 있으면EvaluateFormulaInText(숫자·연산자만 남겨Application.Evaluate)와ApplyUnitFromLine(천원·백만원 배율)으로, 없으면SumMoneyValuesWon으로 단위 붙은 금액을 합산합니다. 오른쪽 금액과 다르면 B:C 를 연분홍으로 칠하고 비고에 '불일치' 를 적습니다. 단위 없는 숫자, 1000원 미만 금액도 비고로 알립니다.modHwpCalcClipboard227~351줄(한글계산1차)④ 들여쓰기로 상·하위 합계 대조와 요약예산액·총액·합계·소계 줄은 합산에서 뺍니다. 원문 앞 공백 수(GetIndentLevel)가 다음 줄보다 얕으면 상위 항목으로 보고, 더 깊게 들여쓴 하위 줄 합과 같으면 연녹색으로 칠해 '하위 항목과 일치' 로 표시하고 총계에서 빼 이중 합산을 막습니다. 아래에 '합계(계산)', '예산액/총액', '차이' 행을 만들고,UI_Choose로 원/천원 단위를 고르게 해 천원이면 값을 1000으로 나눠#,##0"천원"표시형식을 붙입니다.modTableCalc13~68줄, 520-545 (표계산, TC_ClipboardHasText)⑤ 표 붙여넣기와 열 자동 인식클립보드에 글이 있는지 먼저 확인하고 '표계산' 시트를 비운 뒤 A1:G1 에 No/계산1/결과1/계산2/결과2/비고1/비고2 머리글을 씁니다.ws.Paste Destination:=ws.Range("B2")로 한글 표를 그대로 붙여 넣고,TC_DetectColumns와TC_NormalizeRows로 정리합니다.modTableCalc600~709줄(TC_DetectColumns, TC_FindPair, TC_IsNumText, TC_LooksExpr)⑥ 계산식 열·금액 열 판별과 재배치병합을 풀고 열마다 비어 있지 않은 칸 중 숫자(쉼표·△·단위 허용) 비율이 60% 이상이면num, 연산자나 '원' 이 든 글이 30% 이상이면expr로 분류합니다. '계산식 열 바로 오른쪽이 숫자 열' 인 첫 쌍과 두 번째 쌍을 찾아 B~E 로 옮기고 나머지 열(항목명·차이 등)은 지웁니다. 결과1 칸이 글자인 맨 위 행은 머리글로 보고 최대 3행까지 삭제합니다.TC_NormalizeRows는 결과 칸이 둘 다 빈 행을 앞 행의 계산식 뒤에 이어 붙이고 삭제합니다.modTableCalc93~239줄(표계산)⑦ 행별 재계산·차이·비율행마다 D열 계산식을TC_CleanExpr로 숫자·연산자만 남기고(×→*, ÷→/)Application.Evaluate로 계산해 E열에 넣습니다. 원문 결과2와 다르면 글자를 빨강(vbRed)으로 바꾸고, 계산값이 원문의 500~2000배이면 '산출은 원, 예산은 천원' 표로 보고 1000으로 나눕니다. F열에 결과1−결과2, G열에 F÷E×100 을 넣고, 음수는 △ 기호(ChrW(9651)), 0은;;;로 숨기는 표시형식을 씁니다. B열이 '부분합' 인 행은CalculateAndSetSubtotal로 구간 합을 채웁니다.modTableCalc241~276줄, 375-518 (표계산, CalculateAndSetGrandTotal)⑧ 마지막 부분합·총합계와 마무리마지막 구간이 부분합으로 끝나지 않았으면 마지막 행 다음에 부분합을 추가합니다. 부분합 행은 '배경색이RGB(220, 230, 241)이고 B열이 빈 행' 으로 판별해, 일반 행만 더한 '총합계'(노랑)와 부분합만 더한 '부분합 총합'(연주황)을 두 칸 아래에 만듭니다. 열 너비를 AutoFit 하되 40을 넘지 않게 하고, 메뉴에서 부르지 않았을 때만 완료 메시지와 열 인식 결과를 보여 줍니다.
핵심 VBA 객체·함수
CreateObject("htmlfile").parentWindow.clipboardData.GetData("Text")— 참조 추가 없이 클립보드의 일반 텍스트를 읽는 방법입니다. 실패하면 MSForms DataObject 의 GetFromClipboard·GetText 로 대신 읽습니다.CreateObject("VBScript.RegExp") (.Pattern, .Global, .Execute, .SubMatches)— "([0-9][0-9,\.]*)\s*(백만원|천원|원)" 같은 정규식으로 금액과 단위를 찾아 원 단위로 환산합니다.Application.Evaluate(tempExpr)— "3*12000" 같은 문자열 수식을 Excel 수식처럼 계산해 숫자를 돌려줍니다.Worksheet.Paste Destination:=ws.Range("B2")— 클립보드의 한글 표를 B2 부터 셀 구조 그대로 붙여 넣습니다.Range.UnMerge— 한글 표에서 따라온 병합 셀을 풀어 행·열 분석이 가능하게 합니다.Range.NumberFormat = "#,##0_ ;" & ChrW(9651) & "#,##0;;"— 양수는 천 단위 쉼표, 음수는 △ 기호, 0은 빈칸으로 보이게 하는 사용자 지정 표시형식입니다.Range.Value2 (2차원 배열 읽기·쓰기)— 열 자동 인식에서 붙여 넣은 표 전체를 배열로 한 번에 읽고, 재배치한 배열을 한 번에 다시 씁니다.
부르는 공통 함수
- UI_Choose / UI_ChoiceToMsg (modRibbonUI) — 계산 방식 선택, 원/천원 단위 선택
- HC_CopyRangeAsTable (modHwpCopy) — 결과 범위를 색 유지 HTML 표로 클립보드에 복사
- UI_Run (modRibbonUI) — 리본 tag "HwpCalcMenu" 를 받아 한글계산확인을 호출
이렇게 고쳐 쓰세요
- 불일치 줄 강조색을 노랑으로 — modHwpCalcClipboard 202줄
Interior.color = RGB(255, 230, 230)을RGB(255, 255, 0)으로 바꿉니다. VBARGB(빨,초,파)는 BGR 순서 Long 값을 만들어 주므로 인수는 RGB 순서로 씁니다. - 계산식에서 인식하는 수량 단위 추가(예: '켤레') — modHwpCalcClipboard 615줄과 669줄 정규식 괄호 안 목록 끝
|점|팀)을|점|팀|켤레)로 두 곳 모두 고칩니다. 한 곳만 고치면 '수량단위 확인' 비고가 계속 붙습니다. - 표계산 결과 열 너비 상한 넓히기 — modTableCalc 10줄
MAX_COLUMN_WIDTH As Double = 40의40을60등으로 바꿉니다. - 천원 표 자동 환산 범위 조정 — modTableCalc 139줄
> 500 And ... < 2000의 두 숫자를 바꿉니다. 이 범위 밖이면 원 단위 그대로 비교합니다.
고칠 때 주의
- 부분합 행 판별은 A열 배경색
RGB(220, 230, 241)에 의존합니다(295·393줄). 157·312줄의 부분합 색을 바꾸면 판별 색도 함께 바꿔야 이중 합산이 생기지 않습니다. - 계산식 문자열의 소문자·대문자 x 도 곱하기로 바뀌므로 'x' 가 든 항목명은 계산을 어지럽힐 수 있습니다.
- 모든 결과는 새 통합 문서에 만들어지므로 원본 문서는 바뀌지 않지만, 저장하지 않으면 결과도 사라집니다.
13한글로 복사 HWPX활용기능
①글자만(탭 구분) ②양식 포함(얇은 실선 표, 글자 길이에 맞춘 열 너비) → 한글 Ctrl+V.
리본 tag "HwpCopyMenu" 메뉴와 글자만 복사(HwpCopyText), 양식 포함 복사(HwpCopyTable), HTML 표 생성(HC_BuildHtml), 클립보드 API 호출(HC_SetClipboardHtml)을 모두 담은 모듈입니다. HC_CopyRangeAsTable 은 한글 계산확인 기능도 결과 복사에 재사용합니다.
소스 보기 (408줄)
심화과정: 소스 분석
선택 범위(한 셀이면 연결 영역)의 가장자리 빈 행·열을 잘라낸 뒤, '글자만' 은 탭 구분 텍스트를 DataObject 로 클립보드에 올리고, '양식 포함' 은 VBA 로 HTML <table> 문자열을 직접 만들어 Windows API 로 클립보드에 'HTML Format'(UTF-8) + 유니코드 텍스트를 함께 올립니다. Excel 복사 기능을 쓰지 않으므로 원본 서식(채우기색·조건부서식)이 따라가지 않고, 한글 문서 본문 폭(150mm)에 맞춘 열 너비·정렬·굵게·병합(colspan/rowspan)만 반영된 깔끔한 표가 붙습니다.
모듈 구성
modHwpCopy— 리본 tag "HwpCopyMenu" 메뉴와 글자만 복사(HwpCopyText), 양식 포함 복사(HwpCopyTable), HTML 표 생성(HC_BuildHtml), 클립보드 API 호출(HC_SetClipboardHtml)을 모두 담은 모듈입니다. HC_CopyRangeAsTable 은 한글 계산확인 기능도 결과 복사에 재사용합니다.
코드 흐름 (줄 번호를 누르면 소스의 그 부분으로 이동)
modHwpCopy81~95줄(HwpCopyMenu)① 메뉴에서 복사 방식 선택선택이 Range 가 아니면 안내 후 끝냅니다.UI_Choose로 '글자만 복사' 와 '양식 포함 복사' 중 하나를 고르게 하고, 한 셀만 선택했으면 안내문에 '연결된 표 전체' 라고 알려 줍니다. 선택 번호에 따라HwpCopyText Nothing또는HwpCopyTable Nothing을 부릅니다.modHwpCopy353~408줄(HC_TargetRange, HC_TrimEmpty, HC_CellText)② 대상 범위 정리와 셀 글자 읽기한 셀이면CurrentRegion, 여러 영역이면 첫 영역, 열·행 전체면 사용 영역과 교차한 범위를 씁니다.HC_TrimEmpty는 위·아래·왼쪽·오른쪽 가장자리에서CountA가 0인 행·열을 깎아 냅니다. 셀 글자는 화면 표시값c.Text를 쓰되(표시형식 유지), 오류값은 빈칸으로, 셀 안 줄바꿈은 공백으로 바꿉니다.modHwpCopy33~58줄(HwpCopyText)③ 글자만 복사행마다 셀 글자를vbTab으로 이어 붙이고, 탭을 빼면 빈 문자열인 행은 건너뜁니다.CreateObject("New:{1C3B4210-F441-11CE-B9EA-00AA006B1A69}")로 MSForms DataObject 를 만들어SetText·PutInClipboard로 클립보드에 올립니다.modHwpCopy114~180줄(HC_BuildHtml)④ 양식 포함: 열 너비 계산빈 열은 표시하지 않습니다. 열마다 가장 긴 글자의 폭을 '한글 2·그 외 1' 단위(HC_TextUnits)로 재서 3~44 사이로 제한하고,단위 × 2.1 × 글꼴배율 + 6mm 로 바꿉니다. 합이 150mm 를 넘으면 숫자 셀이 60% 이상인 열은 그대로 두고 글 열만 비례 축소(최소 14mm)하며, 그래도 넘치면 전체를 150mm 에 맞춰 줄입니다.modHwpCopy181~228줄(HC_BuildHtml)⑤ 양식 포함: HTML 표 만들기<table border="1" ...>에 행마다<tr>, 셀마다<td>를 붙입니다. 병합 셀은 첫 셀에서colspan·rowspan을 넣고 나머지 셀은 건너뜁니다. 정렬은 셀 가로 맞춤을 따르고 '일반' 이면 숫자는 오른쪽, 글자는 왼쪽입니다. 너비는width속성(mm×3.78 픽셀)과 style 의 mm 로 함께 지정하고, 굵게·글꼴 크기·숫자 줄바꿈 금지(white-space:nowrap)를 style 에 넣습니다. 동시에 탭 구분 평문(plain)도 만듭니다.modHwpCopy277~322줄(HC_SetClipboardHtml)⑥ CF_HTML 형식으로 클립보드에 올리기Windows 의 HTML 클립보드 형식은 앞에StartHTML/EndHTML/StartFragment/EndFragment바이트 위치 헤더가 필요해, 헤더를 자리만 잡아 길이를 잰 뒤 UTF-8 바이트 수(HC_Utf8Len)로 위치를 계산해 다시 만듭니다.OpenClipboard를 최대 10번(50ms 간격) 시도하고,GlobalAlloc·GlobalLock·CopyMemory로 메모리에 담아SetClipboardData로 'HTML Format' 과CF_UNICODETEXT(13) 두 형식을 올립니다.modHwpCopy98~108줄, 243-265 (HC_CopyRangeAsTable, HC_FillHex, HC_HexBgr)⑦ 다른 기능용 공개 함수와 색 변환HC_CopyRangeAsTable은 다른 모듈이 범위를 같은 방식으로 복사할 때 쓰는 공개 함수로,keepColors가 참이면 채우기색·글자색도 HTML 에 넣습니다. Excel 의Color값은 BGR 순서(0x00BBGGRR) Long 이라HC_HexBgr가 하위 바이트(R)부터 꺼내 HTML 용RRGGBB로 바꿉니다. 흰 채우기(16777215)와 검정 글자(0)는 생략합니다.
핵심 VBA 객체·함수
CreateObject("New:{1C3B4210-F441-11CE-B9EA-00AA006B1A69}").SetText / .PutInClipboard— 참조 추가 없이 MSForms.DataObject 를 만들어 일반 텍스트를 클립보드에 올립니다.RegisterClipboardFormat("HTML Format")— Windows 에 등록된 HTML 클립보드 형식 번호를 얻습니다. 한글은 이 형식을 표로 붙여 넣습니다.OpenClipboard / EmptyClipboard / SetClipboardData / CloseClipboard (user32)— 클립보드를 열어 비우고, 형식별 메모리 핸들을 넘긴 뒤 닫는 Windows API 입니다. 64비트용PtrSafe·LongPtr선언과 32비트 선언을#If VBA7로 나눕니다.GlobalAlloc / GlobalLock / GlobalUnlock / RtlMoveMemory (kernel32)— 클립보드에 넘길 이동 가능 전역 메모리를 잡고 바이트 배열·문자열 내용을 복사합니다.CreateObject("ADODB.Stream") (Charset = "utf-8")— VBA 문자열(UTF-16)을 UTF-8 바이트 배열로 바꿉니다. 앞의 BOM 3바이트는 건너뜁니다.Range.MergeArea / Range.MergeCells— 병합 여부와 병합 영역 크기를 읽어 HTML 의 colspan/rowspan 으로 옮깁니다.Range.Text— 표시형식이 적용된 화면 글자(예: 1,000 / 2026-01-01)를 읽어 그대로 복사합니다.
부르는 공통 함수
- UI_Choose (modRibbonUI) — 글자만/양식 포함 선택 폼
- UI_Ribbon / UI_Run (modRibbonUI) — 리본 tag "HwpCopyMenu" 를 받아 이 메뉴를 호출
이렇게 고쳐 쓰세요
- 붙여 넣을 표의 기본 글꼴을 바꾸기 — 181줄과 211줄의
font-family:'맑은 고딕'두 곳을 원하는 글꼴 이름(예:'함초롬바탕')으로 바꿉니다. - A4 가로 문서용으로 표 최대 폭 넓히기 — 159·160·161·173·174·178줄의
150을 모두237(A4 가로 297mm − 좌우 여백 60mm) 등으로 바꿉니다. 한 곳만 바꾸면 축소 계산이 어긋납니다. - 테두리를 회색으로 — 181줄과 211줄의
#000000을#808080으로 바꿉니다. HTML 색은 RRGGBB 순서이므로 Excel 의 RGB()/BGR 값과 순서가 다릅니다.
고칠 때 주의
- 한글은
<colgroup>을 무시하고 td 의 width 만 따르므로, 열 너비를 바꿀 때는 209줄 width 속성과 211줄 style width 를 함께 고쳐야 합니다. - CF_HTML 헤더의 위치 값은 UTF-8 바이트 수 기준입니다. 문자 수(Len)로 바꾸면 한글이 섞인 표가 깨집니다.
- API 선언(9-29줄)은 64비트·32비트 두 갈래를 함께 고쳐야 합니다.
14학교명·지원청 교명 확인
약칭을 정식 교명으로, 오른쪽 셀에 지원청 표시. 바뀐 내용은 '변경내용' 시트에 정리.
리본 tag "학교명변환" 진입점(6개 메뉴)과 범위 실행(SN_RunOnRange), 배열 기반 일괄 처리(SN_Process), 지원청 열 처리, 변경내용 시트 기록을 담당합니다.
소스 보기 (423줄)
학교 마스터 데이터 저장소입니다. 학교급별 내장 목록(Embed_초/중/고 프로시저 안의 L "정식명|지원청코드|설립구분" 줄)과 현황시트를 Scripting.Dictionary 에 적재하고, SD_Lookup·SD_Names 같은 조회 함수만 바깥에 공개합니다.
소스 보기 (842줄)
약칭 → 정식명 판정 엔진입니다. 정확 일치, 접미사 규칙 후보, 같은 학교급 안의 유사 검색 3단계로 결과 코드(SM_EXACT·SM_CONVERTED·SM_AMBIGUOUS·SM_NOTFOUND·SM_SKIP)를 돌려주고, 정식명 → 약칭 변환도 제공합니다.
소스 보기 (235줄)
학교급별 현황시트(연번·학교명·지원청·설립구분·약칭·비고)를 내장 데이터로 만들어 깔끔한 표 서식을 입히는 모듈입니다. 사용자가 이 시트를 고쳐 두면 이후 판정에 우선 적용됩니다.
소스 보기 (172줄)
심화과정: 소스 분석
선택한 열의 줄여 쓴 학교명(○○초·○○여중·○○공고 등)을 규칙과 학교 마스터 데이터로 판정해 정식 학교명으로 바꾸고, 오른쪽 열에 관할 지원청을 적습니다. 값은 배열로 한 번에 읽어 메모리에서 판정한 뒤 바뀐 셀만 기록하며, 후보가 여럿이면 연노랑, 목록에 없으면 연빨강으로 표시하고 모든 변경 내역을 '변경내용' 시트에 링크와 함께 남깁니다. 마스터 데이터는 문서에 '초등학교현황'·'중학교현황'·'고등학교현황' 시트가 있으면 그 시트를, 없으면 코드에 내장된 공립·사립 학교 목록을 씁니다.
모듈 구성
modSchoolName— 리본 tag "학교명변환" 진입점(6개 메뉴)과 범위 실행(SN_RunOnRange), 배열 기반 일괄 처리(SN_Process), 지원청 열 처리, 변경내용 시트 기록을 담당합니다.modSchoolMatch— 약칭 → 정식명 판정 엔진입니다. 정확 일치, 접미사 규칙 후보, 같은 학교급 안의 유사 검색 3단계로 결과 코드(SM_EXACT·SM_CONVERTED·SM_AMBIGUOUS·SM_NOTFOUND·SM_SKIP)를 돌려주고, 정식명 → 약칭 변환도 제공합니다.modSchoolData— 학교 마스터 데이터 저장소입니다. 학교급별 내장 목록(Embed_초/중/고 프로시저 안의L "정식명|지원청코드|설립구분"줄)과 현황시트를 Scripting.Dictionary 에 적재하고, SD_Lookup·SD_Names 같은 조회 함수만 바깥에 공개합니다.modSchoolSheet— 학교급별 현황시트(연번·학교명·지원청·설립구분·약칭·비고)를 내장 데이터로 만들어 깔끔한 표 서식을 입히는 모듈입니다. 사용자가 이 시트를 고쳐 두면 이후 판정에 우선 적용됩니다.
코드 흐름 (줄 번호를 누르면 소스의 그 부분으로 이동)
modSchoolName17~45줄(학교명변환, SN_MakeSheet)① 메뉴 선택(공통 허브가 자동 백업)리본 허브UI_Run은 이 기능을 삭제·치환 계열로 보고 실행 직전에 활성 시트를 숨김 백업 시트로 복사합니다.UI_Choose로 '학교명 정리 + 지원청', '학교명 정리만', 초·중·고 현황시트 만들기, 세 시트 모두 만들기 중 하나를 고르면SN_RunOnRange,SN_MakeSheet(→SS_Create),SS_CreateAll로 갈라집니다.modSchoolName48~93줄(SN_RunOnRange), 234-258 (SN_OfficeMode)② 범위 받기와 지원청 열 처리 방식Application.InputBox(Type:=8)로 학교명 범위를 받고 첫 열만, 사용 영역과 겹치는 부분만 남깁니다.SD_Load True로 현황시트 유무를 다시 확인해 데이터를 적재합니다. 지원청을 표시할 때는 오른쪽 열에 지원청이 아닌 값이 있는지SD_IsOfficeText로 세어, 있으면 '새 열 끼워 넣기 / 덮어쓰기 / 학교명만 / 취소' 를 묻고 필요하면EntireColumn.Insert로 열을 넣습니다.modSchoolMatch64~120줄(SM_Resolve, SM_Candidates)③ 판정 1·2단계: 정확 일치와 접미사 규칙공백·전각 공백·줄바꿈을 지운 뒤 2자 미만, 숫자, 공백이 2개 이상인 문장, '비고·참고·광고' 같은 흔한 낱말은SM_SKIP으로 건너뜁니다. 마스터에 그대로 있으면SM_EXACT입니다. 아니면SM_Rules의 "약칭|정식" 쌍(예: 약칭 접미사 → 정식 접미사, 초→초등학교 …, 긴 것부터)으로 접미사를 바꾼 후보와, 지역명 접두어를 붙인 후보, '학교' 만 붙인 후보를 만들어 차례로 대조합니다.modSchoolMatch123~216줄(SM_Fuzzy, SM_SplitStem, SM_StemMatch, SM_TypeMatch)④ 판정 3단계: 같은 학교급 유사 검색끝 글자로 학교급(초·중·고)을 추정하고, 약칭을 앞부분(stem)과 유형 키(여·공·상·과·외·예·체)로 나눕니다. 같은 급 목록에서 앞부분으로 시작하고 유형이 맞는 학교를 찾고, 없으면 포함 검색으로 넓힙니다. 1개면SM_CONVERTED, 여러 개면 후보 목록을 비고에 담아SM_AMBIGUOUS, 0개면SM_NOTFOUND를 돌려줍니다.modSchoolData14~179줄(SD_Load, SD_LoadFromSheet, SD_LoadEmbedded, L)⑤ 마스터 데이터 적재정식명 →Array(급, 지원청, 설립구분)사전(mAll)과 급별 사전(mLevel(1 To 3))을Scripting.Dictionary로 만듭니다. 급마다 현황시트에서 '학교명' 머리글을 A1:H8 에서Find로 찾고 '지원청'·'설립구분' 열을 읽으며, 시트가 없으면 내장 목록을 씁니다. 내장 목록(183줄 이후)은 학교급별 프로시저 안에L "정식명|지원청코드|설립구분"한 줄씩 들어 있고, 지원청 코드는SD_OfficeName이 '코드+지원청' 또는 '본청' 으로 바꿉니다.modSchoolName96~231줄(SN_Process)⑥ 배열 판정 후 바뀐 셀만 기록범위 값을Value2배열로 한 번에 읽어 셀마다SM_Resolve결과를 배열에 담습니다(시트 접근 없음). 그다음 화면·계산·이벤트를 끄고, 수식 셀은 제외하며 이전 실행의 색 표시를 지웁니다. 오른쪽 열에 병합이 없으면 학교명 열 서식을PasteSpecial xlPasteFormats로 한 번에 복사하고, 변환된 셀만 값을 바꾸며 확인 필요·목록 없음 셀은 색을 칠합니다. 지원청 값은 대상 행의 연속 구간마다 배열로 한 번에 씁니다(SN_WriteOfficeRun, 텍스트 형식@).modSchoolName279~352줄(SN_LogChange, SN_WriteChangeSheet)⑦ 변경내용 시트변환·확인 필요·목록 없음 건을 2차원 배열에 모았다가 '변경내용' 시트에 No./셀/이전 학교명/변경된 학교명/구분/비고 로 한 번에 씁니다. 셀 주소 칸에는Hyperlinks.Add로 원본 셀로 가는 링크를 걸고, 구분 칸도 같은 표시색으로 칠합니다.modSchoolSheet17~172줄(SS_Create, SS_CreateAll, SS_ApplyStyle)⑧ 현황시트 만들기같은 이름 시트가 있으면 '새로 만들기 / 그대로 두기 / 취소' 를 묻습니다.SD_EmbeddedRows로 내장 목록을 2차원 배열로 받아 4행 머리글 아래에 연번·학교명·지원청·설립구분·약칭(SM_Abbrev)·비고(폐교·개교 예정·분교장 등)를 채웁니다. 서식은 회색 머리글, 머리카락 굵기 가로선, 고정 열 너비, 눈금선 숨김, 틀 고정, 자동 필터, 시트 탭 색으로 꾸밉니다.
핵심 VBA 객체·함수
CreateObject("Scripting.Dictionary") (.Exists, .Add, .Keys)— 학교 정식명을 키로 급·지원청·설립구분을 빠르게 찾는 사전 개체입니다.Range.Value2 (배열 읽기·쓰기)— 범위 전체 값을 2차원 배열로 한 번에 읽고, 지원청 값도 배열로 한 번에 써서 셀 단위 접근을 줄입니다.Range.HasFormula / Range.MergeCells / Range.Interior.Color (범위 전체)— 범위 전체에 물으면 모두 같을 때 True/False, 섞여 있으면 Null 을 돌려줍니다. 이를 이용해 셀마다 검사할지 결정합니다.Range.PasteSpecial xlPasteFormats— 학교명 열의 서식만 지원청 열에 복사합니다.Worksheet.Hyperlinks.Add Anchor, Address:="", SubAddress:="'시트'!A1"— 같은 통합 문서 안 셀로 이동하는 내부 링크를 만듭니다.Range.Find("학교명", LookIn:=xlValues, LookAt:=xlWhole)— 현황시트 위쪽에서 머리글 위치를 찾아 사용자가 열 순서를 바꿔도 읽을 수 있게 합니다.Range.EntireColumn.Insert Shift:=xlToRight— 오른쪽 열에 다른 값이 있을 때 지원청용 새 열을 끼워 넣습니다.
부르는 공통 함수
- UI_Choose (modRibbonUI) — 메뉴, 오른쪽 열 처리 방식, 기존 현황시트 처리 선택
- UI_Run / UI_MakeBackup (modRibbonUI) — 실행 직전 활성 시트를 숨김 백업 시트로 복사
- UI_RestoreBackup (modRibbonUI) — 결과 메시지가 안내하는 되돌리기
이렇게 고쳐 쓰세요
- '확인 필요' 표시색을 연한 주황으로 — modSchoolName 13줄
CLR_MARK_AMB As Long = &HB3F0FF를 바꿉니다. 16진 상수는 BGR(&H00BBGGRR) 순서라 RGB(255,240,179) 가&HB3F0FF로 적혀 있습니다. 연한 주황 RGB(255,214,153) 은&H99D6FF로 씁니다. - 새 약칭 규칙 추가 — modSchoolMatch 31줄 배열 끝
"고|고등학교")앞에"새약칭|정식 접미사", _처럼 넣습니다. 규칙은 위에서부터 검사하므로 짧은 '고' 규칙보다 앞에 둬야 합니다. - 학교로 오인되는 낱말 제외 추가 — modSchoolMatch 37-39줄
SM_IsCommonWord의 Array 목록에"최중"같은 낱말을 쉼표로 이어 추가합니다. - 내장 데이터 갱신 — 코드를 고치지 않아도 현황시트(B열 학교명·C열 지원청·D열 설립구분)를 고쳐 두면 우선 적용됩니다. 내장 목록을 바꾸려면 modSchoolData 의 학교급별 프로시저 안
L "정식명|지원청코드|설립구분"줄을 추가·수정합니다.
고칠 때 주의
- SM_Rules 순서가 바뀌면 '여상' 보다 '상' 류 규칙이 먼저 걸리는 등 판정이 달라집니다. 긴 접미사가 먼저 오도록 유지합니다.
- 같은 이름의 현황시트를 '새로 만들기' 하면 직접 고친 내용이 사라집니다.
- 변경내용 시트는 실행할 때마다 새로 쓰므로, 이전 실행의 기록은 남지 않습니다.
15메모배경넣기 기타기능
복사한 캡처 이미지를 셀 메모 배경으로.
리본 tag "메모에이미지넣기" 진입점과 실제 작업(MemoImage_Run), 키 입력·클립보드 확인 API 선언, PowerShell 로 클립보드 그림 크기 조회·PNG 저장 함수를 담은 단일 모듈입니다.
소스 보기 (275줄)
심화과정: 소스 분석
클립보드에 이미 그림이 있으면 그것을 쓸지 묻고, 없거나 새로 찍기로 하면 keybd_event API 로 Win+Shift+S 키를 눌러 Windows 화면 캡처를 띄웁니다. 숨김 PowerShell 을 실행해 .NET 클립보드 API 로 그림 크기를 읽고 임시 폴더에 PNG 로 저장한 뒤, 사용자가 고른 셀에 메모를 만들어 그 PNG 를 메모 도형의 배경 그림(Fill.UserPicture)으로 채웁니다. 메모 크기는 픽셀을 96dpi 기준 포인트로 바꿔 맞추고 최소·최대 크기를 제한합니다.
모듈 구성
modMemoImage— 리본 tag "메모에이미지넣기" 진입점과 실제 작업(MemoImage_Run), 키 입력·클립보드 확인 API 선언, PowerShell 로 클립보드 그림 크기 조회·PNG 저장 함수를 담은 단일 모듈입니다.
코드 흐름 (줄 번호를 누르면 소스의 그 부분으로 이동)
modMemoImage107~114줄(메모에이미지넣기)① 리본 콜백과 지연 실행리본 콜백 안에서 바로Application.InputBox(Type:=8)를 띄우면 셀 선택 창이 곧바로 취소되는 문제가 있어,control이 있으면Application.OnTime Now로MemoImage_Run을 콜백이 끝난 직후에 실행하도록 예약합니다. 공통 허브UI_Run처럼Nothing으로 부르면 바로 실행합니다.modMemoImage116~150줄(MemoImage_Run)② 클립보드 그림 확인과 화면 캡처IsClipboardFormatAvailable(CF_BITMAP)으로 클립보드에 그림이 있는지 보고, 있으면UI_Choose로 '클립보드 이미지 사용' 과 '새로 캡처' 중 고르게 합니다. 캡처가 필요하면keybd_event로 Win·Shift·S 키를 차례로 누르고(50ms 뒤) 떼어 Windows 캡처 도구를 띄우고, 메시지 상자로 사용자가 캡처를 마칠 때까지 기다립니다. 그래도 그림이 없으면 안내 후 끝냅니다.modMemoImage36~102줄(SaveClipboardToPNG, GetClipboardImageSize)③ PowerShell 로 크기 조회와 PNG 저장WScript.Shell.Run으로 창을 숨긴 PowerShell 을 동기 실행(0, True)합니다.[System.Windows.Forms.Clipboard]::GetImage()로 그림을 얻어 크기는 TEMP 폴더 텍스트 파일에 '가로,세로' 로 적은 뒤 VBA 가Open ... For Input으로 읽고 지우며, 그림은$img.Save(경로, ImageFormat::Png)로 원본 그대로 저장합니다. 저장 여부는Dir(경로)로 확인합니다.modMemoImage161~201줄(MemoImage_Run)④ 대상 셀 고르기TEMP 폴더에 날짜·시각이 붙은 PNG 를 저장한 뒤, 외부 프로세스 실행으로 넘어간 포커스를AppActivate Application.Caption으로 Excel 에 되돌립니다.Application.InputBox(Type:=8)로 메모를 넣을 셀을 고르게 하고, 취소하면 임시 PNG 를 지우고 끝냅니다. 여러 셀을 골라도 첫 셀만 씁니다.modMemoImage203~251줄(MemoImage_Run)⑤ 메모 만들고 그림 배경 채우기기존 메모가 있으면 지우고AddComment로 공백 한 칸짜리 메모를 만듭니다. 픽셀을× 72 / 96으로 포인트로 바꿔 메모 도형 크기를 정하고, 너비 150 미만·높이 100 미만이면 비율을 유지해 키우고, 너비 600 초과면 줄입니다..Fill.UserPicture tempPath로 PNG 를 배경으로 넣고, 메모 글자는 흰색 1pt 로 숨긴 뒤 메모를 평소에는 숨김(마우스를 올리면 표시) 상태로 둡니다.modMemoImage253~275줄(MemoImage_Run)⑥ 임시 파일 삭제와 완료 안내그림은 이미 메모에 포함되었으므로Kill로 임시 PNG 를 지웁니다. 셀을 선택하고 셀 주소와 이미지 픽셀 크기를 메시지로 보여 줍니다.
핵심 VBA 객체·함수
keybd_event (user32)— 키보드 키를 누르고 떼는 동작을 흉내 내는 Windows API 입니다. Win+Shift+S 조합으로 화면 캡처 도구를 엽니다.IsClipboardFormatAvailable(CF_BITMAP) (user32)— 클립보드에 비트맵 그림(형식 번호 2)이 있는지 확인합니다.CreateObject("WScript.Shell").Run psCmd, 0, True— 명령을 창 숨김(0)으로 실행하고 끝날 때까지 기다립니다(True).[System.Windows.Forms.Clipboard]::GetImage() / $img.Save(..., ImageFormat::Png)— PowerShell 안에서 .NET 으로 클립보드 그림을 얻어 PNG 파일로 저장합니다.Range.AddComment / Comment.Shape.Fill.UserPicture— 셀에 메모(노트)를 만들고, 메모 도형의 채우기를 그림 파일로 지정합니다.Application.OnTime Now, "'파일명'!MemoImage_Run"— 지정한 매크로를 현재 작업(리본 콜백)이 끝난 뒤 실행하도록 예약합니다.Application.InputBox(Type:=8)— 시트에서 마우스로 셀을 선택해 Range 로 받습니다.
부르는 공통 함수
- UI_Choose (modRibbonUI) — 클립보드 그림 사용/새 캡처 선택 폼
- UI_Run (modRibbonUI) — 리본 tag "메모에이미지넣기" 를 받아 Nothing 인수로 호출
이렇게 고쳐 쓰세요
- 메모 최대 너비를 600pt 에서 800pt 로 — 235·237·238줄의
600세 곳을 모두800으로 바꿉니다. - 메모를 항상 보이게 하기 — 251줄
rng.Comment.Visible = False를True로 바꿉니다. - 고해상도(125% 배율) 화면에서 크기 맞추기 — 214·215줄의
96#을120#으로 바꾸면 같은 픽셀이 더 작은 포인트로 환산됩니다.
고칠 때 주의
- PowerShell 실행이 정책으로 막힌 PC 에서는 크기 조회 단계에서 실패합니다.
- 저장 경로는 PowerShell 명령 문자열 안에 작은따옴표로 들어가므로(46·75줄), TEMP 경로에 작은따옴표가 있으면 명령이 깨집니다.
- Win+Shift+S 캡처 도구는 Windows 10 이상에서만 동작합니다.
16백업 시트 복원 안전장치
삭제·치환 기능 실행 직전 자동 백업(최근 3개)을 표시하거나 원본에 덮어써 복원.
리본·도형 버튼 공통 진입점(UI_Ribbon, UI_ShapeClick, UI_Run)과 자동 백업·복원, 그리고 선택/입력 폼 래퍼(UI_Choose, UI_ChooseEx, UI_Input)를 모은 허브 모듈입니다.
소스 보기 (401줄)
심화과정: 소스 분석
리본의 모든 버튼은 UI_Ribbon 이 받은 tag 문자열을 UI_Run 에 넘기는 하나의 허브로 들어오며, UI_Run 은 Excel 설정을 보관·복원하고 삭제 계열 기능(빈칸삭제·중복셀값제거·학교명변환) 직전에 활성 시트를 '백업_원본이름_시분초' 숨김 시트로 복사합니다(최근 3개만 보관). '백업 시트 복원' 은 가장 최근 백업 시트를 찾아 이름에서 원본 시트를 추정한 뒤, 백업을 보여 주기만 하거나 원본 시트 내용을 백업 내용으로 덮어씁니다.
모듈 구성
modRibbonUI— 리본·도형 버튼 공통 진입점(UI_Ribbon, UI_ShapeClick, UI_Run)과 자동 백업·복원, 그리고 선택/입력 폼 래퍼(UI_Choose, UI_ChooseEx, UI_Input)를 모은 허브 모듈입니다.
코드 흐름 (줄 번호를 누르면 소스의 그 부분으로 이동)
modRibbonUI23~35줄(UI_Ribbon, UI_ShapeClick)① 리본·도형 버튼의 공통 입구리본 XML 의 모든 버튼은 onAction 으로UI_Ribbon을 부르고, 버튼의tag값(예: "UI_RestoreBackup")을UI_Run에 넘깁니다. 시트 위 도형 버튼은UI_ShapeClick이Application.Caller로 눌린 도형을 찾아 그 도형의 대체 텍스트(AlternativeText)를 프로시저 이름으로 넘깁니다.modRibbonUI37~58줄(UI_Run)② 실행 환경 보관과 사전 처리실행 중 중복 클릭은busy플래그로 막습니다. 활성 통합 문서가 없거나 추가 기능 자체면 새 문서를 만들고, 계산 방식·이벤트·화면 갱신·알림·상태 표시줄·AutomationSecurity를 변수에 보관합니다.UI_IsDestructive가 참이면UI_MakeBackup으로 백업하고,UI_NeedsNewFile이 참인 기능은 새 통합 문서를 열어 그 안에서 실행합니다.modRibbonUI59~130줄(UI_Run)③ tag → 기능 프로시저 분기와 복원Select Case proc로 tag 문자열마다 해당 기능을 호출합니다(리본 콜백 형식의 기능에는Nothing을 넘김). 등록되지 않은 tag 는Err.Raise로 오류를 냅니다. 성공·실패와 관계없이CleanExit에서 보관해 둔 Excel 설정을 모두 되돌리고, 오류가 있었으면 작업 이름·오류 번호·진단 단계(BX_Trace)를 메시지로 보여 줍니다.modRibbonUI161~207줄(UI_IsDestructive, UI_MakeBackup, UI_TrimBackups)④ 자동 백업 만들기와 3개 유지삭제 계열 tag 이면ws.Copy After:=로 시트를 맨 뒤에 복사하고, 이름을백업_+ 원본 이름 앞 18자 +_hhmmss로 바꾼 뒤xlSheetHidden으로 숨깁니다. 이미 백업 시트이면 다시 백업하지 않습니다.UI_TrimBackups는 '백업_' 시트 수를 세어 3개(BACKUP_KEEP)를 넘으면 앞쪽(오래된) 것부터 삭제합니다.modRibbonUI209~231줄(UI_RestoreBackup)⑤ 최근 백업과 원본 시트 찾기시트를 차례로 보며 '백업_' 으로 시작하는 시트 수를 세고 마지막으로 만난 것을 가장 최근 백업으로 봅니다. 없으면 자동 백업이 만들어지는 기능을 안내하고 끝냅니다. 백업 이름에서 접두어와 마지막_시분초를 떼어 내 원본 이름 앞 18자(stem)를 얻고, 이름 앞 18자가 같은 일반 시트를 원본으로 찾습니다.modRibbonUI232~254줄(UI_RestoreBackup)⑥ 표시 또는 덮어쓰기 복원UI_Choose로 '백업 시트만 표시', '원본에 덮어쓰기', '취소' 를 고르게 합니다. 1번은 백업 시트를xlSheetVisible로 보이게 하고 활성화합니다. 2번은 원본을 찾은 경우에만orig.Cells.Clear로 비운 뒤latest.Cells.Copy orig.Cells(1, 1)로 값·서식을 통째로 복사합니다.modRibbonUI258~276줄, 343-381 (UI_Choose, UI_ChoiceToMsg, UI_Input, UI_ChooseEx)⑦ 공통 폼 래퍼다른 기능들이 쓰는 선택·입력 폼 호출도 이 모듈에 있습니다.UI_Choose는 frmChoice 를 만들어Setup·Show vbModal후Result(취소=0)를 돌려주고, 검증용BX_Silent가 켜져 있으면 폼 없이 0을 반환합니다.UI_ChoiceToMsg는 1/2/그 외를 vbYes/vbNo/vbCancel 로 바꾸고,UI_Input은 frmInput 으로 문자열을,UI_ChooseEx는 미리보기·체크 항목 값까지 받아 옵니다.
핵심 VBA 객체·함수
IRibbonControl.Tag— 리본 XML 에서 버튼마다 지정한 tag 문자열입니다. 하나의 콜백(UI_Ribbon)으로 모든 버튼을 구분하는 데 씁니다.Application.Caller— 도형 버튼에서 매크로를 실행했을 때 눌린 도형의 이름을 돌려줍니다.Worksheet.Copy After:=wb.Worksheets(wb.Worksheets.Count)— 시트를 같은 통합 문서 맨 뒤에 복사합니다. 복사본이 새 마지막 시트가 됩니다.Worksheet.Visible = xlSheetHidden / xlSheetVisible— 백업 시트를 숨기거나 다시 보이게 합니다(시트 숨기기 취소 메뉴로도 볼 수 있는 일반 숨김).Range.Copy Destination—latest.Cells.Copy orig.Cells(1, 1)처럼 시트 전체 셀을 값·서식째 다른 시트에 복사합니다.Application.AutomationSecurity = msoAutomationSecurityForceDisable— 기능 실행 중 코드로 여는 파일의 매크로가 자동 실행되지 않게 하고, 끝나면 원래 값으로 되돌립니다.Err.Raise vbObjectError + 221— 등록되지 않은 tag 에 대해 사용자 정의 오류를 일으켜 공통 오류 메시지로 보냅니다.
부르는 공통 함수
- frmChoice (폼) — UI_Choose·UI_ChooseEx 가 띄우는 카드형 선택 폼
- frmInput (폼) — UI_Input 이 띄우는 입력 폼
- BX_EnsureBoardEvents (다른 모듈) — UI_Run 시작 시 호출되는 이벤트 준비 루틴
이렇게 고쳐 쓰세요
- 백업 시트를 3개가 아니라 5개까지 보관 — 14줄
Private Const BACKUP_KEEP As Long = 3의3을5로 바꿉니다. 복원 폼 안내 문구도 이 상수를 쓰므로 함께 바뀝니다. - 다른 기능(예: 테두리 스타일)도 실행 전 자동 백업 — 163줄
Case "b빈칸삭제", "c중복셀값제거", "학교명변환"끝에, "테두리선스타일"처럼 리본 tag 이름을 추가합니다. 220줄 안내 문구도 맞춰 고칩니다. - 백업 시트를 완전히 숨기기(시트 숨기기 취소 메뉴에 안 보이게) — 179줄
copyWs.Visible = xlSheetHidden을xlSheetVeryHidden으로 바꿉니다. 복원 기능의 '표시' 는 237줄에서 xlSheetVisible 로 바꾸므로 그대로 동작합니다.
고칠 때 주의
- 원본 찾기는 시트 이름 앞 18자 비교라, 앞 18자가 같은 시트가 여러 개면 첫 번째 시트가 원본으로 잡힐 수 있습니다.
- '가장 최근' 백업은 시트 순서상 마지막 백업 시트입니다. 백업 시트를 직접 옮기면 판단이 달라집니다.
- 덮어쓰기 복원은 원본 시트의 셀을 모두 지운 뒤 복사하므로 백업 이후에 한 작업은 사라집니다.
17사용방법 B-xlam
설치·기능별 사용설명 페이지(https://140-245-77-55.sslip.io/xlam) 열기.
심화과정: 소스 분석
리본 버튼이 OpenHelpWebsite 를 부르면 사용설명서 주소를 문자열 변수에 담아 ThisWorkbook.FollowHyperlink 로 기본 브라우저에서 엽니다. 열기에 실패하면 오류 처리기로 건너뛰어 주소와 오류 내용을 메시지 상자로 보여 줍니다.
모듈 구성
modHelpSite— 리본 tag "OpenHelpWebsite" 진입점 하나로 사용설명서 웹 페이지를 브라우저로 엽니다.
코드 흐름 (줄 번호를 누르면 소스의 그 부분으로 이동)
modHelpSite3~4줄(OpenHelpWebsite)① 리본 콜백 선언control As IRibbonControl인수를 받는 리본 콜백 형식입니다. 실제로는 공통 허브UI_Run이OpenHelpWebsite Nothing으로 부르므로 인수는 쓰지 않습니다. 4줄에서 주소를 담을 문자열 변수url을 선언합니다.modHelpSite5줄② 사용설명서 주소 지정url = "https://140-245-77-55.sslip.io/xlam"로 열 주소를 정합니다. 주소를 바꾸려면 이 한 줄만 고치면 됩니다.modHelpSite6~8줄③ 오류 처리기 설정과 브라우저 열기On Error GoTo Fail로 이후 오류가 나면 Fail 레이블로 가게 합니다.ThisWorkbook.FollowHyperlink Address:=url은 하이퍼링크를 클릭한 것처럼 기본 브라우저로 주소를 엽니다. 성공하면Exit Sub로 오류 처리기를 건너뛰고 끝납니다.modHelpSite9~12줄(Fail)④ 실패 시 안내보안 정책 등으로 열지 못하면 Fail 레이블로 와서 주소와Err.Description(오류 설명)을 포함한 경고 메시지를 띄워, 사용자가 브라우저에 주소를 직접 입력할 수 있게 합니다.
핵심 VBA 객체·함수
ThisWorkbook.FollowHyperlink Address:=url— 주소를 하이퍼링크처럼 열어 기본 브라우저에서 웹 페이지를 띄웁니다.On Error GoTo Fail— 오류가 나면 지정한 레이블로 실행 위치를 옮기는 VBA 오류 처리 구문입니다.Err.Description— 마지막으로 발생한 오류의 설명 문자열입니다.
부르는 공통 함수
- UI_Ribbon / UI_Run (modRibbonUI) — 리본 tag "OpenHelpWebsite" 를 받아 이 프로시저를 Nothing 인수로 호출
이렇게 고쳐 쓰세요
- 다른 도움말 주소 열기 — 5줄
url = "https://140-245-77-55.sslip.io/xlam"의 따옴표 안 주소를 원하는 주소로 바꿉니다. 10줄 메시지에도 같은 변수가 쓰이므로 따로 고칠 필요가 없습니다. - 메시지 상자 제목 바꾸기 — 11줄 끝의 제목 문자열(
vbExclamation,뒤 따옴표 안)을 원하는 이름으로 바꿉니다.
고칠 때 주의
- 회사·기관 PC 의 보안 정책이 하이퍼링크 열기를 막으면 항상 실패 메시지가 뜹니다. 이 경우 코드가 아니라 정책 문제입니다.
공통공통 모듈·폼 공통
여러 기능이 함께 쓰는 선택 폼(애플 스타일 카드)·미리보기 그림·입력 폼 코드입니다.
폼 공통 디자인 모듈입니다. 색(BGR 16진 상수)·글꼴·치수 상수와 제목·안내문·옵션 카드·버튼·입력 상자·구분선 스타일 함수, 호버 색 전환, 카드 오른쪽 미리보기(UI_DrawPreview) 그리기를 제공합니다.
소스 보기 (377줄)
동적으로 만든 MSForms.Label 을 WithEvents 로 붙잡아 클릭·마우스 이동 이벤트를 소유 폼의 OnPick / OnExtra / OnHover 로 전달하는 이벤트 래퍼 클래스입니다.
소스 보기 (31줄)
UI_Choose·UI_ChooseEx 가 띄우는 선택 폼의 코드 비하인드입니다. 최대 6개 카드, 미리보기, 단일 선택 체크 항목, 숫자키·Esc 키 처리를 담당하며 Result(취소=0)와 Extra 를 공개합니다. 폼 크기·기본 컨트롤 배치 등 시각적 레이아웃 원본은 함께 저장되는 .frx 바이너리 파일에 있고, 이 코드는 그 기본 컨트롤(btn1~6, btnCancel, lblPrompt)을 숨긴 뒤 새로 그립니다.
소스 보기 (193줄)
UI_Input 이 띄우는 입력 폼의 코드 비하인드입니다. .frx 에 있는 txtValue 입력 상자는 그대로 쓰고 btnOK·btnCancel·lblPrompt 는 숨긴 채 라벨 버튼을 새로 만들며, Enter/Esc, 숫자만 입력 검사, Cancelled 속성을 제공합니다.
소스 보기 (119줄)
리본에 연결되지 않은 빌드 검증용 훅입니다. 외부 검증 스크립트가 Application.Run 으로 폼 생략 모드·테스트 폴더를 설정하고 표 복사 기능을 시험합니다.
소스 보기 (16줄)
심화과정: 소스 분석
여러 기능이 함께 쓰는 선택 폼(frmChoice)과 입력 폼(frmInput)은 폼 디자이너에 놓인 기본 버튼을 숨기고, 실행 시점에 Controls.Add("Forms.Label.1") 로 라벨을 만들어 카드형 항목·버튼·미리보기를 그립니다. 라벨에는 클릭 이벤트가 없으므로 WithEvents 래퍼 클래스 clsUIButton 이 클릭·마우스 이동을 받아 폼의 OnPick·OnHover 를 대신 호출하고, 색·글꼴·치수는 modUITheme 상수 한 곳에서 관리합니다. modTestHooks 는 자동 검증용 진입점이며, Sheet1 과 현재_통합_문서(ThisWorkbook) 문서 모듈은 코드가 비어 있습니다.
모듈 구성
modUITheme— 폼 공통 디자인 모듈입니다. 색(BGR 16진 상수)·글꼴·치수 상수와 제목·안내문·옵션 카드·버튼·입력 상자·구분선 스타일 함수, 호버 색 전환, 카드 오른쪽 미리보기(UI_DrawPreview) 그리기를 제공합니다.clsUIButton— 동적으로 만든 MSForms.Label 을 WithEvents 로 붙잡아 클릭·마우스 이동 이벤트를 소유 폼의 OnPick / OnExtra / OnHover 로 전달하는 이벤트 래퍼 클래스입니다.frmChoice— UI_Choose·UI_ChooseEx 가 띄우는 선택 폼의 코드 비하인드입니다. 최대 6개 카드, 미리보기, 단일 선택 체크 항목, 숫자키·Esc 키 처리를 담당하며 Result(취소=0)와 Extra 를 공개합니다. 폼 크기·기본 컨트롤 배치 등 시각적 레이아웃 원본은 함께 저장되는 .frx 바이너리 파일에 있고, 이 코드는 그 기본 컨트롤(btn1~6, btnCancel, lblPrompt)을 숨긴 뒤 새로 그립니다.frmInput— UI_Input 이 띄우는 입력 폼의 코드 비하인드입니다. .frx 에 있는 txtValue 입력 상자는 그대로 쓰고 btnOK·btnCancel·lblPrompt 는 숨긴 채 라벨 버튼을 새로 만들며, Enter/Esc, 숫자만 입력 검사, Cancelled 속성을 제공합니다.modTestHooks— 리본에 연결되지 않은 빌드 검증용 훅입니다. 외부 검증 스크립트가 Application.Run 으로 폼 생략 모드·테스트 폴더를 설정하고 표 복사 기능을 시험합니다.Sheet1— 추가 기능 통합 문서의 워크시트 문서 모듈로, 코드가 없습니다(빈 모듈).현재_통합_문서— 추가 기능 통합 문서(ThisWorkbook) 문서 모듈로, 코드가 없습니다. 통합 문서 열기 같은 이벤트 처리기는 정의되어 있지 않습니다.
코드 흐름 (줄 번호를 누르면 소스의 그 부분으로 이동)
modRibbonUI 258-267 (UI_Choose) → frmChoice 15-24 (UserForm_Initialize, Setup)① 폼 생성과 초기화기능 코드가UI_Choose를 부르면New frmChoice로 폼 인스턴스가 만들어지며UserForm_Initialize가 Result·Extra 를 0으로, 이벤트 래퍼 보관용 Collection 을 새로 만듭니다.Setup은 미리보기·체크 항목 없이SetupEx를 부르는 단축형입니다.frmChoice26~69줄(SetupEx), modUITheme 37-134② 디자이너 컨트롤 숨기고 카드 그리기UI_StyleForm으로 흰 배경·폭 440·가운데 표시를 적용하고, .frx 에 배치된btn1~btn6,btnCancel,lblPrompt를 숨깁니다. 제목·안내문 라벨을Controls.Add로 만들어UI_StyleTitle·UI_StylePrompt로 꾸미고(AutoSize 를 켰다 끄는 방식으로 높이를 글자에 맞춤), 항목마다 카드 라벨(UI_StyleOption), 투명 글자 라벨(UI_StyleOptionText), 번호 라벨(UI_StyleOptionNum) 세 개를 겹쳐 놓고 y 좌표를 누적합니다.frmChoice71~104줄, 107-139 (SetupEx, DrawPreviews, RefreshExtras), modUITheme 245-337 (UI_HexColor, UI_Box, UI_DrawPreview)③ 체크 항목·미리보기·취소 버튼체크 항목 배열이 있으면 한 줄에 균등 폭으로 □/■ 라벨을 만들고 기본 선택을 표시합니다. 그 아래 구분선과 '취소 (Esc)' 버튼을 그린 뒤 폼 높이를 내용에 맞춥니다.DrawPreviews는 사양 문자열(stripe:RRGGBB,line:dot,merge:after등)을UI_DrawPreview에 넘겨 작은 라벨 상자들로 줄무늬 표·선 모양·병합 전후 그림을 그리고, 체크 항목이 바뀌면 기존 미리보기 컨트롤을 지우고 선 굵기를 반영해 다시 그립니다.frmChoice142~147줄(AddBtn), clsUIButton 1-31④ 라벨을 버튼으로: 이벤트 래퍼AddBtn은 라벨마다clsUIButton인스턴스를 만들어lbl(WithEvents),Owner(폼),Index,kind(0 카드, 1 주요 버튼, 2 보조 버튼, 3 글자 라벨, 4 체크 항목)를 넣고 Collection 에 보관합니다. 보관하지 않으면 개체가 사라져 이벤트도 끊깁니다. 클래스의lbl_Click은 kind 4 면Owner.OnExtra, 그 밖은Owner.OnPick을,lbl_MouseMove는Owner.OnHover를 부릅니다.frmChoice150~193줄(OnPick, OnExtra, OnHover, UserForm_KeyDown, UserForm_QueryClose)⑤ 선택·호버·키보드·닫기 처리OnPick은 Result 에 번호를 넣고Me.Hide로 모달을 풀어 호출 측으로 돌아갑니다(취소 버튼은 번호 0).OnHover는 모든 래퍼의SetHover를 불러 같은 번호만 강조색(UI_Hover)으로 바꿉니다. 숫자 1~6(숫자 패드 포함)은 해당 항목 선택, Esc 는 취소이며, 창 닫기(X,CloseMode = 0)는 폼을 파괴하지 않고 숨겨 Result 0 을 읽을 수 있게 합니다. 호출 측UI_Choose가 Result 를 읽은 뒤Unload합니다.modRibbonUI 343-354 (UI_Input) → frmInput 8-119⑥ 입력 폼Setup은 선택 폼과 같은 방식으로 제목·안내문을 그리고, .frx 의txtValue를UI_StyleTextBox로 꾸며 기본값을 넣은 뒤 '확인 (Enter)'·'취소 (Esc)' 라벨 버튼을 만듭니다. 표시될 때 입력 글자 전체를 선택하고, 확인 시numericOnly면IsNumeric으로 검사합니다. 결과는Cancelled속성과txtValue.Text로 호출 측에 전달됩니다.modTestHooks1~16줄⑦ 검증용 훅BX_SetSilent는 공용 변수BX_Silent를 켜서 UI_Choose 가 폼 없이 0을 반환하게 하고,BX_SetTestFolder는 한글 합치기·PDF 변환 모듈의 폴더 대화상자를 생략할 경로를 지정합니다.BX_TestCopyTable은HC_CopyRangeAsTable을 불러 '행x열' 또는 "FAIL" 을 돌려줍니다.
핵심 VBA 객체·함수
Me.Controls.Add("Forms.Label.1", name, True)— 실행 중에 사용자 폼에 Label 컨트롤을 새로 만들어 넣습니다. 카드·버튼·미리보기 상자가 모두 이 방식으로 만들어집니다.Public WithEvents lbl As MSForms.Label— 클래스 모듈에서 컨트롤 이벤트(Click, MouseMove)를 받을 수 있게 선언합니다. 동적 컨트롤의 이벤트 처리에 필수입니다.UserForm.Show vbModal / Me.Hide / Unload— 모달로 띄워 사용자가 고를 때까지 호출 코드를 멈추고, Hide 로 돌아간 뒤 결과를 읽고 Unload 로 해제합니다.UserForm_QueryClose(Cancel, CloseMode)— 창 닫기(X) 시 Cancel = 1 로 폼 파괴를 막고 숨기기만 해 취소 결과를 안전하게 전달합니다.UserForm_KeyDown(KeyCode)— vbKey1~vbKey6, vbKeyNumpad1~6, vbKeyEscape 를 받아 키보드로 선택·취소합니다.RGB(CLng("&H" & Mid$(hex6, 1, 2)), ...)— UI_HexColor 가 "RRGGBB" 문자열을 Excel/VBA 색 값(BGR 순서 Long)으로 바꿉니다.
부르는 공통 함수
- UI_Choose / UI_ChooseEx / UI_Input (modRibbonUI) — 이 폼들을 만들고 띄우는 공개 래퍼
- BX_Silent (modRibbonUI) — 검증 모드에서 폼 표시를 생략하는 공용 변수
- HC_CopyRangeAsTable (modHwpCopy) — modTestHooks 가 시험하는 표 복사 함수
이렇게 고쳐 쓰세요
- 강조색(애플 블루)을 초록으로 — modUITheme 19줄
UI_ClrAccent As Long = &HFF7A00을 바꿉니다. VBA 16진 색은 &H00BBGGRR(BGR) 순서라 RGB(0,122,255) 가&HFF7A00입니다. 초록 RGB(52,168,83) 은&H53A834로 쓰고, 눌림색 20줄UI_ClrAccentDn도 조금 어둡게 맞춥니다. - 폼을 더 넓게 — modUITheme 30줄
UI_FormWidth As Single = 440을520등으로 바꿉니다. 카드·입력 상자 폭이 모두 이 상수에서 계산됩니다. - 폼 글꼴 바꾸기 — modUITheme 24줄
UI_FontName As String = "맑은 고딕"의 글꼴 이름을 바꿉니다. - 선택 항목을 7개 이상 허용 — frmChoice 51줄
If n > 6 Then n = 6의 6을 늘리면 되지만, 31-33줄의btn1~btn6숨김 반복과 180-183줄 숫자키 처리는 6까지만 다루므로 함께 검토해야 합니다.
고칠 때 주의
- modUITheme 17줄
UI_ClrText = &H1C1C1E는 주석의 (28,28,30) 과 BGR 순서가 반대로 적혀 실제로는 RGB(30,28,28) 입니다. 색을 고칠 때는 주석보다 16진 값의 BGR 순서를 기준으로 계산합니다. - 폼의 기본 컨트롤(btn1~6, btnCancel, btnOK, lblPrompt, txtValue)은 .frx 에 들어 있어 코드만으로는 보이지 않습니다. 이 이름을 폼 디자이너에서 바꾸거나 지우면 Setup 이 오류를 냅니다.
- AddBtn 으로 만든 clsUIButton 을 Collection 에 보관하지 않으면 이벤트가 동작하지 않습니다.
소스·분석: 엑셀과 한글 매크로