98 KiB
98 KiB
20260331 하드코딩 텍스트 I18n.swift 통일 계획
작업 체크리스트
- 코드베이스 하드코딩 문구 탐색 전략 수립 (병렬 탐색 + 정적 검색)
I18n.swift와Localizable.xcstrings혼재 지점 식별- 하드코딩 문구 변경 대상 파일 인벤토리 산출 및 문서화
- 1차 전환: SwiftUI/View 레이어 하드코딩 문구를
I18n.*참조로 교체 - 2차 전환: ViewModel/Manager/Repository 레이어의 사용자 노출 문구를
I18n.*로 이관 - 3차 정리:
Localizable.xcstrings의존 경로 제거 및 회귀 점검 - 최종 검증: 빌드/테스트/수동 QA 완료 후 체크리스트 마감
커밋 전략 (권장)
- 권장 방식: 모듈별 커밋 (일괄 커밋 비권장)
- 이유:
- 변경량이 커서(현재 대상 330개) 일괄 커밋 시 리뷰/롤백/원인추적 비용이 급증함.
- 모듈별 커밋은 회귀가 발생해도 영향 범위를 모듈 단위로 제한할 수 있음.
- 충돌 해결 시에도 모듈 경계가 명확해 병합 리스크가 낮아짐.
- 권장 순서:
- 공통 선행 커밋:
I18n.swift공통 키/네임스페이스 토대 정리 - 모듈별 커밋: 각 모듈의 키 추가 + 호출부 교체 + 모듈 단위 검증
- 마무리 커밋:
Localizable.xcstrings의존 제거/정리 및 최종 검증 결과 반영
- 공통 선행 커밋:
수용 기준 (Acceptance Criteria)
- 사용자에게 노출되는 하드코딩 문자열이
SodaLive/Sources/**에 남아있지 않다. - 신규/기존 UI 문구 접근이
I18n.swift네임스페이스를 통해 일관되게 이루어진다. Localizable.xcstrings기반 직접 참조(LocalizedStringKey/String(localized:)/NSLocalizedString)가 제거되거나 예외 사유가 문서화된다.- 문자열 치환 후
SodaLive,SodaLive-devDebug 빌드가 성공한다. - 테스트 가능 범위(스킴 제약 감안)와 주요 화면 수동 QA 결과가 문서 하단 검증 기록에 누적된다.
탐지 기준 및 범위
- 범위:
SodaLive/Sources/**/*.swift - 기준:
- 문자열 리터럴 중 한글 포함(
"...가-힣...") 항목 - SwiftUI 하드코딩 패턴 (
Text("..."),Button("..."),placeholder: "...") - 사용자 노출 가능 메시지 패턴 (
errorMessage = "...",showToast(...),SodaDialog(...))
- 문자열 리터럴 중 한글 포함(
- 제외 원칙(구현 단계에서 최종 필터링): 로그/디버그 전용 문자열, URL/키/식별자/이벤트명 등 비노출 텍스트
현황 스냅샷
I18n.사용 파일: 93개 (grep: \bI18n\.)- String Catalog 계열 API 사용 파일: 21개 (
grep: NSLocalizedString|String(localized:)|LocalizedStringKey) - 하드코딩 후보 파일(자동 탐지 합집합): 360개
- 제외 확정: 비노출 문자열(로그/채널 prefix/API path/아이콘명/색상코드/Preview 샘플) 30개 파일
- 최종 변경 대상: 330개 파일
외부 레퍼런스 기반 전환 원칙 (librarian 반영)
LocalizedStringResource를 로컬라이즈 문자열 표현의 기본 타입으로 사용한다. (I18n.swiftaccessor 반환 타입 설계 시 우선 고려)- String Catalog(
.xcstrings)와 레거시 포맷은 병존 가능하므로, 모듈 단위 점진 이관 전략을 사용한다. - Swift 문자열 자동 추출 설정(
Use Compiler to Extract Swift Strings)을 점검해 누락 리스크를 줄인다. - 동적 키/런타임 생성 문자열은 자동 추출이 어려우므로, 수동 관리 대상(manual string)으로 별도 추적한다.
- 복수형(pluralization), 포맷 토큰(
%d,%@)은 이관 시 깨지기 쉬운 구간이므로 변환 검증 체크포인트를 별도 둔다. - 접근성 문자열(
accessibilityLabel,accessibilityHint)도 i18n 적용 범위에 포함한다.
주요 리스크 체크포인트
- 포맷 토큰/인자 순서 불일치로 인한 문구 깨짐 여부 검증
- plural 규칙 손실 여부 검증
- 동적 문자열 누락 여부 검증(수동 관리 목록 대조)
- 접근성 레이블/힌트 미전환 누락 검증
I18n 키 네이밍/네임스페이스 전략 (librarian 반영)
- 최상위 네임스페이스는 모듈 기준(
I18n.Live,I18n.Chat,I18n.MyPage)으로 고정한다. - 2차 네임스페이스는 화면/도메인 기준(
I18n.Live.Room,I18n.Chat.Talk)으로 확장한다. - 3차 네임스페이스는 컴포넌트/다이얼로그 기준(
I18n.Live.Room.Dialog)으로 분리한다. - 키 이름은 문구 원문이 아닌 역할 중심(
title,subtitle,confirmButton,emptyStateMessage)으로 작성한다. - 파라미터가 필요한 문구는 프로퍼티가 아닌 함수(
func rankTitle(_ count: Int) -> String)로 선언한다. - 중복 문구는 도메인 내부 중복 생성 대신
I18n.Common으로 승격해 재사용한다. - 대규모 이관 단계에서는 키 rename을 금지하고, 화면 단위 교체 + 화면 단위 QA 순서로 진행한다.
변경 대상 파일 요약
- 총 대상 파일 수: 330
- 상위 모듈 수: 21
| 모듈 | 파일 수 |
|---|---|
Audition |
13 |
Chat |
28 |
Content |
78 |
CustomView |
3 |
Dialog |
6 |
Explorer |
40 |
Follow |
1 |
Home |
9 |
IAP |
1 |
ImagePicker |
1 |
Live |
56 |
Main |
3 |
Message |
13 |
MyPage |
41 |
Notification |
2 |
Onboarding |
1 |
Report |
4 |
SearchChannel |
1 |
Settings |
15 |
UI |
6 |
User |
8 |
변경 대상 파일 전체 목록
Audition (13)
SodaLive/Sources/Audition/Applicant/ApplyMethodView.swiftSodaLive/Sources/Audition/Applicant/AuditionApplicantItemView.swiftSodaLive/Sources/Audition/Applicant/AuditionApplicantRecordingView.swiftSodaLive/Sources/Audition/Applicant/AuditionApplyView.swiftSodaLive/Sources/Audition/AuditionItemView.swiftSodaLive/Sources/Audition/AuditionView.swiftSodaLive/Sources/Audition/AuditionViewModel.swiftSodaLive/Sources/Audition/Detail/AuditionDetailView.swiftSodaLive/Sources/Audition/Detail/AuditionDetailViewModel.swiftSodaLive/Sources/Audition/Detail/AuditionSoundManager.swiftSodaLive/Sources/Audition/Role/AuditionDetailRoleItemView.swiftSodaLive/Sources/Audition/Role/AuditionRoleDetailView.swiftSodaLive/Sources/Audition/Role/AuditionRoleDetailViewModel.swift
Chat (28)
SodaLive/Sources/Chat/Character/CharacterItemView.swiftSodaLive/Sources/Chat/Character/CharacterSectionView.swiftSodaLive/Sources/Chat/Character/CharacterView.swiftSodaLive/Sources/Chat/Character/CharacterViewModel.swiftSodaLive/Sources/Chat/Character/Detail/CharacterDetailView.swiftSodaLive/Sources/Chat/Character/Detail/Gallery/CharacterDetailGalleryView.swiftSodaLive/Sources/Chat/Character/New/ViewModels/NewCharacterListViewModel.swiftSodaLive/Sources/Chat/Character/New/Views/NewCharacterListView.swiftSodaLive/Sources/Chat/Character/Recent/RecentCharacterItemView.swiftSodaLive/Sources/Chat/Character/Recent/RecentCharacterSectionView.swiftSodaLive/Sources/Chat/ChatTabView.swiftSodaLive/Sources/Chat/Original/Detail/OriginalWorkDetailHeaderView.swiftSodaLive/Sources/Chat/Original/Detail/OriginalWorkDetailView.swiftSodaLive/Sources/Chat/Original/Detail/OriginalWorkDetailViewModel.swiftSodaLive/Sources/Chat/Original/OriginalTabItemView.swiftSodaLive/Sources/Chat/Original/OriginalWorkViewModel.swiftSodaLive/Sources/Chat/Talk/Room/ChatRoomView.swiftSodaLive/Sources/Chat/Talk/Room/ChatRoomViewModel.swiftSodaLive/Sources/Chat/Talk/Room/Message/AiMessageItemView.swiftSodaLive/Sources/Chat/Talk/Room/Message/TypingIndicatorItemView.swiftSodaLive/Sources/Chat/Talk/Room/Message/UserMessageItemView.swiftSodaLive/Sources/Chat/Talk/Room/Quota/ChatQuotaNoticeItemView.swiftSodaLive/Sources/Chat/Talk/Room/Settings/ChatBgSelectionView.swiftSodaLive/Sources/Chat/Talk/Room/Settings/ChatBgSelectionViewModel.swiftSodaLive/Sources/Chat/Talk/Room/Settings/ChatSettingsView.swiftSodaLive/Sources/Chat/Talk/TalkItemView.swiftSodaLive/Sources/Chat/Talk/TalkView.swiftSodaLive/Sources/Chat/Talk/TalkViewModel.swift
Content (78)
Group 1 (1-10)
SodaLive/Sources/Content/All/ByTheme/ContentAllByThemeView.swiftSodaLive/Sources/Content/All/ByTheme/ContentAllByThemeViewModel.swiftSodaLive/Sources/Content/All/ContentAllView.swiftSodaLive/Sources/Content/All/ContentNewAllItemView.swiftSodaLive/Sources/Content/All/ContentNewAllView.swiftSodaLive/Sources/Content/All/ContentRankingAllView.swiftSodaLive/Sources/Content/All/ContentRankingAllViewModel.swiftSodaLive/Sources/Content/Category/ContentListCategoryView.swiftSodaLive/Sources/Content/ContentItemView.swiftSodaLive/Sources/Content/ContentListItemView.swift
Group 2 (11-20)
SodaLive/Sources/Content/ContentListView.swiftSodaLive/Sources/Content/ContentPlayManager.swiftSodaLive/Sources/Content/ContentRepository.swiftSodaLive/Sources/Content/Create/ContentCreateSelectThemeView.swiftSodaLive/Sources/Content/Create/ContentCreateSelectThemeViewModel.swiftSodaLive/Sources/Content/Create/ContentCreateView.swiftSodaLive/Sources/Content/Create/ContentCreateViewModel.swiftSodaLive/Sources/Content/Create/QuarterTimePickerView.swiftSodaLive/Sources/Content/Create/SelectDatePicker.swiftSodaLive/Sources/Content/Curation/ContentCurationView.swift
Group 3 (21-30)
SodaLive/Sources/Content/Curation/ContentCurationViewModel.swiftSodaLive/Sources/Content/Detail/AudioContentDeleteDialogView.swiftSodaLive/Sources/Content/Detail/AudioContentReportDialogView.swiftSodaLive/Sources/Content/Detail/Comment/AudioContentCommentItemView.swiftSodaLive/Sources/Content/Detail/Comment/AudioContentCommentListView.swiftSodaLive/Sources/Content/Detail/Comment/AudioContentCommentListViewModel.swiftSodaLive/Sources/Content/Detail/Comment/AudioContentListReplyView.swiftSodaLive/Sources/Content/Detail/Comment/AudioContentListReplyViewModel.swiftSodaLive/Sources/Content/Detail/Comment/ContentDetailCommentView.swiftSodaLive/Sources/Content/Detail/ContentDetailInfoLimitedEditionView.swift
Group 4 (31-40)
SodaLive/Sources/Content/Detail/ContentDetailInfoView.swiftSodaLive/Sources/Content/Detail/ContentDetailMenuView.swiftSodaLive/Sources/Content/Detail/ContentDetailMosaicView.swiftSodaLive/Sources/Content/Detail/ContentDetailOtherContentView.swiftSodaLive/Sources/Content/Detail/ContentDetailPlayView.swiftSodaLive/Sources/Content/Detail/ContentDetailPreviousNextContentButtonView.swiftSodaLive/Sources/Content/Detail/ContentDetailPurchaseButton.swiftSodaLive/Sources/Content/Detail/ContentDetailView.swiftSodaLive/Sources/Content/Detail/ContentDetailViewModel.swiftSodaLive/Sources/Content/Detail/ContentOrderConfirmDialogView.swift
Group 5 (41-50)
SodaLive/Sources/Content/Detail/ContentOrderDialogView.swiftSodaLive/Sources/Content/Detail/LiveRoomDonationDialogView.swiftSodaLive/Sources/Content/Main/Banner/ContentMainBannerViewModel.swiftSodaLive/Sources/Content/Main/V2/ContentMainContentThemeView.swiftSodaLive/Sources/Content/Modify/ContentModifyView.swiftSodaLive/Sources/Content/Modify/ContentModifyViewModel.swiftSodaLive/Sources/Content/Player/ContentPlayerView.swiftSodaLive/Sources/Content/Playlist/ContentPlaylistItemView.swiftSodaLive/Sources/Content/Playlist/ContentPlaylistListView.swiftSodaLive/Sources/Content/Playlist/ContentPlaylistListViewModel.swift
Group 6 (51-60)
SodaLive/Sources/Content/Playlist/Create/ContentPlaylistCreateView.swiftSodaLive/Sources/Content/Playlist/Create/ContentPlaylistCreateViewModel.swiftSodaLive/Sources/Content/Playlist/Create/PlaylistAddContentItemView.swiftSodaLive/Sources/Content/Playlist/Create/PlaylistAddContentView.swiftSodaLive/Sources/Content/Playlist/Create/PlaylistCreateContentView.swiftSodaLive/Sources/Content/Playlist/Detail/ContentPlaylistDetailView.swiftSodaLive/Sources/Content/Playlist/Detail/ContentPlaylistDetailViewModel.swiftSodaLive/Sources/Content/Playlist/Detail/PlaylistContentItemView.swiftSodaLive/Sources/Content/Playlist/Modify/ContentPlaylistModifyView.swiftSodaLive/Sources/Content/Playlist/Modify/ContentPlaylistModifyViewModel.swift
Group 7 (61-70)
SodaLive/Sources/Content/Series/Content/SeriesContentAllView.swiftSodaLive/Sources/Content/Series/Content/SeriesContentAllViewModel.swiftSodaLive/Sources/Content/Series/Content/SeriesContentListItemView.swiftSodaLive/Sources/Content/Series/DayOfWeekSeriesView.swiftSodaLive/Sources/Content/Series/Detail/SeriesDetailHomeView.swiftSodaLive/Sources/Content/Series/Detail/SeriesDetailIntroductionView.swiftSodaLive/Sources/Content/Series/Detail/SeriesDetailView.swiftSodaLive/Sources/Content/Series/Detail/SeriesDetailViewModel.swiftSodaLive/Sources/Content/Series/Main/ByGenre/SeriesMainByGenreViewModel.swiftSodaLive/Sources/Content/Series/Main/DayOfWeek/SeriesMainDayOfWeekView.swift
Group 8 (71-78)
SodaLive/Sources/Content/Series/Main/DayOfWeek/SeriesMainDayOfWeekViewModel.swiftSodaLive/Sources/Content/Series/Main/Home/SeriesMainHomeView.swiftSodaLive/Sources/Content/Series/Main/Home/SeriesMainHomeViewModel.swiftSodaLive/Sources/Content/Series/Main/SeriesMainItemView.swiftSodaLive/Sources/Content/Series/Main/SeriesMainView.swiftSodaLive/Sources/Content/Series/SeriesItemView.swiftSodaLive/Sources/Content/Series/SeriesListAllView.swiftSodaLive/Sources/Content/Series/SeriesListAllViewModel.swift
CustomView (3)
SodaLive/Sources/CustomView/ChatTextFieldView.swiftSodaLive/Sources/CustomView/ExpandableTextView.swiftSodaLive/Sources/CustomView/IconAndTitleToggleButton.swift
Dialog (6)
SodaLive/Sources/Dialog/ApplyAuditionCompleteDialog.swiftSodaLive/Sources/Dialog/CommunityPostPurchaseDialog.swiftSodaLive/Sources/Dialog/CreatorFollowNotifyDialog.swiftSodaLive/Sources/Dialog/LivePaymentDialog.swiftSodaLive/Sources/Dialog/LiveRoomPasswordDialog.swiftSodaLive/Sources/Dialog/MemberProfileDialog.swift
Explorer (40)
Group 1 (1-10)
SodaLive/Sources/Explorer/ExplorerSectionView.swiftSodaLive/Sources/Explorer/ExplorerView.swiftSodaLive/Sources/Explorer/ExplorerViewModel.swiftSodaLive/Sources/Explorer/Profile/ChannelDonation/ChannelDonationAllView.swiftSodaLive/Sources/Explorer/Profile/ChannelDonation/ChannelDonationItemView.swiftSodaLive/Sources/Explorer/Profile/CreatorCommunity/All/Comment/CreatorCommunityCommentItemView.swiftSodaLive/Sources/Explorer/Profile/CreatorCommunity/All/Comment/CreatorCommunityCommentListView.swiftSodaLive/Sources/Explorer/Profile/CreatorCommunity/All/Comment/CreatorCommunityCommentListViewModel.swiftSodaLive/Sources/Explorer/Profile/CreatorCommunity/All/Comment/CreatorCommunityCommentReplyView.swiftSodaLive/Sources/Explorer/Profile/CreatorCommunity/All/Comment/CreatorCommunityCommentReplyViewModel.swift
Group 2 (11-20)
SodaLive/Sources/Explorer/Profile/CreatorCommunity/All/Comment/CreatorCommunityCommentView.swiftSodaLive/Sources/Explorer/Profile/CreatorCommunity/All/CreatorCommunityAllItemLockView.swiftSodaLive/Sources/Explorer/Profile/CreatorCommunity/All/CreatorCommunityAllView.swiftSodaLive/Sources/Explorer/Profile/CreatorCommunity/All/CreatorCommunityAllViewModel.swiftSodaLive/Sources/Explorer/Profile/CreatorCommunity/All/CreatorCommunityMenuView.swiftSodaLive/Sources/Explorer/Profile/CreatorCommunity/All/Player/CreatorCommunityMediaPlayerManager.swiftSodaLive/Sources/Explorer/Profile/CreatorCommunity/CreatorCommunityItemView.swiftSodaLive/Sources/Explorer/Profile/CreatorCommunity/CreatorCommunityMoreItemView.swiftSodaLive/Sources/Explorer/Profile/CreatorCommunity/CreatorCommunityNoPostsItemView.swiftSodaLive/Sources/Explorer/Profile/CreatorCommunity/Modify/CreatorCommunityModifyView.swift
Group 3 (21-30)
SodaLive/Sources/Explorer/Profile/CreatorCommunity/Modify/CreatorCommunityModifyViewModel.swiftSodaLive/Sources/Explorer/Profile/CreatorCommunity/Write/CreatorCommunityRecordingVoiceView.swiftSodaLive/Sources/Explorer/Profile/CreatorCommunity/Write/CreatorCommunitySoundManager.swiftSodaLive/Sources/Explorer/Profile/CreatorCommunity/Write/CreatorCommunityWriteView.swiftSodaLive/Sources/Explorer/Profile/CreatorCommunity/Write/CreatorCommunityWriteViewModel.swiftSodaLive/Sources/Explorer/Profile/FanTalk/UserProfileFanTalkAllView.swiftSodaLive/Sources/Explorer/Profile/FanTalk/UserProfileFanTalkCheersItemView.swiftSodaLive/Sources/Explorer/Profile/FanTalk/UserProfileFanTalkView.swiftSodaLive/Sources/Explorer/Profile/FanTalk/UserProfileFanTalkViewModel.swiftSodaLive/Sources/Explorer/Profile/FollowerList/FollowerListView.swift
Group 4 (31-40)
SodaLive/Sources/Explorer/Profile/Series/UserProfileSeriesView.swiftSodaLive/Sources/Explorer/Profile/UserProfileActivitySummaryView.swiftSodaLive/Sources/Explorer/Profile/UserProfileContentView.swiftSodaLive/Sources/Explorer/Profile/UserProfileDonationAllView.swiftSodaLive/Sources/Explorer/Profile/UserProfileDonationAllViewModel.swiftSodaLive/Sources/Explorer/Profile/UserProfileDonationView.swiftSodaLive/Sources/Explorer/Profile/UserProfileIntroduceView.swiftSodaLive/Sources/Explorer/Profile/UserProfileLiveView.swiftSodaLive/Sources/Explorer/Profile/UserProfileView.swiftSodaLive/Sources/Explorer/Profile/UserProfileViewModel.swift
Follow (1)
SodaLive/Sources/Follow/FollowCreatorView.swift
Home (9)
Group 1 (1-9)
SodaLive/Sources/Home/HomeAuditionView.swiftSodaLive/Sources/Home/HomeCreatorRankingItemView.swiftSodaLive/Sources/Home/HomeLatestContentView.swiftSodaLive/Sources/Home/HomeLiveItemView.swiftSodaLive/Sources/Home/HomeTabView.swiftSodaLive/Sources/Home/HomeWeeklyChartItemView.swiftSodaLive/Sources/Home/HomeWeeklyChartView.swiftSodaLive/Sources/Home/RecommendChannel/RecommendChannelContentItemView.swiftSodaLive/Sources/Home/RecommendChannel/RecommendChannelItemView.swift
IAP (1)
SodaLive/Sources/IAP/StoreManager.swift
ImagePicker (1)
SodaLive/Sources/ImagePicker/ImagePicker.swift
Live (56)
Group 1 (1-10)
SodaLive/Sources/Live/Cancel/LiveCancelDialog.swiftSodaLive/Sources/Live/LatestFinishedLiveItemView.swiftSodaLive/Sources/Live/LiveReplayListView.swiftSodaLive/Sources/Live/LiveView.swiftSodaLive/Sources/Live/LiveViewModel.swiftSodaLive/Sources/Live/Now/All/LiveNowAllItemView.swiftSodaLive/Sources/Live/Now/All/LiveNowAllView.swiftSodaLive/Sources/Live/Now/LiveNowItemView.swiftSodaLive/Sources/Live/Now/SectionLiveNowView.swiftSodaLive/Sources/Live/RecommendChannel/SectionRecommendChannelView.swift
Group 2 (11-20)
SodaLive/Sources/Live/Reservation/All/LiveReservationAllItemView.swiftSodaLive/Sources/Live/Reservation/All/LiveReservationAllView.swiftSodaLive/Sources/Live/Reservation/Complete/LiveReservationCompleteView.swiftSodaLive/Sources/Live/Reservation/LiveReservationItemView.swiftSodaLive/Sources/Live/Reservation/MyLiveReservationItemView.swiftSodaLive/Sources/Live/Reservation/SectionLiveReservationView.swiftSodaLive/Sources/Live/Room/Chat/LiveRoomChatItemView.swiftSodaLive/Sources/Live/Room/Chat/LiveRoomDonationChatItemView.swiftSodaLive/Sources/Live/Room/Chat/LiveRoomHeartDonationChatItemView.swiftSodaLive/Sources/Live/Room/Chat/LiveRoomJoinChatItemView.swift
Group 3 (21-30)
SodaLive/Sources/Live/Room/Chat/LiveRoomRouletteDonationChatItemView.swiftSodaLive/Sources/Live/Room/Create/LiveRoomCreateView.swiftSodaLive/Sources/Live/Room/Create/Tag/LiveRoomCreateTagView.swiftSodaLive/Sources/Live/Room/Create/Tag/LiveRoomCreateTagViewModel.swiftSodaLive/Sources/Live/Room/Detail/LiveDetailView.swiftSodaLive/Sources/Live/Room/Detail/LiveDetailViewModel.swiftSodaLive/Sources/Live/Room/Dialog/LiveRoomDonationMessageDialog.swiftSodaLive/Sources/Live/Room/Dialog/LiveRoomDonationMessageItemView.swiftSodaLive/Sources/Live/Room/Dialog/LiveRoomDonationRankingDialog.swiftSodaLive/Sources/Live/Room/Dialog/LiveRoomDonationRankingItemView.swift
Group 4 (31-40)
SodaLive/Sources/Live/Room/Dialog/LiveRoomDonationRankingTotalCanView.swiftSodaLive/Sources/Live/Room/Dialog/LiveRoomHeartRankingDialog.swiftSodaLive/Sources/Live/Room/Dialog/LiveRoomHeartRankingItemView.swiftSodaLive/Sources/Live/Room/Dialog/LiveRoomInfoEditDialog.swiftSodaLive/Sources/Live/Room/Dialog/LiveRoomNoChattingDialogView.swiftSodaLive/Sources/Live/Room/Dialog/LiveRoomProfileDialog.swiftSodaLive/Sources/Live/Room/Dialog/LiveRoomProfileItemTitleView.swiftSodaLive/Sources/Live/Room/Dialog/LiveRoomProfilesDialogView.swiftSodaLive/Sources/Live/Room/Dialog/LiveRoomUserProfileDialogView.swiftSodaLive/Sources/Live/Room/Edit/LiveRoomEditView.swift
Group 5 (41-50)
SodaLive/Sources/Live/Room/Edit/LiveRoomEditViewModel.swiftSodaLive/Sources/Live/Room/LiveRoomViewModel.swiftSodaLive/Sources/Live/Room/Menu/LiveRoomMenuSelectView.swiftSodaLive/Sources/Live/Room/Menu/MenuSettingsView.swiftSodaLive/Sources/Live/Room/Menu/MenuSettingsViewModel.swiftSodaLive/Sources/Live/Room/Routlette/Config/RouletteSettingsOptionView.swiftSodaLive/Sources/Live/Room/Routlette/Config/RouletteSettingsView.swiftSodaLive/Sources/Live/Room/Routlette/Config/RouletteSettingsViewModel.swiftSodaLive/Sources/Live/Room/Routlette/RoulettePreviewDialog.swiftSodaLive/Sources/Live/Room/V2/Component/Button/LiveRoomNewChatView.swift
Group 6 (51-56)
SodaLive/Sources/Live/Room/V2/Component/View/LiveRoomInfoGuestView.swiftSodaLive/Sources/Live/Room/V2/Component/View/LiveRoomInfoHostView.swiftSodaLive/Sources/Live/Room/V2/Component/View/LiveRoomInputChatView.swiftSodaLive/Sources/Live/Room/V2/LiveRoomViewV2.swiftSodaLive/Sources/Live/SectionCommunityPostView.swiftSodaLive/Sources/Live/SectionLatestFinishedLiveView.swift
Main (3)
SodaLive/Sources/Main/EventPopupDialogView.swiftSodaLive/Sources/Main/Home/BottomTabView.swiftSodaLive/Sources/Main/Home/HomeView.swift
Message (13)
Group 1 (1-10)
SodaLive/Sources/Message/MessageFilterTabView.swiftSodaLive/Sources/Message/MessageView.swiftSodaLive/Sources/Message/Text/Detail/TextMessageDetailView.swiftSodaLive/Sources/Message/Text/Detail/TextMessageDetailViewModel.swiftSodaLive/Sources/Message/Text/SelectRecipient/SelectRecipientView.swiftSodaLive/Sources/Message/Text/SelectRecipient/SelectRecipientViewModel.swiftSodaLive/Sources/Message/Text/TextMessageView.swiftSodaLive/Sources/Message/Text/Write/TextMessageWriteView.swiftSodaLive/Sources/Message/Voice/SoundManager.swiftSodaLive/Sources/Message/Voice/VoiceMessageItemView.swift
Group 2 (11-13)
SodaLive/Sources/Message/Voice/VoiceMessageView.swiftSodaLive/Sources/Message/Voice/VoiceMessageViewModel.swiftSodaLive/Sources/Message/Voice/Write/VoiceMessageWriteView.swift
MyPage (41)
Group 1 (1-10)
SodaLive/Sources/MyPage/Auth/AuthButtonView.swiftSodaLive/Sources/MyPage/Block/BlockMemberListView.swiftSodaLive/Sources/MyPage/Block/BlockedMemberListItemView.swiftSodaLive/Sources/MyPage/Can/Charge/CanChargeView.swiftSodaLive/Sources/MyPage/Can/Charge/CanChargeViewModel.swiftSodaLive/Sources/MyPage/Can/Coupon/CanChargeCouponButtonView.swiftSodaLive/Sources/MyPage/Can/Coupon/CanCouponNoticeItemView.swiftSodaLive/Sources/MyPage/Can/Payment/CanPaymentView.swiftSodaLive/Sources/MyPage/Can/Payment/CanPaymentViewModel.swiftSodaLive/Sources/MyPage/Can/Payment/CanPgPaymentView.swift
Group 2 (11-20)
SodaLive/Sources/MyPage/Can/Payment/CanPgPaymentViewModel.swiftSodaLive/Sources/MyPage/Can/Payment/Temp/CanPaymentTempView.swiftSodaLive/Sources/MyPage/Can/Payment/Temp/CanPaymentTempViewModel.swiftSodaLive/Sources/MyPage/Can/Status/CanStatusView.swiftSodaLive/Sources/MyPage/Can/Status/CanStatusViewModel.swiftSodaLive/Sources/MyPage/Can/Status/CanUseStatusView.swiftSodaLive/Sources/MyPage/CanCardView.swiftSodaLive/Sources/MyPage/MyPageView.swiftSodaLive/Sources/MyPage/MyPageViewModel.swiftSodaLive/Sources/MyPage/OrderList/OrderListAllInnerView.swift
Group 3 (21-30)
SodaLive/Sources/MyPage/OrderList/OrderListAllView.swiftSodaLive/Sources/MyPage/OrderList/OrderListAllViewModel.swiftSodaLive/Sources/MyPage/OrderList/OrderListItemView.swiftSodaLive/Sources/MyPage/OrderList/OrderListView.swiftSodaLive/Sources/MyPage/Point/PointStatusView.swiftSodaLive/Sources/MyPage/Point/PointStatusViewModel.swiftSodaLive/Sources/MyPage/Point/Use/PointUseStatusView.swiftSodaLive/Sources/MyPage/Profile/Nickname/NicknameUpdateView.swiftSodaLive/Sources/MyPage/Profile/Nickname/NicknameUpdateViewModel.swiftSodaLive/Sources/MyPage/Profile/ProfileUpdateView.swift
Group 4 (31-40)
SodaLive/Sources/MyPage/Profile/Tag/MemberTagView.swiftSodaLive/Sources/MyPage/Profile/Tag/MemberTagViewModel.swiftSodaLive/Sources/MyPage/ReservationStatus/Cancel/LiveReservationCancelView.swiftSodaLive/Sources/MyPage/ReservationStatus/LiveReservationStatusItemView.swiftSodaLive/Sources/MyPage/ReservationStatus/LiveReservationStatusView.swiftSodaLive/Sources/MyPage/ReservationStatus/LiveReservationStatusViewModel.swiftSodaLive/Sources/MyPage/ReservationStatusView.swiftSodaLive/Sources/MyPage/ServiceCenter/FaqView.swiftSodaLive/Sources/MyPage/ServiceCenter/ServiceCenterButtonView.swiftSodaLive/Sources/MyPage/ServiceCenter/ServiceCenterView.swift
Group 5 (41-41)
SodaLive/Sources/MyPage/ServiceCenter/ServiceCenterViewModel.swift
Notification (2)
SodaLive/Sources/Notification/List/PushNotificationListItemView.swiftSodaLive/Sources/Notification/List/PushNotificationListView.swift
Onboarding (1)
SodaLive/Sources/Onboarding/OnboardingView.swift
Report (4)
SodaLive/Sources/Report/CheersReportDialogView.swiftSodaLive/Sources/Report/ProfileReportDialogView.swiftSodaLive/Sources/Report/ProfileReportMenuView.swiftSodaLive/Sources/Report/UserReportDialogView.swift
SearchChannel (1)
SodaLive/Sources/SearchChannel/SearchChannelView.swift
Settings (15)
Group 1 (1-10)
SodaLive/Sources/Settings/Content/ContentSettingsView.swiftSodaLive/Sources/Settings/Event/EventDetailView.swiftSodaLive/Sources/Settings/Event/EventListView.swiftSodaLive/Sources/Settings/Event/EventListViewModel.swiftSodaLive/Sources/Settings/Language/Models/LanguageOption.swiftSodaLive/Sources/Settings/Language/Views/LanguageSettingsView.swiftSodaLive/Sources/Settings/Notice/NoticeDetailView.swiftSodaLive/Sources/Settings/Notice/NoticeListView.swiftSodaLive/Sources/Settings/Notice/NoticeListViewModel.swiftSodaLive/Sources/Settings/Notification/NotificationSettingsDialog.swift
Group 2 (11-15)
SodaLive/Sources/Settings/Notification/NotificationSettingsView.swiftSodaLive/Sources/Settings/Notification/NotificationSettingsViewModel.swiftSodaLive/Sources/Settings/SettingsView.swiftSodaLive/Sources/Settings/SignOut/SignOutView.swiftSodaLive/Sources/Settings/Terms/TermsViewModel.swift
UI (6)
Group 1 (1-6)
SodaLive/Sources/UI/Component/SelectedButtonView.swiftSodaLive/Sources/UI/Component/SeriesDetailTabView.swiftSodaLive/Sources/UI/Component/SeriesItemBadgeView.swiftSodaLive/Sources/UI/Component/SeriesKeywordChipView.swiftSodaLive/Sources/UI/Component/SeriesListBigItemView.swiftSodaLive/Sources/UI/Component/SeriesListItemView.swift
User (8)
SodaLive/Sources/User/FindPassword/FindPasswordView.swiftSodaLive/Sources/User/FindPassword/FindPasswordViewModel.swiftSodaLive/Sources/User/Login/LoginView.swiftSodaLive/Sources/User/Login/LoginViewModel.swiftSodaLive/Sources/User/SignUp/SignUpView.swiftSodaLive/Sources/User/SignUp/SignUpViewModel.swiftSodaLive/Sources/User/UserTextField.swiftSodaLive/Sources/User/UserViewModel.swift
검증 기록
1차 계획 수립 (2026-03-31)
- 무엇/왜/어떻게:
- 무엇: 하드코딩 문구 전수 후보를 수집하고,
I18n.swift단일화 전환 계획 문서를 작성. - 왜: 현재
Localizable.xcstrings와I18n.swift혼재로 인해 유지보수/일관성 비용이 증가하고 있어 단일 경로 정리가 필요. - 어떻게: explore/librarian 병렬 탐색 + grep/ast-grep 직접 검색을 병행해 대상 파일 인벤토리를 생성.
- 무엇: 하드코딩 문구 전수 후보를 수집하고,
- 실행 명령/도구:
task(subagent_type="explore", ... )x3 (SwiftUI/UIKIt/혼재지점 탐색)task(subagent_type="librarian", ... )x2 (외부 레퍼런스/네이밍 전략)grep("\"[^\"]*[가-힣][^\"]*\"", include=*.swift, path=SodaLive/Sources)grep("\bI18n\.", include=*.swift, path=SodaLive/Sources)grep("NSLocalizedString\(|String\(localized:|LocalizedStringKey\(", include=*.swift, path=SodaLive/Sources)grep("Text\(\"[^\"]+\"\)", include=*.swift, path=SodaLive/Sources)ast_grep_search(pattern="Text(\"$TEXT\")", lang=swift, paths=[SodaLive/Sources])- 보조 집계 스크립트:
python3(후보 파일 dedupe/모듈별 집계)
- 결과:
- 하드코딩 후보 파일 360개 산출 및 모듈별 분류 완료.
rg명령은 환경에 설치되어 있지 않아(command not found) grep/ast-grep/파이썬 스캔으로 대체.
2차 계획 보강 (외부 i18n 레퍼런스 반영, 2026-03-31)
- 무엇/왜/어떻게:
- 무엇: iOS i18n 통합 시 주의점(포맷 토큰/복수형/접근성/동적 키)을 계획 문서에 체크포인트로 반영.
- 왜: 파일 목록만으로는 전환 품질을 담보하기 어려워, 이행 단계별 검증 기준을 사전에 고정할 필요가 있음.
- 어떻게:
librarian조사 결과를 기반으로 전환 원칙/리스크 항목을 문서 상단에 추가.
- 실행 명령/도구:
background_output(task_id="bg_535935bc")
- 결과:
- 외부 레퍼런스 기반 전환 원칙 6개 및 리스크 체크포인트 4개 반영 완료.
- 남은
librarian1개(네이밍 전략)는 완료 알림 수신 후 동일 문서에 추가 반영 예정.
3차 계획 보강 (I18n 키 네이밍 전략 반영, 2026-03-31)
- 무엇/왜/어떻게:
- 무엇:
I18n.swift단일화 시 키 충돌/확장 혼선을 줄이기 위한 네이밍/계층 전략을 반영. - 왜: 파일 전수 목록만으로는 장기 유지보수 규칙이 부족해, 이관 이후에도 일관성을 유지할 기준이 필요함.
- 어떻게:
librarian네이밍 전략 조사 결과를 정리해 문서 상단에 별도 전략 섹션으로 추가.
- 무엇:
- 실행 명령/도구:
background_output(task_id="bg_3bd27691")
- 결과:
- 모듈/화면/컴포넌트 3단계 네임스페이스와 역할 중심 키 네이밍 기준 반영 완료.
- 파라미터 문자열 함수화, 공통 키 승격(
I18n.Common), 키 rename 금지 원칙 반영 완료. - 중복 계획 문서(
20260331_I18n키네임스페이스구조계획.md)는 단일 계획 문서 원칙에 맞춰 제거.
4차 계획 보강 (Agora 후보 제외 반영, 2026-03-31)
- 무엇/왜/어떻게:
- 무엇:
SodaLive/Sources/Agora/Agora.swift를 변경 대상 목록에서 제외. - 왜: 해당 파일의 문자열은
DEBUG_LOG메시지/내부 채널 prefix 등 사용자 노출 텍스트가 아니라 i18n 전환 범위와 맞지 않음. - 어떻게: 변경 대상 섹션에서
Agora모듈과 파일 체크리스트 항목을 제거하고 요약 카운트를 재산정.
- 무엇:
- 실행 명령/도구:
grep("\"[^\"]*\"", include=Agora.swift, path=SodaLive/Sources/Agora)grep("Agora|총 대상 파일 수|상위 모듈 수", include=20260331_하드코딩텍스트_I18n통일계획.md, path=docs)python3(문서 내 체크리스트 파일 수/모듈 수 재계산)
- 결과:
- 변경 대상 파일 수
360 → 359, 상위 모듈 수25 → 24로 갱신. Agora.swift체크리스트 항목 제거 및 문서 내 제외 사유 명시 완료.
- 변경 대상 파일 수
5차 계획 보강 (비노출 문자열 전수 제외 반영, 2026-03-31)
- 무엇/왜/어떻게:
- 무엇: 변경 대상 359개를 재검토해 비노출 문자열만 가진 파일을 계획에서 제외.
- 왜: i18n 전환 대상은 사용자 노출 문구여야 하며, 내부 로그/채널명/API 경로/프리뷰 샘플 문자열은 범위 외이기 때문.
- 어떻게: 후보 전수 스캔 후 파일별 문자열 컨텍스트를 리뷰해 비노출 29개 파일을 추가 제외(이전 Agora 1개 제외 포함 총 30개 제외).
- 실행 명령/도구:
read(SodaLive/Sources/Common/TextViewWrapper.swift)grep("\"[^\"]*\"", include=Agora.swift, path=SodaLive/Sources/Agora)python3(후보 파일 컨텍스트 분류/제외 리스트 산출/문서 섹션 재생성)
- 결과:
Common/TextViewWrapper.swift제외(실사용 문자열 없음, Preview 샘플만 존재).- 내부 로그/채널 prefix/API path/아이콘명/색상코드/Preview 샘플만 가진 29개 파일 추가 제외 완료.
- 변경 대상 파일 수
359 → 330, 상위 모듈 수24 → 21으로 갱신. - 예외 유지:
Content/Detail/ContentDetailPurchaseButton.swift는"원으로"/"캔으로"사용자 노출 텍스트가 있어 유지.
6차 구현 (Audition 모듈 13개 i18n 전환, 2026-03-31)
- 무엇/왜/어떻게:
- 무엇: 변경 대상 목록의
Audition모듈 13개 파일을 전수 처리해 사용자 노출 하드코딩 문구를I18n.*참조로 교체. - 왜: Audition 영역의 UI/토스트/다이얼로그/오류 메시지가 하드코딩 상태여서 다국어 일관성이 깨지고 유지보수 비용이 높았기 때문.
- 어떻게: explore/librarian/Oracle 병렬 분석 +
grep/ast_grep_search직접 검증으로 누락 지점을 수집한 뒤,I18n.swift에I18n.Audition네임스페이스를 추가하고 호출부를 모듈 단위로 일괄 치환.
- 무엇: 변경 대상 목록의
- 실행 명령/도구:
task(subagent_type="explore", ...)x2 (bg_f58a087c,bg_02b03b28)task(subagent_type="librarian", ...)x2 (bg_f62866ac,bg_5cd8656b)task(subagent_type="oracle", ...)x1 (bg_81c2c04e)grep("\"[^\"]*[가-힣][^\"]*\"", include=*.swift, path=SodaLive/Sources/Audition)ast_grep_search(pattern="Text(\"$TEXT\")", lang=swift, paths=[SodaLive/Sources/Audition])xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug buildxcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug buildxcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" testxcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" test
- 결과:
- Audition 호출부 치환 완료 파일:
ApplyMethodView,AuditionApplicantRecordingView,AuditionApplyView,AuditionView,AuditionViewModel,AuditionDetailView,AuditionDetailViewModel,AuditionSoundManager,AuditionDetailRoleItemView,AuditionRoleDetailView,AuditionRoleDetailViewModel. I18n.swift에I18n.Audition(List/ApplyMethod/Apply/Recording/Detail/Vote/Sound) 키셋 추가 및 공통 오류는I18n.Common.commonError로 통합.- 녹음 자동 파일명(
voiceon_now_voice_*)이 사용자에게 그대로 보이던 문제를displayFileName처리(I18n.Audition.Apply.recordedVoiceFileName)로 보정. - Audition 모듈 하드코딩 한글 재검증 결과, 남은 문자열은 Preview 샘플/DEBUG_LOG/서버 메시지 분기 비교(비노출 로직)만 존재.
- 빌드 검증:
SodaLive,SodaLive-devDebug 빌드 모두 성공(** BUILD SUCCEEDED **). - 테스트 검증: 두 스킴 모두
Scheme ... is not currently configured for the test action.로 테스트 액션 미구성 확인(코드 실패 아님, 스킴 제약).
- Audition 호출부 치환 완료 파일:
7차 구현 (Chat 모듈 28개 i18n 전환, 2026-03-31)
- 무엇/왜/어떻게:
- 무엇: 변경 대상 목록의
Chat모듈 28개 파일을 전수 처리해 사용자 노출 하드코딩 문구를I18n.*참조로 교체. - 왜: Chat 영역에
String(localized:)직접 참조, 뷰 리터럴 문구, ViewModel 반복 오류 문구가 혼재되어 다국어 일관성이 깨져 있었기 때문. - 어떻게: explore/librarian/oracle +
grep/ast_grep_search/rg(미설치 확인) 병렬 탐색으로 런타임 노출 문자열을 추출하고,I18n.swift에I18n.Chat네임스페이스를 추가한 뒤 호출부를 치환.
- 무엇: 변경 대상 목록의
- 실행 명령/도구:
task(subagent_type="explore", ...)x2 (bg_c33457a5,bg_e543550a)task(subagent_type="librarian", ...)x2 (bg_47a108d5,bg_91c00954)task(subagent_type="oracle", ...)x1 (bg_a6465165)grep("\"[^\"]*[가-힣][^\"]*\"", include=*.swift, path=SodaLive/Sources/Chat)grep("String\\(localized:|LocalizedStringKey\\(|NSLocalizedString\\(", include=*.swift, path=SodaLive/Sources/Chat)ast_grep_search(pattern="Text(\"$TEXT\")", lang=swift, paths=[SodaLive/Sources/Chat])xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug buildxcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug buildxcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug build(Oracle 후속 보정 후 재검증)xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build(Oracle 후속 보정 후 재검증)xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" testxcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" test
- 결과:
I18n.swift에I18n.Chat(Auth/Character/Original/Talk/Room) 키셋 추가.- Chat 섹션 28개 파일 체크박스 전체 완료 처리.
- 실치환 24개 파일 + Preview/비노출 예외 4개 파일(샘플 데이터 등)로 전수 처리 완료.
- Chat 모듈의
String(localized:)직접 참조 제거 확인. - Oracle 후속 보정: Bootpay 입력값(
payload.pg/payload.method/payload.orderName) 고정값 복원,characterType.rawValue직접 출력 제거, 전송 실패 시error.localizedDescription사용자 노출 제거(I18n.Common.commonError), 최근 대화 헤더 trailing space 제거. - Chat 모듈 하드코딩 한글 재검증 결과, 남은 문자열은 Preview 샘플/SDK 입력값/비노출 분기 로직만 존재.
- 빌드 검증:
SodaLive,SodaLive-devDebug 빌드 모두 성공(** BUILD SUCCEEDED **). - 테스트 검증: 두 스킴 모두
Scheme ... is not currently configured for the test action.로 테스트 액션 미구성 확인(코드 실패 아님, 스킴 제약).
8차 구현 (ImagePicker/CustomView/IAP/Follow/Main/Dialog 15개 파일 i18n 전환, 2026-03-31)
- 무엇/왜/어떻게:
- 무엇: 변경 대상 목록 중
ImagePicker,CustomView,IAP,Follow,Main,Dialog모듈의 15개 파일을 처리해 사용자 노출 하드코딩 문구를I18n.*참조로 정리. - 왜: 주요 공통 UI(버튼/다이얼로그/토스트/탭 라벨/인증 안내)에 하드코딩 문자열이 남아 있어 모듈 간 다국어 일관성이 깨지는 상태였기 때문.
- 어떻게: explore/librarian 병렬 탐색 +
grep/ast_grep_search직접 검증으로 치환 대상을 확정하고,I18n.swift에 모듈별 네임스페이스(ImagePicker,CustomView,IAP,Follow,Main,Dialog)를 추가한 뒤 호출부를 교체.
- 무엇: 변경 대상 목록 중
- 실행 명령/도구:
task(subagent_type="explore", ...)x2 (bg_da05186f,bg_81c85d58)task(subagent_type="librarian", ...)x2 (bg_4b24d2ad,bg_d8b1253f)grep("\"[^\"]*[가-힣][^\"]*\"", include=*.swift, path=SodaLive/Sources/{ImagePicker,CustomView,IAP,Follow,Main,Dialog})(모듈별 개별 실행)grep("String\\(localized:|LocalizedStringKey\\(|NSLocalizedString\\(", include=*.swift, path=...)(모듈별 개별 실행)ast_grep_search(pattern="Text(\"$TEXT\")", lang=swift, paths=[...])lsp_diagnostics(filePath=변경 파일)xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug buildxcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug buildxcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" testxcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" test
- 결과:
- 호출부 치환 완료 파일:
ImagePicker.swift,ExpandableTextView.swift,StoreManager.swift,FollowCreatorView.swift,EventPopupDialogView.swift,BottomTabView.swift,HomeView.swift,ApplyAuditionCompleteDialog.swift,CommunityPostPurchaseDialog.swift,CreatorFollowNotifyDialog.swift,LivePaymentDialog.swift,LiveRoomPasswordDialog.swift,MemberProfileDialog.swift. - 점검만 수행(실치환 없음) 파일:
ChatTextFieldView.swift,IconAndTitleToggleButton.swift(Preview 샘플 문자열만 존재, 런타임 노출 문자열 없음). I18n.swift추가 키셋:I18n.ImagePicker,I18n.CustomView,I18n.IAP,I18n.Follow,I18n.Main(EventPopup/Tab/Auth),I18n.Dialog(ApplyAuditionComplete/CommunityPostPurchase/LivePayment/LiveRoomPassword/MemberProfile).Main/Home/HomeView.swift의 Bootpay 입력값(payload.pg,payload.method,payload.orderName)은 SDK 입력값 유지 원칙에 따라 비노출 고정값으로 유지.- 모듈 재검증 결과, 남은 한글 문자열은 Preview 샘플/
DEBUG_LOG/SDK 입력값(비노출)만 존재. - 빌드 검증:
SodaLive,SodaLive-devDebug 빌드 모두 성공(** BUILD SUCCEEDED **). - 테스트 검증: 두 스킴 모두
Scheme ... is not currently configured for the test action.로 테스트 액션 미구성 확인(코드 실패 아님, 스킴 제약).
- 호출부 치환 완료 파일:
9차 구현 (User/SearchChannel/Report/Notification 15개 파일 i18n 전환, 2026-03-31)
- 무엇/왜/어떻게:
- 무엇: 변경 대상 목록 중
User,SearchChannel,Report,Notification모듈의 15개 파일을 처리해 화면 문자열과 사용자 노출 에러 메시지를I18n.*로 통일했다. - 왜: 로그인/회원가입/비밀번호 재설정/채널 탐색/신고/알림 화면에 하드코딩 문구가 남아 있어 다국어 접근이 일관되지 않았기 때문이다.
- 어떻게: 관련 뷰와 뷰모델의 문자열을 교체하고,
I18n.swift에User,SignUp,FindPassword,SearchChannel,NotificationList,Report키를 보강했다.
- 무엇: 변경 대상 목록 중
- 실행 명령/도구:
rg -n 'Text\\(\"|SecureField\\(\"|TextField\\(\"|DetailNavigationBar\\(title: \\\"|String\\(localized: \\\"|errorMessage = \\\"|let reasons = \\[' SodaLive/Sources/User SodaLive/Sources/SearchChannel SodaLive/Sources/Report SodaLive/Sources/Notification -g '!**/generated/**'rg -n 'I18n\\.(User|SignUp|FindPassword|Login|SearchChannel|NotificationList|Report|Common)' SodaLive/Sources/User SodaLive/Sources/SearchChannel SodaLive/Sources/Report SodaLive/Sources/Notification -g '!**/generated/**'xcodebuild -project "SodaLive.xcodeproj" -scheme "SodaLive" -configuration Debug buildHOME=/tmp/codexhome xcodebuild -project "SodaLive.xcodeproj" -scheme "SodaLive" -configuration Debug -derivedDataPath /tmp/SodaLiveDerivedData -clonedSourcePackagesDirPath /tmp/SodaLiveSPM build
- 결과:
User8개 파일,SearchChannel1개 파일,Report4개 파일,Notification2개 파일 체크박스를 완료 처리했다.PushNotificationListItemView.swift의 시간 구분자도I18n.NotificationList.timestampSeparator로 이관했다.xcodebuild는 샌드박스 내 캐시/시뮬레이터 접근 제약과 이후 네트워크 차단으로 실패했다. 첫 시도는 workspace 인식 문제와 CoreSimulator 환경 오류가 섞여 있었고, 프로젝트 빌드로 전환한 뒤에는 Swift Package 의존성(objectbox-swift-spm)을 GitHub에서 가져오지 못해 중단되었다.- 따라서 이번 턴에서는 정적 치환과 문서 동기화까지 완료했고, 실제 컴파일 성공 여부는 네트워크가 허용되는 환경에서 추가 확인이 필요하다.
10차 계획 보강 (미완료 체크리스트 10개 그룹 재배치, 2026-03-31)
- 무엇/왜/어떻게:
- 무엇:
변경 대상 파일 전체 목록에서 미완료(- [ ]) 항목을 모듈 내부 기준으로 10개 단위 그룹으로 재배치. - 왜: 모듈 단위 처리 시 파일 수가 큰 구간(
Content,Live,MyPage등)의 작업 분할/추적 난이도를 낮추기 위해. - 어떻게: 기존 파일 순서를 유지한 채 미완료 항목 구간에
#### Group N헤더를 삽입해 10개 배치 단위로 분할.
- 무엇:
- 실행 명령/도구:
task(subagent_type="explore", ...)x2 (bg_7f47b389,bg_6b7f2435)grep("^- \\[ \\]SodaLive/Sources/.*\.swift$", include=20260331_하드코딩텍스트_I18n통일계획.md, path=docs)grep("^- \\[x\\]SodaLive/Sources/.*\.swift$", include=20260331_하드코딩텍스트_I18n통일계획.md, path=docs)grep("^#### Group [0-9]+ \\(", include=20260331_하드코딩텍스트_I18n통일계획.md, path=docs)read(docs/20260331_하드코딩텍스트_I18n통일계획.md)
- 결과:
- 그룹 재배치 적용 모듈:
Content(78),Explorer(40),Home(9),Live(56),Message(13),MyPage(41),Settings(15),UI(6). - 총
#### Group헤더 29개를 삽입해 미완료 항목을 10개 단위(마지막 그룹은 잔여 수)로 분할 완료. - 체크 상태 정합성 유지 확인: 미완료 258개, 완료 71개(기존 총합과 동일).
- 그룹 재배치 적용 모듈:
11차 구현 (UI 모듈 Group 1, 6개 파일 처리, 2026-03-31)
- 무엇/왜/어떻게:
- 무엇: 변경 대상 목록의
UI모듈 Group 1(6개 파일)을 점검하고, 런타임 사용자 노출 하드코딩 문구를I18n.*로 전환했다. - 왜: 공용 시리즈 카드 컴포넌트 중
SeriesListItemView.swift에 배지/회차 문구 하드코딩이 남아 있어I18n.swift단일 접근 원칙과 불일치했기 때문이다. - 어떻게: explore/librarian 병렬 탐색 결과를 바탕으로 6개 파일 전수 분류(런타임 노출 vs Preview 샘플) 후, 런타임 노출 문자열만
I18n.Series로 치환했다.
- 무엇: 변경 대상 목록의
- 실행 명령/도구:
task(subagent_type="explore", ...)x2 (bg_f61d6b84,bg_9364b9d5)task(subagent_type="librarian", ...)x2 (bg_83bba666,bg_3a95eb36)grep("\"[^\"]*[가-힣][^\"]*\"", include=*.swift, path=SodaLive/Sources/UI/Component)ast_grep_search(pattern="Text(\"$TEXT\")", lang=swift, paths=[SodaLive/Sources/UI/Component])lsp_diagnostics(filePath=SodaLive/Sources/UI/Component/SeriesListItemView.swift)xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug buildxcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug buildxcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" testxcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" test
- 결과:
- 실치환 파일:
SodaLive/Sources/UI/Component/SeriesListItemView.swift"신작"→I18n.Series.new"완결"→I18n.Series.complete"인기"→I18n.Series.popular"총 \(item.numberOfContent)화"→I18n.Series.totalEpisodes(item.numberOfContent)
- 점검만 수행(실치환 없음):
SelectedButtonView.swift,SeriesDetailTabView.swift,SeriesItemBadgeView.swift,SeriesKeywordChipView.swift,SeriesListBigItemView.swift(남은 한글은 Preview 샘플 데이터/Preview 라벨). - 빌드 검증:
SodaLive,SodaLive-devDebug 빌드 모두 성공(** BUILD SUCCEEDED **). - 테스트 검증: 두 스킴 모두
Scheme ... is not currently configured for the test action.로 테스트 액션 미구성 확인(코드 실패 아님, 스킴 제약). - LSP 진단:
No such module 'Kingfisher'1건 확인. 해당 모듈 해석은 SourceKit 인덱싱 환경 제약으로 재현되며, 동일 파일은 실제xcodebuild에서 컴파일 성공 확인.
- 실치환 파일:
12차 구현 (Settings 모듈 Group 1~2, 15개 파일 처리, 2026-03-31)
- 무엇/왜/어떻게:
- 무엇:
변경 대상 파일 전체 목록의SettingsGroup 1~2(15개 파일)에서 사용자 노출 하드코딩 문구를I18n.*참조로 전환하고 체크박스를 완료 처리. - 왜:
String(localized:)/하드코딩 리터럴/중복 오류 메시지가 혼재되어 Settings 모듈의 i18n 접근이 일관되지 않았기 때문. - 어떻게: explore/librarian 병렬 탐색 +
grep/ast_grep_search/rg직접 점검으로 대상 문자열을 확정한 뒤,I18n.swift의I18n.Settings하위 네임스페이스를 확장하고 호출부를 일괄 치환.
- 무엇:
- 실행 명령/도구:
task(subagent_type="explore", ...)x2 (bg_021e5287,bg_d081115c)task(subagent_type="librarian", ...)x2 (bg_3dc24f38,bg_938d63ee)grep("\"[^\"]*[가-힣][^\"]*\"", include=*.swift, path=SodaLive/Sources/Settings)grep("String\\(localized:|LocalizedStringKey\\(|NSLocalizedString\\(", include=*.swift, path=SodaLive/Sources/Settings)ast_grep_search(pattern="Text(\"$TEXT\")", lang=swift, paths=[SodaLive/Sources/Settings])bash: rg -n ... SodaLive/Sources/Settings(command not found확인)lsp_diagnostics(filePath=변경 파일 전체)xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug buildxcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug buildxcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" testxcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" test
- 결과:
I18n.swift에 Settings 전용 키셋 추가/확장:- 루트:
title,notificationSettings,languageSettings,contentViewSettings,termsOfService,privacyPolicy,appVersionInfo,logout,logoutAllDevices,signOut,companyInfo - 하위:
I18n.Settings.Content,I18n.Settings.Event,I18n.Settings.Language,I18n.Settings.Notice,I18n.Settings.Notification,I18n.Settings.SignOut(안내문/버튼/placeholder 추가)
- 루트:
- Settings Group 1~2 대상 15개 파일 치환 완료 및 문서 체크박스 15개 모두
- [x]반영. - ViewModel 공통 실패 문구 치환 완료:
EventListViewModel,NoticeListViewModel,NotificationSettingsViewModel,TermsViewModel→I18n.Common.commonError. - 재탐지 결과: Settings 모듈 내 한글 리터럴은
NoticeDetailViewPreview 샘플 2건("제목","<h1>콘텐츠</h1>")만 잔존. String(localized:)/NSLocalizedString/LocalizedStringKey직접 참조는 Settings 모듈에서 제거 확인.- 빌드 검증:
SodaLive,SodaLive-devDebug 빌드 모두 성공(** BUILD SUCCEEDED **). - 테스트 검증: 두 스킴 모두
Scheme ... is not currently configured for the test action.로 테스트 액션 미구성 확인(코드 실패 아님, 스킴 제약). - LSP 진단: SourceKit 단독 해석 환경에서 외부 모듈/심볼(
Kingfisher,RichText, 앱 내부 타입) 미해결 오류가 대량 보고되나, 동일 변경셋은xcodebuild실컴파일 통과로 검증 완료.
13차 구현 (Message 모듈 Group 1, 10개 파일 처리, 2026-03-31)
- 무엇/왜/어떻게:
- 무엇:
변경 대상 파일 전체 목록의MessageGroup 1(10개 파일)에서 사용자 노출 하드코딩 문구를I18n.*참조로 전환하고 체크박스를 완료 처리. - 왜: Message 탭/필터/상세/수신자 검색/텍스트 작성/녹음 오류 메시지에 하드코딩 문자열이 남아 있어
I18n.swift단일 접근 원칙과 불일치했기 때문. - 어떻게: explore/librarian 병렬 탐색 +
grep/ast_grep_search직접 점검으로 치환 범위를 확정하고,I18n.swift에I18n.Message네임스페이스를 추가한 뒤 Group 1 파일 호출부를 치환.
- 무엇:
- 실행 명령/도구:
task(subagent_type="explore", ...)x2 (bg_21246137,bg_bc8d6ca7)task(subagent_type="librarian", ...)x2 (bg_fdbe065d,bg_ce19e89c)grep("\"[^\"]*[가-힣][^\"]*\"", include=*.swift, path=SodaLive/Sources/Message)grep("NSLocalizedString\\(|String\\(localized:|LocalizedStringKey\\(", include=*.swift, path=SodaLive/Sources/Message)ast_grep_search(pattern="Text(\"$TEXT\")", lang=swift, paths=[SodaLive/Sources/Message])lsp_diagnostics(filePath=변경 파일 전체)xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug buildxcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug buildxcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" testxcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" test
- 결과:
I18n.swift에I18n.Message(Tab,FilterTab,Text.SelectRecipient,Text.Write,Text.Detail,Voice.Sound) 키셋 추가.- Oracle 후속 보정:
TextMessageDetailViewModel삭제 실패 fallback 키를keepFailed→deleteFailed로 수정,TextMessageWriteView수신자 라벨을 placeholder 키와 분리(I18n.Message.Text.Write.recipientLabel), Message 보관 관련 영문/일문 용어를Archive/Archived기준으로 통일. - 실치환 파일:
MessageFilterTabView.swift,MessageView.swift,TextMessageDetailView.swift,TextMessageDetailViewModel.swift,SelectRecipientView.swift,SelectRecipientViewModel.swift,TextMessageView.swift,TextMessageWriteView.swift,SoundManager.swift. - 점검만 수행(실치환 없음):
VoiceMessageItemView.swift(사용자 노출 한글 하드코딩 없음, 시간 표기00:00숫자 포맷만 존재). - Message Group 1 체크박스 10개
- [x]완료 반영. - Group 1 재탐지 결과 한글 리터럴은
TextMessageDetailView.swiftPreview 샘플 2건("누군가","테스터")만 잔존. TextMessageDetailView날짜 표기는 기존convertDateFormat경로를 유지해 현재 기기 locale 기준으로 출력됨(앱 언어 설정과 다른 locale일 경우 혼합 표기 가능성은 후속 정리 체크포인트로 유지).- Message 모듈 내
String(localized:)/NSLocalizedString/LocalizedStringKey직접 참조 0건 확인. - 빌드 검증:
SodaLive,SodaLive-devDebug 빌드 모두 성공(** BUILD SUCCEEDED **). - 테스트 검증: 두 스킴 모두
Scheme ... is not currently configured for the test action.로 테스트 액션 미구성 확인(코드 실패 아님, 스킴 제약). - LSP 진단: SourceKit 단독 해석 환경에서 외부 모듈/프로젝트 심볼 미해결 오류(
Kingfisher,MessageRepository등)가 보고되나, 동일 변경셋은xcodebuild실컴파일 통과로 검증 완료.
14차 구현 (Message 모듈 Group 2, 3개 파일 처리, 2026-03-31)
- 무엇/왜/어떻게:
- 무엇:
변경 대상 파일 전체 목록의MessageGroup 2(3개 파일)에서 사용자 노출 하드코딩 문구를I18n.*참조로 전환하고 체크박스를 완료 처리. - 왜: Voice 메시지 목록/보관 팝업/작성 화면/ViewModel 토스트 문구가 하드코딩 상태라 Message 모듈의 i18n 접근이 Group 1과 불일치했기 때문.
- 어떻게: explore/librarian 병렬 탐색 +
grep/ast_grep_search/rg직접 점검으로 대상 문자열을 확정하고,I18n.swift의I18n.Message.Voice네임스페이스를 확장한 뒤 호출부를 치환.
- 무엇:
- 실행 명령/도구:
task(subagent_type="explore", ...)x2 (bg_4384335d,bg_fe76ec47)task(subagent_type="librarian", ...)x2 (bg_da2d810f,bg_2416c47e)grep("\"[^\"]*[가-힣][^\"]*\"", include=*.swift, path=SodaLive/Sources/Message/Voice)grep("I18n\\.Message\\.", include=*.swift, path=SodaLive/Sources/Message)ast_grep_search(pattern="Text(\"$TEXT\")", lang=swift, paths=[SodaLive/Sources/Message/Voice])bash: rg -n ...(command not found확인)lsp_diagnostics(filePath=변경 파일 4개)xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug buildxcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug buildxcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" testxcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" test
- 결과:
I18n.swift에I18n.Message.Voice.SavePopup,I18n.Message.Voice.Write,I18n.Message.Voice.Toast키셋 추가.- 치환 완료 파일:
VoiceMessageView.swift,VoiceMessageViewModel.swift,VoiceMessageWriteView.swift. - Voice 보관 팝업(제목/본문/안내/버튼), 작성 화면(타이틀/수신자 라벨/다시 녹음/삭제), ViewModel 토스트/성공·실패 문구를
I18n.*참조로 교체. - 대상 3개 파일 재탐지 결과 한글 하드코딩 리터럴 0건 확인.
MessageGroup 2 체크박스 3개- [x]완료 반영.- 빌드 검증:
SodaLive,SodaLive-devDebug 빌드 모두 성공(** BUILD SUCCEEDED **). - 테스트 검증: 두 스킴 모두
Scheme ... is not currently configured for the test action.로 테스트 액션 미구성 확인(코드 실패 아님, 스킴 제약). - LSP 진단: SourceKit 단독 해석 환경에서 외부 모듈/프로젝트 심볼 미해결 오류(
Moya,I18n,LoadingView등)가 보고되나, 동일 변경셋은xcodebuild실컴파일 통과로 검증 완료.
15차 구현 (MyPage 모듈 Group 1~2, 20개 파일 처리, 2026-03-31)
- 무엇/왜/어떻게:
- 무엇:
변경 대상 파일 전체 목록의MyPageGroup 1~2(20개 파일)에서 사용자 노출 하드코딩 문구를I18n.*참조로 전환하고 체크박스를 완료 처리. - 왜: MyPage 영역에
String(localized:)직접 참조, 뷰 리터럴, ViewModel 반복 에러 문구가 혼재되어 모듈 단위 i18n 일관성이 깨져 있었기 때문. - 어떻게: explore/librarian 병렬 탐색 +
grep/ast_grep_search로 런타임 노출 문자열을 분류한 뒤,I18n.swift에I18n.MyPage네임스페이스를 추가하고 Group 1~2 호출부를 일괄 치환.
- 무엇:
- 실행 명령/도구:
task(subagent_type="explore", ...)x2 (bg_2804258c,bg_56679c82)task(subagent_type="librarian", ...)x2 (bg_82e0b3b7,bg_a708658e)grep("\b(String\(localized:|NSLocalizedString\(|LocalizedStringKey\()", include=*.swift, path=SodaLive/Sources/MyPage)grep("\"[^\"]*[가-힣][^\"]*\"", include=*.swift, path=SodaLive/Sources/MyPage)+ 대상 파일별 개별 재검증ast_grep_search(pattern="Text(\"$TEXT\")", lang=swift, paths=[SodaLive/Sources/MyPage])lsp_diagnostics(filePath=변경 파일 전체)xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug buildxcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug buildxcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" testxcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" test
- 결과:
I18n.swift에I18n.MyPage키셋 추가(Common,Auth,Block,Can,Can.Payment,Main,Category).- 치환 완료 파일:
AuthButtonView,BlockMemberListView,BlockedMemberListItemView,CanChargeView,CanChargeViewModel,CanChargeCouponButtonView,CanPaymentView,CanPaymentViewModel,CanPgPaymentView,CanPgPaymentViewModel,CanPaymentTempView,CanPaymentTempViewModel,CanStatusView,CanStatusViewModel,CanCardView,MyPageView,MyPageViewModel,OrderListAllInnerView. - 점검만 수행(실치환 없음):
CanCouponNoticeItemView.swift,CanUseStatusView.swift(런타임 노출 하드코딩 없음, Preview/불릿/데이터 바인딩만 존재). MyPageView의CategoryButtonItem은LocalizedStringResource→String으로 조정해I18n.*문자열 접근을 통일.- 반복 실패 문구는
I18n.Common.commonError로 통합했고, 본인인증 장문 오류는I18n.MyPage.Auth.verificationErrorWithSupport로 분리. - Group 1~2 체크박스 20개
- [x]반영 완료. - 대상 파일 재탐지 결과, 남은 한글 리터럴은 Preview 샘플/SDK 전달 상수(
payload.pg,payload.method,payload.orderName, PG method rawValue)만 존재. - LSP 진단: SourceKit 단독 해석 환경에서 외부 모듈/프로젝트 심볼 미해결 오류(
Bootpay,Kingfisher,I18n등)가 보고되나, 동일 변경셋은xcodebuild실컴파일 통과로 검증 완료. - 빌드 검증:
SodaLive,SodaLive-devDebug 빌드 모두 성공(** BUILD SUCCEEDED **). - 테스트 검증: 두 스킴 모두
Scheme ... is not currently configured for the test action.로 테스트 액션 미구성 확인(코드 실패 아님, 스킴 제약).
16차 구현 (MyPage 모듈 Group 3~5, 21개 파일 처리, 2026-03-31)
- 무엇/왜/어떻게:
- 무엇:
변경 대상 파일 전체 목록의MyPageGroup 3~5(21개 파일)에서 사용자 노출 하드코딩 문구를I18n.*참조로 전환하고 체크박스를 완료 처리. - 왜: OrderList/Point/Profile/ReservationStatus/ServiceCenter 구간에 뷰 리터럴과 ViewModel 공통 오류 문구가 남아 있어
I18n.swift단일 접근 원칙과 불일치했기 때문. - 어떻게: explore/librarian 병렬 탐색 +
grep/ast_grep_search직접 검증으로 런타임 노출 문자열만 추출한 뒤,I18n.swift에I18n.MyPage하위 네임스페이스(OrderList,Point,Nickname,Profile,ReservationStatus,Reservation,ServiceCenter)를 추가하고 호출부를 일괄 치환.
- 무엇:
- 실행 명령/도구:
task(subagent_type="explore", ...)x2 (bg_df9bea1f,bg_a7a90096)task(subagent_type="librarian", ...)x2 (bg_a3ce40e4,bg_0d380792)background_output(task_id=...)x4 (위 4개 task 결과 수집)grep("\"[^\"]*[가-힣][^\"]*\"", include=*.swift, path=SodaLive/Sources/MyPage/**)grep("String\\(localized:|NSLocalizedString\\(|LocalizedStringKey\\(", include=*.swift, path=SodaLive/Sources/MyPage)ast_grep_search(pattern="Text(\"$TEXT\")", lang=swift, paths=[SodaLive/Sources/MyPage/{OrderList,Point,Profile,ReservationStatus,ServiceCenter}])bash: rg -n ...(command not found확인)lsp_diagnostics(filePath=변경 파일 전체)xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug buildxcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug buildxcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" testxcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" test
- 결과:
I18n.swift에I18n.MyPageGroup 3~5 대응 키셋 추가/확장 완료.- 추가:
OrderList,Point,Nickname,Profile,ReservationStatus,Reservation.LiveStatus,Reservation.Cancel,ServiceCenter. - 재사용: 공통 실패 문구는
I18n.Common.commonError로 통합.
- 추가:
- 치환 완료 파일(20개):
OrderListAllView,OrderListAllViewModel,OrderListItemView,OrderListViewPointStatusView,PointStatusViewModelNicknameUpdateView,NicknameUpdateViewModel,ProfileUpdateViewMemberTagView,MemberTagViewModelLiveReservationCancelView,LiveReservationStatusItemView,LiveReservationStatusView,LiveReservationStatusViewModel,ReservationStatusViewFaqView,ServiceCenterButtonView,ServiceCenterView,ServiceCenterViewModel
- 점검만 수행(실치환 없음, 체크 완료):
PointUseStatusView.swift(런타임 노출 하드코딩 문자열 없음). - 대상 재탐지 결과, Group 3~5 영역의 잔여 한글 리터럴은 Preview 샘플 데이터(
"여행","질문1"등)만 존재. - Group 3~5 체크박스 21개
- [x]완료 반영. - LSP 진단: SourceKit 단독 해석 환경에서 외부 모듈/프로젝트 심볼 미해결 오류(
Kingfisher,RichText,AppState등)가 보고되나, 동일 변경셋은xcodebuild실컴파일 통과로 검증 완료. - 빌드 검증:
SodaLive,SodaLive-devDebug 빌드 모두 성공(** BUILD SUCCEEDED **). - 테스트 검증: 두 스킴 모두
Scheme ... is not currently configured for the test action.로 테스트 액션 미구성 확인(코드 실패 아님, 스킴 제약).
17차 구현 (Home 모듈 Group 1, 9개 파일 처리, 2026-04-01)
- 무엇/왜/어떻게:
- 무엇:
변경 대상 파일 전체 목록의HomeGroup 1(9개 파일)을 전수 점검하고, 런타임 사용자 노출 하드코딩 문구를I18n.*참조로 전환했다. - 왜: 홈 탭의 섹션 헤더/버튼/본인인증 다이얼로그/오류 문구가 하드코딩 상태여서 모듈 간 i18n 접근 방식이 일관되지 않았기 때문이다.
- 어떻게: explore/librarian 병렬 탐색 +
grep/ast_grep_search/read직접 검증으로 런타임 문자열과 Preview 샘플 문자열을 분리한 뒤,I18n.swift에I18n.Home네임스페이스를 추가하고 호출부를 치환했다.
- 무엇:
- 실행 명령/도구:
task(subagent_type="explore", ...)x2 (bg_7a1064e4,bg_2856a903)task(subagent_type="librarian", ...)x1 (bg_2220e841)background_output(task_id=...)x3 (위 3개 task 결과 수집)grep("\"[^\"]*[가-힣][^\"]*\"", include=*.swift, path=SodaLive/Sources/Home)grep("I18n\\.|String\\(localized:|NSLocalizedString\\(|LocalizedStringKey\\(", include=*.swift, path=SodaLive/Sources/Home)ast_grep_search(pattern="Text(\"$TEXT\")", lang=swift, paths=[SodaLive/Sources/Home])bash: rg -n ...(command not found확인)lsp_diagnostics(filePath=변경 파일 전체)xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug buildxcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug buildxcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" testxcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" test
- 결과:
I18n.swift에I18n.Home키셋 추가:liveNowSectionTitle,popularCreatorSectionTitle,onlyOnVoiceOnSectionTitle,popularCharacterChatSectionTitle,recommendChannelSectionTitle,freeContentSectionTitle,pointRentalContentSectionTitle,recommendContentSectionTitle,weeklyChartSectionTitle,RecommendChannel.contentLabel.- 치환 완료 파일(실치환 5개):
HomeCreatorRankingItemView.swift,HomeLatestContentView.swift,HomeTabView.swift,HomeWeeklyChartView.swift,RecommendChannelItemView.swift. - 점검만 수행(실치환 없음 4개):
HomeAuditionView.swift,HomeLiveItemView.swift,HomeWeeklyChartItemView.swift,RecommendChannelContentItemView.swift(잔여 한글은 Preview 샘플 데이터만 존재). - 공통 키 재사용 정리:
I18n.Common.viewAll,I18n.Common.latestContent,I18n.Settings.companyInfo,I18n.Chat.Auth.*,I18n.LiveRoom.follow/following적용. - Oracle 후속 보정: 홈 FAB 버튼 문구를 제목형 키(
uploadTitle)에서 CTA 전용 키(I18n.CreateContent.uploadAction)로 분리해 영문/일문 의미를 버튼 행동과 일치시킴. - Home Group 1 체크박스 9개
- [x]완료 반영.
18차 구현 (Live 모듈 Group 1~2, 20개 파일 처리, 2026-04-01)
- 무엇/왜/어떻게:
- 무엇:
변경 대상 파일 전체 목록의LiveGroup 1~2(20개 파일)를 전수 점검하고, 런타임 사용자 노출 하드코딩 문구를I18n.*로 전환했다. - 왜: Live 메인/실시간 목록/예약/채팅 아이템에 하드코딩 문구가 남아 있어 모듈 간 i18n 접근이 불일치했고, 동일 의미 문구가 ViewModel에 중복되어 유지보수 비용이 높았기 때문이다.
- 어떻게: explore/librarian 병렬 탐색 +
grep/ast_grep_search직접 점검으로 대상 문자열을 분류한 뒤,I18n.swift에 Live 전용 키셋(LiveMain,LiveNow,LiveReservation,LiveChat)을 추가하고 호출부를 치환했다. 기존 공통 키(I18n.Common,I18n.MemberChannel,I18n.Main.Auth)는 재사용했다.
- 무엇:
- 실행 명령/도구:
task(subagent_type="explore", ...)x2 (bg_d093725e,bg_d4acf3b2)task(subagent_type="librarian", ...)x2 (bg_cfe29077,bg_b4c29632)background_output(task_id=...)x4 (위 4개 task 결과 수집)grep("\"[^\"]*[가-힣][^\"]*\"", include=대상파일, path=SodaLive/Sources/Live/**)grep("String\\(localized:|LocalizedStringKey\\(|NSLocalizedString\\(", include=*.swift, path=SodaLive/Sources/Live)ast_grep_search(pattern="Text(\"$TEXT\")", lang=swift, paths=[SodaLive/Sources/Live])bash: rg -n ...(command not found확인)lsp_diagnostics(filePath=변경 파일 전체)xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug buildxcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug buildxcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" testxcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" test
- 결과:
I18n.swift추가/확장 키셋:- 신규:
I18n.LiveMain,I18n.LiveReservation(Section/All/Item/Complete),I18n.LiveChat - 확장:
I18n.LiveNow(sectionTitle/emptyStateMessage/refreshButton/followingChannelsTitle/liveBadge/moreButton),I18n.LiveCancel(title/cancelButton/confirmButton),I18n.MemberChannel.alreadyEndedLive
- 신규:
- 치환 완료 파일(실치환 18개):
LiveCancelDialog.swift,LiveReplayListView.swift,LiveView.swift,LiveViewModel.swiftLiveNowAllView.swift,LiveNowItemView.swift,SectionLiveNowView.swift,SectionRecommendChannelView.swiftLiveReservationAllItemView.swift,LiveReservationAllView.swift,LiveReservationCompleteView.swift,LiveReservationItemView.swift,MyLiveReservationItemView.swift,SectionLiveReservationView.swiftLiveRoomChatItemView.swift,LiveRoomDonationChatItemView.swift,LiveRoomHeartDonationChatItemView.swift,LiveRoomJoinChatItemView.swift
- 점검만 수행(실치환 없음, 체크 완료 2개):
LatestFinishedLiveItemView.swift(런타임 고정 문구 없음, 표시값은 API 기반)LiveNowAllItemView.swift(런타임 문구가 기존I18n참조 또는 데이터 바인딩)
- Group 1~2 체크박스 20개
- [x]반영 완료. - 대상 재탐지 결과, 잔여 한글 리터럴은 Preview 샘플/SDK 입력값(
payload.pg,payload.method,payload.orderName)/서버 메시지 분기 비교(message.contains("종료"))만 존재. - 빌드 검증:
SodaLiveDebug 빌드 성공(** BUILD SUCCEEDED **).SodaLive-devDebug 빌드는 병렬 실행 시build.dblock으로 1회 실패 후, 단독 재실행에서 성공(** BUILD SUCCEEDED **).
- 테스트 검증: 두 스킴 모두
Scheme ... is not currently configured for the test action.로 테스트 액션 미구성 확인(코드 실패 아님, 스킴 제약). - LSP 진단: SourceKit 단독 해석 환경에서 외부 모듈/프로젝트 심볼 미해결 오류(
RefreshableScrollView,Kingfisher,AppState등)가 보고되나, 동일 변경셋은xcodebuild실컴파일 통과로 검증 완료.
19차 구현 (Live 모듈 Group 3~6 마감 보정, 2026-04-01)
- 무엇/왜/어떻게:
- 무엇: Live Group 3~6 마감 단계에서 남아 있던 컴파일 오류(
I18n경로 오참조)를 수정하고, 체크리스트와 검증 기록을 최신화했다. - 왜: 기존 치환 반영 이후
LiveRoomViewModel.swift의 일부 i18n 경로가 실제I18n.swift네임스페이스와 불일치해 빌드를 막고 있었기 때문이다. - 어떻게: 오류 라인(1919, 1964, 1986)을
I18n.swift정의와 대조해 정확한 경로로 교정한 뒤,SodaLive/SodaLive-devDebug 빌드와 test 액션 상태를 재검증했다.
- 무엇: Live Group 3~6 마감 단계에서 남아 있던 컴파일 오류(
- 실행 명령/도구:
xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug buildxcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug buildxcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" testxcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" testread/grep로LiveRoomViewModel.swift,I18n.swift, 계획 문서 대조
- 결과:
LiveRoomViewModel.swift경로 교정 완료:I18n.LiveRoomPassword.MemberProfile.reportProfile→I18n.Dialog.MemberProfile.reportProfileI18n.ChannelManagement.userBlocked→I18n.MemberChannel.userBlockedI18n.ChannelManagement.userUnblocked→I18n.MemberChannel.userUnblocked
- Live Group 3~6 체크박스 36개
- [x]반영 완료. - 빌드 검증:
SodaLive,SodaLive-devDebug 빌드 모두 오류 없이 완료(경고만 존재). - 테스트 검증: 두 스킴 모두
Scheme ... is not currently configured for the test action.로 test action 미구성 확인(코드 실패 아님, 스킴 제약). - LSP 진단 참고: 단일 파일 진단 시
No such module 'Moya'가 보고되나, SourceKit 단독 해석 한계이며 실제xcodebuild컴파일은 통과했다.
20차 구현 (Explorer 모듈 Group 1~4, 40개 파일 처리, 2026-04-01)
- 무엇/왜/어떻게:
- 무엇:
변경 대상 파일 전체 목록의ExplorerGroup 1~4(40개 파일)를 전수 점검하고, 런타임 사용자 노출 하드코딩 문구를I18n.*로 치환했다. - 왜: Explorer(탐색/프로필/팬톡/크리에이터 커뮤니티) 구간에서 하드코딩 문구와 직접 로컬라이제이션 API(
String(localized:))가 혼재되어I18n.swift단일 접근 원칙과 충돌했기 때문이다. - 어떻게: Explorer 대상 파일을 선별해 하드코딩/직접 API 사용을 재탐지하고,
I18n.swift에I18n.Explorer네임스페이스를 추가한 뒤 호출부를 치환했다. 공통 문구는I18n.Common으로 통합 재사용했다.
- 무엇:
- 실행 명령/도구:
lsp_diagnostics(filePath=SodaLive/Sources/Explorer, extension=.swift, severity=all)lsp_diagnostics(filePath=SodaLive/Sources/I18n/I18n.swift, severity=all)grep("String\\(localized:|NSLocalizedString\\(|LocalizedStringKey\\(", include=*.swift, path=SodaLive/Sources/Explorer)grep("\"[^\"]*[가-힣][^\"]*\"", include=*.swift, path=SodaLive/Sources/Explorer)git diff --name-only -- "SodaLive/Sources/Explorer"xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug buildxcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug buildxcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" testxcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" test
- 결과:
- Explorer Group 1~4 체크박스 40개
- [x]완료 상태를 유지/확인했다. - 실치환 파일은 총 37개였고, 나머지 3개(
ChannelDonationAllView.swift등)는 런타임 사용자 노출 하드코딩이 없어 점검만 수행했다. String(localized:)/NSLocalizedString/LocalizedStringKey의 Explorer 직접 사용은 0건으로 확인했다.- Explorer 잔여 한글 리터럴은 Preview 샘플 데이터 및
DEBUG_LOG메시지(비사용자 노출)만 존재함을 재확인했다. - 빌드 검증:
SodaLive,SodaLive-devDebug 빌드 모두 성공(** BUILD SUCCEEDED **). - 테스트 검증: 두 스킴 모두
Scheme ... is not currently configured for the test action.로 test action 미구성 확인(코드 실패 아님, 스킴 제약). - LSP 진단 참고: SourceKit 단독 해석에서 외부 모듈/프로젝트 심볼(
Moya,Kingfisher,I18n등) 미해결 오류가 보고되었으나, 동일 변경셋은xcodebuild실컴파일 통과로 검증했다.
- Explorer Group 1~4 체크박스 40개
21차 구현 (Content 모듈 Group 1, 10개 파일 처리, 2026-04-01)
- 무엇/왜/어떻게:
- 무엇:
변경 대상 파일 전체 목록의ContentGroup 1(10개 파일)을 전수 점검하고, 런타임 사용자 노출 하드코딩 문구를I18n.*참조로 전환했다. - 왜: 콘텐츠 전체/신규/랭킹/테마별 목록 구간에 하드코딩 문자열,
String(localized:)직접 참조, ViewModel 공통 오류 문구가 혼재되어I18n.swift단일 접근 원칙과 불일치했기 때문이다. - 어떻게: explore/librarian 병렬 탐색 +
grep/ast_grep_search/lsp_symbols직접 점검으로 치환 대상을 확정하고,I18n.swift에I18n.Content네임스페이스를 추가한 뒤 Group 1 호출부를 교체했다.
- 무엇:
- 실행 명령/도구:
task(subagent_type="explore", ...)x2 (bg_65648347,bg_d4b726f6)task(subagent_type="librarian", ...)x2 (bg_c8e277d6,bg_a66e0329)background_output(task_id=...)x4 (위 4개 task 결과 수집)grep("\"[^\"]*[가-힣][^\"]*\"", include=Group1 대상 파일)grep("String\\(localized:|NSLocalizedString\\(|LocalizedStringKey\\(", include=Group1 대상 파일)ast_grep_search(pattern="Text(\"$TEXT\")", lang=swift, paths=[SodaLive/Sources/Content])lsp_symbols(filePath=I18n.swift, scope=document, query=Content)lsp_diagnostics(filePath=변경 파일 전체)xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug buildxcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug buildxcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" testxcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" test
- 결과:
I18n.swift에I18n.Content키셋 추가:All(title/freeTitle/pointRentalTitle)New(title/freeTitle/recentTwoWeeksNotice)Ranking(title/weeklyUpdateNotice)Sort(newest/popularity/priceHigh/priceLow)Count(totalPrefix/countUnit)Status(owned/rented/soldOut)
- 치환 완료 파일(실치환 9개):
ContentAllByThemeView.swift,ContentAllByThemeViewModel.swift,ContentAllView.swift,ContentNewAllItemView.swift,ContentNewAllView.swift,ContentRankingAllView.swift,ContentRankingAllViewModel.swift,ContentListCategoryView.swift,ContentListItemView.swift
- 점검만 수행(실치환 없음, 체크 완료 1개):
ContentItemView.swift(런타임 하드코딩 문구 없음, Preview 샘플 문자열만 존재)
- Group 1 체크박스 10개
- [x]완료 반영. - Group 1 재탐지 결과, 남은 한글 리터럴은
ContentRankingAllViewModel.swift의 API 정렬 파라미터 기본값("매출")과 Preview 샘플(ContentItemView.swift,ContentListItemView.swift)만 존재. ContentAllView.swift의String(localized:)직접 참조(내비게이션 타이틀)를I18n.Content.All.*로 전환해 호출 경로를 통일.- LSP 진단: SourceKit 단독 해석 환경에서 외부 모듈/프로젝트 심볼 미해결 오류(
Kingfisher,BaseView,I18n등)가 보고되나, 동일 변경셋은xcodebuild실컴파일 통과로 검증 완료. - 빌드 검증:
SodaLive,SodaLive-devDebug 빌드 모두 성공(** BUILD SUCCEEDED **). - 테스트 검증: 두 스킴 모두
Scheme ... is not currently configured for the test action.로 test action 미구성 확인(코드 실패 아님, 스킴 제약).
22차 구현 (Content 모듈 Group 2, 10개 파일 처리, 2026-04-01)
- 무엇/왜/어떻게:
- 무엇:
변경 대상 파일 전체 목록의ContentGroup 2(11~20, 10개 파일)를 전수 점검하고, 런타임 사용자 노출 하드코딩 문구를I18n.*참조로 전환했다. - 왜: 콘텐츠 목록/큐레이션/생성 플로우 구간에서 하드코딩 문자열이 남아 있어
I18n.swift단일 접근 원칙과 불일치했기 때문이다. - 어떻게: Group 2 대상 파일을 병렬 탐색한 뒤(
explore/librarian),grep/ast_grep_search/lsp_diagnostics로 치환 대상을 확정하고I18n.Content.*,I18n.CreateContent.*키를 추가·연결했다. 동적 글자수 표기는 함수형 키(characterCount(_:))로 처리했다.
- 무엇:
- 실행 명령/도구:
task(subagent_type="explore", ...)x2 (bg_70fab19d,bg_9fe99782)task(subagent_type="librarian", ...)x2 (bg_934b15f1,bg_faea5e5d)background_output(task_id=...)x4 (위 4개 task 결과 수집)grep("\"[^\"]*[가-힣][^\"]*\"", include=Group2 대상 파일)grep("String\\(localized:|NSLocalizedString\\(|LocalizedStringKey\\(", include=Group2 대상 파일)lsp_diagnostics(filePath=변경 파일 전체)xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug buildxcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug buildxcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" testxcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" test
- 결과:
I18n.swift확장:I18n.Content.List.*(목록 섹션/필터/정렬 라벨)I18n.Content.Playback.playFailedI18n.CreateContent.*(테마 선택/입력 폼/알림/검증 문구)I18n.CreateContent.characterCount(_ count: Int)
- 치환 완료 파일(실치환 9개):
ContentListView.swift,ContentPlayManager.swiftContentCreateSelectThemeView.swift,ContentCreateSelectThemeViewModel.swiftContentCreateView.swift,ContentCreateViewModel.swiftQuarterTimePickerView.swift,SelectDatePicker.swiftContentCurationView.swift
- 점검만 수행(실치환 없음, 체크 완료 1개):
ContentRepository.swift(사용자 노출 문구 없음; API 정렬 기본 파라미터"매출"만 존재)
- Group 2 체크박스 10개
- [x]완료 반영. - Group 2 재탐지 결과, 남은 한글 리터럴은
ContentPlayManager.swift의 디버그print로그 3건 및ContentRepository.swift의 API 파라미터 기본값 1건(비노출)만 확인. - 직접 로컬라이제이션 API(
String(localized:),NSLocalizedString,LocalizedStringKey)는 Group 2 대상 파일에서 0건으로 확인. - 빌드 검증:
SodaLive,SodaLive-devDebug 빌드 모두 성공(** BUILD SUCCEEDED **). - 테스트 검증: 두 스킴 모두
Scheme ... is not currently configured for the test action.로 test action 미구성 확인(코드 실패 아님, 스킴 제약). - 수동 QA(문구 경로 수동 점검): Group 2 변경 파일에서 사용자 노출 텍스트가
I18n.*경유인지 라인 단위 검토 완료. 비노출 문자열(디버그 로그/API 파라미터)은 예외로 문서화했다. - LSP 진단 참고: SourceKit 단독 해석 환경에서 외부 모듈/프로젝트 심볼(
Kingfisher,ObjectBox,I18n,AppState등) 미해결 오류가 보고되나, 동일 변경셋은xcodebuild실컴파일 통과로 검증했다.
23차 구현 (Content 모듈 Group 3~5, 30개 파일 처리, 2026-04-01)
- 무엇/왜/어떻게:
- 무엇:
변경 대상 파일 전체 목록의ContentGroup 3~5(30개 파일)를 전수 점검해 사용자 노출 하드코딩 문구를I18n.*참조로 전환했다. - 왜: 댓글/상세/구매/후원/수정/플레이리스트 구간에서 뷰 리터럴·ViewModel 에러문구·다이얼로그 문구가 혼재되어
I18n.swift단일 접근 원칙이 깨져 있었기 때문이다. - 어떻게: 병렬 컨텍스트 수집(
explore/librarian/oracle백그라운드 +grep/read/lsp_diagnostics직접 점검) 후, Group 3~5 범위 파일만 치환했다. 공통 오류는I18n.Common.commonError로 통합하고, 도메인 전용 문구는I18n.ContentDetail.*,I18n.Content.*,I18n.CreateContent.*로 정리했다.
- 무엇:
- 실행 명령/도구:
task(subagent_type="explore", ...)x2 (bg_91507310,bg_db55ce36)task(subagent_type="librarian", ...)x2 (bg_a4a5eff9,bg_b2fde7b7)task(subagent_type="oracle", ...)x1 (bg_3ab5a233)grep("\"[^\"]*[가-힣][^\"]*\"", include=Group3~5 대상 파일)grep("String\\(localized:|NSLocalizedString\\(|LocalizedStringKey\\(", include=Group3~5 대상 파일)lsp_diagnostics(filePath=변경 파일 전체)xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug buildxcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug buildxcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" testxcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" test
- 결과:
I18n.swift확장:I18n.ContentDetail.*대규모 추가(상세 헤더/메뉴/삭제·신고 다이얼로그/댓글/한정판/모자이크/주변 콘텐츠/이전화·다음화/구매·대여/후원 다이얼로그/완료 토스트 등)I18n.Content.Banner.loadFailedI18n.Content.Playlist.*(생성 버튼/빈 상태/개수 표기)I18n.CreateContent.modifyTitle,modifyAction,modifySuccess
- 치환 완료 파일(실치환 28개):
- Group 3: 10개 전부 실치환 완료
- Group 4: 10개 전부 실치환 완료
- Group 5: 8개 실치환 완료 (
ContentOrderDialogView,LiveRoomDonationDialogView,ContentMainBannerViewModel,ContentModifyView,ContentModifyViewModel,ContentPlaylistItemView,ContentPlaylistListView,ContentPlaylistListViewModel)
- 점검만 수행(실치환 없음, 체크 완료 2개):
ContentMainContentThemeView.swift,ContentPlayerView.swift(런타임 노출 하드코딩 없음, Preview 샘플 문자열만 존재)
- Group 3~5 체크박스 30개
- [x]완료 반영. - Group 3~5 재탐지 결과, 남은 한글 리터럴은 Preview 샘플 데이터 및 비노출 문자열 분기(기본 파라미터
reason: "프로필 신고")만 확인. - Oracle 후속 반영:
ContentDetailViewModel에isInsufficientCanError(message:errorProperty:)헬퍼를 추가해 부족 캔 분기를errorProperty우선 + 메시지 보조 판별로 정리했다(기존 단일 텍스트 포함 비교 의존 완화). - 직접 로컬라이제이션 API(
String(localized:),NSLocalizedString,LocalizedStringKey)는 Group 3~5 대상 파일에서 0건. - 빌드 검증:
SodaLive,SodaLive-devDebug 빌드 모두 성공(** BUILD SUCCEEDED **). - 테스트 검증: 두 스킴 모두
Scheme ... is not currently configured for the test action.로 test action 미구성 확인(코드 실패 아님, 스킴 제약). - 수동 QA(문구 경로 수동 점검): Group 3~5 대상 파일 재스캔에서 사용자 노출 문자열의
I18n.*경유를 확인했고, 예외(Preview/비노출 비교 문자열)만 잔존함을 확인했다. - LSP 진단 참고: SourceKit 단독 해석 환경에서 외부 모듈/프로젝트 심볼(
Kingfisher,Moya,I18n,AppState등) 미해결 오류가 보고되나, 동일 변경셋은xcodebuild실컴파일 통과로 검증했다.
24차 구현 (Content 모듈 Group 6~8, 28개 파일 처리, 2026-04-01)
- 무엇/왜/어떻게:
- 무엇:
변경 대상 파일 전체 목록의ContentGroup 6~8(플레이리스트 생성/수정/상세 + 시리즈 목록/상세/메인, 28개 파일)을 전수 점검하고, 런타임 사용자 노출 하드코딩 문구를I18n.*참조로 전환했다. - 왜: Playlist/Series 구간에 화면 타이틀, 버튼, 상태 배지, 정렬 라벨, ViewModel 공통 오류 문구가 하드코딩 상태로 남아 있어
I18n.swift단일 접근 원칙과 불일치했기 때문이다. - 어떻게:
explore/librarian병렬 탐색 +grep/ast_grep_search/read직접 점검으로 런타임 문자열과 Preview 샘플 문자열을 분리한 뒤,I18n.swift의I18n.Content.Playlist,I18n.Playlist,I18n.Series를 확장하고 Group 6~8 호출부를 최소 변경으로 치환했다.
- 무엇:
- 실행 명령/도구:
task(subagent_type="explore", ...)x2 (bg_fa2a12ab,bg_56444808)task(subagent_type="librarian", ...)x1 (bg_fd44cf0e)background_output(task_id=...)x3 (위 3개 task 결과 수집)grep("\"[^\"]*[가-힣][^\"]*\"", include=*.swift, path=SodaLive/Sources/Content/{Playlist,Series})grep("\\bI18n\\.(Content|Series|Common)", include=*.swift, path=SodaLive/Sources/Content)ast_grep_search(pattern="Text(\"$TEXT\")", lang=swift, paths=[SodaLive/Sources/Content/Playlist,SodaLive/Sources/Content/Series])read(SodaLive/Sources/I18n/I18n.swift)+ 대상 파일 병렬readxcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug buildxcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug buildxcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" testxcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" test
- 결과:
I18n.swift확장:I18n.Content.Playlist에 생성/수정/상세 플로우용 키 추가(createTitle,createSave,modifyTitle,modifyAction,titleLabel,descriptionLabel,addContentAction,close,selectionCount(_:),createdDate(_:),contentCount(_:),play,shuffle, 검증 문구 2종)I18n.Playlist.deleteCompleted추가I18n.Series에 메인/상세/요일/상태 배지 키 추가(title,completedSectionTitle,recommendedSectionTitle,byDaySectionTitle,voiceOnOnlyTitle,allEpisodesListen,allEpisodesTitle(_:),registeredOrder,point,free, 요일 8종,age19Badge,publishing(_:))
- 치환 완료 파일(실치환 20개):
- Playlist:
ContentPlaylistCreateView,ContentPlaylistCreateViewModel,PlaylistAddContentView,ContentPlaylistDetailView,ContentPlaylistDetailViewModel,ContentPlaylistModifyView,ContentPlaylistModifyViewModel - Series:
SeriesContentAllView,SeriesContentAllViewModel,SeriesContentListItemView,DayOfWeekSeriesView,SeriesDetailHomeView,SeriesDetailView,SeriesDetailViewModel,SeriesMainByGenreViewModel,SeriesMainDayOfWeekView,SeriesMainDayOfWeekViewModel,SeriesMainHomeView,SeriesMainHomeViewModel,SeriesMainItemView,SeriesMainView,SeriesItemView,SeriesListAllView,SeriesListAllViewModel
- Playlist:
- 점검만 수행(실치환 없음, 체크 완료 8개):
PlaylistAddContentItemView.swift,PlaylistCreateContentView.swift,PlaylistContentItemView.swift(런타임 하드코딩 없음, Preview 샘플만 존재)SeriesDetailIntroductionView.swift(런타임 사용자 노출 하드코딩 없음; 잔여 한글은 서버값 비교용"랜덤"분기만 존재)
- Group 6~8 체크박스 28개
- [x]완료 반영. - 재탐지 결과, 남은 한글 리터럴은 Playlist/Series Preview 샘플 데이터와
SeriesDetailIntroductionView.swift의 비노출 비교 문자열(publishedDaysOfWeek == "랜덤")만 존재. - 빌드 검증:
SodaLive-devDebug 빌드 성공(** BUILD SUCCEEDED **).SodaLiveDebug 빌드는 병렬 실행 시XCBuildData/build.dblock으로 1회 실패했으나, 단독 재실행에서 성공(** BUILD SUCCEEDED **).
- 테스트 검증: 두 스킴 모두
Scheme ... is not currently configured for the test action.로 test action 미구성 확인(코드 실패 아님, 스킴 제약). - 수동 QA(문구 경로 수동 점검): Group 6~8 대상 파일 재스캔에서 사용자 노출 문자열의
I18n.*경유를 확인했고, 예외는 Preview/비노출 비교 문자열만 남김. - LSP 진단 참고: SourceKit 단독 해석 환경에서 외부 모듈/프로젝트 심볼 미해결 오류(
Kingfisher,I18n,BaseView,AppState등)와I18n.swift의 기존LanguageHeaderProvider미해결이 보고되나, 동일 변경셋은xcodebuild실컴파일 통과로 검증했다.
25차 구현 (Onboarding 마감 및 최종 체크리스트 종료, 2026-04-01)
- 무엇/왜/어떻게:
- 무엇: 남아 있던 최종 런타임 하드코딩 문자열
OnboardingView.swift의 CTA 문구를I18n.*로 전환하고, 문서의 미완료 체크리스트를 마감했다. - 왜: 백그라운드 탐색과 정적 검색 결과, 실제 사용자 노출 하드코딩 문자열은 온보딩의
"시작하기"1건만 남아 있었고 이 항목이 View 레이어 전환 및 최종 검증 완료를 막고 있었기 때문이다. - 어떻게:
I18n.swift에I18n.Onboarding.startAction키를 추가하고OnboardingView호출부를 치환했다. 이후 전역 직접 로컬라이제이션 API 사용 여부, 접근성/동적 문자열/포맷·plural 리스크, 빌드·테스트·수동 QA 증적을 다시 점검해 상단 체크리스트를 종료했다.
- 무엇: 남아 있던 최종 런타임 하드코딩 문자열
- 실행 명령/도구:
task(subagent_type="explore", ...)x2 (bg_3818aed0,bg_a692c517)task(subagent_type="librarian", ...)x1 (bg_38369154)background_output(task_id=...)x3 (위 3개 task 결과 수집)read(SodaLive/Sources/Onboarding/OnboardingView.swift)read(SodaLive/Sources/I18n/I18n.swift)grep("Onboarding|startButton|시작하기", include=*.swift, path=SodaLive/Sources)grep("String\\(localized:|NSLocalizedString\\(|LocalizedStringKey\\(", include=*.swift, path=SodaLive/Sources)grep("accessibility(Label|Hint)", include=*.swift, path=SodaLive/Sources)ast_grep_search(pattern="Text(\"$TEXT\")", lang=swift, paths=[SodaLive/Sources])xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug buildxcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug buildxcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" testxcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" test
- 결과:
I18n.swift에I18n.Onboarding.startAction추가,OnboardingView.swift의Text("시작하기")를Text(I18n.Onboarding.startAction)로 치환 완료.Onboarding모듈 체크박스 1개- [x]반영 완료.- 직접 로컬라이제이션 API(
String(localized:),NSLocalizedString,LocalizedStringKey)는SodaLive/Sources전역 재탐지에서 0건 확인. - 최종 잔여 한글 리터럴 재분류 결과, 런타임 사용자 노출 문자열은 제거되었고 Preview 샘플/비노출 구분자/디버그/SDK 입력값만 예외로 유지.
- 리스크 체크포인트 확인:
- 포맷 토큰/plural: 이번 최종 변경은 정적 단일 CTA 문자열 1건만 치환해 손실 없음.
- 동적 문자열: 백그라운드 탐색 결과 기준 미전환 런타임 동적 문자열 추가 발견 없음.
- 접근성 레이블/힌트: 전역 검색에서 하드코딩 한글 접근성 라벨/힌트 0건.
- 수동 QA(실제 기능 점검):
OnboardingView의 하단 CTA가I18n.Onboarding.startAction경유로 렌더링되며 탭 시UserDefaults.isViewedOnboardingView를true로 저장하는 기존 흐름을 코드 경로 기준으로 재확인. - 테스트 검증: 두 스킴 모두
Scheme ... is not currently configured for the test action.로 test action 미구성 확인(코드 실패 아님, 스킴 제약).
26차 구현 (Localizable.xcstrings 제거 및 프로젝트 참조 정리, 2026-04-01)
- 무엇/왜/어떻게:
- 무엇: 더 이상 사용하지 않는
SodaLive/Resources/Localizable.xcstrings파일과SodaLive.xcodeproj/project.pbxproj의 연결 참조를 함께 제거했다. - 왜: 직접 로컬라이제이션 API(
String(localized:),NSLocalizedString,LocalizedStringKey)가 전역 0건으로 정리된 상태에서 String Catalog 파일만 프로젝트 리소스로 남아 있어 유지보수 비용과 혼선을 만들고 있었기 때문이다. - 어떻게:
project.pbxproj에서PBXBuildFile2개,PBXFileReference1개,Resources그룹 항목 1개,PBXResourcesBuildPhase2개를 제거하고 실제Localizable.xcstrings파일도 삭제했다. 이후 전역 검색과 두 스킴 Debug 빌드로 참조 누락 여부를 검증했다.
- 무엇: 더 이상 사용하지 않는
- 실행 명령/도구:
task(subagent_type="explore", ...)x2 (bg_b6e5b2d5,bg_d0f782b3)task(subagent_type="librarian", ...)x1 (bg_6a78e701)background_output(task_id=...)x3 (위 3개 task 결과 수집)grep("Localizable\\.xcstrings|402C20902EFE8C34005FC5CB|402C20912EFE8C34005FC5CB|402C20952EFE8C34005FC5CB", include=project.pbxproj, path=SodaLive.xcodeproj)read(SodaLive.xcodeproj/project.pbxproj)glob("**/*Localizable.xcstrings")grep("String\\(localized:|NSLocalizedString\\(|LocalizedStringKey\\(", include=*.swift, path=SodaLive/Sources)xcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive" -configuration Debug buildxcodebuild -workspace "SodaLive.xcworkspace" -scheme "SodaLive-dev" -configuration Debug build
- 결과:
- 삭제 파일:
SodaLive/Resources/Localizable.xcstrings - 제거 참조:
project.pbxproj내402C20912EFE8C34005FC5CB,402C20952EFE8C34005FC5CB,402C20902EFE8C34005FC5CB및 해당 그룹/리소스 페이즈 연결 전부 제거 완료. - 재탐지 결과:
project.pbxproj와 파일 시스템에서Localizable.xcstrings실참조 0건 확인. 문서 내 과거 계획/검증 기록에만 문자열이 남아 있음. - 직접 로컬라이제이션 API(
String(localized:),NSLocalizedString,LocalizedStringKey)는SodaLive/Sources전역 0건 유지 확인. - 빌드 검증:
SodaLive,SodaLive-devDebug 빌드 모두 성공(** BUILD SUCCEEDED **). - 수동 QA(실제 변경 효과 점검): Xcode 프로젝트 수준에서 리소스 파일/빌드 페이즈/파일 시스템 세 경로가 모두 함께 제거된 상태를 코드와 프로젝트 파일 기준으로 재확인했다.
- 삭제 파일: