본문 바로가기
안드로이드 코딩일지

[Android] 안드로이드 10 이상 기기에서 클립보드 액세스가 안될 때

by HAERO_KR 2021. 2. 20.

앱을 개발하다보면 사용자의 클립보드 데이터를 활용하는 기능이 있다. 대표적으로 카카오뱅크, 토스와 같은 뱅킹 앱의 '복사한 계좌로 이체하기' 기능이 있다. 앱을 진입할 때마다 클립보드에 있는 데이터를 액세스하여 만약 계좌 번호 정보가 담겨있다면 해당 계좌로 빠른 송금을 할 수 있도록 토스트 메세지를 띄워주는 기능이다. 사용자 경험 상 엄청난 편리함을 제공하는 기능이다.

 

카카오뱅크의 '복사한 계좌로 이체하기' 기능 (상당히 꿀 기능이다)

 

이런 기능이 구현될 수 있었던 것은 Android, iOS 가 클립보드 접근 프레임워크를 제공해주기 때문이었다. 그런데 필자는 개발중인 앱에 이 클립보드 프레임워크를 기반으로, 클립보드 데이터를 액세스하고 검사하여 사용상 편리를 제공하는 기능을 구현하던 중 이상한 점을 발견했다. 당연하게도 Android 공식 문서에서 클립보드 프레임워크 사용법을 참조하여 나와있는대로 구현을 했으나, 제대로 동작하지 않았고 관련하여 열심히 구글링을 해보았지만 스택오버플로우 등의 글들을 보면 필자의 코드와 다른 것이 없는 코드들도 아무 문제가 없어보였다.

 

정말 이 문제를 해결하기 위해 며칠동안 삽질을 했는데, 삽질을 하던 중 드디어 특징을 찾아냈다. 바로 'Android 10' 에서만 동작하지 않는 것이었다. 하필이면 메인으로 사용하던 테스트 기기가 Android 10 이 올려져있었고, 설마 다른 기기도 그러나 하고 테스트 했던 Android 9 기기에서는 너무나도 잘 동작하는 것이었다. 

 

스택오버플로우에서 적절한 답변을 얻지 못했던 것은 Android 10 이라는 키워드가 없어서였고,

모두 Android 10 이 나오기 전의 글들이어서 당시에는 모두 동작상 문제가 없었던 것이다. 

 

다시금 안드로이드 공식 문서가 정말 불친절하다고 느낀게, 클립보드 프레임워크 사용법에 관한 문서에 일절 이러한 내용이

담겨있지 않다는 점이다. 'Android 10 에서는 클립보드 접근 정책이 변경되었습니다' 한 줄만 있었어도

이렇게 삽질을 하지 않았을 텐데 말이다.

 

그래서 관련하여 찾아보니 Android 10 에서는 다음과 같이 정책이 변경되었다고 한다.

(관련자료 : https://developer.android.com/about/versions/10/privacy/changes?hl=ko)

 


변경된 Android 정책

이걸 클립보드 프레임워크 사용법 문서에 좀 넣으란 말이다.

 

클립보드 접근 정책이 변경된 것은 찬성하는 바다. 워낙 관련하여 보안 이슈가 많았기 때문이다. 실제로 iOS 에선 특정 앱에서 클립보드 접근 시 해당 사실을 사용자에게 알려주는 기능을 업데이트 하였었는데, 틱톡과 원신 등 몇몇 앱이 클립보드를 무단 접근하는 사실이 발견되어 화제가 되곤 하였다. 클립보드엔 언제나 개인정보가 담길 수 있기 때문에 이처럼 안드로이드도 보안 정책 강화가 필요하긴 했다.

 

아무튼, 안드로이드 10은 위 사진처럼 아예 접근을 막지는 않고 '현재 포커스가 있는 앱이 아닌 경우' 에는 접근을 막았다.

즉, 백그라운드에서 앱이 클립보드에 무단 접근하는 것을 막았다는 뜻이다.

 

다행히 앱이 메인으로 동작하고 있다면 접근이 가능하게 된다.

사용자 입장에선 클립보드 데이터의 사용 목적이 뚜렷하게 보이면 불안감을 덜수 있기 때문에 이렇게 정책을 변경한 듯 하다.

 

앱 기능으로써 클립보드에 접근하여 데이터를 가져오는 동작을 구현하는 경우 다음과 같이 구현해야 한다.

 

우선 해당 기능을 사용하고자 하는 Activity, Fragment 등에서 onWindowFocusChanged() 를 오버라이딩해야한다.

onWindowFocusChanged() 는 앱이 백그라운드가 아닌 포커싱되고 있는 상태로 동작하고 있다면 true 를 반환하게 되는데,

안드로이드 10 정책에 의해 이 메소드가 true 를 반환한 상태에서만 클립보드 접근이 가능하게 된다.

 

override fun onWindowFocusChanged(hasFocus: Boolean) {
        super.onWindowFocusChanged(hasFocus)
        // 앱이 포커싱되어 실행되는 상태라면
        if (hasFocus){
            // ClipboardManger 객체 생성
            val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
            var pasteData: String = ""

            // 클립보드에 아무것도 없거나 PlainText 가 아닌 데이터가 들어있을 경우 예외처리
            if (!clipboard.hasPrimaryClip()) {
            
            } else if ((clipboard.primaryClipDescription?.hasMimeType(MIMETYPE_TEXT_PLAIN)) == false) {
                
            } else {
                // 클립보드에 PlainText 가 담겨있어 데이터를 가져올 수 있는 경우
                val item = clipboard.primaryClip?.getItemAt(0)!!.coerceToText(applicationContext)
                if (!item.isNullOrEmpty()){
                    pasteData = item.toString()
                }
            }
        }
    }

 

따라서 위와 같이 클립보드 접근 기능을 구현하게 된다면 문제없이 정상 동작한다.

클립보드 데이터 가져오기에 대한 구현은 Android 공식 문서와 내용이 같다.

 

이번 삽질을 통해 또 한 번 뼈 저리게 느낀 것은, 안드로이드 개발자들은 항상 최신 안드로이드에 대한 이해가 필요한 것 같다.

최신 패치에서 어떤 것이 바뀌었는지, 어떤 점을 조심해야하는지 알아두고 올바르게 최신 API 대응을 할 줄 알아야할 것 같다. 

앞으로는 안드로이드 최신 기술 동향에 대해 더욱 깊은 관심을 갖고 변경되는 정책을 더욱 염두해야겠다.

(공식 문서가 불친절한 만큼 안드로이드 개발자들이 더욱 노력하자)

 

필자처럼 클립보드 관련 기능이 담긴 앱을 개발하던 중 같은 문제로 고된 삽질을 하고 있는 사람들에게

이 글이 도움이 됐으면 좋겠다.