AI, VBA, Python으로 끝내는 MS 오피스 자동화
프롬프트 엔지니어링부터 파이썬 자동화까지, AI 기반 업무 자동화의 전체 흐름
1차시. 프롬프트 엔지니어링
학습 목표
- 생성형 AI와 업무 자동화의 관계를 이해한다.
- 프롬프트의 기본 구조(지시문과 맥락)를 구분할 수 있다.
- 구조화된 프롬프트를 작성하여 원하는 결과를 얻을 수 있다.
- Few-shot 프롬프팅 기법을 활용할 수 있다.
- AI와 반복 수정(iteration) 방식으로 협업할 수 있다.
1. 생성형 AI와 업무 자동화
1.1 생성형 AI란?
생성형 AI(Generative AI)는 텍스트, 이미지, 코드 등 새로운 콘텐츠를 생성할 수 있는 인공지능입니다. 대표적으로 ChatGPT, Claude, Gemini 등이 있습니다.
1.2 업무 자동화에서의 역할
생성형 AI는 다음과 같은 업무를 자동화하는 데 활용됩니다.
- 문서 작성: 보고서, 안내문, 이메일 초안
- 데이터 처리: 엑셀 함수·수식 생성, VBA 코드 작성
- 분석 보조: 데이터 해석, 요약, 시각화 코드 생성
- 반복 업무 제거: 양식 변환, 파일 정리, 일괄 처리
2. 프롬프트의 기본 구조
프롬프트는 크게 지시문(Instruction)과 맥락(Context) 두 부분으로 구성됩니다.
2.1 지시문 (Instruction)
AI에게 무엇을 해달라는 명령입니다.
| 요소 | 설명 | 예시 |
|---|---|---|
| 행동 | 무엇을 할 것인가 | "작성해줘", "요약해줘", "분류해줘" |
| 형식 | 어떤 형태로 줄 것인가 | "표로 정리해줘", "3줄로 요약해줘" |
| 제약 | 지켜야 할 조건 | "200자 이내로", "한국어로" |
2.2 맥락 (Context)
AI가 정확한 결과를 내기 위해 필요한 배경 정보입니다.
| 요소 | 설명 | 예시 |
|---|---|---|
| 역할 | AI가 어떤 입장인가 | "너는 HR 담당자야" |
| 대상 | 누구를 위한 것인가 | "신입사원 대상" |
| 상황 | 어떤 상황인가 | "분기 실적 보고 시즌" |
| 데이터 | 참고할 자료 | 구체적인 수치, 목록 등 |
2.3 지시문만 있는 프롬프트 vs 맥락이 포함된 프롬프트
나쁜 예시 (지시문만)
교육 안내문 써줘
좋은 예시 (지시문 + 맥락)
너는 대기업 인사팀 사원이야. 다음 주 화요일에 진행하는 사내 엑셀 교육에 대한 안내문을 작성해줘. - 교육명: 엑셀 데이터 분석 실무 - 일시: 2026년 3월 24일(화) 14:00~17:00 - 장소: 본사 8층 대회의실 - 대상: 전 직원 (선착순 30명) - 신청 방법: 사내 인트라넷 > 교육 신청 - 마감: 3월 20일(금) 공식적이지만 딱딱하지 않은 톤으로, 300자 내외로 작성해줘.
3. 프롬프트 구조화 방법
3.1 하이픈(-)을 활용한 목록 구조
정보를 나열할 때 하이픈을 사용하면 AI가 각 항목을 명확히 구분합니다.
다음 조건에 맞는 체크리스트를 만들어줘. - 대상: 신입사원 온보딩 - 기간: 입사 후 첫 1주일 - 항목 수: 10개 - 카테고리: 행정, IT, 팀 적응 - 형식: 체크박스가 있는 표
3.2 마크다운 제목(#)을 활용한 섹션 구조
긴 프롬프트에서 내용을 섹션별로 나눌 때 유용합니다.
# 역할 너는 10년 경력의 HR 컨설턴트야. # 작업 아래 직원 데이터를 분석하여 이직 위험군을 식별해줘. # 데이터 - 직원 수: 1,470명 - 컬럼: 나이, 퇴사여부, 부서, 직무만족도, 월급, 초과근무, 근속연수 # 출력 형식 - 이직 위험 요인 3가지 - 각 요인별 근거 - 대응 방안 1줄씩
3.3 구조화 효과 비교
| 방식 | 장점 | 적합한 상황 |
|---|---|---|
| 하이픈(-) | 조건·항목을 깔끔하게 나열 | 짧은 목록, 제약 조건 |
| 마크다운 제목(#) | 긴 프롬프트를 섹션으로 분리 | 복잡한 요청, 역할+작업+형식 분리 |
| 번호(1, 2, 3) | 순서가 중요한 단계 | 절차, 프로세스 설명 |
4. Few-shot 프롬프팅
4.1 Zero-shot vs One-shot vs Few-shot
| 방식 | 예시 개수 | 설명 |
|---|---|---|
| Zero-shot | 0개 | 예시 없이 지시만 제공 |
| One-shot | 1개 | 예시 1개 제공 |
| Few-shot | 2~5개 | 예시 여러 개 제공 |
예시를 제공하면 AI가 패턴을 학습하여 일관된 형식으로 결과를 생성합니다.
4.2 Zero-shot 예시
직원 피드백을 긍정/부정/중립으로 분류해줘. "팀 분위기가 좋아서 출근이 즐겁습니다."
AI가 형식을 자유롭게 결정하므로 결과가 일관되지 않을 수 있습니다.
4.3 One-shot 예시
직원 피드백을 분류해줘. 아래 예시와 같은 형식으로 출력해줘. 예시) 피드백: "급여가 시장 대비 낮은 것 같습니다." 분류: 부정 키워드: 급여, 불만족 --- 피드백: "팀 분위기가 좋아서 출근이 즐겁습니다."
4.4 Few-shot 예시
직원 피드백을 분류해줘. 아래 예시들과 같은 형식으로 출력해줘. 예시 1) 피드백: "급여가 시장 대비 낮은 것 같습니다." 분류: 부정 키워드: 급여, 불만족 예시 2) 피드백: "재택근무 제도가 잘 되어 있어요." 분류: 긍정 키워드: 근무환경, 만족 예시 3) 피드백: "교육 프로그램이 다양했으면 합니다." 분류: 중립 키워드: 교육, 개선요청 --- 아래 피드백들을 같은 형식으로 분류해줘. 1. "팀 분위기가 좋아서 출근이 즐겁습니다." 2. "야근이 너무 잦아 힘듭니다." 3. "사내 동호회 활동이 활발합니다."
5. AI와 협업하는 반복 수정 방식
5.1 한 번에 완벽한 결과를 기대하지 마세요
프롬프트 엔지니어링의 핵심은 반복(iteration)입니다. 첫 결과가 만족스럽지 않으면 수정 요청을 통해 개선합니다.
5.2 반복 수정 패턴
[1단계: 초안 요청] 사내 교육 안내문을 작성해줘. [2단계: 구체화] 좀 더 공식적인 톤으로 바꿔줘. 교육 일시와 장소를 강조해줘. [3단계: 형식 조정] 핵심 정보를 표로 정리하고, 하단에 문의처를 추가해줘. [4단계: 세부 수정] "신청 마감"을 빨간색 강조 표시로 바꾸고, 전체 길이를 200자로 줄여줘.
5.3 효과적인 수정 요청 표현
| 상황 | 수정 요청 예시 |
|---|---|
| 너무 길 때 | "절반 길이로 줄여줘" |
| 톤이 안 맞을 때 | "좀 더 캐주얼하게 / 공식적으로 바꿔줘" |
| 형식 변경 | "표 형식으로 바꿔줘", "번호 매겨줘" |
| 내용 추가 | "문의처와 마감일을 추가해줘" |
| 내용 삭제 | "인사말 부분은 빼줘" |
| 예시 추가 | "각 항목에 구체적 예시를 하나씩 넣어줘" |
5.4 대화 맥락 활용
ChatGPT는 같은 대화 내에서 이전 내용을 기억합니다. 이를 활용하면 점진적으로 결과를 개선할 수 있습니다.
처음: "HR 데이터 분석 보고서 목차를 만들어줘" 이어서: "2번 항목을 더 세분화해줘" 이어서: "각 항목에 예상 분량(페이지 수)을 추가해줘" 이어서: "이 목차에 맞는 서론을 500자로 써줘"
6. 문서 자동화의 출발점
6.1 프롬프트 엔지니어링이 자동화의 첫 단계인 이유
| 단계 | 내용 | 도구 |
|---|---|---|
| 1단계 | 프롬프트로 원하는 결과를 정의 | ChatGPT |
| 2단계 | 엑셀 함수·수식 자동 생성 | ChatGPT + 엑셀 |
| 3단계 | VBA 코드로 반복 작업 자동화 | ChatGPT + VBA |
| 4단계 | 파일 분리·취합 자동화 | ChatGPT + VBA |
6.2 문서 종류별 프롬프트 전략
| 문서 종류 | 핵심 맥락 | 프롬프트 팁 |
|---|---|---|
| 안내문 | 대상, 일시, 장소, 방법 | 핵심 정보를 목록으로 제공 |
| 보고서 | 목적, 데이터, 결론 방향 | 역할 설정 + 출력 형식 지정 |
| 체크리스트 | 대상, 기간, 카테고리 | 항목 수와 분류 기준 제시 |
| 이메일 | 수신자, 목적, 톤 | 관계와 상황 설명 |
실습
실습 1: 짧고 모호한 프롬프트 vs 구조화된 프롬프트 비교
Step 1. ChatGPT에 아래 프롬프트를 입력합니다.
교육 안내문 써줘
결과를 확인합니다. 어떤 교육인지, 누구 대상인지, 언제인지 불명확한 결과가 나올 것입니다.
Step 2. 이번에는 아래 구조화된 프롬프트를 입력합니다.
너는 대기업 인사팀 사원이야. 다음 주 화요일에 진행하는 사내 엑셀 교육에 대한 안내문을 작성해줘. - 교육명: 엑셀 데이터 분석 실무 - 일시: 2026년 3월 24일(화) 14:00~17:00 - 장소: 본사 8층 대회의실 - 대상: 전 직원 (선착순 30명) - 신청 방법: 사내 인트라넷 > 교육 신청 - 마감: 3월 20일(금) - 톤: 공식적이지만 딱딱하지 않게 - 분량: 300자 내외
비교 포인트: 두 결과의 구체성, 정확성, 활용 가능성을 비교합니다.
실습 2: 맥락 추가하여 결과 개선
실습 1에서 생성한 안내문에 추가 맥락을 제공하여 결과를 개선해봅니다. 반복 수정 방식을 활용하세요.
실습 3: One-shot, Few-shot 프롬프트 작성
직원 피드백 분류 작업을 Zero-shot, One-shot, Few-shot 방식으로 각각 수행하고 결과를 비교합니다.
실습 과제
다음 3개의 문서를 구조화된 프롬프트로 작성하세요.
- 사내 교육 안내문 (역할, 대상, 일시, 장소, 톤, 분량 등을 명시)
- HR 데이터 분석 보고서 목차 (역할 설정 + 마크다운 제목 구조 활용)
- 직원 피드백 분류 (Few-shot 기법 활용, 3개 이상의 예시 포함)
정리
| 핵심 개념 | 요약 |
|---|---|
| 프롬프트 구조 | 지시문(무엇을) + 맥락(배경 정보) |
| 구조화 도구 | 하이픈(-), 마크다운 제목(#), 번호(1,2,3) |
| Few-shot | 예시를 제공하여 출력 형식 통제 |
| 반복 수정 | 한 번에 완벽을 기대하지 말고 점진적으로 개선 |
| 자동화 연결 | 프롬프트 → 함수 생성 → VBA 코드 → 파일 자동화 |
2차시. 엑셀 데이터 처리 자동화
학습 목표
- ChatGPT에 자연어로 엑셀 작업을 요청하는 방법을 익힌다.
- COUNTIF, AVERAGEIF, UNIQUE 등 핵심 함수를 활용할 수 있다.
- 조건부 서식, 필터, 정렬 등 데이터 처리 기능을 적용할 수 있다.
- VBA를 활용하여 함수 입력과 데이터 처리를 자동화할 수 있다.
사용 데이터
- 파일 위치:
실습파일/HR데이터.xlsx(VBA 포함:실습파일/HR데이터(VBA포함).xlsm) - 규모: 1,470명, 35개 컬럼
- 주요 컬럼: 나이, 퇴사여부, 출장빈도, 일급, 부서, 통근거리, 학력, 전공분야, 직원수, 사번, 환경만족도, 성별, 시급, 직무몰입도, 직급, 직무, 직무만족도, 결혼상태, 월급, 월급비율, 이전직장수, 성인여부, 초과근무, 급여인상률, 성과등급, 관계만족도, 기본근무시간, 스톡옵션등급, 총경력연수, 작년교육횟수, 워라밸, 근속연수, 현직무연수, 최근승진후연수, 현관리자근무연수
- 부서(3개): 영업, 연구개발, 인사
- 직무(9개): 영업임원, 연구원, 실험기술자, 제조이사, 의료담당자, 관리자, 영업사원, 연구이사, 인사담당
1. ChatGPT를 활용한 엑셀 작업 자동화
1.1 엑셀 함수를 자연어로 요청하기
엑셀 함수를 외울 필요가 없습니다. ChatGPT에 원하는 작업을 자연어로 설명하면 함수를 생성해줍니다.
프롬프트 작성 팁
| 요소 | 설명 | 예시 |
|---|---|---|
| 데이터 위치 | 어떤 열/셀에 데이터가 있는지 | "O열에 직급 데이터가 있어" |
| 원하는 결과 | 무엇을 구하고 싶은지 | "직급별 인원 수를 구하고 싶어" |
| 조건 | 필터 조건이 있는지 | "직급이 1인 사람만" |
| 출력 위치 | 결과를 어디에 넣을지 | "AJ열에 결과를 넣어줘" |
1.2 주요 엑셀 함수
| 함수 | 용도 | 구문 |
|---|---|---|
| COUNTIF | 조건에 맞는 셀 개수 | =COUNTIF(범위, 조건) |
| AVERAGEIF | 조건에 맞는 평균 | =AVERAGEIF(조건범위, 조건, 평균범위) |
| UNIQUE | 고유값 추출 | =UNIQUE(범위) |
| COUNTA | 비어있지 않은 셀 개수 | =COUNTA(범위) |
| IF | 조건 판단 | =IF(조건, 참값, 거짓값) |
1.3 유용한 엑셀 단축키
| 단축키 | 기능 |
|---|---|
| Ctrl + Shift + L | 필터 토글 |
| Alt + F11 | VBA 편집기 열기 |
| Alt + Tab | 창 전환 |
| Ctrl + Home | 셀 A1로 이동 |
| Ctrl + End | 데이터 마지막 셀로 이동 |
| Ctrl + Shift + End | 현재 셀부터 마지막까지 선택 |
2. 실습: 엑셀 함수 활용
실습 1: 직급별 인원 수 구하기
HR데이터.xlsx의 O열(직급)에는 1~5의 값이 있습니다. 각 직급별 인원 수를 구합니다.
ChatGPT에 이렇게 요청하세요.
엑셀에서 O열에 직급 데이터(1~5)가 있어. AJ1에 "직급", AK1에 "인원수" 헤더를 넣고, AJ2:AJ6에 직급 번호 1~5를 넣고, AK2:AK6에 각 직급별 인원 수를 COUNTIF로 구하는 수식을 알려줘. 데이터는 2행부터 1471행까지 있어.
결과 수식 예시
- AK2 셀:
=COUNTIF($O$2:$O$1471, AJ2) - AK3~AK6: 같은 수식을 아래로 복사
실습 2: 부서별 평균 월급 구하기
E열(부서)과 S열(월급)을 사용하여 부서별 평균 월급을 구합니다.
ChatGPT에 이렇게 요청하세요.
엑셀에서 E열에 부서(영업, 연구개발, 인사), S열에 월급 데이터가 있어. AM1에 "부서", AN1에 "평균월급" 헤더를 넣고, AM2에 "영업", AM3에 "연구개발", AM4에 "인사"를 넣고, AN2:AN4에 각 부서별 평균 월급을 AVERAGEIF로 구하는 수식을 알려줘. 데이터는 2행부터 1471행까지 있어.
결과 수식 예시
- AN2 셀:
=AVERAGEIF($E$2:$E$1471, AM2, $S$2:$S$1471)
실습 3: UNIQUE 함수로 부서 목록 추출
ChatGPT에 이렇게 요청하세요.
엑셀에서 E열에 부서 데이터가 있어. AP1에 "고유부서목록" 헤더를 넣고, AP2에 UNIQUE 함수로 고유한 부서 목록을 추출하는 수식을 알려줘.
결과 수식
- AP2 셀:
=UNIQUE(E2:E1471)
실습 4: 조건부 서식 적용
직급이 1인 행을 주황색으로 강조합니다.
ChatGPT에 이렇게 요청하세요.
엑셀에서 O열에 직급 데이터가 있어. 직급이 1인 행 전체를 주황색 배경으로 강조하는 조건부 서식을 설정하는 방법을 알려줘.
수동 설정 방법
- 데이터 범위(A2:AI1471)를 선택
- 홈 탭 > 조건부 서식 > 새 규칙
- "수식을 사용하여 서식을 지정할 셀 결정" 선택
- 수식 입력:
=$O2=1 - 서식 > 채우기 > 주황색 선택 > 확인
실습 5: 필터링
부서가 "영업"이고 월급이 5,000 이상인 직원을 필터링합니다.
ChatGPT에 이렇게 요청하세요.
엑셀에서 다음 조건으로 필터링하는 방법을 알려줘. - E열(부서) = "영업" - S열(월급) >= 5000
수동 설정 방법
- Ctrl + Shift + L로 필터 활성화
- E열 필터 클릭 > "영업"만 선택
- S열 필터 클릭 > 숫자 필터 > 크거나 같음 > 5000 입력
실습 6: 새 열 추가
6-1. 평가등급텍스트 열
성과등급(Y열) 값을 텍스트로 변환하는 열을 추가합니다.
| 성과등급 | 텍스트 |
|---|---|
| 1 | 미흡 |
| 2 | 보통 |
| 3 | 우수 |
| 4 | 탁월 |
ChatGPT에 이렇게 요청하세요.
엑셀에서 Y열에 성과등급(1~4)이 있어. AJ1에 "평가등급텍스트" 헤더를 넣고, AJ2에 IF 함수로 1=미흡, 2=보통, 3=우수, 4=탁월로 변환하는 수식을 알려줘.
결과 수식
- AJ2 셀:
=IF(Y2=1,"미흡",IF(Y2=2,"보통",IF(Y2=3,"우수",IF(Y2=4,"탁월",""))))
6-2. 관리대상여부 열
다음 조건을 모두 만족하면 "관리대상"으로 표시합니다.
- 직무만족도(Q열) <= 2
- 환경만족도(K열) <= 2
- 워라밸(AE열) <= 2
ChatGPT에 이렇게 요청하세요.
엑셀에서 다음 조건을 모두 만족하면 "관리대상", 아니면 공백을 반환하는 수식을 알려줘. - Q열(직무만족도) <= 2 - K열(환경만족도) <= 2 - AE열(워라밸) <= 2 AK1에 "관리대상여부" 헤더를 넣고, AK2에 수식을 넣을 거야.
결과 수식
- AK2 셀:
=IF(AND(Q2<=2, K2<=2, AE2<=2), "관리대상", "")
3. VBA로 자동화하기
위 실습들을 VBA 매크로로 자동화할 수 있습니다. 각 매크로는 ChatGPT에 요청하여 생성한 코드입니다.
HR데이터(VBA포함).xlsm 파일에는 매크로별로 개별 모듈이 이미 구성되어 있습니다. 직접 작성할 때도 매크로 하나당 모듈 하나씩 분리하는 것을 권장합니다.
| 모듈명 | 기능 | 차시 |
|---|---|---|
| M01_직급별인원수 | 직급별 인원 수 집계 | 2차시 |
| M02_부서별평균월급 | 부서별 평균 월급 계산 | 2차시 |
| M03_피벗테이블 | 부서-직무 피벗테이블 생성 | 2차시 |
| M04_차트생성 | 집계 결과 차트 생성 | 2차시 |
| M05_부서별파일분리 | 부서별 엑셀 파일 분리 저장 | 3차시 |
| M06_부서별파일취합 | 분리된 파일 하나로 취합 | 3차시 |
| M07_수료증슬라이드생성 | PPT 수료증 슬라이드 일괄 생성 | 4차시 |
| M08_수료증PDF생성 | 개인별 수료증 PDF 저장 | 4차시 |
| M09_워드보고서생성 | 워드 문서 자동 생성 | 5차시 |
| M10_이메일발송 | 이메일 자동 발송 | 6차시 |
| M11_엑셀보고서생성 | HR 데이터 엑셀 보고서 생성 | 7차시 |
| M12_PPT보고서생성 | HR 데이터 PPT 보고서 생성 | 7차시 |
매크로 1: 직급별 인원 수
ChatGPT에 이렇게 요청하세요.
엑셀 VBA 매크로를 작성해줘. # 데이터 - 시트명: Sheet1 - O열: 직급 (1~5), 2행~1471행 # 작업 - AJ1에 "직급", AK1에 "인원수" 헤더 입력 - AJ2:AJ6에 직급 번호 1~5 입력 - AK2:AK6에 각 직급별 인원 수를 COUNTIF 수식으로 입력 - 완료 후 MsgBox로 완료 메시지 표시 # 주의 - 한글은 ChrW() 함수를 사용할 것
VBA 코드
Sub 직급별인원수()
Dim ws As Worksheet
Set ws = ThisWorkbook.Sheets("Sheet1")
' 헤더 입력
ws.Range("AJ1").Value = ChrW(51649) & ChrW(44553) ' 직급
ws.Range("AK1").Value = ChrW(51064) & ChrW(50896) & ChrW(49688) ' 인원수
' 직급 번호 입력
Dim i As Long
For i = 1 To 5
ws.Cells(i + 1, 36).Value = i ' AJ열 = 36번째
Next i
' COUNTIF 수식 입력
For i = 2 To 6
ws.Cells(i, 37).Formula = "=COUNTIF($O$2:$O$1471,AJ" & i & ")" ' AK열 = 37번째
Next i
MsgBox ChrW(51649) & ChrW(44553) & ChrW(48324) & " " & ChrW(51064) & ChrW(50896) & ChrW(49688) & " " & ChrW(44228) & ChrW(49328) & ChrW(51060) & " " & ChrW(50756) & ChrW(47308) & ChrW(46104) & ChrW(50632) & ChrW(49845) & ChrW(45768) & ChrW(45796) & "."
' "직급별 인원수 계산이 완료되었습니다."
End Sub
실행 결과
매크로 2: 부서별 평균 월급
ChatGPT에 이렇게 요청하세요.
엑셀 VBA 매크로를 작성해줘. # 데이터 - 시트명: Sheet1 - E열: 부서 (영업, 연구개발, 인사), 2행~1471행 - S열: 월급 # 작업 - AM1에 "부서", AN1에 "평균월급" 헤더 입력 - AM2에 "영업", AM3에 "연구개발", AM4에 "인사" 입력 - AN2:AN4에 각 부서별 평균 월급을 AVERAGEIF 수식으로 입력 - 완료 후 MsgBox로 완료 메시지 표시 # 주의 - 한글은 ChrW() 함수를 사용할 것
VBA 코드
Sub 부서별평균월급()
Dim ws As Worksheet
Set ws = ThisWorkbook.Sheets("Sheet1")
' 헤더 입력
ws.Range("AM1").Value = ChrW(48512) & ChrW(49436) ' 부서
ws.Range("AN1").Value = ChrW(54217) & ChrW(44512) & ChrW(50900) & ChrW(44553) ' 평균월급
' 부서명 입력
ws.Range("AM2").Value = ChrW(50689) & ChrW(50629) ' 영업
ws.Range("AM3").Value = ChrW(50672) & ChrW(44396) & ChrW(44060) & ChrW(48156) ' 연구개발
ws.Range("AM4").Value = ChrW(51064) & ChrW(49324) ' 인사
' AVERAGEIF 수식 입력
Dim i As Long
For i = 2 To 4
ws.Cells(i, 40).Formula = "=AVERAGEIF($E$2:$E$1471,AM" & i & ",$S$2:$S$1471)" ' AN열 = 40번째
Next i
MsgBox ChrW(48512) & ChrW(49436) & ChrW(48324) & " " & ChrW(54217) & ChrW(44512) & ChrW(50900) & ChrW(44553) & " " & ChrW(44228) & ChrW(49328) & ChrW(51060) & " " & ChrW(50756) & ChrW(47308) & ChrW(46104) & ChrW(50632) & ChrW(49845) & ChrW(45768) & ChrW(45796) & "."
' "부서별 평균월급 계산이 완료되었습니다."
End Sub
실행 결과
매크로 3: 고유 부서 목록
ChatGPT에 이렇게 요청하세요.
엑셀 VBA 매크로를 작성해줘. # 데이터 - 시트명: Sheet1 - E열: 부서, 2행~1471행 # 작업 - AP1에 "고유부서목록" 헤더 입력 - E열에서 고유한 부서 값을 추출하여 AP2부터 아래로 입력 - Collection 또는 Dictionary를 사용하여 중복 제거 - 완료 후 MsgBox로 완료 메시지 표시 # 주의 - 한글은 ChrW() 함수를 사용할 것
VBA 코드
Sub 고유부서목록()
Dim ws As Worksheet
Set ws = ThisWorkbook.Sheets("Sheet1")
' 헤더 입력
ws.Range("AP1").Value = ChrW(44256) & ChrW(50976) & ChrW(48512) & ChrW(49436) & ChrW(47785) & ChrW(47197) ' 고유부서목록
' Collection으로 고유값 추출
Dim col As New Collection
Dim cell As Range
Dim val As String
On Error Resume Next
For Each cell In ws.Range("E2:E1471")
val = cell.Value
If val <> "" Then
col.Add val, val
End If
Next cell
On Error GoTo 0
' 고유값 입력
Dim i As Long
For i = 1 To col.Count
ws.Cells(i + 1, 42).Value = col(i) ' AP열 = 42번째
Next i
MsgBox ChrW(44256) & ChrW(50976) & " " & ChrW(48512) & ChrW(49436) & " " & ChrW(47785) & ChrW(47197) & ChrW(51060) & " " & ChrW(52628) & ChrW(52636) & ChrW(46104) & ChrW(50632) & ChrW(49845) & ChrW(45768) & ChrW(45796) & "."
' "고유 부서 목록이 추출되었습니다."
End Sub
실행 결과
매크로 4: 조건부 서식 적용
ChatGPT에 이렇게 요청하세요.
엑셀 VBA 매크로를 작성해줘. # 데이터 - 시트명: Sheet1 - O열: 직급, 2행~1471행 - 데이터 범위: A2:AI1471 # 작업 - O열(직급)이 1인 행 전체에 주황색 배경 조건부 서식 적용 - 조건부 서식 수식: =$O2=1 - 배경색: 주황색 (RGB 255, 165, 0) - 완료 후 MsgBox로 완료 메시지 표시 # 주의 - 한글은 ChrW() 함수를 사용할 것
VBA 코드
Sub 조건부서식적용()
Dim ws As Worksheet
Set ws = ThisWorkbook.Sheets("Sheet1")
' 데이터 범위 설정
Dim rng As Range
Set rng = ws.Range("A2:AI1471")
' 기존 조건부 서식 제거 (선택)
rng.FormatConditions.Delete
' 조건부 서식 추가: O열 = 1이면 주황색 배경
Dim fc As FormatCondition
Set fc = rng.FormatConditions.Add(Type:=xlExpression, Formula1:="=$O2=1")
fc.Interior.Color = RGB(255, 165, 0)
fc.StopIfTrue = False
MsgBox ChrW(51649) & ChrW(44553) & "=1 " & ChrW(54665) & ChrW(50640) & " " & ChrW(51452) & ChrW(54889) & ChrW(49353) & " " & ChrW(48176) & ChrW(44221) & ChrW(49353) & ChrW(51060) & " " & ChrW(51201) & ChrW(50857) & ChrW(46104) & ChrW(50632) & ChrW(49845) & ChrW(45768) & ChrW(45796) & "."
' "직급=1 행에 주황색 배경색이 적용되었습니다."
End Sub
실행 결과
매크로 5: 필터링 실습
ChatGPT에 이렇게 요청하세요.
엑셀 VBA 매크로를 작성해줘. # 데이터 - 시트명: Sheet1 - E열(5번째): 부서 - S열(19번째): 월급 - 데이터 범위: A1:AI1471 (1행은 헤더) # 작업 - E열 = "영업" AND S열 >= 5000 조건으로 AutoFilter 적용 - 완료 후 MsgBox로 완료 메시지 표시 # 주의 - 한글은 ChrW() 함수를 사용할 것
VBA 코드
Sub 필터링실습()
Dim ws As Worksheet
Set ws = ThisWorkbook.Sheets("Sheet1")
' 기존 필터 해제
If ws.AutoFilterMode Then ws.AutoFilterMode = False
' AutoFilter 적용
Dim rng As Range
Set rng = ws.Range("A1:AI1471")
rng.AutoFilter Field:=5, Criteria1:=ChrW(50689) & ChrW(50629) ' 부서 = "영업"
rng.AutoFilter Field:=19, Criteria1:=">=5000" ' 월급 >= 5000
MsgBox ChrW(48512) & ChrW(49436) & "=" & ChrW(50689) & ChrW(50629) & ", " & ChrW(50900) & ChrW(44553) & ">=5000 " & ChrW(54596) & ChrW(53552) & ChrW(47553) & ChrW(51060) & " " & ChrW(51201) & ChrW(50857) & ChrW(46104) & ChrW(50632) & ChrW(49845) & ChrW(45768) & ChrW(45796) & "."
' "부서=영업, 월급>=5000 필터링이 적용되었습니다."
End Sub
실행 결과
매크로 6: 평가등급 열 추가
ChatGPT에 이렇게 요청하세요.
엑셀 VBA 매크로를 작성해줘. # 데이터 - 시트명: Sheet1 - Y열(25번째): 성과등급 (1~4), 2행~1471행 # 작업 - AJ1에 "평가등급텍스트" 헤더 입력 - AJ2:AJ1471에 IF 수식 입력: 1=미흡, 2=보통, 3=우수, 4=탁월 - 완료 후 MsgBox로 완료 메시지 표시 # 주의 - 한글은 ChrW() 함수를 사용할 것 - 수식 내 한글도 ChrW()로 처리
VBA 코드
Sub 평가등급열추가()
Dim ws As Worksheet
Set ws = ThisWorkbook.Sheets("Sheet1")
' 헤더 입력
ws.Range("AJ1").Value = ChrW(54217) & ChrW(44032) & ChrW(46321) & ChrW(44553) & ChrW(53581) & ChrW(49828) & ChrW(53944) ' 평가등급텍스트
' IF 수식 입력
Dim i As Long
Dim formula As String
' 한글 텍스트를 변수에 저장
Dim txt1 As String, txt2 As String, txt3 As String, txt4 As String
txt1 = ChrW(48120) & ChrW(55121) ' 미흡
txt2 = ChrW(48372) & ChrW(53685) ' 보통
txt3 = ChrW(50864) & ChrW(49688) ' 우수
txt4 = ChrW(53441) & ChrW(50900) ' 탁월
For i = 2 To 1471
ws.Cells(i, 36).Value = ""
Select Case ws.Cells(i, 25).Value
Case 1: ws.Cells(i, 36).Value = txt1
Case 2: ws.Cells(i, 36).Value = txt2
Case 3: ws.Cells(i, 36).Value = txt3
Case 4: ws.Cells(i, 36).Value = txt4
End Select
Next i
MsgBox ChrW(54217) & ChrW(44032) & ChrW(46321) & ChrW(44553) & ChrW(53581) & ChrW(49828) & ChrW(53944) & " " & ChrW(50676) & ChrW(51060) & " " & ChrW(52628) & ChrW(44032) & ChrW(46104) & ChrW(50632) & ChrW(49845) & ChrW(45768) & ChrW(45796) & "."
' "평가등급텍스트 열이 추가되었습니다."
End Sub
실행 결과
매크로 7: 관리대상 여부 열 추가
ChatGPT에 이렇게 요청하세요.
엑셀 VBA 매크로를 작성해줘. # 데이터 - 시트명: Sheet1 - Q열(17번째): 직무만족도, 2행~1471행 - K열(11번째): 환경만족도 - AE열(31번째): 워라밸 # 작업 - AK1에 "관리대상여부" 헤더 입력 - AK2:AK1471에 값 입력 - 직무만족도 <= 2 AND 환경만족도 <= 2 AND 워라밸 <= 2 이면 "관리대상" - 아니면 빈 문자열 - 완료 후 MsgBox로 완료 메시지 표시 # 주의 - 한글은 ChrW() 함수를 사용할 것
VBA 코드
Sub 관리대상여부열추가()
Dim ws As Worksheet
Set ws = ThisWorkbook.Sheets("Sheet1")
' 헤더 입력
ws.Range("AK1").Value = ChrW(44288) & ChrW(47532) & ChrW(45824) & ChrW(49345) & ChrW(50668) & ChrW(48512) ' 관리대상여부
' 관리대상 판별
Dim i As Long
Dim txtTarget As String
txtTarget = ChrW(44288) & ChrW(47532) & ChrW(45824) & ChrW(49345) ' 관리대상
For i = 2 To 1471
If ws.Cells(i, 17).Value <= 2 And ws.Cells(i, 11).Value <= 2 And ws.Cells(i, 31).Value <= 2 Then
ws.Cells(i, 37).Value = txtTarget
Else
ws.Cells(i, 37).Value = ""
End If
Next i
MsgBox ChrW(44288) & ChrW(47532) & ChrW(45824) & ChrW(49345) & ChrW(50668) & ChrW(48512) & " " & ChrW(50676) & ChrW(51060) & " " & ChrW(52628) & ChrW(44032) & ChrW(46104) & ChrW(50632) & ChrW(49845) & ChrW(45768) & ChrW(45796) & "."
' "관리대상여부 열이 추가되었습니다."
End Sub
실행 결과
4. 차트 생성 (보너스)
ChatGPT에 이렇게 요청하세요.
엑셀 VBA 매크로를 작성해줘. # 데이터 - 시트명: Sheet1 - AJ1:AK6에 직급별 인원수 데이터가 있음 (AJ=직급, AK=인원수) # 작업 - 직급별 인원수 막대 차트를 생성 - 차트 제목: "직급별 인원 수" - 차트를 새 시트가 아닌 같은 시트에 삽입 - 완료 후 MsgBox로 완료 메시지 표시 # 주의 - 한글은 ChrW() 함수를 사용할 것
실행 결과
정리
| 핵심 내용 | 요약 |
|---|---|
| 자연어 요청 | ChatGPT에 데이터 위치, 원하는 결과, 조건을 설명하면 함수 생성 |
| 핵심 함수 | COUNTIF, AVERAGEIF, UNIQUE, IF, AND |
| 조건부 서식 | 수식 기반으로 특정 조건의 행 강조 |
| 필터링 | AutoFilter로 다중 조건 필터 적용 |
| VBA 자동화 | 반복 작업을 매크로로 한 번에 실행 |
| ChrW() | VBA에서 한글 문자열 처리 방법 |
3차시. 엑셀 파일 분리 및 취합 자동화
학습 목표
- VBA의 개념과 역할을 이해한다.
- VBA 편집기에서 모듈을 삽입하고 코드를 실행할 수 있다.
- ChatGPT에 VBA 코드를 효과적으로 요청하는 프롬프트를 작성할 수 있다.
- 부서별로 데이터를 분리하여 개별 파일로 저장할 수 있다.
- 분리된 파일을 하나의 파일로 취합할 수 있다.
- .xlsm 저장과 .bas 내보내기를 수행할 수 있다.
사용 데이터
- 파일 위치:
실습파일/HR데이터.xlsx - 규모: 1,470명, 35개 컬럼
- 부서(3개): 영업, 연구개발, 인사
- 분리 기준: E열(부서) 기준으로 3개 파일 생성
- 결과 저장 위치:
자동화실행결과/부서별분리/
1. VBA 개념과 역할
1.1 VBA란?
VBA(Visual Basic for Applications)는 Microsoft Office에 내장된 프로그래밍 언어입니다. 엑셀에서 반복적인 작업을 자동화하는 데 사용합니다.
| 항목 | 설명 |
|---|---|
| 정식 명칭 | Visual Basic for Applications |
| 용도 | 엑셀 작업 자동화 (매크로) |
| 실행 환경 | 엑셀 내장 VBA 편집기 |
| 파일 형식 | .xlsm (매크로 포함 통합 문서) |
1.2 VBA로 할 수 있는 일
- 셀에 값·수식 자동 입력
- 조건부 서식, 필터 자동 적용
- 파일 분리: 조건별로 데이터를 나누어 개별 파일로 저장
- 파일 취합: 여러 파일의 데이터를 하나로 합치기
- 차트 자동 생성
- 반복 보고서 자동 생성
1.3 왜 ChatGPT와 함께 사용하는가?
VBA 문법을 몰라도 ChatGPT에 원하는 작업을 자연어로 설명하면 코드를 생성해줍니다.
"나는 VBA를 모르지만, 원하는 작업을 설명할 수 있다" → ChatGPT가 코드를 작성해준다 → 복사하여 실행하면 된다
2. VBA 편집기 사용법
2.1 VBA 편집기 열기
- 엑셀에서 Alt + F11 을 누릅니다.
- VBA 편집기 창이 열립니다.
2.2 모듈 삽입
- VBA 편집기 상단 메뉴에서 삽입 > 모듈 을 클릭합니다.
- 오른쪽에 빈 코드 창이 나타납니다.
- 이 창에 VBA 코드를 붙여넣습니다.
실습 파일
HR데이터(VBA포함).xlsm에는 매크로가 기능별로 개별 모듈에 들어 있습니다.
예: M09_부서별파일분리, M10_파일취합 등직접 만들 때도 매크로 하나당 모듈 하나씩 분리하면 관리가 편합니다.
2.3 코드 실행
| 방법 | 설명 |
|---|---|
| F5 | 커서가 있는 매크로 실행 |
| F8 | 한 줄씩 실행 (디버깅) |
| 실행 버튼 | 툴바의 초록색 재생 버튼 클릭 |
| 매크로 대화상자 | 엑셀에서 Alt + F8 > 매크로 선택 > 실행 |
2.4 코드 실행 전 체크리스트
- HR데이터.xlsx 파일이 열려 있는가?
- 시트명이 "Sheet1"인가?
- 데이터가 1행(헤더) + 2~1471행(데이터)으로 되어 있는가?
- 코드를 올바른 모듈에 붙여넣었는가?
3. ChatGPT에 VBA 코드 요청하는 프롬프트 작성법
3.1 프롬프트 구조
VBA 코드를 요청할 때는 다음 4가지를 명확히 전달합니다.
# 데이터 정보 - 파일명, 시트명 - 데이터 범위 (행, 열) - 컬럼 설명 # 원하는 작업 - 구체적인 동작 설명 - 순서대로 나열 # 출력 결과 - 파일 저장 위치, 파일명 규칙 - 완료 메시지 # 주의사항 - 한글 처리 (ChrW 사용) - 오류 처리 - 기타 제약 조건
3.2 좋은 프롬프트 예시
엑셀 VBA 매크로를 작성해줘. # 데이터 - 파일명: HR데이터.xlsx - 시트명: Sheet1 - 데이터 범위: A1:AI1471 (1행은 헤더, 2~1471행은 데이터) - E열: 부서 (영업, 연구개발, 인사) # 작업 - E열(부서)의 고유값을 기준으로 데이터를 분리 - 각 부서별로 새 워크북을 생성 - 헤더(1행)를 각 파일에 포함 - 해당 부서의 데이터만 복사 - 원본 파일과 같은 폴더에 "부서명.xlsx"로 저장 - 저장 후 워크북 닫기 - 완료 후 MsgBox로 완료 메시지 표시 # 주의 - 한글은 ChrW() 함수를 사용할 것 - 이미 같은 이름의 파일이 있으면 덮어쓰기 - Application.ScreenUpdating을 False로 설정하여 속도 향상
3.3 나쁜 프롬프트 예시
엑셀 파일 분리하는 VBA 코드 써줘
4. 파일 분리 설계
4.1 파일 분리란?
하나의 큰 데이터 파일을 특정 기준(부서, 지역, 날짜 등)으로 나누어 개별 파일로 저장하는 작업입니다.
HR데이터.xlsx (1,470명) ├── 영업.xlsx (446명) ├── 연구개발.xlsx (961명) └── 인사.xlsx (63명)
4.2 설계 시 결정사항
| 항목 | 결정할 내용 | 이 실습의 설정 |
|---|---|---|
| 분리 기준 | 어떤 컬럼으로 나눌 것인가 | E열(부서) |
| 고유값 | 몇 개로 나뉘는가 | 3개 (영업, 연구개발, 인사) |
| 파일명 규칙 | 저장할 파일명 형식 | "부서명.xlsx" |
| 저장 위치 | 어디에 저장할 것인가 | 자동화실행결과/부서별분리/ 폴더 |
| 헤더 포함 | 각 파일에 헤더를 넣을 것인가 | 예 |
| 파일 형식 | xlsx, csv 등 | .xlsx |
5. 실습: 부서별 파일 분리
5.1 VBA 코드 요청 프롬프트
ChatGPT에 이렇게 요청하세요.
엑셀 VBA 매크로를 작성해줘. # 데이터 - 현재 열려 있는 워크북의 Sheet1 시트 - A1:AI1471 (1행: 헤더, 2~1471행: 데이터, 35개 컬럼) - E열(5번째 컬럼): 부서 (영업, 연구개발, 인사) # 작업 1. E열에서 고유한 부서 값을 추출 2. 각 부서별로 새 워크북 생성 3. 헤더(1행)를 새 워크북에 복사 4. 해당 부서의 데이터 행만 새 워크북에 복사 5. `자동화실행결과/부서별분리/` 폴더에 "부서명.xlsx"로 저장 6. 저장 후 새 워크북 닫기 7. 모든 부서 처리 후 MsgBox로 "부서별 파일 분리가 완료되었습니다." 메시지 표시 # 주의 - 한글은 ChrW() 함수를 사용할 것 - Application.ScreenUpdating = False로 속도 향상 - Application.DisplayAlerts = False로 덮어쓰기 경고 무시 - 작업 완료 후 ScreenUpdating, DisplayAlerts를 True로 복원
5.2 VBA 코드
Sub 부서별파일분리()
Dim wsSource As Worksheet
Set wsSource = ThisWorkbook.Sheets("Sheet1")
Dim savePath As String
' 결과를 자동화실행결과\부서별분리 폴더에 저장
Dim resultBase As String
resultBase = ThisWorkbook.Path & "\..\자동화실행결과\"
If Dir(resultBase, vbDirectory) = "" Then MkDir resultBase
savePath = resultBase & "부서별분리\"
If Dir(savePath, vbDirectory) = "" Then MkDir savePath
' 속도 향상 설정
Application.ScreenUpdating = False
Application.DisplayAlerts = False
' E열에서 고유 부서 추출
Dim deptCol As New Collection
Dim cell As Range
Dim deptName As String
On Error Resume Next
For Each cell In wsSource.Range("E2:E1471")
deptName = cell.Value
If deptName <> "" Then
deptCol.Add deptName, deptName
End If
Next cell
On Error GoTo 0
' 데이터 범위 설정
Dim lastCol As Long
lastCol = wsSource.Cells(1, wsSource.Columns.Count).End(xlToLeft).Column
' 각 부서별 파일 생성
Dim d As Long
For d = 1 To deptCol.Count
deptName = deptCol(d)
' 새 워크북 생성
Dim wbNew As Workbook
Set wbNew = Workbooks.Add(xlWBATWorksheet)
Dim wsNew As Worksheet
Set wsNew = wbNew.Sheets(1)
' 헤더 복사
wsSource.Range(wsSource.Cells(1, 1), wsSource.Cells(1, lastCol)).Copy wsNew.Range("A1")
' 해당 부서 데이터 복사
Dim targetRow As Long
targetRow = 2
Dim i As Long
For i = 2 To 1471
If wsSource.Cells(i, 5).Value = deptName Then
wsSource.Range(wsSource.Cells(i, 1), wsSource.Cells(i, lastCol)).Copy wsNew.Cells(targetRow, 1)
targetRow = targetRow + 1
End If
Next i
' 열 너비 자동 맞춤
wsNew.Cells.EntireColumn.AutoFit
' 파일 저장 및 닫기
wbNew.SaveAs savePath & deptName & ".xlsx", xlOpenXMLWorkbook
wbNew.Close SaveChanges:=False
Next d
' 설정 복원
Application.ScreenUpdating = True
Application.DisplayAlerts = True
MsgBox ChrW(48512) & ChrW(49436) & ChrW(48324) & " " & ChrW(54028) & ChrW(51068) & " " & ChrW(48516) & ChrW(47532) & ChrW(44032) & " " & ChrW(50756) & ChrW(47308) & ChrW(46104) & ChrW(50632) & ChrW(49845) & ChrW(45768) & ChrW(45796) & "."
' "부서별 파일 분리가 완료되었습니다."
End Sub
5.3 실행 결과
실행 후 자동화실행결과/부서별분리/ 폴더에 다음 3개 파일이 생성됩니다.
| 파일명 | 내용 |
|---|---|
| 영업.xlsx | 영업 부서 직원 데이터 (446명) |
| 연구개발.xlsx | 연구개발 부서 직원 데이터 (961명) |
| 인사.xlsx | 인사 부서 직원 데이터 (63명) |
6. 파일 취합 설계
6.1 파일 취합이란?
여러 개의 엑셀 파일에 흩어진 데이터를 하나의 파일로 합치는 작업입니다.
영업.xlsx (446명) 연구개발.xlsx (961명) → HR데이터_취합.xlsx (1,470명) 인사.xlsx (63명)
6.2 설계 시 결정사항
| 항목 | 결정할 내용 | 이 실습의 설정 |
|---|---|---|
| 대상 폴더 | 어느 폴더의 파일을 합칠 것인가 | 자동화실행결과/부서별분리/ 폴더 |
| 대상 파일 | 어떤 파일을 합칠 것인가 | *.xlsx (원본 제외) |
| 헤더 처리 | 첫 파일만 헤더 포함 | 예 |
| 결과 파일명 | 합친 결과 파일명 | HR데이터_취합.xlsx |
| 데이터 시트 | 어떤 시트의 데이터를 가져올 것인가 | 각 파일의 첫 번째 시트 |
7. 실습: 파일 취합
7.1 VBA 코드 요청 프롬프트
ChatGPT에 이렇게 요청하세요.
엑셀 VBA 매크로를 작성해줘. # 작업 환경 - 현재 열려 있는 워크북에서 실행 - `자동화실행결과/부서별분리/` 폴더에 있는 .xlsx 파일들을 취합 # 작업 1. `자동화실행결과/부서별분리/` 폴더에서 .xlsx 파일 목록을 가져옴 2. 새 워크북을 생성 3. 첫 번째 파일에서 헤더(1행) 포함하여 데이터 복사 4. 나머지 파일에서는 헤더 제외(2행부터) 데이터 복사 5. 모든 파일의 데이터를 순서대로 아래에 이어 붙이기 6. `자동화실행결과/` 폴더에 "HR데이터_취합.xlsx"로 저장 8. 완료 후 MsgBox로 "파일 취합이 완료되었습니다. (총 X건)" 메시지 표시 # 주의 - 한글은 ChrW() 함수를 사용할 것 - Application.ScreenUpdating = False로 속도 향상 - Dir 함수로 파일 목록 순회 - 각 파일을 열고 데이터 복사 후 닫기
7.2 VBA 코드
Sub 파일취합()
Dim folderPath As String
' 자동화실행결과\부서별분리 폴더에서 읽기
folderPath = ThisWorkbook.Path & "\..\자동화실행결과\부서별분리\"
Dim thisFileName As String
thisFileName = ThisWorkbook.Name
' 속도 향상 설정
Application.ScreenUpdating = False
Application.DisplayAlerts = False
' 새 워크북 생성
Dim wbResult As Workbook
Set wbResult = Workbooks.Add(xlWBATWorksheet)
Dim wsResult As Worksheet
Set wsResult = wbResult.Sheets(1)
Dim nextRow As Long
nextRow = 1
Dim isFirst As Boolean
isFirst = True
Dim totalCount As Long
totalCount = 0
' 폴더 내 xlsx 파일 순회
Dim fileName As String
fileName = Dir(folderPath & "*.xlsx")
Do While fileName <> ""
' 현재 파일과 결과 파일 제외
If fileName <> thisFileName And fileName <> "HR" & ChrW(45936) & ChrW(51060) & ChrW(53552) & "_" & ChrW(52712) & ChrW(54633) & ".xlsx" Then
' 파일 열기
Dim wbSource As Workbook
Set wbSource = Workbooks.Open(folderPath & fileName, ReadOnly:=True)
Dim wsSource As Worksheet
Set wsSource = wbSource.Sheets(1)
' 데이터 범위 확인
Dim lastRow As Long
Dim lastCol As Long
lastRow = wsSource.Cells(wsSource.Rows.Count, 1).End(xlUp).Row
lastCol = wsSource.Cells(1, wsSource.Columns.Count).End(xlToLeft).Column
If isFirst Then
' 첫 파일: 헤더 포함 복사
wsSource.Range(wsSource.Cells(1, 1), wsSource.Cells(lastRow, lastCol)).Copy wsResult.Cells(nextRow, 1)
nextRow = nextRow + lastRow
totalCount = totalCount + (lastRow - 1)
isFirst = False
Else
' 이후 파일: 헤더 제외 (2행부터)
If lastRow >= 2 Then
wsSource.Range(wsSource.Cells(2, 1), wsSource.Cells(lastRow, lastCol)).Copy wsResult.Cells(nextRow, 1)
nextRow = nextRow + (lastRow - 1)
totalCount = totalCount + (lastRow - 1)
End If
End If
wbSource.Close SaveChanges:=False
End If
fileName = Dir()
Loop
' 열 너비 자동 맞춤
wsResult.Cells.EntireColumn.AutoFit
' 결과 파일 저장
Dim resultName As String
resultName = "HR" & ChrW(45936) & ChrW(51060) & ChrW(53552) & "_" & ChrW(52712) & ChrW(54633) & ".xlsx"
Dim resultPath As String
resultPath = ThisWorkbook.Path & "\..\자동화실행결과\"
wbResult.SaveAs resultPath & resultName, xlOpenXMLWorkbook
wbResult.Close SaveChanges:=False
' 설정 복원
Application.ScreenUpdating = True
Application.DisplayAlerts = True
MsgBox ChrW(54028) & ChrW(51068) & " " & ChrW(52712) & ChrW(54633) & ChrW(51060) & " " & ChrW(50756) & ChrW(47308) & ChrW(46104) & ChrW(50632) & ChrW(49845) & ChrW(45768) & ChrW(45796) & "." & vbCrLf & "(" & ChrW(52509) & " " & totalCount & ChrW(44148) & ")"
' "파일 취합이 완료되었습니다." & vbCrLf & "(총 X건)"
End Sub
7.3 실행 결과
실행 후 자동화실행결과/ 폴더에 HR데이터_취합.xlsx 파일이 생성되며, 3개 부서 파일의 데이터가 하나로 합쳐집니다.
8. .xlsm 저장과 .bas 내보내기
8.1 .xlsm으로 저장하기
VBA 매크로가 포함된 파일은 반드시 .xlsm 형식으로 저장해야 합니다. 일반 .xlsx로 저장하면 매크로가 삭제됩니다.
저장 방법
- 파일 > 다른 이름으로 저장
- 파일 형식에서 Excel 매크로 사용 통합 문서 (*.xlsm) 선택
- 저장
8.2 .bas로 내보내기
VBA 코드를 .bas 파일로 내보내면 다른 워크북에서 재사용할 수 있습니다.
내보내기 방법
- Alt + F11로 VBA 편집기 열기
- 왼쪽 프로젝트 탐색기에서 내보낼 모듈을 마우스 오른쪽 클릭
- 파일 내보내기 선택
- 저장 위치와 파일명 지정 후 저장
가져오기 방법
- VBA 편집기에서 파일 > 파일 가져오기
- .bas 파일 선택
- 모듈이 프로젝트에 추가됨
9. 실무 오류와 디버깅
9.1 자주 발생하는 오류
| 오류 | 원인 | 해결 방법 |
|---|---|---|
| 실행 시간 오류 '9': 첨자 사용이 잘못되었습니다 | 시트명이 코드와 다름 | 시트명 확인 후 코드 수정 |
| 실행 시간 오류 '1004' | 파일 경로 오류 또는 권한 부족 | 경로 확인, 파일이 열려있지 않은지 확인 |
| 실행 시간 오류 '13': 형식이 일치하지 않습니다 | 데이터 타입 불일치 | 셀 값의 데이터 타입 확인 |
| 매크로를 사용할 수 없습니다 | 매크로 보안 설정 | 파일 > 옵션 > 보안 센터 > 매크로 설정 |
| 한글 깨짐 | ChrW() 미사용 | 한글 문자열을 ChrW()로 변환 |
9.2 디버깅 방법
F8 단계 실행
- VBA 편집기에서 매크로 첫 줄에 커서를 놓습니다.
- F8을 눌러 한 줄씩 실행합니다.
- 노란색으로 강조된 줄이 현재 실행할 줄입니다.
- 변수 위에 마우스를 올리면 현재 값을 확인할 수 있습니다.
중단점 설정
- 코드 왼쪽 여백을 클릭하면 빨간 점(중단점)이 생깁니다.
- F5로 실행하면 중단점에서 멈춥니다.
- F8로 한 줄씩 진행하며 값을 확인합니다.
Debug.Print 활용
Debug.Print "현재 부서: " & deptName Debug.Print "행 번호: " & i
결과는 VBA 편집기의 직접 실행 창(Ctrl + G)에서 확인합니다.
9.3 ChatGPT로 오류 해결하기
오류가 발생하면 ChatGPT에 다음 정보를 전달합니다.
엑셀 VBA 매크로 실행 중 오류가 발생했어.
# 오류 메시지
실행 시간 오류 '9': 첨자 사용이 잘못되었습니다.
# 오류 발생 위치
Set ws = ThisWorkbook.Sheets("Sheet1")
# 상황
- 시트 이름이 "데이터"로 되어 있음
- 코드에서는 "Sheet1"을 참조하고 있음
이 오류를 해결해줘.
10. 실습 과제: 전체 프로세스 수행
아래 순서대로 전체 프로세스를 직접 수행해보세요.
단계 1: 환경 준비
실습파일/HR데이터(VBA포함).xlsm파일 열기 (또는 HR데이터.xlsx를 열고 직접 VBA 작성)- Alt + F11로 VBA 편집기 열기
- 삽입 > 모듈로 새 모듈 추가 (매크로별로 모듈을 분리하는 것을 권장)
단계 2: 파일 분리 실행
부서별파일분리매크로 코드 붙여넣기- F5로 실행
자동화실행결과/부서별분리/폴더에 영업.xlsx, 연구개발.xlsx, 인사.xlsx 생성 확인
단계 3: 분리 결과 확인
- 각 파일을 열어 데이터 확인
- 헤더가 올바르게 포함되어 있는지 확인
- 각 파일의 데이터가 해당 부서만 포함하는지 확인
단계 4: 파일 취합 실행
파일취합매크로 코드 붙여넣기- F5로 실행
자동화실행결과/폴더에 HR데이터_취합.xlsx 생성 확인- 총 데이터 건수가 1,470건인지 확인
단계 5: 파일 저장 및 내보내기
- HR데이터.xlsx를 HR데이터.xlsm으로 다른 이름으로 저장
- VBA 편집기에서 모듈을 마우스 오른쪽 클릭 > 파일 내보내기 > .bas로 저장
단계 6: 응용 (선택)
- E열(부서) 대신 P열(직무)로 분리 기준을 변경해보기
- ChatGPT에 코드 수정을 요청하여 9개 직무별 파일 분리 수행
정리
| 핵심 내용 | 요약 |
|---|---|
| VBA 편집기 | Alt + F11로 열고, 삽입 > 모듈 > 코드 붙여넣기 > F5 실행 |
| 프롬프트 작성 | 데이터 정보 + 작업 내용 + 출력 형식 + 주의사항 |
| 파일 분리 | 고유값 기준으로 데이터를 나누어 개별 파일로 저장 |
| 파일 취합 | 여러 파일의 데이터를 하나의 파일로 합치기 |
| .xlsm 저장 | 매크로 포함 파일은 반드시 .xlsm으로 저장 |
| .bas 내보내기 | VBA 코드를 파일로 내보내 재사용 |
| 디버깅 | F8 단계 실행, Debug.Print, ChatGPT에 오류 문의 |
4차시. 파워포인트 자동화
학습 목표
- VBA가 엑셀뿐 아니라 파워포인트에서도 동일한 원리로 동작함을 이해한다
- 템플릿 기반 문서 생성(플레이스홀더 치환) 방식을 익힌다
- 엑셀 데이터와 PPT 템플릿을 연결하여 반복 슬라이드를 자동 생성한다
- 생성된 슬라이드를 개별 PDF로 저장하는 자동화를 구현한다
1. VBA는 PPT에서도 동작한다
엑셀에서 사용한 VBA와 동일한 방식으로 파워포인트에서도 매크로를 작성하고 실행할 수 있습니다. 파워포인트 VBA 편집기도 Alt + F11로 열 수 있으며, 모듈을 삽입하고 코드를 붙여 넣는 과정이 엑셀과 같습니다.
파워포인트 VBA 편집기 여는 방법
- 파워포인트를 열고
Alt + F11을 누른다 - [삽입] → [모듈]을 클릭한다
- 코드를 붙여 넣고
F5로 실행한다
.pptm(매크로 사용 프레젠테이션)으로 저장해야 VBA 코드가 보존됩니다.2. 템플릿 기반 문서 생성
템플릿 기반 자동화의 핵심은 플레이스홀더 치환입니다.
- 템플릿 슬라이드에
{{이름}},{{부서명}},{{사번}}등의 플레이스홀더를 미리 배치한다 - VBA 코드가 엑셀에서 데이터를 읽어 플레이스홀더를 실제 값으로 바꾼다
- 한 장의 템플릿으로 N장의 개별 문서를 생성할 수 있다
플레이스홀더 작성 규칙
- 중괄호 두 개로 감싼다:
{{항목명}} - 템플릿의 텍스트 상자 안에 직접 입력한다
- 오타, 띄어쓰기에 주의한다 (코드에서 정확히 일치해야 치환됨)
3. 엑셀 데이터와 PPT 템플릿 연결
[엑셀 명단] [PPT 템플릿] [결과물]
이름|사번|부서명 → {{이름}}, {{사번}} → 개인별 슬라이드
20명 데이터 1장 양식 20장 슬라이드
- 엑셀 파일의 각 행이 하나의 슬라이드에 대응된다
- PPT VBA에서 엑셀 파일을 열어 데이터를 읽는다
- 1행은 헤더이므로 2행부터 데이터를 읽는다
4. 반복 슬라이드 생성
1장의 양식 슬라이드를 N장으로 복제하고, 각 슬라이드에 서로 다른 데이터를 주입합니다.
동작 순서
- 템플릿의 첫 번째 슬라이드를 복제한다
- 복제된 슬라이드의 모든 텍스트 상자를 순회한다
- 플레이스홀더를 해당 행의 데이터로 치환한다
- 다음 행에 대해 반복한다
5. PDF 저장 자동화
생성된 슬라이드를 개별 PDF로 저장합니다.
- 각 슬라이드를 임시 프레젠테이션으로 복사한 뒤 PDF로 내보낸다
- 파일명 규칙:
부서명_이름_수료증.pdf
6. 자동화에 적합한 PPT 업무
| 업무 유형 | 예시 |
|---|---|
| 수료증/표창장 | 교육 수료증, 우수사원 표창장 |
| 명세서/견적서 | 급여명세서, 거래처 견적서 |
| 제안서/보고서 표지 | 고객별 제안서 표지 |
| 회의자료 | 부서별 실적 요약 슬라이드 |
실습 파일
| 파일 위치 | 설명 |
|---|---|
실습파일/실습_교육수료증-명단.xlsx | 수료자 20명 데이터 (시트명: 수료자명단) |
실습파일/실습_교육수료증-템플릿.pptm | 수료증 양식 (플레이스홀더 + VBA 코드 포함) |
.pptm 파일에는 VBA 코드가 이미 모듈로 삽입되어 있습니다. Alt+F8로 바로 실행할 수 있습니다.자동화실행결과/수료증생성결과_PDF/ 폴더에 저장됩니다.수료증 템플릿 디자인
- 슬라이드 크기: A4 세로 (210mm × 297mm)
- 디자인: 네이비(
#1B2A4A) + 골드(#C9A962) 이중 테두리, 코너 장식 - 수여처: 한국GPT협회
수료증 명단 구조 (실습_교육수료증-명단.xlsx)
| 열 | 항목 | 예시 |
|---|---|---|
| A | 이름 | 김민수 |
| B | 사번 | EMP-2024-001 |
| C | 부서명 | 경영지원부 |
| D | 교육명 | MS 오피스 VBA 자동화 |
| E | 수료증번호 | CERT-2026-001 |
| F | 이메일 | minsu.kim@example.com |
부서 목록: 경영지원부, 인사부, 영업부, 연구개발부, 마케팅부 (각 4명씩 총 20명)
실습 1: 수료증 슬라이드 자동 생성
엑셀 명단을 읽어 20명분의 수료증 슬라이드를 자동으로 생성합니다. 1페이지(템플릿 양식)는 그대로 유지하고, 2페이지부터 개인별 수료증이 추가됩니다.
ChatGPT 프롬프트
파워포인트 VBA 코드를 작성해줘.
- 현재 열려 있는 PPT의 첫 번째 슬라이드가 수료증 템플릿이야
- 같은 폴더에 있는 "실습_교육수료증-명단.xlsx" 파일을 읽어야 해
- 시트 이름은 "수료자명단"이야
- 엑셀 구조: A열=이름, B열=사번, C열=부서명, D열=교육명, E열=수료증번호, F열=이메일
- 1행은 헤더이고 2행부터 데이터야 (총 20명)
- 1페이지(템플릿 양식)는 그대로 유지해줘
- 2페이지부터 인원수만큼 슬라이드를 복제해서 개인 정보를 반영해줘
- 각 슬라이드에서 아래 플레이스홀더를 치환해:
- {{이름}} → A열 값
- {{사번}} → B열 값
- {{부서명}} → C열 값
- {{교육명}} → D열 값
- {{수료증번호}} → E열 값
- 완료되면 MsgBox로 생성된 슬라이드 수를 알려줘
VBA 코드: 수료증 슬라이드 자동 생성 (Mod1_슬라이드생성)
Sub 수료증_슬라이드_일괄생성()
'=============================================================
' [모듈1] 엑셀 명단을 읽어 2페이지부터 수료증 슬라이드 생성
' 1페이지 템플릿 양식은 그대로 유지
'=============================================================
Dim prs As Presentation
Dim templateSlide As Slide
Dim newSlide As Slide
Dim xlApp As Object
Dim xlWb As Object
Dim xlWs As Object
Dim lastRow As Long
Dim i As Long
Dim basePath As String
Dim excelPath As String
Dim shp As Shape
Dim shpKey As String
Dim origTexts As Object
Dim totalCount As Long
Set prs = ActivePresentation
basePath = prs.Path & "\"
excelPath = basePath & "실습_교육수료증-명단.xlsx"
' --- 엑셀 파일 존재 확인 ---
If Dir(excelPath) = "" Then
MsgBox "명단 파일을 찾을 수 없습니다." & vbCrLf & excelPath, vbExclamation, "오류"
Exit Sub
End If
' --- 기존 복사 슬라이드 제거 (템플릿 1장만 유지) ---
Do While prs.Slides.Count > 1
prs.Slides(prs.Slides.Count).Delete
Loop
' --- 엑셀 열기 ---
Set xlApp = CreateObject("Excel.Application")
xlApp.Visible = False
xlApp.DisplayAlerts = False
Set xlWb = xlApp.Workbooks.Open(excelPath, ReadOnly:=True)
Set xlWs = xlWb.Sheets("수료자명단")
' --- 데이터 행 수 파악 ---
lastRow = xlWs.Cells(xlWs.Rows.Count, 1).End(-4162).Row
If lastRow < 2 Then
MsgBox "명단 데이터가 없습니다.", vbExclamation, "오류"
GoTo CleanUp
End If
' --- 템플릿 슬라이드 원본 텍스트 백업 ---
Set origTexts = CreateObject("Scripting.Dictionary")
Set templateSlide = prs.Slides(1)
For Each shp In templateSlide.Shapes
If shp.HasTextFrame Then
shpKey = shp.Name
origTexts.Add shpKey, shp.TextFrame.TextRange.Text
End If
Next shp
totalCount = lastRow - 1
' === 인원수만큼 슬라이드 복사 (2페이지부터) ===
For i = 2 To lastRow
templateSlide.Copy
DoEvents
prs.Slides.Paste prs.Slides.Count + 1
DoEvents
Set newSlide = prs.Slides(prs.Slides.Count)
' 복사된 슬라이드의 텍스트를 원본으로 복원 후 치환
For Each shp In newSlide.Shapes
If shp.HasTextFrame Then
shpKey = shp.Name
If origTexts.Exists(shpKey) Then
shp.TextFrame.TextRange.Text = origTexts(shpKey)
End If
End If
Next shp
' 데이터 삽입
Call 슬라이드에_데이터삽입(newSlide, _
Trim(CStr(xlWs.Cells(i, 1).Value)), _
Trim(CStr(xlWs.Cells(i, 2).Value)), _
Trim(CStr(xlWs.Cells(i, 3).Value)), _
Trim(CStr(xlWs.Cells(i, 4).Value)), _
Trim(CStr(xlWs.Cells(i, 5).Value)))
Next i
CleanUp:
xlWb.Close False
xlApp.Quit
Set xlWs = Nothing
Set xlWb = Nothing
Set xlApp = Nothing
MsgBox "수료증 슬라이드 " & totalCount & "장 생성 완료!" & vbCrLf & _
"(1페이지: 템플릿 / 2~" & (totalCount + 1) & "페이지: 개인별 수료증)", vbInformation, "완료"
End Sub
Private Sub 슬라이드에_데이터삽입(ByRef sld As Slide, _
ByVal sName As String, ByVal sEmpNo As String, ByVal sDept As String, _
ByVal sEdu As String, ByVal sCertNo As String)
'----------------------------------------------
' 슬라이드 내 플레이스홀더를 실제 데이터로 치환
'----------------------------------------------
Dim shp As Shape
Dim fullText As String
For Each shp In sld.Shapes
If shp.HasTextFrame Then
fullText = shp.TextFrame.TextRange.Text
If InStr(fullText, "{{") > 0 Then
fullText = Replace(fullText, "{{이름}}", sName)
fullText = Replace(fullText, "{{사번}}", sEmpNo)
fullText = Replace(fullText, "{{부서명}}", sDept)
fullText = Replace(fullText, "{{교육명}}", sEdu)
fullText = Replace(fullText, "{{수료증번호}}", sCertNo)
shp.TextFrame.TextRange.Text = fullText
End If
End If
Next shp
End Sub
ChrW() 변환 없이 "{{이름}}" 형태로 작성하면 코드 가독성이 훨씬 좋습니다. 파워포인트 VBA는 유니코드를 기본 지원하므로 한글을 직접 사용해도 문제없습니다.실행 결과
수료증 템플릿을 기반으로 1페이지(템플릿)는 유지되고, 2~21페이지에 20명분의 개별 수료증 슬라이드가 자동 생성됩니다.

실습 2: 개인별 PDF 저장
개인별 슬라이드를 생성한 뒤, 각각을 새 PPTX에 복사하여 개별 PDF 파일로 저장합니다.
ChatGPT 프롬프트
파워포인트 VBA 코드를 작성해줘. - 같은 폴더에 있는 "실습_교육수료증-명단.xlsx"의 "수료자명단" 시트를 읽어야 해 - 엑셀 구조: A열=이름, B열=사번, C열=부서명, D열=교육명, E열=수료증번호 - 1행은 헤더이고 2행부터 데이터야 (총 20명) - 코드 순서: 1. 실습1처럼 인원수만큼 개인별 슬라이드를 2페이지부터 만든다 2. 새 PPTX를 열고 개인 수료증 슬라이드 1장만 복사한다 (마스터 디자인, 레이아웃 유지) 3. 그 PPTX를 PDF로 저장하고 닫는다 4. 인원수만큼 반복한다 - 저장 폴더: "자동화실행결과\수료증생성결과_PDF" (같은 폴더 하위) - 파일명 규칙: "수료증_이름_수료증번호.pdf" - 완료 후 개인 슬라이드를 삭제하고 1페이지 템플릿만 남겨줘 - 완료되면 MsgBox로 생성된 PDF 수와 저장 위치를 알려줘
VBA 코드: 개인별 PDF 저장 (Mod2_PDF생성)
Sub 수료증_개인별_PDF생성()
'=============================================================
' [모듈2] 개인별 수료증 PDF 파일 생성
' 1) 모듈1처럼 개인별 슬라이드를 모두 만든다
' 2) 새 PPTX를 열고 개인 슬라이드 1장만 복사
' 3) PDF로 저장하고 PPTX를 닫는다
' 4) 인원수만큼 반복
' 저장: 자동화실행결과\수료증생성결과_PDF\수료증_이름_번호.pdf
'=============================================================
Dim prs As Presentation
Dim xlApp As Object
Dim xlWb As Object
Dim xlWs As Object
Dim lastRow As Long
Dim i As Long
Dim basePath As String
Dim excelPath As String
Dim pdfFolder As String
Dim pdfPath As String
Dim fso As Object
Dim shp As Shape
Dim shpKey As String
Dim origTexts As Object
Dim totalCount As Long
Dim successCount As Long
Dim sName As String
Dim sEmpNo As String
Dim sDept As String
Dim sEdu As String
Dim sCertNo As String
Dim fullText As String
Dim parentFolder As String
Dim tempPrs As Presentation
Dim newSlide As Slide
Set prs = ActivePresentation
basePath = prs.Path & "\"
excelPath = basePath & "실습_교육수료증-명단.xlsx"
pdfFolder = basePath & "자동화실행결과\수료증생성결과_PDF"
' --- 엑셀 파일 존재 확인 ---
If Dir(excelPath) = "" Then
MsgBox "명단 파일을 찾을 수 없습니다." & vbCrLf & excelPath, vbExclamation, "오류"
Exit Sub
End If
' --- PDF 폴더 생성 (상위 폴더 포함) ---
Set fso = CreateObject("Scripting.FileSystemObject")
parentFolder = basePath & "자동화실행결과"
If Not fso.FolderExists(parentFolder) Then
fso.CreateFolder parentFolder
End If
If Not fso.FolderExists(pdfFolder) Then
fso.CreateFolder pdfFolder
End If
' --- 엑셀 열기 ---
Set xlApp = CreateObject("Excel.Application")
xlApp.Visible = False
xlApp.DisplayAlerts = False
Set xlWb = xlApp.Workbooks.Open(excelPath, ReadOnly:=True)
Set xlWs = xlWb.Sheets("수료자명단")
lastRow = xlWs.Cells(xlWs.Rows.Count, 1).End(-4162).Row
If lastRow < 2 Then
MsgBox "명단 데이터가 없습니다.", vbExclamation, "오류"
GoTo CleanUp
End If
' --- 기존 복사 슬라이드 제거 (템플릿 1장만 유지) ---
Do While prs.Slides.Count > 1
prs.Slides(prs.Slides.Count).Delete
Loop
' --- 템플릿 슬라이드 원본 텍스트 백업 ---
Set origTexts = CreateObject("Scripting.Dictionary")
For Each shp In prs.Slides(1).Shapes
If shp.HasTextFrame Then
shpKey = shp.Name
origTexts.Add shpKey, shp.TextFrame.TextRange.Text
End If
Next shp
totalCount = lastRow - 1
successCount = 0
' ========================================
' STEP 1: 인원수만큼 슬라이드 복사 & 데이터 삽입 (2페이지부터)
' ========================================
For i = 2 To lastRow
prs.Slides(1).Copy
DoEvents
prs.Slides.Paste prs.Slides.Count + 1
DoEvents
Set newSlide = prs.Slides(prs.Slides.Count)
' 텍스트 원본 복원
For Each shp In newSlide.Shapes
If shp.HasTextFrame Then
shpKey = shp.Name
If origTexts.Exists(shpKey) Then
shp.TextFrame.TextRange.Text = origTexts(shpKey)
End If
End If
Next shp
' 데이터 삽입
sName = Trim(CStr(xlWs.Cells(i, 1).Value))
sEmpNo = Trim(CStr(xlWs.Cells(i, 2).Value))
sDept = Trim(CStr(xlWs.Cells(i, 3).Value))
sEdu = Trim(CStr(xlWs.Cells(i, 4).Value))
sCertNo = Trim(CStr(xlWs.Cells(i, 5).Value))
For Each shp In newSlide.Shapes
If shp.HasTextFrame Then
fullText = shp.TextFrame.TextRange.Text
If InStr(fullText, "{{") > 0 Then
fullText = Replace(fullText, "{{이름}}", sName)
fullText = Replace(fullText, "{{사번}}", sEmpNo)
fullText = Replace(fullText, "{{부서명}}", sDept)
fullText = Replace(fullText, "{{교육명}}", sEdu)
fullText = Replace(fullText, "{{수료증번호}}", sCertNo)
shp.TextFrame.TextRange.Text = fullText
End If
End If
Next shp
Next i
' ========================================
' STEP 2: 개인별 슬라이드를 새 PPTX로 복사 → PDF 저장
' ========================================
For i = 2 To prs.Slides.Count
' 슬라이드에서 이름, 수료증번호 추출 (파일명용)
sName = Trim(CStr(xlWs.Cells(i, 1).Value))
sCertNo = Trim(CStr(xlWs.Cells(i, 5).Value))
' 새 프레젠테이션 생성
Set tempPrs = Application.Presentations.Add(msoFalse)
' 슬라이드 크기 맞추기 (A4 세로)
tempPrs.PageSetup.SlideWidth = prs.PageSetup.SlideWidth
tempPrs.PageSetup.SlideHeight = prs.PageSetup.SlideHeight
' 원본 슬라이드 복사 → 새 프레젠테이션에 붙여넣기
prs.Slides(i).Copy
DoEvents
tempPrs.Slides.Paste
DoEvents
' 마스터/레이아웃 디자인 유지
tempPrs.Slides(1).Design = prs.Slides(i).Design
tempPrs.Slides(1).CustomLayout = prs.Slides(i).CustomLayout
' PDF 저장
pdfPath = pdfFolder & "\" & "수료증_" & sName & "_" & sCertNo & ".pdf"
tempPrs.ExportAsFixedFormat _
Path:=pdfPath, _
FixedFormatType:=2, _
Intent:=1, _
FrameSlides:=0
' 임시 PPTX 닫기 (저장 안 함)
tempPrs.Close
Set tempPrs = Nothing
successCount = successCount + 1
DoEvents
Next i
' --- 생성된 개인 슬라이드 제거 (템플릿만 남기기) ---
Do While prs.Slides.Count > 1
prs.Slides(prs.Slides.Count).Delete
Loop
CleanUp:
xlWb.Close False
xlApp.Quit
Set xlWs = Nothing
Set xlWb = Nothing
Set xlApp = Nothing
Set fso = Nothing
MsgBox "개인별 PDF " & successCount & "개 생성 완료!" & vbCrLf & _
"저장 위치: " & pdfFolder, vbInformation, "완료"
End Sub
실행 결과
자동화실행결과/수료증생성결과_PDF/ 폴더 안에 20개의 개별 PDF 파일이 저장됩니다.

자동화실행결과/수료증생성결과_PDF/ ├── 수료증_김민수_CERT-2026-001.pdf ├── 수료증_이지은_CERT-2026-002.pdf ├── 수료증_박준혁_CERT-2026-003.pdf ├── 수료증_최수정_CERT-2026-004.pdf ├── 수료증_정우진_CERT-2026-005.pdf ├── 수료증_한서연_CERT-2026-006.pdf └── ... (총 20개)
실습 요약
| 단계 | 작업 내용 | 결과 |
|---|---|---|
| 1 | 실습_교육수료증-템플릿.pptm 열기 | 플레이스홀더가 포함된 수료증 양식 + VBA 코드 확인 |
| 2 | Alt+F8 → 수료증_슬라이드_일괄생성 실행 | 1페이지(템플릿) + 20장의 개별 수료증 슬라이드 생성 |
| 3 | Alt+F8 → 수료증_개인별_PDF생성 실행 | 20개의 개별 PDF 파일 저장 |
주의사항
- 파일 형식: VBA 코드가 포함된 파일은 반드시
.pptm(매크로 사용 프레젠테이션)으로 저장해야 합니다..pptx로 저장하면 VBA 코드가 삭제됩니다. - 매크로 허용:
.pptm파일을 열 때 "매크로 사용" 또는 "콘텐츠 사용"을 클릭해야 VBA가 실행됩니다. - 플레이스홀더 오타 주의:
{{이름}}과{{ 이름 }}은 다릅니다. 템플릿과 코드의 플레이스홀더가 정확히 일치해야 합니다. - 파일 경로: PPT 파일과 엑셀 파일(
실습_교육수료증-명단.xlsx)이 같은 폴더에 있어야 합니다. - 엑셀이 이미 열려 있는 경우: 다른 프로세스에서 엑셀 파일을 열고 있으면 오류가 발생할 수 있습니다. 먼저 닫아주세요.
- PowerPoint VBA vs Excel VBA 차이점:
Application.StatusBar는 엑셀 전용입니다. 파워포인트에서는 사용할 수 없으므로 주의하세요.
실습 과제
실습_교육수료증-템플릿.pptm을 열고 플레이스홀더({{이름}},{{사번}}등)를 확인하세요.Alt+F8→수료증_슬라이드_일괄생성을 실행하여 2~21페이지에 20장의 슬라이드를 만드세요.Alt+F8→수료증_개인별_PDF생성을 실행하여자동화실행결과/수료증생성결과_PDF/폴더에 개별 PDF를 저장하세요.- 생성된 PDF를 열어 내용이 올바르게 치환되었는지 확인하세요.
- (선택)
Alt+F11로 VBA 편집기를 열어Mod1_슬라이드생성,Mod2_PDF생성모듈의 코드를 확인하세요.
5차시. MS 워드 자동화
학습 목표
- 워드 문서 자동화의 개념과 활용 사례를 이해한다
- AI가 생성한 글을 워드 양식으로 옮기는 방법을 익힌다
- Word VBA로 서식, 표, 문구 치환을 자동화한다
- AI 생성 초안을 업무 문서 수준으로 완성하는 흐름을 실습한다
1. 워드 문서 자동화 개념
워드 자동화는 파워포인트 자동화와 같은 원리입니다:
"템플릿 + 데이터 + 치환/삽입 + 서식 일괄 적용"
워드 자동화가 효과적인 업무
- 정기 보고서 (월간/분기 보고서)
- 공문, 내부 문서
- 계약서, 제안서
- HR 분석 보고서
2. AI 생성 글을 워드 양식으로 옮기기
ChatGPT에게 보고서 초안을 요청하고, Word VBA로 서식을 자동 적용하는 흐름입니다.
[1단계] ChatGPT로 보고서 초안 생성 (텍스트)
↓
[2단계] 워드 문서에 텍스트 삽입
↓
[3단계] VBA로 서식 자동 적용 (제목, 본문, 표, 여백 등)
↓
[4단계] PDF로 저장
3. Word VBA 기본
Word VBA도 Alt + F11로 편집기를 열고, 모듈을 삽입하여 사용합니다.
기본 동작
- 문서 열기:
Documents.Open - 텍스트 삽입:
Selection.TypeText - 새 문단:
Selection.TypeParagraph - 서식 제어:
Selection.Style,Selection.Font,Selection.ParagraphFormat
4. 서식 자동화
VBA로 제어할 수 있는 주요 서식:
| 서식 항목 | VBA 속성 | 예시 |
|---|---|---|
| 제목 스타일 | Selection.Style | "제목 1", "제목 2" |
| 폰트 | Selection.Font.Name | "맑은 고딕" |
| 글자 크기 | Selection.Font.Size | 11, 14, 18 |
| 줄간격 | ParagraphFormat.LineSpacingRule | 1.5줄 |
| 여백 | PageSetup.TopMargin | 2cm |
5. 표와 그래프 편집 자동화
- 표 삽입:
ActiveDocument.Tables.Add - 표 너비 정렬:
Table.AutoFitBehavior - 머리글 음영:
Cell.Shading.BackgroundPatternColor - 테두리:
Table.Borders
6. 문구 일괄 치환
Find.Execute를 사용하여 문서 전체에서 특정 문구를 일괄 치환할 수 있습니다.
활용 사례
- 날짜 일괄 변경:
2025년 1월→2025년 2월 - 부서명 변경:
인사팀→인사부 - 회사명 변경
실습 흐름
- ChatGPT로 HR 분석 보고서 초안을 생성한다
- Word VBA로 보고서 서식을 자동 적용한다
- 표를 삽입하고 서식을 적용한다
- 문구를 일괄 치환한다
- PDF로 저장한다
실습 1: 워드 문서 생성 + 스타일 적용
ChatGPT로 생성한 HR 보고서 초안을 워드 문서로 만들고 스타일을 자동 적용합니다.
ChatGPT 프롬프트 (보고서 초안 생성)
HR 인사 분석 보고서 초안을 작성해줘. - 보고서 제목: "2025년 상반기 인사 현황 분석 보고서" - 구성: 1. 개요 (3~4줄) 2. 인력 현황 (부서별 인원, 직급별 분포) 3. 퇴사 현황 분석 (퇴사율, 주요 원인) 4. 교육 이수 현황 5. 종합 의견 및 제언 - 각 섹션은 ##로 구분해줘 - 분량: A4 2~3페이지 분량
ChatGPT 프롬프트 (VBA 코드 생성)
Word VBA 코드를 작성해줘. - 새 워드 문서를 생성하고 HR 보고서 내용을 삽입해줘 - 페이지 설정: 여백 상하좌우 2cm - 보고서 제목: "2025년 상반기 인사 현황 분석 보고서" - 스타일: 제목 1, 가운데 정렬, 맑은 고딕 18pt, 굵게 - 섹션 제목 5개: 개요, 인력 현황, 퇴사 현황 분석, 교육 이수 현황, 종합 의견 및 제언 - 스타일: 제목 2, 맑은 고딕 14pt, 굵게 - 각 섹션 본문: 맑은 고딕 11pt, 줄간격 1.5줄 - 본문 내용은 각 섹션에 3~4줄씩 예시 텍스트를 넣어줘 - 한글 문자열은 ChrW()를 사용해줘 - 완료되면 MsgBox로 알려줘
VBA 코드: 워드 문서 생성 + 스타일 적용
Sub CreateHRReport()
Dim doc As Document
Dim sel As Selection
' 새 문서 생성
Set doc = Documents.Add
' 페이지 설정 - 여백 2cm (1cm = 28.35pt)
With doc.PageSetup
.TopMargin = CentimetersToPoints(2)
.BottomMargin = CentimetersToPoints(2)
.LeftMargin = CentimetersToPoints(2)
.RightMargin = CentimetersToPoints(2)
End With
Set sel = Selection
' === 보고서 제목 ===
' "2025년 상반기 인사 현황 분석 보고서"
sel.Style = ActiveDocument.Styles(ChrW(51228) & ChrW(47785) & " 1") ' "제목 1"
sel.Font.Name = ChrW(47567) & ChrW(51008) & " " & ChrW(44256) & ChrW(46357) ' "맑은 고딕"
sel.Font.Size = 18
sel.Font.Bold = True
sel.ParagraphFormat.Alignment = wdAlignParagraphCenter
sel.TypeText "2025" & ChrW(45380) & " " & ChrW(49345) & ChrW(48152) & ChrW(44592) & " " & _
ChrW(51064) & ChrW(49324) & " " & ChrW(54788) & ChrW(54889) & " " & _
ChrW(48516) & ChrW(49437) & " " & ChrW(48372) & ChrW(44256) & ChrW(49436)
' "2025년 상반기 인사 현황 분석 보고서"
sel.TypeParagraph
sel.TypeParagraph
' === 1. 개요 ===
sel.Style = ActiveDocument.Styles(ChrW(51228) & ChrW(47785) & " 2") ' "제목 2"
sel.Font.Name = ChrW(47567) & ChrW(51008) & " " & ChrW(44256) & ChrW(46357)
sel.Font.Size = 14
sel.Font.Bold = True
sel.ParagraphFormat.Alignment = wdAlignParagraphLeft
sel.TypeText "1. " & ChrW(44060) & ChrW(50836) ' "1. 개요"
sel.TypeParagraph
' 개요 본문
sel.Style = ActiveDocument.Styles(ChrW(48376) & ChrW(47928)) ' "본문"
sel.Font.Name = ChrW(47567) & ChrW(51008) & " " & ChrW(44256) & ChrW(46357)
sel.Font.Size = 11
sel.Font.Bold = False
sel.ParagraphFormat.LineSpacingRule = wdLineSpace1pt5
sel.ParagraphFormat.Alignment = wdAlignParagraphLeft
sel.TypeText ChrW(48376) & " " & ChrW(48372) & ChrW(44256) & ChrW(49436) & ChrW(45716) & " 2025" & ChrW(45380) & " " & _
ChrW(49345) & ChrW(48152) & ChrW(44592) & " " & ChrW(51064) & ChrW(49324) & " " & _
ChrW(54788) & ChrW(54889) & ChrW(51012) & " " & ChrW(51333) & ChrW(54633) & ChrW(51201) & ChrW(51004) & ChrW(47196) & " " & _
ChrW(48516) & ChrW(49437) & ChrW(54616) & ChrW(50668) & " " & ChrW(51064) & ChrW(47141) & " " & _
ChrW(50868) & ChrW(50689) & ChrW(44284) & " " & ChrW(53748) & ChrW(49324) & " " & ChrW(46041) & ChrW(54693) & ", " & _
ChrW(44368) & ChrW(50977) & " " & ChrW(51060) & ChrW(49688) & " " & ChrW(54788) & ChrW(54889) & ChrW(51012) & " " & _
ChrW(45812) & ChrW(44256) & " " & ChrW(51080) & ChrW(49845) & ChrW(45768) & ChrW(45796) & "."
' "본 보고서는 2025년 상반기 인사 현황을 종합적으로 분석하여 인력 운영과 퇴사 동향, 교육 이수 현황을 담고 있습니다."
sel.TypeParagraph
sel.TypeParagraph
' === 2. 인력 현황 ===
sel.Style = ActiveDocument.Styles(ChrW(51228) & ChrW(47785) & " 2")
sel.Font.Name = ChrW(47567) & ChrW(51008) & " " & ChrW(44256) & ChrW(46357)
sel.Font.Size = 14
sel.Font.Bold = True
sel.TypeText "2. " & ChrW(51064) & ChrW(47141) & " " & ChrW(54788) & ChrW(54889) ' "2. 인력 현황"
sel.TypeParagraph
sel.Style = ActiveDocument.Styles(ChrW(48376) & ChrW(47928))
sel.Font.Name = ChrW(47567) & ChrW(51008) & " " & ChrW(44256) & ChrW(46357)
sel.Font.Size = 11
sel.Font.Bold = False
sel.ParagraphFormat.LineSpacingRule = wdLineSpace1pt5
sel.TypeText ChrW(51204) & ChrW(52404) & " " & ChrW(51064) & ChrW(50896) & " 1,470" & ChrW(47749) & ChrW(51004) & " " & _
ChrW(45824) & ChrW(54644) & ", " & ChrW(48512) & ChrW(49436) & ChrW(48324) & " " & _
ChrW(51064) & ChrW(50896) & " " & ChrW(48516) & ChrW(54252) & ChrW(45716) & " " & _
ChrW(50500) & ChrW(47000) & ChrW(50752) & " " & ChrW(44057) & ChrW(49845) & ChrW(45768) & ChrW(45796) & "."
' "전체 인원 1,470명에 대해, 부서별 인원 분포는 아래와 같습니다."
sel.TypeParagraph
sel.TypeParagraph
' === 3. 퇴사 현황 분석 ===
sel.Style = ActiveDocument.Styles(ChrW(51228) & ChrW(47785) & " 2")
sel.Font.Name = ChrW(47567) & ChrW(51008) & " " & ChrW(44256) & ChrW(46357)
sel.Font.Size = 14
sel.Font.Bold = True
sel.TypeText "3. " & ChrW(53748) & ChrW(49324) & " " & ChrW(54788) & ChrW(54889) & " " & ChrW(48516) & ChrW(49437)
' "3. 퇴사 현황 분석"
sel.TypeParagraph
sel.Style = ActiveDocument.Styles(ChrW(48376) & ChrW(47928))
sel.Font.Name = ChrW(47567) & ChrW(51008) & " " & ChrW(44256) & ChrW(46357)
sel.Font.Size = 11
sel.Font.Bold = False
sel.ParagraphFormat.LineSpacingRule = wdLineSpace1pt5
sel.TypeText ChrW(49345) & ChrW(48152) & ChrW(44592) & " " & ChrW(53748) & ChrW(49324) & ChrW(50984) & ChrW(51008) & " 16.1%" & _
ChrW(47196) & " " & ChrW(51204) & ChrW(45380) & " " & ChrW(46041) & ChrW(44592) & " " & _
ChrW(45824) & ChrW(48708) & " " & ChrW(49548) & ChrW(54253) & " " & ChrW(44048) & ChrW(49548) & ChrW(54616) & ChrW(50688) & ChrW(49845) & ChrW(45768) & ChrW(45796) & "."
' "상반기 퇴사율은 16.1%로 전년 동기 대비 소폭 감소하였습니다."
sel.TypeParagraph
sel.TypeParagraph
' === 4. 교육 이수 현황 ===
sel.Style = ActiveDocument.Styles(ChrW(51228) & ChrW(47785) & " 2")
sel.Font.Name = ChrW(47567) & ChrW(51008) & " " & ChrW(44256) & ChrW(46357)
sel.Font.Size = 14
sel.Font.Bold = True
sel.TypeText "4. " & ChrW(44368) & ChrW(50977) & " " & ChrW(51060) & ChrW(49688) & " " & ChrW(54788) & ChrW(54889)
' "4. 교육 이수 현황"
sel.TypeParagraph
sel.Style = ActiveDocument.Styles(ChrW(48376) & ChrW(47928))
sel.Font.Name = ChrW(47567) & ChrW(51008) & " " & ChrW(44256) & ChrW(46357)
sel.Font.Size = 11
sel.Font.Bold = False
sel.ParagraphFormat.LineSpacingRule = wdLineSpace1pt5
sel.TypeText ChrW(49345) & ChrW(48152) & ChrW(44592) & " " & ChrW(54596) & ChrW(49688) & " " & ChrW(44368) & ChrW(50977) & " " & _
ChrW(51060) & ChrW(49688) & ChrW(50984) & ChrW(51008) & " 78.5%" & ChrW(51060) & ChrW(47728) & ", " & _
ChrW(47785) & ChrW(54364) & " " & ChrW(44368) & ChrW(50977) & " " & ChrW(51060) & ChrW(49688) & ChrW(50984) & " " & _
ChrW(51228) & ChrW(44256) & ChrW(47484) & " " & ChrW(54596) & ChrW(50836) & ChrW(54633) & ChrW(45768) & ChrW(45796) & "."
' "상반기 필수 교육 이수율은 78.5%이며, 목표 교육 이수율 제고를 위해 필요합니다."
sel.TypeParagraph
sel.TypeParagraph
' === 5. 종합 의견 및 제언 ===
sel.Style = ActiveDocument.Styles(ChrW(51228) & ChrW(47785) & " 2")
sel.Font.Name = ChrW(47567) & ChrW(51008) & " " & ChrW(44256) & ChrW(46357)
sel.Font.Size = 14
sel.Font.Bold = True
sel.TypeText "5. " & ChrW(51333) & ChrW(54633) & " " & ChrW(51032) & ChrW(44204) & " " & _
ChrW(48143) & " " & ChrW(51228) & ChrW(50616)
' "5. 종합 의견 및 제언"
sel.TypeParagraph
sel.Style = ActiveDocument.Styles(ChrW(48376) & ChrW(47928))
sel.Font.Name = ChrW(47567) & ChrW(51008) & " " & ChrW(44256) & ChrW(46357)
sel.Font.Size = 11
sel.Font.Bold = False
sel.ParagraphFormat.LineSpacingRule = wdLineSpace1pt5
sel.TypeText ChrW(51064) & ChrW(47141) & " " & ChrW(50868) & ChrW(50689) & ChrW(51032) & " " & _
ChrW(54952) & ChrW(50984) & ChrW(49457) & ChrW(51012) & " " & ChrW(45458) & ChrW(51060) & ChrW(44256) & " " & _
ChrW(53748) & ChrW(49324) & ChrW(50984) & " " & ChrW(44048) & ChrW(49548) & ChrW(47484) & " " & _
ChrW(50948) & ChrW(54644) & " " & ChrW(51648) & ChrW(49549) & ChrW(51201) & ChrW(51064) & " " & _
ChrW(51064) & ChrW(49324) & " " & ChrW(44288) & ChrW(47532) & ChrW(44032) & " " & _
ChrW(54596) & ChrW(50836) & ChrW(54633) & ChrW(45768) & ChrW(45796) & "."
' "인력 운영의 효율성을 높이고 퇴사율 감소를 위해 지속적인 인사 관리가 필요합니다."
sel.TypeParagraph
' 완료 메시지
MsgBox ChrW(48372) & ChrW(44256) & ChrW(49436) & " " & ChrW(47928) & ChrW(49436) & " " & _
ChrW(49373) & ChrW(49457) & " " & ChrW(48143) & " " & ChrW(49436) & ChrW(49885) & " " & _
ChrW(51201) & ChrW(50857) & " " & ChrW(50756) & ChrW(47308) & "!", vbInformation
' "보고서 문서 생성 및 서식 적용 완료!"
End Sub
실습 2: 표 삽입 + 서식 적용
보고서에 부서별 인원 현황 표를 삽입하고 서식을 자동 적용합니다.
ChatGPT 프롬프트
Word VBA 코드를 작성해줘. - 현재 열려 있는 워드 문서의 커서 위치에 표를 삽입해줘 - 표 구조: 6행 3열 - 1행(머리글): 부서명, 인원수, 비율 - 2행: 경영지원부, 63명, 4.3% - 3행: 인사부, 52명, 3.5% - 4행: 영업부, 446명, 30.3% - 5행: 연구개발부, 961명, 65.4% - 6행(합계): 합계, 1,470명, 100% - 서식: - 표 너비: 문서 폭에 자동 맞춤 - 머리글 행: 배경색 진한 파랑(RGB 0,51,102), 글자 흰색, 굵게, 가운데 정렬 - 합계 행: 굵게, 배경색 연한 회색 - 모든 셀: 맑은 고딕 10pt, 세로 가운데 정렬 - 테두리: 모든 테두리 표시 - 한글 문자열은 ChrW()를 사용해줘
VBA 코드: 표 삽입 + 서식
Sub InsertDepartmentTable()
Dim doc As Document
Dim tbl As Table
Dim rng As Range
Set doc = ActiveDocument
Set rng = Selection.Range
' 6행 3열 표 삽입
Set tbl = doc.Tables.Add(rng, 6, 3)
' 표 너비 자동 맞춤
tbl.AutoFitBehavior wdAutoFitWindow
' 머리글 행 데이터 입력
tbl.Cell(1, 1).Range.Text = ChrW(48512) & ChrW(49436) & ChrW(47749) ' "부서명"
tbl.Cell(1, 2).Range.Text = ChrW(51064) & ChrW(50896) & ChrW(49688) ' "인원수"
tbl.Cell(1, 3).Range.Text = ChrW(48708) & ChrW(50984) ' "비율"
' 데이터 입력
tbl.Cell(2, 1).Range.Text = ChrW(44221) & ChrW(50689) & ChrW(51648) & ChrW(50896) & ChrW(48512) ' "경영지원부"
tbl.Cell(2, 2).Range.Text = "63" & ChrW(47749) ' "63명"
tbl.Cell(2, 3).Range.Text = "4.3%"
tbl.Cell(3, 1).Range.Text = ChrW(51064) & ChrW(49324) & ChrW(48512) ' "인사부"
tbl.Cell(3, 2).Range.Text = "52" & ChrW(47749)
tbl.Cell(3, 3).Range.Text = "3.5%"
tbl.Cell(4, 1).Range.Text = ChrW(50689) & ChrW(50629) & ChrW(48512) ' "영업부"
tbl.Cell(4, 2).Range.Text = "446" & ChrW(47749)
tbl.Cell(4, 3).Range.Text = "30.3%"
tbl.Cell(5, 1).Range.Text = ChrW(50672) & ChrW(44396) & ChrW(44060) & ChrW(48156) & ChrW(48512) ' "연구개발부"
tbl.Cell(5, 2).Range.Text = "961" & ChrW(47749)
tbl.Cell(5, 3).Range.Text = "65.4%"
tbl.Cell(6, 1).Range.Text = ChrW(54633) & ChrW(44228) ' "합계"
tbl.Cell(6, 2).Range.Text = "1,470" & ChrW(47749)
tbl.Cell(6, 3).Range.Text = "100%"
' === 서식 적용 ===
' 전체 표 폰트 설정
tbl.Range.Font.Name = ChrW(47567) & ChrW(51008) & " " & ChrW(44256) & ChrW(46357) ' "맑은 고딕"
tbl.Range.Font.Size = 10
' 전체 셀 세로 가운데 정렬
Dim c As Cell
For Each c In tbl.Range.Cells
c.VerticalAlignment = wdCellAlignVerticalCenter
c.Range.ParagraphFormat.Alignment = wdAlignParagraphCenter
Next c
' 머리글 행 서식 (1행)
Dim i As Long
For i = 1 To 3
With tbl.Cell(1, i)
.Shading.BackgroundPatternColor = RGB(0, 51, 102)
.Range.Font.Color = RGB(255, 255, 255)
.Range.Font.Bold = True
End With
Next i
' 합계 행 서식 (6행)
For i = 1 To 3
With tbl.Cell(6, i)
.Shading.BackgroundPatternColor = RGB(217, 217, 217)
.Range.Font.Bold = True
End With
Next i
' 테두리 설정
With tbl.Borders
.InsideLineStyle = wdLineStyleSingle
.OutsideLineStyle = wdLineStyleSingle
.InsideLineWidth = wdLineWidth050pt
.OutsideLineWidth = wdLineWidth050pt
End With
MsgBox ChrW(54364) & " " & ChrW(49341) & ChrW(51077) & " " & ChrW(48143) & " " & _
ChrW(49436) & ChrW(49885) & " " & ChrW(51201) & ChrW(50857) & " " & _
ChrW(50756) & ChrW(47308) & "!", vbInformation
' "표 삽입 및 서식 적용 완료!"
End Sub
실습 3: 문구 일괄 치환
보고서 내 특정 문구를 일괄 치환합니다.
ChatGPT 프롬프트
Word VBA 코드를 작성해줘. - 현재 열려 있는 워드 문서에서 문구를 일괄 치환해줘 - 치환 목록: 1. "2025년 상반기" → "2025년 하반기" 2. "인사팀" → "인사부" 3. "작성일: 2025.06.30" → "작성일: 2025.12.31" - 각 치환 후 몇 건이 변경되었는지 카운트해줘 - 한글 문자열은 ChrW()를 사용해줘 - 완료되면 MsgBox로 치환 결과를 알려줘
VBA 코드: 문구 일괄 치환
Sub ReplaceTextInDocument()
Dim doc As Document
Dim totalCount As Long
Dim msg As String
Set doc = ActiveDocument
totalCount = 0
msg = ""
' --- 치환 1: "2025년 상반기" → "2025년 하반기" ---
Dim findText1 As String
Dim replaceText1 As String
Dim count1 As Long
findText1 = "2025" & ChrW(45380) & " " & ChrW(49345) & ChrW(48152) & ChrW(44592)
' "2025년 상반기"
replaceText1 = "2025" & ChrW(45380) & " " & ChrW(54616) & ChrW(48152) & ChrW(44592)
' "2025년 하반기"
count1 = CountAndReplace(doc, findText1, replaceText1)
msg = msg & findText1 & " -> " & replaceText1 & ": " & count1 & ChrW(44148) & vbCrLf
totalCount = totalCount + count1
' --- 치환 2: "인사팀" → "인사부" ---
Dim findText2 As String
Dim replaceText2 As String
Dim count2 As Long
findText2 = ChrW(51064) & ChrW(49324) & ChrW(54016) ' "인사팀"
replaceText2 = ChrW(51064) & ChrW(49324) & ChrW(48512) ' "인사부"
count2 = CountAndReplace(doc, findText2, replaceText2)
msg = msg & findText2 & " -> " & replaceText2 & ": " & count2 & ChrW(44148) & vbCrLf
totalCount = totalCount + count2
' --- 치환 3: "작성일: 2025.06.30" → "작성일: 2025.12.31" ---
Dim findText3 As String
Dim replaceText3 As String
Dim count3 As Long
findText3 = ChrW(51089) & ChrW(49457) & ChrW(51068) & ": 2025.06.30" ' "작성일: 2025.06.30"
replaceText3 = ChrW(51089) & ChrW(49457) & ChrW(51068) & ": 2025.12.31" ' "작성일: 2025.12.31"
count3 = CountAndReplace(doc, findText3, replaceText3)
msg = msg & findText3 & " -> " & replaceText3 & ": " & count3 & ChrW(44148) & vbCrLf
totalCount = totalCount + count3
' 결과 메시지
msg = ChrW(47928) & ChrW(44396) & " " & ChrW(51068) & ChrW(44292) & " " & _
ChrW(52824) & ChrW(54872) & " " & ChrW(50756) & ChrW(47308) & "!" & vbCrLf & vbCrLf & msg & _
vbCrLf & ChrW(52509) & " " & ChrW(52824) & ChrW(54872) & " " & ChrW(44148) & ChrW(49688) & ": " & totalCount & ChrW(44148)
' "문구 일괄 치환 완료!" & 상세내역 & "총 치환 건수: N건"
MsgBox msg, vbInformation
End Sub
Function CountAndReplace(doc As Document, findStr As String, replStr As String) As Long
Dim cnt As Long
cnt = 0
' 먼저 횟수 세기
With doc.Content.Find
.ClearFormatting
.Text = findStr
.Forward = True
.Wrap = wdFindStop
.MatchCase = False
.MatchWholeWord = False
Do While .Execute
cnt = cnt + 1
Loop
End With
' 실제 치환
With doc.Content.Find
.ClearFormatting
.Replacement.ClearFormatting
.Text = findStr
.Replacement.Text = replStr
.Forward = True
.Wrap = wdFindContinue
.MatchCase = False
.MatchWholeWord = False
.Execute Replace:=wdReplaceAll
End With
CountAndReplace = cnt
End Function
실습 요약
| 단계 | 작업 내용 | 결과 |
|---|---|---|
| 1 | ChatGPT로 보고서 초안 생성 | 5개 섹션의 HR 보고서 텍스트 |
| 2 | Word VBA로 문서 생성 + 스타일 적용 | 제목/본문 서식이 적용된 워드 문서 |
| 3 | 표 삽입 + 서식 적용 | 부서별 인원 현황 표 (머리글 음영, 테두리) |
| 4 | 문구 일괄 치환 | 날짜, 부서명 등 일괄 변경 |
| 5 | PDF 저장 | 최종 보고서를 PDF로 내보내기 |
주의사항
- 스타일 이름 확인: 워드의 스타일 이름은 언어 설정에 따라 다를 수 있습니다. 한글 워드에서는 "제목 1", "본문" 등을 사용합니다.
- 커서 위치: 표 삽입 시 커서가 원하는 위치에 있는지 확인하세요.
- 문구 치환 주의: 치환할 문구가 정확히 일치해야 합니다. 띄어쓰기나 특수문자 차이에 유의하세요.
- 저장 형식: VBA 코드를 포함하려면
.docm(매크로 사용 문서)으로 저장하세요.
실습 과제
- ChatGPT에게 HR 분석 보고서 초안을 요청하세요.
- Word VBA로 보고서를 생성하고 서식을 적용하세요.
- 부서별 인원 현황 표를 삽입하고 서식을 적용하세요.
- 문구 일괄 치환 기능을 테스트하세요.
- 완성된 보고서를 PDF로 저장하세요. (파일 → 다른 이름으로 저장 → PDF 선택)
6차시. VBA를 이용한 이메일 발송 자동화
학습 목표
- 네이버 메일 SMTP를 이용한 이메일 자동 발송 원리를 이해한다
- 엑셀 명단과 첨부 파일을 매칭하여 자동 발송하는 VBA를 작성한다
- 제목과 본문을 개인화하여 발송하는 방법을 익힌다
- 보안 원칙(비밀번호 관리)을 이해하고 실천한다
1. 이메일 자동 발송 개념
엑셀 VBA에서 CDO(Collaboration Data Objects)를 사용하면 SMTP 서버를 통해 이메일을 자동으로 발송할 수 있습니다.
[엑셀 명단] [첨부 파일 폴더] [발송] 부서명 | 이메일 → 부서명.xlsx → SMTP 서버 경영지원부 | a@b.com 경영지원부_인사현황.xlsx → 수신자에게 전달
자동화 흐름
- 엑셀에서 수신자 명단(부서명, 이메일)을 읽는다
- 부서명에 해당하는 첨부 파일을 찾는다
- 제목과 본문을 개인화한다
- SMTP 서버를 통해 이메일을 발송한다
- 발송 결과를 엑셀에 기록한다
2. 네이버 POP3/SMTP 활성화
네이버 메일에서 SMTP 발송을 사용하려면 먼저 설정을 활성화해야 합니다.
설정 방법
- 네이버 메일 접속 → 환경설정 (톱니바퀴 아이콘)
- [POP3/IMAP 설정] 탭 클릭
- POP3/SMTP 사용 → "사용함"으로 변경
- 저장
네이버 SMTP 서버 정보
| 항목 | 값 |
|---|---|
| SMTP 서버 | smtp.naver.com |
| 포트 | 465 (SSL) |
| 인증 방식 | SSL/TLS |
| 아이디 | 네이버 아이디 |
| 비밀번호 | 애플리케이션 비밀번호 |
3. 애플리케이션 비밀번호 생성
2단계 인증을 사용하는 경우, 일반 비밀번호 대신 애플리케이션 비밀번호를 생성해야 합니다.
생성 방법
- 네이버 → 내 정보 → 보안 설정
- 2단계 인증 → 관리
- 애플리케이션 비밀번호 생성
- 생성된 비밀번호를 메모 (한 번만 표시됨)
4. 이메일 VBA 프롬프트 작성법
ChatGPT에게 이메일 발송 VBA를 요청할 때 아래 정보를 포함합니다:
- SMTP 서버 주소와 포트
- 인증 방식 (SSL)
- 발신자 이메일
- 수신자 명단 구조 (어떤 열에 무엇이 있는지)
- 첨부 파일 위치와 파일명 규칙
- 제목/본문 템플릿
- 비밀번호 입력 방식 (InputBox)
5. 수신자 명단 구조
| 열 | 항목 | 예시 |
|---|---|---|
| A | 부서명 | 경영지원부 |
| B | 이메일 | manager@company.com |
| C | 발송상태 | (VBA가 자동 기록) |
6. 제목/본문 개인화
제목 예시: [인사부] 2025년 상반기 인사현황 자료 송부
본문 예시
경영지원부 담당자님께, 안녕하세요. 인사부입니다. 2025년 상반기 인사현황 자료를 송부드립니다. 첨부 파일을 확인해 주시기 바랍니다. 감사합니다. 인사부 드림
7. 보안 원칙
| 규칙 | 설명 |
|---|---|
| 코드에 비밀번호 직접 입력 금지 | InputBox로 실행 시 입력받도록 한다 |
| 프롬프트에 비밀번호 입력 금지 | ChatGPT 대화에 실제 비밀번호를 넣지 않는다 |
| 실습 후 비밀번호 삭제 | 애플리케이션 비밀번호를 삭제하거나 변경한다 |
| 발송 테스트는 본인 주소로 | 모든 수신 이메일을 본인 주소로 바꿔서 테스트한다 |
실습 흐름
- 네이버 SMTP 설정을 활성화한다
- 애플리케이션 비밀번호를 생성한다
- 엑셀에 수신자 명단을 작성한다
- VBA 이메일 발송 코드를 작성한다
- 본인 주소로 발송 테스트한다
- 발송 완료 후 인증 정보를 제거한다
실습 1: 이메일 발송 VBA
부서별 파일을 각 담당자에게 자동 첨부 발송합니다.
ChatGPT 프롬프트
엑셀 VBA 코드를 작성해줘. CDO.Message를 이용한 이메일 자동 발송이야. - SMTP 서버: smtp.naver.com, 포트 465, SSL 사용 - 발신자 이메일: InputBox로 입력받아 - 비밀번호: InputBox로 입력받아 (코드에 직접 넣지 않아) - 수신자 명단은 현재 시트에 있어: - A열: 부서명 (2행부터 데이터) - B열: 이메일 - C열: 발송상태 (빈칸이면 발송, "완료"면 건너뜀) - 첨부 파일: 엑셀 파일과 같은 폴더의 "부서별파일" 하위 폴더에서 "부서명_인사현황.xlsx" 파일을 찾아 - 이메일 제목: "[부서명] 2025년 상반기 인사현황 자료 송부" - 이메일 본문: HTML 형식으로 아래 내용 포함 - "부서명 담당자님께," (줄바꿈) - "안녕하세요. 인사부입니다." (줄바꿈) - "2025년 상반기 인사현황 자료를 송부드립니다." (줄바꿈) - "첨부 파일을 확인해 주시기 바랍니다." (줄바꿈 2번) - "감사합니다." (줄바꿈) - "인사부 드림" - 발송 성공하면 C열에 "완료"와 발송 시각 기록 - 발송 실패하면 C열에 "실패: 에러메시지" 기록 - 한글 문자열은 ChrW()를 사용해줘 - 각 발송 사이에 2초 대기 (서버 부하 방지) - 완료되면 MsgBox로 발송 건수를 알려줘
VBA 코드: 이메일 발송 (CDO.Message)
Sub SendEmailWithAttachment()
Dim ws As Worksheet
Dim lastRow As Long
Dim i As Long
Dim sendCount As Long
Dim failCount As Long
Dim senderEmail As String
Dim senderPassword As String
Dim basePath As String
Dim attachFolderPath As String
Set ws = ActiveSheet
lastRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row
' 발신자 정보 입력받기 (코드에 직접 넣지 않음)
senderEmail = InputBox(ChrW(48156) & ChrW(49888) & ChrW(51088) & " " & ChrW(51060) & ChrW(47700) & ChrW(51068) & ChrW(51012) & " " & ChrW(51077) & ChrW(47141) & ChrW(54616) & ChrW(49464) & ChrW(50836) & ":" & vbCrLf & _
ChrW(50696) & ") userid@naver.com", _
ChrW(48156) & ChrW(49888) & ChrW(51088) & " " & ChrW(51060) & ChrW(47700) & ChrW(51068))
' "발신자 이메일을 입력하세요: 예) userid@naver.com"
If senderEmail = "" Then Exit Sub
senderPassword = InputBox(ChrW(50528) & ChrW(54540) & ChrW(47532) & ChrW(52992) & ChrW(51060) & ChrW(49496) & " " & _
ChrW(48708) & ChrW(48128) & ChrW(48264) & ChrW(54840) & ChrW(47484) & " " & ChrW(51077) & ChrW(47141) & ChrW(54616) & ChrW(49464) & ChrW(50836) & ":", _
ChrW(48708) & ChrW(48128) & ChrW(48264) & ChrW(54840))
' "애플리케이션 비밀번호를 입력하세요:", "비밀번호"
If senderPassword = "" Then Exit Sub
' 첨부 파일 폴더 경로
basePath = ThisWorkbook.Path
' "부서별파일" 폴더
attachFolderPath = basePath & "\" & ChrW(48512) & ChrW(49436) & ChrW(48324) & ChrW(54028) & ChrW(51068) & "\"
sendCount = 0
failCount = 0
For i = 2 To lastRow
' C열이 비어있는 경우에만 발송
If Trim(ws.Cells(i, 3).Value) = "" Then
Dim deptName As String
Dim recipientEmail As String
Dim attachFilePath As String
Dim emailSubject As String
Dim emailBody As String
deptName = ws.Cells(i, 1).Value ' 부서명
recipientEmail = ws.Cells(i, 2).Value ' 이메일
' 첨부 파일 경로: 부서명_인사현황.xlsx
' 인사현황 = ChrW(51064) & ChrW(49324) & ChrW(54788) & ChrW(54889)
attachFilePath = attachFolderPath & deptName & "_" & _
ChrW(51064) & ChrW(49324) & ChrW(54788) & ChrW(54889) & ".xlsx"
' 이메일 제목: "[부서명] 2025년 상반기 인사현황 자료 송부"
emailSubject = "[" & deptName & "] 2025" & ChrW(45380) & " " & _
ChrW(49345) & ChrW(48152) & ChrW(44592) & " " & _
ChrW(51064) & ChrW(49324) & ChrW(54788) & ChrW(54889) & " " & _
ChrW(51088) & ChrW(47308) & " " & ChrW(49569) & ChrW(48512)
' "[부서명] 2025년 상반기 인사현황 자료 송부"
' 이메일 본문 (HTML)
emailBody = "<html><body style='font-family: Malgun Gothic, sans-serif; font-size: 10pt;'>"
emailBody = emailBody & "<p>" & deptName & " " & _
ChrW(45812) & ChrW(45817) & ChrW(51088) & ChrW(45784) & ChrW(44760) & ",</p>"
emailBody = emailBody & "<p>" & ChrW(50504) & ChrW(45397) & ChrW(54616) & ChrW(49464) & ChrW(50836) & ". " & _
ChrW(51064) & ChrW(49324) & ChrW(48512) & ChrW(51077) & ChrW(45768) & ChrW(45796) & ".</p>"
emailBody = emailBody & "<p>2025" & ChrW(45380) & " " & _
ChrW(49345) & ChrW(48152) & ChrW(44592) & " " & _
ChrW(51064) & ChrW(49324) & ChrW(54788) & ChrW(54889) & " " & _
ChrW(51088) & ChrW(47308) & ChrW(47484) & " " & _
ChrW(49569) & ChrW(48512) & ChrW(46300) & ChrW(47549) & ChrW(45768) & ChrW(45796) & ".</p>"
emailBody = emailBody & "<p>" & ChrW(52392) & ChrW(48512) & " " & _
ChrW(54028) & ChrW(51068) & ChrW(51012) & " " & _
ChrW(54869) & ChrW(51064) & ChrW(54644) & " " & _
ChrW(51452) & ChrW(49884) & ChrW(44592) & " " & _
ChrW(48148) & ChrW(46989) & ChrW(45768) & ChrW(45796) & ".</p>"
emailBody = emailBody & "<br>"
emailBody = emailBody & "<p>" & ChrW(44048) & ChrW(49324) & ChrW(54633) & ChrW(45768) & ChrW(45796) & ".</p>"
emailBody = emailBody & "<p>" & ChrW(51064) & ChrW(49324) & ChrW(48512) & " " & _
ChrW(46300) & ChrW(47548) & "</p>"
emailBody = emailBody & "</body></html>"
' CDO.Message로 이메일 발송
On Error Resume Next
Dim cdoMsg As Object
Dim cdoConfig As Object
Set cdoMsg = CreateObject("CDO.Message")
Set cdoConfig = CreateObject("CDO.Configuration")
With cdoConfig.Fields
.Item("http://schemas.microsoft.com/cdo/configuration/sendusing") = 2
.Item("http://schemas.microsoft.com/cdo/configuration/smtpserver") = "smtp.naver.com"
.Item("http://schemas.microsoft.com/cdo/configuration/smtpserverport") = 465
.Item("http://schemas.microsoft.com/cdo/configuration/smtpusessl") = True
.Item("http://schemas.microsoft.com/cdo/configuration/smtpauthenticate") = 1
.Item("http://schemas.microsoft.com/cdo/configuration/sendusername") = senderEmail
.Item("http://schemas.microsoft.com/cdo/configuration/sendpassword") = senderPassword
.Item("http://schemas.microsoft.com/cdo/configuration/smtpconnectiontimeout") = 30
.Update
End With
With cdoMsg
Set .Configuration = cdoConfig
.From = senderEmail
.To = recipientEmail
.Subject = emailSubject
.HTMLBody = emailBody
' 첨부 파일 추가 (파일이 존재하는 경우)
If Dir(attachFilePath) <> "" Then
.AddAttachment attachFilePath
End If
.Send
End With
If Err.Number = 0 Then
' 발송 성공: C열에 "완료" + 시각 기록
ws.Cells(i, 3).Value = ChrW(50756) & ChrW(47308) & " (" & Format(Now, "yyyy-mm-dd hh:nn:ss") & ")"
' "완료 (2025-01-15 14:30:00)"
sendCount = sendCount + 1
Else
' 발송 실패: C열에 에러 메시지 기록
ws.Cells(i, 3).Value = ChrW(49892) & ChrW(54056) & ": " & Err.Description
' "실패: 에러메시지"
failCount = failCount + 1
Err.Clear
End If
On Error GoTo 0
Set cdoMsg = Nothing
Set cdoConfig = Nothing
' 서버 부하 방지를 위한 2초 대기
Application.Wait Now + TimeValue("00:00:02")
End If
Next i
' 비밀번호 변수 초기화 (메모리에서 제거)
senderPassword = ""
' 완료 메시지
MsgBox ChrW(51060) & ChrW(47700) & ChrW(51068) & " " & ChrW(48156) & ChrW(49569) & " " & _
ChrW(50756) & ChrW(47308) & "!" & vbCrLf & vbCrLf & _
ChrW(49457) & ChrW(44277) & ": " & sendCount & ChrW(44148) & vbCrLf & _
ChrW(49892) & ChrW(54056) & ": " & failCount & ChrW(44148), vbInformation
' "이메일 발송 완료!" & "성공: N건" & "실패: N건"
End Sub
실습 2: 발송 상태 기록
발송 결과를 확인하고 미발송 건을 재발송하는 코드입니다.
ChatGPT 프롬프트
엑셀 VBA 코드를 작성해줘. - 현재 시트의 C열에 이메일 발송 상태가 기록되어 있어 - A열: 부서명, B열: 이메일, C열: 발송상태 - C열 값이 "완료"로 시작하는 행 수, "실패"로 시작하는 행 수, 빈 행 수를 세줘 - 결과를 MsgBox로 보여줘: - 전체: N건 - 발송 완료: N건 - 발송 실패: N건 - 미발송: N건 - 한글 문자열은 ChrW()를 사용해줘
VBA 코드: 발송 상태 확인
Sub CheckSendStatus()
Dim ws As Worksheet
Dim lastRow As Long
Dim i As Long
Dim totalCount As Long
Dim successCount As Long
Dim failCount As Long
Dim pendingCount As Long
Set ws = ActiveSheet
lastRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row
totalCount = 0
successCount = 0
failCount = 0
pendingCount = 0
For i = 2 To lastRow
totalCount = totalCount + 1
Dim statusVal As String
statusVal = Trim(ws.Cells(i, 3).Value)
If Left(statusVal, 2) = ChrW(50756) & ChrW(47308) Then
' "완료"로 시작
successCount = successCount + 1
' 완료 행 배경색: 연한 녹색
ws.Range("A" & i & ":C" & i).Interior.Color = RGB(198, 239, 206)
ElseIf Left(statusVal, 2) = ChrW(49892) & ChrW(54056) Then
' "실패"로 시작
failCount = failCount + 1
' 실패 행 배경색: 연한 빨강
ws.Range("A" & i & ":C" & i).Interior.Color = RGB(255, 199, 206)
Else
' 빈 칸 = 미발송
pendingCount = pendingCount + 1
End If
Next i
' 결과 메시지
Dim msg As String
msg = ChrW(48156) & ChrW(49569) & " " & ChrW(49345) & ChrW(53468) & " " & ChrW(54869) & ChrW(51064) & vbCrLf & vbCrLf
' "발송 상태 확인"
' "전체: N건"
msg = msg & ChrW(51204) & ChrW(52404) & ": " & totalCount & ChrW(44148) & vbCrLf
' "발송 완료: N건"
msg = msg & ChrW(48156) & ChrW(49569) & " " & ChrW(50756) & ChrW(47308) & ": " & successCount & ChrW(44148) & vbCrLf
' "발송 실패: N건"
msg = msg & ChrW(48156) & ChrW(49569) & " " & ChrW(49892) & ChrW(54056) & ": " & failCount & ChrW(44148) & vbCrLf
' "미발송: N건"
msg = msg & ChrW(48120) & ChrW(48156) & ChrW(49569) & ": " & pendingCount & ChrW(44148)
MsgBox msg, vbInformation, ChrW(48156) & ChrW(49569) & " " & ChrW(49345) & ChrW(53468)
' 제목: "발송 상태"
End Sub
실습 요약
| 단계 | 작업 내용 | 결과 |
|---|---|---|
| 1 | 네이버 SMTP 설정 활성화 | POP3/SMTP 사용 설정 완료 |
| 2 | 애플리케이션 비밀번호 생성 | SMTP 인증용 비밀번호 확보 |
| 3 | 수신자 명단 작성 | A열: 부서명, B열: 이메일 |
| 4 | 이메일 발송 VBA 실행 | 부서별 파일 첨부 자동 발송 |
| 5 | 발송 상태 확인 | 성공/실패/미발송 건수 확인 |
| 6 | 인증 정보 제거 | 애플리케이션 비밀번호 삭제 |
보안 체크리스트
- VBA 코드에 비밀번호가 직접 입력되어 있지 않은지 확인
- 엑셀 파일에 비밀번호가 저장되어 있지 않은지 확인
- 네이버 애플리케이션 비밀번호 삭제 또는 비활성화
- ChatGPT 대화 기록에 실제 비밀번호가 포함되지 않았는지 확인
- 테스트 발송 시 수신 주소를 본인 주소로 변경했는지 확인
주의사항
- 발송 테스트는 반드시 본인 주소로: 실습 시 B열의 모든 이메일을 본인 주소로 변경하고 테스트하세요. 타인에게 테스트 메일이 발송되지 않도록 주의합니다.
- 비밀번호는 InputBox로: 코드에 비밀번호를 직접 넣지 마세요.
InputBox로 실행 시마다 입력받습니다. - 발송 간격 유지: 네이버 SMTP는 짧은 시간에 대량 발송 시 차단될 수 있습니다. 각 발송 사이에 2초 이상 대기합니다.
- 첨부 파일 확인: 첨부할 파일이 지정된 경로에 존재하는지 먼저 확인하세요. 파일이 없으면 첨부 없이 발송됩니다.
- 실습 후 정리: 실습이 끝나면 애플리케이션 비밀번호를 삭제하고, POP3/SMTP 설정을 비활성화하는 것을 권장합니다.
실습 과제
- 네이버 SMTP 설정을 활성화하세요.
- 수신자 명단(5개 부서)을 엑셀에 작성하되, 이메일은 모두 본인 주소로 입력하세요.
- 이메일 발송 VBA를 실행하여 5건의 테스트 메일을 발송하세요.
- 발송 상태 확인 VBA로 결과를 점검하세요.
- 발송된 이메일을 열어 제목, 본문, 첨부파일이 올바른지 확인하세요.
- 실습 완료 후 보안 체크리스트를 점검하세요.
7차시. 파이썬을 이용한 MS 오피스 자동화
학습 목표
- 파이썬으로 엑셀/파워포인트/이메일 발송까지 연결하는 자동화 흐름을 이해한다
- VBA와 파이썬의 역할 차이를 구분한다
- pandas, matplotlib, openpyxl, python-pptx, smtplib을 활용해 실습한다
- 구글 코랩과 VS Code 두 가지 환경에서 동일한 결과를 만들어본다
1. 파이썬으로 오피스 자동화 개요
VBA vs 파이썬 비교
| 구분 | VBA | 파이썬 |
|---|---|---|
| 실행 위치 | 엑셀/PPT/워드 내부 | 외부 독립 실행 |
| 강점 | 오피스 내부 자동화, 셀 서식 제어 | 대량 데이터, 외부 파일 연동, 배치 작업 |
| 데이터 처리 | 느림 (1만 행 이상 주의) | 빠름 (수십만 행도 가능) |
| 시각화 | 엑셀 차트에 의존 | matplotlib, seaborn 등 자유로운 시각화 |
| 이메일 발송 | CDO/Outlook 연동 | smtplib으로 직접 발송 |
| 학습 난이도 | 엑셀 사용자에게 익숙 | 초기 환경 설정 필요 |
월간 정기보고 자동화 시나리오
[HR데이터.xlsx] → pandas 로딩 → 집계/시각화 → [엑셀 보고서] + [PPT 보고서] → 이메일 발송
자동화 가능한 작업의 조건:
- 입력 파일 규칙이 일정하다 (같은 컬럼, 같은 형식)
- 출력 양식이 고정되어 있다 (같은 서식, 같은 위치)
- 반복 주기가 있다 (매주, 매월, 매분기)
실습 환경 안내
이 교재에서는 구글 코랩과 로컬 실행(VS Code / JupyterLab) 두 가지 환경을 병렬로 안내합니다. 두 환경 모두 .ipynb (Jupyter 노트북) 형식으로 실습 파일이 제공됩니다.
| 구분 | 구글 코랩 | 로컬 실행 (VS Code / JupyterLab) |
|---|---|---|
| 데이터 보고서 실습 | 실습파일/07-colab-version.ipynb | 실습파일/파이썬-MS오피스-자동화-local-version(데이터보고서).ipynb |
| 수료증 생성 실습 | - | 실습파일/파이썬-MS오피스-자동화-local-version(수료증).ipynb |
| 설치 | 불필요 (웹 브라우저) | Python + 패키지 설치 필요 |
| 파일 접근 | 업로드 또는 드라이브 마운트 | 실습파일/ 폴더에서 직접 접근 |
| 결과 저장 | 코랩 환경에 저장 후 다운로드 | 자동화실행결과/ 폴더에 자동 저장 |
| 파일명 규칙 | 타임스탬프 포함 (YYYYMMDD_HHMMSS) | 타임스탬프 포함 (YYYYMMDD_HHMMSS) |
2. 데이터 파일 로딩
구글 코랩에서
# 방법 1: 파일 직접 업로드
from google.colab import files
uploaded = files.upload() # 파일 선택 창이 열립니다
import pandas as pd
df = pd.read_excel('HR데이터.xlsx')
# 방법 2: 구글 드라이브 마운트
from google.colab import drive
drive.mount('/content/drive')
import pandas as pd
df = pd.read_excel('/content/drive/MyDrive/HR데이터.xlsx')
로컬 실행에서 (VS Code / JupyterLab)
실습파일/파이썬-MS오피스-자동화-local-version(데이터보고서).ipynb 노트북을 VS Code 또는 JupyterLab에서 열어 실행합니다.
import pandas as pd
from pathlib import Path
from datetime import datetime
# HR데이터.xlsx는 같은 실습파일 폴더에 있음
df = pd.read_excel('HR데이터.xlsx')
# 결과 저장 폴더 설정 (자동화실행결과/)
OUTPUT_DIR = Path('../자동화실행결과')
OUTPUT_DIR.mkdir(exist_ok=True)
# 타임스탬프 생성 (파일명에 사용)
TIMESTAMP = datetime.now().strftime('%Y%m%d_%H%M%S')
3. 데이터 기본 탐색
아래 코드는 코랩과 VS Code에서 동일하게 사용합니다.
# 데이터 크기 확인 print(df.shape) # (1470, 35) # 처음 5행 미리보기 df.head() # 전체 컬럼 정보 df.info() # 기술 통계량 df.describe()
HR 데이터 구조:
- 1,470명의 직원 데이터
- 35개 한글 컬럼
- 부서: 영업, 연구개발, 인사
- 직무: 영업임원, 연구원, 실험기술자, 제조이사, 의료담당자, 관리자, 영업사원, 연구이사, 인사담당 (9종)
4. 데이터 유형 파악 및 수정
# 각 컬럼의 데이터 유형 확인 print(df.dtypes)
주요 컬럼별 데이터 유형
| 컬럼 | 유형 | 설명 |
|---|---|---|
| 나이 | int64 | 숫자 |
| 퇴사여부 | object | 문자열 (예/아니오) |
| 부서 | object | 문자열 (영업/연구개발/인사) |
| 월급 | int64 | 숫자 |
| 학력 | int64 | 숫자 (1~5) |
# 학력을 범주형으로 변환하는 예시
학력_매핑 = {1: '고졸이하', 2: '전문대졸', 3: '대졸', 4: '석사', 5: '박사'}
df['학력명'] = df['학력'].map(학력_매핑)
# 문자열을 범주형(category)으로 변환
df['부서'] = df['부서'].astype('category')
df['직무'] = df['직무'].astype('category')
print(df.dtypes)
5. 여러 가지 그래프 그리기
사전 설정
import matplotlib.pyplot as plt
import seaborn as sns
from datetime import datetime
# 한글 폰트 설정
plt.rcParams['font.family'] = 'Malgun Gothic' # Windows
# plt.rcParams['font.family'] = 'AppleGothic' # Mac
plt.rcParams['axes.unicode_minus'] = False
# 타임스탬프 (결과 파일명에 사용)
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
!apt-get install -y fonts-nanum import matplotlib.font_manager as fm fm._rebuild() plt.rcParams['font.family'] = 'NanumGothic'
5-1. 부서별 인원 막대그래프
dept_counts = df['부서'].value_counts()
plt.figure(figsize=(8, 5))
plt.bar(dept_counts.index, dept_counts.values, color=['#4472C4', '#ED7D31', '#70AD47'])
plt.title('부서별 인원 현황', fontsize=14)
plt.xlabel('부서')
plt.ylabel('인원 수')
for i, v in enumerate(dept_counts.values):
plt.text(i, v + 10, str(v), ha='center', fontsize=12)
plt.tight_layout()
plt.savefig(f'chart-부서별인원_{timestamp}.png', dpi=150, bbox_inches='tight')
plt.show()

5-2. 전공분야별 원형그래프
field_counts = df['전공분야'].value_counts()
plt.figure(figsize=(8, 6))
colors = ['#4472C4', '#ED7D31', '#A5A5A5', '#FFC000', '#70AD47', '#5B9BD5']
plt.pie(field_counts.values, labels=field_counts.index, autopct='%1.1f%%',
colors=colors, startangle=90)
plt.title('전공분야별 분포', fontsize=14)
plt.tight_layout()
plt.savefig(f'chart-전공분야별분포_{timestamp}.png', dpi=150, bbox_inches='tight')
plt.show()

5-3. 통근거리 히스토그램
plt.figure(figsize=(8, 5))
plt.hist(df['통근거리'], bins=20, color='#4472C4', edgecolor='white')
plt.title('통근거리 분포', fontsize=14)
plt.xlabel('통근거리')
plt.ylabel('빈도')
plt.tight_layout()
plt.savefig(f'chart-통근거리분포_{timestamp}.png', dpi=150, bbox_inches='tight')
plt.show()

5-4. 월급 박스플롯 (부서별)
plt.figure(figsize=(8, 5))
sns.boxplot(data=df, x='부서', y='월급', palette='Set2')
plt.title('부서별 월급 분포', fontsize=14)
plt.xlabel('부서')
plt.ylabel('월급')
plt.tight_layout()
plt.savefig(f'chart-부서별월급_{timestamp}.png', dpi=150, bbox_inches='tight')
plt.show()

5-5. 퇴사여부별 비교
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
# 퇴사여부별 인원
attrition = df['퇴사여부'].value_counts()
axes[0].bar(attrition.index, attrition.values, color=['#70AD47', '#ED7D31'])
axes[0].set_title('퇴사여부별 인원', fontsize=13)
for i, v in enumerate(attrition.values):
axes[0].text(i, v + 10, str(v), ha='center', fontsize=12)
# 부서별 퇴사율
dept_attrition = df.groupby('부서')['퇴사여부'].apply(
lambda x: (x == '예').mean() * 100
).sort_values(ascending=False)
axes[1].barh(dept_attrition.index, dept_attrition.values, color='#ED7D31')
axes[1].set_title('부서별 퇴사율 (%)', fontsize=13)
axes[1].set_xlabel('퇴사율 (%)')
plt.tight_layout()
plt.savefig(f'chart-퇴사현황_{timestamp}.png', dpi=150, bbox_inches='tight')
plt.show()

6. 현황 데이터 집계
# 부서별 인원 집계
print("=== 부서별 인원 ===")
print(df['부서'].value_counts())
# 직무별 인원 집계
print("\n=== 직무별 인원 ===")
print(df['직무'].value_counts())
# 전공분야별 인원 집계
print("\n=== 전공분야별 인원 ===")
print(df['전공분야'].value_counts())
# 부서별 평균 월급, 평균 나이
dept_summary = df.groupby('부서').agg(
인원수=('사번', 'count'),
평균월급=('월급', 'mean'),
평균나이=('나이', 'mean'),
퇴사율=('퇴사여부', lambda x: round((x == '예').mean() * 100, 1))
).reset_index()
print(dept_summary)
# 피벗테이블: 부서 x 직무별 인원
pivot = pd.pivot_table(df, values='사번', index='부서', columns='직무',
aggfunc='count', fill_value=0)
print(pivot)
7. 집계 결과를 엑셀로 저장
구글 코랩에서
from openpyxl.styles import Font, Alignment, PatternFill, Border, Side
from datetime import datetime
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
# 엑셀 파일 저장 (타임스탬프 포함)
output_file = f'HR_집계결과_{timestamp}.xlsx'
with pd.ExcelWriter(output_file, engine='openpyxl') as writer:
dept_summary.to_excel(writer, sheet_name='부서별현황', index=False)
pivot.to_excel(writer, sheet_name='부서직무별인원')
df['전공분야'].value_counts().to_frame().to_excel(writer, sheet_name='전공분야별인원')
# 서식 적용
from openpyxl import load_workbook
wb = load_workbook(output_file)
ws = wb['부서별현황']
# 헤더 서식
header_fill = PatternFill(start_color='4472C4', end_color='4472C4', fill_type='solid')
header_font = Font(color='FFFFFF', bold=True, size=11)
for cell in ws[1]:
cell.fill = header_fill
cell.font = header_font
cell.alignment = Alignment(horizontal='center')
# 열 너비 자동 조정
for col in ws.columns:
max_len = max(len(str(cell.value or '')) for cell in col)
ws.column_dimensions[col[0].column_letter].width = max_len + 4
wb.save(output_file)
print(f"엑셀 파일 저장 완료: {output_file}")
# 코랩에서 파일 다운로드
from google.colab import files
files.download(output_file)
로컬 실행에서
from openpyxl.styles import Font, Alignment, PatternFill, Border, Side
from pathlib import Path
from datetime import datetime
OUTPUT_DIR = Path('../자동화실행결과')
OUTPUT_DIR.mkdir(exist_ok=True)
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
output_file = str(OUTPUT_DIR / f'HR_집계결과_{timestamp}.xlsx')
with pd.ExcelWriter(output_file, engine='openpyxl') as writer:
dept_summary.to_excel(writer, sheet_name='부서별현황', index=False)
pivot.to_excel(writer, sheet_name='부서직무별인원')
df['전공분야'].value_counts().to_frame().to_excel(writer, sheet_name='전공분야별인원')
# 서식 적용
from openpyxl import load_workbook
wb = load_workbook(output_file)
ws = wb['부서별현황']
header_fill = PatternFill(start_color='4472C4', end_color='4472C4', fill_type='solid')
header_font = Font(color='FFFFFF', bold=True, size=11)
for cell in ws[1]:
cell.fill = header_fill
cell.font = header_font
cell.alignment = Alignment(horizontal='center')
for col in ws.columns:
max_len = max(len(str(cell.value or '')) for cell in col)
ws.column_dimensions[col[0].column_letter].width = max_len + 4
wb.save(output_file)
print(f"엑셀 파일 저장 완료: {output_file}")
8. 결과 그래프를 PPT에 삽입
구글 코랩에서
from pptx import Presentation
from pptx.util import Inches, Pt
from pptx.enum.text import PP_ALIGN
prs = Presentation()
prs.slide_width = Inches(13.333)
prs.slide_height = Inches(7.5)
# 표지 슬라이드
slide = prs.slides.add_slide(prs.slide_layouts[6]) # 빈 슬라이드
txBox = slide.shapes.add_textbox(Inches(2), Inches(2.5), Inches(9), Inches(2))
tf = txBox.text_frame
p = tf.paragraphs[0]
p.text = "HR 데이터 분석 보고서"
p.font.size = Pt(36)
p.font.bold = True
p.alignment = PP_ALIGN.CENTER
p2 = tf.add_paragraph()
p2.text = "파이썬 자동화 실습 결과"
p2.font.size = Pt(20)
p2.alignment = PP_ALIGN.CENTER
# 차트 이미지를 슬라이드에 삽입
chart_files = [
(f'chart-부서별인원_{timestamp}.png', '부서별 인원 현황'),
(f'chart-전공분야별분포_{timestamp}.png', '전공분야별 분포'),
(f'chart-통근거리분포_{timestamp}.png', '통근거리 분포'),
(f'chart-퇴사현황_{timestamp}.png', '퇴사 현황 분석'),
]
for chart_file, title in chart_files:
slide = prs.slides.add_slide(prs.slide_layouts[6])
# 제목 추가
txBox = slide.shapes.add_textbox(Inches(0.5), Inches(0.3), Inches(12), Inches(0.8))
tf = txBox.text_frame
p = tf.paragraphs[0]
p.text = title
p.font.size = Pt(24)
p.font.bold = True
# 차트 이미지 삽입
slide.shapes.add_picture(chart_file, Inches(1.5), Inches(1.5), Inches(10), Inches(5.5))
pptx_file = f'HR_분석보고서_{timestamp}.pptx'
prs.save(pptx_file)
print(f"PPT 저장 완료: {pptx_file}")
# 코랩에서 파일 다운로드
from google.colab import files
files.download(pptx_file)
로컬 실행에서
from pptx import Presentation
from pptx.util import Inches, Pt
from pptx.enum.text import PP_ALIGN
prs = Presentation()
prs.slide_width = Inches(13.333)
prs.slide_height = Inches(7.5)
# 표지 슬라이드
slide = prs.slides.add_slide(prs.slide_layouts[6])
txBox = slide.shapes.add_textbox(Inches(2), Inches(2.5), Inches(9), Inches(2))
tf = txBox.text_frame
p = tf.paragraphs[0]
p.text = "HR 데이터 분석 보고서"
p.font.size = Pt(36)
p.font.bold = True
p.alignment = PP_ALIGN.CENTER
p2 = tf.add_paragraph()
p2.text = "파이썬 자동화 실습 결과"
p2.font.size = Pt(20)
p2.alignment = PP_ALIGN.CENTER
# 차트 이미지를 슬라이드에 삽입 (자동화실행결과 폴더에서 읽기)
chart_files = [
(f'chart-부서별인원_{timestamp}.png', '부서별 인원 현황'),
(f'chart-전공분야별분포_{timestamp}.png', '전공분야별 분포'),
(f'chart-통근거리분포_{timestamp}.png', '통근거리 분포'),
(f'chart-퇴사현황_{timestamp}.png', '퇴사 현황 분석'),
]
for chart_file, title in chart_files:
chart_path = OUTPUT_DIR / chart_file
slide = prs.slides.add_slide(prs.slide_layouts[6])
txBox = slide.shapes.add_textbox(Inches(0.5), Inches(0.3), Inches(12), Inches(0.8))
tf = txBox.text_frame
p = tf.paragraphs[0]
p.text = title
p.font.size = Pt(24)
p.font.bold = True
slide.shapes.add_picture(str(chart_path), Inches(1.5), Inches(1.5), Inches(10), Inches(5.5))
pptx_file = str(OUTPUT_DIR / f'HR_분석보고서_{timestamp}.pptx')
prs.save(pptx_file)
print(f"PPT 저장 완료: {pptx_file}")
9. PPT를 이메일로 자동 발송
구글 코랩에서
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email import encoders
# ===== 발송 설정 =====
smtp_server = 'smtp.naver.com'
smtp_port = 587
sender_email = 'your_id@naver.com' # 본인 네이버 아이디
sender_password = 'your_app_password' # 앱 비밀번호 (네이버 설정에서 생성)
receiver_email = 'receiver@example.com' # 수신자 이메일
# ===== 이메일 구성 =====
msg = MIMEMultipart()
msg['From'] = sender_email
msg['To'] = receiver_email
msg['Subject'] = 'HR 데이터 분석 보고서 - 자동 발송'
body = """안녕하세요,
HR 데이터 분석 보고서를 첨부하여 보내드립니다.
파이썬 자동화를 통해 생성된 보고서입니다.
감사합니다.
"""
msg.attach(MIMEText(body, 'plain', 'utf-8'))
# ===== 파일 첨부 =====
pptx_file = 'HR_분석보고서.pptx'
with open(pptx_file, 'rb') as f:
part = MIMEBase('application', 'octet-stream')
part.set_payload(f.read())
encoders.encode_base64(part)
part.add_header('Content-Disposition', f'attachment; filename="{pptx_file}"')
msg.attach(part)
# ===== 발송 =====
try:
server = smtplib.SMTP(smtp_server, smtp_port)
server.starttls()
server.login(sender_email, sender_password)
server.send_message(msg)
server.quit()
print("이메일 발송 완료!")
except Exception as e:
print(f"발송 실패: {e}")
VS Code에서
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email import encoders
# ===== 발송 설정 =====
smtp_server = 'smtp.naver.com'
smtp_port = 587
sender_email = 'your_id@naver.com'
sender_password = 'your_app_password'
receiver_email = 'receiver@example.com'
# ===== 이메일 구성 =====
msg = MIMEMultipart()
msg['From'] = sender_email
msg['To'] = receiver_email
msg['Subject'] = 'HR 데이터 분석 보고서 - 자동 발송'
body = """안녕하세요,
HR 데이터 분석 보고서를 첨부하여 보내드립니다.
파이썬 자동화를 통해 생성된 보고서입니다.
감사합니다.
"""
msg.attach(MIMEText(body, 'plain', 'utf-8'))
# ===== 파일 첨부 =====
pptx_file = 'HR_분석보고서.pptx'
with open(pptx_file, 'rb') as f:
part = MIMEBase('application', 'octet-stream')
part.set_payload(f.read())
encoders.encode_base64(part)
part.add_header('Content-Disposition', f'attachment; filename="{pptx_file}"')
msg.attach(part)
# ===== 발송 =====
try:
server = smtplib.SMTP(smtp_server, smtp_port)
server.starttls()
server.login(sender_email, sender_password)
server.send_message(msg)
server.quit()
print("이메일 발송 완료!")
except Exception as e:
print(f"발송 실패: {e}")
10. 보안 주의사항
- 아이디와 비밀번호를 코드에 직접 입력한 경우, 실습 후 반드시 삭제합니다
- 네이버 앱 비밀번호를 사용합니다 (계정 비밀번호가 아님)
- 코드를 공유하거나 깃허브에 올릴 때 인증 정보가 포함되지 않았는지 확인합니다
- 실습 시 수신 이메일을 본인 주소로 설정하여 테스트합니다
- 실습이 끝나면 네이버 설정에서 앱 비밀번호를 삭제 또는 비활성화합니다
네이버 SMTP 설정 방법
- 네이버 메일 접속 → 환경설정 → POP3/IMAP 설정
- POP3/SMTP 사용 → 사용함으로 변경
- 네이버 내 정보 → 보안 설정 → 2단계 인증 활성화
- 애플리케이션 비밀번호 생성 → 생성된 비밀번호를 코드에 사용
10-1. (추가 실습) 파이썬으로 수료증 PDF 자동 생성
4차시에서 VBA로 구현한 수료증 PDF 생성을 파이썬으로도 할 수 있습니다.
실습 파일: 실습파일/파이썬-MS오피스-자동화-local-version(수료증).ipynb
from pptx import Presentation
# 핵심 코드 3줄
prs = Presentation('실습_교육수료증-템플릿.pptx') # 템플릿 열기
replace_placeholders(prs.slides[0], data) # 치환
prs.save('수료증_김민수.pptx') # 저장
동작 방식:
python-pptx로 템플릿의{{이름}},{{사번}}등 플레이스홀더를 치환하여 개인별 PPTX 생성- VBScript(
cscript)로 PowerPoint의SaveAs(경로, 32)호출하여 일괄 PDF 변환 - 임시 PPTX 파일 삭제
VBA vs Python 비교
| 항목 | VBA | Python |
|---|---|---|
| 실행 환경 | PowerPoint 매크로 | Jupyter Notebook |
| 템플릿 형식 | .pptm | .pptx |
| PDF 변환 | ExportAsFixedFormat | VBScript 일괄 변환 |
| 확장성 | PowerPoint 한정 | pandas 등과 연동 가능 |
10-2. (참고) 같은 기능을 VBA로도 구현 가능
7차시에서 파이썬으로 만든 엑셀 보고서와 PPT 보고서 기능은 엑셀 VBA로도 구현되어 있습니다.
실습 파일: 실습파일/HR데이터(VBA포함).xlsm — M11, M12 모듈
| 모듈 | 함수명 | 기능 |
|---|---|---|
| M11_엑셀보고서생성 | HR_엑셀보고서_생성() | 부서별현황, 부서직무별인원, 전공분야별인원 3개 시트를 가진 엑셀 보고서 생성 |
| M12_PPT보고서생성 | HR_PPT보고서_생성() | 5종 차트를 자동 생성하고 PPT 슬라이드에 삽입 |
저장 위치: 자동화실행결과/자동화결과_엑셀VBA로-보고서출력/
실행 방법: HR데이터(VBA포함).xlsm을 열고 Alt+F8 → 함수명 선택 → 실행
정리
이번 차시에서 실습한 전체 자동화 흐름:
1. HR데이터.xlsx 로딩 (pandas) ↓ 2. 데이터 유형 확인 및 수정 ↓ 3. 시각화 (matplotlib/seaborn) → 차트 이미지 저장 ↓ 4. 데이터 집계 (groupby, pivot_table) ↓ 5. 집계 결과 → 엑셀 파일 저장 (openpyxl) ↓ 6. 차트 이미지 → PPT 슬라이드 삽입 (python-pptx) ↓ 7. PPT 파일 → 이메일 첨부 발송 (smtplib)
핵심 라이브러리 정리
| 라이브러리 | 용도 | 설치 명령 |
|---|---|---|
| pandas | 데이터 로딩/집계 | pip install pandas |
| matplotlib | 그래프 생성 | pip install matplotlib |
| seaborn | 통계 시각화 | pip install seaborn |
| openpyxl | 엑셀 읽기/쓰기/서식 | pip install openpyxl |
| python-pptx | PPT 생성/편집 | pip install python-pptx |
| smtplib | 이메일 발송 | 기본 내장 (설치 불필요) |
실습 과제
"HR 데이터 → 분석 → 엑셀 저장 → PPT 생성 → 이메일 발송" 전체 파이프라인을 완성하세요.
- HR데이터.xlsx를 로딩하여 부서별/직무별/전공별 집계를 수행하세요
- 차트 5종을 생성하고 이미지 파일로 저장하세요
- 집계 결과를 엑셀 파일로 저장하세요 (헤더 서식 포함)
- 차트 이미지를 PPT 슬라이드에 삽입하세요
- 완성된 PPT를 이메일로 발송하세요 (본인 주소로 테스트)