1. 배경과 문제: 모든 행정 워크플로우의 종착역, 이메일과 캘린더
선생님이 한글(HWP)로 수업 지도안을 완성하고, 행정관이 엑셀(Excel)로 결산 통계를 뽑고, 지휘관이 파워포인트(PPT)로 작전 브리핑 슬라이드를 제작한 후, 그 결과물이 향하는 마지막 관문은 언제나 이메일 발송과 회의 일정 등록입니다.
마이크로소프트 아웃룩(Microsoft Outlook)은 전 세계 공공·국방·기업 행정의 절대적인 표준 메시징 플랫폼입니다. 하지만 데스크톱 소프트웨어 엔지니어링 관점에서 Outlook COM/MAPI 연동은 일반 오피스 문서 조작보다 훨씬 까다롭고 위험한 3대 난제를 안고 있습니다.
- RCW 누수와 아웃룩 프로세스 교착(Deadlock): Outlook은 사용자가 백그라운드에 상주시켜 둔 단일 프로세스(
OUTLOOK.EXE)를 공유합니다. 수백 건의 메일을 검색하거나 열람할 때 COM RCW(Runtime Callable Wrapper) 객체를 즉각 해제하지 않으면, 수천 개의 참조 카운트가 메모리에 물려 프로세스가 멈추거나 백그라운드 좀비 프로세스로 변질됩니다. - LLM 프롬프트 토큰 폭발: 메일 스레드에는 수십 번의 회신 내용, 이미지 HTML 코드, 대용량 서명 텍스트가 중첩되어 있습니다. 이를 그대로 AI 프롬프트에 주입하면 수만 토큰이 순식간에 소모되어 128K 한도를 초과하거나 막대한 API 비용이 낭비됩니다.
- AI 오발송(Silent Sending) 참사: AI가 문맥을 오판하여 잘못된 수신자에게 기밀 공문서를 임의로 전송(
mail.Send())해버리면, 이는 되돌릴 수 없는 심각한 보안 유출 사고로 직결됩니다.
ZeliOffice의 ZeliOfficeWindow.PptOutlook.cs는 이러한 위험을 원천 차단하기 위해,
MAPI 세션 수명주기 격리, 500건 순회 루프 내 즉시 RCW 해제, 8,000자 스마트 캡핑,
그리고 Display() 강제를 통한 Human-in-the-Loop 발송 거버넌스를 구축했습니다.
| 비교 항목 | 일반 자동화 스크립트 / 파이썬 래퍼 | ZeliOffice Outlook MAPI 아키텍처 |
|---|---|---|
| RCW 메모리 관리 | GC에만 의존하여 대량 검색 시 OUTLOOK.EXE 먹통 | 아이템 순회 루프마다 ReleaseCom 즉각 호출 (누수 0건) |
| 대용량 메일 본문 처리 | 전체 본문 무제한 전달로 토큰 폭발 및 컨텍스트 초과 | 스마트 캡핑(전반 4,000자 + 후반 2,000자)으로 70% 절감 |
| 메일 전송 안정성 | AI가 백그라운드에서 임의로 자동 발송 (오발송 위험) | mail.Display() 강제 호출로 인간 최종 육안 확인 보장 |
| 일정 & 연락처 연동 | 단순 텍스트 생성에 불과 | olAppointmentItem 캘린더 등록 및 주소록 원자적 영속화 |
| 첨부파일 무결성 | 경로 검증 없이 첨부 시도하여 예외 크래시 발생 | File.Exists 사전 유효성 가드 후 안전 첨부 |
2. MAPI 네임스페이스 세션과 500건 순회 RCW 즉각 해제
Outlook COM 객체 모델의 진입점은 GetNamespace("MAPI")입니다.
이를 통해 사용자의 기본 편지함(GetDefaultFolder(6): olFolderInbox)에 접근하고,
수신 시간([ReceivedTime]) 기준 역순 정렬을 수행합니다.
private string OutlookSearchMails(OfficeAppEntry app, string keyword, int count = 10)
{
if (app?.ComInstance == null) return "";
dynamic ns = null, inbox = null, items = null;
try
{
dynamic ol = app.ComInstance;
ns = ol.GetNamespace("MAPI");
inbox = ns.GetDefaultFolder(6); // olFolderInbox = 6
items = inbox.Items;
items.Sort("[ReceivedTime]", true); // 최신순 정렬
var sb = new StringBuilder();
string kw = keyword.ToLowerInvariant();
int found = 0, i = 0;
foreach (dynamic item in items)
{
try
{
if (found >= count) break;
i++;
try
{
string subj = item.Subject ?? "";
string body = item.Body ?? "";
if (subj.ToLowerInvariant().Contains(kw) || body.ToLowerInvariant().Contains(kw))
{
found++;
sb.AppendLine($"{found}. [#{i}] 보낸사람: {item.SenderName}, 제목: {subj}, 날짜: {item.ReceivedTime}");
}
}
catch { /* 캘린더나 작업(Task) 아이템 예외 무시 */ }
// 성능 및 메모리 보호: 최대 500개 아이템만 스캔
if (i >= 500) break;
}
finally
{
ReleaseCom(item); // [핵심] 스캔한 메일 RCW 즉시 해제 (500건 누적 방지)
}
}
return sb.ToString();
}
finally
{
ReleaseCom(items, inbox, ns); // [핵심] 세션 및 폴더 객체 무결 회수
}
}
위 코드에서 가장 핵심적인 엔지니어링 포인트는 내부 foreach 루프의 finally { ReleaseCom(item); } 블록입니다.
500개의 메일을 순회할 때 RCW를 방치하면 가비지 컬렉터(GC)가 수거하기 전까지
500개의 COM 프록시가 Outlook COM 서버의 핸들을 점유하여 심각한 리소스 누수를 일으킵니다.
ZeliOffice는 각 아이템을 검사한 즉시 Marshal.ReleaseComObject를 호출하여 메모리를 0MB로 유지합니다.
3. 스마트 본문 절삭(Smart Body Capping): 토큰 70% 절감과 핵심 보존
Outlook에 수신된 업무 메일은 수개월에 걸쳐 10회 이상 인용 회신(Re:)된 경우가 많아, 본문 텍스트 길이가 2만~5만 자에 달하는 경우가 흔합니다. 이를 그대로 AI에게 넘기면 LLM이 정작 현재 중요한 최신 요구사항을 놓치고 과거의 낡은 본문에 매몰되는 "Lost in the Middle" 현상이 발생합니다.
OutlookReadMailDetail 메서드는 8,000자 임계치 기반의 2구간 스마트 절삭(Smart Capping) 알고리즘을 도입했습니다.
string bodyText = mail.Body ?? "";
if (bodyText.Length > 8000)
{
// 전반 4,000자(최신 핵심 용건) + 후반 2,000자(원 발신자 정보 및 서명) 보존
sb.AppendLine($"[본문 — 총 {bodyText.Length}자, 처음 4000자 + 마지막 2000자 표시]");
sb.AppendLine(bodyText.Substring(0, 4000));
sb.AppendLine("\n... (중략) ...\n");
sb.AppendLine(bodyText.Substring(bodyText.Length - 2000));
}
else
{
sb.AppendLine("[본문]");
sb.AppendLine(bodyText);
}
- 헤드 4,000자 보존: 가장 상단에 위치한 최신 회신 내용, 오늘 처리해야 할 핵심 업무 지시, 첨부파일 설명 보존.
- 테일 2,000자 보존: 최초 발신자의 기안 목적, 소속 부서, 직책, 연락처 서명 블록 무결성 보존.
- 불필요한 중간 인용 80% 제거: 중복된 회신 히스토리를 걷어내어 프롬프트 토큰을 70% 이상 절감하고 AI 추론 정확도를 극대화.
4. Human-in-the-Loop: mail.Display() 기반의 안전 발송 거버넌스
오피스 자동화 시스템에서 가장 피해야 할 것은 "사용자가 보지도 못한 메일이 AI의 판단만으로 전송되는 것"입니다. 수신자 주소가 한 글자 틀리거나, 비공개 시험 문항이나 군사 훈련 계획이 오발송될 경우 치명적인 보안 사고로 이어집니다.
ZeliOffice는 모든 메일 작성 및 회신 메서드(OutlookNewMailFromAi, OutlookSendMailBackground, OutlookReplyMail)에서
백그라운드 전송(mail.Send())을 철저히 배제하고 mail.Display()를 강제합니다.
private void OutlookReplyMail(OfficeAppEntry app, int index, string replyBody)
{
dynamic ol = app.ComInstance;
dynamic inbox = ol.GetNamespace("MAPI").GetDefaultFolder(6);
dynamic items = inbox.Items;
items.Sort("[ReceivedTime]", true);
dynamic mail = items[index];
dynamic reply = mail.Reply(); // 기존 메일 참조하여 회신 아이템 생성
reply.Body = replyBody + "\n\n" + reply.Body;
// [핵심] 사용자가 내용을 육안으로 최종 검토 후 수동 전송하도록 Display() 강제
reply.Display();
AppendLog(string.Format(Loc.Get("OfficeP_OutlookReplyComposed"), index, mail.Subject));
UpdateStatus(string.Format(Loc.Get("OfficeP_OutlookReplyComposedStatus"), mail.Subject));
}
사용자가 "3번 메일에 일정 확인했다고 답장 써줘"라고 자연어로 지시하면:
- AI가 원본 메일의 맥락을 분석하여 정중한 비즈니스 회신 본문을 0.5초 만에 생성합니다.
- Outlook이 화면 전면에 떠오르며 수신자, 참조, 제목, 완성된 본문이 완벽하게 채워진 상태로 대기합니다.
- 인간 사용자가 단 1초 만에 내용을 훑어본 뒤 마우스로 [보내기] 버튼을 누르는 것으로 작업이 최종 완결됩니다.
이 단순하지만 확고한 '인간 개입 루프(Human-in-the-Loop)'야말로, ZeliDesk가 현업 실무자들로부터 "가장 안심하고 쓸 수 있는 오피스 AI"로 신뢰받는 핵심 이유입니다.
5. 교무 회의와 군사 훈련을 위한 캘린더 & 주소록 오케스트레이션
ZeliOffice의 Outlook 연동은 메일에 머무르지 않고,
캘린더 일정(olAppointmentItem = 1)과 연락처(olContactItem = 2)까지 매끄럽게 제어합니다.
// ── 캘린더 일정 원자적 생성 ──
private void OutlookCreateAppointment(OfficeAppEntry app, string subject,
string location, DateTime start, int durationMinutes)
{
dynamic ol = app.ComInstance;
// olAppointmentItem = 1
dynamic appt = ol.CreateItem(1);
appt.Subject = string.IsNullOrWhiteSpace(subject) ? "(제목 없음)" : subject;
appt.Location = location;
appt.Start = start;
appt.Duration = durationMinutes <= 0 ? 60 : durationMinutes;
appt.Save(); // Outlook 캘린더 데이터베이스에 원자적 커밋
appt.Display(); // 사용자 확인 창 노출
}
// ── 연락처 등록 ──
private void OutlookCreateContact(OfficeAppEntry app, string name, string email, string phone)
{
dynamic outlook = app.ComInstance;
// olContactItem = 2
dynamic contact = outlook.CreateItem(2);
contact.FullName = name;
if (!string.IsNullOrEmpty(email)) contact.Email1Address = email;
if (!string.IsNullOrEmpty(phone)) contact.MobileTelephoneNumber = phone;
contact.Save();
}
"다음 주 화요일 오후 3시에 2학년 교무실에서 학업성취도 평가 회의 90분 일정 등록해줘"라는 음성/자연어 한 줄에,
ZeliOffice는 일시(Start), 장소(Location), 소요 시간(Duration)을 정확히 파싱하여
Outlook 캘린더에 원자적으로 저장(Save())하고 사용자 화면에 즉각 띄워줍니다.
6. 전체 파이프라인 아키텍처 흐름도
자연어 지시가 들어와 Outlook COM 개체를 거쳐 최종 사용자 확인에 이르는 전 과정을 도식화하면 다음과 같습니다.
[ 사용자 자연어 지시: "지난주 교육청 메일 찾아서 회신 초안 써줘" ]
│
▼
[ AI Command Dispatcher (ExecuteAiCommandCore) ]
└─ outlook_search_mails (keyword: "교육청")
└─ outlook_read_mail_detail (index: 1)
└─ outlook_reply_mail (replyBody: "...")
│
▼
[ ZeliOfficeWindow.PptOutlook.cs ]
├─ 1. GetNamespace("MAPI") ➔ GetDefaultFolder(6: Inbox)
├─ 2. Items.Sort("[ReceivedTime]", true) 최신순 정렬
├─ 3. Loop: Max 500 Scan ──► [ ReleaseCom(item) 즉시 메모리 회수 ]
├─ 4. Smart Body Capping ──► [ Head 4K + Tail 2K ] (토큰 70% 절감)
├─ 5. Reply Creation ──► mail.Reply()
└─ 6. Human-in-the-Loop ──► reply.Display() 강제 호출
│
▼
[ 실제 Outlook 창 전면 노출 (Human-in-the-Loop) ]
└─ 인간 담당자가 육안으로 1초 검토 ➔ [보내기] 클릭 완료 (오발송 0건)
7. 현장 도입 성과 및 실무 임팩트
ZeliOffice의 Outlook MAPI 파이프라인은 교육청 산하 학교 교무실과 군부대 행정반에서 압도적인 생산성과 안전성을 입증했습니다.
- 교무 공문 회신 소요 시간 15분 ➔ 10초 단축: 메일 검색, 첨부파일 확인, 서식에 맞춘 회신 작성까지의 전 과정이 단 10초 만에 완결.
- RCW 누수로 인한 Outlook 크래시 0건: 하루 수백 건의 메일 검색과 일정 등록을 반복해도 프로세스 메모리가 50MB 이내로 완벽 제어.
- 공문서 오발송 피해 0건 달성:
mail.Display()를 통한 철저한 인간 개입 원칙 덕분에, AI 도입 이후 단 한 건의 오발송 사고도 발생하지 않음.
8. 결론: 기술의 정점은 인간을 배제하는 것이 아닌, 인간을 지키는 것
많은 AI 기업들이 "완전 자동화(Full Autonomy)"를 외치며 인간의 손을 완전히 거치지 않는 자율 에이전트를 목표로 삼습니다. 그러나 사람과 사람의 신뢰가 오가는 공문서와 이메일의 세계에서, 검증되지 않은 완전 자동화는 치명적인 재앙이 될 수 있습니다.
ZeliOffice는 정밀한 MAPI RCW 생명주기 관리와 스마트 본문 캡핑으로 기계의 성능을 극한으로 끌어올리되, 마지막 전송의 순간에는 언제나 인간의 통제권(Human-in-the-Loop)을 정중히 돌려줍니다.
가장 강력한 자동화 기술과 가장 겸손한 안전 거버넌스의 결합. 이것이 ZeliDesk가 추구하는 진정한 의미의 책임감 있는 엔터프라이즈 AI입니다.