분명, 모든 컴포저블 함수의 파라미터 타입을 stable로 맞춰둔 상황이었다. 컴포즈 매트릭스 실행 결과 모든 함수들이 skippable로 표시될 터였다. 그래서 불필요한 리컴포지션에 따른 문제를 알기 힘들었다.
위 사진은 문제가 되었던 코드이다. LazyColumn
상, 눈에 보이는 아이템의 마지막 포지션이 총 아이템 갯수와 일치했을 경우, 추가 페이지네이션을 트리거하기 위한 코드이고 해당 메서드는 onScrollToEnd()
메서드이다. 하지만 이 메서드가 2번 호출되는 문제가 있었고, 이로 인해 동일한 API를 2번 호출했었다. 원인이 뭔지 몰라 각 부분에 로그를 찍고 분석해보았다.
위 로 그를 보면 알다시피 give
로 표시된 부분이 API를 호출하는 부분이다. 근데 2번 호출하고 있었다. 문제가 무엇인지 로그를 보며 찬찬히 분석을 했다. 처음엔 로그를 봐도 도통 알 수 없었다. 하지만 계속 분석한 결과, 아주 세밀한 시간에 발생한 타이밍 이슈란걸 알 수 있었다.
위 사진은 상단 로그의 윗 부분을 캡쳐한 것이다. 위 로그를 분석해봤을 때, '결2'부분에서 처음 false
가 찍혀 LaunchedEffect
가 타진것은 납득할 수 있다. 하지만 그 밑, '왼3', '오3'부분에서 각각 'null'과 '-1'이 아니란 사실이 눈에 들어왔다. 이는 즉, API호출이 완료되고, UiState에 값이 할당됨으로 인해 LaunchedEffect
내부에선 값이 순간적으로 할당된 것이었다. 따라서 예상되었던 결과는 '왼3', '오3'에서도 'null'과 '-1'이 나왔어야 했고, '결3'에서도 false
가 출력되어 if (isScrolledToTheEnd && myFeedListVo.hasNext && myFeedListVo.lastId != null)
의 출력 결과는 false
가 나왔어야 했다. 하지만 그러지 못해 true
가 되었고 이로 인해 onScrollToEnd
메서드가 처음 호출된 것이다.
그 이후, UiState에 값이 바인딩됨으로 인해, isScrolledToEnd
변수가 true
로 바뀜으로 인해 리컴포지션을 시작했을 것이고 이때야말로 내가 예상했던 결과가 발생했다.
따라서 위 로직은 ui에 바인딩된 아이템이 없을 경우, 해당 로직 실행을 막는다면 해당 문제를 개선할 수 있게되었다. 아래는 문제를 개선한 코드이다.
로그를 찍어봤을 때에도 예상했던대로 잘 찍히는걸 확인할 수 있다.