duckie-team/quack-quack-android

꽥꽥 컴포넌트 Modifier 안정성의 고민

Closed this issue · 9 comments

꽥꽥 2.0.0에서는 컴포넌트의 디자인을 제어하기 위해 Modifier를 사용합니다. 예를 들어 다음과 같습니다.

QuackTheme {
    QuackText(
        modifier = Modifier.highlight(highlights = listOf("test" to { toast("test") }, "hi" to { toast("bye") })),
        text = "test hi bye",
        typography = QuackTypography.Body1,
    )
}

위 예시에서 쓰인 Modifier#highlight는 Text 컴포넌트 전용 Modifier로, QuackText 컴포저블의 리시버로 QuackText라는 Text 컴포넌트의 스코프가 달려 있기에 사용할 수 있습니다.

@Composable
public fun QuackText.QuackText(
    modifier: Modifier = Modifier,
    text: String,
    typography: QuackTypography,
    singleLine: Boolean = false,
    softWrap: Boolean = true,
    overflow: TextOverflow = TextOverflow.Ellipsis,
)

QuackText 스코프는 꽥꽥 컴포넌트의 스코프와 꽥꽥 테마를 제공하는 최상단 컴포저블 함수인 QuackTheme에서 제공됩니다.

@Composable
public fun QuackTheme(content: @Composable QuackScope.() -> Unit) {
    // ... 테마 지정 코드 생략
    QuackScope.content()
}

public object QuackScope : QuackText by QuackTextScope, QuackButton by QuackButtonScope

하지만 이 방식은 크게 2가지 문제가 있습니다.

첫 번째: QuackTheme 영역 안에서는 모든 Modifier에 접근할 수 있다.

QuackTheme {
    QuackText(
        modifier = Modifier
            .icons(leading = QuackIcon.Heart) // `QuackButton`의 `Modifier`이지만 `QuackText`에서 사용할 수 있습니다.
            .highlight(highlights = listOf("test" to { toast("test") }, "hi" to { toast("bye") })),
        text = "test hi bye",
        typography = QuackTypography.Body1,
    )
}

두 번째: 하나의 컴포저블 안에서 여러 도메인의 컴포넌트를 사용하고 싶을 때 컴포저블의 리시버 지정이 어려워 진다.

@Composable
fun QuackText.AwesomeToolbar() {
    QuackText(...) // OK
    QuackButton(...) // `QuackButton` 디자인용 `Modifier` 사용 불가 (함수의 리시버가 `QuackText`임)
}

이를 어떤 방식으로 해야 합리적으로 안정성 문제를 해결할 수 있을까요?

::toast 는 무엇인가요?

::toast 는 무엇인가요?

아직 작성 중입니다..!! ㅜㅜ
실수로 바로 올려버렸어요

아하 다음에 하실때는 글 작성 완료하신 뒤에 Assignees 추가해주시면 좋을 것 같아요 🙏

아하 다음에 하실때는 글 작성 완료하신 뒤에 Assignees 추가해주시면 좋을 것 같아요 🙏

이게 원래는 올리고 나서 알림이 가는데, 실수로 작성 중에 미리 올려버려서 그렇습니다 🙏
제가 실수만 안 했더라면 문제 없던 거였어요 😢

아하 다음에 하실때는 글 작성 완료하신 뒤에 Assignees 추가해주시면 좋을 것 같아요 🙏

이게 원래는 올리고 나서 알림이 가는데, 실수로 작성 중에 미리 올려버려서 그렇습니다 🙏
제가 실수만 안 했더라면 문제 없던 거였어요 😢

아아 만약 Assignees 를 안해놨으면 폰으로 알람이 오지 않거든요 🙏
사람은 항상 실수하기 때문에 ㅋㅋ (저도 그렇고 ㅋㅋ)
실수 대비 차원에서 그게 좋아서 말씀드린거였어요 ㅋㅋ

아하 다음에 하실때는 글 작성 완료하신 뒤에 Assignees 추가해주시면 좋을 것 같아요 🙏

이게 원래는 올리고 나서 알림이 가는데, 실수로 작성 중에 미리 올려버려서 그렇습니다 🙏
제가 실수만 안 했더라면 문제 없던 거였어요 😢

아아 만약 Assignees 를 안해놨으면 폰으로 알람이 오지 않거든요 🙏 사람은 항상 실수하기 때문에 ㅋㅋ (저도 그렇고 ㅋㅋ) 실수 대비 차원에서 그게 좋아서 말씀드린거였어요 ㅋㅋ

아하 감사합니다! 다음부터는 유념하겠습니다 🙇
지금은 글 다 썼어요!

1번의 경우는 .icons(leading = QuackIcon.Heart) 에서 에러를 내고 싶은 게 의도이실까요?
2번은 좀 생각할 것이 많아보이네요... Modifier 를 매개변수 차원으로 처리하기엔 뭔가 애매해보이기도 하고요..

1번의 경우는 .icons(leading = QuackIcon.Heart) 에서 에러를 내고 싶은 게 의도이실까요?

아니요. 에러 발생은 쉽습니다. 제가 하고 싶은 건 Modifier#iconsQuackButton 외에선 사용할 수 없게 만드는 거예요.

Modifier 를 매개변수 차원으로 처리하기엔 뭔가 애매해보이기도 하고요..

근데 이런 방식으로 접근하지 않으면 디자인 시스템의 재사용성 문제가 해결되지 않습니다.
제가 꽥꽥 2.0.0 아티클에서 다룬 내용들이요.

Solution

  1. QuackText, QuackButton과 같은 컴포넌트 스코프 리시버 제거
  2. 린트를 이용하여 올바르지 않은 Modifier 사용 경고 처리