You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
refactor(core): 동일 화살표 렌더링을 기존 ArrowIcon에 위임한다 #635
현재 소스에는 ReadOnlyField가 없다. setting의 receiver/ReceiverRegisterScreen.kt 관계 앵커는 밑줄과 펼침 상태를 가진 단일 화면 구현이고 기존 core 입력 컴포넌트와 같은 규격이 아니다. 이를 동일 중복으로 간주한 예약은 제거한다.
현재 passkey/PassKeyViewModel.kt는 UserProfileDataSource.isPasskeyRegisteredFlow()와 savePasskeyRegistered(true)로 임시 로컬 등록 상태를 관리한다. #1944의 서버 목록·등록 성공 소비가 이 흐름을 대체한다. 닫힌 PR #1927의 오래된 MVI 구조를 복원하거나 이 이슈에서 옛 UserProfileRepository 추출을 예약하지 않는다. 현재 생산 계약에 대한 #1944의 재판정 범위를 중복 구현하지 않는다.
core 드로어블끼리 동일 사본은 없으며 팝업·프로필 이미지는 이미 공용 구현을 소비한다. 업로드 MIME/실패 계약, 별도 네트워크 바인딩, 서로 다른 디자인 컴포넌트는 동일 계약의 중복으로 취급하지 않는다.
범위 경계
현재 렌더·접근성·기능을 보존하는 위임만 완료 조건으로 둔다. 아이콘 형태·크기·tint, 헤더·입력 팝업·셰브론의 미확정 시각 통일, core 파라미터 확장, 에셋 재크롭 및 컴포넌트 치환을 포함하지 않는다. 이 이슈는 다른 담당자의 timeletter 작업 완료를 기다리는 부모 추적 이슈로 사용하지 않는다.
changed the title [-]refactor(timeletter): core:ui 자원 미사용으로 생긴 중복 6건 수렴 — 아이콘 사본·뷰 전환 토글·섹션 헤더·M3 다이얼로그[/-][+]refactor(timeletter,setting): core 자원 미사용으로 생긴 중복 10건 수렴 — 타임레터 6 · 설정 4[/+]on Aug 4, 2026
둘 다 실제로 쓰이지 않는 죽은 import 입니다. IDE 자동완성이 named argument 를 심볼로 오인해 끼워 넣은 흔적으로 보입니다.
ViewToggleButton.kt:55,79 — contentDescription = "리스트형" / "블록형" 은 named argument
InsertPasswordContent.kt:23,44 — passwordLength · PasswordDots 뿐, password 단독 참조 없음
동작에는 영향이 없습니다(import android.R.attr.password 는 password 라는 이름만 가져오므로 같은 파일 R.drawable.ic_lock 은 그대로 모듈 R 로 해석). 다만 ktlint 의 미사용 import 검사를 빠져나가는 형태라 자동으로는 안 잡힙니다 — 파일 본문에 contentDescription · password 라는 문자열이 named argument 로 등장해서 사용 중으로 오인됩니다. 손으로 지우는 수밖에 없습니다.
세 파일 모두 import ...presentation.R 도, android.R 오import 도 없이 FQN 만 씁니다. 이름 충돌 회피가 아니라 관행으로 보이고, import 한 줄로 줄일 수 있습니다. 이 이슈가 같은 파일들(ViewToggleButton 은 아이콘 사본 항목과 겹칩니다)을 어차피 건드리므로 함께 처리하면 비용이 0에 가깝습니다.
참고
여기 나오는 ic_list 는 feature/setting 에도 같은 이름·같은 54줄 드로어블이 있습니다(모듈 간 이름 충돌 3건 중 하나). 이 이슈가 다루는 ic_image·ic_file 사본과 같은 계열이라, 정리 범위에 넣을지 판단이 필요합니다. 네이밍 규칙 자체(resourcePrefix 도입)는 #1071 에서 따로 다룹니다.
이 중 ic_image · ic_file · default_profile · ic_list 4개는 이 이슈가 이미 다루는 사본입니다. core:ui 로 치환되면 파일이 삭제되어 리네임 대상에서 아예 사라집니다. 그래서 순서가 뒤집히면 헛일이 됩니다 — 사본 정리가 먼저입니다.
남는 9개(container · down_vector · setting · ic_align_* 3 · ic_block · ic_mic · ic_text)는 timeletter_ 프리픽스로 리네임하고 build.gradle.kts 에 resourcePrefix = "timeletter_" 를 걸면 끝입니다. 이 모듈은 문자열이 이미 준수라 남은 작업이 작고, 담당도 이 이슈와 같아 여기 얹는 게 맞다고 봤습니다.
setting · container 처럼 일반명사 이름은 다른 모듈과 충돌하기 쉽습니다. 실제로 ic_list 는 feature/setting 에 같은 이름·같은 54줄 사본이 있습니다.
changed the title [-]refactor(timeletter,setting): core 자원 미사용으로 생긴 중복 10건 수렴 — 타임레터 6 · 설정 4[/-][+]core 자원 미사용으로 생긴 중복 10건 수렴 — 타임레터 6 · 설정 4[/+]on Aug 28, 2026
setting 3번(PassKeyViewModel 의 DataStore 직접 참조) — 0829 기준 여전히 유효
#1282 계약 분리 작업 중 프로필 계약 사용처를 훑다가 재확인했다. 본문 3번의 실측이 지금도 그대로다.
PassKeyViewModel.kt:5,17 이 core.datastore.UserProfileDataSource 를 직접 주입받는다 — UserProfileRepository 미경유.
feature/ 전 소스셋에서 com.afternote.core.datastore 를 import 하는 파일은 여전히 이 하나뿐이고, implementation(projects.core.datastore) 를 선언한 presentation 모듈도 setting 하나뿐이다. 본문대로 그래들 한 줄까지 같이 빠진다.
이 위반을 놓치게 만든 자동 게이트 부재는 별도로 #1432 에 올렸다 — konsist 의 레이어 가드가 domain 패키지 파일만 스캔해서 presentation → core:datastore 는 규칙 사정거리 밖이다. 다만 그 가드는 main 소스 위반이 0이어야 초록으로 들어가므로, 이 항목 수복이 선행이다. 순서만 그렇고 이 이슈에 추가 작업이 붙지는 않는다.
착수 전 점검에서 본문의 "독립적으로 남길 범위" 3항목 중 2항목이 현실과 어긋난 것을 확인했습니다. 기준 리비전은 origin/develop86ab223 이고, 중복은 전부 심볼명으로 세었습니다.
살아 있는 항목 — timeletter 벡터 사본 (1번)
feature/timeletter/presentation/src/main/res/drawable/ic_image.xml 과 ic_file.xml 은 core/ui 의 core_ui_ic_image.xml · core_ui_ic_file.xml 과 들여쓰기(2칸 대 4칸)만 빼고 전 속성이 같습니다. 24dp · viewport 24 · path · strokeAlpha 0.6 · strokeWidth 2 · fillColor #00000000 · strokeColor #000000 · cap/join round 까지 같습니다. 소비처는 세 곳입니다(MediaBottomSheetContent.kt:44,56, TimeLetterWriteScreen.kt:567). 같은 함수가 이미 core_ui_ic_mic(:50)과 core_ui_ic_link(:62)를 CoreUiR 로 쓰고 있어 두 개만 모듈 사본으로 남은 상태입니다. 세 자리 모두 명시 크기 없이 intrinsic 24dp 로 그리므로 치환해도 렌더가 같습니다.
default_profile.xml(67x68dp)은 core_ui_ic_profile_placeholder.xml(134x136dp)의 정확한 1/2 축소본입니다. 좌표를 하나씩 2배 해서 전부 일치했습니다. 원 33.75,33.25 r33.25 → 67.5,66.5 r66.5, 머리 33.5,29 C38.471,29 42.5,24.971 42.5,20 → 67,58 C76.94,58 85,49.94 85,40, 몸통 53.843→107.69 · 48.607→97.22 · 43.146→86.29 · 23.504→47.01 · 13.157→26.31, strokeWidth 4 대 8. 종횡비 67:68 = 134:136 으로 같습니다. 소비처 두 곳(RecipientCard.kt:39 size(67.dp), RecipientListItem.kt:39 size(50.dp))이 모두 명시 크기라 치환해도 렌더가 같습니다.
선례도 있습니다. 7c1c9e9e1(#640)이 mic 아이콘 바이트 동일 사본 2벌을 core_ui_ic_mic 으로 이관했고 #640 은 COMPLETED 로 닫혔습니다.
빠져야 할 항목 — setting ReadOnlyField (2-a)
private fun ReadOnlyField 는 RecipientSetScreen.kt:134 에 그대로 있고 스펙도 AfternoteFieldContainer 와 같습니다. 그런데 RecipientSetScreen 은 전 저장소 참조가 자기 파일 안 3건(선언 :38, Preview :159, Preview 호출 :160)뿐인 도달 불가 화면이고, 열린 PR #2005(#1064)가 이 파일을 통째로 삭제합니다. #1064 본문 33행이 이미 "#635 의 setting 1번 항목은 화면째 삭제되면 사라진다. 이 이슈를 먼저 처리하고 #635 에서는 그 항목을 빼는 쪽이 맞다" 고 적어 뒀습니다.
이미 해소된 항목 — setting 관계 드롭다운 앵커 (2-b)
근거 이슈 #636 이 지목한 형태는 ReceiverRegisterScreen.kt:157-184 의 Row(...background(white, shape).border(1.dp, gray2, shape).clickable{}.padding(24, 13)) 였습니다. origin/develop 의 같은 자리(:243-278)는 Column(...).clickable{ relationExpanded = true }.padding(top = 13.dp) 안에 Row(Text + core_ui_arrowdown 18dp)와 HorizontalDivider(0.8.dp, gray3) 를 두는 밑줄형 앵커로 바뀌어 있습니다. 파일 전체에 .border 는 0건이고 RoundedCornerShape 도 0건이며, 남은 .background 1건(:285)은 앵커가 아니라 DropdownMenu 팝업 자체입니다. AfternoteFieldContainer 로 수렴할 중복이 없습니다. 바뀐 시점은 7612fab11(#577)과 뒤이은 9aeb1e437 이고 원형은 cd5e4e49b 에만 남아 있습니다.
남의 PR 이 이미 수복 중인 항목 — PassKeyViewModel (3번)
PassKeyViewModel.kt:5,17 이 여전히 core.datastore.UserProfileDataSource 를 직접 주입받습니다. 본문이 건 게이트대로 #1944 / PR #1927 과 대조한 결과, 그 PR 이 바로 이 파일을 고쳐 UserProfileDataSource 를 UserProfileCacheRepository 와 feature.setting.domain.PasskeyRepository 로 갈아끼우고 MVI 로 전환합니다. 여기서 손대면 그 PR 과 충돌합니다. develop 게이트는 PresentationLayerDependencyKonsistTest.kt:137 의 KNOWN_LAYER_BYPASSES 등재로 초록입니다.
제안
본문에서 2-a · 2-b · 3 을 빼고 1번만 남긴 범위로 재작성하는 것이 맞습니다. 그러면 남는 일은 timeletter 벡터 사본 3개(ic_image · ic_file · default_profile)를 core/ui 로 수렴하고 소비처 5곳을 CoreUiR 로 바꾸는 것입니다.
다만 착수 전에 확인할 것이 둘 있습니다. timeletter 는 다른 사람의 모듈이라 프로덕션 변경은 담당자 게이트에 걸립니다. 그리고 RecipientCard · RecipientListItem 은 #1402(프로필 크기 시안 소수점 복원)와 #1470(contentDescription 리소스화)이 함께 보고 있어 같은 줄을 건드리면 겹칩니다.
git log --grep '#635' origin/develop 은 2건인데 둘 다 이 이슈를 처리한 커밋이 아닙니다. 5c8663940(#1433)은 개명만 했고, a984a59ab(#1432)는 "남은 위반 1건(PassKeyViewModel)은 KNOWN_LAYER_BYPASSES 에 등재했다. 수복은 setting 담당의 #635 몫" 이라고 적었습니다.
착수 결과입니다. 이 이슈로 제가 쓸 수 있는 프로덕션 코드가 0줄이라, 남은 범위를 두 이슈로 넘기고 관계를 걸었습니다. 기준 리비전은 origin/develop7d87aa3f4 입니다.
왜 0줄인가
앞선 코멘트에서 잰 변경면(벡터 사본 3개 삭제, 소비처 5곳 치환)은 파일이 전부 feature/timeletter 입니다. 그 목록으로 PR JSON 을 만들어 가드를 직접 돌렸습니다.
$ node .github/scripts/validate-pr-module-owner.mjs pr.json files.json
::error::PR #9999 작성자 @1hyok 가 담당하지 않는 모듈의 프로덕션 파일을 바꿨습니다.
timeletter(담당 @koongmai) 7건: .../MediaBottomSheetContent.kt, .../RecipientCard.kt,
.../RecipientListItem.kt, .../TimeLetterWriteScreen.kt, .../default_profile.xml,
.../ic_file.xml, .../ic_image.xml
EXIT=1
선례로 삼을 만한 것이 7c1c9e9e1(#640, 2026-08-28) 인데, 같은 성격의 mic 사본 이관을 제가 직접 했습니다. 다만 그 커밋은 가드 도입 969ecaad4(#1889, 2026-09-04)보다 일주일 앞섭니다. 그 커밋의 파일 목록을 지금 가드에 먹이면 exit 1 이고 timeletter 3건과 mindrecord 1건을 지목합니다. 선례는 해도 된다는 근거가 아니라 가드가 없던 때라 통과했다는 근거입니다. 이 이슈 본문의 완료 조건 마지막 문장도 타인 담당 범위를 직접 수정하지 않는다입니다.
issue-assignee-exempt 라벨은 인계를 기다릴 수 없는 긴급 수선용이라, 렌더 보존 정리에는 맞지 않습니다.
timeletter_ 접두사. 0824 코멘트로 이 이슈에 얹혀 있던 #1071 의 timeletter 몫입니다
관계는 #635 blocked_by #2084, #635 blocked_by #2085, #2085 blocked_by #2084 입니다. 후속 분리분이라 자식이 아니라 blocked_by 로 이었습니다.
#2084 에는 blocked_by #714 도 걸었습니다. PR #1365 가 RecipientCard.kt 와 TimeLetterWriteScreen.kt 를 이미 건드리는데, 그쪽이 TimeletterScreenScreenshotTest 골든을 물고 있어 먼저 들어가야 재생성이 한 번으로 끝납니다. 사본 정리는 렌더가 같아 골든에 영향이 없습니다. 확인해 보니 그 골든 4장은 TimeletterScreen 만 그리고 여기 나오는 자원 셋을 하나도 렌더하지 않습니다.
#1613 과는 MediaBottomSheetContent 의 소비처 2곳이 겹칩니다. #1613 이 먼저 들어오면 그 2곳은 자연히 없어지지만 #2084 가 비지는 않습니다. ic_file 은 TimeLetterWriteScreen.kt:567 이 따로 쓰고 default_profile 은 #1613 범위 밖입니다.
나머지 세 항목이 이 이슈 몫이 아닌 이유
setting 의 ReadOnlyField
ReadOnlyField 는 RecipientSetScreen.kt 안의 private 함수(:134)이고 호출부도 같은 파일 두 곳(:86, :102)뿐입니다. 제 PR #2005 가 이 파일을 161줄 통째로 지웁니다. 중복을 수렴할 게 아니라 한쪽이 사라지는 건이라 이 이슈에서 손댈 것이 없습니다.
관계 드롭다운 앵커
develop 에서 feature/setting/presentation 의 DropdownMenu 는 ReceiverRegisterScreen.kt:279 한 곳뿐입니다. RecipientSetScreen.kt 에는 드롭다운이 없고 "관계" 정적 라벨과 ReadOnlyField 만 있습니다(:74). 중복 쌍이 이미 해소됐고, 남은 그 파일도 #2005 가 지웁니다.
PassKeyViewModel 의 DataStore 직접 의존
본문이 #1944·PR #1927 과 먼저 대조하라고 적어 둔 항목입니다. 대조했습니다. develop 의 PassKeyViewModel 은 core.datastore.UserProfileDataSource 를 직접 주입받지만, PR #1927 의 head 는 그 자리를 core.domain.repository.UserRepository 로 바꾸고 파일을 131줄 다시 씁니다(PasskeyRepository·PasskeyRepositoryImpl 신설 포함). 옛 경계를 이 이슈에서 따로 건드리면 그 PR 과 충돌만 납니다. #1927 소관으로 둡니다.
덤: 0824 코멘트의 ic_list 정정
그 코멘트가 ic_list 를 core 사본 4개 중 하나로 셌는데 다시 재 보니 사본이 아닙니다. timeletter 판은 28dp·viewport 28 의 스트로크 아이콘이고 core/ui/.../core_ui_list.xml 은 24dp·viewport 24 의 Material 채움 아이콘이라 형태가 다릅니다. 통일하면 시각 변경이고, 이 이슈 본문이 뷰 전환 아이콘의 크기·tint 통일을 제외 범위로 못박아 두었습니다. 그래서 #2084 에 넣지 않고 #2085 의 리네임 대상으로만 남겼습니다.
같은 이유로 프리픽스 대상 수도 달라집니다. 0824 에 13개였던 미준수 드로어블이 지금 9개이고(ic_mic 와 ic_align_* 3개가 그사이 정리됐습니다) #2084 가 3개를 지우면 6개가 남습니다.
이 이슈에 남는 것
제 담당인 core 쪽은 할 일이 없습니다. 세 자원이 core/ui 에 이미 있고 core:ui 자신과 afternote·mindrecord·receiver 가 씁니다. #2084 와 #2085 가 닫히면 이 이슈도 닫힙니다.
Activity
1hyok commentedon Aug 1, 2026
담당 재확인 결과 #634와 담당이 동일하다는 기존 전제가 잘못되어 이 이슈를 재개했다.
#634는 마음의 기록 6건만 남겨 Sadturtleman에게 배정했다. 이 이슈는 koongmai 담당인 타임레터 6건과 설정 4건을 함께 추적한다. 설정 단독 이슈 #636은 같은 담당의 동일 작업이므로 이 이슈로 통합한다.
[-]refactor(timeletter): core:ui 자원 미사용으로 생긴 중복 6건 수렴 — 아이콘 사본·뷰 전환 토글·섹션 헤더·M3 다이얼로그[/-][+]refactor(timeletter,setting): core 자원 미사용으로 생긴 중복 10건 수렴 — 타임레터 6 · 설정 4[/+]1hyok commentedon Aug 24, 2026
#1071(리소스 네이밍) 조사 중에 이 이슈 범위인 두 모듈에서 함께 정리할 만한 위생 건이 나와 붙입니다. 실측 리비전
6b381db8(origin/develop).1.
android.R오import 2건 — 두 모듈에 하나씩둘 다 실제로 쓰이지 않는 죽은 import 입니다. IDE 자동완성이 named argument 를 심볼로 오인해 끼워 넣은 흔적으로 보입니다.
ViewToggleButton.kt:55,79—contentDescription = "리스트형"/"블록형"은 named argumentInsertPasswordContent.kt:23,44—passwordLength·PasswordDots뿐,password단독 참조 없음동작에는 영향이 없습니다(
import android.R.attr.password는password라는 이름만 가져오므로 같은 파일R.drawable.ic_lock은 그대로 모듈 R 로 해석). 다만ktlint의 미사용 import 검사를 빠져나가는 형태라 자동으로는 안 잡힙니다 — 파일 본문에contentDescription·password라는 문자열이 named argument 로 등장해서 사용 중으로 오인됩니다. 손으로 지우는 수밖에 없습니다.2. 덤 — timeletter 의 자기 모듈 R 을 FQN 으로 참조 6곳
세 파일 모두
import ...presentation.R도,android.R오import 도 없이 FQN 만 씁니다. 이름 충돌 회피가 아니라 관행으로 보이고, import 한 줄로 줄일 수 있습니다. 이 이슈가 같은 파일들(ViewToggleButton은 아이콘 사본 항목과 겹칩니다)을 어차피 건드리므로 함께 처리하면 비용이 0에 가깝습니다.참고
여기 나오는
ic_list는feature/setting에도 같은 이름·같은 54줄 드로어블이 있습니다(모듈 간 이름 충돌 3건 중 하나). 이 이슈가 다루는ic_image·ic_file사본과 같은 계열이라, 정리 범위에 넣을지 판단이 필요합니다. 네이밍 규칙 자체(resourcePrefix도입)는 #1071 에서 따로 다룹니다.1hyok commentedon Aug 24, 2026
#1071(리소스 네이밍)을 모듈별로 쪼개면서
feature/timeletter몫은 이 이슈로 넘겼습니다. 별도 이슈를 내지 않은 이유와 남는 범위를 적어 둡니다.timeletter는 문자열 9개가 이미 전량timeletter_프리픽스를 지키고, 미준수는 드로어블 13개뿐입니다.이 중
ic_image·ic_file·default_profile·ic_list4개는 이 이슈가 이미 다루는 사본입니다. core:ui 로 치환되면 파일이 삭제되어 리네임 대상에서 아예 사라집니다. 그래서 순서가 뒤집히면 헛일이 됩니다 — 사본 정리가 먼저입니다.남는 9개(
container·down_vector·setting·ic_align_*3 ·ic_block·ic_mic·ic_text)는timeletter_프리픽스로 리네임하고build.gradle.kts에resourcePrefix = "timeletter_"를 걸면 끝입니다. 이 모듈은 문자열이 이미 준수라 남은 작업이 작고, 담당도 이 이슈와 같아 여기 얹는 게 맞다고 봤습니다.setting·container처럼 일반명사 이름은 다른 모듈과 충돌하기 쉽습니다. 실제로ic_list는feature/setting에 같은 이름·같은 54줄 사본이 있습니다.feature/setting의 드로어블 15개도 같은 사정이라 #1077 에서 이 이슈 뒤로 순서를 잡아 뒀습니다.[-]refactor(timeletter,setting): core 자원 미사용으로 생긴 중복 10건 수렴 — 타임레터 6 · 설정 4[/-][+]core 자원 미사용으로 생긴 중복 10건 수렴 — 타임레터 6 · 설정 4[/+]1hyok commentedon Aug 28, 2026
setting 3번(PassKeyViewModel 의 DataStore 직접 참조) — 0829 기준 여전히 유효
#1282 계약 분리 작업 중 프로필 계약 사용처를 훑다가 재확인했다. 본문 3번의 실측이 지금도 그대로다.
PassKeyViewModel.kt:5,17이core.datastore.UserProfileDataSource를 직접 주입받는다 —UserProfileRepository미경유.feature/전 소스셋에서com.afternote.core.datastore를 import 하는 파일은 여전히 이 하나뿐이고,implementation(projects.core.datastore)를 선언한 presentation 모듈도 setting 하나뿐이다. 본문대로 그래들 한 줄까지 같이 빠진다.implementation(projects.core.data)는 main 소스 참조 0건이고 유일한 참조가SettingCompletionTest.kt:7의UserRepositoryImpl직접 조립이다(core:di 모듈 삭제 — 바인딩을 구현 옆으로 옮기고 repository impl 을 internal 로 #930 과 얽힌 그 조립).이 위반을 놓치게 만든 자동 게이트 부재는 별도로 #1432 에 올렸다 — konsist 의 레이어 가드가 domain 패키지 파일만 스캔해서
presentation → core:datastore는 규칙 사정거리 밖이다. 다만 그 가드는 main 소스 위반이 0이어야 초록으로 들어가므로, 이 항목 수복이 선행이다. 순서만 그렇고 이 이슈에 추가 작업이 붙지는 않는다.[-]core 자원 미사용으로 생긴 중복 10건 수렴 — 타임레터 6 · 설정 4[/-][+]core 중복 중 기존 렌더·동작을 보존하는 범위만 정리한다[/+]1hyok commentedon Sep 13, 2026
착수 전 점검에서 본문의 "독립적으로 남길 범위" 3항목 중 2항목이 현실과 어긋난 것을 확인했습니다. 기준 리비전은
origin/develop86ab223 이고, 중복은 전부 심볼명으로 세었습니다.살아 있는 항목 — timeletter 벡터 사본 (1번)
feature/timeletter/presentation/src/main/res/drawable/ic_image.xml과ic_file.xml은core/ui의core_ui_ic_image.xml·core_ui_ic_file.xml과 들여쓰기(2칸 대 4칸)만 빼고 전 속성이 같습니다. 24dp · viewport 24 · path · strokeAlpha 0.6 · strokeWidth 2 · fillColor#00000000· strokeColor#000000· cap/join round 까지 같습니다. 소비처는 세 곳입니다(MediaBottomSheetContent.kt:44,56,TimeLetterWriteScreen.kt:567). 같은 함수가 이미core_ui_ic_mic(:50)과core_ui_ic_link(:62)를CoreUiR로 쓰고 있어 두 개만 모듈 사본으로 남은 상태입니다. 세 자리 모두 명시 크기 없이 intrinsic 24dp 로 그리므로 치환해도 렌더가 같습니다.default_profile.xml(67x68dp)은core_ui_ic_profile_placeholder.xml(134x136dp)의 정확한 1/2 축소본입니다. 좌표를 하나씩 2배 해서 전부 일치했습니다. 원 33.75,33.25 r33.25 → 67.5,66.5 r66.5, 머리 33.5,29 C38.471,29 42.5,24.971 42.5,20 → 67,58 C76.94,58 85,49.94 85,40, 몸통 53.843→107.69 · 48.607→97.22 · 43.146→86.29 · 23.504→47.01 · 13.157→26.31, strokeWidth 4 대 8. 종횡비 67:68 = 134:136 으로 같습니다. 소비처 두 곳(RecipientCard.kt:39size(67.dp),RecipientListItem.kt:39size(50.dp))이 모두 명시 크기라 치환해도 렌더가 같습니다.선례도 있습니다.
7c1c9e9e1(#640)이 mic 아이콘 바이트 동일 사본 2벌을core_ui_ic_mic으로 이관했고 #640 은 COMPLETED 로 닫혔습니다.빠져야 할 항목 — setting ReadOnlyField (2-a)
private fun ReadOnlyField는RecipientSetScreen.kt:134에 그대로 있고 스펙도AfternoteFieldContainer와 같습니다. 그런데RecipientSetScreen은 전 저장소 참조가 자기 파일 안 3건(선언:38, Preview:159, Preview 호출:160)뿐인 도달 불가 화면이고, 열린 PR #2005(#1064)가 이 파일을 통째로 삭제합니다. #1064 본문 33행이 이미 "#635 의 setting 1번 항목은 화면째 삭제되면 사라진다. 이 이슈를 먼저 처리하고 #635 에서는 그 항목을 빼는 쪽이 맞다" 고 적어 뒀습니다.이미 해소된 항목 — setting 관계 드롭다운 앵커 (2-b)
근거 이슈 #636 이 지목한 형태는
ReceiverRegisterScreen.kt:157-184의Row(...background(white, shape).border(1.dp, gray2, shape).clickable{}.padding(24, 13))였습니다.origin/develop의 같은 자리(:243-278)는Column(...).clickable{ relationExpanded = true }.padding(top = 13.dp)안에 Row(Text +core_ui_arrowdown18dp)와HorizontalDivider(0.8.dp, gray3)를 두는 밑줄형 앵커로 바뀌어 있습니다. 파일 전체에.border는 0건이고RoundedCornerShape도 0건이며, 남은.background1건(:285)은 앵커가 아니라 DropdownMenu 팝업 자체입니다.AfternoteFieldContainer로 수렴할 중복이 없습니다. 바뀐 시점은7612fab11(#577)과 뒤이은9aeb1e437이고 원형은cd5e4e49b에만 남아 있습니다.남의 PR 이 이미 수복 중인 항목 — PassKeyViewModel (3번)
PassKeyViewModel.kt:5,17이 여전히core.datastore.UserProfileDataSource를 직접 주입받습니다. 본문이 건 게이트대로 #1944 / PR #1927 과 대조한 결과, 그 PR 이 바로 이 파일을 고쳐UserProfileDataSource를UserProfileCacheRepository와feature.setting.domain.PasskeyRepository로 갈아끼우고 MVI 로 전환합니다. 여기서 손대면 그 PR 과 충돌합니다. develop 게이트는PresentationLayerDependencyKonsistTest.kt:137의KNOWN_LAYER_BYPASSES등재로 초록입니다.제안
본문에서 2-a · 2-b · 3 을 빼고 1번만 남긴 범위로 재작성하는 것이 맞습니다. 그러면 남는 일은 timeletter 벡터 사본 3개(
ic_image·ic_file·default_profile)를core/ui로 수렴하고 소비처 5곳을CoreUiR로 바꾸는 것입니다.다만 착수 전에 확인할 것이 둘 있습니다. timeletter 는 다른 사람의 모듈이라 프로덕션 변경은 담당자 게이트에 걸립니다. 그리고
RecipientCard·RecipientListItem은 #1402(프로필 크기 시안 소수점 복원)와 #1470(contentDescription 리소스화)이 함께 보고 있어 같은 줄을 건드리면 겹칩니다.git log --grep '#635' origin/develop은 2건인데 둘 다 이 이슈를 처리한 커밋이 아닙니다.5c8663940(#1433)은 개명만 했고,a984a59ab(#1432)는 "남은 위반 1건(PassKeyViewModel)은 KNOWN_LAYER_BYPASSES 에 등재했다. 수복은 setting 담당의 #635 몫" 이라고 적었습니다.1hyok commentedon Sep 13, 2026
착수 결과입니다. 이 이슈로 제가 쓸 수 있는 프로덕션 코드가 0줄이라, 남은 범위를 두 이슈로 넘기고 관계를 걸었습니다. 기준 리비전은
origin/develop7d87aa3f4입니다.왜 0줄인가
앞선 코멘트에서 잰 변경면(벡터 사본 3개 삭제, 소비처 5곳 치환)은 파일이 전부
feature/timeletter입니다. 그 목록으로 PR JSON 을 만들어 가드를 직접 돌렸습니다.선례로 삼을 만한 것이
7c1c9e9e1(#640, 2026-08-28) 인데, 같은 성격의 mic 사본 이관을 제가 직접 했습니다. 다만 그 커밋은 가드 도입969ecaad4(#1889, 2026-09-04)보다 일주일 앞섭니다. 그 커밋의 파일 목록을 지금 가드에 먹이면 exit 1 이고 timeletter 3건과 mindrecord 1건을 지목합니다. 선례는 해도 된다는 근거가 아니라 가드가 없던 때라 통과했다는 근거입니다. 이 이슈 본문의 완료 조건 마지막 문장도 타인 담당 범위를 직접 수정하지 않는다입니다.issue-assignee-exempt라벨은 인계를 기다릴 수 없는 긴급 수선용이라, 렌더 보존 정리에는 맞지 않습니다.인계한 것
CoreUiR치환. 렌더 동일성 근거와 줄 번호를 본문에 실었습니다timeletter_접두사. 0824 코멘트로 이 이슈에 얹혀 있던 #1071 의 timeletter 몫입니다관계는
#635 blocked_by #2084,#635 blocked_by #2085,#2085 blocked_by #2084입니다. 후속 분리분이라 자식이 아니라blocked_by로 이었습니다.#2084 에는
blocked_by #714도 걸었습니다. PR #1365 가RecipientCard.kt와TimeLetterWriteScreen.kt를 이미 건드리는데, 그쪽이TimeletterScreenScreenshotTest골든을 물고 있어 먼저 들어가야 재생성이 한 번으로 끝납니다. 사본 정리는 렌더가 같아 골든에 영향이 없습니다. 확인해 보니 그 골든 4장은TimeletterScreen만 그리고 여기 나오는 자원 셋을 하나도 렌더하지 않습니다.#1613 과는
MediaBottomSheetContent의 소비처 2곳이 겹칩니다. #1613 이 먼저 들어오면 그 2곳은 자연히 없어지지만 #2084 가 비지는 않습니다.ic_file은TimeLetterWriteScreen.kt:567이 따로 쓰고default_profile은 #1613 범위 밖입니다.나머지 세 항목이 이 이슈 몫이 아닌 이유
setting 의 ReadOnlyField
ReadOnlyField는RecipientSetScreen.kt안의 private 함수(:134)이고 호출부도 같은 파일 두 곳(:86,:102)뿐입니다. 제 PR #2005 가 이 파일을 161줄 통째로 지웁니다. 중복을 수렴할 게 아니라 한쪽이 사라지는 건이라 이 이슈에서 손댈 것이 없습니다.관계 드롭다운 앵커
develop 에서
feature/setting/presentation의DropdownMenu는ReceiverRegisterScreen.kt:279한 곳뿐입니다.RecipientSetScreen.kt에는 드롭다운이 없고 "관계" 정적 라벨과ReadOnlyField만 있습니다(:74). 중복 쌍이 이미 해소됐고, 남은 그 파일도 #2005 가 지웁니다.PassKeyViewModel 의 DataStore 직접 의존
본문이 #1944·PR #1927 과 먼저 대조하라고 적어 둔 항목입니다. 대조했습니다. develop 의
PassKeyViewModel은core.datastore.UserProfileDataSource를 직접 주입받지만, PR #1927 의 head 는 그 자리를core.domain.repository.UserRepository로 바꾸고 파일을 131줄 다시 씁니다(PasskeyRepository·PasskeyRepositoryImpl신설 포함). 옛 경계를 이 이슈에서 따로 건드리면 그 PR 과 충돌만 납니다. #1927 소관으로 둡니다.덤: 0824 코멘트의
ic_list정정그 코멘트가
ic_list를 core 사본 4개 중 하나로 셌는데 다시 재 보니 사본이 아닙니다. timeletter 판은 28dp·viewport 28 의 스트로크 아이콘이고core/ui/.../core_ui_list.xml은 24dp·viewport 24 의 Material 채움 아이콘이라 형태가 다릅니다. 통일하면 시각 변경이고, 이 이슈 본문이 뷰 전환 아이콘의 크기·tint 통일을 제외 범위로 못박아 두었습니다. 그래서 #2084 에 넣지 않고 #2085 의 리네임 대상으로만 남겼습니다.같은 이유로 프리픽스 대상 수도 달라집니다. 0824 에 13개였던 미준수 드로어블이 지금 9개이고(
ic_mic와ic_align_*3개가 그사이 정리됐습니다) #2084 가 3개를 지우면 6개가 남습니다.이 이슈에 남는 것
제 담당인 core 쪽은 할 일이 없습니다. 세 자원이
core/ui에 이미 있고core:ui자신과 afternote·mindrecord·receiver 가 씁니다. #2084 와 #2085 가 닫히면 이 이슈도 닫힙니다.[-]core 중복 중 기존 렌더·동작을 보존하는 범위만 정리한다[/-][+]refactor(core): 동일 화살표 렌더링을 기존 ArrowIcon에 위임한다[/+]