
Jetpack Compose
Jetpack Compose는 UI 개발을 간소화하기 위해 설계된 최신 툴킷입니다. 반응형 프로그래밍 모델을 Kotlin 프로그래밍 언어의 간결함 및 사용 편의성과 결합합니다. 이는 완전히 선언적인 접근 방식으로, 데이터를 UI 계층 구조로 변환하는 일련의 함수를 호출하여 UI를 설명합니다. 기본 데이터가 변경되면 프레임워크가 이러한 함수를 자동으로 다시 실행하여 UI 계층 구조를 업데이트합니다.


선언형 패러다임 전환
이 기법은 처음부터 화면 전체를 개념적으로 재생성한 후 필요한 변경사항만 적용하는 방식으로 작동합니다. 이러한 접근 방식은 스테이트풀(Stateful) 뷰 계층 구조를 수동으로 업데이트할 때의 복잡성을 방지할 수 있습니다. Compose는 선언형 UI 프레임워크입니다.
예로들면, UI 창에서 " 주문하기 " 버튼을 누른다고 치자.
결론적으론, "한 방향으로 움직인다는 이야기" 이다.
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
StudyComposeTheme {
val clickCount: MutableState<Int> = remember { mutableStateOf(0) }
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
Greeting(
name = "Android",
clickCount.value,
modifier = Modifier.padding(innerPadding),
onClick = {
clickCount.value += 1
Log.d("TAG", "Button clicked: ${clickCount.value}")
}
)
}
}
}
}
}
@Composable
fun Greeting(name: String, clickCount: Int, modifier: Modifier = Modifier, onClick: () -> Unit) {
Column {
Text(text = "주문 횟수: $clickCount")
Text(
text = "Hello $name!",
modifier = modifier
)
Button(onClick = onClick) {
Text(text = "주문하기")
}
}
}

// @Composable 애노테이션은 이 함수가 UI를 구성하는 컴포저블 함수임을 나타냄
@Composable
// Greeting 함수는 4개의 매개변수를 받음:
// - name: 표시할 이름 (String)
// - clickCount: 클릭 횟수를 추적하는 정수 (Int)
// - modifier: UI 요소의 크기, 패딩 등을 수정하는 Modifier (기본값 = Modifier)
// - onClick: 버튼 클릭 시 실행될 람다 함수 (() -> Unit)
fun Greeting(name: String, clickCount: Int, modifier: Modifier = Modifier, onClick: () -> Unit) {
// Column은 자식 요소들을 세로로 배치하는 레이아웃 컴포저블
Column {
// 첫 번째 Text 컴포저블: 주문 횟수를 표시
// String interpolation ($clickCount)을 사용하여 동적으로 클릭 횟수 표시
Text(text = "주문 횟수: $clickCount")
// 두 번째 Text 컴포저블: 인사말 표시
// 외부에서 전달받은 modifier를 적용
// String interpolation ($name)을 사용하여 동적으로 이름 표시
Text(
text = "Hello $name!",
modifier = modifier
)
// Button 컴포저블: 클릭 이벤트를 처리하는 버튼
// onClick 파라미터로 외부에서 전달받은 람다 함수를 전달
Button(onClick = onClick) {
// 버튼 내부의 Text 컴포저블
// 버튼에 표시될 텍스트 설정
Text(text = "주문하기")
}
}
}
Greeing의 매개변수로는 이름,클릭 횟수, Modifier, 버튼 클릭시 이벤트를 받게 된다.
즉, 단순하게 말해서 여기서 버튼을 누르는 동작을 하면
(클릭 처리 발생)
// ComponentActivity를 상속받는 MainActivity 클래스 정의
class MainActivity : ComponentActivity() {
// Activity가 생성될 때 호출되는 onCreate 메서드
override fun onCreate(savedInstanceState: Bundle?) {
// 부모 클래스의 onCreate 호출
super.onCreate(savedInstanceState)
// 엣지 투 엣지 디스플레이 지원을 활성화 (전체 화면 모드)
enableEdgeToEdge()
// Compose UI를 설정하는 setContent 블록
setContent {
// 앱의 테마를 적용하는 커스텀 테마 컴포저블
StudyComposeTheme {
// remember를 사용하여 리컴포지션 간에 상태를 유지
// MutableState<Int>로 클릭 횟수를 추적하는 상태 변수 생성
val clickCount: MutableState<Int> = remember { mutableStateOf(0) }
// Scaffold: 기본적인 Material Design 레이아웃 구조를 제공
// modifier로 최대 크기로 확장하도록 설정
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
// Greeting 컴포저블 호출
Greeting(
// name 파라미터에 "Android" 전달
name = "Android",
// clickCount의 현재 값을 전달
clickCount.value,
// Scaffold의 innerPadding을 적용한 modifier 전달
modifier = Modifier.padding(innerPadding),
// 버튼 클릭 시 실행될 람다 함수 정의
onClick = {
// 클릭 횟수 증가
clickCount.value += 1
// 로그캣에 클릭 횟수 출력
Log.d("TAG", "Button clicked: ${clickCount.value}")
}
)
}
}
}
}
}
위와 같이 상위 (전체코드상) 컴포넌트로 이벤트를 알려주게 된다.
Observer 패턴과 비슷한 형태를 띄게 된다.
그러면 받은 데이터를 가지고 내부에서 처리
(clickCount.Value += 1)를 하여 다시 변경된 데이터를
UI로 보내준다.
우리는 이 과정을 "재구성" 이라고 부를 수 있다.
재구성
명령형 UI 모델에서 위젯을 변경하려면 위젯에서 setter를 호출하여 내부 상태를 변경합니다. Compose에서는 새 데이터를 사용하여 구성 가능한 함수를 다시 호출합니다. 이렇게 하면 함수가 재구성되며, 필요한 경우 함수에서 내보낸 위젯이 새 데이터로 다시 그려집니다. Compose 프레임워크는 변경된 구성요소만 지능적으로 재구성할 수 있습니다.
적응형 앱이란 무엇인가요?
적응형 앱은 앱 디스플레이의 변경사항, 주로 앱 창의 크기에 따라 레이아웃을 변경합니다. 하지만 적응형 앱은 탁자 또는 책 상태와 같은 폴더블 기기의 상태 변경과 화면 밀도 및 글꼴 크기의 변경사항도 수용합니다.

XML로 개발할 때는 Icon을 제외한 모든 Components들을
selelctor라든가 등등 해서 xml 파일로 구현해 집어 넣어야한다.
왜냐하면 단순 PNG, SVG로 하면
어떤 기기는 아이콘이 크고 어떤기기는 아이콘이 작고 등등,
제각각이다
우리는 그동안 이에 대처하기위해

이런식으로 기기마다 대응할 수 있게 각각 폴더를 만든다거나

이런식으로 diments.xml 파일을 만들어 크기별로 잡아주는 경우가 대다수이다.
하지만, Jetpack Compose는 이를 간소화 시킨다.
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent { // 여기서부터 Compose UI 시작
AdaptiveLayout() // 우리가 만든 메인 컴포저블 함수 호출
}
}
}
@Composable // Compose UI 컴포넌트임을 나타내는 어노테이션
fun AdaptiveLayout() {
// 1. 상태 관리
val count = remember { mutableStateOf(0) }
// remember: 리컴포지션(화면 갱신) 시에도 값을 유지
// mutableStateOf: 값이 변경되면 UI를 자동으로 갱신
// 2. 화면 크기 감지
BoxWithConstraints { // 화면 크기를 측정할 수 있는 컨테이너
val isTablet = maxWidth > 600.dp // 화면 너비가 600dp 넘으면 태블릿으로 간주
// 3. 조건부 레이아웃
if (isTablet) {
// 태블릿용 레이아웃
Row( // 가로 방향 레이아웃
modifier = Modifier.padding(16.dp), // 여백 설정
verticalAlignment = Alignment.CenterVertically // 세로 방향 중앙 정렬
) {
Text(
text = "주문 횟수: ${count.value}", // count.value로 현재 값 접근
modifier = Modifier.weight(1f) // 남은 공간 모두 차지
)
Button(
onClick = { count.value++ } // 클릭하면 count 증가
) {
Text("주문하기")
}
}
} else {
// 폰용 레이아웃
Column( // 세로 방향 레이아웃
modifier = Modifier.padding(16.dp)
) {
Text(text = "주문 횟수: ${count.value}")
Button(onClick = { count.value++ }) {
Text("주문하기")
}
}
}
}
}
공부한 자료들 :