무작정 회사를 떠났습니다. 남들이 말하는 *(준)정년 이라는 제도를 핑계 삼으며 말입니다. 이런 저런 핑계는 있었습니다. 그 동안 무작정 살았던 흔적들 때문에 힘이 들기도 했기에 더는 감당이 되지 않는 시간들이 되어 버렸기 때문 이기도 했습니다. - 마음에 위안을 주는 나만의 핑계 일수도-
이제 회사를 떠나 살아본 이야기를 적어 볼 까 합니다. 회사를 떠난 지 13개월 차 되어 갑니다. 지금 이 시간에도 회사를 떠나야 하는 -물론 정상적으로 이직이 되는 사람들은 제외 입니다.- 분들에게 경험 해 보지 않은 시간등에 대한 이야기를 해 볼까 합니다.
*(준)정년 : 회사가 정년을 몇년 남긴 직원에게 정년에 준하는 기준으로 회사를 떠닐 수 있도록 허가 하는 제도 정년이 도래하지 않은 상태에서 떠나기 때문에 회사의 정책에 따라 일정 금액의 보상을 하기도 합니다.
오늘 적어 두고자 하는 주제는 Paging입니다. 웹 개발을 하는 경우에는 각종 framework 등을 이용해서 데이터 조회 시 UI의 부하를 줄이기 위해서 Paging을 할 수 있도록 지원을 받습니다.
xml layout 을 구현할 때는 ListView 와 Adapter을 이용해서 별로 고민을 하지 않았던 부분이기도 합니다. 아니면 많은 데이터가 적재될 때까지 사용을 해 보지 않아서 지금 이 순간에는 느려진 화면 때문에 버려진 앱을 개발했던 것일 수도 있기도 하고요.
Paging 이란
페이징 기법(paging)은 컴퓨터가메인 메모리에서 사용하기 위해2차 기억 장치[a]로부터 데이터를 저장하고 검색하는메모리 관리 기법이다.기법이다. [1]즉가상기억장치를 모두 같은 크기의블록으로 편성하여 운용하는 기법이다. 이때의 일정한 크기를 가진 블록을페이지(page)라고 한다. 주소공간을 페이지 단위로 나누고 실제기억공간은 페이지 크기와 같은프레임으로 나누어 사용한다. - from wiki 백과
이렇게 기술하고 있습니다. 그럼 android 같은 것들은 어떻게 구현을 해야 할까요? 개발자 페이지의 내용을 보면서 구현을 해 보도록 하겠습니다.
class ListViewSource(pContext : Context) : PagingSource<Int, ViewReceiveList>() {
val context = pContext
override fun getRefreshKey(state: PagingState<Int, ViewReceiveList>): Int? { return state.anchorPosition }
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, ViewReceiveList> {
return try { val nextPage = params.key ?: 1 val viewList = dataReadPage(nextPage, params.loadSize) LoadResult.Page( data = viewList, prevKey = if (nextPage == 1) null else viewList[0].id.toInt(), nextKey = if (viewList.isEmpty()) null else viewList[viewList.size - 1].id.toInt() ) } catch (e : IOException) { return LoadResult.Error(e) } }
@SuppressLint("Range") private fun dataReadPage(nextPage: Int, loadSize: Int): List<ViewReceiveList> { val returnList = ArrayList<ViewReceiveList>() val dbHandler = DBHandler.open(context) Log.e("", "readKey=$nextPage") val rs = dbHandler.selectRcvList(nextPage, loadSize) returnList.clear() while (rs.moveToNext()) { val viewRevList = ViewReceiveList() viewRevList.id = rs.getString(rs.getColumnIndex("_id")) viewRevList.strBody = rs.getString(rs.getColumnIndex("strBody")) ... viewRevList.kakaoProfileImage = rs.getString(rs.getColumnIndex("kakao_image")) returnList.add(viewRevList) } dbHandler.close() return returnList } }
DBHandler에서는 select 구문 작성 방법
다음은 여기서 필요한 dbHandler의 select 구문 처리는 어떻게 하는 가입니다. 주의할 부분은 처음 읽어 올때와 다음 페이지로 넘어갈 때 nextKey 을 어떻게 다루어야 하는 가 입니다. 그리고 sqlite에서는 limit을 이용해서 읽어오는 데이터 개수를 조정할 수 있습니다. 그것으로 1 page 분량의 데이터만 조회를 해 오는 것입니다.
fun selectRcvList(nextId: Int, loadSize: Int) : Cursor { val sql = StringBuffer() sql.append("select _id, strBody, inPhoneNumber, chkValue, regDate, eventID, kakao_profile_image from receiveList") if (nextId == 1) { sql.append(" where _id >= $nextId") } else { sql.append(" where _id < $nextId") } sql.append(" order by _id desc") sql.append(" limit $loadSize") return db.rawQuery(sql.toString(), null) }
DataView 모델에 선언하기
다음은 DataViewModel에서 데이터를 조회하는 구현 부분입니다. viewModel에서 viewSource으로 전달하면서 context을 전달해 sqlite dbHandler을 사용 시 이용하게 됩니다.
class DataViewModels (application: Application) : AndroidViewModel(application) {
... @SuppressLint("StaticFieldLeak") val context = application.applicationContext!! val listItemsList : Flow<PagingData<ViewReceiveList>> = Pager(PagingConfig(pageSize = 10)) { ListViewSource(context) }.flow.cachedIn(viewModelScope)
...
}
Compose 화면 구성 하기
이제 리스트 조회하는 Jetpack compose 화면에서의 처리를 보도록 하겠습니다.
화면에서는 LazyColumn을 이용해서 ListView Adapter의 구현과 같이 화면에 데이터를 순서대로 조회하는 구현을 해 볼 수 있었습니다. 이렇게 구현해서 데이터 개수 1만 개가 넘는 sqlite의 데이터 조회를 앱 화면에 무리되지 않는 양으로 paging을 해 조회를 구현해 볼 수 있습니다.
이렇게 구현된 샘플 앱의 이미지 화면입니다. 알림 수신기 앱의 수신 목록 리스트 화면 입니다. 1만 개의 데이터를 다 읽어 드려서 list view을 구현을 한다면 어떻게 동작을 할까는 잘 모르겠습니다. 아무튼 이렇게 구현된 앱의 화면은 부담스럽지 않게 리스트가 스크롤되는 것을 확인할 수 있었습니다.
샘플이미지
마무리
sqlite의 데이터 조회는 이렇게 구현해 보았는 데요. 가장 근접한 room을 이용한다고 해 도 그다지 다르지 않을 거라고 생각이 됩니다. online에서 데이터를 가져온다고 해도 큰 문제없이 구현을 해 볼 수 있을 듯합니다.
#스하리1000명프로젝트, في بعض الأحيان يكون من الصعب التحدث مع العمال الأجانب، أليس كذلك؟ لقد صنعت تطبيقًا بسيطًا يساعد! أنت تكتب بلغتك، والآخرون يرون ذلك بلغتهم. يترجم تلقائيًا بناءً على الإعدادات. مفيد للغاية لإجراء محادثات سهلة. ألق نظرة عندما تحصل على فرصة! https://play.google.com/store/apps/details?id=com.billcoreatech.multichat416
이전 설명서에서 옮겨오는 작업을 시작합니다. (이 화면에 변했습니다. xml layout 버전에서 jetpack compose을 입혔습니다.)
이 앱의 당초 목적은
원래 하고 싶었던 것은 이전 설명서에서도 기술했다싶이 내 신용카드를 사용하시는 여보님(?)에게 신용카드 사용 내역을 알려 그 사용을 줄여볼 요량(?)으로 다가 만들었던 앱입니다.
이 앱의 필수설정
이 앱은 알림수신을 해야 합니다. 구글정책에 따라 앱이 알림을 수신하기 위해서는 휴대폰의 설정에서 알림 수신 권한을 부여해 주어야 합니다. 앱의 구동에 필수적으로 필요한 권한이기 때문에 반드시 권한 설정을 해 주어야 앱이 실행됩니다.
앱을 실행 하면 무조건 확인을 하고 권한이 없는 경우 앱이 종료됩니다.
휴대폰 알림 설정 선택
휴대폰의 알림설정에 알림이 설정되지 않으면 더 실행이 되지 않으므로 꼭 알림 설정을 허용해 주세요.
앱의 로그인 해 보기
이 앱은 수집된 알림을 카카오톡 친구에게 알림을 전달하는 기능이 핵심(?)인 앱이기 때문에 기본적으로 카카오톡을 통한 로그인이 필수입니다.
카카오톡이 설치된 경우 : 카카오톡 간편 로그인 가능
카카오톡이 설치 되지 않은 경우 : 카카오톡 로그인 웹 페이지를 통해 로그인 가능
로그인 순서
기본 화면
앱의 기본 화면은 다음과 같이 기본적인 달력 모양으로 보입니다.
각 날자 별로 하단에 표시되는 숫자는 해당일자에 지출금액으로 확인된 금액의 함께를 표시하며
달력 하단에 표시되는 숫자는 해당월의 기준일부터 다음 달까지 집계되는 금액의 합계를 표시합니다.
메인 화면
이 앱의 사용설명서 페이지 링크로 이동합니다. 앱을 사용하기 전에 기능에 대한 이해를 하고 앱을 사용해 보시면 도움이 됩니다.
설정 화면
이 앱에서는 카카오톡 알림을 전달하는 설정을 해야 합니다. 해서 다음과 같이 설정 화면에서는 이런저런 처리를 진행하게 됩니다.
설정 화면
backup 을 지원 합니다. 다만, 구글의 보안정책 때문에 앱 내부의 저장 스토리지에 백업을 하기 때문에 외부 SD 카드 등에는 저장이 되지 않습니다. 그래서 권한이 부족한 다른 앱에서는 해당 스토리지 경로를 찾아볼 수 없습니다.
restore을 지원합니다. 이 기능도 이 앱 내부의 저장소에서 가져오는 것이라서 권한이 부족한 경우 다른 복원 방법을 사용할 수는 없습니다.
※ 권한 획득이 가능한 앱을 사용하는 경우에는 /storage/emulated/0/Android/data/com.nari.notify2 kakao/files/Download 폴더에서 notify2 Kakao.sqlite 파일을 외부 SD 카드등으로 옮겨서 관리를 해 볼 수 있습니다.
설정에서 필요한 선택을 다 하고 나면 저장 버튼을 클릭해서 해당 정보를 저장하여야만 앱이 동작하는 데 사용이 됩니다.
카카오톡 사용 : 알림이 수신되었을 때 전달해 주는 기능을 활성화하기 위해서 카카오톡 사용을 허가해 주어야 합니다. 추출문구 관리에서 선택된 사용자에게 알림을 전달해 줄 수 있습니다.
월간시작일자 : 매월 조회를 시작할 날자를 지정합니다. 일반적인 칼렌더와 다른 게 이 앱의 달력은 다음 달 월간시작일자까지 화면에 표시합니다. 그 기간 동안의 지출 비용 합계 산정을 할 때도 사용됩니다.
매일 알림 시간 : 비용지출이 예상되는 날자에는 매일 알림을 지원해 보고자 만들어가고 있는 중입니다. (현재는 지원이 미흡할 수 있습니다.)
점검문자열
이 앱의 핵심기능을 사용하기 위한 준비입니다. 점검 문자열을 입력해 주어야 이 앱의 수집된 알림에서 해당 문구의 포함 여부를 확인하여 월간 지출 내역으로 것인지 수신 메시지 보기에 넣을 것이지 체크를 할 수 있습니다.
점검 문자열 화면
추가하기 이용하여 점검 문자열을 추가할 수 있습니다.
다른 내용 보기를 이용하여 사용여부가 체크된 내역만 보거나, 사용여부가 체크되지 않은 것만 보는 것으로 화면을 전환할 수 있습니다.
삭제 버튼을 클릭하면 선택한 항목을 삭제할 수 있습니다.
※ 입력된 항목을 수정하고자 하는 경우에는 해당 항목을 클릭 하면 수정하는 화면으로 넘어갑니다.
점검문자열 추가해 보기
점검문자열 추가를 위해서 추가하기 버튼을 클릭해 다음 화면으로 넘어가 보겠습니다.
점검문자열 추가 화면
뒤로 가기 버튼을 이용해서 이전 화면으로 돌아갈 수 있습니다.
저장하기 버튼을 이용해서 입력한 항목을 저장할 수 있습니다.
카카오톡 친구 (전송여부) : 화면 하단에 나오는 친구 중에서 하나를 선택하면 해당 사용자의 프로필 사진과 이름이 표시됩니다. 그 옆에 체크 박스를 체크해 주는 경우 해당 친구에게 알림이 전달됩니다.
추출단어 (사용여부) : 수신된 알림에 필수적으로 포함되어야 하는 문구를 입력합니다. 단어 단위로 입력을 해야 하기 때문에 띄어쓰기 등을 할 수 없습니다. 사용여부 체크박스를 체크해 주어야 적용됩니다.
입출구분 : 관리대상/출금대상을 선택하는 여부에 따라서 월금지출목록에 표시가 되거나, 수신메시지 목록에 표시가 되거나 합니다. 출금대상을 선택한 경우는 수집된 알림에 점검문자열이 포함되어야 하고, 금액으로 사용될 수 있는 숫자가 포함되어야 합니다.
※ 점검문자열 조회 화면에서 한 개의 리스트를 선택하는 경우에는 이 화면으로 들어오면서 값이 채워져 있는 상태이기 때문에 해당 값을 변경하고 저장하면 사용이 됩니다.
매월 출금 리스트
매월 출금 리스트는 수신되는 알림이 없지만, 지출이 예상되는 경우 등록하여 매월 출금 목록에 표시될 수 있도록 관리할 수 있습니다.
매월 출금 리스트
항목을 추가하는 경우에 사용됩니다. 다음 화면으로 넘어가 새로운 항목을 추가할 수 있습니다.
입력된 리스트의 내용을 월간지출항목 화면의 기준월에 일괄 적용 합니다. 해당월의 일자에 같은 적용가 이미 등록된 경우는 적용되지 않습니다.
삭제 버튼을 클릭하면 선택한 항목을 삭제할 수 있습니다.
※ 입력된 항목을 수정하고자 하는 경우에는 해당 항목을 클릭 하면 수정하는 화면으로 넘어갑니다.
매월 출금 리스트 추가해 보기
매월 출금 항목 추가 화면
뒤로 가기 버튼을 이용해서 이전 화면으로 돌아갈 수 있습니다.
저장하기 버튼을 이용해서 입력한 항목을 저장할 수 있습니다.
일자 : 매월 적용 되어야 하는 일자를 2자리 숫자로 입력하면 됩니다.
거래금액 : 해당일자에 지출이 예상되는 금액을 입력하면 됩니다.
적용 : 지출 예상 금액의 적요 명칭을 추가합니다.
※ 매월 출금 리스트 화면에서 항목을 클릭하는 경우에는 이 화면에 값이 채워진 상태로 넘어옵니다. 필요한 부분만 수정한 후 저장 하면 이전 화면에서 수정된 내용을 확인해 볼 수 있습니다.
수신 메시지 보기
이 화면에서는 점검문자열에 등록된 정보가 있는 경우 추출되어 기록된 정보를 한 목에 조회할 수 있습니다. 이 화면은 단순 조회 전용 화면입니다. 최근에 등록된 정보가 가장 먼저 보이는 형태로 운영됩니다. 과거 자료는 아래로 화면을 내력 확인을 해야 합니다.
수신메시지 보기
월간처리목록 일자별 대상 화면
월간처리목록 화면에서 특정일자를 선택하면 해당 일자 처리 대상 화면으로 넘어갑니다. 이 화면에서는 일자로 처리되어야 하는 것과 처리된 항목을 확인할 수 있으며, 추가 등록을 지원하고 있습니다.
일자별 화면
뒤로 가기 버튼을 이전 화면으로 돌아갑니다.
새로운 항목을 추가 등록 하고자 하는 경우에 사용됩니다.
날자별로 체크가 되어 확인된 사항과 체크되지 않은 사항을 따로따로 조회하고자 하는 경우에 사용됩니다.
화면에 나열된 항목 앞에 체크 박스를 클릭하는 순간 처리 완료 되었다고 저장을 하고 완료 목록으로 넘어가 조회가 되지 않으므로 그때 이 버튼을 클릭해 완료 체크된 리스트를 다시 확인할 수 있습니다.
월간처리목록 추가해 보기
월간 처리 목록 추가는 다음 화면과 같이 진행할 수 있습니다.
월간출금목록 추가해 보기
뒤로 가기 버튼을 이용해서 이전 화면으로 돌아갈 수 있습니다.
저장하기 버튼을 이용해서 입력한 항목을 저장할 수 있습니다.
출금확인 체크 박스는 출금 확인을 했다는 것을 등록하기 때문에 저장하며 바로 완료 목록으로 넘어갑니다.
거래일자는 6자리 숫자로 입력을 하거나 하이픈으로 구분변 연속 문자열로 입력해 주면 됩니다.
거래금액은 콤마를 제외한 숫자를 이용하여 입력합니다.
적요는 필요에 따라서 입력하면 됩니다.
준비는 되었습니다.
이제 문자 알림이 수신되기를 기다려 보겠습니다. 물론 그전에 이 앱을 설치하고 나서 말입니다.
이제 끝나 갑니다. 앱도 게시를 하였고 정리된 글은 조만간 정리해 보겠습니다. 일단 화면만 보여 드려요.
이전 화면은 이렇게 그렸지만...
이전 화면
이전한 화면은 이렇게 그립니다.
이후 화면
더 깔끔한 화면으로 변했습니다. 재활용성은 훨씬 뛰어납니다.
xml layout 을 버리는 일은 그렇게 쉬운 작업은 아니었습니다. linear layout부터 listview까지 다양하게 만들어져 있는 화면들을 jetpack compose 화면으로 옮기는 것도 그렇게 기존에 사용했던 function을 재정의 해서 사용하는 것도...
다만 이렇게 이전을 하는 이유는 훗날 더 손쉽게 앱을 관리할 수 있기 때문이기도 합니다. 디자인을 전공 하지 못한 순수(?) 개발자가 다양한 모양의 앱을 구현하는 것은 조금 난감하게 다가오기도 합니다만...
그래도 이렇게 구현을 해 갈 수 있는 것은 개발도구들의 발전이 있기 때문이지 않을까 하는 생각을 해 봅니다.