[Android] 실용적인 멀티모듈 아키텍처 설계 실전기 (Domain 레이어와 build-logic)
·
Android
안녕하세요! AI가 영어 일기를 첨삭해주는 앱, 'Hilingual' 프로젝트를 진행하고 있는 안드로이드 개발자 한민재입니다."AI가 영어 일기를 첨삭해주는 앱"이라는 아이디어로 시작된 Hilingual 프로젝트는 초기 단계부터 명확한 목표를 가지고 있었어요. 바로 지속 가능한 확장성 확보와 팀 단위 개발 생산성 극대화인데요. 저희는 팀 리더의 명확한 비전과 릴리즈에 대한 강한 의지를 바탕으로, 단순한 사이드 프로젝트를 넘어 '실무에 준하는 프로덕트', '수익을 창출해도 부끄럽지 않을 프로덕트'를 만들자는 높은 목표를 세웠어요. 향후 기능 확장의 가능성이 무궁무진하다고 판단했기에, 프로젝트 규모가 커짐에 따라 발생할 수 있는 코드의 강한 결합, 긴 빌드 시간, 명확한 의존성 분리, 그리고 개발자 간의 코..
[Android Compose] @Composable 종속성을 StateFlow로 바꿔보자
·
Android/Compose
안녕하세요, Android 개발자 한민재입니다.최근 Hilingual 프로젝트의 네비게이션 로직을 리팩토링 했어요. 이번 글에서는 @Composable에 의존하던 내비게이션 상태 로직을 UI와 분리된 상태 홀더(State Holder) 패턴과 StateFlow 를 통해 개선한 과정을 상세히 공유해 보려고 합니다. (해당 작업 PR입니다.)UI와 결합된 로직리팩토링 이전, MainNavigator 클래스는 앱의 메인 화면 탐색을 관리했어요. 이 클래스는 DroidKnightsApp을 레퍼런스로 사용했던 코드에요. 그러나 최근에 해당 작업자 본인이 말하길 “땜빵코드”라 꼭 고쳐서 쓰라고 조언을 받았습니다 😅저는 그 얘기를 듣고 한참을 고민했었어요. 좋은 코드같았는데 뭐가 문제였을까? 하면서 고민한 결과 몇..
[Android Compose] 생명주기를 고려한 Flow 수집으로 Side Effect 유실 막기
·
Android/Compose
안녕하세요! Compose로 개발하다 보면 Side Effect를 어떻게 처리할지 항상 고민인데요. 저도 ViewModel에서 발생한 이벤트를 LaunchedEffect로 받아서 스낵바를 보여주거나 화면을 전환하는 코드를 자주 작성했어요. 그런데 문득 앱이 백그라운드로 전환됐을 때 flow 수집이 중단되나? 생각이 들어서 테스트 해보니 LaunchedEffect가 계속 동작하는걸 발견했어요.이번 글에서는 제가 겪었던 문제와 flowWithLifecycle API로 해결한 경험, 그리고 이 과정에서 만든 재사용 가능한 확장 함수까지 공유해보려합니다! (해당 작업 PR 입니다)어떤 문제가 있었나요?보통 ViewModel의 단방향 이벤트(Side Effect)를 처리할 때 LaunchedEffect를 많이 사..
[Android Compose] Effect Handlers 딥다이브
·
Android/Compose
안녕하세요! 최근 개발 실력이 나름 늘어간다고 생각이 들면서, 이전까지 모호하게 알고 있다고 생각이 들었던 것들에 대해 다시 기초부터 다지는 중인데요. 내가 컴포즈로 개발을 하면서 사이드 이펙트에 대해 고민을 하고 코드를 작성하고 있었나?라는 생각이 들어 정리를 해봤습니다. 다른 분들에게도 도움이 됐으면 좋겠네요!사이드 이펙트란?사이드 이펙트는 컴포저블 함수의 범위를 벗어나 외부 상태가 변경되는 작업을 말해요. 네트워크 요청, 데이터베이스 접근, 파일 시스템 조작 등이 대표적인 예시예요.왜 문제가 될까요?컴포저블 함수는 언제든지, 몇 번이든 호출될 수 있어요. 리컴포지션은 상태 변화, 부모 컴포저블의 변화, 시스템 설정 변경 등 다양한 이유로 발생하죠. 만약 컴포저블 함수 내에서 직접 네트워크 요청을 한..
[Android Compose] 상태 읽기 지연(Defer State Reads)으로 리컴포지션 최적화하기
·
Android/Compose
Android Compose에서 성능 최적화는 단순히 "빠르게 만들기"를 넘어서, 사용자 경험을 좌우하는 핵심 요소예요. 특히 리컴포지션(Recomposition)을 얼마나 효율적으로 관리하느냐에 따라 앱의 반응성과 배터리 효율성이 크게 달라져요.오늘은 상태 읽기 지연(Defer State Reads) 패턴을 활용해 리컴포지션을 효과적으로 줄인 사례를 통해 Compose 성능 최적화를 알아볼게요.📊 리컴포지션의 3단계와 성능 비용Compose의 리컴포지션은 다음 3단계로 진행돼요컴포지션(Composition) - UI 구조를 다시 계산레이아웃(Layout) - 각 요소의 위치와 크기를 결정드로우(Draw) - 실제 화면에 그리기각 단계는 모두 연산 비용이 발생하며, 특히 컴포지션 단계에서는 모든 계산을..
[Kotlin Coroutine] runCatching으로 CancellationException을 안전하게 처리해보자
·
Kotlin
안녕하세요! 안드로이드 Compose 앱을 개발하면서 예외 처리에 대해 고민할 일이 종종 생기곤 하는데요. 특히 suspend 환경에서의 예외 처리라면 더욱 주의해야 하는 것 같아요.Sopt Makers에서 합류한 지도 벌써 4개월이 지났는데요, 그 과정에서 다시 관심을 갖게 된 주제가 있어요. 바로 RunCatching 인데요! 그리고 이에 관련된 이슈는 과거에도 굉장히 활발히 논의됐었어요.Provide a runCatching that does not handle a CancellationException but re-throws it instead. #1814이걸 계기로 기존에 구현된 코드들을 살펴보다가 suspendRunCatching을 어떻게 개선하면 좋을까? 라는 고민이 생겨서, 파헤쳐 본 내..
[Android Compose] Figma의 타원형 Radial Gradient를 구현해보자
·
Android/Compose
🧩 문제 상황: Compose에서 Figma gradient가 구현되지 않는다..!안녕하세요! 오늘은 컴포즈에서 Gradient 구현에 대해 얘기해보려고 해요!최근 참여중인 프로젝트 Spoony에서 아래와 같은 디자인이 있었어요!처음에는 단순히 Brush.radialGradient(...)로 해결되기를 제발 바랬어요,,, 하지만 결과는 너무 처참했어요. 그래서 피그마 스펙을 좀더 자세히 뜯어봤습니다. 중심점이 단순한 원처럼 보이지 않았거든요!border-radius: 40px;background: radial-gradient(141.42% 141.42% at 100% 0%, #FF5235 54.5%, #FFCEC6 100%);위의 ActiveTrack의 CSS스펙이에요. 이걸보고 아..단순한 원은 아니..
[Kotline Coroutine] OnFailure에 로그적기 귀찮아
·
Kotlin
안녕하세요! 오늘은 Result를 사용할 때 실패 시마다 반복해서 적던 로그 코드를 줄이기 위해 만든 확장 함수를 소개하려고 합니다. 사실 정말 간단해요ㅎㅎ1. 왜 만들었어요?제가 참여중인 Spoony에서는 런캐칭으로 감싸 뷰모델에서 onSuccess와 onFailure로 결과값을 처리하고 있었어요. 그러나 작업자마다 로그를 찍는 게 일관적이지 않았고 매번 실패 시에 Timber.e(e)를 적는 게 귀찮게 다가왔어요. 사용되는 부분이 많다 보니 매번 팀버 코드를 작성해야 했고 확장함수로 만드면 어떻냐는 팀원의 아이디어가 나왔어요.그래서 바로 작업에 들어갔습니다.2. Result 실패 처리 반복현재 ViewModel에서 다음과 같이 runCatching과 Result를 조합해 API 호출의 성공/실패를..
[Android Compose] Figma 그림자를 쉽게 만들어 보자
·
Android/Compose
안녕하세요! 오늘은 Jetpack Compose에서 디자이너가 피그마로 디자인한 그림자를 제대로 구현하기 위해 만든 확장함수를 소개합니다! 피그마의 양식을 어떻게 하면 제대로 적용할 수 있을지 고민했어요.1. 선 요약Jetpack Compose에서 제공하는 기본 Modifier.shadow는 커스터마이징에 한계가 있어요디자이너가 피그마에서 디자인한 그림자 효과를 구현하기 위해서 커스텀 확장 함수를 구현했어요그림자 효과를 구현하기 위해 Canvas API와 drawBehind 를 사용했어요성능 최적화를 위해 remember와 composed를 사용해 객체 재생성을 방지할 수 있어요2. 기본 Modifier.shadow의 한계Jetpack Compose에서 제공하는 기본 Modifier.shadow는 간단한..
[Android Jetpack] 이미지 압축 최적화하기: ImageDecoder 도입
·
Android/Jetpack
안녕하세요! Android 개발자 한민재입니다. 이번에는 Spoony 프로젝트를 진행하면서 겪었던 이미지 압축 관련 이슈를 다뤄보려고 해요. 게시글 작성 기능을 개발하면서 마주친 문제와 그 해결 과정을 공유하려고 합니다!해당 이슈가 담긴 PR입니다: https://github.com/SOPT-all/35-APPJAM-ANDROID-SPOONY/pull/200🤔 어떤 문제가 있었나요?게시글 작성 화면에서 이미지 업로드 기능을 구현하던 중이었는데요. 기존 BitmapFactory를 사용했을 때 다음과 같은 문제점들이 있었어요용량이 큰 단일 이미지 혹은 여러 이미지 압축 속도가 느렸어요 (평균 1,200ms 이상)용량이 큰 이미지를 여러 장 업로드하면 앱이 중단되는 현상이 발생했어요압축된 이미지 품질이 일관..