Glacier's Daily Log

(Android) JAVA ArrayList의 복사, 복제 - 복제한 ArrayList가 제대로 작동하지 않을 때, Firebase 무료 할당량 초과 본문

Coding/Android

(Android) JAVA ArrayList의 복사, 복제 - 복제한 ArrayList가 제대로 작동하지 않을 때, Firebase 무료 할당량 초과

h__glacier_ 2022. 10. 4. 18:38
반응형

JAVA ArrayList의 복사, 복제

복제한 ArrayList가 제대로 작동하지 않을 때


내가 개발한 '롤챗' 이라는 어플이 생각보다 많은 사용자들을 처리해 나가고 있다.

 

롤챗 - 리그오브레전드 듀오찾기 및 채팅 플랫폼 - Google Play 앱

리그오브레전드 듀오 찾기, 채팅으로 롤 친구 만들기, 전적검색을 '롤챗' 에서 한번에 만나보세요!

play.google.com

이 앱의 DB서버로는 Firebase의 FireStore를 채택했는데, 최근에 문제가 생겼다.

지금까지 Firebase를 서버로하는 앱을 약 30개 이상 개발하여 출시해보았지만

무료 할당량을 초과하는 서비스는 이 롤챗이 처음이였기 때문이다.

 

다른 인기가 꽤 있었던 앱과 크게 사용자 수의 차이가 없는데, 읽기 수가 터무니없이 많이 기록되는게 이상하게 느껴졌다.

그래서 앱의 FireStore 스냅샷 읽기 부분 로직에 문제가 있다고 직감하게 되었고,

오랜만에 소스를 뜯어보았다.

 

일단 앱 자체가 개발한지 조금 되었고 엄청난 구글링으로 조합한 난해한 코드였기 때문에

구조적으로 문제가 매우 많았다..

 

그중 가장 문제가 되어 보였던 메인 유저 리스트 보여주는 프래그먼트를 수정하였다.

로직은 FireAdapter로, 매번 프래그먼트와 액티비티가 onResume 될 때 마다 데이터를 갱신하는 로직이였는데,

현재 페이징이 되어있지 않고 한번 새로고침 할때마다 몇천개의 데이터를 계속해서 갱신하는 상황이였던 것이다.

 

심지어 내 앱에는 Spinner를 이용하여 필터링하는 기능도 추가되어 있었는데

이렇게 티어별로 필터링을 할 때 내부적으로 ArrayList를 가지고 필터링하는것이 아닌,

FireStore의 자체 멀티쿼리 기능으로 계속 부하를 주고 있었다.

 

따라서 이 로직을 모두 뜯어고쳐서

최초에 한번 데이터를 받아와서 ArrayList에 담은 후, Spinner를 선택할 때 마다

ArrayList.removeIf 메소드를 사용해서 자체적으로 필터링하게 구현하기로 했다.


이렇게 구현하려면 ArrayList<UserModel> 총 두개가 필요하다.

왜냐하면 원본 데이터가 들어갈 ArrayList 하나 (RAW) 가 필요하고

필터링 된 데이터가 실시간으로 반영될 ArrayList 하나 (Filtered) 가 필요하기 때문이다.

 

그래서 예를들어 '아이언' 이 선택되었을 때는

 

1. 새로운 ArrayList (Filtered) 를 생성한 후, 원본 데이터 ArrayList (RAW) 를 복사한다.

 

2. 그리고 이렇게 removeIf를 통해서 filtered 리스트에서 아이언이 포함된 데이터를 제외하고 모두 지워버린다.

3. 그리고 리사이클러뷰에 반영한다.

 

이렇게 구현하게 되었다.

 

하지만 구현하는 과정에서 내 마음대로 되지 않아 삽질을 조금 했다.

이유는 너무나도 기초적인 ArrayList 메모리 주소 할당 부분에서 생겼다.

 

나는 처음에 원본 데이터 리스트를 복사하려고

이런식으로 단순히 대입 연산자를 통해서 temList에 List(원본데이터) 를 넣으려고 했다.

그런데 아무리 해도 복사는 되는데, removeIf로 한번 필터링을 거치고 난 이후부터는 제대로 작동하지 않았다.

 

복사는 되는데, removeIf가 일어난 후 리스트가 원본으로 초기화되지 않고 계속해서 삭제된다?

이 현상을 보고 뭔가 메모리주소 관련해서 잘못됨을 인지하였다.

 

찾아보니 JAVA의 ArrayList는 대입연산자로 할당하였을시에

같은 메모리 주소를 할당하기 때문에, temList의 원소를 삭제해도 원본 List의 원소도 계속해서 삭제되고 있었던 것이다.

 

조금 더 자세히 알아보고 싶다면 JAVA ArrayList 깊은 복사, 얇은 복사 키워드로 찾아보면 될 것 같다.

여튼 그래서 위의 코드를

이렇게 변경하여 깊은복사로 바꾸고 나니

모든 작동이 정상적으로 이루어 졌다.

 

오늘의 교훈 :

깊은 복사 (Deep Copy) : 새로운 메모리 공간에 값을 복사하는 것이기 때문에 데이터가 변경되어도 원본 데이터에는 영향이 없다.

얇은 복사 (Shallow Copy) : 객체를 복사할 시 원본 객체의 인스턴스 변수와 같은 메모리 주소를 참조하여 복사함. 따라서 데이터 변경시 원본 데이터에도 영향을 미친다.

 

 

반응형
Comments