2026/04/14
오늘의 이야기
#스하리1000명프로젝트
오늘 내가 만든앱 하나 알려주고 싶어, 이 앱은 알림수집기 라고 이름을 붙였는 데,
내 폰에 표시 되는 알림을 읽어서 내가 지정한 단어가 들어 있고, 지출기록을 남겨야 하는 알림이
있으면 수집하고, 카카오톡으로 친구에게 전달해 주는 기능을 구현해 줄꺼야. 📲
이번 패치에서는 하루 한번 지정한 시간에 나에게 알림(노티) 하도록 기능을 추가 했어. 🙏
한번 써보고 불편한 거 있으면 말해줘.
앱 바로가기
👉 https://play.google.com/store/apps/details?id=com.nari.notify2kakao
오늘의 이야기
Python 정규식으로 문자열에서 특정 문자만 남기기
Python에서 정규식을 사용하여 문자열에서 특정 문자만 남기고 나머지를 제거하는 방법을 알아보겠습니다. 예를 들어, 영문자, 숫자, 밑줄(_), 하이픈(-), <, 그리고 공백을 제외한 모든 문자를 제거하는 방법을 살펴보겠습니다.
정규식 치환 예제
다음은 정규식을 사용하여 문자열에서 특정 문자만 남기고 나머지를 제거하는 예제입니다:
Python
import re
text = "Hello, World! 1234 @#$%^&*()_+-<> "
# 정규식을 사용하여 영문자, 숫자, 밑줄, 하이픈, <, 공백을 제외한 모든 문자 치환
result = re.sub(r'[^A-Za-z0-9_<\-\s]', '', text)
print(result) # "Hello World 1234 _-<> "
AI가 생성한 코드입니다. 신중하게 검토하고 사용하세요. FAQ의 자세한 정보.
코드 설명
모듈 임포트: re 모듈을 임포트합니다.
문자열 정의: 예제로 사용할 문자열을 정의합니다.
정규식 패턴: [^A-Za-z0-9_<\-\s] 패턴을 사용하여 영문자, 숫자, 밑줄, 하이픈, <, 공백을 제외한 모든 문자를 빈 문자열로 치환합니다.
[^...]: 대괄호 안에 있는 문자들을 제외한 모든 문자를 매칭합니다.
A-Za-z: 모든 영문자.
0-9: 모든 숫자.
_: 밑줄.
-: 하이픈.
<: < 문자.
\s: 공백 문자.
치환 및 출력: re.sub() 함수를 사용하여 치환하고 결과를 출력합니다.
결론
이 방법을 사용하면 문자열에서 특정 문자만 남기고 나머지를 제거할 수 있습니다. 정규식을 활용하면 다양한 문자열 처리 작업을 효율적으로 수행할 수 있습니다.
이 포스트가 도움이 되길 바랍니다! 다른 질문이 있으면 언제든지 알려주세요. 😊
오늘의 이야기
공공데이터를 사용하여 Python으로 데이터 수집 및 처리하기
안녕하세요! 오늘은 공공데이터를 활용하여 Python을 사용해 데이터를 수집하고 처리하는 방법에 대해 소개해드리려고 합니다. 이번 예제에서는 http://apis.data.go.kr/1471000/MdcinGrnIdntfcInfoService01/getMdcinGrnIdntfcInfoList01 API를 사용하여 의약품 정보를 가져오는 과정을 다룹니다.
준비물
- Python 설치
- requests 라이브러리 설치
먼저, requests 라이브러리를 설치합니다:
sh
복사
pip install requests
API 키 발급
공공데이터 포털에서 API 키를 발급받아야 합니다. 발급받은 키를 활용하여 데이터를 요청합니다.
코드 예제
다음은 API를 호출하고 응답 데이터를 처리하는 Python 예제 코드입니다:
python
복사
import requests
# API URL과 키 설정
api_url = "http://apis.data.go.kr/1471000/MdcinGrnIdntfcInfoService01/getMdcinGrnIdntfcInfoList01"
api_key = "YOUR_API_KEY" # 여기서 API 키를 입력하세요.
# 요청 파라미터 설정
params = {
'serviceKey': api_key,
'numOfRows': 10,
'pageNo': 1,
'type': 'json'
}
# API 호출
response = requests.get(api_url, params=params)
# 응답 상태 확인
if response.status_code == 200:
data = response.json()
# 응답 데이터 처리
items = data.get('body', {}).get('items', [])
for item in items:
print(f"품목명: {item.get('ITEM_NAME')}, 성분명: {item.get('MATERIAL_NAME')}")
else:
print("API 요청 실패:", response.status_code)
주요 단계
- API URL 및 키 설정: 발급받은 API 키를 사용하여 요청 URL을 구성합니다.
- 요청 파라미터 설정: 필요한 파라미터를 설정하여 API 요청을 준비합니다.
- API 호출: requests.get() 메서드를 사용하여 API 호출을 수행합니다.
- 응답 상태 확인: 응답 상태 코드를 확인하여 요청이 성공적으로 처리되었는지 확인합니다.
- 응답 데이터 처리: 응답 데이터를 JSON 형식으로 파싱하여 필요한 정보를 추출합니다.
이 예제에서는 공공데이터 포털의 API를 사용하여 의약품 정보를 가져오고, 이를 Python으로 처리하는 방법을 보여줍니다. API를 활용하면 다양한 공공데이터를 쉽게 사용할 수 있으니, 여러분도 한번 시도해 보세요!
오늘의 이야기
#스하리1000명프로젝트
오늘 내가 만든앱 하나 알려주고 싶어, 이 앱은 알림수집기 라고 이름을 붙였는 데,
내 폰에 표시 되는 알림을 읽어서 내가 지정한 단어가 들어 있고, 지출기록을 남겨야 하는 알림이
있으면 수집하고, 카카오톡으로 친구에게 전달해 주는 기능을 구현해 줄꺼야. 📲
이번 패치에서는 하루 한번 지정한 시간에 나에게 알림(노티) 하도록 기능을 추가 했어. 🙏
한번 써보고 불편한 거 있으면 말해줘.
앱 바로가기
👉 https://play.google.com/store/apps/details?id=com.nari.notify2kakao
오늘의 이야기
Android Foreground Services 사용법
안녕하세요! 이번 포스트에서는 Android Foreground Services에 대해 알아보겠습니다. Foreground Service는 사용자가 앱과 상호작용하지 않더라도 백그라운드에서 지속적으로 실행되어야 하는 작업을 수행하는 데 사용됩니다. 아래는 Foreground Service 설정 방법에 대한 자세한 설명입니다.
1. 서비스 클래스 생성
먼저 Service를 상속받는 서비스 클래스를 정의합니다.
class MyForegroundService : Service() {
private lateinit var notification: Notification
override fun onCreate() {
super.onCreate()
startForeground(1, createNotification())
}
private fun createNotification(): Notification {
val builder = NotificationCompat.Builder(this, "channel_id")
.setContentTitle("Foreground Service")
.setContentText("This is a foreground service")
.setSmallIcon(R.drawable.ic_launcher_foreground)
.setContentIntent(PendingIntent.getActivity(this, 0, Intent(this, MainActivity::class.java), 0))
.setAutoCancel(false)
// android api 26 이상의 경우 필요
val channel = NotificationChannel("channel_id", "Channel Name", NotificationManager.IMPORTANCE_DEFAULT)
val notificationManager = getSystemService(NotificationManager::class.java)
notificationManager.createNotificationChannel(channel)
return builder.build()
}
override fun onBind(intent: Intent): IBinder? {
return null
}
override fun onDestroy() {
super.onDestroy()
stopForeground(true)
}
}2. 서비스 시작
서비스를 시작하려면 액티비티나 프래그먼트에서 다음과 같이 호출합니다.
val serviceIntent = Intent(this, MyForegroundService::class.java)
// android api 30 이상의 경우
startForegroundService(serviceIntent)3. AndroidManifest.xml 설정
AndroidManifest.xml 파일에 서비스 선언을 추가합니다.
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE" />
<application
...
<service
android:name=".MyForegroundService"
android:exported="false"
android:foregroundServiceType="connectedDevice"
/>
</application>4. Notification Channel 생성
앱이 Android Oreo(API 레벨 26) 이상을 타겟으로 하는 경우, Notification Channel을 생성해야 합니다.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel("channel_id", "Channel Name", NotificationManager.IMPORTANCE_DEFAULT)
val notificationManager = getSystemService(NotificationManager::class.java)
notificationManager.createNotificationChannel(channel)
}
5. Deprecated 메서드 대체
stopForeground 메서드가 deprecated 되었기 때문에, 대신 stopSelf 또는 stopService 메서드를 사용합니다.
override fun onDestroy() {
super.onDestroy()
stopSelf()
}
6. 안드로이드 생명 주기와 서비스 관리
적절한 시점에 startService와 stopService를 호출하여 서비스의 시작과 종료를 관리해야 합니다. 예를 들어:
- onCreate() 또는 onStart(): 앱이 처음 실행될 때.
- onDestroy(): 앱이 종료될 때.
이 포스트가 Android Foreground Services 설정과 관리에 도움이 되길 바랍니다. 궁금한 점이 있으면 언제든지 댓글로 남겨주세요! 😊
오늘의 이야기
#스하리1000명프로젝트,
Lost in Korea?即使您不会说韩语,这个应用程序也可以帮助您轻松出行。
只需说出您的语言即可 - 它会翻译、搜索并以您的语言显示结果。
非常适合旅行者!支持英语、日语、中文、越南语等10多种语言。
现在就试试吧!
https://play.google.com/store/apps/details?id=com.billcoreatech.opdgang1127
오늘의 이야기
#스치니1000프로젝트 #재미 #행운기원 #Compose #Firebase
🎯 야 너 토요일마다 로또 확인하냐?
나도 맨날 “혹시나~” 하면서 봤거든 ㅋㅋ
근데 이제는 그냥 안 해
AI한테 맡겼어 🤖✨
그것도 구글 Gemini로다가!
그래서 앱 하나 만들었지
👉 “로또 예상번호 by Gemini” 🎱
AI가 분석해서 번호 딱! 뽑아줌
그냥 보고 참고만 하면 됨
재미로 해도 좋고…
혹시 모르는 거잖아? 😏
https://play.google.com/store/apps/details?id=com.billcorea.gptlotto1127
2026/04/13
오늘의 이야기
#스하리1000명프로젝트,
Às vezes é difícil conversar com trabalhadores estrangeiros, certo?
Fiz um aplicativo simples que ajuda! Você escreve na sua língua e os outros veem na deles.
Ele é traduzido automaticamente com base nas configurações.
Muito útil para bate-papos fáceis. Dê uma olhada quando tiver uma chance!
https://play.google.com/store/apps/details?id=com.billcoreatech.multichat416
오늘의 이야기
코인 4.0: 코틀린 멀티플랫폼 개발을 위한 강화된 의존성 주입
• 코틀린 2.0을 기반으로 구축된 코인 4.0은 플랫폼 전반에 걸쳐 일관된 UUID 생성, 향상된 컨텍스트 통과, 보다 스레드 안전하고 성능이 뛰어난 컬렉션을 포함한 다양한 개선 사항을 도입한다.
• 여전히 실험적인 새로운 코인-푸 DSL은 구성자 DSL의 한계를 해결하고 "싱글오프" API에 대한 더 나은 통일된 경험을 제공하는 것을 목표로 한다.
• 코인 4.0은 ViewModel API에 주요 업그레이드를 제공하며 안드로이드, 제트팩 컴포즈 및 기타 프레임워크에 대한 통합이 개선된 완전 멀티플랫폼 접근 방식으로 전환합니다.
• 컴포즈 뷰모델 내비게이션 지원은 인수 주입 개선, 컴포즈 내비게이션 1.7과의 호환성 등을 포함해 도입된다.
• 새로운 그래들 패키지인 koin-androidx-startup은 AndroidX Startup으로 스타트업 프로세스를 최적화하여 부하 시간을 최대 40%까지 줄입니다.
• 코인의 컴포즈 API는 플랫폼 전반에 걸쳐 더 쉬운 통합 및 코드 재사용을 가능하게 하는 더 많은 멀티플랫폼 기능에 대한 지원을 추가하기 위해 재작업되었습니다.
https://blog.insert-koin.io/koin-4-0-official-release-f4827bbcfce3
Koin 4.0 — Official Release
The Koin team is excited to announce the release of Koin 4.0, a major update that brings improvements for Kotlin, Compose & Android
blog.insert-koin.io
오늘의 이야기
Jetpack Compose에서 Navigation 구현하기: compose-destinations와 AnimatedBottomBarCompose 사용
소개
이번 포스트에서는 Jetpack Compose에서 네비게이션을 구현하는 방법을 다룹니다. 특히, compose-destinations와 AnimatedBottomBarCompose 라이브러리를 사용하여 스타일리시한 하단 네비게이션 바를 만드는 방법을 설명합니다.
1. 의존성 추가
먼저, build.gradle 파일에 필요한 의존성을 추가합니다.
dependencies {
implementation("io.github.raamcosta.compose-destinations:core:1.7.0-beta")
ksp("io.github.raamcosta.compose-destinations:ksp:1.7.0-beta")
implementation("com.canopas.compose-animated-navigationbar:bottombar:1.0.1")
implementation("androidx.compose.material3:material3:1.1.0")
}
2. KSP 설정
compose-destinations는 KSP를 사용하므로, 프로젝트의 build.gradle 파일에 KSP 플러그인을 추가해야 합니다.
plugins {
id("com.google.devtools.ksp") version "1.7.10-1.0.6"
}
3. 화면 컴포저블 정의
각 화면을 @Destination 어노테이션을 사용하여 정의합니다.
@Destination
@Composable
fun HomeScreen(navigator: DestinationsNavigator) {
Column {
Text("Home Screen")
Button(onClick = { navigator.navigate(DetailsScreenDestination) }) {
Text("Go to Details")
}
}
}
@Destination
@Composable
fun DetailsScreen(navigator: DestinationsNavigator) {
Column {
Text("Details Screen")
Button(onClick = { navigator.navigateUp() }) {
Text("Back to Home")
}
}
}
4. NavHost 설정
NavHost를 설정하여 네비게이션 그래프를 정의합니다.
@Composable
fun MainScreen() {
val navController = rememberNavController()
Scaffold(
bottomBar = { BottomNavigationBar(navController) }
) {
NavHost(
navController = navController,
startDestination = HomeScreenDestination.route
) {
composable(HomeScreenDestination.route) { HomeScreen(navController.toDestinationsNavigator()) }
composable(DetailsScreenDestination.route) { DetailsScreen(navController.toDestinationsNavigator()) }
}
}
}
5. AnimatedBottomBar 구현
AnimatedBottomBarCompose를 사용하여 하단 네비게이션 바를 구현합니다.
@Composable
fun BottomNavigationBar(navController: NavController) {
val navigator = navController.toDestinationsNavigator()
val items = listOf(
NavigationItem(HomeScreenDestination, Icons.Default.Home, "Home"),
NavigationItem(DetailsScreenDestination, Icons.Default.Info, "Details")
)
val currentRoute = navController.currentBackStackEntryAsState().value?.destination?.route
var selectedItem by remember { mutableStateOf(0) }
AnimatedBottomBar(
selectedItem = selectedItem,
itemSize = items.size,
containerColor = Color.White,
contentColor = Color.Blue,
indicatorColor = Color.Blue,
indicatorStyle = IndicatorStyle.LINE
) {
items.forEachIndexed { index, navigationItem ->
BottomBarItem(
selected = currentRoute == navigationItem.dest.route,
onClick = {
if (currentRoute != navigationItem.dest.route) {
selectedItem = index
navigator.navigate(navigationItem.dest)
}
},
imageVector = navigationItem.icon,
label = navigationItem.label,
containerColor = Color.Transparent,
)
}
}
}
data class NavigationItem(val dest: Direction, val icon: ImageVector, val label: String)결론
이렇게 하면 compose-destinations와 AnimatedBottomBarCompose를 조합하여 스타일리시한 하단 네비게이션 바를 구현할 수 있습니다. 이 방법을 통해 네비게이션을 보다 쉽게 관리하고, 사용자 경험을 향상시킬 수 있습니다.
오늘의 이야기
Koin을 사용하여 UARTManager에서 Room Database 참조하기
안녕하세요! 오늘은 Koin을 사용하여 UARTManager에서 Room Database를 참조하는 방법에 대해 알아보겠습니다. Koin은 간단하고 경량화된 의존성 주입 프레임워크로, 안드로이드 애플리케이션에서 많이 사용됩니다.
1. Room Database 설정
먼저 Room Database를 설정해야 합니다. Entity, DAO, Database 클래스를 정의합니다.
@Entity(tableName = "example_table")
data class ExampleEntity(
@PrimaryKey(autoGenerate = true) val id: Int,
val name: String
)
@Dao
interface ExampleDao {
@Query("SELECT * FROM example_table")
fun getAll(): List<ExampleEntity>
@Insert
fun insert(exampleEntity: ExampleEntity)
}
@Database(entities = [ExampleEntity::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun exampleDao(): ExampleDao
}
2. Koin 모듈 설정
Koin 모듈을 설정하여 Room Database와 DAO를 제공하도록 합니다.
val appModule = module {
single {
Room.databaseBuilder(get(), AppDatabase::class.java, "example.db")
.build()
}
single { get<AppDatabase>().exampleDao() }
}
3. Koin 초기화
Application 클래스에서 Koin을 초기화합니다.
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
startKoin {
androidContext(this@MyApplication)
modules(appModule)
}
}
}
4. UARTManager에서 Room Database 참조
UARTManager 클래스에서 Room Database를 참조합니다.
class UARTManager(private val exampleDao: ExampleDao) {
fun getAllExamples(): List<ExampleEntity> {
return exampleDao.getAll()
}
fun insertExample(exampleEntity: ExampleEntity) {
exampleDao.insert(exampleEntity)
}
}
5. UARTManager 주입
Koin을 사용하여 UARTManager를 주입합니다.
val uartModule = module {
single { UARTManager(get()) }
}
startKoin {
androidContext(this@MyApplication)
modules(listOf(appModule, uartModule))
}
이렇게 설정하면 UARTManager에서 Room Database를 참조할 수 있습니다. Koin을 통해 의존성을 주입하여 코드의 가독성과 유지보수성을 높일 수 있습니다.
Room LiveData 예외 처리
Room을 사용하다 보면 FATAL EXCEPTION: arch_disk_io_1와 같은 오류를 만날 수 있습니다. 이 오류는 주로 다음과 같은 원인으로 발생합니다:
- 스키마 불일치: 데이터베이스 스키마를 변경했지만 버전 번호를 업데이트하지 않은 경우.
- 손상된 데이터베이스: 데이터베이스가 손상된 경우.
- 동시성 문제: 여러 스레드가 동시에 데이터베이스에 접근하는 경우.
- 데이터 변환 문제: LiveData에서 데이터가 변환되거나 관찰되는 방식에 문제가 있는 경우.
이 문제를 해결하기 위해서는 데이터베이스 버전 번호를 증가시키거나, 앱 데이터를 지우고 다시 설치하거나, 적절한 스레딩 메커니즘을 사용하는 등의 방법을 고려할 수 있습니다.
이 요약이 도움이 되셨길 바랍니다! 추가로 궁금한 점이 있으시면 언제든지 질문해 주세요. 😊
오늘의 이야기
#스치니1000프로젝트 #재미 #행운기원 #Compose #Firebase 🎯 야 너 토요일마다 로또 확인하냐? 나도 맨날 “혹시나~” 하면서 봤거든 ㅋㅋ 근데 이제는 그냥 안 해 AI한테 맡겼어 🤖✨ 그것도 구글 Gemini로다가! ...
-
이전 글에서 정리할 것처럼 java에서 kotlin으로 이전을 했습니다. 그러고 나서 보기 시작했는 데, DefaultSharedPrefernces의 사용할 수 없는 환경으로 변경이 된 것을 알게 되었습니다. 이전 prefs = Prefere...