- 최초 작성일: 2000-09-15
- 최종 수정일: 2026-09-30
- 조회수: 16 회
- 작성자: 권현욱 (엑셀러)
- 강의 제목: 엑셀로 못하는게 머야 시리즈(1) - 설문조사 마법사
들어가기 전에
오래 전 엑셀 파일로 진행하던 강의를 웹 글로 복원한 것이라, "버튼을 눌러보세요"처럼 엑셀에서 직접 조작하라는 표현이 남아 있을 수 있습니다. 결과는 본문의 그림이나 표로 확인하실 수 있으며, 현재와 맞지 않는 내용은 보완하되 전반적인 스타일은 당시의 갬성(?)을 유지했습니다.
이번 시간에 소개해 드릴 "On-line 설문조사 마법사 만들기"는 약간 까다롭기는 하지만 매우 재미있고 유용한 것입니다(전적으로 제 기준에서 그렇다는 것이지요 ^^). 이것을 잘 소화하시면 여러분의 내공은 또 한단계 상승할 것입니다.
40여회 동안 강좌를 해오면서 여러 가지 컨트롤에 대해 설명을 드렸는데 오늘은 멀티 페이지(MultiPage)라는 새로운 컨트롤을 활용하는 방법에 대해 설명을 드립니다. 이것만 소개를 드리면 거의 모든 컨트롤에 대해 설명을 드리는 것 같습니다.
많은 윈도우 응용프로그램들은 "마법사(Wizard)"라는 인터페이스를 통해 사용자들의 정보를 입력받거나 혹은 그 반대로 사용자들에게 정보를 제공합니다. Microsoft社는 마법사라는 단어를 참으로 좋아하는 경향이 있습니다. 피벗테이블이나 차트 작업, 문자열 파일을 불러올 때(Import) 등, 3단계 이상 작업을 요하는 사항에 대해서는 거의 어김없이 마법사라는 단계를 거치도록 하는 것 같습니다.
이것은 여담입니다만, 윈도우 98에 보면 PWS(Personal Web Server)라고 해서 서버 가 아닌 윈도우 9X들을 마치 Server처럼 사용할 수 있도록 해주는 일종의 웹서버가 있는데, 여기에는 아예 멀린(Murlin, 원탁의 기사에 나오는 유명한 마법사)의 캐릭터가 등장하더군요. ^^
그건 그렇고… 이 마법사라는 것은 "여러 개의 다이얼로그 박스의 조합"이라고 할 수 있겠습니다. 이것을 통해 사용자는 작업진행의 앞단계 또는 뒷단계로 순차적으로 이 동할 수 있으며(한번의 클릭만으로), 프로그램을 종료할 수도 있습니다. 아직 마법사 가 뭔지 감이 안 오시는 분은 피벗테이블이나 차트를 작성할 때면 어김없이 나타나는 그 마법사를 한번 떠올려(혹은 직접 실행해) 보시기 바랍니다.
엑셀로 못하는게 머야 시리즈(1) - 설문조사 마법사
핵심 요약: MultiPage로 만드는 마법사
MultiPage 컨트롤의 Value 값을 바꾸어 단계를 이동하고, 단계마다 버튼의 활성 상태를 갱신하는 마법사형 UserForm입니다.
- 1단계: MultiPage에 4개 페이지를 만들고 Style을 fmTabStyleNone으로 지정합니다.
- 2단계: 이전/다음 버튼은 Value를 -1, +1하고 UpdateAllControls를 호출합니다.
- 3단계: 완료 버튼으로 입력값을 시트에 옮기고 SendMail로 발송합니다.
Step 1: 설문조사 마법사 미리 보기
일단 버튼을 눌러 오늘 만들어 볼 "설문조사 마법사"를 살펴보고 오시기 바랍니다.
지금까지 만들어 온 것에 비하면 약간 복잡하게 여겨지실 지도 모르겠습니다. 하지만 그만큼 얻는 것이 많을 것입니다. 그리고 일단 설문서를 작성하신 다음에 송신을 꼭 해주시기 바랍니다(건의사항까지 기재…). Exceller's KnowledgeBase를 구독하시는 분이 이제 950여 명 정도이고 하루에 홈페이지 접속건수가 몇 백건 정도 되는데 질문을 하시는 분은 한정이 되어 있는 경향이 있습니다.
강좌가 쉬우면 쉽다, 아니면 어렵다, 이런이런 주제를 다루어 달라 또는 과거의 강좌 중에서 이 부분은 설명을 다시해 달라… 뭐 이런 건의사항들이 많으면 많을수록 보다 충실하고 Interactive한 강좌가 되리라 생각합니다.
아울러 강좌를 보시는 분들의 수준이나 연령대, 어떤 직업에 종사하시는 분이 많은지 등에 대해 파악함으로써 "눈높이 강좌"를 진행하고자 하는 것이므로 가능하면 정확하 게 작성해서 전송해 주시면 좋겠습니다(주민등록번호나 집주소 같은 개인신상정보가 아니라 이름, E-mail, 연령대, 직종 이 정도이니까 크게 어려운 것은 아닐 것입니다).
사설은 이쯤에서 접고… 이제부터 본격적인 강좌에 들어가도록 하겠습니다. 정신바짝 차리고 잘 따라오시기 바랍니다.
Step 2: UserForm과 MultiPage 컨트롤 만들기
(1) Alt + <F11>키를 눌러 VB Editor를 띄운 상태에서 UserForm을 하나 삽입합니다.
(2) 그림을 참고로 UserForm에 필요한 컨트롤들을 그려 넣습니다. 그림을 페이지에 맞추려다 보니 좀 작군요. 일단 VB Editor로 가셔서 각 컨트롤명과 그 속성들을 하나 씩 살펴보시도록 하고 여기서는 MultiPage 컨트롤에 대해서만 자세히 설명을 드리도 록 해야겠습니다. 오늘 강좌의 핵심이 바로 MultiPage 컨트롤이니까요.
(3) MultiPage 컨트롤을 처음 작성하면 2개의 페이지를 기본값으로 가집니다. 그런데 우리가 이번 시간에 만들어 볼 "설문조사 마법사"는 4개의 페이지로 구성되어 있습니다. 따라서 2개의 페이지를 더 추가해야 겠지요?
이럴 때에는 페이지(위 그림의 파란 동그라미 부분)를 마우스 오른쪽 버튼으로 클릭 한 다음 "새 페이지" 메뉴를 선택하면 한 페이지씩 추가가 됩니다. 두 개의 페이지를 추가하여 4페이지가 되도록 만듭니다.
(4) 페이지를 다 추가하셨으면 각 페이지에 필요한 컨트롤을 삽입합니다. 여기에는 그동안 배우신 모든 컨트롤들이 총 망라된 것 같습니다. TextBox, Frame, OptionButton, CheckBox, CommandButton, ComboBox, Label 등…
컨트롤들을 다 추가하셨으면 이제부터 그것에다가 지능을 부여해주는 작업, 즉 코딩 을 해야겠지요?
Step 3: 코드 작성하기
(1) 공통 상수와 초기화 이벤트
(5) MultiPage 중 Page1을 활성화 시킨 상태에서 유저폼의 빈공간(컨트롤이 없는)을 더블클릭 합니다. 코드창의 이벤트 선택 상자에서 초기화이벤트(Initialize)를 선택하 고 아래의 코드를 입력합니다.
Const ID As String = "설문조사 마법사"
Const ProjectName As String = "설문조사 마법사 - "
Private Sub UserForm_Initialize()
With MultiPage1
.Style = fmTabStyleNone
.Value = 0
'//Style 속성을 fmTabStyleNone으로 지정하면 유저폼이 실행되는 순간에는 각 페이지의
'//탭이 숨겨지게 됩니다. 그리고 Value를 0으로 지정하면 첫번째 페이지가 Display됩니다.
'//여기서 또 한가지 알 수 있는 것은, 배열이나 ListIndex 속성과 마찬가지로 MultiPage의
'//Page 속성도 0부터 시작된다는 사실을 추측할 수 있겠지요?
End With
With cmbAge
.RowSource = ""
.AddItem "10대"
.AddItem "20대 초반"
.AddItem "20대 후반"
.AddItem "30대 초반"
.AddItem "30대 후반"
.AddItem "40대"
.AddItem "50대 이상"
'//cmbAge는 사용자의 연령을 입력받기 위한 콤보박스의 이름(Name)입니다.
'//RowSource는 콤보박스나 리스트박스의 원본목록을 지정하는 속성이었지요?
'//따라서 cmbAge의 원본목록을 일단 깨끗이 비운 다음에 AddItem 명령을 통해 콤보박스
'//의 목록을 추가해 줍니다.
End With
With cmbJob
.RowSource = ""
.AddItem "교사"
.AddItem "학생"
.AddItem "회사원"
.AddItem "공무원"
.AddItem "군인/경찰"
.AddItem "연구원"
.AddItem "의사"
.AddItem "변호사"
.AddItem "자영업"
.AddItem "전업주부"
.AddItem "기타"
'//이 부분은 cmbJob, 즉 직업을 입력받기 위한 콤보박스이며, 나머지 사항은 cmbAge와
'//같습니다.
End With
Call UpdateAllControls
'//UpdateAllControls라는 프로시저를 호출합니다.
txtName.SetFocus
End Sub
(2) UpdateAllControls 프로시저
(6) 초기화이벤트에서 UpdateAllControls 프로시저를 호출했으니 거기에 해당하는 코드를 작성해야 겠지요? 이 프로시저는 보시다시피 이벤트 핸들러 프로시저가 아니라 일반 프로시저입니다.
Sub UpdateAllControls()
Select Case MultiPage1.Value
'//멀티페이지의 Value, 즉 멀티페이지 중 몇번째 단계(페이지)를 진행 중이냐에 따라 특정한
'//버튼을 활성화(Activate) 또는 비활성화(Deactivate)시킵니다.
Case 0
btnPrevious.Enabled = False
btnNext.Enabled = True
'//Case가 0이라는 것은 맨 첫번째 페이지를 의미합니다. 따라서 btnPrevious(이전버튼)
'//오브젝트의 Enabled 속성을 False로 지정해 줍니다. 그러면 "<<이전" 버튼이 비활성화
'//되어 선택할 수 없게 되는 것입니다(버튼이 회색으로 된다는 얘기입니다).
Case MultiPage1.Pages.Count - 1
btnPrevious.Enabled = True
btnNext.Enabled = False
'//MultiPage1.Pages.Count-1이라는 것은 무얼 의미하는 것일까요? 바로 멀티페이지의
'//맨 마지막 페이지를 나타냅니다. 그런데 여기서 왜 -1을 해 주었는지 아시겠습니까?
'//멀티페이지의 Page가 0부터 시작된다고 설명을 드렸지요? 그렇기 때문에 1을 빼주는
'//것입니다.
Case Else
btnPrevious.Enabled = True
btnNext.Enabled = True
'//그 이외 경우, 즉 첫 페이지도 아니고 마지막 페이지도 아닌 경우이니까 중간 페이지를
'//의미하겠지요? 그러므로 그런 경우에는 "<<이전" 버튼과 "다음>>" 버튼을 모두 활성화
'//시킵니다.
End Select
Me.Caption = ProjectName & MultiPage1.Pages.Count & " 단계중 " & MultiPage1.Value + 1 & " 단계"
If txtName.Text = "" Then
btnEnd.Enabled = False
Else
btnEnd.Enabled = True
End If
'//이 부분은 "성명" 항목을 공란으로 하였을 경우, "완료" 버튼이 작동하지 않도록 하기 위한
'//부분입니다.
End Sub
(3) 이전, 다음 버튼
(7) "<<이전" 버튼을 더블클릭한 다음, btnPrevious_Click 이벤트 프로시저에 다음의 코드를 입력합니다. 코드는 특별한 것이 없습니다. 이전 단계로 돌아가는 것이니까 멀티페이지의 Value값을 Value-1로 대치합니다. 즉 이전 페이지로 돌아가는 것이지요. 그런 다음에 UpdateAllControls 프로시저를 다시 호출합니다.
Private Sub btnPrevious_Click()
MultiPage1.Value = MultiPage1.Value - 1
Call UpdateAllControls
End Sub
(8) "다음>>" 버튼을 더블클릭하여 클릭이벤트를 발생시킨 다음, 여기에 대응하는 코드를 작성합니다. 코드는 위의 btnPrevious_Click 이벤트 프로시저와 흡사합니다. 다만, 이 버튼이 눌려지면 다음 페이지로 이동해야 하므로 Value-1이 아닌 +1이 되는 것만 다를 뿐입니다.
Private Sub btnNext_Click()
MultiPage1.Value = MultiPage1.Value + 1
Call UpdateAllControls
End Sub
(4) 완료 버튼
(9) "완료" 버튼을 더블클릭하고 코드창에 아래의 코드를 입력합니다. 이 이벤트 프로시저는 좀 길어보입니다만 유저폼의 각 항목에 입력된 값을 워크시트 의 해당 위치에 옮기고 파일을 Exceller에게 발송하는 부분으로 크게 어려운 부분은 없을 것입니다. VB0018, 19 강좌에서 이미 설명드린 사항들을 약간 확장한 것입니다.
Private Sub btnEnd_Click()
Dim intRow As Integer
Dim intResult As Integer
Dim Msg As String
Application.ScreenUpdating = False
Application.DisplayAlerts = False
intRow = Application.WorksheetFunction.CountA(Range("a:a")) + 1
'//ScreenUpdationg은 프로그램 실행 중 화면갱신을 할 것인지의 여부를 지정하는 것입니다.
'//이것을 False로 지정하면 화면갱신을 하지 않게됩니다. DisplayAlerts는 예전에 설명을 드
'//렸지요? 프로그램을 실행하는 동안 경고 메시지를 표시할지 말것인지를 지정하는 것으로
'//False로 하게 되면 경고 메시지를 나타내지 않습니다.
'//이하의 코드는 각 항목을 워크시트로 옮기는 과정입니다.
Cells(intRow, 1) = txtName.Text
Select Case True
Case optMale: Cells(intRow, 2) = "남성"
Case optFemale: Cells(intRow, 2) = "여성"
Case optUnknown: Cells(intRow, 2) = "미상"
End Select
Cells(intRow, 3) = cmbAge.Value
Cells(intRow, 4) = cmbJob.Value
Cells(intRow, 5) = txtEMail.Text
'//사용하고 있는 프로그램
If chkExcel Then Cells(intRow, 6) = "●" Else Cells(intRow, 6) = "X"
If chkWord Then Cells(intRow, 7) = "●" Else Cells(intRow, 7) = "X"
If chkPowerpoint Then Cells(intRow, 8) = "●" Else Cells(intRow, 8) = "X"
If chkAccess Then Cells(intRow, 9) = "●" Else Cells(intRow, 9) = "X"
'//Excel 사용 수준
If optExcel1 Then Cells(intRow, 10) = "초보"
If optExcel2 Then Cells(intRow, 10) = "미숙"
If optExcel3 Then Cells(intRow, 10) = "보통"
If optExcel4 Then Cells(intRow, 10) = "우수"
If optExcel5 Then Cells(intRow, 10) = "전문가"
'//Word 사용 수준
If optWord1 Then Cells(intRow, 11) = "초보"
If optWord2 Then Cells(intRow, 11) = "미숙"
If optWord3 Then Cells(intRow, 11) = "보통"
If optWord4 Then Cells(intRow, 11) = "우수"
If optWord5 Then Cells(intRow, 11) = "전문가"
'//Powerpoint 사용 수준
If optPowerpoint1 Then Cells(intRow, 12) = "초보"
If optPowerpoint2 Then Cells(intRow, 12) = "미숙"
If optPowerpoint3 Then Cells(intRow, 12) = "보통"
If optPowerpoint4 Then Cells(intRow, 12) = "우수"
If optPowerpoint5 Then Cells(intRow, 12) = "전문가"
'//Access 사용 수준
If optAccess1 Then Cells(intRow, 13) = "초보"
If optAccess2 Then Cells(intRow, 13) = "미숙"
If optAccess3 Then Cells(intRow, 13) = "보통"
If optAccess4 Then Cells(intRow, 13) = "우수"
If optAccess5 Then Cells(intRow, 13) = "전문가"
'//Exceller의 Excel 강좌 난이도 평가
Select Case True
Case optEvalExcel1: Cells(intRow, 14) = "매우쉬움"
Case optEvalExcel2: Cells(intRow, 14) = "쉬움"
Case optEvalExcel3: Cells(intRow, 14) = "보통"
Case optEvalExcel4: Cells(intRow, 14) = "어려움"
Case optEvalExcel5: Cells(intRow, 14) = "매우난해"
End Select
'//Exceller의 VBA 강좌 난이도 평가
Select Case True
Case optEvalVBA1: Cells(intRow, 15) = "매우쉬움"
Case optEvalVBA2: Cells(intRow, 15) = "쉬움"
Case optEvalVBA3: Cells(intRow, 15) = "보통"
Case optEvalVBA4: Cells(intRow, 15) = "어려움"
Case optEvalVBA5: Cells(intRow, 15) = "매우난해"
End Select
'//건의사항
Cells(intRow, 16) = txtRequest.Text
Unload Me
'//메일 발송
intResult = MsgBox("메일을 발송할까요", vbQuestion + vbYesNo, ID)
If intResult = vbYes Then
On Error GoTo et
With ActiveWorkbook
.Save
.SendMail Recipients:="exceller@netian.com"
End With
Application.DisplayAlerts = True
Application.ScreenUpdating = True
'//Displayalerts, ScreenUpdating 속성을 False로 하고 작업을 완료한 후에는 가급적 원래
'//상태로 되돌려주고 프로시저를 종료하는 것이 좋습니다.
Else
Application.DisplayAlerts = True
Application.ScreenUpdating = True
MsgBox "다음번에는 작성한 파일을 Exceller에게 보내주세요.", , ID
Exit Sub
End If
Exit Sub
'//메일이 정상적으로 발송되지 않았을 경우 Error Handling 처리 부분
et:
Msg = "메일이 정상적으로 발송되지 못했습니다." & vbCr & vbCr
Msg = Msg & "아래의 에러를 참고하여 수정을 하시든가 아니면" & vbCr
Msg = Msg & "Exceller에게 에러 내용을 알려주시기 바랍니다." & vbCr & vbCr
Msg = Msg & "에러 사유: " & Err.Description
MsgBox Msg, vbQuestion, ID
Err.Clear
End Sub
예제의 SendMail은 Outlook 등 기본 메일 프로그램이 설정되어 있어야 동작하며, 최신 Outlook에서는 보안 확인 창이 나타날 수 있습니다. 또한 예제의 메일 주소는 당시의 것이므로 실습할 때에는 본인의 주소로 바꾸어 사용하시기 바랍니다.
(5) MultiPage의 Change 이벤트
(10) 이번에는 MultiPage 컨트롤을 더블클릭 합니다. 이제 MultiPage1_Change라는 이벤트 프로시저가 생성될 것입니다. 여기서 주목하세요. 지금까지는 더블클릭하면 Click 이벤트가 나타났었는데 이번에는 Change 이벤트가 생성되었습니다.
이 Change 이벤트 프로시저가 바로 오늘 강좌의 핵심입니다. 설문조사 마법사 2단계에서 사용자는 자신이 사용하는 소프트웨어의 종류를 선택하 게 되는데 몇 개를 선택할 지 모릅니다. 따라서 선택된 개수 및 소프트웨어 종류를 배열변수로 받아서 이것을 유저폼 상에 해당되는 항목의 개수만큼만 다시 뿌려주는 것입니다.
Private Sub MultiPage1_Change()
If MultiPage1.Value = 2 Then
Dim intTop As Integer
Dim i As Integer
Dim chkCheckBox(1 To 4) As MSForms.CheckBox
Set chkCheckBox(1) = chkExcel
Set chkCheckBox(2) = chkWord
Set chkCheckBox(3) = chkPowerpoint
Set chkCheckBox(4) = chkAccess
Dim frmFrame(1 To 4) As MSForms.Frame
Set frmFrame(1) = frmExcel
Set frmFrame(2) = frmWord
Set frmFrame(3) = frmPowerpoint
Set frmFrame(4) = frmAccess
'//오브젝트 배열변수를 선언하고 각 차원마다 해당되는 항목들을 assign시킵니다.
intTop = 37
For i = 1 To 4
If chkCheckBox(i) Then
frmFrame(i).Visible = True
frmFrame(i).Top = intTop
intTop = intTop + frmFrame(i).Height + 1
Else
frmFrame(i).Visible = False
'//For~Next 순환문을 페이지 개수만큼 돌면서 해당 CheckBox가 눌려졌는지 검사를 합
'//니다. 만약 눌려져 있으면 유저폼 상에 해당 항목만 차례로 써 주고 그렇지 않으면
'//Visible 속성을 False로 지정하여 나타나지 않도록 합니다.
End If
Next i
If txtName = "" Then
MsgBox "[성명]은 반드시 입력하셔야 합니다", , ID
End If
End If
End Sub
마무리
이상입니다. 생각보다 조금 복잡합니까?
한 回 분량치고는 좀 많은 것 같지만 그래서 일부러 주말을 앞두고 띄우는 것이니까 집에서 심심하시면(아니면 잠이 안오면) 한번씩 읽어보셔도 안 말립니다. ^^
보시다가 혹시 잘못된 부분이나 더 좋은 아이디어가 있으면 연락주시기 바랍니다. 또한 이제 막 VBA에 재미를 붙여가는 분들은 제대로 이해하지 못하실 지도 모르겠는 데 막히는 부분은 꼭 질문을 하세요. 그냥 대충대충 하시다가 나중에 가서야 왜 그때 자세히 설명안했냐고 따지지 마시고… ^^
"처음에는 좀 쉬운 것 같더니만 이제는 왜 이렇게 어려운 것만 하느냐"고 항의(?)아닌 항의를 하시는 분이 간혹 계십니다. 가장 큰 이유는 질문을 하지 않고 대충 그런가보다 하고 넘어가기 때문에 그렇게 되는 것입니다. 막히는 부분이 나오면 어떤 식으로든지 해결을 하고 넘어가야 합니다.
또한 강의 초반에 기초부분을 다룰 때부터 보신 분 중에 "이런 쉬운 것을 다루다니!" 하며 안보시다가 최근의 것을 보며, "이게 뭐야? 갑자기 왜이렇게 어려워?!" 하시는 분도 있는 것 같습니다.
이런 분들도 기초를 확실히 잡은 다음에, 자꾸자꾸 응용을 해 보아야 합니다. 함수가 서말이라도 꿰어야 보배가 되고, VBA가 어려워(보여)도 하늘 아래 VBA입니다. 연습이 대가를 만듭니다(Ubungen Macht Meister!!!). 남이 짠 코드를 가지고 이것저것 바꾸어 가며 연습을 해 보시기 바랍니다. 공연히 애꿋은 머리 탓하지 마세요!
이제 VBA 강좌를 시작한 지 6개월이 지났는데 그동안 강좌를 열심히 따라 오신 분 이라면 크게 힘들지 않을 것이라고 생각합니다.
오늘 설명드린 내용은 그동안 배우신 사항들을 모두 활용해 보는 성격이 강하므로 이것을 잘 소화해서 내 것으로 만드시면 여러분의 내공은 또 한단계 상승입니다. 아마 웬만한 유저폼쯤은 누워서 식은 죽먹기로 만드실 수 있을 것입니다.
다음 시간에 또…
정리 — 마법사 구현 핵심
| 구분 | 사용한 코드 | 역할 |
|---|---|---|
| 탭 숨기기 | MultiPage1.Style = fmTabStyleNone | 실행 시 탭 숨김 |
| 단계 이동 | MultiPage1.Value = Value ± 1 | 이전/다음 페이지 |
| 버튼 제어 | btnPrevious.Enabled | 첫/마지막 단계 제어 |
| 컨트롤 배열 | Dim chkCheckBox(1 To 4) As MSForms.CheckBox | 체크박스 묶음 처리 |
| 메일 발송 | ActiveWorkbook.SendMail | 결과 파일 전송 |
자주 묻는 질문 (FAQ)
Q1. MultiPage의 페이지 번호는 몇 번부터 시작하나요?
배열이나 ListIndex처럼 0부터 시작합니다. 그래서 마지막 페이지는 Pages.Count - 1입니다.
Q2. Click 대신 Change 이벤트를 쓰는 이유는 무엇인가요?
MultiPage를 더블클릭하면 Change 이벤트가 생성되며, 페이지가 바뀔 때마다 필요한 컨트롤을 갱신할 수 있습니다.
Q3. 완료 버튼이 비활성화되는 경우는 언제인가요?
성명 텍스트박스가 비어 있으면 UpdateAllControls에서 완료 버튼을 비활성화합니다.
마치며
이번 강좌는 그동안 배운 컨트롤을 총동원해 보는 종합 실습이므로, 차근차근 따라 하면 내공이 한 단계 올라갑니다.