사용자_입력에_따른_서버_요청_최적화_전략 - boostcampwm-2024/and04-Nature-Album GitHub Wiki
사용자의 입력에 따라 서버로의 요청을 효율적으로 관리하기 위해 다음과 같은 전략을 고려할 수 있다. 이 전략들은 Kotlin과 Android의 공식 문서를 기반으로 한다.
-
설명: 사용자가 입력을 멈춘 후 일정 시간이 지난 경우에만 서버 요청을 보낸다. 이를 통해 빠른 연속 입력으로 인한 불필요한 요청을 줄일 수 있다.
-
Kotlin Flow 예시:
searchQuery .debounce(300) // 입력 멈춤 후 300ms 대기 .distinctUntilChanged() // 중복된 입력 무시 .flatMapLatest { query -> repository.searchFriends(query) // 서버 요청 } .collect { results -> _searchResults.value = results // 결과 업데이트 }
-
설명: 일정한 시간 간격으로 최신 값을 방출하여 서버 요청을 보낸다. 이를 통해 입력이 빈번하게 발생하더라도 일정한 주기로만 서버 요청을 수행할 수 있다.
-
Kotlin Flow 예시:
searchQuery .sample(1000) // 1초마다 최신 값 방출 .filter { it.isNotBlank() } // 빈 입력 무시 .distinctUntilChanged() // 중복된 입력 무시 .flatMapLatest { query -> repository.searchFriends(query) // 서버 요청 } .collect { results -> _searchResults.value = results // 결과 업데이트 }
-
설명: 입력된 문자열의 길이가 특정 기준 이상일 때만 서버 요청을 보낸다. 이를 통해 너무 짧은 입력으로 인한 불필요한 요청을 방지할 수 있다.
-
Kotlin Flow 예시:
searchQuery .filter { it.length >= 2 } // 최소 2자 이상일 때만 처리 .debounce(300) .distinctUntilChanged() .flatMapLatest { query -> repository.searchFriends(query) } .collect { results -> _searchResults.value = results }
-
설명: 이전에 검색한 결과를 캐시에 저장하고, 동일한 검색어에 대해 캐시된 결과를 반환하여 서버 요청을 줄인다.
-
Kotlin 예시:
private val cache = mutableMapOf<String, List<Friend>>()
fun searchFriends(query: String) { if (cache.containsKey(query)) { _searchResults.value = cache[query]!! } else { viewModelScope.launch { val results = repository.searchFriends(query) cache[query] = results _searchResults.value = results } } }
-
설명: 서버 요청 전에 네트워크 연결 상태를 확인하여, 연결이 없을 경우 요청을 보내지 않고 사용자에게 알림을 제공한다.
-
Kotlin 예시:
if (isNetworkAvailable()) { searchQuery .debounce(300) .collect { query -> val results = repository.searchFriends(query) _searchResults.value = results } } else { // 네트워크 연결 없음 알림 처리 }
-
https://developer.android.com/develop/connectivity/network-ops/reading-network-state?hl=ko
이러한 전략들을 조합하여 사용자 입력에 따른 서버 요청을 효율적으로 관리할 수 있다. 특히, debounce
와 distinctUntilChanged
를 함께 사용하면 불필요한 요청을 효과적으로 줄일 수 있다.
- 정의: 이벤트가 발생한 후 일정 시간 동안 새로운 이벤트가 발생하지 않으면 마지막 이벤트를 처리한다.
- 동작 방식: 이벤트가 발생할 때마다 대기 시간을 초기화하며, 대기 시간이 끝날 때까지 새로운 이벤트가 발생하지 않은 경우에만 마지막 이벤트를 처리한다.
-
사용 사례:
- 사용자 입력 처리: 검색창에서 사용자가 입력을 멈춘 뒤 최종 입력 값으로만 서버 요청을 보낸다.
- 실시간 입력 최적화: 연속 입력 이벤트를 안정화한 후 결과를 처리한다.
- 장점: 불필요한 작업(이벤트)을 모두 무시하고 안정화된 결과만 제공한다.
Debounce
는 연속적인 입력 이벤트 중 마지막 이벤트만 처리하여 불필요한 작업을 줄이는 기법이다. 순간 검색(Instant Search) 기능에서 이를 효과적으로 활용할 수 있다.
-
Debounce를 사용하지 않았을 때의 문제점: 사용자가 검색어를 입력할 때마다 서버 요청을 보낸다면, 짧은 시간 동안 많은 요청이 발생하여 서버 과부하가 생길 수 있다.
-
해결책:
Debounce
를 적용하여 사용자가 입력을 멈춘 후 일정 시간이 지난 마지막 텍스트 이벤트만 서버로 전달한다. -
예시:
사용자가 "안드로이드"를 검색할 때, 매 입력마다 서버 요청을 보내는 대신 마지막 입력("안드로이드")만 서버에 전달한다.
-
입력 과정:
ㅇ → 아 → 안 → 안ㄷ → 안드 → 안드로 → 안드로이 → 안드로이드
-
요청 과정(With Debounce):
(지연 후) "안드로이드"만 서버로 전달
-
- Debounce를 사용하지 않았을 때의 문제점: 회원가입 시 이메일 또는 ID 중복 검사를 위해 API 요청을 호출할 때, 입력 이벤트마다 호출하면 비효율적이다.
-
해결책:
Debounce
를 사용해 적절한 지연 시간을 두고, 마지막 유효한 텍스트만 API 호출에 사용한다. -
추가 개선:
- 입력 필드가 비어 있거나(빈 문자열), 부적절한 텍스트(예: 잘못된 형식의 이메일)가 포함된 경우, 이를 필터링하여 서버 요청을 줄일 수 있다.
sample
연산자는 데이터 스트림에서 지정된 시간 간격마다 최신 값을 방출하여 처리하는 데 유용하다. 이를 통해 빠르게 발생하는 이벤트 중에서 주기적으로 최신 상태를 추출할 수 있다.
sample
연산자의 특징:
- 주기적 샘플링: 지정된 시간 간격으로 데이터 스트림을 샘플링하여 최신 값을 방출한다.
- 이벤트 손실 가능성: 샘플링 주기 내에 여러 이벤트가 발생하면, 그 중 일부는 무시되고 가장 최신의 값만 방출된다.
사용 예시:
사용자가 입력하는 동안 일정 시간 간격으로 최신 입력 값을 처리하고자 할 때 sample
연산자를 활용할 수 있다. 예를 들어, 사용자가 검색어를 입력할 때마다 서버에 요청을 보내는 대신, 1초마다 최신 입력 값을 서버에 전달하여 불필요한 요청을 줄일 수 있다.
Throttle은 이벤트의 빈도를 제한하여 연속적으로 발생하는 이벤트 중 일부만 처리하는 기법이다.
이를 통해 빠르게 발생하는 이벤트를 제어하고, 성능을 최적화하거나 불필요한 작업을 줄일 수 있다.
Throttle은 두 가지 주요 방식으로 나뉜다: ThrottleFirst와 ThrottleLast이다.
- 정의: 지정된 시간 간격 내에서 발생한 첫 번째 이벤트만 처리한다.
- 특징: 이후 간격 내 발생한 이벤트는 무시되며, 간격이 끝난 후 다시 첫 번째 이벤트를 처리한다.
-
사용 사례:
- 버튼의 중복 클릭 방지: 사용자가 버튼을 빠르게 여러 번 클릭해도 일정 간격 동안 한 번만 클릭 이벤트가 처리되도록 구현한다.
- 서버 요청 제한: 사용자 입력 등으로 인해 과도한 서버 요청이 발생하지 않도록 요청 빈도를 제한한다.
- 정의: 지정된 시간 간격이 지나면 해당 간격 내 발생한 마지막 이벤트를 처리한다.
- 특징: 간격 내의 다른 이벤트는 무시되며, 간격이 끝난 시점에 마지막 이벤트를 방출한다.
-
사용 사례:
- 주기적 상태 업데이트: 실시간 센서 데이터나 UI 상태를 최신 값으로만 갱신한다.
- 최신 데이터 유지: 일정 간격으로 최신 상태를 반영하며 이전 데이터는 무시한다.
- 주기적으로 데이터를 갱신하거나 최신 상태만 유지하고 싶을 때 적합하다.
ThrottleFirst는 Kotlin의 sample
연산자를 사용하여 쉽게 구현할 수 있다.
ThrottleLast는 별도의 로직을 작성하여 구현해야 한다.
특징 | ThrottleFirst (sample) | ThrottleLast (커스텀 구현) |
---|---|---|
처리 방식 | 시간 간격 내 첫 번째 이벤트만 처리 | 시간 간격 내 마지막 이벤트만 처리 |
사용 목적 | 버튼 중복 클릭 방지, 요청 빈도 제한 | 최신 데이터 유지, 주기적 업데이트 |
Kotlin에서 구현 방법 | sample 연산자로 구현 가능 | 직접 구현 필요 |