2026/03/08

오늘의 이야기

https://emmanueldav.medium.com/tips-and-tricks-that-would-improve-your-skills-as-an-android-developer-7e2e81351877



 


Tips and Tricks that would improve your skills as an Android Developer


Here are the best Tips and tricks that would Improve your skills and speed as an Android developer


emmanueldav.medium.com




링크 사이트 입니다.


 


간혹은 필요해 보입니다. 





오늘의 이야기

요사이 한동안 즐겨 보았던 드라마 생각이 나는 바탕화면이 떠~억 올라온다. 


얼음돌



찬바람이 불어 치는 바다... 이 사진은 어디 북극해에서라도 촬영을 한 것일지?  자연 훼손이 심각해지고 있어서 이제는 더 늦을 수 없을 만큼 중요해져 가고 있을 것 같은데, 


 


난 이 사진을 보면서 얼음돌 생각이 난다... 한 두어 달은 재미있었는 데, 그 끝이 새드엔딩이라... 시즌2를 기다려야 하는 건지 잘 모르겠다.


 


아무튼 시즌2가 돌아올 때 까지 쭈~욱 기다려 볼게... 무덕이.


환혼 이미지



 





오늘의 이야기


#스하리1000명프로젝트,
Parfois, il est difficile de parler avec des travailleurs étrangers, n'est-ce pas ?
J'ai créé une application simple qui aide ! Vous écrivez dans votre langue et les autres le voient dans la leur.
Il se traduit automatiquement en fonction des paramètres.
Super pratique pour des discussions faciles. Jetez-y un oeil quand vous en aurez l'occasion !
https://play.google.com/store/apps/details?id=com.billcoreatech.multichat416




오늘의 이야기

요소수가 품귀현상을 보이던 시점에 앱을 하나 만들었던 기억이 납니다. 그러던 어느 날 data.go.kr에서 메일을 하나 받았습니다. 이제는 더 이상 정보 제공을 하지 않는다는...


공공데이터 포털 공지사항



그리고 오피넷 API을 사용하라는 안내와 함께 말입니다.  그래서 오피넷에 무료 API 사용을 허가받을 수 있는지 메일을 보냈습니다. 무료 이용이 가능하다는 안내와 함께, API 사용을 위한 Key 도 보내 주더군요.


 


API 가이드를 읽어보다 보니 지도에 위치 정보를 표기하기 위해서는 위, 경도 좌표가 필요한데 (구글맵 기준) 오피넷에서는 우리나라에서만 사용하는 것 같은 고정위치 연동 좌표를 전달하는 것으로 확인이 되었습니다. 위,경도 좌표가 아니기 때문에 표기를 할 수 없다는 문제가 발생했습니다.  그래서 알지 못하는 짧은 지식을 동원하여 이것저것 찾아보던 중...


https://github.com/locationtech/proj4j



 


GitHub - locationtech/proj4j: Java port of the Proj.4 library for coordinate reprojection


Java port of the Proj.4 library for coordinate reprojection - GitHub - locationtech/proj4j: Java port of the Proj.4 library for coordinate reprojection


github.com




이런 변환 방법이 있다는 것을 알게 되었죠. 해서 적용을 해 보기로 했습니다. 다만, 이런저런 방법으로 찾아보니 적용하는 방법에 약간의 차이가 있다는 것을 알게 되어 일부 수정을 해 보았습니다.


 


implementation 'org.locationtech.proj4j:proj4j:1.1.5'

gradle 파일에는 위와 같이 참조하는 implementation을 추가합니다. 


 


이제 코드를 구현해 보겠습니다.


val crsFactory = CRSFactory()
val WGS84 = crsFactory.createFromParameters(
"WGS84",
"+title=WGS84 +proj=latlong +ellps=WGS84 +datum=WGS84 +units=degrees"
)
val TM128 = crsFactory.createFromParameters(
"TM128",
"+proj=tmerc +lat_0=38 +lon_0=128 +k=0.9999 +x_0=400000 +y_0=600000 +ellps=bessel +towgs84=-146.43,507.89,681.46 +units=m +no_defs"
)
val ctFactory = CoordinateTransformFactory()
val wgsToUtm = ctFactory.createTransform(TM128, WGS84)
val result = ProjCoordinate()

val retrofit = RetrofitApi.opNetCreate()
retrofit.getUreaPrice(code = "당신의 API KEY", out = "json", area = "17")
.enqueue(object : retrofit2.Callback<UreaPriceBean> {
override fun onResponse(call: Call<UreaPriceBean>, response: Response<UreaPriceBean>) {
if (response.code() == 200) {
for(oilBean in response.body()?.oilBean!!.oilData) {
Log.e("onResponse=", "${oilBean.osNm} ${oilBean.gisXCoor} ${oilBean.gisYCoor}")
wgsToUtm.transform(ProjCoordinate(oilBean.gisXCoor.toDouble(), oilBean.gisYCoor.toDouble()), result)
Log.e("result=", "${result.y} ${result.x} ")
}
}
Log.e("onResponse=","${response.message()}")
}

override fun onFailure(call: Call<UreaPriceBean>, t: Throwable) {
Log.e("onFailure=", "${t.message}")
}

})

 


이제 실행해 보겠습니다.  아직은 화면에 표시 구현을 하지 않아서, 일단 로그를 찍어 보았습니다. 


 


로그



 


위치 정보로 지도에서 검색



지도에서 주유소 위치에 마크가 표시되는 것을 확인하였으니, 이제 앱으로 구현해 보고 정리가 완료되면 앱을 게시해 보겠습니다.


 


실제 구현된 이미지를 보도록 하겠습니다.


 


오피넷 API 활용 앱 예시



실제 적용한 앱은 playstore 에서 받아볼 수 있습니다.  


https://play.google.com/store/apps/details?id=com.billcoreatech.ontheway801 



 


주변검색, 주변찾기, 가는길에, 요소수 주유소 - Google Play 앱


내주변의 장소를 선택하고, 선택한 위치에 도착하면 사전에 등록한 알림을 보내 줍니다.


play.google.com




 





오늘의 이야기

https://github.com/afollestad/material-dialogs



 


GitHub - afollestad/material-dialogs: 😍 A beautiful, fluid, and extensible dialogs API for Kotlin & Android.


😍 A beautiful, fluid, and extensible dialogs API for Kotlin & Android. - GitHub - afollestad/material-dialogs: 😍 A beautiful, fluid, and extensible dialogs API for Kotlin & Android.


github.com




 


오늘은 미디움 에서 받은 메일의 내용중에서 기억해 두면 좋을 것 같은 정보 하나를 읽어 보도록 하겠습니다. 


 


앱을 만들다 보면 알림 다이얼로그를 구현해야 하는 경우가 종종 발생합니다. 이런 경우 일반적이라면 AlertDiaglog 을 활용하게 됩니다.  통상적인 다이얼로그는 theme 을 기준으로 해서 화면에 구현이 되고, 사각형 박스 형태로만 운영이 됩니다. 또한 컴스터하게 다이얼로그를 만들려고 할려면 layout  파일을 구성하거나 하는 방법으로 구현을 하기도 했습니다.


 


jetpack compose 으로 넘어가면서 그 마저도 화면을 일일이 구현하는 방식으로 전환 되어야 했습니다. 이제는 조금은 수월하게 알림을 구현해 볼 수 있을까 ? 도전해 보도록 하겠습니다.


 


먼저 일반적으로 작성한 코드를 살펴 볼께요.  다음과 같이 builder 을 만들고, 설정을 하고,  선언을 한 다음,


버튼의 색도 필요에 따라서 색을 입혀볼 수 있었습니다.


 


변경전 화면



val builder = AlertDialog.Builder(this@MainComposeActivity, R.style.Theme_MaterialComponents_Dialog_Alert)
.setTitle("[" + documents.placeName + "]" + getString(R.string.add_geofences))
.setMessage(getString(R.string.msgAddGeoFences))
.setPositiveButton(getString(R.string.OK), DialogInterface.OnClickListener{ dialogInterface, i ->
doAddGeoFence(documents)
})
.setNegativeButton(getString(R.string.CANCEL), DialogInterface.OnClickListener { dialogInterface, i ->

})
var alertDialog = builder.create()
alertDialog.show()
alertDialog.window?.setBackgroundDrawable(ColorDrawable(android.graphics.Color.TRANSPARENT))
alertDialog.getButton(DialogInterface.BUTTON_POSITIVE).setTextColor(getColor(R.color.white))
alertDialog.getButton(DialogInterface.BUTTON_NEGATIVE).setTextColor(getColor(R.color.softRed))

 


 


 


 


 


 


변경후에는 위에서 소개된 라이브러리를 이용해서 이겠지만, 코드가 무척 단순해 졌음을 알 수 있습니다.


변경후 예시



MaterialDialog(this).show {
icon (R.drawable.ic_location_softgreen)
title(text = "[" + documents.placeName + "]" + getString(R.string.add_geofences))
message(R.string.msgAddGeoFences)
positiveButton (R.string.OK) {
doAddGeoFence(documents)
}
negativeButton (R.string.CANCEL){

}
}

 


다만 font 나 버튼색을 입히기 위헤서 style 파일에 다이얼로그을 위한 style 을 지정했습니다.


<style name="AppTheme.Custom" parent="Theme.Ontheway801">

<item name="md_corner_radius">16dp</item>
<item name="md_color_title">@color/softBlue</item>
<item name="md_color_content">@color/softBlue</item>
<item name="md_color_button_text">@color/softBlue</item>
<item name="md_background_color">@color/transParent</item>
<item name="md_font_title">@font/poorstory_regular</item>
<item name="md_font_body">@font/poorstory_regular</item>
<item name="md_font_button">@font/poorstory_regular</item>

</style>

 


또한 onCreate 에서 setTheme 을 이용해서 설정을 해 주었습니다.


 


setTheme( R.style.AppTheme_Custom )

 


위애서 소개했던 material-dialogs 중에서 이제 겨우 1개에 대한 내용을 읽어보고 따라하기를 해 보았습니다. ㅋ~


언제 다 경험을 해 볼 수 있을까요?





오늘의 이야기


#billcorea #운동동아리관리앱
🏸 Schneedle, une application incontournable des clubs de badminton !
👉 Match Play – Enregistrez des scores et trouvez des adversaires 🎉
Parfait partout, seul, entre amis ou en club ! 🤝
Si vous aimez le badminton, essayez-le

Accédez à l'application 👉 https://play.google.com/store/apps/details?id=com.billcorea.matchplay




오늘의 이야기

about Android Studio



개발 툴에 패치가 있네요... 다른 건 아직이고 testOption에 대한 부분이 설명이 있어서 한번 읽어 볼꼐요.. 


 


Gradle Managed Virtual Devices (그래들 관리 가상 머신?)


In order to improve consistency, performance, and reliability when using Android Virtual Devices for your automated instrumented tests, we're introducing Gradle Managed Virtual Devices. This feature allows you to configure virtual test devices in your project's Gradle files that the build system uses to fully manage those devices—that is, create, deploy, and tear down—to execute your automated tests.
 
You can specify a virtual device that you want Gradle to use for testing your app in your module-level build.gradle file. The following code sample creates a Pixel 2 running API level 30 as a Gradle managed device.


 


자동화된 계측 테스트에 Android 가상 장치를 사용할 때 일관성, 성능 및 안정성을 개선하기 위해 Gradle 관리 가상 장치를 도입합니다. 이 기능을 사용하면 빌드 시스템이 자동화된 테스트를 실행하기 위해 해당 장치를 완전히 관리(즉, 생성, 배포 및 해체)하는 데 사용하는 프로젝트의 Gradle 파일에서 가상 테스트 장치를 구성할 수 있습니다.
 
모듈 수준 build.gradle 파일에서 Gradle이 앱 테스트에 사용할 가상 기기를 지정할 수 있습니다. 다음 코드 샘플은 API 레벨 30을 실행하는 Pixel 2를 Gradle 관리 기기로 생성합니다.


 


--- 아마도 테스트와 관련된 패치인 것으로 보이지만... 아직이라. 확인이 되면 다시 수정해 보도록 하겠습니다.





오늘의 이야기

앱을 만들다 보면 지도가 들어가는 앱을 만들게 되는 경우가 있다. GoogleMap API 등을 이용해서 앱을 만들게 되는 데, 이번에는 Jetpack Compose 기반의 GoogleMap 을 구현해 볼까 한다.


 


googleMap 들어 있는 앱(개발중...)



아직은 예전 java 코드에서 kotlin 코드로 이전 작업을 하는 중이라 미완성의 모습으로 보이는 데, 일단, 앱에 구글맵을 넣어 보았다.


 


먼저 gradle 파일에 추가해야 하는 것들은 다음과 같다.


// compose Maps
implementation 'com.google.android.gms:play-services-maps:18.1.0'
implementation "com.google.accompanist:accompanist-permissions:0.26.0-alpha"
implementation 'com.google.maps.android:maps-compose:2.5.3'
implementation "androidx.compose.foundation:foundation:$compose_version"
implementation 'com.google.maps.android:maps-compose-widgets:2.5.3'

버전에 따라서 달라질 수 는 있지만, 오늘 현재는 위와 같이 설정해 주면 된다. 


 


다음은 manifast 파일에 권한 설정을 보겠다.


 


<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />

일반적으로는 background_location 은 잘 사용하지 않는다, 구글에서도 background_location 권한을 사용하는 경우에는 사용자에게 권한 사용을 하고 있다는 안내를 통해서 권한 획득을 해야 한다고 하고 있고, fine_location 권한까지는 릴리즈 하는 동안에도 그다지 크게 통제(?)를 하지는 않지만, background_location 권한은 playstore에 릴리즈 하는 동안에도 까다롭게 심사를 하는 편이다.


 


이제 화면을 Jetpack compose 을 통해서 구현할 것이니 코드를 보도록 하겠다.



@OptIn(ExperimentalPermissionsApi::class)
@Composable
fun MapsScreen (
navigator: DestinationsNavigator,
) {

// 기본위치 표시을 위해 싱가폴의 위치정보
val singapore = LatLng(1.35, 103.87)
// 지도의 크기 조정에 따른 상태 기록
val cameraPositionState = rememberCameraPositionState {
position = CameraPosition.fromLatLngZoom(singapore, 10f)
}
// 구글맵 환경설정 관리
var uiSettings by remember { mutableStateOf(MapUiSettings(zoomControlsEnabled = sp.getBoolean("isMyLocationEnabled", false))) }
// 구글맵의 형태등을 관리 MayType.NORMAL 일반적인 맵, SATELLITE 위성사진, HYBRID 일반과 위성사진의 혼합등등 설정
var properties by remember {
mutableStateOf(MapProperties(mapType = MapType.NORMAL, isMyLocationEnabled = sp.getBoolean("isMyLocationEnabled", false)))
}
// 권한 획득에 관한 설정
val permissionState = rememberPermissionState(
permission = Manifest.permission.ACCESS_FINE_LOCATION,
)

....

when (permissionState.status) {
PermissionStatus.Granted -> {
Box(Modifier.fillMaxWidth().height(screenHeight * .3f)) {
GoogleMap(
modifier = Modifier.matchParentSize(),
cameraPositionState = cameraPositionState,
properties = properties,
uiSettings = uiSettings,
) {
Marker(
state = MarkerState(position = singapore),
title = "Singapore",
snippet = "Marker in Singapore"
)
}
Switch(
checked = uiSettings.zoomControlsEnabled,
onCheckedChange = {
uiSettings = uiSettings.copy(zoomControlsEnabled = it)
properties = properties.copy(isMyLocationEnabled = it)
}
)
}
}
else -> {
navigator.navigate(HomeScreenDestination)
}
}
}
}

 


대략적인 구글맵을 사용한 코드는 위와 같이 구현이 될 수 있다.  좀 더 자세한 것은 아래 링크를 참고해서 다시 봐야 할 듯 하지만...


 


https://developers.google.com/maps/documentation/android-sdk/maps-compose



 


지도 Compose 라이브러리  |  Android용 Maps SDK  |  Google Developers


의견 보내기 지도 Compose 라이브러리 Jetpack Compose는 UI 개발을 간소화하고 가속화하는 선언적인 네이티브 UI 도구 모음입니다. Jetpack Compose를 사용하면 앱이 어떻게 표시될지 설명하고 Jetpack Compose


developers.google.com




 


이제 실행 되는 모습을 잠시 살펴보면서 마무리를 해야겠다.


 



실행 동영상


 





오늘의 이야기


#스하리1000명프로젝트,
In Korea verloren? Auch wenn Sie kein Koreanisch sprechen, hilft Ihnen diese App dabei, sich problemlos fortzubewegen.
Sprechen Sie einfach Ihre Sprache – es übersetzt, sucht und zeigt Ergebnisse in Ihrer Sprache an.
Ideal für Reisende! Unterstützt mehr als 10 Sprachen, darunter Englisch, Japanisch, Chinesisch, Vietnamesisch und mehr.
Probieren Sie es jetzt aus!
https://play.google.com/store/apps/details?id=com.billcoreatech.opdgang1127




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로다가! ...