Firestore 로그 데이터를 Retool로 시각화하기!

최근에 Android 앱에서 실험용 성능 로그를 Firestore에 쌓아두고 있었는데,이 데이터를 좀 더 쉽게 확인할 수 있는 대시보드가 필요해졌다.그래서 Retool을 활용해서 간단한 내부용 분석 페이지를 만들어보았다.Firestore REST API를 그대로 호출하고, 받은 데이터를 Retool에서 가공해 UI로 보여주는 흐름이다. Firestore 데이터를 가져오기 위한 기본 설정 Firestore 문서를 REST API로 읽기 위해 다음과 같이 GET 요청을 구성했다.프로젝트 ID와 컬렉션 … Read more

Firebase Firestore를 붙여서 성능 결과를 수집해보기!

최근까지는 성능 측정 결과를 Room DB에만 저장하고 있었다.하지만 여러 사용자 디바이스의 결과를 한 곳에 모아서 보고 싶어졌고, 결국 Firebase Firestore를 붙여보기로 했다.몇 가지 후보가 있었는데, Firestore를 선택한 이유부터 Android 코드까지 정리 라쭈고 .. Firestore를 선택한 이유 처음에는 Supabase나 MongoDB Atlas 같은 것도 고민했었다.하지만 이번 목적은 복잡한 통계나 대규모 서버 운영이 아니라, 여기에 초점을 맞췄다. Firestore를 … Read more

Firestore에 쌓인 데이터를 Retool로 대시보드 형태로 시각화하기

안드로이드 앱에서 실험용 로그 데이터를 Firestore에 넣어두긴 했는데,이걸 매번 콘솔에서 열어서 보기는 너무 번거로웠다.그래서 이번에는 Retool을 활용해서Firebase Firestore 데이터를 직접 불러오고,테이블과 차트로 바로 확인할 수 있는 간단한 대시보드를 만들어봤다. 구성은 Firestore → Retool REST 연결 → 테이블/차트로 시각화 순서로 진행했다. Firestore를 Retool에 연결하기 위한 준비 Firestore REST API는 인증이 필요한데,이때 서비스 계정(JSON 키)을 사용한다.여기서 중요한 … Read more

Column + verticalScroll() vs LazyColumn, 무엇이 더 성능이 좋을까?

Jetpack Compose에서 스크롤 가능한 리스트 UI를 구현할 때 Column + verticalScroll()과 LazyColumn 중 무엇을 선택해야 하는지 고민하는 경우가 많다. 결론부터 말하면, 리스트라면 대부분 LazyColumn이 더 적합하다. 1. Column + verticalScroll()의 동작 방식 Column에 verticalScroll()을 붙이면 전체 내용을 한 번에 모두 그린다. 즉, 아이템이 5~10개 정도로 매우 적다면 큰 차이는 없다. 하지만 20개 이상부터는 체감 차이가 … Read more

sealed class vs enum, 언제 무엇을 써야 할까?

Kotlin에서 상태나 결과를 표현할 때 enum과 sealed class 사이에서 고민하는 경우가 많다.두 타입은 겉보기엔 비슷해 보이지만, 의도와 쓰임새는 분명히 다르다.핵심은 “값의 집합인가, 타입의 집합인가”다. enum: 고정된 값의 집합 enum은 값이 미리 정해진 유한한 목록일 때 가장 적합하다. 특징 예시 이런 경우 ExampleStatus는 when 문에서도 모든 케이스가 명확하게 드러난다. sealed class: 서로 다른 타입의 묶음 sealed … Read more

스레드와 Allocator, 그리고 왜 같이 이야기해야 하는가

이미지 처리, 추론 파이프라인, 고성능 시스템을 다루다 보면자연스럽게 “스레드”와 “allocator”라는 단어를 만나게 된다.이 둘은 각각 따로 보면 단순하지만, 같이 쓰이기 시작하는 순간 매우 중요한 설계 포인트가 된다. 이 글에서는 스레드(Thread)란 무엇인가 정의 스레드는 프로그램 안에서 실제로 코드를 실행하는 실행 흐름 단위다. 중요한 점은 서로 다른 스레드가 같은 코드, 같은 데이터에 동시에 접근할 수 있다는 사실이다. … Read more

RetinaFace, SCRFD, ncnn, TFLite 기본 개념 정리

이 글은 얼굴 검출 모델인 RetinaFace와 SCRFD, 그리고 이를 구동하는 런타임 포맷인 ncnn과 TFLite에 대해 기본 개념을 기록하기 위함임!각 모델과 포맷이 어떤 특성을 가지고 있고, 어떤 상황에서 활용되는지 정리한다. 1. RetinaFace란 무엇인가 RetinaFace는 InsightFace 팀에서 공개한 얼굴 검출 모델이다.anchor 기반의 구조를 사용하며, 입력 이미지에서 얼굴의 위치(bounding box)와 랜드마크(일반적으로 5점)를 동시에 예측한다.대규모 데이터셋(WIDER FACE 등)으로 사전 … Read more

Jetpack Compose에서 Lottie 재생 제어하기

animateLottieCompositionAsState vs LottieAnimatable 차이와 선택 기준 Jetpack Compose에서 Lottie 애니메이션을 붙일 때 자주 쓰는 방식은 두 가지다. 둘 다 같은 라이브러리(Lottie Compose) 안에 있지만, 재생 제어 모델이 완전히 다르다.특히 “토글로 잠깐 보여주는 애니메이션”이나 “항상 0프레임부터 다시 재생” 같은 요구가 있으면 선택이 갈린다. 1) animateLottieCompositionAsState: 선언형, 자동 재생 핵심 개념 animateLottieCompositionAsState는 Compose의 상태 변화에 맞춰 진행(progress)을 … Read more

왜 Android / Kotlin에서는 RAII가 불가능한가

C++의 RAII는 자원 관리 문제를 매우 강력하게 해결한다.그렇다면 자연스럽게 이런 질문이 생긴다. “왜 Kotlin이나 Android에서는 RAII를 쓰지 않는가?” 정확히 말하면,Android / Kotlin에서는 RAII를 언어 차원에서 구현할 수 없다. RAII의 전제 조건 RAII가 성립하려면 다음 조건이 필요하다. C++은 이 세 가지를 모두 만족한다. Kotlin / Java의 객체 생명주기 Kotlin과 Java는 GC 기반 언어다. 다음 코드를 보자. … Read more

Android에서 API 키 안전하게 관리하기 – Secrets Gradle Plugin 사용법

프로젝트하다가 실수로 API 키를 GitHub에 푸시한 적 있으신가요? 저는 있습니다… Google Maps API 키를 하드코딩해놨다가 커밋하고 나서 식은땀이 났던 기억이 나네요. 오늘은 Google에서 제공하는 Secrets Gradle Plugin으로 API 키를 안전하게 관리하는 방법을 공유해볼게요. 기존에 어떻게 했나? 보통 이런 식으로 했죠: // ❌ 절대 하면 안 되는 방법 class MapsActivity : AppCompatActivity() { private val API_KEY … Read more