이 프로젝트는 문제를 해결하기 위해 시작되지 않았다
대부분의 개발 글은 “문제를 해결하기 위해 시작했다”는 문장으로 출발한다. 독자는 그 문장을 보는 순간 이미 안심한다. 이유가 있고, 방향이 있고, 그 방향에 따라 기술이 선택되었을 것이라고 자연스럽게 가정하기 때문이다. 하지만 실제로 대부분의 개인 프로젝트는 그렇게 시작되지 않는다. 문제를 명확하게 정의한 뒤 해결책을 찾는 구조는 오히려 드물고, 대개는 설명하기 어려운 불편함이나 어딘가 맞지 않는 감각에서 출발한다. 이 프로젝트 역시 마찬가지였다. 분명히 불편했지만, 무엇이 문제인지 언어로 정리할 수 없는 상태였다. 그 상태에서 “이걸 만들어야겠다”는 결정을 내리는 것은 논리라기보다는 충동에 가까웠다.
조금 더 솔직하게 말하면, 이 프로젝트에는 처음부터 해결해야 할 명확한 대상이 없었다. 독서 기록 앱이 필요하다는 생각은 있었지만, 기존 서비스가 왜 부족한지 정확히 설명할 수는 없었다. 단순히 “뭔가 다르다”는 감각만 존재했고, 그 감각이 계속 쌓이면서 어느 순간 임계점을 넘었을 뿐이다. 이 지점이 중요하다. 문제를 정의하지 못한 상태에서 시작된 프로젝트는 처음부터 올바른 설계를 가질 수 없다. 왜냐하면 설계는 문제를 전제로 만들어지기 때문이다. 그런데 문제 자체가 모호한 상태라면, 그 위에 쌓이는 구조 역시 모호할 수밖에 없다. 결국 이 프로젝트의 출발점은 “문제를 해결하기 위한 시도”가 아니라, 설명할 수 없는 불편함을 견디지 못한 상태였다.

이렇게 시작된 프로젝트는 자연스럽게 하나의 특징을 갖는다. 처음부터 방향이 정해져 있지 않기 때문에, 이후의 모든 선택이 일관성을 가지기 어렵다. 어떤 선택은 맞는 것처럼 보이고, 어떤 선택은 나중에 가서 완전히 틀린 것으로 드러난다. 그런데 이건 예외적인 상황이 아니라 오히려 기본 상태에 가깝다. 문제를 명확히 정의하지 못한 채 시작하는 순간, 설계는 더 이상 계획이 아니라 탐색이 된다. 그리고 이 탐색 과정은 대부분 기록되지 않는다. 우리는 보통 결과만 남기고, 그 이전의 혼란과 애매함은 제거해버리기 때문이다. 이 시리즈는 그 제거된 영역에서부터 출발한다.
그래서 우리는 항상 틀린 설계로 시작한다
문제를 제대로 정의하지 못한 상태에서 프로젝트를 시작하면, 사람은 자연스럽게 익숙한 패턴으로 돌아간다. 새로운 구조를 만들어낼 기준이 없기 때문이다. 결국 선택은 논리적 판단이 아니라 경험 기반의 자동 반응이 된다. 이전에 사용했던 구조, 이미 알고 있는 패턴, 익숙하게 다뤄본 기술들이 무의식적으로 끌려온다. 이 프로젝트에서도 같은 일이 반복되었다. 처음에는 “이 정도면 충분하겠지”라는 생각으로 구조를 잡았고, 그 구조는 과거에 여러 번 사용했던 방식과 크게 다르지 않았다. 당연히 그 순간에는 문제가 없어 보였다. 오히려 안정적이고 검증된 선택처럼 느껴졌다.
하지만 시간이 조금만 지나면 그 선택이 어떤 가정 위에 만들어졌는지가 드러나기 시작한다. 그리고 그 가정은 대부분 틀려 있다. 문제를 정확히 정의하지 않았기 때문에, 설계는 실제 요구와 맞지 않는 방향으로 흘러가기 시작한다. 처음에는 작은 불편함으로 나타나지만, 점점 구조 전체를 흔드는 형태로 확대된다. 이 과정에서 중요한 것은 “틀린 설계를 했다”는 사실이 아니다. 더 중요한 것은 틀릴 수밖에 없는 상태에서 시작했다는 점이다. 우리는 종종 잘못된 설계를 개인의 판단 실수로 해석하지만, 실제로는 구조적으로 피할 수 없는 결과인 경우가 많다.

이 지점에서 많은 개발 글이 현실을 왜곡한다. 대부분의 글은 처음부터 올바른 선택을 했던 것처럼 서술된다. 마치 설계가 한 번에 완성된 것처럼 보이도록 구성된다. 하지만 실제 개발 과정은 그와 정반대다. 처음 선택은 대부분 틀리고, 그 틀림을 수정하는 과정이 반복되면서 점점 나아진다. 이 시리즈는 이 전제를 숨기지 않는다. 오히려 이 전제를 중심에 둔다. 우리가 처음에 만든 구조는 틀릴 가능성이 높은 것이 아니라, 거의 확실하게 틀려 있다는 가정에서 출발한다. 그리고 그 틀림이 어떤 방식으로 드러나는지를 따라가는 것이 이 시리즈의 핵심 흐름이 된다.
설계는 만들어지는 것이 아니라, 무너지면서 드러난다
설계를 하나의 완성된 결과물로 보는 시각은 실제 개발 과정을 지나치게 단순화한다. 우리는 종종 설계를 “만드는 것”이라고 표현하지만, 실제로는 만들어진 설계보다 무너진 설계가 더 많은 정보를 제공한다. 이 프로젝트에서도 처음에 세운 구조는 비교적 빠르게 안정된 것처럼 보였다. 기능은 동작했고, 데이터는 흐르고 있었으며, 큰 문제 없이 사용할 수 있는 상태였다. 하지만 그 안정감은 오래가지 않았다. 특정 상황에서 구조가 예상과 다르게 반응하기 시작했고, 그 순간부터 기존 설계의 가정이 하나씩 깨지기 시작했다.
이 과정에서 흥미로운 점은, 설계를 수정하는 순간이 아니라 설계가 깨지는 순간에 더 많은 것이 드러난다는 것이다. 어떤 부분이 유연하지 않은지, 어떤 데이터 흐름이 잘못 연결되어 있는지, 어떤 가정이 현실과 맞지 않는지 등은 설계가 실패할 때 가장 명확하게 보인다. 그리고 이 실패는 단순히 오류를 의미하지 않는다. 오히려 시스템이 어떤 방향으로 재구성되어야 하는지를 알려주는 신호에 가깝다. 문제는 대부분의 경우 이 과정을 기록하지 않는다는 데 있다. 우리는 실패를 제거하고, 수정된 결과만 남긴다. 그 결과, 설계가 왜 그렇게 되었는지 이해할 수 있는 근거가 사라진다.

이 시리즈는 그 제거된 과정을 중심에 둔다. 설계가 어떻게 만들어졌는지가 아니라, 어떻게 무너졌는지를 따라간다. 처음의 가정이 어떤 방식으로 깨졌고, 그 깨짐이 어떤 선택을 강제했는지, 그리고 그 선택이 다시 어떤 문제를 만들어냈는지를 순서대로 드러낸다. 이 접근 방식은 읽기에 불편할 수 있다. 결과를 빠르게 알고 싶은 독자에게는 비효율적으로 느껴질 수 있다. 하지만 설계의 본질은 결과가 아니라 과정에 있다. 결과만으로는 재현할 수 없지만, 과정이 남아 있으면 판단을 재사용할 수 있다. 이 시리즈는 바로 그 지점을 기록하려고 한다.
우리는 결과가 아니라, 실패를 기록하려고 한다
앞에서 이야기한 흐름을 그대로 받아들이면, 이 시리즈가 어떤 형태를 가져야 하는지는 자연스럽게 결정된다. 만약 설계가 실패를 통해 드러난다면, 기록 역시 성공이 아니라 실패를 중심으로 이루어져야 한다. 그런데 대부분의 개발 글은 이 지점에서 정반대의 선택을 한다. 실패는 제거되고, 정리된 결과만 남는다. 코드 역시 가장 깔끔한 상태로 재구성되고, 의사결정 과정은 단순화된다. 그 결과, 글은 읽기 쉬워지지만 동시에 재현 불가능한 형태가 된다. 왜 그 구조를 선택했는지, 어떤 상황에서 그 선택이 무너지는지에 대한 맥락이 사라지기 때문이다.
이 프로젝트에서는 그 방향을 의도적으로 거부한다. 이 글에서 다루는 내용은 “어떻게 하면 잘 만들 수 있는가”가 아니라, 어떻게 실패했고, 그 실패가 어떤 선택을 강제했는가이다. 실패는 단순한 오류나 버그가 아니라, 설계의 한계를 드러내는 사건이다. 그리고 그 사건이 발생하는 순간, 기존의 판단 기준은 더 이상 유효하지 않게 된다. 이때 우리는 새로운 기준을 만들어야 한다. 중요한 것은 이 기준이 어떻게 만들어졌는지를 기록하는 것이다. 그래야만 이후의 선택이 이해될 수 있다. 결과만 남기면, 그 결과는 다른 맥락에서는 아무 의미가 없다.

이 시리즈를 읽다 보면, 어떤 글에서는 결론이 명확하지 않게 느껴질 수 있다. 심지어 “그래서 결국 뭐가 맞는 거지”라는 질문이 남을 수도 있다. 하지만 그 질문 자체가 이 글의 목적에 더 가깝다. 우리는 정답을 제공하려고 하지 않는다. 대신, 어떤 상황에서 어떤 선택이 왜 틀렸는지를 드러내는 것에 집중한다. 그 과정을 통해 독자는 자신의 상황에 맞는 판단 기준을 스스로 구성할 수 있다. 이것이 결과 중심 기록과 실패 중심 기록의 가장 큰 차이다. 결과는 복사할 수 있지만, 판단은 재구성해야 한다. 이 시리즈는 복사가 아니라 재구성을 전제로 한다.
이 시리즈의 단위는 기능이 아니라 “설계 붕괴”다
이제 자연스럽게 다음 질문으로 이어진다. 그렇다면 이 시리즈는 어떤 단위로 구성되어야 하는가. 일반적인 개발 글은 기능 단위로 나뉜다. 로그인 구현, 데이터 저장 구조, UI 구성 방식처럼 명확한 기능을 기준으로 글이 분리된다. 이 방식은 직관적이고 이해하기 쉽다. 하지만 이 프로젝트에서는 그 방식이 적합하지 않다. 기능 단위로 나누는 순간, 설계가 어떻게 무너졌는지에 대한 흐름이 끊어지기 때문이다. 하나의 기능은 대부분 여러 가정 위에 올라가 있고, 그 가정이 깨지는 순간 기능 단위의 경계는 의미를 잃는다.
그래서 이 시리즈는 기능이 아니라 설계가 붕괴되는 지점을 기준으로 나뉜다. 하나의 글은 하나의 붕괴를 다룬다. 그 붕괴는 보통 이렇게 시작된다. 특정한 가정이 존재하고, 그 가정 위에서 구조가 만들어진다. 초기에는 문제가 없어 보이지만, 특정 상황에서 그 가정이 깨진다. 그 순간 기존 구조는 유지될 수 없게 되고, 새로운 선택이 강제된다. 그리고 그 선택 역시 또 다른 가정을 만든다. 이 흐름이 하나의 글을 구성하는 기본 단위가 된다. 즉, 글의 시작은 “어떤 가정을 했는가”이고, 끝은 “그 가정이 어떻게 무너졌는가”이다.

이 구조를 선택하면 글의 성격이 달라진다. 각 글은 독립적인 튜토리얼이 아니라, 이전 글의 결과 위에서만 이해될 수 있는 연속적인 기록이 된다. 어떤 글을 단독으로 읽으면 맥락이 부족하게 느껴질 수 있다. 하지만 순서대로 읽으면 하나의 흐름이 만들어진다. 이 방식은 읽는 데 더 많은 집중을 요구하지만, 대신 설계의 변화 과정을 더 정확하게 전달할 수 있다. 중요한 것은 기능이 아니라, 그 기능이 왜 그런 형태로 바뀌었는가이다. 이 시리즈는 그 “왜”를 중심에 둔다.
이 시리즈는 시간 순서가 아니라 사고 순서로 진행된다
여기까지 오면 또 하나의 선택이 남는다. 이 과정을 시간 순서대로 기록할 것인가, 아니면 다른 기준을 사용할 것인가. 일반적인 개발기는 시간 순서를 따른다. “처음에는 이렇게 시작했고, 다음에는 이것을 추가했고, 이후에는 이런 문제가 발생했다”는 식으로 흐름이 이어진다. 이 방식은 자연스럽지만, 동시에 많은 정보를 왜곡한다. 실제 사고 과정은 시간 순서대로 정렬되지 않기 때문이다. 어떤 판단은 나중에야 의미가 드러나고, 어떤 선택은 이전 단계로 되돌아가면서 수정된다. 시간 순서는 사건의 순서를 보여주지만, 판단의 구조를 설명하지는 못한다.
이 시리즈는 시간 대신 사고의 흐름을 기준으로 구성된다. 어떤 선택이 다음 선택을 어떻게 제한했는지, 어떤 가정이 이후 구조를 어떻게 왜곡했는지를 중심으로 전개된다. 이 방식에서는 과거로 돌아가는 일이 자연스럽게 발생한다. 이미 지나간 단계라도, 그 의미가 뒤에서 드러나면 다시 끌어와 설명한다. 중요한 것은 언제 발생했는지가 아니라, 어떤 맥락에서 이해되어야 하는가이다. 이 접근은 글을 더 복잡하게 만들 수 있다. 하지만 설계의 실제 구조를 드러내기 위해서는 이 정도의 복잡성은 피할 수 없다.

이 방식으로 글을 구성하면 독자는 단순한 진행 상황이 아니라, 판단의 연쇄를 따라가게 된다. 어떤 선택이 왜 이루어졌는지, 그리고 그 선택이 다음 단계에서 어떤 제약을 만들었는지를 이해하게 된다. 이것은 단순히 정보를 전달하는 것과는 다른 경험이다. 결과를 빠르게 얻고 싶은 독자에게는 불편할 수 있지만, 설계를 이해하고 싶은 사람에게는 훨씬 더 많은 단서를 제공한다. 결국 이 시리즈는 “무엇을 만들었는가”보다, “어떻게 생각했는가”를 기록하는 작업에 가깝다. 그리고 그 사고의 흐름은 시간보다 훨씬 복잡한 방식으로 전개된다.
그래서 이 시리즈는 불편하게 읽힌다
여기까지의 구조를 그대로 받아들이면, 이 시리즈가 일반적인 개발 글처럼 읽히지 않는 이유는 이미 결정된 것이나 다름없다. 우리는 의도적으로 결과를 정리하지 않고, 실패를 제거하지 않으며, 사고의 흐름을 그대로 드러낸다. 이 세 가지 선택이 겹치면 글은 자연스럽게 매끄럽지 않게 된다. 어떤 문장은 중간에 끊긴 것처럼 느껴지고, 어떤 전개는 다시 뒤로 돌아가는 것처럼 보인다. 독자는 특정 결론을 기대하고 읽기 시작하지만, 그 결론이 바로 나오지 않기 때문에 답답함을 느낄 수 있다. 하지만 이 불편함은 글쓰기의 부족함이 아니라, 구조적인 선택에서 비롯된 것이다. 오히려 매끄럽게 읽힌다면, 그 글은 이미 많은 것을 제거했을 가능성이 높다.
이 시리즈에서 불편함은 피해야 할 요소가 아니라, 의도적으로 남겨둔 신호에 가깝다. 실제 설계 과정은 정제된 문장처럼 흘러가지 않는다. 중간에 방향이 바뀌고, 이전의 선택을 부정해야 하는 순간이 반복된다. 어떤 판단은 나중에 가서야 의미를 갖게 되고, 어떤 결론은 다시 취소된다. 이러한 과정은 본질적으로 비선형적이며, 따라서 읽는 경험 역시 일정 부분 비선형성을 가질 수밖에 없다. 이 글을 읽는 동안 느껴지는 불편함은 바로 그 비선형성의 흔적이다. 우리는 그 흔적을 제거하지 않는다. 왜냐하면 그것이 설계의 실제 모습에 더 가깝기 때문이다.

이 불편함은 결국 독자의 읽는 방식을 바꾸도록 강제한다. 빠르게 결론을 얻으려는 태도는 이 글에서는 잘 작동하지 않는다. 대신, 각 문장을 하나의 판단 단위로 받아들이고, 그 판단이 어떻게 이어지는지를 따라가야 한다. 이 과정은 더 많은 집중을 요구하지만, 동시에 더 많은 맥락을 제공한다. 중요한 것은 이 글이 독자를 배려하지 않는다는 점이 아니라, 다른 방식으로 배려하고 있다는 점이다. 결과를 쉽게 전달하는 대신, 판단의 구조를 그대로 보여준다. 이 선택은 불편함을 동반하지만, 그 불편함 자체가 하나의 정보로 작동한다.
이 시리즈를 어떻게 읽어야 하는가
이제 이 글을 어떤 방식으로 읽어야 하는지가 자연스럽게 따라온다. 만약 이 시리즈를 튜토리얼처럼 접근한다면, 대부분의 내용은 기대와 맞지 않게 느껴질 것이다. 특정 기능을 어떻게 구현하는지, 어떤 기술을 선택해야 하는지, 정답에 가까운 구조가 무엇인지와 같은 질문은 이 글에서 직접적으로 다루어지지 않는다. 대신, 왜 그런 질문이 생겼는지, 그 질문이 어떤 상황에서 무너지는지를 보여준다. 따라서 이 글을 읽는 방식 역시 달라져야 한다. 핵심은 “무엇을 배울 것인가”가 아니라, “어떤 기준이 만들어지고 있는가”를 따라가는 것이다.
각 글을 읽을 때는 결과를 기억하려 하기보다, 판단이 바뀌는 지점을 주목해야 한다. 어떤 가정이 있었고, 그 가정이 왜 유지되지 못했는지, 그리고 그 순간 어떤 선택지가 등장했는지를 따라가는 것이 중요하다. 이 과정을 통해 독자는 하나의 정답을 얻는 것이 아니라, 여러 개의 판단 기준을 얻게 된다. 그리고 이 기준은 그대로 복사해서 사용할 수 있는 형태가 아니다. 각자의 상황에 맞게 다시 구성해야 한다. 이 시리즈는 바로 그 재구성을 전제로 한다. 읽는 행위 자체가 하나의 설계 과정이 되는 셈이다.

이러한 읽기 방식은 처음에는 낯설게 느껴질 수 있다. 하지만 몇 개의 글을 지나가면, 자연스럽게 패턴이 보이기 시작한다. 특정 상황에서 어떤 선택이 반복되는지, 어떤 가정이 자주 깨지는지, 그리고 그때마다 어떤 방향으로 구조가 이동하는지가 드러난다. 이 패턴을 인식하는 순간, 이 시리즈는 더 이상 단순한 기록이 아니라 하나의 참고 프레임으로 작동하기 시작한다. 중요한 것은 내용을 외우는 것이 아니라, 판단이 움직이는 방식을 이해하는 것이다. 이 글은 그 방식을 직접적으로 설명하지 않는다. 대신, 흐름 속에서 드러나도록 구성되어 있다.
이 시리즈가 다루지 않는 것들
여기까지 읽었다면, 이 시리즈가 무엇을 다루는지는 어느 정도 감이 잡힌다. 그렇다면 반대로, 무엇을 다루지 않는지도 명확히 할 필요가 있다. 이 글은 특정 기술 스택을 추천하지 않는다. 어떤 프레임워크가 더 좋은지, 어떤 아키텍처가 더 효율적인지에 대한 결론을 제공하지 않는다. 또한 완성된 구조를 기준으로 설명하지 않는다. 일반적인 개발 글에서 기대할 수 있는 “이렇게 하면 된다”는 형태의 가이드는 의도적으로 제거되어 있다. 이는 단순한 생략이 아니라, 의도적인 배제에 가깝다. 왜냐하면 이러한 정보는 맥락 없이 전달될 때 오히려 잘못된 판단을 강화할 수 있기 때문이다.
또한 이 시리즈는 최적화를 목표로 하지 않는다. 더 빠른 방법, 더 효율적인 구조, 더 적은 비용으로 구현하는 방법과 같은 질문은 이 글의 중심이 아니다. 물론 결과적으로 그런 선택이 등장할 수는 있지만, 그것이 목표는 아니다. 이 글에서 중요한 것은 선택의 결과가 아니라, 선택이 만들어지는 과정이다. 따라서 특정한 정답을 찾으려는 접근은 이 시리즈와 맞지 않는다. 오히려 “왜 이 선택이 틀렸는가”를 이해하는 것이 더 중요한 지점이다. 이 관점에서 보면, 이 시리즈는 일반적인 기술 문서와는 상당히 다른 방향을 가진다.

이러한 배제는 글의 범위를 좁히는 것이 아니라, 오히려 더 명확하게 만든다. 무엇을 설명하지 않는지를 정하는 순간, 무엇을 설명해야 하는지가 선명해진다. 이 시리즈는 기술 자체를 설명하는 글이 아니라, 기술을 선택하는 과정에서 발생하는 판단의 변화를 기록하는 글이다. 따라서 특정 기술에 대한 깊은 설명을 기대한다면, 이 글은 그 기대를 충족시키지 못할 것이다. 하지만 설계가 어떻게 변해가는지를 이해하려는 목적이라면, 이 시리즈는 다른 종류의 정보를 제공한다. 그리고 그 정보는 종종 정답보다 더 오래 남는다.
이제야 출발점으로 돌아간다
여기까지의 내용은 실제로 무언가를 만들기 위한 과정이라기보다, 어떻게 기록할 것인가에 대한 정의에 가까웠다. 우리는 문제를 명확하게 정의하지 못한 상태에서 시작했고, 그 결과로 틀린 설계에서 출발할 수밖에 없었으며, 설계는 만들어지는 것이 아니라 무너지면서 드러난다는 전제를 받아들였다. 그리고 그 전제를 기반으로, 결과가 아니라 실패를 기록하고, 기능이 아니라 설계 붕괴를 단위로 삼으며, 시간 순서가 아니라 사고의 흐름을 따라가는 구조를 선택했다. 이 모든 설명은 결국 하나의 목적을 가진다. 이 시리즈가 무엇을 하려는 글인지, 그리고 무엇을 하지 않으려는 글인지 명확히 하기 위한 준비 과정이었다.
하지만 이 지점에서 멈춰 있으면, 이 글은 단순한 선언으로 끝난다. 아무리 구조를 정교하게 정의해도, 실제 사례가 없다면 그 구조는 여전히 추상적인 상태에 머무른다. 이제 필요한 것은 정의가 아니라 적용이다. 그리고 그 적용은 언제나 처음으로 돌아가는 것에서 시작된다. 이 시리즈에서의 “처음”은 특정 기능을 구현하는 순간이 아니다. 오히려 그보다 훨씬 이전, 아무것도 정해지지 않은 상태에서 어떤 선택이 시작되었는지를 바라보는 지점이다. 우리는 그 지점으로 다시 돌아가야 한다. 왜냐하면 모든 설계는 결국 그 출발점의 조건에 의해 결정되기 때문이다.

이제 다음 글에서 다루게 될 내용은, 우리가 이 프로젝트를 시작하게 된 가장 초기의 상태다. 그 상태는 정리된 요구사항도 없고, 명확한 목표도 없으며, 심지어 해결해야 할 문제가 무엇인지조차 불분명한 상태다. 하지만 바로 그 अस्पष्ट함이 이후의 모든 선택을 만들어낸다. 어떤 구조를 선택했는지보다, 왜 그런 구조를 선택할 수밖에 없었는지를 이해하려면 이 출발점을 그대로 드러내야 한다. 이 시리즈는 그 과정을 숨기지 않는다. 오히려 가장 먼저 드러낸다. 그리고 그 드러냄이 이후의 모든 글을 연결하는 기준이 된다.
이제야 비로소, 이 글이 실제 개발기로 이어질 준비가 된다. 지금까지는 “어떻게 기록할 것인가”를 정리했다면, 다음부터는 “무엇이 기록될 것인가”를 다루게 된다. 중요한 것은, 이 두 단계가 분리되어 있다는 점이다. 기록 방식이 정의되지 않은 상태에서 시작하면, 결국 다시 결과 중심의 글로 돌아갈 수밖에 없다. 우리는 그 반복을 피하기 위해 이 과정을 먼저 거쳤다. 그리고 이제 그 기반 위에서, 실제로 어떤 선택들이 이루어졌고, 그 선택들이 어떻게 무너졌는지를 따라가기 시작한다. 이 시리즈는 여기서부터 비로소 움직이기 시작한다.