Skip to content
마지막 작성자: hyejung-song
최근 업데이트: 25. 7. 8. 오후 1:09

iOS 개발 가이드 ​

iOS에서 모바일 신분증 App2App 연동을 구현할 때 필요한 URL 스킴 방식의 흐름과 예제 코드를 정리했어요. 앱 설치 확인부터 M200 요청, 앱 호출, VP 제출까지 단계별로 따라 할 수 있어요.

1. URL scheme indirect mode ​

모바일 신분증 앱 설치 여부 확인 ​

  • Info.plist의 Queried URL Schemes 항목에 다음을 추가해요
구분운영/개발테스트베드
SchemesMobileIDtMobileID
Bundle IDkr.go.mobileidkr.go.mobileid.tbe
Universal Link사용 가능준비 중

App2App 연동 흐름도

앱 설치 여부 확인 예시 ​

swift
static func canAppOpen(schemes: String) -> Bool {
    if let appUrl = URL(string: "\(schemes)://") {
        return UIApplication.shared.canOpenURL(appUrl)
    }
    return false
}

앱 호출 예시 ​

swift
static func appOpen() {
    if CommonUtility.getAppOpen(schemes: "MobileID") {
        if let appUrl = URL(string: "MobileID://") {
            if #available(iOS 10.0, *) {
                UIApplication.shared.open(appUrl, options: [:], completionHandler: nil)
            } else {
                UIApplication.shared.open(appUrl)
            }
        }
    } else {
        print("모바일 신분증 앱 미설치")
    }
}

SP서버로 M200 메시지 요청 ​

swift
func requestProfile() {
let url = "SP 서버 주소"
let param: [String: Any] = ["mode": "indirect", "svcCode": "mdl.1"]

    RestManager.request(urlString: url, param: param, requestType: .post) { resp in
        let msgData = CommonUtility.convertStringToDictionary(
            text: IWCommonUtil.decodeBase64URL(resp["data"] as! String) ?? ""
        )
        let m200Str = msgData?["m200Base64"] as? String ?? ""
        self.app2AppLogin(
            mode: msgData?["mode"] as! String,
            msg: m200Str
        )
    } failureClosure: { msg in
        // M200 수신 실패
    }
}
  • 요청 예시
json
{
  "mode": "indirect",
  "svcCode": "mdl.1"
}
  • 응답 예시
json
{
  "result": true,
  "data": "eyJjbWQiOiI1MzAiLCJtb2RlIjoiZGly…"
}
  • m200Base64 디코딩 예시
json
{
  "cmd": "530",
  "m200Base64": "eyJ0eXBlIjoibWlwIiwidmVyc2lvbiI6I…",
  "mode": "indirect",
  "svcCode": "mdl.1"
}
  • m200 메시지 구조
json
{
  "ci": false,
  "cmd": "200",
  "host": "SP서버URL",
  "image": "https://www.mobileid.go.kr/resources/images/main/mdl_ico_homepage.ico",
  "mode": "indirect",
  "profile": "eyJlbmNvZGluZyI6IlVURi04IiwiaW…",
  "telno": false,
  "trxcode": "20240813025840401CBEAB157",
  "type": "mip",
  "version": "1.0.0"
}

모바일 신분증 앱 호출 (Scheme 방식) ​

swift
func app2AppLogin(mode: String, msg: String) {
    let hostScheme = "MobileID"
    let clientScheme = "spDemo"
    let actionNameRequest = "verify"

    var urlComps = URLComponents(string: "\(hostScheme)://\(actionNameRequest)")
    var queryItems = [URLQueryItem]()
    queryItems.append(URLQueryItem(name: "clientScheme", value: clientScheme))
    queryItems.append(URLQueryItem(name: "data", value: msg))
    queryItems.append(URLQueryItem(name: "mode", value: mode))
    urlComps?.queryItems = queryItems

    guard let url = urlComps?.url else {
        print("URL 생성 실패")
        return
    }

    UIApplication.shared.open(url, options: [:]) { opened in
        // 앱 호출 성공
    }
}

VP 제출 (M400) ​

Indirect 방식 ​

swift
func sendVPIndirect(vp: String) {
    var urlComps = URLComponents(string: "\(self.presentationObj.clientScheme)://verify")
    var queryItems = [URLQueryItem]()
    let sendVp = self.makeSendVpStr(vp: vp)

    queryItems.append(URLQueryItem(name: "success", value: "true"))
    queryItems.append(URLQueryItem(name: "data", value: sendVp))
    urlComps?.queryItems = queryItems

    guard let url = urlComps?.url else { return }
    UIApplication.shared.open(url, options: [:])
}

makeSendVPStr 함수 ​

swift
func makeSendVPStr(
    presentType: PRESENT_TYPE,
    trxcode: String,
    nonce: String,
    authType: [String],
    encryptType: VC_ENCRYPT_TYPE,
    vp: String
) -> String {
    let dids = walletService.getDids()!

    if presentType == .ZKP {
        return """
        {
          "type": "mip",
          "version": "1.0.0",
          "cmd": "400",
          "trxcode": "\(trxcode)",
          "request": "presentation",
          "vp": {
            "presentType": \(presentType.rawValue),
            "encryptType": \(encryptType.rawValue),
            "keyType": 2,
            "zkpNonce": "\(nonce)",
            "timezone": "\(self.getTimeZone())",
            "type": "VERIFY",
            "data": "\(vp)"
          }
        }
        """
    } else {
        return """
        {
          "type": "mip",
          "version": "1.0.0",
          "cmd": "400",
          "trxcode": "\(trxcode)",
          "request": "presentation",
          "vp": {
            "presentType": \(presentType.rawValue),
            "encryptType": \(encryptType.rawValue),
            "keyType": 2,
            "authType": \(authType),
            "did": "\(dids.id)",
            "nonce": "\(nonce)",
            "timezone": "\(self.getTimeZone())",
            "type": "VERIFY",
            "data": "\(vp)"
          }
        }
        """
    }
}

Direct 방식 ​

swift
func sendVPDirect(vp: String) {
    var url: String

    if let callbackURL = IWQRData(jsonString: self.presentationObj.profile).profile.callBackUrl,
       callbackURL.count > 0 {
        url = callbackURL
    } else if let host = self.presentationObj.host {
        url = "SP 서버 주소/mip/vp"
    } else {
        print("VP Direct url 없음")
        return
    }

    let sendVp = self.makeSendVpStr(vp: vp)
    let param: [String: Any] = [
        "data": IWCommonUtil.encodeBase64URL(with: sendVp) ?? "",
        "result": true,
        "success": true
    ]

    RestManager.request(
        urlString: url,
        param: param,
        requestType: .post
    ) { resp in
        print("VP Direct 제출 성공: \(resp)")
    } failureClosure: { err in
        print("VP Direct 제출 실패: \(String(describing: err))")
    }
}

SP 서버로 VP 제출 ​

swift
func requestVerify() {
    let url = "SP 서버 URL/mip/vp"
    let vpParam = """
    {
      "type": "mip",
      "version": "1.0.0",
      "cmd": "400",
      "request": "presentation",
      "trxCode": "\(self.trxCode)",
      "vp": "\(self.vp)"
    }
    """

    RestManager.request(
        urlString: url,
        param: vpParam,
        requestType: .post
    ) { resp in
        // 성공 처리
    } failureClosure: { msg in
        // 실패 처리
    }
}

2. URL scheme direct mode ​

  • mode를 direct로 설정
  • M200 메시지에 SP 서버 주소 포함
  • 자세한 전송 방식은 3.1.4.2 참조
  • 웹사이트를 통해 모바일 신분증 앱을 여는 방식
  • verify.html 웹페이지를 통해 앱 호출
  • URL은 1024자 이하로 유지해야 함

예시:

text
https://mobileid.go.kr/verify.html?mode=direct&data=BASE64_ENCODED_M200&clientScheme=spDemo