2026/03/23

오늘의 이야기

https://github.com/blocoio/android-template



 


GitHub - blocoio/android-template: Android app starter template


Android app starter template. Contribute to blocoio/android-template development by creating an account on GitHub.


github.com




위 글에서 찾은 안드로이드 앱 탬플릿입니다. 이걸 따라 해 보면 쉽게 앱을 구현해 볼 수 있을 듯합니다.   아래 적은 글은 원본에서 번역한 자료임을 밝혀 둡니다.


 


---


 


3개의 주요 모듈이 있는 클린 아키텍처



  • 데이터(데이터베이스, API 및 기본 설정 코드용)

  • 도메인(비즈니스 로직 및 모델용)

  • AndroidApp(UI 로직용, MVVM 포함)


테스트



  • Mockk 라이브러리

  • 단위 테스트

  • 애플리케이션 테스트

    • 테스트 작업 방법에 대한 예



  • 활동 테스트( Compose Testing 포함 )

    • 테스트에서 코루틴 범위로 작업하는 방법에 대한 예




기타 유용한 기능



시작하기



  1. 이 리포지토리 추출을 다운로드하고 Android Studio에서 템플릿 폴더를 엽니다.

  2. 앱 패키지 이름 바꾸기 io.bloco.template

  3. 패키지와 함께 매니페스트 패키지의 이름이 바뀌었는지 확인합니다.

  4. 컴포저블을 템플릿 이름으로 교체

  5. 에서 androidApp/build.gradleapplicationId를 새 앱 패키지로 변경합니다.

  6. 에서 androidApp/build.gradleAndroid Studio가 제안하는 종속성을 업데이트합니다.

  7. 에서 string.xml애플리케이션 이름을 설정합니다.


NewProjectCI 내에서 프로젝트를 실행하면 위의 모든 단계가 자동으로 수행됩니다. Android Studio를 사용하여 프로젝트를 실행한 다음 Main.kt 파일을 열고 "Run MainKt"를 누를 수 있습니다. 새 패키지 이름과 앱 이름에 대한 메시지가 표시됩니다.



  1. 애플리케이션 스타일 설정 Theme.kt및 설정 Color.kt

  2. 앱 아이콘 교체

  3. 원치 않는 예제 파일 삭제

  4. 실행./gradlew dependencyUpdates 및 종속성 확인

  5. 사용할 준비가


이제 새 앱 작업을 시작할 준비가 되었습니다.


노트



  • Android 템플릿에는. github/workflowsLint 검사, 단위 테스트 및 종속성 검사가 포함됩니다. 이 프로젝트 작업 흐름을 쉽게 가져와서 몇 가지 경로 변경으로 용도를 변경할 수 있습니다. 또한 test.yml에서 Instrumentation Testing 및 CodeCoverage에 대한 주석이 달린 예제를 찾을 수 있으며 깨끗한 프로젝트를 유지하도록 조언하지만 비밀 키를 교체해야 합니다. 자신과 함께.


 







오늘의 이야기

https://github.com/tehras/charts



 


GitHub - tehras/charts: Simple Android compose charts.


Simple Android compose charts. Contribute to tehras/charts development by creating an account on GitHub.


github.com




 


이 글은 위 링크에서 참고하였습니다.  이것도 꼭 도전해 보아야겠습니다. 앞에서 펌 했던 것보다는 jetpack compose을 활용한 차트 그리기이니 앞으로의 앱 개발에 필수 요소가 아닐까 하는 생각이 듭니다.


 


샘플이미지



Android Jetpack Compose 라이브러리를 사용하여 차트를 그리고 애니메이트하는 라이브러리입니다.


구현:


최신 릴리스 = 



 


build.gradle(앱)



dependecies {
implementation "com.github.tehras:charts:$latest_release"
}

 


설정.gradle



repositories {
google()
mavenCentral()
maven { url 'https://jitpack.io'}
}


 



프로젝트가 3년이라는 사실에도 불구하고 6개월 전에 대대적으로 업데이트되었으며 SDK 33을 대상으로 하는 거의 최신 종속성을 기반으로 합니다.


README는 라이브러리 사용에 대한 기본 정보를 제공하며 시작하기에 충분합니다.


버전: 0.2.4-alpha
라이선스: Apache-2.0 라이선스


 


막대 차트 예제

@Composable
fun MyBarChartParent() {
fun BarChart(
barChartData = BarChartData(bars = listOf(Bar(label = "Bar Label", value = 100f, color = Color.Red)),
// Optional properties.
modifier = Modifier.fillMaxSize(),
animation = simpleChartAnimation(),
barDrawer = SimpleBarDrawer(),
xAxisDrawer = SimpleXAxisDrawer(),
yAxisDrawer = SimpleYAxisDrawer(),
labelDrawer = SimpleValueDrawer()
)
}


원형 차트 예제

@Composable
fun MyChartParent() {
PieChart(
pieChartData = PieChartData(listOf(Slice(...), Slice(...),....)),
// Optional properties.
modifier = Modifier.fillMaxSize(),
animation = simpleChartAnimation(),
sliceDrawer = SimpleSliceDrawer()
)
}

선 차트 예제

@Composable
fun MyLineChartParent() {
LineChart(
linesChartData = listOf(LineChartData(points = listOf(LineChartData.Point(1f,"Label 1"), ...))),
// Optional properties.
modifier = Modifier.fillMaxSize(),
animation = simpleChartAnimation(),
pointDrawer = FilledCircularPointDrawer(),
lineDrawer = SolidLineDrawer(),
xAxisDrawer = SimpleXAxisDrawer(),
yAxisDrawer = SimpleYAxisDrawer(),
horizontalOffset = 5f,
labels = listOf("label 1" ...)
)
}

 


 





오늘의 이야기


#스하리1000명프로젝트,
한국에서 길을 잃었나요? 한국어를 못하더라도 이 앱을 사용하면 쉽게 돌아다닐 수 있습니다.
귀하의 언어로 말하면 귀하의 언어로 번역, 검색 및 결과가 표시됩니다.
여행자에게 좋습니다! 영어, 일본어, 중국어, 베트남어 등 10개 이상의 언어를 지원합니다.
지금 사용해 보세요!
https://play.google.com/store/apps/details?id=com.billcoreatech.opdgang1127




2026/03/22

오늘의 이야기

https://github.com/hi-manshu/Kalendar



 


GitHub - hi-manshu/Kalendar: Kalendar is a powerful and customizable calendar library for Android applications. It provides a fl


Kalendar is a powerful and customizable calendar library for Android applications. It provides a flexible and intuitive way to display and interact with calendars in your app. With Kalendar, you ca...


github.com




 


이글은 위 원본 소스에서 참고 했습니다. 다음에 꼭 도전해 본 이야기를 적어 보겠습니다.


카렌더 만들기 샘플



 


Kalendar 라이브러리는 Jetpack Compose를 사용하여 Android 앱에서 캘린더 구성요소를 만들기 위한 강력한 도구입니다. 다양한 특징과 기능으로 사용자 지정 가능한 캘린더 보기를 제공합니다.


Kalendar 라이브러리는 Android 앱에서 대화형의 시각적으로 매력적인 캘린더를 만드는 프로세스를 단순화합니다. 유연한 구성 옵션, 이벤트 통합 및 대화형 콜백을 통해 사용자 경험을 향상시키는 강력한 캘린더 구성 요소를 쉽게 구축할 수 있습니다.


만든❤️Android 개발자를 위한 Himanshu 제작


캘린더는 세 가지 유형의 캘린더를 지원합니다.



  • Oceanic : WeekView를 나타냅니다.

  • Firey : MonthView를 나타냅니다.

  • Endlos : 현재 달부터 시작하는 스크롤 달력을 나타냅니다.


시작하기


Kalendar 라이브러리를 Android 앱에 통합하려면 다음 단계를 따르세요.


앱 수준 build.gradle 파일에 Kalendar 라이브러리를 종속 항목으로 추가합니다.



implementation 'com.himanshoe:kalendar:1.3.2'

 


스크롤링 달력 Endlos를 추가하려면 다음 종속성을 추가하십시오.



implementation 'com.himanshoe:kalendar-endlos:1.3.2'

 


Kalendar 라이브러리를 사용하는 방법에 대한 자세한 문서 및 예제는 Oceanic/Firey에 대한 Kalendar 문서 및 스크롤 캘린더에 대한 Kalendar Endlos 문서를 참조하십시오 .


 


 





오늘의 이야기

https://github.com/patrykandpatrick/vico



 


GitHub - patrykandpatrick/vico: A light and extensible chart library for Android.


A light and extensible chart library for Android. Contribute to patrykandpatrick/vico development by creating an account on GitHub.


github.com




 


이 게시글에서 찾은 자료입니다. 언제 한번 도전해 보아야 하겠습니다. 


 


그래프 샘플 이미지



 


Vico는 가볍고 확장 가능한 Android용 차트 라이브러리입니다. Jetpack Compose 및 보기 시스템과 모두 호환되지만 두 가지 주요 모듈( compose및) 은 views독립적입니다.


Vico에는 세 가지 기본 학습 리소스가 있습니다.



  • 위키 에는 설정 가이드가 포함되어 있으며 Vico의 기능과 구조에 대한 개요가 나와 있습니다.

  • 샘플 앱은 Jetpack Compose 및 뷰 시스템에서 Vico를 사용하는 방법을 보여줍니다. 이 문서 상단에 표시된 차트는 샘플 앱에서 구현됩니다.

  • API 참조는 Vico의 API에 대한 포괄적인 설명서입니다.





오늘의 이야기



#스치니1000프로젝트 #재미 #행운기원 #Compose #Firebase

🎯 야 너 토요일마다 로또 확인하냐?
나도 맨날 "혹시나~" 하면서 봤거든 ㅋㅋ

근데 이제는 그냥 안 해
AI한테 맡겼어 🤖✨

그것도 구글 Gemini로다가!

그래서 앱 하나 만들었지
👉 "로또 예상번호 by Gemini" 🎱

AI가 분석해서 번호 딱! 뽑아줌
그냥 보고 참고만 하면 됨

재미로 해도 좋고…
혹시 모르는 거잖아? 😏


https://play.google.com/store/apps/details?id=com.billcorea.gptlotto1127




오늘의 이야기

무작정 회사를 떠났습니다.  남들이 말하는 *(준)정년 이라는 제도를 핑계 삼으며 말입니다.  이런 저런 핑계는 있었습니다. 그 동안 무작정 살았던 흔적들 때문에 힘이 들기도 했기에 더는 감당이 되지 않는 시간들이 되어 버렸기 때문 이기도 했습니다. - 마음에 위안을 주는 나만의 핑계 일수도-
 

이제 회사를 떠나 살아본 이야기를 적어 볼 까 합니다.  회사를 떠난 지 13개월 차 되어 갑니다.  지금 이 시간에도 회사를 떠나야 하는  -물론 정상적으로 이직이 되는 사람들은 제외 입니다.- 분들에게 경험 해 보지 않은 시간등에 대한 이야기를 해 볼까 합니다.
 
*(준)정년 : 회사가 정년을 몇년 남긴 직원에게 정년에 준하는 기준으로 회사를 떠닐 수 있도록 허가 하는 제도  정년이 도래하지 않은 상태에서 떠나기 때문에 회사의 정책에 따라 일정 금액의 보상을 하기도 합니다.





오늘의 이야기


#스하리1000명프로젝트

스치니들!
내가 만든 이 앱은, 내 폰에 오는 알림 중에서 중요한 키워드가 있는 경우
등록해둔 친구에게 자동으로 전달해주는 앱이야 📲

예를 들어, 카드 결제 알림을 와이프나 자녀에게 보내주거나
이번 달 지출을 달력처럼 확인할 수도 있어!

앱을 함께 쓰려면 친구도 설치 & 로그인해줘야 해.
그래야 친구 목록에서 서로 선택할 수 있으니까~
서로 써보고 불편한 점 있으면 알려줘 🙏

👉 https://play.google.com/store/apps/details?id=com.nari.notify2kakao





오늘의 이야기

The emulator process for AVD Pixel_3a_API_33 has terminated.  이런 메시지를 만나는 날이 옵니다. 


간혹 노트북으로 코딩과 테스트를 하다가 노트북을 그냥 덮고 종료를 했거나, 갑자기 전원이 나가는 바람에 AVD 가 정상적으로 종료되지 않은 상태에서 컴퓨터가 꺼진 경우에 해당될 듯합니다. (제 경우에 한해서...)


 


아무튼 이런 경우를 만나게 되면 구글 검색을 통해서 다가오는 여러가지 방법들 중에서 이걸 제일 먼저 해 보시길 추천드립니다. 


 


일단 windows 탐색기를 열어 봅니다. 


 


탐색기 경로



이렇게 찾아 들어가서 보면 (사용자 폴더의 사용자 이름 아래에 .android 폴더 아래) 에서 실행하고자 하는 AVD 이름 폴더에 *. lock으로 lock을 하고 있는 폴더가 보입니다. 


 


중복 실행 방지를 위해서 생성이 되는 폴더 인데요. 이 폴더를 삭제하면 됩니다. AVD 가 실행 중이라면 권한 때문에 삭제가 되지 않을 수 있으니,  작업관리자 등으로 확인 후 AVD 가 중지 되어 있다면 삭제가 가능할 것 같습니다.


 


The emulator process for AVD Pixel_3a_API_33 has terminated.


 


이런 메시지를 띄우고 AVD 가 실행 되지 않을 때 조치 하는 방법 하나를 알아 보았습니다.


 





오늘의 이야기

오늘 적어 두고자 하는 주제는 Paging입니다.  웹 개발을 하는 경우에는 각종 framework 등을 이용해서 데이터 조회 시 UI의 부하를 줄이기 위해서 Paging을 할 수 있도록 지원을 받습니다. 


 


xml layout 을 구현할 때는 ListView 와 Adapter을 이용해서 별로 고민을 하지 않았던 부분이기도 합니다. 아니면 많은 데이터가 적재될 때까지 사용을 해 보지 않아서 지금 이 순간에는 느려진 화면 때문에 버려진 앱을 개발했던 것일 수도 있기도 하고요.


 


Paging 이란

페이징 기법(paging)은 컴퓨터가 메인 메모리에서 사용하기 위해 2차 기억 장치[a]로부터 데이터를 저장하고 검색하는 메모리 관리 기법이다.기법이다. [1]  가상기억장치를 모두 같은 크기의 블록으로 편성하여 운용하는 기법이다. 이때의 일정한 크기를 가진 블록을 페이지(page)라고 한다. 주소공간을 페이지 단위로 나누고 실제기억공간은 페이지 크기와 같은 프레임으로 나누어 사용한다.  - from wiki 백과


 


이렇게 기술하고 있습니다.  그럼 android 같은 것들은 어떻게 구현을 해야 할까요?  개발자 페이지의 내용을 보면서 구현을 해 보도록 하겠습니다. 


 


https://developer.android.com/topic/libraries/architecture/paging/v3-overview?hl=ko 



 


페이징 라이브러리 개요  |  Android 개발자  |  Android Developers


컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. Paging 라이브러리 개요   Android Jetpack의 구성요소 페이징 라이브러리를 사용하면 로컬 저장소에서나 네트워


developer.android.com




 


Gradle 설정 (module)

gradle 설정에 추가한 내용입니다.  이 글을 쓰는 현재는 3.2.0이 최신인 듯합니다.



// Paging 3.0
implementation 'androidx.paging:paging-compose:3.2.0'


 


ListViewSource 예시

다음은 paging source와 view model 정의를 해 보아야 합니다. 다만, 개발자 페이지 등에서 찾을 수 있는 것에는 Sqlite을 활용하는 부분을 찾아보기가 어려웠습니다.   나름 따라 하기를 해 보면서 다음과 같은 코드 등을 구현해 볼 수 있었습니다. 


 




import android.annotation.SuppressLint
import android.content.Context
import android.util.Log
import androidx.paging.PagingSource
import androidx.paging.PagingState
import java.io.IOException

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을 해 조회를 구현해 볼 수 있습니다.




import coil.compose.AsyncImage
import com.google.android.gms.ads.AdRequest
import com.google.android.gms.ads.AdSize
import com.google.android.gms.ads.AdView
...
import com.ramcosta.composedestinations.annotation.Destination

@Destination
@Composable
fun ListViewScreen (dataViewModels: DataViewModels) {

// 데이터 조회 페이지 호출 하기
val viewLists : LazyPagingItems<ViewReceiveList> = dataViewModels.listItemsList.collectAsLazyPagingItems()

LazyColumn(
modifier = Modifier.fillMaxSize(),
) {

items(viewLists.itemCount) {index ->
DoDisplayItem(item = viewLists[index], index = index)
}
}

}

@Composable
fun DoDisplayItem(item: ViewReceiveList?, index: Int) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(3.dp)
.border(1.dp, color = borderLine),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
if (item != null) {
Row(
modifier = Modifier.fillMaxWidth().padding(3.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Start
) {
Text(text = String.format("%s/%s/%d ",item.id, item.eventID, index), style = typography.bodyMedium, modifier = Modifier.padding(3.dp))
}
if (item.kakaoProfileImage.isNotEmpty()) {
Row(
modifier = Modifier.fillMaxWidth().padding(3.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Start
) {
AsyncImage(
model = item.kakaoProfileImage,
contentDescription = item.inPhoneNumber,
modifier = Modifier
.width(60.dp)
.height(60.dp)
.wrapContentWidth(align = Alignment.Start)
.padding(3.dp)
)
Text(text = item.inPhoneNumber, style = typography.bodyMedium, modifier = Modifier.padding(3.dp))
}
} else {
Text(text = item.inPhoneNumber, style = typography.bodyMedium, modifier = Modifier.padding(3.dp))
}
Text(text = item.strBody, style = typography.bodyMedium, modifier = Modifier.padding(3.dp))
Row(
modifier = Modifier.fillMaxWidth().padding(3.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.End
) {
Text(text = item.chkValue, style = typography.bodyMedium, modifier = Modifier.padding(3.dp))
Text(text = item.regDate, style = typography.bodyMedium, modifier = Modifier.padding(3.dp))
}
}
}
}


 


샘플앱 화면 보기

이렇게 구현된 샘플 앱의 이미지 화면입니다.  알림 수신기 앱의  수신 목록 리스트 화면 입니다. 1만 개의 데이터를 다 읽어 드려서  list view을 구현을 한다면 어떻게 동작을 할까는 잘 모르겠습니다. 아무튼 이렇게 구현된 앱의 화면은 부담스럽지 않게 리스트가 스크롤되는 것을 확인할 수 있었습니다.


샘플이미지



 


마무리

sqlite의 데이터 조회는 이렇게 구현해 보았는 데요. 가장 근접한 room을 이용한다고 해 도 그다지 다르지 않을 거라고 생각이 됩니다. online에서 데이터를 가져온다고 해도 큰 문제없이 구현을 해 볼 수 있을 듯합니다. 


 





오늘의 이야기


#스하리1000명프로젝트,
في بعض الأحيان يكون من الصعب التحدث مع العمال الأجانب، أليس كذلك؟
لقد صنعت تطبيقًا بسيطًا يساعد! أنت تكتب بلغتك، والآخرون يرون ذلك بلغتهم.
يترجم تلقائيًا بناءً على الإعدادات.
مفيد للغاية لإجراء محادثات سهلة. ألق نظرة عندما تحصل على فرصة!
https://play.google.com/store/apps/details?id=com.billcoreatech.multichat416




오늘의 이야기

Hi Developers at Billcorea (빌코리아), After a recent review, we've found an issue with your app, 리모트 페이 (결제도 공유가 되나요, 원격 결제) (com.billcorea...