2026/04/27
오늘의 이야기
#스하리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
오늘의 이야기
#스치니1000프로젝트 #재미 #행운기원 #Compose #Firebase
🎯 야 너 토요일마다 로또 확인하냐?
나도 맨날 “혹시나~” 하면서 봤거든 ㅋㅋ
근데 이제는 그냥 안 해
AI한테 맡겼어 🤖✨
그것도 구글 Gemini로다가!
그래서 앱 하나 만들었지
👉 “로또 예상번호 by Gemini” 🎱
AI가 분석해서 번호 딱! 뽑아줌
그냥 보고 참고만 하면 됨
재미로 해도 좋고…
혹시 모르는 거잖아? 😏
https://play.google.com/store/apps/details?id=com.billcorea.gptlotto1127
오늘의 이야기
Android에서 Hilt + Room + Firebase Realtime Database를 함께 사용하는 구조 설계
이 글은 Android 앱에서 Hilt를 사용한 의존성 주입, Room으로 로컬 DB를 구성하고, Firebase Realtime Database로 클라우드와 데이터를 연동하는 구조를 설계하는 방법을 다룹니다. 예시 코드마다 구체적인 설명과 함께, 주의사항과 실무 팁도 포함되어 있습니다.
🧱 프로젝트 구조 개요
📁 app/
├── di/ // Hilt 모듈 정의
├── data/
│ ├── local/ // Room 관련 코드
│ ├── remote/ // Firebase 관련 코드
│ ├── repository/ // Repository 패턴 구현
│ └── mapper/ // Entity ↔ Domain 변환
├── domain/ // 앱 전반에서 쓰이는 공통 데이터 모델
├── ui/ // Compose UI 화면
├── viewmodel/ // ViewModel 정의
└── MainActivity.kt
💡 팁: 레이어를 분리함으로써 유지보수가 쉬워지고, 테스트도 용이해집니다. 특히 Firebase와 Room을 함께 쓸 때는 'source of truth'를 명확히 구분해야 합니다.
📦 1. 도메인 모델 Member
data class Member(
val name: String = "",
val tokenId: String = "",
val role: String = "member",
val status: String = "",
val nextMatchIn: Int = -1,
val opponent: String = "",
val lat: Double = 0.0,
val lon: Double = 0.0
)
설명: 이 모델은 Room이나 Firebase에 의존하지 않는, 순수한 앱 로직 전용 데이터 모델입니다. ViewModel이나 Repository, Firebase 직렬화에 모두 사용될 수 있습니다.
주의: Firebase Realtime Database는 직렬화 시 기본 생성자와 모든 속성의 기본값을 요구하므로, = "", = -1 등을 반드시 지정해 주어야 합니다.
🏠 2. Room Entity MemberEntity
@Entity(tableName = "members")
data class MemberEntity(
@PrimaryKey(autoGenerate = true) val id: Int = 0,
val name: String,
val tokenId: String,
val role: String,
val status: String,
val nextMatchIn: Int,
val opponent: String,
val lat: Double,
val lon: Double
)
설명: Room은 반드시 @Entity와 @PrimaryKey가 필요합니다. 여기서는 id를 자동 생성 키로 사용하고, tokenId는 일반 필드로 처리합니다.
주의: Firebase의 tokenId는 앱 재설치 등으로 변경될 수 있기 때문에, 고유 식별자로 사용하지 말고 별도로 auto-generated ID를 쓰는 게 안전합니다.
🔁 3. Mapper 함수
fun Member.toEntity(): MemberEntity = MemberEntity(
name, tokenId, role, status, nextMatchIn, opponent, lat, lon
)
fun MemberEntity.toDomain(): Member = Member(
name, tokenId, role, status, nextMatchIn, opponent, lat, lon
)
설명: Room Entity와 앱 전용 모델 간에 변환을 책임지는 함수입니다. 이 함수를 통해 구조가 다르거나 어노테이션 충돌 없이 안전하게 변환할 수 있습니다.
팁: 이 함수를 mapper 패키지에 따로 두면 여러 레이어에서 재사용 가능합니다.
🧾 4. DAO 정의
@Dao
interface MemberDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(member: MemberEntity)
@Query("SELECT * FROM members")
fun getAll(): Flow<List>
@Delete
suspend fun delete(member: MemberEntity)
}
설명: Room에서 SQL 없이 데이터를 조작할 수 있는 핵심 인터페이스입니다. Flow를 리턴하여 Compose와 함께 reactive하게 사용할 수 있습니다.
주의: insert와 delete는 suspend로 지정해야 코루틴 안에서 호출 가능합니다. 비동기 안전성을 확보하세요.
📚 5. Room Database
@Database(entities = [MemberEntity::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun memberDao(): MemberDao
}
설명: Room에서 사용하는 DB 클래스입니다. DAO를 연결하고 전체 데이터베이스를 관리합니다.
주의: 데이터베이스 이름, 버전 변경 시에는 마이그레이션을 고려해야 합니다.
☁️ 6. FirebaseDataSource
class FirebaseDataSource @Inject constructor() {
private val db = FirebaseDatabase.getInstance().getReference("members")
fun saveMember(member: Member) {
db.child(member.tokenId).setValue(member)
}
fun getAllMembers(onComplete: (List<Member>) -> Unit) {
db.get().addOnSuccessListener {
val members = it.children.mapNotNull { snap ->
snap.getValue(Member::class.java)
}
onComplete(members)
}
}
}
설명: Firebase 연동을 담당하는 클래스입니다. 네트워크 I/O만 책임지며, UI나 DB 레이어와는 분리되어야 합니다.
주의: 콜백 기반이므로 suspend 함수로 감싸거나 Coroutine 내부에서 다루는 것이 안전합니다.
📦 7. Repository
class MemberRepository @Inject constructor(
private val dao: MemberDao,
private val firebase: FirebaseDataSource
) {
fun getLocalMembers(): Flow<List<Member>> =
dao.getAll().map { it.map { e -> e.toDomain() } }
suspend fun insert(member: Member) {
dao.insert(member.toEntity())
firebase.saveMember(member)
}
suspend fun syncFromFirebase() {
firebase.getAllMembers { members ->
CoroutineScope(Dispatchers.IO).launch {
members.forEach { dao.insert(it.toEntity()) }
}
}
}
}
설명: 로컬(RDB)과 원격(Firebase) 데이터를 모두 처리하는 Repository입니다. ViewModel이나 UI는 이 계층만 접근하도록 구성합니다.
주의: CoroutineScope를 직접 사용할 경우 lifecycle을 주의해야 하며, ViewModelScope 안에서 호출하는 것이 안전합니다.
🧠 8. ViewModel
@HiltViewModel
class MemberViewModel @Inject constructor(
private val repository: MemberRepository
) : ViewModel() {
val members = repository.getLocalMembers()
.stateIn(viewModelScope, SharingStarted.Lazily, emptyList())
fun insert(member: Member) {
viewModelScope.launch {
repository.insert(member)
}
}
fun sync() {
viewModelScope.launch {
repository.syncFromFirebase()
}
}
}
설명: UI 로직을 담당하는 ViewModel입니다. UI와 Repository 사이에서 데이터를 연결하며, LifecycleScope를 활용해 안전하게 비동기 작업을 처리합니다.
🧩 9. Hilt 모듈
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
@Provides
fun provideDatabase(@ApplicationContext context: Context): AppDatabase =
Room.databaseBuilder(context, AppDatabase::class.java, "app_db").build()
@Provides
fun provideMemberDao(db: AppDatabase): MemberDao = db.memberDao()
}
설명: Hilt로 Room과 DAO를 의존성 주입하기 위한 설정입니다. Hilt가 컴파일 타임에 의존성 그래프를 구성합니다.
주의: @Provides는 SingletonComponent 범위에서 관리하므로, 앱 전체에서 인스턴스를 공유합니다.
🖼️ 10. UI 예제 (Jetpack Compose)
@Composable
fun MemberScreen(viewModel: MemberViewModel = hiltViewModel()) {
val members by viewModel.members.collectAsState()
Column {
members.forEach {
Text("🙋♂️ ${it.name} (${it.role})")
}
Button(onClick = {
val member = Member(name = "홍길동", tokenId = UUID.randomUUID().toString())
viewModel.insert(member)
}) {
Text("멤버 추가")
}
}
}
설명: Compose 화면에서 ViewModel의 데이터를 관찰하고 버튼으로 멤버를 추가하는 예제입니다.
팁: Compose에서는 collectAsState()를 활용해 Flow나 StateFlow를 쉽게 관찰할 수 있습니다.
✅ 마무리
Hilt, Room, Firebase를 조합하면 강력한 구조로 안정적인 앱을 만들 수 있습니다. 다만 각 기술의 동작 원리와 데이터 흐름을 분리하는 설계가 매우 중요합니다.
- 💡 Entity ↔ Domain ↔ DTO 구조를 명확히 나누자
- ☁️ Firebase는 항상 변할 수 있다는 전제하에 다루자
- 🧠 ViewModel과 Repository에서 Flow와 Coroutine을 적극 활용하자
오늘의 이야기
#스하리1000명프로젝트,
Nawala sa Korea? Kahit na hindi ka nagsasalita ng Korean, tinutulungan ka ng app na ito na madaling makalibot.
Sabihin lang ang iyong wika—ito ay nagsasalin, naghahanap, at nagpapakita ng mga resulta pabalik sa iyong wika.
Mahusay para sa mga manlalakbay! Sinusuportahan ang 10+ wika kabilang ang English, Japanese, Chinese, Vietnamese, at higit pa.
Subukan ito ngayon!
https://play.google.com/store/apps/details?id=com.billcoreatech.opdgang1127
오늘의 이야기
💡 DevExpress dxChart & dxDataGrid 활용 가이드
이 블로그에서는 dxChart 및 dxDataGrid를 사용하는 방법에 대해 설명합니다. 각 질문별로 요점을 강조하고 예제를 포함하여 쉽게 이해할 수 있도록 구성하였습니다.
📊 dxChart 관련 질문 & 해결 방법
가능합니다! dxChart에서는 여러 개의 시리즈를 활용할 수 있습니다.
$("#chartContainer").dxChart({
dataSource: myData,
series: [
{ type: "bar", valueField: "barValue", name: "Bar Chart" },
{ type: "line", valueField: "lineValue", name: "Line Chart" }
]
});
각 시리즈를 서로 다른 축(valueAxis)에 바인딩하면 됩니다.
$("#chartContainer").dxChart({
dataSource: myData,
series: [
{ type: "bar", valueField: "largeValue", name: "Large Data", axis: "largeAxis" },
{ type: "line", valueField: "smallValue", name: "Small Data", axis: "smallAxis" }
],
valueAxis: [
{ name: "largeAxis", position: "left", min: 0, max: 100000 },
{ name: "smallAxis", position: "right", min: -100, max: 100 }
]
});
label.format 속성을 설정하여 해결할 수 있습니다.
$("#chartContainer").dxChart({
dataSource: myData,
series: [
{
valueField: "sales",
name: "Sales",
label: {
visible: true,
format: {
type: "fixedPoint",
precision: 2
}
}
}
]
});
📋 dxDataGrid 관련 질문 & 해결 방법
`cellTemplate` 또는 `calculateCellValue`를 활용하여 NaN을 공백으로 변환할 수 있습니다.
$("#gridContainer").dxDataGrid({
dataSource: myData,
columns: [
{
dataField: "price",
cellTemplate: function(container, options) {
let value = options.value;
container.text(isNaN(value) ? "" : value.toFixed(2));
}
}
]
});
`argumentAxis.inverted` 옵션을 활용하면 **X축 순서를 반대로 변경**할 수 있습니다.
$("#chartContainer").dxChart({
dataSource: myData,
argumentAxis: { inverted: true },
series: [{ valueField: "value", argumentField: "category", type: "bar" }]
});
🎯 마무리
이 블로그에서는 dxChart 및 dxDataGrid의 핵심 기능을 정리했습니다. 더 많은 활용 방법을 알고 싶다면 DevExpress 공식 문서를 참고하세요!
2026/04/26
오늘의 이야기
1. 전체 기능 설계도(텍스트 플로우)
[회원 관리]
|
v
[참여자 인식 (Watch)]
|
v
[팀 배정/리그 대진 생성]
|
v
[경기 진행]
| \
v \
[경기 결과 입력] [심박수 감지 및 알림 (Watch)]
| |
v |
[결과 저장 및 요약] <------/
|
v
[순위/통계/공유]2. 주요 기능별 상세
A. 회원 관리
- 회원 등록 및 목록 조회
- 동호회 정보 관리
B. 참여자 인식 및 팀 배정
- Wear OS Watch와의 연동
- 워치 착용자 자동 인식
- 인원 파악 및 명단 동기화
- 팀 자동/수동 배정
- 복식팀 조합 알고리즘
- 임의/무작위 배정 선택
C. 리그 대진표 생성
- 현재 팀 구성에 따른 리그 생성
- 경기 일정표 및 매치업 자동 생성
D. 경기 진행
- 경기 시작/종료 기록
- 세트/포인트 입력(스마트폰/워치)
- 실시간 경기 상황 표시
- Watch에서 심박수 실시간 측정
- 200bpm 이상 감지 시 알림
- 알림에 따라 휴식 권고
E. 결과 처리/저장
- 세트별 승패 기록
- 2승 기준 자동 종료 판정/기록 저장
- 각 팀/개인별 경기 통계
F. 리그 결과 및 공유
- 순위 집계 및 시각화(리그/전체)
- 통계(승률, 점수 등) 제공
- 결과 내보내기(공유, 백업 등)
3. 모듈 간 관계 요약(도식 예시)
- Watch 앱
- [심박수 측정] → [알림 팝업]
- [참여 인식] → [팀 선택] → [경기 결과 입력]
- ↔ 데이터 실시간 동기화 (Firebase, RESTful API)
- 스마트폰 앱
- [회원 관리] → [참여 인원 동기화]
- [팀/리그 설정] → [대진표 생성]
- [경기 상황 모니터링] ← [Watch 결과 수신]
- [리그 결과 및 통계] → [사용자 화면 표시/공유]
4. 기능 설계도 (Markdown 표 예시)
| 주요기능 | 세부설명 | 연동방식 | 주요화면 예시 |
|---|---|---|---|
| 회원 관리 | 회원 등록, 수정, 목록 | 스마트폰 | 회원 목록/등록 화면 |
| 참여자 인식 | Watch 착용자 자동 인식, 인원 동기화 | Watch/스마트폰 | 참여자 리스트 |
| 팀 배정/리그 생성 | 팀 자동/수동 편성, 리그 대진 자동 생성 | 스마트폰 | 팀 구성/대진표 화면 |
| 경기 진행 | 세트/포인트 입력, 경기 진행 상태 표시 | 스마트폰/Watch | 경기 진행/입력 화면 |
| 심박수 체크 및 알림 | 200bpm 이상 시 알림, 휴식 권고 | Watch | 알림(팝업) |
| 결과 저장/통계 | 경기 결과 저장, 통계 집계 | 스마트폰/서버 | 결과, 통계 화면 |
| 리그 결과 공유 | 최종 순위, 통계 공유/내보내기 | 스마트폰 | 결과 요약/공유 화면 |
오늘의 이야기
🌟 Welcome Korea – 당신의 현지 음식 가이드 앱!
이전에 Welcome Jeju라는 앱을 아시나요? 제주도 내 맛집과 장소 추천을 모으는 데 집중했던 간단한 안드로이드 앱이었죠. 매일 "맛집" 태그가 달린 인기 블로그나 웹 게시물을 스크래핑하여 "핫플레이스" 정보를 수집했었어요.
이제 저희 앱이 더 넓은 목적을 가지고 진화했습니다!
왜 리뉴얼이 필요했을까요?
한국을 방문할 때 구글 지도는 특히 명동과 같은 지역에서 자세한 현지 정보가 부족한 경우가 많습니다. 네이버 지도나 카카오 맵 같은 앱들은 한국어 사용자에게 최적화되어 있지만, 외국인 관광객들에게는 불편한 경향이 있죠.
명동 근처에서 프로젝트를 진행하면서, 많은 외국인 관광객들이 휴대폰에 몰두한 채 어디로 가야 할지 헤매는 모습을 보았습니다. 저의 서툰 영어로는 큰 도움을 드릴 수 없었기에, 외국인 여행자들에게 더 나은 서비스를 제공하기 위해 앱을 업그레이드하기로 결심했습니다.
또한, 앱 경험을 개선하기 위해 다양한 AI 도구들에게 조언을 구하며 얻은 통찰력도 이번 업데이트에 반영했습니다.
업데이트된 버전의 새로운 기능은 무엇인가요?
새 버전은 환영 화면으로 시작하며, 이제 다음 언어들을 지원합니다:
- 한국어
- 영어
- 중국어 (간체 & 번체)
- 필리핀어
- 베트남어
- 태국어
저는 한국을 방문하는 관광객의 큰 부분을 차지하는 동남아시아 방문객들이 주로 사용하는 언어에 초점을 맞췄습니다.
🧭 주요 기능
1. 다국어 지원
인터페이스 언어는 시작할 때 선택할 수 있으며, 나중에 메뉴를 통해 변경할 수 있습니다.
2. 메인 화면 – 주변 탐색
다음 기준으로 식당이나 장소를 검색할 수 있습니다:
- 키워드 (반경 5km 이내)
- 카테고리
- 한국 블로거들이 매일 선별한 데일리 핫플레이스
3. 음성 검색
키워드만 사용하여 음성으로 검색할 수 있습니다. 모국어로 말하면 앱이:
- 음성 입력을 한국어로 변환하고,
- 한국어로 검색한 다음,
- 결과를 다시 사용자의 언어로 번역해줍니다.
🔐 참고: 이 기능을 사용하려면 음성 인식 권한이 필요합니다.
4. 검색 결과 아이콘
- 📍 위치 마커: 검색 키워드에 기반한 위치
- 🚩 깃발 마커: 매일 수집된 "핫플레이스" 강조 표시
📱 지금 다운로드하세요!
앱은 현재 플레이 스토어에서 다운로드할 수 있습니다:
👉 Welcome Korea (맛집 리스트 모아보기)
“웹에 흩어져 있는 모든 제주 음식 팁을 한곳에 모았습니다 – 이제 더 똑똑하고, 다국어를 지원하며, 관광객 친화적입니다.”
오늘의 이야기
#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
오늘의 이야기
🔮 M - Mystery & Need
"복식 경기, 오늘 몇 명 올까? 누구랑 팀 짜야 하지?"
호기심 자극 메인 카피: "워치만 차면 자동 팀 구성된다고?"
- 매 경기, 팀 짜기 너무 번거롭지 않으셨나요?
- 경기 결과는 늘 기억 속에만 남으셨나요?
- 심박수 200 넘는 친구가 있는데, 아무도 몰랐다면?
이 앱 하나면, 모든 문제 해결됩니다.
❤️ A - Appeal & Affirm
- "모임 시작 전 워치만 차면 자동으로 참여 등록 끝"
- "알고리즘이 실시간으로 복식 팀 구성!"
- "경기 결과는 자동 저장, 다음 팀도 자동 배정"
- "심박수 200 이상? 워치가 쉬는 시간 알림까지!"
우리 브랜드의 철학은 스마트하게 운동을 즐기게 하자는 것.
💎 V - Value & Gap
| 기능 | 사용 전 | 사용 후 |
|---|---|---|
| 팀 편성 | 수기 작성, 시간 지연 | 자동 구성, 대기 없음 |
| 기록 저장 | 누락, 분실 | Firebase 자동 저장 |
| 안전 관리 | 없음 | 심박수 감지 → 알림 |
| 경기 흐름 | 중단 많음 | 리그 자동 운영 |
단순 앱이 아닙니다. 오늘 경기의 매니저가 되어드립니다.
🔍 E - Evidence & Interest
- Google 인증 API, Firebase 공식 기술 사용
- 베타 유저 94%가 “운영이 훨씬 수월해졌다” 응답
- "우리 클럽에서는 매주 이 앱으로 경기 운영해요" – 사용자 후기
- 캠핑/체육관/사내 동호회까지 확장 가능
🟢 N - Nudge (Reassure & CTA)
- 교환/환불: 앱은 설치형, Pro 결제 시 3일 내 환불 가능
- 심박수 센서 민감도 조절 가능
- "지금 설치하면 프리미엄 1개월 무료!"
- "워치 연동은 한정 기간 전용 기능!"
👉 앱 배포가 시작 되면 다시 ...
** 실제 이글의 연재는 2025.06.24 에 시작 합니다.
오늘의 이야기
🌟 Welcome Korea – あなたのローカルグルメガイドアプリ
以前のバージョンはどんな感じだったのか?
以前のWelcome Jejuは、シンプルなAndroidアプリで、済州島周辺の食事処や観光スポットのおすすめ情報を集めていました。人気ブログやウェブ記事から「맛집」(美味しい店)タグをつけた情報を毎日スクレイピングしていました。
なぜリニューアルが必要だったのか?
韓国を訪れる際、特に明洞のような観光地ではGoogleマップに詳しい地元情報が不足しています。NaverマップやKakaoマップは韓国語に最適化されていますが、外国人観光客にとっては使いにくい面があります。
明洞付近で外国人観光客がスマホの画面をじっと見て立ち止まっている姿をよく見かけました。彼らの多くは、今どこにいるのか、どこに行けばよいのか分からず戸惑っているようでした。私の英語力ではあまり助けられなかったため、もっと外国人旅行者に役立つアプリにしたいと思い、改良を決意しました。
さらに、様々なAIツールを活用してユーザー体験の向上を図りました。
新しいバージョンの特徴は?
最新バージョンはウェルカム画面から始まり、以下の言語に対応しています。
- 韓国語
- 英語
- 中国語(簡体字・繁体字)
- フィリピン語
- ベトナム語
- タイ語
特に東南アジアからの訪問者が多いため、彼らに使いやすい言語を重点的にサポートしています。
🧭 主な機能
- 多言語対応
起動時に言語選択ができ、後からメニューで変更も可能です。 - メイン画面 – 近くを探索
キーワードやカテゴリで検索でき、
半径5km以内のスポットが対象。
韓国のブロガーが毎日更新する「ホットプレイス」情報も見られます。 - 音声検索
母国語でキーワードを話すだけで、
アプリが音声を韓国語に変換し検索。
検索結果は元の言語に翻訳して表示されます。
※ 音声認識の許可が必要です。 - 検索結果アイコン
📍 位置マーカー:検索キーワードに基づく場所
🚩 フラグマーカー:毎日収集される「ホットプレイス」
📱 ダウンロードはこちら
Google Playストアで配信中:
👉 Welcome (맛집 리스트 모아보기)
「ウェブ上に散らばる済州島のグルメ情報をまとめました。今はもっと賢く、多言語対応で旅行者に優しいアプリです。」
https://play.google.com/store/apps/details?id=com.billcoreatech.opdgang1127&pcampaignid=web_share
옵디강 (Welcome) - Google Play 앱
인터넷에 널린(?) 전국 맛집 정보 제공
play.google.com
오늘의 이야기
#스하리1000명프로젝트
오늘 내가 만든앱 하나 알려주고 싶어, 이 앱은 알림수집기 라고 이름을 붙였는 데,
내 폰에 표시 되는 알림을 읽어서 내가 지정한 단어가 들어 있고, 지출기록을 남겨야 하는 알림이
있으면 수집하고, 카카오톡으로 친구에게 전달해 주는 기능을 구현해 줄꺼야. 📲
이번 패치에서는 하루 한번 지정한 시간에 나에게 알림(노티) 하도록 기능을 추가 했어. 🙏
한번 써보고 불편한 거 있으면 말해줘.
앱 바로가기
👉 https://play.google.com/store/apps/details?id=com.nari.notify2kakao
오늘의 이야기
Eclipse에서 MyBatis XML Schema 검증 방법 xml 검증 방안 MyBatis를 사용할 때 XML 매퍼 파일은 SQL 실행의 핵심입니다. 하지만 문법 오류나 매핑 문제로 인해...
-
주소 검색
-
이전 글에서 정리할 것처럼 java에서 kotlin으로 이전을 했습니다. 그러고 나서 보기 시작했는 데, DefaultSharedPrefernces의 사용할 수 없는 환경으로 변경이 된 것을 알게 되었습니다. 이전 prefs = Prefere...