[Android] 면접 예상 질문 목록

성승모·2025년 10월 13일

Android

목록 보기
1/8

Android OS 및 라이브러리

안드로이드 4대 컴포넌트

  1. Activity: UI를 제공하는 컴포넌트로, 사용자가 애플리케이션과 상호작용할 수 있는 화면을 구성한다. 각 Activity는 독립적으로 존재하며, 다른 Activity와 Intent를 통해 상호작용할 수 있다.
  2. Service: 백그라운드에서 실행되는 컴포넌트로, UI가 없고, 장기 실행 작업을 수행할 때 사용된다. 예를 들어, 음악 재생, 데이터 다운로드 등이 해당한다.
  3. Broadcast Receiver: 시스템 또는 다른 앱에서 발생하는 브로드캐스트 메시지를 수신한다. 배터리 상태 변화, 네트워크 연결 상태 변경 등의 이벤트를 처리할 수 있다.
  4. Content Provider: 앱 간에 데이터를 공유할 수 있도록 해주는 컴포넌트. 데이터베이스, 파일 등 다양한 데이터를 관리할 수 있으나, 최근에는 보안 상 이유로 ContentProvider 기반으로 구현된 API를 이용한다. 예를 들어, MediaStore, ContactsContract 등이 있다.

Activity 생명 주기


Fragment 생명 주기


Activity와 Fragment 간 데이터 전달 방법

  1. Activity → Fragment : Activity에서 Fragment 호출 시 Bundle을 이용해 넘겨줄 수 있다.
  2. Fragment → Actvitiy : CallBack을 Activity가 정의하거나 CallBack Interface를 Activity가 구현하여 Fragment에서 CallBack 함수를 호출하여 전달한다.
  3. 양 방향: 공통 ViewModel 이용

GC란

 C언어에서는 개발자가 직접 free()를 호출하여 할당을 해제해줘야 하지만, Java와 Kotlin은 JVM이 메모리를 직접 관리한다. JVM은 다음을 가정하여 메모리에서 삭제할 객체를 판별한다.

  • 대부분의 객체는 금방 접근 불가능한 상태(Unreachable)가 된다.
  • 오래된 객체에서 새로운 객체로의 참조는 아주 적게 존재한다.

 따라서, "참조 중인가"를 판단하기 위해 JVVM이 직접 참조하는 GC Root를 시작으로 참조를 따라가 객체마다 생존 플래그를 추가한다. 따라서, GC는 이 생존 플래그가 없는 객체를 삭제하는 것이다.

 또한 JVM은 Heap 영역을 Young, Old, Permanent(최근엔 Metaspace)로 나누어 관리한다.

  • Young: 새로 생성된 객체가 존재하며, 대부분 짧은 생명 주기를 가지고 있다. 따라서, Minor GC가 자주 발생하며 객체가 긴 시간 살아남는 경우 Old로 이동시킨다.
  • Old: 비교적 긴 시간 존재하는 객체가 존재하는 영역이며, 비교적 큰 메모리를 차지하며, 이 곳의 객체는 Major GC로 정리된다.
  • Permanent: JVM의 메타데이터와 클래스가 저장된다.

 최적화는 GC 호출 횟수를 줄이고, GC 수행 시간을 줄이는 것이라고 할 수 있다. 따라서, 다음 항목들을 지켜 안정된 런타임을 제공하는 것이 필수이다.

  • 비효율적인 객체 생성 -> Object Pooling, ViewHolder 패턴, Lazy Initialization
  • Bitmap 메모리 관리 -> Glide, Coil 이용, 사용 후 메모리 해제
  • 메모리 프로파일링로 개발 시 모니터링하고 적절한 대책 세우기

Android 에서 클린 아키텍처

 클린 아키텍처는 기본적으로 구현체(고수준)와 인터페이스(저수준)가 서로 영향을 주지 않도록 설계하여 관심사를 분리시키고 의존도를 낮춘다. 특히, 단방향 데이터 흐름(UDF)을 강조하였으며, 추상화 및 DIP로 구현한다. 이는 Android 권장 아키텍처에도 반영되어 있으며 큰 틀을 문서로 제공한다.

  1. Data Layer
    : 여러 DataSource를 포함하는 "Repository 구현체"로 구성되어 있다. DataSource는 데이터에 CRUD 연산을 수행하고 Repository는 데이터 연산을 조합하여 수행하거나 결과를 앱에서 쓰이는 형태로 가공하여 제공한다.
  2. Domain Layer
    : 여러 번 재사용되는 비즈니스 코드를 캡슐화하는 역할을 하며, DIP를 위해 Data Layer에서 사용되는 Repository의 인터페이스를 정의하여 사용한다.
  3. Presentation Layer
    : 당연히 UI 관련한 코드들을 포함하고 있으며, 사용자와 직접 상호작용하는 View, 상태를 저장하는 State Holder, 비즈니스 코드와 UI 코드를 분리하는 ViewModel 등이 있다.

의존도를 살펴보면 Data -> Domain <- Presentation으로 되어있으며 자세한 내용은 이 을 확인해보자.


Android에서의 비동기 프로그래밍 구현

  • Coroutine: 경량 스레드로, 비동기/동시성 코드를 동기적 스타일로 작성하게 해줌.
  • suspend 함수: 일시 중단 가능한 함수. 호출자는 suspend 컨텍스트(또는 coroutine) 안에서만 호출 가능.

Retrofit과 OkHttp

 OkHttp는 HTTP 요청 및 응답을 처리하는 저수준의 HTTP 클라이언트 라이브러리로, 네트워크 요청을 간소화하고 효율적인 네트워크 연결 재사용 및 캐싱 기능을 제공한다. 이를 발전시킨 것이 Retrofit이며, Retrofit은 Java 및 Android 용 type-safe한 REST API를 사용하여 안정적으로 HTTP 통신을 할 수 있게 해주는 라이브러리다. 다음 특징을 갖는다.

  • Interface를 활용
  • OkHttp 라이브러리가 제공하는 인터셉터, 캐싱 등도 가능
  • 간단한 어노테이션을 통해 메서드와 URL을 정의하여 코드가 명확함.
  • ConverterFactory를 사용하여 다양한 데이터 형식(JSON, XML)에 대해데이터 변환 컨버터를 제공
  • 동기, 비동기 둘 다 가능

로컬에 데이터를 저장할 수 있는 방법

  1. SharedPreferences
    • Key-Value 형태로 데이터를 XML 파일로 저장 -> 구조화 불가
    • UI 설정, 로그인 여부, 토큰 등 간단한 값에 적합
    • I/O가 메인 스레드에서 동기적으로 처리됨 → UI 지연 위험
  2. DataStore
    • SharedPreferences의 개선판
    • 코루틴 기반 비동기 I/O, Flow 지원
    • Key-Value 또는 타입 안전 직렬화 지원
  3. Room
    • Android 공식 로컬 DB 솔루션
    • SQLite 위에서 동작
    • 코루틴, Flow, RxJava 지원

Hilt에 대해 설명해주세요.

Hilt의 기본 원리

 기존 Dagger 2를 기반으로 한 Android용 DI 프레임워크이다. 컴파일 타임에 의존성 그래프를 생성하고, 생성자, 모듈, 스코프를 기반으로 객체 생명주기를 관리한다. 다음과 같은 기본 골자를 가진다.

1.@Inject로 의존성 선언
2.@Module + @Provides/@Binds 로 의존성 제공
3. Application~ViewModel~Activity까지의 Component 계층을 자동 생성
4. Component 간 Scope를 통해 객체 생명주기를 자동 관리

Scope는

 주입된 객체가 어떤 생명 주기를 가질지 정의할 수 있으며, GC 타이밍을 직접적으로 결정할 수 있는 것이기 때문에 적절한 사용이 필요하다.

ScopeComponent생명주기대표 예시
@SingletonApplicationComponent앱 전체Retrofit, DB 등
@ActivityRetainedScopedActivityRetainedComponentViewModel 수준Repository 등
@ViewModelScopedViewModelComponentViewModel 생명주기UseCase 등
@ActivityScopedActivityComponentActivity 생명주기UI Helper 등
@FragmentScopedFragmentComponentFragment 생명주기Fragment-specific dep
@ServiceScopedServiceComponentService 생명주기Foreground Service 등

@ActivityRetainedScoped@ViewModelScoped가 서로 같은 것으로 보인다. 예를 들어 설명하자면, viewmodel의 lifecycle Owner가 activity면 서로 같다. 하지만 fragment라면 activity보다 짧은 생명 주기를 갖으며 이 때는 서로 다를 것이다. (참고)

EntryPoint

 Hilt의 의존성 그래프 외부에서 DI가 필요할 때 사용하는 진입점을 나타내며, Hilt가 직접 주입할 수 없는 클래스(BroadcastReceiver, 직접 작성하 클래스)가 그 대상이며, 비슷한 @AndroidEntryPoint는 Hilt에서 지원하는 Activity, Fragment를 EntryPoint로 지정할 때 사용하면 된다.

@Provides와 @Binds

 Module에서 객체를 생성하기 위한 로직을 정의할 때 사용된다. @Provides는 직접 객체 생성 로직을 포함하며, Retrfit이나 Room 같이 직접 정의해야하는 곳에 이용한다. 반면에, @Binds는 인터페이스 타입을 반환하며, 컴파일 타임에 바인딩하여 비교적 빠르다는 특징이 있다.

구분@Provides@Binds
목적직접 객체 생성 로직 포함인터페이스와 구현체 바인딩
사용 위치함수 내부에서 new 생성 가능추상 함수로 선언
리턴 타입자유로움반드시 인터페이스 타입
성능약간 느림 (런타임 생성)컴파일 타임 바인딩 (빠름)

Compose

Compose란 + 기존 xml 방법과의 차이

 Android의 공식 선언형 UI 프레임워크로, UI를 직접 갱신하는 대신 현재 상태를 구독하고 구독한 상태가 변경되면 UI를 자동으로 다시 그리는 시스템을 갖는다. Kotlin 코드로만 구성되며 UDF를 준수한다.


페이즈

 화면을 그리는 과정는 Frame 단위 내에서 이루어진다. 해당 Frame은 다음의 세 페이즈를 거쳐 수행된다.

  • Composition: 컴포저블 함수를 실행하고 UI를 구조화한다.
  • Layout: 측정과 배치의 과정을 거쳐 각 트리 노드마다 2차원 좌표를 계산하여 컴퍼저블이 어디에 위치할지 정한다.
  • Drawing: 기기의 화면에 UI 요소들을 렌더링한다.

기본적으로 UDF를 지키며 페이즈가 수행된다. 그리고 각 페이즈 상태를 추적하고 있기 때문에 모든 페이즈가 항상 재실행되는 것은 아니다.


리컴포지션은

 리컴포지션은 상태가 변경되어 새롭게 호출된 컴포저블 함수의 트리를 업데이트하는 것을 의미한다. 상태와 트리를 추적하고 있기 때문에 변경된 데이터와 상관없는 컴퍼저블 및 트리는 그대로 재사용된다. 따라서, 최적화는 재사용을 최대로 하는 것이 목표이다. 그러기 위해선, 다음과 같은 방법을 따라야한다.

  • 불필요한 State 제거
  • remember / rememberSaveable 활용
  • derivedStateOf 사용
  • 복잡한 Composable 분리

Side-Effect는

 Side-Effect란 Composable 함수가 자신의 UI 외부에 영향을 미치는 동작을 의미한다. 예를 들어, 네트워크 호출, 토스트, 화면 전환, 로그, 애니메이션 등이 있다. 대부분 리컴포지션 시에도 상태를 유지 또는 기억해야 한다. 이를 위해 다양한 API를 제공하고 있다.

API용도특징
LaunchedEffectsuspend 함수 호출, 코루틴 실행key 값이 바뀔 때만 실행
rememberCoroutineScopeComposable 내부에서 코루틴 실행scope 직접 생성, 취소 책임 개발자
SideEffectUI 상태 외부 적용매 Recomposition 시 실행됨
DisposableEffect리소스 생성/해제onDispose로 cleanup 가능
produceState외부 데이터 → State 변환Flow, Channel 등과 연동
rememberUpdatedState최신 상태 참조 유지람다/콜백 참조 안전하게 유지

상태를 관리하기 위한 API나 패턴

API

  • remember: 컴포저블 함수 내에서 값을 기억하여 리컴포지션 시 유지
  • rememberSaveable: 화면 회전과 같은 Configuration Change에도 상태를 보존
  • mutableStateOf: Compose 상태의 기본 단위로, 값이 변경되면 자동으로 리컴포지션을 트리거
  • derivedStateOf: 다른 상태를 기반으로 계산된 상태를 만들 때 사용해 불필요한 리컴포지션을 방지
  • rememberUpdatedState: 리컴포지션 중 람다나 콜백을 최신 상태로 안전하게 유지하도록 돕는다.

패턴

  • State Hoisting: 컴포저블을 Stateless로 만들기 위해, 상태를 외부에서 관리하고, 이벤트 전달 역할만 수행하도록 분리하는 것
  • Single Source of Truth: 상태를 한 곳에서만 관리하도록 하여 UDF를 준수하도록 한다. 이를 통해 상태 변경과 UI 갱신이 명확해져 Side-Effect 및 리컴포지션 관리에 도움이 된다.

profile
안녕하세요!

0개의 댓글