<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>dodokim_lab.log</title>
        <link>https://velog.io/</link>
        <description>AI를 부려 낯선 도메인을 해체하고 현장의 문제를 해결합니다.</description>
        <lastBuildDate>Thu, 01 Oct 2026 01:20:21 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>dodokim_lab.log</title>
            <url>https://velog.velcdn.com/images/dodokim_lab/profile/deba8c6d-5643-4868-833f-f555441e44e3/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. dodokim_lab.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/dodokim_lab" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[알림톡에는 파일을 못 붙인다]]></title>
            <link>https://velog.io/@dodokim_lab/four-decisions</link>
            <guid>https://velog.io/@dodokim_lab/four-decisions</guid>
            <pubDate>Thu, 01 Oct 2026 01:20:21 GMT</pubDate>
            <description><![CDATA[<h3 id="알림만-보내면-되는-줄-알았다">알림만 보내면 되는 줄 알았다</h3>
<p>신고가 끝나면 고객에게 알린다. 실무자가 하던 일이고, 그걸 시스템이 보내게 만들었다. 메시지 종류를 정하고, 보낸 기록이 남는 틀과 팝빌로 내보내는 연결을 만들었다. 여기까지는 하루 안에 됐다.</p>
<p>만들고 나서 빠진 게 보였다. 고객이 받아야 하는 건 &quot;끝났습니다&quot;라는 말이 아니라 신고서와 납부서 같은 서류다. 그런데 알림톡에는 파일을 못 붙이고, 글자와 버튼만 들어간다.</p>
<p>알림을 보내는 쪽을 만들어놓고, 정작 보내야 하는 건 못 보내는 상태였다.</p>
<h3 id="버튼-뒤에-링크를-뒀다">버튼 뒤에 링크를 뒀다</h3>
<p>서류는 서버에 올려두고, 버튼에 그 서류로 가는 주소를 달았다. 고객이 버튼을 누르면 서류가 내려온다. 세무나 법무 쪽에서는 다들 이렇게 한다. 메시지 규정에도 걸리지 않는다.</p>
<p>이때 그 주소는 열쇠 자체였다. <strong>주소를 아는 사람이 곧 받는 사람이다.</strong> 주소가 카톡 대화창에 남고, 그 폰을 여는 사람이면 누구나 연다.</p>
<p>본인 확인을 붙일까 생각했다. 그리고 과하다고 적었다. 고객용 사이트를 따로 만들거나 인증을 붙이는 건 혼자 돌리는 시스템에 오버스펙이라고. 주소를 아무나 못 맞히게 길게 만들고, 며칠 지나면 죽게 했다. 그 정도면 된다고 봤다.</p>
<h3 id="하루-만에-뒤집었다">하루 만에 뒤집었다</h3>
<p>그 판단은 하루를 못 갔다.</p>
<p>버튼을 누르면 파일이 바로 내려오지 않고 페이지가 하나 뜬다. 그 페이지가 휴대폰 끝 네 자리를 묻는데, 알림이 간 바로 그 번호의 끝자리다. 맞으면 그때 서류가 내려온다. 몇 번 틀리면 그 링크는 죽고, 실무자가 다시 보내야 한다.</p>
<p>대가가 있었다. 확인 페이지를 웹사이트 쪽 새 주소에 두면 버튼 주소도 바뀌고, 버튼 주소가 바뀌면 카카오 쪽 심사를 처음부터 다시 받아야 한다. 페이지는 웹사이트가, 파일은 서버가 맡는 구조가 심사를 한 번 더 기다리는 것보다 낫다고 보고 알면서 바꿨다. 오버스펙이라고 적은 지 하루 만에 그걸 넣었고, 심사도 처음부터 다시 신청했다.</p>
<h3 id="신고-한-건은-폴더째-떨어진다">신고 한 건은 폴더째 떨어진다</h3>
<p>링크 하나에 파일 하나로 만들었다. 서류를 하나 올리면 링크가 하나 나온다.</p>
<p>실제로 신고 한 건이 끝나면 파일이 하나가 아니다. 신고서, 납부서, 영수증, 그 신고에 쓴 자료까지 폴더째 떨어진다. 실무자는 그 폴더를 통째로 보내야 한다.</p>
<p>선택지가 셋이었다.</p>
<ul>
<li>폴더를 압축해 파일 하나로 올린다.</li>
<li>파일마다 알림을 따로 보낸다.</li>
<li>링크 하나에 파일 여러 개를 매단다.</li>
</ul>
<p>압축 하나면 보내는 쪽은 링크 하나로 끝난다. 그런데 받는 쪽은 폰이다. <strong>폰에서 압축 파일은 여는 것부터 일이다.</strong></p>
<p>세 번째로 갔다. 링크를 열면 파일 목록이 뜨고 파일마다 따로 받는다. 한꺼번에 받는 버튼도 있는데, 그건 전체를 압축해서 내려준다. 거절한 건 압축이 아니라 압축 하나만 주는 쪽이었다. 본인 확인은 한 번만 하면 된다.</p>
<h3 id="결정은-넷이었다">결정은 넷이었다</h3>
<p>&quot;서류 보내드릴게요&quot;는 한 문장이다. 시스템으로 옮기니 결정이 넷으로 갈렸다.</p>
<ul>
<li>어디로 보내나. 카톡으로.</li>
<li>뭘로 보내나. 파일이 아니라 링크로.</li>
<li>누가 여나. 그 번호의 주인이.</li>
<li>몇 개를 보내나. 폴더째.</li>
</ul>
<p>처음부터 있던 건 어디로 하나였다. 나머지 셋은 만들고 나서야 보였고, 이틀 동안 세 번 고쳤다.</p>
<h3 id="남길-것">남길 것</h3>
<p>세 번 고친 것을 나란히 놓고 보니 공통점이 하나 있었다. 셋 다 <strong>받는 사람 폰 화면에 있던 것들</strong>이다. 파일이 안 붙는 것도, 링크만 알면 열리는 것도, 압축 하나로 주면 폰에서 풀어야 하는 것도.</p>
<p>나는 보내는 쪽부터 만들었다. 메시지를 고르고, 기록을 남기고, 팝빌로 내보내는 길까지 깔았다. 받는 사람 화면은 그리지 않았고, 그래서 그 화면에 있던 것들이 하나씩 뒤늦게 왔다.</p>
<p>&quot;보내드릴게요&quot;를 시스템으로 옮기면 그 한 문장은 사라지고 결정 넷이 남는다. 넷은 받는 쪽에서 세야 한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[우리는 자격이 안 됐다]]></title>
            <link>https://velog.io/@dodokim_lab/what-i-actually-wanted</link>
            <guid>https://velog.io/@dodokim_lab/what-i-actually-wanted</guid>
            <pubDate>Mon, 28 Sep 2026 12:08:02 GMT</pubDate>
            <description><![CDATA[<h3 id="직접-붙고-싶었다">직접 붙고 싶었다</h3>
<p>신고가 끝나면 고객에게 알림을 보낸다. 그때까지는 사람이 하나씩 보내고 있었고, 이걸 우리 시스템에서 보내게 하려고 했다.</p>
<p>경로는 카카오톡 알림톡으로 정했다. 신고가 끝났다는 통지는 알림톡이 받는 정보성 메시지에 그대로 맞고, 받는 사람이 채널을 친구로 추가하지 않아도 받는다.</p>
<p>계획서에는 연결 방식을 &quot;직연동&quot;이라고 적었다. 중간에 아무것도 안 끼우고 우리 서버가 카카오에 바로 붙는 것이다.</p>
<h3 id="아무나-못-붙는다">아무나 못 붙는다</h3>
<p>알아보니 그렇게 되는 구조가 아니었다.</p>
<p>카카오톡 알림은 카카오가 심사해서 인증한 회사들만 본사 시스템에 직접 연결할 수 있다. 나머지는 전부 이 심사통과업체를 거쳐서 보낸다. 예외가 없다. 팝빌 같은 중계 서비스들이 심사통과업체를 거쳐 알림톡을 보내주는 쪽이다.</p>
<p>인증을 받으려면 발송 실적과 매출, 보증금, 기술 심사를 넘어야 한다. 앞의 셋은 규모를 보는 조건이다.</p>
<p>실무자 세 명인 세무사무소가 낼 수 있는 게 아니다. 계획서에는 글자 그대로 추진하면 <strong>영업 응대조차 받기 어렵고</strong>, 받더라도 자격이 안 된다고 적어뒀다.</p>
<p>이건 기술 문제가 아니다. 코드를 잘 짜서 넘을 수 있는 벽이 아니다. 작은 사업장에서 뭔가를 붙이려고 할 때 자주 만나는 종류의 벽이고, 대개 문서 어디에도 &quot;당신은 자격이 없습니다&quot;라고 안 적혀 있다. 조건을 읽고 스스로 알아채야 한다.</p>
<h3 id="직연동에는-뜻이-둘-섞여-있었다">&quot;직연동&quot;에는 뜻이 둘 섞여 있었다</h3>
<p>남은 길은 하나였다. 심사통과업체를 거쳐서 보내는 것이다. 그러면 계획서의 &quot;직연동&quot;은 통째로 막힌 셈인데, 그 단어를 다시 뜯어보니 뜻이 둘 섞여 있었다.</p>
<p>하나는 <strong>카카오와 직접 계약을 맺는 것</strong>이다. 이건 자격이 안 돼서 불가능하다.</p>
<p>다른 하나는 <strong>사람이 남의 사이트에 로그인해서 손으로 보내지 않는 것</strong>이다. 중계 서비스들은 대개 자기네 웹 화면을 주고, 거기 로그인해서 보내면 발송은 된다. 다만 그건 우리 시스템 밖에서 일어나는 일이다. 원래 만들려던 것에는 보낸 기록을 우리 쪽에 쌓고 실패한 건 우리 화면에서 다시 보내는 일까지 들어 있었다. 남의 화면에서 보내면 그 기록부터 우리 쪽에 없다.</p>
<p>내가 진짜로 원한 건 두 번째였다. 첫 번째는 그걸 이루는 방법이라고 착각한 거였다.</p>
<p>비교도 처음부터 잘못 세워져 있었다. &quot;직접 붙는 것&quot;과 &quot;거쳐서 보내는 것&quot;을 견주고 있었는데, 직접 붙는 건 애초에 선택지가 아니었다. 실제 선택지는 &quot;거쳐서 보내는 것&quot;과 &quot;지금처럼 사람이 손으로 보내는 것&quot; 둘이었다.</p>
<p>그러면 답이 나온다. <strong>중계 서비스를 거치더라도, 그쪽이 열어둔 연결 통로를 우리 서버가 직접 부르면 된다.</strong> 사람은 남의 콘솔에 로그인하지 않고, 발송은 우리 화면에서 우리 시스템을 통해 나간다. 원하던 건 그대로 된다.</p>
<h3 id="팝빌은-이미-있었다">팝빌은 이미 있었다</h3>
<p>어디를 거칠지도 답이 있었다. 이 시스템을 처음 짤 때부터 외부 연동은 팝빌로 하기로 정해두고 계정도 열어뒀는데, 팝빌이 바로 그 알림톡 중계를 하는 곳이었다. 새로 계약할 곳도, 새로 결제를 걸 곳도 없었다.</p>
<p>처음 짤 때 규칙도 하나 걸어뒀다. 팝빌 코드는 연동 전용 자리에만 들어오고, 핵심 로직 쪽에서 팝빌을 직접 부르면 빌드가 깨진다. 나중에 중계 서비스를 바꿔야 하면 그 자리 하나만 바꾸면 된다. 그렇게 자리만 만들어두고, 팝빌을 실제로 부른 건 알림톡이 처음이었다.</p>
<p>1인이 운영하는 시스템에서는 <strong>관리해야 할 계정이 하나 늘어나는 게 기능 하나 만드는 것보다 무거울 때가 있다.</strong> 결국 거래처는 하나도 안 늘리고 끝났다.</p>
<h3 id="팝빌로-알림톡을-붙이는-순서">팝빌로 알림톡을 붙이는 순서</h3>
<p>붙이는 순서는 넷이다.</p>
<ol>
<li>사무소 카카오 채널을 팝빌에 연결한다. 알림톡은 카카오 비즈니스 채널 이름으로 나가는데, 채널은 이미 만들어져 있었다. 팝빌 관리 화면에서 그 채널을 등록한다.</li>
<li>보낼 문구를 팝빌에 템플릿으로 올려 카카오 심사를 받는다. 알림톡은 심사를 통과한 문구만 보낸다. 정보를 알리는 통지만 되고 광고처럼 읽히는 표현이나 혜택 안내가 섞이면 떨어진다. 굵은 글씨는 아예 안 되고 이모지도 줄여야 해서, 내가 써둔 초안에서 굵은 글씨가 빠지고 이모지 다섯 개가 빠졌다. 세목마다 문구가 따로 필요해 다섯 종을 잡았다가, 종소세 납부 안내와 환급 안내 두 종으로 먼저 시작하고 나머지는 운영이 자리 잡으면 붙이기로 했다. 심사는 한 번에 2~3영업일쯤 걸리고 문구를 고치면 다시 받는다. 넷 중 제일 오래 걸리는 게 이 심사다.</li>
<li>팝빌 연동 키를 서버에 넣는다. 키는 시험용과 운영용이 따로다. 가입하면 시험용이 나오고, 운영용은 운영 전환을 신청해야 메일로 따로 온다. 시험 환경은 요금이 나가지 않고, 운영 쪽에서 심사를 통과한 채널과 템플릿이 시험 쪽에도 따라온다. 시험용 키로 운영 쪽을 부르면 오류로 돌아온다.</li>
<li>서버가 팝빌 SDK로 알림톡을 보낸다. 고객 이름이나 금액 같은 빈칸은 우리 시스템이 채우고, 템플릿은 팝빌이 매긴 번호로 고른다. 카톡이 안 닿는 사람에게 문자로 대신 보내는 기능도 있는데, 그러려면 보내는 번호를 팝빌에 따로 인증받아야 하고 통신사에서 이용증명원을 떼어 올려야 한다. 처음엔 알림톡만으로 시작하기로 했다.</li>
</ol>
<h3 id="붙여보니-계획서와-달랐던-것">붙여보니 계획서와 달랐던 것</h3>
<p>계획서대로 되지 않은 게 셋이었다.</p>
<ol>
<li>계획서에는 채널을 연결한 뒤 &#39;발신 키&#39;를 따로 받는 단계가 있었다. 막상 SDK를 열어보니 그런 값을 넣는 자리가 아예 없었고, 채널만 연결돼 있으면 보낼 수 있었다.</li>
<li>템플릿 번호도 예상과 달랐다. 카카오 쪽에서 매기는 템플릿 ID를 쓰는 줄 알았는데, 발송 때 넘기는 건 팝빌이 따로 매긴 숫자였다.</li>
<li>팝빌 키를 비워둔 채 운영 서버에 배포했더니 서버가 통째로 안 떴다. 키는 심사가 끝나면 넣을 생각이었다. 키가 없으면 알림 쪽만 꺼지게 해뒀는데, 그 알림 쪽을 쓰는 다른 부분이 빈자리를 못 견뎠다. 키 칸에 &quot;심사 끝나면 교체&quot;라는 임시 값을 채우고서야 올라왔다.</li>
</ol>
<p>셋째는 처음 짤 때 걸어둔 규칙과 부딪힌다. 팝빌 코드는 한 자리에 가둬뒀는데, 그 자리가 비자 서버 전체가 섰다. 남의 코드를 한곳에 가두는 것과, 남이 없을 때도 버티게 만드는 건 다른 일이었다.</p>
<h3 id="남길-것">남길 것</h3>
<p>계획서에 &quot;직연동&quot;이라고 적었을 때, 나는 방법을 목표 자리에 적어두고 있었다. 그래서 그 방법이 막히자 목표까지 같이 막힌 것처럼 보였다.</p>
<p>막힌 게 목표인지 방법인지는 구분해야 한다. 대부분은 방법이 막힌다. 그리고 방법이 막혔을 때 물어볼 건 <strong>어떻게 뚫느냐가 아니라, 내가 원래 뭘 하려던 거였느냐</strong>다.</p>
<p>작은 사업장에서 뭘 붙이려다 자격에서 걸리는 일은 앞으로도 계속 있을 것이다. 그때마다 규모를 키워서 자격을 맞출 수는 없다. 대신 원하던 것을 다시 적어보면 대개 다른 길이 하나쯤 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[데이터가 없는 줄 알았다]]></title>
            <link>https://velog.io/@dodokim_lab/the-data-was-there</link>
            <guid>https://velog.io/@dodokim_lab/the-data-was-there</guid>
            <pubDate>Sat, 26 Sep 2026 01:45:42 GMT</pubDate>
            <description><![CDATA[<h3 id="가르칠-게-없다고-생각했다">가르칠 게 없다고 생각했다</h3>
<p>시스템이 지출 항목을 자동으로 붙이려면 기준이 있어야 한다. 어떤 가게가 어떤 항목으로 가는지를 어디선가 배워야 한다.</p>
<p>처음엔 그걸 앞으로 쌓을 생각이었다. 실무자가 쓰면서 고쳐주면 그게 쌓여서 기준이 된다. 맞는 말인데, 그러면 <strong>시작하는 순간에는 아무것도 없다.</strong> 처음 몇 달은 시스템이 거의 못 맞추고, 실무자는 전부 손으로 고쳐야 한다. 그 구간을 못 넘기면 아무도 안 쓴다.</p>
<p>그래서 시작할 때 쓸 게 뭐가 있나 찾아봤다.</p>
<h3 id="엑셀-두-장">엑셀 두 장</h3>
<p>있었다.</p>
<p>지난 반년 동안 실무자가 손으로 처리해온 기록이었다. 한 장은 받은 그대로의 원본이고, 한 장은 그걸 정리해서 회계 프로그램에 올린 결과다. 오백 건쯤 됐다.</p>
<p>두 장을 나란히 놓으면 사람이 내린 판단이 그대로 보인다. 이 줄에 있던 가게가 결과에서는 이 항목으로 가 있다. 저 가게는 저 항목으로 갔다.</p>
<p>이게 정답지다. 문제와 답이 짝지어진 채로 반년치가 쌓여 있었다. 다만 그게 &quot;학습 데이터&quot;라고 이름 붙어 있지 않았을 뿐이다. 그냥 지난 일이 남아 있는 폴더였다.</p>
<h3 id="하나씩-짝지으면-안-되는-이유">하나씩 짝지으면 안 되는 이유</h3>
<p>처음 만든 건 단순했다. 가게 이름과 항목을 하나씩 묶는다. 이 가게가 나오면 이 항목.</p>
<p>돌려보니 맞긴 맞는다. 그 오백 건 안에 있는 가게는 다 맞춘다.</p>
<p>문제는 그 밖이다. <strong>오백 건에 없던 가게가 나오면 시스템은 아무 말도 못 한다.</strong> 그리고 새로운 가게는 매달 나온다. 사업하는 곳이 늘 가던 데만 가지 않는다.</p>
<p>이렇게 만들면 반년치를 통째로 외운 것이지 배운 게 아니다. 외운 것은 새로운 걸 만났을 때 아무 소용이 없다.</p>
<h3 id="거꾸로-읽기">거꾸로 읽기</h3>
<p>방향을 바꿨다.</p>
<p>가게에서 항목으로 가는 대신, <strong>항목에서 가게로 거꾸로 갔다.</strong> 같은 항목으로 처리된 가게들을 한 무더기로 모아놓고, 그 이름들에서 겹치는 말을 뽑았다.</p>
<p>그러면 개별 가게가 아니라 <strong>사람이 무엇을 보고 그 항목으로 정했는지</strong>가 나온다. 이름에 이런 말이 들어 있으면 대체로 이쪽이더라, 하는 것들이다. 처음 보는 가게라도 이름에 그 말이 있으면 같은 판단을 할 수 있다.</p>
<p>한 사람이 반년 동안 무의식적으로 쓰던 기준이, 뽑아놓고 보니 눈에 보이는 목록이 됐다. 누가 가르쳐준 적 없이 실무 안에서 굳어진 기준이었다.</p>
<h3 id="반년-전-답이-다시-나오는가">반년 전 답이 다시 나오는가</h3>
<p>뽑았다고 바로 켜지는 않았다.</p>
<p>두 가지를 했다. 먼저 <strong>뽑힌 걸 초안으로 세워두고 사람이 먼저 봤다.</strong> 어떤 무더기에서 어떤 말이 뽑혔는지를 화면에 늘어놓고, 확인한 것만 실제로 켜지게 했다. 뽑는 과정이 아무리 그럴듯해도 그 결과가 회계 판단으로 맞는지는 다른 문제다. 사람이 한 번 보고 넘기는 단계를 없애면, 틀린 기준이 조용히 전체에 퍼진다.</p>
<p>그다음이 검증이다. 뽑은 기준으로 <strong>원본을 처음부터 다시 돌려, 반년 전에 사람이 낸 답과 맞춰보는 검사를 붙여뒀다.</strong> 기준을 손볼 때마다 이 검사를 돌리면 된다.</p>
<p>이게 좋은 검증인 이유는 답을 내가 만들지 않았다는 데 있다. 이미 사람이 낸 답이 있고, 그건 내가 손댈 수 없다. 시스템이 그 답에 얼마나 가는지만 본다.</p>
<h3 id="남길-것">남길 것</h3>
<p>자동화를 붙이려는 곳에서 제일 자주 듣는 말이 &quot;우리는 데이터가 없어요&quot;다.</p>
<p>없는 경우도 있다. 그런데 대부분은 <strong>데이터가 데이터처럼 안 생겨서</strong> 없다고 느끼는 쪽이다. 사람이 손으로 해온 일이 어딘가 파일로 남아 있으면, 그건 이미 정답지다. 그 일을 왜 그렇게 했는지는 본인도 설명 못 할 때가 많은데, 결과를 모아놓고 거꾸로 보면 기준이 드러난다.</p>
<p>시작할 때 필요한 건 새 데이터가 아니라 <strong>이미 한 일을 다시 읽는 일</strong>이었다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[카드사 여섯 곳, 양식이 여섯 개였다]]></title>
            <link>https://velog.io/@dodokim_lab/outside-the-code</link>
            <guid>https://velog.io/@dodokim_lab/outside-the-code</guid>
            <pubDate>Fri, 11 Sep 2026 07:30:37 GMT</pubDate>
            <description><![CDATA[<h3 id="여섯-곳이-여섯-개-다-달랐다">여섯 곳이 여섯 개 다 달랐다</h3>
<p>명세 파일을 올리면 시스템이 읽어서 거래 목록으로 바꾼다. 처음엔 한 카드사 것만 읽을 줄 알았다. 그 카드사 파일로 만들었으니까.</p>
<p>다른 카드사 파일을 올리면 그냥 죽었다. 읽을 줄 아는 표시를 못 찾으니 거기서 멈춘다.</p>
<p>그래서 실제로 쓰는 카드사 명세를 모아 나란히 열어봤다. 여섯 곳이었고, 양식이 여섯 개 다 달랐다. 어떤 건 다섯 번째 줄부터 표가 시작되고 어떤 건 열두 번째 줄부터 시작한다. 항목 이름도 제각각이다. 어떤 곳은 &quot;가맹점명&quot;이라고 쓰고 어떤 곳은 다른 말을 쓴다.</p>
<p>여기까지는 예상한 범위였다. 예상 못 한 건 그다음이었다.</p>
<h3 id="같은-카드사인데-두-개다">같은 카드사인데 두 개다</h3>
<p>한 카드사는 혼자서 양식이 두 개였다.</p>
<p>부가세 신고용으로 받은 파일과, 그냥 이용내역으로 받은 파일의 생김새가 달랐다. 카드사 사이트에서 무엇을 목적으로 받느냐에 따라 다른 파일이 내려온다. 실무자는 필요에 따라 둘 다 쓴다.</p>
<p>그러니까 &quot;어느 카드사냐&quot;만으로는 부족하다. <strong>어느 카드사에서 무엇으로 받았느냐</strong>까지 봐야 한다.</p>
<p>그리고 두 곳은 파일에 암호가 걸려 있었다. 우리가 건 게 아니라 카드사 사이트에서 받으면 그렇게 나온다. 열려면 암호가 필요한데, 그 전까진 파일 안을 볼 수조차 없다.</p>
<h3 id="고르게-할-것인가-알아보게-할-것인가">고르게 할 것인가, 알아보게 할 것인가</h3>
<p>처음 화면에는 카드사를 고르는 칸이 있었다. 올릴 때 카드사를 선택하면 시스템이 거기에 맞춰 읽는다.</p>
<p>만드는 쪽에서는 이게 제일 깔끔하다. 판별을 안 해도 되고, 틀릴 일도 없다.</p>
<p>쓰는 쪽에서 보면 다르다. 실무자는 파일을 내려받고, 그게 어느 카드사 건지 확인하고, 목록에서 그걸 찾아서 고르고, 그다음에 올린다. 게다가 같은 카드사에 양식이 둘이면 그 둘 중 뭘 받았는지도 기억해야 한다. 파일 하나 올리는 데 판단이 두 번 들어간다.</p>
<p>업체를 백 곳 넘게 담당하는 사람에게 판단 두 번은 작은 게 아니다. 그리고 이건 <strong>시스템이 이미 알 수 있는 것을 사람에게 묻는 일</strong>이다. 파일 안에 답이 들어 있으니까.</p>
<h3 id="지문으로-알아맞히기">지문으로 알아맞히기</h3>
<p>칸을 없앴다. 대신 파일을 열어 몇 군데만 보고 어디 건지 판정하게 했다.</p>
<p>시트 이름이 무엇인지, 표가 몇 번째 줄부터 시작하는지, 그 줄에 어떤 말이 적혀 있는지. 이 조합이 카드사마다 다르다. 겹치는 곳이 없어서 몇 군데만 보면 갈린다. 사람이 파일을 열었을 때 &quot;아 이건 어디 거네&quot; 하고 아는 그 근거를 그대로 옮긴 셈이다.</p>
<p>암호가 걸린 파일은 먼저 그것부터 알아챈다. 판별을 시도하다 막히면 그때 암호를 묻는다. 안 걸린 파일에는 묻지 않는다. <strong>모든 사람에게 암호를 묻고 대부분이 빈칸으로 넘기는 화면을 만들지 않으려고</strong> 이렇게 했다.</p>
<p>결과적으로 실무자 화면에서 할 일은 하나가 됐다. 파일을 던진다.</p>
<h3 id="바뀔-걸-아는-부분은-코드-밖으로">바뀔 걸 아는 부분은 코드 밖으로</h3>
<p>여기까지 만들고 나서 걸린 게 하나 있었다. <strong>이 양식들은 반드시 바뀐다.</strong></p>
<p>카드사가 사이트를 개편하면 열이 하나 늘거나 이름이 바뀐다. 우리한테 알려줄 리도 없다. 어느 날 갑자기 실무자가 올렸는데 안 읽히는 걸로 알게 된다.</p>
<p>그때마다 코드를 고쳐야 한다면, 한 줄 바꾸자고 빌드하고 배포하는 일을 반복하게 된다. 그러면 급할수록 손이 굼떠진다.</p>
<p>그래서 어느 칸이 무슨 뜻인지를 코드 밖 설정으로 뺐다. 양식이 바뀌면 그 설정만 고친다. 판별 방식과 읽는 방식은 그대로 두고, 자주 바뀌는 것만 밖에 둔 것이다.</p>
<h3 id="남길-것">남길 것</h3>
<p>두 가지가 남았다.</p>
<p>하나는 <strong>선택지를 주는 게 친절이 아니라는 것</strong>이다. 고르게 하면 만드는 쪽은 편하지만 그 편함은 쓰는 사람의 판단으로 산 것이다. 시스템이 알아낼 수 있는 걸 사람에게 묻고 있지 않은지 한 번씩 봐야 한다.</p>
<p>다른 하나는 <strong>바뀔 걸 아는 부분을 코드 안에 두면 언젠가 사고가 된다는 것</strong>이다. 남이 정하는 형식, 남이 바꾸는 규격은 내 코드의 일부가 아니다. 밖에 두고 갈아 끼울 수 있게 해두면, 바뀌는 날이 사고가 아니라 그냥 작업이 된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[세 번 가르치면 자동이 될 줄 알았다]]></title>
            <link>https://velog.io/@dodokim_lab/stopped-counting</link>
            <guid>https://velog.io/@dodokim_lab/stopped-counting</guid>
            <pubDate>Tue, 08 Sep 2026 04:20:04 GMT</pubDate>
            <description><![CDATA[<h3 id="검수가-안-줄어드는-자동화">검수가 안 줄어드는 자동화</h3>
<p>카드 명세가 들어오면 시스템이 지출 항목을 붙인다. 틀린 건 실무자가 고친다. 그 고친 걸 시스템이 배워서 다음부터 알아서 붙이면, 손이 점점 덜 간다. 여기까지가 처음 그림이었다.</p>
<p>첫 설계는 이랬다. 실무자가 고치면 그게 규칙 후보로 쌓이고, 나중에 후보 목록을 열어 승인하면 규칙이 된다.</p>
<p>숫자를 넣어보니 이상했다. 100건을 고치면 규칙 후보가 30개쯤 생긴다. 그러면 100건을 본 다음에 30개를 또 봐야 한다.</p>
<p><strong>검수가 안 준다.</strong> 줄이려고 만든 건데 일이 하나 더 붙었다. 이러면 자동화라고 부를 이유가 없다.</p>
<p>그래서 순서를 뒤집었다. 사람이 규칙을 만들고 시스템이 적용하는 게 아니라, <strong>사람은 고르기만 하고 규칙은 시스템이 만든다.</strong> 고치는 행위 자체가 규칙 등록이 되게 하는 것이다.</p>
<h3 id="라벨링이-곧-규칙이-되게-했다">라벨링이 곧 규칙이 되게 했다</h3>
<p>바로 규칙으로 만들면 위험한 경우가 있다. 실무자가 고치는 이유가 두 갈래이기 때문이다.</p>
<p>하나는 &quot;이번 건만 예외&quot;다. 평소엔 복리후생비로 처리하는 회식이 이번엔 접대비인 경우. 다른 하나는 &quot;앞으로 이건 다 이렇게&quot;다. 앞엣것을 규칙으로 만들면 다음 달 회식까지 접대비가 된다.</p>
<p>그래서 버튼을 갈랐다. 인라인으로 고치면 그 건만 바뀌고, 규칙으로 만들려면 별도 버튼을 누른다. 그리고 <strong>규칙 버튼을 같은 가맹점에 세 번 누르면 네 번째부터 시스템이 알아서 붙게</strong> 했다.</p>
<p>세 번을 둔 이유는 단순하다. 한 번은 실수일 수 있다. 세 번이면 실수가 아니다.</p>
<h3 id="열흘-동안-규칙이-하나도-안-생겼다">열흘 동안 규칙이 하나도 안 생겼다</h3>
<p>만들어서 올리고 열흘쯤 지나서 봤다.</p>
<p>자동으로 붙은 게 하나도 없었다. 규칙이 아예 만들어지지 않았다.</p>
<p>기록을 열어보니 실무자가 규칙 버튼을 안 누른 게 아니었다. 여러 번 눌렀다. 그런데 <strong>카운트가 전부 1에서 멈춰 있었다.</strong></p>
<h3 id="사람-눈에-같은-가게-기계-눈에-남남">사람 눈에 같은 가게, 기계 눈에 남남</h3>
<p>같은 카페 체인인데 지점 이름이 붙어 있었다. 한 지점에서 한 번, 다른 지점에서 한 번. 사람이 보면 둘 다 같은 카페고 같은 항목으로 처리할 게 뻔하다. 시스템은 가맹점 이름을 통째로 놓고 세고 있었으니 둘을 남남으로 봤다.</p>
<p>각자 하나씩 세다가 끝난다. 셋에 영원히 못 닿는다.</p>
<p>이게 특이한 상황이 아니라는 게 문제였다. 카드 명세에 찍히는 가맹점 이름은 원래 지점 단위다. 체인점에서 결제하면 거의 항상 이렇게 된다. 즉 <strong>자주 가는 곳일수록 학습이 안 되는 구조</strong>였다.</p>
<p>묶는 방법이야 있었다. 지점 이름을 떼고 앞부분만 보고 세면 된다. 그런데 그러면 이름이 비슷한 다른 가게까지 같이 묶인다. 앞부분이 몇 글자냐를 정하는 순간, 그 숫자가 틀리는 경우를 계속 만나게 된다.</p>
<p>대표님한테 물었다. 판단은 묶지 말자는 쪽이었다. <strong>같은 체인이라도 지점이 다르면 다른 가게로 친다.</strong> 하나의 가맹점 이름에 하나의 규칙을 붙인다.</p>
<p>그러면 세 번을 세는 장치는 의미가 없어진다. 각 가맹점은 어차피 따로 세어질 테니까. 그래서 세는 걸 통째로 없앴다. 실무자가 규칙 버튼을 한 번 누르면 그 자리에서 규칙이 된다.</p>
<h3 id="그래도-검수는-줄여야-했다">그래도 검수는 줄여야 했다</h3>
<p>세는 걸 없앴다는 건 <strong>틀린 규칙이 바로 들어올 수 있다</strong>는 뜻이다. 처음에 세 번을 뒀던 이유가 그거였으니까.</p>
<p>여기서 방향을 바꿨다. 잘못 들어오는 걸 막는 대신, <strong>잘못 들어와도 싸게 되돌릴 수 있게</strong> 만들었다.</p>
<p>거래를 세 등급으로 나눴다. 확실한 건 자동으로 통과시키고 개수만 보여준다. 애매한 건 한 화면에 모아서 한 번에 승인한다. 못 맞춘 것만 실무자가 개별로 본다. <strong>실무자는 사실상 마지막 것만 본다.</strong></p>
<p>그리고 신고를 확정하기 전까지는 자동으로 붙은 것도 한 번에 되돌릴 수 있게 했다. 되돌리면 같은 패턴이 한꺼번에 다시 분류되고 그 규칙은 꺼진다.</p>
<p>되돌리는 비용이 싸니까 자동 적용을 과감하게 할 수 있다. 반대로 되돌리기가 비싸면 임계를 높이게 되고, 임계가 높으면 아무것도 안 배운다. <strong>처음에 세 번을 뒀던 건 사실 이 준비가 안 돼 있어서였다.</strong></p>
<h3 id="남길-것">남길 것</h3>
<p>세 번이라는 숫자를 정할 때 나는 학습의 문제를 풀고 있다고 생각했다. 몇 번이면 믿을 만한가.</p>
<p>실제 문제는 다른 데 있었다. <strong>무엇과 무엇을 같은 것으로 볼 것인가.</strong> 그게 안 정해진 상태에서 횟수만 정해두니, 아무리 세도 하나에서 멈췄다.</p>
<p>기계에게 뭔가를 가르칠 때 사람은 대개 &quot;몇 번 보여주면 알아들을까&quot;를 먼저 생각한다. 그런데 그 앞에 질문이 하나 더 있다. <strong>내가 같다고 생각하는 두 가지를 기계도 같다고 보는가.</strong> 거기가 어긋나 있으면 횟수는 아무 의미가 없다.</p>
<p>기다리는 동안 아무것도 안 배웠다. 한 번을 믿었으면 열흘 치를 배웠을 것이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA["자동으로 검증합니다"는 거짓말이었다]]></title>
            <link>https://velog.io/@dodokim_lab/changed-the-label</link>
            <guid>https://velog.io/@dodokim_lab/changed-the-label</guid>
            <pubDate>Fri, 04 Sep 2026 01:49:04 GMT</pubDate>
            <description><![CDATA[<h3 id="화면에-써놓은-문장">화면에 써놓은 문장</h3>
<p>업체별로 지출을 분류하는 화면을 만들고 얼마 안 됐을 때다. 그 자료를 올리는 화면의 마지막 단계에 안내문을 하나 붙여뒀었다.</p>
<blockquote>
<p>결과가 달라졌는지 자동으로 검증합니다 (사고 예방)</p>
</blockquote>
<p>읽으면 검증이 알아서 도는 걸로 읽힌다. 나도 그렇게 읽었다.</p>
<p>코드는 그렇지 않았다. 사람이 목록 화면에 들어가서 버튼을 눌러야 검증이 돌았다. 안 누르면 0건이다. 하루를 안 누르면 하루치가 밀리는 게 아니라, 그냥 아무것도 검증되지 않은 채로 지나간다.</p>
<p>그 문장을 쓴 것도 나고, 그렇게 안 만든 것도 나다. 거짓말을 하려던 게 아니라 <strong>만들려던 것을 이미 만든 것처럼 써놓은 것</strong>에 가깝다. 결과는 같다. 화면을 보는 사람은 문장을 믿는다.</p>
<h3 id="그날-안에-진짜로-만들었다">그날 안에 진짜로 만들었다</h3>
<p>발견한 날 바로 자동화를 붙였다. 빌드할 때마다 검증이 같이 돌게 만드는 방식이다. 검증 표본을 코드 저장소 안에 파일로 넣어두고, 테스트가 그걸 읽어서 지금 규칙으로 다시 계산해 답이 달라졌는지 보게 했다.</p>
<p>이러면 문장이 사실이 된다. 사람이 버튼을 안 눌러도 검증이 돌고, 답이 달라졌으면 빌드가 막힌다. 잘못된 규칙이 배포까지 가지 못한다.</p>
<p>만들고 나서 같은 날 되돌렸다.</p>
<h3 id="같은-날-되돌린-이유">같은 날 되돌린 이유</h3>
<p>문제는 표본이 어디에 있느냐였다.</p>
<p>이 검증의 표본은 실무자가 자료를 올릴 때마다 늘어난다. 어떤 가맹점을 어떤 장부 항목으로 봐야 하는지, 실제로 처리된 결과가 그대로 쌓이는 것이라 계속 두꺼워진다. 그런데 그 표본을 코드 저장소 안에 두면, 표본이 하나 늘 때마다 이 사이클을 돌아야 한다.</p>
<ol>
<li>화면에서 결과 파일을 내려받고</li>
<li>저장소의 정해진 자리에 옮겨 넣고</li>
<li>커밋해서 올리고</li>
<li>배포가 끝나기를 기다린다</li>
</ol>
<p>한 번은 한다. 열 번도 할 수 있다. 그런데 이건 혼자 굴리는 시스템이다. 급한 일이 있는 날엔 건너뛴다. 건너뛰면 표본이 안 늘고, 표본이 안 늘면 그 자동 검증은 <strong>살아 있는 채로 아무것도 못 잡는 상태</strong>가 된다. 빌드는 계속 초록불이고, 그래서 아무도 이상하다고 생각하지 않는다.</p>
<p>돌아가는 걸 확인하고 되돌렸다. 안 돌아가서가 아니라, <strong>몇 주 뒤에 죽어 있을 게 보여서</strong> 되돌렸다.</p>
<p>지속 가능성 문제라고 하면 거창한데, 실제로는 훨씬 단순한 이야기다. 사람이 매번 해야 하는 절차를 전제로 하는 자동화는, 그 사람이 안 하면 그날로 끝난다. 그리고 1인 운영에서 그 사람은 항상 나 하나다.</p>
<h3 id="자동으로-둘-것과-손에-맡길-것">자동으로 둘 것과 손에 맡길 것</h3>
<p>되돌리고 나서 검증을 두 종류로 갈랐다. 기준은 하나였다 — <strong>표본이 늘어나는가.</strong></p>
<p><strong>안 늘어나는 표본은 자동으로 둔다.</strong> 규칙 엔진 자체가 망가졌는지 보는 표본이 여기 해당한다. 커피 체인에서 결제한 건이 어떤 항목으로 분류돼야 하는지 같은, 답이 변하지 않는 케이스들이다. 이런 건 한 번 적어두면 늘릴 일이 없으니 코드 저장소 안에 둬도 아무 비용이 없다. 그래서 이쪽은 지금도 빌드할 때마다 자동으로 돈다.</p>
<p><strong>계속 늘어나는 표본은 사람 손에 맡긴다.</strong> 운영에서 쌓이는 실제 처리 결과가 여기다. 이건 저장소가 아니라 데이터베이스에 넣었다. 자료를 올리는 순간 표본도 같이 쌓이니 사람이 옮길 일이 없고, 검증은 화면의 버튼으로 아무 때나 돌린다. 배포 사이클이 아예 안 붙는다.</p>
<p>정리하면 이렇다. <strong>자동화의 비용은 자동화를 만드는 데 드는 게 아니라 자동화에 재료를 대는 데 든다.</strong> 재료가 계속 들어와야 하는 자동화는, 재료 대는 일이 수동이면 결국 멈춘다. 그래서 자동으로 만들지 말지를 정하기 전에 재료가 어디서 오는지를 먼저 본다.</p>
<h3 id="남길-것">남길 것</h3>
<p>문장은 이렇게 바꿨다.</p>
<blockquote>
<p>자동으로 검증합니다 → 버튼을 누르면 검증합니다</p>
</blockquote>
<p>코드를 문장에 맞추는 대신 문장을 코드에 맞춘 것이다. 후퇴처럼 보이고, 실제로 후퇴가 맞다. 대가도 명확하다. 규칙을 잘못 고쳐도 빌드가 안 막히고, <strong>아무도 버튼을 안 누르면 영원히 안 걸린다.</strong></p>
<p>그걸 알면서 골랐다. 못 지킬 문장을 화면에 걸어두는 쪽이 더 위험하기 때문이다. 화면의 문장은 그걸 읽는 사람의 행동을 바꾼다. &quot;자동으로 검증합니다&quot;를 읽은 사람은 검증을 확인하지 않는다. 확인할 필요가 없다고 적혀 있으니까.</p>
<p>기능이 부족한 것보다 <strong>부족한 기능을 충분한 것처럼 적어놓는 쪽</strong>이 사고에 가깝다. 앞의 것은 불편하고, 뒤의 것은 사람을 방심시킨다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[복원했더니 한글이 깨져 있었다]]></title>
            <link>https://velog.io/@dodokim_lab/not-the-backup</link>
            <guid>https://velog.io/@dodokim_lab/not-the-backup</guid>
            <pubDate>Tue, 18 Aug 2026 11:04:42 GMT</pubDate>
            <description><![CDATA[<h3 id="만들어뒀다는-것과-되살아난다는-것">만들어뒀다는 것과 되살아난다는 것</h3>
<p>백업을 여러 층으로 깔았다. 야간에 도는 자동 백업, 그걸 서버 바깥으로 한 벌 더 보내는 보관, 서버 통째 스냅샷, 배포 직전에 한 번 더 뜨는 덤프.</p>
<p>층을 나눈 이유는 단순하다. 하나만 있으면 그 하나가 실패했을 때 아무것도 안 남는다.</p>
<p>그런데 층을 아무리 쌓아도 확인이 안 된 게 하나 있었다. <strong>이게 실제로 되살아나느냐</strong>다. 백업 파일이 매일 생기는 것과, 그 파일로 서비스를 되돌릴 수 있는 것은 다른 얘기다. 앞은 매일 확인되지만 뒤는 사고가 나기 전까지 한 번도 확인되지 않는다.</p>
<p>그래서 마지막 단계로 복구 리허설을 했다. 이걸 건너뛰면 앞의 층들이 전부 종잇조각이 된다.</p>
<h3 id="16초-그리고-깨진-글자">1.6초, 그리고 깨진 글자</h3>
<p>보관해둔 덤프를 하나 골라 받았다. 로컬에 일회용 DB를 하나 띄우고, 그 덤프를 복원했다.</p>
<p><strong>1.6초</strong> 걸렸다.</p>
<p>그리고 열어봤다. 한글이 깨져 있었다.</p>
<p>하루를 들여 깔아놓은 백업이, 되살리면 글자가 망가진다는 뜻이었다.</p>
<h3 id="깨진-건-백업이-아니었다">깨진 건 백업이 아니었다</h3>
<p>확인해보니 파일은 멀쩡했다.</p>
<p>문제는 그 파일을 <strong>열어보는 쪽의 문자셋 설정</strong>이었다. 접속하는 클라이언트가 다른 문자 규격을 가정하고 있어서, 멀쩡한 데이터를 잘못 해석해 보여준 것이다. 접속 설정을 맞추니 그대로 나왔다.</p>
<p>만약 여기서 &quot;복원하면 한글이 깨진다&quot;고 결론냈으면 어떻게 됐을까. 그 문장을 절차서에 적어두고, 멀쩡한 백업을 못 믿게 됐을 거다. 진짜 사고가 났을 때 쓸 수 있는 걸 앞에 두고 다른 방법을 찾느라 시간을 썼을 거다. <strong>백업이 고장 난 게 아니라 백업에 대한 내 판단이 고장 나는 쪽</strong>이 더 위험하다.</p>
<p>그래서 두 가지를 했다. 백업 스크립트가 파일을 만들 때 문자 규격을 명시하도록 박아뒀고, 리허설 절차서에 이 함정을 적어뒀다 — &quot;글자가 깨져 보이면 파일부터 의심하지 말고 접속 설정을 먼저 볼 것&quot;.</p>
<h3 id="눈으로-보는-걸론-부족하다">눈으로 보는 걸론 부족하다</h3>
<p>글자가 제대로 나온다고 복원이 성공한 건 아니다. 눈으로 보는 건 화면에 뜬 일부만 보는 거니까.</p>
<p>그래서 백업을 뜰 때 항목별 건수를 같이 기록해둔다. 복원한 쪽에서 같은 걸 세서 그 기록과 맞춰봤다. <strong>7개 항목 전부 일치.</strong> 구조 변경 이력도 깨끗하게 따라와 있었다.</p>
<p>이렇게까지 하는 이유는, 부분 손실이 제일 무섭기 때문이다. 아예 복원이 안 되면 바로 안다. 90%만 복원되면 한동안 모른다. 그리고 모르는 동안 그 위에 새 데이터가 쌓인다.</p>
<h3 id="남길-것">남길 것</h3>
<p>절차서에는 실측 소요시간도 같이 적었다. 사고가 났을 때 필요한 건 &quot;백업이 있다&quot;가 아니라 <strong>&quot;몇 분이면 돌아온다&quot;</strong>니까. 이 숫자가 있으면 사고 상황에서 판단이 빨라진다. 되돌릴지 버티면서 고칠지를 정할 수 있다.</p>
<p>정리하면 이날 배운 건 두 겹이다.</p>
<p>만들어둔 걸 한 번은 되살려봐야 한다. 그리고 되살려본 결과를 <strong>잘못 읽을 수도 있다.</strong> 확인을 했다는 사실이 확인이 맞았다는 뜻은 아니다. 그래서 판단 기준을 머리에 두지 않고 절차서에 적어두는 편이 낫다. 다음에 같은 화면을 보는 사람은 그때의 나만큼 침착하지 않을 테니까 — 그게 몇 달 뒤의 나라도.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[두 달 동안 아무도 안 봤다]]></title>
            <link>https://velog.io/@dodokim_lab/nobody-was-looking</link>
            <guid>https://velog.io/@dodokim_lab/nobody-was-looking</guid>
            <pubDate>Wed, 12 Aug 2026 13:13:45 GMT</pubDate>
            <description><![CDATA[<h3 id="출력-맨-끝에-한-줄이-붙어-있었다">출력 맨 끝에 한 줄이 붙어 있었다</h3>
<p>그날 하려던 건 백업이었다. 서비스가 돌기 시작하면 기능보다 무서운 게 데이터라, 하루를 잡고 백업 체계를 깔기로 했다.</p>
<p>설치는 순조로웠다. 문제는 설치 스크립트가 뱉은 출력의 맨 끝 한 줄이었다.</p>
<p><strong>디스크 사용량 90%.</strong></p>
<p>두 달 전에 확인했을 땐 12%였다.</p>
<p>100%가 되면 DB가 쓰기를 멈춘다. 백업은 데이터가 사라진 뒤에 되살리는 장치지, 서비스가 멈추는 걸 막아주지는 않는다. 백업 체계를 만들러 들어갔다가 백업으로도 못 막을 사고를 먼저 만난 셈이다.</p>
<p>여기서 제일 마음에 걸린 건 90%라는 숫자가 아니었다. <strong>그 두 달 동안 아무도 안 봤다는 것</strong>이다. 디스크를 보러 들어간 것도 아니었으니, 오늘 이걸 안 것도 실력이 아니라 운이다. 백업을 깔기로 한 날이 하필 이날이었을 뿐이다.</p>
<h3 id="무엇부터-셌나">무엇부터 셌나</h3>
<p>원인을 짐작으로 정하면 엉뚱한 걸 지우게 된다. 그래서 후보를 몇 개 세워놓고 서버에서 직접 셌다.</p>
<p>배포 산출물, 시스템 로그, DB 볼륨, 그리고 DB가 변경 이력을 남기는 파일. 각각이 실제로 몇 GB인지를 하나씩 확인했다.</p>
<p>결과는 배포 산출물이었다. <strong>212개. 그중 실제로 쓰이는 건 5개. 45GB.</strong> 시스템 로그도 1GB 가까이 있었지만 부차적이었고, DB 볼륨은 300MB대로 건강했다.</p>
<p>배포할 때마다 새 산출물이 올라가는데 옛것이 남아 있었다. 정리하는 명령은 파이프라인에 들어 있었다. 다만 그 옵션이 <strong>이름표가 안 붙은 것만</strong> 지운다. 배포 산출물에는 버전 이름표가 붙어 있으니 정리 대상이 아니었던 것으로 보인다.</p>
<p>&quot;~로 보인다&quot;고 쓰는 이유가 있다. 확실한 건 산출물이 212개 쌓여 있었다는 사실이고, 정리 옵션의 사정거리가 그 이유라는 건 가장 유력한 설명이지 실험으로 확정한 게 아니다. 다만 그 설명 위에서 고쳤고, 고친 뒤로는 안 쌓인다.</p>
<h3 id="치우는-것과-안-쌓이게-하는-것">치우는 것과 안 쌓이게 하는 것</h3>
<p>지우고 나니 90%에서 19%로 떨어졌다. 58GB 중 11GB.</p>
<p>여기서 끝내면 두 달 뒤에 같은 일이 난다. 그래서 배포 파이프라인의 정리 옵션을 바꿨다. 이제 <strong>사흘 지난 것은 이름표가 붙어 있어도 지운다.</strong></p>
<p>부작용이 하나 있다. 이 방식은 만들어진 시점을 기준으로 지우기 때문에, 가끔씩만 쓰는 보조 도구들도 같이 지워진다. 그것들은 다음에 필요할 때 다시 받아온다. 하루에 한 번 몇백 MB를 다시 받는 셈인데, 디스크가 차서 서비스가 멈추는 것보다는 싸다고 봤다.</p>
<p>한 번 치우는 건 그날의 일이고, 다시 안 쌓이게 하는 게 본 수정이다.</p>
<h3 id="진짜로-고친-것">진짜로 고친 것</h3>
<p>이날 진짜로 고친 건 디스크가 아니다. <strong>두 달 동안 아무도 안 보고 있었다는 것</strong>을 고쳤다.</p>
<p>백업이 실패하면 알려주는 감시를 붙였는데, 붙이자마자 실패 알림이 왔다. 설정을 잘못했나 싶었다. 오작동이라고 생각한 게 먼저였다. 그런데 디스크를 정리하고 나니 알림이 정상으로 돌아왔다.</p>
<p>정확히 무엇 때문에 그 알림이 왔는지까지 확인한 건 아니다. 다만 순서는 분명하다 — 디스크가 꽉 차 있는 동안 실패했고, 비우고 나니 통과했다.</p>
<h3 id="남길-것">남길 것</h3>
<p>만드는 건 하루면 된다. 살아 있는지 확인하는 건 계속해야 한다.</p>
<p>디스크가 두 달 만에 12%에서 90%가 된 것도 같은 얘기다. 만들 때는 멀쩡했고, 그 뒤로 아무도 안 봤을 뿐이다. 누가 잘못한 게 아니라 <strong>보는 사람이 정해져 있지 않았던 것</strong>이다. 혼자 하는 프로젝트에서는 이게 특히 잘 생긴다. 만든 사람과 지켜볼 사람이 같은 사람인데, 만드는 동안에는 지켜볼 시간이 없다.</p>
<p>그래서 요즘은 뭘 만들 때 &quot;이게 고장나면 누가 알게 되나&quot;를 같이 정한다. 그 답이 없으면 그건 아직 안 만든 거다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[되면 안 되는 게 되는 것]]></title>
            <link>https://velog.io/@dodokim_lab/blocked-by-luck</link>
            <guid>https://velog.io/@dodokim_lab/blocked-by-luck</guid>
            <pubDate>Sat, 08 Aug 2026 06:02:26 GMT</pubDate>
            <description><![CDATA[<h3 id="100곳에-한-번에-보내는-버튼">100곳에 한 번에 보내는 버튼</h3>
<p>부가세 신고 기한이 다가오면 등록된 업체 전부에 서류 요청 알림톡을 보내야 한다. 지금까지는 한 곳씩 보내는 화면만 있었다. 100곳을 하나씩 누르는 건 실무자가 반나절을 쓴다는 뜻이고, 그러다 몇 곳을 빠뜨린다.</p>
<p>그래서 일괄 발송을 붙였다.</p>
<p>보내는 부분은 금방 됐다. 한 곳씩 보내는 경로가 이미 있으니, 대상 목록을 만들어 그 경로를 여러 번 태우면 된다. 굳이 &quot;여러 건 한 번에&quot; 방식을 새로 쓰지 않았다 — 건별로 상태가 남고 실패한 것만 다시 보낼 수 있는 지금 구조가 운영에는 더 낫다고 봤다.</p>
<p>시간이 걸린 건 그다음이었다. <strong>한 번에 100곳에 나간다는 건, 실수도 한 번에 100배가 된다는 뜻이다.</strong></p>
<h3 id="밤에는-버튼-자체를-막았다">밤에는 버튼 자체를 막았다</h3>
<p>알림톡은 밤에 보내지 않는다. 받는 사람이 놀란다.</p>
<p>원래는 밤에 보내려 하면 개별 건이 &quot;밤이라 보류&quot; 상태로 대기했다가 다음 날 나가는 구조였다. 한 건일 때는 괜찮다. 100건이면 아니다. 밤에 일괄 버튼을 누르면 보류 100개가 한꺼번에 쌓이고, 그걸 다음 날 하나씩 풀어야 한다.</p>
<p>그래서 일괄 발송은 밤에 <strong>버튼 자체를 거절</strong>하도록 했다. 개별 발송의 보류 규칙은 그대로 두고, 일괄만 다르게 취급한 것이다.</p>
<p>같은 규칙이라도 100배가 되면 다른 문제가 된다 — 이게 이 작업 내내 반복된 패턴이었다.</p>
<h3 id="대표님이-짚은-것">대표님이 짚은 것</h3>
<p>일이 거의 끝났을 때 대표님이 하나를 짚었다.</p>
<p>업체마다 금액이 다른 알림톡이 있다. 종소세 환급이나 납부 안내 같은 것들이다. 그건 일괄 목록에 뜨면 안 된다는 거였다. 공통 문구 하나로 100곳에 보내면 금액이 전부 남의 것이 되니까 — 100건 전부 오발송이다.</p>
<p>되돌릴 방법도 없다. 카카오톡으로 이미 나간 메시지다.</p>
<h3 id="우연히-막히는-것과-막아둔-것">우연히 막히는 것과 막아둔 것</h3>
<p>솔직히 처음엔 그럴 일이 없다고 생각했다. 그런 템플릿은 구조상 다른 조건에 걸려서 일괄로 보내려 해도 어차피 막힐 가능성이 높았다.</p>
<p>실제로 그럴 가능성이 높았다는 것까지는 맞다. 그런데 그건 <strong>우연히 막히는 것</strong>이지 막아둔 게 아니다.</p>
<p>우연히 막히는 것에는 세 가지 문제가 있다. 조건이 바뀌면 사라진다. 사라져도 아무도 모른다. 그리고 사라졌다는 걸 알게 되는 시점이 대개 사고가 난 뒤다.</p>
<p>&quot;실수로 누를 일이 없다&quot;는 것도 같은 종류의 방어다. 오늘은 맞고 내일은 모른다.</p>
<h3 id="데이터로-박았다">데이터로 박았다</h3>
<p>그래서 알림톡 템플릿마다 &quot;일괄 발송 가능&quot; 여부를 데이터로 넣었다. 기본값은 <strong>불가</strong>다. 새 템플릿을 만들면 자동으로 막혀 있고, 검토해서 안전하다고 확인한 것만 켠다.</p>
<p>막는 자리는 두 군데다. 발송 화면의 템플릿 목록에서 아예 안 보이게 거르고, 그걸 우회해서 서버에 직접 요청해도 서버가 거절한다. 화면만 막으면 화면을 안 거치는 경로가 남는다.</p>
<p>그리고 판정 기준을 세 문장짜리 체크리스트로 적어서 문서에 남겼다. 다음에 새 템플릿을 만들 때 이 판단을 다시 하게 되는데, 그때 기준이 기억에만 있으면 흔들린다. &quot;이건 괜찮겠지&quot;가 한 번 통과하면 그다음부터는 기준이 없는 것과 같다.</p>
<h3 id="남길-것">남길 것</h3>
<p>자동화에서 무서운 건 안 되는 게 아니다. 안 되면 사람이 안다. 화면에 에러가 뜨고, 실무자가 전화를 하고, 내가 고친다.</p>
<p><strong>되면 안 되는 게 되는 게 무섭다.</strong> 100곳에 잘못된 내용이 나가면 되돌릴 방법이 없다. 시스템은 성공했다고 보고할 것이고, 로그에는 아무 문제도 안 남는다. 알게 되는 건 고객이 전화를 걸어올 때다.</p>
<p>그래서 자동화를 만들 때 &quot;무엇을 되게 할까&quot;만큼 &quot;무엇을 절대 안 되게 할까&quot;에 시간을 쓰게 됐다. 후자는 요구사항에 안 적혀 있다. 적히지 않는 이유는 간단하다 — 사람이 하던 시절에는 그런 실수를 할 방법 자체가 없었기 때문이다. 한 곳씩 보내던 시절에 &quot;100곳에 남의 금액을 보내지 마세요&quot;라는 규칙이 있었을 리 없다.</p>
<p>그리고 이걸 짚은 건 내가 아니라 그 일을 해본 사람이다. 코드를 쓰는 건 점점 쉬워지는데, 무엇을 막아야 하는지는 여전히 그 일을 해본 사람에게서 나온다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[코드엔 버그가 없었다]]></title>
            <link>https://velog.io/@dodokim_lab/see-before-you-fix</link>
            <guid>https://velog.io/@dodokim_lab/see-before-you-fix</guid>
            <pubDate>Wed, 05 Aug 2026 11:48:21 GMT</pubDate>
            <description><![CDATA[<h3 id="제보-두-건">제보 두 건</h3>
<p>운영 중에 제보가 두 건 올라왔다.</p>
<p>하나, 올린 것과 다른 파일이 받아진다. 둘, 받기를 눌러도 파일이 제대로 안 받아진다.</p>
<p>둘 다 데이터가 잘못 나가는 쪽이라 급했다. 바로 코드를 따라갔다.</p>
<h3 id="전수로-훑었는데-버그가-없었다">전수로 훑었는데 버그가 없었다</h3>
<p>업로드하는 화면부터 시작했다. 파일을 올리고 그 응답을 받아 다음 요청에 넘기는 구간 — 순서대로 기다리고 있어서 경합이 생길 구조가 아니었다. 서버 쪽 저장 로직도 봤다. 파일마다 고유한 이름을 새로 만들어 저장하고 있어서 덮어쓰기가 구조적으로 불가능했다. 조회할 때도 해당 건에 속한 파일만, 지정된 순서대로 읽고 있었다. 다운로드 시점에는 그 파일이 정말 그 건의 것인지 한 번 더 검증하고 있었다.</p>
<p>화면이 기대하는 응답 형태와 서버가 주는 형태도 필드 단위로 맞춰봤다. 정확히 일치했다.</p>
<p>여섯 군데를 훑고 나온 결론은 하나였다. <strong>정적으로 잡히는 버그가 없다.</strong></p>
<p>이때가 제일 위험한 순간이다. 원인은 모르겠는데 뭔가는 해야 할 것 같으니까, 짐작으로 한 줄 바꾸고 배포하고 물어보게 된다. &quot;이제 되나요?&quot; 그게 아니라고 하면 또 한 줄 바꾼다. 이걸 몇 번 반복하면 코드에는 근거 없는 방어 코드가 쌓이고, 원인은 그대로 남는다.</p>
<p>그래서 고치는 걸 멈췄다.</p>
<h3 id="고치는-대신-보는-걸-먼저-만들었다">고치는 대신 보는 걸 먼저 만들었다</h3>
<p>두 증상 다 실제 나간 데이터를 봐야 확정할 수 있는 종류였다. 그런데 그걸 볼 방법이 없었다. 나간 뒤에는 아무것도 남지 않았고, 화면에서 다시 열어볼 수도 없었다.</p>
<p>그래서 진단 도구부터 만들었다. 무엇이 나갔는지, 어떤 파일이 붙었는지를 나중에 그대로 꺼내볼 수 있는 화면이다.</p>
<p>설계에서 한 가지를 정했다. <strong>나갈 때의 내용을 그 시점에 통째로 저장한다.</strong> 나중에 다시 조립하지 않는다. 템플릿이 바뀌면 과거에 나간 문장도 같이 바뀌어 보일 텐데, 그러면 &quot;그때 정확히 뭐가 나갔나&quot;를 영영 못 본다. 지금 필요한 건 재현이 아니라 증거였다.</p>
<p>만들고 나니 화면 하나가 생겼다. 실제로 나간 문장, 그때 채워진 값들, 붙은 파일 목록, 그리고 그 파일을 바로 열어보는 버튼.</p>
<h3 id="한-건은-코드가-아니라-자리-문제였다">한 건은 코드가 아니라 자리 문제였다</h3>
<p>그 화면으로 실제 데이터를 열자 한 건은 금방 정리됐다.</p>
<p>파일은 멀쩡했다. 서버도 정확한 응답을 주고 있었다. 문제는 <strong>어디서 여느냐</strong>였다.</p>
<p>안내 메시지의 버튼을 누르면 메신저 앱 안에서 브라우저가 열린다. 앱 바깥의 브라우저가 아니라 앱이 품고 있는 브라우저다. 그 안에서는 화면이 파일을 메모리에 만들어놓고 내려받게 하는 방식이 동작하지 않는다. 우리 화면이 정확히 그 방식이었다.</p>
<p>그래서 그 환경에서만 다른 경로로 바꿨다. 서버가 이미 &quot;이건 내려받는 파일이다&quot;라고 알려주는 응답을 주고 있었으니, 화면은 그 주소로 그냥 이동만 하면 됐다. 백엔드는 손댈 게 없었다.</p>
<p>정적 분석으로 절대 안 잡히는 종류였다. 코드는 처음부터 옳았고, 옳은 코드가 특정 실행 환경에서 안 돌았을 뿐이다.</p>
<h3 id="남은-한-건과-재현-못-한-것을-다루는-법">남은 한 건과, 재현 못 한 것을 다루는 법</h3>
<p>다른 한 건은 지금도 재현이 안 된다.</p>
<p>다만 코드를 훑을 때 딱 한 군데 마음에 걸린 곳이 있었다. 화면에서 파일을 <strong>순번으로 식별</strong>하고 있었다. 첫 번째, 두 번째, 세 번째. 순서대로 올리기만 하면 문제가 없다. 그런데 중간에 하나를 빼면 뒤가 한 칸씩 당겨진다. 그때부터 순번은 다른 파일을 가리킨다.</p>
<p>이게 원인이라고 단정하지는 않았다. 실제 데이터에서 재현된 적이 없으니까. 대신 순번 대신 각 파일에 고유한 표식을 붙이도록 고쳤다. 유력한 후보 하나를 차단한 것이고, 재발하면 이제 진단 화면으로 확정할 수 있다.</p>
<p>원인을 모르는 채로 닫는 건 찜찜하다. 그래도 &quot;아마 이거일 것&quot;을 &quot;이거였다&quot;로 적어두는 것보다는 낫다. 기록이 틀리면 다음 사람이 — 대개 몇 달 뒤의 나인데 — 그 틀린 기록 위에 판단을 쌓는다.</p>
<h3 id="남길-것">남길 것</h3>
<p>이 작업에서 남은 건 수정 두 줄이 아니라 화면 하나다.</p>
<p>다음에 같은 제보가 오면 코드를 노려보는 대신 데이터를 연다. 그게 몇 시간을 아낀다. 그런데 이 화면은 버그가 없었으면 안 만들었을 것이다. 기능 목록에 &quot;발송 내역 상세 보기&quot; 같은 건 우선순위가 늘 밀린다.</p>
<p>AI에게 개발을 맡기면서 코드 쓰는 시간은 확실히 줄었다. 대신 &quot;지금 무슨 일이 벌어지는지 볼 수 있나&quot;가 병목이 됐다. AI는 시키면 고쳐준다. 문제는 뭘 고쳐야 하는지를 내가 모른다는 거고, 그건 코드를 더 읽는다고 나오지 않는다.</p>
<p>못 보는 상태에서 고치면, 고친 게 아니라 찍은 거다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[AI가 만든 코드, 어떻게 확인하세요?]]></title>
            <link>https://velog.io/@dodokim_lab/how-to-check-ai-code</link>
            <guid>https://velog.io/@dodokim_lab/how-to-check-ai-code</guid>
            <pubDate>Tue, 04 Aug 2026 02:16:34 GMT</pubDate>
            <description><![CDATA[<h3 id="코드는-감시하는데-그-코드를-만드는-건-아무도-안-본다">코드는 감시하는데, 그 코드를 만드는 건 아무도 안 본다</h3>
<p>AI한테 코드를 시키면, 그 코드는 리뷰한다. 리뷰 기준을 정해두고 통과 여부를 본다.</p>
<p>그런데 그 코드를 뽑아내는 건 코드가 아니다. 도메인 규칙, 코딩 컨벤션, 리뷰 기준, 작업 문서 운영 방식 — AI가 세션이 바뀌어도 같은 품질로 일하게 만드는 설정 뭉치다. 이게 계속 쌓인다.</p>
<p>그래서 질문이 이렇게 된다. AI가 짠 코드는 내가 감시하는데, 그 AI가 물고 일하는 규칙은 누가 감시하지?</p>
<p>코드에는 리뷰 기준이 있는데, 그 리뷰 기준 문서 자체는 아무도 검증을 안 한다. 문서끼리 모순이 생겨도, 영역이 통째로 비어 있어도, 규칙이 낡아서 현실과 어긋나도 알 방법이 없다. 혼자 하는 프로젝트라 봐줄 동료도 없다.</p>
<h3 id="감사관을-하나-만들었다">감사관을 하나 만들었다</h3>
<p>담당은 코드가 아니라 AI 설정 자체다. 영역별로 검사항목을 여덟에서 열 개씩 정했다. 말로만 하면 추상적이니, 실제로 넣었던 항목 몇 개를 일반화해서 옮기면 이렇다.</p>
<ul>
<li><strong>&quot;해당 없음&quot;에는 사유 한 줄 강제.</strong> 검사 항목을 건너뛸 땐 이유를 적게 했다. 다음 리뷰에서 그 영역이 &quot;해당 없음&quot;에서 &quot;해당 있음&quot;으로 바뀐 걸 감지하려면, 지난번에 왜 건너뛰었는지가 남아 있어야 한다.</li>
<li><strong>한 항목엔 한 책임만.</strong> &quot;외부 호출에 추적 ID를 남기는가 + 중복 방지 키 형식이 맞는가&quot;가 한 항목에 묶여 있었다. 절반만 지켜지면 통과로도 실패로도 표기할 수가 없다. 둘로 쪼갰다.</li>
<li><strong>통과 집계 규칙 명문화.</strong> 통과 수와 비대상 수를 합치면 항목 수와 정확히 같아야 한다. 안 맞으면 리뷰어가 항목을 빼먹었다는 뜻이다. 리뷰 결과 자체를 검산 가능하게 만드는 장치다.</li>
<li><strong>문서 간 모순 검사.</strong> 한 문서는 &quot;이 규칙은 세션 시작 때 자동으로 딸려 들어간다&quot;고 적혀 있는데 정작 주입 목록에는 등록이 안 돼 있는 경우 — 이게 제일 위험하다. 규칙이 지켜지고 있다고 믿는데, 실제로는 아무도 안 읽는 상태.</li>
</ul>
<p>첫 시험 가동은 몇 분 만에 끝났고, 결과도 나쁘지 않았다. 문제는 그다음이었다.</p>
<h3 id="닫자마자-보인-구멍">닫자마자 보인 구멍</h3>
<p>작업을 완료 처리한 직후에 알았다. 검사 대상 분배표에 코드 영역은 다 있는데, <strong>AI 설정 문서들 자체는 &#39;검사 안 함&#39;으로 박혀 있었다.</strong> 감사관을 만들면서 감사관을 감사 대상에서 빼먹은 거다. 그날 다시 열어서 검사 영역으로 추가했다.</p>
<p>사람 조직에서 겪던 일들이다 — 책임 중복, 사각지대, 문서 부패. AI 설정이라고 다르지 않았다. 그런데 여기까진 그나마 눈에 보이는 구멍이었다. 진짜 문제는 눈에 안 보이는 쪽에 있었다.</p>
<h3 id="진짜-문제는-침묵이었다">진짜 문제는 &#39;침묵&#39;이었다</h3>
<p>감사를 이어가면서 알게 된 건, 이 환경이 고장 나는 방식이 하나같이 똑같다는 거였다. 에러가 안 난다. 화면도 정상, 종료 코드도 정상. 그래서 몇 주씩, 몇 달씩 그냥 간다. 버그였다면 진작 알았다. 내가 겪은 건 버그가 아니라 침묵이었다.</p>
<p><strong>하나. 규칙을 써놨는데 AI가 안 받는다.</strong> 규칙 문서는 계속 커진다. 그런데 어느 선을 넘으면 그게 통째로 안 들어간다. 앞부분만 잘려 들어가고 나머지는 조용히 빠진다. 에러가 아니라 조용한 강등이다. 파일이 디스크에 있다는 것과, 그게 실제로 AI 눈앞에 들어왔다는 건 전혀 다른 얘기였다. 규칙을 아무리 잘 써도, AI가 그걸 받았는지는 별개 문제다.</p>
<p>그래서 이렇게 한다. 새 세션을 열면 <strong>&quot;지금 어떤 규칙·문서를 읽고 있는지 목록으로 보여줘&quot;</strong>부터 시킨다. 파일이 디스크에 있다는 것과 그게 컨텍스트에 들어왔다는 건 다른 얘기니까, 목록으로 눈으로 확인하는 게 제일 빠르다. 설정을 고친 날은 반드시 새 세션에서 확인한다 — 실행 중인 세션에는 대부분 반영이 안 된다. 설정이 여러 층(전역·프로젝트·로컬)으로 겹쳐 있으면 어느 쪽이 이겼는지도 화면에 안 나오니, 고쳤으면 이겼는지까지 물어본다. 세션이 길어졌을 때도 같다. 대화가 압축되면서 앞서 준 제약이 조용히 사라질 수 있어서, 중요한 제약은 작업 직전에 한 번 더 얹는다.</p>
<p>문서 자체에도 방어선을 하나 깔았다. 규칙 문서 앞머리에 <strong>&quot;이 글이 잘려 보이면 원본 경로를 열어 끝까지 읽어라&quot;</strong>를 한 줄 박아둔다. 다시 상한을 넘더라도 완전 무력화 대신 한 번의 읽기로 복구되게 만드는 장치다. 같은 이유로 제일 중요한 경고는 문서 뒤가 아니라 앞쪽으로 옮겼다. 잘리는 건 언제나 뒤쪽이니까.</p>
<p><strong>둘. 규칙이 낡는다.</strong> 설정은 그걸 쓰던 시절의 모델에 맞춰져 있다. 얼마나 장황한지, 어떻게 생각하는지를 전제로 튜닝한 값들이다. 모델 세대가 바뀌면 어제의 튜닝이 오늘의 부작용이 된다. 어떤 값은 새 모델에선 무의미하고, 어떤 값은 오히려 방해가 된다. 그런데 문서와 설정은 코드처럼 컴파일 에러를 안 낸다. 낡아도 그냥 조용히 적용될 뿐이다. 어디에도 &quot;이건 어느 세대 기준으로 넣었다&quot;가 안 적혀 있으니, 다시 볼 계기 자체가 없다.</p>
<p>그래서 주기적으로 설정을 훑는다. 모델명이나 사고 옵션처럼 세대를 타는 키워드로 검색해서, 걸리는 항목마다 <strong>&quot;이 값은 언제·왜 넣었고, 지금도 유효한지 근거를 대줘&quot;</strong>를 시킨다. 여기서 제일 중요한 건 기억이나 추측으로 판단하지 않는 것이다. 걸린 항목은 공식 문서로 확인한다 — 내가 예전에 읽은 기억이 이미 낡은 정보일 수 있으니까.</p>
<p>그리고 트리거를 밖에서 빌린다. 새 모델이 언제 나오는지 내가 챙길 필요는 없다. 새 모델 가이드가 올라오는 걸 &quot;내 설정도 훑어볼 때&quot;라는 신호로 쓴다. 낡은 것은 스스로 낡았다고 말하지 않으니, 주기와 계기를 바깥에서 가져오는 수밖에 없다.</p>
<p><strong>셋. 내가 AI한테 기억시켜둔 것도 낡는다.</strong> 설정이 모델 세대에 묶인다면, 메모리는 사실의 시점에 묶인다. 그때는 맞던 사실이 지금은 틀리다. 이건 그나마 해결이 간단했다 — &quot;내 메모리에서 낡았거나 서로 모순인 사실을 찾아줘&quot; 한 문장이면 AI가 훑어준다. 틀린 기억은 지우기보다 &quot;이건 이제 틀림&quot; 마커로 바꿔둔다. 같은 오판이 재발할 때 방어선이 된다.</p>
<p>세 개 다 공통점이 있다. 신호가 없다. 그리고 신호가 없는 것은 &quot;이상 없음&quot;이 아니라 &quot;모름&quot;이다.</p>
<h3 id="그래서-감시-장치를-넣었다-그-장치들이-고장나-있었다">그래서 감시 장치를 넣었다. 그 장치들이 고장나 있었다</h3>
<p>낡고 침묵하는 걸 잡으려고 감시 장치를 붙였다. 그리고 그게 진짜 우는지 시험해봤다. 일부러 틀린 상황을 만들어서 — &quot;이건 경고가 반드시 떠야 하는 상황&quot;을 인위로 만들어서 — 넣어봤다.</p>
<p>안 울렸다. 완벽하게 잘 도는데, 실전에서는 아무것도 안 잡는 상태였다. 손으로 돌리면 멀쩡한데, 정작 자동으로 도는 경로에서는 한 번도 발화한 적이 없었다. 이 시험을 안 했으면 지금도 모르고 있었을 거다.</p>
<p>여기서 이 글의 결론이 나온다. <strong>감시 장치는 만들 때 한 번이 아니라, 울려야 하는 상황을 일부러 만들어서 주기적으로 다시 시험해야 한다.</strong> AI한테 이렇게 시키면 된다 — &quot;네가 만든 검사가 진짜 위반을 잡는지, 위반을 일부러 집어넣어서 막히는 걸 보여줘.&quot; 통과해버리면 그 검사는 가짜다.</p>
<p>수정도 똑같다. &quot;고쳤다&quot;는 건 재현으로만 인정한다. 수정이 의도대로 동작하는 것과, 수정이 실제로 그 구멍을 막는 건 별개의 문제다. 실제로 내가 막았다고 믿은 구멍이 뒤에 두 번 다시 뚫렸는데, 전부 이 확인을 건너뛰었기 때문이었다.</p>
<p>하나 더 붙였다. AI가 &quot;통과했습니다&quot;라고 하면 <strong>&quot;무엇을 근거로 통과라고 판단했는지 보여줘&quot;</strong>를 되묻는다. 근거를 못 대면 그건 통과가 아니라 모름이다. AI의 &quot;완료했습니다&quot;는 보고이지 증거가 아니다. 이 구분 하나만 지켜도 거짓 합격의 상당수가 걸러진다.</p>
<h3 id="자기가-자기를-검사하면-어지간해선-통과다">자기가 자기를 검사하면, 어지간해선 통과다</h3>
<p>이 과정에서 원칙이 하나 더 생겼다. 자기 설정을 자기가 검사하게 하면 어지간해선 통과시킨다. 사람 셀프 리뷰가 관대해지는 것과 똑같다. 그래서 규칙을 고칠 때의 검토는 반드시 다른 영역 검사자가 교차로 하도록 못 박았다.</p>
<p>리뷰도 마찬가지다. 리뷰는 판정까지가 역할이고, 지적을 반영한 코드는 새 코드다. 이 구분이 없으면 리뷰를 통과한 코드와 리뷰 후 바뀐 코드가 섞인다. 지적을 반영한 부분이 아무 검토 없이 통과하는 일이 실제로 벌어졌다. 그래서 반영분은 다시 리뷰하도록 절차로 박았다.</p>
<h3 id="결국-세-문장으로-줄었다">결국 세 문장으로 줄었다</h3>
<p>장치를 여러 개 만들었지만, 매일 실제로 쓰는 건 질문 세 개다. 각각이 막는 침묵이 다르다.</p>
<table>
<thead>
<tr>
<th>물어볼 것</th>
<th>막는 침묵</th>
</tr>
</thead>
<tbody><tr>
<td>&quot;지금 어떤 규칙·문서를 읽고 있는지 목록으로 보여줘&quot;</td>
<td>규칙이 도달하지 못하는 것</td>
</tr>
<tr>
<td>&quot;이 값은 언제·왜 넣었고, 지금도 유효한지 근거를 대줘&quot;</td>
<td>설정·기억이 조용히 낡는 것</td>
</tr>
<tr>
<td>&quot;네가 만든 검사가 진짜 위반을 잡는지, 위반을 일부러 넣어서 막히는 걸 보여줘&quot;</td>
<td>감시가 거짓으로 합격시키는 것</td>
</tr>
</tbody></table>
<p>세 문장 다 공통점이 있다. <strong>AI한테 결과를 묻는 게 아니라 근거를 묻는다.</strong> 결과는 언제나 그럴듯하게 나오고, 그럴듯한 결과는 아무것도 보증하지 않는다.</p>
<h3 id="남길-것">남길 것</h3>
<p>혼자 일해도 팀의 견제 장치는 가질 수 있다. 오히려 그게 없으면 1인 개발은 그대로 무너진다.</p>
<p>AI를 붙일 때 진짜 조심해야 하는 건, AI가 틀리는 순간이 아니다. 틀렸는데 아무 신호가 없는 순간이다. 코드도, 문서도, 설정도 다 정상으로 보이는데 그 아래 깔린 가정 하나가 조용히 무너져 있는 것. 그리고 그 가정은 아무도 검사하지 않는다.</p>
<p>AI에게 개발을 맡길수록, 사람이 지켜야 할 건 코드가 아니다. 그 코드를 뽑아내는 환경 — 규칙이 진짜 전달됐는지, 낡지 않았는지, 감시가 정말 우는지 — 다. 요즘 AI 쓰는 시간의 절반은 시키는 데가 아니라 의심하는 데 들어가는데, 그 의심까지 시스템으로 만들 수 있게 됐다는 게 요즘 시대의 재미다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[AI가 잘 만들수록, 내가 뭘 모르는지 모른다]]></title>
            <link>https://velog.io/@dodokim_lab/what-to-ask</link>
            <guid>https://velog.io/@dodokim_lab/what-to-ask</guid>
            <pubDate>Sat, 01 Aug 2026 11:33:37 GMT</pubDate>
            <description><![CDATA[<h3 id="다-처리했는데-화면엔-실패라고-뜬다">다 처리했는데 화면엔 &#39;실패&#39;라고 뜬다</h3>
<p>AI로 만든 기능이 분명히 잘 됐는데, 정작 자료가 몰린 날 화면이 빨갛게 죽는다 — 뒤에서 시스템은 멀쩡히 다 처리하고 있는데도.</p>
<p>요즘은 코드를 몰라도 Claude 같은 AI한테 &quot;카드 명세서 업로드 기능 만들어줘&quot; 하면 그럴듯한 게 나온다. 실제로 잘 된다 — 작은 파일로 테스트하는 동안은.</p>
<p>문제는 실무 규모에서 터진다. 실무자가 수천 건짜리 파일을 올린 순간, 화면에 빨간 에러가 떴다. 그런데 서버 로그를 보면 36초에 걸쳐 전부 정상 처리하고 &#39;성공&#39;이 찍혀 있다. 서버는 일을 다 했는데, 사용자 화면은 실패라고 말하는 상태였다.</p>
<h3 id="범인은-코드가-아니라-30초-규칙">범인은 코드가 아니라 &#39;30초 규칙&#39;</h3>
<p>원인은 중간에 있는 프록시였다. 연결을 지켜보다 응답이 늦으면 먼저 끊어버리는 &#39;문지기&#39;라고 보면 된다. 요청이 30초를 넘기자 이 문지기가 &quot;얘 죽었나 보다&quot; 하고 연결을 먼저 끊어버린 거다. 뒤에서 서버는 멀쩡히 일하고 있는데, 앞단은 이미 실패로 처리한 뒤였다.</p>
<p>이게 AI 코딩의 함정이다. AI는 &quot;정상적으로 잘 되는 경로(happy path)&quot;를 기가 막히게 짜준다. 그런데 <strong>&quot;실무 규모에서 어디가 먼저 터지는가&quot;는 먼저 알려주지 않는다.</strong> 물어보지 않으면. 30초 타임아웃, 큰 파일, 동시 요청 같은 &#39;벽&#39;은 실제로 그 규모를 맞아본 사람이 알고 챙겨야 한다.</p>
<p>이 상태가 특히 나쁜 이유가 하나 더 있다. 데이터는 다 들어갔는데 화면만 실패라고 뜨니, 사용자는 다시 올리게 되고 — 그러면 같은 데이터가 이중으로 처리된다.</p>
<h3 id="타임아웃을-늘리는-건-해결이-아니다">타임아웃을 늘리는 건 해결이 아니다</h3>
<p>급하면 프록시 타임아웃을 늘리면 된다. 실제로 180초로 늘려서 하루를 벌었다. 근데 이건 해결이 아니라 유예다. 파일이 더 커지면 늘린 한도도 또 뚫린다. 상한을 쫓아 올리는 건 끝이 없다.</p>
<p>진짜 해결은 구조를 바꾸는 거였다. 오래 걸리는 작업은 <strong>받자마자 &quot;접수됐습니다&quot;부터 답하게</strong> 했다. 실제 처리는 뒤에서 하고, 화면은 몇 초마다 &quot;다 됐나요?&quot;를 물어본다. 처리에 얼마가 걸리든 연결이 끊길 일이 없다.</p>
<p>AI한테 일을 시킬 때도 이렇게 말하면 된다 — &quot;시간 오래 걸리는 작업이니까, 요청을 받으면 바로 접수 응답부터 주고 처리는 백그라운드로 돌린 다음, 화면에서 진행 상태를 확인하게 만들어줘.&quot; 이 한마디가 있고 없고가 위 사고를 가른다.</p>
<h3 id="화려한-방법이-항상-답은-아니다">화려한 방법이 항상 답은 아니다</h3>
<p>실시간 푸시 같은 더 세련된 방법도 있었지만 안 썼다. 혼자 운영하는 시스템은 부품 하나 늘어나는 게 전부 비용이라, 몇 초 간격으로 물어보는 걸로 충분한 일에 그 비용을 낼 이유가 없었다. 폴링도 처리 중인 작업이 있을 때만 켜고, 다 끝나면 끈다.</p>
<p>백그라운드 처리량도 숫자로 정했다. 서버가 메모리 2GB짜리 작은 인스턴스인데, 파일 하나 파싱에 메모리를 50MB쯤 쓴다. 동시에 4개면 200MB — 그래서 작업 스레드를 기본 2개, 최대 4개로 묶고, 그 이상 밀리면 큐에서 순서를 기다리게 했다. 화려한 튜닝이 아니라 서버 사양에서 역산한 상한선이다.</p>
<p>AI는 종종 제일 화려한 방법을 권한다. 근데 1인 운영에서는 &#39;덜 세련되고 덜 고장 나는 쪽&#39;이 맞을 때가 많다. 이 판단도 사람 몫이다.</p>
<p>하나는 더 챙겼다. 서버가 처리 도중에 재시작되면 &#39;처리 중&#39;으로 남은 작업이 조용히 사라지는데, 이걸 부팅할 때 찾아서 실패로 표시하게 했다. 조용히 사라지는 작업이 실패라고 뜨는 작업보다 훨씬 나쁘다. AI는 이런 &#39;중간에 죽었을 때&#39;를 웬만해선 먼저 챙겨주지 않는다.</p>
<h3 id="진짜-위험은-뭘-모르는지-모르는-것">진짜 위험은 &#39;뭘 모르는지 모르는 것&#39;</h3>
<p>이 사건이 남긴 기준은 기술이 아니었다. 서버가 일을 다 끝냈어도, 그게 사용자 화면에서 안 끝났으면 안 끝난 거다. 성공을 정의하는 건 서버 로그가 아니라 사용자가 보는 화면이다.</p>
<p>그런데 더 깊은 문제가 있다. AI는 시킨 건 잘 만든다. 근데 뭘 시켜야 할지는 그 일을 아는 사람만 안다. 이 사건도 결국 &quot;실무에선 파일이 수천 건씩 몰린다&quot;는 현장 감각을 내가 몰랐기 때문에 터진 거다. 작은 파일로만 테스트하는 개발자는 그 규모를 모른다.</p>
<p>제일 위험한 건 내가 뭘 모르는지조차 모르는 상태다. AI가 답을 척척 내줄수록, 애초에 질문을 놓쳤다는 걸 눈치채기 더 어려워진다. AI에게 개발을 맡길수록, 사람이 남겨야 할 건 코드가 아니라 &quot;무엇을 물어야 하는가&quot;다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[자동화 대신 관리자 화면부터 만들었다]]></title>
            <link>https://velog.io/@dodokim_lab/admin-first</link>
            <guid>https://velog.io/@dodokim_lab/admin-first</guid>
            <pubDate>Thu, 30 Jul 2026 20:26:59 GMT</pubDate>
            <description><![CDATA[<h3 id="토대-다음은-시스템이었다">토대 다음은 시스템이었다</h3>
<p>워크스페이스로 바닥을 깔고 나니 이제 진짜 만들 차례였다. 대표님이 처음부터 바란 건 흩어진 자료를 시스템으로 모으는 것. 문제는 어디서부터 손을 대느냐였다.</p>
<h3 id="개발자의-당연한-순서">개발자의 당연한 순서</h3>
<p>내 머릿속 1순위는 입력 자동화였다. 카드 내역을 자동으로 긁어오는 유료 방식을 붙이면, 사람이 파일을 만질 일 자체가 없어진다. 입력이 자동이면 뒤가 다 자동이니까 — 개발자 눈엔 당연한 출발점이다.</p>
<p>대표님 의견은 달랐다. &quot;그거, 쓸만한 게 거의 없어요.&quot;</p>
<h3 id="실무가-아픈-데이터는-다른-곳에-있었다">실무가 아픈 데이터는 다른 곳에 있었다</h3>
<p>이유를 파보니 구조가 보였다. 자동으로 긁히는 데이터와, 사무소가 손으로 정리하느라 아픈 데이터가 서로 달랐다. 정작 정리가 필요한 건 고객 사장님들이 이메일과 메신저로 제각각 던져주는 개별 명세 파일들이었다. 카드사마다 양식이 다르고, 보내는 사람은 그게 어떻게 처리되는지 모른다.</p>
<p>자동화하기 좋은 데이터를 자동화하는 건 쉽다. 실무가 아픈 데이터를 다뤄야 하는데, 그 둘이 같지 않다는 걸 대표님은 경험으로 알고 있었고 나는 몰랐다. 그래서 입력 자동화는 시작도 하기 전에 접었다. 돈도 안 들었고 코드도 안 버렸다.</p>
<h3 id="그런데-그-일을-할-곳이-없었다">그런데 그 일을 할 &#39;곳&#39;이 없었다</h3>
<p>방향을 틀고 나니 더 근본적인 게 걸렸다. 실무자가 받은 명세 파일을 올리고, 업체별로 지출을 장부 항목으로 나누는 일 — 그 일을 담을 화면이 사무소 어디에도 없었다.</p>
<p>지금까지 이 일은 실무자 세 사람이 각자 자기 PC에서, 각자의 방식으로 하고 있었다. 같은 작업을 저마다 다른 기준으로 한다. 어제 어떻게 처리했는지는 각자의 기억에 있다. 이걸 하나의 화면, 하나의 기준으로 모으지 않으면 표준화도 축적도 안 된다.</p>
<p>그제서야 알았다. 자동화를 붙일 데가 아니라, 사람이 일할 데부터 있어야 한다는 걸. 그래서 첫 시스템은 자동수집이 아니라 관리자 화면 — 어드민이 됐다.</p>
<h3 id="첫-화면은-사람이-일하는-화면이었다">첫 화면은 사람이 일하는 화면이었다</h3>
<p>어드민의 첫 세 화면은 이렇게 잡았다.</p>
<ul>
<li><strong>업체를 등록하는 화면.</strong> 이 동네는 모든 일이 업체 단위로 돌아간다 — 어느 화면에서든 &quot;지금 어느 업체 일을 보고 있는가&quot;가 먼저다. 등록은 수기로, 실무자가 직접 입력한다.</li>
<li><strong>명세 파일을 올리는 화면.</strong> 실무자가 카드사를 고르고 파일을 얹으면 시스템이 받아준다. 카드사마다 다른 양식은 시스템이 흡수한다.</li>
<li><strong>올린 내역을 업체별로 분류하는 화면.</strong> 아직 자동으로 분류해주는 건 없다. 사람이 분류하는 화면부터 만들었다.</li>
</ul>
<p>화면 구조는 개발자 관행(데이터 종류별 메뉴)이 아니라 실무 관행(업체별 컨텍스트)에 맞췄다. 세무사들이 익숙한 방식이 그거였다.</p>
<p>도메인도 이 단계에서 하나 배웠다. 같은 지출이라도 업종에 따라 장부에 다르게 적힌다 — 카페가 산 커피와 IT 회사가 산 커피는 다른 항목이다. 화면과 데이터가 처음부터 &quot;업체 단위&quot;여야 하는 진짜 이유였다.</p>
<h3 id="어드민은-자동화의-결과가-아니라-시작이었다">어드민은 자동화의 결과가 아니라 시작이었다</h3>
<p>만들고 나서 정리된 생각이 있다. 나는 자동화부터 하려고 했는데, 자동화란 사람이 어떻게 일하는지가 화면으로 서 있어야 그 위에 붙일 수 있는 것이었다. 무엇을 자동화할지는 기술이 아니라 실무가 정한다.</p>
<p>이 관리자 화면 하나가 서자 뒤의 일들이 전부 여기에 얹혔다. 다음 편은 그 업로드 화면이 실전에서 겪은 일이다. 서버는 성공했는데 화면은 실패했던 날.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[USB 들고 퇴근하던 사무소]]></title>
            <link>https://velog.io/@dodokim_lab/workspace-first</link>
            <guid>https://velog.io/@dodokim_lab/workspace-first</guid>
            <pubDate>Wed, 29 Jul 2026 12:23:03 GMT</pubDate>
            <description><![CDATA[<h3 id="시스템을-깔기-전에-파일은-usb로-다녔다">시스템을 깔기 전에, 파일은 USB로 다녔다</h3>
<p>본격적인 시스템은 아직 그리기도 전이었다. 그런데 그 전에 눈에 걸리는 게 있었다. 사무소의 파일이 USB로 다니고 있었다.</p>
<p>이 사무소의 협업은 이렇게 굴러갔다. 실무자끼리 파일을 주고받을 땐 메신저, 양이 많으면 USB. 고객 자료는 팩스로, 이메일로, 문자로 제각각 들어온다. 이메일은 각자 개인 계정을 쓰는데 포털도 제각각이다. 데스크탑에는 비밀번호가 없었다. 업무용 AI 구독도 대표님 개인 명의로 따로 결제하고 있었다.</p>
<p>특별한 사무소여서가 아니다. 작은 사업장에선 이게 흔한 풍경이다. 누구도 문제라고 생각한 적이 없었을 뿐이다.</p>
<h3 id="요청은-db로-만들고-싶다였다">요청은 &quot;DB로 만들고 싶다&quot;였다</h3>
<p>대표님의 바람은 처음부터 하나였다. 사무소에 쌓인 자료들을 DB로 만들고 싶다는 것. 파일은 각자의 데스크탑 폴더에 흩어져 있고, 중복되고, 유실될 수 있다. 그래서 출력해 둔 종이 문서를 폐기하지도 못한다.</p>
<p>이건 특정한 날 받은 요청이 아니다. 홈페이지를 만들며 이야기를 나누다 보면 &quot;그러면 나 이런 것도 해보고 싶었는데&quot;가 겹겹이 나왔다. 그 목록을 듣다가 판단이 섰다. 이건 홈페이지 외주가 아니라 작은 사업장의 IT 컨설팅이다. 그리고 DB화든 자동화든, 그 전에 깔려야 하는 게 있다 — 다 같이 쓰는 저장소다.</p>
<p>일원화하자는 제안은 그래서 홈페이지 이야기와 함께 초기부터 나왔다. 실행도 오래 끌지 않았다 — 제안부터 적용까지 3영업일.</p>
<h3 id="사흘-동안-한-일">사흘 동안 한 일</h3>
<ul>
<li>사무소 공용 워크스페이스(협업 도구 묶음)를 만들고, 실무자마다 계정을 발급했다. 홈페이지 때문에 확보해 둔 사무소 도메인이 있어서, 이메일 주소도 그 도메인으로 만들었다.</li>
<li>공유 드라이브의 폴더 구조를 잡았다. 기존 파일을 옮겨 담는 건 실무자들 몫으로 남겼다 — 어떤 파일이 어디로 가야 하는지는 그분들이 안다.</li>
<li>각자 PC에 폴더 동기화를 붙였다. 탐색기에서 늘 쓰던 폴더처럼 보이는데, 실체는 온라인에 있다.</li>
<li>내친김에 업무용 메신저를 세팅하고, 비밀번호 없던 데스크탑에 비밀번호를 걸었다.</li>
</ul>
<h3 id="기술이-아니라-교육이-일이었다">기술이 아니라 교육이 일이었다</h3>
<p>세팅 자체는 어려울 게 없다. 시간은 다른 데서 나갔다.</p>
<p>세 사람 자리를 돌면서 같이 눌러봤다. 매뉴얼 문서는 만들지 않았다. 실무자 세 명을 위해 문서를 쓰는 것보다 옆에서 한 번씩 같이 해보는 게 빨랐다.</p>
<p>이 과정에서 배운 게 있다. 대부분의 사람들은 기술을 파편으로 이해한다. 엑셀이 계산을 자동으로 해준다는 건 안다. 그런데 온라인 스프레드시트는 엑셀이 아닌 줄 안다. 파일 동기화 툴을 권하면 좋다며 쓰는데, 팀원끼리 연동은 안 되어 있다. 도구 하나하나는 알아도, 묶였을 때의 이득은 체감해 본 적이 없는 거다.</p>
<p>거부감도 있었다. 듣도 보도 못한 개발자가 와서 잘 쓰던 방식을 버리라고 하는 셈이니까. 이건 대표님이 정리해 주셨다 — 일단 시키는 대로 해보자고. 그 뒤로는 대부분 협조적이었다.</p>
<h3 id="아직-안-끝난-것들">아직 안 끝난 것들</h3>
<p>다 바뀐 건 아니다. 이메일은 아직 전환 중이다. 실무자 주소가 바뀌면 고객에게 안내하는 내용과 방식도 바뀌어야 해서, 이건 시간이 걸린다. 팩스로 들어오는 자료와 PDF 수기 작업은 여전히 남아 있고, 종이 문서 폐기도 100%는 못 갔다. 보이는 대로 하나씩 고치는 중이다.</p>
<h3 id="바뀐-것">바뀐 것</h3>
<p>이번 편에는 시간 수치가 없다. 대신 장면이 하나 바뀌었다.</p>
<p>전에는 집에서 일을 이어가려면 USB에 파일을 담아 퇴근했다. 지금은 그냥 집에서 연다. 작업의 기준점이 되는 원본이 온라인에 있다는 걸 인지한 뒤로는, 세 사람 모두 최신 버전이 어느 파일인지 헤매지 않는다.</p>
<p>폴더 동기화를 보던 대표님이 물었다. &quot;야 이거 내 개인 노트북에도 연동돼? 이야 좋다.&quot; 온라인에 원본이 있다는 개념 자체가 처음이었던 거다.</p>
<h3 id="배움">배움</h3>
<p>거의 모든 작은 사업장에 이 일이 있다고 생각하게 됐다. 어느 회사의 어떤 도구를 고르는지는 중요하지 않다. 하나의 표준으로 통일하기만 해도 효과는 크다. 다만 그 이득을 체감할 기회가 없었을 뿐이다.</p>
<p>&quot;우리 홈페이지 좀 만들어주면 안 되냐&quot;는 말이 나오는 사업장은, 대개 홈페이지만 필요한 게 아니었다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[코드보다 먼저, 네 사람의 하루를 그렸다]]></title>
            <link>https://velog.io/@dodokim_lab/four-personas</link>
            <guid>https://velog.io/@dodokim_lab/four-personas</guid>
            <pubDate>Mon, 27 Jul 2026 01:46:04 GMT</pubDate>
            <description><![CDATA[<h3 id="기능-목록부터-쓰지-않았다">기능 목록부터 쓰지 않았다</h3>
<p>홈페이지 하나로 시작한 일이 업무 시스템 구축이 됐다는 이야기를 지난 편에 썼다. 그래서 뭐부터 만들었냐 하면 — 코드가 아니라 문서 네 장부터였다.</p>
<p>세무는 나한테 완전히 낯선 도메인이다. 낯선 도메인에서 기능 목록부터 쓰면, 아는 도메인(개발)의 상상으로 채우게 된다. 그 상상이 현장과 어긋난다는 걸 이미 홈페이지 견적 단계에서 배웠다. 그래서 순서를 바꿨다.</p>
<h3 id="네-사람의-하루">네 사람의 하루</h3>
<p>먼저 구조부터. 이 사무소의 일은 위계형 분업이 아니다. 실무자 한 사람이 업체를 백여 곳씩 나눠 맡는다. 자료 수집부터 분류, 고객 관리까지 전 과정을 직접 담당하고, 신고까지 직접 진행하는 경우도 있다.</p>
<p>문제는 그 전 과정이 각자의 스타일로 굴러간다는 점이었다. 자료 받는 방식도, 분류 기준도, 고객 관리 방법도 사람마다 다르다. 일하면서 생긴 노하우는 옆자리로 건너가지 않고 그 사람 안에만 쌓인다. 사무소 차원에서 축적되는 건 없다.</p>
<p>그래서 이 일을 통과하는 네 사람을 각각 한 장씩 적었다. 하루의 흐름, 답답한 지점(pain point), 잘 됐을 때의 기준.</p>
<ul>
<li><strong>대표님.</strong> 최종 책임자다. 처음의 요청 자체가 사무소에 쌓인 자료를 한곳에 모으는 것이었다.</li>
<li><strong>실무자.</strong> 업체 백여 곳의 자료가 이 사람을 통과한다. 문서에는 마감의 풍경을 적었다 — 마감 때 카드내역 수천 건이 한 번에 쏟아지고, 모르는 가맹점이 나오면 옆자리에 묻거나 &#39;기타&#39;로 던지고 넘어간다. 같은 가맹점이 다음 달에 또 나와도 매번 처음부터라는 게 핵심 답답함이다.</li>
<li><strong>자료를 보내주는 고객 사장님.</strong> 이메일이나 메신저로 파일을 던진다. 형식은 제각각이고, 본인은 그게 어떻게 처리되는지 모른다.</li>
<li><strong>시스템을 관리할 사람.</strong> 지금은 나다. 규칙과 계정을 관리하고, 뭔가 꼬였을 때 원인을 찾을 수 있어야 한다.</li>
</ul>
<h3 id="기준을-숫자로-박았다">기준을 숫자로 박았다</h3>
<p>문서마다 &quot;잘 됐을 때&quot;를 숫자로 정의했다. 실무자 기준으로는 — 거래 1,000건당 검토 시간 30분 이내. 자료 누락 0건.</p>
<p>이 숫자들은 나중에 기능의 성적표가 된다. &quot;편해졌다&quot;는 감상 대신 &quot;1,000건에 30분 걸렸나&quot;를 물을 수 있게.</p>
<h3 id="문서가-결정을-바꿨다">문서가 결정을 바꿨다</h3>
<p>이후 뭘 만들지 헷갈릴 때마다 이 문서로 돌아갔다. 이건 누구의 하루가 나아지는 일인가 — 답이 없으면 안 만들었다.</p>
<p>여기까지는 당연한 이야기다. 그런데 머릿속 그림이 아니라 문서로 적어두니, 실제 결정이 달라지는 순간이 왔다. 업무 통계 화면을 대표 전용으로 잠글까 하다가, 실무자 문서에 적어둔 한 줄 — 본인의 작업 통계는 본인이 직접 볼 수 있어야 한다 — 때문에 실무자 전원 공개로 열었다. 머릿속에만 있었으면 관성적으로 잠갔을 거다.</p>
<p>기능 목록은 그다음이었다. 낯선 도메인일수록 순서는 이게 맞다고 생각한다 — 사람의 하루 먼저, 기능은 그다음.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[개인 세무사무소의 IT 컨설팅, 어떻게 시작하게 되었는가]]></title>
            <link>https://velog.io/@dodokim_lab/how-it-started</link>
            <guid>https://velog.io/@dodokim_lab/how-it-started</guid>
            <pubDate>Sat, 25 Jul 2026 11:09:38 GMT</pubDate>
            <description><![CDATA[<p>홈페이지 하나 만들어달라는 부탁에서 시작됐다.</p>
<p>첫 부탁은 소박했다</p>
<p>아이 친구 엄마 중에 세무사무소를 운영하는 대표님이 있다. 어느 날 물으셨다. &quot;홈페이지, 만드는 거 어렵나?&quot;</p>
<p>사연이 있었다. 이미 한 번 외주로 만든 홈페이지가 있는데 — 도메인도 이상하고, 기능도 제대로 동작하지 않고, 너무 촌스럽기까지 했다. 돈을 냈으니 사용은 하지만 맘에 들지 않는 상태. 백엔드 개발자에게 홈페이지는 어려운 일이 아니라서 가볍게 수락했다. 그런데 제대로 만들려면 이 사무소가 어떻게 굴러가는지 알아야 했다. 그래서 물었다 — 평소엔 어떤 프로그램을 쓰시는지, 하루가 어떻게 흘러가는지.</p>
<p>그 질문이 이 모든 일의 시작이었다.</p>
<p>현장을 들여다보니</p>
<p>3인 사무소의 책상 위에는 프로그램이 여러 개 떠 있었다. 신고용, 기장용, 문서 작업용 — 각각 따로 비용이 나가는데, 서로 연결은 되지 않는다. 한쪽에서 내려받은 파일을 사람이 옮겨서 다른 쪽에 넣는다.</p>
<p>실무는 이렇게 흘러간다. 고객 업체들이 이메일과 메신저로 자료를 보낸다. 실무자가 그걸 받아 엑셀로 열고, 거래 한 건 한 건을 손으로 분류한다. 마감 주간이면 이 작업이 수천 건 단위로 쏟아진다. 처음 보는 항목이 나오면 옆자리에 묻고, 옆자리도 모르면 &#39;기타&#39;로 던지고 넘어간다. 이 모든 과정을 각자 자기 스타일대로 한다 — 자료 받는 방식도, 분류 기준도 사람마다 다르다.</p>
<p>가장 흥미로웠던 건 이 지점이다 — 모든 작업이 실무자 개인의 기억에 의존한다. 기록으로 관리되지 않으니, 한 명이 이탈하거나, 인수인계를 하거나, 다른 담당자가 대신 처리할 때 오류가 언제든 생길 수 있는 구조다. 지식의 위치가 문서나 시스템이 아니라 개인이었다.</p>
<p>그래서 할 일이 점점 늘어났다</p>
<p>홈페이지를 새로 만드는 건 기본이었다. 현장을 보고 나니 그 위로 할 일이 쌓였다.</p>
<p>먼저 흩어진 협업 도구부터 일원화하자고 제안했다. 그다음은 사무소 전용 시스템 — 흩어진 프로그램들의 기능을 통폐합하고, 개인의 기억에 있던 지식을 시스템으로 옮기는 쪽으로. 홈페이지 외주가 어느새 업무 시스템 구축이 되어 있었다.</p>
<p>문제는 규모다. 예전 기준이라면 기획자, 백엔드, 프론트엔드, 인프라까지 팀 하나가 붙어야 하는 일이다.</p>
<p>질문이 바뀌었다</p>
<p>예전의 나였다면 이렇게 고민했을 거다. &quot;이걸 혼자 할 수 있을까?&quot;</p>
<p>그런데 AI를 제대로 부려보면서 질문 자체가 바뀌었다. &quot;이걸 두 명 이상이 할 이유가 없는데?&quot;</p>
<p>지식과 코딩 그 자체의 가치는 빠르게 낮아지고 있다. 대신 중요해지는 건 도메인과 현실 — 현장이 진짜로 어떻게 굴러가는지 이해하고, 그걸 동작하는 시스템으로 번역하는 일이다. 그 번역만 사람이 제대로 하면, 나머지 규모는 AI가 감당하는 시대가 됐다. 말로는 누구나 할 수 있는 이야기라서, 직접 확인하기로 했다. 완전히 낯선 도메인에서, 혼자서, 처음부터 끝까지.</p>
<p>무엇을 기록할 것인가</p>
<p>이 시리즈는 그 검증의 기록이다. 몇 가지 원칙을 미리 밝혀둔다.</p>
<p>성공담보다 시행착오를 쓴다. 가정이 현장에서 깨진 순간, 하루 만에 되돌린 결정, 정성껏 만들었는데 휴지가 된 문서 — 그런 것들이 본편이다.</p>
<p>숫자는 시간으로 쓴다. 매출이나 금액이 아니라, &quot;몇 시간 걸리던 일이 몇 분이 됐나&quot;를 기준으로 기록한다.</p>
<p>고객을 특정할 수 있는 디테일은 각색한다. 사무소의 이름, 실제 데이터, 시스템의 구체적인 구현은 쓰지 않는다. 흐름과 배움만 남긴다.</p>
<p>다음 편은 첫 현장 방문 이야기다. 코드를 쓰기 전에, 이 일을 통과하는 네 사람의 하루를 문서로 그리는 것부터 시작했다.</p>
]]></description>
        </item>
    </channel>
</rss>