

Activity → Fragment : Activity에서 Fragment 호출 시 Bundle을 이용해 넘겨줄 수 있다.Fragment → Actvitiy : CallBack을 Activity가 정의하거나 CallBack Interface를 Activity가 구현하여 Fragment에서 CallBack 함수를 호출하여 전달한다.양 방향: 공통 ViewModel 이용 C언어에서는 개발자가 직접 free()를 호출하여 할당을 해제해줘야 하지만, Java와 Kotlin은 JVM이 메모리를 직접 관리한다. JVM은 다음을 가정하여 메모리에서 삭제할 객체를 판별한다.
따라서, "참조 중인가"를 판단하기 위해 JVVM이 직접 참조하는 GC Root를 시작으로 참조를 따라가 객체마다 생존 플래그를 추가한다. 따라서, GC는 이 생존 플래그가 없는 객체를 삭제하는 것이다.
또한 JVM은 Heap 영역을 Young, Old, Permanent(최근엔 Metaspace)로 나누어 관리한다.
최적화는 GC 호출 횟수를 줄이고, GC 수행 시간을 줄이는 것이라고 할 수 있다. 따라서, 다음 항목들을 지켜 안정된 런타임을 제공하는 것이 필수이다.
클린 아키텍처는 기본적으로 구현체(고수준)와 인터페이스(저수준)가 서로 영향을 주지 않도록 설계하여 관심사를 분리시키고 의존도를 낮춘다. 특히, 단방향 데이터 흐름(UDF)을 강조하였으며, 추상화 및 DIP로 구현한다. 이는 Android 권장 아키텍처에도 반영되어 있으며 큰 틀을 문서로 제공한다.
의존도를 살펴보면 Data -> Domain <- Presentation으로 되어있으며 자세한 내용은 이 글을 확인해보자.
OkHttp는 HTTP 요청 및 응답을 처리하는 저수준의 HTTP 클라이언트 라이브러리로, 네트워크 요청을 간소화하고 효율적인 네트워크 연결 재사용 및 캐싱 기능을 제공한다. 이를 발전시킨 것이 Retrofit이며, Retrofit은 Java 및 Android 용 type-safe한 REST API를 사용하여 안정적으로 HTTP 통신을 할 수 있게 해주는 라이브러리다. 다음 특징을 갖는다.
SharedPreferencesDataStoreRoom기존 Dagger 2를 기반으로 한 Android용 DI 프레임워크이다. 컴파일 타임에 의존성 그래프를 생성하고, 생성자, 모듈, 스코프를 기반으로 객체 생명주기를 관리한다. 다음과 같은 기본 골자를 가진다.
1.@Inject로 의존성 선언
2.@Module + @Provides/@Binds 로 의존성 제공
3. Application~ViewModel~Activity까지의 Component 계층을 자동 생성
4. Component 간 Scope를 통해 객체 생명주기를 자동 관리
주입된 객체가 어떤 생명 주기를 가질지 정의할 수 있으며, GC 타이밍을 직접적으로 결정할 수 있는 것이기 때문에 적절한 사용이 필요하다.
| Scope | Component | 생명주기 | 대표 예시 |
|---|---|---|---|
@Singleton | ApplicationComponent | 앱 전체 | Retrofit, DB 등 |
@ActivityRetainedScoped | ActivityRetainedComponent | ViewModel 수준 | Repository 등 |
@ViewModelScoped | ViewModelComponent | ViewModel 생명주기 | UseCase 등 |
@ActivityScoped | ActivityComponent | Activity 생명주기 | UI Helper 등 |
@FragmentScoped | FragmentComponent | Fragment 생명주기 | Fragment-specific dep |
@ServiceScoped | ServiceComponent | Service 생명주기 | Foreground Service 등 |
@ActivityRetainedScoped와 @ViewModelScoped가 서로 같은 것으로 보인다. 예를 들어 설명하자면, viewmodel의 lifecycle Owner가 activity면 서로 같다. 하지만 fragment라면 activity보다 짧은 생명 주기를 갖으며 이 때는 서로 다를 것이다. (참고)
Hilt의 의존성 그래프 외부에서 DI가 필요할 때 사용하는 진입점을 나타내며, Hilt가 직접 주입할 수 없는 클래스(BroadcastReceiver, 직접 작성하 클래스)가 그 대상이며, 비슷한 @AndroidEntryPoint는 Hilt에서 지원하는 Activity, Fragment를 EntryPoint로 지정할 때 사용하면 된다.
Module에서 객체를 생성하기 위한 로직을 정의할 때 사용된다. @Provides는 직접 객체 생성 로직을 포함하며, Retrfit이나 Room 같이 직접 정의해야하는 곳에 이용한다. 반면에, @Binds는 인터페이스 타입을 반환하며, 컴파일 타임에 바인딩하여 비교적 빠르다는 특징이 있다.
| 구분 | @Provides | @Binds |
|---|---|---|
| 목적 | 직접 객체 생성 로직 포함 | 인터페이스와 구현체 바인딩 |
| 사용 위치 | 함수 내부에서 new 생성 가능 | 추상 함수로 선언 |
| 리턴 타입 | 자유로움 | 반드시 인터페이스 타입 |
| 성능 | 약간 느림 (런타임 생성) | 컴파일 타임 바인딩 (빠름) |
Android의 공식 선언형 UI 프레임워크로, UI를 직접 갱신하는 대신 현재 상태를 구독하고 구독한 상태가 변경되면 UI를 자동으로 다시 그리는 시스템을 갖는다. Kotlin 코드로만 구성되며 UDF를 준수한다.
화면을 그리는 과정는 Frame 단위 내에서 이루어진다. 해당 Frame은 다음의 세 페이즈를 거쳐 수행된다.
Composition: 컴포저블 함수를 실행하고 UI를 구조화한다.Layout: 측정과 배치의 과정을 거쳐 각 트리 노드마다 2차원 좌표를 계산하여 컴퍼저블이 어디에 위치할지 정한다.Drawing: 기기의 화면에 UI 요소들을 렌더링한다.기본적으로 UDF를 지키며 페이즈가 수행된다. 그리고 각 페이즈 상태를 추적하고 있기 때문에 모든 페이즈가 항상 재실행되는 것은 아니다.
리컴포지션은 상태가 변경되어 새롭게 호출된 컴포저블 함수의 트리를 업데이트하는 것을 의미한다. 상태와 트리를 추적하고 있기 때문에 변경된 데이터와 상관없는 컴퍼저블 및 트리는 그대로 재사용된다. 따라서, 최적화는 재사용을 최대로 하는 것이 목표이다. 그러기 위해선, 다음과 같은 방법을 따라야한다.
Side-Effect란 Composable 함수가 자신의 UI 외부에 영향을 미치는 동작을 의미한다. 예를 들어, 네트워크 호출, 토스트, 화면 전환, 로그, 애니메이션 등이 있다. 대부분 리컴포지션 시에도 상태를 유지 또는 기억해야 한다. 이를 위해 다양한 API를 제공하고 있다.
| API | 용도 | 특징 |
|---|---|---|
LaunchedEffect | suspend 함수 호출, 코루틴 실행 | key 값이 바뀔 때만 실행 |
rememberCoroutineScope | Composable 내부에서 코루틴 실행 | scope 직접 생성, 취소 책임 개발자 |
SideEffect | UI 상태 외부 적용 | 매 Recomposition 시 실행됨 |
DisposableEffect | 리소스 생성/해제 | onDispose로 cleanup 가능 |
produceState | 외부 데이터 → State 변환 | Flow, Channel 등과 연동 |
rememberUpdatedState | 최신 상태 참조 유지 | 람다/콜백 참조 안전하게 유지 |
remember: 컴포저블 함수 내에서 값을 기억하여 리컴포지션 시 유지rememberSaveable: 화면 회전과 같은 Configuration Change에도 상태를 보존 mutableStateOf: Compose 상태의 기본 단위로, 값이 변경되면 자동으로 리컴포지션을 트리거derivedStateOf: 다른 상태를 기반으로 계산된 상태를 만들 때 사용해 불필요한 리컴포지션을 방지rememberUpdatedState: 리컴포지션 중 람다나 콜백을 최신 상태로 안전하게 유지하도록 돕는다.State Hoisting: 컴포저블을 Stateless로 만들기 위해, 상태를 외부에서 관리하고, 이벤트 전달 역할만 수행하도록 분리하는 것Single Source of Truth: 상태를 한 곳에서만 관리하도록 하여 UDF를 준수하도록 한다. 이를 통해 상태 변경과 UI 갱신이 명확해져 Side-Effect 및 리컴포지션 관리에 도움이 된다.