먼저 밝힌다. 2026년 6월에 이 매거진은 1인 사업자가 여러 역할을 한 화면으로 모은 이야기를 실었다. 이 현장은 특정 업체가 아니다. 실제로 그렇게 돌아가는 형태를 그대로 구성했고, 어느 곳인지는 밝히지 않는다. 아래는 그 형태대로 직접 만든 것이다.
혼자 사업하는 사람에게 진짜 아픈 건 일의 양이 아니다. 전환이다. 주문 하나 보다가 세금 날짜 보고, 거래처 메일 보다가 다시 주문으로 — 그때마다 잡고 있던 실이 끊긴다.
그래서 "한 화면에 다 모으면 된다"는 말은 절반만 맞다. 다 모으는 건 그냥 목록이고, 목록 만드는 건 어렵지 않다. 어려운 건 그 목록을 혼자 믿는 일이다. 틀렸을 때 잡아 줄 사람이 없기 때문이다.
그러니 만들 것은 "모든 걸 보여주는 화면"이 아니라 "틀렸는지 확인할 수 있는 화면" 이다. 두 가지를 넣었다.
출처 없는 항목은 없다
/// Where this came from. An item with no source is a note somebody typed and
/// forgot; an item with one can be checked.
final String source;
실행 로그다.
[sales] Follow up Hanul, quoted 2 weeks ago — 9 days late 1.8M won (from quote log)
[supply] Reorder packaging film — 2 days late 0.5M won (from stock below reorder point)
[sales] Quote for Mirae — 40 units — in 1 days 3.2M won (from inbox 04-18)
[making] Batch 118 labels — in 3 days (from production plan)
[books] VAT quarterly return — in 5 days (from filing calendar rule)
[books] Reconcile March card statement — in 8 days (from bank feed)
여섯 줄 전부 (from …) 을 달고 있다. 출처 없는 줄은 누가 언제 왜 적었는지 모르는 메모이고, 혼자 일하는 사람의 화면에서 그런 줄은 시간이 지나면 무시된다. 무시되기 시작하면 화면 전체가 무시된다.
검증이 빈 출처를 잡는다.
MISSING=$(grep -c "(from )" captures/run.log || true)
[ "$MISSING" -eq 0 ] || { echo " $MISSING item(s) appeared with an empty source"; exit 1; }
왜 이게 맨 위인지 화면이 말한다
여기가 이 편의 중심이다.
목록은 정렬돼야 한다. 그런데 정렬 함수는 코드 안에 있고, 코드 안에 있는 결정은 아무도 반박할 수 없다.
/// Why the list is in this order, in words the owner can disagree with.
///
/// A sort function hidden in code is a decision nobody can argue with. Naming
/// the rule on the screen means the owner can look at the top item and say
/// "no, the packaging can wait" — and know which rule to change.
static const orderingRule =
'late first, then money at stake, then soonest due';
그 문장이 화면 아래에 그대로 뜬다.
ordering rule on screen: "late first, then money at stake, then soonest due"
이게 있으면 사장이 맨 위 항목을 보고 "아니 포장재는 나중에 해도 되는데"라고 말할 수 있고, 어느 규칙을 고쳐야 하는지도 안다. 없으면 그냥 "이 앱은 순서가 이상하네"로 끝나고, 결국 안 쓰게 된다.
그리고 검증이 화면이 말한 규칙대로 실제로 정렬됐는지 대조한다.
line = [l for l in open(sys.argv[1]) if 'late, in order:' in l][0]
amounts = [float(m) for m in re.findall(r'\(([0-9.]+)M won\)', line)]
if amounts != sorted(amounts, reverse=True):
print(f' late items are not ordered by money at stake: {amounts}'); sys.exit(1)
통과 로그다.
late items ordered by money at stake: [1.8, 0.5]
말한 규칙과 실제 순서가 어긋나는 것이 흔한 버그이고, 혼자 쓰는 도구에서는 그 어긋남을 아무도 지적해 주지 않는다.
