Skip to content

refactor: #55 메모 입력 상태 경계 정리 (실시간 입력 반영)#58

Open
zaehorang wants to merge 6 commits intodevelopfrom
feature/55-emoji-context-comments-review-ready
Open

refactor: #55 메모 입력 상태 경계 정리 (실시간 입력 반영)#58
zaehorang wants to merge 6 commits intodevelopfrom
feature/55-emoji-context-comments-review-ready

Conversation

@zaehorang
Copy link
Contributor

About this PR

🔖 Related Issue


📚 Contents

배경 (Before)

초기 요구사항은 “입력 중에도 즉시 이모지 변환이 반영되는 UX” 였습니다.

다만 출시 일정 내에 이를 그대로 구현하려면, 한글 IME 조합(입력 중 marked text) 상태까지 포함해 실시간 치환을 안정적으로 처리해야 했고, 그 과정에서

  • 입력 도중 문자열이 예상치 못한 타이밍에 변하는 문제
  • 커서/조합 흐름이 흔들릴 수 있는 리스크
  • 예외 케이스(조합/커서/붙여넣기/선택 영역) 방어에 따른 구현 복잡도

가 빠르게 커질 수 있다고 판단했습니다.

그래서 출시를 우선 맞추기 위해, 당시 가능한 안정적인 대안으로 변환 확정 타이밍을 띄어쓰기/줄바꿈 같은 경계 입력 시점으로 제안/적용했습니다.

출시 이후에는 원래 요구 UX(입력 중 반응성)를 달성하기 위해, IME 안정성과 UX를 함께 만족시키는 “실시간 반영 방식” 을 다시 설계했고, 그 결과가 이번 PR입니다. (with AI 🤖)

참고: 저장 데이터(원문/이모지)는 기존과 동일하게 둘 다 유지하며, 저장 상태/플로우도 이전과 동일합니다.

구현 방식 고민 (How)

이번 변경의 핵심 질문은 “무엇을 즉시 반영하고, 무엇을 보류할 것인가” 였습니다.

  1. 매 타이핑마다 즉시 치환을 적용하면,
    • 영문 입력: 입력 직후 곧바로 다른 형태(이모지/치환 결과)로 보이면 사용자가 방금 입력한 값이 맞는지 확인하기 어려워집니다.
    • 한글 입력(조합): 입력 중(예: → 받침/쌍자음 조합 가능) 상태는 아직 확정이 아니기 때문에, 조합 중간에 치환이 들어가면 의도한 완성 글자 인지가 어려워지고 입력 흐름이 불안정해질 수 있습니다.
  2. 그래서 정책을 현재 입력 중 1글자(또는 marked 구간)는 보류 하고, 직전 확정(confirmed) 텍스트부터 반영 으로 잡았습니다.
  3. 붙여넣기/다글자 입력은 입력 직후 즉시 반영하고, 저장 시점에는 보류 구간까지 포함해 final snapshot으로 flush합니다.

즉, 이번 PR에서 말하는 “실시간”은 무조건 즉시 치환이 아니라, 입력 엔진 안정성과 UX 반응성을 함께 만족시키는 반영 타이밍 제어로 구현했습니다.

아키텍처 고민 (Why this boundary)

초기에는 MVI를 단순하게 해석해 입력 관련 상태를 Store에 최대한 올리려 했습니다.
하지만 입력 도메인에서는 View도 입력 상태를 알아야 하는데, 중간값까지 Store로 올리면 Store와 View가 동일/유사 상태를 동시에 보유하게 되고, 결과적으로 실시간 동기화 코드가 비대해지는 문제가 발생했습니다.

  • 기능적으로 “깨지는” 문제가 당장 발생했다기보다,
  • IME/커서/붙여넣기 구간의 예외를 방어하기 위해 Store-View 간 동기화/보정 로직이 늘어나면서
  • 변경 비용과 영향 범위가 빠르게 커졌습니다.

그래서 상태를 아래 기준으로 분리했습니다.

  1. 화면 정책/비즈니스 판단 상태 + 저장 트랜잭션: Store 소유
  2. UITextView 생명주기에 묶인 입력 진행 상태(IME marked/unmarked, 커서/부분 치환 과정 등): InputView(Coordinator) 로컬 소유
  3. 저장 시점 데이터: final snapshot 이벤트로 경계 전달

결론적으로 MVI를 버린 것이 아니라, 입력 엔진 진행 상태와 도메인 정책/저장 트랜잭션 상태의 책임 경계를 명확히 분리한 변경입니다.

최종 구조 (After)

Store(정책/트랜잭션) -> MemoView(중재) -> InputView(입력/IME/변환 처리)

  • InputView는 Store를 직접 참조하지 않습니다.
  • Store는 isInputEmpty, savePhase 등 정책 상태와 저장 트랜잭션을 담당합니다.
  • InputView는 draft(original/emoji/revision)와 IME 조합 상태를 로컬에서 처리합니다.
  • 저장 시점에는 InputView가 final snapshot(원문/이모지 포함)을 만들어 경계로 전달합니다.

주요 변경 사항

  1. 입력 계약 타입 정리
  • MemoInputSeed
  • MemoInputSnapshot
  • MemoInputUICommand
  • MemoInputUIEvent
  1. Input 경계 계약 고정
  • 입력: inputSeed, isEmojiPresentationEnabled, uiCommandEvent
  • 출력: onDraftAvailabilityChanged, onFinalSnapshotReady, onFinalSnapshotFailed
  1. 중재 계층 명확화
  • MemoView가 Store side effect와 Input 이벤트를 연결

검증

아래 테스트/시나리오를 통해 회귀를 확인했습니다.

  • saveTappedTwiceBeforeFinalSyncEmitsSingleRequest
  • emptyFinalSnapshotDoesNotPersist
  • finalSyncCompletedWithMismatchedRequestIDDoesNotPersist
  • finalSyncFailedMatchingRequestIDReturnsIdle
  • finalSyncFailedWithMismatchedRequestIDIsIgnored
  • savePhase 진행 중 navigation/delete/background tap guard
  • IME marked/unmarked 입력 안정성
  • ComfieZone plain-mode 매핑 보존/삭제/교체 안정성

📸 Screenshot

  • 로직/테스트/문서 중심 변경으로 스크린샷은 생략했습니다.

Other information 🔥

리뷰 포인트

  1. 기존 트리거(띄어쓰기/줄바꿈) 기반에서 입력 이벤트 기반 반영으로 바뀐 설명이 코드 동작과 일치하는지
  2. 입력 중간 상태를 View 로컬로 둔 경계 설계가 타당한지 (동기화 복잡도 관점)
  3. IME/ComfieZone 매핑 회귀 케이스가 충분히 커버되는지

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: 메모 입력 중 즉시 이모지 변환 방식으로 개선

1 participant