문서 이력
  • 2022.02.13. 포스팅
  • 2022.02.23. 문구 수정


이 글은 한투 모바일 API 샘플 앱 만들기 시리즈의 첫번째 글입니다.

한국투자증권 API (CommExpert)를 사용하여 안드로이드 어플리케이션을 만드는 과정을 설명합니다. 최대한 쉽게 설명할 예정이지만, 이해하기 힘든 부분은 메일이나 덧글로 질문 부탁드립니다.

한국투자증권에서 제공하는 API, 가이드 문서 및 샘플 앱은 https://securities.koreainvestment.com/main/customer/systemdown/OpenAPI.jsp 에서 참고하실 수 있습니다.

개발환경은 다음과 같습니다.

OS : macOS Big Sur
IDE : Android Studio 2021.1.1
실행 Android 기기 : 갤럭시 S20(Android 10, Android 11) 2대

프로젝트 생성

Empty Activity로 프로젝트를 생성합니다.

qry

CommExpert 라이브러리 다운로드

CommExpert 라이브러리는 한국투자증권에서 제공하는 안드로이드용 라이브러리 입니다. 한국투자증권 사이트에서 직접 다운 받으신 후 라이브러리를 빌드하여 사용하셔도 되지만, 지금은 간편하게 제가 미리 만들어놓은 라이브러리 파일을 사용하도록 하는 방법을 말씀드리겠습니다.

라이브러리 파일은 아래의 압축파일을 다운로드합니다.

다운로드

라이브러리 import

다운 받은 총 3개의 파일 전부를 프로젝트 폴더의 “app/libs”에 복사합니다. 복사를 위해서 Android View를 Project View 로 변경하여 줍니다.

qry

Project View 선택

libs 폴더 내부에 aar 라이브러리 파일들을 넣어줍니다.

qry

libs 폴더 내부에 aar 라이브러리 파일들을 넣어줍니다.

app/build.gradle 에 dependencies를 다음과 같이 추가해줍니다.

build.gradle 파일

주의) 현재 build.gradle 파일은 프로젝트 내부에 총 2개가 있습니다.

다음 3개의 줄을 추가해 줍니다.

implementation files('libs/iTGUtilLib-release.aar')
implementation files('libs/mVigsEngine-release.aar')
implementation files('libs/CommExpert-release.aar')

AndroidManifest.xml 수정

app/src/main/AndroidManifest.xml 파일을 열어 다음을 추가합니다.

<!-- 권한 설정 -->
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>

<uses-permission android:name="android.permission.READ_PHONE_STATE" android:maxSdkVersion="29"/>
<uses-permission android:name="android.permission.READ_PHONE_NUMBERS"/>

<!-- application -->
<application
    ...

    android:requestLegacyExternalStorage="true"
    android:networkSecurityConfig="@xml/network_security_config"
    android:usesCleartextTraffic="true"
    >

<uses-library android:name="org.apache.http.legacy" android:required="false"/>

권한 중 READ_PHONE_STATE 권한은 전화번호 API 에 요청할 때 사용되는 권한입니다.(ex. 주소록) API 함수 중에 특정 함수를 사용하는 경우, Android 11 (API 레벨 30) 이상에서는 대신 READ_PHONE_NUMBERS 권한을 요청해야 합니다. android:maxSdkVersion=”29” 는 안드로이드 10 이하에서만 해당 권한을 요청하겠다는 의미입니다. (참조 : 안드로이드 개발자 사이트)

네트워크 관련 설정 파일 추가

app/src/main/res 폴더에 xml 폴더를 추가하고 network_security_config.xml 파일을 생성합니다.

디렉토리를 생성합니다

디렉토리를 생성합니다

이름이 network_security_config인 xml 파일을 생성합니다.

이름이 network_security_config인 xml 파일을 생성합니다.

파일의 내용을 다음과 같이 변경합니다.

<?xml version="1.0" encoding="utf-8"?>

<network-security-config>
    <base-config cleartextTrafficPermitted="true">
        <trust-anchors>
            <certificates src="system" />
            <certificates src="user" />
        </trust-anchors>
    </base-config>
</network-security-config>

MainActivity 구현

자동으로 Gradle sync 작업이 이루어지지 않았다면, 안드로이드 스튜디오 오른쪽 위의 다음 아이콘을 클릭해 동기화를 진행합니다.

자동으로 생성된 MainActivity.kt에 코드를 구현하여 서버에 연결을 시도합니다. 이번에 구현해 볼 코드는 다음과 같이 구성됩니다.

- CommExpert 라이브러리 초기화
- 라이브러리 초기화 결과 처리
- 안드로이드 런타임 권한 요청

CommExpert 라이브러리 초기화

CommExpert 라이브러리를 동작시키기 위해서, 최초 앱 실행 시 초기화 동작이 필요합니다. 라이브러리를 초기화하는 함수를 구현해보도록 하겠습니다.

MainActivity.kt의 MainActivity Class에 다음 멤버 함수를 추가합니다.

import com.commexpert.CommExpertMng // 자동으로 import가 되지 않는다면 추가해줍니다.

class MainActivity : AppCompatActivity() {
		... // 기존 코드 생략
		
		private val TAG : String = "HantooSample" // 로깅용 태그

		private fun startApp() {
        // 초기화 및 통신 접속
        CommExpertMng.InitActivity(this)
        CommExpertMng.InitCommExpert(this)

        //Listener 셋팅
        CommExpertMng.getInstance().SetInitListener(this@MainActivity)

        //"0"리얼 ,  "1" 모의투자
        CommExpertMng.getInstance().SetDevSetting("1")
    }
}

현재 SetInitListener를 호출하는 라인이 오류로 표시될 것입니다. 인자로 넘겨주는 MainActivity가 IExpertInitListener 객체가 아니기 때문입니다. 다음 단계에서 해결해보도록 하겠습니다.

라이브러리 초기화 결과 처리

라이브러리 초기화 함수에 대한 결과를 처리하는 IExpertInitListener를 상속받는 클래스의 객체가 필요합니다.

이번 예제에서는 MainActivity를 IExpertInitListener 인터페이스를 상속받도록 하여 구현해보도록 하겠습니다. MainActivity에 상속을 추가하고, override 함수를 구현합니다.

class MainActivity : AppCompatActivity(), IExpertInitListener {
		... // 기존 코드 생략

		override fun onSessionConnecting() {
        Log.d(TAG, "서버 접속 시작")
    }

    override fun onSessionConnected(isSuccess: Boolean, msg: String?) {
        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show()
        if (msg != null) {
            Log.d(TAG, msg)
        }
    }

    override fun onAppVersionState(p0: Boolean) {
        Log.d(TAG, "라이브러리 버젼체크 완료.")

    }

    override fun onMasterDownState(p0: Boolean) {
        Log.d(TAG, "Master 파일 DownLoad...")

    }

    override fun onMasterLoadState(p0: Boolean) {
        Log.d(TAG, "Master 파일 Loading...")

    }

    override fun onInitFinished() {
        Log.d(TAG, "초기화 작업 완료")
        Toast.makeText(this, "초기화 작업 완료", Toast.LENGTH_SHORT).show()

        // TODO : Login
    }

    override fun onRequiredRefresh() {
        Log.d(TAG, "재접속 완료")
    }
}

안드로이드 권한 요청

AndroidManifest.xml에 선언한 권한 리스트에서 런타임 권한에 대한 요청이 필요합니다. 즉, 앱이 실행중에 권한 허가를 받아야 하므로, 앱 실행 직후에 권한을 요청합니다. (참고 : 안드로이드 개발자 사이트)

MainActivity.kt의 MainActivity Class에 다음 멤버 함수들을 추가합니다.

class MainActivity : AppCompatActivity(), IExpertInitListener {
    ... // 기존 코드 생략

    private fun requestPermissions() : Boolean {
        // 앱에서 필요한 권한 리스트
        val permissions: Array<String> = arrayOf( Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.READ_PHONE_NUMBERS, Manifest.permission.READ_PHONE_STATE)
        // 요청 해야할 권한 리스트
        val needPermissions : ArrayList<String> = ArrayList<String>()

        // 필요 권한 리스트 중 아직 허용되지 않은 권한들만 추가
        for (perm in permissions){
            if (perm == Manifest.permission.READ_PHONE_STATE && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R){
                // Android 11 이상에서는 READ_PHONE_STATE 권한 체크 X
            }
            else if (ActivityCompat.checkSelfPermission(this, perm) != PackageManager.PERMISSION_GRANTED){
                needPermissions.add(perm)
            }
        }

        // 요청 리스트가 비어있지 않다면 권한 요청 팝업
        return if (needPermissions.isNotEmpty()){
            Log.d("HantooSample", "권한 요청")

            ActivityCompat.requestPermissions(this, needPermissions.toArray(arrayOfNulls<String>(needPermissions.size)),0 )
            true
        } else
            false
    }

    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        if (requestCode == 0) {
            if (grantResults.isNotEmpty()) {
                for (i in grantResults.indices) {
                    if (grantResults[i] != PackageManager.PERMISSION_GRANTED) {
                        Toast.makeText(this, "앱 실행을 위한 권한이 취소 되었습니다", Toast.LENGTH_LONG).show();
                        Log.e(TAG, "권한 취소 : ${permissions[i]}")
                        finishAndRemoveTask()
                        return
                    }
                    else {
                        Log.d(TAG, "권한 승인 : ${permissions[i]}")
                    }
                }

                Toast.makeText(this, "앱 실행을 위한 권한이 설정 되었습니다", Toast.LENGTH_LONG).show()
                startApp()
                return

            } else {
                Toast.makeText(this, "앱 실행을 위한 권한이 취소 되었습니다", Toast.LENGTH_LONG).show()
                Log.e(TAG, "grantResults is Empty")
            }
        }

        finishAndRemoveTask()
    }
}

requestPermissions 함수는 앱에서 필요한 런타임 권한 중 아직 허용되지 않은 권한에 대해 요청 API 함수를 호출합니다. 필요한 권한이 모두 이미 허용되어 있다면, false를 반환합니다.

onRequestPermissionsResult 함수는 권한 요청 팝업에 대한 결과를 처리하는 오버라이딩 함수입니다. 권한 요청이 취소되었을 경우, 액티비티를 종료합니다. 권한이 전부 허용 되었을 경우, 초기화 함수를 호출합니다.

onCreate 구현

지금까지 구현한 함수를 최종적으로 onCreate 함수에서 호출합니다. MainActivty의 자동으로 생성된 onCreate 함수에 다음 코드를 추가합니다.

if (!requestPermissions()) // 권한이 이미 전부 허용되었다면,
    startApp() // 초기화 함수 바로 호출

앱 실행 및 초기화 결과 확인

권한 요청 팝업을 확인합니다.

라이브러리 초기화에 성공하였을 경우, 서버 접속 성공 토스트 메시지를 확인할 수 있습니다.

안드로이드 로그캣을 통해 초기화 과정 및 결과를 확인합니다.

LBS 서버 연결만 성공하고 서버 재접속을 계속 시도하다가 연결이 되지 않고 종료되는 현상이 나타나면, 앱을 완전히 삭제 후 다시 Run 해보시길 바랍니다.

여기까지, 프로젝트를 생성하고 설정하여 서버에 연결하는 코드까지 구현해보았습니다. 다음은 로그인 기능 구현에 대해 포스팅 할 예정입니다.

포스팅에서 구현되는 샘플 프로젝트의 코드는 다음 Github 저장소에 공유하였습니다 : Link

Leave a comment