Jetpack Compose Comapre to Web

개발자의 도구들·2024년 9월 12일

DEF

Compose는 어떻게 동작하는지 공부해 보자.

How it works?

work flow를 정리하기 이전에, UI가 어떤식으로 작동? 구성되면 좋을지에 대한 부분을 먼저 정리하고 하나씩 물음에 해결하는 방식으로 공부를 진행

내가 알고잇는 웹구조와 비교하며 공부를 진행해보았다.


1. 태그:
    웹에서는 main, section, container(div) 태그를 통해서 계층적으로 구조를 형성 
    compose 에서는 어떤식으로 구성되는 것일까? -
    
2. CSS: 
    웹에서는 CSS를 html에 바로 적용하거나, js를 통해 동적으로 적용. 혹은 css  파일로 적요.
    compose 에서는 CSS를 어떻게 처리할까?
    
    - css 크기 규격 용어 정리 (web에서는 px, auto, vw,vh)
    
3. position
    웹에서는 relative, absolutre 등으로 포지션을 계층적으로 나눈 후.
    top, left, right, bottom 을 적용하여 배치 or padding, margin을 통해 배치를 진행함
    앱에서는 어떻게 되는걸까??
    
4. 동적 contents, function
    웹에서는 js를 직접 or library를 통해 동적 처리를 수행,
    compose에서는 어떤 식으로 데이터를 처리하거나, 기능을 동작시킬까?
    
5. scroll animation or html 5
   : 웹에서는 스크롤에 따른 애니메이션 효과를 적용할 수 있었는데,
     Compose에서는 어떻게 적용?
     

Structure

Compose: Architecture

이건 따로 여기에 저장 해둠

Basic Usage

Define UI

@Composable
fun Greeting(name: String) {
    Text(text = "Hello, $name!")
}

가장 기본적으로, compose는 @Composable 어노테이션으로 선언이 가능하다. 즉, method 형태로 UI선언이 가능하다는 이야기.

State Management

@Composable 
fun Counter() {
    val count by remember { mutableStateOF(0) }
    
    Button(onClick = { count++ } {
        Text("Clicked $count times")
    }
}

compose는 state의 concept으로 built 된다. statae가 변하게 되면, 관련된 UI parts는 다시 composed 된다.

datamdel과 함꼐 UI동기화를 쉽게 할 수 있음.

using State, remeber

Recomposition

다시 compoable function이 시작됨. UI의 변화를 반영하기 위함. state가 변하는 상황이 생길때만 recompose 된다.

→ 불필요한 UI 업데이트 감소

Modifiers

UI 컴포넌트를 꾸미기 위해 사용되는 클래스.

  • 레이아웃 속성, 그리고 행동 조차도 변화가 가능해진다

,

@Composable
fun PaddedText() {
    Text("Hellot, Compose!!", modifer = Modifier.padding(16.dp))
}

질문에 대한 답

1. UI를 어떻게 구조화 하는가

질문: web의 laayout은 main > section > div 등의 구조인데 Compose는 이를 어떤 구조로 해결하는지?

Answer

Compose 역시 계층 구조가 존재한다. 계층구조에 사용되는 여러 Class를 아래에 정리

Column : div와 비슷 한 컨테이너. 대신 자식 요소를 수직으로 배치하는게 기본

Row: flex contaier 처럼 자식들을 수평으로 배치 

Box: 역시나 div와 유사. 자식들을 각각 top위치에 배치

ConstarintLayout: Androide View의 ConstraintLayout기능을 그대로 가져온 것./ web에서는 flexbox, frid 와 비슷.
 복잦ㅂ한 레이아웃을 정의하는데 사용된다.
 
-> 자식들간의 규칙을 설정 할 수 있음

example

@Composable
fun ProfileScreen() {
    Column {
        Row {
            Image(painter = ..., contentDescription = "Profile picture")
            Text("User name")
        }
        Text("Bio: Compose enthusiast.")
    }
}

@Composable
fun MyApp() {
    Column(modifier = Modifier.fillMaxSize()) {
        TopBar()
        ContentSection()
        Footer()
    }
}

@Composable
fun TopBar() {
    Row(modifer = Modifier.fillMaxWIdht()) {
        Text("Title", modifer = Modifer.weight(if))
        Buttom(onClick = {/*do something*/}) {
            Text("Settings")
        }
    }
}

@Composable
fun ContentSection() {
    Column(modifer = Modifier.fillMaxWidth()) {
        Text("Welcome to the app")
    }
}

@Composable
fun Footer() {
    Text(
        text = "© 2024 My Company",
        modifer = Modifer
            .align(Alignment.CenterHorizontally)
            .padding(8.dp)
    )
}

위 코드를 보면 알겠지만, Container로 선언 후 다른 @Composable을 쌓아서 사용하고 있다. 참고로 @Composable이 붙은 메서드는 class 처럼 이름 첫 시작이 대문자이다. 이는 하나의 Class처럼 보이게끔 하기 위함인듯하다.

2. CSS 기능과 규격

일단 위 글을 정리하면서 CSS역할을 하는게 Modifer라는 걸 대충 눈치를 챘다.

그럼 규격에 대해서 좀 더 자세히 정리할 필요가 잇음

dp

Density-Indepedent Pixels.

px처럼 사용하는 단위.

Q&A
1. Desity로 부터 독립한 pirxel을 의미하는데 .. 그럼 Density가 무엇을 읨히는지?

sp

Scale -independent Pixels

폰트사이즈에 사용됨

percent Based

vh, vw, % 로 사용되던 것 들

// fill 100% of parent's width 
Modifier.fillMaxWidth()

// fills 50% of the parents'witdh
Modifier.fillMaxWidth(0.5f)

weight

마치 fliexBox처럼 쓸 수 있음

Row {
    Box(modifer = Modifier.weight(1f))
    Box(odifier = modifier.weight(1f)) // fills 50% of the Row
}

automatic Sizes

web의 auto와 관련된 부분

Compose에서는 wrap content가 default이다 → ⚠️이거 무슨말인지 잘 모르겠어

Box {
    Text("Auto - sized content")
}

// Text composable will take up only as mush space as its content needs.

Min, Max, and Exact Sizes

You can also specify minimum, maximum, or exact sizes using Modifier methods like requiredSize(), requiredWidth(), requiredHeight(), minWidth(), maxWidth(), and more.

Box(
    modifier = Modifier
        .widthIn(min = 100.dp, max = 200.dp)  // Similar to CSS `min-width: 100px; max-width: 200px;`
        .height(100.dp)  // Fixed height, similar to `height: 100px;`
)

Summary of Differences

Web (CSS)Jetpack Compose (Modifiers)Description
pxdpDensity-independent pixels, scales with screen density.
em, remspFor text scaling based on user preferences.
%, vw, vhfillMaxWidth, fillMaxHeight, weightPercentages or viewport-based dimensions.
autoWrapping content (default behavior)Auto-size to fit content.
min-width, max-widthwidthIn(), heightIn()Set minimum and maximum width or height.
paddingpadding()Adds padding to composables.
marginUse padding() + parent space managementMargins can be managed using combinations of modifiers.

3. Position

position: relative, absolute, fixed, and sticky와 연관되어 잇는 부분.

Relative

기준점을 정하는 거

@Composable
fun RelativePositioningExample() {
    Box {
        Text("Hellow, world!!", modifier = Modifier.offset(x = 16.dp, y = 8.dp))
    }
}

Modifier.offset(x = 16.dp, y = 8.dp) moves the composable 16dp to the right and 8dp down from its original position.

Absolute

부모 요소로 부터 자유로워 지는거

@Composable
fun AbsolutePositioningExample() {
    Box {
        Text(
            "Absolutely Positioned!",
            modifier = Modifier.absoluteOffset(x = 30.dp, y = 50.dp)
        )
    }
}

Fixed

직접적으로 fiexf를 지원하는건 없다.

하지만 Trick을 이용해 구현은 가능하다.

@Composable
fun FixedPositioningExample() {
    Box {
        // Content that scrolls
        LazyColumn {
            items(100) { index ->
                Text("Item $index")
            }
        }
        // "Fixed" header
        Text(
            "Fixed Header",
            modifier = Modifier
                .align(Alignment.TopCenter)  // Keeps this header at the top
                .background(Color.Gray)
                .padding(8.dp)
        )
    }
}

sticky

sticky는 잘 사용하지 않아서 어떤 개념인지 공부가 필요.

간략하게는 특정 스크롤 포지션을 만나면 해당 elemnt를 relative로 변경하는 것을 말하는 것 같다.

벗어 나면 fixed가 됨

@Composable
fun StickyHeaderExample() {
    LazyColumn {
        stickyHeader {
            Text("Sticky Header", Modifier.background(Color.Gray).padding(8.dp))
        }
        items(100) { index ->
            Text("Item $index")
        }
    }
}

Algin-times, justify-content

Modifier.align(), Modifier.layout()이걸로 가능하다고 함

@Composable
fun AlignmentExample() {
    Box(modifier = Modifier.fillMaxSize()) {
        Text("Centered Text", modifier = Modifier.align(Alignment.Center))  // Centered in the Box
    }
}

Summary of Positioning Concepts in Jetpack Compose

Web (CSS)Jetpack Compose (Modifier/Layout)Description
position: static;Default behaviorElements flow naturally in the layout without any offset or positioning.
position: relative;Modifier.offset(x: Dp, y: Dp)Elements are offset relative to their default position.
position: absolute;Modifier.absoluteOffset(x: Dp, y: Dp)Elements are placed at fixed coordinates, relative to the parent container.
position: fixed;Modifier.align() + container structureElements stay fixed in place, not affected by scrolling (custom handling).
position: sticky;LazyColumn with stickyHeaderHeaders that stick at the top while scrolling.
align-items, justify-contentModifier.align() and other layout modifiersAligning elements within a container.

질문 4. 동작 정의

이건 근데 애초에 ktolin이라서 얼마든지 커스텀이 가능할 듯 싶다.

간단한 것들만 따로 정리해봣음

기본적으로 jetpack compose는 상태가 변함에 따라 UI가 변경된다. 이댸 compose는 Recomposes가 된다.

Statemangement

dynamic UI의 핵심. 상태가 변화하면 - > compse는 recompose를 발생

State를 사용하여 변하하는 데이터를 hold 해둘 수 있음.
remeber, mutalbeStateOF, and Viewmodel 등을 사용할 수 잇다. 
@Composable
fun Counter() {
    // Remeber and store the mutable state of the counter
    var count by remember { mutableStateOf(0) }
    
    // Column layout to vertically stack the elements.
    Column(
        modifer = Modifer.padding(16.dp)
        horizontalAligment = Aligment.CenterHorizontally
    ) {
        // Dynamic text that updates whenver 'count' changes
        Text(text = "Count: $count", fontsize = 24.sp)
        
        Button(
            onClick = {  count++ }
            modifer = Modifer=padding(top = 16.dp)
        ) {
            Text(text = "Increment"
        }
    }
}

질문 5. 스크롤 애니메이션

web에서는 GSAP과 같은 라이브러리를 사용하여 스크롤 애니메이션 구현이 가능함.

Compose에서도 당연히 물론 비슷한 기능을 제공하는 API가 존재한다.

  • Modifier.graphicsLayer and Modifier.animate: Apply animations to elements as they scroll.
  • LazyListState and rememberScrollState: Track the scroll position to trigger animations.
  • AnimatedVisibility, animateDpAsState, animateFloatAsState: Animate composables based on state changes.

code

@Composable
fun FadingScrollAnimationList() {
    val listState = rememberLazyListState()
    
    LazyColumn(state = listState) {
        items(50) 
    }
}

4,5번은 필요한 시점에서 추가 공부 후 다시 정리.

핵심 정리

1. JETPACK Compse는 Web가 유사하게 구조를 짜서 만들고 속성을 조정할 수 있다.

2. UI를 더욱 화려하게 해중 animation(by scroll) 기능 역시 가능하다. 

3. 언어 자체가 kotlin이라서 기능적인 측면에서는 훨씬 더 쉽고 유연하게 작동할 것 같다.
profile
Studying to be Web & App full stack developer

0개의 댓글