Study

안드로이드 면접 북 스터디 Q50 ~ Q59

hegunhee 2025. 10. 20. 21:58

안드로이드 기술 면접 책 북 스터디를 하면서
보다 간결하게 실제 면접하는 것처럼 글을 정리해보려 한다.

 

중요하다고 생각된 질문만 정리했다.

 

Q51) ViewBinding을 사용하면 어떤 장점이 있나요? 

ViewBinding은 레이아웃의 뷰와 상호 작용하는 프로세스를 단순화하기 위해 도입된 기능입니다.

수동으로 findViewById()를 호출하지 않아도 되고, 뷰에 접근하는 타입-세이프 방식을 제공하여

보일러 플레이트 코드를 줄이고 잠재적인 런타임 오류를 최소화합니다.

 

ViewBinding 작동 방식

프로세스에서 ViewBinding을 활성화하면 안드로이드는 각 XML 레이아웃 파일에 대한 바인딩 클래스를 생성합니다.

생성된 바인딩 클래스의 이름은 레이아웃 파일 이름에서 파생되어

각 밑줄은 카멜 케이스로 변환되고 이름 끝에 Binding이 추가됩니다.
예) activity_main.xml -> ActivityMainBinding

 

class MainActivity : AppCompatActivity() {
    // 바인딩 클래스 인스턴스 선언
    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // 레이아웃 인플레이트 및 바인딩 클래스 초기화
        binding = ActivityMainBinding.inflate(layoutInflater)
        // 루트 뷰를 액티비티의 콘텐츠 뷰로 설정
        setContentView(binding.root)
        // 바인딩 객체를 통해 뷰에 직접 접근
        binding.textView.text = "Hello, ViewBinding!"
        binding.button.setOnClickListener { /* 클릭 리스너 로직 */ }
    }
}

 

ViewBinding의 장점

  • 타입 안정성
    캐스팅할 필요 없이 직접 뷰에 접근하여 타입 불일치로 인한 런타임 오류를 제거합니다.
  • 더 깔끔한 코드
    findViewById()를 호출할 필요가 없습니다.
  • Null 안정성
    nullable 타입의 뷰를 자동으로 처리하여 더 안전한 코드를 보장합니다.
  • 성능
    DataBinding과 달리 ViewBinding은 바인딩 표현식이나 추가 XML 파싱을 사용하지 않으므로
    런타임 오버헤드가 최소화됩니다.

Q54) Jetpack ViewModel에 대해 설명해 주세요

Jetpack VIewModel은 생명주기를 인식하는 방식으로 UI 관련 데이터를

저장하고 관리하도록 설계된 안드로이드 아키텍처 컴포넌트의 핵심 구성요소입니다.

 

화면 히전과 같은 구성 변경 시에도 데이터가 유지되도록 보장하면서 UI 로직과

비즈니스 로직을 분리하여 개발자가 견고하고 유지 관리 가능한 앱을 만드는 데 도움을 줍니다.

 

ViewModel의 주요 목적은 구성 변경 중에 UI 관련 데이터를 보존하는 것입니다.
사용자가 기기를 회전하면 Activity/Fragment가 소멸하고 다시 생성되지만
ViewModel은 파괴되지 않아 데이터가 그대로 유지되도록 보장합니다.

data class DiceUiState(
    val firstDieValue: Int? = null,
    val secondDieValue: Int? = null,
    val numberOfRolls: Int = 0,
)

class DiceRollViewModel : ViewModel() {

    // 화면 UI 상태 노출
    private val _uiState = MutableStateFlow(DiceUiState())
    val uiState: StateFlow<DiceUiState> = _uiState.asStateFlow()

    // 비즈니스 로직 처리
    fun rollDice() {
        _uiState.update { currentState ->
            currentState.copy(
                firstDieValue = Random.nextInt(from = 1, until = 7),
                secondDieValue = Random.nextInt(from = 1, until = 7),
                numberOfRolls = currentState.numberOfRolls + 1,
            )
        }
    }
}

 

ViewModel의 상태 값은 Activity가 구성 변경으로 인해 다시 생성되더라도 유지됩니다.

 

ViewModel의 특징

  1. 생명주기 인식(Lifecycle Awareness)
    Activity 또는 Fragment의 생명주기에 범위가 지정됩니다.
    사용자가 화면에서 벗어나는 등 연관된 UI 컴포넌트가 더 이상 사용되지 않을 때 자동으로 소멸됩니다.
  2. 구성 변경 간 지속성
    구성 변경 중에 소멸되고 다시 생성되는 Activity/Fragment와 달리 ViewModel은 상태를 유지하며
    데이터 손실을 방지하고 데이터의 반복적인 재로드를 피합니다.
  3. 관심사 분리(Seperation of Concerns)
    ViewModel은 UI 관련 로직과 비즈니스 로직을 분리하여 더 깔끔하고 유지 관리하기 쉬운
    코드를 설계하는 데 도움이 됩니다

요약

Jetpack ViewModel은 구성 변경 시에도 원활하게 유지되도록
보장하면서 UI 상태 관련 데이터를 저장하고 관리하도록 설계된 Jetpack의 핵심 구성 요소 입니다.
생명주기를 인식하고 MVVM 아키텍처 패턴과 효과적으로 통합되어 화면 회전과 같은 이벤트 중에
데이터를 유지함으로써 상태 관리를 단순화하고 전반적인 개발 경험을 향상시킵니다.

 

Q56) Dagger2와 Hilt의 동작원리 및 차이점에 대해 설명해 주세요

 

두 라이브러리 모두 의존성 주입 라이브러리 입니다.

Google에서 개발하고 공식적으로 지원할 뿐만 아니라, 대규모 프로젝트에서 사용성이 검증되었습니다.  

Dagger2 란?

안드로이드 및 JVM 환경을 위한 정적 컴파일 타임 기반의 의존성 주입 라이브러리 입니다.
객체 생성을 관리하고 의존성을 자동으로 제공하여 모듈성을 개선하고

애플리케이션 테스트를 용이하게 하도록 설계되었습니다.

Dagger2는 컴파일 타임에 코드를 생성하여, 리플렉션에 기반한 DI 프레임워크에 비해 더 나은 성능을 보장합니다.

@Module, @Provides, @Inject와 같은 어노테이션을 사용하여 의존성을 선언하고 요청합니다.

개발자는 컴포넌트와 모듈을 통해 의존성 그래프를 생성하며, Dagger2는 런타임에 이를 자동으로 해결합니다. 

@Module
class NetworkModule {
    @Provides
    fun provideRetrofit(): Retrofit {
        return Retrofit.Builder()
            .baseUrl("https://example.com")
            .build()
    }
}

@Component(modules = [NetworkModule::class])
interface AppComponent {
    // MainActivity에 의존성 주입
    fun inject(activity: MainActivity)
}

class MainActivity : AppCompatActivity() {
    // Retrofit 의존성 주입 요청
    @Inject
    lateinit var retrofit: Retrofit

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // Dagger 컴포넌트 생성 및 주입 실행
        DaggerAppComponent.create().inject(this)
        // 이제 retrofit 인스턴스 사용 가능
    }
}

Hilt란 무엇인가요?

Dagger2 위에 구축된 안드로이드 전용 의존성 주입 라이브러리입니다.

안드로이드 생명주기와 밀접한 관련이 있는 클래스에 스코프가 지정된 사전 정의된 컴포넌트를 제공하여

Dagger를 안드로이드 프로젝트에 통합하는 프로세스를 전체적으로 단순화합니다.

@HiltAndroidApp 및 @AndroidEntryPoint와 같은 어노테이션을 제공하여

DI 설정을 간소화함으로써 Dagger2에 필요한 많은 보일러 플레이트 코드를 제거합니다.

@Singleton, @ActivityScoped와 같은 범위를 정의하여 의존성 생명주기를 관리합니다.  

 

@HiltAndroidApp // Hilt 사용을 위한 Application 클래스 어노테이션
class MyApplication : Application()

@AndroidEntryPoint // Hilt가 의존성을 주입할 Activity
class MainActivity : AppCompatActivity() {
    @Inject // Retrofit 의존성 주입 요청
    lateinit var retrofit: Retrofit

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        // Hilt가 자동으로 의존성 주입 처리
    }
}

@Module
@InstallIn(SingletonComponent::class) // 모듈이 설치될 컴포넌트 지정 (앱 전체 범위)
object NetworkModule {
    @Provides
    fun provideRetrofit(): Retrofit {
        return Retrofit.Builder()
            .baseUrl("https://example.com")
            .build()
    }
}

Dagger2와 Hilt의 주요 차이점

  1. 통합 프로세스
    Hilt는 컴포넌트와 인젝터를 수동으로 정의해줄 필요 없이
    사전 정의된 컴포넌트와 생명주기에 스코핑된 어노테이션을 제공하여 이를 단순화합니다.
  2. 안드로이드 생명주기 통합
    Hilt는 Android에 특화되어 있으며 Activity, Fragment, ViewModel과 같은
    안드로이드 컴포넌트에 대한 내장 지원을 제공합니다.
  3. 스코핑
    Hilt는 @Singleton, @ActivityScoped, @FragmentScoped와 같이
    안드로이드 생명주기 클래스와 밀접하게 통합된 사전 정의된 범위를 제공합니다
    Dagger는 수동 설정 및 커스텀 어노테이션이 필요합니다.
  4. 코드 단순성
    Hilt는 많은 보일러 플레이트 코드를 추상화하여 DI 설정의 복잡성을 줄여줍니다.
  5. 사용 사례
    Dagger2는 JVM 환경이나 복잡하고 커스텀된 의존성 주입 그래프가 필요한 프로젝트에 적합합니다.
    Hilt는 안드로이드 프로젝트를 위해 맞춤 설계되었습니다.

 

요약

Dagger2와 Hilt는 모두 객체 생성 및 관리를 간소화하는 의존성 주입 라이브러리입니다.
Dagger2는 순수하고 Java 또는 안드로이드 프로젝트에서 사용할 수 있지만
더 많은 수동적이고 복잡한 설정이 요구됩니다.

 

반면에 Hilt는 Dagger2 위에 구축되었지만 생명주기를 인식하는 컴포넌트와 쉽게 통합하여
보일러 플레이트 코드를 줄여 안드로이드에 특화된 DI 솔루션을 제공합니다