잘못된 입력을 막아라 — VBA 입력 검증 폼 만들기

UserForm의 실행 버튼은 입력값을 검사하는 경계입니다. 폴더가 비었거나 금액·날짜가 계약에 맞지 않으면 파일 작업에 넘기지 않고 해당 컨트롤로 포커스를 돌려야 합니다. 이 예제는 파일을 만드는 본체를 붙이지 않고 정상 입력 확인 메시지까지만 실행합니다. 폼 검증과 파일 생성 성공을 구분하기 위해서입니다.

컨트롤 이름부터 동일하게 만들기

Windows 데스크톱 Excel의 VBA 편집기를 기준으로 새 통합 문서를 .xlsm으로 저장합니다. Alt+F11 → 삽입 → UserForm에서 폼 이름을 frm보고서로 정하고 아래 컨트롤을 만듭니다. Caption에 적은 글자와 (Name) 속성은 다릅니다. 코드는 (Name)으로 참조합니다.

VBA 입력 검증 통과와 실제 파일 저장을 구분하는 연습 흐름도
설명용 도해이며 실제 앱 실행 화면이나 실행 성공 증빙이 아닙니다.
종류 (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가 비어 있으므로 먼저 연습 파일을 저장해야 합니다.

frm보고서의 컨트롤 이름을 코드와 대조하고 컴파일한 뒤 사용자 실행, 첫 오류 수정, 재검사로 돌아가는 입력 검증 폼의 설명용 절차 도해
설명용 도해 · 실제 앱 실행 화면이 아닙니다. 이름 대조·컴파일·사용자 실행·첫 오류 수정·재검사의 절차를 나타냅니다. 120000·2026-09-01·09는 본문의 정상 입력 기대값이며 폼 열기·클릭·포커스 이동은 실행 검증하지 않았습니다. 예제는 파일을 생성하지 않습니다.

UserForm 모듈에 붙이는 전체 코드

  1. 프로젝트 탐색기에서 frm보고서를 선택하고 각 컨트롤의 (Name)을 위 표와 대조합니다.

  2. 폼의 코드 창에 아래 전체 코드를 붙여넣고 디버그 메뉴에서 VBA 프로젝트 컴파일을 실행합니다.

  3. 폼을 선택해 F5로 실행하거나 별도 표준 모듈에서 frm보고서.Show를 호출합니다.

  4. 가상 입력으로 조회 가능한 연습 폴더와 금액 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.

작성자 소개