diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml index 3e698ad..6ddc7d0 100644 --- a/.gitea/workflows/deploy.yml +++ b/.gitea/workflows/deploy.yml @@ -78,6 +78,9 @@ jobs: - name: Copy migrations run: mkdir -p ./publish/db && cp -r db/migrations ./publish/db/ || true + - name: Validate migration version uniqueness + run: bash scripts/validate_migrations.sh db/migrations + - name: Generate build info run: | COMMIT_HASH=$(git rev-parse --short HEAD) @@ -109,6 +112,9 @@ jobs: - name: Package artifact run: | cp deploy_gb.sh ./publish/deploy_gb.sh + mkdir -p ./publish/scripts + cp scripts/validate_migrations.sh ./publish/scripts/validate_migrations.sh + chmod +x ./publish/scripts/validate_migrations.sh tar -czf taxbaik_deploy.tgz -C ./publish . echo "✓ Package: $(du -sh taxbaik_deploy.tgz | cut -f1)" @@ -175,7 +181,12 @@ jobs: test -s "\$DEPLOY_DIR/proxy/TaxBaik.Proxy.dll" \ || { echo "FATAL: TaxBaik.Proxy.dll 없음" >&2; exit 1; } - echo "--- [3/4] Green-Blue 배포 실행 ---" + echo "--- [3/5] 마이그레이션 사전 검증 ---" + test -x "\$DEPLOY_DIR/scripts/validate_migrations.sh" \ + || { echo "FATAL: validate_migrations.sh 없음" >&2; exit 1; } + "\$DEPLOY_DIR/scripts/validate_migrations.sh" "\$DEPLOY_DIR/db/migrations" "postgresql://taxbaik:taxbaik123@localhost:5432/taxbaikdb" + + echo "--- [4/5] Green-Blue 배포 실행 ---" chmod +x "\$DEPLOY_DIR/deploy_gb.sh" "\$DEPLOY_DIR/deploy_gb.sh" "\$DEPLOY_DIR" diff --git a/db/migrations/V025__AddNineBlogPosts.sql b/db/migrations/V025__AddNineBlogPosts.sql deleted file mode 100644 index ede0e6a..0000000 --- a/db/migrations/V025__AddNineBlogPosts.sql +++ /dev/null @@ -1,551 +0,0 @@ --- V025: Add 9 new blog posts with correct SQL structure --- All posts follow BLOG_TEMPLATE.md guidelines: 3-step structure, accuracy principle, list format - -INSERT INTO blog_posts (title, content, slug, category_id, is_published, seo_title, seo_description, tags, created_at, updated_at) VALUES - --- 1. 프리랜서가 놓친 경비 5가지 -( -'프리랜서가 놓친 경비 5가지 - 이것도 인정될까요?', -$$# 프리랜서가 놓친 경비 5가지 - -"프리랜서인데 경비로 인정되는 게 뭐고 안 되는 게 뭐죠?" - -많은 프리랜서들이 이 질문을 합니다. 소득세법 제34조에 따르면 필요경비는 소득을 얻기 위해 직접 또는 간접적으로 필요한 비용입니다. - -1️⃣ 이 정도는 누구나 배울 수 있어요 - -기본 경비: -- 통신비: 인터넷, 휴대폰 요금 -- 교육비: 업무 관련 강좌, 자격증 -- 차량유지비: 업무용 차량 유지 -- 소프트웨어: 업무용 프로그램, 구독료 -- 사무실비: 작업 공간, 임차료 - -영수증만 있으면 대부분 인정됩니다. - -2️⃣ 하지만 현실은 복잡해요 - -통신비는 얼마까지? -- 개인과 업무를 구분해야 함 (예: 핸드폰 60% 만 경비) -- 세무청이 불인정하면 증빙책임은 당신 - -차량유지비는 모두 경비? -- 업무용만 전부 가능 -- 개인 차량의 일부만 인정 (주관적 판단) -- 휘발유 영수증만으로는 부족 (주행 기록 요구 가능) - -소프트웨어는 모두 경비? -- 영상 제작자: 어도비는 필수 (인정) -- 엑셀: 모든 직업이 사용하지만 개인용도도 있음 (일부만 인정) -- 채팅앱: 고객 소통이지만 개인도 섞임 (판단 필요) - -연간 경비가 50%를 넘으면? -- 2025년 기준 평균은 30~40% -- 당신이 50%를 신고하면 세무청이 의심 - -3️⃣ 그래서 세무사가 필요합니다 - -세무사는: -- 통신비/차량비의 합리적 배분 기준 제시 -- 소프트웨어별 업무 관련성 판단 -- 세무청 질의에 대한 공식 근거 준비 -- 이의신청 시 법적 논거 제시 - -법적 근거: 소득세법 제34조 필요경비 규정 -$$, -'freelancer-expenses', -1, -true, -'Freelancer Expenses - Tax Deduction Guide', -'5 common expenses freelancers overlook, with tax law basis (소득세법 제34조)', -'프리랜서,경비,필요경비,소득세,세무', -NOW(), -NOW() -), - --- 2. 월세 신고하는 방법 -( -'월세 신고하는 방법 - 환급받을 수 있는 금액이 있습니다', -$$# 월세 신고하는 방법 - -"월세를 낼 때 세금 환급이 있다던데 정말인가요?" - -소득세법 제59조의2에 따르면 월세세액공제가 있습니다. 신고하지 않으면 한 푼도 못 받습니다. - -1️⃣ 이 정도는 누구나 배울 수 있어요 - -월세세액공제 조건 (2025년 기준): -- 본인 거주 주택의 월세: 연 750만 원 한도 -- 필요 서류: 임대차계약서, 월세 납부 증빙 -- 환급액: 연 월세의 10% (최대 75만 원) - -예시 (월 60만 원 월세): -- 연 월세: 720만 원 -- 환급액: 72만 원 - -2️⃣ 하지만 현실은 복잡해요 - -증빙 서류가 충분한가? -- 임대차계약서: 필수 -- 월세 납부 증빙: 현금? 계좌이체? 어느 정도? -- 세무청이 불인정하면? 환급 못 받음 - -선택지가 있다고? -- 표준세액공제 vs 월세세액공제: 어느 게 더 유리? -- 부양가족이 있으면? 배우자가 신청하면? -- 전세금이 있으면? 월세와 함께? - -2년 뒤에 적용된다고? -- 2023년 월세는 2025년 환급 -- 기한을 놓치면? 5년 내 수정신고 가능하지만 복잡 - -3️⃣ 그래서 세무사가 필요합니다 - -세무사는: -- 증빙 서류 사전 점검 -- 월세 vs 표준세액 최적 선택 -- 배우자/부양가족 고려 -- 기한 관리 및 수정신고 - -법적 근거: 소득세법 제59조의2 월세세액공제 -$$, -'monthly-rent-tax-credit', -1, -true, -'Monthly Rent Tax Credit Guide', -'How to claim rental tax deduction (월세세액공제) under Income Tax Act Article 59-2', -'월세,세액공제,환급,소득세', -NOW(), -NOW() -), - --- 3. 자녀 증여세 계산하기 -( -'자녀 증여세 계산하기 - 기초공제를 모르면 손해봅니다', -$$# 자녀 증여세 계산하기 - -"자녀에게 돈을 주면 세금을 내야 하나요?" - -상속세및증여세법 제13조에 따르면 기초공제가 있습니다. 공제 한도 내면 세금 0원입니다. - -1️⃣ 이 정도는 누구나 배울 수 있어요 - -기초공제 (2025년 기준): -- 자녀 1명당 5,000만 원 (10년마다) -- 미성년자 자녀: 2,000만 원 (10년마다) - -예시 (자녀 1명, 성인): -- 5,000만 원 선물 = 증여세 0원 -- 6,000만 원 선물 = 1,000만 원 초과분에 대해 세금 계산 - -공제 계산: -- 10년 단위로 계산 -- 2015년 1,000만 원 + 2025년 4,000만 원 = 연 500만 원 × 10년 계산 - -2️⃣ 하지만 현실은 복잡해요 - -10년이 정확히 몇 년인가? -- 정확히 10년이어야 함 -- 9년 11개월은 계산에 포함됨 -- 세무청 판단이 엄격함 - -자녀가 여러 명이면? -- 자녀별로 5,000만 원씩 -- 배우자 증여분은 별도 계산 -- 자녀가 결혼하면? 성인 vs 미성년 기준 - -증여세율은 얼마인가? -- 초과분의 10~50% (금액별로 다름) -- 1,000만 원 초과시 10%에서 시작 -- 계산 복잡함 - -3️⃣ 그래서 세무사가 필요합니다 - -세무사는: -- 기초공제 정확한 계산 -- 자녀 수에 따른 최적 증여 계획 -- 세율 시뮬레이션 -- 장기 증여 전략 수립 - -법적 근거: 상속세및증여세법 제13조 기초공제 -$$, -'gift-tax-calculation', -1, -true, -'Gift Tax for Children Calculation', -'How to calculate inheritance and gift tax with basic deduction (상속세및증여세법 제13조)', -'증여세,자녀,기초공제,상속세', -NOW(), -NOW() -), - --- 4. 사업자 등록 타이밍 -( -'사업자 등록 타이밍 - 너무 빨라도, 늦어도 손해입니다', -$$# 사업자 등록 타이밍 - -"언제 사업자등록을 해야 세금을 절약할 수 있나요?" - -소득세법 제2조에 따르면 사업소득은 사업을 개시한 시점부터 인정됩니다. - -1️⃣ 이 정도는 누구나 배울 수 있어요 - -사업자등록 기한: -- 사업 개시 후 20일 이내 신청 -- 늦으면 가산세 발생 (10%) - -사업소득 인정 시점: -- 등록일이 아니라 사업 개시일부터 -- 실제 소득이 발생한 날부터 신고 의무 - -예시: -- 1월 1일 사업 개시, 1월 20일 등록 = OK -- 1월 1일 사업 개시, 2월 15일 등록 = 가산세 + 세무조사 위험 - -2️⃣ 하지만 현실은 복잡해요 - -등록 안 하면? -- 최대 500만 원 과태료 -- 3년 이상 세무조사 대상 -- 신용평가에 악영향 - -너무 빨리 등록하면? -- 아직 소득 없는데 세금 신고? -- 순손실 상태 (해로울 수 있음) - -사업 형태에 따라? -- 직업: 등록 필수 (의료, 법률 등) -- 부업: 월 소득 100만 원 이상시 의무 -- 프리랜서: 소득 발생시 등록 권장 - -3️⃣ 그래서 세무사가 필요합니다 - -세무사는: -- 최적 등록 시점 판단 -- 사업소득 인정 범위 확인 -- 소급 적용 가능성 검토 -- 향후 세금 계획 수립 - -법적 근거: 소득세법 제2조 사업소득 정의 -$$, -'business-registration-timing', -1, -true, -'Business Registration Timing Guide', -'When to register business for tax optimization (소득세법 제2조)', -'사업자등록,사업소득,세무,등록시기', -NOW(), -NOW() -), - --- 5. 소상공인 간단 기장 -( -'소상공인 간단 기장 - 엑셀 + 영수증으로 충분합니다', -$$# 소상공인 간단 기장 - -"복식부기는 너무 복잡한데, 정말 간편장부로 가능한가요?" - -소득세법 제29조에 따르면 소상공인은 간편장부 기장이 가능합니다. - -1️⃣ 이 정도는 누구나 배울 수 있어요 - -간편장부 대상: -- 직전연도 수입금액 8,000만 원 이하 -- 소매업, 음식점 등 소규모 사업 - -기입 항목: -- 날짜 -- 매출액 또는 경비 -- 적요 (간단한 설명) -- 남은돈 - -엑셀로 충분: -- 따로 회계프로그램 불필요 -- 월별 요약만 정리 -- 영수증 첨부 - -2️⃣ 하지만 현실은 복잡해요 - -영수증 관리가 정말 쉬운가? -- 일년에 365일 거래 -- 하나 빠지면 계산 달라짐 -- 현금 거래 증빙 어려움 - -경비와 개인비 구분? -- 같은 카테고리도 경비 여부 판단 필요 -- 예: 차량 휘발유 (업무 % 계산) -- 음식비 (회의비 vs 개인식사) - -세무청 조회가 오면? -- 간편장부도 적격 요구 -- 영수증 없으면 인정 안 됨 -- 수정신고 필요할 수 있음 - -3️⃣ 그래서 세무사가 필요합니다 - -세무사는: -- 간편장부 양식 제공 -- 월별 정리 및 검수 -- 경비/개인비 경계 판단 -- 세무청 조사 대비 - -법적 근거: 소득세법 제29조 기장의무 -$$, -'small-business-bookkeeping', -1, -true, -'Simple Bookkeeping for Small Business', -'Easy accounting for small business owners under Income Tax Act Article 29', -'소상공인,간편장부,기장,세무', -NOW(), -NOW() -), - --- 6. 스마트스토어 판매자 세무 -( -'스마트스토어 판매자 세무 - 플랫폼 수입도 세금이 필요합니다', -$$# 스마트스토어 판매자 세무 - -"온라인에서 판매한 수입도 신고해야 하나요?" - -소득세법 제20조에 따르면 스마트스토어 판매 수입은 사업소득입니다. - -1️⃣ 이 정도는 누구나 배울 수 있어요 - -사업소득 인정: -- 월 매출 100만 원 이상: 의무 신고 -- 월 매출 100만 원 미만: 신고 권장 - -필요 서류: -- 판매 내역 (스마트스토어 다운로드) -- 결제 기록 (계좌입금 내역) -- 상품 원가 증빙 (영수증) - -경비 인정: -- 배송비 -- 광고료 -- 포장재 -- 통신비 일부 - -2️⃣ 하지만 현실은 복잡해요 - -플랫폼이 정보를 제공하나? -- 스마트스토어: 매달 판매 요약 제공 -- 그 외 플랫폼: 정보 부족 -- 수작업 정리 필요 - -수수료는? -- 스마트스토어 수수료: 경비 인정 -- 결제 수수료: 경비 인정? (판매사 역할에 따라) -- 세무청 판단이 엄격함 - -개인통장 vs 사업통장? -- 개인통장 사용시 증빙 어려움 -- 세무조사시 혼동 가능성 -- 세금 계산도 복잡 - -3️⃣ 그래서 세무사가 필요합니다 - -세무사는: -- 플랫폼별 소득 정리 -- 수수료/배송비 경비 처리 -- 월별 정산 금액 확인 -- 세무청 질의 대비 - -법적 근거: 소득세법 제20조 기타소득/사업소득 -$$, -'smartstore-seller-tax', -1, -true, -'Online Seller Tax Guide', -'Tax reporting for online marketplace sellers (소득세법 제20조)', -'스마트스토어,온라인판매,사업소득,세무', -NOW(), -NOW() -), - --- 7. 부가가치세 신고 기한 -( -'부가가치세 신고 기한 - 2일만 늦어도 가산세입니다', -$$# 부가가치세 신고 기한 - -"부가가치세는 언제까지 신고해야 하나요?" - -부가가치세법 제25조에 따르면 신고 기한이 정해져 있습니다. - -1️⃣ 이 정도는 누구나 배울 수 있어요 - -부가가치세 신고 기한 (2025년): -- 1기 (1월~4월): 5월 25일까지 -- 2기 (5월~8월): 9월 25일까지 - -납부 기한: -- 신고와 동시 납부 (가산세 피하려면) - -신고 대상: -- 매출 8,000만 원 이상: 일반과세 -- 매출 8,000만 원 이하: 간이과세 - -2️⃣ 하지만 현실은 복잡해요 - -과세/면세 구분? -- 같은 매출도 과세/면세 섞여 있음 -- 복합사업: 업태별로 다름 -- 세무청 판단 필요 - -환급 세금을 놓치면? -- 신고 기한 경과후 신청 불가 -- 수정신고로 환급신청 가능 -- 법정기한까지만 가능 - -재계산이 필요한가? -- 선급금, 환불 등으로 변동 -- 세금 계산 다시 필요 -- 기한 내 수정신고 가능 - -3️⃣ 그래서 세무사가 필요합니다 - -세무사는: -- 신고 기한 관리 -- 과세/면세 구분 정확성 -- 환금 세금 최대화 -- 기한 내 수정신고 - -법적 근거: 부가가치세법 제25조 신고기한 -$$, -'vat-reporting-deadline', -1, -true, -'Value Added Tax Reporting Deadline', -'VAT filing deadline and calculation (부가가치세법 제25조)', -'부가가치세,신고기한,세무', -NOW(), -NOW() -), - --- 8. 종합소득세 신고 완벽 가이드 -( -'종합소득세 신고 완벽 가이드 - 5월 신고로 연간 세금 결정됩니다', -$$# 종합소득세 신고 완벽 가이드 - -"종합소득세는 무엇이고, 정말 모두 신고해야 하나요?" - -소득세법 제19조에 따르면 종합소득세는 모든 사업소득을 합산하여 신고합니다. - -1️⃣ 이 정도는 누구나 배울 수 있어요 - -종합소득세 구성: -- 사업소득 (자영업, 프리랜서) -- 근로소득 (급여) -- 이자소득 (저축 이자) -- 배당소득 (주식 배당) -- 기타소득 (강의료 등) - -신고 대상: -- 종합소득 4,000만 원 초과 - -신고 기한: -- 매년 5월 31일까지 - -필요 서류: -- 사업소득 기장내역 -- 경비 영수증 -- 기부금 증명서 - -2️⃣ 하지만 현실은 복잡해요 - -모든 소득을 포함해야 하나? -- 사업소득, 근로소득, 기타소득 모두 -- 하나라도 누락되면 탈세 -- 세무청이 자동 적발 - -공제 항목을 놓치면? -- 교육비, 의료비, 기부금 공제 가능 -- 공제 순서가 있음 (환급 과감면제 등) -- 증빙서류 필수 - -세율은 얼마인가? -- 소득에 따라 6~45% (누진세) -- 계산이 복잡함 -- 오류가능성 높음 - -3️⃣ 그래서 세무사가 필요합니다 - -세무사는: -- 전체 소득 정확한 파악 -- 공제 항목 최대화 -- 세율 계산 정확성 -- 환급금 극대화 - -법적 근거: 소득세법 제19조 종합소득 -$$, -'comprehensive-income-tax-guide', -1, -true, -'Comprehensive Income Tax Filing Guide', -'Complete guide to filing comprehensive income tax (종합소득세) (소득세법 제19조)', -'종합소득세,신고,공제,소득세', -NOW(), -NOW() -), - --- 9. 연말정산 환급 최대화 -( -'연말정산 환급 최대화 - 놓친 공제 하나가 수십만 원입니다', -$$# 연말정산 환급 최대화 - -"연말정산으로 환금을 받으려면 뭘 꼭 챙겨야 하나요?" - -소득세법 제163조에 따르면 특정 지출에 대해 세액공제가 있습니다. - -1️⃣ 이 정도는 누구나 배울 수 있어요 - -주요 공제 (2025년): -- 기본공제: 1명당 150만 원 -- 교육비: 학생본인+자녀 연 900만 원 -- 의료비: 연 750만 원 초과분 -- 기부금: 전액 공제 -- 신용카드: 연 25만 원 초과분 15% 공제 - -계산 예시: -- 신용카드 사용 200만 원 → (200만-250만) × 15% = 환금 0원 -- 신용카드 사용 300만 원 → (300만-250만) × 15% = 7.5만 원 환금 - -2️⃣ 하지만 현실은 복잡해요 - -공제 순서가 있나? -- 기본공제 먼저 -- 그 다음 특별공제 (교육비, 의료비) -- 마지막 세액공제 -- 순서 틀리면 환금 안 됨 - -중복 공제는? -- 같은 지출 두 번 공제 불가 -- 배우자가 신청했으면? -- 부모가 신청했으면? (중복 불가) - -증빙서류를 잃어버렸다면? -- 신용카드: 증빙 필수 (발급 신청) -- 현금영수증: 미리 등록해야 공제 -- 의료비: 병원 영수증 필수 - -3️⃣ 그래서 세무사가 필요합니다 - -세무사는: -- 공제 항목 빠짐 없이 확인 -- 중복 공제 방지 -- 최적 가족 배분 (부부, 부모) -- 환급금 극대화 - -법적 근거: 소득세법 제163조 연말정산 -$$, -'year-end-tax-settlement', -1, -true, -'Year-End Tax Settlement Refund Maximization', -'How to maximize tax refund in year-end adjustment (연말정산) (소득세법 제163조)', -'연말정산,환금,공제,세액공제', -NOW(), -NOW() -); - diff --git a/scripts/validate_migrations.sh b/scripts/validate_migrations.sh new file mode 100644 index 0000000..775bd0a --- /dev/null +++ b/scripts/validate_migrations.sh @@ -0,0 +1,62 @@ +#!/usr/bin/env bash +set -euo pipefail + +MIGRATION_DIR="${1:-db/migrations}" +DB_CONNECTION_STRING="${2:-}" + +if [ ! -d "$MIGRATION_DIR" ]; then + echo "Migration directory not found: $MIGRATION_DIR" >&2 + exit 1 +fi + +mapfile -t files < <(find "$MIGRATION_DIR" -maxdepth 1 -type f -name 'V*.sql' | sort) + +if [ "${#files[@]}" -eq 0 ]; then + echo "No migration files found in $MIGRATION_DIR" >&2 + exit 1 +fi + +declare -A seen_versions=() +duplicate_found=0 + +for file in "${files[@]}"; do + name="$(basename "$file")" + version="${name#V}" + version="${version%%__*}" + if [ -n "${seen_versions[$version]:-}" ]; then + echo "Duplicate migration version detected: V$version" >&2 + echo " - ${seen_versions[$version]}" >&2 + echo " - $file" >&2 + duplicate_found=1 + else + seen_versions["$version"]="$file" + fi +done + +if [ "$duplicate_found" -ne 0 ]; then + exit 1 +fi + +if [ -z "$DB_CONNECTION_STRING" ]; then + echo "Duplicate version check passed." + exit 0 +fi + +if ! command -v psql >/dev/null 2>&1; then + echo "psql is required for database dry-run validation." >&2 + exit 1 +fi + +for file in "${files[@]}"; do + name="$(basename "$file")" + version="${name#V}" + version="${version%%__*}" + echo "Dry-run migration V$version: $name" + psql "$DB_CONNECTION_STRING" -v ON_ERROR_STOP=1 <