원티드 앱에 Compose 적용해보기 — 2

Nampyo Jeong
원티드랩 기술 블로그
9 min readOct 1, 2021

지난 글에서 composable UI에서 기존 view를 사용해봤고, 이번에는 기존 xml에서 composable UI를 사용하는 방법을 알아보겠습니다.

원티드 커뮤니티에는 에러가 발생했을 때 표시해주는 custom UI가 있습니다. (유저 분들은 보신 적이 없는 화면이길 바랍니다.)

이 ErrorView를 compose로 변환하고 xml에서 사용할 수 있게 만들어보겠습니다.

Composable UI 만들기

특별한 내용은 없으므로 코드만 공유하겠습니다.

@Composable
fun ErrorView(
logoImage: Painter = painterResource(
R.drawable.img_error_cup
),
titleImage: Painter = painterResource(
R.drawable.img_sorry_text
),
errorMessage: String = stringResource(
R.string.user_noti_server_error
),
onClick: () -> Unit = {},
) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 24.dp, vertical = 88.dp),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Image(
painter = logoImage,
contentDescription = "ErrorView logo image",
modifier = Modifier.size(
width = 128.dp,
height = 80.dp,
),
)
Spacer(Modifier.height(42.dp))
Image(
painter = titleImage,
contentDescription = "ErrorView title image",
)
Spacer(Modifier.height(12.dp))
Text(
text = errorMessage,
style = TextStyle(
fontSize = 16.sp,
color = colorResource(id = R.color.fixed_text_gray),
),
textAlign = TextAlign.Center,
)
Spacer(Modifier.height(32.dp))

val buttonColor = colorResource(id = R.color.fixed_w_blue)
Button(
onClick = onClick,
modifier = Modifier.width(240.dp),
elevation = ButtonDefaults.elevation(
defaultElevation = 0.dp
),
shape = RoundedCornerShape(percent = 50),
border = BorderStroke(
width = 1.dp,
color = buttonColor,
),
colors = ButtonDefaults.buttonColors(
backgroundColor = Color.Transparent,
),
) {
Text(
text = stringResource(
id = R.string.user_home_error_retry
),
modifier = Modifier.padding(
horizontal = 24.dp,
vertical = 8.dp,
),
style = TextStyle(
color = buttonColor,
fontSize = 16.sp,
fontWeight = FontWeight.Bold,
)
)
}
}
}

AbstractComposeView 설정

composable을 xml에서 사용하려면 AbstractComposeView로 CustomView를 만들어줘야 합니다. (link)

⚠️ 다만, 실제로 실행해보면 ViewTreeLifecycleOwner 오류가 발생해서 appcompat 라이브러리가 1.3.0 이상이 필요합니다. (link)

ErrorView는 기존에도 CustomView로 만들어져 있어서 부모 클래스 교체하고 일부 내용만 수정하면 간단하게 작업을 완료할 수 있었습니다.

우선 부모 클래스를 교체하고 Content() 함수를 오버라이드 합니다.

class ErrorView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : AbstractComposeView(context, attrs, defStyleAttr) {

@Composable
override fun Content() {
ErrorView()
}
}

attribute로 설정되는 요소들을 멤버변수에 MutableState로 만들어줍니다.

class ErrorView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : AbstractComposeView(context, attrs, defStyleAttr) {

var logoImageRes by mutableStateOf(R.drawable.img_error_cup)
var titleImageRes by mutableStateOf(R.drawable.img_sorry_text)
var errorMessageRes by mutableStateOf(R.string.user_noti_server_error)
var onRetryClickListener by mutableStateOf({})


@Composable
override fun Content() {
ErrorView(
logoImage = painterResource(logoImageRes),
titleImage = painterResource(titleImageRes),
errorMessage = stringResource(errorMessageRes),
onClick = onRetryClickListener,

)
}
}

마지막으로 styledAttributes를 연결해주면 완료!

class ErrorView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : AbstractComposeView(context, attrs, defStyleAttr) {

var logoImageRes by mutableStateOf(R.drawable.img_error_cup)
var titleImageRes by mutableStateOf(R.drawable.img_sorry_text)
var errorMessageRes by mutableStateOf(R.string.user_noti_server_error)
var onRetryClickListener by mutableStateOf({})

init {
attrs?.let {
context.obtainStyledAttributes(
it,
R.styleable.ErrorView
).run {
logoImageRes = getResourceId(
R.styleable.ErrorView_android_src,
R.drawable.img_error_cup
)

errorMessageRes = getResourceId(
R.styleable.ErrorView_android_text,
R.string.user_noti_server_error
)

recycle()
}
}
}


@Composable
override fun Content() {
ErrorView(
logoImage = painterResource(logoImageRes),
titleImage = painterResource(titleImageRes),
errorMessage = stringResource(errorMessageRes),
onClick = onRetryClickListener,
)
}
}

마무리

지난 글에서는 compose 기초 문법을 포함하다보니 꽤 길어졌는데 이번에는 비교적 간단하게 글을 작성해봤습니다.
CustomView를 만들 때 여전히 styledAttributes가 필요한 부분은 아쉽지만 xml layout을 별도로 만들지 않아도 compose로 충분히 호환성 있게 만들 수 있는 것으로도 충분히 만족스럽니다.

다음 글에서는 LazyColumn을 사용해서 ListView를 구현하는 방법에 대해서 써보겠습니다. 감사합니다.

원티드에서는 다양한 직군에서 적극적으로 채용중입니다! 서버, 웹, 앱, 디자인 등 제품을 만들어가는 각자의 분야에서 전문적인 분들과 함께 일하기를 기대하고 있습니다. 회사 채용 정보 페이지를 확인해 주세요!

--

--