봄날은 갔다. 이제 그 정신으로 공부하자

네트워크 비동기 통신에서 중복 요청 방지 본문

android Tip

네트워크 비동기 통신에서 중복 요청 방지

길재의 그 정신으로 공부하자 2021. 3. 5. 11:04

해당 글은 이전에 작성한 중복 클릭 방지의 후속편 입니다.

https://als2019.tistory.com/51

 

 

시간차를 이용한 중복 방지는 사용자가 버튼을 난타했을 때 여러번 처리되는 문제를 해결할 수 있지만

네트워크와 같이 비동기로 처리되는 경우 시간차를 이용한 방식은 정답이 될 수 없습니다.

 

간단히 생각하면 아래와 같은 문제가 발생할 수 있기 때문 입니다.

로그인 버튼을 눌렀을 때 상세보기 버튼을 클릭 시 상세보기 화면으로 이동한다고 했을 때

시간차(1sec)를 이용한 중복 방지만 적용한다면 사용자가 로그인 버튼을 난타해도  1초 동안은 로그인 버튼이 

다시 눌리는 일이 없으므로 로그인 화면은 1번만 활성화되게 됩니다. 

아름답고 안전한 시나리오 입니다. ^___^

 

하지만 네트워크 처리와 같은 비동기 방식이 중간에 개입한다면 문제는 달라지게 됩니다.

위 시나리오에 한가지를 추가해보겠습니다.

추가되는 시나리오는 다음과 같습니다.

“사용자가 로그인 버튼을 눌렀을때 해당 사용자의 기본 단말 정보를 서버로 전송한 후 로그인 화면으로 이동한다.”

 

별거 아닌 것 같지만 위 시나리오가 추가되면 아주 골때리는 일이 발생할 우려가 있습니다.

무슨일이냐하면 난타로 인해 발생하는 로그인 화면 중복 실행이 간헐적으로 발생할 수 있다는 점 입니다.

 

사용자가 로그인 버튼을 난타 한다고 가정했을 때 1초 이내에 네트워크 작업이 완료되지 않을 경우, 

로그인 버튼은 다시 눌리게 되기 때문 입니다.

즉, 아래와 같이 로그인 화면이 두번 실행되는 경우가 발생합니다.

그러면 사용자는 로그인하고 왔는데 다시 로그인해야하는 황당한 경험을 하게 됩니다. ㅠ_ㅠ

 

이러한 문제 때문에 제가 이전에 작성한 글에서도 시간차를 이용한 방법은 중복 클릭 방지의 완벽한 대안은 아니다라고

언급했었습니다. ㅠ_ㅠ

 

물론 중복 클릭 방지 시간을 1초에서 2초로 늘리는 꼼수가 있지만 이는 완벽한 해결책은 될 수 없습니다. ㅠ_ㅠ

 

비동기 방식에서 중복 요청을 방지하려면 당연한 이야기겠지만 동일한 비동기 요청이 중복으로 요청되지 않도록 처리하면 됩니다.

너무 뻔한 소리인가요… -_-;;;

 

저같은 경우는 비동기 중복 요청 방지를 위해 List를 사용합니다.

왜? Map이 아닌 List를 사용하지 않는 분도 계실 수 있겠으나 관리하는 아이템의 갯수가 10개 넘어가는 경우가 거의 없고

Key 외에는 다른 값이 필요 없으므로 List가 사용에 적합합니다. 

Map을 선호하시면 Map을 사용해도 무관합니다.

 

비동기 요청 전에 자신의 Key(Method와 URL 조합)를 List에 추가하고 요청이 완료(성공이든 실패이든)되면 List에서 

해당 Key를 삭제해 줍니다. 

즉, List에 Key값이 있는 경우, 해당 요청이 처리 중이므로 요청해서는 안됩니다.

 

이 부분을 코드로 구현하면 아래와 같습니다.

object DoubleRequestChecker {

    var responsingList: MutableList<String> = ArrayList()

    fun addResponse(method: String, url: String): Boolean? {
        val key = generateKey(method, url)
        if (responsingList.contains(key)) {
            return false
        }
        responsingList.add(key)
        return true
    }

    fun clear(method: String, url: String) {
        val key = generateKey(method, url)
        responsingList.remove(key)
    }

    fun clearAll() {
        responsingList.clear()
    }
    
    private fun generateKey(method: String, url: String): String {
        return method + url
    }
}

 

 

이렇게 만들어진 Object를  아래와 같이 사용해주면 됩니다.

// 비동기 네트워크 요청 전
// false이면 요청 중이 아닌 상태이므로 해당 요청 처리
// true이면 중복 요청이므로 해당 요청 처리하면 안됨.
if(!DoubleRequestChecker.addResponse(method, url)){
    // 비동기 네트워크 처리 
    …
    // 완료되면 아래와 같이 목록에서 삭제 요청
    DoubleRequestChecker.clear(method, url)
}

 

그럴리는 없겠지만 주의사항

Key 생성시 절대 Timestamp를 사용한 unique 값을 만들어 사용하면 안됩니다.

unique key값을 사용하는 경우, 동일한 요청이라도 다른 요청으로 인식될 있기 때문 입니다.

정리

위 네트워크 중복 방지의 핵심은 중복 요청을 확인하는 Key 값에 있습니다.

저는 간단한 예시를 통해 method와 URL로 key를 생성했지만 경우에 따라 그것만으로 부족한 경우가 있을 수 있습니다.

그럴 경우에는 요청하는 Body Param 등을 Key 생성에 활용하면 됩니다.

 

UI Thread에서 비동기 상황까지 고려하여 중복 방지 기능을 추가할 수 있지만 그렇게 하는 경우 고려해야하는 부분이 너무 많아지므로

특별한 경우가 아니라면 UI Thread에서의 중복 방지는 시간차 방식을, 비동기 네트워크 요청 방식에서는 방식을 사용하면 중복 요청을 막을 있습니다.

Comments