고객 통합 가이드
이 문서는 imprun Mobile SDK를 자신의 Android 앱에 통합하려는 고객 개발자 를 위한 가이드입니다.
빠른 시작
1단계: SDK 의존성 추가
// app/build.gradle.kts
dependencies {
implementation ( "kr.co.imprun:mobile-sdk:1.0.0" )
}
SDK는 내부적으로 다음 라이브러리를 사용합니다.
앱에서 이미 사용 중이라면 버전 충돌에 주의하세요:
androidx.webkit:webkit:1.10.0
com.squareup.okhttp3:okhttp:4.12.0
com.google.code.gson:gson:2.10.1
androidx.security:security-crypto:1.1.0-alpha06
2단계: 권한 설정
<!-- AndroidManifest.xml -->
< uses-permission android:name = "android.permission.INTERNET" />
3단계: 엔진 초기화
import kr.co.imprun.sdk.core.AutomationEngine
import kr.co.imprun.sdk.core.SDKConfig
class MyActivity : AppCompatActivity () {
private lateinit var engine: AutomationEngine
override fun onCreate (savedInstanceState: Bundle ?) {
super . onCreate (savedInstanceState)
val config = SDKConfig (
debug = true , // 개발 중에만 true
defaultTimeout = 60_000 // 60초 타임아웃
)
engine = AutomationEngine ( this , config)
engine. initialize { success ->
if (success) {
Log. i ( "MyApp" , "SDK 초기화 완료" )
// 이제 로그인 자동화를 실행할 수 있음
} else {
Log. e ( "MyApp" , "SDK 초기화 실패" )
}
}
}
override fun onDestroy () {
super . onDestroy ()
engine. cleanup () // 리소스 정리 필수
}
}
4단계: 로그인 실행
import kr.co.imprun.sdk.core.LoginCredentials
private fun executeLogin () {
val credentials = LoginCredentials (
id = "사용자아이디" ,
password = "base64인코딩된비밀번호" // Base64.encode(rawPassword)
)
engine. executeLogin ( "HMTAX" , "ID_PW" , credentials) { result ->
result. onSuccess { session ->
// 로그인 성공!
Log. i ( "MyApp" , "사용자: ${ session.userName } " )
Log. i ( "MyApp" , "납세자번호: ${ session.taxPayerNo } " )
Log. i ( "MyApp" , "쿠키: ${ session.cookies } " )
// session 데이터를 서버로 전송하거나 후속 작업 수행
sendSessionToServer (session)
}
. onFailure { error ->
// 로그인 실패
Log. e ( "MyApp" , "로그인 실패: ${ error.message } " )
handleLoginError (error)
}
}
}
SDK 설정
SDKConfig
속성 타입 기본값 설명 debugBoolean false디버그 로그 활성화 logLevelLogLevel INFO로그 출력 레벨 (DEBUG, INFO, WARN, ERROR, NONE) defaultTimeoutLong 30000기본 타임아웃 (ms) useScriptCacheBoolean true스크립트 캐싱 사용 여부 useSecureStorageBoolean true암호화된 저장소 사용 여부
val config = SDKConfig (
debug = false ,
logLevel = LogLevel.WARN,
defaultTimeout = 60_000
)
지원하는 사이트
홈택스 (HMTAX)
사이트 코드 : "HMTAX"
로그인 유형 코드 설명 ID/PW "ID_PW"아이디 + 비밀번호 로그인
입력 형식 :
val credentials = LoginCredentials (
id = "홈택스아이디" ,
password = Base64. encodeToString (
"평문비밀번호" . toByteArray (),
Base64.NO_WRAP
)
)
password는 반드시 Base64 인코딩된 값 이어야 합니다.
평문 비밀번호를 직접 전달하면 로그인에 실패합니다.
반환 데이터 (SessionInfo) :
data class SessionInfo (
val siteCode: String , // "HMTAX"
val timestamp: Long , // 로그인 시각 (epoch ms)
val rawResponse: String , // 원시 JSON 응답
val cookies: Map < String , String >, // JSESSIONID 등
// 홈택스 전용 필드
val userId: String ?, // "pak2251"
val userName: String ?, // "박준*" (마스킹)
val taxPayerNo: String ?, // 납세자식별번호 (마스킹)
val publicUserNo: String ?, // 공개사용자번호
val tin: String ?, // TIN
val loginResultCode: String ?, // "01" (성공)
val loginCertCode: String ? // "03" (ID/PW 인증)
)
에러 처리
에러 유형
에러 코드 의미 대응 FORM_NOT_FOUND로그인 폼 요소를 찾을 수 없음 사이트 UI 변경 가능성 — 관리자에게 문의 TIMEOUT로그인 응답 시간 초과 (30초) 네트워크 확인 또는 재시도 SERVER_ERROR서버가 비-JSON 응답 반환 사이트 점검 중일 수 있음 TRANSACTION_FAILED서버 트랜잭션 실패 재시도 RESULT_ERROR응답 결과 코드 오류 에러 메시지 확인 LOGIN_RESULT_02비밀번호 오류 비밀번호 확인 LOGIN_RESULT_03존재하지 않는 아이디 아이디 확인 LOGIN_RESULT_04계정 잠금 사이트에서 직접 해제 필요 LOGIN_RESULT_05탈퇴한 사용자 - PARSE_ERROR응답 파싱 실패 관리자에게 문의
에러 처리 예제
engine. executeLogin ( "HMTAX" , "ID_PW" , credentials) { result ->
result. onFailure { error ->
val message = error.message ?: "알 수 없는 에러"
when {
message. contains ( "LOGIN_RESULT_02" ) ->
showToast ( "비밀번호가 올바르지 않습니다" )
message. contains ( "LOGIN_RESULT_03" ) ->
showToast ( "존재하지 않는 아이디입니다" )
message. contains ( "LOGIN_RESULT_04" ) ->
showToast ( "계정이 잠겼습니다. 홈택스에서 해제하세요" )
message. contains ( "TIMEOUT" ) ->
showRetryDialog ( "응답 시간이 초과되었습니다. 다시 시도하시겠습니까?" )
message. contains ( "SERVER_ERROR" ) ->
showToast ( "서버 점검 중입니다. 잠시 후 다시 시도하세요" )
else ->
showToast ( "로그인 실패: $message " )
}
}
}
세션 활용
쿠키 기반 후속 요청
로그인 성공 후 SessionInfo.cookies를 사용하여 인증된 API 호출이 가능합니다:
result. onSuccess { session ->
// OkHttp에 쿠키 설정
val cookieHeader = session.cookies.entries
. joinToString ( "; " ) { " ${ it.key } = ${ it. value } " }
val request = Request. Builder ()
. url ( "https://mob.hometax.go.kr/jsonAction.do?actionId=..." )
. header ( "Cookie" , cookieHeader)
. build ()
okHttpClient. newCall (request). enqueue ( object : Callback {
override fun onResponse (call: Call , response: Response ) {
val body = response.body?. string ()
// 인증된 데이터 처리
}
override fun onFailure (call: Call , e: IOException ) { .. . }
})
}
서버로 세션 전송
result. onSuccess { session ->
val payload = JSONObject (). apply {
put ( "siteCode" , session.siteCode)
put ( "userId" , session.userId)
put ( "userName" , session.userName)
put ( "taxPayerNo" , session.taxPayerNo)
put ( "tin" , session.tin)
put ( "cookies" , JSONObject (session.cookies))
put ( "timestamp" , session.timestamp)
}
// 자사 서버로 전송
apiClient. post ( "/api/sessions" , payload)
}
라이프사이클 관리
올바른 사용 패턴
class LoginActivity : AppCompatActivity () {
private var engine: AutomationEngine ? = null
override fun onCreate (savedInstanceState: Bundle ?) {
super . onCreate (savedInstanceState)
engine = AutomationEngine ( this , SDKConfig (debug = BuildConfig.DEBUG))
}
fun startLogin () {
engine?. initialize { success ->
if (success) {
performLogin ()
}
}
}
override fun onDestroy () {
super . onDestroy ()
engine?. cleanup () // WebView, 코루틴, 캐시 정리
engine = null
}
}
cleanup()을 호출하지 않으면 WebView 메모리 누수가 발생합니다
AutomationEngine은 Activity/Fragment의 Context를 필요로 합니다 — Application Context를 사용하지 마세요
한 번에 하나의 AutomationEngine 인스턴스만 사용하세요
보안 참고사항
비밀번호 저장 금지 : 앱에서 비밀번호를 영구 저장하지 마세요. 사용 즉시 메모리에서 삭제하세요.
WebView 격리 : SDK의 WebView는 앱의 다른 WebView와 독립적입니다.
세션 유효기간 : 홈택스 세션(JSESSIONID)은 약 30분 유효합니다.
HTTPS 전용 : SDK는 HTTPS URL만 허용합니다.
데이터 암호화 : SecureStorage는 AES-256-GCM으로 암호화됩니다.
자주 묻는 질문
SDK는 @JavascriptInterface 애노테이션을 사용하므로, ProGuard에서 NativeBridge 클래스가 난독화되지 않도록 해야 합니다.
SDK의 consumer rules가 자동으로 적용되지만, 문제가 생기면: -keepclassmembers class kr.co.imprun.sdk.bridge.NativeBridge {
@android.webkit.JavascriptInterface <methods>;
}
API 24 (Android 7.0)입니다.
atDocumentStart 주입은 AndroidX WebKit 1.5.0+가 필요하며,
미지원 기기에서는 자동으로 onPageFinished 폴백을 사용합니다.
네. Google Play가 포함된 에뮬레이터(API 28+)에서 정상 동작합니다.
여러 사이트 로그인을 순차적으로 실행할 수 있나요?
가능합니다. 단, 각 로그인 사이의 쿠키 격리를 위해
engine.cleanup() → engine.initialize() 사이클을 권장합니다.