물에 살고싶은 개발자

RecyclerView 데이터 꼬임,복제,뭐 등등의 이름으로 불리는 문제 본문

Android

RecyclerView 데이터 꼬임,복제,뭐 등등의 이름으로 불리는 문제

돼지사랑 2019. 2. 26. 13:12

급한 사람들을 위해 결론부터 

RecyclerView를 쓰는데 데이터 꼬임현상이 일어난다면 ViewHolder 안에서 setTag()를 안쓰면 된다!



이 글을 시작하기전에 RecyclerView에 대해 짧게 한마디만 하고 시작하겠다.

RecyclerView는 ListView의 단점을 보완하고 더 유연하게 만들어진 많은 데이터를 출력시켜주는 View다.

ListView의 단점 중 초보개발자를 가장 많이 괴롭히는게 뷰홀더패턴을 이용하지 않으면 생기는 데이터꼬임,복제 현상이다.

그것이 바로 이 글을 쓰게 된 이유인데, RecyclerView를 쓰다가도 데이터가 꼬이거나 복제되는 현상이 나타날 수 있다.

이 글에서는 데이터 꼬임 현상만을 얘기할 것이다.


문제는 RecyclerView 자체가 ViewHolder 패턴을 강제하기 때문에 구조적으로 저런 현상이 나타날 수 없다는 것이다.

그만큼 더 문제해결의 실마리를 찾기가 어렵...다고하면 자존심상하니까 시간이 많이 들었다고 하겠다.



그럼 이제 본격적으로 TMI를 방출해보겠다.

기존의 ListView나 RecyclerView나 데이터꼬임의 원인은 하나다. View의 재사용 때문이다. 

리스트 형태의 UI는 많이들 봣으니 여기에 대해서 말할 필요는 없을것같고, 문제는 이를 출력해주기 위해서는 

View를 오~~~지게 많이 만들어야 한다는 점이다. 이런 이유로 ListView라는 녀석이 등장했다.

(이전에 다른게 있었는지는 모른다. 난 ListView부터 썻으니 여기서부터 얘기하겠음ㅎㅎ)


View를 많이 만드는게 왜 문제가 되느냐? 정말 심플하다. 우리의 스마트폰엔 메모리가 한정되어 있기 때문이다.

안그래도 이미지출력이다 동영상이다 뭐다 해서 메모리 왕창 잡아먹다가 안드로이드 OS한테 쫓겨나서 앱이 죽고 또 메모리줄이느라 머리싸매는게 일상인데

View까지 메모리를 많이잡아먹는다면? 절대 개발자에게 있어서 반가운 상황이 아니다.


그래서 ListView의 가장 중요한 원리중 하나는 일단 화면에 보이는거 +2~3개만 더 만들어두고 걔네를 돌려쓰는것이다.

만약 화면에 ListView에 출력해줄 아이템이 5개가 보인다 하면 위아래로 아이템을 출력할 View를 한두개정도씩은 더 만들어두는것이다. 

이렇게 되면 수십,수백,수천개의 View를 만들어야 하는게 고작 5~9개만 만들어서 돌려써도 된다.

5~9개에 View에다 그냥 데이터만 세팅해주면서 돌려쓰면 수십,수백,수천개가 될지 모르는 수많은 데이터를 전부 출력시켜줄 수 있다.

이해하기 쉽게 비유하자면, 셔터누르면 그림이 옆으로 돌아가는 장난감 카메라 같은 원리라는것이다. 근데 그 화면이 몇개가 반복되는게 아니라 원하는대로 다 나옴ㅋ


자 그럼 ListView가 짱짱맨인거같은데 어떤 단점이 생겼느냐?

비유를 해보자. 사람들이 1시간이상씩 기다리는 맛집이 있는데, 사람을 5명밖에 못받는다. 접시도 사람 1명당 1개씩 나가야하는데 7개밖에 없다.

처음에는 괜찮다. 2개의 여유가 있으니 7개까지는 깔끔하게 주문들어오는대로 나갈 수 있는데, 8명째의 사람이 들어오면서 문제가 생긴다.

접시도 7개밖에 못사는마당에 맛집사장님이 여유가 있을리 없다. 그러다보니 8번손님에게 1번 접시가 나가는데, 

1번손님이 먹었던 음식물찌꺼끼가 묻은채로 나가는일이 생긴다. 


ListView에서도 마찬가지다. 화면에 데이터의 index 10번까지 보인다면, 사용하다보면 index 0번에 있던 데이터의 찌꺼기가 index12~13번쯤에서 튀어나온다.

예를들어 아이템에 체크박스가 있다면, 1번을 체크했을때 12~13쯤에서 처음나오는놈이 체크돼서 나오는 현상이 생긴다.

이를 방지하기 위해 ListView를 사용할땐 개발자가 알아서(구글링으로) ViewHolder 패턴을 적용했다. 


여기서 문제가 발생하는데, 개발자가 ViewHolder 패턴을 구현하다보니 이래저래 문제가 생기기도 하고 여러모로 불편하다.

그래서 [ViewHolder 패턴은 이렇게 써야 문제가 안생긴다] 라는 글이 슬슬 검색결과 1페이지에 나올 즈음에 RecyclerView가 등장하게 된다.



RecyclerView의 등장으로 ViewHolder 패턴이 강제되었다. 어댑터에서 RecyclerView의 어댑터를 상속받는 순간 

강제로 인플레이터와 ViewHolder 패턴이 적용된 Method를 오버라이드 해야 사용할 수 있기때문이다.


내가 이 글을 쓰게 된 이유가 여기서 시작된다.

RecyclerView를 확실히 ListView보다 모든 면에서 더 편하다. 인플레이팅도 알아서 해주고 뷰홀더로 데이터꼬임현상도 알아서 없애주기때문에.

그러나 성능상의 이유라던가 비즈니스로직의 이유로 ViewHolder 안에 있는 View에 태그를 세팅하는순간 ListView에서 자주보던 오류가 발생한다.

데이터가 반복적으로 출력되는 것이다. 


이정도 오류야 뭐 ListView를 쓰던 우리는 금방 해결할...수....있....을거라고 생각했는데 코드를 보면 아무 문제가 없어보인다.

아니 오히려 직접 ViewHolder 패턴을 사용한것이 아니기때문에 더 보이지않는다. 


내 경우엔 그것이 setTag() 때문이었다.

선임자가 만들어둔 안드로이드 프로젝트를 받아서 업데이트하는 과정에서 ListView로 구성되어있던게 문제를 일으켰다.

데이터꼬임 혹은 데이터반복 뿐만 아니고, 특정 상황 또는 환경 또는 디바이스에서 앱이 죽는 현상까지 보인것이다.

그래서 ListView를 깔끔하게 RecyclerView로 바꾸고 어댑터도 사용법에 맞게 복붙했다.


그리고나서 확인을 해보니 데이터가 반복이 되는것이 아닌가?

대략 4~5번째까지 데이터가 출력이 되고, 그 이후부터는 계속 0~4 인덱스의 데이터만 반복적으로 출력되는 것이다.

오랜시간동안 이런저런 시도도 해보고 스크롤을 강제로 시켜서 그런가 싶어 해보기도 하고 RecyclerView의 성능향상을 시켜보기도하고

별짓을 다하다가 결국 다른 RecyclerView의 어댑터랑 비교해보니 차이점이 딱 하나 있었다.


setTag()였다. 아니나다를까 setTag() 코드 한줄만 주석처리 후 실행해보니 해당 오류는 말끔히 사라진것이다.


내 추측으로는 처음 생성된 View에 데이터가 세팅되면서 태그가 지정되고, 그 태그로 인해 해당 아이템의 고유성이 부여되는바람에

뷰를 재사용할때마다 처음 세팅됏던 데이터가 출력되는게 아닌가 싶다. 아무튼, 이걸 버그라고 해야할지 기능이라고 해야할지 잘 모르겠다.


주절주절 길게 생각나는대로 써내려왔는데, 결론은 맨 윗줄에 있는것과 같다.

setTag() 안쓰면 됨 ㅇㅇ


Comments