2026/03/07

오늘의 이야기

 




해가 저무는  바다는
이 더위를 식혀줄 요량인지 바람이  살랑 거린다.
하루 종일 이글 거리던 햇살을 이제 감추어야 하는 시간이 되어 바라보던 바닷가에

아이가 바다를 바라본다. 무엇을 남기고 싶은 건가?
이제 긴 여름도 입추라는 계절의 변화에 아직 버티고 있는 건지?

아직도 한낮의 기온은 30도를 넘나 든다.  입추는  언제쯤이나 실감을 하게 될지...

바다가 보이는 바위틈에서 서서...
오늘도 하루를 잘 보냈으니, 다가올 내일도 나에겐 행복한 기운만 함께 하길 바라본다.





오늘의 이야기

parametermap으로 String []을 전달해서 select 처리할 때 변수로 사용해 보고자 했다. 


흑~ 값이 전달 되지 않는다... 이유는 무엇인가?  이틀 무렵이나 구글 신(?)에서 질문을 했으나, 답이 찾아지지 않는다.


 


그런던 이튼날 답을 찾았다. ㅋ~


 


String[] aString 

HashMap <String, object> paramenterMap = new HaspMap<>();
paramemterMap.put("stringArr", aString);

...

파라미터 전달은 이 정도...


 


xml에서 sql은 어떻게?


 


<select id="select" resultType="resultVo" parameterType="map">
select * from table_name
where key in
<foreach collection="stringArr" item="item" open="(" close=")" separator=",">
'${item}'
</foreach>

</select>

이렇게 코딩하면 끝...


 


이전에는 #{item}이라고 코딩하면 될 꺼라 믿었고... 구글 신(?)이 알려주는 해법(?)들도 많은 글들이 그렇게 되어 있었지만...


 


결국에는 '${item}' 이 답이라는 결론(?)을 얻었다.  ㅠㅠ;





오늘의 이야기



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

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

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

그것도 구글 Gemini로다가!

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

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

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


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




오늘의 이야기

https://vtsen.hashnode.dev/free-android-development-learning-resources-for-beginners



 


Free Android Development Learning Resources for Beginners


Ultimate free resources and courses that I find useful while learning Android development as a beginner


vtsen.hashnode.dev




 


오늘은 아침에 찾은 링크 하나를 공유하는 것으로...


 


짧은 영어 이기는 해도 배우는 것들이 다 영문 사이트 이므로... 한 번은 읽어봐야 할 것 같아서...


 


끝.


 


링크이미지






오늘의 이야기


#스하리1000명프로젝트

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

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

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

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





오늘의 이야기

앱에서 알림을 구현하는 방법은 여러 가지가 있다. alertDiaglog을 이용하는 방법도 있기는 하지만, 요새는 SnackBar을 구현하는 경우가 많은 것 같다. Jetpack Compose에서는 아직 잘 모르겠는 부분이 있어서 구글을 하다 찾아보게 되었다.


 


https://stackoverflow.com/questions/68909340/how-to-show-snackbar-with-a-button-onclick-in-jetpack-compose



 


How to show snackbar with a button onclick in Jetpack Compose


I want to show snackbar with a button onclick in Jetpack Compose I tried this Button(onClick = { Snackbar(action = {}) { Text("hello") } } But AS said "@Composable


stackoverflow.com




 


이제 실전으로 갈 볼까 ? 먼저 위 글에서 퍼온 코드를 일부 수정해서 공통적으로 사용할 수 있도록 준비를 해 보아야겠다.


 


import android.annotation.SuppressLint
import android.util.Log
import androidx.compose.material.Scaffold
import androidx.compose.material.SnackbarResult
import androidx.compose.material.rememberScaffoldState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import kotlinx.coroutines.launch

@SuppressLint("UnusedMaterialScaffoldPaddingParameter", "CoroutineCreationDuringComposition")
@Composable
fun SnackBarShow(
message:String, actionLabel: String,
doDismissed:() -> Unit,
doActionPerformed:() -> Unit
) {
val scaffoldState = rememberScaffoldState() // this contains the `SnackbarHostState`
val coroutineScope = rememberCoroutineScope()

Scaffold(
modifier = Modifier,
scaffoldState = scaffoldState // attaching `scaffoldState` to the `Scaffold`
) {
coroutineScope.launch {
// using the `coroutineScope` to `launch` showing the snackbar
// taking the `snackbarHostState` from the attached `scaffoldState`
val snackbarResult = scaffoldState.snackbarHostState.showSnackbar(
message = message,
actionLabel = actionLabel
)
when (snackbarResult) {
SnackbarResult.Dismissed -> {
Log.e("SnackBarShow", "Dismissed")
doDismissed()
}
SnackbarResult.ActionPerformed -> {
Log.e("SnackBarShow", "SnackBar's button clicked")
doActionPerformed()
}
}
}
}
}

코드를 일부 동작할 수 있도록 수정했다.  @SuppressLint("UnusedMaterialScaffoldPaddingParameter", "CoroutineCreationDuringComposition")는 코드에서 오류 표시가 나는 것 때문에 수정을 하기는 했다. Padding Parameter을 사용하지 않는 것과 DuringComposition 부분인 것 같기는 하지만, 아직은 잘 모른다.


 


그리고 doDismissed(), doActionPerformed() 함수는 화면의 버튼을 클릭 했을 때 동작을 처리하는 부분을 구현해야 하기 때문에 return 될 함수의 선언으로 추가해 주었다.


 


이제 MainActivity 에서 코드를 구현해 보아야겠다.


 


setContent{
SnackBarShow(
getString(R.string.msgFinish), getString(R.string.titleFinish),
doDismissed = {},
doActionPerformed = {
finish()
}
)
}

어디에서는 간에 setCotent 로 감싸고 나서 위에서 작성한 SnackBarShow을 호출해 주는 것이다. 파라미터는 메시지로 보여줄 것과 버튼에 들어갈 문구를 전달하고 return 되는 함수는 SnackBar의 버튼을 클릭하지 않은 경우 처리와 , 버튼을 클릭했을 때 사용할 처리를 위해서 구현한 함수 2개를 돌려받았다.


 


실행이미지



 





오늘의 이야기

https://flatteredwithflutter.com/using-compose-destinations%ef%bf%bc/



 


Using compose destinations


We will cover briefly: Current navigation in composeUsing compose destinations(Optional) Modify existing test cases Current navigation in compose We get Compose Navigation from the Jetpack Com…


flatteredwithflutter.com




navigation 의 첫 이야기 다음... 그것을 어떻게 풀어낼 것인가를 찾아 돌아다니다가 또 하나의 링크를 찾았다. 이것을 보면서 이해를 하기 시작해 본다. 


 


그래서 오늘은 따라해 보기를 해 보아야겠다.


 


먼저 build gradle 에 설정을 따라해 본다. 


 


plugins {
...
id 'com.google.devtools.ksp' version '1.7.0-1.0.6'
}

1.7.0-1.0.6 은 코틀린 버전과 연계가 되어야 하는 버전을 맞추어 주는 것으로 이해를 하였다. 코틀린이 1.7.10까지 패치가 되어 가는 것 같기는 하지만, 일단 확인된 바로는 1.7.0 까지 인 것 같아서...


 


android {

...

applicationVariants.all { variant ->
kotlin.sourceSets {
getByName(variant.name) {
kotlin.srcDir("build/generated/ksp/${variant.name}/kotlin")
}
}
}

kotlin.sourceSets.all {
languageSettings.optIn("kotlin.RequiresOptIn")
}

...

}

이 부분이 들어가면 gradle 빌드를 통해서 kotlin 이라는 폴더가 생기면서 필요한 class 등을 만들어 주는 경로를 설정하는 것으로 이해가 되었다.


 


dependencies {

...
// compose destination
// https://github.com/raamcosta/compose-destinations 에서 최종 버전을 확인
implementation 'io.github.raamcosta.compose-destinations:animations-core:1.7.15-beta'
ksp 'io.github.raamcosta.compose-destinations:ksp:1.7.15-beta'
implementation "androidx.hilt:hilt-navigation-compose:1.0.0"

}

다음은 implementation  을 선언해 주는 것인데, 버전 확인은 원작자의 github 에서 확인하여 수정하면 최신 버전이 사용될 것 같다. 


 


다음은 activity 을 만들어 주어야 하는 데... 지금은 테스트 하는 것이기 때문에 간단하게 수정해 보았다.


override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

doUpdateCheck()

setContent {

MainTheme {
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier.fillMaxSize(),
color = softBlue
) {
// github 에서 본 것 처럼 추가.
val navHostEngine = rememberAnimatedNavHostEngine()
DestinationsNavHost(navGraph = NavGraphs.root, engine = navHostEngine)

}
}
}
}

이렇게 추가를 해 주고 나면 NavGraphs 가 생성이 되지 않아서 오류 표시가 나오지만, 일단은 무시하고 화면을 구성할 부분을 만들어 주었다.


 


@Destination(start = true)
@Composable
fun LoginScreen(
navigator: DestinationsNavigator
) {
Card(modifier = Modifier.fillMaxSize()) {
Text(text = stringResource(id = R.string.AppId))
}
Button(onClick = {
navigator.navigate(HomeScreenDestination)
}) {
Text(text = stringResource(id = R.string.action_geoList))
}

}

@Destination
@Composable
fun HomeScreen(
navigator: DestinationsNavigator
) {
/*...*/
Button(
onClick = {
navigator.navigate(
ProfileScreenDestination(
id = "someId",
isEditable = true
)
)
}
) {
Text(text = stringResource(id = R.string.action_addItem))

}
}

@Destination
@Composable
fun ProfileScreen(
navigator: DestinationsNavigator,
id: String,
isEditable: Boolean = false
) {
Button (onClick = {
navigator.popBackStack()
navigator.navigate(SearchScreenDestination("Text"))
}) {
Text(text = stringResource(id = R.string.action_Setting))
}
}

@Destination
@Composable
fun SearchScreen(
navigator: DestinationsNavigator,
query: String?
) {
Button (onClick = {
navigator.navigate(HomeScreenDestination)
}) {
Text(text = stringResource(id = R.string.action_setHome))
}
}

github 에서 보았던 예제를 참고해서 만들었고 동작 확인을 위해서 버튼만 추가해서 처리가 되는 지 확인해 보았다.


 


그 다음은 빌드를 위한 준비를 해 보자. android studio 의 terminal 에서 command 창을 열어서 


 


gradlew 실행



./gradlew clean build 을 입력해서 실행해 주면 gradle 을 실행 되면서 필요한 빌드를 하게 된다. 


 


생성된 폴더 와 class들



실행이 완료 되면 kotlin 폴더 아래와 위 그림과 같이 generated 된 파일들이 생성이 되고 이제 정말 앱을 빌드할 준비가 된다. 이제 나의 앱을 build 해서 실행해 보면 된다. 


 


https://billcorea.tistory.com/205



 


안드로이드 앱 만들기 : Compose Navigation ... 인터넷 펌.


https://medium.com/@cybercoder.naj/compose-navigation-in-3-minutes-5cff3c57c34e Compose Navigation in 3 Minutes Quick guide for navigation between composables in a Compose project medium.com 나름대..


billcorea.tistory.com




이렇게 실행해 보면 navigation 선언등등 이전 포스팅에서 적었던 것 같은 NavigationItem 등등 선언하지 않아도 navigation 을 구현할 수 있으므로 추가 하거나 할 때 다른 작업들을 잊어 버려도 오류가 나지 않을 것 같다.   (기능등을 비교해 보면 좋을 것 같다.)


 


즐~ 코딩 하길 바라며... 이걸 만든 원작자에게 감사의 인사를 보낸다...





오늘의 이야기


#스하리1000명프로젝트,
A veces es difícil hablar con trabajadores extranjeros, ¿verdad?
¡Hice una aplicación sencilla que ayuda! Escribes en tu idioma y los demás lo ven en el suyo.
Se traduce automáticamente según la configuración.
Súper útil para chatear fácilmente. ¡Echa un vistazo cuando tengas la oportunidad!
https://play.google.com/store/apps/details?id=com.billcoreatech.multichat416




오늘의 이야기

https://medium.com/@ibrajix/how-i-built-this-nice-looking-app-using-jetpack-compose-3974db7eb9e



 


How I built this nice looking app using Jetpack Compose


Jetpack compose is the future of building UI's on Android.


ibrajix.medium.com




이 글을 읽고 있는 중이다... kotlin을 이용해서 앱을 구현해 보고 있는 중이라서...  이글에는 splash 화면에 대한 이야기도 있는 것 같고, navigation에 대한 이야기도 있는 듯하다. navigation 은 원래 이분이 작성한 것은 아닌 듯하고,


저 글에서 봐야 하는 것은 splash 화면에 대한 부분인데, 난 그것도 보다도 아래 나와 있는 navigation에 대한 부분이 보였다.


 


@Destination 태그를 이용해서 android 가 제공하는 navigation 보다 수월하게 사용할 수 있다는 이야기를 하고 싶은 것 같다. 아직은 조금 더 배워야 할 것 같아서 링크를 달아 두려고 한다.  아래 링크는 라이브러리 형태로 활용하는 설명이 나와 있는 원본 게시물이다. 이것도 나중에 정독을 해야 할 것 같다.


 


https://github.com/raamcosta/compose-destinations



 


GitHub - raamcosta/compose-destinations: Annotation processing library for type-safe Jetpack Compose navigation with no boilerpl


Annotation processing library for type-safe Jetpack Compose navigation with no boilerplate. - GitHub - raamcosta/compose-destinations: Annotation processing library for type-safe Jetpack Compose na...


github.com




 


시니어 개발자로 살아 보는 건... 늘 배워야 하는 가 보다.


 





오늘의 이야기

kotlin 과 compose 의 버전 호환성



 


앱을 만들다 보니 이런 건도 알고 있어야 하네... 


 


빌드 오류 메시지



Caused by: org.gradle.api.GradleException: Compilation error. See log for more details 이 메시지를 보기 전에 지나가버린 메시지 가 있는데, 그건 잘 보이지 않는 경우가 있다. 


 


그래서 빌드창을 위아래로 드래그를 해 봐도 이것만 봐서는 알 수가 없고, 구글링을 해도 딱히 맞는 오류 대처 방안이 보이지 않는 다.  그래서 다시 빌드를 하면서 메시지들이 넘어가지 않도록 조절을 해서 찾은 이전 메시지


 




앞에서 로그의 자세히 보라고 했으니 잘 찾아 보았다면 헤매지 않아도 되겠지만, 영어가 짧은 개발자는 마지막 메시지를 그냥 구글링을 해 보게 된다는 것이다. ㅋㅋ~


 


그래서 찾은 상세 메시지에는 kotlin 컴파일러와 compose 컴파일러가 호환되는 버전을 찾아 주도록 해야 하는 부분이 있는 것이다. 


 


kotlin 이 1.7.10 까지 패치가 되었다고는 하는데, compose와 궁합이 맞는 버전은 아직 까지는 1.7.0 인 것 같다.  그래서 일단은 1.7.0과 1.2.0과 매칭을 해서 이번 작업을 시작해 보아야겠다.  자세한 정보는 아래 링크에서 참고하시길...


 


https://developer.android.com/jetpack/androidx/releases/compose-kotlin



 


Compose와 Kotlin의 호환성 지도  |  Android 개발자  |  Android Developers


Compose와 Kotlin의 호환성 지도 종속 항목 선언 Compose 컴파일러에 관한 종속 항목을 추가하려면 프로젝트에 Google Maven 저장소를 추가해야 합니다. 자세한 내용은 Google Maven 저장소를 읽어보세요. 다


developer.android.com




 





오늘의 이야기


#billcorea #운동동아리관리앱
🏸 Schneedle, ¡una aplicación imprescindible para los clubes de bádminton!
👉 Match Play: registra puntuaciones y encuentra oponentes 🎉
¡Perfecto para cualquier lugar, solo, con amigos o en un club! 🤝
Si te gusta el bádminton, definitivamente pruébalo.

Ir a la aplicación 👉 https://play.google.com/store/apps/details?id=com.billcorea.matchplay




오늘의 이야기

  해가 저무는  바다는 이 더위를 식혀줄 요량인지 바람이  살랑 거린다. 하루 종일 이글 거리던 햇살을 이제 감추어야 하는 시간이 되어 바라보던 바닷가에 아이가 바다를 바라본다. 무엇을 남기고 싶은 건가? 이제 긴 여름도 입추라는 계절의 변화에 아직...