나는 앱을 만들려고 한 적이 없다

처음부터 앱을 만들겠다는 의도가 있었던 것은 아니다. 특정 시장을 겨냥하거나, 서비스를 키워보겠다는 계획도 없었다. 기술을 실험하기 위해 사이드 프로젝트를 시작한 것도 아니었다. 오히려 그 반대에 가까웠다. 별다른 목적 없이 책을 읽다가, 아주 단순한 불편함 하나가 계속 머릿속에 남아 있었다. 그 불편함은 크지도 않았고, 당장 해결하지 않으면 안 되는 문제도 아니었다. 그런데 이상하게도 그 감각이 사라지지 않았다. 한 번 느낀 뒤로는, 책을 읽을 때마다 같은 지점에서 계속 걸렸다.

여기서 중요한 건 불편함의 크기가 아니라 지속성이다. 대부분의 불편함은 시간이 지나면 잊힌다. 하지만 이건 그렇지 않았다. 읽고 나면 끝나는 게 아니라, 읽고 나서 오히려 더 또렷해졌다. 책을 덮은 뒤에 남는 감각이 어딘가 비어 있었다. 그 빈 공간이 반복되면서 하나의 질문으로 굳어졌다. 나는 무엇을 놓치고 있는 걸까. 이 질문은 기능 요구로 바로 이어지지 않았다. 오히려 더 모호한 상태로 남았다. 그래서 처음에는 해결하려고 하지 않았다. 그냥 계속 관찰했다.

이 시점에서 이미 하나의 전제가 만들어진다. “무언가 부족하다”는 감각이다. 문제는 이 감각이 정확한 정의 없이도 너무 쉽게 확신으로 변한다는 점이다. 나는 그 불편함을 문제라고 받아들였고, 그 문제를 해결해야 한다고 생각했다. 하지만 그때의 나는 그 문제가 무엇인지 정확히 설명할 수 없었다. 단지 반복적으로 느껴졌기 때문에, 존재한다고 믿었을 뿐이다. 이 상태에서 시작된 모든 판단은 결국 하나의 방향으로 흐른다. 문제를 해결하려는 시도는 시작되지만, 문제 정의는 여전히 비어 있는 상태로 남는다.

그래서 이 글의 출발점은 “앱을 만들기로 했다”가 아니다. 오히려 그 반대다. 아무 계획도 없이, 문제를 제대로 정의하지 못한 상태에서 출발했다. 그리고 이 상태는 이후 모든 설계 판단의 출발점이 된다. 나중에 돌아보면, 이 순간이 가장 중요한 지점이었다. 왜냐하면 이후의 모든 선택은 이 모호한 문제 정의 위에 쌓이기 때문이다. 이 시리즈는 바로 그 지점에서부터 시작한다.

불편함의 정체 — “기록이 없다”는 착각

불편함을 계속 추적하다 보면, 결국 하나의 문장으로 정리된다. “기록이 남지 않는다.” 책을 읽는 동안에는 분명히 시간이 흐르고, 어떤 행동이 축적된다. 하지만 책을 덮는 순간, 그 모든 것이 사라진다. 오늘 얼마나 읽었는지, 언제 멈췄는지, 중간에 얼마나 쉬었는지 같은 정보는 기억에 의존하게 된다. 그리고 기억은 금방 흐릿해진다. 그래서 나는 자연스럽게 결론을 내렸다. 기록이 없기 때문에 불편한 것이다. 이 판단은 직관적으로 맞는 것처럼 보인다.

하지만 이 판단에는 중요한 착각이 하나 숨어 있다. 나는 “기록이 없다”고 생각했지만, 실제로는 기록이 존재하지 않는 것이 아니었다. 오히려 기록은 계속 생성되고 있었다. 읽기 시작한 시점, 멈춘 시점, 읽은 분량, 중단된 이유 같은 것들은 모두 데이터로 표현될 수 있는 요소였다. 문제는 그 데이터가 수집되지 않는 것이 아니라, 구조화되지 않는다는 점이었다. 즉, 기록의 부재가 아니라 기록의 형태가 정의되지 않은 상태였다. 이 차이는 겉으로 보면 작지만, 설계에서는 결정적인 차이를 만든다.

기록이라는 개념은 단일 값으로 생각하기 쉽다. 예를 들어 “오늘 30분 읽었다” 같은 형태다. 하지만 이 값은 실제로는 여러 사건의 결과다. 언제 시작했고, 언제 끝났으며, 그 사이에 어떤 변화가 있었는지를 모두 포함한다. 그런데 이 과정을 무시하고 결과만 남기면, 데이터는 축약된다. 축약된 데이터는 간단하지만, 설명력이 떨어진다. 나는 처음에 이 차이를 인식하지 못했다. 그래서 기록이 없다고 생각했고, 기록을 추가하면 문제가 해결될 것이라고 믿었다.

이 지점에서 이미 방향이 잘못 잡힌다. 문제를 단순화하면 해결은 쉬워 보인다. 하지만 그 단순화가 문제의 본질을 제거한다면, 이후의 모든 설계는 잘못된 기반 위에 쌓이게 된다. 나는 기록을 추가하는 것이 아니라, 기록을 어떻게 정의해야 하는지를 고민해야 했지만, 그 단계까지 가지 못했다. 그래서 “기록이 없다”는 문장은 사실상 잘못된 문제 정의였다. 정확히 말하면, 나는 기록을 어떻게 모델링해야 하는지를 모르는 상태였다.

이 차이는 나중에 설계를 완전히 갈라놓는다. 단순 기록을 추가하는 시스템과, 시간 흐름을 표현하는 시스템은 전혀 다른 구조를 가진다. 전자는 상태를 중심으로 움직이고, 후자는 이벤트를 중심으로 움직인다. 나는 이 시점에서 그 차이를 이해하지 못했다. 그래서 문제를 해결하려는 시도는 시작되었지만, 그 방향은 이미 어긋나 있었다. 이 글은 그 어긋남이 어디에서 시작되었는지를 드러내기 위한 것이다.

기존 앱들이 해결하지 못한 이유 — “완료 중심 모델”

문제를 “기록의 부재”로 정의한 상태에서, 가장 먼저 한 행동은 기존 앱을 다시 살펴보는 것이었다. 이미 수많은 독서 앱이 존재하고 있었기 때문에, 그 안에 답이 있을 것이라고 생각했다. 실제로 앱들은 잘 만들어져 있었다. 통계는 정교했고, 디자인도 완성도가 높았으며, 사용자 경험도 매끄러웠다. 하지만 계속 사용하다 보니 이상한 점이 하나 드러났다. 모든 앱이 비슷한 방향을 향하고 있었다. 그 방향은 과정이 아니라 결과였다.

대부분의 앱은 “완독”을 중심으로 설계되어 있었다. 몇 권을 읽었는지, 목표를 얼마나 달성했는지, 평점은 몇 점인지 같은 지표들이 핵심이었다. 이 구조는 이해하기 쉽다. 결과는 명확하고, 측정하기 쉽고, 비교하기도 쉽다. 그래서 시스템을 설계할 때도 유리하다. 하지만 이 구조에는 한 가지 전제가 숨어 있다. 읽는 과정은 중요하지 않다는 전제다. 과정은 결과를 만들기 위한 수단일 뿐이고, 기록할 가치가 있는 것은 결과라는 판단이다.

이 전제는 사용자 경험을 일정 방향으로 고정시킨다. 사용자는 자연스럽게 “완독”이라는 목표를 향해 움직이게 된다. 하지만 이 구조는 특정한 질문에 답하지 못한다. 예를 들어 한 권을 읽기까지 몇 번의 시도가 있었는지, 왜 특정 구간에서 멈췄는지, 실제로 투자한 시간은 얼마나 되는지 같은 질문은 이 모델에서 다루기 어렵다. 왜냐하면 이 질문들은 결과가 아니라 과정에 대한 것이기 때문이다. 그리고 과정은 단일 값으로 표현되지 않는다.

여기서 중요한 건 이 문제가 기능 부족이 아니라 모델의 한계라는 점이다. 기능을 몇 개 더 추가한다고 해서 해결되지 않는다. 왜냐하면 기반이 되는 데이터 구조가 그 질문을 수용하지 못하기 때문이다. 나는 처음에는 이 차이를 명확히 이해하지 못했다. 단순히 “내가 원하는 기능이 없다”고 생각했다. 하지만 계속 분석하다 보니, 문제는 기능이 아니라 구조라는 사실이 드러났다. 기존 앱들은 잘못된 것이 아니라, 다른 문제를 해결하고 있었던 것이다.

결국 나는 여기서 하나의 결론에 도달한다. 내가 찾고 있던 것은 기존 앱들이 제공하는 것과 다른 종류의 데이터였다. 결과가 아니라 흐름, 상태가 아니라 시간, 단일 값이 아니라 이벤트의 집합이다. 하지만 이 결론 역시 완전하지 않았다. 왜냐하면 나는 여전히 그 데이터를 어떻게 모델링해야 하는지를 알지 못했기 때문이다. 그래서 다음 단계로 넘어간다. 문제를 다시 정의하려는 시도, 그리고 그 과정에서 발생하는 두 번째 설계 붕괴로 이어진다.

내가 원했던 것 — “성과”가 아니라 “흐름”

기존 앱들을 살펴본 뒤에 남은 감각은 단순한 불만이 아니었다. 기능이 부족하다는 느낌도 아니었고, UX가 불편하다는 수준의 문제도 아니었다. 오히려 더 구조적인 어긋남에 가까웠다. 내가 보고 싶었던 것과 앱들이 보여주는 것이 완전히 다른 축에 놓여 있었다. 그 차이를 처음에는 명확하게 설명할 수 없었지만, 반복해서 사용하다 보니 점점 형태가 드러나기 시작했다. 내가 궁금했던 것은 “얼마나 읽었는가”가 아니라 “어떻게 읽었는가”였다. 그런데 기존 앱들은 그 질문 자체를 다루지 않았다.

이 지점에서 중요한 변화가 하나 생긴다. 문제를 “기록이 없다”에서 “기록의 방향이 다르다”로 수정하는 순간이다. 나는 더 이상 결과를 보고 싶지 않았다. 대신 과정 자체를 보고 싶었다. 한 권을 읽기까지 몇 번의 시도가 있었는지, 어떤 날은 왜 읽지 않았는지, 중간에 어디에서 멈췄는지 같은 흐름이 필요했다. 이 흐름은 단순한 데이터가 아니라 시간 위에서 움직이는 구조였다. 그래서 이걸 단일 값으로 표현하는 것은 애초에 불가능했다.

여기서 하나의 중요한 인식이 생긴다. 나는 데이터를 보고 싶었던 것이 아니라, 시간의 구조를 보고 싶었던 것이다. 이 차이는 생각보다 크다. 데이터는 저장하면 끝이지만, 시간 구조는 설계해야 한다. 시간은 자동으로 정렬되지 않는다. 어떤 단위로 나눌지, 어떤 사건을 기록할지, 어떤 기준으로 연결할지를 모두 정의해야 한다. 그리고 그 정의는 결국 시스템 전체의 형태를 바꾼다. 이 시점에서 나는 처음으로 문제의 방향을 제대로 바라보기 시작했다.

하지만 이 역시 완전한 해답은 아니었다. 방향은 맞았지만, 방법은 여전히 없었다. 흐름을 보고 싶다는 것은 선언에 가깝고, 실제 설계로 내려오려면 더 많은 결정이 필요하다. 무엇을 기준으로 나눌 것인가, 어디까지를 하나의 단위로 볼 것인가, 어떤 데이터가 필수이고 어떤 데이터는 버려도 되는가 같은 질문들이 이어진다. 그리고 이 질문들은 자연스럽게 기존에 생각했던 단순한 모델을 무너뜨리기 시작한다. 이 지점에서 설계는 처음으로 균열이 생긴다.

첫 번째 설계 — 상태 기반 모델 (그리고 이게 왜 틀렸는가)

흐름이라는 개념을 인식한 이후에도, 처음 시도한 설계는 여전히 단순한 형태를 벗어나지 못했다. 가장 자연스럽게 떠오르는 구조는 상태 기반 모델이었다. 책이라는 객체가 있고, 그 책의 상태가 있으며, 진행률이 존재하는 구조다. 이 모델은 직관적이다. 대부분의 시스템이 이 방식으로 설계되기 때문에, 별다른 고민 없이 받아들이기 쉽다. 나 역시 처음에는 이 구조를 그대로 사용했다. 책 단위로 상태를 관리하고, 진행률을 업데이트하면 충분하다고 생각했다.

하지만 이 모델은 아주 빠르게 한계를 드러낸다. 상태는 항상 “지금”만을 설명한다. 현재 읽고 있는지, 완독했는지, 중단했는지 같은 정보는 현재 시점에서는 의미가 있지만, 그 상태가 어떻게 만들어졌는지는 설명하지 못한다. 진행률도 마찬가지다. 60%라는 값이 있다고 해서, 언제 그 지점에 도달했는지, 그 과정이 연속적이었는지 단절되었는지 같은 정보는 전혀 알 수 없다. 이 모델은 결과를 저장하는 데는 적합하지만, 과정을 복원하는 데는 완전히 실패한다.

이 문제는 단순한 기능 부족으로 해결되지 않는다. 상태 모델 위에 로그를 추가하거나, 히스토리를 붙이는 방식으로는 근본적인 해결이 되지 않는다. 왜냐하면 기반이 되는 개념이 이미 잘못 정의되어 있기 때문이다. 상태는 시간의 축을 압축한 결과다. 따라서 상태만으로는 시간의 구조를 표현할 수 없다. 나는 이 사실을 설계 도중에 체감하게 된다. 모델이 단순해서 좋은 것이 아니라, 단순하기 때문에 중요한 것을 놓치고 있다는 것을 알게 된다.

결국 이 지점에서 하나의 결론에 도달한다. 이 모델은 내가 해결하려는 문제를 담을 수 없다. “지금 상태”를 중심으로 설계된 구조는 “과정”을 중심으로 하는 요구사항과 충돌한다. 그래서 이 모델을 유지한 채로는 아무리 확장을 해도 원하는 방향으로 갈 수 없다. 이 인식은 설계를 다시 시작해야 한다는 신호였다. 단순히 구조를 보완하는 수준이 아니라, 기반 개념 자체를 바꿔야 한다는 의미였다. 그리고 이 지점에서 질문이 폭발하기 시작한다.

질문이 폭발하는 순간 — 설계가 깨지기 시작한다

상태 기반 모델이 한계를 드러내는 순간, 설계는 조용히 무너지지 않는다. 오히려 반대로, 질문이 한꺼번에 쏟아지기 시작한다. 이전까지는 생각하지 않았던 문제들이 연쇄적으로 드러난다. 예를 들어 읽기 시작한 순간을 기록해야 하는지, 몇 분 읽고 바로 종료한 세션도 의미가 있는지 같은 질문이 등장한다. 이 질문들은 단순한 디테일처럼 보이지만, 실제로는 모델의 구조를 직접적으로 건드린다. 어떤 질문에 어떻게 답하느냐에 따라 시스템의 형태가 완전히 달라진다.

이 과정에서 중요한 건 질문의 수가 아니라 방향이다. 질문이 많아졌다는 것은 문제가 복잡해졌다는 의미가 아니다. 오히려 기존 모델이 이 질문들을 수용하지 못한다는 증거다. 예를 들어 진행률을 어디에 저장해야 하는지에 대한 고민은 단순한 위치 선택 문제가 아니다. 이 값이 상태인지, 이벤트인지, 스냅샷인지에 따라 전체 구조가 바뀐다. 만약 이 질문에 명확하게 답하지 않으면, 나중에 데이터 정합성이 깨지고, UI와 로직이 서로 충돌하게 된다.

여기서 설계는 더 이상 안정적인 상태를 유지하지 못한다. 기존에 세워둔 구조가 하나씩 흔들리기 시작한다. 작은 질문 하나가 모델 전체를 다시 보게 만든다. 이 시점에서 나는 깨닫게 된다. 문제는 내가 질문을 많이 하고 있다는 것이 아니라, 처음 세운 모델이 이 질문들을 예상하지 못했다는 것이다. 즉, 설계가 현실을 따라가지 못하고 있었다. 그리고 이 간극이 커질수록, 기존 구조를 유지하는 것은 점점 더 어려워진다.

결국 이 단계는 “수정”이 아니라 “붕괴”에 가깝다. 기존 모델을 유지한 채로 문제를 해결하려는 시도는 점점 더 비효율적으로 변한다. 그래서 선택지는 하나로 좁혀진다. 구조를 다시 나누는 것, 그리고 시간이라는 개념을 모델의 중심으로 끌어오는 것이다. 하지만 이 변화는 단순한 리팩토링이 아니다. 시스템을 바라보는 방식 자체를 바꾸는 일이다. 이 지점에서부터 비로소 새로운 설계가 시작된다.

구조를 다시 나누기 시작하다 — Record / Session / Event

질문이 모델을 감당하지 못하는 순간, 선택지는 두 가지로 갈린다. 기존 구조를 유지한 채 계속 덧붙이거나, 구조 자체를 다시 정의하거나. 전자는 단기적으로는 편하다. 필드를 몇 개 더 추가하고, 예외 케이스를 코드로 처리하면 당장은 돌아간다. 하지만 이 방식은 시간이 지날수록 시스템을 설명하기 어려운 상태로 만든다. 나는 이미 그 방향으로 몇 번 시도해봤고, 그 결과가 좋지 않다는 것을 알고 있었다. 그래서 이번에는 처음부터 방향을 바꾸기로 했다.

핵심은 단일 모델을 유지하지 않는 것이었다. 기존에는 “책”이라는 단위에 모든 것을 넣으려고 했다. 상태도, 진행률도, 읽은 시간도 전부 하나의 객체 안에 들어가야 한다고 생각했다. 하지만 이 접근 자체가 문제였다. 서로 다른 성질의 데이터를 하나의 레이어에 억지로 넣고 있었기 때문이다. 그래서 분리를 시작했다. 책 단위의 상태는 그대로 두되, 실제로 읽는 행위는 별도의 단위로 분리하고, 그 행위에서 발생하는 사건들을 또 따로 떼어내기 시작했다.

이 과정에서 등장한 것이 Record, Session, Event라는 세 개의 개념이다. Record는 책 단위의 상태를 유지한다. Session은 실제로 읽은 시간의 단위를 표현한다. 그리고 Event는 그 세션 안에서 발생하는 사건들을 기록한다. 여기서 중요한 건 단순히 모델이 늘어난 것이 아니라, 시간이라는 축이 명시적으로 분리되었다는 점이다. 이전에는 시간 정보가 상태 안에 묻혀 있었지만, 이제는 시간 자체가 독립된 구조를 가진다. 이 변화는 단순한 구조 변경이 아니라, 시스템을 바라보는 관점을 바꾸는 일이었다.

하지만 이 시점에서도 설계는 완성되지 않았다. 오히려 더 많은 질문이 생겼다. Session은 어디까지를 하나로 볼 것인가, Event는 어떤 기준으로 정의할 것인가, Record와 Session의 관계는 어떻게 유지할 것인가 같은 문제들이 이어졌다. 분리를 했다는 것은 책임을 나눴다는 의미이지만, 동시에 그 책임 사이의 경계를 정의해야 한다는 의미이기도 하다. 그래서 이 단계는 해결이라기보다 새로운 문제의 시작에 가까웠다. 그럼에도 불구하고, 하나는 분명해졌다. 이전의 상태 기반 모델로는 더 이상 돌아갈 수 없다는 점이다.

progressPct 하나가 드러낸 설계의 본질

구조를 나누기 시작하면서, 이전에는 단순해 보였던 요소들이 전혀 다른 의미를 가지기 시작한다. 그 대표적인 예가 progressPct였다. 처음에는 이 값이 별다른 고민 없이 Record에 들어가야 한다고 생각했다. 책 단위로 관리되는 정보처럼 보였기 때문이다. 진행률은 결국 책의 상태를 나타내는 값이라고 받아들였고, 그래서 자연스럽게 Record에 포함시켰다. 이 판단은 직관적으로 맞아 보였고, 별다른 의심 없이 넘어갔다.

하지만 Session과 Event를 분리한 이후, 이 값의 위치가 이상해지기 시작한다. 진행률은 고정된 값이 아니다. 읽는 행위가 끝날 때마다 바뀌고, 경우에 따라서는 감소할 수도 있다. 이걸 Record에 두면, 현재 상태는 표현할 수 있지만 그 값이 어떻게 만들어졌는지는 사라진다. 반대로 Session에 두면, 각 시점의 값을 기록할 수 있지만 전체 상태를 바로 파악하기는 어렵다. 이 선택은 단순한 저장 위치의 문제가 아니라, 데이터를 어떻게 해석할 것인가에 대한 문제였다.

이 지점에서 중요한 전환이 일어난다. 진행률을 “현재 상태”로 볼 것인가, 아니면 “세션 종료 시점의 스냅샷”으로 볼 것인가라는 질문이다. 전자를 선택하면 상태 중심 모델로 돌아가고, 후자를 선택하면 시간 중심 모델을 유지하게 된다. 나는 후자를 선택했다. progressPct는 세션이 끝날 때 기록되는 값이고, 그 값들의 집합이 전체 흐름을 만든다는 방향이다. 이 결정은 작은 것처럼 보이지만, 시스템 전체의 철학을 고정시킨다.

결국 이 하나의 필드가 설계의 본질을 드러낸다. 이건 단순히 데이터를 어디에 저장할 것인가의 문제가 아니라, 시간을 어떻게 모델링할 것인가의 문제였다. 그리고 이 선택을 통해 시스템은 명확하게 방향을 갖게 된다. 상태를 중심으로 할 것인가, 아니면 이벤트를 중심으로 할 것인가. 나는 이 지점에서 더 이상 상태 중심으로 돌아갈 수 없다는 것을 확신하게 된다. 설계는 점점 더 명확해지고 있었지만, 동시에 또 다른 제약을 만들어내고 있었다.

로컬 우선 선택 — 해결이 아니라 또 다른 문제의 시작

구조가 어느 정도 정리되면서, 다음으로 마주한 문제는 시스템을 어디에 둘 것인가였다. 대부분의 서비스는 자연스럽게 서버 중심 구조로 간다. 로그인, 데이터 동기화, 백업 같은 기능들이 기본 전제로 깔린다. 나 역시 처음에는 이 방향을 고려했다. 하지만 조금만 생각해보면, 이 구조는 나에게 과한 선택이었다. 개인 데이터를 서버에 올릴 필요가 있는지, 1인 개발자가 서버를 안정적으로 운영할 수 있는지, 오프라인 환경에서 어떻게 동작할 것인지 같은 질문들이 이어졌다.

이 질문들에 답을 하다 보니, 자연스럽게 로컬 우선이라는 방향으로 기울게 된다. 모든 핵심 데이터를 디바이스 안에 두고, 앱 자체가 완결된 구조를 가지는 방식이다. 이 접근은 단순해 보인다. 서버를 줄이면 관리 부담이 줄어들고, 동기화 문제도 사라질 것처럼 보인다. 그래서 이 선택은 처음에는 매우 합리적으로 느껴졌다. 실제로 구현 난이도도 낮아지는 것처럼 보였다. 하지만 이 판단 역시 하나의 가정 위에 서 있었다.

로컬 우선 구조는 단순함을 얻는 대신, 다른 종류의 제약을 만들어낸다. 데이터 정합성을 클라이언트에서 모두 책임져야 하고, 상태 관리가 훨씬 엄격해진다. 서버가 중재자가 되어주지 않기 때문에, 모든 로직이 스스로 일관성을 유지해야 한다. 또한 나중에 확장을 고려하면, 이 구조는 오히려 더 큰 제약이 될 수 있다. 하지만 이 시점에서는 그 문제를 충분히 인식하지 못했다. 서버를 줄였기 때문에 문제가 줄어들 것이라고 생각했다.

결국 이 선택도 하나의 설계 가정이었다. 이전의 상태 기반 모델처럼, 이 역시 나중에 다시 검증되어야 할 대상이었다. 나는 문제를 해결했다고 생각했지만, 실제로는 또 다른 방향으로 문제를 이동시켰을 뿐이었다. 설계는 점점 더 정교해지고 있었지만, 동시에 새로운 불확실성을 만들어내고 있었다. 그리고 이 흐름은 다음 단계에서 다시 한 번 크게 흔들리게 된다.

이 글의 결론 — 나는 문제를 제대로 정의하지 못했다

여기까지의 흐름을 따라오면 하나의 착각이 계속 반복되고 있었다는 것을 알 수 있다. 나는 계속해서 문제를 해결하고 있다고 생각했다. 기록이 없다는 불편함을 발견했고, 그걸 해결하기 위해 구조를 만들고, 모델을 나누고, 데이터를 정리했다. 그리고 그 과정에서 점점 더 정교한 설계로 나아가고 있다고 믿었다. 하지만 실제로 일어난 일은 조금 다르다. 나는 문제를 해결한 것이 아니라, 문제를 계속 다시 정의하고 있었을 뿐이다. 그리고 그 정의는 매번 이전보다 조금씩 더 정확해졌지만, 처음부터 올바른 것은 아니었다.

처음의 출발점은 “기록이 없다”였다. 이 문장은 직관적으로 이해되지만, 실제 문제를 설명하지 못한다. 그 다음 단계에서는 “흐름을 보고 싶다”로 바뀌었다. 이건 방향을 잡는 데는 도움이 되었지만, 여전히 구현 가능한 형태는 아니었다. 이후에는 상태 모델이 깨지고, 시간 단위가 분리되고, 이벤트 중심 구조가 등장했다. 이 과정은 분명히 발전처럼 보인다. 하지만 중요한 건, 이 모든 변화가 처음부터 계획된 것이 아니라, 기존 설계가 더 이상 버틸 수 없어서 발생했다는 점이다. 즉, 설계는 진화한 것이 아니라 밀려난 것이다.

이 지점에서 하나를 명확히 해야 한다. 나는 앱을 만들려고 시작한 것이 아니다. 기능을 구현하려고 한 것도 아니다. 나는 문제를 해결하려고 시작했다. 그런데 그 문제 자체를 정확히 정의하지 못했다. 그래서 설계는 계속 흔들렸다. 하나의 가정을 세우고, 그 가정이 현실과 맞지 않으면 다시 바꾸는 과정이 반복되었다. 이 반복은 비효율처럼 보일 수 있지만, 실제로는 필연적인 과정이었다. 왜냐하면 문제를 제대로 이해하기 전에는 어떤 설계도 안정적으로 유지될 수 없기 때문이다.

결국 이 글의 결론은 단순하다. 나는 처음부터 틀린 상태에서 시작했다. 그리고 그 상태는 쉽게 사라지지 않았다. 오히려 설계를 진행할수록 더 명확하게 드러났다. 하지만 이 사실은 실패라기보다는 출발점에 가깝다. 문제를 잘못 정의했다는 것을 인식하는 순간, 비로소 제대로 된 설계가 시작될 수 있기 때문이다. 이 시리즈는 바로 그 지점 이후의 이야기를 다룬다. 다음 글에서는 이 잘못된 문제 정의 위에서 시작된 도메인 설계가 어떻게 한 번 더 무너졌는지, 그리고 왜 그 붕괴가 필연적이었는지를 구체적으로 다룬다.