♻️ Refactor - Coil 이미지 소스를 ImageModel 타입으로 분리

Open
#154 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
4/5
Estimated time
3-5 days
Newbie friendliness
52/100
Issue type
Refactor
Clarity
Clearly specified
Activity status
Active
Tech stack
kotlin
Domain
mobile

Research direction

First confirm that #153 has been merged, then inspect the affected FeedCard, ImageViewerScreen, notification, mypage, and upload screens and the PreviewImages helper. Define ImageModel and its Coil adapter, then trace server-response mappings and gallery Uri handling across the listed modules. Done means all checklist items are complete and screenshot comparison CI shows no UI diff.

Written by the indexing model from the issue text.

Description

♻️ REFACTOR

🛠️ Issue Description

Coil에 넘기는 이미지 소스를 String 대신 ImageModel 타입으로 표현한다. 네트워크 URL / 로컬 리소스 / 로컬 Uri 를 타입으로 구분해, 호출부가 무엇을 넘기는지 시그니처에서 드러나게 한다.

💭 Cause and effect

왜 지금 문제인가

현재 컴포넌트 시그니처는 전부 String이다.

FeedCard(
    profileImageUrl: String,
    productImageUrls: List<String>,
    ...
)

String이 http URL인지, 로컬 리소스인지, 갤러리에서 고른 content:// Uri인지 타입이 말해주지 않는다. 실제로 세 가지가 전부 섞여 들어온다.

  • 서버 응답 → http URL (Feed.images, Author.profileImage, NotificationItem.imageUrl)
  • 갤러리 선택 → content:// Uri (UploadScreen)
  • 프리뷰 → 로컬 drawable
#153에서 드러난 구체적 증거

스크린샷 비교 CI(#153)를 붙이면서 프리뷰가 외부 URL을 때리면 안 되게 됐다. 파라미터 타입이 String이라 로컬 리소스를 넘길 방법이 없어, 문자열을 직접 조립하는 우회책을 넣었다.

// core/designsystem/.../preview/PreviewImages.kt
@Composable
private fun resourceUri(resId: Int): String {
    val packageName = LocalContext.current.packageName
    return remember(packageName, resId) { "android.resource://$packageName/$resId" }
}

Coil의 model 파라미터는 원래 Any?Int(resId) / Uri / String 을 다 받는다. 즉 타입을 좁혀놓은 쪽이 우리고, 그 대가를 문자열 조립으로 치르고 있다.

ImageModel이 있으면 이렇게 끝난다.

profileImageUrl = ImageModel.Resource(R.drawable.preview_avatar)
예상 결과
  • 호출부가 무엇을 넘기는지 시그니처에서 읽힌다
  • PreviewImages의 문자열 조립을 걷어낼 수 있다
  • 프리뷰에 네트워크 이미지를 넣는 실수가 ImageModel.Network(...)로 눈에 띈다
    (단 타입이 막아주지는 않는다 — #153의 CI grep 가드는 계속 필요하다)
범위
sealed interface ImageModel {
    @JvmInline value class Network(val url: String) : ImageModel
    @JvmInline value class Resource(@DrawableRes val resId: Int) : ImageModel
    @JvmInline value class Local(val uri: Uri) : ImageModel
}

AsyncImage(model = ...) 에 넘길 때 Any로 변환하는 어댑터 하나를 둔다.

영향 범위:

  • core:designsystemFeedCard
  • core:uiImageViewerScreen
  • feature:notificationNotificationItem, NotificationScreen, NotificationDetailScreen
  • feature:mypageMyPageScreen, BlockedAccountsScreen
  • feature:uploadUploadScreen (갤러리 Uri 경로)
  • 서버 응답 String을 ImageModel.Network로 감싸는 매핑 계층 (domain / core:data)
⚠️ 선행 조건

#153(스크린샷 비교 CI)이 먼저 머지돼야 한다. 순서가 중요하다.

  • #153을 먼저 넣으면 이 리팩토링이 UI를 의도치 않게 바꿨는지 CI가 잡아준다. 시그니처를 대거 건드리는 작업이라 안전망이 있는 편이 낫다.
  • 반대로 이 작업을 #153에 합치면, CI 도입 PR이 프로덕션 시그니처 대공사를 끌고 들어와 리뷰어가 두 가지를 동시에 봐야 한다.
📝 check-lists
  • #153 머지 확인
  • ImageModel sealed interface 정의 (core:designsystem 또는 core:common)
  • AsyncImage 어댑터 (ImageModel -> Coil model)
  • core:designsystem FeedCard 시그니처 전환
  • core:ui ImageViewerScreen 전환
  • feature:notification 3개 화면 전환
  • feature:mypage 2개 화면 전환
  • feature:upload 전환 (갤러리 Uri 경로 포함)
  • 서버 응답 -> ImageModel.Network 매핑 계층
  • PreviewImagesandroid.resource:// 문자열 조립 제거, ImageModel.Resource로 교체
  • 스크린샷 비교 CI에서 diff가 나지 않는지 확인 (UI가 안 바뀌어야 정상)
Dominant language
Kotlin
Stars
9
Forks
1
Avg merge
4h 19m
Merged PRs (30d)
5

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from Nexters/BuyOrNot-Android

All issues in Nexters/BuyOrNot-Android

Similar issues

More Kotlin issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.