딜리셔스 안드로이드 개발자가 신상캐시 송금 서비스에 Jetpack Compose 를 시범 적용한 경험담이다. 대부분은 컨버팅이 쉬웠지만 TextField 의 포커스 처리와 천 단위 콤마, 그리고 무한 스크롤에서 커스텀이 많이 필요했던 지점을 코드와 함께 남긴다.
핵심 포인트- 기존에는 xml layout 으로 화면을 그렸는데, Compose 를 쓰면 Kotlin 만으로 개발되고 뷰를 직접 조작하며 생기던 번거로움과 버그 위험이 줄어든다.
- 기존 코드와 호환돼 전부 바꾸지 않고 점진적으로 적용할 수 있다는 점이 시도의 계기가 됐다.
- 손이 갔던 부분: Button·TextField 같은 Composable 에 안드로이드가 미리 정해 둔 padding·elevation 기본값이 있어 디자인 시안에 맞추려면 재조정이 필요했다.
- TextField 는 Composable 함수라 특정 객체인지 판단할 수 없어, 기존 `dispatchTouchEvent` 로 포커스를 해제하던 방식 대신 포커싱 여부 변수를 따로 둬야 했다.
- 천 단위 콤마는 `onValueChange` 로 처리하려다 커서 위치 버그를 만났고, `VisualTransformation` 으로 해결했다.
상세 정리- 예제는 Compose 1.0.5 기준이며, 안드로이드 팀 스터디를 계기로 몇몇 동료가 새 기능 개발에 시범 적용했다.
- 구글이 내세우는 이유(코드 감소, 직관적, 빠른 개발, 강력한 성능)를 인용한 뒤 "개발자들이 편하게 UI 개발하라고 만든 툴킷" 으로 요약한다.
- 기본 Composable 부터 복잡도 높은 것까지 대부분의 기능이 미리 구현돼 있어 코드가 간결했고 컨버팅에 큰 어려움이 없었다고 평가한다.
- 콤마 처리 1차 시도: ViewModel 에 LiveData 변수를 두고 observe 하며 TextField 값을 관리하고, keyboardType 을 Number 로 준 뒤 파싱한 값을 콤마 포함 문자열로 업데이트했다.
- 붙여넣기 등으로 유효하지 않은 Long 값이 들어오면 0L 로 초기화되도록 확장 함수를 만들어 썼다.
- 버그의 정체: 커서 위치를 계산할 때 콤마가 고려되지 않았다. 그런데 EditText 의 `setSelection` 을 대체할 함수를 Compose 에서 찾지 못했다.
- 해결: `VisualTransformation` 은 화면에 보이는 출력 값만 바꾸는 인터페이스로 비밀번호 마스킹, 천 단위 콤마, 전화번호 포맷팅에 자주 쓰인다. 텍스트 추가·삭제 시 커서 위치를 얼마나 보정할지 처리하는 클래스를 직접 만들었다.
- 구조 변경: ViewModel 은 콤마를 뺀 숫자만 들고 있게 하고, 화면에만 콤마가 보이도록 분리했다.
- 무한 스크롤: 첫 진입 시에는 전체 화면 로딩으로, 두 번째 로딩부터는 이미 불러온 아이템 아래에 인디케이터나 실패 메시지를 붙이는 예외 처리를 그대로 유지했다.
- 로딩 상태를 판단할 enum 을 만들고, 그 값에 따라 LazyColumn 안에서 그릴 Composable 을 분기했다. 상단 고정 Header 만 밖에 둔다.
- 스크롤 감지는 `LazyListState` 로 한다. 스크롤을 관찰·제어하고 화면에 처음·마지막으로 보이는 아이템 인덱스를 얻을 수 있어, 관련 판단 로직을 확장 함수로 정리했다.
- 스크롤이 바닥에 닿기 전에 미리 통신하도록 buffer(마지막 아이템으로부터 몇 개 남았을 때 로드할지)를 두고, 음수면 예외를 던지게 했다.
- 총평 ①: 코드가 확실히 줄었다. layout 을 따로 그릴 필요가 없고 도형이나 selector 를 위해 여러 xml 을 만들던 작업이 몇 줄로 처리된다.
- 총평 ②: ViewModel 과의 상호 사용에 최적화돼 있어 예전처럼 View 와 Data 가 따로 노는 일이 거의 없을 것으로 본다.
- 주의점: 아직 정식 출시되지 않은 Composable 이 많아 형태가 바뀔 수 있고, 기존 위젯과 생김새·동작이 달라 이전과 같은 UX 를 주려면 주의해 커스텀해야 한다.
왜 읽나Compose 도입을 검토하는 안드로이드 개발자에게, 쉬운 부분과 실제로 시간이 걸리는 부분(입력 필드·무한 스크롤)을 코드 수준으로 구분해 준다.