Jetpack Compose 면접 질문 20선 (2026년)
Jetpack Compose 면접에서 자주 출제되는 20가지 질문을 해설합니다. 리컴포지션, 상태 관리, 사이드 이펙트, 내비게이션, 성능 최적화, 아키텍처 패턴을 포괄적으로 다룹니다.

Jetpack Compose는 Android UI 개발의 표준 프레임워크로 자리 잡았습니다. 2026년 현재 Compose 지식은 Android 개발자 면접에서 필수 항목입니다. 이 글에서는 면접에서 실제로 출제되는 20가지 질문을 기초부터 고급까지 체계적으로 해설합니다.
각 질문의 답을 암기하는 것이 아니라 설계 의도를 이해하는 것이 중요합니다. 면접관은 Compose의 '왜'를 설명할 수 있는 후보자를 높이 평가합니다.
기초 개념 (Q1~Q4)
Q1: Jetpack Compose와 XML 기반 UI의 주요 차이점은 무엇입니까?
XML 기반 UI에서는 레이아웃 파일로 뷰 계층을 선언하고, Activity/Fragment에서 findViewById나 ViewBinding으로 참조합니다. 상태 변경 시 뷰를 수동으로 업데이트해야 합니다.
Jetpack Compose는 선언적 UI 프레임워크입니다. UI를 Composable 함수로 기술하면 상태 변화 시 프레임워크가 자동으로 UI를 재구성합니다. 뷰 참조 관리나 XML-Kotlin 전환이 불필요해지며, UI 로직이 하나의 Kotlin 파일에 집약됩니다.
// XML approach: separate layout file + Activity code
// activity_main.xml -> findViewById or ViewBinding
// Compose approach: everything in Kotlin
@Composable
fun Greeting(name: String) {
Text(
text = "Hello, $name!",
style = MaterialTheme.typography.headlineMedium
)
}Q2: 리컴포지션이란 무엇이며 언제 발생합니까?
리컴포지션은 상태 변화에 따라 Compose 런타임이 Composable 함수를 다시 실행하는 과정입니다. Compose는 변경된 상태를 읽는 Composable만 재실행하므로, 전체 UI 트리를 재구성할 필요가 없습니다.
리컴포지션은 Composable 함수가 읽고 있는 State 객체의 값이 변경될 때 발생합니다. Compose 컴파일러가 각 Composable의 의존성을 추적하여 필요한 부분만 효율적으로 업데이트합니다.
@Composable
fun Counter() {
var count by remember { mutableStateOf(0) }
// Only this Text recomposes when count changes
Text(text = "Count: $count")
Button(onClick = { count++ }) {
// This Text does NOT recompose (static content)
Text("Increment")
}
}Q3: remember의 역할은 무엇입니까?
remember는 리컴포지션 사이에 값을 유지하는 메커니즘입니다. Composable 함수는 재실행될 때마다 로컬 변수가 초기화됩니다. remember를 사용하면 이전 컴포지션에서 계산된 값을 보존하여 불필요한 재계산을 방지합니다.
remember는 컴포지션 생명주기에 연결되어 있으며, Composable이 컴포지션에서 제거되면 저장된 값도 함께 해제됩니다.
@Composable
fun FilteredList(items: List<String>, query: String) {
// Recalculated only when items or query changes
val filtered = remember(items, query) {
items.filter { it.contains(query, ignoreCase = true) }
}
LazyColumn {
items(filtered) { item ->
Text(text = item)
}
}
}Q4: remember와 rememberSaveable의 차이점은 무엇입니까?
remember는 컴포지션 내에서만 값을 유지합니다. 화면 회전이나 프로세스 종료 같은 구성 변경 시 값이 손실됩니다.
rememberSaveable은 Bundle에 값을 저장하므로, 구성 변경이나 프로세스 재생성 후에도 값이 복원됩니다. 폼 입력이나 스크롤 위치 등 사용자가 의도적으로 설정한 상태를 유지하는 데 적합합니다.
@Composable
fun SearchScreen() {
// Lost on configuration change
var tempFlag by remember { mutableStateOf(false) }
// Survives configuration change and process death
var searchQuery by rememberSaveable { mutableStateOf("") }
OutlinedTextField(
value = searchQuery,
onValueChange = { searchQuery = it },
label = { Text("Search") }
)
}화면 회전 후 초기화되어도 괜찮은 임시 상태에는 remember를, 사용자 입력이나 내비게이션 상태 등 보존해야 하는 값에는 rememberSaveable을 사용합니다.
상태 관리 (Q5~Q7)
Q5: 상태 호이스팅이란 무엇입니까?
상태 호이스팅은 Composable 내부의 상태를 호출자로 이동하는 패턴입니다. 상태를 갖는 Composable을 '스테이트풀', 상태를 전달받기만 하는 Composable을 '스테이트리스'라고 합니다.
스테이트리스 Composable은 재사용성이 높고, 테스트가 용이하며, 프리뷰에서의 확인도 간편합니다. 상태와 이벤트 콜백을 매개변수로 전달하여 단방향 데이터 흐름을 구현합니다.
// Stateless composable: receives state, emits events
@Composable
fun EmailInput(
email: String,
onEmailChange: (String) -> Unit,
modifier: Modifier = Modifier
) {
OutlinedTextField(
value = email,
onValueChange = onEmailChange,
label = { Text("Email") },
modifier = modifier
)
}
// Stateful wrapper: owns and manages state
@Composable
fun EmailForm() {
var email by rememberSaveable { mutableStateOf("") }
EmailInput(
email = email,
onEmailChange = { email = it }
)
}Q6: derivedStateOf는 언제 사용합니까?
derivedStateOf는 다른 상태 값에서 파생되는 값을 효율적으로 계산하기 위해 사용합니다. 입력 상태가 변경될 때만 재계산이 이루어지며, 결과가 동일하면 리컴포지션이 트리거되지 않습니다.
리스트 필터링이나 유효성 검사 결과 산출 등 기존 상태를 변환하는 처리에 적합합니다.
@Composable
fun TodoList(todos: List<Todo>) {
var hideCompleted by remember { mutableStateOf(false) }
// Only recalculated when todos or hideCompleted changes
val visibleTodos by remember(todos) {
derivedStateOf {
if (hideCompleted) todos.filter { !it.done } else todos
}
}
val completedCount by remember(todos) {
derivedStateOf { todos.count { it.done } }
}
Text("Completed: $completedCount / ${todos.size}")
LazyColumn {
items(visibleTodos) { todo ->
TodoRow(todo)
}
}
}Q7: ViewModel에서 StateFlow와 Compose State 중 어느 것을 사용해야 합니까?
StateFlow는 Kotlin Coroutines 라이브러리의 일부로, Compose 외부 레이어에서도 사용할 수 있습니다. ViewModel에서는 StateFlow로 UI 상태를 보유하고, Composable에서 collectAsState()로 Compose State로 변환하는 것이 일반적인 패턴입니다.
mutableStateOf를 ViewModel 내부에서 직접 사용하는 것도 기술적으로 가능하지만, ViewModel이 Compose API에 의존하게 됩니다. StateFlow를 사용하면 ViewModel의 테스트 용이성이 향상되고, Compose 외부 UI 프레임워크와의 호환성도 유지됩니다.
class ProfileViewModel : ViewModel() {
private val _uiState = MutableStateFlow(ProfileUiState())
val uiState: StateFlow<ProfileUiState> = _uiState.asStateFlow()
fun updateName(name: String) {
_uiState.update { it.copy(name = name) }
}
}
data class ProfileUiState(
val name: String = "",
val isLoading: Boolean = false
)
@Composable
fun ProfileScreen(viewModel: ProfileViewModel = viewModel()) {
val uiState by viewModel.uiState.collectAsState()
ProfileContent(
name = uiState.name,
isLoading = uiState.isLoading,
onNameChange = viewModel::updateName
)
}Android 면접 준비가 되셨나요?
인터랙티브 시뮬레이터, flashcards, 기술 테스트로 연습하세요.
사이드 이펙트 (Q8~Q9)
Q8: LaunchedEffect, DisposableEffect, SideEffect의 차이점은 무엇입니까?
Compose의 사이드 이펙트 API는 컴포지션 생명주기에 연동된 비동기 처리나 리소스 관리를 안전하게 수행하기 위한 메커니즘입니다.
LaunchedEffect: 코루틴 스코프를 제공하며, key가 변경되면 취소 후 재시작합니다. API 호출이나 애니메이션 등 suspend 함수 실행에 사용합니다.
DisposableEffect: 리소스 확보와 해제를 처리합니다. onDispose 블록에서 정리 처리를 기술합니다. 리스너 등록/해제나 콜백 설정에 적합합니다.
SideEffect: 리컴포지션이 성공할 때마다 실행됩니다. Compose 외부 시스템과의 동기화에 사용합니다. suspend 함수는 사용할 수 없습니다.
@Composable
fun UserProfile(userId: String) {
// LaunchedEffect: runs suspend function, restarts when userId changes
LaunchedEffect(userId) {
viewModel.loadUser(userId)
}
// DisposableEffect: acquire/release resources
DisposableEffect(Unit) {
val listener = object : LocationListener {
override fun onLocationChanged(location: Location) {
// Handle location update
}
}
locationManager.requestLocationUpdates(listener)
onDispose {
locationManager.removeUpdates(listener)
}
}
// SideEffect: sync with external system on every recomposition
SideEffect {
analytics.setCurrentScreen("UserProfile")
}
}Q9: rememberCoroutineScope는 언제 사용합니까?
rememberCoroutineScope는 Composable 함수 외부(콜백이나 이벤트 핸들러 등)에서 코루틴을 시작할 때 사용합니다. LaunchedEffect가 Composable의 생명주기에 자동으로 연결되는 반면, rememberCoroutineScope는 사용자 액션에 따라 수동으로 코루틴을 시작하는 경우에 적합합니다.
스코프는 Composable 생명주기에 연결되어 있어, Composable이 파괴되면 스코프 내 코루틴도 취소됩니다.
@Composable
fun SnackbarDemo(snackbarHostState: SnackbarHostState) {
val scope = rememberCoroutineScope()
Button(onClick = {
// Launch coroutine from a callback
scope.launch {
snackbarHostState.showSnackbar(
message = "Action completed",
duration = SnackbarDuration.Short
)
}
}) {
Text("Show Snackbar")
}
}레이아웃 (Q10~Q12)
Q10: LazyColumn의 성능을 최적화하는 방법은?
LazyColumn은 RecyclerView에 해당하는 Composable로, 화면에 표시되는 항목만 컴포즈합니다. 성능 최적화의 핵심 포인트는 다음과 같습니다.
key 지정: key 매개변수로 각 항목에 안정적인 식별자를 설정하면, 리스트 변경 시 불필요한 리컴포지션을 방지할 수 있습니다.
contentType 활용: 서로 다른 항목 유형을 contentType으로 구분하면, Compose가 항목 재사용을 최적화할 수 있습니다.
무거운 처리의 캐시: 항목 내에서 이미지 로딩이나 계산을 수행할 때는 remember로 캐시합니다.
@Composable
fun OptimizedList(items: List<Item>) {
LazyColumn {
items(
items = items,
key = { it.id }, // Stable key for reordering
contentType = { it.type } // Efficient item reuse
) { item ->
when (item.type) {
ItemType.HEADER -> HeaderRow(item)
ItemType.CONTENT -> ContentRow(item)
ItemType.FOOTER -> FooterRow(item)
}
}
}
}Q11: 커스텀 Layout은 어떻게 만듭니까?
Compose의 Layout 컴포저블을 사용하면 독자적인 배치 로직을 정의할 수 있습니다. MeasurePolicy로 자식 요소의 측정과 배치를 제어합니다.
각 자식 요소를 measurable.measure(constraints)로 측정하고, layout(width, height) 블록 내에서 placeable.place(x, y)를 호출하여 배치합니다.
@Composable
fun StaggeredGrid(
modifier: Modifier = Modifier,
columns: Int = 2,
content: @Composable () -> Unit
) {
Layout(
content = content,
modifier = modifier
) { measurables, constraints ->
val columnWidth = constraints.maxWidth / columns
val itemConstraints = constraints.copy(
minWidth = columnWidth,
maxWidth = columnWidth
)
val placeables = measurables.map { it.measure(itemConstraints) }
val columnHeights = IntArray(columns)
val placements = placeables.map { placeable ->
val col = columnHeights.indexOfMin()
val position = Pair(col * columnWidth, columnHeights[col])
columnHeights[col] += placeable.height
position
}
layout(constraints.maxWidth, columnHeights.max()) {
placeables.forEachIndexed { index, placeable ->
val (x, y) = placements[index]
placeable.place(x, y)
}
}
}
}
fun IntArray.indexOfMin(): Int {
var minIndex = 0
for (i in indices) {
if (this[i] < this[minIndex]) minIndex = i
}
return minIndex
}Q12: MaterialTheme 커스터마이징 방법은?
Material Design 3 테마는 컬러 스킴, 타이포그래피, 셰이프의 세 축으로 구성됩니다. MaterialTheme 컴포저블에서 커스텀 값을 제공하여 앱 전체에 일관된 디자인 시스템을 구현합니다.
private val DarkColorScheme = darkColorScheme(
primary = Color(0xFFBB86FC),
secondary = Color(0xFF03DAC5),
background = Color(0xFF121212)
)
private val LightColorScheme = lightColorScheme(
primary = Color(0xFF6200EE),
secondary = Color(0xFF03DAC5),
background = Color(0xFFFFFFFF)
)
@Composable
fun AppTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
content: @Composable () -> Unit
) {
val colorScheme = if (darkTheme) DarkColorScheme else LightColorScheme
MaterialTheme(
colorScheme = colorScheme,
typography = AppTypography,
shapes = AppShapes,
content = content
)
}
// Usage in composables
@Composable
fun ThemedCard() {
Card(
colors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.surface
)
) {
Text(
text = "Themed content",
style = MaterialTheme.typography.bodyLarge
)
}
}내비게이션 (Q13~Q14)
Q13: Compose에서 NavHost를 사용한 화면 전환 구현 방법은?
NavHost는 Compose Navigation의 핵심 컴포넌트로, NavController와 연동하여 화면 전환을 관리합니다. 각 화면은 라우트 문자열로 식별되며, composable 함수로 등록합니다.
@Composable
fun AppNavigation() {
val navController = rememberNavController()
NavHost(
navController = navController,
startDestination = "home"
) {
composable("home") {
HomeScreen(
onNavigateToDetail = { id ->
navController.navigate("detail/$id")
}
)
}
composable(
route = "detail/{itemId}",
arguments = listOf(
navArgument("itemId") { type = NavType.StringType }
)
) { backStackEntry ->
val itemId = backStackEntry.arguments?.getString("itemId")
DetailScreen(itemId = itemId)
}
}
}Q14: 화면 간 데이터를 전달하는 방법은?
Compose Navigation에서는 라우트 매개변수와 savedStateHandle을 사용하여 데이터를 전달합니다. 단순한 값은 라우트 경로나 쿼리 매개변수에 포함하고, 복잡한 객체는 ViewModel이나 리포지토리를 통해 공유합니다.
previousBackStackEntry의 savedStateHandle을 사용하면 이전 화면으로 결과를 반환하는 것도 가능합니다.
// Type-safe navigation with arguments
@Composable
fun AppNavigation() {
val navController = rememberNavController()
NavHost(navController = navController, startDestination = "list") {
composable("list") {
ListScreen(
onItemClick = { id, title ->
navController.navigate("detail/$id?title=$title")
}
)
}
composable(
route = "detail/{id}?title={title}",
arguments = listOf(
navArgument("id") { type = NavType.IntType },
navArgument("title") {
type = NavType.StringType
defaultValue = ""
}
)
) { entry ->
DetailScreen(
id = entry.arguments?.getInt("id") ?: 0,
title = entry.arguments?.getString("title") ?: ""
)
}
}
}
// Returning results to previous screen
fun NavController.navigateBackWithResult(key: String, value: String) {
previousBackStackEntry?.savedStateHandle?.set(key, value)
popBackStack()
}내비게이션 인자에 직렬화 가능한 객체를 직접 전달하는 것은 권장되지 않습니다. ID를 전달하고, 대상 화면의 ViewModel에서 데이터를 가져오는 패턴이 권장됩니다.
성능 (Q15~Q17)
Q15: 불필요한 리컴포지션을 방지하는 방법은?
리컴포지션 최적화는 Compose 앱의 성능에 직결됩니다. 주요 기법은 다음과 같습니다.
안정적인 타입 사용: Compose 컴파일러는 @Stable 또는 @Immutable로 어노테이션된 타입이나 프리미티브, String 같은 내장 안정 타입을 스킵 가능으로 판단합니다.
람다 안정화: Composable에 전달하는 람다를 remember로 감싸거나 메서드 참조를 사용하여 리컴포지션을 억제합니다.
derivedStateOf 활용: 빈번하게 변화하는 상태에서 파생되는 값은 derivedStateOf로 래핑하여 불필요한 리컴포지션을 줄입니다.
// Mark classes as stable for the Compose compiler
@Immutable
data class UserData(
val id: String,
val name: String,
val avatarUrl: String
)
// Use remember for lambda stability
@Composable
fun ItemList(
items: List<UserData>,
onItemClick: (String) -> Unit
) {
val stableCallback = remember(onItemClick) { onItemClick }
LazyColumn {
items(items, key = { it.id }) { item ->
UserRow(
user = item,
onClick = { stableCallback(item.id) }
)
}
}
}Q16: Compose 성능 프로파일링 방법은?
Android Studio에는 Compose 성능 분석을 위한 전용 도구가 마련되어 있습니다.
Layout Inspector: 컴포지션 트리를 실시간으로 시각화하고, 각 Composable의 리컴포지션 횟수를 확인할 수 있습니다.
Composition Tracing: System Trace와 통합되어 각 Composable 함수의 실행 시간을 측정할 수 있습니다. composition-tracing 라이브러리를 추가하면 사용할 수 있습니다.
컴파일러 리포트: Compose 컴파일러의 메트릭을 활성화하면 각 함수의 스킵 가능성과 매개변수 안정성에 관한 리포트가 생성됩니다.
// Enable compiler metrics in build.gradle.kts
// kotlinOptions {
// freeCompilerArgs += listOf(
// "-P", "plugin:androidx.compose.compiler.plugins.kotlin:metricsDestination=build/compose-metrics",
// "-P", "plugin:androidx.compose.compiler.plugins.kotlin:reportsDestination=build/compose-reports"
// )
// }
// Trace specific compositions
@Composable
fun HeavyScreen() {
trace("HeavyScreen") {
// Composable content here
val data = remember { expensiveComputation() }
DataDisplay(data)
}
}Q17: Modifier 순서가 왜 중요합니까?
Modifier 체인은 위에서 아래로 순서대로 적용됩니다. 순서에 따라 레이아웃과 렌더링 결과가 크게 달라집니다.
padding과 background의 순서가 대표적인 예입니다. background를 먼저 적용하고 padding을 적용하면 패딩 영역에도 배경색이 그려집니다. 반대로 하면 패딩 영역은 투명합니다.
@Composable
fun ModifierOrderDemo() {
// Background covers padding area
Text(
text = "Option A",
modifier = Modifier
.background(Color.Red)
.padding(16.dp)
)
// Background does NOT cover padding area
Text(
text = "Option B",
modifier = Modifier
.padding(16.dp)
.background(Color.Red)
)
// Clickable area includes padding
Text(
text = "Option C",
modifier = Modifier
.clickable { /* handle click */ }
.padding(16.dp)
)
// Clickable area excludes padding
Text(
text = "Option D",
modifier = Modifier
.padding(16.dp)
.clickable { /* handle click */ }
)
}Modifier 체인은 바깥쪽에서 안쪽으로의 '래퍼'로 생각하면 직관적으로 이해할 수 있습니다. 먼저 적용된 Modifier가 가장 바깥쪽 레이어가 됩니다.
아키텍처 (Q18~Q20)
Q18: ViewModel + UiState 패턴의 구현 방법은?
ViewModel + UiState 패턴은 Compose 앱에서 Google이 권장하는 아키텍처입니다. UI 상태를 단일 데이터 클래스로 표현하고, ViewModel이 상태 업데이트를 관리합니다.
단방향 데이터 흐름(UDF)으로 상태 흐름이 예측 가능해지며, 디버깅과 테스트가 용이해집니다.
data class SearchUiState(
val query: String = "",
val results: List<SearchResult> = emptyList(),
val isLoading: Boolean = false,
val error: String? = null
)
sealed interface SearchEvent {
data class QueryChanged(val query: String) : SearchEvent
data object Search : SearchEvent
data object ClearError : SearchEvent
}
class SearchViewModel(
private val repository: SearchRepository
) : ViewModel() {
private val _uiState = MutableStateFlow(SearchUiState())
val uiState: StateFlow<SearchUiState> = _uiState.asStateFlow()
fun onEvent(event: SearchEvent) {
when (event) {
is SearchEvent.QueryChanged -> {
_uiState.update { it.copy(query = event.query) }
}
is SearchEvent.Search -> performSearch()
is SearchEvent.ClearError -> {
_uiState.update { it.copy(error = null) }
}
}
}
private fun performSearch() {
viewModelScope.launch {
_uiState.update { it.copy(isLoading = true, error = null) }
try {
val results = repository.search(_uiState.value.query)
_uiState.update { it.copy(results = results, isLoading = false) }
} catch (e: Exception) {
_uiState.update { it.copy(error = e.message, isLoading = false) }
}
}
}
}
@Composable
fun SearchScreen(viewModel: SearchViewModel = viewModel()) {
val uiState by viewModel.uiState.collectAsState()
Column {
SearchBar(
query = uiState.query,
onQueryChange = { viewModel.onEvent(SearchEvent.QueryChanged(it)) },
onSearch = { viewModel.onEvent(SearchEvent.Search) }
)
when {
uiState.isLoading -> CircularProgressIndicator()
uiState.error != null -> ErrorMessage(uiState.error!!)
else -> SearchResults(uiState.results)
}
}
}Q19: Composable 함수를 테스트하는 방법은?
Compose에는 UI 테스트용 compose-ui-test 라이브러리가 제공됩니다. ComposeTestRule을 사용하여 Composable을 테스트 환경에 배치하고, 시맨틱 트리를 통해 UI를 검증하고 조작합니다.
class LoginScreenTest {
@get:Rule
val composeTestRule = createComposeRule()
@Test
fun loginButton_disabled_whenFieldsEmpty() {
composeTestRule.setContent {
LoginScreen(onLogin = {})
}
// Verify button is disabled
composeTestRule
.onNodeWithText("Login")
.assertIsNotEnabled()
}
@Test
fun loginButton_enabled_whenFieldsFilled() {
composeTestRule.setContent {
LoginScreen(onLogin = {})
}
// Fill in fields
composeTestRule
.onNodeWithTag("email_field")
.performTextInput("user@example.com")
composeTestRule
.onNodeWithTag("password_field")
.performTextInput("password123")
// Verify button is enabled
composeTestRule
.onNodeWithText("Login")
.assertIsEnabled()
}
@Test
fun errorMessage_displayed_onLoginFailure() {
composeTestRule.setContent {
LoginScreen(onLogin = {})
}
// Verify error message appears
composeTestRule
.onNodeWithText("Invalid credentials")
.assertExists()
}
}Q20: Compose와 XML 뷰의 상호 운용 방법은?
기존 프로젝트에 Compose를 도입하는 것은 단계적으로 진행할 수 있습니다. ComposeView로 XML 레이아웃 안에 Composable을 삽입하고, AndroidView로 Composable 안에 XML 뷰를 표시합니다.
// Compose inside XML layout
class LegacyActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_legacy)
val composeView = findViewById<ComposeView>(R.id.compose_container)
composeView.setContent {
MaterialTheme {
NewFeatureComposable()
}
}
}
}
// XML View inside Compose
@Composable
fun LegacyMapView() {
AndroidView(
factory = { context ->
MapView(context).apply {
// Initialize legacy MapView
}
},
update = { mapView ->
// Update when state changes
},
modifier = Modifier.fillMaxSize()
)
}
// In XML layout:
// <androidx.compose.ui.platform.ComposeView
// android:id="@+id/compose_container"
// android:layout_width="match_parent"
// android:layout_height="wrap_content" />새로운 기능은 Compose로, 기존 화면은 필요에 따라 XML에서 Compose로 단계적으로 마이그레이션하는 것이 Google의 권장 접근 방식입니다. Compose와 XML은 같은 앱 내에서 공존할 수 있습니다.
결론
Jetpack Compose 면접 대비에서는 API 사용법뿐만 아니라 설계 의도를 이해하는 것이 요구됩니다. 리컴포지션 메커니즘, 상태 호이스팅의 의의, 사이드 이펙트 API의 구분 등 '왜 그렇게 설계되었는지'를 설명할 수 있는 것이 합격의 열쇠입니다.
면접 준비 체크리스트
- ✅ 선언적 UI와 리컴포지션 메커니즘을 설명할 수 있다
- ✅ remember, rememberSaveable, derivedStateOf를 구분하여 사용할 수 있다
- ✅ 상태 호이스팅과 단방향 데이터 흐름을 이해하고 있다
- ✅ LaunchedEffect, DisposableEffect, SideEffect의 차이를 설명할 수 있다
- ✅ LazyColumn 최적화 기법(key, contentType)을 알고 있다
- ✅ Modifier 적용 순서가 결과에 영향을 미친다는 것을 이해하고 있다
- ✅ ViewModel + UiState 패턴의 구현 방법을 파악하고 있다
- ✅ Compose UI 테스트 방법을 설명할 수 있다
- ✅ XML 뷰와의 상호 운용 방법을 알고 있다
연습을 시작하세요!
면접 시뮬레이터와 기술 테스트로 지식을 테스트하세요.
이 질문들에 대한 답변을 준비할 때는 공식 문서와 Google Codelabs를 참고하는 것을 권장합니다. 실제로 샘플 코드를 작성하여 동작을 확인하면, 면접 시 자신감 있게 답변할 수 있습니다.
태그
공유
관련 기사

Android MVVM vs MVI: 2026년에 어떤 아키텍처를 선택해야 할까?
Android에서 MVVM과 MVI의 심층 비교: 장단점, 사용 사례, 그리고 2026년에 올바른 아키텍처를 선택하기 위한 실용적인 가이드.

Kotlin 코루틴 완전 가이드 2026: Android 비동기 처리 마스터하기
Android 개발에 필수적인 Kotlin 코루틴을 기초부터 고급 패턴까지 체계적으로 학습합니다. suspend 함수, 스코프, 디스패처, Flow를 실전 코드와 함께 배울 수 있습니다.

데이터 분석가를 위한 SQL: 윈도우 함수, CTE, 고급 쿼리 기법
SQL 윈도우 함수, CTE(공통 테이블 식), 고급 분석 쿼리를 실용적인 코드 예제와 함께 설명합니다. 데이터 분석가 면접 준비와 실무에 필수적인 기법입니다.