잘못된 입력을 막아라 — VBA 입력 검증 폼 만들기
UserForm의 실행 버튼은 입력값을 검사하는 경계입니다. 폴더가 비었거나 금액·날짜가 계약에 맞지 않으면 파일 작업에 넘기지 않고 해당 컨트롤로 포커스를 돌려야 합니다. 이 예제는 파일을 만드는 본체를 붙이지 않고 정상 입력 확인 메시지까지만 실행합니다. 폼 검증과 파일 생성 성공을 구분하기 위해서입니다.
컨트롤 이름부터 동일하게 만들기
Windows 데스크톱 Excel의 VBA 편집기를 기준으로 새 통합 문서를 .xlsm으로 저장합니다. Alt+F11 → 삽입 → UserForm에서 폼 이름을 frm보고서로 정하고 아래 컨트롤을 만듭니다. Caption에 적은 글자와 (Name) 속성은 다릅니다. 코드는 (Name)으로 참조합니다.

| 종류 | (Name) | 역할 |
|---|---|---|
| TextBox | txt폴더 | 조회 가능한 연습 폴더 |
| TextBox | txt금액 | 쉼표 없는 원 단위 정수 |
| TextBox | txt기준일 | YYYY-MM-DD 기준일 |
| ComboBox | cbo월 | 01~12 중 하나 |
| CheckBox | chk동의 | 연습 입력 확인 |
| CommandButton | btn실행 | 최종 검사 실행 |
chk동의의 Caption은 ‘연습 입력 확인’으로 지정합니다. 개인정보 처리에 관한 동의를 수집하는 폼이 아니며 개인정보나 응답을 저장하지 않습니다. 폼의 한글 컨트롤 이름이 코드와 정확히 맞아야 합니다.
숫자·날짜·목록에 적용하는 입력 계약
금액은 0~999999999 범위의 정수만 받습니다. 120000은 가능하지만 120,000, 12.5, -1, 1E5는 거부합니다. 쉼표를 무조건 지워 12,3을 123으로 바꾸는 식의 자동 정규화는 하지 않습니다. 허용 형식을 사용자가 고치도록 안내합니다.
날짜는 그레고리력 설정에서 1900~2099의 YYYY-MM-DD만 받습니다. DateSerial은 범위를 넘긴 일을 다음 달로 보정할 수 있으므로, 만든 날짜의 연·월·일이 입력과 같은지도 검사합니다. 가상 입력 2026-02-30은 거부하고 2024-02-29는 날짜 검사에서 통과합니다. 기준일과 월은 독립 입력이며 같은 월인지 확인하는 규칙은 이 예제에 없습니다.
폴더는 존재하는 디렉터리인지 조회할 뿐 쓰기 권한이나 보고서 생성 성공을 보장하지 않습니다. 저장하지 않은 통합 문서에서는 ThisWorkbook.Path가 비어 있으므로 먼저 연습 파일을 저장해야 합니다.

UserForm 모듈에 붙이는 전체 코드
-
프로젝트 탐색기에서
frm보고서를 선택하고 각 컨트롤의(Name)을 위 표와 대조합니다. -
폼의 코드 창에 아래 전체 코드를 붙여넣고 디버그 메뉴에서 VBA 프로젝트 컴파일을 실행합니다.
-
폼을 선택해 F5로 실행하거나 별도 표준 모듈에서
frm보고서.Show를 호출합니다. -
가상 입력으로 조회 가능한 연습 폴더와 금액 120000·기준일 2026-09-01을 입력하고 월 09를 선택한 뒤 연습 입력 확인에 체크하고 실행 버튼을 누릅니다. 컨트롤 정의가 없는 상태에서 코드만 붙이면 컴파일되지 않습니다. 첨부는 자동 설치되는 FRM 디자이너 파일이 아니라 컨트롤 생성표와 같은 코드의 텍스트 파일입니다.
Option Explicit
Private Sub UserForm_Initialize()
Dim m As Long
cbo월.Clear
cbo월.Style = fmStyleDropDownList
For m = 1 To 12
cbo월.AddItem Format$(m, "00")
Next m
txt폴더.Value = ThisWorkbook.Path
txt금액.Value = "120000"
txt기준일.Value = "2026-09-01"
chk동의.Value = False
End Sub
Private Function FolderExists(ByVal path As String) As Boolean
On Error GoTo NotFound
FolderExists = ((GetAttr(path) And vbDirectory) = vbDirectory)
Exit Function
NotFound:
FolderExists = False
End Function
Private Function ValidISODate(ByVal value As String) As Boolean
Dim y As Long, m As Long, d As Long, parsed As Date
If Not value Like "####-##-##" Then Exit Function
On Error GoTo InvalidDate
y = CLng(Left$(value, 4))
m = CLng(Mid$(value, 6, 2))
d = CLng(Right$(value, 2))
If y < 1900 Or y > 2099 Or m < 1 Or m > 12 Or d < 1 Or d > 31 Then Exit Function
parsed = DateSerial(y, m, d)
ValidISODate = (Year(parsed) = y And Month(parsed) = m And Day(parsed) = d)
Exit Function
InvalidDate:
ValidISODate = False
End Function
Private Function 입력검증() As Boolean
Dim amount As String, path As String
path = Trim$(CStr(txt폴더.Value))
If Len(path) = 0 Then
MsgBox "폴더 경로를 입력하세요.": txt폴더.SetFocus: Exit Function
End If
If Not FolderExists(path) Then
MsgBox "읽을 수 있는 폴더를 선택하세요.": txt폴더.SetFocus: Exit Function
End If
amount = Trim$(CStr(txt금액.Value))
If Len(amount) = 0 Then
MsgBox "금액을 입력하세요.": txt금액.SetFocus: Exit Function
End If
If amount Like "*[!0-9]*" Or Len(amount) > 9 Then
MsgBox "금액은 쉼표 없는 0~999999999 정수로 입력하세요.": txt금액.SetFocus: Exit Function
End If
If Not ValidISODate(CStr(txt기준일.Value)) Then
MsgBox "1900~2099 범위의 실제 날짜를 YYYY-MM-DD로 입력하세요.": txt기준일.SetFocus: Exit Function
End If
If cbo월.ListIndex < 0 Then
MsgBox "월을 목록에서 선택하세요.": cbo월.SetFocus: Exit Function
End If
If chk동의.Value <> True Then
MsgBox "연습 입력 확인에 체크하세요.": chk동의.SetFocus: Exit Function
End If
입력검증 = True
End Function
Private Sub btn실행_Click()
If Not 입력검증() Then Exit Sub
MsgBox "검증 통과: 금액=" & CStr(CLng(Trim$(CStr(txt금액.Value)))) _
& ", 기준일=" & CStr(txt기준일.Value) & ", 월=" & CStr(cbo월.Value) _
& vbCrLf & "연습 모드: 파일을 생성하거나 저장하지 않았습니다.", vbInformation
End Sub
초기화는 목록과 기본값만 채웁니다. 실제 검사는 btn실행_Click에서 실행하므로 폼이 열리기 전에 빈 값을 검사해 사용자를 막지 않습니다. 실패 경로는 Exit Function 뒤 버튼 이벤트의 Exit Sub로 끝나며 Unload Me를 호출하지 않아 입력을 고칠 수 있습니다.
정상 입력과 경계 입력의 기대 결과

| 입력 | 기대 판정 또는 포커스 |
|---|---|
| 기존 폴더, 120000, 2026-09-01, 09, 체크 | 검증 통과; 파일 쓰기 없음 |
| 폴더 빈칸 또는 일반 파일 경로 | txt폴더 |
| 금액 -1, 120,000, 12.5, 10자리 | txt금액 |
| 금액 0 또는 999999999 | 금액 검사 통과; 나머지 검사 계속 |
| 2026-02-30 | txt기준일 |
| 2024-02-29 | 날짜 검사 통과; 나머지 검사 계속 |
| 월 미선택 | cbo월 |
| 체크 해제 | chk동의 |
정상 실습 입력의 예상 메시지 값은 금액=120000, 기준일=2026-09-01, 월=09입니다. 동시에 ‘파일을 생성하거나 저장하지 않았습니다’가 표시됩니다. 실패 표에서는 다른 필드를 먼저 정상 값으로 맞춘 뒤 한 항목씩 바꿔야 지정된 포커스를 관찰할 수 있습니다. 여러 필드가 틀리면 검사 순서의 첫 오류에서 멈춥니다.
위 값은 합성 입력과 코드에서 도출한 기대값입니다. Excel/VBA에서 실제 폼을 열고 클릭한 실행 검증은 아직 하지 않았습니다. 첨부의 예상 결과 목록을 실제 실행 결과로 바꿔 읽지 마세요.
컴파일 실패와 런타임 실패를 나누어 복구
‘변수가 정의되지 않았습니다’라면 컨트롤의 (Name)과 코드 참조를 먼저 비교합니다. txt처럼 존재하지 않는 약칭을 쓰지 않습니다. Option Explicit는 이런 오타를 본 처리 전에 드러냅니다. TextBox는 txt폴더, txt금액, txt기준일 세 개입니다.
컨트롤이 숨겨져 있거나 비활성화됐다면 SetFocus도 실패할 수 있습니다. 이 예제는 표의 모든 입력 컨트롤이 보이고 활성화된 기본 폼을 전제로 합니다. 폴더 조회가 실패한 경우 경로를 바꾸되, 권한을 자동 수정하거나 파일을 지우는 복구를 붙이지 않습니다.
UserForm 검증 범위와 보고서 저장의 한계
보고서/PDF 본체를 추가할 때는 검증 통과 뒤의 별도 단계로 두고 실제 생성 실패를 별도로 처리해야 합니다. 검증 통과 메시지를 ‘보고서 저장 완료’로 바꾸는 것만으로 그 기능이 생기지 않습니다. 운영 폼으로 확장하기 전에는 금액 범위, 날짜와 월의 관계, 출력 경로 정책부터 정하세요.
사용한 VBA 동작의 근거
Microsoft API 참고 문서: SetFocus, DateSerial. 문서의 API 설명과 이 예제의 입력 계약은 구분합니다. 오류 후 정리 흐름은 On Error 가이드에서 이어서 확인할 수 있습니다.
SetFocus는 컨트롤로 포커스를 옮기며 실패하면 오류가 발생합니다. DateSerial은 인수 범위를 넘기면 더 큰 단위로 보정하므로 입력 일자의 역대조가 필요합니다.
출처 원문 수집일: S1 2026-09-23; S2 2026-09-23.