시간 추적에서 청구 앱까지: 항목을 브랜드 PDF로 전환하기
프로젝트 시간을 기록해 인보이스로 묶고, 클라이언트에 보낼 수 있는 브랜드된 PDF를 생성하는 시간 추적→청구 앱을 구축하세요.

무엇을 만들고 왜 중요한가요
시간 추적→청구 앱은 흔한 혼란을 정리합니다: 근무 시간이 캘린더, 채팅, 메모 등에 흩어지고, 청구일이 되면 누군가 한 달치를 손으로 재구성해야 합니다. 그 과정에서 빠진 청구 시간, 잘못된 요금, 중복 라인, 총합 불일치 같은 실수가 발생하죠.
이 앱은 시간 단위로 청구하는 사람이라면 누구에게나 유용합니다: 여러 클라이언트를 다루는 프리랜서, 같은 프로젝트에 여러 명이 시간을 기록하는 에이전시, 내부적으로 고객이나 부서에 시간을 청구하는 팀 등입니다.
목표는 실용적입니다: 프로젝트별 시간 항목을 캡처하고, 이를 인보이스 레코드로 묶어 클라이언트가 이해할 수 있는 브랜드 PDF를 생성하는 것입니다. 워크플로가 안정되면 인보이스는 월말의 소동이 아니라 규칙적인 작업이 됩니다.
"단순함 우선" 원칙은 보통 다음을 의미합니다:
- 시간을 기록하는 한 가지 방법(날짜, 프로젝트, 시간, 메모)
- 하나의 요금 규칙(프로젝트별 또는 개인별)
- 기간별 클라이언트당 한 개의 인보이스
- 로고와 사업자 정보를 포함한 하나의 PDF 레이아웃
- 명확한 상태(Draft, Sent, Paid)
작은 시나리오 예: 두 명으로 구성된 스튜디오가 “Client A - Website Updates” 프로젝트의 시간을 추적합니다. 각자가 주중에 항목을 기록하고, 금요일에 그 프로젝트와 날짜 범위로 인보이스를 생성하면 앱이 항목을 인보이스 라인으로 변환하고 PDF가 재입력 없이 전송할 준비가 됩니다.
AppMaster 같은 노코드 플랫폼을 사용하는 경우, 영수증, 다중 통화, 할인, 승인 같은 추가 기능을 넣기 전에 데이터와 워크플로를 먼저 제대로 설계하세요. 핵심 흐름이 빠르고 정확하며 쉽게 망가지지 않으면 다른 기능은 나중에 더 쉽게 추가할 수 있습니다.
포함할 핵심 기능(초기에는 무엇을 빼야 하는지)
작은 첫 버전은 ‘보낼 수 있는 인보이스’를 더 빨리 만들어 줍니다. 시간 캡처, 시간→명확한 인보이스 라인 변환, 클라이언트가 추가 질문 없이 읽을 수 있는 PDF 생성에 집중하세요.
몇 가지 핵심 레코드로 시작하세요(나중에 이름을 바꿀 수 있지만 구조는 중요합니다): Client, Project, Time Entry, Invoice, Invoice Line.
인보이스 워크플로는 인보이스 레코드의 단일 상태 필드로 간단하게 유지하세요. Draft, Sent, Paid로 많은 팀의 요구를 오랫동안 충족할 수 있습니다.
필수 액션은 매주 실제로 발생하는 작업과 일치해야 합니다:
- 시간 기록(수동 입력이 보통 가장 빨리 만들 수 있고 수정하기 쉬움)
- 시간 승인(간단히 ‘Approved’ 상태만 있어도 됨)
- 승인된 시간으로 인보이스 생성
- PDF 내보내기
"브랜딩"은 화려함을 의미하지 않습니다. 일관되고 신뢰할 수 있다는 뜻입니다: 로고, 사업자 정보, 인보이스 번호와 날짜, 명확한 합계, 결제 안내.
초기에는 세금, 할인, 다중 통화, 첨부파일을 빼세요. 유용하긴 하지만 반올림, 관할권 규칙, 환율, 파일 저장 같은 엣지 케이스를 만들어 첫 출시를 늦춥니다.
데이터 모델: 필요한 레코드와 중요한 필드
시간 추적→청구 앱은 데이터 모델에 생사가 달려 있습니다. 작고 예측 가능하게 유지해 합계가 항상 약속한 대로 맞도록 하세요.
최소 테이블 세트는 보통 다음과 같습니다:
- Client: 이름, 청구 이메일, 청구 주소, 기본 통화, 결제 조건(예: Net 14)
- Project: client_id, 프로젝트 이름, 기본 시급(선택), 활성 플래그
- Time entry: project_id, 담당자(이름 또는 user_id), 날짜, 소요 시간(시간), 설명, rate_at_time, billable(예/아니오), invoiced_invoice_id(청구 전까지 비어있음)
- Invoice: client_id, project_id(선택), 인보이스 번호, 발행일, 만기일, 상태, 소계, 세금, 총계
요금은 앱이 복잡해지는 지점입니다. 한 가지 접근법을 선택하고 일관되게 유지하세요: 프로젝트별 요금, 개인별 요금, 또는 작업/서비스별 고정 요금.
기본 요금을 프로젝트나 개인에 저장하더라도, 항목 생성(또는 승인) 시 실제 사용된 요금을 각 시간 항목에 rate_at_time으로 복사하세요. 이렇게 하면 요금이 나중에 바뀌어도 놀라움이 발생하지 않습니다. 인보이스는 작업이 발생했을 때의 사실을 반영해야 합니다.
시간 항목은 별도 상태를 생략하고 invoiced_invoice_id가 비어있는지 여부로 판별할 수 있습니다. 인보이스는 상태를 엄격하게 유지하세요: Draft, Ready, Sent, Paid(필요하면 Void 추가).
AppMaster에서는 Data Designer가 PostgreSQL에 잘 매핑되어 관계를 명확히 유지하고 필드를 불필요하게 복제하지 않도록 돕습니다.
프로젝트별 시간 항목 캡처(단순 UX)
시간 캡처는 앱이 손쉽게 느껴지느냐 무시되느냐를 결정합니다. 첫 버전은 지루하고 빠르게 만드세요: 한 화면, 하나의 주요 액션, 가능한 선택지를 최소화.
시작할 캡처 방법을 하나만 선택하세요. 보통 초기에는 수동 입력이 가장 유리합니다. 타이머는 사람들이 실제로 하루를 어떻게 추적하는지 파악한 후 추가하세요. 타이머를 추가하더라도 놓친 중지를 보정할 수 있게 수동 편집을 허용하세요.
청구 품질을 보호하는 필드는 필수로 만드세요:
- 프로젝트(또는 클라이언트 + 프로젝트)
- 날짜
- 소요 시간(시간 및 분)
- 짧은 설명(클라이언트가 알아볼 수 있는 내용)
- 담당자(여러 명이 시간을 기록하면 필요)
반올림 규칙은 조기에 결정하세요. 반올림 방식은 신뢰와 합계에 영향을 줍니다. 일반적 접근은 6분 단위(0.1시간)입니다. 각 항목을 반올림하는 것이 일일 합계를 반올림하는 것보다 설명과 감사 측면에서 더 간단합니다.
청구에 여러 사람이 관여한다면 가벼운 승인 단계를 추가하세요. 실무 규칙: 승인되면 기본적으로 항목을 편집 불가로 잠그세요. 변경이 필요하면 관리자 역할이 다시 열고 누가 왜 변경했는지 기록하게 하세요.
시간을 인보이스 라인으로 전환하기(롤업 규칙)
롤업은 원시 로그를 클라이언트가 이해할 수 있는 인보이스 라인으로 바꾸는 부분입니다. 규칙을 단순하고 반복 가능하게 유지해 생성하는 모든 인보이스를 신뢰할 수 있게 하세요.
한 가지 액션으로 시작하세요: 클라이언트와 날짜 범위를 선택하면 일치하는 청구되지 않은 시간 항목만 끌어옵니다. 이 필터가 중복 청구를 막는 안전장치입니다. 항목에 클라이언트나 프로젝트가 빠져있으면 “청구 준비 안 됨”으로 처리해 수정될 때까지 롤업에서 제외하세요.
항목을 인보이스 라인으로 그룹화하는 방법
그룹화는 라인 수와 클라이언트가 검토하기 쉬운 정도를 결정합니다. 기본값을 하나 정하고 필요하면 옵션 스위치 하나만 추가하세요.
일반적인 그룹화 옵션:
- 프로젝트별
- 담당자별(요금이 다를 때 유용)
- 일별 또는 주별
- 작업/카테고리별(Design vs Development)
무엇을 선택하든 각 라인은 명확한 레이블, 총 시간, 요금, 라인 금액을 보여야 합니다. 요금이 변경될 수 있다면 각 항목에 저장된 rate_at_time(또는 ‘유효 시작’ 날짜가 있는 요금 테이블)을 사용하세요. 현재 요금 하나만 사용하면 안 됩니다.
청구로 표시하기(유연하게 처리하기)
항목을 인보이스에 추가할 때 각 시간 항목에 인보이스 ID를 저장하세요. 그러면 감사 추적이 가능하고 같은 항목이 다시 뽑히는 것을 방지합니다.
수정은 발생합니다. 인보이스에서 라인을 제거하면 기록을 삭제하지 마세요. 영향을 받은 시간 항목의 인보이스 ID를 분리(필드 비우기)하고 합계를 재계산하며 “잘못된 프로젝트로 인해 2.0h 제거” 같은 짧은 메모를 저장하세요.
AppMaster에서는 unbilled 항목 쿼리 → 그룹화 → 인보이스 라인 생성 → 각 항목에 인보이스 참조 업데이트 같은 단일 비즈니스 프로세스로 이 흐름을 구현하기 좋습니다.
인보이스 레코드: 합계, 번호 체계, 상태
인보이스 레코드는 나중에 보낼 수 있고 추적 가능하며 감사할 수 있는 컨테이너입니다. 누군가 프로젝트 이름을 바꾸거나 기본 요금을 변경해도 인보이스 자체는 안정적이어야 합니다.
실용적인 인보이스 헤더 항목:
- 인보이스 번호(고유하고 사람이 읽기 쉬운 형식)
- 발행일 및 만기일
- 청구 대상 정보(클라이언트 이름, 청구 주소, 필요 시 세금 ID)
- 메모(결제 안내, 짧은 감사 문구)
- 통화(해외 청구 시 저장된 환율 선택적 포함)
합계를 예측 가능하게 유지하세요. 소계는 인보이스 라인의 합입니다. 그런 다음 할인(고정액 또는 %), 세금(보통 할인된 소계에 대해 계산)을 적용하고 최종 합계를 저장하세요. 사용된 정확한 세율과 할인 값을 저장해 나중에 같은 인보이스를 재현할 수 있도록 하세요.
인보이스 번호는 복잡할 필요 없습니다. 패턴을 정하고 일관되게 사용하세요: 연속 번호(000123), 연도별(2026-00123), 또는 클라이언트 접두사+번호(ACME-014). 일관성이 완벽함보다 중요합니다.
상태는 커뮤니케이션과 내부 통제에 집중하세요:
- Draft(편집 가능, 전송되지 않음)
- Ready(합계 잠김)
- Sent(클라이언트와 공유됨)
- Paid(결제 확인됨)
- Overdue(만기일 경과)
- Void(취소, 기록 보존)
클라이언트가 읽기 쉬운 브랜드 PDF 생성
좋은 인보이스 PDF는 두 가지 질문에 빠르게 답합니다: 무엇을 청구하는가, 어떻게 결제하는가. PDF는 인보이스 레코드에서 생성하세요(원시 시간 항목에서 생성하지 말 것). 그래야 문서가 항상 인보이스 번호, 합계, 상태와 일치합니다.
대부분의 클라이언트는 매번 같은 블록을 기대합니다:
- 헤더(사업자명, 인보이스 번호, 발행일)
- 클라이언트 정보(회사, 담당자, 청구 주소, 필요 시 세금 ID)
- 라인 아이템(설명, 수량 또는 시간, 요금, 라인 합계)
- 합계(소계, 세금, 할인, 총합)
- 결제 조건(만기일, 허용 결제 수단, 연체료 안내(사용 시))
브랜딩도 중요하지만 가독성이 더 중요합니다. 악센트 색상 하나만 사용하고 깔끔한 글꼴을 쓰며 총합을 쉽게 스캔할 수 있게 하세요.
레이아웃 문제는 실제 데이터로 테스트할 때 드러납니다. 긴 설명과 30줄 이상의 항목으로 테스트하세요. 열 머리글이 새 페이지에도 반복되고 합계 블록이 함께 유지되는지 확인하세요.
AppMaster에서 PDF를 생성하면 PDF를 인보이스 레코드에 파일(또는 저장 참조)과 함께 타임스탬프 및 버전과 저장해 두세요. 그러면 클라이언트에게 재전송할 때 같은 문서를 쉽게 보낼 수 있습니다.
단계별 빌드 계획(노코드 워크플로)
무엇을 "진실의 원천(source of truth)"으로 할지 결정하세요. 시간 항목은 원시 사실입니다. 인보이스는 보낼 수 있고 나중에 감사를 할 수 있는 스냅샷입니다.
1) 먼저 데이터 모델을 설계하세요
테이블과 관계를 만들고, 기본이 안정되면 몇 가지 품질 필드를 추가하세요:
- Clients
- Projects
- Time Entries
- Invoices
- Invoice Lines
2) 두 개의 간단한 화면을 만드세요
UI는 최소화하세요:
- 시간 입력 폼: 프로젝트, 날짜, 소요 시간, 메모, 저장
- 인보이스 검토: 클라이언트, 기간, 라인, 합계, 상태
관리 및 검토용으로 웹 UI면 보통 충분합니다. 이동 중에 시간을 기록하는 사용자를 위해 나중에 모바일 화면을 추가하세요.
3) 롤업 로직을 자동화하세요
다음과 같은 흐름을 구축하세요: 클라이언트+기간 선택 → 청구되지 않은 항목 가져오기 → 그룹화 → 인보이스 라인 생성. 항목을 인보이스에 연결하는 작업은 인보이스가 승인되거나 Ready로 이동한 후에 수행하세요.
4) PDF 생성 및 저장
인보이스 헤더, 클라이언트 정보, 라인을 템플릿에 넣어 출력물을 생성하고 인보이스 레코드에 저장하는 "Generate PDF" 액션을 추가하세요.
예시: 주간 시간 기록에서 클라이언트용 인보이스로
3인 에이전시가 한 클라이언트( Northstar Co )에 대해 두 프로젝트(Website Refresh, Monthly Support)에 대해 2주간 청구합니다. 팀은 Alex(디자인), Priya(개발), Sam(PM)으로 구성됩니다. 모두 매일 클라이언트, 프로젝트, 날짜, 짧은 메모를 선택해 시간을 기록합니다.
매일 항목은 Draft로 저장됩니다. 금요일 오후 Sam이 "이번 주, Northstar Co"로 필터된 검토 화면을 열어 두 개의 메모를 수정(“Homepage hero” 대신 “Hero” 등), 청구/비청구 여부를 확인하고 주를 잠급니다.
그 주의 항목 예시는 다음과 같습니다:
| Date | Person | Project | Hours | Note |
|---|---|---|---|---|
| Mon | Priya | Website Refresh | 2.5 | Header layout fixes |
| Tue | Alex | Website Refresh | 3.0 | New homepage mock |
| Tue | Sam | Monthly Support | 1.0 | Client call |
| Wed | Priya | Website Refresh | 4.0 | Contact form logic |
| Thu | Alex | Monthly Support | 1.5 | Banner update |
| Thu | Priya | Monthly Support | 2.0 | Email template tweak |
| Fri | Sam | Website Refresh | 1.0 | QA and handoff |
Sam이 "인보이스 생성"을 클릭하면 앱이 단순 규칙으로 항목을 인보이스 라인으로 롤업합니다: 프로젝트와 청구 요금으로 그룹화하고, 시간을 합산하며 짧은 설명을 가져옵니다. 인보이스는 결국 3개의 라인을 갖습니다:
| Line | Description | Qty | Rate | Amount |
|---|---|---|---|---|
| 1 | Website Refresh (Design) | 3.0 hrs | $120 | $360 |
| 2 | Website Refresh (Development/PM) | 7.5 hrs | $140 | $1,050 |
| 3 | Monthly Support | 4.5 hrs | $110 | $495 |
시스템은 NS-2026-014 같은 인보이스 번호를 할당하고 소계와 세금을 계산한 뒤 상태를 Ready로 설정합니다. 한 번 더 클릭하면 로고, 클라이언트 주소, 라인 상세, 합계, 결제 메모가 포함된 브랜드 PDF가 생성됩니다. 전송 후 상태는 Sent로 업데이트되고 기본 시간 항목은 인보이스 참조가 설정되어 중복 청구를 방지합니다.
흔한 실수와 피하는 방법
대부분 문제는 산수의 문제가 아니라 워크플로의 문제입니다.
청구된 시간 항목을 잠그지 않음. 사람들이 같은 항목을 편집하거나 다시 선택할 수 있으면 결국 중복 청구가 발생합니다. 각 시간 항목에 인보이스 참조를 두고, 청구된 항목은 "청구 준비" 보기에 나타나지 않게 하세요.
요금 변경 시 기록을 덮어씀. "현재" 프로젝트나 사용자 요금만 사용하면 과거 요금 변경으로 오래된 인보이스가 바뀔 수 있습니다. 각 항목에 유효한 요금인 rate_at_time을 복사하세요.
승인된 시간을 감사 없이 편집함. 승인자, 승인 시간, 승인 후 변경에 대한 짧은 변경 노트를 추가하세요.
실제 데이터에서 깨지는 PDF. 긴 설명, 많은 라인 아이템, 큰 숫자는 템플릿을 압박합니다.
레이아웃 문제를 예방하는 간단한 수정:
- 설명 길이 최대치를 설정하고 넘치는 내용은 노트 섹션으로 이동
- 줄 바꿈 허용 및 30+ 행으로 테스트
- 테이블 공간을 확보하려면 헤더를 간결하게 유지
- 일관된 숫자 형식(통화, 소수점) 사용
모호한 상태 흐름. 규칙이 없으면 인보이스가 두 번 전송되거나 아예 전송되지 않을 수 있습니다.
간단하고 안전한 흐름: Draft -> Ready -> Sent -> Paid. Draft에서만 롤업을 허용하고, 합계가 잠길 때만 PDF 생성을 허용하세요.
짧은 체크리스트와 실용적인 다음 단계
인보이스를 보내기 전에 간단한 검토를 하세요. 잘못된 합계, 빠진 세부사항, 화면에서는 괜찮아 보이지만 인쇄하면 깨지는 PDF를 대부분 예방합니다.
송신 전 체크리스트:
- 클라이언트 정보가 완전한가(법적 명칭, 청구 주소, 올바른 담당자)
- 인보이스 기간이 정확한가(시작/종료일이 작업과 일치)
- 합계가 일관된가(소계, 세금, 총합이 항목, 요금, 반올림과 일치)
- 시간이 빠지거나 중복되지 않았는가(빠진 청구 없음, 중복 포함 없음)
- 운영 필드가 정리되어 있는가(고유 인보이스 번호, 올바른 상태, 인보이스에 PDF 저장됨)
그런 다음 "프린터 관점"으로 PDF를 미리보기하세요. 로고 배치, 긴 주소, 테이블 줄 바꿈, 페이지 구분을 확인하세요. 짧은 인보이스(1-2 라인)와 긴 인보이스(20+ 라인) 모두 테스트하세요.
기초가 안정되면 다음 단계를 진행하세요:
- 일관된 템플릿으로 이메일로 인보이스 전송
- Stripe 결제 연동으로 결제 시 자동으로 인보이스를 Paid로 표시
- 권한 추가로 요금 편집, 시간 승인, 상태 변경 권한을 제한
빠르게 만들고 반복하고 싶다면 AppMaster (appmaster.io)는 노코드 인보이스 앱을 실제 데이터베이스, 비즈니스 로직, PDF 생성과 함께 만들고 요구사항이 바뀔 때 깨끗한 소스 코드를 재생성할 수 있는 실용적인 옵션입니다.
이번 주에 한 가지를 개선할 수 있다면 "청구되지 않은 시간"이 누락되지 않도록 하는 것입니다. 그것만으로도 시간을 절약하고 수익을 보호할 수 있습니다.
자주 묻는 질문
먼저 각 시간 항목에 프로젝트, 날짜, 소요 시간(기간), 짧은 설명이 있는지 확인하세요. 그런 다음 클라이언트와 날짜 범위를 선택해 인보이스를 생성하고, 청구되지 않은 항목만 끌어와 인보이스 라인으로 그룹화한 뒤 인보이스 스냅샷에서 PDF를 생성하세요.
다섯 개 레코드를 사용하세요: Client, Project, Time Entry, Invoice, Invoice Line. 필드는 최소화하되 각 시간 항목에 rate_at_time를 포함하고, 청구 이력을 일관되게 유지하고 중복 청구를 막기 위해 invoiced_invoice_id 참조를 두세요.
작업 시점에 사용된 요금을 각 시간 항목에 저장하세요(예: rate_at_time). 기본값은 프로젝트나 개인에 둘 수 있지만, 인보이스는 항상 저장된 요금으로 계산해 이후 요금 변경으로 과거 인보이스가 바뀌지 않게 하세요.
하나의 반올림 규칙을 선택해 일관되게 적용하고 프로세스에서 투명하게 공개하세요. 일반적인 방법은 6분 단위(0.1시간)로 각 항목을 반올림하는 것입니다. 각 항목을 반올림하면 설명과 감사를 위해 더 간단합니다.
인보이스에 단일 상태 필드를 사용하고 간결하게 유지하세요: Draft, Ready, Sent, Paid. 규칙은 명확히 하세요(예: 롤업은 Draft 상태에서만 허용, Ready에서 합계 잠금).
인보이스 생성 시 invoiced_invoice_id가 비어있는 시간 항목만 가져오도록 필터링하고, 항목이 인보이스에 붙으면 해당 필드에 값을 설정하세요. 또한 청구 대상 목록에서 이미 청구된 항목을 숨겨 같은 시간이 다시 선택되지 않도록 하세요.
인보이스는 원시 시간 항목이 아니라 인보이스 레코드에서 PDF를 생성하세요. 그래야 문서가 항상 인보이스 번호, 합계, 상태와 일치합니다. 명확한 헤더, 클라이언트 정보, 항목, 합계, 결제 지침을 포함하고, 긴 설명과 30줄 이상의 항목으로 템플릿을 테스트하세요.
기록을 삭제하지 마세요. 영향을 받은 시간 항목의 인보이스 참조를 분리(필드 비우기)하고 인보이스 라인과 합계를 다시 생성하며 짧은 수정 노트를 남기세요. 이렇게 하면 나중에 무슨 변경이 있었는지 설명할 수 있습니다.
처음에는 수동 시간 입력으로 시작하세요. 빠르게 만들 수 있고 수정하기 쉽습니다. 타이머는 놓침, 편집, 장치 문제 같은 엣지 케이스를 더하므로 핵심 워크플로가 안정된 후에 추가하는 것이 좋습니다.
핵심 흐름(시간 항목 캡처, 승인/잠금, 청구되지 않은 시간에서 인보이스 생성, PDF 생성)을 먼저 구축하세요. 세금, 다중 통화, 할인, 첨부파일은 초기에는 생략하세요. 이 기능들은 엣지 케이스를 만들어 출시를 지연시킬 수 있습니다.


