Mendix 앱에서 SAML SSO 환경의 딥링크(DeepLink)를 구현하면서 만난 5가지 환경 제약과 최종 해결법을 정리합니다. SPA 라우팅, ADFS SSO cont 파라미터 무시, 세션 불일치, HTMLSnippet script 태그 미작동, 메일 클라이언트 URL fragment 제거 문제까지 — 각각의 원인과 우회 방법을 실무 경험 기반으로 공유합니다.
Mendix + SAML SSO 환경에서
딥링크(DeepLink) 구현 삽질기
표준 DeepLink 모듈이 안 통하는 환경에서 쿠키 기반 우회로 해결하기까지
1. 목표 — 뭘 만들려고 했나
회의록 생성 시 관련자에게 메일 발송 → 메일 내 "시스템 바로가기" 링크 클릭 → SSO 자동 로그인 → 해당 회의록 상세 페이지 자동 오픈
간단해 보이지만, Mendix의 DeepLink 모듈 문서대로 구현했는데 전혀 작동하지 않았습니다. 이유는 환경 제약 5가지가 동시에 존재했기 때문입니다.
2. 환경 제약 — 왜 표준 방식이 안 됐나
하나씩은 흔한 문제인데, 5개가 동시에 걸리니까 해결책 하나가 다른 제약에 의해 무효화되는 연쇄 구조였습니다.
| 제약사항 | 증상 | 영향 |
|---|---|---|
| ① SPA 라우팅 | Mendix 앱이 SPA 모드로 동작. /p/페이지이름 형태의 직접 URL 접근 시 page not found |
외부 랜딩 페이지 방식 불가 |
| ② ADFS SSO | 회사 SSO(ADFS)가 로그인 후 cont 파라미터를 무시하고 항상 홈으로 리다이렉트 |
SSO 후 딥링크 URL 복귀 불가 |
| ③ 세션 불일치 | DeepLink 모듈이 세션 ID 기반 PendingLink를 저장하나, SSO 후 새 세션 생성으로 조회 실패 | DeepLink 표준 방식 사용 불가 |
| ④ HTMLSnippet | SPA 환경에서 <script> 태그가 렌더링 안 됨 |
<img onerror> 트릭으로 우회 필요 |
| ⑤ 메일 클라이언트 | 링크의 URL fragment(# 이후)가 클릭 시 전달 안 됨 |
hash 기반 토큰 전달 불가 |
3. 시도한 접근법 5가지와 실패 이유
/link/reviewmeeting/토큰 → DeepLink 핸들러 → PendingLink DB 저장 → SSO → 홈에서 조회
실패: SSO 로그인 시 세션 ID가 바뀌어 PendingLink 조회 실패. deeplink$pendinglink 테이블이 항상 비어있었음.
DeepLink 모듈이 /sso?cont=/link/reviewmeeting/토큰으로 리다이렉트하도록 설정
실패: ADFS가 cont 파라미터를 무시하고 항상 홈으로 보냄.
/p/PG_DeepLink_Landing?token=토큰에서 쿠키 저장 후 SSO 이동
실패: SPA 앱에서 /p/페이지이름 직접 접근 자체가 불가. page not found.
https://도메인/#dl=reviewmeeting&tk=토큰
실패: 메일 클라이언트가 # 이후를 전달하지 않음. 주소복사하면 있지만 클릭하면 사라짐.
https://도메인/?dl=reviewmeeting&tk=토큰
성공! query string은 모든 환경에서 전달됨. <img onerror>로 <script> 없이 JS 실행. 쿠키로 SSO 너머로 토큰 전달.
4. 최종 해결 — 쿠키 기반 우회 아키텍처
비로그인 사용자 흐름
→
https://도메인/?dl=reviewmeeting&tk=토큰→ 비로그인이라 PG_Login으로 리다이렉트 (query string 유지됨)
→ PG_Login HTMLSnippet: query string에서 토큰 읽어 쿠키 저장
→ 자동으로
/sso 이동 → ADFS SSO 로그인→ 로그인 완료 → PG_Home
→ PG_Home HTMLSnippet: 쿠키 감지 →
/link/reviewmeeting/토큰 이동→ 이미 로그인 상태이므로 DeepLink 핸들러가 MF 즉시 실행
→ 회의록 페이지 오픈 ✅
이미 로그인 사용자 흐름
→
https://도메인/?dl=reviewmeeting&tk=토큰→ PG_Home 로드
→ HTMLSnippet: query string에서 토큰 읽음 →
/link/reviewmeeting/토큰 이동→ DeepLink 핸들러: MF 즉시 실행
→ 회의록 페이지 오픈 ✅
DeepLink 모듈의 PendingLink(세션 기반)를 완전히 우회합니다. 쿠키로 토큰을 SSO 너머로 전달하고, 로그인 완료 후에 DeepLink URL로 재진입하면 이미 로그인 상태이므로 세션 문제가 발생하지 않습니다.
5. 구현 코드
① 메일 URL (MF의 Change Object)
// IVK_ShowPage_ReviewMeeting_NewEdit → Change Object → Details
'https://도메인/?dl=reviewmeeting&tk=' + $NewReviewMeeting/Token
② PG_Login HTMLSnippet
Content Type: HTML. <script> 태그가 SPA에서 작동하지 않으므로 <img onerror> 트릭 사용.
<!-- PG_Login HTMLSnippet (Content Type: HTML) -->
<img src="x" onerror="
(function() {
var params = new URLSearchParams(window.location.search);
var dl = params.get('dl');
var tk = params.get('tk');
if (!dl || !tk) return;
var path = '/link/' + dl + '/' + tk;
document.cookie = 'dl_path=' + encodeURIComponent(path)
+ '; path=/; max-age=300';
// 자동 SSO 이동 (일반 로그인에는 영향 없음)
window.location.href = '/sso';
})();
" style="display:none">
③ PG_Home HTMLSnippet
<!-- PG_Home HTMLSnippet (Content Type: HTML) -->
<img src="x" onerror="
(function() {
// 1) 쿠키 확인 (SSO 로그인 후 복귀)
var match = document.cookie.match(/dl_path=([^;]+)/);
if (match) {
var path = decodeURIComponent(match[1]);
document.cookie = 'dl_path=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;';
setTimeout(function() {
window.location.href = path;
}, 500);
return;
}
// 2) query string 확인 (이미 로그인 상태)
var params = new URLSearchParams(window.location.search);
var dl = params.get('dl');
var tk = params.get('tk');
if (!dl || !tk) return;
var path = '/link/' + dl + '/' + tk;
setTimeout(function() {
window.location.href = path;
}, 500);
})();
" style="display:none">
④ MF_DeepLink_OpenReviewMeeting (딥링크 MF)
// 파라미터: UrlPath (String)
① Retrieve ReviewMeeting
XPath: [Token = $UrlPath]
Range: First
② if $ReviewMeeting != empty:
③ Retrieve ActionItem
④ Retrieve ActionItemClear
⑤ Retrieve ExternalInspectionIssue
⑥ Create UIContext_2
⑦ Show Page: PG_ReviewMeeting_NewEdit
// 파라미터 5개 전부 넘기기
else:
Show message: '해당 회의록을 찾을 수 없습니다'
Create 시
random()으로 Token을 생성하면 표현식이 두 번 평가되어 Create와 Change Object에서 다른 값이 들어갈 수 있습니다. Change Object 하나에서 Token과 Details를 분리해서 Set하거나, Token Set을 별도 Change Object로 먼저 실행한 후 Details에서 $Object/Token을 참조하세요.6. 교훈 — 삽질에서 배운 것
PendingLink가 세션 ID 기반이라, SSO처럼 세션이 바뀌는 환경에서는 근본적으로 작동하지 않습니다. 공식 문서에는 이 제약이 명시되어 있지 않습니다.
Mendix 앱이 SPA 모드일 때 /p/페이지이름 URL 직접 접근이 안 됩니다. 모든 라우팅이 /#/ hash 기반으로 동작하며, 외부에서 특정 페이지를 직접 여는 것이 불가능합니다.
Mendix SPA의 HTMLSnippet에서 <script> 태그가 렌더링되지 않는 경우가 있습니다. <img src="x" onerror="JS코드" style="display:none">로 우회하면 확실하게 JS가 실행됩니다.
Outlook 등 메일 클라이언트가 URL의 # 이후를 제거합니다. 토큰 전달은 반드시 query string (?key=value) 방식을 사용해야 합니다.
ADFS SSO가 cont 파라미터를 무시하더라도, 같은 도메인의 쿠키는 SSO 리다이렉트를 거쳐도 유지됩니다. 이 특성을 활용해 로그인 전에 쿠키로 토큰을 저장하고, 로그인 후에 읽어서 딥링크를 실행할 수 있습니다.