본문 바로가기
감정일기(가칭)

📘 DiaryContentCell 리팩토링 기록 — 단일 String 통합 방식에서 → 구조체 기반 다중 필드 관리로 개선하기

by 밤새는 탐험가89 2025. 11. 17.
728x90
SMALL

Diary Editor 화면에서 사용자가 작성하는 “감정일기 내용”은 총 4개의 텍스트 입력 영역으로 나뉘어 있다.

 

- 상황

- 생각 / 원인

- 새로운 시각 / 반박

- 다음 행동

 

처음 설계할 때 나는 각 섹션의 입력값을 하나의 문자열로 합쳐서 ViewModel에 전달하는 방식을 사용했다.


❌ 1. 기존 방식 – 하나의 문자열로 통합

private func emitCombinedText() {
    let combined = """
    [상황]
    \(situationSection.text.trimmingCharacters(in: .whitespacesAndNewlines))
    
    [생각 / 원인]
    \(thoughtSection.text.trimmingCharacters(in: .whitespacesAndNewlines))
    
    [새로운 시각 / 반박]
    \(reevalSection.text.trimmingCharacters(in: .whitespacesAndNewlines))
    
    [다음 행동]
    \(actionSection.text.trimmingCharacters(in: .whitespacesAndNewlines))
    """

    onContentChanged?(combined)
}

 

🔍 문제점

이 구조는 UI → ViewModel 데이터 전달이 단일 문자열 하나라서 편해 보이지만,
유효성 검사를 실제로 적용하려고 하니 문제가 터졌다.

 

예를 들어 유효성 검사에서:

상황 미입력 → 에러 표시

생각 미입력 → 에러 표시

 

이런 식으로 필드별로 검증해야 하는데
단일 문자열을 다시 분리해서 분석해야 하는 상황이 되어버린다.

 

즉,

“유저가 어떤 섹션을 비워놨는지”
정확하게 알 수가 없다.
결국 유효성 검사 로직이 지저분해지고 유지보수가 어려워지는 문제 발생.


⭐ 2. 개선 방향 – 입력값을 “섹션별로 분리해서 전달하자”

 

그래서 아예 하나의 문자열을 보내는 대신,
4개의 필드를 각각 따로 ViewModel에 전달하는 구조로 변경하기로 했다.


✅ 3. 구조체 기반의 개선된 데이터 전달 방식

ContentSections 구조체 정의

struct ContentSections {
    let situation: String
    let thought: String
    let reeval: String
    let action: String
}

 

이렇게 하면 ViewModel에서 각 필드에 대해:

 

1. 상황 → trimmedSituation

2. 생각 → trimmedThought

3. 반박 → trimmedReeval

4. 행동 → trimmedAction

 

각각 독립적으로 검증할 수 있게 된다.


✅ 4. DiaryContentCell에서 입력값을 구조체로 변환

emitContentSections() — 입력값 합성

private func emitContentSections() {
    let sections = ContentSections(
        situation: situationSection.text.trimmingCharacters(in: .whitespacesAndNewlines),
        thought: thoughtSection.text.trimmingCharacters(in: .whitespacesAndNewlines),
        reeval: reevalSection.text.trimmingCharacters(in: .whitespacesAndNewlines),
        action: actionSection.text.trimmingCharacters(in: .whitespacesAndNewlines)
    )
    
    onContentChanged?(sections)
}

✅ 5. 모든 TextView 변화에 반응하도록 바인딩

private func bindSections() {
    let allSections = [situationSection, thoughtSection, reevalSection, actionSection]
    for section in allSections {
        
        section.textChanged = { [weak self] _ in
            self?.emitContentSections()
        }
        
        section.onFocusChanged = { [weak self] view in
            self?.onFocusChanged?(view)
        }
        
    }
}

 

중요한 점은:

 

사용자가 4개의 텍스트 중 어떤 것을 입력해도

→ 항상 ContentSections 구조체가 다시 만들어져

→ ViewModel로 전달된다

728x90
LIST