본문 바로가기

Stack/Lowcode

[Mendix] 자동 메일 발송 기능 개발기: Logic 에러부터 HTML 테이블까지 (feat. 회사 API)

반응형

지난 포스팅에서 제가 개발 중인 시스템의 데이터를 적재하는 것까지는 성공했습니다. 이제 남은 건 "조치 기한이 임박하거나 지난 건들에 대해 담당자에게 메일을 쏘는 것"이었죠.

간단하게 루프(Loop) 돌려서 API 호출하면 끝날 줄 알았는데... 역시나 Mendix는 호락호락하지 않았습니다. (제발 한 번에 좀 돼라 😭)

오늘의 삽질 기록은 크게 세 가지입니다.

  1. 날짜 계산 로직의 함정 (trimToDays)
  2. 사내 메일 API 연동 (Invalid Address)
  3. 이메일 본문 예쁘게 만들기 (HTML 노가다)

1. 날짜 계산 에러: "값이 없는데 어떻게 계산해요?"

조치 기한(DueDate)을 기준으로 3일 전(Upcoming), 1일 지남(Overdue1), 7일 이상 지남(Overdue7) 조건을 걸어 분기 처리를 하려고 했습니다.

JavaScript
trimToDays($Iterator/IssueClearDueDate) = $Target_Upcoming3
OR
trimToDays($Iterator/IssueClearDueDate) = $Target_Overdue1

그런데 코드를 돌리자마자 빨간 맛 에러가 뜹니다. Caused by: com.mendix.modules.microflowengine.MicroflowException: Failed to evaluate expression...

알고 보니 Mendix의 trimToDays() 함수는 NULL(Empty) 값을 처리하지 못해서 뻗어버린 것이었습니다. 조치 기한이 미정인 데이터가 들어오니 계산을 못하고 누워버린 거죠.

🚀 해결: 방어 로직 추가 (Guard Clause)

가장 확실한 방법은 데이터를 가져올 때(Retrieve)부터 날짜 없는 녀석들을 쳐내는 것이었습니다.

[Image Prompt: A screenshot of Mendix Studio Pro Retrieve Action properties. The XPath constraint section highlights '[IssueClearDueDate != empty]'.]

  • Retrieve 단계: XPath Constraint에 [IssueClearDueDate != empty] 추가.
  • Expression 단계: 혹시 몰라 조건식 앞단에도 방어 로직 추가. $Iterator/IssueClearDueDate != empty AND ( ... )

이렇게 하니 빈 값 때문에 멈추는 일 없이 루프가 아주 잘 돕니다. (편안-)


2. API 연동 이슈: "주소를 줬는데 왜 못 받니"

로직은 통과했는데, 이번엔 메일 서버가 뱉어냅니다. "errorCode":"ML641", "errorMessage":"Failed to send mail due to invalid address."

hyeonlee.net@xxx.com... 분명 맞는 주소인데 왜 Invalid라고 할까요?

🕵️‍♂️ 범인 1: 보이지 않는 공백

DB에서 가져온 사번/ID 뒤에 공백(Space)이 숨어있을 수 있습니다. trim($Iterator/UserID) + '@xxx.com' 처럼 trim() 함수로 공백을 싹 날려줘야 안전합니다.

🕵️‍♂️ 범인 2: 객체 연결 (Association) 누락 (★핵심)

이게 진짜 문제였습니다. 사내 공통 모듈은 단순히 MailMain(메일 본체)만 만들어서 되는 게 아니었습니다. MailReceiver(받는 사람 명단) 객체를 따로 만들어서 본체에 **연결(Association)**해줘야 했던 거죠.

저는 본체에만 주소를 텍스트로 박아넣고 "왜 안 가!" 하고 있었던 겁니다.

[Image Prompt: A diagram showing the relationship between Mendix Entities. 'MailMain' entity is on the left, 'MailReceiver' entity is on the right, connected by an association line. An arrow points to the MailReceiver with text "Must create this object!".]

🚀 해결: MailReceiver 객체 생성 및 매핑

  1. Create Object로 MailReceiver 생성.
  2. Email, RecipientType(To/CC) 속성 설정.
  3. 가장 중요: MailReceiver_MailMain 관계(Association)에 방금 만든 메일 본체 객체(NewMailMain)를 연결!

이렇게 "본체 + 수신자 명단" 세트를 맞춰주니 그제야 메일이 정상 발송되었습니다.


3. HTML 본문: "텍스트 말고 표(Table)로 보여줘"

메일은 가는데, 텍스트가 줄글로 쭉 나열되니 가독성이 최악이었습니다. "깔끔하게 표 안에 넣어서, 중요 내용은 빨간색으로 보여주세요." 라는 요청 사항 반영을 위해 HTML 태그를 깎기 시작했습니다.

여기서 또 두 가지 난관이 있었는데요.

  1. 줄바꿈: \n을 써도 메일에서는 무시됨 → <br> 태그 필수.
  2. 날짜 포맷: toString()은 못생김 → formatDateTime() 써야 함.

🚀 해결: Inline CSS & Null Safety 적용

이메일 클라이언트는 최신 CSS를 잘 안 먹기 때문에, 옛날 방식인 <table> 태그와 인라인 스타일(style="...")을 써야 가장 안전합니다. 그리고 여기서도 값이 없으면 에러가 나므로 if-then-else 처리가 필수입니다.

JavaScript
// 실제 적용한 HTML 생성 코드 일부
'<table style="border-collapse: collapse; border: 1px solid #ddd;">' +
  '<tr>' +
    '<td style="background-color: #f9f9f9; font-weight: bold;">조치기한</td>' +
    '<td style="color: #d9534f; font-weight: bold;">' +
      // 값이 있을 때만 포맷팅, 없으면 '미정' 출력
      (if $Iterator/DueDate != empty
       then formatDateTime($Iterator/DueDate, 'yyyy-MM-dd')
       else '미정') +
    '</td>' +
  '</tr>' +
'</table>'

[Image Prompt: A visual representation of an email body. It shows a neat HTML table with fields like 'Issue Date', 'Category', and 'Details'. The 'Due Date' field is highlighted in red text.]

이렇게 문자열 변수(Body_text)에 HTML 코드를 담아서 API의 Content 파라미터로 넘겨주니, 아웃룩에서 아주 깔끔한 표 형태로 메일이 도착했습니다.


4. 결론: 자동화의 맛!

이제 매일 아침 시스템이 알아서:

  1. 날짜 계산해서 대상자 추려내고 (guard clause)
  2. 수신자 객체 딱딱 만들어서 (Association)
  3. HTML로 예쁘게 포장해 (Inline CSS) 메일을 쏴줍니다.

"시스템 바로가기" 버튼을 눌러서 해당 게시글로 바로 이동하는 DeepLink 기능도 개발 중인데, 이건 StackOverflow 에러와의 전쟁 중이라... 해결되면 다음 포스팅에서 다뤄보겠습니다. (무한 루프의 늪... 🤮)

Mendix로 이메일 기능 구현하시는 분들, 꼭 MailReceiver 객체 따로 챙기시고 HTML엔 if-then-else 방어 로직 잊지 마세요!

반응형