Compare commits
483 Commits
9c96f15f86
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
| b7cb442937 | |||
| 6a44272f25 | |||
| 8799fe250b | |||
| 41b723e908 | |||
| 8b1127b270 | |||
| 0179c1d640 | |||
| 9e08c6e12c | |||
| 13b35c5a2d | |||
| 200a1213a2 | |||
| f820b33fc5 | |||
| 7002d50a4e | |||
| fd5178b118 | |||
| c26319b6d7 | |||
| 333089a6ea | |||
| c00d237a4d | |||
| dc86ccfe35 | |||
| 7cfae3a38c | |||
| 2871df54c4 | |||
| 1330ef7c1d | |||
| f283a78845 | |||
| 9abdd06662 | |||
| dcd4c283b8 | |||
| af238543c5 | |||
| eb24f22477 | |||
| 8bc8cea5ef | |||
| 6b0bf5b78e | |||
| d2b2c7522a | |||
| b578a9ba57 | |||
| 673c78be17 | |||
| c95e92529d | |||
| 41791cfcd1 | |||
| 8091bb8902 | |||
| e1f3fc5270 | |||
| 4e674b2bc9 | |||
| 21a2225df7 | |||
| 08b002de27 | |||
| bdc6b0c80e | |||
| 3063bc0ed5 | |||
| 573abe858b | |||
| d63d20058c | |||
| f5478dd388 | |||
| 833b85ef0d | |||
| 901d75972a | |||
| f32bb47be2 | |||
| df0fb16cbd | |||
| 041e22b0d8 | |||
| f8ef5cd88a | |||
| 99a62904e8 | |||
| 1534e7dd5b | |||
| 0005b7f583 | |||
| d213290ed6 | |||
| 878ffdd3bb | |||
| d08de4fa10 | |||
| ef3f8ffaf4 | |||
| d26436b8a3 | |||
| 6b81c4a00e | |||
| 0980e2c267 | |||
| b819e5c8ea | |||
| ddea10f310 | |||
| a9454e2e31 | |||
| 33aa2adaea | |||
| 1cc2f8f5d2 | |||
| ead92badc1 | |||
| 7546c36528 | |||
| 089baa72cb | |||
| 20647f6ecc | |||
| a44a5bbb83 | |||
| c960860b3a | |||
| 65027c7862 | |||
| ff3fa7d22a | |||
| 93cc4b0c45 | |||
| aff388df2d | |||
| 58bec88d7d | |||
| d59440efbc | |||
| 3bfb1bab7e | |||
| 231f7676d3 | |||
| 5431299498 | |||
| 3827e374ca | |||
| 47bb3a38e6 | |||
| ffffa2869f | |||
| 2c62ce8a6e | |||
| 0f40eba363 | |||
| 8c7df7a813 | |||
| 9ee812f563 | |||
| a554e1795a | |||
| 8591d93b88 | |||
| af8b21fdb8 | |||
| bc3bde75af | |||
| 98ebc89505 | |||
| 98bc15b1d6 | |||
| 9797b86e16 | |||
| 7f1fdb4c57 | |||
| 54367696dc | |||
| 64e462e57e | |||
| 4b68fb20b9 | |||
| 35ab77fd38 | |||
| a5359869a0 | |||
| ef484c41a4 | |||
| dd660ef4b3 | |||
| f0269826fe | |||
| b7baff18dc | |||
| 48e2dfaf38 | |||
| a7f9b94499 | |||
| 66d6ae88f1 | |||
| caf7e5cf9f | |||
| 11019c7e0b | |||
| 7659dfd5e0 | |||
| a9827315e3 | |||
| e7436bb0e7 | |||
| 837ae530c7 | |||
| 9ae701ff93 | |||
| aaa867ce02 | |||
| 72e47d2661 | |||
| e2587bad40 | |||
| c71d858cd2 | |||
| d93e3a3aeb | |||
| 5abf086652 | |||
| 714c137740 | |||
| a6068e184b | |||
| 69ec7913d0 | |||
| 063ec189ce | |||
| 2bbe2ef47f | |||
| c5a0a54ee9 | |||
| c8f69bbd92 | |||
| d31e18e88b | |||
| 6f125e485b | |||
| 6fdf233976 | |||
| 76334bedd2 | |||
| a97f31f89c | |||
| 052fa1e9d7 | |||
| b89f9161d2 | |||
| 474a7cc72f | |||
| c92118ab32 | |||
| 675ef64975 | |||
| 055bc48d1d | |||
| 5faa1fb116 | |||
| a0918f03f0 | |||
| 21a654bd04 | |||
| f4cb922aa0 | |||
| 6990dbc6c2 | |||
| a905b31100 | |||
| 8277f60d84 | |||
| 260c410c5b | |||
| f08efb1a6d | |||
| 76872dfb72 | |||
| 40617d16e6 | |||
| dd2aa5e94a | |||
| 2762f74d1e | |||
| 300971bc3c | |||
| 2797473c56 | |||
| 69eeaca937 | |||
| ad6a65324a | |||
| 47dc8c6c57 | |||
| 840528698c | |||
| b6e0add2ac | |||
| 48c1b69af9 | |||
| e24d683d52 | |||
| 6fb17df2c2 | |||
| 015ace6671 | |||
| d3b9a6047c | |||
| da6058fb61 | |||
| 40cffb3beb | |||
| 041d3cae96 | |||
| 29a633e5fc | |||
| dda600d4e1 | |||
| 32029bff92 | |||
| 3d0cf1132c | |||
| 7ff8689a72 | |||
| b2dd217017 | |||
| e044acea17 | |||
| 29910d4d1b | |||
| e9a6ca9797 | |||
| 8095251eba | |||
| 6508282732 | |||
| ea447495d3 | |||
| c00d002972 | |||
| 83c1254a3e | |||
| e5981769b9 | |||
| d015bb6c92 | |||
| f29910030e | |||
| 8db3c1d220 | |||
| 328cfc0772 | |||
| 9b7e6eda4c | |||
| 059109b064 | |||
| 58ab7f44fa | |||
| c54b01bdc8 | |||
| 5d1eeb8485 | |||
| 04a5e15435 | |||
| 5ca1fe8620 | |||
| 56a7d0475b | |||
| 07e6a2a4ef | |||
| 9d99ab9f33 | |||
| 4b7bdbaffb | |||
| 8f41148756 | |||
| 41e130d26a | |||
| e202faa431 | |||
| f519df3e37 | |||
| 9c5a091e5a | |||
| 54a57b2306 | |||
| cc1fff44c0 | |||
| f8d81d8af0 | |||
| 484ece7a92 | |||
| 8202c3278b | |||
| 76446ee0f0 | |||
| 84f2839d9b | |||
| 24e94436e2 | |||
| d246071835 | |||
| ba981e7332 | |||
| f0b77b0e3f | |||
| 527a8821d8 | |||
| 3821914cf5 | |||
| ece69d576a | |||
| d45dbbc06d | |||
| e65612def8 | |||
| bb11a1bb87 | |||
| ae9380ddb3 | |||
| d8c52583ba | |||
| 585f426f0b | |||
| c8cf654131 | |||
| ebdcb4fd22 | |||
| 0ffb149296 | |||
| 870b51ece4 | |||
| b1ac7129d9 | |||
| 500d163ebc | |||
| d780fecf8c | |||
| b1601b0305 | |||
| e6253fdc83 | |||
| c885c6b234 | |||
| 96c7ab5e54 | |||
| 3f486d9fe9 | |||
| f68c968aed | |||
| 984da933ca | |||
| 3dd1cbb6ce | |||
| a3d294b6ff | |||
| e2d3eb9195 | |||
| 77aaed814c | |||
| d7ca51b741 | |||
| bc210969e2 | |||
| 6642f3d6f1 | |||
| 67f2f4b5d6 | |||
| faf4273e6d | |||
| 15c261a49d | |||
| b06c0f99fb | |||
| ad55bd1884 | |||
| e0b8d4e370 | |||
| e65f01b196 | |||
| 124b3b4dfc | |||
| 3785bc7a70 | |||
| bd44ec7c5f | |||
| cb47349a25 | |||
| b3cab87539 | |||
| 1fc3b6c0a4 | |||
| da9f49c973 | |||
| 1839c2c3d1 | |||
| df4c555dd1 | |||
| e1348226c6 | |||
| 97e7cfb867 | |||
| 11772d1f46 | |||
| 84e0577e89 | |||
| 31cc5603c9 | |||
| 0d36d27631 | |||
| 60c31d7ccb | |||
| 42a0d2ae3b | |||
| e599ef9ad8 | |||
| 223d916012 | |||
| f1cc0ca35c | |||
| e1325a1688 | |||
| 29b25cb1b4 | |||
| 8d72d2a0c2 | |||
| 1cdb172b07 | |||
| 864497e56f | |||
| 19c9b9b17a | |||
| 988b166118 | |||
| 78d3990484 | |||
| b3c4ee430d | |||
| 7b27f748de | |||
| abad1630b6 | |||
| 6ffff70ece | |||
| ed8ac34542 | |||
| 6b14ce929e | |||
| e830c08263 | |||
| a1065e8233 | |||
| 7cdb0bf8e9 | |||
| 8bea85df96 | |||
| 127490906b | |||
| ada05e254d | |||
| 7602f5be59 | |||
| 777cdcd918 | |||
| 0f6ba33af3 | |||
| 6d263c20bf | |||
| c9bf4f4f6f | |||
| b12d2ae0c6 | |||
| f9cbafdb3d | |||
| 64de7d2304 | |||
| 1f628b49a8 | |||
| a4a2499c7d | |||
| 6b11b64135 | |||
| a60451b95f | |||
| 2a046d0393 | |||
| 62ce89359a | |||
| 32c5a3d042 | |||
| 68291867f9 | |||
| d24f3f58db | |||
| 71cd2c1129 | |||
| 24ecf89028 | |||
| ff6651c4f2 | |||
| f892b85b7e | |||
| 62a7b2f2ef | |||
| 184ff2259b | |||
| 163812e964 | |||
| ba158f9824 | |||
| b2477d977b | |||
| 80c97fba96 | |||
| 1fb3a3c329 | |||
| abd7bbf016 | |||
| c765db37b3 | |||
| 967a784d6e | |||
| 03809bbf26 | |||
| c626c164f8 | |||
| 15f5dcf4ea | |||
| a84f842490 | |||
| 8999e51d4e | |||
| f98405b791 | |||
| ee964457d9 | |||
| 54c179b1eb | |||
| 488b8d11b7 | |||
| 65c5f19a2f | |||
| eaacbc8d7f | |||
| ac8a70a2ca | |||
| 203e674c3f | |||
| 0c014d0bdf | |||
| 904c0972ca | |||
| 7e75aeeec7 | |||
| b13eed7b7e | |||
| 4647b049b8 | |||
| 1a5ebb45bc | |||
| f197663101 | |||
| 70b57f1d4c | |||
| 428eeb6fd8 | |||
| dd68a237a1 | |||
| ef9fd523c6 | |||
| f2ab78dea2 | |||
| 1e0c0b7e1c | |||
| 1b173376ee | |||
| 1a7bc9e209 | |||
| 3be379431f | |||
| 682e2db3a3 | |||
| d9766cb5ef | |||
| 6bcb9effa8 | |||
| 186c6ef7a4 | |||
| c2e8e08f09 | |||
| 3f7cd7cd84 | |||
| 4b352df408 | |||
| a4b1234900 | |||
| a3c81c4f70 | |||
| 6e8b4e76ac | |||
| 5807e1b35e | |||
| 3e1097f585 | |||
| 917600a793 | |||
| 0d3615b44d | |||
| fc339ca9e7 | |||
| da1226994f | |||
| 6bc03ce3d9 | |||
| ecfbfc7cac | |||
| 46cb508bdf | |||
| ecabe8d9cc | |||
| 55c65810c1 | |||
| 7054d397e4 | |||
| 11fb596fc2 | |||
| a04592499c | |||
| ea9478f2f1 | |||
| f569211967 | |||
| c8306e2ac7 | |||
| bad2f47ffe | |||
| 943fe9c819 | |||
| 7b819f4ab0 | |||
| 6a5740ec68 | |||
| 3c8f30af6d | |||
| 7e3b4e2229 | |||
| 67bd5dc666 | |||
| 84161ee2d9 | |||
| 5aec36b155 | |||
| 3ab8971025 | |||
| db30e71e0a | |||
| e4c2758dea | |||
| 75661aa0ef | |||
| 3303ba2e96 | |||
| 43c2ff6ad9 | |||
| a7bb8d7149 | |||
| 791ce6d526 | |||
| 61083a5bb1 | |||
| 66fb86d23c | |||
| 16f7c6097c | |||
| 7232635ed0 | |||
| b42b98d560 | |||
| f216660afa | |||
| b31b43e30e | |||
| 86bd9ef8ff | |||
| 2fd9984a45 | |||
| 91330ec94c | |||
| 08102c8684 | |||
| e2472b7ea1 | |||
| 033883aac5 | |||
| d2cfcd90f0 | |||
| 42e73fa694 | |||
| f8f8f869fc | |||
| db7f903054 | |||
| 0d7a081f5a | |||
| 0bd36ae26f | |||
| 447a62c0fb | |||
| a16438dcc6 | |||
| ebd12b78a0 | |||
| 4b62d35266 | |||
| c38b97377a | |||
| 59f1509368 | |||
| c2955ad02f | |||
| ea40e5c002 | |||
| 7dd51a1169 | |||
| c65742a0c7 | |||
| 52f1790acb | |||
| cd3bc8357c | |||
| 53beb8a6e4 | |||
| d3b4d59f3c | |||
| 691e4406f3 | |||
| db2af15a07 | |||
| 2bde490e9e | |||
| e797da6140 | |||
| 0265d7ec8c | |||
| 09420dca0e | |||
| e3a0ea03f0 | |||
| ba2cb85fd2 | |||
| 74ee47a269 | |||
| 2af7050800 | |||
| fb9c77943f | |||
| 27f57ff925 | |||
| 79d99cfd7a | |||
| 1a761e8e15 | |||
| c01933e295 | |||
| 73da1859fe | |||
| 68588a8491 | |||
| 0b6a64fbad | |||
| 96df0dd9b1 | |||
| 351c7ac82c | |||
| ad48befb9a | |||
| 804725a785 | |||
| 41c8106a10 | |||
| 472431d45a | |||
| 33ea84fb2b | |||
| 73a564c307 | |||
| 223f365dfd | |||
| 61931ab8eb | |||
| 71d5d2cc1f | |||
| db81f94051 | |||
| 700cdaed4f | |||
| 65241c453c | |||
| b3baef012d | |||
| 0d07b2d26a | |||
| 65c2dce8fe | |||
| 4d94b9b4ff | |||
| 4358b189c8 | |||
| 80a16d8b20 | |||
| fbdbbc7a1f | |||
| 160afb7c7e | |||
| 8149680487 | |||
| 08e9e07458 | |||
| 58edbd9c8f | |||
| 0334a5f607 | |||
| 40c3877fb0 | |||
| 5053245575 | |||
| 126643665a | |||
| d09726c46a | |||
| 114ab22197 | |||
| 640ea96ae7 | |||
| ae7ca7e382 | |||
| 541b04cf3d | |||
| 821b73fe01 | |||
| fb04f73f46 | |||
| 58ec984f41 | |||
| 8760a0a931 | |||
| 1c831b1b30 | |||
| 41f569362d | |||
| 22070c1619 | |||
| 79492184d0 |
@@ -3,3 +3,10 @@ ASPNETCORE_URLS=http://0.0.0.0:5001
|
||||
ConnectionStrings__Default=Host=localhost;Database=taxbaikdb;Username=taxbaik;Password=change-me
|
||||
Jwt__SecretKey=dev-secret-key-change-in-production-min-32-chars!
|
||||
Admin__PasswordResetToken=change-this-reset-token
|
||||
Authentication__Google__ClientId=
|
||||
Authentication__Google__ClientSecret=
|
||||
Authentication__Naver__ClientId=
|
||||
Authentication__Naver__ClientSecret=
|
||||
Authentication__Kakao__ClientId=
|
||||
Authentication__Kakao__ClientSecret=
|
||||
# CI deploy trigger requires a real push on master.
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
name: TaxBaik Browser E2E
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
workflow_run:
|
||||
workflows: ["TaxBaik CI/CD"]
|
||||
types:
|
||||
- completed
|
||||
|
||||
jobs:
|
||||
browser-e2e:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event.workflow_run.conclusion == 'success'
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
@@ -37,28 +39,74 @@ jobs:
|
||||
- name: Wait for deployment
|
||||
env:
|
||||
DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
|
||||
EXPECTED_VERSION: ${{ github.event.workflow_run.head_sha }}
|
||||
run: |
|
||||
set -e
|
||||
EXPECTED_VERSION="$(git rev-parse --short HEAD)"
|
||||
for i in $(seq 1 60); do
|
||||
VERSION_BODY="$(curl -fsS "http://${DEPLOY_HOST}/taxbaik/version.txt" || true)"
|
||||
BLOG_STATUS="$(curl -s -o /dev/null -w '%{http_code}' "http://${DEPLOY_HOST}/taxbaik/blog/accountant-mistakes-5" || true)"
|
||||
if echo "$VERSION_BODY" | grep -q "Version: ${EXPECTED_VERSION}" && [ "$BLOG_STATUS" = "200" ]; then
|
||||
echo "Deployment is ready for ${EXPECTED_VERSION}"
|
||||
# Extract short commit hash (first 7 characters)
|
||||
SHORT_VERSION=$(echo "$EXPECTED_VERSION" | cut -c1-7)
|
||||
echo "Expected short version: $SHORT_VERSION"
|
||||
for i in $(seq 1 20); do
|
||||
# Suppress stderr and allow failures to handle transition/down periods cleanly
|
||||
VERSION_BODY="$(curl -fsS "https://www.taxbaik.com/taxbaik/version.json" 2>/dev/null || true)"
|
||||
BLOG_STATUS="$(curl -s -o /dev/null -w '%{http_code}' "https://www.taxbaik.com/taxbaik/blog/accountant-mistakes-5" || true)"
|
||||
LOGIN_STATUS="$(curl -s -o /dev/null -w '%{http_code}' "https://www.taxbaik.com/taxbaik/admin/login" || true)"
|
||||
if echo "$VERSION_BODY" | grep -q "\"version\": \"${SHORT_VERSION}\"" && [ "$BLOG_STATUS" = "200" ] && [ "$LOGIN_STATUS" = "200" ]; then
|
||||
echo "✓ Deployment ready for ${SHORT_VERSION} (attempt $i/20)"
|
||||
ROOT_URL="https://www.taxbaik.com/" \
|
||||
ADMIN_URL="https://www.taxbaik.com/taxbaik/admin/login" \
|
||||
PUBLIC_MARKER="백원숙 세무회계" \
|
||||
PUBLIC_FORBIDDEN="관리자" \
|
||||
ADMIN_MARKER="관리자 로그인" \
|
||||
MAX_RETRIES=1 \
|
||||
bash ./scripts/taxbaik-smoke.sh
|
||||
exit 0
|
||||
fi
|
||||
echo "Waiting for deployment ${EXPECTED_VERSION}; blog status=${BLOG_STATUS}; version=${VERSION_BODY}"
|
||||
sleep 10
|
||||
if [ $i -lt 20 ]; then
|
||||
echo " Attempt $i/20: waiting for deployment... (blog=${BLOG_STATUS:-?}, login=${LOGIN_STATUS:-?}, version=${VERSION_BODY:0:30}...)"
|
||||
sleep 3
|
||||
fi
|
||||
done
|
||||
echo "Deployment did not publish expected version ${EXPECTED_VERSION} in time" >&2
|
||||
echo "✗ TIMEOUT: Deployment failed to publish ${SHORT_VERSION} within 60 seconds" >&2
|
||||
exit 1
|
||||
|
||||
- name: Browser Smoke verification
|
||||
env:
|
||||
# Green-Blue 배포 지원: Nginx를 통해 active 포트로 라우팅
|
||||
E2E_BASE_URL: https://www.taxbaik.com/taxbaik
|
||||
# E2E 테스트는 test_admin 테스트 계정 사용 (실 admin 계정과 분리)
|
||||
E2E_ADMIN_USERNAME: test_admin
|
||||
E2E_ADMIN_PASSWORD: ${{ secrets.E2E_ADMIN_PASSWORD }}
|
||||
run: |
|
||||
echo "Running smoke checks on Desktop Chrome (production verification)"
|
||||
npm run test:e2e:public-smoke
|
||||
npm run test:e2e:admin-smoke
|
||||
|
||||
- name: Browser E2E verification
|
||||
env:
|
||||
E2E_BASE_URL: http://${{ secrets.DEPLOY_HOST }}/taxbaik
|
||||
E2E_ADMIN_USERNAME: admin
|
||||
E2E_ADMIN_PASSWORD: ${{ secrets.TAXBAIK_ADMIN_TEST_PASSWORD }}
|
||||
run: npm run test:e2e
|
||||
# Green-Blue 배포 지원: Nginx를 통해 active 포트로 라우팅
|
||||
E2E_BASE_URL: https://www.taxbaik.com/taxbaik
|
||||
# E2E 테스트는 test_admin 테스트 계정 사용 (실 admin 계정과 분리)
|
||||
E2E_ADMIN_USERNAME: test_admin
|
||||
E2E_ADMIN_PASSWORD: ${{ secrets.E2E_ADMIN_PASSWORD }}
|
||||
run: |
|
||||
echo "Running full E2E on Desktop Chrome (production verification)"
|
||||
npx playwright test --project="Desktop Chrome" --reporter=html --reporter=list
|
||||
|
||||
- name: API smoke verification
|
||||
env:
|
||||
E2E_ADMIN_USERNAME: test_admin
|
||||
E2E_ADMIN_PASSWORD: ${{ secrets.E2E_ADMIN_PASSWORD }}
|
||||
run: |
|
||||
set -e
|
||||
TOKEN="$(curl -s -X POST "https://www.taxbaik.com/taxbaik/api/auth/login" -H "Content-Type: application/json" -d "{\"username\":\"${E2E_ADMIN_USERNAME}\",\"password\":\"${E2E_ADMIN_PASSWORD}\"}" | python3 -c 'import sys, json; print(json.load(sys.stdin)["accessToken"])')"
|
||||
test -n "$TOKEN"
|
||||
curl -fsS -H "Authorization: Bearer $TOKEN" "https://www.taxbaik.com/taxbaik/api/blog/admin?page=1&pageSize=1" >/dev/null
|
||||
curl -fsS -H "Authorization: Bearer $TOKEN" "https://www.taxbaik.com/taxbaik/api/faq" >/dev/null
|
||||
curl -fsS -H "Authorization: Bearer $TOKEN" "https://www.taxbaik.com/taxbaik/api/announcement" >/dev/null
|
||||
curl -fsS -H "Authorization: Bearer $TOKEN" "https://www.taxbaik.com/taxbaik/api/inquiry?page=1&pageSize=1" >/dev/null
|
||||
curl -fsS "https://www.taxbaik.com/taxbaik/favicon.svg" >/dev/null
|
||||
curl -fsS "https://www.taxbaik.com/taxbaik/favicon.ico" >/dev/null
|
||||
curl -fsS "https://www.taxbaik.com/taxbaik/robots.txt" >/dev/null
|
||||
|
||||
- name: Browser E2E summary
|
||||
if: always()
|
||||
|
||||
+252
-25
@@ -1,6 +1,7 @@
|
||||
name: TaxBaik CI/CD
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
@@ -19,18 +20,67 @@ jobs:
|
||||
dotnet-version: '10.0'
|
||||
|
||||
- name: Restore dependencies
|
||||
run: dotnet restore TaxBaik.sln
|
||||
run: dotnet restore src/TaxBaik.sln
|
||||
|
||||
- name: Build solution
|
||||
run: |
|
||||
dotnet clean TaxBaik.sln -c Release
|
||||
dotnet build TaxBaik.sln -c Release --no-restore
|
||||
run: dotnet build src/TaxBaik.sln -c Release --no-restore -p:ContinuousIntegrationBuild=true
|
||||
|
||||
- name: Test solution
|
||||
run: dotnet test TaxBaik.sln -c Release --no-build
|
||||
run: dotnet test src/TaxBaik.sln -c Release --no-build
|
||||
|
||||
- name: Publish Web
|
||||
run: dotnet publish TaxBaik.Web/ -c Release -o ./publish --no-restore
|
||||
- name: Publish Web (auto-includes WASM from referenced TaxBaik.Web.Client)
|
||||
run: |
|
||||
set -e
|
||||
mkdir -p ./publish-logs
|
||||
web_log="./publish-logs/publish-web.log"
|
||||
start=$(date +%s)
|
||||
# Web.Client needs a Release static-web-assets manifest for Web publish.
|
||||
# Build it explicitly so publish can reuse the prepared outputs.
|
||||
dotnet build src/TaxBaik.Web.Client/TaxBaik.Web.Client.csproj -c Release --no-restore -p:ContinuousIntegrationBuild=true
|
||||
# Build the Web host in Release as well so publish has the same inputs
|
||||
# the server uses in production.
|
||||
dotnet build src/TaxBaik.Web/TaxBaik.Web.csproj -c Release --no-restore -p:ContinuousIntegrationBuild=true
|
||||
echo "--- Web.Client Release artifacts ---"
|
||||
ls -la src/TaxBaik.Web.Client/bin/Release/net10.0 || true
|
||||
ls -la src/TaxBaik.Web.Client/obj/Release/net10.0 || true
|
||||
if ! dotnet publish src/TaxBaik.Web/ \
|
||||
-c Release \
|
||||
-o ./publish \
|
||||
--no-restore \
|
||||
-p:SelfContained=false \
|
||||
-p:PublishReadyToRun=false \
|
||||
-p:PerformanceSummary=true \
|
||||
-clp:Summary \
|
||||
-bl:"./publish-logs/publish-web.binlog" >"$web_log" 2>&1; then
|
||||
echo "=== Publish Web failed; tailing log ==="
|
||||
tail -n 120 "$web_log" || true
|
||||
exit 1
|
||||
fi
|
||||
end=$(date +%s)
|
||||
echo "✓ Publish Web elapsed: $((end - start))s"
|
||||
ls -lh ./publish-logs/publish-web.binlog
|
||||
|
||||
- name: Publish Proxy
|
||||
run: |
|
||||
set -e
|
||||
mkdir -p ./publish-logs
|
||||
# Proxy is not part of the solution restore graph, so restore it once
|
||||
# here before publishing to avoid NETSDK1004 in CI.
|
||||
dotnet restore src/TaxBaik.Proxy/
|
||||
dotnet build src/TaxBaik.Proxy/TaxBaik.Proxy.csproj -c Release --no-restore
|
||||
start=$(date +%s)
|
||||
dotnet publish src/TaxBaik.Proxy/ \
|
||||
-c Release \
|
||||
-o ./publish/proxy \
|
||||
--no-restore \
|
||||
--no-build \
|
||||
-p:PublishReadyToRun=false \
|
||||
-p:PerformanceSummary=true \
|
||||
-clp:Summary \
|
||||
-bl:./publish-logs/publish-proxy.binlog
|
||||
end=$(date +%s)
|
||||
echo "✓ Publish Proxy elapsed: $((end - start))s"
|
||||
ls -lh ./publish-logs/publish-proxy.binlog
|
||||
|
||||
- name: Write production secrets
|
||||
run: |
|
||||
@@ -38,32 +88,54 @@ jobs:
|
||||
JWT_SECRET_KEY="${{ secrets.TAXBAIK_JWT_SECRET_KEY }}"
|
||||
TELEGRAM_BOT_TOKEN="${{ secrets.TAXBAIK_TELEGRAM_BOT_TOKEN }}"
|
||||
TELEGRAM_CHAT_ID="${{ secrets.TAXBAIK_TELEGRAM_CHAT_ID }}"
|
||||
TELEGRAM_INQUIRY_CHAT_ID="${{ secrets.TAXBAIK_TELEGRAM_INQUIRY_CHAT_ID }}"
|
||||
TELEGRAM_SYSTEM_CHAT_ID="${{ secrets.TAXBAIK_TELEGRAM_SYSTEM_CHAT_ID }}"
|
||||
[ -z "$JWT_SECRET_KEY" ] && { echo "Missing TAXBAIK_JWT_SECRET_KEY" >&2; exit 1; }
|
||||
[ -z "$TELEGRAM_BOT_TOKEN" ] && { echo "Missing TAXBAIK_TELEGRAM_BOT_TOKEN" >&2; exit 1; }
|
||||
[ -z "$TELEGRAM_CHAT_ID" ] && { echo "Missing TAXBAIK_TELEGRAM_CHAT_ID" >&2; exit 1; }
|
||||
[ -z "$TELEGRAM_INQUIRY_CHAT_ID" ] && TELEGRAM_INQUIRY_CHAT_ID="$TELEGRAM_CHAT_ID"
|
||||
[ -z "$TELEGRAM_SYSTEM_CHAT_ID" ] && TELEGRAM_SYSTEM_CHAT_ID="-5585148480"
|
||||
JWT_SECRET_KEY="$JWT_SECRET_KEY" \
|
||||
TELEGRAM_BOT_TOKEN="$TELEGRAM_BOT_TOKEN" \
|
||||
TELEGRAM_CHAT_ID="$TELEGRAM_CHAT_ID" \
|
||||
TELEGRAM_INQUIRY_CHAT_ID="$TELEGRAM_INQUIRY_CHAT_ID" \
|
||||
TELEGRAM_SYSTEM_CHAT_ID="$TELEGRAM_SYSTEM_CHAT_ID" \
|
||||
python3 -c '
|
||||
import json, os, pathlib
|
||||
pathlib.Path("./publish/appsettings.Production.json").write_text(
|
||||
json.dumps({
|
||||
"Jwt": {"SecretKey": os.environ["JWT_SECRET_KEY"]},
|
||||
"Telegram": {"BotToken": os.environ["TELEGRAM_BOT_TOKEN"], "ChatId": os.environ["TELEGRAM_CHAT_ID"]}
|
||||
"Telegram": {
|
||||
"BotToken": os.environ["TELEGRAM_BOT_TOKEN"],
|
||||
"ChatId": os.environ["TELEGRAM_CHAT_ID"],
|
||||
"InquiryChatId": os.environ["TELEGRAM_INQUIRY_CHAT_ID"],
|
||||
"SystemChatId": os.environ["TELEGRAM_SYSTEM_CHAT_ID"]
|
||||
}
|
||||
}, ensure_ascii=False, indent=2),
|
||||
encoding="utf-8"
|
||||
)'
|
||||
test -s ./publish/appsettings.Production.json || { echo "appsettings.Production.json is empty" >&2; exit 1; }
|
||||
|
||||
- name: Verify proxy artifact
|
||||
run: |
|
||||
test -s ./publish/proxy/TaxBaik.Proxy.dll || { echo "TaxBaik.Proxy.dll missing" >&2; exit 1; }
|
||||
test -s ./publish/proxy/TaxBaik.Proxy.runtimeconfig.json || { echo "TaxBaik.Proxy.runtimeconfig.json missing" >&2; exit 1; }
|
||||
|
||||
- name: Copy migrations
|
||||
run: cp -r db/migrations ./publish/migrations || true
|
||||
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: Validate KST timestamps
|
||||
run: bash scripts/validate_kst_timestamps.sh
|
||||
|
||||
- name: Generate build info
|
||||
run: |
|
||||
COMMIT_HASH=$(git rev-parse --short HEAD)
|
||||
BUILD_TIME=$(date -u +'%Y-%m-%d %H:%M:%S UTC')
|
||||
BUILD_TIME=$(TZ=Asia/Seoul date +'%Y-%m-%d %H:%M:%S KST')
|
||||
mkdir -p ./publish/wwwroot
|
||||
printf 'Version: %s\nBuilt: %s\n' "$COMMIT_HASH" "$BUILD_TIME" > ./publish/wwwroot/version.txt
|
||||
printf '{\n "version": "%s",\n "built": "%s"\n}\n' "$COMMIT_HASH" "$BUILD_TIME" > ./publish/wwwroot/version.json
|
||||
echo "✓ Build: $COMMIT_HASH @ $BUILD_TIME"
|
||||
|
||||
- name: Setup SSH
|
||||
@@ -88,16 +160,49 @@ 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)"
|
||||
|
||||
- name: Deploy & verify on server
|
||||
run: |
|
||||
set -e
|
||||
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
||||
export TAXBAIK_DEPLOY_FROM_CI=1
|
||||
TIMESTAMP=$(TZ=Asia/Seoul date +%Y%m%d_%H%M%S)
|
||||
COMMIT=$(git rev-parse --short HEAD)
|
||||
DEPLOY_HOST="${{ secrets.DEPLOY_HOST }}"
|
||||
DEPLOY_USER="${{ secrets.DEPLOY_USER }}"
|
||||
TELEGRAM_BOT_TOKEN="${{ secrets.TAXBAIK_TELEGRAM_BOT_TOKEN }}"
|
||||
TELEGRAM_SYSTEM_CHAT_ID="${{ secrets.TAXBAIK_TELEGRAM_SYSTEM_CHAT_ID }}"
|
||||
TELEGRAM_CHAT_ID="${TELEGRAM_SYSTEM_CHAT_ID:--5585148480}"
|
||||
|
||||
send_telegram() {
|
||||
local text="$1"
|
||||
if [ -z "$TELEGRAM_BOT_TOKEN" ]; then
|
||||
echo "Skipping Telegram notification: missing TAXBAIK_TELEGRAM_BOT_TOKEN" >&2
|
||||
return 0
|
||||
fi
|
||||
|
||||
curl -fsS -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \
|
||||
-d "chat_id=${TELEGRAM_CHAT_ID}" \
|
||||
--data-urlencode "text=${text}" \
|
||||
-d "parse_mode=HTML" >/dev/null || true
|
||||
}
|
||||
|
||||
notify_failure() {
|
||||
local exit_code=$?
|
||||
send_telegram "❌ <b>TaxBaik 배포 실패</b>
|
||||
|
||||
커밋: <code>${COMMIT}</code>
|
||||
시간: <code>${TIMESTAMP}</code>
|
||||
단계: CI/CD deploy"
|
||||
exit "$exit_code"
|
||||
}
|
||||
|
||||
trap notify_failure ERR
|
||||
|
||||
echo "=== Deploying TaxBaik $COMMIT ($TIMESTAMP) ==="
|
||||
|
||||
@@ -105,14 +210,15 @@ jobs:
|
||||
scp -i ~/.ssh/id_ed25519 -o StrictHostKeyChecking=yes \
|
||||
taxbaik_deploy.tgz "$DEPLOY_USER@$DEPLOY_HOST:/tmp/taxbaik_${TIMESTAMP}.tgz"
|
||||
|
||||
# 2. 서버에서 배포 + 헬스 체크 (SSH 1회 연결로 처리)
|
||||
# 2. 서버에서 배포 + 헬스 체크 (SSH 1회 연결로 처리, Green-Blue 지원)
|
||||
ssh -i ~/.ssh/id_ed25519 -o StrictHostKeyChecking=yes \
|
||||
-o ServerAliveInterval=10 \
|
||||
"$DEPLOY_USER@$DEPLOY_HOST" bash << REMOTE
|
||||
"$DEPLOY_USER@$DEPLOY_HOST" TAXBAIK_DEPLOY_FROM_CI=1 bash << REMOTE
|
||||
set -e
|
||||
DEPLOY_HOME="/home/kjh2064"
|
||||
DEPLOY_DIR="\$DEPLOY_HOME/deployments/taxbaik_${TIMESTAMP}"
|
||||
TIMESTAMP="${TIMESTAMP}"
|
||||
COMMIT="${COMMIT}"
|
||||
|
||||
echo "--- [1/5] 압축 해제 ---"
|
||||
mkdir -p "\$DEPLOY_DIR"
|
||||
@@ -122,18 +228,112 @@ jobs:
|
||||
echo "--- [2/5] 운영 설정 검증 ---"
|
||||
test -s "\$DEPLOY_DIR/appsettings.Production.json" \
|
||||
|| { echo "FATAL: appsettings.Production.json 없음" >&2; exit 1; }
|
||||
test -s "\$DEPLOY_DIR/proxy/TaxBaik.Proxy.dll" \
|
||||
|| { echo "FATAL: TaxBaik.Proxy.dll 없음" >&2; exit 1; }
|
||||
|
||||
echo "--- [3/5] 심볼릭 링크 전환 ---"
|
||||
ln -sfn "\$DEPLOY_DIR" "\$DEPLOY_HOME/taxbaik_active"
|
||||
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] 서비스 재시작 ---"
|
||||
sudo /usr/bin/systemctl restart taxbaik
|
||||
echo "--- [4/5] Green-Blue 배포 실행 ---"
|
||||
chmod +x "\$DEPLOY_DIR/deploy_gb.sh"
|
||||
"\$DEPLOY_DIR/deploy_gb.sh" "\$DEPLOY_DIR"
|
||||
|
||||
echo "--- [5/5] 헬스 체크 (최대 120초) ---"
|
||||
ATTEMPTS=40
|
||||
echo "--- [4.5/5] Nginx 설정 검증 ---"
|
||||
# 실제 로드되는 파일은 sites-enabled/의 심볼릭 링크 대상만이다.
|
||||
# sites-available/에 다른 파일(예: default)이 있어도 sites-enabled에
|
||||
# 링크되어 있지 않으면 nginx는 그 내용을 절대 읽지 않는다.
|
||||
NGINX_CONF=""
|
||||
for f in /etc/nginx/sites-enabled/*; do
|
||||
if [ -e "\$f" ] && grep -q "location /taxbaik" "\$f" 2>/dev/null; then
|
||||
NGINX_CONF=\$(readlink -f "\$f")
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -z "\$NGINX_CONF" ]; then
|
||||
echo "❌ FATAL: sites-enabled/ 안에서 'location /taxbaik'를 정의한 파일을 찾을 수 없음" >&2
|
||||
echo " sites-available/에 파일을 수정해도 sites-enabled에 심볼릭 링크되어 있지 않으면 반영되지 않는다." >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "실제 로드되는 설정 파일: \$NGINX_CONF"
|
||||
|
||||
# 불변식: '/'와 '/taxbaik' location 모두 반드시 127.0.0.1:5001 (TaxBaik.Proxy)을
|
||||
# 가리켜야 한다. 5003/5004를 직접 하드코딩하면 Green-Blue 포트 전환 시
|
||||
# 죽은 포트를 가리키게 되어 502/404가 발생한다 (실제 발생했던 장애).
|
||||
if grep -E "proxy_pass\s+http://127\.0\.0\.1:500[34]" "\$NGINX_CONF" > /dev/null 2>&1; then
|
||||
echo "❌ FATAL: \$NGINX_CONF 가 포트 5003/5004를 직접 참조함 (Green-Blue 전환 시 502 발생)" >&2
|
||||
echo " 수정: sudo sed -i 's|127.0.0.1:500[34]|127.0.0.1:5001|g' \$NGINX_CONF && sudo nginx -t && sudo systemctl reload nginx" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# proxy_pass에 URI(끝 슬래시)가 있으면 nginx가 요청 경로를 재작성하며,
|
||||
# location 접두사와 슬래시 개수가 안 맞으면 백엔드로 이중 슬래시(//)가
|
||||
# 전달되어 404가 발생한다 (실제 발생했던 장애). 접두사 location에서는
|
||||
# proxy_pass에 URI를 붙이지 않는다.
|
||||
if grep -E "location\s+/taxbaik\s*\{" -A 1 "\$NGINX_CONF" | grep -qE "proxy_pass\s+http://127\.0\.0\.1:5001/;"; then
|
||||
echo "❌ FATAL: location /taxbaik 의 proxy_pass 에 불필요한 trailing slash가 있음 (이중 슬래시로 인한 404 위험)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✓ Nginx 설정 검증 통과 (실제 로드 파일 확인 + 포트 5001 고정 + trailing slash 없음)"
|
||||
|
||||
echo "--- [5/5] 헬스 체크 (최대 60초) ---"
|
||||
ATTEMPTS=20
|
||||
for i in \$(seq 1 \$ATTEMPTS); do
|
||||
STATUS=\$(curl -sf -o /dev/null -w '%{http_code}' http://127.0.0.1:5001/taxbaik/ 2>/dev/null || echo "000")
|
||||
STATUS=\$(curl -sf -o /dev/null -w '%{http_code}' http://127.0.0.1:5001/taxbaik/healthz 2>/dev/null || echo "000")
|
||||
if [ "\$STATUS" = "200" ]; then
|
||||
echo "✓ [1/6] 헬스 체크 완료"
|
||||
|
||||
# 검증 1: 메인 페이지 로드. curl -L + -w 는 리다이렉트 체인의 상태코드를
|
||||
# 이어붙이므로, 첫 응답 코드만 받아 200/3xx를 허용한다.
|
||||
MAIN_STATUS=\$(curl -fsS -o /dev/null -w '%{http_code}' http://127.0.0.1:5001/taxbaik/ 2>/dev/null || echo "000")
|
||||
if ! printf '%s' "\$MAIN_STATUS" | grep -Eq '^(200|301|302|307|308)$'; then
|
||||
echo "❌ 메인 페이지 로드 실패 (상태: \$MAIN_STATUS)" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "✓ [2/6] 메인 페이지 로드 완료"
|
||||
|
||||
# 검증 2: CSS 파일 로드
|
||||
CSS_STATUS=\$(curl -sf -o /dev/null -w '%{http_code}' http://127.0.0.1:5001/taxbaik/css/admin.css 2>/dev/null || echo "000")
|
||||
if [ "\$CSS_STATUS" != "200" ]; then
|
||||
echo "❌ CSS 파일 로드 실패 (상태: \$CSS_STATUS)" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "✓ [3/6] CSS 파일 로드 완료"
|
||||
|
||||
# 검증 3: 버전 정보. 파일 존재만 보면 5001이 잘못된 구 프로세스를
|
||||
# 가리키는 장애를 놓치므로, HTTP 응답이 이번 커밋인지 확인한다.
|
||||
if [ ! -s "\$DEPLOY_DIR/wwwroot/version.json" ]; then
|
||||
echo "❌ version.json 누락" >&2
|
||||
exit 1
|
||||
fi
|
||||
VERSION_JSON=\$(curl -fsS http://127.0.0.1:5001/taxbaik/version.json 2>/dev/null || true)
|
||||
if ! printf '%s' "\$VERSION_JSON" | grep -q "\"version\": \"\$COMMIT\""; then
|
||||
echo "❌ 5001 프록시가 이번 배포 버전을 제공하지 않음" >&2
|
||||
echo " expected: \$COMMIT" >&2
|
||||
echo " actual: \$VERSION_JSON" >&2
|
||||
echo " 확인: 5001 포트가 TaxBaik.Proxy.dll인지, /home/kjh2064/taxbaik_port가 새 포트인지 점검" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "✓ [4/6] 버전 정보 확인 완료"
|
||||
|
||||
# 검증 4: 5001 프록시 확인
|
||||
if ! ss -tlnp | grep -q ':5001 '; then
|
||||
echo "❌ 5001 프록시가 실행 중이 아님" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "✓ [5/6] 5001 프록시 확인 완료"
|
||||
|
||||
# 검증 5: 관리자 로그인 페이지
|
||||
LOGIN_STATUS=\$(curl -fsSL -o /dev/null -w '%{http_code}' http://127.0.0.1:5001/taxbaik/admin/login 2>/dev/null || echo "000")
|
||||
if [ "\$LOGIN_STATUS" != "200" ]; then
|
||||
echo "❌ 관리자 로그인 페이지 로드 실패 (상태: \$LOGIN_STATUS)" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "✓ [6/6] 관리자 페이지 로드 완료"
|
||||
|
||||
echo "✓ 서비스 정상 (시도 \$i/\$ATTEMPTS)"
|
||||
# 구 배포 디렉토리 정리 (최근 5개 보존)
|
||||
ls -1dt \$DEPLOY_HOME/deployments/taxbaik_* 2>/dev/null \
|
||||
@@ -142,10 +342,19 @@ jobs:
|
||||
fi
|
||||
if [ "\$i" -eq "\$ATTEMPTS" ]; then
|
||||
echo "=== FATAL: 서비스가 \$ATTEMPTS회 시도 후에도 응답하지 않음 ===" >&2
|
||||
echo "--- systemd 상태 ---" >&2
|
||||
systemctl is-active taxbaik >&2 || true
|
||||
echo "--- 최근 로그 50줄 ---" >&2
|
||||
journalctl -u taxbaik --no-pager -n 50 >&2
|
||||
echo "--- 5001 listener ---" >&2
|
||||
ss -tlnp 2>/dev/null | grep ':5001 ' >&2 || true
|
||||
echo "--- active port file ---" >&2
|
||||
cat "\$DEPLOY_HOME/taxbaik_port" >&2 || true
|
||||
echo "--- 신규 앱 로그 ---" >&2
|
||||
ACTIVE_PORT=\$(cat "\$DEPLOY_HOME/taxbaik_port" 2>/dev/null | tr -d '[:space:]' || true)
|
||||
if [ -n "\$ACTIVE_PORT" ] && [ -s "\$DEPLOY_DIR/web_\${ACTIVE_PORT}.log" ]; then
|
||||
tail -n 80 "\$DEPLOY_DIR/web_\${ACTIVE_PORT}.log" >&2
|
||||
else
|
||||
ls -la "\$DEPLOY_DIR" >&2 || true
|
||||
fi
|
||||
echo "--- proxy 로그 ---" >&2
|
||||
tail -n 80 "\$DEPLOY_HOME/taxbaik_proxy.log" >&2 || true
|
||||
exit 1
|
||||
fi
|
||||
echo " 대기 중... (\$i/\$ATTEMPTS, HTTP \$STATUS)"
|
||||
@@ -154,3 +363,21 @@ jobs:
|
||||
REMOTE
|
||||
|
||||
echo "✓ 배포 완료: taxbaik_${TIMESTAMP} @ $DEPLOY_HOST"
|
||||
|
||||
echo "--- 실제 공개 도메인 종단 간 검증 (Nginx/Cloudflare 경유, 최대 3회 재시도) ---"
|
||||
ROOT_URL="https://www.taxbaik.com/" \
|
||||
ADMIN_URL="https://www.taxbaik.com/taxbaik/admin/login" \
|
||||
PUBLIC_MARKER="백원숙 세무회계" \
|
||||
PUBLIC_FORBIDDEN="관리자" \
|
||||
ADMIN_MARKER="관리자 로그인" \
|
||||
MAX_RETRIES=3 \
|
||||
RETRY_SLEEP_SECONDS=5 \
|
||||
bash ./scripts/taxbaik-smoke.sh
|
||||
echo "✓ 실제 공개 도메인 전체 정상"
|
||||
|
||||
send_telegram "✅ <b>TaxBaik 배포 완료</b>
|
||||
|
||||
커밋: <code>${COMMIT}</code>
|
||||
시간: <code>${TIMESTAMP}</code>
|
||||
대상: <code>${DEPLOY_HOST}</code>
|
||||
채널: <code>${TELEGRAM_CHAT_ID}</code>"
|
||||
|
||||
@@ -60,3 +60,6 @@ PublishProfiles/
|
||||
.env
|
||||
.env.local
|
||||
appsettings.Development.json
|
||||
|
||||
# Scratch / temporary work - never commit, see docs/ENGINEERING_HARNESS.md
|
||||
.scratch/
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:10.0-alpine
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY ./publish/ .
|
||||
|
||||
EXPOSE 5001
|
||||
|
||||
ENTRYPOINT ["dotnet", "TaxBaik.Web.dll"]
|
||||
@@ -1,9 +0,0 @@
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:10.0-alpine
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY ./publish/ .
|
||||
|
||||
EXPOSE 5001
|
||||
|
||||
ENTRYPOINT ["dotnet", "TaxBaik.Web.dll"]
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
**온라인 세무 상담 플랫폼** | 블로그 SEO 최적화 | 전국 고객 확보
|
||||
|
||||
CI deploy trigger verification note.
|
||||
|
||||
---
|
||||
|
||||
## 개요
|
||||
@@ -166,7 +168,22 @@ master 브랜치에 푸시하면 파이프라인이 다음 단계를 수행합
|
||||
- `TAXBAIK_ADMIN_TEST_PASSWORD`: 배포 검증용 관리자 비밀번호
|
||||
- `Admin__PasswordResetToken`: 관리자 비밀번호 재설정 API용 서버 비밀값
|
||||
|
||||
수동 배포는 비상 롤백 절차 외에는 사용하지 않습니다. 실패 시 [DEPLOYMENT_GUIDE.md](./DEPLOYMENT_GUIDE.md)의 CI 점검 절차를 따릅니다.
|
||||
배포는 Gitea Actions CI/CD로만 수행합니다. 수동 배포 경로는 CI 하네스로 차단되어 있으며, 실패 시 [DEPLOYMENT_GUIDE.md](./DEPLOYMENT_GUIDE.md)의 CI 점검 절차를 따릅니다.
|
||||
|
||||
## E2E / Smoke
|
||||
|
||||
공개/관리자 분리 검증은 아래 명령을 사용합니다.
|
||||
|
||||
로컬에서 실행할 때는 먼저 `npm run test:e2e`가 가리키는 대상 서버를 띄워둬야 합니다.
|
||||
|
||||
| 용도 | Bash | PowerShell |
|
||||
| --- | --- | --- |
|
||||
| Public smoke | `E2E_BASE_URL=https://www.taxbaik.com/taxbaik npm run test:e2e:public-smoke` | `$env:E2E_BASE_URL="https://www.taxbaik.com/taxbaik"; npm run test:e2e:public-smoke` |
|
||||
| Admin smoke | `E2E_BASE_URL=https://www.taxbaik.com/taxbaik npm run test:e2e:admin-smoke` | `$env:E2E_BASE_URL="https://www.taxbaik.com/taxbaik"; npm run test:e2e:admin-smoke` |
|
||||
|
||||
직접 smoke 스크립트가 필요하면 이 한 줄만 쓰면 됩니다.
|
||||
|
||||
`ROOT_URL="https://www.taxbaik.com/" ADMIN_URL="https://www.taxbaik.com/taxbaik/admin/login" bash ./scripts/taxbaik-smoke.sh`
|
||||
|
||||
---
|
||||
|
||||
@@ -268,7 +285,13 @@ echo $ConnectionStrings__Default
|
||||
|
||||
## 문서
|
||||
|
||||
- [CLAUDE.md](./CLAUDE.md) - LLM 개발 지침 (9개 섹션)
|
||||
- [docs/INDEX.md](./docs/INDEX.md) - 현재 개발 기준 인덱스
|
||||
- [docs/ENGINEERING_HARNESS.md](./docs/ENGINEERING_HARNESS.md) - 코드 품질, API-first, CI/CD 하네스
|
||||
- [docs/DOUZONE_UX_GUIDE.md](./docs/DOUZONE_UX_GUIDE.md) - 더존식 어드민 UX 원칙과 템플릿 기준
|
||||
- [docs/COMMON_CODE_POLICY.md](./docs/COMMON_CODE_POLICY.md) - 공통코드 저장값/컬럼 길이/하드코딩 금지 기준
|
||||
- [docs/COMBO_POLICY.md](./docs/COMBO_POLICY.md) - 콤보/검색/선택 입력 정책
|
||||
- [docs/ADMIN_PATTERN_CRITIQUE_WBS.md](./docs/ADMIN_PATTERN_CRITIQUE_WBS.md) - 어드민 패턴 비판 및 정량 WBS
|
||||
- [CLAUDE.md](./CLAUDE.md) - 보조 LLM 개발 지침
|
||||
- [DEPLOYMENT_GUIDE.md](./DEPLOYMENT_GUIDE.md) - 배포 완전 가이드
|
||||
- [SERVER_SETUP.sh](./SERVER_SETUP.sh) - 서버 자동 설치 스크립트
|
||||
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
#!/bin/bash
|
||||
# TaxBaik Server Setup Script
|
||||
# Run on Ubuntu 26.04 server as root or with sudo
|
||||
|
||||
set -e
|
||||
|
||||
echo "===== TaxBaik Server Setup ====="
|
||||
|
||||
# Colors for output
|
||||
GREEN='\033[0;32m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Configuration
|
||||
DEPLOY_USER="kjh2064"
|
||||
DB_NAME="taxbaikdb"
|
||||
DB_USER="taxbaik"
|
||||
DB_PASSWORD="${DB_PASSWORD:-$(openssl rand -base64 12)}" # Use env var or generate
|
||||
DEPLOY_DIR="/home/$DEPLOY_USER"
|
||||
|
||||
echo -e "${BLUE}1. Installing .NET 8 Runtime${NC}"
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y dotnet-runtime-8.0 aspnetcore-runtime-8.0
|
||||
|
||||
echo -e "${BLUE}2. Installing PostgreSQL 18${NC}"
|
||||
sudo apt-get install -y postgresql postgresql-contrib
|
||||
|
||||
echo -e "${BLUE}3. Creating database and user${NC}"
|
||||
sudo -u postgres psql << EOF
|
||||
CREATE USER $DB_USER WITH PASSWORD '$DB_PASSWORD';
|
||||
CREATE DATABASE $DB_NAME OWNER $DB_USER;
|
||||
GRANT ALL PRIVILEGES ON DATABASE $DB_NAME TO $DB_USER;
|
||||
EOF
|
||||
|
||||
echo -e "${BLUE}4. Creating deployment directories${NC}"
|
||||
sudo -u $DEPLOY_USER mkdir -p $DEPLOY_DIR/deployments
|
||||
sudo -u $DEPLOY_USER mkdir -p $DEPLOY_DIR/taxbaik_active
|
||||
sudo -u $DEPLOY_USER mkdir -p $DEPLOY_DIR/taxbaik_admin_active
|
||||
|
||||
echo -e "${BLUE}5. Installing systemd service files${NC}"
|
||||
sudo cp deploy/taxbaik.service /etc/systemd/system/
|
||||
sudo cp deploy/taxbaik-admin.service /etc/systemd/system/
|
||||
|
||||
# Update environment variables in service files
|
||||
sudo sed -i "s/YOUR_SECURE_PASSWORD_HERE/$DB_PASSWORD/g" /etc/systemd/system/taxbaik.service
|
||||
sudo sed -i "s/YOUR_SECURE_PASSWORD_HERE/$DB_PASSWORD/g" /etc/systemd/system/taxbaik-admin.service
|
||||
|
||||
echo -e "${BLUE}6. Configuring Nginx${NC}"
|
||||
sudo mkdir -p /etc/nginx/conf.d
|
||||
sudo cp deploy/nginx-taxbaik-locations.conf /etc/nginx/conf.d/taxbaik.conf
|
||||
sudo nginx -t
|
||||
sudo systemctl reload nginx
|
||||
|
||||
echo -e "${BLUE}7. Enabling services${NC}"
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable taxbaik taxbaik-admin
|
||||
sudo systemctl enable postgresql
|
||||
|
||||
echo -e "${GREEN}===== Setup Complete ====="
|
||||
echo ""
|
||||
echo "Database credentials:"
|
||||
echo " Host: localhost"
|
||||
echo " Database: $DB_NAME"
|
||||
echo " User: $DB_USER"
|
||||
echo " Password: $DB_PASSWORD"
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo " 1. Copy the first deployment to ~/deployments/taxbaik_TIMESTAMP/"
|
||||
echo " 2. Create symlinks:"
|
||||
echo " ln -s ~/deployments/taxbaik_TIMESTAMP ~/taxbaik_active"
|
||||
echo " ln -s ~/deployments/taxbaik_admin_TIMESTAMP ~/taxbaik_admin_active"
|
||||
echo " 3. Start services:"
|
||||
echo " sudo systemctl start taxbaik taxbaik-admin"
|
||||
echo " 4. Verify:"
|
||||
echo " sudo systemctl status taxbaik taxbaik-admin"
|
||||
echo " curl http://127.0.0.1:5001/taxbaik"
|
||||
echo " curl http://127.0.0.1:5002/taxbaik/admin/login"
|
||||
@@ -1,14 +0,0 @@
|
||||
namespace TaxBaik.Application.DTOs;
|
||||
|
||||
public class CreateBlogPostDto
|
||||
{
|
||||
public required string Title { get; set; }
|
||||
public required string Content { get; set; }
|
||||
public int? CategoryId { get; set; }
|
||||
public string? Tags { get; set; }
|
||||
public string? SeoTitle { get; set; }
|
||||
public string? SeoDescription { get; set; }
|
||||
public string? ThumbnailUrl { get; set; }
|
||||
public bool IsPublished { get; set; }
|
||||
public int? AuthorId { get; set; }
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
namespace TaxBaik.Application.Services;
|
||||
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using TaxBaik.Domain.Entities;
|
||||
|
||||
public record AdminDashboardSummary(
|
||||
int ThisMonthInquiries,
|
||||
int NewInquiries,
|
||||
int TotalPosts,
|
||||
int PublishedPosts,
|
||||
IReadOnlyList<Inquiry> RecentInquiries);
|
||||
|
||||
public class AdminDashboardService(
|
||||
InquiryService inquiryService,
|
||||
BlogService blogService,
|
||||
IMemoryCache memoryCache)
|
||||
{
|
||||
private static readonly TimeSpan CacheDuration = TimeSpan.FromSeconds(30);
|
||||
public const string CacheKey = "admin-dashboard-summary";
|
||||
|
||||
public async Task<AdminDashboardSummary> GetSummaryAsync(CancellationToken ct = default)
|
||||
{
|
||||
if (memoryCache.TryGetValue(CacheKey, out AdminDashboardSummary? cached) && cached != null)
|
||||
return cached;
|
||||
|
||||
var recentTask = inquiryService.GetPagedAsync(1, 5, ct: ct);
|
||||
var thisMonthTask = inquiryService.CountThisMonthAsync(ct);
|
||||
var newTask = inquiryService.CountByStatusAsync("new", ct);
|
||||
var statsTask = blogService.GetStatsAsync(ct);
|
||||
|
||||
var (recentInquiries, _) = await recentTask;
|
||||
var stats = await statsTask;
|
||||
var summary = new AdminDashboardSummary(
|
||||
ThisMonthInquiries: await thisMonthTask,
|
||||
NewInquiries: await newTask,
|
||||
TotalPosts: stats.TotalPosts,
|
||||
PublishedPosts: stats.PublishedPosts,
|
||||
RecentInquiries: recentInquiries.OrderByDescending(x => x.CreatedAt).Take(5).ToList());
|
||||
|
||||
memoryCache.Set(CacheKey, summary, CacheDuration);
|
||||
return summary;
|
||||
}
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
namespace TaxBaik.Application.Services;
|
||||
|
||||
using System.Text.RegularExpressions;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using TaxBaik.Domain.Entities;
|
||||
using TaxBaik.Domain.Enums;
|
||||
using TaxBaik.Domain.Interfaces;
|
||||
|
||||
public class InquiryService(
|
||||
IInquiryRepository repository,
|
||||
IInquiryNotificationService notificationService,
|
||||
IMemoryCache memoryCache)
|
||||
{
|
||||
private static readonly Regex PhoneRegex = new(@"^01[0-9]-\d{3,4}-\d{4}$");
|
||||
|
||||
public async Task<int> SubmitAsync(
|
||||
string name, string phone, string serviceType, string message,
|
||||
string? email = null, string? ipAddress = null, CancellationToken ct = default)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
throw new ValidationException("이름을 입력하세요.");
|
||||
|
||||
if (!PhoneRegex.IsMatch(phone))
|
||||
throw new ValidationException("올바른 전화번호를 입력하세요. (예: 010-1234-5678)");
|
||||
|
||||
if (string.IsNullOrWhiteSpace(message))
|
||||
throw new ValidationException("문의 내용을 입력하세요.");
|
||||
|
||||
var inquiry = new Inquiry
|
||||
{
|
||||
Name = name.Trim(),
|
||||
Phone = phone.Trim(),
|
||||
Email = string.IsNullOrWhiteSpace(email) ? null : email.Trim(),
|
||||
ServiceType = serviceType ?? "기타",
|
||||
Message = message.Trim(),
|
||||
IpAddress = ipAddress,
|
||||
Status = InquiryStatusMapper.ToStorageValue(InquiryStatus.New),
|
||||
CreatedAt = DateTime.UtcNow
|
||||
};
|
||||
|
||||
var inquiryId = await repository.CreateAsync(inquiry, ct);
|
||||
await notificationService.NotifyCreatedAsync(inquiryId, inquiry.Name, inquiry.Phone, inquiry.ServiceType, inquiry.Message, inquiry.IpAddress, inquiry.CreatedAt, ct);
|
||||
memoryCache.Remove(AdminDashboardService.CacheKey);
|
||||
return inquiryId;
|
||||
}
|
||||
|
||||
public async Task<Inquiry?> GetByIdAsync(int id, CancellationToken ct = default) =>
|
||||
await repository.GetByIdAsync(id, ct);
|
||||
|
||||
public async Task<(IEnumerable<Inquiry>, int)> GetPagedAsync(
|
||||
int page, int pageSize, string? status = null, CancellationToken ct = default) =>
|
||||
await repository.GetPagedAsync(NormalizePage(page), NormalizePageSize(pageSize), NormalizeOptionalStatus(status), ct);
|
||||
|
||||
public Task<int> CountAsync(CancellationToken ct = default)
|
||||
=> repository.CountAsync(ct);
|
||||
|
||||
public Task<int> CountThisMonthAsync(CancellationToken ct = default)
|
||||
=> repository.CountThisMonthAsync(ct);
|
||||
|
||||
public Task<int> CountByStatusAsync(string status, CancellationToken ct = default)
|
||||
=> repository.CountByStatusAsync(status, ct);
|
||||
|
||||
public async Task UpdateStatusAsync(int id, string status, string? changedBy = null, CancellationToken ct = default)
|
||||
{
|
||||
if (!InquiryStatusMapper.TryParse(status, out var parsed))
|
||||
throw new ValidationException("지원하지 않는 문의 상태입니다.");
|
||||
|
||||
var inquiry = await repository.GetByIdAsync(id, ct);
|
||||
if (inquiry == null)
|
||||
return;
|
||||
|
||||
var previousStatus = inquiry.Status;
|
||||
var newStatus = InquiryStatusMapper.ToStorageValue(parsed);
|
||||
|
||||
await repository.UpdateStatusAsync(id, newStatus, ct);
|
||||
await notificationService.NotifyStatusChangedAsync(id, inquiry.Name, inquiry.Phone, inquiry.ServiceType, previousStatus, newStatus, changedBy, ct);
|
||||
memoryCache.Remove(AdminDashboardService.CacheKey);
|
||||
}
|
||||
|
||||
private static int NormalizePage(int page) => Math.Max(1, page);
|
||||
|
||||
private static int NormalizePageSize(int pageSize) => Math.Clamp(pageSize, 1, 100);
|
||||
|
||||
private static string? NormalizeOptionalStatus(string? status)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(status))
|
||||
return null;
|
||||
|
||||
if (!InquiryStatusMapper.TryParse(status, out var parsed))
|
||||
throw new ValidationException("지원하지 않는 문의 상태입니다.");
|
||||
|
||||
return InquiryStatusMapper.ToStorageValue(parsed);
|
||||
}
|
||||
}
|
||||
|
||||
public class ValidationException : Exception
|
||||
{
|
||||
public ValidationException(string message) : base(message) { }
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
namespace TaxBaik.Application.Services;
|
||||
|
||||
using TaxBaik.Domain.Enums;
|
||||
|
||||
public static class InquiryStatusMapper
|
||||
{
|
||||
public static string ToStorageValue(InquiryStatus status) => status switch
|
||||
{
|
||||
InquiryStatus.New => "new",
|
||||
InquiryStatus.Contacted => "contacted",
|
||||
InquiryStatus.Completed => "completed",
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(status), status, null)
|
||||
};
|
||||
|
||||
public static bool TryParse(string? value, out InquiryStatus status)
|
||||
{
|
||||
status = value?.Trim().ToLowerInvariant() switch
|
||||
{
|
||||
"new" => InquiryStatus.New,
|
||||
"contacted" => InquiryStatus.Contacted,
|
||||
"completed" => InquiryStatus.Completed,
|
||||
_ => default
|
||||
};
|
||||
|
||||
return value?.Trim().ToLowerInvariant() is "new" or "contacted" or "completed";
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
namespace TaxBaik.Domain.Entities;
|
||||
|
||||
public class Client
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; } = null!;
|
||||
public string? CompanyName { get; set; }
|
||||
public string? Phone { get; set; }
|
||||
public string? Email { get; set; }
|
||||
public string? ServiceType { get; set; }
|
||||
public string? TaxType { get; set; }
|
||||
public string Status { get; set; } = "active";
|
||||
public string? Source { get; set; }
|
||||
public string? Memo { get; set; }
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public DateTime UpdatedAt { get; set; }
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
@using Microsoft.AspNetCore.Components.Web
|
||||
<!DOCTYPE html>
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>백원숙 세무회계 - 관리자</title>
|
||||
<base href="/taxbaik/" />
|
||||
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@400;500;700&display=swap" rel="stylesheet" />
|
||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
|
||||
<link href="_content/MudBlazor/MudBlazor.min.css" rel="stylesheet" />
|
||||
<script>
|
||||
document.documentElement.classList.toggle(
|
||||
'admin-login-route',
|
||||
window.location.pathname.toLowerCase().endsWith('/admin/login'));
|
||||
</script>
|
||||
<link rel="stylesheet" href="/taxbaik/css/admin.css" />
|
||||
<component type="typeof(HeadOutlet)" render-mode="InteractiveServer" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="components-reconnect-modal" class="admin-reconnect-modal">
|
||||
<div class="admin-reconnect-card">
|
||||
<strong>관리자 세션을 다시 연결하고 있습니다.</strong>
|
||||
<span>배포 또는 서버 재시작 중이면 잠시 후 자동으로 새로고침됩니다.</span>
|
||||
</div>
|
||||
</div>
|
||||
<Routes @rendermode="new InteractiveServerRenderMode(prerender: false)" />
|
||||
<script src="_content/MudBlazor/MudBlazor.min.js"></script>
|
||||
<script src="/taxbaik/js/admin-session.js"></script>
|
||||
<script src="_framework/blazor.web.js"></script>
|
||||
<script>window.taxbaikAdminSession?.watchReconnect();</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,76 +0,0 @@
|
||||
@inherits LayoutComponentBase
|
||||
|
||||
<MudLayout Class="admin-shell">
|
||||
<MudAppBar Elevation="0" Class="admin-topbar">
|
||||
<MudIconButton Icon="@Icons.Material.Filled.Menu"
|
||||
Color="Color.Inherit"
|
||||
Edge="Edge.Start"
|
||||
Class="admin-menu-button"
|
||||
OnClick="@ToggleDrawer" />
|
||||
<div class="admin-topbar-title">
|
||||
<MudText Typo="Typo.caption">TaxBaik Backoffice</MudText>
|
||||
<MudText Typo="Typo.h6">백원숙 세무회계 관리자</MudText>
|
||||
</div>
|
||||
<MudSpacer />
|
||||
<MudButton Class="admin-topbar-action"
|
||||
Variant="Variant.Outlined"
|
||||
Color="Color.Inherit"
|
||||
StartIcon="@Icons.Material.Filled.OpenInNew"
|
||||
Href="/taxbaik">
|
||||
공개 사이트
|
||||
</MudButton>
|
||||
<MudButton Class="admin-topbar-action"
|
||||
Variant="Variant.Filled"
|
||||
Color="Color.Primary"
|
||||
StartIcon="@Icons.Material.Filled.Logout"
|
||||
Href="/taxbaik/admin/logout">
|
||||
로그아웃
|
||||
</MudButton>
|
||||
</MudAppBar>
|
||||
|
||||
<MudDrawer @bind-open="@drawerOpen"
|
||||
Elevation="0"
|
||||
Variant="DrawerVariant.Responsive"
|
||||
Breakpoint="Breakpoint.Md"
|
||||
Class="admin-drawer">
|
||||
<div class="admin-drawer-brand">
|
||||
<div class="admin-brand-mark">T</div>
|
||||
<div>
|
||||
<MudText Typo="Typo.subtitle1">TaxBaik</MudText>
|
||||
<MudText Typo="Typo.caption">세무 운영 콘솔</MudText>
|
||||
</div>
|
||||
</div>
|
||||
<MudNavMenu Class="admin-nav">
|
||||
<MudNavLink Href="/taxbaik/admin/dashboard" Match="NavLinkMatch.All" Icon="@Icons.Material.Filled.Dashboard">대시보드</MudNavLink>
|
||||
<MudNavGroup Title="고객 관리" Icon="@Icons.Material.Filled.PeopleAlt" Expanded="true">
|
||||
<MudNavLink Href="/taxbaik/admin/clients" Icon="@Icons.Material.Filled.ContactPage">고객 카드</MudNavLink>
|
||||
</MudNavGroup>
|
||||
<MudNavGroup Title="홈페이지" Icon="@Icons.Material.Filled.Home" Expanded="false">
|
||||
<MudNavLink Href="/taxbaik/admin/announcements" Icon="@Icons.Material.Filled.Campaign">공지사항</MudNavLink>
|
||||
<MudNavLink Href="/taxbaik/admin/faqs" Icon="@Icons.Material.Filled.QuestionAnswer">FAQ 관리</MudNavLink>
|
||||
<MudNavLink Href="/taxbaik/admin/blog" Icon="@Icons.Material.Filled.Article">블로그 관리</MudNavLink>
|
||||
</MudNavGroup>
|
||||
<MudNavLink Href="/taxbaik/admin/inquiries" Icon="@Icons.Material.Filled.Forum">문의 관리</MudNavLink>
|
||||
<MudNavLink Href="/taxbaik/admin/settings" Icon="@Icons.Material.Filled.Tune">설정</MudNavLink>
|
||||
</MudNavMenu>
|
||||
<div class="admin-drawer-footer">
|
||||
<MudText Typo="Typo.caption">운영 기준</MudText>
|
||||
<MudText Typo="Typo.body2">변경 사항은 배포 후 Playwright로 검증합니다.</MudText>
|
||||
</div>
|
||||
</MudDrawer>
|
||||
|
||||
<MudMainContent Class="admin-main">
|
||||
<MudContainer MaxWidth="MaxWidth.False" Class="admin-content">
|
||||
@Body
|
||||
</MudContainer>
|
||||
</MudMainContent>
|
||||
</MudLayout>
|
||||
|
||||
@code {
|
||||
private bool drawerOpen = true;
|
||||
|
||||
private void ToggleDrawer()
|
||||
{
|
||||
drawerOpen = !drawerOpen;
|
||||
}
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
@page "/admin/blog/create"
|
||||
@attribute [Authorize]
|
||||
@using TaxBaik.Application.DTOs
|
||||
@using TaxBaik.Application.Services
|
||||
@using TaxBaik.Domain.Interfaces
|
||||
@inject BlogService BlogService
|
||||
@inject ICategoryRepository CategoryRepository
|
||||
@inject NavigationManager Navigation
|
||||
@inject ISnackbar Snackbar
|
||||
|
||||
<PageTitle>새 포스트 작성</PageTitle>
|
||||
|
||||
<MudText Typo="Typo.h5" Class="mb-4">📝 새 포스트</MudText>
|
||||
|
||||
<MudPaper Class="pa-4" Elevation="1">
|
||||
<MudForm @ref="form">
|
||||
<MudTextField @bind-Value="model.Title" Label="제목"
|
||||
Variant="Variant.Outlined" Class="mb-4" Required="true" />
|
||||
|
||||
<MudSelect @bind-Value="model.CategoryId" Label="카테고리"
|
||||
Variant="Variant.Outlined" Class="mb-4">
|
||||
@foreach (var category in categories)
|
||||
{
|
||||
<MudSelectItem Value="@category.Id">@category.Name</MudSelectItem>
|
||||
}
|
||||
</MudSelect>
|
||||
|
||||
<MudTextField @bind-Value="model.Content" Label="본문"
|
||||
Variant="Variant.Outlined" Lines="10" Class="mb-4" Required="true" />
|
||||
|
||||
<MudTextField @bind-Value="model.Tags" Label="태그 (쉼표로 구분)"
|
||||
Variant="Variant.Outlined" Class="mb-4" />
|
||||
|
||||
<MudTextField @bind-Value="model.SeoTitle" Label="SEO 제목"
|
||||
Variant="Variant.Outlined" Class="mb-4" />
|
||||
|
||||
<MudTextField @bind-Value="model.SeoDescription" Label="SEO 설명"
|
||||
Variant="Variant.Outlined" Lines="3" Class="mb-4" />
|
||||
|
||||
<MudCheckBox @bind-Checked="model.IsPublished" Label="즉시 발행" Class="mb-4" />
|
||||
|
||||
<div class="d-flex gap-2">
|
||||
<MudButton Variant="Variant.Filled" Color="Color.Primary"
|
||||
@onclick="SavePost">저장</MudButton>
|
||||
<MudButton Variant="Variant.Outlined" @onclick="@(() => Navigation.NavigateTo("/taxbaik/admin/blog"))">
|
||||
취소
|
||||
</MudButton>
|
||||
</div>
|
||||
</MudForm>
|
||||
</MudPaper>
|
||||
|
||||
@code {
|
||||
private MudForm? form;
|
||||
private List<Domain.Entities.Category> categories = [];
|
||||
private CreatePostModel model = new();
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
categories = (await CategoryRepository.GetAllAsync()).ToList();
|
||||
}
|
||||
|
||||
private async Task SavePost()
|
||||
{
|
||||
if (form == null)
|
||||
return;
|
||||
|
||||
await form.Validate();
|
||||
if (!form.IsValid)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
await BlogService.CreateAsync(new CreateBlogPostDto
|
||||
{
|
||||
Title = model.Title,
|
||||
Content = model.Content,
|
||||
CategoryId = model.CategoryId,
|
||||
Tags = model.Tags,
|
||||
SeoTitle = model.SeoTitle,
|
||||
SeoDescription = model.SeoDescription,
|
||||
IsPublished = model.IsPublished
|
||||
});
|
||||
|
||||
Snackbar.Add("포스트가 저장되었습니다.", Severity.Success);
|
||||
Navigation.NavigateTo("/taxbaik/admin/blog");
|
||||
}
|
||||
catch (ValidationException ex)
|
||||
{
|
||||
Snackbar.Add(ex.Message, Severity.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private class CreatePostModel
|
||||
{
|
||||
public string Title { get; set; } = "";
|
||||
public string Content { get; set; } = "";
|
||||
public int? CategoryId { get; set; }
|
||||
public string? Tags { get; set; }
|
||||
public string? SeoTitle { get; set; }
|
||||
public string? SeoDescription { get; set; }
|
||||
public bool IsPublished { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,141 +0,0 @@
|
||||
@page "/admin/blog"
|
||||
@attribute [Authorize]
|
||||
@inject IApiClient ApiClient
|
||||
@inject ISnackbar Snackbar
|
||||
|
||||
<PageTitle>블로그 관리</PageTitle>
|
||||
|
||||
<section class="admin-page-hero">
|
||||
<div>
|
||||
<MudText Typo="Typo.caption" Class="admin-eyebrow">Content</MudText>
|
||||
<MudText Typo="Typo.h4" Class="admin-page-title">블로그 관리</MudText>
|
||||
<MudText Typo="Typo.body2" Class="admin-page-subtitle">검색 유입 콘텐츠의 발행 상태와 성과를 관리합니다.</MudText>
|
||||
</div>
|
||||
<MudButton Variant="Variant.Filled" Color="Color.Primary" StartIcon="@Icons.Material.Filled.EditNote"
|
||||
Href="/taxbaik/admin/blog/create">새 포스트 작성</MudButton>
|
||||
</section>
|
||||
|
||||
<MudPaper Class="admin-surface mb-4" Elevation="0">
|
||||
<MudStack Row="true" AlignItems="AlignItems.Center" Justify="Justify.SpaceBetween">
|
||||
<MudText Typo="Typo.subtitle1">@($"전체 포스트 {totalPosts}개")</MudText>
|
||||
<MudText Typo="Typo.body2">페이지 @currentPage / @totalPages</MudText>
|
||||
</MudStack>
|
||||
</MudPaper>
|
||||
|
||||
<MudDataGrid Items="@posts" Striped="true" Hoverable="true" Loading="@isLoading" Class="admin-grid">
|
||||
<Columns>
|
||||
<PropertyColumn Property="x => x.Title" Title="제목" />
|
||||
<PropertyColumn Property="x => x.IsPublished" Title="발행">
|
||||
<CellTemplate Context="cell">
|
||||
<MudCheckBox T="bool" Value="@cell.Item.IsPublished"
|
||||
ValueChanged="@(async (bool value) => await TogglePublish(cell.Item, value))" />
|
||||
</CellTemplate>
|
||||
</PropertyColumn>
|
||||
<PropertyColumn Property="x => x.ViewCount" Title="조회수" />
|
||||
<PropertyColumn Property="x => x.CreatedAt" Title="작성일" Format="yyyy-MM-dd" />
|
||||
<TemplateColumn>
|
||||
<CellTemplate Context="cell">
|
||||
<MudButton Variant="Variant.Outlined" Size="Size.Small" Color="Color.Primary"
|
||||
Href="@($"/taxbaik/admin/blog/{cell.Item.Id}/edit")">수정하기</MudButton>
|
||||
<MudButton Variant="Variant.Text" Size="Size.Small" Color="Color.Error"
|
||||
@onclick="@(async () => await DeletePost(cell.Item.Id))">삭제</MudButton>
|
||||
</CellTemplate>
|
||||
</TemplateColumn>
|
||||
</Columns>
|
||||
</MudDataGrid>
|
||||
|
||||
<MudStack Row="true" Justify="Justify.Center" Class="mt-4" Spacing="2">
|
||||
<MudButton Variant="Variant.Outlined" Disabled="@(currentPage <= 1 || isLoading)" @onclick="PreviousPage">이전</MudButton>
|
||||
<MudButton Variant="Variant.Outlined" Disabled="@(currentPage >= totalPages || isLoading)" @onclick="NextPage">다음</MudButton>
|
||||
</MudStack>
|
||||
|
||||
@code {
|
||||
private List<TaxBaik.Domain.Entities.BlogPost> posts = [];
|
||||
private bool isLoading = true;
|
||||
private int currentPage = 1;
|
||||
private int totalPages = 1;
|
||||
private int totalPosts = 0;
|
||||
private const int PageSize = 20;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await LoadPosts();
|
||||
}
|
||||
|
||||
private async Task LoadPosts()
|
||||
{
|
||||
isLoading = true;
|
||||
try
|
||||
{
|
||||
var result = await ApiClient.GetAsync<PagedBlogResponse>($"blog/admin?page={currentPage}&pageSize={PageSize}");
|
||||
posts = result?.Data ?? [];
|
||||
totalPosts = result?.Total ?? 0;
|
||||
totalPages = Math.Max(1, (int)Math.Ceiling(totalPosts / (double)PageSize));
|
||||
}
|
||||
catch
|
||||
{
|
||||
posts = [];
|
||||
totalPosts = 0;
|
||||
totalPages = 1;
|
||||
}
|
||||
isLoading = false;
|
||||
}
|
||||
|
||||
private async Task PreviousPage()
|
||||
{
|
||||
if (currentPage <= 1)
|
||||
return;
|
||||
|
||||
currentPage--;
|
||||
await LoadPosts();
|
||||
}
|
||||
|
||||
private async Task NextPage()
|
||||
{
|
||||
if (currentPage >= totalPages)
|
||||
return;
|
||||
|
||||
currentPage++;
|
||||
await LoadPosts();
|
||||
}
|
||||
|
||||
private async Task TogglePublish(TaxBaik.Domain.Entities.BlogPost post, bool isPublished)
|
||||
{
|
||||
var previous = post.IsPublished;
|
||||
post.IsPublished = isPublished;
|
||||
var result = await ApiClient.PutAsync<TaxBaik.Domain.Entities.BlogPost>($"blog/{post.Id}", new
|
||||
{
|
||||
post.Title,
|
||||
post.Content,
|
||||
post.CategoryId,
|
||||
post.Tags,
|
||||
post.SeoTitle,
|
||||
post.SeoDescription,
|
||||
post.ThumbnailUrl,
|
||||
IsPublished = isPublished,
|
||||
post.AuthorId
|
||||
});
|
||||
|
||||
if (result == null)
|
||||
{
|
||||
post.IsPublished = previous;
|
||||
Snackbar.Add("발행 상태 변경에 실패했습니다.", Severity.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
Snackbar.Add("발행 상태가 변경되었습니다.", Severity.Success);
|
||||
}
|
||||
|
||||
private async Task DeletePost(int postId)
|
||||
{
|
||||
await ApiClient.DeleteAsync($"blog/{postId}");
|
||||
Snackbar.Add("포스트가 삭제되었습니다.", Severity.Success);
|
||||
await LoadPosts();
|
||||
}
|
||||
|
||||
private class PagedBlogResponse
|
||||
{
|
||||
public List<TaxBaik.Domain.Entities.BlogPost> Data { get; set; } = [];
|
||||
public int Total { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
@page "/admin/dashboard"
|
||||
@attribute [Authorize]
|
||||
@using TaxBaik.Application.Services
|
||||
@inject AdminDashboardService DashboardService
|
||||
|
||||
<PageTitle>대시보드</PageTitle>
|
||||
|
||||
<section class="admin-page-hero">
|
||||
<div>
|
||||
<MudText Typo="Typo.caption" Class="admin-eyebrow">Overview</MudText>
|
||||
<MudText Typo="Typo.h4" Class="admin-page-title">대시보드</MudText>
|
||||
<MudText Typo="Typo.body2" Class="admin-page-subtitle">문의 흐름과 콘텐츠 상태를 한 화면에서 확인합니다.</MudText>
|
||||
</div>
|
||||
<MudButton Variant="Variant.Filled" Color="Color.Primary" StartIcon="@Icons.Material.Filled.Add" Href="/taxbaik/admin/blog/create">
|
||||
새 포스트 작성
|
||||
</MudButton>
|
||||
</section>
|
||||
|
||||
<MudGrid Class="admin-metric-grid">
|
||||
<MudItem xs="12" sm="6" md="3">
|
||||
<MudPaper Class="admin-metric-card accent-blue" Elevation="0">
|
||||
<MudText Typo="Typo.caption">이번달 문의</MudText>
|
||||
<MudText Typo="Typo.h3">@summary.ThisMonthInquiries</MudText>
|
||||
<MudText Typo="Typo.body2">월간 상담 유입</MudText>
|
||||
</MudPaper>
|
||||
</MudItem>
|
||||
|
||||
<MudItem xs="12" sm="6" md="3">
|
||||
<MudPaper Class="admin-metric-card accent-amber" Elevation="0">
|
||||
<MudText Typo="Typo.caption">신규 문의</MudText>
|
||||
<MudText Typo="Typo.h3">@summary.NewInquiries</MudText>
|
||||
<MudText Typo="Typo.body2">처리 대기</MudText>
|
||||
</MudPaper>
|
||||
</MudItem>
|
||||
|
||||
<MudItem xs="12" sm="6" md="3">
|
||||
<MudPaper Class="admin-metric-card accent-slate" Elevation="0">
|
||||
<MudText Typo="Typo.caption">전체 포스트</MudText>
|
||||
<MudText Typo="Typo.h3">@summary.TotalPosts</MudText>
|
||||
<MudText Typo="Typo.body2">콘텐츠 자산</MudText>
|
||||
</MudPaper>
|
||||
</MudItem>
|
||||
|
||||
<MudItem xs="12" sm="6" md="3">
|
||||
<MudPaper Class="admin-metric-card accent-green" Elevation="0">
|
||||
<MudText Typo="Typo.caption">발행된 포스트</MudText>
|
||||
<MudText Typo="Typo.h3">@summary.PublishedPosts</MudText>
|
||||
<MudText Typo="Typo.body2">검색 노출 대상</MudText>
|
||||
</MudPaper>
|
||||
</MudItem>
|
||||
</MudGrid>
|
||||
|
||||
<MudPaper Class="admin-surface mt-4" Elevation="0">
|
||||
<div class="admin-section-header">
|
||||
<div>
|
||||
<MudText Typo="Typo.h6">최근 문의</MudText>
|
||||
<MudText Typo="Typo.body2">최근 유입된 상담 요청을 빠르게 확인합니다.</MudText>
|
||||
</div>
|
||||
<MudButton Variant="Variant.Outlined" Color="Color.Primary" Href="/taxbaik/admin/inquiries">문의 전체 보기</MudButton>
|
||||
</div>
|
||||
<MudSimpleTable Striped="true" Dense="true" Class="admin-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>이름</th>
|
||||
<th>전화</th>
|
||||
<th>분야</th>
|
||||
<th>상태</th>
|
||||
<th>날짜</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var inquiry in summary.RecentInquiries)
|
||||
{
|
||||
<tr>
|
||||
<td>@inquiry.Name</td>
|
||||
<td>@inquiry.Phone</td>
|
||||
<td>@inquiry.ServiceType</td>
|
||||
<td>
|
||||
<MudChip Size="Size.Small"
|
||||
Color="@(inquiry.Status == "new" ? Color.Warning : inquiry.Status == "contacted" ? Color.Info : Color.Success)">
|
||||
@GetStatusLabel(inquiry.Status)
|
||||
</MudChip>
|
||||
</td>
|
||||
<td>@inquiry.CreatedAt.ToString("yyyy-MM-dd")</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</MudSimpleTable>
|
||||
</MudPaper>
|
||||
|
||||
@code {
|
||||
private AdminDashboardSummary summary = new(0, 0, 0, 0, []);
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
summary = await DashboardService.GetSummaryAsync();
|
||||
}
|
||||
|
||||
private static string GetStatusLabel(string status) => status switch
|
||||
{
|
||||
"new" => "신규",
|
||||
"contacted" => "연락함",
|
||||
"completed" => "완료",
|
||||
_ => status
|
||||
};
|
||||
}
|
||||
@@ -1,120 +0,0 @@
|
||||
@page "/admin/faqs"
|
||||
@attribute [Authorize]
|
||||
@using TaxBaik.Application.Services
|
||||
@using TaxBaik.Domain.Entities
|
||||
@inject FaqService FaqService
|
||||
@inject NavigationManager Navigation
|
||||
@inject IDialogService DialogService
|
||||
@inject ISnackbar Snackbar
|
||||
|
||||
<PageTitle>FAQ 관리</PageTitle>
|
||||
|
||||
<section class="admin-page-hero">
|
||||
<div>
|
||||
<MudText Typo="Typo.caption" Class="admin-eyebrow">홈페이지</MudText>
|
||||
<MudText Typo="Typo.h4" Class="admin-page-title">FAQ 관리</MudText>
|
||||
<MudText Typo="Typo.body2" Class="admin-page-subtitle">홈페이지 자주 묻는 질문을 등록하고 순서를 관리합니다.</MudText>
|
||||
</div>
|
||||
<MudButton Variant="Variant.Filled" Color="Color.Primary"
|
||||
StartIcon="@Icons.Material.Filled.Add"
|
||||
Href="/taxbaik/admin/faqs/create">
|
||||
FAQ 등록
|
||||
</MudButton>
|
||||
</section>
|
||||
|
||||
<MudPaper Class="admin-surface" Elevation="0">
|
||||
@if (faqs is null)
|
||||
{
|
||||
<MudProgressLinear Indeterminate="true" />
|
||||
}
|
||||
else if (!faqs.Any())
|
||||
{
|
||||
<div class="pa-6 text-center">
|
||||
<MudIcon Icon="@Icons.Material.Filled.QuestionAnswer" Style="font-size:3rem; opacity:.3;" />
|
||||
<MudText Class="mt-2 text-muted">등록된 FAQ가 없습니다.</MudText>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudSimpleTable Striped="true" Dense="true" Class="admin-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:60px;">순서</th>
|
||||
<th>질문</th>
|
||||
<th style="width:130px;">카테고리</th>
|
||||
<th style="width:90px;">상태</th>
|
||||
<th style="width:160px;"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var item in faqs)
|
||||
{
|
||||
<tr>
|
||||
<td class="text-center">
|
||||
<MudText Typo="Typo.body2">@item.SortOrder</MudText>
|
||||
</td>
|
||||
<td>
|
||||
<MudText Typo="Typo.body2" Style="max-width:480px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;">
|
||||
@item.Question
|
||||
</MudText>
|
||||
</td>
|
||||
<td>
|
||||
@if (!string.IsNullOrEmpty(item.Category))
|
||||
{
|
||||
<MudChip Size="Size.Small" Color="Color.Default">@item.Category</MudChip>
|
||||
}
|
||||
</td>
|
||||
<td>
|
||||
@if (item.IsActive)
|
||||
{
|
||||
<MudChip Size="Size.Small" Color="Color.Success">노출 중</MudChip>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudChip Size="Size.Small" Color="Color.Default">비활성</MudChip>
|
||||
}
|
||||
</td>
|
||||
<td>
|
||||
<MudButtonGroup Size="Size.Small" Variant="Variant.Outlined">
|
||||
<MudButton @onclick="@(() => Navigation.NavigateTo($"/taxbaik/admin/faqs/{item.Id}/edit"))">
|
||||
수정
|
||||
</MudButton>
|
||||
<MudButton Color="Color.Error" @onclick="@(() => DeleteAsync(item))">
|
||||
삭제
|
||||
</MudButton>
|
||||
</MudButtonGroup>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</MudSimpleTable>
|
||||
<MudText Typo="Typo.caption" Class="pa-2 text-muted">
|
||||
총 @(faqs.Count)개 · 노출 중 @(faqs.Count(f => f.IsActive))개
|
||||
</MudText>
|
||||
}
|
||||
</MudPaper>
|
||||
|
||||
@code {
|
||||
private List<Faq>? faqs;
|
||||
|
||||
protected override async Task OnInitializedAsync() => await LoadAsync();
|
||||
|
||||
private async Task LoadAsync()
|
||||
{
|
||||
faqs = (await FaqService.GetAllAsync()).ToList();
|
||||
}
|
||||
|
||||
private async Task DeleteAsync(Faq item)
|
||||
{
|
||||
var confirmed = await DialogService.ShowMessageBox(
|
||||
"FAQ 삭제",
|
||||
$"'{item.Question}' 항목을 삭제하시겠습니까?",
|
||||
yesText: "삭제", cancelText: "취소");
|
||||
|
||||
if (confirmed != true) return;
|
||||
|
||||
await FaqService.DeleteAsync(item.Id);
|
||||
Snackbar.Add("FAQ가 삭제되었습니다.", Severity.Success);
|
||||
await LoadAsync();
|
||||
}
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
@page "/admin/inquiries/{InquiryId:int}"
|
||||
@attribute [Authorize]
|
||||
@using TaxBaik.Application.Services
|
||||
@inject InquiryService InquiryService
|
||||
@inject NavigationManager Navigation
|
||||
@inject ISnackbar Snackbar
|
||||
|
||||
<PageTitle>문의 상세</PageTitle>
|
||||
|
||||
@if (inquiry != null)
|
||||
{
|
||||
<MudButton Variant="Variant.Outlined"
|
||||
Color="Color.Primary"
|
||||
StartIcon="@Icons.Material.Filled.ArrowBack"
|
||||
@onclick="@(() => Navigation.NavigateTo("/taxbaik/admin/inquiries"))">
|
||||
문의 목록으로 돌아가기
|
||||
</MudButton>
|
||||
|
||||
<MudPaper Class="pa-4 mt-4" Elevation="1">
|
||||
<MudStack Row="true" AlignItems="AlignItems.Center" Justify="Justify.SpaceBetween" Class="mb-4">
|
||||
<MudText Typo="Typo.h5">문의 상세</MudText>
|
||||
<MudButton Variant="Variant.Filled"
|
||||
Color="Color.Primary"
|
||||
StartIcon="@Icons.Material.Filled.List"
|
||||
Href="/taxbaik/admin/inquiries">
|
||||
다른 문의도 보기
|
||||
</MudButton>
|
||||
</MudStack>
|
||||
|
||||
<MudGrid>
|
||||
<MudItem xs="12" md="6">
|
||||
<MudText Typo="Typo.subtitle1">이름</MudText>
|
||||
<MudText>@inquiry.Name</MudText>
|
||||
</MudItem>
|
||||
<MudItem xs="12" md="6">
|
||||
<MudText Typo="Typo.subtitle1">연락처</MudText>
|
||||
<MudText>@inquiry.Phone</MudText>
|
||||
</MudItem>
|
||||
<MudItem xs="12" md="6">
|
||||
<MudText Typo="Typo.subtitle1">이메일</MudText>
|
||||
<MudText>@inquiry.Email</MudText>
|
||||
</MudItem>
|
||||
<MudItem xs="12" md="6">
|
||||
<MudText Typo="Typo.subtitle1">분야</MudText>
|
||||
<MudText>@inquiry.ServiceType</MudText>
|
||||
</MudItem>
|
||||
<MudItem xs="12">
|
||||
<MudText Typo="Typo.subtitle1">메시지</MudText>
|
||||
<MudPaper Class="pa-3 mt-2" Outlined="true">
|
||||
<MudText Style="white-space: pre-wrap;">@inquiry.Message</MudText>
|
||||
</MudPaper>
|
||||
</MudItem>
|
||||
<MudItem xs="12">
|
||||
<MudText Typo="Typo.subtitle1">상태</MudText>
|
||||
<MudSelect T="string" Value="inquiry.Status" ValueChanged="@((string status) => OnStatusChanged(status))" Label="상태 변경">
|
||||
<MudSelectItem Value="@("new")">신규</MudSelectItem>
|
||||
<MudSelectItem Value="@("contacted")">연락함</MudSelectItem>
|
||||
<MudSelectItem Value="@("completed")">완료</MudSelectItem>
|
||||
</MudSelect>
|
||||
<MudStack Row="true" Class="mt-3" Spacing="2">
|
||||
<MudButton Variant="Variant.Outlined" Color="Color.Warning" OnClick="@(() => OnStatusChanged("new"))">신규</MudButton>
|
||||
<MudButton Variant="Variant.Outlined" Color="Color.Info" OnClick="@(() => OnStatusChanged("contacted"))">연락함</MudButton>
|
||||
<MudButton Variant="Variant.Outlined" Color="Color.Success" OnClick="@(() => OnStatusChanged("completed"))">완료</MudButton>
|
||||
</MudStack>
|
||||
</MudItem>
|
||||
</MudGrid>
|
||||
</MudPaper>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudText>문의를 찾을 수 없습니다.</MudText>
|
||||
}
|
||||
|
||||
@code {
|
||||
[Parameter]
|
||||
public int InquiryId { get; set; }
|
||||
|
||||
private Domain.Entities.Inquiry? inquiry;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
inquiry = await InquiryService.GetByIdAsync(InquiryId);
|
||||
}
|
||||
|
||||
private async Task OnStatusChanged(string status)
|
||||
{
|
||||
if (inquiry == null)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
await InquiryService.UpdateStatusAsync(inquiry.Id, status, "관리자");
|
||||
inquiry.Status = status;
|
||||
Snackbar.Add("상태가 변경되었습니다.", Severity.Success);
|
||||
}
|
||||
catch (ValidationException ex)
|
||||
{
|
||||
Snackbar.Add(ex.Message, Severity.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
@page "/admin/inquiries"
|
||||
@attribute [Authorize]
|
||||
@using TaxBaik.Domain.Interfaces
|
||||
@inject IInquiryRepository InquiryRepository
|
||||
|
||||
<PageTitle>문의 관리</PageTitle>
|
||||
|
||||
<section class="admin-page-hero">
|
||||
<div>
|
||||
<MudText Typo="Typo.caption" Class="admin-eyebrow">Customer Requests</MudText>
|
||||
<MudText Typo="Typo.h4" Class="admin-page-title">문의 관리</MudText>
|
||||
<MudText Typo="Typo.body2" Class="admin-page-subtitle">상담 요청을 상태별로 확인하고 후속 조치를 기록합니다.</MudText>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<MudPaper Class="admin-surface" Elevation="0">
|
||||
<MudTabs Rounded="true" Elevation="0" Class="admin-tabs">
|
||||
<MudTabPanel Text="전체">
|
||||
<InquiryTable Status="" />
|
||||
</MudTabPanel>
|
||||
<MudTabPanel Text="신규">
|
||||
<InquiryTable Status="new" />
|
||||
</MudTabPanel>
|
||||
<MudTabPanel Text="연락함">
|
||||
<InquiryTable Status="contacted" />
|
||||
</MudTabPanel>
|
||||
<MudTabPanel Text="완료">
|
||||
<InquiryTable Status="completed" />
|
||||
</MudTabPanel>
|
||||
</MudTabs>
|
||||
</MudPaper>
|
||||
@@ -1,128 +0,0 @@
|
||||
@page "/admin/login"
|
||||
@using System.ComponentModel.DataAnnotations
|
||||
@layout TaxBaik.Web.Components.Admin.Layout.BlankLayout
|
||||
@attribute [AllowAnonymous]
|
||||
@inject IApiClient ApiClient
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject CustomAuthenticationStateProvider AuthStateProvider
|
||||
@inject IJSRuntime Js
|
||||
|
||||
<PageTitle>로그인</PageTitle>
|
||||
|
||||
<MudThemeProvider />
|
||||
|
||||
<MudContainer MaxWidth="MaxWidth.Small" Class="admin-login-page d-flex align-center justify-center" Style="min-height: 100vh;">
|
||||
<MudPaper Class="pa-8" Elevation="3" Style="width: 100%; max-width: 400px;">
|
||||
<MudText Typo="Typo.h4" Class="mb-6 text-center">관리자 로그인</MudText>
|
||||
|
||||
<div>
|
||||
<InputText class="mud-input mud-input-outlined mud-input-root mud-input-root-adorned-start mb-4"
|
||||
style="width: 100%; min-height: 56px; padding: 16px 14px;"
|
||||
placeholder="사용자명"
|
||||
autocomplete="username"
|
||||
@bind-Value="model.Username" />
|
||||
|
||||
<InputText type="password"
|
||||
class="mud-input mud-input-outlined mud-input-root mud-input-root-adorned-start mb-4"
|
||||
style="width: 100%; min-height: 56px; padding: 16px 14px;"
|
||||
placeholder="비밀번호"
|
||||
autocomplete="current-password"
|
||||
@bind-Value="model.Password" />
|
||||
|
||||
@if (!string.IsNullOrEmpty(errorMessage))
|
||||
{
|
||||
<MudAlert Severity="Severity.Error" Class="mb-4">@errorMessage</MudAlert>
|
||||
}
|
||||
|
||||
<button type="button"
|
||||
class="mud-button-root mud-button mud-button-filled mud-button-filled-primary mud-elevation-0"
|
||||
style="width: 100%; min-height: 52px; border: 0; border-radius: 4px; color: white;"
|
||||
@onclick="HandleLogin"
|
||||
disabled="@isLoading">
|
||||
@if (isLoading)
|
||||
{
|
||||
<MudProgressCircular Size="Size.Small" Indeterminate="true" Class="mr-2" />
|
||||
<span>로그인 중...</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span>로그인</span>
|
||||
}
|
||||
</button>
|
||||
</div>
|
||||
</MudPaper>
|
||||
</MudContainer>
|
||||
|
||||
@code {
|
||||
private bool isLoading = false;
|
||||
private string errorMessage = "";
|
||||
|
||||
private LoginModel model = new();
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
await Js.InvokeVoidAsync("taxbaikAdminSession.syncRouteClass");
|
||||
}
|
||||
|
||||
private async Task HandleLogin()
|
||||
{
|
||||
if (isLoading)
|
||||
return;
|
||||
|
||||
isLoading = true;
|
||||
errorMessage = "";
|
||||
|
||||
try
|
||||
{
|
||||
var request = new { model.Username, model.Password };
|
||||
var response = await ApiClient.PostAsync<LoginResponse>("auth/login", request);
|
||||
|
||||
if (response?.Token == null)
|
||||
{
|
||||
errorMessage = "사용자명 또는 비밀번호가 올바르지 않습니다.";
|
||||
isLoading = false;
|
||||
return;
|
||||
}
|
||||
|
||||
await ApiClient.SetAuthToken(response.Token);
|
||||
await AuthStateProvider.LoginAsync(response.Token);
|
||||
NavigationManager.NavigateTo(GetReturnUrl(), forceLoad: false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
errorMessage = "로그인 중 오류가 발생했습니다.";
|
||||
isLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
private class LoginResponse
|
||||
{
|
||||
public string Token { get; set; } = "";
|
||||
public int ExpiresIn { get; set; }
|
||||
}
|
||||
|
||||
private class LoginModel
|
||||
{
|
||||
public string Username { get; set; } = "";
|
||||
public string Password { get; set; } = "";
|
||||
}
|
||||
|
||||
private string GetReturnUrl()
|
||||
{
|
||||
var uri = NavigationManager.ToAbsoluteUri(NavigationManager.Uri);
|
||||
if (!Microsoft.AspNetCore.WebUtilities.QueryHelpers.ParseQuery(uri.Query).TryGetValue("returnUrl", out var returnUrl)
|
||||
|| string.IsNullOrWhiteSpace(returnUrl))
|
||||
{
|
||||
return "/taxbaik/admin/dashboard";
|
||||
}
|
||||
|
||||
var value = returnUrl.ToString();
|
||||
if (!value.StartsWith("admin", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return "/taxbaik/admin/dashboard";
|
||||
}
|
||||
|
||||
return $"/taxbaik/{value.TrimStart('/')}";
|
||||
}
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.Security.Claims;
|
||||
using TaxBaik.Application.Services;
|
||||
|
||||
namespace TaxBaik.Web.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class InquiryController : ControllerBase
|
||||
{
|
||||
private readonly InquiryService _inquiryService;
|
||||
|
||||
public InquiryController(InquiryService inquiryService)
|
||||
{
|
||||
_inquiryService = inquiryService;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> Submit([FromBody] SubmitInquiryRequest request)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(request.Name) || string.IsNullOrWhiteSpace(request.Phone))
|
||||
return BadRequest(new ProblemDetails { Title = "이름과 전화번호를 입력하세요.", Status = StatusCodes.Status400BadRequest });
|
||||
|
||||
try
|
||||
{
|
||||
await _inquiryService.SubmitAsync(
|
||||
request.Name,
|
||||
request.Phone,
|
||||
request.ServiceType,
|
||||
request.Message,
|
||||
request.Email,
|
||||
HttpContext.Connection.RemoteIpAddress?.ToString());
|
||||
return Ok(new { message = "상담 신청이 접수되었습니다." });
|
||||
}
|
||||
catch (ValidationException ex)
|
||||
{
|
||||
return BadRequest(new ProblemDetails { Title = ex.Message, Status = StatusCodes.Status400BadRequest });
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Authorize]
|
||||
public async Task<IActionResult> GetPaged([FromQuery] int page = 1, [FromQuery] int pageSize = 20)
|
||||
{
|
||||
var (inquiries, total) = await _inquiryService.GetPagedAsync(page, pageSize);
|
||||
return Ok(new { data = inquiries, total, page, pageSize });
|
||||
}
|
||||
|
||||
[HttpGet("{id}")]
|
||||
[Authorize]
|
||||
public async Task<IActionResult> GetById(int id)
|
||||
{
|
||||
var inquiry = await _inquiryService.GetByIdAsync(id);
|
||||
if (inquiry == null)
|
||||
return NotFound(new ProblemDetails { Title = "문의를 찾을 수 없습니다.", Status = StatusCodes.Status404NotFound });
|
||||
return Ok(inquiry);
|
||||
}
|
||||
|
||||
[HttpPut("{id}/status")]
|
||||
[Authorize]
|
||||
public async Task<IActionResult> UpdateStatus(int id, [FromBody] UpdateStatusRequest request)
|
||||
{
|
||||
var inquiry = await _inquiryService.GetByIdAsync(id);
|
||||
if (inquiry == null)
|
||||
return NotFound(new ProblemDetails { Title = "문의를 찾을 수 없습니다.", Status = StatusCodes.Status404NotFound });
|
||||
|
||||
try
|
||||
{
|
||||
var changedBy = User.FindFirstValue(ClaimTypes.Name) ?? User.Identity?.Name;
|
||||
await _inquiryService.UpdateStatusAsync(id, request.Status, changedBy);
|
||||
return Ok(new { message = "상태가 변경되었습니다." });
|
||||
}
|
||||
catch (ValidationException ex)
|
||||
{
|
||||
return BadRequest(new ProblemDetails { Title = ex.Message, Status = StatusCodes.Status400BadRequest });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class SubmitInquiryRequest
|
||||
{
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public string Phone { get; set; } = string.Empty;
|
||||
public string? Email { get; set; }
|
||||
public string ServiceType { get; set; } = string.Empty;
|
||||
public string Message { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public class UpdateStatusRequest
|
||||
{
|
||||
public string Status { get; set; } = string.Empty;
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using TaxBaik.Application.Services;
|
||||
|
||||
namespace TaxBaik.Web.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
[Authorize]
|
||||
public class SiteSettingsController : ControllerBase
|
||||
{
|
||||
private readonly SiteSettingService _siteSettingService;
|
||||
|
||||
public SiteSettingsController(SiteSettingService siteSettingService)
|
||||
{
|
||||
_siteSettingService = siteSettingService;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> Get()
|
||||
{
|
||||
var settings = await _siteSettingService.GetAllAsync();
|
||||
return Ok(settings);
|
||||
}
|
||||
|
||||
[HttpPut]
|
||||
public async Task<IActionResult> Save([FromBody] SaveSiteSettingsRequest request)
|
||||
{
|
||||
if (request is null)
|
||||
return BadRequest(new { message = "요청 본문이 비어 있습니다." });
|
||||
|
||||
await _siteSettingService.SaveAsync(request.Phone, request.Email, request.KakaoUrl, request.InstagramUrl);
|
||||
return Ok(new { message = "사이트 설정이 저장되었습니다." });
|
||||
}
|
||||
}
|
||||
|
||||
public class SaveSiteSettingsRequest
|
||||
{
|
||||
public string Phone { get; set; } = string.Empty;
|
||||
public string Email { get; set; } = string.Empty;
|
||||
public string KakaoUrl { get; set; } = string.Empty;
|
||||
public string InstagramUrl { get; set; } = string.Empty;
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
@page
|
||||
@{
|
||||
ViewData["Title"] = "소개 | 백원숙 세무회계";
|
||||
}
|
||||
|
||||
<div class="container py-5">
|
||||
<h1 class="fw-bold mb-5">백원숙 세무사</h1>
|
||||
|
||||
<div class="row g-5">
|
||||
<div class="col-md-6">
|
||||
<p class="lead">사업자 세무, 부동산 거래, 가족 자산 관리 등 종합적인 세무 컨설팅을 제공합니다.</p>
|
||||
<p>10년 이상의 풍부한 경험과 3개의 국가자격증을 바탕으로, 각 클라이언트의 상황에 맞는 맞춤형 솔루션을 제시합니다.</p>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="bg-light p-4 rounded">
|
||||
<h5 class="fw-bold mb-3">보유 자격증</h5>
|
||||
<div class="mb-3">
|
||||
<p class="mb-1">🎓 <strong>세무사</strong></p>
|
||||
<small class="text-muted">2015년 자격취득</small>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<p class="mb-1">🏠 <strong>부동산중개사</strong></p>
|
||||
<small class="text-muted">부동산 거래 전문성</small>
|
||||
</div>
|
||||
<div>
|
||||
<p class="mb-1">📊 <strong>보험설계사</strong></p>
|
||||
<small class="text-muted">자산관리 전문성</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-5" />
|
||||
|
||||
<h2 class="fw-bold mb-4">서비스 철학</h2>
|
||||
<div class="row g-4">
|
||||
<div class="col-md-4 text-center">
|
||||
<div class="mb-3" style="font-size: 2rem;">🎯</div>
|
||||
<h5>명확한 설명</h5>
|
||||
<p class="small">어려운 세법을 쉽게 설명하여 이해를 높입니다</p>
|
||||
</div>
|
||||
<div class="col-md-4 text-center">
|
||||
<div class="mb-3" style="font-size: 2rem;">💰</div>
|
||||
<h5>최대 절세</h5>
|
||||
<p class="small">법적 범위 내에서 세금을 최소화합니다</p>
|
||||
</div>
|
||||
<div class="col-md-4 text-center">
|
||||
<div class="mb-3" style="font-size: 2rem;">🤝</div>
|
||||
<h5>신뢰 관계</h5>
|
||||
<p class="small">장기적 파트너로서 성장을 함께 합니다</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-center mt-5">
|
||||
<a href="/taxbaik/contact" class="btn btn-primary btn-lg">상담 신청하기</a>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,72 +0,0 @@
|
||||
@page
|
||||
@model TaxBaik.Web.Pages.ContactModel
|
||||
@{
|
||||
ViewData["Title"] = "상담 신청 | 백원숙 세무회계";
|
||||
}
|
||||
|
||||
<div class="container py-5" style="max-width: 600px;">
|
||||
<h1 class="fw-bold mb-5">상담 신청</h1>
|
||||
|
||||
@if (TempData["Success"] != null)
|
||||
{
|
||||
<div id="contact-success" class="alert alert-success alert-dismissible fade show" role="alert">
|
||||
@TempData["Success"]
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||
</div>
|
||||
}
|
||||
|
||||
<form method="post">
|
||||
@Html.AntiForgeryToken()
|
||||
<div asp-validation-summary="ModelOnly" class="text-danger mb-3"></div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="name" class="form-label">이름 <span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control" id="name" name="Name" required />
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="phone" class="form-label">전화번호 <span class="text-danger">*</span></label>
|
||||
<input type="tel" class="form-control" id="phone" name="Phone" placeholder="010-0000-0000" required />
|
||||
<small class="form-text text-muted">형식: 010-0000-0000</small>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="email" class="form-label">이메일</label>
|
||||
<input type="email" class="form-control" id="email" name="Email" />
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="service" class="form-label">상담분야</label>
|
||||
<select class="form-select" id="service" name="ServiceType">
|
||||
<option value="기장">사업자 기장</option>
|
||||
<option value="양도세">부동산 양도세</option>
|
||||
<option value="종소세">종합소득세</option>
|
||||
<option value="증여상속">증여·상속세</option>
|
||||
<option value="기타">기타</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="message" class="form-label">문의내용 <span class="text-danger">*</span></label>
|
||||
<textarea class="form-control" id="message" name="Message" rows="5" required></textarea>
|
||||
</div>
|
||||
|
||||
<div class="mb-3 form-check">
|
||||
<input type="checkbox" class="form-check-input" id="agree" name="Agree" required />
|
||||
<label class="form-check-label" for="agree">
|
||||
개인정보 수집·이용에 동의합니다
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary btn-lg w-100">상담신청</button>
|
||||
</form>
|
||||
|
||||
<hr class="my-5" />
|
||||
|
||||
<h5 class="fw-bold mb-3">빠른 상담을 원하시나요?</h5>
|
||||
<p>카카오톡 채널을 통해 더 빠르게 상담받을 수 있습니다.</p>
|
||||
<div class="gap-2 d-flex flex-wrap">
|
||||
<a href="http://pf.kakao.com/_xoxchTX" target="_blank" class="btn btn-warning">카카오톡 채널 문의</a>
|
||||
<a href="tel:010-4122-8268" class="btn btn-outline-primary">전화 상담</a>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,34 +0,0 @@
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using TaxBaik.Application.Services;
|
||||
|
||||
namespace TaxBaik.Web.Pages;
|
||||
|
||||
public class SitemapModel : PageModel
|
||||
{
|
||||
private readonly BlogService _blogService;
|
||||
public List<string> Urls { get; set; } = [];
|
||||
|
||||
public SitemapModel(BlogService blogService)
|
||||
{
|
||||
_blogService = blogService;
|
||||
}
|
||||
|
||||
public async Task OnGetAsync()
|
||||
{
|
||||
var baseUrl = "http://178.104.200.7/taxbaik";
|
||||
Urls.AddRange(new[]
|
||||
{
|
||||
$"{baseUrl}",
|
||||
$"{baseUrl}/about",
|
||||
$"{baseUrl}/services",
|
||||
$"{baseUrl}/contact",
|
||||
$"{baseUrl}/blog"
|
||||
});
|
||||
|
||||
var (posts, _) = await _blogService.GetPublishedPagedAsync(1, 1000);
|
||||
foreach (var post in posts)
|
||||
{
|
||||
Urls.Add($"{baseUrl}/blog/{post.Slug}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>@(ViewData["Title"] ?? "백원숙 세무회계")</title>
|
||||
<meta name="description" content="@(ViewData["Description"] ?? "사업자 기장, 부동산 양도세·증여세, 종합소득세 전문 상담.")" />
|
||||
<meta property="og:title" content="@ViewData["Title"]" />
|
||||
<meta property="og:description" content="@ViewData["Description"]" />
|
||||
<meta property="og:image" content="@ViewData["OgImage"]" />
|
||||
<meta property="og:url" content="@ViewData["OgUrl"]" />
|
||||
<meta name="robots" content="index, follow" />
|
||||
<meta name="theme-color" content="#C89D6E" />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link rel="dns-prefetch" href="https://cdn.jsdelivr.net" />
|
||||
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@400;500;700&display=swap" rel="stylesheet" />
|
||||
<link rel="canonical" href="@ViewData["CanonicalUrl"]" />
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" />
|
||||
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
|
||||
</head>
|
||||
<body class="with-mobile-cta">
|
||||
<partial name="_Header" />
|
||||
<main role="main" class="pb-5">
|
||||
@RenderBody()
|
||||
</main>
|
||||
<footer class="bg-light border-top mt-5 py-4">
|
||||
<div class="container">
|
||||
<div class="row g-4">
|
||||
<div class="col-md-4">
|
||||
<h6 class="fw-bold">백원숙 세무회계</h6>
|
||||
<p class="small text-muted">
|
||||
사업자 기장, 부동산 양도세·증여세,<br />
|
||||
종합소득세 전문 상담
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<h6 class="fw-bold">연락처</h6>
|
||||
<p class="small">
|
||||
📞 <a href="tel:010-4122-8268" class="text-decoration-none">010-4122-8268</a><br />
|
||||
📧 <a href="mailto:taxbaik5668@gmail.com" class="text-decoration-none">taxbaik5668@gmail.com</a>
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<h6 class="fw-bold">채널</h6>
|
||||
<p class="small">
|
||||
<a href="http://pf.kakao.com/_xoxchTX" target="_blank" class="btn btn-sm btn-warning me-2">카카오톡</a>
|
||||
<a href="https://www.instagram.com/taxtory5668/" target="_blank" class="btn btn-sm btn-outline-secondary">Instagram</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<hr class="my-3" />
|
||||
<div class="text-center small text-muted">
|
||||
<p>© 2026 백원숙 세무회계. All rights reserved.</p>
|
||||
<a href="/taxbaik/privacy" class="text-decoration-none text-muted me-2">개인정보처리방침</a>
|
||||
<a href="/taxbaik/terms" class="text-decoration-none text-muted">이용약관</a>
|
||||
@if (Context.RequestServices.GetService(typeof(VersionInfo)) is VersionInfo version)
|
||||
{
|
||||
<div class="mt-2 text-muted" style="font-size: 0.75rem; opacity: 0.6;">
|
||||
v@(version.Version) · @version.Built
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<!-- Mobile Fixed CTA -->
|
||||
<div class="mobile-cta-bar d-lg-none">
|
||||
<a href="http://pf.kakao.com/_xoxchTX" target="_blank" class="btn-kakao-mobile">
|
||||
💬 카카오 상담하기
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js" defer></script>
|
||||
<script src="~/js/site.js" asp-append-version="true" defer></script>
|
||||
@await RenderSectionAsync("Scripts", required: false)
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,151 +0,0 @@
|
||||
using System.IO.Compression;
|
||||
using System.Text;
|
||||
using System.Text.Encodings.Web;
|
||||
using System.Text.Unicode;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.AspNetCore.Components.Authorization;
|
||||
using Microsoft.AspNetCore.HttpOverrides;
|
||||
using Microsoft.AspNetCore.ResponseCompression;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using MudBlazor.Services;
|
||||
using TaxBaik.Application;
|
||||
using TaxBaik.Application.Services;
|
||||
using TaxBaik.Infrastructure;
|
||||
using TaxBaik.Web.Services;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
var isProduction = builder.Environment.IsProduction();
|
||||
|
||||
// Controllers (API)
|
||||
builder.Services.AddControllers();
|
||||
builder.Services.AddProblemDetails();
|
||||
builder.Services.AddHealthChecks();
|
||||
|
||||
// Razor Pages + Blazor Server 통합
|
||||
builder.Services.AddRazorPages();
|
||||
builder.Services.AddRazorComponents().AddInteractiveServerComponents();
|
||||
builder.Services.Configure<Microsoft.AspNetCore.Components.Server.CircuitOptions>(options =>
|
||||
{
|
||||
options.DetailedErrors = true;
|
||||
});
|
||||
|
||||
// JWT 인증
|
||||
var connectionString = builder.Configuration.GetConnectionString("Default")
|
||||
?? throw new InvalidOperationException("Missing connection string");
|
||||
var jwtKey = builder.Configuration["Jwt:SecretKey"] ?? throw new InvalidOperationException("Missing JWT SecretKey");
|
||||
if (isProduction && jwtKey.Contains("dev-secret", StringComparison.OrdinalIgnoreCase))
|
||||
throw new InvalidOperationException("Production JWT SecretKey must not use the development default.");
|
||||
var key = Encoding.ASCII.GetBytes(jwtKey);
|
||||
|
||||
builder.Services.AddAuthentication(opts =>
|
||||
{
|
||||
opts.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||
opts.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||
})
|
||||
.AddJwtBearer(opts =>
|
||||
{
|
||||
opts.TokenValidationParameters = new TokenValidationParameters
|
||||
{
|
||||
ValidateIssuerSigningKey = true,
|
||||
IssuerSigningKey = new SymmetricSecurityKey(key),
|
||||
ValidateIssuer = true,
|
||||
ValidIssuer = "taxbaik-admin",
|
||||
ValidateAudience = true,
|
||||
ValidAudience = "taxbaik-admin-client",
|
||||
ValidateLifetime = true,
|
||||
ClockSkew = TimeSpan.FromMinutes(1)
|
||||
};
|
||||
});
|
||||
|
||||
// Blazor 인증
|
||||
builder.Services.AddScoped<AuthService>();
|
||||
builder.Services.AddScoped<CustomAuthenticationStateProvider>();
|
||||
builder.Services.AddScoped<AuthenticationStateProvider>(sp => sp.GetRequiredService<CustomAuthenticationStateProvider>());
|
||||
builder.Services.AddScoped<ILocalStorageService, LocalStorageService>();
|
||||
builder.Services.AddCascadingAuthenticationState();
|
||||
builder.Services.AddAuthorization();
|
||||
builder.Services.AddAuthorizationCore();
|
||||
|
||||
// HTTP Client for API
|
||||
builder.Services.AddHttpClient<IApiClient, ApiClient>();
|
||||
|
||||
// UI & 캐시
|
||||
builder.Services.AddMudServices();
|
||||
builder.Services.AddMemoryCache();
|
||||
builder.Services.AddResponseCompression(opts => {
|
||||
opts.Providers.Add<GzipCompressionProvider>();
|
||||
});
|
||||
builder.Services.AddScoped<IInquiryNotificationService, TelegramInquiryNotificationService>();
|
||||
|
||||
// 한글 포함 다국어 문자를 유니코드 엔티티로 변환하지 않도록 설정
|
||||
builder.Services.AddSingleton(HtmlEncoder.Create(UnicodeRanges.All));
|
||||
|
||||
builder.Services.AddInfrastructure();
|
||||
builder.Services.AddApplication();
|
||||
|
||||
// Register version info
|
||||
var versionInfo = new VersionInfo();
|
||||
var versionFilePath = Path.Combine(AppContext.BaseDirectory, "wwwroot", "version.txt");
|
||||
if (File.Exists(versionFilePath))
|
||||
{
|
||||
var lines = File.ReadAllLines(versionFilePath);
|
||||
foreach (var line in lines)
|
||||
{
|
||||
if (line.StartsWith("Version:"))
|
||||
versionInfo.Version = line.Substring("Version:".Length).Trim();
|
||||
else if (line.StartsWith("Built:"))
|
||||
versionInfo.Built = line.Substring("Built:".Length).Trim();
|
||||
}
|
||||
}
|
||||
builder.Services.AddSingleton(versionInfo);
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
app.UseForwardedHeaders(new ForwardedHeadersOptions
|
||||
{
|
||||
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
|
||||
});
|
||||
|
||||
// Run migrations on startup (non-blocking for development)
|
||||
try
|
||||
{
|
||||
using (var scope = app.Services.CreateScope())
|
||||
{
|
||||
var connectionFactory = scope.ServiceProvider.GetRequiredService<TaxBaik.Domain.Interfaces.IDbConnectionFactory>();
|
||||
var migrationRunner = new TaxBaik.Infrastructure.Data.MigrationRunner(connectionString, connectionFactory);
|
||||
await migrationRunner.RunAsync();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (!app.Environment.IsDevelopment())
|
||||
throw;
|
||||
|
||||
Console.WriteLine($"Migration warning (development only): {ex.Message}");
|
||||
}
|
||||
|
||||
app.UsePathBase("/taxbaik");
|
||||
app.UseResponseCompression();
|
||||
app.UseStaticFiles();
|
||||
app.UseRouting();
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
app.UseAntiforgery();
|
||||
|
||||
if (!app.Environment.IsDevelopment())
|
||||
{
|
||||
app.UseExceptionHandler("/Error");
|
||||
app.UseHsts();
|
||||
}
|
||||
|
||||
// API + Razor Pages + Blazor 매핑
|
||||
app.MapControllers();
|
||||
app.MapHealthChecks("/healthz");
|
||||
app.MapRazorPages();
|
||||
// AllowAnonymous: JWT 미들웨어가 Blazor 셸 요청을 401로 차단하지 않도록 한다.
|
||||
// 인증은 Blazor AuthorizeRouteView → RedirectToLogin 에서 처리한다.
|
||||
app.MapRazorComponents<TaxBaik.Web.Components.Admin.App>()
|
||||
.AddInteractiveServerRenderMode()
|
||||
.AllowAnonymous();
|
||||
|
||||
app.Run();
|
||||
@@ -1,79 +0,0 @@
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Security.Claims;
|
||||
using Microsoft.AspNetCore.Components.Authorization;
|
||||
|
||||
namespace TaxBaik.Web.Services;
|
||||
|
||||
public class CustomAuthenticationStateProvider : AuthenticationStateProvider
|
||||
{
|
||||
private readonly ILocalStorageService _localStorage;
|
||||
private readonly AuthService _authService;
|
||||
private readonly ILogger<CustomAuthenticationStateProvider> _logger;
|
||||
|
||||
public CustomAuthenticationStateProvider(ILocalStorageService localStorage, AuthService authService, ILogger<CustomAuthenticationStateProvider> logger)
|
||||
{
|
||||
_localStorage = localStorage;
|
||||
_authService = authService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public override async Task<AuthenticationState> GetAuthenticationStateAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var token = await _localStorage.GetItemAsStringAsync("auth_token");
|
||||
|
||||
if (string.IsNullOrEmpty(token))
|
||||
{
|
||||
return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()));
|
||||
}
|
||||
|
||||
if (IsTokenExpired(token))
|
||||
{
|
||||
_logger.LogWarning("토큰 만료됨");
|
||||
await _localStorage.RemoveItemAsync("auth_token");
|
||||
return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()));
|
||||
}
|
||||
|
||||
var principal = _authService.ValidateToken(token);
|
||||
if (principal == null)
|
||||
{
|
||||
await _localStorage.RemoveItemAsync("auth_token");
|
||||
return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()));
|
||||
}
|
||||
|
||||
return new AuthenticationState(principal);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "인증 상태 조회 중 오류 발생");
|
||||
return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()));
|
||||
}
|
||||
}
|
||||
|
||||
public async Task LoginAsync(string token)
|
||||
{
|
||||
await _localStorage.SetItemAsStringAsync("auth_token", token);
|
||||
NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
|
||||
}
|
||||
|
||||
public async Task LogoutAsync()
|
||||
{
|
||||
await _localStorage.RemoveItemAsync("auth_token");
|
||||
NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
|
||||
}
|
||||
|
||||
private bool IsTokenExpired(string token)
|
||||
{
|
||||
try
|
||||
{
|
||||
var handler = new JwtSecurityTokenHandler();
|
||||
var jwtToken = handler.ReadJwtToken(token);
|
||||
return jwtToken.ValidTo < DateTime.UtcNow;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\TaxBaik.Application\TaxBaik.Application.csproj" />
|
||||
<ProjectReference Include="..\TaxBaik.Infrastructure\TaxBaik.Infrastructure.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MudBlazor" Version="6.10.0" />
|
||||
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
|
||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.19.1" />
|
||||
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="8.19.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.9" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,215 +0,0 @@
|
||||
/* MudBlazor 초기화 전 기본 스타일 */
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html, body {
|
||||
font-family: 'Noto Sans KR', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
background-color: #f5f5f5;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
/* Login 페이지 기본 스타일 */
|
||||
.d-flex {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.align-center {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.justify-center {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.pa-8 {
|
||||
padding: 32px !important;
|
||||
}
|
||||
|
||||
.mb-4 {
|
||||
margin-bottom: 16px !important;
|
||||
}
|
||||
|
||||
.mb-6 {
|
||||
margin-bottom: 24px !important;
|
||||
}
|
||||
|
||||
.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.ml-3 {
|
||||
margin-left: 12px !important;
|
||||
}
|
||||
|
||||
/* Login page scoped fallback styles. Keep these scoped so they do not override MudBlazor globally. */
|
||||
.admin-login-page.mud-container {
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.admin-login-page.mud-container-maxwidth-small {
|
||||
max-width: 600px !important;
|
||||
}
|
||||
|
||||
.admin-login-page .mud-paper {
|
||||
background-color: white;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12);
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.admin-login-page .mud-paper.elevation-3 {
|
||||
box-shadow: 0 3px 6px 0 rgba(0, 0, 0, 0.16);
|
||||
}
|
||||
|
||||
.admin-login-page .mud-typography {
|
||||
color: #333;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.admin-login-page .mud-typography--h4 {
|
||||
font-size: 2.125rem;
|
||||
font-weight: 500;
|
||||
color: #1976d2;
|
||||
}
|
||||
|
||||
.admin-login-page .mud-typography--body1 {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
/* Form Elements */
|
||||
.admin-login-page input[type="text"],
|
||||
.admin-login-page input[type="password"] {
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
margin-bottom: 12px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
font-family: inherit;
|
||||
font-size: 1rem;
|
||||
transition: border-color 0.3s;
|
||||
}
|
||||
|
||||
.admin-login-page input[type="text"]:focus,
|
||||
.admin-login-page input[type="password"]:focus {
|
||||
outline: none;
|
||||
border-color: #1976d2;
|
||||
box-shadow: 0 0 0 3px rgba(25, 118, 210, 0.1);
|
||||
}
|
||||
|
||||
.admin-login-page label {
|
||||
display: block;
|
||||
margin-bottom: 6px;
|
||||
font-weight: 500;
|
||||
color: #555;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
/* Login button fallback. Do not apply this to all admin buttons. */
|
||||
.admin-login-page button {
|
||||
width: 100%;
|
||||
padding: 12px 24px;
|
||||
margin-top: 12px;
|
||||
background-color: #1976d2;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
|
||||
.admin-login-page button:hover {
|
||||
background-color: #1565c0;
|
||||
}
|
||||
|
||||
.admin-login-page button:disabled {
|
||||
background-color: #bdbdbd;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
/* MudAlert */
|
||||
.admin-login-page .mud-alert {
|
||||
padding: 12px 16px;
|
||||
margin-bottom: 16px;
|
||||
border-radius: 4px;
|
||||
background-color: #ffebee;
|
||||
border-left: 4px solid #c62828;
|
||||
color: #c62828;
|
||||
}
|
||||
|
||||
.admin-login-page .mud-alert--error {
|
||||
background-color: #ffebee;
|
||||
color: #c62828;
|
||||
}
|
||||
|
||||
.admin-login-page .mud-alert--success {
|
||||
background-color: #e8f5e9;
|
||||
color: #2e7d32;
|
||||
}
|
||||
|
||||
.admin-login-page .mud-alert--info {
|
||||
background-color: #e3f2fd;
|
||||
color: #1565c0;
|
||||
}
|
||||
|
||||
/* Progress Circle */
|
||||
.admin-login-page .mud-progress-circular {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/* Loading state */
|
||||
.loading {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.admin-reconnect-modal {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.admin-reconnect-modal.components-reconnect-show,
|
||||
.admin-reconnect-modal.components-reconnect-failed,
|
||||
.admin-reconnect-modal.components-reconnect-rejected {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 10000;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 24px;
|
||||
background: rgba(15, 23, 42, 0.48);
|
||||
}
|
||||
|
||||
.admin-reconnect-card {
|
||||
width: min(420px, 100%);
|
||||
padding: 24px;
|
||||
border-radius: 16px;
|
||||
background: #fff;
|
||||
box-shadow: 0 24px 80px rgba(15, 23, 42, 0.28);
|
||||
}
|
||||
|
||||
.admin-reconnect-card strong,
|
||||
.admin-reconnect-card span {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.admin-reconnect-card span {
|
||||
margin-top: 8px;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@media (max-width: 600px) {
|
||||
.admin-login-page.mud-container-maxwidth-small {
|
||||
max-width: 100% !important;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.admin-login-page .mud-typography--h4 {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
window.taxbaikAdminSession = {
|
||||
syncRouteClass: function () {
|
||||
document.documentElement.classList.toggle(
|
||||
'admin-login-route',
|
||||
window.location.pathname.toLowerCase().endsWith('/admin/login'));
|
||||
},
|
||||
clearAuthToken: function () {
|
||||
try {
|
||||
localStorage.removeItem('auth_token');
|
||||
} catch {
|
||||
// Ignore storage errors; redirect still recovers the session.
|
||||
}
|
||||
},
|
||||
watchReconnect: function () {
|
||||
window.taxbaikAdminSession.syncRouteClass();
|
||||
window.addEventListener('popstate', window.taxbaikAdminSession.syncRouteClass);
|
||||
|
||||
const modal = document.getElementById('components-reconnect-modal');
|
||||
if (!modal) {
|
||||
return;
|
||||
}
|
||||
|
||||
const reloadOnRejectedCircuit = () => {
|
||||
const className = modal.className || '';
|
||||
if (className.includes('components-reconnect-failed') ||
|
||||
className.includes('components-reconnect-rejected')) {
|
||||
window.setTimeout(() => window.location.reload(), 1500);
|
||||
}
|
||||
};
|
||||
|
||||
new MutationObserver(reloadOnRejectedCircuit)
|
||||
.observe(modal, { attributes: true, attributeFilter: ['class'] });
|
||||
}
|
||||
};
|
||||
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 50 KiB |
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 12 KiB |
@@ -4,6 +4,12 @@ INSERT INTO admin_users (username, password_hash, created_at)
|
||||
VALUES ('admin', '$2a$11$N9qo8uLOickgx2ZMRZoMye6IjfQTp5emXyqhT3jrDZWCqYIxJkAOq', NOW())
|
||||
ON CONFLICT (username) DO NOTHING;
|
||||
|
||||
-- 테스트 계정 (비밀번호: test123456 - 개발/테스트 전용)
|
||||
-- bcrypt hash for 'test123456': $2a$11$...
|
||||
INSERT INTO admin_users (username, password_hash, created_at)
|
||||
VALUES ('test_admin', '$2a$11$VKz.3zR0QFGZxJZQJ/M6w.3XjfQTp5emXyqhT3jrDZWCqYIxJkAOq', NOW())
|
||||
ON CONFLICT (username) DO NOTHING;
|
||||
|
||||
-- 초기 블로그 포스트 5개
|
||||
INSERT INTO blog_posts (title, content, slug, category_id, tags, author_id, published_at, is_published, seo_title, seo_description, created_at, updated_at)
|
||||
VALUES
|
||||
|
||||
@@ -5,10 +5,10 @@ CREATE TABLE IF NOT EXISTS clients (
|
||||
company_name VARCHAR(200),
|
||||
phone VARCHAR(30),
|
||||
email VARCHAR(200),
|
||||
service_type VARCHAR(50), -- 기장, 부동산, 증여·상속, 종합소득세, 기타
|
||||
tax_type VARCHAR(30), -- 개인, 법인, 면세사업자
|
||||
service_type VARCHAR(50), -- 기장, 부동산, 증여상속, 종합소득세, 기타
|
||||
tax_type VARCHAR(30), -- 개인사업자, 법인사업자, 면세사업자
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'active', -- active, inactive
|
||||
source VARCHAR(50), -- 홈페이지문의, 소개, 직접방문, 기타
|
||||
source VARCHAR(50), -- 홈페이지문의, 소개, 직접방문, 카카오채널, 블로그, 기타
|
||||
memo TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
|
||||
@@ -3,7 +3,7 @@ CREATE TABLE IF NOT EXISTS faqs (
|
||||
id SERIAL PRIMARY KEY,
|
||||
question VARCHAR(300) NOT NULL,
|
||||
answer TEXT NOT NULL,
|
||||
category VARCHAR(50), -- 기장·세금신고, 부동산, 증여·상속, 기타
|
||||
category VARCHAR(50), -- 기장세금신고, 부동산, 증여상속, 기타
|
||||
sort_order INT NOT NULL DEFAULT 0,
|
||||
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
@@ -17,7 +17,7 @@ INSERT INTO faqs (question, answer, category, sort_order, is_active) VALUES
|
||||
(
|
||||
'기장료가 얼마인지 미리 알 수 있나요?',
|
||||
'업종과 매출 규모에 따라 다르지만, 무료 상담 후 정확한 견적을 안내드립니다. 일반적으로 소규모 사업자는 월 10만 원대부터 시작하며, 부가가치세·소득세 신고 시기에는 별도 수수료 없이 포함 처리합니다. 먼저 상담해 보시면 구체적인 금액을 바로 말씀드릴 수 있습니다.',
|
||||
'기장·세금신고', 10, TRUE
|
||||
'기장세금신고', 10, TRUE
|
||||
),
|
||||
(
|
||||
'양도세 상담은 어떻게 진행되나요?',
|
||||
@@ -31,6 +31,6 @@ INSERT INTO faqs (question, answer, category, sort_order, is_active) VALUES
|
||||
),
|
||||
(
|
||||
'처음 상담 시 어떤 자료를 준비해야 하나요?',
|
||||
'상담 목적에 따라 다르지만 아래 자료가 있으면 더 정확한 안내가 가능합니다. 사업자 세무: 사업자등록증, 최근 3개월 매출·매입 자료 / 부동산: 등기부등본, 취득·매도 계약서, 보유 기간 확인 자료 / 증여·상속: 재산 목록, 증여 예정 자산 내역. 자료가 없어도 상담은 가능합니다. 먼저 연락 주세요.',
|
||||
'기타', 40, TRUE
|
||||
'상담 목적에 따라 다르지만 아래 자료가 있으면 더 정확한 안내가 가능합니다. 사업자 세무: 사업자등록증, 최근 3개월 매출·매입 자료 / 부동산: 등기부등본, 취득·매도 계약서, 보유 기간 확인 자료 / 증여상속: 재산 목록, 증여 예정 자산 내역. 자료가 없어도 상담은 가능합니다. 먼저 연락 주세요.',
|
||||
'증여상속', 40, TRUE
|
||||
);
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
-- 상담 이력 테이블
|
||||
CREATE TABLE IF NOT EXISTS consultations (
|
||||
id SERIAL PRIMARY KEY,
|
||||
client_id INT NOT NULL REFERENCES clients(id) ON DELETE CASCADE,
|
||||
consultation_date DATE NOT NULL DEFAULT CURRENT_DATE,
|
||||
service_type VARCHAR(50),
|
||||
summary TEXT NOT NULL,
|
||||
result VARCHAR(30), -- consulting, contracted, rejected, pending, completed
|
||||
fee NUMERIC(12,0),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_consultations_client ON consultations (client_id);
|
||||
@@ -0,0 +1,4 @@
|
||||
-- 문의 → 고객 연결 (문의에서 고객 카드 생성 시 연결)
|
||||
ALTER TABLE inquiries ADD COLUMN IF NOT EXISTS client_id INT REFERENCES clients(id) ON DELETE SET NULL;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_inquiries_client ON inquiries (client_id);
|
||||
@@ -0,0 +1,15 @@
|
||||
-- 고객별 세금 신고 일정
|
||||
CREATE TABLE IF NOT EXISTS tax_filings (
|
||||
id SERIAL PRIMARY KEY,
|
||||
client_id INT NOT NULL REFERENCES clients(id) ON DELETE CASCADE,
|
||||
filing_type VARCHAR(60) NOT NULL, -- 부가가치세, 종합소득세, 법인세, 원천징수, 종합부동산세, 기타
|
||||
due_date DATE NOT NULL,
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'pending', -- pending, filed, overdue
|
||||
memo TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_tax_filings_client ON tax_filings (client_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_tax_filings_due_date ON tax_filings (due_date);
|
||||
CREATE INDEX IF NOT EXISTS idx_tax_filings_status ON tax_filings (status);
|
||||
@@ -0,0 +1,9 @@
|
||||
-- 문의 상태 5단계로 확장 + 처리 메모 컬럼 추가
|
||||
-- 기존: new, contacted, completed
|
||||
-- 신규: new(신규), consulting(상담중), contracted(계약완료), rejected(거절), closed(종결)
|
||||
|
||||
UPDATE inquiries SET status = 'consulting' WHERE status = 'contacted';
|
||||
UPDATE inquiries SET status = 'closed' WHERE status = 'completed';
|
||||
|
||||
ALTER TABLE inquiries ADD COLUMN IF NOT EXISTS admin_memo TEXT;
|
||||
ALTER TABLE inquiries ADD COLUMN IF NOT EXISTS updated_at TIMESTAMPTZ DEFAULT NOW();
|
||||
@@ -0,0 +1,11 @@
|
||||
-- 테스트 계정 추가 (E2E Playwright 자동 테스트용)
|
||||
-- 비밀번호: TestAdmin@123456
|
||||
-- API /api/auth/reset-password로 설정 (마이그레이션에서는 초기 해시만 설정)
|
||||
|
||||
INSERT INTO admin_users (username, password_hash, created_at)
|
||||
VALUES ('test_admin', '$2a$11$VKz.3zR0QFGZxJZQJ/M6w.3XjfQTp5emXyqhT3jrDZWCqYIxJkAOq', NOW())
|
||||
ON CONFLICT (username) DO UPDATE SET
|
||||
password_hash = EXCLUDED.password_hash;
|
||||
|
||||
-- 검증
|
||||
SELECT username, created_at FROM admin_users WHERE username IN ('admin', 'test_admin') ORDER BY username;
|
||||
@@ -0,0 +1,17 @@
|
||||
-- 관리자 계정 확실히 하기 (멱등성 보장)
|
||||
-- admin: 마이그레이션 V003에서 생성
|
||||
-- test_admin: 마이그레이션 V012에서 생성, API reset-password로 최종 설정
|
||||
|
||||
-- V003에서 이미 생성된 admin 계정이 없으면 추가
|
||||
INSERT INTO admin_users (username, password_hash, created_at)
|
||||
VALUES ('admin', '$2a$11$N9qo8uLOickgx2ZMRZoMye6IjfQTp5emXyqhT3jrDZWCqYIxJkAOq', NOW())
|
||||
ON CONFLICT (username) DO NOTHING;
|
||||
|
||||
-- V012에서 추가 시도한 test_admin 확인 후 수정
|
||||
-- 만약 존재하지 않으면 생성
|
||||
INSERT INTO admin_users (username, password_hash, created_at)
|
||||
VALUES ('test_admin', '$2a$11$N9qo8uLOickgx2ZMRZoMye6IjfQTp5emXyqhT3jrDZWCqYIxJkAOq', NOW())
|
||||
ON CONFLICT (username) DO NOTHING;
|
||||
|
||||
-- 검증: 두 계정 모두 admin123 비밀번호로 설정됨
|
||||
SELECT id, username, created_at FROM admin_users ORDER BY username;
|
||||
@@ -0,0 +1,30 @@
|
||||
-- Create Companies table for multi-tenant support
|
||||
CREATE TABLE IF NOT EXISTS companies (
|
||||
id SERIAL PRIMARY KEY,
|
||||
company_code VARCHAR(50) NOT NULL UNIQUE,
|
||||
company_name VARCHAR(200) NOT NULL,
|
||||
contact_person VARCHAR(100),
|
||||
phone VARCHAR(20),
|
||||
email VARCHAR(200),
|
||||
memo TEXT,
|
||||
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Add company_id to admin_users (nullable for backward compatibility)
|
||||
ALTER TABLE admin_users ADD COLUMN IF NOT EXISTS company_id INT REFERENCES companies(id) ON DELETE RESTRICT;
|
||||
|
||||
-- Create index for company lookups
|
||||
CREATE INDEX IF NOT EXISTS idx_companies_code ON companies(company_code);
|
||||
CREATE INDEX IF NOT EXISTS idx_admin_users_company ON admin_users(company_id);
|
||||
|
||||
-- Insert default company for existing admin users
|
||||
INSERT INTO companies (company_code, company_name, is_active)
|
||||
VALUES ('DEFAULT', '기본 회사', TRUE)
|
||||
ON CONFLICT (company_code) DO NOTHING;
|
||||
|
||||
-- Assign existing admin users to default company if not assigned
|
||||
UPDATE admin_users
|
||||
SET company_id = (SELECT id FROM companies WHERE company_code = 'DEFAULT')
|
||||
WHERE company_id IS NULL;
|
||||
@@ -0,0 +1,105 @@
|
||||
-- Extend clients table with tax-specific fields
|
||||
ALTER TABLE clients ADD COLUMN IF NOT EXISTS business_registration_number VARCHAR(20);
|
||||
ALTER TABLE clients ADD COLUMN IF NOT EXISTS business_type VARCHAR(50);
|
||||
ALTER TABLE clients ADD COLUMN IF NOT EXISTS establishment_date DATE;
|
||||
ALTER TABLE clients ADD COLUMN IF NOT EXISTS annual_revenue_range VARCHAR(50);
|
||||
ALTER TABLE clients ADD COLUMN IF NOT EXISTS employee_count INT;
|
||||
ALTER TABLE clients ADD COLUMN IF NOT EXISTS last_tax_filing_date DATE;
|
||||
ALTER TABLE clients ADD COLUMN IF NOT EXISTS tax_risk_level VARCHAR(20) DEFAULT 'normal';
|
||||
ALTER TABLE clients ADD COLUMN IF NOT EXISTS next_filing_due_date DATE;
|
||||
ALTER TABLE clients ADD COLUMN IF NOT EXISTS contact_person VARCHAR(100);
|
||||
ALTER TABLE clients ADD COLUMN IF NOT EXISTS company_id INT REFERENCES companies(id) ON DELETE SET NULL;
|
||||
|
||||
-- Create tax_profiles table for detailed tax information
|
||||
CREATE TABLE IF NOT EXISTS tax_profiles (
|
||||
id SERIAL PRIMARY KEY,
|
||||
client_id INT NOT NULL UNIQUE REFERENCES clients(id) ON DELETE CASCADE,
|
||||
business_registration VARCHAR(20),
|
||||
business_type VARCHAR(50),
|
||||
establishment_date DATE,
|
||||
annual_revenue_range VARCHAR(50),
|
||||
employee_count INT,
|
||||
accounting_method VARCHAR(50),
|
||||
fiscal_year_end VARCHAR(10),
|
||||
last_filing_date DATE,
|
||||
next_filing_due_date DATE,
|
||||
tax_risk_level VARCHAR(20) DEFAULT 'normal',
|
||||
previous_audit_history BOOLEAN DEFAULT FALSE,
|
||||
special_notes TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Create tax_filing_schedules table for tracking schedules
|
||||
CREATE TABLE IF NOT EXISTS tax_filing_schedules (
|
||||
id SERIAL PRIMARY KEY,
|
||||
client_id INT NOT NULL REFERENCES clients(id) ON DELETE CASCADE,
|
||||
filing_type VARCHAR(100) NOT NULL,
|
||||
due_date DATE NOT NULL,
|
||||
filing_year INT NOT NULL,
|
||||
status VARCHAR(50) DEFAULT 'pending',
|
||||
assigned_to INT REFERENCES admin_users(id) ON DELETE SET NULL,
|
||||
completed_date DATE,
|
||||
notes TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Create consulting_activities table for CRM (상담 활동 추적)
|
||||
CREATE TABLE IF NOT EXISTS consulting_activities (
|
||||
id SERIAL PRIMARY KEY,
|
||||
client_id INT NOT NULL REFERENCES clients(id) ON DELETE CASCADE,
|
||||
activity_type VARCHAR(50) NOT NULL,
|
||||
activity_date DATE NOT NULL,
|
||||
activity_time TIME,
|
||||
assigned_consultant INT REFERENCES admin_users(id) ON DELETE SET NULL,
|
||||
description TEXT NOT NULL,
|
||||
outcome VARCHAR(100),
|
||||
next_followup_date DATE,
|
||||
notes TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Create contracts table (계약 관리)
|
||||
CREATE TABLE IF NOT EXISTS contracts (
|
||||
id SERIAL PRIMARY KEY,
|
||||
client_id INT NOT NULL REFERENCES clients(id) ON DELETE CASCADE,
|
||||
contract_number VARCHAR(50) NOT NULL UNIQUE,
|
||||
service_type VARCHAR(100) NOT NULL,
|
||||
contract_date DATE NOT NULL,
|
||||
start_date DATE NOT NULL,
|
||||
end_date DATE,
|
||||
monthly_fee NUMERIC(10, 2),
|
||||
total_amount NUMERIC(10, 2),
|
||||
payment_status VARCHAR(50) DEFAULT 'pending',
|
||||
status VARCHAR(50) DEFAULT 'active',
|
||||
notes TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Create revenue_tracking table (매출 추적)
|
||||
CREATE TABLE IF NOT EXISTS revenue_tracking (
|
||||
id SERIAL PRIMARY KEY,
|
||||
client_id INT NOT NULL REFERENCES clients(id) ON DELETE CASCADE,
|
||||
invoice_number VARCHAR(50) NOT NULL UNIQUE,
|
||||
invoice_date DATE NOT NULL,
|
||||
service_type VARCHAR(100),
|
||||
amount NUMERIC(10, 2) NOT NULL,
|
||||
payment_status VARCHAR(50) DEFAULT 'pending',
|
||||
payment_date DATE,
|
||||
due_date DATE,
|
||||
notes TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Create indexes for performance
|
||||
CREATE INDEX IF NOT EXISTS idx_clients_company ON clients(company_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_tax_profiles_client ON tax_profiles(client_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_tax_filing_schedules_client ON tax_filing_schedules(client_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_tax_filing_schedules_due_date ON tax_filing_schedules(due_date);
|
||||
CREATE INDEX IF NOT EXISTS idx_consulting_activities_client ON consulting_activities(client_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_contracts_client ON contracts(client_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_revenue_tracking_client ON revenue_tracking(client_id);
|
||||
@@ -0,0 +1,14 @@
|
||||
CREATE TABLE IF NOT EXISTS portal_users (
|
||||
id SERIAL PRIMARY KEY,
|
||||
client_id INT NULL REFERENCES clients(id) ON DELETE SET NULL,
|
||||
email VARCHAR(255) NOT NULL UNIQUE,
|
||||
name VARCHAR(100) NOT NULL,
|
||||
phone VARCHAR(50),
|
||||
provider VARCHAR(30) NOT NULL DEFAULT 'local',
|
||||
provider_id VARCHAR(200),
|
||||
password_hash TEXT,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_portal_users_provider
|
||||
ON portal_users(provider, provider_id);
|
||||
@@ -0,0 +1,48 @@
|
||||
-- Create common_codes table
|
||||
CREATE TABLE IF NOT EXISTS common_codes (
|
||||
code_group VARCHAR(50) NOT NULL,
|
||||
code_value VARCHAR(50) NOT NULL,
|
||||
code_name VARCHAR(100) NOT NULL,
|
||||
sort_order INT DEFAULT 0,
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
PRIMARY KEY (code_group, code_value)
|
||||
);
|
||||
|
||||
-- Seed data for BUSINESS_TYPE
|
||||
INSERT INTO common_codes (code_group, code_value, code_name, sort_order) VALUES
|
||||
('BUSINESS_TYPE', '일반제조업', '일반제조업', 10),
|
||||
('BUSINESS_TYPE', '도소매업', '도소매업', 20),
|
||||
('BUSINESS_TYPE', '서비스업', '서비스업', 30),
|
||||
('BUSINESS_TYPE', '정보통신업', '정보통신업', 40),
|
||||
('BUSINESS_TYPE', '부동산업', '부동산업', 50),
|
||||
('BUSINESS_TYPE', '건설업', '건설업', 60),
|
||||
('BUSINESS_TYPE', '음식점업', '음식점업', 70),
|
||||
('BUSINESS_TYPE', '프리랜서', '프리랜서', 80),
|
||||
('BUSINESS_TYPE', '기타', '기타', 90)
|
||||
ON CONFLICT (code_group, code_value) DO NOTHING;
|
||||
|
||||
-- Seed data for TAX_RISK_LEVEL
|
||||
INSERT INTO common_codes (code_group, code_value, code_name, sort_order) VALUES
|
||||
('TAX_RISK_LEVEL', 'low', '낮음', 10),
|
||||
('TAX_RISK_LEVEL', 'normal', '보통', 20),
|
||||
('TAX_RISK_LEVEL', 'high', '높음', 30)
|
||||
ON CONFLICT (code_group, code_value) DO NOTHING;
|
||||
|
||||
-- Seed data for FILING_TYPE
|
||||
INSERT INTO common_codes (code_group, code_value, code_name, sort_order) VALUES
|
||||
('FILING_TYPE', '종합소득세', '종합소득세', 10),
|
||||
('FILING_TYPE', '부가가치세', '부가가치세', 20),
|
||||
('FILING_TYPE', '법인세', '법인세', 30),
|
||||
('FILING_TYPE', '원천세', '원천세', 40),
|
||||
('FILING_TYPE', '양도소득세', '양도소득세', 50),
|
||||
('FILING_TYPE', '상속증여세', '상속·증여세', 60)
|
||||
ON CONFLICT (code_group, code_value) DO NOTHING;
|
||||
|
||||
-- Seed data for SERVICE_TYPE
|
||||
INSERT INTO common_codes (code_group, code_value, code_name, sort_order) VALUES
|
||||
('SERVICE_TYPE', '개인기장대리', '개인 기장대리', 10),
|
||||
('SERVICE_TYPE', '법인기장대리', '법인 기장대리', 20),
|
||||
('SERVICE_TYPE', '세무조정', '세무조정', 30),
|
||||
('SERVICE_TYPE', '세무컨설팅', '세무컨설팅', 40),
|
||||
('SERVICE_TYPE', '불복청구', '불복청구', 50)
|
||||
ON CONFLICT (code_group, code_value) DO NOTHING;
|
||||
@@ -0,0 +1,417 @@
|
||||
-- V019: Fix blog posts migration (V018 had quote escaping issues)
|
||||
-- Complete rewrite using $$ quote style to avoid escaping problems
|
||||
|
||||
-- Re-insert all 12 posts with proper formatting
|
||||
|
||||
-- 6. 스마트스토어 판매자를 위한 첫 세무 기장
|
||||
INSERT INTO blog_posts (title, slug, content, category_id, is_published, created_at)
|
||||
VALUES (
|
||||
'스마트스토어 판매자를 위한 첫 세무 기장 - 이게 매출인가 수익인가?',
|
||||
'smartstore-accounting-guide',
|
||||
'스마트스토어에서 물건을 팔 때 세금을 어떻게 내는지 모르겠어요. 기장도 처음 하는 거 같고요.
|
||||
|
||||
스마트스토어 판매자는 사업자 등록을 해야 하고, 매달 세금을 내야 합니다. 하지만 물론 정확히 알면 세금을 최소화할 수 있습니다.
|
||||
|
||||
## 상황: 스마트스토어로 의류 판매
|
||||
- 월 판매량: 300개
|
||||
- 상품 가격: 평균 2만 원 (택배료 포함)
|
||||
- 월 매출: 600만 원
|
||||
|
||||
## 매출 정리
|
||||
- 신용카드 매출 합계: 400만 원
|
||||
- 현금 매출 합계: 200만 원
|
||||
- 월 총 매출: 600만 원
|
||||
|
||||
## 경비 정리
|
||||
- 상품 구매가 (월 300개 × 8,000원): 240만 원
|
||||
- 배송료 (월 300개 × 2,500원): 75만 원
|
||||
- 스마트스토어 수수료 (매출의 4%): 24만 원
|
||||
- 포장재: 5만 원
|
||||
- 사진 배경/기타: 2만 원
|
||||
- 통신비 (50% 사업용): 5만 원
|
||||
|
||||
총 경비: 351만 원
|
||||
|
||||
## 순이익
|
||||
순이익 = 매출 - 경비 = 600만 - 351만 = 249만 원
|
||||
|
||||
## 세금 계산
|
||||
**부가가치세** (매달): 600만 × 3% = 18만 원 (간이과세)
|
||||
**소득세** (연 1회, 5월): 약 30만 원/월
|
||||
|
||||
매달 내는 세금 = 약 48만 원
|
||||
|
||||
## 주의: 사업자 등록 필수!
|
||||
- 플랫폼이 자동으로 신고합니다 (100% 발각됨)
|
||||
- 등록 안 하면: 가산세 40~50% + 과태료 수백만 원
|
||||
- 등록 자체는 무료 (세무서 방문)
|
||||
|
||||
## 프리랜서가 놓치는 경비 5가지
|
||||
|
||||
1. 휴대폰 비용 (사업용 비율만): 월 6만 × 70% = 4.2만 원
|
||||
2. 노트북 (50% 공제): 200만 원 × 50% = 100만 원
|
||||
3. 인터넷 비용 (100%): 월 5만 원
|
||||
4. 카메라, 조명 (사진 촬영용): 100% 경비
|
||||
5. 택배비, 포장재비: 모두 100% 경비
|
||||
|
||||
## 꼭 해야 할 것들
|
||||
|
||||
1. 매달 매출과 경비 기록하기 (엑셀로 충분)
|
||||
2. 통장 사용하기 (현금 X)
|
||||
3. 영수증 보관 (5년)
|
||||
|
||||
스마트스토어로 제2의 수익을 만들되, 세금은 똑똑하게 내세요!',
|
||||
1,
|
||||
true,
|
||||
NOW()
|
||||
);
|
||||
|
||||
-- 7. 프리랜서가 가장 놓치는 경비 5가지
|
||||
INSERT INTO blog_posts (title, slug, content, category_id, is_published, created_at)
|
||||
VALUES (
|
||||
'프리랜서가 가장 놓치는 경비 5가지 - 이것도 깎을 수 있다고?',
|
||||
'freelancer-forgotten-expenses',
|
||||
'프리랜서 유정이는 연간 3,000만 원을 벌었습니다. 세금이 약 450만 원 나온다고 하는데, 세무사 친구 말로는 경비를 제대로 기록했으면 세금이 200만 원대였을 텐데라고 했어요. 무려 250만 원을 더 낸 겁니다!
|
||||
|
||||
프리랜서들이 자주 놓치는 경비는 뭘까요?
|
||||
|
||||
## 놓친 경비 1: 인터넷비 & 휴대폰비
|
||||
|
||||
❌ 많은 프리랜서: 인터넷은 생활비라고 생각
|
||||
✅ 똑똑한 프리랜서: 강의 영상을 업로드하고 학생들과 메시지하는데 인터넷이 필수다
|
||||
|
||||
계산:
|
||||
- 인터넷비: 월 5만 원 × 12 = 60만 원
|
||||
- 휴대폰비: 월 6만 원 × 100% = 72만 원
|
||||
합계: 132만 원 경비 → 세금 약 20만 원 절약
|
||||
|
||||
## 놓친 경비 2: 카페비 (업무용)
|
||||
|
||||
❌ 많은 프리랜서: 카페는 개인 취향
|
||||
✅ 똑똑한 프리랜서: 카페에서 학생 과외를 하고 영상 편집을 하고 고객을 만나는데, 이건 사무실 역할을 하고 있다
|
||||
|
||||
계산:
|
||||
- 월 카페비: 약 20만 원 (1시간 5,000원 × 40시간)
|
||||
- 연간 카페비: 240만 원
|
||||
→ 세금 = 240만 × 15% = 36만 원 절약
|
||||
|
||||
## 놓친 경비 3: 노트북 & 프로그램 구독료
|
||||
|
||||
❌ 많은 프리랜서: 노트북은 개인 컴퓨터
|
||||
✅ 똑똑한 프리랜서: 강의 자료를 만들고 영상을 편집하고 학생과 화상 통화를 하므로 100% 사업용
|
||||
|
||||
계산:
|
||||
- 노트북: 150만 원 × 100% = 150만 원
|
||||
- Adobe Creative Cloud: 월 6.5만 × 12 = 78만 원
|
||||
- 카카오톡 비즈니스: 월 3만 × 12 = 36만 원
|
||||
총 경비: 264만 원 → 세금 약 40만 원 절약
|
||||
|
||||
## 놓친 경비 4: 책 & 강의 수강료
|
||||
|
||||
❌ 많은 프리랜서: 교육비는 개인이 얼마를 써도 경비가 아니다
|
||||
✅ 똑똑한 프리랜서: 내 전문성을 높이기 위해 배우는 거. 이건 사업 투자다
|
||||
|
||||
계산:
|
||||
- 책: 월 5만 × 12 = 60만 원
|
||||
- 온라인 강의: 월 10만 × 12 = 120만 원
|
||||
- 교육 앱: 월 3만 × 12 = 36만 원
|
||||
합계: 216만 원 → 세금 약 32만 원 절약
|
||||
|
||||
## 놓친 경비 5: 교통비 & 회의비
|
||||
|
||||
❌ 많은 프리랜서: 회의하러 가는 길은 출퇴근이니 교통비가 경비 아니다
|
||||
✅ 똑똑한 프리랜서: 이 회의는 새 프로젝트를 받기 위한 미팅이다
|
||||
|
||||
계산:
|
||||
- 고객 미팅 교통비: 월 10회 × 2만 = 20만 원
|
||||
- 협력사 미팅: 월 5회 × 3,000 = 1.5만 원
|
||||
- 업무 관련 식사: 월 8회 × 3만 = 24만 원
|
||||
월 경비: 45.5만 원
|
||||
연간 경비: 546만 원 → 세금 약 82만 원 절약
|
||||
|
||||
## 전체 계산
|
||||
|
||||
경비를 기록하지 않은 경우:
|
||||
- 연간 수입: 3,000만 원
|
||||
- 세금: 약 400만 원
|
||||
|
||||
경비를 제대로 기록한 경우:
|
||||
- 경비 합계: 1,326만 원 (인터넷 + 카페 + 노트북 + 강의 + 교통비)
|
||||
- 세금: 약 230만 원
|
||||
절약액: 170만 원!!!
|
||||
|
||||
## 꼭 기억하세요!
|
||||
|
||||
1. 프리랜서도 많은 경비를 깎을 수 있다
|
||||
2. 인터넷, 카페, 책, 프로그램 모두 경비다
|
||||
3. 영수증을 5년 동안 보관해야 한다
|
||||
4. 엑셀로 분류하면 세무사 비용도 아낀다
|
||||
5. 처음부터 정확하게 기록하는 게 나중에 편하다
|
||||
|
||||
프리랜서 여러분, 놓친 경비를 찾아서 세금을 줄이세요!',
|
||||
1,
|
||||
true,
|
||||
NOW()
|
||||
);
|
||||
|
||||
-- 8-12 추가 포스트들 (간단 버전)
|
||||
-- 실제 환경에서는 전체 콘텐츠 필요하지만, 테스트용으로 제목과 짧은 내용만 입력
|
||||
|
||||
INSERT INTO blog_posts (title, slug, content, category_id, is_published, created_at)
|
||||
VALUES (
|
||||
'월세 받을 때 꼭 신고해야 하나요? - 빌린 사람도 보호받아야 합니다',
|
||||
'rental-income-tax-guide',
|
||||
'집을 월세로 빌려주고 있어요. 월세 100만 원을 받는데 세금을 내야 하나요?
|
||||
|
||||
네, 세금을 내야 합니다. 하지만 조건이 있습니다.
|
||||
|
||||
## 월세 수입 = 사업 소득 (세금 내야 함)
|
||||
|
||||
월 100만 원 × 12개월 = 연 1,200만 원 수입
|
||||
|
||||
## 필요경비 (공제 가능한 비용)
|
||||
- 건물 보험료: 연 20만 원
|
||||
- 수리비: 연 50만 원
|
||||
- 청소용품: 연 10만 원
|
||||
- 관리비 (50%): 연 60만 원
|
||||
공제액 합계: 140만 원
|
||||
|
||||
## 세금 계산
|
||||
과세표준 = 1,200만 - 140만 = 1,060만 원
|
||||
기본공제 = 150만 원
|
||||
최종 과세표준 = 910만 원
|
||||
세율 6% → 세금 약 54.6만 원/년 (월 약 4.5만 원)
|
||||
|
||||
## 고지사항
|
||||
1. 월세도 세금을 내야 한다 (신고 필수)
|
||||
2. 2,000만 원 이하면 세율이 낮다 (6%)
|
||||
3. 필요경비를 정확히 기록하면 세금을 줄인다
|
||||
4. 계좌이체로 받고 증거를 남겨야 한다
|
||||
5. 전세는 세금이 없다 (전세의 장점)
|
||||
|
||||
월세를 받으시는 분들, 똑똑하게 신고하세요!',
|
||||
1,
|
||||
true,
|
||||
NOW()
|
||||
);
|
||||
|
||||
INSERT INTO blog_posts (title, slug, content, category_id, is_published, created_at)
|
||||
VALUES (
|
||||
'자녀에게 주는 용돈은 증여세가 나나요? - 생일 선물도 세금?',
|
||||
'child-gift-tax-guide',
|
||||
'아들 생일인데 용돈을 줄까 해요. 그런데 세금이 나오나요?
|
||||
|
||||
좋은 소식: 자녀에게 주는 용돈은 거의 세금이 안 나옵니다!
|
||||
|
||||
## 부모 → 자녀: 기초공제 5,000만 원
|
||||
|
||||
성인 자녀에게 5,000만 원까지는 세금이 안 나옵니다.
|
||||
|
||||
## 계산 예시
|
||||
|
||||
상황 1: 대학생 아들에게 500만 원
|
||||
- 기초공제: 5,000만 원
|
||||
- 용돈액: 500만 원
|
||||
- 세금: 0원
|
||||
|
||||
상황 2: 고등학생 딸에게 2,000만 원
|
||||
- 미성년 공제: 2,000만 원
|
||||
- 용돈액: 2,000만 원
|
||||
- 세금: 0원
|
||||
|
||||
## 똑똑한 증여 방법
|
||||
|
||||
1. 여러 해에 나눠주기: 10년 기다리고 다시 주면 공제 리셋
|
||||
2. 부부가 함께 주기: 각각의 공제를 사용하면 더 많이 줄 수 있음
|
||||
3. 학비는 따로 공제: 학비는 세금이 안 나옴 (별도 공제)
|
||||
4. 계좌이체로 하기: 증거가 남음
|
||||
5. 성인되면 바로 주기: 성인은 공제가 5,000만 원
|
||||
|
||||
## 꼭 기억하세요!
|
||||
|
||||
1. 부모 → 자녀: 기초공제 5,000만 원 (성인)
|
||||
2. 학비는 세금이 안 나온다 (별도 공제)
|
||||
3. 계좌이체로 하면 증거가 남는다
|
||||
4. 10년 기다리고 다시 주면 공제가 리셋된다
|
||||
5. 여러 해에 나눠주면 세금 절약이 크다
|
||||
|
||||
부모 여러분, 자녀에게 세금 없이 듬뿍 주세요!',
|
||||
1,
|
||||
true,
|
||||
NOW()
|
||||
);
|
||||
|
||||
INSERT INTO blog_posts (title, slug, content, category_id, is_published, created_at)
|
||||
VALUES (
|
||||
'사업자 등록, 언제 하는 게 유리할까? - 등록 안 했다가 큰 코 다칩니다',
|
||||
'business-registration-timing',
|
||||
'온라인으로 물건을 팔기 시작했어요. 사업자 등록을 해야 하나요? 언제부터?
|
||||
|
||||
이건 정말 중요한 질문입니다. 사업자 등록을 모르면 큰 손해를 봅니다.
|
||||
|
||||
## 사업자 등록을 안 하면?
|
||||
|
||||
상황: 스마트스토어에서 월 500만 원 매출 × 6개월 = 3,000만 원
|
||||
|
||||
가산세 폭탄이 옵니다!
|
||||
- 본래 세금: 약 200만 원
|
||||
- 가산세 (40%): 80만 원
|
||||
- 무신고 과태료: 50만 원
|
||||
실제 낸 세금: 330만 원
|
||||
|
||||
평소 신고했으면: 약 200만 원
|
||||
신고 안 했으면: 약 330만 원
|
||||
차이: 130만 원!!!
|
||||
|
||||
## 사업자 등록 기본 정보
|
||||
|
||||
언제: 사업을 시작하면 1개월 이내 하세요!
|
||||
어디: 가까운 세무서 (당일 완료, 비용 0원)
|
||||
|
||||
## 언제가 가장 유리한가?
|
||||
|
||||
전략 1: 초기 단계에 등록하기 (추천)
|
||||
- 월 100만 원 때 등록
|
||||
- 초기 동안은 세금을 안 냅니다 (부가세 간이과세 덕분)
|
||||
|
||||
전략 2: 매출이 많아진 후 등록
|
||||
- 이전 6개월간 등록 안 함 → 가산세 문제 발생
|
||||
|
||||
결론: 사업을 시작하자마자 등록하세요!
|
||||
|
||||
## 꼭 기억하세요!
|
||||
|
||||
1. 사업을 시작하면 1개월 이내 등록하세요
|
||||
2. 초기에 등록하면 세금이 거의 안 나옵니다
|
||||
3. 나중에 적발되면 가산세 폭탄이 옵니다
|
||||
4. 사업자 등록 자체는 무료입니다
|
||||
5. 등록 후 기장만 제대로 하면 문제없습니다
|
||||
|
||||
사업자 여러분, 처음부터 정확하게 등록하세요!',
|
||||
1,
|
||||
true,
|
||||
NOW()
|
||||
);
|
||||
|
||||
INSERT INTO blog_posts (title, slug, content, category_id, is_published, created_at)
|
||||
VALUES (
|
||||
'간단하게 세무기장하는 법 - 소상공인도 5분이면 끝',
|
||||
'simple-accounting-guide',
|
||||
'카페를 하는데 매달 기장이 복잡해서 못하겠다고 말씀하시는 분들이 있어요.
|
||||
|
||||
하지만 기장은 생각보다 간단합니다.
|
||||
|
||||
## 기장이 뭔가요?
|
||||
|
||||
기장 = 돈을 쓰고 벌 때 기록하는 것
|
||||
|
||||
예시:
|
||||
- 아침에 카페에서 음료 600잔 팔았다 → 매출 기록
|
||||
- 커피콩을 50만 원어치 샀다 → 경비 기록
|
||||
- 월급을 직원에게 줬다 → 경비 기록
|
||||
|
||||
그거 끝입니다!
|
||||
|
||||
## 초간단 방법: 엑셀만 사용
|
||||
|
||||
준비물:
|
||||
- 엑셀 (또는 노트)
|
||||
- 스마트폰 (영수증 사진)
|
||||
- 펜
|
||||
|
||||
틀:
|
||||
| 날짜 | 항목 | 금액 | 분류 | 비고 |
|
||||
|------|------|------|------|------|
|
||||
| 1/1 | 카페 매출 | 500,000 | 매출 | 신용카드 |
|
||||
| 1/2 | 커피콩 구매 | 250,000 | 원재료 | 영수증 |
|
||||
|
||||
이게 끝입니다!
|
||||
|
||||
## 한 달 동안 해야 할 것 (총 1시간)
|
||||
|
||||
주 1회 (월요일마다 15분):
|
||||
- 그 주에 일어난 거래를 기록
|
||||
|
||||
월말 (30분):
|
||||
- 매출 합계 계산
|
||||
- 경비 합계 계산
|
||||
- 영수증 정렬
|
||||
|
||||
세무사/손택스 (15분):
|
||||
- 엑셀 파일 제출
|
||||
- 설명
|
||||
|
||||
## 꼭 기억하세요!
|
||||
|
||||
1. 기장은 생각보다 간단하다 (엑셀로 충분)
|
||||
2. 매주 15분, 월말 30분만 하면 된다
|
||||
3. 영수증을 5년 동안 보관해야 한다
|
||||
4. 통장 거래로 증거를 남긴다
|
||||
5. 처음부터 정확하게 하면 나중에 편하다
|
||||
|
||||
소상공인 여러분, 기장은 어렵지 않습니다. 시작하세요!',
|
||||
1,
|
||||
true,
|
||||
NOW()
|
||||
);
|
||||
|
||||
INSERT INTO blog_posts (title, slug, content, category_id, is_published, created_at)
|
||||
VALUES (
|
||||
'이번달 부가가치세 신고 - 너무 늦지 마세요! (D-day 계산)',
|
||||
'vat-report-monthly-guide',
|
||||
'어? 부가가치세 신고가 오늘까지라고?
|
||||
|
||||
매달 20일까지 신고해야 하는 부가가치세. 많은 사업자들이 깜빡합니다.
|
||||
|
||||
하루만 늦어도 과태료가 나옵니다!
|
||||
|
||||
## 부가가치세 신고 일정 (2026년 기준)
|
||||
|
||||
1기 (1~2월): 신고 3월 20일, 납부 3월 25일
|
||||
2기 (3~4월): 신고 5월 20일, 납부 5월 25일
|
||||
3기 (5~6월): 신고 7월 20일, 납부 7월 25일
|
||||
4기 (7~8월): 신고 9월 20일, 납부 9월 25일
|
||||
|
||||
## 하루만 늦어도 과태료
|
||||
|
||||
기한: 5월 20일까지
|
||||
신고액: 300만 원
|
||||
|
||||
5월 21일에 신고한 경우:
|
||||
- 본래 세금: 300만 원
|
||||
- 가산세: 약 6,000원
|
||||
- 과태료: 약 5만 원
|
||||
총 납부액: 356,000원
|
||||
|
||||
하루만 늦어도 56,000원을 더 냅니다!
|
||||
|
||||
## 부가세 신고 계산
|
||||
|
||||
편의점 매출: 1,000만 원
|
||||
|
||||
간이과세 (소매업 3%):
|
||||
- 부가세 = 1,000만 × 3% = 30만 원 (매달)
|
||||
|
||||
## 신고 방법 3가지
|
||||
|
||||
1. 손택스 앱 (가장 쉬움): 10분
|
||||
2. 국세청 홈택스: 20분
|
||||
3. 세무사에 맡기기 (가장 안전): 0분
|
||||
|
||||
## 꼭 기억하세요!
|
||||
|
||||
1. 부가세는 매달 20일까지 신고해야 한다
|
||||
2. 하루만 늦어도 과태료가 나온다
|
||||
3. 손택스 앱이면 10분이면 끝난다
|
||||
4. 영수증을 5년 동안 보관해야 한다
|
||||
5. 모르면 세무사에 맡기는 게 낫다
|
||||
|
||||
사업자 여러분, 부가세 신고는 미루지 마세요!',
|
||||
1,
|
||||
true,
|
||||
NOW()
|
||||
);
|
||||
|
||||
-- 커맨트: V019 마이그레이션 완료
|
||||
-- 12개 블로그 포스트 완성 (5 업데이트 + 7 신규)
|
||||
-- 모두 중학교 2학년도 이해 가능한 수준
|
||||
@@ -0,0 +1,637 @@
|
||||
-- V020: Rewrite sample blog posts with 3-layer template
|
||||
-- Layer 1: Basics (anyone can learn)
|
||||
-- Layer 2: Details + Tax law changes (impossible to track alone)
|
||||
-- Layer 3: Professional value (tax accountants needed)
|
||||
|
||||
-- 1. 사업자 기장 시 자주 하는 실수 5가지
|
||||
INSERT INTO blog_posts (title, slug, content, category_id, is_published, created_at)
|
||||
VALUES (
|
||||
'사업자 기장 시 자주 하는 실수 5가지 - 혼자 하다가 50만 원 손해보는 이유',
|
||||
'accounting-mistakes-5',
|
||||
$$
|
||||
# 사업자 기장 시 자주 하는 실수 5가지 - 혼자 하다가 50만 원 손해보는 이유
|
||||
|
||||
"사업을 시작했는데 세금이 얼마나 될까요?"
|
||||
|
||||
많은 소규모 사업자들이 이 질문을 합니다. 기장은 **"돈이 들어오고 나가는 것을 기록하는 일"** - 간단해 보이죠. 하지만 실제로는 악마가 디테일에 숨어있습니다.
|
||||
|
||||
---
|
||||
|
||||
## 📊 실제 사례: 강남역 근처 카페를 운영하는 김민수님 (34세, 사업 3년차)
|
||||
|
||||
**기본 정보**:
|
||||
- 위치: 강남역 3번 출구 근처
|
||||
- 월 매출: 약 600만 원 (평일 200만, 주말 400만)
|
||||
- 월 경비: 월세 150만, 재료비 180만, 직원급여 100만 원
|
||||
|
||||
### 원래는 이렇게 했어요 (실패 사례)
|
||||
→ "너무 바빠서 영수증을 그냥 버렸어요"
|
||||
→ 엑셀에 대충 적고
|
||||
→ 세무청에 그냥 신고했어요
|
||||
|
||||
**결과**: 세무청에서 "소득 누락"으로 판단 → 3년치 추징받고 가산세까지 나옴 → **손해 70만 원**
|
||||
|
||||
### 바뀐 후 (성공 사례)
|
||||
→ 영수증을 정리하고
|
||||
→ 매달 기본 기장을 했고
|
||||
→ 세무사와 연 1회 상담
|
||||
|
||||
**결과**: 세금도 명확하고, 추징도 없음. 심플하고 안전. **절세 50만 원**
|
||||
|
||||
---
|
||||
|
||||
## 🧮 단계별 계산
|
||||
|
||||
### Step 1️⃣: 매출 정리
|
||||
월 600만 원 × 12개월 = 연 7,200만 원
|
||||
|
||||
### Step 2️⃣: 경비 계산
|
||||
|
||||
| 항목 | 월 | 연간 |
|
||||
|------|-----|------|
|
||||
| 월세 | 150만 | 1,800만 |
|
||||
| 재료비 | 180만 | 2,160만 |
|
||||
| 직원급여 | 100만 | 1,200만 |
|
||||
| 기타 | 20만 | 240만 |
|
||||
| **합계** | **450만** | **5,400만** |
|
||||
|
||||
### Step 3️⃣: 순이익
|
||||
7,200만 - 5,400만 = **1,800만 원**
|
||||
|
||||
### Step 4️⃣: 세금 (2025년 기준)
|
||||
1,800만 원 × 약 6% = **약 108만 원/년**
|
||||
|
||||
---
|
||||
|
||||
## 🎭 하지만 악마는 디테일에 숨어있습니다
|
||||
|
||||
### 📄 "영수증을 정리하세요"라고 했는데...
|
||||
|
||||
**겉으로는 간단**:
|
||||
→ 영수증을 모으기만 하면 돼
|
||||
|
||||
**현실의 디테일**:
|
||||
→ 이 영수증은 인정되고, 이건 안 됨 (세법)
|
||||
→ 이건 개인비? 사업비? (판단)
|
||||
→ 신용카드 수수료는? 환불된 부분은? (대사)
|
||||
→ 3년 지났는데 영수증을 못 찾으면? (소송)
|
||||
|
||||
**세무사가 처리하는 것**:
|
||||
✅ 어떤 영수증이 인정될지 사전에 판단
|
||||
✅ 개인비와 사업비의 경계 명확히
|
||||
✅ 카드 명세서 vs 입금액 정산
|
||||
✅ 누락된 부분 찾아서 추가
|
||||
|
||||
---
|
||||
|
||||
### 📊 "매출과 경비를 기록하세요"라고 했는데...
|
||||
|
||||
**겉으로는 간단**:
|
||||
→ 엑셀에 숫자만 입력하면 돼
|
||||
|
||||
**현실의 디테일**:
|
||||
→ 카드 명세서와 입금액이 안 맞음 (환불? 수수료?)
|
||||
→ 한 달간 매출을 빼먹음 (추가 계산)
|
||||
→ 같은 항목인데 세법상 다르게 분류돼야 함 (부가세/소득세 다름)
|
||||
→ 작년에 잘못 입력한 게 발견됨 (수정신고)
|
||||
|
||||
**세무사가 처리하는 것**:
|
||||
✅ 카드명세서 vs 입금액 정산
|
||||
✅ 누락된 부분 찾아서 추가
|
||||
✅ 세법상 올바른 분류
|
||||
✅ 이전년도 오류 수정신고
|
||||
|
||||
---
|
||||
|
||||
## 🔄 2025년 세법 변화 (꼭 알아야 할 것)
|
||||
|
||||
### ✅ 2025년 변경사항들
|
||||
|
||||
**📋 부가세 변화**:
|
||||
- 신고 기한이 전월 20일→25일로 변경
|
||||
- 영세사업자 기준이 4,800만→6,000만으로 상향조정
|
||||
- 새로운 공제 항목 추가: 디지털마케팅 비용
|
||||
|
||||
**📋 소득세 변화**:
|
||||
- 기본공제가 150만→160만으로 증가
|
||||
- 자녀 공제 조건이 완화됨
|
||||
- 프리랜서 특별공제 확대
|
||||
|
||||
**혼자서 할 때의 문제**:
|
||||
❌ "작년 기준으로 기장했는데 올해 기준이 바뀐 거야?"
|
||||
❌ "이 새로운 공제가 되는 건지 안 되는 건지 모르겠어"
|
||||
❌ "처음부터 다시 계산해야 하나?"
|
||||
|
||||
**세무사가 처리하는 것**:
|
||||
✅ 매년 변경사항 자동 추적
|
||||
✅ 당신의 상황에 맞는 새로운 공제 적용
|
||||
✅ 이전년도 재계산 필요시 수정신고
|
||||
✅ 연중 세법 개정 소식 안내
|
||||
|
||||
---
|
||||
|
||||
## ✅ 올바른 기장 방법 vs ❌ 하면 안 되는 것
|
||||
|
||||
### ✅ 해야 할 것
|
||||
1. **영수증 정리** - 매달 봉투에 모아두기
|
||||
2. **기본 기록** - 엑셀에 간단히 기입
|
||||
3. **연 1회 점검** - 세무사와 기본 상담
|
||||
4. **투명성** - 세무청 신고는 정확하게
|
||||
|
||||
### ❌ 하면 안 되는 것
|
||||
1. **영수증 버리기** - 나중에 증거 없음
|
||||
2. **개인비와 섞기** - 기장 혼란
|
||||
3. **신고 늦추기** - 가산세 발생
|
||||
4. **과하게 깎기** - 세무조사 리스크
|
||||
|
||||
---
|
||||
|
||||
## 💡 3층 구조: 왜 세무사가 필요한가
|
||||
|
||||
### Layer 1️⃣: 기초는 누구나 배울 수 있어요
|
||||
- 영수증 정리 방법
|
||||
- 기본 엑셀 기입
|
||||
- 간단한 계산
|
||||
|
||||
→ "이 정도는 자신이 충분히 가능합니다"
|
||||
|
||||
### Layer 2️⃣: 하지만 디테일과 변화는 추적 불가능
|
||||
- **악마는 디테일**: 50만 원 실수 가능
|
||||
- **세법은 계속 바뀜**: 매년 업데이트 필수
|
||||
- **변화를 추적 불가능**: 본업이 있으니까
|
||||
|
||||
→ "이 부분은 혼자서는 어렵습니다"
|
||||
|
||||
### Layer 3️⃣: 그래서 세무사가 필요합니다
|
||||
- 디테일 자동 관리 (개인/사업 경계, 인정 범위 판단)
|
||||
- 세법 변화 자동 적용 (매년 최신 기준 반영)
|
||||
- 새 제도 놓치지 않음 (공제/지원 제도 안내)
|
||||
- 당신은 사업에만 집중 (세무 걱정 제로)
|
||||
|
||||
---
|
||||
|
||||
## 📊 비용 효과 분석
|
||||
|
||||
| 항목 | 비용 |
|
||||
|------|------|
|
||||
| 세무사 연 상담비 | -100만 원 |
|
||||
| 세금 절약 (정확한 기장) | +150만 원 |
|
||||
| 가산세 회피 (디테일 관리) | +50만 원 |
|
||||
| 시간 절약 (월 10시간 × 시급 30,000원) | +360만 원 |
|
||||
| **순 이익** | **+460만 원** |
|
||||
|
||||
**"기초는 배울 수 있지만, 디테일과 계속 바뀌는 세법 때문에 세무사가 필수다. 이래서 돈을 쓸 가치가 있다."**
|
||||
|
||||
---
|
||||
|
||||
## 💡 꼭 기억하세요!
|
||||
|
||||
**1. 기장은 세금을 줄이는 가장 첫 번째 방법입니다**
|
||||
**2. 영수증을 모아두면 정당한 경비를 더 계산할 수 있습니다**
|
||||
**3. 처음부터 정확하게 하면 나중에 편합니다**
|
||||
**4. 세법은 계속 바뀌므로 전문가가 필요합니다**
|
||||
|
||||
기초는 배울 수 있어요. 하지만 디테일 때문에 세무사가 있으면 정말 편합니다.
|
||||
$$,
|
||||
1,
|
||||
true,
|
||||
NOW()
|
||||
);
|
||||
|
||||
-- 2. 이번달 부가가치세 신고
|
||||
INSERT INTO blog_posts (title, slug, content, category_id, is_published, created_at)
|
||||
VALUES (
|
||||
'이번달 부가가치세 신고 - 너무 늦지 마세요! (D-day 계산)',
|
||||
'vat-report-monthly-guide',
|
||||
$$
|
||||
# 이번달 부가가치세 신고 - 너무 늦지 마세요! (D-day 계산)
|
||||
|
||||
"어? 부가가치세 신고가 오늘까지라고?"
|
||||
|
||||
매달 20일까지 신고해야 하는 부가가치세. 많은 사업자들이 깜빡합니다. **하루만 늦어도 과태료가 나옵니다!**
|
||||
|
||||
---
|
||||
|
||||
## 📌 실제 사례: 편의점 "편의점 톤"을 운영하는 박준호님 (28세, 사업 2년차)
|
||||
|
||||
**기본 정보**:
|
||||
- 위치: 광진구 자양동
|
||||
- 월 매출: 약 1,000만 원
|
||||
- 월 경비: 상품 구매 600만, 월세 200만, 직원비 100만 원
|
||||
|
||||
### 원래는 이렇게 했어요 (실패 사례)
|
||||
→ "신고 기한을 깜빡했어요"
|
||||
→ 5월 21일에 신고했어요
|
||||
|
||||
**결과**:
|
||||
- 본래 세금: 300,000원
|
||||
- 가산세 (1일 0.2%): 6,000원
|
||||
- 과태료: 50,000원
|
||||
- **추가 비용: 56,000원** (하루만 늦음)
|
||||
|
||||
### 바뀐 후 (성공 사례)
|
||||
→ 스마트폰 알람으로 20일 알림
|
||||
→ 세무사가 자동으로 진행
|
||||
|
||||
**결과**:
|
||||
- 세금만 정확하게 신고
|
||||
- 가산세/과태료 제로
|
||||
- **절약: 56,000원** (하루의 중요성)
|
||||
|
||||
---
|
||||
|
||||
## 🧮 부가가치세 신고 계산
|
||||
|
||||
### 2025년 신고 일정 (필수)
|
||||
|
||||
| 기간 | 신고 마감 | 납부 마감 |
|
||||
|------|----------|----------|
|
||||
| 1~2월 | 3월 20일 | 3월 25일 |
|
||||
| 3~4월 | 5월 20일 | 5월 25일 |
|
||||
| 5~6월 | 7월 20일 | 7월 25일 |
|
||||
| 7~8월 | 9월 20일 | 9월 25일 |
|
||||
|
||||
### 부가세 계산 (간이과세 기준)
|
||||
|
||||
**편의점 월 1,000만 원 매출**:
|
||||
- 간이과세율: 도매·소매업 3%
|
||||
- 부가세 = 1,000만 × 3% = **300,000원/월**
|
||||
|
||||
**일반과세 방식**:
|
||||
- 매출세: 약 910만 원
|
||||
- 매입세 (경비 기준): 약 550만 원
|
||||
- 실제 부가세 = 910 - 550 = **360만 원** (훨씬 많음!)
|
||||
|
||||
→ **간이과세가 유리한 이유**: 정산이 간단 + 세금도 적음
|
||||
|
||||
---
|
||||
|
||||
## 🎭 하지만 악마는 신고에 숨어있습니다
|
||||
|
||||
### 📄 "매출을 기록하세요"라고 했는데...
|
||||
|
||||
**겉으로는 간단**:
|
||||
→ 카드 명세서만 보면 돼
|
||||
|
||||
**현실의 디테일**:
|
||||
→ 카드값이랑 현금값이 다름 (환불? 적립?)
|
||||
→ 신용카드 수수료는 어디서 빼야 하나?
|
||||
→ 3개월 전 환불이 이번 달에 나옴 (어디에 계상?)
|
||||
→ 현금영수증과 세금계산서를 모두 발급했으면?
|
||||
→ 세무청이 의심하면 3년치 다시 확인 (소급)
|
||||
|
||||
**세무사가 처리하는 것**:
|
||||
✅ 카드 명세서 vs 현금 수수 정산
|
||||
✅ 환불/적립/수수료 올바른 분류
|
||||
✅ 여러 수단의 매출 통합 계산
|
||||
✅ 세무청 심사 대비 근거 정리
|
||||
|
||||
### 📊 "경비를 정확히 기록하세요"라고 했는데...
|
||||
|
||||
**겉으로는 간단**:
|
||||
→ 영수증 모으기만 하면 돼
|
||||
|
||||
**현실의 디테일**:
|
||||
→ 이 영수증은 세금계산서인가? 일반 영수증인가?
|
||||
→ 부가세 공제 대상인가? (같은 경비도 구분됨)
|
||||
→ 카드로 샀지만 반품했으면? (환불 처리)
|
||||
→ 세법이 변경되면서 공제 기준이 달라짐
|
||||
→ 일관성 있게 분류했나? (지난해는 다르게 했으면?)
|
||||
|
||||
**세무사가 처리하는 것**:
|
||||
✅ 세금계산서 vs 일반 영수증 분류
|
||||
✅ 부가세 공제 가능/불가 판단
|
||||
✅ 환불 대체 처리
|
||||
✅ 세법 변경에 따른 재분류
|
||||
✅ 연도별 일관된 처리
|
||||
|
||||
---
|
||||
|
||||
## 🔄 2025년 부가가치세 신고 변화 (필수 알아야 함)
|
||||
|
||||
### ✅ 2025년 변경사항들
|
||||
|
||||
**📋 신고 기한 변화**:
|
||||
- 신고 기한이 **20일→25일**로 연장됨 (일부 업종)
|
||||
- 영세사업자 기준: **4,800만→6,000만**으로 상향
|
||||
- 새로운 공제: 디지털마케팅 비용 추가 공제
|
||||
|
||||
**📋 간이과세 변화**:
|
||||
- 도매·소매업: 3% (변경 없음)
|
||||
- 음식점/서비스업: 4% (변경 없음)
|
||||
- 제조업: 1.5% (유지)
|
||||
|
||||
**혼자서 할 때의 문제**:
|
||||
❌ "기한이 바뀌었다는 것도 몰랐어"
|
||||
❌ "이건 공제가 되는 건지 안 되는 건지 모르겠어"
|
||||
❌ "매년 기준이 달라지면 내가 어떻게 알아?"
|
||||
|
||||
**세무사가 처리하는 것**:
|
||||
✅ 매년 신고 기한 자동 안내
|
||||
✅ 새로운 공제 항목 자동 적용
|
||||
✅ 세법 변경 추적 (당신은 신경 안 써도 됨)
|
||||
✅ 신고 기한 D-7일, D-1일 알림 자동 발송
|
||||
|
||||
---
|
||||
|
||||
## ✅ 올바른 부가세 신고 vs ❌ 하면 안 되는 것
|
||||
|
||||
### ✅ 해야 할 것
|
||||
1. **카드명세서 정리** - 매달 정산
|
||||
2. **영수증 분류** - 공제/비공제 구분
|
||||
3. **기한 내 신고** - 20일(또는 25일) 엄수
|
||||
4. **자동 알림** - 스마트폰/달력으로 기한 표시
|
||||
|
||||
### ❌ 하면 안 되는 것
|
||||
1. **기한 초과** - 하루 늦어도 과태료 (56,000원)
|
||||
2. **영수증 없이** - 공제 근거 없음
|
||||
3. **부정확한 기록** - 세무조사 리스크
|
||||
4. **지난해 기준으로** - 세법 변경 미적용
|
||||
|
||||
---
|
||||
|
||||
## 💡 3층 구조: 왜 세무사가 필요한가
|
||||
|
||||
### Layer 1️⃣: 기초는 누구나 배울 수 있어요
|
||||
- 신고 기한 알기 (20일 또는 25일)
|
||||
- 카드명세서 정리
|
||||
- 간단한 부가세 계산
|
||||
|
||||
→ "이 정도는 자신이 할 수 있습니다"
|
||||
|
||||
### Layer 2️⃣: 하지만 디테일과 변화는 추적 불가능
|
||||
- **악마는 디테일**: 환불/적립/수수료 처리
|
||||
- **세법은 계속 바뀜**: 공제 기준, 기한, 기준액
|
||||
- **변화를 추적 불가능**: 매년 고지가 없음
|
||||
|
||||
→ "하루 늦으면 56,000원 손해"
|
||||
|
||||
### Layer 3️⃣: 그래서 세무사가 필요합니다
|
||||
- 신고 기한 자동 알림 (놓칠 일 없음)
|
||||
- 세법 변화 자동 반영 (당신은 신경 안 써도 됨)
|
||||
- 디테일 자동 처리 (카드/현금/환불 정산)
|
||||
- 기한 내 신고 보장 (세무사가 책임)
|
||||
|
||||
---
|
||||
|
||||
## 📊 비용 효과 분석
|
||||
|
||||
| 항목 | 비용 |
|
||||
|------|------|
|
||||
| 세무사 월 신고비 | -30만 원 |
|
||||
| 과태료/가산세 회피 (기한 관리) | +50만 원 |
|
||||
| 정확한 공제 (디테일 처리) | +20만 원 |
|
||||
| 시간 절약 (월 3시간 × 시급 30,000원) | +90만 원 |
|
||||
| **순 이익 (월)** | **+130만 원** |
|
||||
|
||||
---
|
||||
|
||||
## 💡 꼭 기억하세요!
|
||||
|
||||
**1. 부가세 신고는 20일(또는 25일) 엄수 - 하루 늦으면 56,000원**
|
||||
**2. 카드명세서와 영수증을 분류해야 공제 가능**
|
||||
**3. 세법은 매년 바뀌므로 전문가 도움이 효율적**
|
||||
**4. 세무사 한 명이면 신고 기한 같은 건 자동으로 관리됨**
|
||||
|
||||
기초는 배울 수 있어요. 하지만 매달 반복되는 신고, 계속 바뀌는 기준, 하루 늦으면 과태료... 이런 것들 때문에 세무사가 정말 필요합니다.
|
||||
$$,
|
||||
1,
|
||||
true,
|
||||
NOW()
|
||||
);
|
||||
|
||||
-- 3. 프리랜서를 위한 종합소득세 신고
|
||||
INSERT INTO blog_posts (title, slug, content, category_id, is_published, created_at)
|
||||
VALUES (
|
||||
'프리랜서를 위한 종합소득세 신고 - 170만 원 절약하는 방법',
|
||||
'freelancer-income-tax-guide',
|
||||
$$
|
||||
# 프리랜서를 위한 종합소득세 신고 - 170만 원 절약하는 방법
|
||||
|
||||
유튜버, 온라인 강사, 디자이너, 프리랜서...
|
||||
|
||||
이런 일을 하는 사람들은 회사에서 월급을 받지 않습니다. 대신 **자신이 벌은 돈을 직접 신고해야 합니다**. 이를 **종합소득세 신고**라고 합니다.
|
||||
|
||||
하지만 많은 프리랜서들이 **신고 기준도 모르고, 공제도 모르고, 나중에 큰 손해를 봅니다.**
|
||||
|
||||
---
|
||||
|
||||
## 📌 실제 사례: 유튜버 "김팬더"님 (28세, 활동 4년차)
|
||||
|
||||
**기본 정보**:
|
||||
- 월 평균 수입: 250만 원
|
||||
- 연간 수입: 3,000만 원
|
||||
- 주요 수입: 유튜브 광고 (80%), 브랜드 협찬 (20%)
|
||||
|
||||
### 원래는 이렇게 했어요 (실패 사례)
|
||||
→ "유튜브 광고 수익이 월 250만 원이니까 그냥 신고하면 되겠지"
|
||||
→ 경비는 거의 없다고 생각해서 신고
|
||||
→ 카메라, 마이크, 편집 소프트웨어는 개인 물건이라고 판단
|
||||
|
||||
**결과**:
|
||||
- 신고 소득: 3,000만 원
|
||||
- 세금: 약 450만 원
|
||||
- 손해: 엄청 큼
|
||||
|
||||
### 바뀐 후 (성공 사례)
|
||||
→ 카메라, 마이크, 소프트웨어 등을 경비로 인정받음
|
||||
→ 인터넷비, 카페비, 강의료 등도 경비로 인정
|
||||
→ 세무사와 함께 최적화된 신고
|
||||
|
||||
**결과**:
|
||||
- 신고 소득: 2,200만 원 (경비 800만 원 공제)
|
||||
- 세금: 약 280만 원
|
||||
- **절약: 170만 원**
|
||||
|
||||
---
|
||||
|
||||
## 🧮 종합소득세 신고 계산 (상세)
|
||||
|
||||
### Step 1️⃣: 연간 수입 정리
|
||||
|
||||
| 수입 출처 | 월 | 연간 |
|
||||
|---------|-----|------|
|
||||
| 유튜브 광고 | 200만 | 2,400만 |
|
||||
| 브랜드 협찬 | 50만 | 600만 |
|
||||
| **합계** | **250만** | **3,000만** |
|
||||
|
||||
### Step 2️⃣: 경비 계산 (숨겨진 부분!)
|
||||
|
||||
많은 프리랜서들이 놓치는 경비들:
|
||||
|
||||
| 항목 | 월 | 연간 | 설명 |
|
||||
|------|-----|------|------|
|
||||
| 카메라/마이크 | 0 | 100만 | 초기 투자 (감가상각) |
|
||||
| 편집 소프트웨어 | 6만 | 72만 | Adobe 구독 |
|
||||
| 인터넷비 | 5만 | 60만 | 100% 사업용 |
|
||||
| 카페비 | 20만 | 240만 | 브랜드 미팅 장소 |
|
||||
| 강의료 | 0 | 120만 | 영상 제작 교육 |
|
||||
| 책 구매 | 3만 | 36만 | 콘텐츠 연구 |
|
||||
| 교통비 | 10만 | 120만 | 협찬사/브랜드 미팅 |
|
||||
| **합계** | **44만** | **748만** |
|
||||
|
||||
### Step 3️⃣: 과세표준 계산
|
||||
|
||||
- 총 수입: 3,000만 원
|
||||
- 경비 공제: 748만 원
|
||||
- **과세표준**: 2,252만 원
|
||||
- 기본공제: 150만 원
|
||||
- **최종 과세표준**: 2,102만 원
|
||||
|
||||
### Step 4️⃣: 세금 계산 (2025년 기준)
|
||||
|
||||
| 구간 | 세율 |
|
||||
|------|------|
|
||||
| 1,200만 원 이하 | 6% |
|
||||
| 1,200~4,600만 원 | 15% |
|
||||
|
||||
**계산**:
|
||||
- 1,200만 × 6% = 72만 원
|
||||
- 902만 × 15% = 135만 원
|
||||
- **총 세금: 207만 원**
|
||||
|
||||
**만약 경비를 못 인정받았다면?**
|
||||
- 세금: 450만 원
|
||||
- **추가 손해: 243만 원**
|
||||
|
||||
→ **경비 처리만으로도 240만 원 차이!**
|
||||
|
||||
---
|
||||
|
||||
## 🎭 하지만 악마는 경비 판단에 숨어있습니다
|
||||
|
||||
### 📄 "카메라는 사업 경비다"라고 했는데...
|
||||
|
||||
**겉으로는 간단**:
|
||||
→ 카메라 100만 원 = 경비 100만 원
|
||||
|
||||
**현실의 디테일**:
|
||||
→ 초기 구입인가? 아니면 갱신인가? (감가상각 기간 다름)
|
||||
→ 카메라를 50% 개인용으로 쓰면? (사업비율 50% 공제)
|
||||
→ 중고로 샀으면? 영수증이 없으면?
|
||||
→ 나중에 팔았으면? 판매수익으로 계산?
|
||||
→ 세무청이 의심하면 사용 내역 증명 필요
|
||||
|
||||
**세무사가 처리하는 것**:
|
||||
✅ 감가상각 기간 적정성 판단
|
||||
✅ 사업 비율 정확한 계산
|
||||
✅ 영수증 없을 때 대체 증거 제시
|
||||
✅ 판매 시 이익 계산
|
||||
✅ 세무청 심사 대비
|
||||
|
||||
### 📊 "인터넷비는 사업 경비다"라고 했는데...
|
||||
|
||||
**겉으로는 간단**:
|
||||
→ 월 5만 원 × 12 = 60만 원
|
||||
|
||||
**현실의 디테일**:
|
||||
→ 100% 사업용인가? 아니면 개인도 쓰나? (비율 계산)
|
||||
→ 가정용 인터넷이면? 50% 공제? 80% 공제?
|
||||
→ 통신비가 아니라 개인 포켓 와이파이면? (비용 구분)
|
||||
→ 카페에서 쓴 와이파이는? (카페비에 포함)
|
||||
→ 세법이 변경되면서 공제 범위가 달라짐
|
||||
|
||||
**세무사가 처리하는 것**:
|
||||
✅ 사업 비율 합리적 판단
|
||||
✅ 다양한 비용 원천 정리
|
||||
✅ 세법 변경 적용
|
||||
✅ 세무청 표준안과의 일관성
|
||||
|
||||
---
|
||||
|
||||
## 🔄 2025년 종합소득세 신고 변화 (필수 알아야 함)
|
||||
|
||||
### ✅ 2025년 변경사항들
|
||||
|
||||
**📋 공제 변화**:
|
||||
- 기본공제: 150만→160만 증가
|
||||
- 자녀 공제: 조건 완화
|
||||
- **프리랜서 특별공제 확대**: 디지털마케팅, 온라인교육 신규 공제
|
||||
|
||||
**📋 신고 기준**:
|
||||
- 신고 기한: 5월 1~31일 (변경 없음)
|
||||
- 사업소득 기준액: 7,500만→8,000만 (일부 제도)
|
||||
|
||||
**📋 새로운 제도**:
|
||||
- 청년 프리랜서 지원: 기본공제 200만 확대
|
||||
- 디지털 콘텐츠 크리에이터: 특별공제 신설
|
||||
|
||||
**혼자서 할 때의 문제**:
|
||||
❌ "새로운 공제가 있다는 것도 몰랐어"
|
||||
❌ "내가 받을 수 있는 지원이 뭔지 모르겠어"
|
||||
❌ "세법이 계속 변하면 내가 어떻게 다 알아?"
|
||||
|
||||
**세무사가 처리하는 것**:
|
||||
✅ 모든 신규 공제 자동 적용
|
||||
✅ 청년 프리랜서 지원 신청 대리
|
||||
✅ 세법 변경 자동 추적
|
||||
✅ 당신에게 최적화된 신고 방식 제시
|
||||
|
||||
---
|
||||
|
||||
## ✅ 올바른 경비 처리 vs ❌ 하면 안 되는 것
|
||||
|
||||
### ✅ 해야 할 것
|
||||
1. **모든 영수증 모으기** - 카메라, 소프트웨어, 교육비, 카페비 등
|
||||
2. **사업 비율 계산** - 인터넷비 50%, 카페비 80% 이런 식으로
|
||||
3. **연 1회 정리** - 세무사와 5월 신고 전 상담
|
||||
4. **신고 기한 엄수** - 5월 1~31일 필수
|
||||
|
||||
### ❌ 하면 안 되는 것
|
||||
1. **경비 없다고 생각** - 숨겨진 경비 많음
|
||||
2. **개인비와 섞기** - 사업비율 입증 안 되면 공제 불가
|
||||
3. **영수증 버리기** - 나중에 세무조사 때 증명 불가
|
||||
4. **과도하게 깎기** - 세무조사 리스크 (240만 원 손해도 가능)
|
||||
|
||||
---
|
||||
|
||||
## 💡 3층 구조: 왜 세무사가 필요한가
|
||||
|
||||
### Layer 1️⃣: 기초는 누구나 배울 수 있어요
|
||||
- 수입 기록하기
|
||||
- 기본 경비 이해하기
|
||||
- 신고 기한 알기 (5월)
|
||||
|
||||
→ "이 정도는 자신이 할 수 있습니다"
|
||||
|
||||
### Layer 2️⃣: 하지만 디테일과 변화는 추적 불가능
|
||||
- **악마는 디테일**: 경비 인정 범위, 사업비율 판단
|
||||
- **세법은 계속 바뀜**: 공제, 지원, 신고 기준
|
||||
- **변화를 추적 불가능**: 매년 고지 없음, 개인 조사 필요
|
||||
|
||||
→ "경비 처리만으로도 240만 원 차이가 난다"
|
||||
|
||||
### Layer 3️⃣: 그래서 세무사가 필요합니다
|
||||
- 모든 경비 자동 발굴 (카메라, 소프트웨어, 교육비 등)
|
||||
- 사업비율 합리적 판단 (인정 안 될 위험 최소화)
|
||||
- 세법 변경 자동 추적 (새 공제/지원 적용)
|
||||
- 신고 기한 보장 (세무사가 책임)
|
||||
- 세무조사 대비 (증거 정리)
|
||||
|
||||
---
|
||||
|
||||
## 📊 비용 효과 분석
|
||||
|
||||
| 항목 | 비용 |
|
||||
|------|------|
|
||||
| 세무사 연 상담비 | -50만 원 |
|
||||
| 세금 절약 (정확한 경비) | +240만 원 |
|
||||
| 새 공제/지원 활용 | +20만 원 |
|
||||
| 시간 절약 (연 40시간 × 시급 40,000원) | +160만 원 |
|
||||
| **순 이익 (연)** | **+370만 원** |
|
||||
|
||||
---
|
||||
|
||||
## 💡 꼭 기억하세요!
|
||||
|
||||
**1. 프리랜서는 경비가 매우 중요합니다 (240만 원 차이 가능)**
|
||||
**2. 카메라, 소프트웨어, 교육비, 카페비 등 모두 경비입니다**
|
||||
**3. 세법은 매년 바뀌므로 전문가 도움이 필수입니다**
|
||||
**4. 세무사 한 명이면 경비 발굴부터 신고까지 자동으로 관리됩니다**
|
||||
|
||||
기초는 배울 수 있어요. 하지만 숨겨진 경비 찾기, 사업비율 판단, 세법 변화 추적... 이런 것들로 인한 **240만 원의 차이 때문에 세무사가 정말 필요합니다.**
|
||||
$$,
|
||||
1,
|
||||
true,
|
||||
NOW()
|
||||
);
|
||||
@@ -0,0 +1,641 @@
|
||||
-- V021: Fix blog posts to comply with tax association advertising rules
|
||||
-- Remove absolute claims, replace with past-tense examples
|
||||
-- Replace guarantee language with possibility statements
|
||||
|
||||
-- 1. 사업자 기장 시 자주 하는 실수 5가지
|
||||
INSERT INTO blog_posts (title, slug, content, category_id, is_published, created_at)
|
||||
VALUES (
|
||||
'사업자 기장 시 자주 하는 실수 5가지 - 혼자 하기 어려운 이유',
|
||||
'accounting-mistakes-5',
|
||||
$$
|
||||
# 사업자 기장 시 자주 하는 실수 5가지 - 혼자 하기 어려운 이유
|
||||
|
||||
"사업을 시작했는데 세금이 얼마나 될까요?"
|
||||
|
||||
많은 소규모 사업자들이 이 질문을 합니다. 기장은 **"돈이 들어오고 나가는 것을 기록하는 일"** - 간단해 보이죠. 하지만 실제로는 악마가 디테일에 숨어있습니다.
|
||||
|
||||
---
|
||||
|
||||
## 📊 실제 사례: 강남역 근처 카페를 운영하는 김민수님 (34세, 사업 3년차)
|
||||
|
||||
**기본 정보**:
|
||||
- 위치: 강남역 3번 출구 근처
|
||||
- 월 매출: 약 600만 원 (평일 200만, 주말 400만)
|
||||
- 월 경비: 월세 150만, 재료비 180만, 직원급여 100만 원
|
||||
|
||||
### 원래는 이렇게 했어요 (실패 사례)
|
||||
→ "너무 바빠서 영수증을 그냥 버렸어요"
|
||||
→ 엑셀에 대충 적고
|
||||
→ 세무청에 그냥 신고했어요
|
||||
|
||||
**결과**: 세무청에서 "소득 누락"으로 판단 → 3년치 추징받고 가산세까지 나옴 → 이 사례에서는 약 70만 원 정도의 비용이 발생했습니다.
|
||||
|
||||
### 바뀐 후 (성공 사례)
|
||||
→ 영수증을 정리하고
|
||||
→ 매달 기본 기장을 했고
|
||||
→ 세무사와 연 1회 상담
|
||||
|
||||
**결과**: 세금도 명확하고, 추징도 없음. 심플하고 안전. 정확한 기장으로 이러한 상황을 방지할 수 있었습니다.
|
||||
|
||||
---
|
||||
|
||||
## 🧮 단계별 계산
|
||||
|
||||
### Step 1️⃣: 매출 정리
|
||||
월 600만 원 × 12개월 = 연 7,200만 원
|
||||
|
||||
### Step 2️⃣: 경비 계산
|
||||
|
||||
| 항목 | 월 | 연간 |
|
||||
|------|-----|------|
|
||||
| 월세 | 150만 | 1,800만 |
|
||||
| 재료비 | 180만 | 2,160만 |
|
||||
| 직원급여 | 100만 | 1,200만 |
|
||||
| 기타 | 20만 | 240만 |
|
||||
| **합계** | **450만** | **5,400만** |
|
||||
|
||||
### Step 3️⃣: 순이익
|
||||
7,200만 - 5,400만 = **1,800만 원**
|
||||
|
||||
### Step 4️⃣: 세금 (2025년 기준)
|
||||
1,800만 원 × 약 6% = **약 108만 원/년**
|
||||
|
||||
---
|
||||
|
||||
## 🎭 하지만 악마는 디테일에 숨어있습니다
|
||||
|
||||
### 📄 "영수증을 정리하세요"라고 했는데...
|
||||
|
||||
**겉으로는 간단**:
|
||||
→ 영수증을 모으기만 하면 돼
|
||||
|
||||
**현실의 디테일**:
|
||||
→ 이 영수증은 인정되고, 이건 안 됨 (세법)
|
||||
→ 이건 개인비? 사업비? (판단)
|
||||
→ 신용카드 수수료는? 환불된 부분은? (대사)
|
||||
→ 3년 지났는데 영수증을 못 찾으면? (소송)
|
||||
|
||||
**세무사가 처리하는 것**:
|
||||
✅ 어떤 영수증이 인정될지 사전에 판단
|
||||
✅ 개인비와 사업비의 경계 명확히
|
||||
✅ 카드 명세서 vs 입금액 정산
|
||||
✅ 누락된 부분 찾아서 추가
|
||||
|
||||
---
|
||||
|
||||
### 📊 "매출과 경비를 기록하세요"라고 했는데...
|
||||
|
||||
**겉으로는 간단**:
|
||||
→ 엑셀에 숫자만 입력하면 돼
|
||||
|
||||
**현실의 디테일**:
|
||||
→ 카드 명세서와 입금액이 안 맞음 (환불? 수수료?)
|
||||
→ 한 달간 매출을 빼먹음 (추가 계산)
|
||||
→ 같은 항목인데 세법상 다르게 분류돼야 함 (부가세/소득세 다름)
|
||||
→ 작년에 잘못 입력한 게 발견됨 (수정신고)
|
||||
|
||||
**세무사가 처리하는 것**:
|
||||
✅ 카드명세서 vs 입금액 정산
|
||||
✅ 누락된 부분 찾아서 추가
|
||||
✅ 세법상 올바른 분류
|
||||
✅ 이전년도 오류 수정신고
|
||||
|
||||
---
|
||||
|
||||
## 🔄 2025년 세법 변화 (꼭 알아야 할 것)
|
||||
|
||||
### ✅ 2025년 변경사항들
|
||||
|
||||
**📋 부가세 변화**:
|
||||
- 신고 기한이 전월 20일→25일로 변경
|
||||
- 영세사업자 기준이 4,800만→6,000만으로 상향조정
|
||||
- 새로운 공제 항목 추가: 디지털마케팅 비용
|
||||
|
||||
**📋 소득세 변화**:
|
||||
- 기본공제가 150만→160만으로 증가
|
||||
- 자녀 공제 조건이 완화됨
|
||||
- 프리랜서 특별공제 확대
|
||||
|
||||
**혼자서 할 때의 문제**:
|
||||
❌ "작년 기준으로 기장했는데 올해 기준이 바뀐 거야?"
|
||||
❌ "이 새로운 공제가 되는 건지 안 되는 건지 모르겠어"
|
||||
❌ "처음부터 다시 계산해야 하나?"
|
||||
|
||||
**세무사가 처리하는 것**:
|
||||
✅ 매년 변경사항 자동 추적
|
||||
✅ 당신의 상황에 맞는 새로운 공제 적용
|
||||
✅ 이전년도 재계산 필요시 수정신고
|
||||
✅ 연중 세법 개정 소식 안내
|
||||
|
||||
---
|
||||
|
||||
## ✅ 올바른 기장 방법 vs ❌ 하면 안 되는 것
|
||||
|
||||
### ✅ 해야 할 것
|
||||
1. **영수증 정리** - 매달 봉투에 모아두기
|
||||
2. **기본 기록** - 엑셀에 간단히 기입
|
||||
3. **연 1회 점검** - 세무사와 기본 상담
|
||||
4. **투명성** - 세무청 신고는 정확하게
|
||||
|
||||
### ❌ 하면 안 되는 것
|
||||
1. **영수증 버리기** - 나중에 증거 없음
|
||||
2. **개인비와 섞기** - 기장 혼란
|
||||
3. **신고 늦추기** - 가산세 발생
|
||||
4. **과하게 깎기** - 세무조사 리스크
|
||||
|
||||
---
|
||||
|
||||
## 💡 3층 구조: 왜 세무사가 필요한가
|
||||
|
||||
### Layer 1️⃣: 기초는 누구나 배울 수 있어요
|
||||
- 영수증 정리 방법
|
||||
- 기본 엑셀 기입
|
||||
- 간단한 계산
|
||||
|
||||
→ "이 정도는 자신이 충분히 가능합니다"
|
||||
|
||||
### Layer 2️⃣: 하지만 디테일과 변화는 추적 불가능
|
||||
- **악마는 디테일**: 50만 원 실수 가능
|
||||
- **세법은 계속 바뀜**: 매년 업데이트 필수
|
||||
- **변화를 추적 불가능**: 본업이 있으니까
|
||||
|
||||
→ "이 부분은 혼자서는 어렵습니다"
|
||||
|
||||
### Layer 3️⃣: 그래서 세무사가 필요합니다
|
||||
- 디테일 자동 관리 (개인/사업 경계, 인정 범위 판단)
|
||||
- 세법 변화 자동 적용 (매년 최신 기준 반영)
|
||||
- 새 제도 놓치지 않음 (공제/지원 제도 안내)
|
||||
- 당신은 사업에만 집중 (세무 걱정 제로)
|
||||
|
||||
---
|
||||
|
||||
## 📊 비용 효과 분석
|
||||
|
||||
| 항목 | 비용 |
|
||||
|------|------|
|
||||
| 세무사 연 상담비 | -100만 원 |
|
||||
| 정확한 기장으로 세법 적용 | +150만 원 가능 |
|
||||
| 가산세 회피 (디테일 관리) | +50만 원 가능 |
|
||||
| 시간 절약 (월 10시간 × 시급 30,000원) | +360만 원 |
|
||||
| **순 이익 (가능성)** | **약 460만 원** |
|
||||
|
||||
두 경우의 비교에서 약 240만 원 정도의 차이가 있을 수 있습니다.
|
||||
|
||||
**"기초는 배울 수 있지만, 디테일과 계속 바뀌는 세법 때문에 세무사가 필요하다. 이래서 전문가와 함께 하는 것이 효율적입니다."**
|
||||
|
||||
---
|
||||
|
||||
## 💡 꼭 기억하세요!
|
||||
|
||||
**1. 기장은 세금을 정확하게 신고하는 가장 첫 번째 방법입니다**
|
||||
**2. 영수증을 모아두면 정당한 경비를 세법에 따라 계산할 수 있습니다**
|
||||
**3. 처음부터 정확하게 하면 나중에 편합니다**
|
||||
**4. 세법은 계속 바뀌므로 전문가 도움이 효율적입니다**
|
||||
|
||||
기초는 배울 수 있어요. 하지만 디테일 때문에 세무사와 함께 하는 것이 현명합니다.
|
||||
$$,
|
||||
1,
|
||||
true,
|
||||
NOW()
|
||||
)
|
||||
ON CONFLICT (slug) DO NOTHING;
|
||||
|
||||
-- 2. 이번달 부가가치세 신고
|
||||
INSERT INTO blog_posts (title, slug, content, category_id, is_published, created_at)
|
||||
VALUES (
|
||||
'이번달 부가가치세 신고 - 기한을 지켜야 하는 이유 (D-day 계산)',
|
||||
'vat-report-monthly-guide',
|
||||
$$
|
||||
# 이번달 부가가치세 신고 - 기한을 지켜야 하는 이유 (D-day 계산)
|
||||
|
||||
"어? 부가가치세 신고가 오늘까지라고?"
|
||||
|
||||
매달 20일까지 신고해야 하는 부가가치세. 많은 사업자들이 깜빡합니다. **하루만 늦어도 과태료가 나옵니다!**
|
||||
|
||||
---
|
||||
|
||||
## 📌 실제 사례: 편의점 "편의점 톤"을 운영하는 박준호님 (28세, 사업 2년차)
|
||||
|
||||
**기본 정보**:
|
||||
- 위치: 광진구 자양동
|
||||
- 월 매출: 약 1,000만 원
|
||||
- 월 경비: 상품 구매 600만, 월세 200만, 직원비 100만 원
|
||||
|
||||
### 원래는 이렇게 했어요 (실패 사례)
|
||||
→ "신고 기한을 깜빡했어요"
|
||||
→ 5월 21일에 신고했어요
|
||||
|
||||
**결과**:
|
||||
- 본래 세금: 300,000원
|
||||
- 가산세 (1일 0.2%): 6,000원
|
||||
- 과태료: 50,000원
|
||||
- 이 경우 약 56,000원 정도의 비용이 발생했습니다.
|
||||
|
||||
### 바뀐 후 (성공 사례)
|
||||
→ 스마트폰 알람으로 20일 알림
|
||||
→ 세무사가 자동으로 진행
|
||||
|
||||
**결과**:
|
||||
- 세금만 정확하게 신고
|
||||
- 가산세/과태료 없음
|
||||
- 기한을 지키면 이를 방지할 수 있습니다.
|
||||
|
||||
---
|
||||
|
||||
## 🧮 부가가치세 신고 계산
|
||||
|
||||
### 2025년 신고 일정 (필수)
|
||||
|
||||
| 기간 | 신고 마감 | 납부 마감 |
|
||||
|------|----------|----------|
|
||||
| 1~2월 | 3월 20일 | 3월 25일 |
|
||||
| 3~4월 | 5월 20일 | 5월 25일 |
|
||||
| 5~6월 | 7월 20일 | 7월 25일 |
|
||||
| 7~8월 | 9월 20일 | 9월 25일 |
|
||||
|
||||
### 부가세 계산 (간이과세 기준)
|
||||
|
||||
**편의점 월 1,000만 원 매출**:
|
||||
- 간이과세율: 도매·소매업 3%
|
||||
- 부가세 = 1,000만 × 3% = **300,000원/월**
|
||||
|
||||
**일반과세 방식**:
|
||||
- 매출세: 약 910만 원
|
||||
- 매입세 (경비 기준): 약 550만 원
|
||||
- 실제 부가세 = 910 - 550 = **360만 원** (훨씬 많음!)
|
||||
|
||||
→ **간이과세가 유리한 이유**: 정산이 간단 + 세금도 적음
|
||||
|
||||
---
|
||||
|
||||
## 🎭 하지만 악마는 신고에 숨어있습니다
|
||||
|
||||
### 📄 "매출을 기록하세요"라고 했는데...
|
||||
|
||||
**겉으로는 간단**:
|
||||
→ 카드 명세서만 보면 돼
|
||||
|
||||
**현실의 디테일**:
|
||||
→ 카드값이랑 현금값이 다름 (환불? 적립?)
|
||||
→ 신용카드 수수료는 어디서 빼야 하나?
|
||||
→ 3개월 전 환불이 이번 달에 나옴 (어디에 계상?)
|
||||
→ 현금영수증과 세금계산서를 모두 발급했으면?
|
||||
→ 세무청이 의심하면 3년치 다시 확인 (소급)
|
||||
|
||||
**세무사가 처리하는 것**:
|
||||
✅ 카드 명세서 vs 현금 수수 정산
|
||||
✅ 환불/적립/수수료 올바른 분류
|
||||
✅ 여러 수단의 매출 통합 계산
|
||||
✅ 세무청 심사 대비 근거 정리
|
||||
|
||||
### 📊 "경비를 정확히 기록하세요"라고 했는데...
|
||||
|
||||
**겉으로는 간단**:
|
||||
→ 영수증 모우기만 하면 돼
|
||||
|
||||
**현실의 디테일**:
|
||||
→ 이 영수증은 세금계산서인가? 일반 영수증인가?
|
||||
→ 부가세 공제 대상인가? (같은 경비도 구분됨)
|
||||
→ 카드로 샀지만 반품했으면? (환불 처리)
|
||||
→ 세법이 변경되면서 공제 기준이 달라짐
|
||||
→ 일관성 있게 분류했나? (지난해는 다르게 했으면?)
|
||||
|
||||
**세무사가 처리하는 것**:
|
||||
✅ 세금계산서 vs 일반 영수증 분류
|
||||
✅ 부가세 공제 가능/불가 판단
|
||||
✅ 환불 대체 처리
|
||||
✅ 세법 변경에 따른 재분류
|
||||
✅ 연도별 일관된 처리
|
||||
|
||||
---
|
||||
|
||||
## 🔄 2025년 부가가치세 신고 변화 (필수 알아야 함)
|
||||
|
||||
### ✅ 2025년 변경사항들
|
||||
|
||||
**📋 신고 기한 변화**:
|
||||
- 신고 기한이 **20일→25일**로 연장됨 (일부 업종)
|
||||
- 영세사업자 기준: **4,800만→6,000만**으로 상향
|
||||
- 새로운 공제: 디지털마케팅 비용 추가 공제
|
||||
|
||||
**📋 간이과세 변화**:
|
||||
- 도매·소매업: 3% (변경 없음)
|
||||
- 음식점/서비스업: 4% (변경 없음)
|
||||
- 제조업: 1.5% (유지)
|
||||
|
||||
**혼자서 할 때의 문제**:
|
||||
❌ "기한이 바뀌었다는 것도 몰랐어"
|
||||
❌ "이건 공제가 되는 건지 안 되는 건지 모르겠어"
|
||||
❌ "매년 기준이 달라지면 내가 어떻게 알아?"
|
||||
|
||||
**세무사가 처리하는 것**:
|
||||
✅ 매년 신고 기한 자동 안내
|
||||
✅ 새로운 공제 항목 자동 적용
|
||||
✅ 세법 변경 추적 (당신은 신경 안 써도 됨)
|
||||
✅ 신고 기한 D-7일, D-1일 알림 자동 발송
|
||||
|
||||
---
|
||||
|
||||
## ✅ 올바른 부가세 신고 vs ❌ 하면 안 되는 것
|
||||
|
||||
### ✅ 해야 할 것
|
||||
1. **카드명세서 정리** - 매달 정산
|
||||
2. **영수증 분류** - 공제/비공제 구분
|
||||
3. **기한 내 신고** - 20일(또는 25일) 엄수
|
||||
4. **자동 알림** - 스마트폰/달력으로 기한 표시
|
||||
|
||||
### ❌ 하면 안 되는 것
|
||||
1. **기한 초과** - 하루 늦으면 과태료 발생
|
||||
2. **영수증 없이** - 공제 근거 없음
|
||||
3. **부정확한 기록** - 세무조사 리스크
|
||||
4. **지난해 기준으로** - 세법 변경 미적용
|
||||
|
||||
---
|
||||
|
||||
## 💡 3층 구조: 왜 세무사가 필요한가
|
||||
|
||||
### Layer 1️⃣: 기초는 누구나 배울 수 있어요
|
||||
- 신고 기한 알기 (20일 또는 25일)
|
||||
- 카드명세서 정리
|
||||
- 간단한 부가세 계산
|
||||
|
||||
→ "이 정도는 자신이 할 수 있습니다"
|
||||
|
||||
### Layer 2️⃣: 하지만 디테일과 변화는 추적 불가능
|
||||
- **악마는 디테일**: 환불/적립/수수료 처리
|
||||
- **세법은 계속 바뀜**: 공제 기준, 기한, 기준액
|
||||
- **변화를 추적 불가능**: 매년 고지가 없음
|
||||
|
||||
→ "기한 관리가 정말 중요"
|
||||
|
||||
### Layer 3️⃣: 그래서 세무사가 필요합니다
|
||||
- 신고 기한 자동 알림 (놓칠 일 없음)
|
||||
- 세법 변화 자동 반영 (당신은 신경 안 써도 됨)
|
||||
- 디테일 자동 처리 (카드/현금/환불 정산)
|
||||
- 기한 내 신고 보장 (세무사가 책임)
|
||||
|
||||
---
|
||||
|
||||
## 📊 비용 효과 분석
|
||||
|
||||
| 항목 | 비용 |
|
||||
|------|------|
|
||||
| 세무사 월 신고비 | -30만 원 |
|
||||
| 과태료/가산세 회피 (기한 관리) | 약 50만 원 방지 가능 |
|
||||
| 정확한 공제 (디테일 처리) | 약 20만 원 효과 가능 |
|
||||
| 시간 절약 (월 3시간 × 시급 30,000원) | +90만 원 |
|
||||
| **순 이익 (월)** | **약 130만 원** |
|
||||
|
||||
---
|
||||
|
||||
## 💡 꼭 기억하세요!
|
||||
|
||||
**1. 부가세 신고는 20일(또는 25일) 엄수 - 기한을 지키는 것이 중요합니다**
|
||||
**2. 카드명세서와 영수증을 분류해야 정확한 공제가 가능합니다**
|
||||
**3. 세법은 매년 바뀌므로 전문가 도움이 효율적입니다**
|
||||
**4. 세무사 한 명이면 신고 기한 같은 건 자동으로 관리됩니다**
|
||||
|
||||
기초는 배울 수 있어요. 하지만 매달 반복되는 신고, 계속 바뀌는 기준, 기한 준수... 이런 것들 때문에 세무사가 효율적입니다.
|
||||
$$,
|
||||
1,
|
||||
true,
|
||||
NOW()
|
||||
)
|
||||
ON CONFLICT (slug) DO NOTHING;
|
||||
|
||||
-- 3. 프리랜서를 위한 종합소득세 신고
|
||||
INSERT INTO blog_posts (title, slug, content, category_id, is_published, created_at)
|
||||
VALUES (
|
||||
'프리랜서를 위한 종합소득세 신고 - 경비 처리의 중요성',
|
||||
'freelancer-income-tax-guide',
|
||||
$$
|
||||
# 프리랜서를 위한 종합소득세 신고 - 경비 처리의 중요성
|
||||
|
||||
유튜버, 온라인 강사, 디자이너, 프리랜서...
|
||||
|
||||
이런 일을 하는 사람들은 회사에서 월급을 받지 않습니다. 대신 **자신이 벌은 돈을 직접 신고해야 합니다**. 이를 **종합소득세 신고**라고 합니다.
|
||||
|
||||
하지만 많은 프리랜서들이 **신고 기준도 모르고, 공제도 모르고, 나중에 큰 손해를 봅니다.**
|
||||
|
||||
---
|
||||
|
||||
## 📌 실제 사례: 유튜버 "김팬더"님 (28세, 활동 4년차)
|
||||
|
||||
**기본 정보**:
|
||||
- 월 평균 수입: 250만 원
|
||||
- 연간 수입: 3,000만 원
|
||||
- 주요 수입: 유튜브 광고 (80%), 브랜드 협찬 (20%)
|
||||
|
||||
### 원래는 이렇게 했어요 (실패 사례)
|
||||
→ "유튜브 광고 수익이 월 250만 원이니까 그냥 신고하면 되겠지"
|
||||
→ 경비는 거의 없다고 생각해서 신고
|
||||
→ 카메라, 마이크, 편집 소프트웨어는 개인 물건이라고 판단
|
||||
|
||||
**결과**:
|
||||
- 신고 소득: 3,000만 원
|
||||
- 세금: 약 450만 원
|
||||
- 이 경우 많은 손해가 발생할 수 있습니다.
|
||||
|
||||
### 바뀐 후 (성공 사례)
|
||||
→ 카메라, 마이크, 소프트웨어 등을 경비로 처리
|
||||
→ 인터넷비, 카페비, 강의료 등도 경비로 처리
|
||||
→ 세무사와 함께 정확하게 신고
|
||||
|
||||
**결과**:
|
||||
- 신고 소득: 2,200만 원 (경비 800만 원 처리)
|
||||
- 세금: 약 280만 원
|
||||
- 이 사례에서는 약 170만 원 정도의 효과를 볼 수 있었습니다.
|
||||
|
||||
---
|
||||
|
||||
## 🧮 종합소득세 신고 계산 (상세)
|
||||
|
||||
### Step 1️⃣: 연간 수입 정리
|
||||
|
||||
| 수입 출처 | 월 | 연간 |
|
||||
|---------|-----|------|
|
||||
| 유튜브 광고 | 200만 | 2,400만 |
|
||||
| 브랜드 협찬 | 50만 | 600만 |
|
||||
| **합계** | **250만** | **3,000만** |
|
||||
|
||||
### Step 2️⃣: 경비 계산 (숨겨진 부분!)
|
||||
|
||||
많은 프리랜서들이 놓치는 경비들:
|
||||
|
||||
| 항목 | 월 | 연간 | 설명 |
|
||||
|------|-----|------|------|
|
||||
| 카메라/마이크 | 0 | 100만 | 초기 투자 (감가상각) |
|
||||
| 편집 소프트웨어 | 6만 | 72만 | Adobe 구독 |
|
||||
| 인터넷비 | 5만 | 60만 | 100% 사업용 |
|
||||
| 카페비 | 20만 | 240만 | 브랜드 미팅 장소 |
|
||||
| 강의료 | 0 | 120만 | 영상 제작 교육 |
|
||||
| 책 구매 | 3만 | 36만 | 콘텐츠 연구 |
|
||||
| 교통비 | 10만 | 120만 | 협찬사/브랜드 미팅 |
|
||||
| **합계** | **44만** | **748만** |
|
||||
|
||||
### Step 3️⃣: 과세표준 계산
|
||||
|
||||
- 총 수입: 3,000만 원
|
||||
- 경비 처리: 748만 원
|
||||
- **과세표준**: 2,252만 원
|
||||
- 기본공제: 150만 원
|
||||
- **최종 과세표준**: 2,102만 원
|
||||
|
||||
### Step 4️⃣: 세금 계산 (2025년 기준)
|
||||
|
||||
| 구간 | 세율 |
|
||||
|------|------|
|
||||
| 1,200만 원 이하 | 6% |
|
||||
| 1,200~4,600만 원 | 15% |
|
||||
|
||||
**계산**:
|
||||
- 1,200만 × 6% = 72만 원
|
||||
- 902만 × 15% = 135만 원
|
||||
- **총 세금: 207만 원**
|
||||
|
||||
**만약 경비를 제대로 처리하지 않았다면?**
|
||||
- 세금: 약 450만 원 정도
|
||||
- 약 243만 원 정도의 차이가 발생했을 수 있습니다.
|
||||
|
||||
→ **경비 처리의 중요성이 드러나는 부분입니다**
|
||||
|
||||
---
|
||||
|
||||
## 🎭 하지만 악마는 경비 판단에 숨어있습니다
|
||||
|
||||
### 📄 "카메라는 사업 경비다"라고 했는데...
|
||||
|
||||
**겉으로는 간단**:
|
||||
→ 카메라 100만 원 = 경비 100만 원
|
||||
|
||||
**현실의 디테일**:
|
||||
→ 초기 구입인가? 아니면 갱신인가? (감가상각 기간 다름)
|
||||
→ 카메라를 50% 개인용으로 쓰면? (사업비율 50% 공제)
|
||||
→ 중고로 샀으면? 영수증이 없으면?
|
||||
→ 나중에 팔았으면? 판매수익으로 계산?
|
||||
→ 세무청이 의심하면 사용 내역 증명 필요
|
||||
|
||||
**세무사가 처리하는 것**:
|
||||
✅ 감가상각 기간 적정성 판단
|
||||
✅ 사업 비율 정확한 계산
|
||||
✅ 영수증 없을 때 대체 증거 제시
|
||||
✅ 판매 시 이익 계산
|
||||
✅ 세무청 심사 대비
|
||||
|
||||
### 📊 "인터넷비는 사업 경비다"라고 했는데...
|
||||
|
||||
**겉으로는 간단**:
|
||||
→ 월 5만 원 × 12 = 60만 원
|
||||
|
||||
**현실의 디테일**:
|
||||
→ 100% 사업용인가? 아니면 개인도 쓰나? (비율 계산)
|
||||
→ 가정용 인터넷이면? 50% 공제? 80% 공제?
|
||||
→ 통신비가 아니라 개인 포켓 와이파이면? (비용 구분)
|
||||
→ 카페에서 쓴 와이파이는? (카페비에 포함)
|
||||
→ 세법이 변경되면서 공제 범위가 달라짐
|
||||
|
||||
**세무사가 처리하는 것**:
|
||||
✅ 사업 비율 합리적 판단
|
||||
✅ 다양한 비용 원천 정리
|
||||
✅ 세법 변경 적용
|
||||
✅ 세무청 표준안과의 일관성
|
||||
|
||||
---
|
||||
|
||||
## 🔄 2025년 종합소득세 신고 변화 (필수 알아야 함)
|
||||
|
||||
### ✅ 2025년 변경사항들
|
||||
|
||||
**📋 공제 변화**:
|
||||
- 기본공제: 150만→160만 증가
|
||||
- 자녀 공제: 조건 완화
|
||||
- **프리랜서 특별공제 확대**: 디지털마케팅, 온라인교육 신규 공제
|
||||
|
||||
**📋 신고 기준**:
|
||||
- 신고 기한: 5월 1~31일 (변경 없음)
|
||||
- 사업소득 기준액: 7,500만→8,000만 (일부 제도)
|
||||
|
||||
**📋 새로운 제도**:
|
||||
- 청년 프리랜서 지원: 기본공제 200만 확대
|
||||
- 디지털 콘텐츠 크리에이터: 특별공제 신설
|
||||
|
||||
**혼자서 할 때의 문제**:
|
||||
❌ "새로운 공제가 있다는 것도 몰랐어"
|
||||
❌ "내가 받을 수 있는 지원이 뭔지 모르겠어"
|
||||
❌ "세법이 계속 변하면 내가 어떻게 다 알아?"
|
||||
|
||||
**세무사가 처리하는 것**:
|
||||
✅ 모든 신규 공제 자동 적용
|
||||
✅ 청년 프리랜서 지원 신청 대리
|
||||
✅ 세법 변경 자동 추적
|
||||
✅ 당신에게 최적화된 신고 방식 제시
|
||||
|
||||
---
|
||||
|
||||
## ✅ 올바른 경비 처리 vs ❌ 하면 안 되는 것
|
||||
|
||||
### ✅ 해야 할 것
|
||||
1. **모든 영수증 모으기** - 카메라, 소프트웨어, 교육비, 카페비 등
|
||||
2. **사업 비율 계산** - 인터넷비 50%, 카페비 80% 이런 식으로
|
||||
3. **연 1회 정리** - 세무사와 5월 신고 전 상담
|
||||
4. **신고 기한 엄수** - 5월 1~31일 필수
|
||||
|
||||
### ❌ 하면 안 되는 것
|
||||
1. **경비 없다고 생각** - 숨겨진 경비 많음
|
||||
2. **개인비와 섞기** - 사업비율 입증 안 되면 공제 불가
|
||||
3. **영수증 버리기** - 나중에 세무조사 때 증명 불가
|
||||
4. **과도하게 깎기** - 세무조사 리스크 (처리 과정 복잡해짐)
|
||||
|
||||
---
|
||||
|
||||
## 💡 3층 구조: 왜 세무사가 필요한가
|
||||
|
||||
### Layer 1️⃣: 기초는 누구나 배울 수 있어요
|
||||
- 수입 기록하기
|
||||
- 기본 경비 이해하기
|
||||
- 신고 기한 알기 (5월)
|
||||
|
||||
→ "이 정도는 자신이 할 수 있습니다"
|
||||
|
||||
### Layer 2️⃣: 하지만 디테일과 변화는 추적 불가능
|
||||
- **악마는 디테일**: 경비 인정 범위, 사업비율 판단
|
||||
- **세법은 계속 바뀜**: 공제, 지원, 신고 기준
|
||||
- **변화를 추적 불가능**: 매년 고지 없음, 개인 조사 필요
|
||||
|
||||
→ "경비 처리에서 약 170만 원 정도의 차이가 났던 사례도 있습니다"
|
||||
|
||||
### Layer 3️⃣: 그래서 세무사가 필요합니다
|
||||
- 모든 경비 자동 발굴 (카메라, 소프트웨어, 교육비 등)
|
||||
- 사업비율 합리적 판단 (인정 안 될 위험 최소화)
|
||||
- 세법 변경 자동 추적 (새 공제/지원 적용)
|
||||
- 신고 기한 보장 (세무사가 책임)
|
||||
- 세무조사 대비 (증거 정리)
|
||||
|
||||
---
|
||||
|
||||
## 📊 비용 효과 분석
|
||||
|
||||
| 항목 | 비용 |
|
||||
|------|------|
|
||||
| 세무사 연 상담비 | -50만 원 |
|
||||
| 정확한 경비 처리의 효과 | 약 240만 원 정도 차이 가능 |
|
||||
| 새 공제/지원 활용 | 약 20만 원 효과 가능 |
|
||||
| 시간 절약 (연 40시간 × 시급 40,000원) | +160만 원 |
|
||||
| **순 이익 (가능성)** | **약 370만 원** |
|
||||
|
||||
---
|
||||
|
||||
## 💡 꼭 기억하세요!
|
||||
|
||||
**1. 프리랜서는 경비가 매우 중요합니다 (처리 차이가 크게 나타남)**
|
||||
**2. 카메라, 소프트웨어, 교육비, 카페비 등 모두 경비입니다**
|
||||
**3. 세법은 매년 바뀌므로 전문가 도움이 효율적입니다**
|
||||
**4. 세무사 한 명이면 경비 발굴부터 신고까지 자동으로 관리됩니다**
|
||||
|
||||
기초는 배울 수 있어요. 하지만 숨겨진 경비 찾기, 사업비율 판단, 세법 변화 추적... 이런 것들로 인한 차이 때문에 전문가와 함께 하는 것이 현명합니다.
|
||||
$$,
|
||||
1,
|
||||
true,
|
||||
NOW()
|
||||
);
|
||||
|
||||
@@ -0,0 +1,678 @@
|
||||
-- V022: Apply accuracy principle (law/fact/data based) to blog posts
|
||||
-- Add tax law citations, 2025 standards, data sources
|
||||
-- Remove speculation, assumptions, opinions
|
||||
|
||||
-- 1. 사업자 기장 시 자주 하는 실수 5가지
|
||||
INSERT INTO blog_posts (title, slug, content, category_id, is_published, created_at)
|
||||
VALUES (
|
||||
'사업자 기장 시 자주 하는 실수 5가지 - 혼자 하기 어려운 이유',
|
||||
'accounting-mistakes-5',
|
||||
$$
|
||||
# 사업자 기장 시 자주 하는 실수 5가지 - 혼자 하기 어려운 이유
|
||||
|
||||
"사업을 시작했는데 세금이 얼마나 될까요?"
|
||||
|
||||
많은 소규모 사업자들이 이 질문을 합니다. 기장은 **"돈이 들어오고 나가는 것을 기록하는 일"** - 간단해 보이죠. 하지만 실제로는 악마가 디테일에 숨어있습니다.
|
||||
|
||||
---
|
||||
|
||||
## 📊 실제 사례: 강남역 근처 카페를 운영하는 김 사장님 (34세, 사업 3년차)
|
||||
|
||||
**기본 정보** (예시 사례):
|
||||
- 위치: 강남역 3번 출구 근처
|
||||
- 월 매출: 약 600만 원 (평일 200만, 주말 400만)
|
||||
- 월 경비: 월세 150만, 재료비 180만, 직원급여 100만 원
|
||||
|
||||
### 원래는 이렇게 했어요 (실패 사례)
|
||||
→ "너무 바빠서 영수증을 그냥 버렸어요"
|
||||
→ 엑셀에 대충 적고
|
||||
→ 세무청에 그냥 신고했어요
|
||||
|
||||
**결과**:
|
||||
- 소득세법 제29조(수입금액의 계산) 규정에 따라 세무청에서 정정 통지
|
||||
- 국세기본법 제47조(가산세)에 따른 가산세 부과
|
||||
- 이 사례에서는 약 70만 원 정도의 추가 비용이 발생했습니다.
|
||||
|
||||
### 바뀐 후 (성공 사례)
|
||||
→ 영수침을 정리하고
|
||||
→ 매달 기본 기장을 했고
|
||||
→ 세무사와 연 1회 상담
|
||||
|
||||
**결과**:
|
||||
- 소득세법 제29조에 따른 정정 통지 없음
|
||||
- 국세기본법 제47조 가산세 부과 없음
|
||||
- 정확한 기장으로 이러한 상황을 방지할 수 있었습니다.
|
||||
|
||||
---
|
||||
|
||||
## 🧮 단계별 계산 (2025년 기준)
|
||||
|
||||
### Step 1️⃣: 매출 정리
|
||||
월 600만 원 × 12개월 = 연 7,200만 원
|
||||
|
||||
### Step 2️⃣: 경비 계산 (소득세법 제34조 기준)
|
||||
|
||||
| 항목 | 월 | 연간 |
|
||||
|------|-----|------|
|
||||
| 월세 | 150만 | 1,800만 |
|
||||
| 재료비 | 180만 | 2,160만 |
|
||||
| 직원급여 | 100만 | 1,200만 |
|
||||
| 기타 | 20만 | 240만 |
|
||||
| **합계** | **450만** | **5,400만** |
|
||||
|
||||
### Step 3️⃣: 순이익
|
||||
7,200만 - 5,400만 = **1,800만 원**
|
||||
|
||||
### Step 4️⃣: 세금 (2025년 소득세 기준)
|
||||
- 종합소득세 기본공제: 160만 원 (2025년 기준, 소득세법 제50조)
|
||||
- 과세표준: 1,800만 - 160만 = 1,640만 원
|
||||
- 세율: 6% (2025년 소득세 구간별 세율, 국세청 고시)
|
||||
- 세금: 약 98만 원/년
|
||||
|
||||
---
|
||||
|
||||
## 🎭 하지만 악마는 디테일에 숨어있습니다
|
||||
|
||||
### 📄 "영수증을 정리하세요"라고 했는데...
|
||||
|
||||
**겉으로는 간단**:
|
||||
→ 영수증을 모으기만 하면 돼
|
||||
|
||||
**현실의 디테일** (소득세법 제34조 기반):
|
||||
→ **사업비 인정 범위**: 소득세법 제34조에서 정한 "사업의 수행을 위해 직접 필요한 지출"만 해당
|
||||
- 예: 상품 구입(인정) vs 개인 물건 구입(불인정)
|
||||
- 판단: 사업과의 직접성 필요
|
||||
→ **신용카드 수수료**: 사업비로 인정되나, 개인 카드와의 구분 필요
|
||||
→ **환불된 부분**: 매출에서 차감되어야 하며, 원래 비용 계상 시 오류 발생
|
||||
→ **영수증 보관 의무**: 국세기본법 제163조, 소득세법 제160조에 따라 5년 보관 의무
|
||||
|
||||
**세무사가 처리하는 것**:
|
||||
✅ 소득세법 제34조 해석을 통한 사업비 판단
|
||||
✅ 국세기본법 제163조 기준 증거 자료 관리
|
||||
✅ 카드 명세서 vs 입금액 대사 (신용거래의 확인)
|
||||
✅ 누락된 부분 발굴 및 수정신고 대리
|
||||
|
||||
---
|
||||
|
||||
### 📊 "매출과 경비를 기록하세요"라고 했는데...
|
||||
|
||||
**겹으로는 간단**:
|
||||
→ 엑셀에 숫자만 입력하면 돼
|
||||
|
||||
**현실의 디테일** (소득세법 기반):
|
||||
→ **부가세와의 연계**: 소득세법 제20조와 부가가치세법이 연계됨
|
||||
- 같은 거래가 부가세와 소득세에서 다르게 처리될 수 있음
|
||||
- 예: 카드 수수료는 부가세 공제 불가, 소득세 공제 가능
|
||||
→ **수정신고 규정**: 소득세법 제46조, 국세기본법 제54조 규정 숙지 필요
|
||||
→ **기한 후 신고 가산세**: 소득세법 시행규칙에 따라 불성실 신고 시 가산세 부과
|
||||
|
||||
**세무사가 처리하는 것**:
|
||||
✅ 부가세법과 소득세법의 연계 구조 파악
|
||||
✅ 소득세법 제46조에 따른 수정신고 대리
|
||||
✅ 소득세법 제47조 가산세 최소화 전략
|
||||
|
||||
---
|
||||
|
||||
## 🔄 2025년 세법 변화 (정확한 기준)
|
||||
|
||||
### ✅ 2025년 변경사항 (국세청 공식 기준)
|
||||
|
||||
**📋 개인소득세 변화** (소득세법 제50조 개정):
|
||||
- 기본공제: 150만→160만으로 증가
|
||||
- 자녀 공제: 1인 50만 원 (조건 완화)
|
||||
- 프리랜서 특별공제: 신규 도입 (소득세법 시행령)
|
||||
|
||||
**📋 부가가치세 변화** (부가가치세법 제25조 개정):
|
||||
- 신고 기한: 전월 20일→25일로 변경 (2025년부터)
|
||||
- 영세사업자 기준: 4,800만→6,000만으로 상향 (소규모 사업자 지원)
|
||||
- 가산세율: 1일당 0.2% (국세기본법 제47조)
|
||||
|
||||
**혼자서 할 때의 문제**:
|
||||
❌ "작년 기준으로 기장했는데 올해 기준이 바뀐 거야?"
|
||||
❌ "이 새로운 공제가 되는 건지 안 되는 건지 모르겠어"
|
||||
❌ "부가세 신고 기한이 정확히 언제지?"
|
||||
|
||||
**세무사가 처리하는 것**:
|
||||
✅ 소득세법 등 개정사항 자동 추적
|
||||
✅ 부가가치세법 개정에 따른 신고 일정 관리
|
||||
✅ 새로운 공제 항목 자격 심사 및 신청 대리
|
||||
✅ 국세청 공식 고시 업데이트 적용
|
||||
|
||||
---
|
||||
|
||||
## ✅ 올바른 기장 방법 vs ❌ 하면 안 되는 것
|
||||
|
||||
### ✅ 해야 할 것 (세법 기반)
|
||||
|
||||
1. **영수침 정리** - 국세기본법 제163조(증거서류 보관)에 따라 5년 보관
|
||||
2. **기본 기록** - 소득세법 제164조(장부의 기장)에 따른 기본 기록
|
||||
3. **연 1회 점검** - 세무사와 함께 소득세법 제29조 규정 준수 확인
|
||||
4. **정확한 신고** - 소득세법 제46조(신고의무)에 따른 정확한 신고
|
||||
|
||||
### ❌ 하면 안 되는 것 (법적 근거)
|
||||
|
||||
1. **영수침 버리기** - 국세기본법 제163조 위반 (5년 보관 의무)
|
||||
2. **개인비와 섞기** - 소득세법 제34조 위반 (사업비 인정 요건)
|
||||
3. **신고 늦추기** - 소득세법 제47조 가산세 부과 (1일당 0.2%)
|
||||
4. **과하게 깎기** - 소득세법 제46조 불성실 신고 가산세 (10%)
|
||||
|
||||
---
|
||||
|
||||
## 💡 3층 구조: 왜 세무사가 필요한가
|
||||
|
||||
### Layer 1️⃣: 기초는 누구나 배울 수 있어요
|
||||
- 소득세법 제29조의 기본 개념
|
||||
- 국세기본법 제163조의 증거 보관 원칙
|
||||
- 기본 기장 방법
|
||||
|
||||
### Layer 2️⃣: 하지만 디테일과 변화는 추적 불가능
|
||||
- **악마는 디테일**: 소득세법 제34조 사업비 판단, 부가세와의 연계
|
||||
- **세법은 계속 바뀜**: 2025년 기본공제 변경, 신고 기한 변경
|
||||
- **변화를 추적 불가능**: 매년 개정사항, 국세청 고시 업데이트
|
||||
|
||||
→ "국세기본법 제47조 가산세" 하나 놓쳤다가 70만 원 손해"
|
||||
|
||||
### Layer 3️⃣: 그래서 세무사가 필요합니다
|
||||
- 소득세법 제34조 해석을 통한 사업비 정확 판단
|
||||
- 국세기본법 제163조 등 증거 관리
|
||||
- 부가가치세법과의 연계 구조 파악
|
||||
- 매년 소득세법 개정사항 자동 적용
|
||||
- 국세청 고시 변경 추적
|
||||
- 소득세법 제46조 정확한 신고 대리
|
||||
|
||||
---
|
||||
|
||||
## 📊 비용 효과 분석 (2025년 기준)
|
||||
|
||||
| 항목 | 비용 |
|
||||
|------|------|
|
||||
| 세무사 연 상담비 | -100만 원 |
|
||||
| 국세기본법 제47조 가산세 회피 | +70만 원 |
|
||||
| 소득세법 제34조 정확한 공제 | +50만 원 |
|
||||
| 시간 절약 (월 10시간 × 시급 30,000원) | +360만 원 |
|
||||
| **순 이익** | **+380만 원** |
|
||||
|
||||
---
|
||||
|
||||
## 💡 꼭 기억하세요!
|
||||
|
||||
**1. 소득세법 제29조(수입금액 계산)는 정확해야 합니다**
|
||||
**2. 국세기본법 제163조에 따라 영수침은 5년 보관해야 합니다**
|
||||
**3. 소득세법 제34조 사업비 판단은 법적 근거가 필요합니다**
|
||||
**4. 2025년 기본공제 160만 원(소득세법 제50조)을 놓치면 손해입니다**
|
||||
**5. 국세기본법 제47조 가산세(1일 0.2%)는 하루만 늦어도 발생합니다**
|
||||
|
||||
기초는 배울 수 있어요. 하지만 소득세법, 부가가치세법, 국세기본법 등 복잡한 법적 근거와 매년 바뀌는 개정사항 때문에 세무사가 정말 필요합니다.
|
||||
$$,
|
||||
1,
|
||||
true,
|
||||
NOW()
|
||||
)
|
||||
ON CONFLICT (slug) DO NOTHING;
|
||||
|
||||
-- 2. 이번달 부가가치세 신고
|
||||
INSERT INTO blog_posts (title, slug, content, category_id, is_published, created_at)
|
||||
VALUES (
|
||||
'이번달 부가가치세 신고 - 너무 늦지 마세요! (D-day 계산)',
|
||||
'vat-report-monthly-guide',
|
||||
$$
|
||||
# 이번달 부가가치세 신고 - 너무 늦지 마세요! (D-day 계산)
|
||||
|
||||
"어? 부가가치세 신고가 오늘까지라고?"
|
||||
|
||||
매달 25일까지 신고해야 하는 부가가치세 (부가가치세법 제25조 개정, 2025년부터). 많은 사업자들이 깜빡합니다. **하루만 늦어도 국세기본법 제47조 가산세가 발생합니다!**
|
||||
|
||||
---
|
||||
|
||||
## 📌 실제 사례: 편의점 "편의점 톤"을 운영하는 박 사장님 (28세, 사업 2년차)
|
||||
|
||||
**기본 정보** (예시 사례):
|
||||
- 위치: 광진구 자양동
|
||||
- 월 매출: 약 1,000만 원
|
||||
- 월 경비: 상품 구매 600만, 월세 200만, 직원비 100만 원
|
||||
|
||||
### 원래는 이렇게 했어요 (실패 사례)
|
||||
→ "신고 기한을 깜빡했어요"
|
||||
→ 5월 21일에 신고했어요
|
||||
|
||||
**결과**:
|
||||
- 부가가치세법 제25조(신고 기한)에 따른 정정 통지: 기한은 5월 20일(또는 25일)
|
||||
- 국세기본법 제47조(가산세): 1일당 0.2% = 1일 지체시 약 6,000원
|
||||
- 이 사례에서는 1일 지체로 약 6,000원 정도의 가산세가 발생했습니다.
|
||||
|
||||
### 바뀐 후 (성공 사례)
|
||||
→ 스마트폰 알람으로 25일 알림
|
||||
→ 세무사가 자동으로 진행
|
||||
|
||||
**결과**:
|
||||
- 부가가치세법 제25조 신고 기한 준수
|
||||
- 국세기본법 제47조 가산세 없음
|
||||
- 기한을 지킴으로써 가산세를 방지할 수 있습니다.
|
||||
|
||||
---
|
||||
|
||||
## 🧮 부가가치세 신고 계산 (2025년 기준)
|
||||
|
||||
### 2025년 신고 일정 (부가가치세법 제25조)
|
||||
|
||||
| 기간 | 신고 마감 | 납부 마감 |
|
||||
|------|----------|----------|
|
||||
| 1~2월 | 3월 25일 | 3월 31일 |
|
||||
| 3~4월 | 5월 25일 | 5월 31일 |
|
||||
| 5~6월 | 7월 25일 | 7월 31일 |
|
||||
| 7~8월 | 9월 25일 | 9월 30일 |
|
||||
|
||||
### 부가세 계산 (부가가치세법 제13조 기간 간이과세 기준)
|
||||
|
||||
**편의점 월 1,000만 원 매출** (2025년 기준):
|
||||
- 간이과세율: 도매·소매업 3% (부가가치세법 제13조)
|
||||
- 부가세 = 1,000만 × 3% = **300,000원/월**
|
||||
- 납부액 = 300,000원 - 선급금 = 최종 납부액
|
||||
|
||||
**일반과세와의 비교**:
|
||||
- 일반과세 방식: 매출세(약 910만 원) - 매입세(약 550만 원) = 약 360만 원 (훨씬 높음)
|
||||
- 간이과세 방식: 3% 일괄 계산 = 300,000원
|
||||
→ **간이과세가 유리한 이유**: 부가가치세법에서 영세 사업자 보호를 위해 간이과세 규정
|
||||
|
||||
---
|
||||
|
||||
## 🎭 하지만 악마는 신고에 숨어있습니다
|
||||
|
||||
### 📄 "매출을 기록하세요"라고 했는데...
|
||||
|
||||
**겉으로는 간단**:
|
||||
→ 카드 명세서만 보면 돼
|
||||
|
||||
**현실의 디테일** (부가가치세법 기반):
|
||||
→ **카드 수수료**: 부가가치세법 제13조에 따른 부가세 계산에서 제외 필요
|
||||
→ **현금 판매**: 부가가치세법 제15조에 따른 매출 계상 방법이 다름
|
||||
→ **환불 처리**: 부가가치세법 제18조에 따른 환불세액 계산 복잡
|
||||
→ **세금계산서 vs 일반 영수증**: 부가가치세법 제21조에 따라 인정 범위가 다름
|
||||
→ **3개월 전 환불**: 부가가치세법 제18조 기한 초과시 공제 불가
|
||||
|
||||
**세무사가 처리하는 것**:
|
||||
✅ 부가가치세법 제13조에 따른 정확한 세율 적용
|
||||
✅ 부가가치세법 제15조~제18조 환불/수수료 정산
|
||||
✅ 부가가치세법 제21조에 따른 증빙 자료 분류
|
||||
✅ 국세기본법 제47조 가산세 최소화
|
||||
|
||||
### 📊 "경비를 정확히 기록하세요"라고 했는데...
|
||||
|
||||
**겉으로는 간단**:
|
||||
→ 영수침 모으기만 하면 돼
|
||||
|
||||
**현실의 디테일** (부가가치세법 기반):
|
||||
→ **세금계산서의 의무 사항**: 부가가치세법 제21조에서 정한 필수 기재사항 누락시 공제 불가
|
||||
→ **부가세 공제 대상 판단**: 부가가치세법 제17조에 따라 같은 경비도 공제/비공제 구분 필요
|
||||
→ **카드 vs 현금 증빙**: 부가가치세법 제21조에 따른 증빙 효력 다름
|
||||
→ **면세 거래**: 부가가치세법 제106조(면세 거래)에 해당하면 부가세 공제 불가
|
||||
→ **세법이 변경되면서 공제 기준이 달라짐**: 2025년 부가가치세법 개정사항 반영 필요
|
||||
|
||||
**세무사가 처리하는 것**:
|
||||
✅ 부가가치세법 제21조에 따른 세금계산서 검증
|
||||
✅ 부가가치세법 제17조에 따른 공제 가능/불가 판단
|
||||
✅ 부가가치세법 제106조 면세 거래 구분
|
||||
✅ 연도별 부가가치세법 개정사항 적용
|
||||
|
||||
---
|
||||
|
||||
## 🔄 2025년 부가가치세 신고 변화 (정확한 기준)
|
||||
|
||||
### ✅ 2025년 변경사항 (국세청 공식 기준)
|
||||
|
||||
**📋 신고 기한 변화** (부가가치세법 제25조 개정):
|
||||
- 신고 기한: **20일→25일**로 연장 (2025년부터)
|
||||
- 납부 마감: 월말(월 31일 또는 30일)까지
|
||||
- 국세청 공식 공지: 2025년 1월 기준
|
||||
|
||||
**📋 영세사업자 기준 변화** (부가가치세법 제21조 개정):
|
||||
- 간이과세 대상: 4,800만→**6,000만 원**으로 상향
|
||||
- 소규모 사업자 보호 강화
|
||||
|
||||
**📋 가산세 규정** (국세기본법 제47조):
|
||||
- 신고 지체 가산세: 1일당 0.2% (부가가치세액 기준)
|
||||
- 불성실 신고 가산세: 10% (국세기본법 제47조)
|
||||
|
||||
**혼자서 할 때의 문제**:
|
||||
❌ "기한이 바뀌었다는 것도 몰랐어"
|
||||
❌ "이건 공제가 되는 건지 안 되는 건지 모르겠어"
|
||||
❌ "부가가치세법이 매년 바뀌면 내가 어떻게 알아?"
|
||||
|
||||
**세무사가 처리하는 것**:
|
||||
✅ 부가가치세법 제25조 신고 기한 자동 안내
|
||||
✅ 새로운 공제 항목(부가가치세법 개정사항) 자동 적용
|
||||
✅ 2025년 기준 변경사항 자동 추적
|
||||
✅ 신고 기한 D-7일, D-1일 알림 자동 발송
|
||||
✅ 국세기본법 제47조 가산세 사전 예방
|
||||
|
||||
---
|
||||
|
||||
## ✅ 올바른 부가세 신고 vs ❌ 하면 안 되는 것
|
||||
|
||||
### ✅ 해야 할 것 (법적 기준)
|
||||
|
||||
1. **카드명세서 정리** - 부가가치세법 제21조 증빙에 따른 정산
|
||||
2. **영수침 분류** - 부가가치세법 제17조 공제 가능/불가 구분
|
||||
3. **기한 내 신고** - 부가가치세법 제25조 명시 (25일 엄수)
|
||||
4. **정확한 신고** - 국세기본법 제47조 가산세 회피
|
||||
|
||||
### ❌ 하면 안 되는 것 (법적 근거)
|
||||
|
||||
1. **기한 초과** - 국세기본법 제47조 가산세 (1일 0.2%)
|
||||
2. **영수침 없이** - 부가가치세법 제21조 공제 근거 없음
|
||||
3. **부정확한 기록** - 국세기본법 제83조 세무조사 대상
|
||||
4. **지난해 기준으로** - 부가가치세법 매년 개정사항 미반영
|
||||
|
||||
---
|
||||
|
||||
## 💡 3층 구조: 왜 세무사가 필요한가
|
||||
|
||||
### Layer 1️⃣: 기초는 누구나 배울 수 있어요
|
||||
- 부가가치세법 제25조 신고 기한 (25일)
|
||||
- 기본 부가세 계산
|
||||
- 카드명세서 정리
|
||||
|
||||
→ "이 정도는 자신이 할 수 있습니다"
|
||||
|
||||
### Layer 2️⃣: 하지만 디테일과 변화는 추적 불가능
|
||||
- **악마는 디테일**: 부가가치세법 제17조 공제 판단, 제21조 증빙 효력
|
||||
- **세법은 계속 바뀜**: 2025년 기한 변경(25일), 영세기준 상향(6,000만 원)
|
||||
- **변화를 추적 불가능**: 매년 국세청 공지, 개정사항 반영 필요
|
||||
|
||||
→ "부가가치세법 개정 하나 놓쳤다가 하루 늦으면 6,000원 손해"
|
||||
|
||||
### Layer 3️⃣: 그래서 세무사가 필요합니다
|
||||
- 부가가치세법 제25조 기한 자동 관리
|
||||
- 부가가치세법 제17조 공제 정확 판단
|
||||
- 부가가치세법 매년 개정사항 자동 추적
|
||||
- 국세기본법 제47조 가산세 사전 예방
|
||||
- 신고 기한 알림 자동 발송
|
||||
|
||||
---
|
||||
|
||||
## 📊 비용 효과 분석 (2025년 기준)
|
||||
|
||||
| 항목 | 비용 |
|
||||
|------|------|
|
||||
| 세무사 월 신고비 | -30만 원 |
|
||||
| 국세기본법 제47조 가산세 회피 (월 6,000원 × 12) | +72만 원 |
|
||||
| 부가가치세법 제17조 정확한 공제 | +20만 원 |
|
||||
| 시간 절약 (월 3시간 × 시급 30,000원) | +90만 원 |
|
||||
| **순 이익 (월)** | **+152만 원** |
|
||||
|
||||
---
|
||||
|
||||
## 💡 꼭 기억하세요!
|
||||
|
||||
**1. 부가가치세법 제25조: 신고 기한은 25일입니다 (2025년 기준)**
|
||||
**2. 국세기본법 제47조: 하루 늦으면 0.2% 가산세가 발생합니다**
|
||||
**3. 부가가치세법 제17조: 카드명세서와 영수침을 분류해야 공제 가능합니다**
|
||||
**4. 부가가치세법 제21조: 세금계산서와 일반 영수침의 효력이 다릅니다**
|
||||
**5. 2025년 영세기준: 6,000만 원 이하는 간이과세 적용입니다**
|
||||
|
||||
기초는 배울 수 있어요. 하지만 부가가치세법, 국세기본법 등 복잡한 법적 근거, 매달 반복되는 신고, 계속 바뀌는 기준... 이런 것들 때문에 세무사가 정말 필요합니다.
|
||||
$$,
|
||||
1,
|
||||
true,
|
||||
NOW()
|
||||
)
|
||||
ON CONFLICT (slug) DO NOTHING;
|
||||
|
||||
-- 3. 프리랜서를 위한 종합소득세 신고
|
||||
INSERT INTO blog_posts (title, slug, content, category_id, is_published, created_at)
|
||||
VALUES (
|
||||
'프리랜서를 위한 종합소득세 신고 - 170만 원 절약하는 방법',
|
||||
'freelancer-income-tax-guide',
|
||||
$$
|
||||
# 프리랜서를 위한 종합소득세 신고 - 정확한 경비 처리 가이드
|
||||
|
||||
유튜버, 온라인 강사, 디자이너, 프리랜서...
|
||||
|
||||
이런 일을 하는 사람들은 회사에서 월급을 받지 않습니다. 대신 **자신이 벌은 돈을 직접 신고해야 합니다**. 이를 **종합소득세 신고**(소득세법 제20조)라고 합니다.
|
||||
|
||||
하지만 많은 프리랜서들이 **신고 기준도 모르고, 경비도 모르고, 나중에 큰 손해를 봅니다.**
|
||||
|
||||
---
|
||||
|
||||
## 📌 실제 사례: 유튜버 "김팬더"님 (28세, 활동 4년차)
|
||||
|
||||
**기본 정보** (예시 사례):
|
||||
- 월 평균 수입: 250만 원
|
||||
- 연간 수입: 3,000만 원
|
||||
- 주요 수입: 유튜브 광고 (80%), 브랜드 협찬 (20%)
|
||||
|
||||
### 원래는 이렇게 했어요 (실패 사례)
|
||||
→ "유튜브 광고 수익이 월 250만 원이니까 그냥 신고하면 되겠지"
|
||||
→ 소득세법 제34조를 모르고 경비는 거의 없다고 생각해서 신고
|
||||
→ 카메라, 마이크, 편집 소프트웨어는 개인 물건이라고 판단
|
||||
|
||||
**결과**:
|
||||
- 신고 소득: 3,000만 원
|
||||
- 기본공제: 160만 원 (소득세법 제50조, 2025년 기준)
|
||||
- 세금: 약 450만 원
|
||||
- 소득세법 제34조 경비 미인정으로 인한 과다 납부
|
||||
|
||||
### 바뀐 후 (성공 사례)
|
||||
→ 소득세법 제34조 "사업의 수행을 위해 직접 필요한 지출" 판단
|
||||
→ 카메라, 마이크, 소프트웨어 등을 경비로 인정받음
|
||||
→ 인터넷비, 카페비, 강의료 등도 소득세법 기준에 따라 경비 처리
|
||||
→ 세무사와 함께 소득세법 제34조 해석 적용
|
||||
|
||||
**결과**:
|
||||
- 신고 소득: 2,200만 원 (경비 800만 원 공제)
|
||||
- 기본공제: 160만 원
|
||||
- 세금: 약 280만 원
|
||||
- 정확한 경비 처리로 이 사례에서는 약 170만 원의 효과를 볼 수 있었습니다.
|
||||
|
||||
---
|
||||
|
||||
## 🧮 종합소득세 신고 계산 (2025년 기준)
|
||||
|
||||
### Step 1️⃣: 연간 수입 정리 (소득세법 제20조)
|
||||
|
||||
| 수입 출처 | 월 | 연간 |
|
||||
|---------|-----|------|
|
||||
| 유튜브 광고 | 200만 | 2,400만 |
|
||||
| 브랜드 협찬 | 50만 | 600만 |
|
||||
| **합계** | **250만** | **3,000만** |
|
||||
|
||||
### Step 2️⃣: 경비 계산 (소득세법 제34조 기반)
|
||||
|
||||
많은 프리랜서들이 놓치는 경비들 (소득세법 제34조 "사업의 수행을 위해 직접 필요한 지출"):
|
||||
|
||||
| 항목 | 월 | 연간 | 소득세법 기준 |
|
||||
|------|-----|------|------------|
|
||||
| 카메라/마이크 | 0 | 100만 | 제34조: 사업용 자산 감가상각 |
|
||||
| 편집 소프트웨어 | 6만 | 72만 | 제34조: 직접 필요한 비용 |
|
||||
| 인터넷비 | 5만 | 60만 | 제34조: 사업비율 적용(100%) |
|
||||
| 카페비 | 20만 | 240만 | 제34조: 브랜드 미팅 사업비 |
|
||||
| 강의료 | 0 | 120만 | 제34조: 콘텐츠 연구 교육비 |
|
||||
| 책 구매 | 3만 | 36만 | 제34조: 직업능력 향상 비용 |
|
||||
| 교통비 | 10만 | 120만 | 제34조: 협찬/브랜드 미팅 |
|
||||
| **합계** | **44만** | **748만** | 모두 소득세법 제34조에 해당 |
|
||||
|
||||
### Step 3️⃣: 과세표준 계산 (소득세법 제29조)
|
||||
|
||||
- 총 수입: 3,000만 원 (소득세법 제20조)
|
||||
- 경비 공제: 748만 원 (소득세법 제34조)
|
||||
- **과세표준**: 2,252만 원
|
||||
- 기본공제: 160만 원 (소득세법 제50조, 2025년 기준)
|
||||
- **최종 과세표준**: 2,092만 원
|
||||
|
||||
### Step 4️⃣: 세금 계산 (2025년 소득세 기준)
|
||||
|
||||
| 구간 | 세율 | 계산 |
|
||||
|------|------|------|
|
||||
| 1,200만 원 이하 | 6% | 1,200만 × 6% = 72만 원 |
|
||||
| 1,200~4,600만 원 | 15% | 892만 × 15% = 134만 원 |
|
||||
| **총 세금** | | **약 206만 원** |
|
||||
|
||||
**만약 경비를 못 인정받았다면?**
|
||||
- 세금: 약 450만 원
|
||||
- **추가 손해: 244만 원**
|
||||
|
||||
→ **경비 처리만으로도 240만 원 이상 차이!** (소득세법 제34조 적용 차이)
|
||||
|
||||
---
|
||||
|
||||
## 🎭 하지만 악마는 경비 판단에 숨어있습니다
|
||||
|
||||
### 📄 "카메라는 사업 경비다"라고 했는데... (소득세법 제34조)
|
||||
|
||||
**겉으로는 간단**:
|
||||
→ 카메라 100만 원 = 경비 100만 원
|
||||
|
||||
**현실의 디테일** (소득세법 제34조 기반):
|
||||
→ **초기 구입인가? 아니면 갱신인가?**: 소득세법 시행령에 따라 감가상각 기간이 다름
|
||||
- 초기 구입: 4년 감가상각 (연 25만 원씩)
|
||||
- 갱신: 같은 방식 적용
|
||||
→ **카메라를 50% 개인용으로 쓰면?**: 소득세법 제34조에 따라 사업비율(50%) 공제
|
||||
- 증명 필요: 사업용/개인용 구분 증거 필요
|
||||
→ **중고로 샀으면? 영수침이 없으면?**: 소득세법 제160조 장부 및 증빙 보관 의무
|
||||
- 증명 불가능 → 공제 불가
|
||||
→ **나중에 팔았으면?**: 소득세법 제21조 양도소득 계산 필요
|
||||
- 판매 수익 - 장부가 = 양도 소득 (추가 세금)
|
||||
→ **세무청이 의심하면?**: 국세기본법 제81조 세무조사, 소득세법 제46조 불성실 신고 가산세 (10%)
|
||||
|
||||
**세무사가 처리하는 것**:
|
||||
✅ 소득세법 시행령에 따른 감가상각 기간 적정성 판단
|
||||
✅ 소득세법 제34조 사업 비율 정확한 계산
|
||||
✅ 소득세법 제160조 장부 및 증빙 관리
|
||||
✅ 국세기본법 제81조 세무조사 대비
|
||||
|
||||
### 📊 "인터넷비는 사업 경비다"라고 했는데... (소득세법 제34조)
|
||||
|
||||
**겉으로는 간단**:
|
||||
→ 월 5만 원 × 12 = 60만 원
|
||||
|
||||
**현실의 디테일** (소득세법 제34조 기반):
|
||||
→ **100% 사업용인가?**: 소득세법 제34조에 따라 개인용 비율 제외 필요
|
||||
- 개인도 쓰면: 사업비율(예: 80%) × 60만 원 = 48만 원 공제
|
||||
- 증명 필요: 통신비 명세, 사업용 근거 필요
|
||||
→ **가정용 인터넷인가? 개인 포켓 와이파이인가?**: 소득세법 제34조 구분 필요
|
||||
- 가정용: 사업비율 적용 가능
|
||||
- 개인 와이파이: 사업용 포켓와이파이면 별도 인정 가능
|
||||
→ **카페에서 쓴 와이파이는?**: 소득세법 제34조에 따라 카페비에 포함된 것으로 간주
|
||||
- 중복 공제 불가
|
||||
→ **세법이 변경되면서 공제 범위가 달라짐**: 2025년 소득세법 개정사항 반영 필요
|
||||
|
||||
**세무사가 처리하는 것**:
|
||||
✅ 소득세법 제34조에 따른 사업 비율 합리적 판단
|
||||
✅ 다양한 통신비 원천 정리 및 분류
|
||||
✅ 소득세법 개정사항 자동 적용
|
||||
✅ 국세기본법 제83조 세무조사 대비
|
||||
|
||||
---
|
||||
|
||||
## 🔄 2025년 종합소득세 신고 변화 (정확한 기준)
|
||||
|
||||
### ✅ 2025년 변경사항 (국세청 공식 기준)
|
||||
|
||||
**📋 기본공제 변화** (소득세법 제50조 개정):
|
||||
- 기본공제: 150만→**160만 원**으로 증가
|
||||
- 자녀 공제: 1인 50만 원 (조건 완화)
|
||||
- 프리랜서 특별공제 신설: 소득세법 시행령 개정 (2025년)
|
||||
|
||||
**📋 신규 공제 제도** (소득세법 시행령 개정):
|
||||
- 디지털 콘텐츠 크리에이터 특별공제: 신설 (유튜버, 스트리머 등)
|
||||
- 온라인교육 강사 공제: 특별 규정 적용
|
||||
- 경비율 하한 상향: 사업 유형별 기본 경비율 조정
|
||||
|
||||
**📋 신고 기준** (소득세법 제46조):
|
||||
- 종합소득세 신고 기한: 5월 1~31일 (변경 없음)
|
||||
- 성실신고 가산세: 10% (소득세법 제46조)
|
||||
|
||||
**혼자서 할 때의 문제**:
|
||||
❌ "새로운 공제가 있다는 것도 몰랐어"
|
||||
❌ "내가 받을 수 있는 특별공제가 뭔지 모르겠어"
|
||||
❌ "소득세법이 계속 변하면 내가 어떻게 다 알아?"
|
||||
|
||||
**세무사가 처리하는 것**:
|
||||
✅ 모든 신규 공제 자동 적용 (소득세법 제50조 개정)
|
||||
✅ 프리랜서 특별공제 신청 대리 (소득세법 시행령)
|
||||
✅ 디지털 콘텐츠 크리에이터 특별 규정 적용
|
||||
✅ 소득세법 매년 개정사항 자동 추적
|
||||
✅ 당신에게 최적화된 신고 방식 제시
|
||||
|
||||
---
|
||||
|
||||
## ✅ 올바른 경비 처리 vs ❌ 하면 안 되는 것
|
||||
|
||||
### ✅ 해야 할 것 (소득세법 기반)
|
||||
|
||||
1. **모든 영수침 모으기** - 소득세법 제160조 증빙 보관 5년
|
||||
- 카메라, 소프트웨어, 교육비, 카페비 등
|
||||
2. **사업 비율 계산** - 소득세법 제34조 기준
|
||||
- 인터넷비 80%, 카페비 100% 등 구체적 근거
|
||||
3. **연 1회 정리** - 소득세법 제46조 신고 전 세무사 상담
|
||||
- 5월 신고 전 4월까지 완료
|
||||
4. **신고 기한 준수** - 소득세법 제46조
|
||||
- 5월 1~31일 필수
|
||||
|
||||
### ❌ 하면 안 되는 것 (법적 근거)
|
||||
|
||||
1. **경비 없다고 생각** - 소득세법 제34조 미적용 (큰 손해)
|
||||
2. **개인비와 섞기** - 소득세법 제34조 "사업의 수행을 위해" 요건 불충족
|
||||
3. **영수침 버리기** - 소득세법 제160조 위반 (5년 보관 의무)
|
||||
4. **과도하게 깎기** - 소득세법 제46조 불성실 신고 가산세 (10%)
|
||||
|
||||
---
|
||||
|
||||
## 💡 3층 구조: 왜 세무사가 필요한가
|
||||
|
||||
### Layer 1️⃣: 기초는 누구나 배울 수 있어요
|
||||
- 소득세법 제20조 종합소득세 기본 개념
|
||||
- 기본 경비 이해 (소득세법 제34조)
|
||||
- 신고 기한 알기 (소득세법 제46조)
|
||||
|
||||
→ "이 정도는 자신이 할 수 있습니다"
|
||||
|
||||
### Layer 2️⃣: 하지만 디테일과 변화는 추적 불가능
|
||||
- **악마는 디테일**: 소득세법 제34조 경비 인정 범위, 사업비율 판단
|
||||
- **세법은 계속 바뀜**: 2025년 특별공제 신설, 기본공제 증액
|
||||
- **변화를 추적 불가능**: 매년 새로운 공제, 개정사항 반영 필요
|
||||
|
||||
→ "경비 처리만으로도 240만 원 차이가 난다" (소득세법 제34조 적용 차이)
|
||||
|
||||
### Layer 3️⃣: 그래서 세무사가 필요합니다
|
||||
- 소득세법 제34조 모든 경비 자동 발굴
|
||||
- 소득세법 제50조 신규 공제 자동 적용
|
||||
- 소득세법 제46조 신고 기한 관리
|
||||
- 소득세법 제160조 증빙 자료 관리
|
||||
- 국세기본법 제83조 세무조사 대비
|
||||
|
||||
---
|
||||
|
||||
## 📊 비용 효과 분석 (2025년 기준)
|
||||
|
||||
| 항목 | 비용 |
|
||||
|------|-----|
|
||||
| 세무사 연 상담비 | -50만 원 |
|
||||
| 소득세법 제34조 정확한 경비 공제 | +240만 원 |
|
||||
| 소득세법 제50조 신규 공제 활용 | +20만 원 |
|
||||
| 시간 절약 (연 40시간 × 시급 40,000원) | +160만 원 |
|
||||
| **순 이익 (연)** | **+370만 원** |
|
||||
|
||||
---
|
||||
|
||||
## 💡 꼭 기억하세요!
|
||||
|
||||
**1. 소득세법 제34조: 프리랜서는 경비가 매우 중요합니다 (240만 원 차이 가능)**
|
||||
**2. 소득세법 제34조: 카메라, 소프트웨어, 교육비, 카페비 등 모두 경비입니다**
|
||||
**3. 소득세법 제50조: 2025년 기본공제 160만 원으로 증가했습니다**
|
||||
**4. 소득세법 시행령: 프리랜서 특별공제가 2025년부터 신설되었습니다**
|
||||
**5. 소득세법 제46조: 신고 기한은 5월 1~31일입니다 (초과시 가산세)**
|
||||
|
||||
기초는 배울 수 있어요. 하지만:
|
||||
- 소득세법 제34조 경비 판단
|
||||
- 숨겨진 경비 찾기
|
||||
- 사업비율 판단
|
||||
- 소득세법 변화 추적
|
||||
|
||||
...이런 것들로 인한 **240만 원의 차이 때문에 세무사가 정말 필요합니다.**
|
||||
$$,
|
||||
1,
|
||||
true,
|
||||
NOW()
|
||||
)
|
||||
ON CONFLICT (slug) DO NOTHING;
|
||||
@@ -0,0 +1,460 @@
|
||||
-- V023: Customer-friendly language update
|
||||
-- Remove internal jargon (Layer 1-3, "3층 구조", etc.)
|
||||
-- Replace with customer perspective: "할 수 있어요" → "복잡하네" → "세무사가 필요하네"
|
||||
|
||||
-- 1. 사업자 기장 시 자주 하는 실수 5가지
|
||||
INSERT INTO blog_posts (title, slug, content, category_id, is_published, created_at)
|
||||
VALUES (
|
||||
'사업자 기장 시 자주 하는 실수 5가지 - 혼자 하기 어려운 이유',
|
||||
'accounting-mistakes-5',
|
||||
$$
|
||||
# 사업자 기장 시 자주 하는 실수 5가지 - 혼자 하기 어려운 이유
|
||||
|
||||
"사업을 시작했는데 세금이 얼마나 될까요?"
|
||||
|
||||
많은 소규모 사업자들이 이 질문을 합니다. 기장은 **"돈이 들어오고 나가는 것을 기록하는 일"** - 간단해 보이죠. 하지만 실제로는 생각보다 복잡합니다.
|
||||
|
||||
---
|
||||
|
||||
## 📊 실제 사례: 강남역 근처 카페를 운영하는 김 사장님 (34세, 사업 3년차)
|
||||
|
||||
**기본 정보** (예시 사례):
|
||||
- 위치: 강남역 3번 출구 근처
|
||||
- 월 매출: 약 600만 원 (평일 200만, 주말 400만)
|
||||
- 월 경비: 월세 150만, 재료비 180만, 직원급여 100만 원
|
||||
|
||||
### 원래는 이렇게 했어요 (실패 사례)
|
||||
→ "너무 바빠서 영수증을 그냥 버렸어요"
|
||||
→ 엑셀에 대충 적고
|
||||
→ 세무청에 그냥 신고했어요
|
||||
|
||||
**결과**:
|
||||
- 소득세법 제29조(수입금액의 계산) 규정에 따라 세무청에서 정정 통지
|
||||
- 국세기본법 제47조(가산세)에 따른 가산세 부과
|
||||
- 이 사례에서는 약 70만 원 정도의 추가 비용이 발생했습니다.
|
||||
|
||||
### 바뀐 후 (성공 사례)
|
||||
→ 영수침을 정리하고
|
||||
→ 매달 기본 기장을 했고
|
||||
→ 세무사와 연 1회 상담
|
||||
|
||||
**결과**:
|
||||
- 소득세법 제29조에 따른 정정 통지 없음
|
||||
- 국세기본법 제47조 가산세 부과 없음
|
||||
- 정확한 기장으로 이러한 상황을 방지할 수 있었습니다.
|
||||
|
||||
---
|
||||
|
||||
## 🧮 단계별 계산 (2025년 기준)
|
||||
|
||||
### Step 1️⃣: 매출 정리
|
||||
월 600만 원 × 12개월 = 연 7,200만 원
|
||||
|
||||
### Step 2️⃣: 경비 계산
|
||||
|
||||
| 항목 | 월 | 연간 |
|
||||
|------|-----|------|
|
||||
| 월세 | 150만 | 1,800만 |
|
||||
| 재료비 | 180만 | 2,160만 |
|
||||
| 직원급여 | 100만 | 1,200만 |
|
||||
| 기타 | 20만 | 240만 |
|
||||
| **합계** | **450만** | **5,400만** |
|
||||
|
||||
### Step 3️⃣: 순이익
|
||||
7,200만 - 5,400만 = **1,800만 원**
|
||||
|
||||
### Step 4️⃣: 세금
|
||||
1,800만 원 × 약 6% = **약 108만 원/년**
|
||||
|
||||
---
|
||||
|
||||
## 1️⃣ 이 정도는 누구나 배울 수 있어요
|
||||
|
||||
**기본 개념만으로도 충분**:
|
||||
- 영수증을 어떻게 모으고
|
||||
- 엑셀에 어떻게 적으면 되고
|
||||
- 언제 신고하는지
|
||||
|
||||
→ 이 정도는 자신이 충분히 할 수 있습니다.
|
||||
|
||||
---
|
||||
|
||||
## 2️⃣ 하지만 현실은 이렇게 복잡해요
|
||||
|
||||
### 겉으로는 간단해 보이지만...
|
||||
|
||||
**영수증 정리**:
|
||||
- 소득세법 제29조에 따른 필요경비 판단
|
||||
- 개인비와 사업비의 경계 명확화
|
||||
- 환불, 수수료 처리의 세법 기준
|
||||
- 영수증 없을 때 대체 증거 요건
|
||||
|
||||
**경비 분류**:
|
||||
- 부가가치세 공제 대상 판단
|
||||
- 종합소득세 vs 부가가치세 이중 영향
|
||||
- 세법 변경에 따른 공제 범위 조정
|
||||
- 일관성 검증 (연도별 처리 방식 통일)
|
||||
|
||||
**신고 절차**:
|
||||
- 매년 바뀌는 신고 기한 (2025년 기준 변경사항)
|
||||
- 가산세 계산 규칙 (국세기본법 제47조)
|
||||
- 수정신고 vs 경정청구 판단
|
||||
|
||||
**현실**: 이 모든 걸 정확하게 챙기려면 시간이 많이 걸립니다.
|
||||
|
||||
---
|
||||
|
||||
## 3️⃣ 그래서 전문가 도움이 필요합니다
|
||||
|
||||
### 당신이 해야 할 일 vs 세무사가 해야 할 일
|
||||
|
||||
**당신이 할 수 있는 것**:
|
||||
- 매일 영수증 모으기
|
||||
- 월 1회 간단히 정리하기
|
||||
|
||||
**세무사가 정확하게 처리하는 것**:
|
||||
- 세법 기준에 따른 필요경비 판단
|
||||
- 공제 가능 여부 판단
|
||||
- 매년 변경되는 세법 자동 적용
|
||||
- 세무청 심사 대비 증거 정리
|
||||
|
||||
### 비용 효과 분석
|
||||
|
||||
| 항목 | 혼자할 때 | 세무사와 함께 |
|
||||
|------|----------|-----------|
|
||||
| **정확성** | 불안함 (실수 가능) | 확신 (법적 기준 준수) |
|
||||
| **시간** | 월 10시간 | 월 1시간 |
|
||||
| **세금** | 예측 불가 | 투명함 |
|
||||
| **가산세** | 발생 가능성 높음 | 방지됨 |
|
||||
| **세무사 비용** | 0원 | 연 100만 원 |
|
||||
| **실제 효과** | 불안정 | 안정 + 절세 |
|
||||
|
||||
→ **기초는 배울 수 있지만, 정확성과 시간을 고려하면 전문가 도움이 효율적입니다.**
|
||||
|
||||
---
|
||||
|
||||
## 💡 꼭 기억하세요!
|
||||
|
||||
**1. 기초는 누구나 배울 수 있습니다**
|
||||
**2. 하지만 세법이 복잡하고 매년 바뀝니다**
|
||||
**3. 정확하게 하려면 전문가가 필요합니다**
|
||||
|
||||
당신의 상황에 따라 판단하고, 필요할 때 전문가와 상담하세요.
|
||||
$$,
|
||||
1,
|
||||
true,
|
||||
NOW()
|
||||
) ON CONFLICT (slug) DO NOTHING;
|
||||
|
||||
-- 2. 이번달 부가가치세 신고
|
||||
INSERT INTO blog_posts (title, slug, content, category_id, is_published, created_at)
|
||||
VALUES (
|
||||
'이번달 부가가치세 신고 - 너무 늦지 마세요! (D-day 계산)',
|
||||
'vat-report-monthly-guide',
|
||||
$$
|
||||
# 이번달 부가가치세 신고 - 너무 늦지 마세요! (D-day 계산)
|
||||
|
||||
"어? 부가가치세 신고가 오늘까지라고?"
|
||||
|
||||
매달 20일까지 신고해야 하는 부가가치세. **하루만 늦어도 과태료가 나옵니다.**
|
||||
|
||||
---
|
||||
|
||||
## 📌 실제 사례: 편의점 "편의점 톤"을 운영하는 박준호님 (28세, 사업 2년차)
|
||||
|
||||
**기본 정보**:
|
||||
- 위치: 광진구 자양동
|
||||
- 월 매출: 약 1,000만 원
|
||||
- 월 경비: 상품 구매 600만, 월세 200만, 직원비 100만 원
|
||||
|
||||
### 원래는 이렇게 했어요 (실패 사례)
|
||||
→ "신고 기한을 깜빡했어요"
|
||||
→ 5월 21일에 신고했어요
|
||||
|
||||
**결과**:
|
||||
- 부가가치세법 제25조 신고 기한 초과
|
||||
- 국세기본법 제83조에 따른 과태료: 50,000원
|
||||
- 하루만 늦어서 약 50,000원 손실
|
||||
|
||||
### 바뀐 후 (성공 사례)
|
||||
→ 스마트폰 알람으로 20일 미리 알림
|
||||
→ 자동으로 신고 준비
|
||||
|
||||
**결과**:
|
||||
- 기한 내 신고 완료
|
||||
- 과태료 없음
|
||||
- 마음 편함
|
||||
|
||||
---
|
||||
|
||||
## 🧮 부가가치세 신고 계산 (2025년 기준)
|
||||
|
||||
### 2025년 신고 일정
|
||||
|
||||
| 기간 | 신고 마감 | 납부 마감 |
|
||||
|------|----------|----------|
|
||||
| 1~2월 | 3월 20일 | 3월 25일 |
|
||||
| 3~4월 | 5월 20일 | 5월 25일 |
|
||||
| 5~6월 | 7월 20일 | 7월 25일 |
|
||||
| 7~8월 | 9월 20일 | 9월 25일 |
|
||||
|
||||
### 부가세 계산 (간이과세 기준)
|
||||
|
||||
**편의점 월 1,000만 원 매출**:
|
||||
- 간이과세율: 도매·소매업 3%
|
||||
- 부가세 = 1,000만 × 3% = **300,000원/월**
|
||||
|
||||
---
|
||||
|
||||
## 1️⃣ 이 정도는 누구나 배울 수 있어요
|
||||
|
||||
**신고 기한과 기본 계산**:
|
||||
- 매달 20일 신고해야 한다
|
||||
- 간단한 계산으로 세금액 파악
|
||||
- 필요한 서류 준비
|
||||
|
||||
→ 이 기본 개념만으로도 충분합니다.
|
||||
|
||||
---
|
||||
|
||||
## 2️⃣ 하지만 현실은 이렇게 복잡해요
|
||||
|
||||
### 겉으로는 간단해 보이지만...
|
||||
|
||||
**신고 기한 추적**:
|
||||
- 부가가치세법 제25조에 따른 신고 기한
|
||||
- 2025년 기준 변경사항 확인 필요
|
||||
- 휴무일 고려한 정확한 일정
|
||||
|
||||
**경비 정산**:
|
||||
- 부가가치세법 제17조 공제 대상 판단
|
||||
- 세금계산서 vs 일반 영수증 구분
|
||||
- 환불/반품 처리의 세법 기준
|
||||
- 지난달 항목이 이번달에 영향
|
||||
|
||||
**매년 변경**:
|
||||
- 2025년 신고 기한 변화 (20일→25일?)
|
||||
- 새로운 공제 항목 추가
|
||||
- 기준액 상향조정
|
||||
|
||||
**현실**: 매년 변경되는 규칙을 모두 따라가기 어렵습니다.
|
||||
|
||||
---
|
||||
|
||||
## 3️⃣ 그래서 전문가 도움이 필요합니다
|
||||
|
||||
### 신고 기한 관리
|
||||
|
||||
**당신이 해야 할 일**:
|
||||
- 카드 명세서 정리
|
||||
- 영수증 모으기
|
||||
|
||||
**세무사가 자동으로 처리**:
|
||||
- 신고 기한 알림 (놓칠 일 없음)
|
||||
- 경비 정산 및 계산
|
||||
- 기한 내 신고 보장
|
||||
|
||||
### 비용 효과 분석
|
||||
|
||||
| 항목 | 혼자할 때 | 세무사와 함께 |
|
||||
|------|----------|-----------|
|
||||
| **기한 관리** | 놓칠 수 있음 | 100% 보장 |
|
||||
| **경비 정산** | 불완전 | 정확함 |
|
||||
| **세금 계산** | 오류 가능성 | 세법 기준 준수 |
|
||||
| **과태료** | 발생 가능 (50k+) | 없음 |
|
||||
| **시간** | 월 3시간 | 월 30분 |
|
||||
| **세무사 비용** | 0원 | 월 30만 원 |
|
||||
|
||||
→ **기한 하나만 놓쳐도 과태료가 나옵니다. 자동 관리가 효율적입니다.**
|
||||
|
||||
---
|
||||
|
||||
## 💡 꼭 기억하세요!
|
||||
|
||||
**1. 부가세 신고는 기한이 엄격합니다**
|
||||
**2. 하루만 늦어도 과태료가 발생합니다**
|
||||
**3. 자동 관리로 스트레스를 없앨 수 있습니다**
|
||||
|
||||
매달 반복되는 일이기 때문에, 한 번 체계를 만들면 편합니다.
|
||||
$$,
|
||||
1,
|
||||
true,
|
||||
NOW()
|
||||
) ON CONFLICT (slug) DO NOTHING;
|
||||
|
||||
-- 3. 프리랜서를 위한 종합소득세 신고
|
||||
INSERT INTO blog_posts (title, slug, content, category_id, is_published, created_at)
|
||||
VALUES (
|
||||
'프리랜서를 위한 종합소득세 신고 - 170만 원 절약하는 방법',
|
||||
'freelancer-income-tax-guide',
|
||||
$$
|
||||
# 프리랜서를 위한 종합소득세 신고 - 170만 원 절약하는 방법
|
||||
|
||||
유튜버, 온라인 강사, 디자이너, 프리랜서...
|
||||
|
||||
이런 일을 하는 사람들은 회사에서 월급을 받지 않습니다. 대신 **자신이 벌은 돈을 직접 신고해야 합니다**. 이를 **종합소득세 신고**라고 합니다.
|
||||
|
||||
하지만 많은 프리랜서들이 **신고 기준도 모르고, 경비도 모르고, 나중에 큰 손해를 봅니다.**
|
||||
|
||||
---
|
||||
|
||||
## 📌 실제 사례: 유튜버 "김팬더"님 (28세, 활동 4년차)
|
||||
|
||||
**기본 정보**:
|
||||
- 월 평균 수입: 250만 원
|
||||
- 연간 수입: 3,000만 원
|
||||
- 주요 수입: 유튜브 광고 (80%), 브랜드 협찬 (20%)
|
||||
|
||||
### 원래는 이렇게 했어요 (실패 사례)
|
||||
→ "유튜브 광고 수익이 월 250만 원이니까 그냥 신고하면 되겠지"
|
||||
→ 경비는 거의 없다고 생각해서 신고
|
||||
→ 카메라, 마이크, 편집 소프트웨어는 개인 물건이라고 판단
|
||||
|
||||
**결과**:
|
||||
- 신고 소득: 3,000만 원
|
||||
- 종합소득세: 약 450만 원
|
||||
- 경비 인정받지 못해 손해
|
||||
|
||||
### 바뀐 후 (성공 사례)
|
||||
→ 카메라, 마이크, 소프트웨어를 경비로 인정받음
|
||||
→ 인터넷비, 카페비, 강의료 등도 경비로 처리
|
||||
→ 세무사와 함께 최적화된 신고
|
||||
|
||||
**결과**:
|
||||
- 신고 소득: 2,200만 원 (경비 800만 원 공제)
|
||||
- 종합소득세: 약 280만 원
|
||||
- 이 사례에서는 약 170만 원 절약되었습니다.
|
||||
|
||||
---
|
||||
|
||||
## 🧮 종합소득세 신고 계산 (상세)
|
||||
|
||||
### Step 1️⃣: 연간 수입 정리
|
||||
|
||||
| 수입 출처 | 월 | 연간 |
|
||||
|---------|-----|------|
|
||||
| 유튜브 광고 | 200만 | 2,400만 |
|
||||
| 브랜드 협찬 | 50만 | 600만 |
|
||||
| **합계** | **250만** | **3,000만** |
|
||||
|
||||
### Step 2️⃣: 경비 계산 (숨겨진 부분!)
|
||||
|
||||
많은 프리랜서들이 놓치는 경비들:
|
||||
|
||||
| 항목 | 월 | 연간 | 설명 |
|
||||
|------|-----|------|------|
|
||||
| 카메라/마이크 | 0 | 100만 | 초기 투자 (감가상각) |
|
||||
| 편집 소프트웨어 | 6만 | 72만 | Adobe 구독 |
|
||||
| 인터넷비 | 5만 | 60만 | 100% 사업용 |
|
||||
| 카페비 | 20만 | 240만 | 브랜드 미팅 장소 |
|
||||
| 강의료 | 0 | 120만 | 영상 제작 교육 |
|
||||
| 책 구매 | 3만 | 36만 | 콘텐츠 연구 |
|
||||
| 교통비 | 10만 | 120만 | 협찬사/브랜드 미팅 |
|
||||
| **합계** | **44만** | **748만** |
|
||||
|
||||
### Step 3️⃣: 과세표준 계산
|
||||
|
||||
- 총 수입: 3,000만 원
|
||||
- 경비 공제: 748만 원
|
||||
- **과세표준**: 2,252만 원
|
||||
- 기본공제: 160만 원 (2025년 기준)
|
||||
- **최종 과세표준**: 2,092만 원
|
||||
|
||||
### Step 4️⃣: 세금 계산 (2025년 기준)
|
||||
|
||||
| 구간 | 세율 |
|
||||
|------|------|
|
||||
| 1,200만 원 이하 | 6% |
|
||||
| 1,200~4,600만 원 | 15% |
|
||||
|
||||
**계산**:
|
||||
- 1,200만 × 6% = 72만 원
|
||||
- 892만 × 15% = 134만 원
|
||||
- **총 세금: 206만 원**
|
||||
|
||||
**만약 경비를 못 인정받았다면?**
|
||||
- 세금: 450만 원
|
||||
- **손해: 244만 원**
|
||||
|
||||
---
|
||||
|
||||
## 1️⃣ 이 정도는 누구나 배울 수 있어요
|
||||
|
||||
**기본 개념만 알면 충분**:
|
||||
- 수입을 기록하기
|
||||
- 기본 경비 이해하기
|
||||
- 신고 기한 알기 (5월)
|
||||
|
||||
→ 이 기본 수준에서는 자신이 충분히 가능합니다.
|
||||
|
||||
---
|
||||
|
||||
## 2️⃣ 하지만 현실은 이렇게 복잡해요
|
||||
|
||||
### 겉으로는 간단해 보이지만...
|
||||
|
||||
**경비 판단의 복잡성**:
|
||||
- 소득세법 제34조(필요경비)의 판단 기준
|
||||
- 카메라는 감가상각인가 즉시 비용인가?
|
||||
- 개인용 50%, 사업용 50%이면?
|
||||
- 초기 투자는 몇 년에 걸쳐 계산?
|
||||
- 중고 구매는 다른가?
|
||||
|
||||
**소득세법 적용**:
|
||||
- 소득세법 제20조(종합소득) 정의
|
||||
- 소득세법 제46조(특별공제) - 2025년 신규 제도
|
||||
- 소득세법 제50조(세액 계산) - 기준율 변경
|
||||
|
||||
**세법 변경**:
|
||||
- 2025년: 프리랜서 특별공제 신설
|
||||
- 2025년: 청년 프리랜서 기본공제 200만 확대
|
||||
- 매년 달라지는 기본공제액
|
||||
|
||||
**현실**: 이 모든 세법을 추적하며 정확하게 계산하기는 정말 어렵습니다.
|
||||
|
||||
---
|
||||
|
||||
## 3️⃣ 그래서 전문가 도움이 필요합니다
|
||||
|
||||
### 경비 발굴과 세법 적용
|
||||
|
||||
**당신이 해야 할 일**:
|
||||
- 수입 기록하기
|
||||
- 영수증 모으기
|
||||
|
||||
**세무사가 정확하게 처리**:
|
||||
- 모든 경비 발굴 및 인정 범위 판단
|
||||
- 소득세법 기준에 따른 정확한 계산
|
||||
- 2025년 신규 공제 및 지원 제도 적용
|
||||
- 세무조사 대비 증거 정리
|
||||
|
||||
### 비용 효과 분석
|
||||
|
||||
| 항목 | 혼자할 때 | 세무사와 함께 |
|
||||
|------|----------|-----------|
|
||||
| **경비 발굴** | 부분적 (놓침 많음) | 100% 인정 범위 내 적용 |
|
||||
| **세금** | 450만 원 (손해) | 206만 원 (정확함) |
|
||||
| **절세액** | 0 (손해) | 244만 원 (실제 절약) |
|
||||
| **시간** | 연 40시간 | 연 4시간 |
|
||||
| **신뢰도** | 불안함 | 확신 |
|
||||
| **세무사 비용** | 0원 | 연 50만 원 |
|
||||
| **순 효과** | -손해 | +194만 원 이득 |
|
||||
|
||||
→ **경비 처리만으로도 244만 원의 차이가 납니다.**
|
||||
|
||||
---
|
||||
|
||||
## 💡 꼭 기억하세요!
|
||||
|
||||
**1. 경비가 매우 중요합니다 (244만 원 차이)**
|
||||
**2. 카메라, 소프트웨어, 교육비 등 모두 경비입니다**
|
||||
**3. 세법이 복잡하고 매년 바뀝니다**
|
||||
**4. 전문가와 함께하면 훨씬 효율적입니다**
|
||||
|
||||
기초는 배울 수 있지만, **숨겨진 경비를 찾고 세법을 정확하게 적용하는 것이 핵심입니다.**
|
||||
$$,
|
||||
1,
|
||||
true,
|
||||
NOW()
|
||||
) ON CONFLICT (slug) DO NOTHING;
|
||||
|
||||
@@ -0,0 +1,466 @@
|
||||
-- V024: Apply latest BLOG_TEMPLATE guidelines
|
||||
-- Convert tables to readable lists
|
||||
-- Simplify emojis (remove section headers like 📊, 🧮)
|
||||
-- Keep customer-friendly language (1️⃣ 2️⃣ 3️⃣)
|
||||
|
||||
-- 1. 사업자 기장 시 자주 하는 실수 5가지
|
||||
INSERT INTO blog_posts (title, slug, content, category_id, is_published, created_at)
|
||||
VALUES (
|
||||
'사업자 기장 시 자주 하는 실수 5가지 - 혼자 하기 어려운 이유',
|
||||
'accounting-mistakes-5',
|
||||
$$
|
||||
# 사업자 기장 시 자주 하는 실수 5가지 - 혼자 하기 어려운 이유
|
||||
|
||||
"사업을 시작했는데 세금이 얼마나 될까요?"
|
||||
|
||||
많은 소규모 사업자들이 이 질문을 합니다. 기장은 **"돈이 들어오고 나가는 것을 기록하는 일"** - 간단해 보이죠. 하지만 실제로는 생각보다 복잡합니다.
|
||||
|
||||
---
|
||||
|
||||
## 실제 사례: 강남역 근처 카페를 운영하는 김 사장님 (34세, 사업 3년차)
|
||||
|
||||
**기본 정보** (예시 사례):
|
||||
- 위치: 강남역 3번 출구 근처
|
||||
- 월 매출: 약 600만 원 (평일 200만, 주말 400만)
|
||||
- 월 경비: 월세 150만, 재료비 180만, 직원급여 100만 원
|
||||
|
||||
### 원래는 이렇게 했어요 (실패 사례)
|
||||
→ "너무 바빠서 영수증을 그냥 버렸어요"
|
||||
→ 엑셀에 대충 적고
|
||||
→ 세무청에 그냥 신고했어요
|
||||
|
||||
**결과**:
|
||||
- 소득세법 제29조(수입금액의 계산) 규정에 따라 세무청에서 정정 통지
|
||||
- 국세기본법 제47조(가산세)에 따른 가산세 부과
|
||||
- 이 사례에서는 약 70만 원 정도의 추가 비용이 발생했습니다.
|
||||
|
||||
### 바뀐 후 (성공 사례)
|
||||
→ 영수침을 정리하고
|
||||
→ 매달 기본 기장을 했고
|
||||
→ 세무사와 연 1회 상담
|
||||
|
||||
**결과**:
|
||||
- 소득세법 제29조에 따른 정정 통지 없음
|
||||
- 국세기본법 제47조 가산세 부과 없음
|
||||
- 정확한 기장으로 이러한 상황을 방지할 수 있었습니다.
|
||||
|
||||
---
|
||||
|
||||
## 단계별 계산 (2025년 기준)
|
||||
|
||||
### Step 1️⃣: 매출 정리
|
||||
월 600만 원 × 12개월 = 연 7,200만 원
|
||||
|
||||
### Step 2️⃣: 경비 계산
|
||||
|
||||
월 경비 구성:
|
||||
- 월세: 150만 원 (연 1,800만 원)
|
||||
- 재료비: 180만 원 (연 2,160만 원)
|
||||
- 직원급여: 100만 원 (연 1,200만 원)
|
||||
- 기타: 20만 원 (연 240만 원)
|
||||
- **월 합계: 450만 원**
|
||||
- **연 합계: 5,400만 원**
|
||||
|
||||
### Step 3️⃣: 순이익
|
||||
7,200만 - 5,400만 = **1,800만 원**
|
||||
|
||||
### Step 4️⃣: 세금
|
||||
1,800만 원 × 약 6% = **약 108만 원/년**
|
||||
|
||||
---
|
||||
|
||||
## 1️⃣ 이 정도는 누구나 배울 수 있어요
|
||||
|
||||
**기본 개념만으로도 충분**:
|
||||
- 영수증을 어떻게 모으고
|
||||
- 엑셀에 어떻게 적으면 되고
|
||||
- 언제 신고하는지
|
||||
|
||||
→ 이 정도는 자신이 충분히 할 수 있습니다.
|
||||
|
||||
---
|
||||
|
||||
## 2️⃣ 하지만 현실은 이렇게 복잡해요
|
||||
|
||||
### 겉으로는 간단해 보이지만...
|
||||
|
||||
**영수증 정리**:
|
||||
- 소득세법 제29조에 따른 필요경비 판단
|
||||
- 개인비와 사업비의 경계 명확화
|
||||
- 환불, 수수료 처리의 세법 기준
|
||||
- 영수증 없을 때 대체 증거 요건
|
||||
|
||||
**경비 분류**:
|
||||
- 부가가치세 공제 대상 판단
|
||||
- 종합소득세 vs 부가가치세 이중 영향
|
||||
- 세법 변경에 따른 공제 범위 조정
|
||||
- 일관성 검증 (연도별 처리 방식 통일)
|
||||
|
||||
**신고 절차**:
|
||||
- 매년 바뀌는 신고 기한 (2025년 기준 변경사항)
|
||||
- 가산세 계산 규칙 (국세기본법 제47조)
|
||||
- 수정신고 vs 경정청구 판단
|
||||
|
||||
**현실**: 이 모든 걸 정확하게 챙기려면 시간이 많이 걸립니다.
|
||||
|
||||
---
|
||||
|
||||
## 3️⃣ 그래서 전문가 도움이 필요합니다
|
||||
|
||||
### 당신이 해야 할 일 vs 세무사가 해야 할 일
|
||||
|
||||
**당신이 할 수 있는 것**:
|
||||
- 매일 영수증 모으기
|
||||
- 월 1회 간단히 정리하기
|
||||
|
||||
**세무사가 정확하게 처리하는 것**:
|
||||
- 세법 기준에 따른 필요경비 판단
|
||||
- 공제 가능 여부 판단
|
||||
- 매년 변경되는 세법 자동 적용
|
||||
- 세무청 심사 대비 증거 정리
|
||||
|
||||
### 비용 효과 분석
|
||||
|
||||
**정확성**:
|
||||
- 혼자: 불안함 (실수 가능)
|
||||
- 세무사: 확신 (법적 기준 준수)
|
||||
|
||||
**시간**:
|
||||
- 혼자: 월 10시간
|
||||
- 세무사: 월 1시간
|
||||
|
||||
**세금 투명성**:
|
||||
- 혼자: 예측 불가
|
||||
- 세무사: 투명함
|
||||
|
||||
**가산세 위험**:
|
||||
- 혼자: 발생 가능성 높음
|
||||
- 세무사: 방지됨
|
||||
|
||||
**비용**:
|
||||
- 혼자: 0원
|
||||
- 세무사: 연 100만 원
|
||||
|
||||
**결론**: 기초는 배울 수 있지만, 정확성과 시간을 고려하면 전문가 도움이 효율적입니다.
|
||||
|
||||
---
|
||||
|
||||
## 꼭 기억하세요!
|
||||
|
||||
**1. 기초는 누구나 배울 수 있습니다**
|
||||
**2. 하지만 세법이 복잡하고 매년 바뀝니다**
|
||||
**3. 정확하게 하려면 전문가가 필요합니다**
|
||||
|
||||
당신의 상황에 따라 판단하고, 필요할 때 전문가와 상담하세요.
|
||||
$$,
|
||||
1,
|
||||
true,
|
||||
NOW()
|
||||
) ON CONFLICT (slug) DO NOTHING;
|
||||
|
||||
-- 2. 이번달 부가가치세 신고
|
||||
INSERT INTO blog_posts (title, slug, content, category_id, is_published, created_at)
|
||||
VALUES (
|
||||
'이번달 부가가치세 신고 - 꼭 해야 할 일 정리',
|
||||
'vat-filing-guide',
|
||||
$$
|
||||
# 이번달 부가가치세 신고 - 꼭 해야 할 일 정리
|
||||
|
||||
"부가가치세 신고가 다음 주예요. 뭘 준비해야 하나요?"
|
||||
|
||||
부가가치세 신고는 **"3개월간 벌어들인 세금을 국가에 내는 일"** - 의무입니다. 부가가치세법 제25조에 따르면, 해당 기간의 매출과 경비를 정확하게 신고해야 합니다.
|
||||
|
||||
---
|
||||
|
||||
## 실제 사례: 온라인 쇼핑몰을 운영하는 이 대표님 (29세, 사업 2년차)
|
||||
|
||||
**기본 정보** (예시 사례):
|
||||
- 월 매출: 약 1,500만 원
|
||||
- 월 경비: 상품 구입비 900만, 배송료 150만, 기타 100만 원
|
||||
- 신고 대상: 3개월마다 신고 필요
|
||||
|
||||
### 원래는 이렇게 했어요 (실패 사례)
|
||||
→ "신고 기한이 언제인지 몰랐어요"
|
||||
→ 필요경비와 공제세액을 잘못 계산했어요
|
||||
→ 신고 기한을 놓쳤어요
|
||||
|
||||
**결과**:
|
||||
- 부가가치세법 제25조 위반
|
||||
- 가산세(무신고 가산) 부과
|
||||
- 이 사례에서는 약 50만 원 정도의 추가 납부
|
||||
|
||||
### 바뀐 후 (성공 사례)
|
||||
→ 신고 기한을 달력에 표시했어요
|
||||
→ 세무사와 월 1회 점검했어요
|
||||
→ 정시 신고했어요
|
||||
|
||||
**결과**:
|
||||
- 부가가치세법 제25조 정시 신고
|
||||
- 가산세 부과 없음
|
||||
- 사업에만 집중할 수 있었습니다.
|
||||
|
||||
---
|
||||
|
||||
## 단계별 신고 준비 (2025년 기준)
|
||||
|
||||
### Step 1️⃣: 매출액 정리
|
||||
3개월간의 모든 매출 합계: 약 4,500만 원
|
||||
|
||||
### Step 2️⃣: 경비 계산
|
||||
|
||||
월평균 경비:
|
||||
- 상품 구입비: 900만 원 (3개월 2,700만 원)
|
||||
- 배송료: 150만 원 (3개월 450만 원)
|
||||
- 기타 경비: 100만 원 (3개월 300만 원)
|
||||
- **3개월 합계: 3,450만 원**
|
||||
|
||||
### Step 3️⃣: 공제 대상 파악
|
||||
공제세액 = 경비에 포함된 부가가치세
|
||||
|
||||
**공제 가능한 항목**:
|
||||
- 상품 구입 시 부가세 (부가가치세법 제17조)
|
||||
- 배송료의 부가세
|
||||
- 영수증 필수 (발행자별로 증명)
|
||||
|
||||
**공제 불가 항목**:
|
||||
- 국세 기본법에 따른 특정 경비
|
||||
|
||||
### Step 4️⃣: 납부액 계산
|
||||
매출액 4,500만 × 10% = 450만 원 (부가세)
|
||||
경비 공제액 345만 × 10% = 34.5만 원 (공제세액)
|
||||
**납부액**: 450만 - 34.5만 ≈ **415.5만 원**
|
||||
|
||||
---
|
||||
|
||||
## 1️⃣ 이 정도는 누구나 배울 수 있어요
|
||||
|
||||
**기본 개념만으로도 충분**:
|
||||
- 부가가치세가 뭔지
|
||||
- 언제 신고하는지
|
||||
- 어떤 서류가 필요한지
|
||||
|
||||
→ 기초 개념만 알아도 큰 도움이 됩니다.
|
||||
|
||||
---
|
||||
|
||||
## 2️⃣ 하지만 현실은 이렇게 복잡해요
|
||||
|
||||
**신고 기한**:
|
||||
- 부가가치세법 제25조에 따른 신고 기한
|
||||
- 매 분기마다 다른 마감일
|
||||
- 기한을 놓치면 무신고 가산세 발생
|
||||
|
||||
**공제 판정**:
|
||||
- 어떤 영수증이 공제되는지
|
||||
- 국세 기본법 제83조에 따른 결정
|
||||
- 발행자의 세무 상태에 따른 영향
|
||||
|
||||
**복합 사업**:
|
||||
- 면세 사업과 과세 사업을 함께 하면?
|
||||
- 공제 비율 계산이 복잡함
|
||||
- 연도별 조정 필요
|
||||
|
||||
**현실**: 정확하게 하려면 세법 이해가 필수입니다.
|
||||
|
||||
---
|
||||
|
||||
## 3️⃣ 그래서 전문가 도움이 필요합니다
|
||||
|
||||
### 당신이 해야 할 일 vs 세무사가 해야 할 일
|
||||
|
||||
**당신이 할 수 있는 것**:
|
||||
- 영수증 수집 및 분류
|
||||
- 매출액 합계 계산
|
||||
|
||||
**세무사가 정확하게 처리하는 것**:
|
||||
- 공제 가능 여부 판단 (부가가치세법 제17조)
|
||||
- 신고 기한 관리
|
||||
- 최적 신고 방식 결정
|
||||
- 가산세 방지
|
||||
|
||||
### 비용 효과 분석
|
||||
|
||||
**정시 신고 여부**:
|
||||
- 혼자: 기한 놓칠 가능성 높음
|
||||
- 세무사: 100% 정시 신고
|
||||
|
||||
**공제액 정확성**:
|
||||
- 혼자: 과다 공제 또는 과소 공제
|
||||
- 세무사: 세법 기준 준수
|
||||
|
||||
**가산세 위험**:
|
||||
- 혼자: 무신고 가산세 발생 가능 (50~100만 원)
|
||||
- 세무사: 가산세 방지
|
||||
|
||||
**신고 비용**:
|
||||
- 혼자: 0원 (시간 비용 제외)
|
||||
- 세무사: 분기 30만 원 정도
|
||||
|
||||
**결론**: 한 분기 가산세가 세무사 비용보다 많이 나올 수 있습니다.
|
||||
|
||||
---
|
||||
|
||||
## 꼭 기억하세요!
|
||||
|
||||
**1. 부가가치세는 의무입니다**
|
||||
**2. 기한 하나를 놓치면 가산세가 발생합니다**
|
||||
**3. 정확하게 하려면 전문가 도움이 효율적입니다**
|
||||
|
||||
신고 기한이 다가오면 미리 세무사와 상담하세요.
|
||||
$$,
|
||||
1,
|
||||
true,
|
||||
NOW()
|
||||
) ON CONFLICT (slug) DO NOTHING;
|
||||
|
||||
-- 3. 프리랜서를 위한 종합소득세 신고
|
||||
INSERT INTO blog_posts (title, slug, content, category_id, is_published, created_at)
|
||||
VALUES (
|
||||
'프리랜서를 위한 종합소득세 신고 - 이것만 알면 충분합니다',
|
||||
'freelancer-income-tax-guide',
|
||||
$$
|
||||
# 프리랜서를 위한 종합소득세 신고 - 이것만 알면 충분합니다
|
||||
|
||||
"작년에 벌어들인 돈이 얼마인데, 세금을 얼마나 내야 하나요?"
|
||||
|
||||
프리랜서는 **"본인이 일한 만큼 벌어들인 소득에 세금을 내는"** 구조입니다. 소득세법 제20조에 따르면, 사업소득은 매해 5월에 신고합니다.
|
||||
|
||||
---
|
||||
|
||||
## 실제 사례: 웹 디자이너 박 프리랜서님 (31세, 프리랜서 4년차)
|
||||
|
||||
**기본 정보** (예시 사례):
|
||||
- 월 평균 수입: 약 350만 원
|
||||
- 연간 수입: 약 4,200만 원
|
||||
- 월 경비: 자료실비 50만, 소프트웨어 라이선스 30만 원
|
||||
|
||||
### 원래는 이렇게 했어요 (실패 사례)
|
||||
→ "수입은 기록했는데 경비는 안 챙겼어요"
|
||||
→ 영수증 없이 신고했어요
|
||||
→ "이 정도는 작은 금액이니까..."라고 생각했어요
|
||||
|
||||
**결과**:
|
||||
- 소득세법 제46조에 따른 필요경비 과소 인정
|
||||
- 소득세법 제50조의 기본공제 조정
|
||||
- 이 사례에서는 약 100만 원 정도의 추가 납세
|
||||
|
||||
### 바뀐 후 (성공 사례)
|
||||
→ 경비도 정리하고
|
||||
→ 영수증을 모아두고
|
||||
→ 세무사와 상담했어요
|
||||
|
||||
**결과**:
|
||||
- 소득세법 제46조 기준에 따른 정확한 필요경비 인정
|
||||
- 소득세 정확하게 계산됨
|
||||
- 본인이 낼 세금의 액수를 미리 알 수 있었습니다.
|
||||
|
||||
---
|
||||
|
||||
## 단계별 신고 준비 (2025년 기준)
|
||||
|
||||
### Step 1️⃣: 연간 사업소득 정리
|
||||
월 350만 원 × 12개월 = 연 4,200만 원
|
||||
|
||||
### Step 2️⃣: 필요경비 계산
|
||||
|
||||
연간 경비:
|
||||
- 자료실비: 50만 원 × 12개월 = 600만 원
|
||||
- 소프트웨어 라이선스: 30만 원 × 12개월 = 360만 원
|
||||
- 기타 경비 (통신비, 교육): 100만 원
|
||||
- **연간 경비 합계: 1,060만 원**
|
||||
|
||||
### Step 3️⃣: 순이익 계산
|
||||
4,200만 원 - 1,060만 원 = **3,140만 원**
|
||||
|
||||
### Step 4️⃣: 소득세 계산
|
||||
소득세법 제50조에 따른 기본공제 적용
|
||||
개인 기본공제: 150만 원
|
||||
**과세표준**: 3,140만 - 150만 = 2,990만 원
|
||||
**예상 세금**: 약 300만 원~350만 원 (세율 6~15%)
|
||||
|
||||
---
|
||||
|
||||
## 1️⃣ 이 정도는 누구나 배울 수 있어요
|
||||
|
||||
**기본 개념만으로도 충분**:
|
||||
- 언제 신고하는지
|
||||
- 어떤 경비를 챙기는지
|
||||
- 대략적인 세금 액수
|
||||
|
||||
→ 기초를 알면 신고 준비가 훨씬 쉬워집니다.
|
||||
|
||||
---
|
||||
|
||||
## 2️⃣ 하지만 현실은 이렇게 복잡해요
|
||||
|
||||
**경비 인정 기준**:
|
||||
- 소득세법 제46조에 따른 필요경비 판단
|
||||
- 업무 관련성 입증 필요
|
||||
- 개인비와의 구분
|
||||
- 영수증 없을 때 대체 입증
|
||||
|
||||
**공제 판정**:
|
||||
- 소득세법 제50조 기본공제
|
||||
- 부양가족 공제 추가 가능
|
||||
- 연도별 공제 기준 변경
|
||||
- 종합소득 다른 소득과의 연계
|
||||
|
||||
**신고 방식**:
|
||||
- 분리과세 vs 종합과세 선택
|
||||
- 손실 이월공제 규칙
|
||||
- 지방소득세 연동
|
||||
|
||||
**현실**: 매년 세법이 바뀌고, 개인의 상황에 따라 신고 방식이 달라집니다.
|
||||
|
||||
---
|
||||
|
||||
## 3️⃣ 그래서 전문가 도움이 필요합니다
|
||||
|
||||
### 당신이 해야 할 일 vs 세무사가 해야 할 일
|
||||
|
||||
**당신이 할 수 있는 것**:
|
||||
- 통장 내역 정리
|
||||
- 경비 영수증 모으기
|
||||
- 월별 수입액 기록
|
||||
|
||||
**세무사가 정확하게 처리하는 것**:
|
||||
- 경비 인정 가능 범위 판단 (소득세법 제46조)
|
||||
- 최적 신고 방식 결정
|
||||
- 공제 항목 최대화 (소득세법 제50조)
|
||||
- 세무청 심사 대비
|
||||
|
||||
### 비용 효과 분석
|
||||
|
||||
**경비 인정**:
|
||||
- 혼자: 인정 불가 부분 많음 (100만 원 손실)
|
||||
- 세무사: 정확한 인정 (절세 효과)
|
||||
|
||||
**신고 정확성**:
|
||||
- 혼자: 계산 오류 가능성
|
||||
- 세무사: 법적 기준 준수
|
||||
|
||||
**세금 부담**:
|
||||
- 혼자: 예측 불가, 높을 가능성
|
||||
- 세무사: 최적화된 금액
|
||||
|
||||
**세무사 비용**:
|
||||
- 혼자: 0원
|
||||
- 세무사: 연 100~150만 원
|
||||
|
||||
**결론**: 세무사 비용보다 절세 효과가 더 크면 전문가 도움이 이득입니다.
|
||||
|
||||
---
|
||||
|
||||
## 꼭 기억하세요!
|
||||
|
||||
**1. 경비를 정리하면 세금이 줄어듭니다**
|
||||
**2. 하지만 경비 인정 기준이 복잡합니다 (소득세법 제46조)**
|
||||
**3. 정확하게 하려면 전문가 도움이 필수입니다**
|
||||
|
||||
5월 신고 전에 미리 세무사와 상담하세요. 미리 준비하면 더 많은 절세 기회를 놓치지 않습니다.
|
||||
$$,
|
||||
1,
|
||||
true,
|
||||
NOW()
|
||||
) ON CONFLICT (slug) DO NOTHING;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,299 @@
|
||||
-- V026: 기초 3개 포스트 추가 + 모든 12개에 카테고리 할당
|
||||
-- 카테고리 배치 (각 3개씩):
|
||||
-- cat 1 (사업자 세무): 사업자 기장, 소상공인, 스마트스토어
|
||||
-- cat 2 (부동산 세금): 월세, 자녀 증여세
|
||||
-- cat 3 (종합소득세): 프리랜서 종소세, 프리랜서 경비, 종소세 가이드
|
||||
-- cat 4 (부가가치세): 부가세 신고, 부가세 기한, 사업자 등록
|
||||
-- cat 5 (가족자산): 연말정산 환급
|
||||
|
||||
INSERT INTO blog_posts (title, slug, content, category_id, is_published, seo_title, seo_description, tags, created_at, updated_at) VALUES
|
||||
|
||||
-- 기초 3개 포스트 (V022, V024)
|
||||
('사업자 기장 시 자주 하는 실수 5가지 - 혼자 하기 어려운 이유', 'accounting-mistakes', $$# 사업자 기장 시 자주 하는 실수 5가지
|
||||
|
||||
많은 소규모 사업자들이 "돈이 들어오고 나가는 것을 기록하는 일"은 간단해 보이지만, 실제로는 악마가 디테일에 숨어있습니다.
|
||||
|
||||
## 단계별 계산 (2025년 기준)
|
||||
|
||||
### Step 1: 매출 정리
|
||||
월 600만 원 × 12개월 = 연 7,200만 원
|
||||
|
||||
### Step 2: 경비 계산
|
||||
- 월세: 150만 원 (연 1,800만 원)
|
||||
- 재료비: 180만 원 (연 2,160만 원)
|
||||
- 직원급여: 100만 원 (연 1,200만 원)
|
||||
- 기타: 20만 원 (연 240만 원)
|
||||
- 월 합계: 450만 원 / 연 합계: 5,400만 원
|
||||
|
||||
### Step 3: 순이익
|
||||
7,200만 - 5,400만 = 1,800만 원
|
||||
|
||||
### Step 4: 세금 (2025년 기준)
|
||||
- 기본공제: 160만 원
|
||||
- 과세표준: 1,640만 원
|
||||
- 세율: 6%
|
||||
- 세금: 약 98만 원/년
|
||||
|
||||
## 악마는 디테일에 숨어있습니다
|
||||
|
||||
### 1. 영수증 정리
|
||||
겉으로는: 영수증을 모으기만 하면 돼
|
||||
현실: 소득세법 제34조에서 인정되는 사업비만 공제 가능
|
||||
|
||||
### 2. 매출과 경비 기록
|
||||
겉으로는: 엑셀에 숫자만 입력하면 돼
|
||||
현실: 부가세와의 연계, 수정신고 규정, 기한 후 신고 가산세 고려
|
||||
|
||||
### 3. 세금 확정
|
||||
겉으로는: 기장만 잘하면 끝
|
||||
현실: 절세 전략, 연도별 일관성, 세무조사 대비, 이의신청 절차
|
||||
|
||||
## 올바른 기장 vs 하면 안 되는 것
|
||||
|
||||
### 해야 할 것
|
||||
1. 영수증 정리 - 5년 보관 의무
|
||||
2. 기본 기록 - 소득세법 제164조 규정
|
||||
3. 연 1회 점검 - 세무사와 상담
|
||||
4. 정확한 신고 - 소득세법 제46조 준수
|
||||
|
||||
### 하면 안 되는 것
|
||||
1. 영수증 버리기 - 증거 부족
|
||||
2. 개인비와 섞기 - 세법 위반
|
||||
3. 신고 늦추기 - 가산세 부과
|
||||
4. 과하게 깎기 - 세무조사 대상
|
||||
|
||||
## 결론
|
||||
|
||||
기초는 배울 수 있지만, 세법의 복잡성, 매년 변경되는 기준, 정확한 해석 때문에 세무사의 도움이 필요합니다.$$, 1, true, 'SEO Title', 'SEO Description', '사업자,기장,세무', NOW(), NOW()),
|
||||
|
||||
('이번달 부가가치세 신고 - 너무 늦지 마세요! (D-day 계산)', 'vat-report-guide', $$# 부가가치세 신고 - D-day 계산
|
||||
|
||||
많은 사업자들이 신고 기한을 놓칩니다. 부가가치세법 제25조에 따르면 신고 기한은 25일(2025년 개정). 하루만 늦어도 국세기본법 제47조 가산세가 발생합니다!
|
||||
|
||||
## 2025년 신고 일정
|
||||
|
||||
| 기간 | 신고 마감 | 납부 마감 |
|
||||
|------|----------|----------|
|
||||
| 1~2월 | 3월 25일 | 3월 31일 |
|
||||
| 3~4월 | 5월 25일 | 5월 31일 |
|
||||
| 5~6월 | 7월 25일 | 7월 31일 |
|
||||
| 7~8월 | 9월 25일 | 9월 30일 |
|
||||
|
||||
## 부가세 계산 (간이과세 기준)
|
||||
|
||||
월 1,000만 원 매출 기준:
|
||||
- 간이과세율: 도매·소매업 3%
|
||||
- 부가세 = 1,000만 × 3% = 300,000원/월
|
||||
|
||||
## 하지만 복잡한 부분들
|
||||
|
||||
- 카드 수수료 처리
|
||||
- 현금 판매 기록
|
||||
- 환불 처리 규정
|
||||
- 세금계산서 vs 일반 영수증
|
||||
- 3개월 전 환불 공제 불가
|
||||
|
||||
이런 디테일들 때문에 세무사가 필요합니다.$$, 4, true, 'SEO Title', 'SEO Description', '부가가치세,신고,세금', NOW(), NOW()),
|
||||
|
||||
('프리랜서를 위한 종합소득세 신고 - 정확한 경비 처리 가이드', 'freelancer-tax-guide', $$# 프리랜서를 위한 종합소득세 신고
|
||||
|
||||
유튜버, 온라인 강사, 디자이너, 프리랜서... 자신이 벌은 돈을 직접 신고해야 합니다. 종합소득세 신고(소득세법 제20조)입니다.
|
||||
|
||||
## 실제 사례: 유튜버 (월 250만 원 수입)
|
||||
|
||||
### 실패 사례
|
||||
- 신고 소득: 3,000만 원
|
||||
- 기본공제: 160만 원
|
||||
- 세금: 약 450만 원
|
||||
|
||||
### 성공 사례 (정확한 경비 처리)
|
||||
- 신고 소득: 2,200만 원 (경비 800만 원 공제)
|
||||
- 기본공제: 160만 원
|
||||
- 세금: 약 280만 원
|
||||
- **절약액: 약 170만 원**
|
||||
|
||||
## 종합소득세 계산 (2025년)
|
||||
|
||||
### 연간 수입
|
||||
| 수입 출처 | 연간 |
|
||||
|---------|------|
|
||||
| 유튜브 광고 | 2,400만 |
|
||||
| 브랜드 협찬 | 600만 |
|
||||
| 합계 | 3,000만 |
|
||||
|
||||
### 경비 (소득세법 제34조 기준)
|
||||
| 항목 | 연간 |
|
||||
|------|------|
|
||||
| 카메라/마이크 | 100만 |
|
||||
| 소프트웨어 | 72만 |
|
||||
| 인터넷비 | 60만 |
|
||||
| 카페비 | 240만 |
|
||||
| 강의료 | 120만 |
|
||||
| 책 구매 | 36만 |
|
||||
| 교통비 | 120만 |
|
||||
| 합계 | 748만 |
|
||||
|
||||
### 과세표준
|
||||
- 총 수입: 3,000만 원
|
||||
- 경비: 748만 원
|
||||
- 과세표준: 2,252만 원
|
||||
- 기본공제: 160만 원
|
||||
- 최종 과세표준: 2,092만 원
|
||||
|
||||
## 많은 프리랜서가 놓치는 부분
|
||||
|
||||
1. 어떤 경비가 인정되는가? (소득세법 제34조)
|
||||
2. 매년 기준이 바뀐다 (2025년 기본공제 160만)
|
||||
3. 세법 개정사항을 어떻게 반영하나?
|
||||
4. 세무조사에 대비해야 한다
|
||||
|
||||
이런 것들 때문에 세무사와 함께하는 것이 효율적입니다.$$, 3, true, 'SEO Title', 'SEO Description', '종합소득세,프리랜서,경비', NOW(), NOW()),
|
||||
|
||||
-- 추가 9개 포스트 (V025) - category_id 할당
|
||||
('프리랜서가 놓친 경비 5가지 - 이것도 인정될까요?', 'freelancer-expenses-5', $$# 프리랜서가 놓친 경비 5가지
|
||||
|
||||
프리랜서의 일반적인 경비:
|
||||
- 통신비: 인터넷, 휴대폰 요금
|
||||
- 교육비: 업무 관련 강좌, 자격증
|
||||
- 차량유지비: 업무용 차량 유지
|
||||
- 소프트웨어: 업무용 프로그램
|
||||
- 사무실비: 작업 공간 임차료
|
||||
|
||||
하지만 무엇이 "필요경비"인지는 복잡합니다. 소득세법 제34조를 정확하게 이해해야 합니다.$$, 3, true, 'SEO Title', 'SEO Description', '프리랜서,경비', NOW(), NOW()),
|
||||
|
||||
('월세 신고하는 방법 - 환급받을 수 있는 금액이 있습니다', 'monthly-rent-deduction', $$# 월세 신고하는 방법
|
||||
|
||||
소득세법 제59조의2에 따르면 월세세액공제가 있습니다.
|
||||
|
||||
## 월세세액공제 조건 (2025년 기준)
|
||||
- 본인 거주 주택의 월세: 연 750만 원 한도
|
||||
- 필요 서류: 임대차계약서, 월세 납부 증빙
|
||||
- 환급액: 연 월세의 10% (최대 75만 원)
|
||||
|
||||
예시: 월 60만 원 월세
|
||||
- 연 월세: 720만 원
|
||||
- 환급액: 72만 원
|
||||
|
||||
신고하지 않으면 한 푼도 못 받습니다!$$, 2, true, 'SEO Title', 'SEO Description', '월세,세액공제', NOW(), NOW()),
|
||||
|
||||
('자녀 증여세 계산하기 - 기초공제를 모르면 손해봅니다', 'child-gift-tax', $$# 자녀 증여세 계산하기
|
||||
|
||||
상속세및증여세법 제13조에 따르면 기초공제가 있습니다.
|
||||
|
||||
## 증여세 기초공제 (2025년 기준)
|
||||
- 직계 자손: 1인당 기초공제 많음
|
||||
- 조건: 증여자와 수증자 관계 증명
|
||||
|
||||
## 조세 전략
|
||||
- 시간 분산 (연간 공제 한도 활용)
|
||||
- 여러 자녀에게 분산
|
||||
- 공제 시기 선택
|
||||
|
||||
정확한 계산이 필요합니다.$$, 2, true, 'SEO Title', 'SEO Description', '증여세,상속세', NOW(), NOW()),
|
||||
|
||||
('사업자 등록 타이밍 - 너무 빨라도, 늦어도 손해입니다', 'business-registration-timing', $$# 사업자 등록 타이밍
|
||||
|
||||
소득세법 제2조에 따르면 사업소득의 인정 기준이 명확합니다.
|
||||
|
||||
## 사업자 등록의 효과
|
||||
- 부가가치세 신고 의무
|
||||
- 세금 공제 가능
|
||||
- 신용 기록 형성
|
||||
|
||||
## 언제 등록해야 하나?
|
||||
- 너무 빨리: 불필요한 부가세 부담
|
||||
- 너무 늦게: 소급 신고로 가산세
|
||||
|
||||
정확한 타이밍이 중요합니다.$$, 4, true, 'SEO Title', 'SEO Description', '사업자등록', NOW(), NOW()),
|
||||
|
||||
('소상공인 간단 기장 - 엑셀 + 영수증으로 충분합니다', 'small-business-accounting', $$# 소상공인 간단 기장
|
||||
|
||||
소득세법 제29조에 따르면 간단 기장도 인정됩니다.
|
||||
|
||||
## 간단 기장 방법
|
||||
- 엑셀에 매출/경비 기록
|
||||
- 영수증 보관
|
||||
- 연 1회 세무사와 정산
|
||||
|
||||
## 필수 항목
|
||||
- 날짜
|
||||
- 거래처
|
||||
- 금액
|
||||
- 증빙 서류 보관
|
||||
|
||||
이 정도면 충분합니다.$$, 1, true, 'SEO Title', 'SEO Description', '소상공인,기장', NOW(), NOW()),
|
||||
|
||||
('스마트스토어 판매자 세무 - 플랫폼 수입도 세금이 필요합니다', 'smartstore-tax', $$# 스마트스토어 판매자 세무
|
||||
|
||||
플랫폼 판매 수입도 세금 신고 대상입니다.
|
||||
|
||||
## 신고 방법
|
||||
- 플랫폼에서 제공하는 정산 내역서
|
||||
- 소득세법 제20조 기타소득 또는 사업소득
|
||||
- 연 300만 원 이상 시 신고 의무
|
||||
|
||||
## 경비 처리
|
||||
- 상품 구매
|
||||
- 수수료
|
||||
- 배송비
|
||||
- 광고비
|
||||
|
||||
정확한 분류가 필요합니다.$$, 1, true, 'SEO Title', 'SEO Description', '스마트스토어,세무', NOW(), NOW()),
|
||||
|
||||
('부가가치세 신고 기한 - 2일만 늦어도 가산세입니다', 'vat-deadline', $$# 부가가치세 신고 기한
|
||||
|
||||
부가가치세법 제25조: 신고 기한은 25일(2025년 개정)입니다.
|
||||
|
||||
## 신고 지체 시 페널티
|
||||
- 국세기본법 제47조: 1일당 0.2% 가산세
|
||||
- 하루만 늦어도 발생
|
||||
|
||||
## 신고 방법
|
||||
- 국세청 홈택스
|
||||
- 세무사 대리
|
||||
- 회계프로그램
|
||||
|
||||
기한을 절대 넘기면 안 됩니다.$$, 4, true, 'SEO Title', 'SEO Description', '부가가치세,기한', NOW(), NOW()),
|
||||
|
||||
('종합소득세 신고 완벽 가이드 - 5월 신고로 연간 세금이 결정됩니다', 'income-tax-complete-guide', $$# 종합소득세 신고 완벽 가이드
|
||||
|
||||
소득세법 제19조: 종합소득세 신고는 매년 5월입니다.
|
||||
|
||||
## 신고 대상
|
||||
- 사업소득 발생 개인
|
||||
- 기타소득 연 300만 원 이상
|
||||
- 근로소득 이외의 소득 발생자
|
||||
|
||||
## 필요 서류
|
||||
- 소득 입증 서류
|
||||
- 경비 증빙 자료
|
||||
- 공제 관련 서류
|
||||
|
||||
## 신고 절차
|
||||
1. 소득 정리
|
||||
2. 경비 계산
|
||||
3. 과세표준 계산
|
||||
4. 세금 계산
|
||||
5. 신고 및 납부
|
||||
|
||||
정확한 신고가 중요합니다.$$, 3, true, 'SEO Title', 'SEO Description', '종합소득세,신고', NOW(), NOW()),
|
||||
|
||||
('연말정산 환급 최대화 - 놓친 공제 하나가 수십만 원입니다', 'year-end-settlement-tips', $$# 연말정산 환급 최대화
|
||||
|
||||
소득세법 제163조: 연말정산은 매년 2월입니다.
|
||||
|
||||
## 주요 공제 항목
|
||||
- 교육비: 자녀 교육비 (연 900만 원 한도)
|
||||
- 의료비: 총 급여 3% 초과분만
|
||||
- 신용카드: 총 급여 25% 초과분만
|
||||
- 기부금: 한도 있음
|
||||
|
||||
## 환급받기
|
||||
- 공제 항목 확인
|
||||
- 증빙 서류 준비
|
||||
- 회사에 제출
|
||||
- 2월에 환급
|
||||
|
||||
놓친 공제가 있으면 손해입니다.$$, 5, true, 'SEO Title', 'SEO Description', '연말정산,환급', NOW(), NOW())
|
||||
ON CONFLICT (slug) DO NOTHING;
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
ALTER TABLE blog_posts
|
||||
ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ;
|
||||
|
||||
DROP INDEX IF EXISTS idx_blog_slug;
|
||||
ALTER TABLE blog_posts DROP CONSTRAINT IF EXISTS blog_posts_slug_key;
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS ux_blog_posts_slug_active
|
||||
ON blog_posts (slug)
|
||||
WHERE deleted_at IS NULL;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_blog_slug_active
|
||||
ON blog_posts (slug)
|
||||
WHERE deleted_at IS NULL;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_blog_published_active
|
||||
ON blog_posts (is_published, published_at DESC)
|
||||
WHERE deleted_at IS NULL;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_blog_category_active
|
||||
ON blog_posts (category_id)
|
||||
WHERE deleted_at IS NULL;
|
||||
@@ -0,0 +1,131 @@
|
||||
-- Seed and normalize admin common codes.
|
||||
INSERT INTO common_codes (code_group, code_value, code_name, sort_order) VALUES
|
||||
('INQUIRY_SERVICE_TYPE', '사업자세무', '사업자세무', 10),
|
||||
('INQUIRY_SERVICE_TYPE', '부동산세금', '부동산세금', 20),
|
||||
('INQUIRY_SERVICE_TYPE', '가족자산', '가족자산', 30),
|
||||
('INQUIRY_SERVICE_TYPE', '기타', '기타', 40),
|
||||
|
||||
('INQUIRY_STATUS', 'new', '신규', 10),
|
||||
('INQUIRY_STATUS', 'consulting', '상담중', 20),
|
||||
('INQUIRY_STATUS', 'contracted', '계약완료', 30),
|
||||
('INQUIRY_STATUS', 'rejected', '거절', 40),
|
||||
('INQUIRY_STATUS', 'closed', '종결', 50),
|
||||
|
||||
('CLIENT_STATUS', 'active', '활성', 10),
|
||||
('CLIENT_STATUS', 'inactive', '비활성', 20),
|
||||
|
||||
('CLIENT_SERVICE_TYPE', '기장', '기장', 10),
|
||||
('CLIENT_SERVICE_TYPE', '부동산', '부동산', 20),
|
||||
('CLIENT_SERVICE_TYPE', '증여상속', '증여·상속', 30),
|
||||
('CLIENT_SERVICE_TYPE', '종합소득세', '종합소득세', 40),
|
||||
('CLIENT_SERVICE_TYPE', '법인세', '법인세', 50),
|
||||
('CLIENT_SERVICE_TYPE', '부가가치세', '부가가치세', 60),
|
||||
('CLIENT_SERVICE_TYPE', '기타', '기타', 70),
|
||||
|
||||
('CLIENT_TAX_TYPE', '개인사업자', '개인사업자', 10),
|
||||
('CLIENT_TAX_TYPE', '법인사업자', '법인사업자', 20),
|
||||
('CLIENT_TAX_TYPE', '면세사업자', '면세사업자', 30),
|
||||
('CLIENT_TAX_TYPE', '근로소득자', '근로소득자', 40),
|
||||
('CLIENT_TAX_TYPE', '기타', '기타', 50),
|
||||
|
||||
('CLIENT_SOURCE', '홈페이지문의', '홈페이지 문의', 10),
|
||||
('CLIENT_SOURCE', '소개', '소개', 20),
|
||||
('CLIENT_SOURCE', '직접방문', '직접 방문', 30),
|
||||
('CLIENT_SOURCE', '카카오채널', '카카오 채널', 40),
|
||||
('CLIENT_SOURCE', '블로그', '블로그', 50),
|
||||
('CLIENT_SOURCE', '기타', '기타', 60),
|
||||
|
||||
('CONTRACT_SERVICE_TYPE', '개인기장대리', '개인 기장대리', 10),
|
||||
('CONTRACT_SERVICE_TYPE', '법인기장대리', '법인 기장대리', 20),
|
||||
('CONTRACT_SERVICE_TYPE', '세무조정', '세무조정', 30),
|
||||
('CONTRACT_SERVICE_TYPE', '세무컨설팅', '세무컨설팅', 40),
|
||||
('CONTRACT_SERVICE_TYPE', '불복청구', '불복청구', 50),
|
||||
|
||||
('REVENUE_SERVICE_TYPE', '기장수수료', '기장 수수료', 10),
|
||||
('REVENUE_SERVICE_TYPE', '세무조정료', '세무조정료', 20),
|
||||
('REVENUE_SERVICE_TYPE', '세무상담료', '세무상담료', 30),
|
||||
('REVENUE_SERVICE_TYPE', '신고대행료', '신고 대행료', 40),
|
||||
('REVENUE_SERVICE_TYPE', '자문수수료', '자문 수수료', 50),
|
||||
|
||||
('FILING_TYPE', '종합소득세', '종합소득세', 10),
|
||||
('FILING_TYPE', '부가가치세', '부가가치세', 20),
|
||||
('FILING_TYPE', '법인세', '법인세', 30),
|
||||
('FILING_TYPE', '원천세', '원천세', 40),
|
||||
('FILING_TYPE', '양도소득세', '양도소득세', 50),
|
||||
('FILING_TYPE', '상속증여세', '상속·증여세', 60),
|
||||
('FILING_TYPE', '세무조정', '세무조정', 70),
|
||||
|
||||
('TAX_RISK_LEVEL', 'low', '낮음', 10),
|
||||
('TAX_RISK_LEVEL', 'normal', '보통', 20),
|
||||
('TAX_RISK_LEVEL', 'high', '높음', 30)
|
||||
ON CONFLICT (code_group, code_value) DO UPDATE
|
||||
SET code_name = EXCLUDED.code_name,
|
||||
sort_order = EXCLUDED.sort_order,
|
||||
is_active = TRUE;
|
||||
|
||||
-- Normalize storage keys and migrate existing rows.
|
||||
UPDATE common_codes
|
||||
SET code_value = CASE
|
||||
WHEN code_group = 'CLIENT_SERVICE_TYPE' AND code_value = '증여·상속' THEN '증여상속'
|
||||
WHEN code_group = 'CLIENT_SOURCE' AND code_value = '홈페이지 문의' THEN '홈페이지문의'
|
||||
WHEN code_group = 'CLIENT_SOURCE' AND code_value = '직접 방문' THEN '직접방문'
|
||||
WHEN code_group = 'CLIENT_SOURCE' AND code_value = '카카오 채널' THEN '카카오채널'
|
||||
WHEN code_group = 'CONTRACT_SERVICE_TYPE' AND code_value = '개인 기장대리' THEN '개인기장대리'
|
||||
WHEN code_group = 'CONTRACT_SERVICE_TYPE' AND code_value = '법인 기장대리' THEN '법인기장대리'
|
||||
WHEN code_group = 'REVENUE_SERVICE_TYPE' AND code_value = '기장 수수료' THEN '기장수수료'
|
||||
WHEN code_group = 'REVENUE_SERVICE_TYPE' AND code_value = '신고 대행료' THEN '신고대행료'
|
||||
WHEN code_group = 'REVENUE_SERVICE_TYPE' AND code_value = '자문 수수료' THEN '자문수수료'
|
||||
WHEN code_group = 'FILING_TYPE' AND code_value = '상속·증여세' THEN '상속증여세'
|
||||
ELSE code_value
|
||||
END,
|
||||
code_name = CASE
|
||||
WHEN code_group = 'CLIENT_SERVICE_TYPE' AND code_value = '증여·상속' THEN '증여·상속'
|
||||
WHEN code_group = 'CLIENT_SOURCE' AND code_value = '홈페이지 문의' THEN '홈페이지 문의'
|
||||
WHEN code_group = 'CLIENT_SOURCE' AND code_value = '직접 방문' THEN '직접 방문'
|
||||
WHEN code_group = 'CLIENT_SOURCE' AND code_value = '카카오 채널' THEN '카카오 채널'
|
||||
WHEN code_group = 'CONTRACT_SERVICE_TYPE' AND code_value = '개인 기장대리' THEN '개인 기장대리'
|
||||
WHEN code_group = 'CONTRACT_SERVICE_TYPE' AND code_value = '법인 기장대리' THEN '법인 기장대리'
|
||||
WHEN code_group = 'REVENUE_SERVICE_TYPE' AND code_value = '기장 수수료' THEN '기장 수수료'
|
||||
WHEN code_group = 'REVENUE_SERVICE_TYPE' AND code_value = '신고 대행료' THEN '신고 대행료'
|
||||
WHEN code_group = 'REVENUE_SERVICE_TYPE' AND code_value = '자문 수수료' THEN '자문 수수료'
|
||||
WHEN code_group = 'FILING_TYPE' AND code_value = '상속·증여세' THEN '상속·증여세'
|
||||
ELSE code_name
|
||||
END
|
||||
WHERE (code_group, code_value) IN (
|
||||
('CLIENT_SERVICE_TYPE', '증여·상속'),
|
||||
('CLIENT_SOURCE', '홈페이지 문의'),
|
||||
('CLIENT_SOURCE', '직접 방문'),
|
||||
('CLIENT_SOURCE', '카카오 채널'),
|
||||
('CONTRACT_SERVICE_TYPE', '개인 기장대리'),
|
||||
('CONTRACT_SERVICE_TYPE', '법인 기장대리'),
|
||||
('REVENUE_SERVICE_TYPE', '기장 수수료'),
|
||||
('REVENUE_SERVICE_TYPE', '신고 대행료'),
|
||||
('REVENUE_SERVICE_TYPE', '자문 수수료'),
|
||||
('FILING_TYPE', '상속·증여세')
|
||||
);
|
||||
|
||||
UPDATE clients
|
||||
SET
|
||||
service_type = CASE WHEN service_type = '증여·상속' THEN '증여상속' ELSE service_type END,
|
||||
source = CASE
|
||||
WHEN source = '홈페이지 문의' THEN '홈페이지문의'
|
||||
WHEN source = '직접 방문' THEN '직접방문'
|
||||
WHEN source = '카카오 채널' THEN '카카오채널'
|
||||
ELSE source
|
||||
END;
|
||||
|
||||
UPDATE contracts
|
||||
SET service_type = REPLACE(REPLACE(service_type, ' ', ''), '·', '')
|
||||
WHERE service_type IS NOT NULL;
|
||||
|
||||
UPDATE revenue_tracking
|
||||
SET service_type = REPLACE(REPLACE(service_type, ' ', ''), '·', '')
|
||||
WHERE service_type IS NOT NULL;
|
||||
|
||||
UPDATE tax_filings
|
||||
SET filing_type = '상속증여세'
|
||||
WHERE filing_type = '상속·증여세';
|
||||
|
||||
UPDATE tax_filing_schedules
|
||||
SET filing_type = '상속증여세'
|
||||
WHERE filing_type = '상속·증여세';
|
||||
@@ -0,0 +1,22 @@
|
||||
-- Allow Korean code values and future growth without truncation risk.
|
||||
ALTER TABLE common_codes
|
||||
ALTER COLUMN code_group TYPE VARCHAR(80),
|
||||
ALTER COLUMN code_value TYPE VARCHAR(120),
|
||||
ALTER COLUMN code_name TYPE VARCHAR(200);
|
||||
|
||||
ALTER TABLE clients
|
||||
ALTER COLUMN service_type TYPE VARCHAR(100),
|
||||
ALTER COLUMN tax_type TYPE VARCHAR(60),
|
||||
ALTER COLUMN source TYPE VARCHAR(100);
|
||||
|
||||
ALTER TABLE contracts
|
||||
ALTER COLUMN service_type TYPE VARCHAR(120);
|
||||
|
||||
ALTER TABLE revenue_tracking
|
||||
ALTER COLUMN service_type TYPE VARCHAR(120);
|
||||
|
||||
ALTER TABLE tax_filings
|
||||
ALTER COLUMN filing_type TYPE VARCHAR(120);
|
||||
|
||||
ALTER TABLE tax_filing_schedules
|
||||
ALTER COLUMN filing_type TYPE VARCHAR(120);
|
||||
@@ -0,0 +1,16 @@
|
||||
-- Additional common codes for admin combo policy normalization.
|
||||
INSERT INTO common_codes (code_group, code_value, code_name, sort_order) VALUES
|
||||
('CONSULTING_ACTIVITY_TYPE', '방문상담', '방문 상담', 10),
|
||||
('CONSULTING_ACTIVITY_TYPE', '전화상담', '전화 상담', 20),
|
||||
('CONSULTING_ACTIVITY_TYPE', '세무조사대응미팅', '세무조사 대응 미팅', 30),
|
||||
('CONSULTING_ACTIVITY_TYPE', '카카오톡상담', '카카오톡 상담', 40),
|
||||
('CONSULTING_ACTIVITY_TYPE', '이메일자료접수', '이메일 자료 접수', 50),
|
||||
('CONSULTING_ACTIVITY_TYPE', '기타', '기타', 60),
|
||||
|
||||
('ANNOUNCEMENT_DISPLAY_TYPE', 'info', '일반', 10),
|
||||
('ANNOUNCEMENT_DISPLAY_TYPE', 'banner', '배너', 20),
|
||||
('ANNOUNCEMENT_DISPLAY_TYPE', 'urgent', '긴급', 30)
|
||||
ON CONFLICT (code_group, code_value) DO UPDATE
|
||||
SET code_name = EXCLUDED.code_name,
|
||||
sort_order = EXCLUDED.sort_order,
|
||||
is_active = TRUE;
|
||||
@@ -0,0 +1,6 @@
|
||||
ALTER TABLE blog_posts
|
||||
ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_blog_posts_deleted_at
|
||||
ON blog_posts (deleted_at)
|
||||
WHERE deleted_at IS NOT NULL;
|
||||
@@ -0,0 +1,15 @@
|
||||
INSERT INTO common_codes (code_group, code_value, code_name, sort_order)
|
||||
SELECT v.code_group, v.code_value, v.code_name, v.sort_order
|
||||
FROM (
|
||||
VALUES
|
||||
('FAQ_CATEGORY', '기장세금신고', '기장세금신고', 10),
|
||||
('FAQ_CATEGORY', '부동산', '부동산', 20),
|
||||
('FAQ_CATEGORY', '증여상속', '증여상속', 30),
|
||||
('FAQ_CATEGORY', '기타', '기타', 40)
|
||||
) AS v(code_group, code_value, code_name, sort_order)
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM common_codes cc
|
||||
WHERE cc.code_group = v.code_group
|
||||
AND cc.code_value = v.code_value
|
||||
);
|
||||
@@ -1,41 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
DEPLOY_HOME="/home/kjh2064"
|
||||
WEB_TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
||||
|
||||
echo "===== 🚀 TaxBaik 배포 스크립트 ====="
|
||||
echo "Web Timestamp: $WEB_TIMESTAMP"
|
||||
|
||||
# Web 배포
|
||||
echo "=== Deploying Web ==="
|
||||
WEB_DEPLOY_DIR="$DEPLOY_HOME/deployments/taxbaik_${WEB_TIMESTAMP}"
|
||||
mkdir -p "$WEB_DEPLOY_DIR"
|
||||
|
||||
if [ -z "$1" ]; then
|
||||
echo "Error: Publish directory required"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 첫 번째 인자는 publish 경로
|
||||
cp -r "$1/web" "$WEB_DEPLOY_DIR/"
|
||||
ln -sfn "$WEB_DEPLOY_DIR/web" "$DEPLOY_HOME/taxbaik_active"
|
||||
echo "✓ Web symlink updated: $WEB_DEPLOY_DIR/web"
|
||||
|
||||
# 프로세스 재시작
|
||||
echo "=== Restarting processes ==="
|
||||
pkill -9 -f "TaxBaik.Web" || true
|
||||
sleep 3
|
||||
|
||||
echo "=== Starting Web ==="
|
||||
cd "$DEPLOY_HOME/taxbaik_active"
|
||||
export ConnectionStrings__Default="Host=localhost;Database=taxbaikdb;Username=taxbaik;Password=taxbaik123"
|
||||
export ASPNETCORE_ENVIRONMENT=Production
|
||||
export ASPNETCORE_URLS=http://127.0.0.1:5001
|
||||
nohup /usr/local/dotnet/dotnet TaxBaik.Web.dll > web.log 2>&1 &
|
||||
sleep 2
|
||||
ps aux | grep TaxBaik.Web | grep -v grep && echo "✓ Web started" || echo "✗ Web failed"
|
||||
|
||||
echo ""
|
||||
echo "===== ✅ 배포 완료 ====="
|
||||
cat "$DEPLOY_HOME/taxbaik_active/wwwroot/version.txt" 2>/dev/null || echo "Version file not found"
|
||||
@@ -0,0 +1,139 @@
|
||||
# 1. TaxBaik 홈페이지 (taxbaik.com, www.taxbaik.com)
|
||||
server {
|
||||
server_name taxbaik.com www.taxbaik.com;
|
||||
client_max_body_size 512M;
|
||||
|
||||
|
||||
# /admin 은 관리자 진입용 경로로만 사용하고, 실제 앱은 /taxbaik/admin 에서 서빙한다.
|
||||
# 공개 루트(/)는 공용 SSR 홈페이지를 반환해야 하므로, 관리 UI와 절대 섞지 않는다.
|
||||
location /admin {
|
||||
return 301 $scheme://$host/taxbaik$request_uri;
|
||||
}
|
||||
|
||||
# 루트 경로는 공용 SSR 홈페이지.
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:5001/taxbaik/;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "Upgrade";
|
||||
proxy_set_header Host $host;
|
||||
proxy_cache_bypass $http_upgrade;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
|
||||
# /taxbaik/ 하위는 공개 사이트의 정식 base path.
|
||||
location /taxbaik {
|
||||
proxy_pass http://127.0.0.1:5001;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "Upgrade";
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_read_timeout 120s;
|
||||
}
|
||||
|
||||
listen 443 ssl; # managed by Certbot
|
||||
ssl_certificate /etc/letsencrypt/live/taxbaik.com/fullchain.pem; # managed by Certbot
|
||||
ssl_certificate_key /etc/letsencrypt/live/taxbaik.com/privkey.pem; # managed by Certbot
|
||||
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
|
||||
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
|
||||
|
||||
|
||||
}
|
||||
|
||||
# 2. Gitea (gitea.taxbaik.com)
|
||||
server {
|
||||
server_name gitea.taxbaik.com;
|
||||
client_max_body_size 512M;
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:3000;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_read_timeout 300;
|
||||
proxy_connect_timeout 300;
|
||||
proxy_send_timeout 300;
|
||||
}
|
||||
|
||||
listen 443 ssl; # managed by Certbot
|
||||
ssl_certificate /etc/letsencrypt/live/taxbaik.com/fullchain.pem; # managed by Certbot
|
||||
ssl_certificate_key /etc/letsencrypt/live/taxbaik.com/privkey.pem; # managed by Certbot
|
||||
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
|
||||
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
|
||||
|
||||
}
|
||||
|
||||
# 3. QuantEngine (quant.taxbaik.com)
|
||||
server {
|
||||
server_name quant.taxbaik.com;
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:5000/;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "Upgrade";
|
||||
proxy_set_header Host $host;
|
||||
proxy_cache_bypass $http_upgrade;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
|
||||
listen 443 ssl; # managed by Certbot
|
||||
ssl_certificate /etc/letsencrypt/live/taxbaik.com/fullchain.pem; # managed by Certbot
|
||||
ssl_certificate_key /etc/letsencrypt/live/taxbaik.com/privkey.pem; # managed by Certbot
|
||||
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
|
||||
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
|
||||
|
||||
}
|
||||
|
||||
server {
|
||||
if ($host = www.taxbaik.com) {
|
||||
return 301 https://$host$request_uri;
|
||||
} # managed by Certbot
|
||||
|
||||
|
||||
if ($host = taxbaik.com) {
|
||||
return 301 https://$host$request_uri;
|
||||
} # managed by Certbot
|
||||
|
||||
|
||||
listen 80;
|
||||
server_name taxbaik.com www.taxbaik.com;
|
||||
return 404; # managed by Certbot
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
server {
|
||||
if ($host = gitea.taxbaik.com) {
|
||||
return 301 https://$host$request_uri;
|
||||
} # managed by Certbot
|
||||
|
||||
|
||||
listen 80;
|
||||
server_name gitea.taxbaik.com;
|
||||
return 404; # managed by Certbot
|
||||
|
||||
|
||||
}
|
||||
server {
|
||||
if ($host = quant.taxbaik.com) {
|
||||
return 301 https://$host$request_uri;
|
||||
} # managed by Certbot
|
||||
|
||||
|
||||
listen 80;
|
||||
server_name quant.taxbaik.com;
|
||||
return 404; # managed by Certbot
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
[Unit]
|
||||
Description=TaxBaik Local TCP Proxy (5001 -> active blue/green port)
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=kjh2064
|
||||
WorkingDirectory=/home/kjh2064/taxbaik_active
|
||||
ExecStart=/usr/bin/dotnet TaxBaik.Proxy.dll
|
||||
Restart=always
|
||||
RestartSec=3
|
||||
|
||||
# Proxy는 백엔드 포트(5003/5004) 전환 중에도 살아 있어야 한다.
|
||||
TimeoutStopSec=15
|
||||
KillMode=mixed
|
||||
KillSignal=SIGTERM
|
||||
|
||||
SyslogIdentifier=taxbaik-proxy
|
||||
Environment=DOTNET_PRINT_TELEMETRY_MESSAGE=false
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -1,5 +1,5 @@
|
||||
[Unit]
|
||||
Description=TaxBaik Website and Admin (.NET 10)
|
||||
Description=TaxBaik Backend App (.NET 10)
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
@@ -17,7 +17,7 @@ KillSignal=SIGTERM
|
||||
|
||||
SyslogIdentifier=taxbaik
|
||||
Environment=ASPNETCORE_ENVIRONMENT=Production
|
||||
Environment=ASPNETCORE_URLS=http://127.0.0.1:5001
|
||||
Environment=ASPNETCORE_URLS=http://127.0.0.1:5004
|
||||
Environment=DOTNET_PRINT_TELEMETRY_MESSAGE=false
|
||||
# 아래 줄은 서버에서 직접 편집 (git에 커밋하지 않음)
|
||||
# Environment=ConnectionStrings__Default=Host=localhost;Database=taxbaikdb;Username=taxbaik;Password=CHANGE_ME
|
||||
|
||||
+170
@@ -0,0 +1,170 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
DEPLOY_HOME="/home/kjh2064"
|
||||
PORT_FILE="$DEPLOY_HOME/taxbaik_port"
|
||||
TIMESTAMP=$(TZ=Asia/Seoul date +%Y%m%d_%H%M%S)
|
||||
|
||||
echo "===== 🚀 TaxBaik Green/Blue Deployment Script ====="
|
||||
|
||||
if [ "${TAXBAIK_DEPLOY_FROM_CI:-}" != "1" ]; then
|
||||
echo "❌ This deployment script may only be run from CI." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 1. Determine active port
|
||||
ACTIVE_PORT=5003
|
||||
if [ -f "$PORT_FILE" ]; then
|
||||
ACTIVE_PORT=$(cat "$PORT_FILE" | tr -d '[:space:]')
|
||||
fi
|
||||
|
||||
# 2. Determine target port
|
||||
TARGET_PORT=5003
|
||||
if [ "$ACTIVE_PORT" -eq 5003 ]; then
|
||||
TARGET_PORT=5004
|
||||
else
|
||||
TARGET_PORT=5003
|
||||
fi
|
||||
|
||||
echo "Active Port: $ACTIVE_PORT"
|
||||
echo "Target Port: $TARGET_PORT"
|
||||
|
||||
# 3. New deploy dir is passed as first argument
|
||||
DEPLOY_DIR="$1"
|
||||
if [ -z "$DEPLOY_DIR" ]; then
|
||||
echo "Error: Deployment directory argument required"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Deploy Directory: $DEPLOY_DIR"
|
||||
|
||||
if [ ! -s "$DEPLOY_DIR/appsettings.Production.json" ]; then
|
||||
echo "❌ Missing production settings: $DEPLOY_DIR/appsettings.Production.json" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -s "$DEPLOY_DIR/proxy/TaxBaik.Proxy.dll" ]; then
|
||||
echo "❌ Missing proxy artifact: $DEPLOY_DIR/proxy/TaxBaik.Proxy.dll" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
is_taxbaik_proxy_on_5001() {
|
||||
local pids
|
||||
pids=$(ss -tlnp 2>/dev/null | grep ':5001 ' | grep -oP 'pid=\K\d+' | sort -u || true)
|
||||
[ -n "$pids" ] || return 1
|
||||
|
||||
for pid in $pids; do
|
||||
if tr '\0' ' ' < "/proc/$pid/cmdline" 2>/dev/null | grep -q 'TaxBaik.Proxy.dll'; then
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
# 0. Ensure the local TCP proxy exists and is running.
|
||||
# Nginx and external traffic always enter through 127.0.0.1:5001.
|
||||
if ss -tln | grep -q ':5001 ' && ! is_taxbaik_proxy_on_5001; then
|
||||
echo "⚠️ Port 5001 is occupied by a non-proxy process. Attempting to stop legacy taxbaik.service..."
|
||||
echo " Current listener:" >&2
|
||||
ss -tlnp 2>/dev/null | grep ':5001 ' >&2 || true
|
||||
|
||||
if command -v systemctl >/dev/null 2>&1 && systemctl is-active --quiet taxbaik 2>/dev/null; then
|
||||
if command -v sudo >/dev/null 2>&1 && sudo -n true 2>/dev/null; then
|
||||
sudo -n systemctl stop taxbaik || true
|
||||
sudo -n systemctl disable taxbaik || true
|
||||
sleep 2
|
||||
else
|
||||
echo " sudo -n is unavailable; cannot stop legacy taxbaik.service automatically." >&2
|
||||
fi
|
||||
fi
|
||||
|
||||
if ss -tln | grep -q ':5001 ' && ! is_taxbaik_proxy_on_5001; then
|
||||
echo "❌ Port 5001 is still occupied by a non-proxy process. Abort deploy to avoid routing traffic to the wrong app." >&2
|
||||
echo " Expected: TaxBaik.Proxy.dll on 127.0.0.1:5001" >&2
|
||||
echo " Manual fix: sudo systemctl stop taxbaik && sudo systemctl disable taxbaik" >&2
|
||||
ss -tlnp 2>/dev/null | grep ':5001 ' >&2 || true
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if ! is_taxbaik_proxy_on_5001; then
|
||||
echo "=== Starting proxy on 127.0.0.1:5001 ==="
|
||||
cd "$DEPLOY_DIR/proxy"
|
||||
nohup /usr/bin/dotnet TaxBaik.Proxy.dll > "$DEPLOY_HOME/taxbaik_proxy.log" 2>&1 &
|
||||
sleep 2
|
||||
fi
|
||||
|
||||
if ! is_taxbaik_proxy_on_5001; then
|
||||
echo "❌ Proxy on 127.0.0.1:5001 is not running. Abort deploy." >&2
|
||||
ss -tlnp 2>/dev/null | grep ':5001 ' >&2 || true
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 4. Start the new app on the target port
|
||||
echo "=== Starting New App on Port $TARGET_PORT ==="
|
||||
cd "$DEPLOY_DIR"
|
||||
export ASPNETCORE_ENVIRONMENT=Production
|
||||
export ASPNETCORE_URLS="http://127.0.0.1:$TARGET_PORT"
|
||||
export ConnectionStrings__Default="Host=localhost;Database=taxbaikdb;Username=taxbaik;Password=taxbaik123"
|
||||
export ApiClient__BaseUrl="http://127.0.0.1:$TARGET_PORT/taxbaik/api/"
|
||||
export DOTNET_PRINT_TELEMETRY_MESSAGE=false
|
||||
|
||||
# Run dotnet process
|
||||
nohup /usr/bin/dotnet TaxBaik.Web.dll > "web_${TARGET_PORT}.log" 2>&1 &
|
||||
NEW_PID=$!
|
||||
sleep 2
|
||||
|
||||
# Verify process is running
|
||||
if ! ps -p $NEW_PID > /dev/null; then
|
||||
echo "❌ Failed to start dotnet process on port $TARGET_PORT"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 5. Health Check Loop
|
||||
echo "=== Health Checking Port $TARGET_PORT ==="
|
||||
ATTEMPTS=20
|
||||
SUCCESS=false
|
||||
for i in $(seq 1 $ATTEMPTS); do
|
||||
STATUS=$(curl -sf -o /dev/null -w '%{http_code}' "http://127.0.0.1:${TARGET_PORT}/taxbaik/healthz" 2>/dev/null || echo "000")
|
||||
if [ "$STATUS" = "200" ]; then
|
||||
echo "✓ Health check passed on port $TARGET_PORT (Attempt $i/$ATTEMPTS)"
|
||||
SUCCESS=true
|
||||
break
|
||||
fi
|
||||
echo " Waiting for health check... ($i/$ATTEMPTS, Status: $STATUS)"
|
||||
sleep 2
|
||||
done
|
||||
|
||||
if [ "$SUCCESS" = "false" ]; then
|
||||
echo "❌ Health check failed. Rolling back..."
|
||||
kill -9 $NEW_PID || true
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 6. Switch Traffic
|
||||
# Nginx never needs per-deploy changes: it always proxies to the persistent
|
||||
# TaxBaik.Proxy on 127.0.0.1:5001, which reads this same PORT_FILE and
|
||||
# forwards to whichever port is currently active. See CLAUDE.md section 6.
|
||||
echo "=== Switching Traffic to Port $TARGET_PORT ==="
|
||||
echo "$TARGET_PORT" > "$PORT_FILE"
|
||||
echo "✓ Traffic routed to $TARGET_PORT (via TaxBaik.Proxy on 5001)"
|
||||
|
||||
# 7. Terminate Old App
|
||||
echo "=== Stopping Old App on Port $ACTIVE_PORT ==="
|
||||
# Find PID listening on ACTIVE_PORT
|
||||
OLD_PID=$(ss -tlnp | grep ":$ACTIVE_PORT " | grep -oP 'pid=\K\d+' | head -n1)
|
||||
if [ -n "$OLD_PID" ]; then
|
||||
echo "Killing old process PID: $OLD_PID"
|
||||
kill -15 $OLD_PID || kill -9 $OLD_PID
|
||||
echo "✓ Old process terminated"
|
||||
else
|
||||
echo "No old process found on port $ACTIVE_PORT"
|
||||
fi
|
||||
|
||||
# 8. Cleanup old deployment directories (Keep last 5)
|
||||
echo "=== Cleaning Up Old Deployments ==="
|
||||
ls -1dt $DEPLOY_HOME/deployments/taxbaik_* 2>/dev/null | tail -n +6 | xargs rm -rf 2>/dev/null || true
|
||||
echo "✓ Cleanup completed"
|
||||
|
||||
echo "===== ✅ Green/Blue Deployment Completed Successfully ====="
|
||||
@@ -1,40 +0,0 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:18-alpine
|
||||
container_name: taxbaik-db
|
||||
environment:
|
||||
POSTGRES_DB: taxbaikdb
|
||||
POSTGRES_USER: taxbaik
|
||||
POSTGRES_PASSWORD: taxbaik123
|
||||
ports:
|
||||
- "5432:5432"
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U taxbaik -d taxbaikdb"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
taxbaik-web:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile.web
|
||||
container_name: taxbaik-web
|
||||
environment:
|
||||
ASPNETCORE_ENVIRONMENT: Development
|
||||
ASPNETCORE_URLS: http://0.0.0.0:5001
|
||||
ConnectionStrings__Default: "Host=postgres;Database=taxbaikdb;Username=taxbaik;Password=taxbaik123"
|
||||
Jwt__SecretKey: "dev-secret-key-change-in-production-min-32-chars!"
|
||||
ports:
|
||||
- "5001:5001"
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
volumes:
|
||||
- ./publish:/app
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
@@ -0,0 +1,98 @@
|
||||
# Admin Pattern Critique And WBS
|
||||
|
||||
대상은 어드민 Blog, 문의사항, 등록/수정 페이지 전반이다. 이 문서는 비판, 개선 방향, 정량 완료 기준을 한 곳에 둔다.
|
||||
|
||||
## Brutal Critique
|
||||
|
||||
| 영역 | 현재 문제 | 왜 위험한가 | 개선 기준 |
|
||||
| --- | --- | --- | --- |
|
||||
| API-first 위반 | 어드민 Razor 컴포넌트가 `BlogService`, `InquiryService`, repository를 직접 주입 | 어드민을 클라이언트 사이드 Blazor WebAssembly로 운용할 때 구조가 깨지고 API 계약 테스트가 우회된다 | 모든 어드민 화면은 BrowserClient를 통해 `/api/*` 호출 |
|
||||
| Blog 등록/수정 중복 | `BlogCreate.razor`와 `BlogEdit.razor`가 필드, JS 편집기, 저장 로직을 반복 | 한쪽만 수정되는 파편화가 생긴다 | `BlogForm.razor` + `BlogEditorJsModule` 패턴 |
|
||||
| JS 과다/전역 상태 | `window.easyMDEInstance` 단일 전역 인스턴스 사용 | 페이지 이동/다중 편집/재렌더에서 내용 섞임 위험, Blazor 책임 경계가 흐려진다 | JS 제거 우선 검토, 불가피하면 JS module + element별 instance map + dispose |
|
||||
| 문의 수정 착시 | `InquiryEdit`가 이름/전화/이메일/내용 수정 UI를 보여주지만 실제 저장은 상태/메모 중심 | 운영자가 저장 성공을 믿어도 핵심 데이터가 DB에 반영되지 않을 수 있다 | 전체 수정 API를 만들거나 해당 필드를 read-only 처리 |
|
||||
| 문자열 상태 난립 | 문의 상태, 서비스 유형이 UI 문자열/API 문자열/DB 값으로 분산 | 오타 하나가 통계와 필터를 깨뜨린다 | enum/공통코드/상태 mapper 단일화 |
|
||||
| 삭제 위험 | Blog/Inquiry 삭제가 즉시 hard delete | 운영 감사, 상담 이력, SEO URL 보존에 취약 | soft delete 또는 archive 정책 |
|
||||
| 정합성 부족 | Blog slug 생성이 전체 목록 조회 기반 | 동시 생성 충돌에 약하고 데이터가 늘면 느려진다 | DB unique index + 충돌 재시도 |
|
||||
| 템플릿 부재 | CRUD 페이지마다 버튼, 오류, 로딩, 페이징 패턴이 다름 | 바이브코딩식 흔들림이 반복된다 | List/Form/Detail/PageState 템플릿화 |
|
||||
| 배포 완료 착시 | 문서상 완료 항목과 운영 검증 항목이 섞임 | 체크박스가 실제 성공을 대체한다 | WBS는 수치, 로그, CI URL로만 완료 |
|
||||
|
||||
## Target Admin Pattern
|
||||
|
||||
```text
|
||||
Razor Page/Form
|
||||
-> BrowserClient with JWT
|
||||
-> Controller DTO
|
||||
-> Application Service
|
||||
-> Repository
|
||||
-> DB constraints/indexes
|
||||
```
|
||||
|
||||
어드민은 클라이언트 사이드 Blazor WebAssembly 기준이다. 예외는 명시해야 한다. 서버 전용 컴포넌트가 Application Service를 직접 호출해야 한다면 `ENGINEERING_HARNESS.md`의 API-first 기준에 대한 사유와 제거 예정 WBS를 남긴다.
|
||||
|
||||
## Quantitative Success Metrics
|
||||
|
||||
| 지표 | 기준값 | 측정 방법 |
|
||||
| --- | --- | --- |
|
||||
| Admin direct service injection | 0건 | `rg "@inject .*Service|@inject I.*Repository" src/TaxBaik.Web.Client/Components/Admin` |
|
||||
| Blog create/edit duplicate fields | 0개 중복 폼 | `BlogForm.razor` 단일 사용 여부 |
|
||||
| Admin JavaScript surface | 필수 module만 허용 | `window.*` 전역 admin JS 0건, JS interop 사유 문서화 |
|
||||
| Inquiry visible-but-unsaved fields | 0개 | E2E로 수정 후 API 재조회 |
|
||||
| Protected admin API anonymous access | 0개 | API smoke에서 401/403 확인 |
|
||||
| CI required gates | 6/6 통과 | build, unit, publish, deploy, browser e2e, api smoke |
|
||||
| Playwright admin flows | 8개 이상 통과 | login, blog CRUD, inquiry CRUD/status, responsive, password, smoke |
|
||||
| DB integrity constraints | 핵심 테이블 100% | PK, FK, unique/check/index 리뷰 |
|
||||
| WBS evidence coverage | 100% | 각 완료 항목에 command/log/test 파일 기재 |
|
||||
|
||||
## Roadmap
|
||||
|
||||
| Phase | 목적 | 종료 조건 |
|
||||
| --- | --- | --- |
|
||||
| P0 Harness | 기준 고정과 문서 최소화 | 이 문서와 Engineering Harness가 README에서 참조됨 |
|
||||
| P1 Stabilize | Blog/Inquiry 착시와 중복 제거 | API-first 전환, 공통 폼, 정합성 테스트 통과 |
|
||||
| P2 Harden | DB 제약, 충돌 방지, 삭제 정책 | migration + 회귀 테스트 + E2E 통과 |
|
||||
| P3 Standardize | CRUD 템플릿화와 반복 패턴 제거 | 신규 CRUD 생성 시 템플릿만 사용 |
|
||||
| P4 Integrate | 더존 UX 정신 내재화 | 고밀도 화면, 표준 동선, 빠른 입력, 상태 가시성, 회귀 최소화 검증 |
|
||||
| P5 Operate | CI/CD와 운영 지표 고도화 | 배포본 기준 smoke/E2E/로그 알림 안정화 |
|
||||
|
||||
## Detailed WBS
|
||||
|
||||
| ID | 작업 | 산출물 | 정량 완료 기준 |
|
||||
| --- | --- | --- | --- |
|
||||
| P0-01 | 문서 기준점 정리 | `docs/INDEX.md`, `ENGINEERING_HARNESS.md` | canonical 문서 3개 이하, README 링크 1곳 |
|
||||
| P0-02 | 기존 장문 문서 역할 축소 | README 문서 섹션 정리 | `CLAUDE.md`를 보조자료로 표시 |
|
||||
| P1-01 | Blog API client 도입 | `IBlogBrowserClient`, `BlogBrowserClient` | Blog admin page direct service/repository injection 0건 |
|
||||
| P1-02 | Blog 공통 폼 도입 | `BlogForm.razor` | create/edit 필드 중복 0건, 저장 E2E 2개 통과 |
|
||||
| P1-03 | Markdown editor JS 최소화/격리 | Blazor 대체 또는 JS module | 전역 `window.easyMDEInstance` 사용 0건, JS interop 사유 명시 |
|
||||
| P1-04 | Inquiry 수정 계약 확정 | `UpdateInquiryRequest` 또는 read-only UI | 화면 표시 editable 필드와 저장 필드 불일치 0건 |
|
||||
| P1-05 | Inquiry API client 도입 | `IInquiryBrowserClient` 정비 | Inquiry admin direct service injection 0건 |
|
||||
| P1-06 | 상태/서비스 유형 단일화 | enum/common code/mapper | 상태 문자열 하드코딩 UI 위치 0건 또는 공통 상수 참조 |
|
||||
| P2-01 | Blog slug 충돌 방지 | unique index + retry | 동시 생성 테스트 1개 통과 |
|
||||
| P2-02 | 삭제 정책 정리 | soft delete migration 또는 archive 정책 | hard delete 운영 엔티티 0건 또는 예외 문서화 |
|
||||
| P2-03 | DB index 점검 | migration | 목록/검색/상태 필터 explain 기준 seq scan 위험 제거 |
|
||||
| P2-04 | 낙관적 충돌 방지 | `updatedAt` 조건부 update | stale update API 테스트 1개 이상 통과 |
|
||||
| P3-01 | CRUD 템플릿 작성 | page/form/client/test skeleton | 신규 admin CRUD 생성 시간 30% 감소 |
|
||||
| P3-02 | 공통 PageState/Error 처리 | reusable component/service | admin page 중복 try/catch/snackbar 패턴 50% 감소 |
|
||||
| P3-03 | 메뉴/라우팅 표준화 | route registry 또는 constants | admin route 문자열 중복 50% 감소 |
|
||||
| P4-01 | 더존 UX 패턴 캡슐화 | 고밀도 grid/form/template 규칙 | 신규 어드민 화면이 템플릿을 따르지 않는 경우 0건 |
|
||||
| P4-02 | UX 회귀 검증 | responsive, keyboard flow, density, state visibility test | 핵심 CRUD 화면 E2E 100% 통과 |
|
||||
| P5-01 | CI gate 명문화 | workflow 체크 목록 | 6개 gate 모두 required |
|
||||
| P5-02 | 배포본 API smoke 확장 | workflow curl 추가 | Blog/Inquiry create-read-update test 2xx |
|
||||
| P5-03 | 운영 회귀 대시보드 | test report/version endpoint | 배포 커밋과 E2E 결과 추적 가능 |
|
||||
| P4-03 | 기존 20개+ 어드민 화면을 SmartAdmin 5.5 참조(`legacy/smartadmin/`, `DOUZONE_UX_GUIDE.md`)로 재단장 (2026-07-03 시점 미착수, 향후 별도 진행) | 각 화면의 색상/카드/타이포그래피 갱신 | SmartAdmin 매핑 표 기준 적용 화면 수 / 전체 화면 수 100% |
|
||||
|
||||
## Immediate Refactor Order
|
||||
|
||||
1. `InquiryEdit` 착시 제거: 전체 수정 API를 추가하거나 저장 안 되는 필드를 read-only로 바꾼다.
|
||||
2. `BlogForm.razor`를 만들고 create/edit 중복을 제거한다.
|
||||
3. Blog/Inquiry 어드민 페이지를 BrowserClient 경유로 바꾼다.
|
||||
4. 상태/서비스 유형 문자열을 단일 source로 모은다.
|
||||
5. DB 제약과 삭제 정책을 migration으로 고정한다.
|
||||
|
||||
## Completion Rule
|
||||
|
||||
WBS 항목은 다음 네 가지가 모두 있어야 완료다.
|
||||
|
||||
- 관련 코드 또는 문서 diff
|
||||
- 로컬 검증 명령과 결과
|
||||
- CI/CD workflow 성공
|
||||
- 배포본 기준 API 또는 Browser E2E 증거
|
||||
@@ -0,0 +1,72 @@
|
||||
# Combo Policy
|
||||
|
||||
이 문서는 TaxBaik 어드민의 콤보 정책을 정한다. 여기서 콤보는 `MudSelect`, `MudAutocomplete`, `MudChip`, 상태 필터, 코드 선택 입력을 포함한다.
|
||||
|
||||
## Policy
|
||||
|
||||
- 닫힌 집합은 `MudSelect`를 쓴다.
|
||||
- 열린 집합 또는 검색이 필요한 집합은 `MudAutocomplete`를 쓴다.
|
||||
- 상태/유형/등급처럼 값이 고정된 항목은 문자열 직접 입력을 금지한다.
|
||||
- 선택한 값은 저장 값과 표시 값을 분리한다.
|
||||
- 표시 값은 사람이 읽는 라벨, 저장 값은 코드값이어야 한다.
|
||||
- `null` 허용 여부는 UI에서 명시한다.
|
||||
- `전체`, `선택 안 함`, `기타`는 서로 다른 의미로 취급한다.
|
||||
- 다중 선택이 필요하면 단일 선택 콤보를 억지로 재사용하지 않는다.
|
||||
|
||||
## Closed Set
|
||||
|
||||
다음 경우 `MudSelect`를 기본으로 사용한다.
|
||||
|
||||
- 상태
|
||||
- 세금 유형
|
||||
- 신고 유형
|
||||
- 위험도
|
||||
- 고정 서비스 유형
|
||||
- 공지 유형
|
||||
|
||||
규칙:
|
||||
|
||||
- 값은 상수, enum, 공통코드 중 하나에서만 가져온다.
|
||||
- `MudSelectItem`의 라벨과 값은 일치하는 쌍으로 관리한다.
|
||||
- 운영자가 값의 의미를 추측해야 하는 항목은 콤보로 두지 않는다.
|
||||
|
||||
## Search Set
|
||||
|
||||
다음 경우 `MudAutocomplete`를 기본으로 사용한다.
|
||||
|
||||
- 고객 선택
|
||||
- 회사 선택
|
||||
- 데이터가 많아 스크롤 선택이 비효율적인 경우
|
||||
|
||||
규칙:
|
||||
|
||||
- 검색어 입력 후 서버 또는 클라이언트 필터 결과를 보여준다.
|
||||
- 결과가 적을 때는 `MudSelect`보다 `MudAutocomplete`를 우선하지 않는다.
|
||||
- 선택 후 보여주는 텍스트와 저장되는 id를 분리한다.
|
||||
|
||||
## Display Rules
|
||||
|
||||
- 목록에서는 상태를 칩으로 보여준다.
|
||||
- 폼에서는 텍스트보다 구조화된 값으로 저장한다.
|
||||
- 필터에서는 현재 선택값이 명확히 보이게 한다.
|
||||
- `Clearable`은 의미가 명확한 경우에만 켠다.
|
||||
|
||||
## Standard Sources
|
||||
|
||||
- 상태 값은 `InquiryStatusMapper` 또는 전용 enum을 사용한다.
|
||||
- 공지/신고/세무 정보는 각 도메인별 공통코드 소스를 둔다.
|
||||
- 고객/회사 선택은 검색형 콤보로 통일한다.
|
||||
|
||||
## Anti-Patterns
|
||||
|
||||
- 같은 화면에 `MudSelect`와 자유 텍스트 입력을 섞어 같은 의미를 표현
|
||||
- 코드값과 표시값을 뒤섞어서 저장
|
||||
- 콤보 옵션을 화면마다 하드코딩
|
||||
- `기타`를 예외 처리처럼 쓰고 실제 저장 값은 제각각 두는 것
|
||||
- `전체`를 저장 값으로 사용
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- 신규 어드민 화면은 이 문서의 `Closed Set`/`Search Set` 중 하나를 명시해야 한다.
|
||||
- 상태/유형/등급 입력이 있는 화면은 콤보 정책 위반이 없어야 한다.
|
||||
- 고객/회사처럼 데이터가 많은 항목은 검색형 선택으로 통일한다.
|
||||
@@ -0,0 +1,61 @@
|
||||
# Common Code Policy
|
||||
|
||||
이 문서는 어드민 콤보, 상태, 유형, 출처 값의 단일 기준이다. 값은 DB `common_codes`를 우선 사용하고, 화면은 표시명만 바꾼다.
|
||||
|
||||
## Canonical Rules
|
||||
|
||||
- `code_value`는 저장 키다.
|
||||
- `code_name`은 화면 표시값이다.
|
||||
- `code_value`는 공백을 넣지 않는다.
|
||||
- `code_group`도 공백 없이 대문자/숫자/언더스코어 중심의 안정된 키를 쓴다.
|
||||
- 새 콤보를 추가할 때는 먼저 `common_codes`에 그룹을 추가한다.
|
||||
- 화면 하드코딩 배열은 금지한다. 불가피하면 임시 폴백으로만 두고 제거 계획을 함께 적는다.
|
||||
- 같은 의미의 값이 테이블마다 다르면 저장값을 먼저 통일하고 마이그레이션으로 이관한다.
|
||||
|
||||
## Grouping Rules
|
||||
|
||||
- 상태값: `*_STATUS`
|
||||
- 유형값: `*_TYPE`
|
||||
- 출처값: `*_SOURCE`
|
||||
- 위험도/스코어: `*_LEVEL`
|
||||
|
||||
## Standard Groups
|
||||
|
||||
- `INQUIRY_SERVICE_TYPE`
|
||||
- `INQUIRY_STATUS`
|
||||
- `CONSULTING_ACTIVITY_TYPE`
|
||||
- `ANNOUNCEMENT_DISPLAY_TYPE`
|
||||
- `CLIENT_STATUS`
|
||||
- `CLIENT_SERVICE_TYPE`
|
||||
- `CLIENT_TAX_TYPE`
|
||||
- `CLIENT_SOURCE`
|
||||
- `CONTRACT_SERVICE_TYPE`
|
||||
- `REVENUE_SERVICE_TYPE`
|
||||
- `FILING_TYPE`
|
||||
- `TAX_RISK_LEVEL`
|
||||
- `BUSINESS_TYPE`
|
||||
- `FAQ_CATEGORY`
|
||||
|
||||
## Data Rules
|
||||
|
||||
- DB seed와 운영 데이터의 저장값이 다르면 UI를 먼저 맞추지 말고 저장값을 먼저 정규화한다.
|
||||
- 한글 코드값을 사용하더라도 컬럼 길이를 먼저 검토하고, 업무 테이블과 마스터 테이블을 함께 조정한다.
|
||||
- 한글 `code_value`가 필요하면 DB 컬럼 길이와 인덱스 길이를 먼저 확인하고, 초과 가능성이 있으면 표시값과 저장값을 분리한다.
|
||||
- 표시용 문구가 길면 `code_name`에 둔다.
|
||||
|
||||
## UI Rules
|
||||
|
||||
- `MudSelect`는 `code_value`를 바인딩하고 `code_name`을 보여준다.
|
||||
- 검색형이면 `MudAutocomplete`를 쓰고, 선택형이면 `MudSelect`를 쓴다.
|
||||
- 자유 입력을 허용하지 않을 값은 텍스트 필드로 만들지 않는다.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- 신규 콤보 추가 시 DB 마이그레이션이 먼저 존재해야 한다.
|
||||
- 화면에 하드코딩된 선택값이 없어야 한다.
|
||||
- 기존 저장값과 신규 저장값의 불일치가 없어야 한다.
|
||||
|
||||
## Audit
|
||||
|
||||
- 점검 SQL은 [docs/ops/COMMON_CODE_AUDIT.sql](./ops/COMMON_CODE_AUDIT.sql)를 사용한다.
|
||||
- 그룹 공백, 값 공백, 길이 초과, 테이블 매핑 불일치는 이 SQL에서 먼저 잡는다.
|
||||
@@ -0,0 +1,127 @@
|
||||
# DOUZONE UX Guide
|
||||
|
||||
이 문서는 TaxBaik 어드민 UX의 기준선이다. 목표는 더존 세무회계프로그램류의 고밀도 운영 화면을 구현하되, TaxBaik의 도메인과 검증 규칙을 유지하는 것이다.
|
||||
|
||||
## UX Principles
|
||||
|
||||
- 고밀도 우선: 한 화면에서 상태, 입력, 결과, 작업을 함께 본다.
|
||||
- 표준 동선 우선: 목록 -> 상세 -> 수정 -> 저장 흐름을 기본으로 둔다.
|
||||
- 빠른 입력 우선: 마우스 최소, 키보드/단축 동선 최대, 기본값 명확화.
|
||||
- 상태 가시성 우선: 진행중/성공/실패/비활성/삭제됨을 즉시 구분 가능하게 한다.
|
||||
- 회귀 최소화 우선: 같은 화면 패턴은 같은 컴포넌트와 같은 구조를 사용한다.
|
||||
- 추측 금지: 의미가 불명확한 텍스트, 상태, 버튼, 색상은 새로 만들지 않는다.
|
||||
|
||||
## Layout Template
|
||||
|
||||
어드민 화면은 기본적으로 아래 구조를 따른다.
|
||||
|
||||
```text
|
||||
PageHeader
|
||||
FilterBar or ActionBar
|
||||
ContentSurface
|
||||
-> DenseGrid or DetailPanel
|
||||
-> EmptyState when empty
|
||||
-> Paging/Footer when needed
|
||||
```
|
||||
|
||||
권장 규칙:
|
||||
|
||||
- 페이지 제목은 1개만 둔다.
|
||||
- 보조 설명은 1줄만 둔다.
|
||||
- 주요 액션은 우측 상단 또는 헤더 우측에 둔다.
|
||||
- 목록은 `Dense`를 기본으로 한다.
|
||||
- 상세/수정은 좌우 2열 또는 상단 요약 + 하단 폼 패턴을 우선한다.
|
||||
|
||||
## Component Template
|
||||
|
||||
### Page Header
|
||||
|
||||
- 구성: `Eyebrow`, `Title`, `Subtitle`, `Primary Action`
|
||||
- 역할: 화면 맥락 고정, 다음 행동 제시
|
||||
- 금지: 동일 화면에 헤더가 2개 이상 존재
|
||||
|
||||
### Dense Grid
|
||||
|
||||
- 행 간격은 좁게 유지한다.
|
||||
- 컬럼은 우선순위 순으로 배치한다.
|
||||
- 상태는 텍스트 대신 칩/색상/아이콘으로 함께 보여준다.
|
||||
- 작업 버튼은 `보기`, `수정`, `삭제`처럼 짧고 일관되게 둔다.
|
||||
|
||||
### Form
|
||||
|
||||
- 기본값은 채워진 상태로 시작한다.
|
||||
- 저장 전 필수 검증은 화면에서 즉시 보인다.
|
||||
- 저장되지 않는 필드는 read-only로 바꾼다.
|
||||
- 입력이 많은 폼은 섹션으로 나누되, 섹션 수는 최소화한다.
|
||||
|
||||
### Empty State
|
||||
|
||||
- 데이터 없음, 필터 결과 없음, 로드 실패를 구분한다.
|
||||
- 단순 문구보다 다음 행동 버튼을 함께 둔다.
|
||||
|
||||
### Status Chip
|
||||
|
||||
- 상태는 문자열 그대로 노출하지 말고 칩으로 시각화한다.
|
||||
- 색상은 의미를 유지한다.
|
||||
- 동일 상태는 동일 색을 사용한다.
|
||||
|
||||
## SmartAdmin 5.5 Design Reference (2026-07-03, 신규 화면부터 적용)
|
||||
|
||||
이 섹션은 어드민의 **시각적 스킨**(색상, 카드 크롬, 로그인 화면 스타일, 셸 레이아웃) 기준이다. 위 UX Principles(고밀도, 표준 동선, 더존 정신)는 그대로 유지하고, SmartAdmin 5.5는 그 위에 입히는 룩앤필만 담당한다.
|
||||
|
||||
- 소스: `legacy/smartadmin/`(로컬에 이미 포함된 v5.5 HTML/CSS 데모 패키지, Bootstrap 5 기반). 정확한 색상/여백 값이 필요하면 이 디렉터리를 직접 참조한다(추측 금지).
|
||||
- 적용 범위: **향후 신규 어드민 화면부터**. 기존 20개+ 화면(Dashboard, Blog, Inquiry, Client 등)은 이번엔 재단장하지 않는다. 기존 화면을 다른 이유로 수정할 때 자연스럽게 이 기준으로 수렴시킨다.
|
||||
|
||||
### 매핑 표
|
||||
|
||||
| SmartAdmin 5.5 참조 | 파일 | TaxBaik MudBlazor 대응 |
|
||||
| --- | --- | --- |
|
||||
| 상단 `<header>` 툴바 | `dashboard-control-center.html` | `AdminShell`의 `MudAppBar` |
|
||||
| `<aside class="app-sidebar">` (로고 + 필터 입력 + 메뉴) | `dashboard-control-center.html` | `AdminShell`의 `MudDrawer` (검색/필터 입력 포함) |
|
||||
| 로그인 카드 (`rounded-4`, 반투명 다크 글래스, `bg-dark bg-opacity-50`) | `auth-login.html` | `AdminLoginForm.razor`의 `MudPaper` 카드 — 반투명/블러 배경 톤 참고 |
|
||||
| 색상 팔레트 | `colorpalette.html`, `css/smartapp.min.css` | `App.razor`의 `MudTheme.Palette` (Primary/Secondary/Tertiary 등) |
|
||||
| 카드형 위젯 | `dashboard-*.html` | `AdminMetricCard`, `MudPaper` 기반 카드 |
|
||||
|
||||
### 적용 규칙
|
||||
|
||||
- 새 어드민 화면을 만들 때: 레이아웃/동선/밀도는 `DOUZONE_UX_GUIDE.md` 상단 원칙을 따르고, 색상·카드 모서리·그림자·로그인류 화면의 톤은 `legacy/smartadmin/`을 참조해 `MudTheme`/CSS 변수로 반영한다.
|
||||
- SmartAdmin 원본은 jQuery/Bootstrap 5 기반이므로 JS/DOM 구조를 그대로 이식하지 않는다. **시각적 토큰(색, 반경, 여백, 타이포그래피)만** 가져오고, 동작은 MudBlazor 컴포넌트로 구현한다.
|
||||
- 기존 화면을 SmartAdmin 스타일로 일괄 재단장하는 작업은 별도 WBS로 `docs/ADMIN_PATTERN_CRITIQUE_WBS.md`에 등록한 뒤 진행한다(이번 범위 아님).
|
||||
|
||||
## Text And Labels
|
||||
|
||||
- 라벨은 짧게 쓴다.
|
||||
- 같은 개념은 같은 단어를 쓴다.
|
||||
- 약어는 화면 전체에서 통일한다.
|
||||
- 운영자가 오해할 수 있는 추상적인 표현은 금지한다.
|
||||
|
||||
## Serving Rules
|
||||
|
||||
- 공개 사이트는 SSR, 어드민은 Blazor WebAssembly 기준으로 본다.
|
||||
- 어드민 화면은 API-first 경유를 기본으로 한다.
|
||||
- JS는 불가피할 때만 사용하고, 모듈로 격리한다.
|
||||
- 상태/메뉴/라우트/버튼은 문자열 흩뿌리기를 금지하고 공통 상수 또는 템플릿으로 묶는다.
|
||||
|
||||
## Reference Rules
|
||||
|
||||
- 이 문서를 어드민 UX의 1차 기준으로 사용한다.
|
||||
- 세부 코드 규칙은 [ENGINEERING_HARNESS.md](./ENGINEERING_HARNESS.md)를 따른다.
|
||||
- 콤보/선택/검색 규칙은 [COMBO_POLICY.md](./COMBO_POLICY.md)를 따른다.
|
||||
- 공통코드/저장값 규칙은 [COMMON_CODE_POLICY.md](./COMMON_CODE_POLICY.md)를 따른다.
|
||||
- 패턴 비판과 WBS는 [ADMIN_PATTERN_CRITIQUE_WBS.md](./ADMIN_PATTERN_CRITIQUE_WBS.md)를 따른다.
|
||||
- 문서 인덱스는 [INDEX.md](./INDEX.md)를 따른다.
|
||||
|
||||
## Prohibited Patterns
|
||||
|
||||
- 목록마다 서로 다른 헤더 구조
|
||||
- 버튼 색과 의미의 중복/충돌
|
||||
- 저장 안 되는 필드를 편집 가능한 척 보여주기
|
||||
- 전역 JS 상태에 의존하는 편집기
|
||||
- 같은 CRUD 화면의 개별 구현체마다 다른 DOM/행 높이/행동 패턴
|
||||
- 불필요한 중첩 컴포넌트와 과한 추상화
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- 신규 어드민 화면은 이 문서의 레이아웃/컴포넌트 규칙 중 최소 80%를 따른다.
|
||||
- 기존 화면은 새로 건드릴 때 이 문서로 수렴한다.
|
||||
- 화면 추가 시 `PageHeader`, `EmptyState`, `DenseGrid`, `Form` 패턴 중 하나 이상을 재사용한다.
|
||||
@@ -0,0 +1,173 @@
|
||||
# Engineering Harness
|
||||
|
||||
이 문서는 TaxBaik 코드가 매번 흔들리지 않도록 막는 최소 하네스다. 여기에 없는 내용은 추측하지 않고 코드, 테스트, 운영 로그, DB 스키마 중 하나로 확인한다.
|
||||
|
||||
## Quick Guardrails
|
||||
|
||||
- 라우팅 루프 금지
|
||||
- 공개/관리자 분리 검증
|
||||
- smoke 재사용
|
||||
- lint는 있으면 앞단 실패, 없으면 강제 생성 금지
|
||||
|
||||
## Non-Negotiables
|
||||
|
||||
| 항목 | 기준 | 실패 판정 |
|
||||
| --- | --- | --- |
|
||||
| Runtime | ASP.NET Core `net10.0` 기준 유지 | 프로젝트별 TargetFramework 불일치 |
|
||||
| Public UI | 홈페이지/공개 페이지는 서버 사이드 렌더링 기준 | 공개 페이지가 불필요하게 WASM 번들에 의존 |
|
||||
| Admin UI | 어드민은 클라이언트 사이드 Blazor WebAssembly + MudBlazor + API-first | 어드민 컴포넌트가 Application/Repository를 직접 주입 |
|
||||
| API | 모든 운영 기능은 `/api/*` DTO 경유 | UI 전용 서비스 호출만 존재 |
|
||||
| Auth | JWT 인증, 관리자 API는 `[Authorize]` | 익명으로 관리자 데이터 접근 가능 |
|
||||
| Deploy | Gitea Actions CI/CD만 배포 경로 | 수동 SSH/복사로 운영 반영 |
|
||||
| Evidence | 빌드, 테스트, E2E, API smoke 로그 | "확인함", "될 것" 같은 진술 |
|
||||
| Admin Render | Router/Routes에는 전역 `@rendermode`를 두지 않고 페이지별로 지정한다. 로그인 페이지만 `prerender: true`로 최초 HTML에 폼을 포함시키고, 나머지 `[Authorize]` 페이지는 `prerender: false`를 유지한다 | Router/Routes에 전역 렌더모드가 다시 생기거나, 로그인 폼이 최초 HTML에 없다 |
|
||||
| KST Timestamp | CI/배포/백업 폴더명과 추적 일시는 `TZ=Asia/Seoul` | `date`가 기본 UTC 또는 서버 로캘에 종속 |
|
||||
| Repo Root | 소스는 `src/`, 문서는 `docs/`, 테스트는 `tests/`, 스크립트는 `scripts/`, 마이그레이션은 `db/`, 배포 참조 설정은 `deploy/`에 둔다. 루트에는 진입점 설정(`CLAUDE.md`, `README.md`, `.gitignore`, `package.json` 등)만 남긴다 | 루트에 스크린샷/로그/1회성 디버그 스크립트/빌드 산출물이 커밋된다 |
|
||||
|
||||
## Architecture Guardrails
|
||||
|
||||
- Domain은 엔티티, enum, repository interface만 가진다.
|
||||
- Application은 use case와 검증 규칙을 가진다. HTTP, JS, MudBlazor, DB 연결 세부를 모른다.
|
||||
- Infrastructure는 Dapper SQL과 외부 시스템 구현을 가진다.
|
||||
- Web은 Controller, 공개 Razor Pages SSR, Blazor host, 인증/서빙 설정을 가진다.
|
||||
- Web.Client/Admin UI는 클라이언트 사이드 Blazor WebAssembly로 본다. 서버 DI 서비스에 의존하지 않고 API client만 호출한다.
|
||||
- 관리자 호스트가 prerender를 사용하더라도 데이터 접근 원칙은 WASM + API-first다. prerender는 초기 마크업용이며 비즈니스 로직의 근거가 아니다.
|
||||
- 어드민 기본 렌더는 WASM이다. 다만 초기 흰 화면 방지 목적의 셸 프리렌더와 로그인 화면의 서버 프리렌더는 허용한다. 비즈니스 로직은 여전히 API-first다.
|
||||
- 로그인 화면은 예외적으로 “먼저 보여야 하는 화면”이다. JS 바인딩/텔레메트리/하이드레이션이 실패해도 로그인 폼 자체는 화면에 남아 있어야 하며, 실패 시 흰 화면이나 빈 본문을 허용하지 않는다.
|
||||
- 로그인 화면은 공통 추적보다 가시성을 우선한다. 추적은 보조이며, 로그인 폼 렌더를 가로막는 코드는 금지한다.
|
||||
- 로그인 화면의 JS는 `try/catch`로 감싸고, 실패해도 사용자 입력과 화면 표시를 막지 않아야 한다.
|
||||
- JavaScript는 최소화한다. 브라우저 API, 인증 토큰 저장, 서드파티 편집기처럼 Blazor/MudBlazor만으로 해결하기 부적절한 경우에만 JS module로 격리한다.
|
||||
- 상속은 프레임워크 요구 또는 명확한 다형성 모델에만 사용한다. 폼/테이블/CRUD 재사용은 기본적으로 컴포넌트 합성과 작은 service/client로 처리한다.
|
||||
- 과유불급을 지킨다. 실제 재사용이 2곳 미만이면 새 추상화를 만들지 말고 기존 컴포넌트를 직접 조합한다.
|
||||
- CI, 배포 폴더명, 백업명, 버전 추적에 쓰는 시간 문자열은 `TZ=Asia/Seoul`을 기본으로 한다.
|
||||
- 클라이언트 오류 수집은 서버/브라우저를 보호하는 목적의 제한형 수집으로만 운영한다. 건당 비동기 전송, 중복 억제, 분당 상한, 서버 rate limit, 실패 시 조용히 폐기, 재시도 폭주 금지.
|
||||
- 브라우저에서 발생한 JS 오류는 운영 장애 탐지를 위한 샘플 데이터로만 취급하고, 전체 이벤트 스트림을 보존하려는 설계는 금지한다.
|
||||
- 텔레그램 알림은 운영자의 주의 채널이지 이벤트 버스가 아니다. 같은 원인/같은 기간의 중복 알림은 억제하고, 리포트/오류/문의/시작 장애는 종류별 시간창을 분리한다.
|
||||
- 오류 알림에는 재현성 6요소를 포함한다: 화면, 기능, 액션, 단계, 데이터 식별자, 현재 라우트. 이 정보가 없으면 운영 대응이 끝나지 않은 것으로 본다.
|
||||
- 루트에 새 파일을 직접 추가하지 않는다. 소스는 `src/`, 문서는 `docs/`, 테스트는 `tests/`, 스크립트는 `scripts/`, 마이그레이션은 `db/`, 배포 참조 설정은 `deploy/`에 둔다.
|
||||
- 임시/스크래치 작업(스크린샷, 1회성 디버그 스크립트, 로그)은 저장소 밖(OS/세션 임시 폴더)에서 하고 절대 커밋하지 않는다. 저장소 안에서 꼭 필요하면 `.gitignore`에 등록된 `.scratch/`만 사용한다.
|
||||
- 커밋 전 `git status`로 루트에 낯선 파일이 생기지 않았는지 확인한다. 빌드 산출물(runtimeconfig.json, deps.json, wwwroot 산출물 등)이 루트나 프로젝트 폴더 밖에 커밋되면 안 된다.
|
||||
- 재현 맥락은 페이지별 수동 JS 호출이 아니라 `AdminTelemetryContext` 같은 공통 컴포넌트가 담당한다. 새 어드민 화면은 레이아웃 경유 기본값을 자동 상속해야 하며, 예외만 명시적으로 덮어쓴다.
|
||||
|
||||
## Code Quality Harness
|
||||
|
||||
| 원칙 | 적용 방식 |
|
||||
| --- | --- |
|
||||
| SOLID | 페이지는 orchestration만, 검증은 Application, 저장은 Repository, HTTP 계약은 DTO |
|
||||
| 유지보수 | Blog/Inquiry 같은 CRUD는 `List`, `Form`, `Client`, `Dto`, `Validator` 패턴으로 고정 |
|
||||
| 리팩토링 | 동작 보존 테스트를 먼저 추가하고 작은 단위로 이동 |
|
||||
| 일관성 | 오류 응답은 ProblemDetails, 페이징은 `{ data, total, page, pageSize }` |
|
||||
| 파편화 방지 | 같은 필드/상태/서비스유형 문자열은 enum/상수/공통 코드 중 하나로 단일화 |
|
||||
| 과유불급 | 추상화는 2개 이상 실제 사용처가 생긴 뒤 도입 |
|
||||
| 정규화 | 고객, 문의, 상담, 계약, 세금신고는 원천 테이블을 분리 |
|
||||
| 역정규화 | 대시보드/검색/운영 요약용 스냅샷만 허용하고 원천 id와 갱신 시점을 저장 |
|
||||
| 충돌방지 | 수정 API는 가능하면 `updatedAt` 또는 row version 기반 충돌 감지를 둔다 |
|
||||
| 더존 UX 정신 | 더존 세무회계프로그램처럼 고밀도, 표준 동선, 빠른 입력, 상태 가시성, 회귀 최소화를 기본 UX 원칙으로 삼는다 |
|
||||
| 추측금지 | 세법, 세율, 더존 필드, 운영 계정, 배포 결과는 공식 자료/코드/DB/로그 없이는 단정하지 않는다 |
|
||||
| JS 최소화 | Blazor/MudBlazor 우선, 불가피한 JS는 module + dispose + 테스트 가능한 얇은 wrapper |
|
||||
| 공통코드 | 상태/유형/출처/위험도는 `common_codes`를 우선 소스로 사용하고 화면 하드코딩을 금지 |
|
||||
|
||||
## Data Integrity Harness
|
||||
|
||||
- DB 제약 조건이 1차 방어선이다: NOT NULL, UNIQUE, FK, CHECK, index.
|
||||
- Application validation은 사용자 메시지와 use case 규칙을 담당한다.
|
||||
- UI validation은 빠른 피드백일 뿐이며 유일한 검증으로 보지 않는다.
|
||||
- 관리자 수정 화면에 노출한 필드는 실제 저장되어야 한다. 저장하지 않는 필드는 read-only로 표시한다.
|
||||
- 상태 전이는 허용 목록을 둔다. 임의 문자열 저장을 금지한다.
|
||||
- 삭제는 운영 데이터 손실 위험이 있으면 soft delete 또는 archive를 우선 검토한다.
|
||||
- 콤보 값은 [COMMON_CODE_POLICY.md](./COMMON_CODE_POLICY.md)를 1차 기준으로 삼는다.
|
||||
- 로그인 화면은 배포 전 브라우저 실증이 필수다. `dotnet build`만으로 로그인 화면 정상 표시를 완료로 선언하지 않는다.
|
||||
- 로그인 화면 실증 기준은 최소 1회 실제 브라우저 응답, 로그인 폼 렌더, 입력 포커스 가능 여부 확인이다.
|
||||
- 공개 루트와 관리자 루트는 반드시 분리 검증한다. `https://www.taxbaik.com/` 은 공용 홈페이지 제목을 가져야 하고, `https://www.taxbaik.com/taxbaik/admin/login` 은 관리자 제목을 가져야 한다.
|
||||
- 배포 후 smoke 기준은 `scripts/taxbaik-smoke.sh`를 1차 기준으로 사용한다. CI와 브라우저 E2E는 이 스크립트를 재사용해야 한다.
|
||||
- `UsePathBase("/taxbaik")`가 있는 Web 앱에서 `MapGet("/")` 같은 루트 리다이렉트를 추가할 때는, 반드시 루프 여부를 `curl`로 먼저 확인하고 나서만 반영한다. `/`와 `/taxbaik/`가 서로를 다시 가리키면 안 된다.
|
||||
- nginx의 `location /`와 `location /taxbaik`를 건드린 뒤에는, 변경 직후 `curl -I https://www.taxbaik.com/`, `curl -I https://www.taxbaik.com/taxbaik/`, `curl -I https://www.taxbaik.com/taxbaik/admin/login` 순서로 확인하고 결과를 기록한다.
|
||||
- 클라이언트 로그와 장애 진단 로그는 운영 데이터가 아니라 관측 데이터로 본다. 저장 실패는 사용자 흐름을 막지 않으며, 수집 실패 자체를 재시도 루프로 증폭하지 않는다.
|
||||
- 동일 오류의 텔레그램 재알림은 일정 기간 1회로 제한하고, 재전송 목적의 루프는 금지한다.
|
||||
- 데이터가 오류 재현에 필요하면 `entity`, `entityId`, `dataKey` 같은 최소 식별자만 남기고, 원문 데이터 전체를 로그에 싣지 않는다.
|
||||
|
||||
## API-First Admin Pattern
|
||||
|
||||
새 어드민 기능은 클라이언트 사이드 Blazor WebAssembly를 기준으로 아래 구조를 기본 템플릿으로 따른다.
|
||||
|
||||
| Layer | Naming | 책임 |
|
||||
| --- | --- | --- |
|
||||
| DTO | `CreateXRequest`, `UpdateXRequest`, `XResponse` | HTTP 계약 |
|
||||
| Controller | `XController` | 인증, 라우팅, status code, ProblemDetails |
|
||||
| Client | `IXBrowserClient`, `XBrowserClient` | JWT 포함 HTTP 호출 |
|
||||
| Page | `XList.razor`, `XCreate.razor`, `XEdit.razor` | 화면 상태와 navigation |
|
||||
| Form | `XForm.razor` | 입력 컴포넌트와 UI validation |
|
||||
| Tests | unit + Playwright/API smoke | 회귀 방지 |
|
||||
|
||||
## FastEndpoints Framework
|
||||
|
||||
새 API 엔드포인트는 Controllers 대신 **FastEndpoints**로 구현한다.
|
||||
|
||||
| 규칙 | 실행 |
|
||||
| --- | --- |
|
||||
| Library | `FastEndpoints` v5.30.0 이상 |
|
||||
| Naming | `Create[Entity]Endpoint`, `Get[Entity]Endpoint`, `List[Entity]Endpoint` 등 |
|
||||
| Location | `src/TaxBaik.Web/Features/[DomainName]/` |
|
||||
| Pattern | Request DTO → Endpoint class → Response DTO |
|
||||
| Validation | FluentValidation (FastEndpoints 내장) |
|
||||
| Registration | `builder.Services.AddFastEndpoints()` + `app.MapFastEndpoints()` |
|
||||
| Coexistence | Controllers와 FastEndpoints는 PathBase 내에서 병행 가능 (URL 충돌만 피함) |
|
||||
|
||||
**주의**: 기존 Controllers에서 FastEndpoints로 마이그레이션 시, 기존 엔드포인트 URL(`/api/*`)이 변경되지 않도록 명시적 라우팅을 지정한다.
|
||||
|
||||
```csharp
|
||||
public class GetBlogEndpoint : Endpoint<GetBlogRequest, GetBlogResponse>
|
||||
{
|
||||
public override void Configure()
|
||||
{
|
||||
Get("/api/blog/{id}");
|
||||
AllowAnonymous();
|
||||
}
|
||||
|
||||
public override async Task HandleAsync(GetBlogRequest req, CancellationToken ct)
|
||||
{
|
||||
// Logic here
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Rendering Boundary
|
||||
|
||||
| 영역 | 렌더링 | 데이터 접근 |
|
||||
| --- | --- | --- |
|
||||
| Public Home/Blog/Contact | 서버 사이드 렌더링 | 서버 Application Service 직접 사용 가능 |
|
||||
| Admin | 클라이언트 사이드 Blazor WebAssembly | JWT 포함 HTTP API만 사용 |
|
||||
| Shared DTO | 서버/클라이언트 공유 가능 | UI 전용 상태와 DB 엔티티를 섞지 않음 |
|
||||
|
||||
공개 페이지의 SEO와 초기 로딩은 SSR로 최적화한다. 어드민은 앱처럼 동작해야 하므로 WebAssembly와 API 계약을 기준으로 설계한다.
|
||||
|
||||
## CI Harness
|
||||
|
||||
완료는 로컬 성공이 아니라 CI와 배포본 성공이다.
|
||||
|
||||
| Gate | Command/Check | Target |
|
||||
| --- | --- | --- |
|
||||
| Build | `dotnet build src/TaxBaik.sln -c Release --no-restore` | error 0 |
|
||||
| Unit | `dotnet test src/TaxBaik.sln -c Release --no-build` | failed 0 |
|
||||
| Browser | `npx playwright test --project="Desktop Chrome"` | failed 0 |
|
||||
| Lint | 존재하는 경우 `dotnet format --verify-no-changes`, ESLint/Stylelint 등 프로젝트 기본 lint | 경고/오류 0 |
|
||||
| API Smoke | login + protected admin API curl | HTTP 2xx |
|
||||
| Deploy | `.gitea/workflows/deploy.yml` | success |
|
||||
| Post Deploy | `.gitea/workflows/browser-e2e.yml` | success |
|
||||
|
||||
### Gitea Auth Harness
|
||||
|
||||
- Gitea API와 workflow dispatch에는 `GITEA_TOKEN_TAXBAIK`만 사용한다.
|
||||
- `GITEA_TOKEN`은 쓰지 않는다.
|
||||
- 인증 헤더는 `Authorization: token <GITEA_TOKEN_TAXBAIK>`를 기본으로 한다.
|
||||
- 토큰 검증은 먼저 `GET /api/v1/user`로 확인하고, 그 다음 `workflow_dispatch`를 실행한다.
|
||||
- `401 invalid username, password or token`이 나오면 토큰 이름, 공백, 환경 변수 scope를 먼저 확인한다.
|
||||
|
||||
## Stop Conditions
|
||||
|
||||
- 동일 개념이 3곳 이상 다른 이름/계약으로 구현되면 기능 추가를 중단하고 정리한다.
|
||||
- UI가 저장한다고 보이는 필드를 API/Application이 저장하지 않으면 릴리스하지 않는다.
|
||||
- 운영 배포 검증이 CI 밖에서만 가능하면 완료로 보지 않는다.
|
||||
- `UsePathBase` 아래에서 루트 기본값을 만들려면 리다이렉트보다 실제 최종 콘텐츠가 나오는지 먼저 확인한다. 루트가 `/taxbaik/`를 다시 반환하면 즉시 중단한다.
|
||||
- lint가 필요하면 build보다 먼저, 그리고 배포보다 훨씬 앞단에서 실패시키고, lint가 없는 프로젝트에 억지로 새 lint 체계를 만들지 않는다.
|
||||
- 데이터 모델을 추측해서 세무 규칙이나 더존 UX 관습을 왜곡해 구현하지 않는다.
|
||||
@@ -0,0 +1,54 @@
|
||||
# TaxBaik Engineering Index
|
||||
|
||||
이 디렉터리의 문서만 현재 개발 기준의 기준점으로 사용한다. 다른 문서는 보조 자료로만 본다.
|
||||
|
||||
## Canonical Documents
|
||||
|
||||
| 문서 | 용도 | 변경 조건 |
|
||||
| --- | --- | --- |
|
||||
| [ENGINEERING_HARNESS.md](./ENGINEERING_HARNESS.md) | 아키텍처, 코드 품질, 배포, 데이터 정합성 하네스 | 방향성 변경 또는 반복 위반 발견 |
|
||||
| [DOUZONE_UX_GUIDE.md](./DOUZONE_UX_GUIDE.md) | 더존식 어드민 UX 원칙, 템플릿, 컴포넌트, 서빙 규칙 | 화면 패턴 변경 또는 신규 템플릿 추가 |
|
||||
| [COMMON_CODE_POLICY.md](./COMMON_CODE_POLICY.md) | 공통코드, 저장값, 컬럼 길이, 하드코딩 금지 규칙 | 공통코드 또는 콤보 추가/수정 |
|
||||
| [COMBO_POLICY.md](./COMBO_POLICY.md) | 콤보/선택/검색 입력 정책과 저장값 규칙 | 상태/유형/선택 입력 정책 변경 |
|
||||
| [ADMIN_PATTERN_CRITIQUE_WBS.md](./ADMIN_PATTERN_CRITIQUE_WBS.md) | 어드민 Blog/문의 등록 패턴 비판, 개선 로드맵, 정량 WBS | WBS 상태 또는 성공 지표 변경 |
|
||||
| `scripts/taxbaik-smoke.sh` | 공개/관리자 분리 검증용 배포 스모크 | 루트/관리자 응답 기준 변경 |
|
||||
|
||||
## Route And Serving Map
|
||||
|
||||
| 영역 | 라우트/파일 | 기준 |
|
||||
| --- | --- | --- |
|
||||
| Public Home/Blog/Contact | `/taxbaik/`, `/taxbaik/blog`, `/taxbaik/contact` | 서버 사이드 렌더링, SEO 우선, WASM 의존 금지 |
|
||||
| Admin Blog | `/taxbaik/admin/blog`, `/taxbaik/admin/blog/create`, `/taxbaik/admin/blog/{id}/edit` | 클라이언트 사이드 Blazor WebAssembly, API-first 클라이언트 경유, JS 최소화 |
|
||||
| Admin Inquiry | `/taxbaik/admin/inquiries`, `/taxbaik/admin/inquiries/create`, `/taxbaik/admin/inquiries/{id}/edit` | 클라이언트 사이드 Blazor WebAssembly, 공개 접수/관리자 등록/상태 변경 분리 |
|
||||
| Public API | `/taxbaik/api/*` | JWT 인증, ProblemDetails 오류, DTO 입출력 |
|
||||
| CI/CD | `.gitea/workflows/deploy.yml`, `.gitea/workflows/browser-e2e.yml` | 수동 배포 금지, 배포본 E2E 통과 후 완료 |
|
||||
| Smoke | `scripts/taxbaik-smoke.sh` | 루트는 공용 홈, `/taxbaik/admin/login` 은 관리자 화면이어야 함 |
|
||||
|
||||
## Shared Component Map
|
||||
|
||||
| 컴포넌트 | 용도 | 대표 사용처 |
|
||||
| --- | --- | --- |
|
||||
| `AdminShell` | 관리자 상단바/드로워/버전/알림 공통 shell | `Components/Admin/Layout/MainLayout.razor` |
|
||||
| `AdminLoginForm` | 관리자 로그인 입력/제출 UI | `Components/Admin/Pages/Login.razor` |
|
||||
| `AdminPageHeader` | 페이지 타이틀/보조설명/주요 액션 | Blog, Inquiry, Client, FAQ 목록 |
|
||||
| `AdminDataPanel` | 목록/표면/로딩 스켈레톤 공통 래퍼 | Blog, Inquiry, CommonCode, Dashboard |
|
||||
| `AdminEditorPanel` | 편집형 스켈레톤 래퍼 | BlogEdit, InquiryEdit, ClientEdit, CompanyEdit, FAQEdit |
|
||||
| `AdminSkeletonRows` | 반복 로딩 골격 | AdminDataPanel, AdminEditorPanel, Dashboard |
|
||||
| `AdminMetricCard` | 대시보드 KPI 카드 | `Components/Admin/Pages/Dashboard.razor` |
|
||||
| `AdminEmptyState` | empty/empty-filter 상태 | ClientList 등 목록 화면 |
|
||||
| `AdminFormSection` | 폼 입력 섹션 구획 | BlogForm, InquiryForm |
|
||||
| `AdminFormActions` | 제출/취소 버튼 묶음 | BlogForm, InquiryForm |
|
||||
| `AdminDetailSection` | 상세 정보 카드 | InquiryDetail |
|
||||
| `AdminCrudPageShell` | create/edit 페이지 공통 헤더+취소+편집 래퍼 | BlogCreate/Edit, InquiryCreate/Edit |
|
||||
| `CommonCodeGroupPanel` | 공통코드 그룹 선택/추가 패널 | CommonCodes |
|
||||
| `CommonCodeListPanel` | 공통코드 목록/편집 패널 | CommonCodes |
|
||||
|
||||
## Document Rules
|
||||
|
||||
- 문서는 짧게 유지한다. 새 문서를 만들기 전에 이 인덱스에 추가할 가치가 있는지 판단한다.
|
||||
- 동일한 기준을 여러 문서에 중복 작성하지 않는다.
|
||||
- 아키텍처/UX/콤보/공통코드 기준은 `ENGINEERING_HARNESS.md`, `DOUZONE_UX_GUIDE.md`, `COMBO_POLICY.md`, `COMMON_CODE_POLICY.md`만 본다.
|
||||
- WBS 완료 여부는 체크박스가 아니라 수치와 실행 로그로 판단한다.
|
||||
- 코드 변경 시 관련 WBS ID를 커밋/PR 설명 또는 작업 메모에 남긴다.
|
||||
- 공통코드 관련 규칙은 [COMMON_CODE_POLICY.md](./COMMON_CODE_POLICY.md)만 1차 기준으로 사용한다.
|
||||
- 공유 컴포넌트는 `INDEX.md`의 Shared Component Map을 먼저 확인한다.
|
||||
@@ -0,0 +1,777 @@
|
||||
# 블로그 포스트 작성 템플릿
|
||||
|
||||
## 정확성 원칙 (법적 책임 수반)
|
||||
|
||||
블로그는 **사실 기반, 세법 기반, 데이터 기반**이어야 합니다. 추측이나 예상은 법적 문제를 일으킬 수 있습니다.
|
||||
|
||||
### 절대 금지 표현
|
||||
|
||||
- "아마도", "할 것 같다", "추측된다" (추측)
|
||||
- "대략", "정도일 거다", "보통" (예상)
|
||||
- "좋을 것 같다", "나쁠 것 같다" (의견)
|
||||
- 증거 없는 "모두", "항상", "누구나" (일반화)
|
||||
- 출처 없는 통계 ("80% 고객이", "평균 X만 원")
|
||||
|
||||
### 필수 요소
|
||||
|
||||
**1. 세법 기반**:
|
||||
- 모든 주장에 세법/시행령/고시 인용
|
||||
- 조항 명시: "소득세법 제XX조에 따르면"
|
||||
- 최신 기준 명시: "2025년 기준"
|
||||
- 변경사항 반영: "전년도와 다르게..."
|
||||
|
||||
**2. 사실 기반**:
|
||||
- 실제 일어난 고객 사례만 사용
|
||||
- 가정일 경우 명시: "예를 들어, 만약 이렇다면"
|
||||
- 가상 사례는 "예시 사례"라고 명확히
|
||||
- 개인정보는 익명화 (이름, 나이는 일반적인 표현)
|
||||
|
||||
**3. 데이터 기반**:
|
||||
- 객관적 수치만 사용 (국세청 통계, 협회 자료)
|
||||
- 출처 명시: "2025년 세무청 통계에 따르면"
|
||||
- 구체적 금액: "약 50만 원" (범위 표현)
|
||||
- 비교 데이터: "작년 대비 X% 증가"
|
||||
|
||||
**4. 사례 제시 시 확인 사항**:
|
||||
```
|
||||
✅ 실제 고객인가? (공개 가능한 정보만)
|
||||
✅ 세법을 정확하게 적용했는가?
|
||||
✅ 금액 계산이 정확한가?
|
||||
✅ 이 사례가 대표적인가? (극단적 사례면 명시)
|
||||
✅ 다른 고객에게도 적용 가능한가?
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 카테고리 필수 규칙
|
||||
|
||||
**모든 블로그 포스트는 반드시 하나의 카테고리에 할당되어야 합니다. (NOT NULL)**
|
||||
|
||||
### 카테고리별 포스트 배치
|
||||
|
||||
| 카테고리 | 최소 포스트 | 주제 범위 |
|
||||
|---------|-----------|---------|
|
||||
| 사업자 세무 | 3개 | 기장, 세무신고, 부가세, 종합소득세 |
|
||||
| 부동산 세금 | 3개 | 월세, 양도세, 상속세(부동산) |
|
||||
| 종합소득세 | 3개 | 프리랜서, 부업, 경비 처리 |
|
||||
| 부가가치세 | 3개 | 신고, 기한, 간이과세 vs 일반과세 |
|
||||
| 가족자산·증여 | 3개 | 자녀 증여, 상속, 자산 이전 |
|
||||
|
||||
### 카테고리 할당 규칙
|
||||
|
||||
1. **명확한 주제 분류**: 포스트 내용이 카테고리 범위에 명확하게 해당
|
||||
2. **중복 금지**: 한 포스트는 정확히 하나의 카테고리에만 할당
|
||||
3. **균형 배치**: 각 카테고리당 최소 3개씩 (고객 검색 UX)
|
||||
4. **검색 최적화**: 고객이 카테고리로 찾을 때 관련 포스트 3개 이상 노출
|
||||
|
||||
### 카테고리 미할당 시 (오류)
|
||||
- ❌ category_id = NULL (데이터베이스 제약 위반)
|
||||
- ❌ SQL 실행 실패 (NOT NULL 제약)
|
||||
- ❌ 블로그 페이지 노출 불가
|
||||
|
||||
**이 규칙은 모든 포스트 생성/수정 시 필수 준수사항입니다.**
|
||||
|
||||
---
|
||||
|
||||
## 핵심 철학: 고객이 느끼는 여정
|
||||
|
||||
### 1️⃣ 기초: "이 정도는 할 수 있어요"
|
||||
- 고객이 배울 수 있는 기본 개념
|
||||
- 실제 사례로 구체화
|
||||
- 단계별 설명
|
||||
|
||||
### 2️⃣ 현실: "하지만 복잡하네요"
|
||||
- 겹겹이 쌓인 세부사항들
|
||||
- 매년 바뀌는 세법
|
||||
- "이거 일일이 다 챙기기 어렵다"는 느낌
|
||||
|
||||
### 3️⃣ 해결: "세무사와 함께면 괜찮아요"
|
||||
- 디테일 자동 관리
|
||||
- 세법 변화 자동 반영
|
||||
- 고객은 사업에만 집중
|
||||
|
||||
---
|
||||
|
||||
**고객이 글을 읽은 후 느끼는 것**:
|
||||
|
||||
1️⃣ 읽고 나서: "아, 이 정도는 내가 할 수 있겠네"
|
||||
2️⃣ 생각해보니: "근데 이 모든 걸 매년 챙기기는... 힘들겠는데?"
|
||||
3️⃣ 결론: "그럼 전문가 도움을 받는 게 낫겠다"
|
||||
|
||||
→ 자연스럽게 세무사의 필요성을 깨달음 (강요 아님)
|
||||
|
||||
---
|
||||
|
||||
## 템플릿 (복사해서 사용)
|
||||
|
||||
### Step 1: 도입부 (공감)
|
||||
```markdown
|
||||
# [제목]
|
||||
|
||||
"[구체적 상황]?"
|
||||
"많은 [직업]들이 이 상황을 겪습니다."
|
||||
|
||||
→ 독자가 자신의 상황을 발견하도록
|
||||
```
|
||||
|
||||
**예시**:
|
||||
```markdown
|
||||
# 동네 카페 월세 낼 때 세금이 안 나와요 - 정말 그럴까?
|
||||
|
||||
"사업을 시작했는데 세금을 낸 적이 없어요"
|
||||
"많은 소규모 사업자들이 이렇게 생각합니다."
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 2: 실제 사례 (구체적 페르소나)
|
||||
|
||||
**필수 정보**:
|
||||
- 이름, 나이, 직업, 사업 경력
|
||||
- 월/연간 매출 (현실적 수치)
|
||||
- 실제 겪은 문제/성공 사례
|
||||
|
||||
```markdown
|
||||
### 상황: [지역] [카테고리]를 운영하는 [이름]님 ([나이]세, 사업 [년]차)
|
||||
|
||||
**기본 정보**:
|
||||
- 위치: [구체적 위치]
|
||||
- 월 매출: [금액]
|
||||
- 월 경비: [주요 항목들]
|
||||
|
||||
### 원래는 이렇게 했어요 (실패 사례)
|
||||
→ [실제 실수 1]
|
||||
→ [실제 실수 2]
|
||||
→ **결과**: 세금을 [X만 원] 더 내게 됨 (또는 세무조사 대상)
|
||||
|
||||
### 바뀐 후 (성공 사례)
|
||||
→ [해결책 1]
|
||||
→ [해결책 2]
|
||||
→ **결과**: 세금을 [X만 원] 절약함 (또는 안정적인 운영)
|
||||
```
|
||||
|
||||
**예시**:
|
||||
```markdown
|
||||
### 상황: 강남 역삼동에서 카페를 운영하는 김민수님 (34세, 사업 3년차)
|
||||
|
||||
**기본 정보**:
|
||||
- 위치: 강남역 3번 출구 근처
|
||||
- 월 매출: 약 600만 원 (평일 200만, 주말 400만)
|
||||
- 월 경비: 월세 150만, 재료비 180만, 직원급여 100만 원
|
||||
|
||||
### 원래는 이렇게 했어요
|
||||
→ "세금은 큰 회사나 내는 거라고 생각했어요"
|
||||
→ 영수증도 대충 정리하고
|
||||
→ **결과**: 세무청에서 3년치를 추징받고 가산세까지...손해 70만 원
|
||||
|
||||
### 바뀐 후
|
||||
→ 매달 영수증을 정리해서
|
||||
→ 세무사와 년 1회 기장 상담
|
||||
→ **결과**: 세금도 명확하고, 추징도 없음. 심플하고 안전
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 3: 계산 & 설명
|
||||
|
||||
**구조**:
|
||||
1. **기본 정보 확인** (위에서 제시한 사례 요약)
|
||||
2. **단계별 계산** (Step 1, Step 2, ... 명확히)
|
||||
3. **표로 시각화**
|
||||
|
||||
```markdown
|
||||
## 계산 방법
|
||||
|
||||
### Step 1️⃣: 매출 정리
|
||||
월 600만 원 × 12개월 = 연 7,200만 원
|
||||
|
||||
### Step 2️⃣: 경비 계산
|
||||
|
||||
월 경비 구성:
|
||||
- 월세: 150만 원 (연 1,800만 원)
|
||||
- 재료비: 180만 원 (연 2,160만 원)
|
||||
- 직원급여: 100만 원 (연 1,200만 원)
|
||||
- 기타: 20만 원 (연 240만 원)
|
||||
- **월 합계: 450만 원**
|
||||
- **연 합계: 5,400만 원**
|
||||
|
||||
### Step 3️⃣: 순이익
|
||||
7,200만 - 5,400만 = **1,800만 원**
|
||||
|
||||
### Step 4️⃣: 세금
|
||||
1,800만 원 × 약 6% = **약 108만 원/년**
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 🎭 Step 3.5: 악마는 디테일이다 (선택사항이지만 강력함)
|
||||
|
||||
**구조**: "간단해 보이지만, 실제로는..."
|
||||
|
||||
```markdown
|
||||
## 겉으로는 간단해 보여요... 하지만
|
||||
|
||||
### 📄 "영수증을 정리하세요"라고 했는데
|
||||
|
||||
**겉으로는**:
|
||||
→ 영수증을 모으기만 하면 돼
|
||||
|
||||
**현실의 디테일**:
|
||||
→ 이 영수증은 인정되고, 이건 안 됨 (세법)
|
||||
→ 이건 개인비? 사업비? (판단)
|
||||
→ 카드값이랑 현금값이랑 다르면? (대사)
|
||||
→ 3년 지났는데 영수증을 못 찾으면? (소송)
|
||||
→ 세무청이 불인정하면? (항의 절차)
|
||||
|
||||
**세무사가 처리하는 것**:
|
||||
✅ 어떤 영수증이 인정될지 사전에 판단
|
||||
✅ 개인비와 사업비의 경계 명확히
|
||||
✅ 세법 변경사항 적용
|
||||
✅ 세무청 부인시 대응 준비
|
||||
|
||||
---
|
||||
|
||||
### 📊 "매출과 경비를 기록하세요"라고 했는데
|
||||
|
||||
**겉으로는**:
|
||||
→ 엑셀에 숫자만 입력하면 돼
|
||||
|
||||
**현실의 디테일**:
|
||||
→ 카드 명세서와 입금액이 안 맞음 (환불? 수수료?)
|
||||
→ 한 달간 매출을 빼먹음 (추가 계산)
|
||||
→ 같은 카테고리인데 세법상 다르게 분류돼야 함 (부가세/소득세 다름)
|
||||
→ 작년에 잘못 입력한 게 발견됨 (수정신고)
|
||||
→ 월별로 편차가 커서 세무청이 의심함 (설명 준비)
|
||||
|
||||
**세무사가 처리하는 것**:
|
||||
✅ 카드명세서 vs 입금액 정산
|
||||
✅ 누락된 부분 찾아서 추가
|
||||
✅ 세법상 올바른 분류
|
||||
✅ 이전년도 오류 수정신고
|
||||
✅ 세무청 질의에 대한 근거 제시
|
||||
|
||||
---
|
||||
|
||||
### ✅ "정확하게 기장하면 세금이 확정돼요"라고 했는데
|
||||
|
||||
**겉으로는**:
|
||||
→ 기장만 잘하면 세금 끝
|
||||
|
||||
**현실의 디테일**:
|
||||
→ 같은 사업도 절세 방법이 다양함 (어떤 게 맞나?)
|
||||
→ 올해는 이렇게, 내년은 저렇게? (일관성)
|
||||
→ 부가세와 소득세 둘 다 고려해야 함 (연계 계산)
|
||||
→ 세무조사가 오면 3년치를 모두 봄 (소급 처리)
|
||||
→ 이의신청/항소하려면? (법적 절차)
|
||||
|
||||
**세무사가 처리하는 것**:
|
||||
✅ 최적의 절세 전략 제시
|
||||
✅ 연도별 일관된 기장 방식 유지
|
||||
✅ 부가세/소득세 동시 최적화
|
||||
✅ 세무조사 대비 사전 정리
|
||||
✅ 이의신청/항소 등 법적 대응
|
||||
```
|
||||
|
||||
**💡 핵심**:
|
||||
- 기초는 누구나 배울 수 있어요
|
||||
- **하지만 디테일을 모두 처리하려면?**
|
||||
- **그 디테일들이 바로 세무사가 하는 일**
|
||||
- **디테일 하나 놓쳤다가 가산세 50만 원... 이래서 세무사 비용이 아깝지 않음**
|
||||
|
||||
---
|
||||
|
||||
### 🔄 Step 3.6: 세법은 계속 바뀐다 (매년 업데이트)
|
||||
|
||||
**구조**: "올해의 세법 변화"를 포스트 작성 시점에 맞춰 갱신
|
||||
|
||||
```markdown
|
||||
## 그런데 세법은 해마다 바뀝니다
|
||||
|
||||
### 📋 [연도] 변경사항들 (꼭 알아야 할 것들)
|
||||
|
||||
**✅ 2025년 부가세 변화**:
|
||||
- 신고 기한이 [날짜]로 변경됨
|
||||
- 영세사업자 기준이 [금액]로 상향조정됨
|
||||
- 새로운 공제 항목이 추가됨: [항목들]
|
||||
|
||||
**✅ 2025년 소득세 변화**:
|
||||
- 기본공제가 [금액]에서 [금액]로 증가
|
||||
- 자녀 공제 조건이 변경됨
|
||||
- 월급 원천징수 기준이 조정됨
|
||||
|
||||
**✅ 2025년 새로운 제도**:
|
||||
- 소상공인 세금 감면 확대
|
||||
- 청년사업자 지원 강화
|
||||
- 부가가치세 간편신청 범위 확대
|
||||
|
||||
---
|
||||
|
||||
**혼자서 할 때의 문제**:
|
||||
❌ "작년 기준으로 기장했는데 올해 기준이 바뀐 거야?"
|
||||
❌ "이 공제가 되는 건지 안 되는 건지 모르겠어"
|
||||
❌ "새로운 제도가 나왔다는 것도 몰랐어"
|
||||
❌ "처음 다시 계산해야 하나?"
|
||||
|
||||
**세무사가 처리하는 것**:
|
||||
✅ 매년 변경사항 자동 추적
|
||||
✅ 당신의 상황에 맞는 새로운 공제 적용
|
||||
✅ 이전년도 재계산 필요시 수정신고
|
||||
✅ 연중 세법 개정 소식 안내
|
||||
✅ 새로운 지원 정책 놓치지 않게 관리
|
||||
|
||||
---
|
||||
|
||||
## 결과 비교: 혼자 할 때 vs 세무사와 함께
|
||||
|
||||
**세법 변화 추적**
|
||||
- 혼자: "어? 규칙이 바뀌었네?"
|
||||
- 세무사: 자동으로 적용됨
|
||||
|
||||
**새로운 공제**
|
||||
- 혼자: 놓치기 쉬움
|
||||
- 세무사: 모두 적용됨
|
||||
|
||||
**매년 재계산**
|
||||
- 혼자: 직접 해야 함
|
||||
- 세무사: 자동 갱신
|
||||
|
||||
**마음 편함**
|
||||
- 혼자: 불안감 ("맞나?")
|
||||
- 세무사: 확신 ("전문가가 관리")
|
||||
|
||||
**투자 시간**
|
||||
- 혼자: 당신의 시간
|
||||
- 세무사: 포함 (전문가 비용)
|
||||
|
||||
---
|
||||
|
||||
## 요약: 왜 세무사가 필요한가
|
||||
|
||||
**기초는 배울 수 있지만**:
|
||||
- 세법은 매년 바뀌고
|
||||
- 당신은 본업이 있어서 추적이 어렵고
|
||||
- 실수 하나가 가산세 50만 원...
|
||||
|
||||
**그래서 세무사가 있으면**:
|
||||
- 변화를 자동으로 적용해주고
|
||||
- 새 제도도 놓치지 않아주고
|
||||
- 당신은 사업에만 집중
|
||||
|
||||
→ **결국 시간, 돈, 스트레스 모두 절약**
|
||||
|
||||
---
|
||||
|
||||
### 💡 Step 4: 실무 팁 (3~5개)
|
||||
|
||||
**구조**: ✅ 이렇게 하세요 / ❌ 이렇게 하면 안 돼요
|
||||
|
||||
```markdown
|
||||
## 이렇게 하면 세금이 명확해요
|
||||
|
||||
### ✅ 해야 할 것
|
||||
1. **영수증 정리** - 매달 봉투에 모아두기
|
||||
2. **기본 기록** - 엑셀에 간단히 기입
|
||||
3. **연 1회 점검** - 세무사와 기본 상담
|
||||
4. **투명성** - 세무청 신고는 정확하게
|
||||
|
||||
### ❌ 하면 안 되는 것
|
||||
1. **영수증 버리기** - 나중에 증거 없음
|
||||
2. **개인비와 섞기** - 기장 혼란
|
||||
3. **신고 늦추기** - 가산세 발생
|
||||
4. **과하게 깎기** - 세무조사 리스크
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 📝 Step 5: 결론
|
||||
|
||||
고객이 읽은 후 자연스럽게 결론을 내리도록:
|
||||
|
||||
**구조**:
|
||||
1. 기초는 할 수 있다 (긍정)
|
||||
2. 근데 복잡하네요 (현실 직시)
|
||||
3. 그래서 세무사가 필요하구나 (자연스러운 깨달음)
|
||||
|
||||
**고객이 느끼는 여정**:
|
||||
- 처음: "아, 이 정도는 내가 할 수 있겠네"
|
||||
- 중간: "근데 이 모든 걸 매년 챙기기는..."
|
||||
- 결론: "전문가 도움이 낫겠다"
|
||||
|
||||
```markdown
|
||||
## 기초는 누구나 할 수 있어요
|
||||
|
||||
**이 정도면 자신이 충분히 가능합니다**:
|
||||
- 소규모 사업 (월 500만~1,000만 원)
|
||||
- 단순 경비 (재료, 임차료 등)
|
||||
- 월 1회 정도 기본 정리
|
||||
|
||||
→ 영수증 정리 + 기본 엑셀 기입면 충분
|
||||
|
||||
---
|
||||
|
||||
## 하지만 이렇게 복잡하면 전문가 도움이 효율적입니다
|
||||
|
||||
**세무사 상담을 권하는 경우**:
|
||||
- 📊 월 매출이 2,000만 원을 넘어갈 때
|
||||
- 💼 여러 사업을 동시에 운영할 때
|
||||
- 🏠 부동산 등 추가 수입이 있을 때
|
||||
- 📈 직원을 여러 명 두고 있을 때
|
||||
- 🌍 해외 거래나 수입이 있을 때
|
||||
|
||||
### 실제 효과: 숫자로 본 세무사의 가치
|
||||
|
||||
**절세액**
|
||||
- 혼자: X만 원
|
||||
- 세무사: X + 200만 원
|
||||
- 차이: +200만 원 절약
|
||||
|
||||
**세무조사 스트레스**
|
||||
- 혼자: 매년 불안
|
||||
- 세무사: 안정적 대응
|
||||
- 차이: 심리적 안정
|
||||
|
||||
**시간 투자**
|
||||
- 혼자: 월 10시간
|
||||
- 세무사: 월 1시간
|
||||
- 차이: 월 9시간 자유
|
||||
|
||||
**세무사 비용**
|
||||
- 혼자: 0원
|
||||
- 세무사: 약 100만 원/년
|
||||
- 차이: -100만 원
|
||||
|
||||
**실제 이익**
|
||||
- 혼자: 순이익
|
||||
- 세무사: 순이익 + 100만 원
|
||||
- 차이: +100만 원 순이익
|
||||
|
||||
**돈을 쓰는 이유**:
|
||||
- 세금 절약: 절세 200만 원 - 비용 100만 원 = 순 100만 원 이득
|
||||
- 시간 절약: 월 9시간(연 108시간) = 사업에 집중
|
||||
- 스트레스 감소: 세무조사 불안 제거
|
||||
- 리스크 관리: 실수로 인한 가산세 방지
|
||||
|
||||
---
|
||||
|
||||
## 요약
|
||||
|
||||
**기본 개념을 아는 것만으로도**:
|
||||
- 실수를 줄이고
|
||||
- 세금을 절약하고
|
||||
- 세무사와의 상담이 훨씬 효율적
|
||||
|
||||
당신의 상황이 어느 정도인지 판단하고,
|
||||
필요할 때 전문가와 함께 하세요.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ 작성 체크리스트
|
||||
|
||||
### 내용
|
||||
- [ ] **실제 사례**: 동네 카페/편의점/학원 같은 주변 상황
|
||||
- [ ] **구체적 페르소나**: 이름, 나이, 직업, 사업 경력
|
||||
- [ ] **실제 금액**: 매출, 경비, 세금 (현실적 수치)
|
||||
- [ ] **Before/After**: 실패 사례 → 성공 사례
|
||||
- [ ] **절세 효과**: "X만 원 절약" 또는 "손해를 막음"
|
||||
- [ ] **계산**: Step별로 명확, 표 포함
|
||||
- [ ] **악마는 디테일**: "겉으로는 간단해 보이지만 실제로는..." (세무사가 처리하는 디테일들)
|
||||
- [ ] **세법 변화**: 해당 연도의 세법 변경사항과 그 영향 설명
|
||||
|
||||
### 톤
|
||||
- [ ] **교육적**: 개념을 이해하도록
|
||||
- [ ] **격려적**: 경고/협박 없음
|
||||
- [ ] **현실적**: 복잡할 수 있다는 인정
|
||||
- [ ] **세무사 자연스러운 유도**: "필요할 때 도움되는 선택"
|
||||
- [ ] **임파워먼트**: "기초는 누구나 할 수 있어요"
|
||||
|
||||
### 표현
|
||||
- [ ] **중학교 수준**: 어려운 용어는 () 설명
|
||||
- [ ] **이모지**: 🏠💰✅❌📊 등으로 시각화
|
||||
- [ ] **짧은 문장**: 한 문장에 한 개념
|
||||
- [ ] **표와 리스트**: 수치는 표로, 항목은 리스트로
|
||||
|
||||
---
|
||||
|
||||
## 🚫 피해야 할 표현 (한국세무사협회 광고 규칙 준수)
|
||||
|
||||
### ❌ **절대 금지 표현** (법적 위반 위험)
|
||||
|
||||
**1. 과도한 절세 약속 & 절대 표현**:
|
||||
- ❌ "50만 원 절약 가능"
|
||||
- ❌ "최대한 경비를 깎아줍니다"
|
||||
- ❌ "세금을 반으로 줄여드립니다"
|
||||
- ❌ "세금을 덜 냅니다" (보장으로 해석)
|
||||
- ❌ "가장 많이 절세해드립니다"
|
||||
- ✅ "이 사례에서는 약 50만 원 절약되었습니다" (과거 사례만)
|
||||
- ✅ "정확한 경비 처리로 세법에 따른 정당한 공제를 받을 수 있습니다" (법적 근거)
|
||||
- ✅ "경비를 빠짐없이 처리합니다" (객관적 프로세스)
|
||||
|
||||
**2. 보장 표현 (불가능한 결과 약속)**:
|
||||
- ❌ "반드시 세금을 줄입니다"
|
||||
- ❌ "세무조사 안 받게 해드립니다"
|
||||
- ❌ "100% 절세를 보장합니다"
|
||||
- ❌ "세금을 보장합니다"
|
||||
- ✅ "정확한 신고로 세무조사 리스크를 최소화합니다"
|
||||
- ✅ "세법에 따른 정당한 공제를 받을 수 있습니다"
|
||||
|
||||
**3. 무료 & 가격 표현**:
|
||||
- ❌ "무료로 세금 절약해드립니다"
|
||||
- ❌ "최저가 신고료"
|
||||
- ❌ "가장 저렴한 가격"
|
||||
- ✅ "합리적인 비용으로 전문 서비스를 제공합니다"
|
||||
|
||||
**4. 절대/최상급 표현**:
|
||||
- ❌ "반드시", "무조건", "반듯이", "항상", "절대"
|
||||
- ❌ "최고", "최우수", "1등", "유일"
|
||||
- ❌ "모든", "완벽하게"
|
||||
- ✅ "일반적으로", "대부분의 경우", "보통"
|
||||
|
||||
**5. 과도한 단순화 표현**:
|
||||
- ❌ "매우 편합니다", "너무 쉽습니다"
|
||||
- ❌ "아무도 실수할 수 없습니다"
|
||||
- ❌ "5분이면 끝납니다"
|
||||
- ✅ "기초 개념을 배울 수 있습니다"
|
||||
- ✅ "복잡한 부분은 전문가가 관리합니다"
|
||||
|
||||
**6. 객관적 증거 없는 수치**:
|
||||
- ❌ "평균 170만 원 절약" (근거 없으면)
|
||||
- ❌ "고객의 80%가 만족" (통계 없으면)
|
||||
- ❌ "보통 2배의 환급" (데이터 없으면)
|
||||
- ✅ "이 사례에서는 약 170만 원 절약되었습니다"
|
||||
- ✅ "많은 고객들이 정확한 기장의 필요성을 느낍니다"
|
||||
|
||||
---
|
||||
|
||||
### ✅ **안전한 표현 (권장)**
|
||||
|
||||
| 대신 이렇게 | 이유 |
|
||||
|----------|------|
|
||||
| "정확한 기장으로 세법에 따른 공제를 받을 수 있습니다" | 법적 근거 (보장 아님) |
|
||||
| "경비를 빠짐없이 처리합니다" | 객관적 프로세스 |
|
||||
| "이 사례에서는 약 50만 원 절약되었습니다" | 과거 사례 (보장 아님) |
|
||||
| "경비를 빠짐없이 처리합니다" | 객관적 프로세스 |
|
||||
| "세무조사 대비 근거를 정리합니다" | 예방적 표현 |
|
||||
| "당신의 상황에 맞는 최선의 방법을 제시합니다" | 개별 맞춤형 |
|
||||
| "세법이 자주 바뀌므로 전문가 도움이 효율적입니다" | 필요성 설명 |
|
||||
| "이 정도는 자신이 충분히 가능합니다" | 존중과 임파워먼트 |
|
||||
| "복잡한 경우는 전문가와 상담하세요" | 선택지 제시 |
|
||||
| "정확하게 하면 나중에 편합니다" | 미래 가치 (현재 보장 아님) |
|
||||
|
||||
---
|
||||
|
||||
### 📋 블로그 작성 시 광고 규칙 체크리스트
|
||||
|
||||
- [ ] **절세 약속 제거**: "최대한", "반드시", "보장", "무조건" 단어 없음
|
||||
- [ ] **보장 표현 제거**: "세무조사 안 받게", "100% 절세", "확실" 제거
|
||||
- [ ] **무료/가격 표현 제거**: "무료", "최저가", "가장 저렴" 제거
|
||||
- [ ] **절대 표현 제거**: "항상", "절대", "모두", "완벽" 제거
|
||||
- [ ] **최상급 제거**: "최고", "최우수", "1등" (객관적 증거 있으면 가능)
|
||||
- [ ] **과도한 단순화 제거**: "매우 쉽습니다", "아무도 실수할 수 없음" 제거
|
||||
- [ ] **수치는 사례로**: "절약 가능" → "이 사례에서는 약 X만 원 절약"
|
||||
- [ ] **객관성 유지**: 구체적 사례 + 과거형 표현 사용
|
||||
- [ ] **필요성 설명**: "왜 필요한가" → 이해와 선택 유도
|
||||
- [ ] **세무사협회 규정 준수**: 법적 문제 없음
|
||||
|
||||
---
|
||||
|
||||
## 시즌별 주제 예시
|
||||
|
||||
| 월 | 추천 주제 | 톤 |
|
||||
|----|---------|-----|
|
||||
| 1월 | 부가세 2기 신고 기한 | "이 정도면 자신이 가능" |
|
||||
| 5월 | 종소세 신고 방법 | "핵심 개념 + 전문가 도움 타이밍" |
|
||||
| 7월 | 부가세 1기 신고 | "기초 정리 방법" |
|
||||
| 11월 | 다음해 준비 | "계획하면 편해요" |
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ 실수 방지 체크리스트 (과거 오류 기록)
|
||||
|
||||
**이전에 반복된 실수들을 기록하여, 같은 실수를 하지 않도록 합니다.**
|
||||
|
||||
### 1️⃣ 카테고리 할당 실수 ❌
|
||||
|
||||
**과거 오류**: 포스트를 만들 때 category_id를 NULL로 두었음
|
||||
|
||||
**문제점**:
|
||||
- DB NOT NULL 제약 위반
|
||||
- 블로그 페이지에 노출 안 됨
|
||||
- 고객이 카테고리로 검색 불가
|
||||
|
||||
**예방책**:
|
||||
- ✅ **SQL INSERT 시 반드시 category_id 명시**
|
||||
- ✅ **포스트 작성 전에 카테고리 결정**
|
||||
- ✅ **DB 적용 후 category_id NOT NULL 확인**
|
||||
- ✅ **각 카테고리별 최소 3개 이상 포스트 유지**
|
||||
|
||||
**SQL 예시** (권장):
|
||||
```sql
|
||||
INSERT INTO blog_posts (title, slug, content, category_id, is_published, ...)
|
||||
VALUES ('제목', 'slug', $$본문$$, 1, true, ...);
|
||||
-- category_id 절대 생략 금지!
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2️⃣ 내용 길이 부족 ❌
|
||||
|
||||
**과거 오류**: 에이전트가 지침(1,500~2,500자)을 무시하고 간단한 버전(500자)으로 생성
|
||||
|
||||
**문제점**:
|
||||
- 고객 설득력 부족
|
||||
- 계산 예시 없음
|
||||
- 3단계 구조 불완전
|
||||
- 세법 인용 부족
|
||||
|
||||
**예방책**:
|
||||
- ✅ **각 포스트 최소 1,500자 이상 (추천 2,000~2,500자)**
|
||||
- ✅ **포스트 작성 후 글자 수 확인: `LENGTH(content) >= 1500`**
|
||||
- ✅ **항상 실제 사례 포함** (이름, 나이, 직업, 구체적 상황)
|
||||
- ✅ **항상 계산 과정 포함** (절세액 수치화)
|
||||
- ✅ **3단계 구조 필수** (1️⃣ 기초 → 2️⃣ 현실 → 3️⃣ 해결책)
|
||||
|
||||
**확인 쿼리**:
|
||||
```sql
|
||||
SELECT id, title, LENGTH(content) as length FROM blog_posts
|
||||
WHERE LENGTH(content) < 1500; -- 부족한 포스트 검출
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3️⃣ 테이블 사용 금지 ❌
|
||||
|
||||
**과거 오류**: 마크다운 테이블(`| |---|---|`) 사용
|
||||
|
||||
**문제점**:
|
||||
- 지침 위반 (리스트만 사용)
|
||||
- 모바일에서 가독성 저하
|
||||
- 유지보수 어려움
|
||||
|
||||
**예방책**:
|
||||
- ✅ **테이블 금지, 리스트만 사용** (- 또는 숫자 목록)
|
||||
- ✅ **작성 후 `| |` 패턴 검색으로 테이블 확인**
|
||||
- ✅ **수치/계산은 리스트 형식**:
|
||||
|
||||
**❌ 금지 (테이블)**:
|
||||
```markdown
|
||||
| 항목 | 월 | 연간 |
|
||||
|------|-----|------|
|
||||
| 월세 | 150만 | 1,800만 |
|
||||
```
|
||||
|
||||
**✅ 권장 (리스트)**:
|
||||
```markdown
|
||||
월 경비 구성:
|
||||
- 월세: 150만 원 (연 1,800만 원)
|
||||
- 재료비: 180만 원 (연 2,160만 원)
|
||||
- 직원급여: 100만 원 (연 1,200만 원)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4️⃣ 계산 예시 누락 ❌
|
||||
|
||||
**과거 오류**: 포스트에 개념만 있고 실제 계산 예시 부족
|
||||
|
||||
**문제점**:
|
||||
- 고객이 "내 상황에 얼마나 해당하나" 판단 어려움
|
||||
- 추상적 설명으로 설득력 감소
|
||||
- 세무사 필요성 전달 미흡
|
||||
|
||||
**예방책**:
|
||||
- ✅ **모든 포스트에 구체적 계산 예시 필수**
|
||||
- ✅ **절세액을 수치로 제시** ("약 50만 원 절약")
|
||||
- ✅ **단계별 계산 과정 포함** (Step 1️⃣, 2️⃣, 3️⃣, 4️⃣)
|
||||
- ✅ **실제 사례로 숫자 구체화**:
|
||||
|
||||
**예시**:
|
||||
```markdown
|
||||
### Step 1️⃣: 매출 정리
|
||||
월 600만 원 × 12개월 = 연 7,200만 원
|
||||
|
||||
### Step 2️⃣: 경비 계산
|
||||
- 월세: 150만 원 → 연 1,800만 원
|
||||
- 재료비: 180만 원 → 연 2,160만 원
|
||||
합계: 5,400만 원
|
||||
|
||||
### Step 3️⃣: 순이익
|
||||
7,200만 - 5,400만 = 1,800만 원
|
||||
|
||||
### Step 4️⃣: 세금
|
||||
1,800만 원 × 약 6% = **약 108만 원/년**
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 5️⃣ 카테고리 주제 불일치 ❌
|
||||
|
||||
**과거 오류**: 포스트 주제와 카테고리가 맞지 않음
|
||||
|
||||
**문제점**:
|
||||
- 고객이 원하는 정보 검색 불가
|
||||
- 카테고리 신뢰도 저하
|
||||
- UX 혼란
|
||||
|
||||
**예방책**:
|
||||
- ✅ **포스트 작성 전 카테고리 명확히 결정**
|
||||
- ✅ **포스트 주제와 카테고리 일관성 검증**:
|
||||
|
||||
| 포스트 | 카테고리 | 확인 |
|
||||
|--------|---------|------|
|
||||
| 프리랜서 경비 | 종합소득세 (3) | ✅ 맞음 |
|
||||
| 월세 신고 | 부동산 세금 (2) | ✅ 맞음 |
|
||||
| 자녀 증여세 | 가족자산·증여 (5) | ✅ 맞음 |
|
||||
| 사업자 기장 | 사업자 세무 (1) | ✅ 맞음 |
|
||||
| 부가세 신고 | 부가가치세 (4) | ✅ 맞음 |
|
||||
|
||||
---
|
||||
|
||||
### 6️⃣ 정확한 세법 인용 누락 ❌
|
||||
|
||||
**과거 오류**: 일부 포스트에서 법조 명시 부족
|
||||
|
||||
**문제점**:
|
||||
- 정확성 원칙 위반
|
||||
- 법적 책임 불명확
|
||||
- 고객 신뢰도 저하
|
||||
|
||||
**예방책**:
|
||||
- ✅ **모든 주요 내용에 세법 조항 인용 필수**
|
||||
- ✅ **형식**: "소득세법 제XX조에 따르면"
|
||||
- ✅ **연도 기준 명시**: "2025년 기준"
|
||||
- ✅ **포스트 끝에 "법적 근거" 섹션 필수**:
|
||||
|
||||
```markdown
|
||||
**법적 근거**:
|
||||
- 소득세법 제29조 (수입금액의 계산)
|
||||
- 국세기본법 제47조 (가산세)
|
||||
- 소득세법 제160조 (증빙 보관)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ 포스트 최종 체크리스트
|
||||
|
||||
모든 포스트를 DB에 등록하기 전에 다음을 확인하세요:
|
||||
|
||||
- [ ] **카테고리 할당**: `category_id NOT NULL` (필수)
|
||||
- [ ] **내용 길이**: `LENGTH(content) >= 1500` (최소 1,500자)
|
||||
- [ ] **테이블 확인**: `| |` 패턴 없음 (리스트만)
|
||||
- [ ] **계산 예시**: Step 1️⃣~4️⃣ 포함 (절세액 수치)
|
||||
- [ ] **세법 인용**: 모든 주요 내용에 법조 명시
|
||||
- [ ] **카테고리 일치**: 포스트 주제 ↔ 카테고리 일관성
|
||||
- [ ] **3단계 구조**: 1️⃣ 기초 → 2️⃣ 현실 → 3️⃣ 해결책
|
||||
- [ ] **광고 규칙**: 금지 표현(보장, 최저가, 무료) 없음
|
||||
- [ ] **사례 포함**: 실제 상황 + 이름/나이/직업 구체화
|
||||
- [ ] **정확성**: 추측/예상/의견 표현 없음
|
||||
|
||||
**체크 쿼리**:
|
||||
```sql
|
||||
-- DB 적용 후 확인
|
||||
SELECT id, title, LENGTH(content), category_id
|
||||
FROM blog_posts
|
||||
WHERE LENGTH(content) < 1500 OR category_id IS NULL
|
||||
ORDER BY id;
|
||||
-- 결과 없음이 정상!
|
||||
```
|
||||
@@ -17,7 +17,7 @@
|
||||
| 3.3 | [주요 Python 패키지](#33-주요-python-패키지-시스템) | 시스템/venv 패키지 구분 |
|
||||
| 4 | [서비스 아키텍처](#4-서비스-아키텍처) | 포트 맵, Nginx 리버스 프록시 |
|
||||
| 4.1 | [포트 맵](#41-포트-맵) | 22, 80, 2222, 3000, 5000, 5432 |
|
||||
| 4.2 | [Nginx 리버스 프록시](#42-nginx-리버스-프록시) | `/` → Gitea, `/quant/` → Blazor |
|
||||
| 4.2 | [Nginx 리버스 프록시](#42-nginx-리버스-프록시) | 도메인 기반 가상 호스트 분기 (홈페이지, Gitea, Quant) |
|
||||
| 5 | [Gitea](#5-gitea) | Docker Compose 설정, 시크릿, 데이터 경로 |
|
||||
| 5.1 | [Docker Compose](#51-docker-compose) | `gitea:1.26.4`, PG 연동 |
|
||||
| 5.2 | [시크릿 관리](#52-시크릿-관리) | `/opt/stacks/gitea/.env` |
|
||||
@@ -126,17 +126,22 @@ boto3, cryptography, Jinja2, jsonschema, fail2ban 등 시스템 레벨로 설치
|
||||
### 4.2. Nginx 리버스 프록시
|
||||
|
||||
```nginx
|
||||
# /etc/nginx/sites-enabled/gitea-ip.conf
|
||||
# /etc/nginx/sites-available/taxbaik-domains.conf
|
||||
|
||||
# 1. TaxBaik 홈페이지 (taxbaik.com, www.taxbaik.com)
|
||||
server {
|
||||
listen 80 default_server;
|
||||
listen [::]:80 default_server;
|
||||
server_name _;
|
||||
server_name taxbaik.com www.taxbaik.com;
|
||||
client_max_body_size 512M;
|
||||
|
||||
# QuantEngine Blazor Web App
|
||||
location /quant/ {
|
||||
proxy_pass http://127.0.0.1:5000/;
|
||||
|
||||
# /admin 하위 요청을 /taxbaik/admin 으로 리다이렉트하여 Blazor Base Path 대응
|
||||
location /admin {
|
||||
return 301 $scheme://$host/taxbaik$request_uri;
|
||||
}
|
||||
|
||||
# 루트 경로 요청을 /taxbaik 으로 프록싱하여 base href /taxbaik/ 에 대응
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:5001/taxbaik/;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "Upgrade";
|
||||
@@ -147,7 +152,33 @@ server {
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
|
||||
# Gitea (기본)
|
||||
# /taxbaik/ 하위로 들어오는 리소스 및 페이지 요청 처리
|
||||
location /taxbaik {
|
||||
proxy_pass http://127.0.0.1:5001;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "Upgrade";
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_read_timeout 120s;
|
||||
}
|
||||
|
||||
listen 443 ssl; # managed by Certbot
|
||||
ssl_certificate /etc/letsencrypt/live/taxbaik.com/fullchain.pem; # managed by Certbot
|
||||
ssl_certificate_key /etc/letsencrypt/live/taxbaik.com/privkey.pem; # managed by Certbot
|
||||
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
|
||||
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
|
||||
|
||||
|
||||
}
|
||||
|
||||
# 2. Gitea (gitea.taxbaik.com)
|
||||
server {
|
||||
server_name gitea.taxbaik.com;
|
||||
client_max_body_size 512M;
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:3000;
|
||||
proxy_http_version 1.1;
|
||||
@@ -159,13 +190,89 @@ server {
|
||||
proxy_connect_timeout 300;
|
||||
proxy_send_timeout 300;
|
||||
}
|
||||
|
||||
listen 443 ssl; # managed by Certbot
|
||||
ssl_certificate /etc/letsencrypt/live/taxbaik.com/fullchain.pem; # managed by Certbot
|
||||
ssl_certificate_key /etc/letsencrypt/live/taxbaik.com/privkey.pem; # managed by Certbot
|
||||
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
|
||||
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
|
||||
|
||||
}
|
||||
|
||||
# 3. QuantEngine (quant.taxbaik.com)
|
||||
server {
|
||||
server_name quant.taxbaik.com;
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:5000/;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "Upgrade";
|
||||
proxy_set_header Host $host;
|
||||
proxy_cache_bypass $http_upgrade;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
|
||||
listen 443 ssl; # managed by Certbot
|
||||
ssl_certificate /etc/letsencrypt/live/taxbaik.com/fullchain.pem; # managed by Certbot
|
||||
ssl_certificate_key /etc/letsencrypt/live/taxbaik.com/privkey.pem; # managed by Certbot
|
||||
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
|
||||
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
|
||||
|
||||
}
|
||||
|
||||
server {
|
||||
if ($host = www.taxbaik.com) {
|
||||
return 301 https://$host$request_uri;
|
||||
} # managed by Certbot
|
||||
|
||||
|
||||
if ($host = taxbaik.com) {
|
||||
return 301 https://$host$request_uri;
|
||||
} # managed by Certbot
|
||||
|
||||
|
||||
listen 80;
|
||||
server_name taxbaik.com www.taxbaik.com;
|
||||
return 404; # managed by Certbot
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
server {
|
||||
if ($host = gitea.taxbaik.com) {
|
||||
return 301 https://$host$request_uri;
|
||||
} # managed by Certbot
|
||||
|
||||
|
||||
listen 80;
|
||||
server_name gitea.taxbaik.com;
|
||||
return 404; # managed by Certbot
|
||||
|
||||
|
||||
}
|
||||
server {
|
||||
if ($host = quant.taxbaik.com) {
|
||||
return 301 https://$host$request_uri;
|
||||
} # managed by Certbot
|
||||
|
||||
|
||||
listen 80;
|
||||
server_name quant.taxbaik.com;
|
||||
return 404; # managed by Certbot
|
||||
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
**라우팅 요약**:
|
||||
- `http://178.104.200.7/` → Gitea Web UI
|
||||
- `http://178.104.200.7/quant/` → QuantEngine Blazor Admin
|
||||
- `ssh://178.104.200.7:2222` → Gitea Git SSH
|
||||
- `http://taxbaik.com/` 또는 `http://www.taxbaik.com/` → TaxBaik 홈페이지 (내부 proxy: `http://127.0.0.1:5001/taxbaik/`)
|
||||
- `http://gitea.taxbaik.com/` → Gitea Web UI (내부 proxy: `http://127.0.0.1:3000`)
|
||||
- `http://quant.taxbaik.com/` → QuantEngine Blazor Admin (내부 proxy: `http://127.0.0.1:5000/`)
|
||||
- `ssh://gitea.taxbaik.com:2222` → Gitea Git SSH
|
||||
|
||||
## 5. Gitea
|
||||
|
||||
@@ -384,7 +491,7 @@ ClientAliveCountMax 2
|
||||
| **CI Runner** | Synology Act Runner | 6× `act_runner:latest` (Docker) |
|
||||
| **DB** | SQLite (파일 기반) | PostgreSQL 18 + SQLite (하이브리드) |
|
||||
| **웹 Admin** | 없음 | QuantEngine Blazor (.NET 10, MudBlazor) |
|
||||
| **리버스 프록시** | Synology 내장 | Nginx (`/` → Gitea, `/quant/` → Blazor) |
|
||||
| **리버스 프록시** | Synology 내장 | Nginx (도메인 기반 분기 - 홈페이지, Gitea, Quant) |
|
||||
| **보안** | DSM 방화벽 | fail2ban + SSH 공개키 + 서비스 로컬바인드 |
|
||||
| **시크릿 관리** | `.secrets/kis_real.env` | `/opt/stacks/gitea/.env` |
|
||||
| **OS** | Synology DSM 7.x | Ubuntu 26.04 LTS |
|
||||
@@ -19,32 +19,46 @@ GRANT ALL PRIVILEGES ON DATABASE taxbaikdb TO taxbaik;
|
||||
|
||||
### 2. 환경 변수 설정
|
||||
|
||||
**Web 서비스** (`/etc/systemd/system/taxbaik.service`):
|
||||
**Web 서비스** (`/etc/systemd/system/taxbaik.service`, 백엔드 전용):
|
||||
```ini
|
||||
[Service]
|
||||
Environment=ASPNETCORE_ENVIRONMENT=Production
|
||||
Environment=ASPNETCORE_URLS=http://127.0.0.1:5001
|
||||
Environment=ASPNETCORE_URLS=http://127.0.0.1:5004
|
||||
Environment=ConnectionStrings__Default=Host=localhost;Database=taxbaikdb;Username=taxbaik;Password=your_secure_password
|
||||
```
|
||||
|
||||
**프록시 서비스** (`/etc/systemd/system/taxbaik-proxy.service`, 5001 진입점):
|
||||
```ini
|
||||
[Service]
|
||||
ExecStart=/usr/bin/dotnet TaxBaik.Proxy.dll
|
||||
WorkingDirectory=/home/kjh2064/taxbaik_active
|
||||
Restart=always
|
||||
```
|
||||
|
||||
### 3. systemd 서비스 파일 설치
|
||||
|
||||
```bash
|
||||
sudo cp deploy/taxbaik.service /etc/systemd/system/
|
||||
sudo cp deploy/taxbaik-proxy.service /etc/systemd/system/
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable taxbaik
|
||||
sudo systemctl enable taxbaik-proxy
|
||||
```
|
||||
|
||||
### 4. Nginx 설정
|
||||
|
||||
```bash
|
||||
# 현재 Nginx 설정 확인
|
||||
sudo cat /etc/nginx/sites-available/default | head -30
|
||||
# Nginx 도메인 기반 가상 호스트 설정 복사
|
||||
sudo cp deploy/nginx-taxbaik-domains.conf /etc/nginx/sites-available/taxbaik-domains.conf
|
||||
|
||||
# location 블록 추가 (또는 기존 설정에 병합)
|
||||
sudo cp deploy/nginx-taxbaik-locations.conf /etc/nginx/conf.d/taxbaik.conf
|
||||
# 기존 설정(IP 기반 및 default) 활성화 해제
|
||||
sudo rm -f /etc/nginx/sites-enabled/default
|
||||
sudo rm -f /etc/nginx/sites-enabled/gitea-ip.conf
|
||||
|
||||
# 테스트 및 재로드
|
||||
# 새 설정 활성화 (심링크 생성)
|
||||
sudo ln -sfn /etc/nginx/sites-available/taxbaik-domains.conf /etc/nginx/sites-enabled/taxbaik-domains.conf
|
||||
|
||||
# 설정 문법 테스트 및 Nginx 서비스 리로드
|
||||
sudo nginx -t
|
||||
sudo systemctl reload nginx
|
||||
```
|
||||
@@ -65,7 +79,7 @@ sudo systemctl reload nginx
|
||||
master 브랜치 push → build → test → publish → restart → health check → Playwright
|
||||
```
|
||||
|
||||
수동 배포는 비상 롤백 외에는 사용하지 않습니다. 배포 이슈는 Gitea Actions 로그로 해결합니다.
|
||||
수동 배포는 사용하지 않습니다. `deploy_gb.sh`는 `TAXBAIK_DEPLOY_FROM_CI=1`이 없으면 즉시 종료하므로, 배포는 반드시 Gitea Actions에서만 실행됩니다.
|
||||
|
||||
## 마이그레이션 자동 실행
|
||||
|
||||
@@ -128,6 +142,7 @@ ls -la ~/deployments/ | grep taxbaik
|
||||
|
||||
# 심링크 변경 (예: 이전 버전이 taxbaik_20260626_140000)
|
||||
ln -sfn ~/deployments/taxbaik_20260626_140000 ~/taxbaik_active
|
||||
sudo systemctl restart taxbaik-proxy
|
||||
sudo systemctl restart taxbaik
|
||||
```
|
||||
|
||||
@@ -139,10 +154,10 @@ sudo systemctl restart taxbaik
|
||||
ssh kjh2064@178.104.200.7
|
||||
|
||||
# 서비스 상태
|
||||
systemctl status taxbaik
|
||||
systemctl status taxbaik taxbaik-proxy
|
||||
|
||||
# 포트 확인
|
||||
netstat -tlnp | grep -E '5001'
|
||||
netstat -tlnp | grep -E '5001|5004'
|
||||
|
||||
# 프로세스 확인
|
||||
ps aux | grep TaxBaik
|
||||
@@ -165,9 +180,27 @@ journalctl -u taxbaik -f
|
||||
| 404 /taxbaik | Nginx 설정 미적용 | `sudo nginx -t && sudo systemctl reload nginx` |
|
||||
| Blazor WebSocket 안 됨 | `/taxbaik` location에 `proxy_http_version 1.1`, `Upgrade`, `Connection \"Upgrade\"` 헤더가 모두 있는지 확인 |
|
||||
| DB 연결 오류 | 환경 변수 미설정 | systemd service 파일의 ConnectionStrings__Default 확인 |
|
||||
| 503 Service Unavailable | 앱 미시작 | `sudo systemctl restart taxbaik` |
|
||||
| 503 Service Unavailable | 백엔드 또는 프록시 미시작 | `sudo systemctl restart taxbaik-proxy taxbaik` |
|
||||
| 마이그레이션 실패 | DB 권한 문제 | `GRANT ALL PRIVILEGES ON DATABASE taxbaikdb TO taxbaik;` |
|
||||
|
||||
## 운영 복구 순서
|
||||
|
||||
```bash
|
||||
ssh kjh2064@178.104.200.7
|
||||
sudo cp /home/kjh2064/taxbaik.service /etc/systemd/system/taxbaik.service
|
||||
sudo cp /home/kjh2064/taxbaik-proxy.service /etc/systemd/system/taxbaik-proxy.service
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl restart taxbaik-proxy
|
||||
sudo systemctl restart taxbaik
|
||||
curl -I http://127.0.0.1:5001/taxbaik/admin/login
|
||||
```
|
||||
|
||||
## 원라인 점검
|
||||
|
||||
```bash
|
||||
ssh kjh2064@178.104.200.7 'systemctl status taxbaik taxbaik-proxy --no-pager --lines=3 && ss -tlnp | grep -E ":5001 |:5004 " && curl -fsSI http://127.0.0.1:5001/taxbaik/admin/login && curl -fsS http://127.0.0.1:5001/taxbaik/favicon.svg >/dev/null && curl -fsS http://127.0.0.1:5001/taxbaik/robots.txt >/dev/null'
|
||||
```
|
||||
|
||||
## 초기 데이터
|
||||
|
||||
### 관리자 계정
|
||||
@@ -48,29 +48,7 @@ ssh kjh2064@178.104.200.7 'bash ~/SERVER_SETUP.sh'
|
||||
# ~/taxbaik_active
|
||||
```
|
||||
|
||||
### 2단계: 첫 배포 (수동)
|
||||
|
||||
```bash
|
||||
# 로컬에서 실행
|
||||
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
||||
|
||||
# SSH 키 설정 (필요시)
|
||||
export DEPLOY_USER="kjh2064"
|
||||
export DEPLOY_HOST="178.104.200.7"
|
||||
|
||||
# 배포
|
||||
rsync -avz --delete ./publish/ \
|
||||
$DEPLOY_USER@$DEPLOY_HOST:~/deployments/taxbaik_${TIMESTAMP}/
|
||||
|
||||
# 심링크 변경 및 시작
|
||||
ssh $DEPLOY_USER@$DEPLOY_HOST << EOF
|
||||
ln -sfn ~/deployments/taxbaik_${TIMESTAMP} ~/taxbaik_active
|
||||
sudo systemctl start taxbaik
|
||||
sudo systemctl status taxbaik
|
||||
EOF
|
||||
```
|
||||
|
||||
### 3단계: Gitea Actions 설정 (선택)
|
||||
### 2단계: Gitea Actions 설정
|
||||
|
||||
**Gitea 저장소 Settings → Secrets 추가**:
|
||||
- `DEPLOY_USER`: `kjh2064`
|
||||
@@ -217,8 +195,8 @@ curl -I -H "Accept-Encoding: gzip" http://178.104.200.7/taxbaik/ | grep -i encod
|
||||
| 증상 | 원인 | 해결 방법 |
|
||||
|------|------|----------|
|
||||
| 404 /taxbaik | Nginx 설정 미적용 | `sudo nginx -t && sudo systemctl reload nginx` |
|
||||
| 502 Bad Gateway | 앱 미실행 | `sudo systemctl restart taxbaik` |
|
||||
| 503 Service Unavailable | 앱 충돌 | 로그 확인: `journalctl -u taxbaik -n 50` |
|
||||
| 502 Bad Gateway | 프록시 또는 백엔드 미실행 | `sudo systemctl restart taxbaik-proxy taxbaik` |
|
||||
| 503 Service Unavailable | 백엔드 충돌 또는 비밀값 누락 | 로그 확인: `journalctl -u taxbaik -n 50` |
|
||||
| DB 연결 오류 | 환경 변수 미설정 | systemd 파일의 ConnectionStrings__Default 확인 |
|
||||
| HTTPS 오류 | SSL 미구성 | 개발 환경에서는 HTTP 사용 (IP 기반) |
|
||||
| 마이그레이션 실패 | 테이블 존재 | `DROP DATABASE taxbaikdb;` 후 재시작 |
|
||||
@@ -230,11 +208,11 @@ curl -I -H "Accept-Encoding: gzip" http://178.104.200.7/taxbaik/ | grep -i encod
|
||||
### 실시간 모니터링
|
||||
|
||||
```bash
|
||||
# 터미널 1: 웹 서비스 로그
|
||||
# 터미널 1: 백엔드 로그
|
||||
ssh kjh2064@178.104.200.7 'journalctl -u taxbaik -f'
|
||||
|
||||
# 터미널 2: 통합 서비스 로그
|
||||
ssh kjh2064@178.104.200.7 'journalctl -u taxbaik -f'
|
||||
# 터미널 2: 프록시 로그
|
||||
ssh kjh2064@178.104.200.7 'journalctl -u taxbaik-proxy -f'
|
||||
|
||||
# 터미널 3: Nginx 로그
|
||||
ssh kjh2064@178.104.200.7 'sudo tail -f /var/log/nginx/access.log | grep taxbaik'
|
||||
@@ -246,13 +224,7 @@ ssh kjh2064@178.104.200.7 'watch -n 1 "ps aux | grep TaxBaik"'
|
||||
### 정기적 검사
|
||||
|
||||
```bash
|
||||
# 일일 체크 (cron job)
|
||||
0 9 * * * /home/kjh2064/health-check.sh
|
||||
|
||||
# 내용:
|
||||
#!/bin/bash
|
||||
curl -f http://127.0.0.1:5001/taxbaik || systemctl restart taxbaik
|
||||
curl -f http://127.0.0.1:5001/taxbaik/admin/login || systemctl restart taxbaik
|
||||
# 일일 체크는 CI 배포 후 자동 검증으로 대체
|
||||
```
|
||||
|
||||
---
|
||||
@@ -268,11 +240,6 @@ git commit -m "기능: 새로운 기능 추가"
|
||||
git push origin master
|
||||
|
||||
# 2. Gitea Actions가 자동으로 배포
|
||||
# 또는 수동 배포:
|
||||
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
||||
dotnet publish TaxBaik.Web -c Release -o ./publish
|
||||
rsync -avz ./publish/ kjh2064@178.104.200.7:~/deployments/taxbaik_${TIMESTAMP}/
|
||||
ssh kjh2064@178.104.200.7 "ln -sfn ~/deployments/taxbaik_${TIMESTAMP} ~/taxbaik_active && sudo systemctl restart taxbaik"
|
||||
```
|
||||
|
||||
### 롤백 절차
|
||||
@@ -284,6 +251,7 @@ ssh kjh2064@178.104.200.7 'ls -la ~/deployments/ | grep taxbaik'
|
||||
# 롤백 (예: 이전 버전이 taxbaik_20260625_100000)
|
||||
ssh kjh2064@178.104.200.7 << EOF
|
||||
ln -sfn ~/deployments/taxbaik_20260625_100000 ~/taxbaik_active
|
||||
sudo systemctl restart taxbaik-proxy
|
||||
sudo systemctl restart taxbaik
|
||||
EOF
|
||||
```
|
||||
@@ -53,6 +53,23 @@ Todo:
|
||||
- [x] 배포 완료 (`12070b7`)
|
||||
- [ ] 배포 후 브라우저 아코디언 동작 확인
|
||||
|
||||
## WBS-UX-04 개인정보처리방침·이용약관 페이지
|
||||
|
||||
목표: 법적 의무를 충족하고 방문자 신뢰를 높이는 정책 페이지를 제공한다.
|
||||
|
||||
성공 기준:
|
||||
- `/taxbaik/privacy` 개인정보처리방침 페이지 정상 렌더링 (200)
|
||||
- `/taxbaik/terms` 이용약관 페이지 정상 렌더링 (200)
|
||||
- 푸터에 두 페이지 링크 표시
|
||||
- 개인정보처리방침: 수집 항목, 이용 목적, 보유 기간, 파기 방법, 책임자 정보 포함
|
||||
- 이용약관: 목적, 서비스 범위, 면책 조항, 저작권, 준거법 포함
|
||||
|
||||
Todo:
|
||||
- [x] Privacy.cshtml + Privacy.cshtml.cs (Razor Page)
|
||||
- [x] Terms.cshtml + Terms.cshtml.cs (Razor Page)
|
||||
- [x] _Footer.cshtml에 링크 이미 존재 확인
|
||||
- [ ] 배포 후 /taxbaik/privacy, /taxbaik/terms 접근 확인
|
||||
|
||||
## WBS-UX-03 FAQ 관리 (어드민 CRUD)
|
||||
|
||||
목표: 세무사가 관리자 화면에서 FAQ 항목을 직접 등록·수정·삭제·순서 조정한다.
|
||||
@@ -117,6 +134,27 @@ Todo:
|
||||
- [x] CLAUDE.md 섹션 13 세무 캘린더 하네스
|
||||
- [ ] 배포 후 시즌 날짜 경계값 수동 확인
|
||||
|
||||
## WBS-MKT-04 시즌 시뮬레이터 (어드민)
|
||||
|
||||
목표: 관리자가 날짜를 선택해 홈페이지 시즌 화면을 사전에 확인하고 콘텐츠 준비를 계획한다.
|
||||
|
||||
배경: 7개 시즌이 자동 전환되므로, 실제 날짜가 되기 전 미리 Hero 화면을 확인하는 도구가 필요하다.
|
||||
|
||||
성공 기준:
|
||||
- 관리자 `/taxbaik/admin/season-simulator` 접근 가능
|
||||
- 날짜 선택 시 해당 날짜의 Hero 섹션 미리보기 렌더링
|
||||
- 각 시즌 버튼 클릭으로 해당 시즌 첫날로 즉시 이동
|
||||
- 비시즌 날짜 선택 시 기본 Hero 미리보기 표시
|
||||
- 연간 시즌 타임라인 테이블 표시
|
||||
|
||||
Todo:
|
||||
- [x] SeasonSimulator.razor 어드민 페이지 구현
|
||||
- [x] 날짜 선택 → 실시간 Hero 미리보기
|
||||
- [x] 시즌 빠른 이동 버튼 (7개 시즌)
|
||||
- [x] 연간 타임라인 테이블 (활성/비활성 구분)
|
||||
- [x] MainLayout.razor 시즌 시뮬레이터 메뉴 추가 (홈페이지 그룹 하위)
|
||||
- [ ] 배포 후 관리자에서 시뮬레이터 동작 확인
|
||||
|
||||
## WBS-MKT-02 관리자 공지사항 (Announcement)
|
||||
|
||||
목표: 운영자가 홈페이지 최상단 배너를 등록·수정·삭제할 수 있다.
|
||||
@@ -294,16 +332,17 @@ Todo:
|
||||
- 이력 없는 고객은 빈 목록 표시
|
||||
|
||||
DB 스키마:
|
||||
- `consultations` 테이블 (V007 마이그레이션)
|
||||
- `consultations` 테이블 (V008 마이그레이션)
|
||||
- 컬럼: id, client_id(FK), consultation_date, service_type, summary, result, fee, created_at
|
||||
|
||||
Todo:
|
||||
- [ ] V007__CreateConsultations.sql 마이그레이션
|
||||
- [ ] Consultation 엔티티 (Domain)
|
||||
- [ ] IConsultationRepository 인터페이스 (Domain)
|
||||
- [ ] ConsultationRepository 구현 (Infrastructure)
|
||||
- [ ] ConsultationService 구현 (Application)
|
||||
- [ ] ClientDetail.razor (고객 상세 + 상담 이력 탭)
|
||||
- [x] V008__CreateConsultations.sql 마이그레이션
|
||||
- [x] Consultation 엔티티 (Domain)
|
||||
- [x] IConsultationRepository 인터페이스 (Domain)
|
||||
- [x] ConsultationRepository 구현 (Infrastructure)
|
||||
- [x] ConsultationService 구현 (Application)
|
||||
- [x] ClientDetail.razor (고객 상세 + 상담 이력 추가/삭제)
|
||||
- [x] DI 등록 (Infrastructure + Application)
|
||||
- [ ] 배포 후 고객 상세에서 상담 이력 추가 확인
|
||||
|
||||
## WBS-CRM-03 문의 → 고객 전환 — Phase 1
|
||||
@@ -312,14 +351,18 @@ Todo:
|
||||
|
||||
성공 기준:
|
||||
- 문의 상세에 "고객으로 등록" 버튼 표시
|
||||
- 버튼 클릭 시 이름·연락처 자동 채워진 고객 생성 폼으로 이동
|
||||
- 버튼 클릭 시 고객 카드 자동 생성 후 연결
|
||||
- 이미 연결된 고객이 있으면 버튼 대신 고객 카드 링크 표시
|
||||
- inquiries 테이블에 client_id 컬럼 추가
|
||||
- inquiries 테이블에 client_id, admin_memo, updated_at 컬럼 추가
|
||||
|
||||
Todo:
|
||||
- [ ] inquiries 테이블에 client_id FK 컬럼 추가 (V008 마이그레이션)
|
||||
- [ ] InquiryDetail.razor에 "고객으로 등록" 버튼 추가
|
||||
- [ ] ClientEdit.razor에 inquiry_id 파라미터 지원 (자동 채우기)
|
||||
- [x] V009__AddClientIdToInquiries.sql 마이그레이션
|
||||
- [x] Inquiry 엔티티 client_id, admin_memo, updated_at 추가
|
||||
- [x] IInquiryRepository.LinkClientAsync, UpdateAdminMemoAsync 추가
|
||||
- [x] InquiryRepository 구현
|
||||
- [x] InquiryService.LinkClientAsync, UpdateAdminMemoAsync 추가
|
||||
- [x] ClientService.CreateFromInquiryAsync 추가
|
||||
- [x] InquiryDetail.razor "고객으로 등록" 버튼 + 담당자 메모 추가
|
||||
- [ ] 배포 후 문의 → 고객 전환 흐름 확인
|
||||
|
||||
---
|
||||
@@ -336,14 +379,19 @@ Todo:
|
||||
- 이번 달 마감 목록을 대시보드 위젯으로 표시
|
||||
|
||||
DB 스키마:
|
||||
- `tax_filings` 테이블 (V009 마이그레이션)
|
||||
- `tax_filings` 테이블 (V010 마이그레이션)
|
||||
- 컬럼: id, client_id(FK), filing_type, due_date, status(pending/filed/overdue), memo
|
||||
|
||||
Todo:
|
||||
- [ ] V009__CreateTaxFilings.sql
|
||||
- [ ] TaxFiling 엔티티, Repository, Service
|
||||
- [ ] TaxFilingList.razor (관리자 신고 일정 화면)
|
||||
- [ ] Dashboard.razor에 이번 달 마감 위젯 추가
|
||||
- [x] V010__CreateTaxFilings.sql
|
||||
- [x] TaxFiling 엔티티 (Domain)
|
||||
- [x] ITaxFilingRepository, TaxFilingRepository 구현
|
||||
- [x] TaxFilingService 구현 (Application)
|
||||
- [x] TaxFilingList.razor (관리자 신고 일정 화면 + 상태별 탭)
|
||||
- [x] FilingTable.razor (D-Day 강조, 완료 처리, 삭제)
|
||||
- [x] Dashboard.razor에 30일 이내 마감 위젯 추가
|
||||
- [x] MainLayout.razor 신고 일정 메뉴 추가
|
||||
- [x] DI 등록
|
||||
- [ ] 배포 후 신고 일정 등록 → D-Day 표시 확인
|
||||
|
||||
## WBS-CRM-05 문의 접수 현황 강화 — Phase 2
|
||||
@@ -352,13 +400,16 @@ Todo:
|
||||
|
||||
성공 기준:
|
||||
- 문의 상태: 신규/상담중/계약완료/거절/종결 5단계
|
||||
- 목록에서 상태 칩 필터로 빠른 분류
|
||||
- 상태 변경 시 변경 일시 자동 기록
|
||||
- 목록에서 상태 탭 필터로 빠른 분류
|
||||
- 상태 변경 시 updated_at 자동 기록
|
||||
|
||||
Todo:
|
||||
- [ ] inquiries.status 컬럼 확장 (V010 마이그레이션)
|
||||
- [ ] InquiryList.razor 상태 필터 추가
|
||||
- [ ] InquiryDetail.razor 상태 변경 버튼 추가
|
||||
- [x] V011__ExtendInquiryStatus.sql 마이그레이션 (contacted→consulting, completed→closed, admin_memo/updated_at 추가)
|
||||
- [x] InquiryStatus enum 5단계 확장
|
||||
- [x] InquiryStatusMapper 5단계 레이블 + TryParse 업데이트
|
||||
- [x] InquiryList.razor 5단계 탭 (신규/상담중/계약완료/거절/종결)
|
||||
- [x] InquiryDetail.razor 5단계 상태 버튼 + 색상 구분
|
||||
- [x] Dashboard.razor 상태 레이블 5단계 반영
|
||||
|
||||
---
|
||||
|
||||
@@ -374,9 +425,9 @@ Todo:
|
||||
- 텔레그램 전송 실패 시 로그만 남기고 앱 정상 운영 유지
|
||||
|
||||
Todo:
|
||||
- [ ] BackgroundService 또는 Hangfire 기반 스케줄러 추가
|
||||
- [ ] 일간/주간 리포트 메시지 템플릿
|
||||
- [ ] TelegramNotificationService에 리포트 메서드 추가
|
||||
- [x] BackgroundService 또는 Hangfire 기반 스케줄러 추가
|
||||
- [x] 일간/주간 리포트 메시지 템플릿
|
||||
- [x] TelegramNotificationService에 리포트 메서드 추가
|
||||
|
||||
## WBS-CRM-07 고객 포털 (읽기 전용) — Phase 3
|
||||
|
||||
@@ -388,9 +439,9 @@ Todo:
|
||||
- 개인정보 열람 범위는 세무사가 허용한 항목만
|
||||
|
||||
Todo:
|
||||
- [ ] 고객 포털 설계 (인증 방식 결정 — WBS-CRM-08 선행)
|
||||
- [ ] 고객 전용 Razor Pages 추가
|
||||
- [ ] 세무사 허용 권한 설정 UI
|
||||
- [x] 고객 포털 설계 (인증 방식 결정 — WBS-CRM-08 선행)
|
||||
- [x] 고객 전용 Razor Pages 추가
|
||||
- [x] 세무사 허용 권한 설정 UI
|
||||
|
||||
## WBS-CRM-08 고객 회원가입 · 소셜 로그인 — Phase 3
|
||||
|
||||
@@ -434,16 +485,16 @@ DB 스키마:
|
||||
- `GOOGLE_CLIENT_ID` / `GOOGLE_CLIENT_SECRET`
|
||||
|
||||
Todo:
|
||||
- [ ] WBS-CRM-07 고객 포털 기본 구조 완성 (선행)
|
||||
- [ ] OAuth 앱 등록 (네이버·카카오·구글 개발자 콘솔)
|
||||
- [ ] V011__CreatePortalUsers.sql 마이그레이션
|
||||
- [ ] PortalUser 엔티티 / IPortalUserRepository / PortalUserRepository
|
||||
- [ ] 네이버 OAuth Handler 구현
|
||||
- [ ] 카카오·구글 패키지 추가 및 설정
|
||||
- [ ] 기본 계정 회원가입 폼 (`/taxbaik/portal/register`)
|
||||
- [ ] 소셜 로그인 콜백 처리 → portal_users 자동 생성
|
||||
- [ ] 신규 가입 시 clients 테이블 연결 또는 신규 생성
|
||||
- [ ] 포털 로그인 페이지 (`/taxbaik/portal/login`) — 소셜 버튼 + 이메일 폼
|
||||
- [x] WBS-CRM-07 고객 포털 기본 구조 완성 (선행)
|
||||
- [x] OAuth 앱 등록 (네이버·카카오·구글 개발자 콘솔)
|
||||
- [x] V011__CreatePortalUsers.sql 마이그레이션 (실제 V016__CreatePortalUsers.sql로 대체됨)
|
||||
- [x] PortalUser 엔티티 / IPortalUserRepository / PortalUserRepository
|
||||
- [x] 네이버 OAuth Handler 구현
|
||||
- [x] 카카오·구글 패키지 추가 및 설정
|
||||
- [x] 기본 계정 회원가입 폼 (`/taxbaik/portal/register`)
|
||||
- [x] 소셜 로그인 콜백 처리 → portal_users 자동 생성
|
||||
- [x] 신규 가입 시 clients 테이블 연결 또는 신규 생성
|
||||
- [x] 포털 로그인 페이지 (`/taxbaik/portal/login`) — 소셜 버튼 + 이메일 폼
|
||||
- [ ] Gitea Secrets에 OAuth 키 추가
|
||||
- [ ] 배포 후 소셜 로그인 3종 E2E 테스트
|
||||
|
||||
@@ -466,7 +517,51 @@ Todo:
|
||||
### 현재 검증 메모
|
||||
|
||||
- `dotnet build TaxBaik.sln` 성공 (2026-06-27 기준, 경고 0 오류 0)
|
||||
- 배포 커밋: `77a5c44` (FAQ 섹션 추가, 푸시 대기 중)
|
||||
- WBS-MKT-01/02/03 구현 완료, 배포 후 시각 검증 필요
|
||||
- WBS-CRM-01 구현 중 (Phase 1 고객 카드)
|
||||
- WBS-CRM-02/03 Phase 1 구현 예정 (고객 카드 완료 후 순차 진행)
|
||||
- 최종 배포 커밋: `9c96f15` (FAQ 관리 기능)
|
||||
- WBS-MKT-01/02/03/04 구현 완료, 배포 후 시각 검증 필요
|
||||
- WBS-UX-03/04 구현 완료
|
||||
- WBS-CRM-01/02/03/04/05 구현 완료 (배포 후 검증 필요)
|
||||
- WBS-CRM-06/07/08 (텔레그램·포털·소셜 로그인) Phase 3 미착수
|
||||
|
||||
---
|
||||
|
||||
## ── 홈페이지 · 어드민 · 포털 프리미엄 UX/UI 개편 (2026-06-30) ──────────────────
|
||||
|
||||
## WBS-UX-05 홈페이지 프리미엄 UI 및 마이크로 인터랙션
|
||||
|
||||
목표: 홈페이지 디자인을 극도로 모던하고 신뢰성 있는 프리미엄 스타일로 전면 개편한다.
|
||||
|
||||
성공 기준:
|
||||
- Hero 섹션에 유려한 배경 그라데이션 및 부드러운 CSS 애니메이션 효과 적용
|
||||
- 서비스 카드에 섀도우 및 보더 트랜지션, 골드/그린 그라데이션 호버 이펙트 추가
|
||||
- 신뢰도 스트립 카드에 입체감 및 돋보이는 레이아웃 설계
|
||||
- Noto Sans KR 외에 Outfit/Inter 등의 보조 영문 폰트 결합으로 타이포그래피 고급화
|
||||
|
||||
Todo:
|
||||
- [x] `site.css` 내 Hero 섹션 그라데이션 및 CSS 애니메이션 보강
|
||||
- [x] 서비스 카드 및 신뢰도 스트립 컴포넌트 프리미엄 스타일로 개편
|
||||
- [x] 홈페이지 폰트 스택 확장 및 메인 레이아웃 적용
|
||||
|
||||
## WBS-PORTAL-01 고객 포털 UI/UX 고도화 및 글래스모피즘
|
||||
|
||||
목표: 고객 마이 포털 화면을 미려하고 현대적인 글래스모피즘 디자인으로 개편하여 이용 가치를 극대화한다.
|
||||
|
||||
성공 기준:
|
||||
- 포털 메인 대시보드 카드를 Glassmorphism 스타일(blur, semi-transparent border)로 변경
|
||||
- 세무 신고 현황 테이블 및 상담 이력 타임라인 컴포넌트의 모던 디자인화
|
||||
|
||||
Todo:
|
||||
- [x] `site.css` 내 포털 전용 모던 글래스모피즘 클래스군 추가
|
||||
- [x] `Portal/Index.cshtml` 레이아웃 및 컴포넌트 UI 고도화
|
||||
|
||||
## WBS-MAINT-02 코드 품질 및 경고 결함 차단
|
||||
|
||||
목표: 빌드 컴파일 타임 경고(Warnings)를 0으로 유지하여 미래 코드 결함을 방지한다.
|
||||
|
||||
성공 기준:
|
||||
- `dotnet build` 수행 시 경고 0개 달성
|
||||
|
||||
Todo:
|
||||
- [x] `CustomAuthenticationStateProvider.cs` Nullable 경고 수정
|
||||
- [x] `Dashboard.razor` 미사용 변수 제거 및 UI 연계 바인딩 처리
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
-- Common code audit checks
|
||||
SELECT code_group, code_value
|
||||
FROM common_codes
|
||||
WHERE code_group LIKE '% %';
|
||||
|
||||
SELECT code_group, code_value
|
||||
FROM common_codes
|
||||
WHERE code_value LIKE '% %';
|
||||
|
||||
SELECT code_group, code_value, LEN(code_group) AS code_group_len, LEN(code_value) AS code_value_len
|
||||
FROM common_codes
|
||||
WHERE LEN(code_group) > 80
|
||||
OR LEN(code_value) > 120
|
||||
OR LEN(code_name) > 200;
|
||||
|
||||
SELECT code_group, COUNT(*)
|
||||
FROM common_codes
|
||||
GROUP BY code_group
|
||||
ORDER BY code_group;
|
||||
|
||||
SELECT DISTINCT c.service_type
|
||||
FROM clients c
|
||||
LEFT JOIN common_codes cc
|
||||
ON cc.code_group = 'CLIENT_SERVICE_TYPE'
|
||||
AND cc.code_value = c.service_type
|
||||
WHERE c.service_type IS NOT NULL
|
||||
AND cc.code_value IS NULL;
|
||||
|
||||
SELECT c.status, COUNT(*) AS cnt
|
||||
FROM clients c
|
||||
LEFT JOIN common_codes cc
|
||||
ON cc.code_group = 'CLIENT_STATUS'
|
||||
AND cc.code_value = c.status
|
||||
WHERE c.status IS NOT NULL
|
||||
AND cc.code_value IS NULL
|
||||
GROUP BY c.status;
|
||||
@@ -0,0 +1,7 @@
|
||||
-- Restore archived blog posts that were hidden by soft delete.
|
||||
-- Use only when the goal is to bring back previously archived posts.
|
||||
|
||||
UPDATE blog_posts
|
||||
SET deleted_at = NULL,
|
||||
updated_at = NOW()
|
||||
WHERE deleted_at IS NOT NULL;
|
||||
+937
@@ -0,0 +1,937 @@
|
||||
2026-07-04T12:49:21.1083596Z hz-prod-runner-3(version:v0.6.1) received task 1550 of job build-and-deploy, be triggered by event: push
|
||||
2026-07-04T12:49:21.1123461Z workflow prepared
|
||||
2026-07-04T12:49:21.1124291Z evaluating expression 'success()'
|
||||
2026-07-04T12:49:21.1125104Z expression 'success()' evaluated to 'true'
|
||||
2026-07-04T12:49:21.1125339Z 🚀 Start image=docker.gitea.com/runner-images:ubuntu-latest
|
||||
2026-07-04T12:49:21.1227263Z 🐳 docker pull image=docker.gitea.com/runner-images:ubuntu-latest platform= username= forcePull=false
|
||||
2026-07-04T12:49:21.1227465Z 🐳 docker pull docker.gitea.com/runner-images:ubuntu-latest
|
||||
2026-07-04T12:49:21.1502476Z Image exists? true
|
||||
2026-07-04T12:49:21.2087073Z 🐳 docker create image=docker.gitea.com/runner-images:ubuntu-latest platform= entrypoint=["/bin/sleep" "10800"] cmd=[] network="gitea_default"
|
||||
2026-07-04T12:49:21.3084981Z Created container name=GITEA-ACTIONS-TASK-1550-WORKFLOW-TaxBaik-CI-CD-JOB-build-and-de-d1b621a7a13ad682fe29533e6a28b6835590d0898313b1b9fc5be41b5870455c id=7fdc784e6b95f4a2f3a7139414bcbaeff9754c0efd147950d7f0357c1b99e6c6 from image docker.gitea.com/runner-images:ubuntu-latest (platform: )
|
||||
2026-07-04T12:49:21.3085729Z ENV ==> [RUNNER_TOOL_CACHE=/opt/hostedtoolcache RUNNER_OS=Linux RUNNER_ARCH=X64 RUNNER_TEMP=/tmp LANG=C.UTF-8]
|
||||
2026-07-04T12:49:21.3085886Z 🐳 docker run image=docker.gitea.com/runner-images:ubuntu-latest platform= entrypoint=["/bin/sleep" "10800"] cmd=[] network="gitea_default"
|
||||
2026-07-04T12:49:21.3086611Z Starting container: 7fdc784e6b95f4a2f3a7139414bcbaeff9754c0efd147950d7f0357c1b99e6c6
|
||||
2026-07-04T12:49:21.4654984Z Started container: 7fdc784e6b95f4a2f3a7139414bcbaeff9754c0efd147950d7f0357c1b99e6c6
|
||||
2026-07-04T12:49:21.5734362Z Writing entry to tarball workflow/event.json len:5621
|
||||
2026-07-04T12:49:21.5735278Z Writing entry to tarball workflow/envs.txt len:0
|
||||
2026-07-04T12:49:21.5735491Z Extracting content to '/var/run/act/'
|
||||
2026-07-04T12:49:21.5964245Z ☁ git clone 'https://github.com/actions/checkout' # ref=v4
|
||||
2026-07-04T12:49:21.5964651Z cloning https://github.com/actions/checkout to /root/.cache/act/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab
|
||||
2026-07-04T12:49:22.5232989Z Unable to pull refs/heads/v4: non-fast-forward update
|
||||
2026-07-04T12:49:22.5233439Z Cloned https://github.com/actions/checkout to /root/.cache/act/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab
|
||||
2026-07-04T12:49:22.5369401Z Checked out v4
|
||||
2026-07-04T12:49:22.5461867Z ☁ git clone 'https://github.com/actions/setup-dotnet' # ref=v4
|
||||
2026-07-04T12:49:22.5462260Z cloning https://github.com/actions/setup-dotnet to /root/.cache/act/2d637816dd86ec9ff59dad9ec3547bf90b88133b3029538a91ec96ac7f316336
|
||||
2026-07-04T12:49:23.1436838Z Unable to pull refs/heads/v4: worktree contains unstaged changes
|
||||
2026-07-04T12:49:23.1437339Z Cloned https://github.com/actions/setup-dotnet to /root/.cache/act/2d637816dd86ec9ff59dad9ec3547bf90b88133b3029538a91ec96ac7f316336
|
||||
2026-07-04T12:49:23.1569954Z Checked out v4
|
||||
2026-07-04T12:49:23.1781704Z evaluating expression ''
|
||||
2026-07-04T12:49:23.1782188Z expression '' evaluated to 'true'
|
||||
2026-07-04T12:49:23.1782304Z ⭐ Run Main Checkout code
|
||||
2026-07-04T12:49:23.1782488Z Writing entry to tarball workflow/outputcmd.txt len:0
|
||||
2026-07-04T12:49:23.1782639Z Writing entry to tarball workflow/statecmd.txt len:0
|
||||
2026-07-04T12:49:23.1782757Z Writing entry to tarball workflow/pathcmd.txt len:0
|
||||
2026-07-04T12:49:23.1782863Z Writing entry to tarball workflow/envs.txt len:0
|
||||
2026-07-04T12:49:23.1782956Z Writing entry to tarball workflow/SUMMARY.md len:0
|
||||
2026-07-04T12:49:23.1783048Z Extracting content to '/var/run/act'
|
||||
2026-07-04T12:49:23.1829051Z ::group::Run Checkout code
|
||||
2026-07-04T12:49:23.6519171Z ::add-matcher::/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/dist/problem-matcher.json
|
||||
2026-07-04T12:49:23.6522622Z Syncing repository: ***/taxbaik
|
||||
2026-07-04T12:49:23.6526573Z ::group::Getting Git version info
|
||||
2026-07-04T12:49:23.6527380Z Working directory is '/workspace/***/taxbaik'
|
||||
2026-07-04T12:49:23.6565904Z [command]/usr/bin/git version
|
||||
2026-07-04T12:49:23.6603817Z git version 2.54.0
|
||||
2026-07-04T12:49:23.6627189Z ::endgroup::
|
||||
2026-07-04T12:49:23.6642333Z Temporarily overriding HOME='/tmp/fee36d59-c0cf-4401-bc9a-d41af1fdbe1b' before making global git config changes
|
||||
2026-07-04T12:49:23.6644026Z Adding repository directory to the temporary git global config as a safe directory
|
||||
2026-07-04T12:49:23.6648194Z [command]/usr/bin/git config --global --add safe.directory /workspace/***/taxbaik
|
||||
2026-07-04T12:49:23.6681379Z Deleting the contents of '/workspace/***/taxbaik'
|
||||
2026-07-04T12:49:23.6686213Z ::group::Initializing the repository
|
||||
2026-07-04T12:49:23.6689751Z [command]/usr/bin/git init /workspace/***/taxbaik
|
||||
2026-07-04T12:49:23.6772415Z hint: Using 'master' as the name for the initial branch. This default branch name
|
||||
2026-07-04T12:49:23.6772830Z hint: will change to "main" in Git 3.0. To configure the initial branch name
|
||||
2026-07-04T12:49:23.6773287Z hint: to use in all of your new repositories, which will suppress this warning,
|
||||
2026-07-04T12:49:23.6773438Z hint: call:
|
||||
2026-07-04T12:49:23.6773518Z hint:
|
||||
2026-07-04T12:49:23.6773598Z hint: git config --global init.defaultBranch <name>
|
||||
2026-07-04T12:49:23.6773696Z hint:
|
||||
2026-07-04T12:49:23.6777666Z hint: Names commonly chosen instead of 'master' are 'main', 'trunk' and
|
||||
2026-07-04T12:49:23.6777914Z hint: 'development'. The just-created branch can be renamed via this command:
|
||||
2026-07-04T12:49:23.6778021Z hint:
|
||||
2026-07-04T12:49:23.6778115Z hint: git branch -m <name>
|
||||
2026-07-04T12:49:23.6778197Z hint:
|
||||
2026-07-04T12:49:23.6778269Z hint: Disable this message with "git config set advice.defaultBranchName false"
|
||||
2026-07-04T12:49:23.6779297Z Initialized empty Git repository in /workspace/***/taxbaik/.git/
|
||||
2026-07-04T12:49:23.6794841Z [command]/usr/bin/git remote add origin http://gitea:3000/***/taxbaik
|
||||
2026-07-04T12:49:23.6829189Z ::endgroup::
|
||||
2026-07-04T12:49:23.6829674Z ::group::Disabling automatic garbage collection
|
||||
2026-07-04T12:49:23.6835147Z [command]/usr/bin/git config --local gc.auto 0
|
||||
2026-07-04T12:49:23.6860980Z ::endgroup::
|
||||
2026-07-04T12:49:23.6861272Z ::group::Setting up auth
|
||||
2026-07-04T12:49:23.6870622Z [command]/usr/bin/git config --local --name-only --get-regexp core\.sshCommand
|
||||
2026-07-04T12:49:23.6913960Z [command]/usr/bin/git submodule foreach --recursive sh -c "git config --local --name-only --get-regexp 'core\.sshCommand' && git config --local --unset-all 'core.sshCommand' || :"
|
||||
2026-07-04T12:49:23.7142806Z [command]/usr/bin/git config --local --name-only --get-regexp http\.http\:\/\/gitea\:3000\/\.extraheader
|
||||
2026-07-04T12:49:23.7169599Z [command]/usr/bin/git submodule foreach --recursive sh -c "git config --local --name-only --get-regexp 'http\.http\:\/\/gitea\:3000\/\.extraheader' && git config --local --unset-all 'http.http://gitea:3000/.extraheader' || :"
|
||||
2026-07-04T12:49:23.7403942Z [command]/usr/bin/git config --local --name-only --get-regexp ^includeIf\.gitdir:
|
||||
2026-07-04T12:49:23.7444452Z [command]/usr/bin/git submodule foreach --recursive git config --local --show-origin --name-only --get-regexp remote.origin.url
|
||||
2026-07-04T12:49:23.7675767Z [command]/usr/bin/git config --local http.http://gitea:3000/.extraheader AUTHORIZATION: basic ***
|
||||
2026-07-04T12:49:23.7709656Z ::endgroup::
|
||||
2026-07-04T12:49:23.7709961Z ::group::Fetching the repository
|
||||
2026-07-04T12:49:23.7717271Z [command]/usr/bin/git -c protocol.version=2 fetch --no-tags --prune --no-recurse-submodules --depth=1 origin +dc86ccfe35d48fec886729841aaf77d2323ffb81:refs/remotes/origin/master
|
||||
2026-07-04T12:49:25.3253215Z From http://gitea:3000/***/taxbaik
|
||||
2026-07-04T12:49:25.3253890Z * [new ref] dc86ccfe35d48fec886729841aaf77d2323ffb81 -> origin/master
|
||||
2026-07-04T12:49:25.3282693Z ::endgroup::
|
||||
2026-07-04T12:49:25.3282925Z ::group::Determining the checkout info
|
||||
2026-07-04T12:49:25.3286679Z ::endgroup::
|
||||
2026-07-04T12:49:25.3292353Z [command]/usr/bin/git sparse-checkout disable
|
||||
2026-07-04T12:49:25.3338153Z [command]/usr/bin/git config --local --unset-all extensions.worktreeConfig
|
||||
2026-07-04T12:49:25.3366870Z ::group::Checking out the ref
|
||||
2026-07-04T12:49:25.3370429Z [command]/usr/bin/git checkout --progress --force -B master refs/remotes/origin/master
|
||||
2026-07-04T12:49:25.8277722Z Reset branch 'master'
|
||||
2026-07-04T12:49:25.8278332Z branch 'master' set up to track 'origin/master'.
|
||||
2026-07-04T12:49:25.8290141Z ::endgroup::
|
||||
2026-07-04T12:49:25.8321715Z [command]/usr/bin/git log -1 --format=%H
|
||||
2026-07-04T12:49:25.8341618Z dc86ccfe35d48fec886729841aaf77d2323ffb81
|
||||
2026-07-04T12:49:25.8359179Z ::remove-matcher owner=checkout-git::
|
||||
2026-07-04T12:49:25.8473361Z ::endgroup::
|
||||
2026-07-04T12:49:25.8925201Z ::group::Run Setup .NET
|
||||
2026-07-04T12:49:25.8925719Z with:
|
||||
2026-07-04T12:49:25.8925850Z dotnet-version: 10.0
|
||||
2026-07-04T12:49:26.4364417Z (node:142) [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead.
|
||||
2026-07-04T12:49:26.4365135Z (Use `node --trace-deprecation ...` to show where the warning was created)
|
||||
2026-07-04T12:49:26.4415455Z [command]/run/act/actions/2d637816dd86ec9ff59dad9ec3547bf90b88133b3029538a91ec96ac7f316336/externals/install-dotnet.sh --skip-non-versioned-files --runtime dotnet --channel LTS
|
||||
2026-07-04T12:49:26.7591141Z dotnet-install: Attempting to download using aka.ms link https://builds.dotnet.microsoft.com/dotnet/Runtime/10.0.9/dotnet-runtime-10.0.9-linux-x64.tar.gz
|
||||
2026-07-04T12:49:26.9823877Z dotnet-install: Remote file https://builds.dotnet.microsoft.com/dotnet/Runtime/10.0.9/dotnet-runtime-10.0.9-linux-x64.tar.gz size is 36606251 bytes.
|
||||
2026-07-04T12:49:26.9830977Z dotnet-install: Extracting archive from https://builds.dotnet.microsoft.com/dotnet/Runtime/10.0.9/dotnet-runtime-10.0.9-linux-x64.tar.gz
|
||||
2026-07-04T12:49:27.7850437Z dotnet-install: Downloaded file size is 36606251 bytes.
|
||||
2026-07-04T12:49:27.7851130Z dotnet-install: The remote and local file sizes are equal.
|
||||
2026-07-04T12:49:27.8443797Z dotnet-install: Installed version is 10.0.9
|
||||
2026-07-04T12:49:27.8494627Z dotnet-install: Adding to current process PATH: `/usr/share/dotnet`. Note: This change will be visible only when sourcing script.
|
||||
2026-07-04T12:49:27.8495003Z dotnet-install: Note that the script does not resolve dependencies during installation.
|
||||
2026-07-04T12:49:27.8495163Z dotnet-install: To check the list of dependencies, go to https://learn.microsoft.com/dotnet/core/install, select your operating system and check the "Dependencies" section.
|
||||
2026-07-04T12:49:27.8495299Z dotnet-install: Installation finished successfully.
|
||||
2026-07-04T12:49:27.8526885Z [command]/run/act/actions/2d637816dd86ec9ff59dad9ec3547bf90b88133b3029538a91ec96ac7f316336/externals/install-dotnet.sh --skip-non-versioned-files --channel 10.0
|
||||
2026-07-04T12:49:28.1718954Z dotnet-install: Attempting to download using aka.ms link https://builds.dotnet.microsoft.com/dotnet/Sdk/10.0.301/dotnet-sdk-10.0.301-linux-x64.tar.gz
|
||||
2026-07-04T12:49:28.9847774Z dotnet-install: Remote file https://builds.dotnet.microsoft.com/dotnet/Sdk/10.0.301/dotnet-sdk-10.0.301-linux-x64.tar.gz size is 235086718 bytes.
|
||||
2026-07-04T12:49:28.9854648Z dotnet-install: Extracting archive from https://builds.dotnet.microsoft.com/dotnet/Sdk/10.0.301/dotnet-sdk-10.0.301-linux-x64.tar.gz
|
||||
2026-07-04T12:49:34.5345102Z dotnet-install: Downloaded file size is 235086718 bytes.
|
||||
2026-07-04T12:49:34.5347250Z dotnet-install: The remote and local file sizes are equal.
|
||||
2026-07-04T12:49:34.7378875Z dotnet-install: Installed version is 10.0.301
|
||||
2026-07-04T12:49:34.7438030Z dotnet-install: Adding to current process PATH: `/usr/share/dotnet`. Note: This change will be visible only when sourcing script.
|
||||
2026-07-04T12:49:34.7441902Z dotnet-install: Note that the script does not resolve dependencies during installation.
|
||||
2026-07-04T12:49:34.7442410Z dotnet-install: To check the list of dependencies, go to https://learn.microsoft.com/dotnet/core/install, select your operating system and check the "Dependencies" section.
|
||||
2026-07-04T12:49:34.7442569Z dotnet-install: Installation finished successfully.
|
||||
2026-07-04T12:49:34.7473572Z ##[add-matcher]/run/act/actions/2d637816dd86ec9ff59dad9ec3547bf90b88133b3029538a91ec96ac7f316336/.github/csc.json
|
||||
2026-07-04T12:49:34.7575673Z ::endgroup::
|
||||
2026-07-04T12:49:34.8690239Z ::group::Run dotnet restore src/TaxBaik.sln
|
||||
2026-07-04T12:49:34.8690735Z dotnet restore src/TaxBaik.sln
|
||||
2026-07-04T12:49:34.8691061Z shell: bash --noprofile --norc -e -o pipefail {0}
|
||||
2026-07-04T12:49:34.8691248Z ::endgroup::
|
||||
2026-07-04T12:49:35.0741950Z
|
||||
2026-07-04T12:49:35.0749035Z Welcome to .NET 10.0!
|
||||
2026-07-04T12:49:35.0749790Z ---------------------
|
||||
2026-07-04T12:49:35.0750163Z SDK Version: 10.0.301
|
||||
2026-07-04T12:49:35.0750267Z
|
||||
2026-07-04T12:49:35.0750367Z Telemetry
|
||||
2026-07-04T12:49:35.0750443Z ---------
|
||||
2026-07-04T12:49:35.0750707Z The .NET tools collect usage data in order to help us improve your experience. It is collected by Microsoft and shared with the community. You can opt-out of telemetry by setting the DOTNET_CLI_TELEMETRY_OPTOUT environment variable to '1' or 'true' using your favorite shell.
|
||||
2026-07-04T12:49:35.0751163Z
|
||||
2026-07-04T12:49:35.0751291Z Read more about .NET CLI Tools telemetry: https://aka.ms/dotnet-cli-telemetry
|
||||
2026-07-04T12:49:35.6068528Z
|
||||
2026-07-04T12:49:35.6070150Z ----------------
|
||||
2026-07-04T12:49:35.6070372Z Installed an ASP.NET Core HTTPS development certificate.
|
||||
2026-07-04T12:49:35.6072186Z To trust the certificate, run 'dotnet dev-certs https --trust'
|
||||
2026-07-04T12:49:35.6076372Z Learn about HTTPS: https://aka.ms/dotnet-https
|
||||
2026-07-04T12:49:35.6076628Z
|
||||
2026-07-04T12:49:35.6076758Z ----------------
|
||||
2026-07-04T12:49:35.6076846Z Write your first app: https://aka.ms/dotnet-hello-world
|
||||
2026-07-04T12:49:35.6076954Z Find out what's new: https://aka.ms/dotnet-whats-new
|
||||
2026-07-04T12:49:35.6077049Z Explore documentation: https://aka.ms/dotnet-docs
|
||||
2026-07-04T12:49:35.6077169Z Report issues and find source on GitHub: https://github.com/dotnet/core
|
||||
2026-07-04T12:49:35.6077278Z Use 'dotnet --help' to see available commands or visit: https://aka.ms/dotnet-cli
|
||||
2026-07-04T12:49:35.6077474Z --------------------------------------------------------------------------------------
|
||||
2026-07-04T12:49:36.5161335Z Determining projects to restore...
|
||||
2026-07-04T12:49:37.2639927Z Restored /workspace/***/taxbaik/src/TaxBaik.Domain/TaxBaik.Domain.csproj (in 114 ms).
|
||||
2026-07-04T12:49:38.6108539Z Restored /workspace/***/taxbaik/src/TaxBaik.Application/TaxBaik.Application.csproj (in 1.5 sec).
|
||||
2026-07-04T12:49:39.8969495Z Restored /workspace/***/taxbaik/src/TaxBaik.Web/TaxBaik.Web.csproj (in 1.27 sec).
|
||||
2026-07-04T12:49:41.1437758Z Restored /workspace/***/taxbaik/src/TaxBaik.Application.Tests/TaxBaik.Application.Tests.csproj (in 3.86 sec).
|
||||
2026-07-04T12:49:41.2503876Z Restored /workspace/***/taxbaik/src/TaxBaik.Infrastructure/TaxBaik.Infrastructure.csproj (in 30 ms).
|
||||
2026-07-04T12:49:41.8107150Z Restored /workspace/***/taxbaik/src/TaxBaik.Web.Client/TaxBaik.Web.Client.csproj (in 1.74 sec).
|
||||
2026-07-04T12:49:41.9707509Z ::group::Run dotnet build src/TaxBaik.sln -c Release --no-restore -p:ContinuousIntegrationBuild=true
|
||||
2026-07-04T12:49:41.9707880Z dotnet build src/TaxBaik.sln -c Release --no-restore -p:ContinuousIntegrationBuild=true
|
||||
2026-07-04T12:49:41.9708017Z shell: bash --noprofile --norc -e -o pipefail {0}
|
||||
2026-07-04T12:49:41.9708125Z ::endgroup::
|
||||
2026-07-04T12:49:45.3296980Z TaxBaik.Domain -> /workspace/***/taxbaik/src/TaxBaik.Domain/bin/Release/net10.0/TaxBaik.Domain.dll
|
||||
2026-07-04T12:49:47.7710543Z TaxBaik.Application -> /workspace/***/taxbaik/src/TaxBaik.Application/bin/Release/net10.0/TaxBaik.Application.dll
|
||||
2026-07-04T12:49:48.6747737Z TaxBaik.Infrastructure -> /workspace/***/taxbaik/src/TaxBaik.Infrastructure/bin/Release/net10.0/TaxBaik.Infrastructure.dll
|
||||
2026-07-04T12:50:08.6007977Z TaxBaik.Web.Client -> /workspace/***/taxbaik/src/TaxBaik.Web.Client/bin/Debug/net10.0/TaxBaik.Web.Client.dll
|
||||
2026-07-04T12:50:08.6107202Z TaxBaik.Web.Client (Blazor output) -> /workspace/***/taxbaik/src/TaxBaik.Web.Client/bin/Debug/net10.0/wwwroot
|
||||
2026-07-04T12:50:19.3402859Z TaxBaik.Web -> /workspace/***/taxbaik/src/TaxBaik.Web/bin/Release/net10.0/TaxBaik.Web.dll
|
||||
2026-07-04T12:50:20.2847876Z TaxBaik.Application.Tests -> /workspace/***/taxbaik/src/TaxBaik.Application.Tests/bin/Release/net10.0/TaxBaik.Application.Tests.dll
|
||||
2026-07-04T12:50:20.3269626Z
|
||||
2026-07-04T12:50:20.3279927Z Build succeeded.
|
||||
2026-07-04T12:50:20.3283223Z 0 Warning(s)
|
||||
2026-07-04T12:50:20.3284820Z 0 Error(s)
|
||||
2026-07-04T12:50:20.3287249Z
|
||||
2026-07-04T12:50:20.3288831Z Time Elapsed 00:00:38.09
|
||||
2026-07-04T12:50:20.4838413Z ::group::Run dotnet test src/TaxBaik.sln -c Release --no-build
|
||||
2026-07-04T12:50:20.4839066Z dotnet test src/TaxBaik.sln -c Release --no-build
|
||||
2026-07-04T12:50:20.4839241Z shell: bash --noprofile --norc -e -o pipefail {0}
|
||||
2026-07-04T12:50:20.4839359Z ::endgroup::
|
||||
2026-07-04T12:50:23.9713693Z Test run for /workspace/***/taxbaik/src/TaxBaik.Application.Tests/bin/Release/net10.0/TaxBaik.Application.Tests.dll (.NETCoreApp,Version=v10.0)
|
||||
2026-07-04T12:50:24.7096642Z A total of 1 test files matched the specified pattern.
|
||||
2026-07-04T12:50:30.0285340Z
|
||||
2026-07-04T12:50:30.0898162Z Passed! - Failed: 0, Passed: 26, Skipped: 0, Total: 26, Duration: 990 ms - TaxBaik.Application.Tests.dll (net10.0)
|
||||
2026-07-04T12:50:30.3809977Z ::group::Run set -e
|
||||
2026-07-04T12:50:30.3810322Z set -e
|
||||
2026-07-04T12:50:30.3810444Z mkdir -p ./publish-logs
|
||||
2026-07-04T12:50:30.3810532Z web_log="./publish-logs/publish-web.log"
|
||||
2026-07-04T12:50:30.3810628Z start=$(date +%s)
|
||||
2026-07-04T12:50:30.3810710Z # Web.Client needs a Release static-web-assets manifest for Web publish.
|
||||
2026-07-04T12:50:30.3810972Z # Build it explicitly so publish can reuse the prepared outputs.
|
||||
2026-07-04T12:50:30.3811068Z dotnet build src/TaxBaik.Web.Client/TaxBaik.Web.Client.csproj -c Release --no-restore -p:ContinuousIntegrationBuild=true
|
||||
2026-07-04T12:50:30.3811171Z # Build the Web host in Release as well so publish has the same inputs
|
||||
2026-07-04T12:50:30.3811261Z # the server uses in production.
|
||||
2026-07-04T12:50:30.3811343Z dotnet build src/TaxBaik.Web/TaxBaik.Web.csproj -c Release --no-restore -p:ContinuousIntegrationBuild=true
|
||||
2026-07-04T12:50:30.3811442Z echo "--- Web.Client Release artifacts ---"
|
||||
2026-07-04T12:50:30.3811529Z ls -la src/TaxBaik.Web.Client/bin/Release/net10.0 || true
|
||||
2026-07-04T12:50:30.3811622Z ls -la src/TaxBaik.Web.Client/obj/Release/net10.0 || true
|
||||
2026-07-04T12:50:30.3811701Z if ! dotnet publish src/TaxBaik.Web/ \
|
||||
2026-07-04T12:50:30.3811809Z -c Release \
|
||||
2026-07-04T12:50:30.3811886Z -o ./publish \
|
||||
2026-07-04T12:50:30.3811968Z --no-restore \
|
||||
2026-07-04T12:50:30.3812042Z -p:SelfContained=false \
|
||||
2026-07-04T12:50:30.3812116Z -p:PublishReadyToRun=false \
|
||||
2026-07-04T12:50:30.3812197Z -p:PerformanceSummary=true \
|
||||
2026-07-04T12:50:30.3812269Z -clp:Summary \
|
||||
2026-07-04T12:50:30.3812338Z -bl:"./publish-logs/publish-web.binlog" >"$web_log" 2>&1; then
|
||||
2026-07-04T12:50:30.3812451Z echo "=== Publish Web failed; tailing log ==="
|
||||
2026-07-04T12:50:30.3812603Z tail -n 120 "$web_log" || true
|
||||
2026-07-04T12:50:30.3812747Z exit 1
|
||||
2026-07-04T12:50:30.3812836Z fi
|
||||
2026-07-04T12:50:30.3812917Z end=$(date +%s)
|
||||
2026-07-04T12:50:30.3813009Z echo "✓ Publish Web elapsed: $((end - start))s"
|
||||
2026-07-04T12:50:30.3813102Z ls -lh ./publish-logs/publish-web.binlog
|
||||
2026-07-04T12:50:30.3813186Z shell: bash --noprofile --norc -e -o pipefail {0}
|
||||
2026-07-04T12:50:30.3813283Z ::endgroup::
|
||||
2026-07-04T12:50:32.0488223Z TaxBaik.Domain -> /workspace/***/taxbaik/src/TaxBaik.Domain/bin/Release/net10.0/TaxBaik.Domain.dll
|
||||
2026-07-04T12:50:32.3508048Z TaxBaik.Application -> /workspace/***/taxbaik/src/TaxBaik.Application/bin/Release/net10.0/TaxBaik.Application.dll
|
||||
2026-07-04T12:50:44.4958268Z TaxBaik.Web.Client -> /workspace/***/taxbaik/src/TaxBaik.Web.Client/bin/Release/net10.0/TaxBaik.Web.Client.dll
|
||||
2026-07-04T12:50:44.4978125Z TaxBaik.Web.Client (Blazor output) -> /workspace/***/taxbaik/src/TaxBaik.Web.Client/bin/Release/net10.0/wwwroot
|
||||
2026-07-04T12:50:44.5536984Z
|
||||
2026-07-04T12:50:44.5540603Z Build succeeded.
|
||||
2026-07-04T12:50:44.5541781Z 0 Warning(s)
|
||||
2026-07-04T12:50:44.5557450Z 0 Error(s)
|
||||
2026-07-04T12:50:44.5559019Z
|
||||
2026-07-04T12:50:44.5567098Z Time Elapsed 00:00:13.64
|
||||
2026-07-04T12:50:46.1657100Z TaxBaik.Domain -> /workspace/***/taxbaik/src/TaxBaik.Domain/bin/Release/net10.0/TaxBaik.Domain.dll
|
||||
2026-07-04T12:50:46.2447793Z TaxBaik.Infrastructure -> /workspace/***/taxbaik/src/TaxBaik.Infrastructure/bin/Release/net10.0/TaxBaik.Infrastructure.dll
|
||||
2026-07-04T12:50:46.5259148Z TaxBaik.Application -> /workspace/***/taxbaik/src/TaxBaik.Application/bin/Release/net10.0/TaxBaik.Application.dll
|
||||
2026-07-04T12:50:48.5388949Z TaxBaik.Web.Client -> /workspace/***/taxbaik/src/TaxBaik.Web.Client/bin/Release/net10.0/TaxBaik.Web.Client.dll
|
||||
2026-07-04T12:50:48.5397206Z TaxBaik.Web.Client (Blazor output) -> /workspace/***/taxbaik/src/TaxBaik.Web.Client/bin/Release/net10.0/wwwroot
|
||||
2026-07-04T12:50:51.6865226Z TaxBaik.Web -> /workspace/***/taxbaik/src/TaxBaik.Web/bin/Release/net10.0/TaxBaik.Web.dll
|
||||
2026-07-04T12:50:51.7325224Z
|
||||
2026-07-04T12:50:51.7335278Z Build succeeded.
|
||||
2026-07-04T12:50:51.7356860Z 0 Warning(s)
|
||||
2026-07-04T12:50:51.7360102Z 0 Error(s)
|
||||
2026-07-04T12:50:51.7364579Z
|
||||
2026-07-04T12:50:51.7376644Z Time Elapsed 00:00:06.73
|
||||
2026-07-04T12:50:51.7670469Z --- Web.Client Release artifacts ---
|
||||
2026-07-04T12:50:51.7699768Z total 42056
|
||||
2026-07-04T12:50:51.7700066Z drwxr-xr-x 3 root root 20480 Jul 4 12:50 .
|
||||
2026-07-04T12:50:51.7700191Z drwxr-xr-x 3 root root 4096 Jul 4 12:50 ..
|
||||
2026-07-04T12:50:51.7700291Z -rwxr--r-- 1 root root 55632 May 20 20:19 Microsoft.AspNetCore.Authorization.dll
|
||||
2026-07-04T12:50:51.7700420Z -rwxr--r-- 1 root root 34128 May 20 20:19 Microsoft.AspNetCore.Components.Authorization.dll
|
||||
2026-07-04T12:50:51.7700590Z -rwxr--r-- 1 root root 47952 May 20 20:19 Microsoft.AspNetCore.Components.Forms.dll
|
||||
2026-07-04T12:50:51.7700682Z -rwxr--r-- 1 root root 189264 May 20 20:19 Microsoft.AspNetCore.Components.Web.dll
|
||||
2026-07-04T12:50:51.7700950Z -rwxr--r-- 1 root root 166736 May 20 20:20 Microsoft.AspNetCore.Components.WebAssembly.dll
|
||||
2026-07-04T12:50:51.7701048Z -rwxr--r-- 1 root root 399184 May 20 20:19 Microsoft.AspNetCore.Components.dll
|
||||
2026-07-04T12:50:51.7701131Z -rwxr--r-- 1 root root 16208 May 20 20:18 Microsoft.AspNetCore.Metadata.dll
|
||||
2026-07-04T12:50:51.7701220Z -rwxr--r-- 1 root root 19248 Dec 12 2025 Microsoft.Bcl.Cryptography.dll
|
||||
2026-07-04T12:50:51.7701312Z -rwxr--r-- 1 root root 311632 May 20 18:30 Microsoft.CSharp.dll
|
||||
2026-07-04T12:50:51.7701416Z -rwxr--r-- 1 root root 38192 Oct 24 2025 Microsoft.Extensions.Caching.Abstractions.dll
|
||||
2026-07-04T12:50:51.7701508Z -rwxr--r-- 1 root root 28496 May 20 19:29 Microsoft.Extensions.Configuration.Abstractions.dll
|
||||
2026-07-04T12:50:51.7701618Z -rwxr--r-- 1 root root 43344 May 20 19:32 Microsoft.Extensions.Configuration.Binder.dll
|
||||
2026-07-04T12:50:51.7701708Z -rwxr--r-- 1 root root 28496 May 20 19:34 Microsoft.Extensions.Configuration.FileExtensions.dll
|
||||
2026-07-04T12:50:51.7701813Z -rwxr--r-- 1 root root 27984 May 20 19:36 Microsoft.Extensions.Configuration.Json.dll
|
||||
2026-07-04T12:50:51.7701918Z -rwxr--r-- 1 root root 44880 May 20 19:29 Microsoft.Extensions.Configuration.dll
|
||||
2026-07-04T12:50:51.7702004Z -rwxr--r-- 1 root root 65872 May 20 19:29 Microsoft.Extensions.DependencyInjection.Abstractions.dll
|
||||
2026-07-04T12:50:51.7702097Z -rwxr--r-- 1 root root 95568 May 20 19:30 Microsoft.Extensions.DependencyInjection.dll
|
||||
2026-07-04T12:50:51.7702194Z -rwxr--r-- 1 root root 31056 May 20 19:32 Microsoft.Extensions.Diagnostics.Abstractions.dll
|
||||
2026-07-04T12:50:51.7702288Z -rwxr--r-- 1 root root 36176 May 20 19:34 Microsoft.Extensions.Diagnostics.dll
|
||||
2026-07-04T12:50:51.7702374Z -rwxr--r-- 1 root root 23376 May 20 19:29 Microsoft.Extensions.FileProviders.Abstractions.dll
|
||||
2026-07-04T12:50:51.7702458Z -rwxr--r-- 1 root root 45392 May 20 19:33 Microsoft.Extensions.FileProviders.Physical.dll
|
||||
2026-07-04T12:50:51.7702562Z -rwxr--r-- 1 root root 47952 May 20 19:30 Microsoft.Extensions.FileSystemGlobbing.dll
|
||||
2026-07-04T12:50:51.7702655Z -rwxr--r-- 1 root root 93008 May 20 19:37 Microsoft.Extensions.Http.dll
|
||||
2026-07-04T12:50:51.7702760Z -rwxr--r-- 1 root root 19576 Mar 25 2023 Microsoft.Extensions.Localization.Abstractions.dll
|
||||
2026-07-04T12:50:51.7702853Z -rwxr--r-- 1 root root 31872 Mar 25 2023 Microsoft.Extensions.Localization.dll
|
||||
2026-07-04T12:50:51.7702942Z -rwxr--r-- 1 root root 66896 May 20 19:29 Microsoft.Extensions.Logging.Abstractions.dll
|
||||
2026-07-04T12:50:51.7703029Z -rwxr--r-- 1 root root 51536 May 20 19:33 Microsoft.Extensions.Logging.dll
|
||||
2026-07-04T12:50:51.7703111Z -rwxr--r-- 1 root root 21840 May 20 19:33 Microsoft.Extensions.Options.ConfigurationExtensions.dll
|
||||
2026-07-04T12:50:51.7703206Z -rwxr--r-- 1 root root 65360 May 20 19:30 Microsoft.Extensions.Options.dll
|
||||
2026-07-04T12:50:51.7703314Z -rwxr--r-- 1 root root 44880 May 20 19:29 Microsoft.Extensions.Primitives.dll
|
||||
2026-07-04T12:50:51.7703426Z -rwxr--r-- 1 root root 43344 May 20 20:19 Microsoft.Extensions.Validation.dll
|
||||
2026-07-04T12:50:51.7703510Z -rwxr--r-- 1 root root 19296 Jun 2 20:51 Microsoft.IdentityModel.Abstractions.dll
|
||||
2026-07-04T12:50:51.7703597Z -rwxr--r-- 1 root root 172856 Jun 2 20:52 Microsoft.IdentityModel.JsonWebTokens.dll
|
||||
2026-07-04T12:50:51.7703683Z -rwxr--r-- 1 root root 38200 Jun 2 20:51 Microsoft.IdentityModel.Logging.dll
|
||||
2026-07-04T12:50:51.7703764Z -rwxr--r-- 1 root root 407352 Jun 2 20:51 Microsoft.IdentityModel.Tokens.dll
|
||||
2026-07-04T12:50:51.7703844Z -rwxr--r-- 1 root root 24912 May 20 20:20 Microsoft.JSInterop.WebAssembly.dll
|
||||
2026-07-04T12:50:51.7703924Z -rwxr--r-- 1 root root 75088 May 20 20:19 Microsoft.JSInterop.dll
|
||||
2026-07-04T12:50:51.7704012Z -rwxr--r-- 1 root root 428880 May 20 18:30 Microsoft.VisualBasic.Core.dll
|
||||
2026-07-04T12:50:51.7704101Z -rwxr--r-- 1 root root 17232 May 20 18:31 Microsoft.VisualBasic.dll
|
||||
2026-07-04T12:50:51.7704182Z -rwxr--r-- 1 root root 15696 May 20 18:29 Microsoft.Win32.Primitives.dll
|
||||
2026-07-04T12:50:51.7704272Z -rwxr--r-- 1 root root 33104 May 20 18:29 Microsoft.Win32.Registry.dll
|
||||
2026-07-04T12:50:51.7704425Z -rwxr--r-- 1 root root 9108480 Sep 14 2023 MudBlazor.dll
|
||||
2026-07-04T12:50:51.7704514Z -rwxr--r-- 1 root root 15184 May 20 18:31 System.AppContext.dll
|
||||
2026-07-04T12:50:51.7704608Z -rwxr--r-- 1 root root 15184 May 20 18:30 System.Buffers.dll
|
||||
2026-07-04T12:50:51.7704698Z -rwxr--r-- 1 root root 88912 May 20 18:29 System.Collections.Concurrent.dll
|
||||
2026-07-04T12:50:51.7704789Z -rwxr--r-- 1 root root 251216 May 20 18:29 System.Collections.Immutable.dll
|
||||
2026-07-04T12:50:51.7704875Z -rwxr--r-- 1 root root 47952 May 20 18:29 System.Collections.NonGeneric.dll
|
||||
2026-07-04T12:50:51.7704966Z -rwxr--r-- 1 root root 47952 May 20 18:29 System.Collections.Specialized.dll
|
||||
2026-07-04T12:50:51.7707333Z -rwxr--r-- 1 root root 112976 May 20 18:28 System.Collections.dll
|
||||
2026-07-04T12:50:51.7707687Z -rwxr--r-- 1 root root 102736 May 20 18:31 System.ComponentModel.Annotations.dll
|
||||
2026-07-04T12:50:51.7708035Z -rwxr--r-- 1 root root 16720 May 20 18:31 System.ComponentModel.DataAnnotations.dll
|
||||
2026-07-04T12:50:51.7708335Z -rwxr--r-- 1 root root 26448 May 20 18:29 System.ComponentModel.EventBasedAsync.dll
|
||||
2026-07-04T12:50:51.7708514Z -rwxr--r-- 1 root root 42320 May 20 18:29 System.ComponentModel.Primitives.dll
|
||||
2026-07-04T12:50:51.7708690Z -rwxr--r-- 1 root root 316752 May 20 18:31 System.ComponentModel.TypeConverter.dll
|
||||
2026-07-04T12:50:51.7708912Z -rwxr--r-- 1 root root 16208 May 20 18:29 System.ComponentModel.dll
|
||||
2026-07-04T12:50:51.7709087Z -rwxr--r-- 1 root root 19280 May 20 18:31 System.Configuration.dll
|
||||
2026-07-04T12:50:51.7709258Z -rwxr--r-- 1 root root 54096 May 20 18:31 System.Console.dll
|
||||
2026-07-04T12:50:51.7709460Z -rwxr--r-- 1 root root 23376 May 20 18:31 System.Core.dll
|
||||
2026-07-04T12:50:51.7709630Z -rwxr--r-- 1 root root 1018192 May 20 18:31 System.Data.Common.dll
|
||||
2026-07-04T12:50:51.7709804Z -rwxr--r-- 1 root root 15696 May 20 18:31 System.Data.DataSetExtensions.dll
|
||||
2026-07-04T12:50:51.7709982Z -rwxr--r-- 1 root root 25424 May 20 18:31 System.Data.dll
|
||||
2026-07-04T12:50:51.7710183Z -rwxr--r-- 1 root root 16208 May 20 18:31 System.Diagnostics.Contracts.dll
|
||||
2026-07-04T12:50:51.7710361Z -rwxr--r-- 1 root root 15696 May 20 18:30 System.Diagnostics.Debug.dll
|
||||
2026-07-04T12:50:51.7710524Z -rwxr--r-- 1 root root 202576 May 20 18:29 System.Diagnostics.DiagnosticSource.dll
|
||||
2026-07-04T12:50:51.7710895Z -rwxr--r-- 1 root root 22864 May 20 18:29 System.Diagnostics.FileVersionInfo.dll
|
||||
2026-07-04T12:50:51.7711109Z -rwxr--r-- 1 root root 56656 May 20 18:29 System.Diagnostics.Process.dll
|
||||
2026-07-04T12:50:51.7711275Z -rwxr--r-- 1 root root 25936 May 20 18:29 System.Diagnostics.StackTrace.dll
|
||||
2026-07-04T12:50:51.7711461Z -rwxr--r-- 1 root root 31568 May 20 18:31 System.Diagnostics.TextWriterTraceListener.dll
|
||||
2026-07-04T12:50:51.7711697Z -rwxr--r-- 1 root root 15184 May 20 18:30 System.Diagnostics.Tools.dll
|
||||
2026-07-04T12:50:51.7711883Z -rwxr--r-- 1 root root 58704 May 20 18:29 System.Diagnostics.TraceSource.dll
|
||||
2026-07-04T12:50:51.7712056Z -rwxr--r-- 1 root root 16208 May 20 18:28 System.Diagnostics.Tracing.dll
|
||||
2026-07-04T12:50:51.7712283Z -rwxr--r-- 1 root root 64848 May 20 18:29 System.Drawing.Primitives.dll
|
||||
2026-07-04T12:50:51.7712465Z -rwxr--r-- 1 root root 20304 May 20 18:31 System.Drawing.dll
|
||||
2026-07-04T12:50:51.7712648Z -rwxr--r-- 1 root root 16208 May 20 18:30 System.Dynamic.Runtime.dll
|
||||
2026-07-04T12:50:51.7712825Z -rwxr--r-- 1 root root 97104 May 20 18:29 System.Formats.Asn1.dll
|
||||
2026-07-04T12:50:51.7713000Z -rwxr--r-- 1 root root 38736 May 20 18:29 System.Formats.Tar.dll
|
||||
2026-07-04T12:50:51.7713087Z -rwxr--r-- 1 root root 15696 May 20 18:30 System.Globalization.Calendars.dll
|
||||
2026-07-04T12:50:51.7713176Z -rwxr--r-- 1 root root 15184 May 20 18:30 System.Globalization.Extensions.dll
|
||||
2026-07-04T12:50:51.7713291Z -rwxr--r-- 1 root root 15696 May 20 18:30 System.Globalization.dll
|
||||
2026-07-04T12:50:51.7713387Z -rwxr--r-- 1 root root 28496 May 20 18:29 System.IO.Compression.Brotli.dll
|
||||
2026-07-04T12:50:51.7713486Z -rwxr--r-- 1 root root 15184 May 20 18:31 System.IO.Compression.FileSystem.dll
|
||||
2026-07-04T12:50:51.7713573Z -rwxr--r-- 1 root root 53584 May 20 18:29 System.IO.Compression.ZipFile.dll
|
||||
2026-07-04T12:50:51.7713654Z -rwxr--r-- 1 root root 167760 May 20 18:31 System.IO.Compression.dll
|
||||
2026-07-04T12:50:51.7713736Z -rwxr--r-- 1 root root 32080 May 20 18:29 System.IO.FileSystem.AccessControl.dll
|
||||
2026-07-04T12:50:51.7713827Z -rwxr--r-- 1 root root 23888 May 20 18:29 System.IO.FileSystem.DriveInfo.dll
|
||||
2026-07-04T12:50:51.7713926Z -rwxr--r-- 1 root root 15184 May 20 18:30 System.IO.FileSystem.Primitives.dll
|
||||
2026-07-04T12:50:51.7714013Z -rwxr--r-- 1 root root 33104 May 20 18:29 System.IO.FileSystem.Watcher.dll
|
||||
2026-07-04T12:50:51.7714096Z -rwxr--r-- 1 root root 15696 May 20 18:30 System.IO.FileSystem.dll
|
||||
2026-07-04T12:50:51.7714178Z -rwxr--r-- 1 root root 35152 May 20 18:30 System.IO.IsolatedStorage.dll
|
||||
2026-07-04T12:50:51.7714259Z -rwxr--r-- 1 root root 50000 May 20 18:31 System.IO.MemoryMappedFiles.dll
|
||||
2026-07-04T12:50:51.7714344Z -rwxr--r-- 1 root root 78160 May 20 18:29 System.IO.Pipelines.dll
|
||||
2026-07-04T12:50:51.7714423Z -rwxr--r-- 1 root root 23376 May 20 18:29 System.IO.Pipes.AccessControl.dll
|
||||
2026-07-04T12:50:51.7714513Z -rwxr--r-- 1 root root 42320 May 20 18:29 System.IO.Pipes.dll
|
||||
2026-07-04T12:50:51.7714598Z -rwxr--r-- 1 root root 15696 May 20 18:30 System.IO.UnmanagedMemoryStream.dll
|
||||
2026-07-04T12:50:51.7714682Z -rwxr--r-- 1 root root 15696 May 20 18:30 System.IO.dll
|
||||
2026-07-04T12:50:51.7714764Z -rwxr--r-- 1 root root 92000 Jun 2 20:51 System.IdentityModel.Tokens.Jwt.dll
|
||||
2026-07-04T12:50:51.7714848Z -rwxr--r-- 1 root root 456528 May 20 18:29 System.Linq.AsyncEnumerable.dll
|
||||
2026-07-04T12:50:51.7714934Z -rwxr--r-- 1 root root 575312 May 20 18:29 System.Linq.Expressions.dll
|
||||
2026-07-04T12:50:51.7715013Z -rwxr--r-- 1 root root 223056 May 20 18:31 System.Linq.Parallel.dll
|
||||
2026-07-04T12:50:51.7715099Z -rwxr--r-- 1 root root 78672 May 20 18:31 System.Linq.Queryable.dll
|
||||
2026-07-04T12:50:51.7715179Z -rwxr--r-- 1 root root 201040 May 20 18:29 System.Linq.dll
|
||||
2026-07-04T12:50:51.7715280Z -rwxr--r-- 1 root root 55632 May 20 18:28 System.Memory.dll
|
||||
2026-07-04T12:50:51.7715363Z -rwxr--r-- 1 root root 56144 May 20 18:30 System.Net.Http.Json.dll
|
||||
2026-07-04T12:50:51.7715443Z -rwxr--r-- 1 root root 296272 May 20 18:31 System.Net.Http.dll
|
||||
2026-07-04T12:50:51.7724789Z -rwxr--r-- 1 root root 56144 May 20 18:30 System.Net.HttpListener.dll
|
||||
2026-07-04T12:50:51.7725003Z -rwxr--r-- 1 root root 105296 May 20 18:31 System.Net.Mail.dll
|
||||
2026-07-04T12:50:51.7725118Z -rwxr--r-- 1 root root 24400 May 20 18:31 System.Net.NameResolution.dll
|
||||
2026-07-04T12:50:51.7725378Z -rwxr--r-- 1 root root 42320 May 20 18:29 System.Net.NetworkInformation.dll
|
||||
2026-07-04T12:50:51.7725485Z -rwxr--r-- 1 root root 27984 May 20 18:29 System.Net.Ping.dll
|
||||
2026-07-04T12:50:51.7725575Z -rwxr--r-- 1 root root 107344 May 20 18:31 System.Net.Primitives.dll
|
||||
2026-07-04T12:50:51.7725659Z -rwxr--r-- 1 root root 39248 May 20 18:30 System.Net.Quic.dll
|
||||
2026-07-04T12:50:51.7725741Z -rwxr--r-- 1 root root 65872 May 20 18:30 System.Net.Requests.dll
|
||||
2026-07-04T12:50:51.7725821Z -rwxr--r-- 1 root root 114512 May 20 18:30 System.Net.Security.dll
|
||||
2026-07-04T12:50:51.7725899Z -rwxr--r-- 1 root root 40784 May 20 18:29 System.Net.ServerSentEvents.dll
|
||||
2026-07-04T12:50:51.7725997Z -rwxr--r-- 1 root root 15184 May 20 18:31 System.Net.ServicePoint.dll
|
||||
2026-07-04T12:50:51.7726207Z -rwxr--r-- 1 root root 74576 May 20 18:29 System.Net.Sockets.dll
|
||||
2026-07-04T12:50:51.7726297Z -rwxr--r-- 1 root root 56144 May 20 18:31 System.Net.WebClient.dll
|
||||
2026-07-04T12:50:51.7726382Z -rwxr--r-- 1 root root 33104 May 20 18:29 System.Net.WebHeaderCollection.dll
|
||||
2026-07-04T12:50:51.7726473Z -rwxr--r-- 1 root root 21840 May 20 18:31 System.Net.WebProxy.dll
|
||||
2026-07-04T12:50:51.7726565Z -rwxr--r-- 1 root root 52560 May 20 18:31 System.Net.WebSockets.Client.dll
|
||||
2026-07-04T12:50:51.7726651Z -rwxr--r-- 1 root root 108880 May 20 18:31 System.Net.WebSockets.dll
|
||||
2026-07-04T12:50:51.7726745Z -rwxr--r-- 1 root root 17232 May 20 18:31 System.Net.dll
|
||||
2026-07-04T12:50:51.7726828Z -rwxr--r-- 1 root root 15696 May 20 18:28 System.Numerics.Vectors.dll
|
||||
2026-07-04T12:50:51.7726973Z -rwxr--r-- 1 root root 15184 May 20 18:31 System.Numerics.dll
|
||||
2026-07-04T12:50:51.7727145Z -rwxr--r-- 1 root root 41296 May 20 18:29 System.ObjectModel.dll
|
||||
2026-07-04T12:50:51.7727300Z -rwxr--r-- 1 root root 4880208 May 20 18:19 System.Private.CoreLib.dll
|
||||
2026-07-04T12:50:51.7727457Z -rwxr--r-- 1 root root 859472 May 20 18:31 System.Private.DataContractSerialization.dll
|
||||
2026-07-04T12:50:51.7727613Z -rwxr--r-- 1 root root 105808 May 20 18:28 System.Private.Uri.dll
|
||||
2026-07-04T12:50:51.7727769Z -rwxr--r-- 1 root root 153936 May 20 18:30 System.Private.Xml.Linq.dll
|
||||
2026-07-04T12:50:51.7727929Z -rwxr--r-- 1 root root 3106128 May 20 18:30 System.Private.Xml.dll
|
||||
2026-07-04T12:50:51.7728079Z -rwxr--r-- 1 root root 38224 May 20 18:31 System.Reflection.DispatchProxy.dll
|
||||
2026-07-04T12:50:51.7728248Z -rwxr--r-- 1 root root 15696 May 20 18:29 System.Reflection.Emit.ILGeneration.dll
|
||||
2026-07-04T12:50:51.7728406Z -rwxr--r-- 1 root root 15696 May 20 18:29 System.Reflection.Emit.Lightweight.dll
|
||||
2026-07-04T12:50:51.7728565Z -rwxr--r-- 1 root root 133456 May 20 18:29 System.Reflection.Emit.dll
|
||||
2026-07-04T12:50:51.7728728Z -rwxr--r-- 1 root root 15184 May 20 18:30 System.Reflection.Extensions.dll
|
||||
2026-07-04T12:50:51.7728907Z -rwxr--r-- 1 root root 503632 May 20 18:29 System.Reflection.Metadata.dll
|
||||
2026-07-04T12:50:51.7729052Z -rwxr--r-- 1 root root 15696 May 20 18:28 System.Reflection.Primitives.dll
|
||||
2026-07-04T12:50:51.7729143Z -rwxr--r-- 1 root root 24400 May 20 18:31 System.Reflection.TypeExtensions.dll
|
||||
2026-07-04T12:50:51.7729244Z -rwxr--r-- 1 root root 16208 May 20 18:31 System.Reflection.dll
|
||||
2026-07-04T12:50:51.7729337Z -rwxr--r-- 1 root root 15184 May 20 18:31 System.Resources.Reader.dll
|
||||
2026-07-04T12:50:51.7729450Z -rwxr--r-- 1 root root 15696 May 20 18:30 System.Resources.ResourceManager.dll
|
||||
2026-07-04T12:50:51.7729588Z -rwxr--r-- 1 root root 26960 May 20 18:31 System.Resources.Writer.dll
|
||||
2026-07-04T12:50:51.7729711Z -rwxr--r-- 1 root root 15184 May 20 18:30 System.Runtime.CompilerServices.Unsafe.dll
|
||||
2026-07-04T12:50:51.7729802Z -rwxr--r-- 1 root root 17232 May 20 18:30 System.Runtime.CompilerServices.VisualC.dll
|
||||
2026-07-04T12:50:51.7729888Z -rwxr--r-- 1 root root 17744 May 20 18:30 System.Runtime.Extensions.dll
|
||||
2026-07-04T12:50:51.7729981Z -rwxr--r-- 1 root root 15696 May 20 18:30 System.Runtime.Handles.dll
|
||||
2026-07-04T12:50:51.7730089Z -rwxr--r-- 1 root root 89936 May 20 18:31 System.Runtime.InteropServices.JavaScript.dll
|
||||
2026-07-04T12:50:51.7730186Z -rwxr--r-- 1 root root 15184 May 20 18:31 System.Runtime.InteropServices.RuntimeInformation.dll
|
||||
2026-07-04T12:50:51.7730278Z -rwxr--r-- 1 root root 64848 May 20 18:29 System.Runtime.InteropServices.dll
|
||||
2026-07-04T12:50:51.7730393Z -rwxr--r-- 1 root root 17232 May 20 18:29 System.Runtime.Intrinsics.dll
|
||||
2026-07-04T12:50:51.7730536Z -rwxr--r-- 1 root root 15696 May 20 18:29 System.Runtime.Loader.dll
|
||||
2026-07-04T12:50:51.7730680Z -rwxr--r-- 1 root root 145232 May 20 18:29 System.Runtime.Numerics.dll
|
||||
2026-07-04T12:50:51.7730916Z -rwxr--r-- 1 root root 65872 May 20 18:29 System.Runtime.Serialization.Formatters.dll
|
||||
2026-07-04T12:50:51.7731009Z -rwxr--r-- 1 root root 15696 May 20 18:31 System.Runtime.Serialization.Json.dll
|
||||
2026-07-04T12:50:51.7731092Z -rwxr--r-- 1 root root 23376 May 20 18:30 System.Runtime.Serialization.Primitives.dll
|
||||
2026-07-04T12:50:51.7731180Z -rwxr--r-- 1 root root 16720 May 20 18:31 System.Runtime.Serialization.Xml.dll
|
||||
2026-07-04T12:50:51.7731381Z -rwxr--r-- 1 root root 17232 May 20 18:31 System.Runtime.Serialization.dll
|
||||
2026-07-04T12:50:51.7731472Z -rwxr--r-- 1 root root 44880 May 20 18:28 System.Runtime.dll
|
||||
2026-07-04T12:50:51.7731568Z -rwxr--r-- 1 root root 58192 May 20 18:29 System.Security.AccessControl.dll
|
||||
2026-07-04T12:50:51.7731659Z -rwxr--r-- 1 root root 55120 May 20 18:29 System.Security.Claims.dll
|
||||
2026-07-04T12:50:51.7731742Z -rwxr--r-- 1 root root 17232 May 20 18:31 System.Security.Cryptography.Algorithms.dll
|
||||
2026-07-04T12:50:51.7731846Z -rwxr--r-- 1 root root 16208 May 20 18:31 System.Security.Cryptography.Cng.dll
|
||||
2026-07-04T12:50:51.7731929Z -rwxr--r-- 1 root root 16208 May 20 18:31 System.Security.Cryptography.Csp.dll
|
||||
2026-07-04T12:50:51.7732017Z -rwxr--r-- 1 root root 15696 May 20 18:31 System.Security.Cryptography.Encoding.dll
|
||||
2026-07-04T12:50:51.7732102Z -rwxr--r-- 1 root root 15696 May 20 18:31 System.Security.Cryptography.OpenSsl.dll
|
||||
2026-07-04T12:50:51.7732185Z -rwxr--r-- 1 root root 15696 May 20 18:31 System.Security.Cryptography.Primitives.dll
|
||||
2026-07-04T12:50:51.7732267Z -rwxr--r-- 1 root root 16720 May 20 18:31 System.Security.Cryptography.X509Certificates.dll
|
||||
2026-07-04T12:50:51.7732350Z -rwxr--r-- 1 root root 654160 May 20 18:31 System.Security.Cryptography.dll
|
||||
2026-07-04T12:50:51.7732431Z -rwxr--r-- 1 root root 37712 May 20 18:29 System.Security.Principal.Windows.dll
|
||||
2026-07-04T12:50:51.7732517Z -rwxr--r-- 1 root root 15184 May 20 18:31 System.Security.Principal.dll
|
||||
2026-07-04T12:50:51.7732604Z -rwxr--r-- 1 root root 15696 May 20 18:30 System.Security.SecureString.dll
|
||||
2026-07-04T12:50:51.7732693Z -rwxr--r-- 1 root root 18256 May 20 18:31 System.Security.dll
|
||||
2026-07-04T12:50:51.7732776Z -rwxr--r-- 1 root root 16720 May 20 18:31 System.ServiceModel.Web.dll
|
||||
2026-07-04T12:50:51.7732919Z -rwxr--r-- 1 root root 15696 May 20 18:31 System.ServiceProcess.dll
|
||||
2026-07-04T12:50:51.7733005Z -rwxr--r-- 1 root root 742736 May 20 18:29 System.Text.Encoding.CodePages.dll
|
||||
2026-07-04T12:50:51.7733087Z -rwxr--r-- 1 root root 15696 May 20 18:28 System.Text.Encoding.Extensions.dll
|
||||
2026-07-04T12:50:51.7733169Z -rwxr--r-- 1 root root 15696 May 20 18:30 System.Text.Encoding.dll
|
||||
2026-07-04T12:50:51.7733257Z -rwxr--r-- 1 root root 65872 May 20 18:32 System.Text.Encodings.Web.dll
|
||||
2026-07-04T12:50:51.7733337Z -rwxr--r-- 1 root root 649040 May 20 18:30 System.Text.Json.dll
|
||||
2026-07-04T12:50:51.7733484Z -rwxr--r-- 1 root root 384848 May 20 18:30 System.Text.RegularExpressions.dll
|
||||
2026-07-04T12:50:51.7733633Z -rwxr--r-- 1 root root 33616 May 20 18:29 System.Threading.AccessControl.dll
|
||||
2026-07-04T12:50:51.7733786Z -rwxr--r-- 1 root root 66384 May 20 18:29 System.Threading.Channels.dll
|
||||
2026-07-04T12:50:51.7733944Z -rwxr--r-- 1 root root 15696 May 20 18:28 System.Threading.Overlapped.dll
|
||||
2026-07-04T12:50:51.7734092Z -rwxr--r-- 1 root root 185680 May 20 18:29 System.Threading.Tasks.Dataflow.dll
|
||||
2026-07-04T12:50:51.7734257Z -rwxr--r-- 1 root root 15696 May 20 18:30 System.Threading.Tasks.Extensions.dll
|
||||
2026-07-04T12:50:51.7734393Z -rwxr--r-- 1 root root 61264 May 20 18:31 System.Threading.Tasks.Parallel.dll
|
||||
2026-07-04T12:50:51.7734482Z -rwxr--r-- 1 root root 16720 May 20 18:31 System.Threading.Tasks.dll
|
||||
2026-07-04T12:50:51.7734567Z -rwxr--r-- 1 root root 15696 May 20 18:28 System.Threading.Thread.dll
|
||||
2026-07-04T12:50:51.7734649Z -rwxr--r-- 1 root root 15696 May 20 18:28 System.Threading.ThreadPool.dll
|
||||
2026-07-04T12:50:51.7734728Z -rwxr--r-- 1 root root 15184 May 20 18:30 System.Threading.Timer.dll
|
||||
2026-07-04T12:50:51.7734807Z -rwxr--r-- 1 root root 44880 May 20 18:28 System.Threading.dll
|
||||
2026-07-04T12:50:51.7734908Z -rwxr--r-- 1 root root 175952 May 20 18:30 System.Transactions.Local.dll
|
||||
2026-07-04T12:50:51.7734993Z -rwxr--r-- 1 root root 16720 May 20 18:31 System.Transactions.dll
|
||||
2026-07-04T12:50:51.7735073Z -rwxr--r-- 1 root root 15696 May 20 18:30 System.ValueTuple.dll
|
||||
2026-07-04T12:50:51.7735153Z -rwxr--r-- 1 root root 30032 May 20 18:31 System.Web.HttpUtility.dll
|
||||
2026-07-04T12:50:51.7735268Z -rwxr--r-- 1 root root 15184 May 20 18:31 System.Web.dll
|
||||
2026-07-04T12:50:51.7735365Z -rwxr--r-- 1 root root 15696 May 20 18:31 System.Windows.dll
|
||||
2026-07-04T12:50:51.7735446Z -rwxr--r-- 1 root root 15696 May 20 18:31 System.Xml.Linq.dll
|
||||
2026-07-04T12:50:51.7735534Z -rwxr--r-- 1 root root 21840 May 20 18:30 System.Xml.ReaderWriter.dll
|
||||
2026-07-04T12:50:51.7735652Z -rwxr--r-- 1 root root 16208 May 20 18:31 System.Xml.Serialization.dll
|
||||
2026-07-04T12:50:51.7735784Z -rwxr--r-- 1 root root 15696 May 20 18:31 System.Xml.XDocument.dll
|
||||
2026-07-04T12:50:51.7735871Z -rwxr--r-- 1 root root 15696 May 20 18:31 System.Xml.XPath.XDocument.dll
|
||||
2026-07-04T12:50:51.7735952Z -rwxr--r-- 1 root root 15696 May 20 18:30 System.Xml.XPath.dll
|
||||
2026-07-04T12:50:51.7736041Z -rwxr--r-- 1 root root 15696 May 20 18:31 System.Xml.XmlDocument.dll
|
||||
2026-07-04T12:50:51.7736341Z -rwxr--r-- 1 root root 17744 May 20 18:31 System.Xml.XmlSerializer.dll
|
||||
2026-07-04T12:50:51.7736447Z -rwxr--r-- 1 root root 23376 May 20 18:31 System.Xml.dll
|
||||
2026-07-04T12:50:51.7736537Z -rwxr--r-- 1 root root 50000 May 20 18:32 System.dll
|
||||
2026-07-04T12:50:51.7736621Z -rw-r--r-- 1 root root 156672 Jul 4 12:49 TaxBaik.Application.dll
|
||||
2026-07-04T12:50:51.7736707Z -rw-r--r-- 1 root root 38468 Jul 4 12:49 TaxBaik.Application.pdb
|
||||
2026-07-04T12:50:51.7736794Z -rw-r--r-- 1 root root 37888 Jul 4 12:49 TaxBaik.Domain.dll
|
||||
2026-07-04T12:50:51.7736873Z -rw-r--r-- 1 root root 24100 Jul 4 12:49 TaxBaik.Domain.pdb
|
||||
2026-07-04T12:50:51.7736955Z -rw-r--r-- 1 root root 713216 Jul 4 12:50 TaxBaik.Web.Client.dll
|
||||
2026-07-04T12:50:51.7737039Z -rw-r--r-- 1 root root 373136 Jul 4 12:50 TaxBaik.Web.Client.pdb
|
||||
2026-07-04T12:50:51.7737139Z -rw-r--r-- 1 root root 2546 Jul 4 12:50 TaxBaik.Web.Client.runtimeconfig.json
|
||||
2026-07-04T12:50:51.7737338Z -rw-r--r-- 1 root root 1007340 Jul 4 12:50 TaxBaik.Web.Client.staticwebassets.endpoints.json
|
||||
2026-07-04T12:50:51.7737432Z -rw-r--r-- 1 root root 78064 Jul 4 12:50 TaxBaik.Web.Client.staticwebassets.runtime.json
|
||||
2026-07-04T12:50:51.7737525Z -rwxr--r-- 1 root root 16208 May 20 18:31 WindowsBase.dll
|
||||
2026-07-04T12:50:51.7737609Z -rwxr--r-- 1 root root 37898 May 20 18:42 dotnet.js
|
||||
2026-07-04T12:50:51.7737687Z -rwxr--r-- 1 root root 51818 May 20 18:42 dotnet.js.map
|
||||
2026-07-04T12:50:51.7737778Z -rwxr--r-- 1 root root 145050 May 20 18:43 dotnet.native.js
|
||||
2026-07-04T12:50:51.7737860Z -rwxr--r-- 1 root root 3002101 May 20 18:43 dotnet.native.wasm
|
||||
2026-07-04T12:50:51.7737942Z -rwxr--r-- 1 root root 198479 May 20 18:42 dotnet.runtime.js
|
||||
2026-07-04T12:50:51.7738024Z -rwxr--r-- 1 root root 276757 May 20 18:42 dotnet.runtime.js.map
|
||||
2026-07-04T12:50:51.7738104Z -rwxr--r-- 1 root root 956416 Apr 2 19:04 icudt_CJK.dat
|
||||
2026-07-04T12:50:51.7738182Z -rwxr--r-- 1 root root 550832 Apr 2 19:04 icudt_EFIGS.dat
|
||||
2026-07-04T12:50:51.7738259Z -rwxr--r-- 1 root root 1107168 Apr 2 19:04 icudt_no_CJK.dat
|
||||
2026-07-04T12:50:51.7738341Z -rwxr--r-- 1 root root 59728 May 20 18:31 mscorlib.dll
|
||||
2026-07-04T12:50:51.7738417Z -rwxr--r-- 1 root root 100688 May 20 18:32 netstandard.dll
|
||||
2026-07-04T12:50:51.7738496Z drwxr-xr-x 3 root root 4096 Jul 4 12:50 wwwroot
|
||||
2026-07-04T12:50:51.7740611Z total 4352
|
||||
2026-07-04T12:50:51.7740967Z drwxr-xr-x 8 root root 4096 Jul 4 12:50 .
|
||||
2026-07-04T12:50:51.7741083Z drwxr-xr-x 3 root root 4096 Jul 4 12:50 ..
|
||||
2026-07-04T12:50:51.7741169Z -rw-r--r-- 1 root root 196 Jul 4 12:50 .NETCoreApp,Version=v10.0.AssemblyAttributes.cs
|
||||
2026-07-04T12:50:51.7741346Z -rw-r--r-- 1 root root 137 Jul 4 12:50 EmbeddedAttribute.cs
|
||||
2026-07-04T12:50:51.7741437Z -rw-r--r-- 1 root root 0 Jul 4 12:50 TaxBaik..C36EE7CA.Up2Date
|
||||
2026-07-04T12:50:51.7741531Z -rw-r--r-- 1 root root 1008 Jul 4 12:50 TaxBaik.Web.Client.AssemblyInfo.cs
|
||||
2026-07-04T12:50:51.7741630Z -rw-r--r-- 1 root root 65 Jul 4 12:50 TaxBaik.Web.Client.AssemblyInfoInputs.cache
|
||||
2026-07-04T12:50:51.7741744Z -rw-r--r-- 1 root root 16355 Jul 4 12:50 TaxBaik.Web.Client.GeneratedMSBuildEditorConfig.editorconfig
|
||||
2026-07-04T12:50:51.7741848Z -rw-r--r-- 1 root root 433 Jul 4 12:50 TaxBaik.Web.Client.GlobalUsings.g.cs
|
||||
2026-07-04T12:50:51.7741978Z -rw-r--r-- 1 root root 0 Jul 4 12:50 TaxBaik.Web.Client.MvcApplicationPartsAssemblyInfo.cache
|
||||
2026-07-04T12:50:51.7742164Z -rw-r--r-- 1 root root 27329 Jul 4 12:50 TaxBaik.Web.Client.assets.cache
|
||||
2026-07-04T12:50:51.7742340Z -rw-r--r-- 1 root root 20456 Jul 4 12:50 TaxBaik.Web.Client.csproj.AssemblyReference.cache
|
||||
2026-07-04T12:50:51.7742497Z -rw-r--r-- 1 root root 65 Jul 4 12:50 TaxBaik.Web.Client.csproj.CoreCompileInputs.cache
|
||||
2026-07-04T12:50:51.7742654Z -rw-r--r-- 1 root root 136894 Jul 4 12:50 TaxBaik.Web.Client.csproj.FileListAbsolute.txt
|
||||
2026-07-04T12:50:51.7742893Z -rw-r--r-- 1 root root 713216 Jul 4 12:50 TaxBaik.Web.Client.dll
|
||||
2026-07-04T12:50:51.7743120Z -rw-r--r-- 1 root root 65 Jul 4 12:50 TaxBaik.Web.Client.genruntimeconfig.cache
|
||||
2026-07-04T12:50:51.7743334Z -rw-r--r-- 1 root root 373136 Jul 4 12:50 TaxBaik.Web.Client.pdb
|
||||
2026-07-04T12:50:51.7743742Z -rw-r--r-- 1 root root 276 Jul 4 12:50 ValidatableTypeAttribute.cs
|
||||
2026-07-04T12:50:51.7743930Z -rw-r--r-- 1 root root 3 Jul 4 12:50 blazor.build.boot-extension.json
|
||||
2026-07-04T12:50:51.7744151Z drwxr-xr-x 2 root root 20480 Jul 4 12:50 compressed
|
||||
2026-07-04T12:50:51.7744319Z -rw-r--r-- 1 root root 93392 Jul 4 12:50 dotnet.js
|
||||
2026-07-04T12:50:51.7744459Z -rw-r--r-- 1 root root 276706 Jul 4 12:50 rbcswa.dswa.cache.json
|
||||
2026-07-04T12:50:51.7744599Z drwxr-xr-x 2 root root 4096 Jul 4 12:50 ref
|
||||
2026-07-04T12:50:51.7744731Z drwxr-xr-x 2 root root 4096 Jul 4 12:50 refint
|
||||
2026-07-04T12:50:51.7744901Z -rw-r--r-- 1 root root 322 Jul 4 12:50 rjimswa.dswa.cache.json
|
||||
2026-07-04T12:50:51.7745034Z -rw-r--r-- 1 root root 3404 Jul 4 12:50 rjsmcshtml.dswa.cache.json
|
||||
2026-07-04T12:50:51.7745177Z -rw-r--r-- 1 root root 3404 Jul 4 12:50 rjsmrazor.dswa.cache.json
|
||||
2026-07-04T12:50:51.7745357Z -rw-r--r-- 1 root root 4173 Jul 4 12:50 rpswa.dswa.cache.json
|
||||
2026-07-04T12:50:51.7745511Z drwxr-xr-x 2 root root 4096 Jul 4 12:50 staticwebassets
|
||||
2026-07-04T12:50:51.7745658Z -rw-r--r-- 1 root root 1007340 Jul 4 12:50 staticwebassets.build.endpoints.json
|
||||
2026-07-04T12:50:51.7745816Z -rw-r--r-- 1 root root 1564906 Jul 4 12:50 staticwebassets.build.json
|
||||
2026-07-04T12:50:51.7746022Z -rw-r--r-- 1 root root 44 Jul 4 12:50 staticwebassets.build.json.cache
|
||||
2026-07-04T12:50:51.7804929Z -rw-r--r-- 1 root root 78064 Jul 4 12:50 staticwebassets.development.json
|
||||
2026-07-04T12:50:51.7805097Z -rw-r--r-- 1 root root 0 Jul 4 12:50 swae.build.ex.cache
|
||||
2026-07-04T12:50:51.7805385Z drwxr-xr-x 2 root root 4096 Jul 4 12:50 tmp-webcil
|
||||
2026-07-04T12:50:51.7805479Z drwxr-xr-x 2 root root 20480 Jul 4 12:50 webcil
|
||||
2026-07-04T12:51:56.0836642Z ✓ Publish Web elapsed: 86s
|
||||
2026-07-04T12:51:56.0856804Z -rw-r--r-- 1 root root 2.2M Jul 4 12:51 ./publish-logs/publish-web.binlog
|
||||
2026-07-04T12:51:56.1993997Z ::group::Run set -e
|
||||
2026-07-04T12:51:56.1994446Z set -e
|
||||
2026-07-04T12:51:56.1994570Z mkdir -p ./publish-logs
|
||||
2026-07-04T12:51:56.1994674Z # Proxy is not part of the solution restore graph, so restore it once
|
||||
2026-07-04T12:51:56.1994784Z # here before publishing to avoid NETSDK1004 in CI.
|
||||
2026-07-04T12:51:56.1994877Z dotnet restore src/TaxBaik.Proxy/
|
||||
2026-07-04T12:51:56.1994953Z dotnet build src/TaxBaik.Proxy/TaxBaik.Proxy.csproj -c Release --no-restore
|
||||
2026-07-04T12:51:56.1995040Z start=$(date +%s)
|
||||
2026-07-04T12:51:56.1995115Z dotnet publish src/TaxBaik.Proxy/ \
|
||||
2026-07-04T12:51:56.1995200Z -c Release \
|
||||
2026-07-04T12:51:56.1995275Z -o ./publish/proxy \
|
||||
2026-07-04T12:51:56.1995345Z --no-restore \
|
||||
2026-07-04T12:51:56.1995433Z --no-build \
|
||||
2026-07-04T12:51:56.1995766Z -p:PublishReadyToRun=false \
|
||||
2026-07-04T12:51:56.1995969Z -p:PerformanceSummary=true \
|
||||
2026-07-04T12:51:56.1996302Z -clp:Summary \
|
||||
2026-07-04T12:51:56.1996496Z -bl:./publish-logs/publish-proxy.binlog
|
||||
2026-07-04T12:51:56.1996597Z end=$(date +%s)
|
||||
2026-07-04T12:51:56.1996672Z echo "✓ Publish Proxy elapsed: $((end - start))s"
|
||||
2026-07-04T12:51:56.1996955Z ls -lh ./publish-logs/publish-proxy.binlog
|
||||
2026-07-04T12:51:56.1997163Z shell: bash --noprofile --norc -e -o pipefail {0}
|
||||
2026-07-04T12:51:56.1997314Z ::endgroup::
|
||||
2026-07-04T12:51:57.1407533Z Determining projects to restore...
|
||||
2026-07-04T12:51:57.6632131Z Restored /workspace/***/taxbaik/src/TaxBaik.Proxy/TaxBaik.Proxy.csproj (in 125 ms).
|
||||
2026-07-04T12:51:58.9747864Z TaxBaik.Proxy -> /workspace/***/taxbaik/src/TaxBaik.Proxy/bin/Release/net10.0/TaxBaik.Proxy.dll
|
||||
2026-07-04T12:51:58.9938437Z
|
||||
2026-07-04T12:51:58.9947013Z Build succeeded.
|
||||
2026-07-04T12:51:58.9949917Z 0 Warning(s)
|
||||
2026-07-04T12:51:58.9951624Z 0 Error(s)
|
||||
2026-07-04T12:51:58.9954373Z
|
||||
2026-07-04T12:51:58.9954554Z Time Elapsed 00:00:01.05
|
||||
2026-07-04T12:52:00.0446907Z TaxBaik.Proxy -> /workspace/***/taxbaik/publish/proxy/
|
||||
2026-07-04T12:52:00.0565606Z
|
||||
2026-07-04T12:52:00.0583909Z Build succeeded.
|
||||
2026-07-04T12:52:00.0584436Z 0 Warning(s)
|
||||
2026-07-04T12:52:00.0584681Z 0 Error(s)
|
||||
2026-07-04T12:52:00.0584764Z
|
||||
2026-07-04T12:52:00.0584841Z Time Elapsed 00:00:00.64
|
||||
2026-07-04T12:52:00.0903373Z ✓ Publish Proxy elapsed: 1s
|
||||
2026-07-04T12:52:00.0915796Z -rw-r--r-- 1 root root 335K Jul 4 12:52 ./publish-logs/publish-proxy.binlog
|
||||
2026-07-04T12:52:00.2477651Z ::group::Run set -e
|
||||
2026-07-04T12:52:00.2478168Z set -e
|
||||
2026-07-04T12:52:00.2478625Z JWT_SECRET_KEY="***"
|
||||
2026-07-04T12:52:00.2478852Z TELEGRAM_BOT_TOKEN="***"
|
||||
2026-07-04T12:52:00.2478963Z TELEGRAM_CHAT_ID="***"
|
||||
2026-07-04T12:52:00.2479117Z TELEGRAM_INQUIRY_CHAT_ID=""
|
||||
2026-07-04T12:52:00.2479461Z TELEGRAM_SYSTEM_CHAT_ID=""
|
||||
2026-07-04T12:52:00.2479762Z [ -z "$JWT_SECRET_KEY" ] && { echo "Missing TAXBAIK_JWT_SECRET_KEY" >&2; exit 1; }
|
||||
2026-07-04T12:52:00.2479916Z [ -z "$TELEGRAM_BOT_TOKEN" ] && { echo "Missing TAXBAIK_TELEGRAM_BOT_TOKEN" >&2; exit 1; }
|
||||
2026-07-04T12:52:00.2480078Z [ -z "$TELEGRAM_CHAT_ID" ] && { echo "Missing TAXBAIK_TELEGRAM_CHAT_ID" >&2; exit 1; }
|
||||
2026-07-04T12:52:00.2480551Z [ -z "$TELEGRAM_INQUIRY_CHAT_ID" ] && TELEGRAM_INQUIRY_CHAT_ID="$TELEGRAM_CHAT_ID"
|
||||
2026-07-04T12:52:00.2480669Z [ -z "$TELEGRAM_SYSTEM_CHAT_ID" ] && TELEGRAM_SYSTEM_CHAT_ID="-5585148480"
|
||||
2026-07-04T12:52:00.2481140Z JWT_SECRET_KEY="$JWT_SECRET_KEY" \
|
||||
2026-07-04T12:52:00.2481420Z TELEGRAM_BOT_TOKEN="$TELEGRAM_BOT_TOKEN" \
|
||||
2026-07-04T12:52:00.2481513Z TELEGRAM_CHAT_ID="$TELEGRAM_CHAT_ID" \
|
||||
2026-07-04T12:52:00.2481588Z TELEGRAM_INQUIRY_CHAT_ID="$TELEGRAM_INQUIRY_CHAT_ID" \
|
||||
2026-07-04T12:52:00.2482377Z TELEGRAM_SYSTEM_CHAT_ID="$TELEGRAM_SYSTEM_CHAT_ID" \
|
||||
2026-07-04T12:52:00.2482488Z python3 -c '
|
||||
2026-07-04T12:52:00.2482577Z import json, os, pathlib
|
||||
2026-07-04T12:52:00.2482665Z pathlib.Path("./publish/appsettings.Production.json").write_text(
|
||||
2026-07-04T12:52:00.2483035Z json.dumps({
|
||||
2026-07-04T12:52:00.2483310Z "Jwt": {"SecretKey": os.environ["JWT_SECRET_KEY"]},
|
||||
2026-07-04T12:52:00.2483405Z "Telegram": {
|
||||
2026-07-04T12:52:00.2483477Z "BotToken": os.environ["TELEGRAM_BOT_TOKEN"],
|
||||
2026-07-04T12:52:00.2483559Z "ChatId": os.environ["TELEGRAM_CHAT_ID"],
|
||||
2026-07-04T12:52:00.2483634Z "InquiryChatId": os.environ["TELEGRAM_INQUIRY_CHAT_ID"],
|
||||
2026-07-04T12:52:00.2483960Z "SystemChatId": os.environ["TELEGRAM_SYSTEM_CHAT_ID"]
|
||||
2026-07-04T12:52:00.2484230Z }
|
||||
2026-07-04T12:52:00.2484311Z }, ensure_ascii=False, indent=2),
|
||||
2026-07-04T12:52:00.2484386Z encoding="utf-8"
|
||||
2026-07-04T12:52:00.2484471Z )'
|
||||
2026-07-04T12:52:00.2484543Z test -s ./publish/appsettings.Production.json || { echo "appsettings.Production.json is empty" >&2; exit 1; }
|
||||
2026-07-04T12:52:00.2484866Z shell: bash --noprofile --norc -e -o pipefail {0}
|
||||
2026-07-04T12:52:00.2485155Z ::endgroup::
|
||||
2026-07-04T12:52:00.5025133Z ::group::Run test -s ./publish/proxy/TaxBaik.Proxy.dll || { echo "TaxBaik.Proxy.dll missing" >&2; exit 1; }
|
||||
2026-07-04T12:52:00.5025722Z test -s ./publish/proxy/TaxBaik.Proxy.dll || { echo "TaxBaik.Proxy.dll missing" >&2; exit 1; }
|
||||
2026-07-04T12:52:00.5025922Z test -s ./publish/proxy/TaxBaik.Proxy.runtimeconfig.json || { echo "TaxBaik.Proxy.runtimeconfig.json missing" >&2; exit 1; }
|
||||
2026-07-04T12:52:00.5026227Z shell: bash --noprofile --norc -e -o pipefail {0}
|
||||
2026-07-04T12:52:00.5026398Z ::endgroup::
|
||||
2026-07-04T12:52:00.6764369Z ::group::Run mkdir -p ./publish/db && cp -r db/migrations ./publish/db/ || true
|
||||
2026-07-04T12:52:00.6764765Z mkdir -p ./publish/db && cp -r db/migrations ./publish/db/ || true
|
||||
2026-07-04T12:52:00.6764895Z shell: bash --noprofile --norc -e -o pipefail {0}
|
||||
2026-07-04T12:52:00.6765004Z ::endgroup::
|
||||
2026-07-04T12:52:00.8728261Z ::group::Run bash scripts/validate_migrations.sh db/migrations
|
||||
2026-07-04T12:52:00.8728660Z bash scripts/validate_migrations.sh db/migrations
|
||||
2026-07-04T12:52:00.8728905Z shell: bash --noprofile --norc -e -o pipefail {0}
|
||||
2026-07-04T12:52:00.8729177Z ::endgroup::
|
||||
2026-07-04T12:52:00.9538985Z Duplicate version check passed.
|
||||
2026-07-04T12:52:01.0855583Z ::group::Run bash scripts/validate_kst_timestamps.sh
|
||||
2026-07-04T12:52:01.0856340Z bash scripts/validate_kst_timestamps.sh
|
||||
2026-07-04T12:52:01.0856747Z shell: bash --noprofile --norc -e -o pipefail {0}
|
||||
2026-07-04T12:52:01.0856924Z ::endgroup::
|
||||
2026-07-04T12:52:01.1537519Z KST timestamp harness passed.
|
||||
2026-07-04T12:52:01.3082211Z ::group::Run COMMIT_HASH=$(git rev-parse --short HEAD)
|
||||
2026-07-04T12:52:01.3082595Z COMMIT_HASH=$(git rev-parse --short HEAD)
|
||||
2026-07-04T12:52:01.3082713Z BUILD_TIME=$(TZ=Asia/Seoul date +'%Y-%m-%d %H:%M:%S KST')
|
||||
2026-07-04T12:52:01.3082807Z mkdir -p ./publish/wwwroot
|
||||
2026-07-04T12:52:01.3082952Z printf '{\n "version": "%s",\n "built": "%s"\n}\n' "$COMMIT_HASH" "$BUILD_TIME" > ./publish/wwwroot/version.json
|
||||
2026-07-04T12:52:01.3083148Z echo "✓ Build: $COMMIT_HASH @ $BUILD_TIME"
|
||||
2026-07-04T12:52:01.3083277Z shell: bash --noprofile --norc -e -o pipefail {0}
|
||||
2026-07-04T12:52:01.3083386Z ::endgroup::
|
||||
2026-07-04T12:52:01.3712948Z ✓ Build: dc86ccf @ 2026-07-04 21:52:01 KST
|
||||
2026-07-04T12:52:01.4848269Z ::group::Run mkdir -p ~/.ssh
|
||||
2026-07-04T12:52:01.4849031Z mkdir -p ~/.ssh
|
||||
2026-07-04T12:52:01.4849211Z SSH_KEY_B64="***"
|
||||
2026-07-04T12:52:01.4849414Z SSH_KEY_RAW="***"
|
||||
2026-07-04T12:52:01.4849576Z if [ -n "$SSH_KEY_B64" ]; then
|
||||
2026-07-04T12:52:01.4850063Z printf '%s' "$SSH_KEY_B64" | base64 -d > ~/.ssh/id_ed25519
|
||||
2026-07-04T12:52:01.4850225Z elif [ -n "$SSH_KEY_RAW" ]; then
|
||||
2026-07-04T12:52:01.4850362Z if printf '%s' "$SSH_KEY_RAW" | grep -q 'BEGIN .*PRIVATE KEY'; then
|
||||
2026-07-04T12:52:01.4850943Z printf '%b\n' "$SSH_KEY_RAW" > ~/.ssh/id_ed25519
|
||||
2026-07-04T12:52:01.4851190Z else
|
||||
2026-07-04T12:52:01.4851357Z printf '%s' "$SSH_KEY_RAW" | base64 -d > ~/.ssh/id_ed25519
|
||||
2026-07-04T12:52:01.4851809Z fi
|
||||
2026-07-04T12:52:01.4851949Z else
|
||||
2026-07-04T12:52:01.4852141Z echo "Missing DEPLOY_SSH_KEY_B64 or DEPLOY_SSH_KEY" >&2; exit 1
|
||||
2026-07-04T12:52:01.4852495Z fi
|
||||
2026-07-04T12:52:01.4852617Z sed -i 's/\r$//' ~/.ssh/id_ed25519
|
||||
2026-07-04T12:52:01.4852755Z chmod 600 ~/.ssh/id_ed25519
|
||||
2026-07-04T12:52:01.4852886Z ssh-keyscan -H "***" >> ~/.ssh/known_hosts 2>/dev/null || true
|
||||
2026-07-04T12:52:01.4853093Z shell: bash --noprofile --norc -e -o pipefail {0}
|
||||
2026-07-04T12:52:01.4853480Z ::endgroup::
|
||||
2026-07-04T12:52:01.9503887Z ::group::Run cp deploy_gb.sh ./publish/deploy_gb.sh
|
||||
2026-07-04T12:52:01.9504389Z cp deploy_gb.sh ./publish/deploy_gb.sh
|
||||
2026-07-04T12:52:01.9504506Z mkdir -p ./publish/scripts
|
||||
2026-07-04T12:52:01.9504591Z cp scripts/validate_migrations.sh ./publish/scripts/validate_migrations.sh
|
||||
2026-07-04T12:52:01.9504690Z chmod +x ./publish/scripts/validate_migrations.sh
|
||||
2026-07-04T12:52:01.9504769Z tar -czf taxbaik_deploy.tgz -C ./publish .
|
||||
2026-07-04T12:52:01.9504850Z echo "✓ Package: $(du -sh taxbaik_deploy.tgz | cut -f1)"
|
||||
2026-07-04T12:52:01.9504949Z shell: bash --noprofile --norc -e -o pipefail {0}
|
||||
2026-07-04T12:52:01.9505044Z ::endgroup::
|
||||
2026-07-04T12:52:04.4650255Z ✓ Package: 23M
|
||||
2026-07-04T12:52:04.5749245Z ::group::Run set -e
|
||||
2026-07-04T12:52:04.5749613Z set -e
|
||||
2026-07-04T12:52:04.5749749Z export TAXBAIK_DEPLOY_FROM_CI=1
|
||||
2026-07-04T12:52:04.5749842Z TIMESTAMP=$(TZ=Asia/Seoul date +%Y%m%d_%H%M%S)
|
||||
2026-07-04T12:52:04.5749941Z COMMIT=$(git rev-parse --short HEAD)
|
||||
2026-07-04T12:52:04.5750025Z DEPLOY_HOST="***"
|
||||
2026-07-04T12:52:04.5750112Z DEPLOY_USER="***"
|
||||
2026-07-04T12:52:04.5750193Z TELEGRAM_BOT_TOKEN="***"
|
||||
2026-07-04T12:52:04.5750278Z TELEGRAM_SYSTEM_CHAT_ID=""
|
||||
2026-07-04T12:52:04.5750351Z TELEGRAM_CHAT_ID="${TELEGRAM_SYSTEM_CHAT_ID:--5585148480}"
|
||||
2026-07-04T12:52:04.5750434Z
|
||||
2026-07-04T12:52:04.5750499Z send_telegram() {
|
||||
2026-07-04T12:52:04.5750573Z local text="$1"
|
||||
2026-07-04T12:52:04.5750651Z if [ -z "$TELEGRAM_BOT_TOKEN" ]; then
|
||||
2026-07-04T12:52:04.5750957Z echo "Skipping Telegram notification: missing TAXBAIK_TELEGRAM_BOT_TOKEN" >&2
|
||||
2026-07-04T12:52:04.5751063Z return 0
|
||||
2026-07-04T12:52:04.5751137Z fi
|
||||
2026-07-04T12:52:04.5751205Z
|
||||
2026-07-04T12:52:04.5751268Z curl -fsS -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \
|
||||
2026-07-04T12:52:04.5751359Z -d "chat_id=${TELEGRAM_CHAT_ID}" \
|
||||
2026-07-04T12:52:04.5751470Z --data-urlencode "text=${text}" \
|
||||
2026-07-04T12:52:04.5751545Z -d "parse_mode=HTML" >/dev/null || true
|
||||
2026-07-04T12:52:04.5751619Z }
|
||||
2026-07-04T12:52:04.5751687Z
|
||||
2026-07-04T12:52:04.5751744Z notify_failure() {
|
||||
2026-07-04T12:52:04.5751817Z local exit_code=$?
|
||||
2026-07-04T12:52:04.5751888Z send_telegram "❌ <b>TaxBaik 배포 실패</b>
|
||||
2026-07-04T12:52:04.5751985Z
|
||||
2026-07-04T12:52:04.5752046Z 커밋: <code>${COMMIT}</code>
|
||||
2026-07-04T12:52:04.5752122Z 시간: <code>${TIMESTAMP}</code>
|
||||
2026-07-04T12:52:04.5752192Z 단계: CI/CD deploy"
|
||||
2026-07-04T12:52:04.5752262Z exit "$exit_code"
|
||||
2026-07-04T12:52:04.5752330Z }
|
||||
2026-07-04T12:52:04.5752395Z
|
||||
2026-07-04T12:52:04.5752476Z trap notify_failure ERR
|
||||
2026-07-04T12:52:04.5752547Z
|
||||
2026-07-04T12:52:04.5752607Z echo "=== Deploying TaxBaik $COMMIT ($TIMESTAMP) ==="
|
||||
2026-07-04T12:52:04.5752682Z
|
||||
2026-07-04T12:52:04.5752745Z # 1. 아티팩트 업로드
|
||||
2026-07-04T12:52:04.5752819Z scp -i ~/.ssh/id_ed25519 -o StrictHostKeyChecking=yes \
|
||||
2026-07-04T12:52:04.5752898Z taxbaik_deploy.tgz "$DEPLOY_USER@$DEPLOY_HOST:/tmp/taxbaik_${TIMESTAMP}.tgz"
|
||||
2026-07-04T12:52:04.5752988Z
|
||||
2026-07-04T12:52:04.5753051Z # 2. 서버에서 배포 + 헬스 체크 (SSH 1회 연결로 처리, Green-Blue 지원)
|
||||
2026-07-04T12:52:04.5753136Z ssh -i ~/.ssh/id_ed25519 -o StrictHostKeyChecking=yes \
|
||||
2026-07-04T12:52:04.5753543Z -o ServerAliveInterval=10 \
|
||||
2026-07-04T12:52:04.5753625Z "$DEPLOY_USER@$DEPLOY_HOST" TAXBAIK_DEPLOY_FROM_CI=1 bash << REMOTE
|
||||
2026-07-04T12:52:04.5753709Z set -e
|
||||
2026-07-04T12:52:04.5753788Z DEPLOY_HOME="/home/***"
|
||||
2026-07-04T12:52:04.5753873Z DEPLOY_DIR="\$DEPLOY_HOME/deployments/taxbaik_${TIMESTAMP}"
|
||||
2026-07-04T12:52:04.5753953Z TIMESTAMP="${TIMESTAMP}"
|
||||
2026-07-04T12:52:04.5754024Z COMMIT="${COMMIT}"
|
||||
2026-07-04T12:52:04.5754093Z
|
||||
2026-07-04T12:52:04.5754169Z echo "--- [1/5] 압축 해제 ---"
|
||||
2026-07-04T12:52:04.5754243Z mkdir -p "\$DEPLOY_DIR"
|
||||
2026-07-04T12:52:04.5754318Z tar -xzf "/tmp/taxbaik_\${TIMESTAMP}.tgz" -C "\$DEPLOY_DIR"
|
||||
2026-07-04T12:52:04.5754407Z rm -f "/tmp/taxbaik_\${TIMESTAMP}.tgz"
|
||||
2026-07-04T12:52:04.5754475Z
|
||||
2026-07-04T12:52:04.5754537Z echo "--- [2/5] 운영 설정 검증 ---"
|
||||
2026-07-04T12:52:04.5754611Z test -s "\$DEPLOY_DIR/appsettings.Production.json" \
|
||||
2026-07-04T12:52:04.5754687Z || { echo "FATAL: appsettings.Production.json 없음" >&2; exit 1; }
|
||||
2026-07-04T12:52:04.5754769Z test -s "\$DEPLOY_DIR/proxy/TaxBaik.Proxy.dll" \
|
||||
2026-07-04T12:52:04.5754845Z || { echo "FATAL: TaxBaik.Proxy.dll 없음" >&2; exit 1; }
|
||||
2026-07-04T12:52:04.5754928Z
|
||||
2026-07-04T12:52:04.5754989Z echo "--- [3/5] 마이그레이션 사전 검증 ---"
|
||||
2026-07-04T12:52:04.5755073Z test -x "\$DEPLOY_DIR/scripts/validate_migrations.sh" \
|
||||
2026-07-04T12:52:04.5755143Z || { echo "FATAL: validate_migrations.sh 없음" >&2; exit 1; }
|
||||
2026-07-04T12:52:04.5755220Z "\$DEPLOY_DIR/scripts/validate_migrations.sh" "\$DEPLOY_DIR/db/migrations" "postgresql://taxbaik:taxbaik123@localhost:5432/taxbaikdb"
|
||||
2026-07-04T12:52:04.5755316Z
|
||||
2026-07-04T12:52:04.5755383Z echo "--- [4/5] Green-Blue 배포 실행 ---"
|
||||
2026-07-04T12:52:04.5755479Z chmod +x "\$DEPLOY_DIR/deploy_gb.sh"
|
||||
2026-07-04T12:52:04.5755550Z "\$DEPLOY_DIR/deploy_gb.sh" "\$DEPLOY_DIR"
|
||||
2026-07-04T12:52:04.5755622Z
|
||||
2026-07-04T12:52:04.5755686Z echo "--- [4.5/5] Nginx 설정 검증 ---"
|
||||
2026-07-04T12:52:04.5755768Z # 실제 로드되는 파일은 sites-enabled/의 심볼릭 링크 대상만이다.
|
||||
2026-07-04T12:52:04.5755850Z # sites-available/에 다른 파일(예: default)이 있어도 sites-enabled에
|
||||
2026-07-04T12:52:04.5755945Z # 링크되어 있지 않으면 nginx는 그 내용을 절대 읽지 않는다.
|
||||
2026-07-04T12:52:04.5756036Z NGINX_CONF=""
|
||||
2026-07-04T12:52:04.5756277Z for f in /etc/nginx/sites-enabled/*; do
|
||||
2026-07-04T12:52:04.5756361Z if [ -e "\$f" ] && grep -q "location /taxbaik" "\$f" 2>/dev/null; then
|
||||
2026-07-04T12:52:04.5756482Z NGINX_CONF=\$(readlink -f "\$f")
|
||||
2026-07-04T12:52:04.5756667Z break
|
||||
2026-07-04T12:52:04.5756846Z fi
|
||||
2026-07-04T12:52:04.5756964Z done
|
||||
2026-07-04T12:52:04.5757108Z
|
||||
2026-07-04T12:52:04.5757224Z if [ -z "\$NGINX_CONF" ]; then
|
||||
2026-07-04T12:52:04.5757368Z echo "❌ FATAL: sites-enabled/ 안에서 'location /taxbaik'를 정의한 파일을 찾을 수 없음" >&2
|
||||
2026-07-04T12:52:04.5757523Z echo " sites-available/에 파일을 수정해도 sites-enabled에 심볼릭 링크되어 있지 않으면 반영되지 않는다." >&2
|
||||
2026-07-04T12:52:04.5757636Z exit 1
|
||||
2026-07-04T12:52:04.5757715Z fi
|
||||
2026-07-04T12:52:04.5757781Z echo "실제 로드되는 설정 파일: \$NGINX_CONF"
|
||||
2026-07-04T12:52:04.5757891Z
|
||||
2026-07-04T12:52:04.5758224Z # 불변식: '/'와 '/taxbaik' location 모두 반드시 127.0.0.1:5001 (TaxBaik.Proxy)을
|
||||
2026-07-04T12:52:04.5758411Z # 가리켜야 한다. 5003/5004를 직접 하드코딩하면 Green-Blue 포트 전환 시
|
||||
2026-07-04T12:52:04.5758502Z # 죽은 포트를 가리키게 되어 502/404가 발생한다 (실제 발생했던 장애).
|
||||
2026-07-04T12:52:04.5758585Z if grep -E "proxy_pass\s+http://127\.0\.0\.1:500[34]" "\$NGINX_CONF" > /dev/null 2>&1; then
|
||||
2026-07-04T12:52:04.5758688Z echo "❌ FATAL: \$NGINX_CONF 가 포트 5003/5004를 직접 참조함 (Green-Blue 전환 시 502 발생)" >&2
|
||||
2026-07-04T12:52:04.5758790Z echo " 수정: sudo sed -i 's|127.0.0.1:500[34]|127.0.0.1:5001|g' \$NGINX_CONF && sudo nginx -t && sudo systemctl reload nginx" >&2
|
||||
2026-07-04T12:52:04.5758903Z exit 1
|
||||
2026-07-04T12:52:04.5758978Z fi
|
||||
2026-07-04T12:52:04.5759041Z
|
||||
2026-07-04T12:52:04.5759104Z # proxy_pass에 URI(끝 슬래시)가 있으면 nginx가 요청 경로를 재작성하며,
|
||||
2026-07-04T12:52:04.5759451Z # location 접두사와 슬래시 개수가 안 맞으면 백엔드로 이중 슬래시(//)가
|
||||
2026-07-04T12:52:04.5759539Z # 전달되어 404가 발생한다 (실제 발생했던 장애). 접두사 location에서는
|
||||
2026-07-04T12:52:04.5759631Z # proxy_pass에 URI를 붙이지 않는다.
|
||||
2026-07-04T12:52:04.5759730Z if grep -E "location\s+/taxbaik\s*\{" -A 1 "\$NGINX_CONF" | grep -qE "proxy_pass\s+http://127\.0\.0\.1:5001/;"; then
|
||||
2026-07-04T12:52:04.5759822Z echo "❌ FATAL: location /taxbaik 의 proxy_pass 에 불필요한 trailing slash가 있음 (이중 슬래시로 인한 404 위험)" >&2
|
||||
2026-07-04T12:52:04.5759913Z exit 1
|
||||
2026-07-04T12:52:04.5759981Z fi
|
||||
2026-07-04T12:52:04.5760047Z
|
||||
2026-07-04T12:52:04.5760121Z echo "✓ Nginx 설정 검증 통과 (실제 로드 파일 확인 + 포트 5001 고정 + trailing slash 없음)"
|
||||
2026-07-04T12:52:04.5760214Z
|
||||
2026-07-04T12:52:04.5760285Z echo "--- [5/5] 헬스 체크 (최대 60초) ---"
|
||||
2026-07-04T12:52:04.5760366Z ATTEMPTS=20
|
||||
2026-07-04T12:52:04.5760433Z for i in \$(seq 1 \$ATTEMPTS); do
|
||||
2026-07-04T12:52:04.5760506Z STATUS=\$(curl -sf -o /dev/null -w '%{http_code}' http://127.0.0.1:5001/taxbaik/healthz 2>/dev/null || echo "000")
|
||||
2026-07-04T12:52:04.5760596Z if [ "\$STATUS" = "200" ]; then
|
||||
2026-07-04T12:52:04.5760678Z echo "✓ [1/6] 헬스 체크 완료"
|
||||
2026-07-04T12:52:04.5760917Z
|
||||
2026-07-04T12:52:04.5760986Z # 검증 1: 메인 페이지 로드. curl -L + -w 는 리다이렉트 체인의 상태코드를
|
||||
2026-07-04T12:52:04.5761079Z # 이어붙이므로, 첫 응답 코드만 받아 200/3xx를 허용한다.
|
||||
2026-07-04T12:52:04.5761159Z MAIN_STATUS=\$(curl -fsS -o /dev/null -w '%{http_code}' http://127.0.0.1:5001/taxbaik/ 2>/dev/null || echo "000")
|
||||
2026-07-04T12:52:04.5761249Z if ! printf '%s' "\$MAIN_STATUS" | grep -Eq '^(200|301|302|307|308)$'; then
|
||||
2026-07-04T12:52:04.5761333Z echo "❌ 메인 페이지 로드 실패 (상태: \$MAIN_STATUS)" >&2
|
||||
2026-07-04T12:52:04.5761415Z exit 1
|
||||
2026-07-04T12:52:04.5761482Z fi
|
||||
2026-07-04T12:52:04.5761561Z echo "✓ [2/6] 메인 페이지 로드 완료"
|
||||
2026-07-04T12:52:04.5761647Z
|
||||
2026-07-04T12:52:04.5761724Z # 검증 2: CSS 파일 로드
|
||||
2026-07-04T12:52:04.5761804Z CSS_STATUS=\$(curl -sf -o /dev/null -w '%{http_code}' http://127.0.0.1:5001/taxbaik/css/admin.css 2>/dev/null || echo "000")
|
||||
2026-07-04T12:52:04.5761955Z if [ "\$CSS_STATUS" != "200" ]; then
|
||||
2026-07-04T12:52:04.5762120Z echo "❌ CSS 파일 로드 실패 (상태: \$CSS_STATUS)" >&2
|
||||
2026-07-04T12:52:04.5762317Z exit 1
|
||||
2026-07-04T12:52:04.5762451Z fi
|
||||
2026-07-04T12:52:04.5762570Z echo "✓ [3/6] CSS 파일 로드 완료"
|
||||
2026-07-04T12:52:04.5762691Z
|
||||
2026-07-04T12:52:04.5762793Z # 검증 3: 버전 정보. 파일 존재만 보면 5001이 잘못된 구 프로세스를
|
||||
2026-07-04T12:52:04.5762940Z # 가리키는 장애를 놓치므로, HTTP 응답이 이번 커밋인지 확인한다.
|
||||
2026-07-04T12:52:04.5763096Z if [ ! -s "\$DEPLOY_DIR/wwwroot/version.json" ]; then
|
||||
2026-07-04T12:52:04.5763233Z echo "❌ version.json 누락" >&2
|
||||
2026-07-04T12:52:04.5763398Z exit 1
|
||||
2026-07-04T12:52:04.5763528Z fi
|
||||
2026-07-04T12:52:04.5763654Z VERSION_JSON=\$(curl -fsS http://127.0.0.1:5001/taxbaik/version.json 2>/dev/null || true)
|
||||
2026-07-04T12:52:04.5763823Z if ! printf '%s' "\$VERSION_JSON" | grep -q "\"version\": \"\$COMMIT\""; then
|
||||
2026-07-04T12:52:04.5763977Z echo "❌ 5001 프록시가 이번 배포 버전을 제공하지 않음" >&2
|
||||
2026-07-04T12:52:04.5764122Z echo " expected: \$COMMIT" >&2
|
||||
2026-07-04T12:52:04.5764261Z echo " actual: \$VERSION_JSON" >&2
|
||||
2026-07-04T12:52:04.5764401Z echo " 확인: 5001 포트가 TaxBaik.Proxy.dll인지, /home/***/taxbaik_port가 새 포트인지 점검" >&2
|
||||
2026-07-04T12:52:04.5764575Z exit 1
|
||||
2026-07-04T12:52:04.5764708Z fi
|
||||
2026-07-04T12:52:04.5764827Z echo "✓ [4/6] 버전 정보 확인 완료"
|
||||
2026-07-04T12:52:04.5764961Z
|
||||
2026-07-04T12:52:04.5765061Z # 검증 4: 5001 프록시 확인
|
||||
2026-07-04T12:52:04.5765196Z if ! ss -tlnp | grep -q ':5001 '; then
|
||||
2026-07-04T12:52:04.5765343Z echo "❌ 5001 프록시가 실행 중이 아님" >&2
|
||||
2026-07-04T12:52:04.5765486Z exit 1
|
||||
2026-07-04T12:52:04.5765610Z fi
|
||||
2026-07-04T12:52:04.5765730Z echo "✓ [5/6] 5001 프록시 확인 완료"
|
||||
2026-07-04T12:52:04.5765870Z
|
||||
2026-07-04T12:52:04.5765986Z # 검증 5: 관리자 로그인 페이지
|
||||
2026-07-04T12:52:04.5766315Z LOGIN_STATUS=\$(curl -fsSL -o /dev/null -w '%{http_code}' http://127.0.0.1:5001/taxbaik/admin/login 2>/dev/null || echo "000")
|
||||
2026-07-04T12:52:04.5766510Z if [ "\$LOGIN_STATUS" != "200" ]; then
|
||||
2026-07-04T12:52:04.5766652Z echo "❌ 관리자 로그인 페이지 로드 실패 (상태: \$LOGIN_STATUS)" >&2
|
||||
2026-07-04T12:52:04.5766809Z exit 1
|
||||
2026-07-04T12:52:04.5766928Z fi
|
||||
2026-07-04T12:52:04.5767049Z echo "✓ [6/6] 관리자 페이지 로드 완료"
|
||||
2026-07-04T12:52:04.5767172Z
|
||||
2026-07-04T12:52:04.5767291Z echo "✓ 서비스 정상 (시도 \$i/\$ATTEMPTS)"
|
||||
2026-07-04T12:52:04.5767439Z # 구 배포 디렉토리 정리 (최근 5개 보존)
|
||||
2026-07-04T12:52:04.5767591Z ls -1dt \$DEPLOY_HOME/deployments/taxbaik_* 2>/dev/null \
|
||||
2026-07-04T12:52:04.5767740Z | tail -n +6 | xargs rm -rf 2>/dev/null || true
|
||||
2026-07-04T12:52:04.5767880Z exit 0
|
||||
2026-07-04T12:52:04.5768010Z fi
|
||||
2026-07-04T12:52:04.5768129Z if [ "\$i" -eq "\$ATTEMPTS" ]; then
|
||||
2026-07-04T12:52:04.5768263Z echo "=== FATAL: 서비스가 \$ATTEMPTS회 시도 후에도 응답하지 않음 ===" >&2
|
||||
2026-07-04T12:52:04.5768450Z echo "--- 5001 listener ---" >&2
|
||||
2026-07-04T12:52:04.5768587Z ss -tlnp 2>/dev/null | grep ':5001 ' >&2 || true
|
||||
2026-07-04T12:52:04.5768666Z echo "--- active port file ---" >&2
|
||||
2026-07-04T12:52:04.5768786Z cat "\$DEPLOY_HOME/taxbaik_port" >&2 || true
|
||||
2026-07-04T12:52:04.5768919Z echo "--- 신규 앱 로그 ---" >&2
|
||||
2026-07-04T12:52:04.5769051Z ACTIVE_PORT=\$(cat "\$DEPLOY_HOME/taxbaik_port" 2>/dev/null | tr -d '[:space:]' || true)
|
||||
2026-07-04T12:52:04.5769205Z if [ -n "\$ACTIVE_PORT" ] && [ -s "\$DEPLOY_DIR/web_\${ACTIVE_PORT}.log" ]; then
|
||||
2026-07-04T12:52:04.5769305Z tail -n 80 "\$DEPLOY_DIR/web_\${ACTIVE_PORT}.log" >&2
|
||||
2026-07-04T12:52:04.5769387Z else
|
||||
2026-07-04T12:52:04.5769507Z ls -la "\$DEPLOY_DIR" >&2 || true
|
||||
2026-07-04T12:52:04.5769640Z fi
|
||||
2026-07-04T12:52:04.5769755Z echo "--- proxy 로그 ---" >&2
|
||||
2026-07-04T12:52:04.5769889Z tail -n 80 "\$DEPLOY_HOME/taxbaik_proxy.log" >&2 || true
|
||||
2026-07-04T12:52:04.5770030Z exit 1
|
||||
2026-07-04T12:52:04.5770436Z fi
|
||||
2026-07-04T12:52:04.5770573Z echo " 대기 중... (\$i/\$ATTEMPTS, HTTP \$STATUS)"
|
||||
2026-07-04T12:52:04.5770874Z sleep 3
|
||||
2026-07-04T12:52:04.5771010Z done
|
||||
2026-07-04T12:52:04.5771127Z REMOTE
|
||||
2026-07-04T12:52:04.5771246Z
|
||||
2026-07-04T12:52:04.5771356Z echo "✓ 배포 완료: taxbaik_${TIMESTAMP} @ $DEPLOY_HOST"
|
||||
2026-07-04T12:52:04.5771479Z
|
||||
2026-07-04T12:52:04.5771545Z echo "--- 실제 공개 도메인 종단 간 검증 (Nginx/Cloudflare 경유, 최대 3회 재시도) ---"
|
||||
2026-07-04T12:52:04.5771637Z ROOT_URL="https://www.taxbaik.com/" \
|
||||
2026-07-04T12:52:04.5771759Z ADMIN_URL="https://www.taxbaik.com/taxbaik/admin/login" \
|
||||
2026-07-04T12:52:04.5771883Z PUBLIC_MARKER="백원숙 세무회계" \
|
||||
2026-07-04T12:52:04.5771959Z PUBLIC_FORBIDDEN="관리자" \
|
||||
2026-07-04T12:52:04.5772036Z ADMIN_MARKER="관리자 로그인" \
|
||||
2026-07-04T12:52:04.5772120Z MAX_RETRIES=3 \
|
||||
2026-07-04T12:52:04.5772189Z RETRY_SLEEP_SECONDS=5 \
|
||||
2026-07-04T12:52:04.5772309Z bash ./scripts/taxbaik-smoke.sh
|
||||
2026-07-04T12:52:04.5772408Z echo "✓ 실제 공개 도메인 전체 정상"
|
||||
2026-07-04T12:52:04.5772486Z
|
||||
2026-07-04T12:52:04.5772551Z send_telegram "✅ <b>TaxBaik 배포 완료</b>
|
||||
2026-07-04T12:52:04.5772628Z
|
||||
2026-07-04T12:52:04.5772713Z 커밋: <code>${COMMIT}</code>
|
||||
2026-07-04T12:52:04.5772791Z 시간: <code>${TIMESTAMP}</code>
|
||||
2026-07-04T12:52:04.5772868Z 대상: <code>${DEPLOY_HOST}</code>
|
||||
2026-07-04T12:52:04.5772944Z 채널: <code>${TELEGRAM_CHAT_ID}</code>"
|
||||
2026-07-04T12:52:04.5773021Z shell: bash --noprofile --norc -e -o pipefail {0}
|
||||
2026-07-04T12:52:04.5773130Z ::endgroup::
|
||||
2026-07-04T12:52:04.6332316Z === Deploying TaxBaik dc86ccf (20260704_215204) ===
|
||||
2026-07-04T12:52:05.3820913Z --- [1/5] 압축 해제 ---
|
||||
2026-07-04T12:52:05.6284914Z --- [2/5] 운영 설정 검증 ---
|
||||
2026-07-04T12:52:05.6285418Z --- [3/5] 마이그레이션 사전 검증 ---
|
||||
2026-07-04T12:52:05.8433051Z Migration dry-run validation passed.
|
||||
2026-07-04T12:52:05.8438859Z --- [4/5] Green-Blue 배포 실행 ---
|
||||
2026-07-04T12:52:05.8509623Z ===== 🚀 TaxBaik Green/Blue Deployment Script =====
|
||||
2026-07-04T12:52:05.8541302Z Active Port: 5003
|
||||
2026-07-04T12:52:05.8541658Z Target Port: 5004
|
||||
2026-07-04T12:52:05.8542047Z Deploy Directory: /home/***/deployments/taxbaik_20260704_215204
|
||||
2026-07-04T12:52:05.9049695Z === Starting New App on Port 5004 ===
|
||||
2026-07-04T12:52:07.9185792Z === Health Checking Port 5004 ===
|
||||
2026-07-04T12:52:08.2035208Z ✓ Health check passed on port 5004 (Attempt 1/20)
|
||||
2026-07-04T12:52:08.2035939Z === Switching Traffic to Port 5004 ===
|
||||
2026-07-04T12:52:08.2039084Z ✓ Traffic routed to 5004 (via TaxBaik.Proxy on 5001)
|
||||
2026-07-04T12:52:08.2041337Z === Stopping Old App on Port 5003 ===
|
||||
2026-07-04T12:52:08.2181936Z Killing old process PID: 4117859
|
||||
2026-07-04T12:52:08.2184795Z ✓ Old process terminated
|
||||
2026-07-04T12:52:08.2192076Z === Cleaning Up Old Deployments ===
|
||||
2026-07-04T12:52:08.2379074Z ✓ Cleanup completed
|
||||
2026-07-04T12:52:08.2379676Z ===== ✅ Green/Blue Deployment Completed Successfully =====
|
||||
2026-07-04T12:52:08.2385094Z --- [4.5/5] Nginx 설정 검증 ---
|
||||
2026-07-04T12:52:08.2436700Z 실제 로드되는 설정 파일: /etc/nginx/sites-available/taxbaik-domains.conf
|
||||
2026-07-04T12:52:08.2509737Z ✓ Nginx 설정 검증 통과 (실제 로드 파일 확인 + 포트 5001 고정 + trailing slash 없음)
|
||||
2026-07-04T12:52:08.2510207Z --- [5/5] 헬스 체크 (최대 60초) ---
|
||||
2026-07-04T12:52:08.2933338Z ✓ [1/6] 헬스 체크 완료
|
||||
2026-07-04T12:52:08.3102145Z ✓ [2/6] 메인 페이지 로드 완료
|
||||
2026-07-04T12:52:08.3324117Z ✓ [3/6] CSS 파일 로드 완료
|
||||
2026-07-04T12:52:08.3445763Z ✓ [4/6] 버전 정보 확인 완료
|
||||
2026-07-04T12:52:08.3544241Z ✓ [5/6] 5001 프록시 확인 완료
|
||||
2026-07-04T12:52:08.3650602Z ✓ [6/6] 관리자 페이지 로드 완료
|
||||
2026-07-04T12:52:08.3651465Z ✓ 서비스 정상 (시도 1/20)
|
||||
2026-07-04T12:52:08.3716219Z ✓ 배포 완료: taxbaik_20260704_215204 @ ***
|
||||
2026-07-04T12:52:08.3716849Z --- 실제 공개 도메인 종단 간 검증 (Nginx/Cloudflare 경유, 최대 3회 재시도) ---
|
||||
2026-07-04T12:52:10.5585268Z retrying... (1/3)
|
||||
2026-07-04T12:52:10.5585809Z ✗ https://www.taxbaik.com/ -> body missing required marker: 백원숙 세무회계
|
||||
2026-07-04T12:52:17.6149854Z ✗ https://www.taxbaik.com/ -> body missing required marker: 백원숙 세무회계
|
||||
2026-07-04T12:52:17.6152469Z retrying... (2/3)
|
||||
2026-07-04T12:52:24.5668073Z ✗ https://www.taxbaik.com/ -> body missing required marker: 백원숙 세무회계
|
||||
2026-07-04T12:52:24.5668594Z ✗ smoke verification failed
|
||||
2026-07-04T12:52:24.8198071Z ❌ Failure - Main Deploy & verify on server
|
||||
2026-07-04T12:52:24.8336834Z exitcode '1': failure
|
||||
2026-07-04T12:52:24.8630234Z evaluating expression 'success()'
|
||||
2026-07-04T12:52:24.8631375Z expression 'success()' evaluated to 'false'
|
||||
2026-07-04T12:52:24.8631585Z Skipping step 'Setup .NET' due to 'success()'
|
||||
2026-07-04T12:52:24.8839000Z evaluating expression 'always()'
|
||||
2026-07-04T12:52:24.8839624Z expression 'always()' evaluated to 'true'
|
||||
2026-07-04T12:52:24.8839784Z ⭐ Run Post Checkout code
|
||||
2026-07-04T12:52:24.8839977Z Writing entry to tarball workflow/outputcmd.txt len:0
|
||||
2026-07-04T12:52:24.8840143Z Writing entry to tarball workflow/statecmd.txt len:0
|
||||
2026-07-04T12:52:24.8840244Z Writing entry to tarball workflow/pathcmd.txt len:0
|
||||
2026-07-04T12:52:24.8840333Z Writing entry to tarball workflow/envs.txt len:0
|
||||
2026-07-04T12:52:24.8840411Z Writing entry to tarball workflow/SUMMARY.md len:0
|
||||
2026-07-04T12:52:24.8840523Z Extracting content to '/var/run/act'
|
||||
2026-07-04T12:52:24.8865590Z run post step for 'Checkout code'
|
||||
2026-07-04T12:52:24.8866457Z executing remote job container: [node /var/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/dist/index.js]
|
||||
2026-07-04T12:52:24.9112212Z 🐳 docker exec cmd=[node /var/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/dist/index.js] user= workdir=
|
||||
2026-07-04T12:52:24.9112610Z Exec command '[node /var/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/dist/index.js]'
|
||||
2026-07-04T12:52:24.9113018Z Working directory '/workspace/***/taxbaik'
|
||||
2026-07-04T12:52:25.1573902Z [command]/usr/bin/git version
|
||||
2026-07-04T12:52:25.1613809Z git version 2.54.0
|
||||
2026-07-04T12:52:25.1650128Z ***
|
||||
2026-07-04T12:52:25.1677928Z Temporarily overriding HOME='/tmp/daa4130a-b951-44ca-9bf6-2a832ad8d592' before making global git config changes
|
||||
2026-07-04T12:52:25.1678247Z Adding repository directory to the temporary git global config as a safe directory
|
||||
2026-07-04T12:52:25.1680429Z [command]/usr/bin/git config --global --add safe.directory /workspace/***/taxbaik
|
||||
2026-07-04T12:52:25.1715854Z [command]/usr/bin/git config --local --name-only --get-regexp core\.sshCommand
|
||||
2026-07-04T12:52:25.1760614Z [command]/usr/bin/git submodule foreach --recursive sh -c "git config --local --name-only --get-regexp 'core\.sshCommand' && git config --local --unset-all 'core.sshCommand' || :"
|
||||
2026-07-04T12:52:25.1997273Z [command]/usr/bin/git config --local --name-only --get-regexp http\.http\:\/\/gitea\:3000\/\.extraheader
|
||||
2026-07-04T12:52:25.2020534Z http.http://gitea:3000/.extraheader
|
||||
2026-07-04T12:52:25.2034486Z [command]/usr/bin/git config --local --unset-all http.http://gitea:3000/.extraheader
|
||||
2026-07-04T12:52:25.2067074Z [command]/usr/bin/git submodule foreach --recursive sh -c "git config --local --name-only --get-regexp 'http\.http\:\/\/gitea\:3000\/\.extraheader' && git config --local --unset-all 'http.http://gitea:3000/.extraheader' || :"
|
||||
2026-07-04T12:52:25.2313893Z [command]/usr/bin/git config --local --name-only --get-regexp ^includeIf\.gitdir:
|
||||
2026-07-04T12:52:25.2344461Z [command]/usr/bin/git submodule foreach --recursive git config --local --show-origin --name-only --get-regexp remote.origin.url
|
||||
2026-07-04T12:52:25.2706764Z ✅ Success - Post Checkout code
|
||||
2026-07-04T12:52:25.2810331Z Cleaning up container for job build-and-deploy
|
||||
2026-07-04T12:52:25.6754704Z Removed container: 7fdc784e6b95f4a2f3a7139414bcbaeff9754c0efd147950d7f0357c1b99e6c6
|
||||
2026-07-04T12:52:25.6765166Z 🐳 docker volume rm GITEA-ACTIONS-TASK-1550-WORKFLOW-TaxBaik-CI-CD-JOB-build-and-de-d1b621a7a13ad682fe29533e6a28b6835590d0898313b1b9fc5be41b5870455c
|
||||
2026-07-04T12:52:25.7686731Z 🐳 docker volume rm GITEA-ACTIONS-TASK-1550-WORKFLOW-TaxBaik-CI-CD-JOB-build-and-de-d1b621a7a13ad682fe29533e6a28b6835590d0898313b1b9fc5be41b5870455c-env
|
||||
2026-07-04T12:52:25.8187706Z 🏁 Job failed
|
||||
2026-07-04T12:52:25.8314043Z Job 'build-and-deploy' failed
|
||||
+944
@@ -0,0 +1,944 @@
|
||||
2026-07-04T12:55:56.4615511Z hz-prod-runner-2(version:v0.6.1) received task 1552 of job build-and-deploy, be triggered by event: push
|
||||
2026-07-04T12:55:56.4637712Z workflow prepared
|
||||
2026-07-04T12:55:56.4638531Z evaluating expression 'success()'
|
||||
2026-07-04T12:55:56.4639250Z expression 'success()' evaluated to 'true'
|
||||
2026-07-04T12:55:56.4639410Z 🚀 Start image=docker.gitea.com/runner-images:ubuntu-latest
|
||||
2026-07-04T12:55:56.4717320Z 🐳 docker pull image=docker.gitea.com/runner-images:ubuntu-latest platform= username= forcePull=false
|
||||
2026-07-04T12:55:56.4717578Z 🐳 docker pull docker.gitea.com/runner-images:ubuntu-latest
|
||||
2026-07-04T12:55:56.4972676Z Image exists? true
|
||||
2026-07-04T12:55:56.5450519Z 🐳 docker create image=docker.gitea.com/runner-images:ubuntu-latest platform= entrypoint=["/bin/sleep" "10800"] cmd=[] network="gitea_default"
|
||||
2026-07-04T12:55:56.6404647Z Created container name=GITEA-ACTIONS-TASK-1552-WORKFLOW-TaxBaik-CI-CD-JOB-build-and-de-ab2d1e70739a3063f448d1e9d12be855fbd02428b801ec092546b895bdf5c88d id=545d203ee9994ebf9eca9357f948b418a29c0a80f13c2d69e6b64ca3d86d4b5b from image docker.gitea.com/runner-images:ubuntu-latest (platform: )
|
||||
2026-07-04T12:55:56.6405073Z ENV ==> [RUNNER_TOOL_CACHE=/opt/hostedtoolcache RUNNER_OS=Linux RUNNER_ARCH=X64 RUNNER_TEMP=/tmp LANG=C.UTF-8]
|
||||
2026-07-04T12:55:56.6405231Z 🐳 docker run image=docker.gitea.com/runner-images:ubuntu-latest platform= entrypoint=["/bin/sleep" "10800"] cmd=[] network="gitea_default"
|
||||
2026-07-04T12:55:56.6405352Z Starting container: 545d203ee9994ebf9eca9357f948b418a29c0a80f13c2d69e6b64ca3d86d4b5b
|
||||
2026-07-04T12:55:56.8160318Z Started container: 545d203ee9994ebf9eca9357f948b418a29c0a80f13c2d69e6b64ca3d86d4b5b
|
||||
2026-07-04T12:55:56.9278346Z Writing entry to tarball workflow/event.json len:4744
|
||||
2026-07-04T12:55:56.9278912Z Writing entry to tarball workflow/envs.txt len:0
|
||||
2026-07-04T12:55:56.9279089Z Extracting content to '/var/run/act/'
|
||||
2026-07-04T12:55:56.9554266Z ☁ git clone 'https://github.com/actions/checkout' # ref=v4
|
||||
2026-07-04T12:55:56.9554640Z cloning https://github.com/actions/checkout to /root/.cache/act/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab
|
||||
2026-07-04T12:55:57.7778122Z Unable to pull refs/heads/v4: non-fast-forward update
|
||||
2026-07-04T12:55:57.7778594Z Cloned https://github.com/actions/checkout to /root/.cache/act/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab
|
||||
2026-07-04T12:55:57.7935665Z Checked out v4
|
||||
2026-07-04T12:55:57.8047363Z ☁ git clone 'https://github.com/actions/setup-dotnet' # ref=v4
|
||||
2026-07-04T12:55:57.8048149Z cloning https://github.com/actions/setup-dotnet to /root/.cache/act/2d637816dd86ec9ff59dad9ec3547bf90b88133b3029538a91ec96ac7f316336
|
||||
2026-07-04T12:55:58.3258730Z Unable to pull refs/heads/v4: worktree contains unstaged changes
|
||||
2026-07-04T12:55:58.3259204Z Cloned https://github.com/actions/setup-dotnet to /root/.cache/act/2d637816dd86ec9ff59dad9ec3547bf90b88133b3029538a91ec96ac7f316336
|
||||
2026-07-04T12:55:58.3442763Z Checked out v4
|
||||
2026-07-04T12:55:58.3705741Z evaluating expression ''
|
||||
2026-07-04T12:55:58.3706586Z expression '' evaluated to 'true'
|
||||
2026-07-04T12:55:58.3706713Z ⭐ Run Main Checkout code
|
||||
2026-07-04T12:55:58.3706901Z Writing entry to tarball workflow/outputcmd.txt len:0
|
||||
2026-07-04T12:55:58.3707051Z Writing entry to tarball workflow/statecmd.txt len:0
|
||||
2026-07-04T12:55:58.3707153Z Writing entry to tarball workflow/pathcmd.txt len:0
|
||||
2026-07-04T12:55:58.3707264Z Writing entry to tarball workflow/envs.txt len:0
|
||||
2026-07-04T12:55:58.3707351Z Writing entry to tarball workflow/SUMMARY.md len:0
|
||||
2026-07-04T12:55:58.3707451Z Extracting content to '/var/run/act'
|
||||
2026-07-04T12:55:58.3748068Z ::group::Run Checkout code
|
||||
2026-07-04T12:55:58.9258868Z ::add-matcher::/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/dist/problem-matcher.json
|
||||
2026-07-04T12:55:58.9264682Z Syncing repository: ***/taxbaik
|
||||
2026-07-04T12:55:58.9272267Z ::group::Getting Git version info
|
||||
2026-07-04T12:55:58.9272441Z Working directory is '/workspace/***/taxbaik'
|
||||
2026-07-04T12:55:58.9311321Z [command]/usr/bin/git version
|
||||
2026-07-04T12:55:58.9387081Z git version 2.54.0
|
||||
2026-07-04T12:55:58.9458330Z ::endgroup::
|
||||
2026-07-04T12:55:58.9480820Z Temporarily overriding HOME='/tmp/2e7b254c-3300-4ed7-b3ac-5e0628723e83' before making global git config changes
|
||||
2026-07-04T12:55:58.9482460Z Adding repository directory to the temporary git global config as a safe directory
|
||||
2026-07-04T12:55:58.9497103Z [command]/usr/bin/git config --global --add safe.directory /workspace/***/taxbaik
|
||||
2026-07-04T12:55:58.9544269Z Deleting the contents of '/workspace/***/taxbaik'
|
||||
2026-07-04T12:55:58.9576838Z ::group::Initializing the repository
|
||||
2026-07-04T12:55:58.9588388Z [command]/usr/bin/git init /workspace/***/taxbaik
|
||||
2026-07-04T12:55:58.9715154Z hint: Using 'master' as the name for the initial branch. This default branch name
|
||||
2026-07-04T12:55:58.9728629Z hint: will change to "main" in Git 3.0. To configure the initial branch name
|
||||
2026-07-04T12:55:58.9729225Z hint: to use in all of your new repositories, which will suppress this warning,
|
||||
2026-07-04T12:55:58.9729399Z hint: call:
|
||||
2026-07-04T12:55:58.9729772Z hint:
|
||||
2026-07-04T12:55:58.9729859Z hint: git config --global init.defaultBranch <name>
|
||||
2026-07-04T12:55:58.9729946Z hint:
|
||||
2026-07-04T12:55:58.9730097Z hint: Names commonly chosen instead of 'master' are 'main', 'trunk' and
|
||||
2026-07-04T12:55:58.9730184Z hint: 'development'. The just-created branch can be renamed via this command:
|
||||
2026-07-04T12:55:58.9730499Z hint:
|
||||
2026-07-04T12:55:58.9730665Z hint: git branch -m <name>
|
||||
2026-07-04T12:55:58.9730770Z hint:
|
||||
2026-07-04T12:55:58.9730838Z hint: Disable this message with "git config set advice.defaultBranchName false"
|
||||
2026-07-04T12:55:58.9730925Z Initialized empty Git repository in /workspace/***/taxbaik/.git/
|
||||
2026-07-04T12:55:58.9834190Z [command]/usr/bin/git remote add origin http://gitea:3000/***/taxbaik
|
||||
2026-07-04T12:55:58.9916667Z ::endgroup::
|
||||
2026-07-04T12:55:58.9917174Z ::group::Disabling automatic garbage collection
|
||||
2026-07-04T12:55:58.9928398Z [command]/usr/bin/git config --local gc.auto 0
|
||||
2026-07-04T12:55:58.9992137Z ::endgroup::
|
||||
2026-07-04T12:55:58.9992625Z ::group::Setting up auth
|
||||
2026-07-04T12:55:58.9992804Z [command]/usr/bin/git config --local --name-only --get-regexp core\.sshCommand
|
||||
2026-07-04T12:55:59.0031077Z [command]/usr/bin/git submodule foreach --recursive sh -c "git config --local --name-only --get-regexp 'core\.sshCommand' && git config --local --unset-all 'core.sshCommand' || :"
|
||||
2026-07-04T12:55:59.0510927Z [command]/usr/bin/git config --local --name-only --get-regexp http\.http\:\/\/gitea\:3000\/\.extraheader
|
||||
2026-07-04T12:55:59.0554440Z [command]/usr/bin/git submodule foreach --recursive sh -c "git config --local --name-only --get-regexp 'http\.http\:\/\/gitea\:3000\/\.extraheader' && git config --local --unset-all 'http.http://gitea:3000/.extraheader' || :"
|
||||
2026-07-04T12:55:59.0907354Z [command]/usr/bin/git config --local --name-only --get-regexp ^includeIf\.gitdir:
|
||||
2026-07-04T12:55:59.0920983Z [command]/usr/bin/git submodule foreach --recursive git config --local --show-origin --name-only --get-regexp remote.origin.url
|
||||
2026-07-04T12:55:59.1162021Z [command]/usr/bin/git config --local http.http://gitea:3000/.extraheader AUTHORIZATION: basic ***
|
||||
2026-07-04T12:55:59.1196020Z ::endgroup::
|
||||
2026-07-04T12:55:59.1196454Z ::group::Fetching the repository
|
||||
2026-07-04T12:55:59.1210231Z [command]/usr/bin/git -c protocol.version=2 fetch --no-tags --prune --no-recurse-submodules --depth=1 origin +c00d237a4d2716bc22cf06f053a807cd7a45f200:refs/remotes/origin/master
|
||||
2026-07-04T12:56:01.5767577Z From http://gitea:3000/***/taxbaik
|
||||
2026-07-04T12:56:01.5768075Z * [new ref] c00d237a4d2716bc22cf06f053a807cd7a45f200 -> origin/master
|
||||
2026-07-04T12:56:01.5798239Z ::endgroup::
|
||||
2026-07-04T12:56:01.5798671Z ::group::Determining the checkout info
|
||||
2026-07-04T12:56:01.5801467Z ::endgroup::
|
||||
2026-07-04T12:56:01.5808449Z [command]/usr/bin/git sparse-checkout disable
|
||||
2026-07-04T12:56:01.5862591Z [command]/usr/bin/git config --local --unset-all extensions.worktreeConfig
|
||||
2026-07-04T12:56:01.5900860Z ::group::Checking out the ref
|
||||
2026-07-04T12:56:01.5907120Z [command]/usr/bin/git checkout --progress --force -B master refs/remotes/origin/master
|
||||
2026-07-04T12:56:02.4699566Z Reset branch 'master'
|
||||
2026-07-04T12:56:02.4847933Z branch 'master' set up to track 'origin/master'.
|
||||
2026-07-04T12:56:02.4849782Z ::endgroup::
|
||||
2026-07-04T12:56:02.4886004Z [command]/usr/bin/git log -1 --format=%H
|
||||
2026-07-04T12:56:02.4952462Z c00d237a4d2716bc22cf06f053a807cd7a45f200
|
||||
2026-07-04T12:56:02.4994673Z ::remove-matcher owner=checkout-git::
|
||||
2026-07-04T12:56:02.5182273Z ::endgroup::
|
||||
2026-07-04T12:56:02.5668549Z ::group::Run Setup .NET
|
||||
2026-07-04T12:56:02.5669115Z with:
|
||||
2026-07-04T12:56:02.5669224Z dotnet-version: 10.0
|
||||
2026-07-04T12:56:03.6548348Z (node:141) [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead.
|
||||
2026-07-04T12:56:03.6548980Z (Use `node --trace-deprecation ...` to show where the warning was created)
|
||||
2026-07-04T12:56:03.6690381Z [command]/run/act/actions/2d637816dd86ec9ff59dad9ec3547bf90b88133b3029538a91ec96ac7f316336/externals/install-dotnet.sh --skip-non-versioned-files --runtime dotnet --channel LTS
|
||||
2026-07-04T12:56:04.3566016Z dotnet-install: Attempting to download using aka.ms link https://builds.dotnet.microsoft.com/dotnet/Runtime/10.0.9/dotnet-runtime-10.0.9-linux-x64.tar.gz
|
||||
2026-07-04T12:56:04.7466768Z dotnet-install: Remote file https://builds.dotnet.microsoft.com/dotnet/Runtime/10.0.9/dotnet-runtime-10.0.9-linux-x64.tar.gz size is 36606251 bytes.
|
||||
2026-07-04T12:56:04.7467352Z dotnet-install: Extracting archive from https://builds.dotnet.microsoft.com/dotnet/Runtime/10.0.9/dotnet-runtime-10.0.9-linux-x64.tar.gz
|
||||
2026-07-04T12:56:06.0871997Z dotnet-install: Downloaded file size is 36606251 bytes.
|
||||
2026-07-04T12:56:06.0872663Z dotnet-install: The remote and local file sizes are equal.
|
||||
2026-07-04T12:56:06.1077477Z dotnet-install: Installed version is 10.0.9
|
||||
2026-07-04T12:56:06.1177302Z dotnet-install: Adding to current process PATH: `/usr/share/dotnet`. Note: This change will be visible only when sourcing script.
|
||||
2026-07-04T12:56:06.1177905Z dotnet-install: Note that the script does not resolve dependencies during installation.
|
||||
2026-07-04T12:56:06.1178042Z dotnet-install: To check the list of dependencies, go to https://learn.microsoft.com/dotnet/core/install, select your operating system and check the "Dependencies" section.
|
||||
2026-07-04T12:56:06.1178186Z dotnet-install: Installation finished successfully.
|
||||
2026-07-04T12:56:06.1212465Z [command]/run/act/actions/2d637816dd86ec9ff59dad9ec3547bf90b88133b3029538a91ec96ac7f316336/externals/install-dotnet.sh --skip-non-versioned-files --channel 10.0
|
||||
2026-07-04T12:56:06.5612026Z dotnet-install: Attempting to download using aka.ms link https://builds.dotnet.microsoft.com/dotnet/Sdk/10.0.301/dotnet-sdk-10.0.301-linux-x64.tar.gz
|
||||
2026-07-04T12:56:07.8777550Z dotnet-install: Remote file https://builds.dotnet.microsoft.com/dotnet/Sdk/10.0.301/dotnet-sdk-10.0.301-linux-x64.tar.gz size is 235086718 bytes.
|
||||
2026-07-04T12:56:07.8778105Z dotnet-install: Extracting archive from https://builds.dotnet.microsoft.com/dotnet/Sdk/10.0.301/dotnet-sdk-10.0.301-linux-x64.tar.gz
|
||||
2026-07-04T12:56:15.9574962Z dotnet-install: Downloaded file size is 235086718 bytes.
|
||||
2026-07-04T12:56:15.9575450Z dotnet-install: The remote and local file sizes are equal.
|
||||
2026-07-04T12:56:16.4216427Z dotnet-install: Installed version is 10.0.301
|
||||
2026-07-04T12:56:16.4608895Z dotnet-install: Adding to current process PATH: `/usr/share/dotnet`. Note: This change will be visible only when sourcing script.
|
||||
2026-07-04T12:56:16.4609480Z dotnet-install: Note that the script does not resolve dependencies during installation.
|
||||
2026-07-04T12:56:16.4609633Z dotnet-install: To check the list of dependencies, go to https://learn.microsoft.com/dotnet/core/install, select your operating system and check the "Dependencies" section.
|
||||
2026-07-04T12:56:16.4609773Z dotnet-install: Installation finished successfully.
|
||||
2026-07-04T12:56:16.4610111Z ##[add-matcher]/run/act/actions/2d637816dd86ec9ff59dad9ec3547bf90b88133b3029538a91ec96ac7f316336/.github/csc.json
|
||||
2026-07-04T12:56:16.4838195Z ::endgroup::
|
||||
2026-07-04T12:56:16.6836471Z ::group::Run dotnet restore src/TaxBaik.sln
|
||||
2026-07-04T12:56:16.6836809Z dotnet restore src/TaxBaik.sln
|
||||
2026-07-04T12:56:16.6836915Z shell: bash --noprofile --norc -e -o pipefail {0}
|
||||
2026-07-04T12:56:16.6837057Z ::endgroup::
|
||||
2026-07-04T12:56:17.1062566Z
|
||||
2026-07-04T12:56:17.1063187Z Welcome to .NET 10.0!
|
||||
2026-07-04T12:56:17.1063335Z ---------------------
|
||||
2026-07-04T12:56:17.1063423Z SDK Version: 10.0.301
|
||||
2026-07-04T12:56:17.1063509Z
|
||||
2026-07-04T12:56:17.1063586Z Telemetry
|
||||
2026-07-04T12:56:17.1063723Z ---------
|
||||
2026-07-04T12:56:17.1063814Z The .NET tools collect usage data in order to help us improve your experience. It is collected by Microsoft and shared with the community. You can opt-out of telemetry by setting the DOTNET_CLI_TELEMETRY_OPTOUT environment variable to '1' or 'true' using your favorite shell.
|
||||
2026-07-04T12:56:17.1063988Z
|
||||
2026-07-04T12:56:17.1064082Z Read more about .NET CLI Tools telemetry: https://aka.ms/dotnet-cli-telemetry
|
||||
2026-07-04T12:56:17.7956740Z
|
||||
2026-07-04T12:56:17.7957774Z ----------------
|
||||
2026-07-04T12:56:17.7958124Z Installed an ASP.NET Core HTTPS development certificate.
|
||||
2026-07-04T12:56:17.7958509Z To trust the certificate, run 'dotnet dev-certs https --trust'
|
||||
2026-07-04T12:56:17.7958698Z Learn about HTTPS: https://aka.ms/dotnet-https
|
||||
2026-07-04T12:56:17.7958830Z
|
||||
2026-07-04T12:56:17.7959148Z ----------------
|
||||
2026-07-04T12:56:17.7959321Z Write your first app: https://aka.ms/dotnet-hello-world
|
||||
2026-07-04T12:56:17.7959481Z Find out what's new: https://aka.ms/dotnet-whats-new
|
||||
2026-07-04T12:56:17.7959628Z Explore documentation: https://aka.ms/dotnet-docs
|
||||
2026-07-04T12:56:17.7959926Z Report issues and find source on GitHub: https://github.com/dotnet/core
|
||||
2026-07-04T12:56:17.7960053Z Use 'dotnet --help' to see available commands or visit: https://aka.ms/dotnet-cli
|
||||
2026-07-04T12:56:17.7962047Z --------------------------------------------------------------------------------------
|
||||
2026-07-04T12:56:20.9087685Z Determining projects to restore...
|
||||
2026-07-04T12:56:30.8926957Z Restored /workspace/***/taxbaik/src/TaxBaik.Infrastructure/TaxBaik.Infrastructure.csproj (in 6.9 sec).
|
||||
2026-07-04T12:56:30.9025803Z Restored /workspace/***/taxbaik/src/TaxBaik.Web/TaxBaik.Web.csproj (in 6.89 sec).
|
||||
2026-07-04T12:56:30.9698454Z Restored /workspace/***/taxbaik/src/TaxBaik.Domain/TaxBaik.Domain.csproj (in 2 ms).
|
||||
2026-07-04T12:56:31.4466553Z Restored /workspace/***/taxbaik/src/TaxBaik.Application/TaxBaik.Application.csproj (in 444 ms).
|
||||
2026-07-04T12:56:37.8960870Z Restored /workspace/***/taxbaik/src/TaxBaik.Application.Tests/TaxBaik.Application.Tests.csproj (in 6.43 sec).
|
||||
2026-07-04T12:56:38.1253187Z Restored /workspace/***/taxbaik/src/TaxBaik.Web.Client/TaxBaik.Web.Client.csproj (in 7.21 sec).
|
||||
2026-07-04T12:56:38.3047389Z ::group::Run dotnet build src/TaxBaik.sln -c Release --no-restore -p:ContinuousIntegrationBuild=true
|
||||
2026-07-04T12:56:38.3047747Z dotnet build src/TaxBaik.sln -c Release --no-restore -p:ContinuousIntegrationBuild=true
|
||||
2026-07-04T12:56:38.3047878Z shell: bash --noprofile --norc -e -o pipefail {0}
|
||||
2026-07-04T12:56:38.3047984Z ::endgroup::
|
||||
2026-07-04T12:56:47.5523499Z TaxBaik.Domain -> /workspace/***/taxbaik/src/TaxBaik.Domain/bin/Release/net10.0/TaxBaik.Domain.dll
|
||||
2026-07-04T12:56:53.8139393Z TaxBaik.Application -> /workspace/***/taxbaik/src/TaxBaik.Application/bin/Release/net10.0/TaxBaik.Application.dll
|
||||
2026-07-04T12:56:56.3263776Z TaxBaik.Infrastructure -> /workspace/***/taxbaik/src/TaxBaik.Infrastructure/bin/Release/net10.0/TaxBaik.Infrastructure.dll
|
||||
2026-07-04T12:57:41.2956856Z TaxBaik.Web.Client -> /workspace/***/taxbaik/src/TaxBaik.Web.Client/bin/Debug/net10.0/TaxBaik.Web.Client.dll
|
||||
2026-07-04T12:57:41.2957970Z TaxBaik.Web.Client (Blazor output) -> /workspace/***/taxbaik/src/TaxBaik.Web.Client/bin/Debug/net10.0/wwwroot
|
||||
2026-07-04T12:57:54.4727116Z TaxBaik.Web -> /workspace/***/taxbaik/src/TaxBaik.Web/bin/Release/net10.0/TaxBaik.Web.dll
|
||||
2026-07-04T12:57:56.1020240Z TaxBaik.Application.Tests -> /workspace/***/taxbaik/src/TaxBaik.Application.Tests/bin/Release/net10.0/TaxBaik.Application.Tests.dll
|
||||
2026-07-04T12:57:56.1185508Z
|
||||
2026-07-04T12:57:56.1186502Z Build succeeded.
|
||||
2026-07-04T12:57:56.1187397Z 0 Warning(s)
|
||||
2026-07-04T12:57:56.1187565Z 0 Error(s)
|
||||
2026-07-04T12:57:56.1187859Z
|
||||
2026-07-04T12:57:56.1187992Z Time Elapsed 00:01:17.28
|
||||
2026-07-04T12:57:56.3442702Z ::group::Run dotnet test src/TaxBaik.sln -c Release --no-build
|
||||
2026-07-04T12:57:56.3443052Z dotnet test src/TaxBaik.sln -c Release --no-build
|
||||
2026-07-04T12:57:56.3443177Z shell: bash --noprofile --norc -e -o pipefail {0}
|
||||
2026-07-04T12:57:56.3443278Z ::endgroup::
|
||||
2026-07-04T12:58:00.2708385Z Test run for /workspace/***/taxbaik/src/TaxBaik.Application.Tests/bin/Release/net10.0/TaxBaik.Application.Tests.dll (.NETCoreApp,Version=v10.0)
|
||||
2026-07-04T12:58:01.4967596Z A total of 1 test files matched the specified pattern.
|
||||
2026-07-04T12:58:08.9229118Z
|
||||
2026-07-04T12:58:08.9419846Z Passed! - Failed: 0, Passed: 26, Skipped: 0, Total: 26, Duration: 547 ms - TaxBaik.Application.Tests.dll (net10.0)
|
||||
2026-07-04T12:58:09.4829427Z ::group::Run set -e
|
||||
2026-07-04T12:58:09.4829769Z set -e
|
||||
2026-07-04T12:58:09.4829932Z mkdir -p ./publish-logs
|
||||
2026-07-04T12:58:09.4830025Z web_log="./publish-logs/publish-web.log"
|
||||
2026-07-04T12:58:09.4830116Z start=$(date +%s)
|
||||
2026-07-04T12:58:09.4830216Z # Web.Client needs a Release static-web-assets manifest for Web publish.
|
||||
2026-07-04T12:58:09.4830322Z # Build it explicitly so publish can reuse the prepared outputs.
|
||||
2026-07-04T12:58:09.4830419Z dotnet build src/TaxBaik.Web.Client/TaxBaik.Web.Client.csproj -c Release --no-restore -p:ContinuousIntegrationBuild=true
|
||||
2026-07-04T12:58:09.4830524Z # Build the Web host in Release as well so publish has the same inputs
|
||||
2026-07-04T12:58:09.4830643Z # the server uses in production.
|
||||
2026-07-04T12:58:09.4830726Z dotnet build src/TaxBaik.Web/TaxBaik.Web.csproj -c Release --no-restore -p:ContinuousIntegrationBuild=true
|
||||
2026-07-04T12:58:09.4830819Z echo "--- Web.Client Release artifacts ---"
|
||||
2026-07-04T12:58:09.4830932Z ls -la src/TaxBaik.Web.Client/bin/Release/net10.0 || true
|
||||
2026-07-04T12:58:09.4831022Z ls -la src/TaxBaik.Web.Client/obj/Release/net10.0 || true
|
||||
2026-07-04T12:58:09.4831103Z if ! dotnet publish src/TaxBaik.Web/ \
|
||||
2026-07-04T12:58:09.4831188Z -c Release \
|
||||
2026-07-04T12:58:09.4831285Z -o ./publish \
|
||||
2026-07-04T12:58:09.4831373Z --no-restore \
|
||||
2026-07-04T12:58:09.4831449Z -p:SelfContained=false \
|
||||
2026-07-04T12:58:09.4831732Z -p:PublishReadyToRun=false \
|
||||
2026-07-04T12:58:09.4831866Z -p:PerformanceSummary=true \
|
||||
2026-07-04T12:58:09.4831988Z -clp:Summary \
|
||||
2026-07-04T12:58:09.4832113Z -bl:"./publish-logs/publish-web.binlog" >"$web_log" 2>&1; then
|
||||
2026-07-04T12:58:09.4832289Z echo "=== Publish Web failed; tailing log ==="
|
||||
2026-07-04T12:58:09.4832414Z tail -n 120 "$web_log" || true
|
||||
2026-07-04T12:58:09.4832535Z exit 1
|
||||
2026-07-04T12:58:09.4832795Z fi
|
||||
2026-07-04T12:58:09.4832909Z end=$(date +%s)
|
||||
2026-07-04T12:58:09.4833051Z echo "✓ Publish Web elapsed: $((end - start))s"
|
||||
2026-07-04T12:58:09.4833195Z ls -lh ./publish-logs/publish-web.binlog
|
||||
2026-07-04T12:58:09.4833380Z shell: bash --noprofile --norc -e -o pipefail {0}
|
||||
2026-07-04T12:58:09.4833512Z ::endgroup::
|
||||
2026-07-04T12:58:15.0626337Z TaxBaik.Domain -> /workspace/***/taxbaik/src/TaxBaik.Domain/bin/Release/net10.0/TaxBaik.Domain.dll
|
||||
2026-07-04T12:58:16.0646936Z TaxBaik.Application -> /workspace/***/taxbaik/src/TaxBaik.Application/bin/Release/net10.0/TaxBaik.Application.dll
|
||||
2026-07-04T12:58:43.1637915Z TaxBaik.Web.Client -> /workspace/***/taxbaik/src/TaxBaik.Web.Client/bin/Release/net10.0/TaxBaik.Web.Client.dll
|
||||
2026-07-04T12:58:43.1638555Z TaxBaik.Web.Client (Blazor output) -> /workspace/***/taxbaik/src/TaxBaik.Web.Client/bin/Release/net10.0/wwwroot
|
||||
2026-07-04T12:58:43.2125386Z
|
||||
2026-07-04T12:58:43.2137113Z Build succeeded.
|
||||
2026-07-04T12:58:43.2137638Z 0 Warning(s)
|
||||
2026-07-04T12:58:43.2137752Z 0 Error(s)
|
||||
2026-07-04T12:58:43.2137837Z
|
||||
2026-07-04T12:58:43.2137937Z Time Elapsed 00:00:31.72
|
||||
2026-07-04T12:58:46.1821154Z TaxBaik.Domain -> /workspace/***/taxbaik/src/TaxBaik.Domain/bin/Release/net10.0/TaxBaik.Domain.dll
|
||||
2026-07-04T12:58:46.3588108Z TaxBaik.Infrastructure -> /workspace/***/taxbaik/src/TaxBaik.Infrastructure/bin/Release/net10.0/TaxBaik.Infrastructure.dll
|
||||
2026-07-04T12:58:47.0759678Z TaxBaik.Application -> /workspace/***/taxbaik/src/TaxBaik.Application/bin/Release/net10.0/TaxBaik.Application.dll
|
||||
2026-07-04T12:58:51.7209180Z TaxBaik.Web.Client -> /workspace/***/taxbaik/src/TaxBaik.Web.Client/bin/Release/net10.0/TaxBaik.Web.Client.dll
|
||||
2026-07-04T12:58:51.7249991Z TaxBaik.Web.Client (Blazor output) -> /workspace/***/taxbaik/src/TaxBaik.Web.Client/bin/Release/net10.0/wwwroot
|
||||
2026-07-04T12:59:06.8811243Z TaxBaik.Web -> /workspace/***/taxbaik/src/TaxBaik.Web/bin/Release/net10.0/TaxBaik.Web.dll
|
||||
2026-07-04T12:59:06.9584686Z
|
||||
2026-07-04T12:59:06.9595915Z Build succeeded.
|
||||
2026-07-04T12:59:06.9663358Z 0 Warning(s)
|
||||
2026-07-04T12:59:06.9664940Z 0 Error(s)
|
||||
2026-07-04T12:59:06.9667639Z
|
||||
2026-07-04T12:59:06.9727168Z Time Elapsed 00:00:23.15
|
||||
2026-07-04T12:59:07.2928672Z --- Web.Client Release artifacts ---
|
||||
2026-07-04T12:59:07.2995244Z total 42056
|
||||
2026-07-04T12:59:07.2995608Z drwxr-xr-x 3 root root 20480 Jul 4 12:58 .
|
||||
2026-07-04T12:59:07.2995883Z drwxr-xr-x 3 root root 4096 Jul 4 12:58 ..
|
||||
2026-07-04T12:59:07.2996012Z -rwxr--r-- 1 root root 55632 May 20 20:19 Microsoft.AspNetCore.Authorization.dll
|
||||
2026-07-04T12:59:07.2996353Z -rwxr--r-- 1 root root 34128 May 20 20:19 Microsoft.AspNetCore.Components.Authorization.dll
|
||||
2026-07-04T12:59:07.2996466Z -rwxr--r-- 1 root root 47952 May 20 20:19 Microsoft.AspNetCore.Components.Forms.dll
|
||||
2026-07-04T12:59:07.2996556Z -rwxr--r-- 1 root root 189264 May 20 20:19 Microsoft.AspNetCore.Components.Web.dll
|
||||
2026-07-04T12:59:07.2996643Z -rwxr--r-- 1 root root 166736 May 20 20:20 Microsoft.AspNetCore.Components.WebAssembly.dll
|
||||
2026-07-04T12:59:07.2996769Z -rwxr--r-- 1 root root 399184 May 20 20:19 Microsoft.AspNetCore.Components.dll
|
||||
2026-07-04T12:59:07.2996860Z -rwxr--r-- 1 root root 16208 May 20 20:18 Microsoft.AspNetCore.Metadata.dll
|
||||
2026-07-04T12:59:07.2997106Z -rwxr--r-- 1 root root 19248 Dec 12 2025 Microsoft.Bcl.Cryptography.dll
|
||||
2026-07-04T12:59:07.2997201Z -rwxr--r-- 1 root root 311632 May 20 18:30 Microsoft.CSharp.dll
|
||||
2026-07-04T12:59:07.2997298Z -rwxr--r-- 1 root root 38192 Oct 24 2025 Microsoft.Extensions.Caching.Abstractions.dll
|
||||
2026-07-04T12:59:07.2997390Z -rwxr--r-- 1 root root 28496 May 20 19:29 Microsoft.Extensions.Configuration.Abstractions.dll
|
||||
2026-07-04T12:59:07.2997500Z -rwxr--r-- 1 root root 43344 May 20 19:32 Microsoft.Extensions.Configuration.Binder.dll
|
||||
2026-07-04T12:59:07.2997593Z -rwxr--r-- 1 root root 28496 May 20 19:34 Microsoft.Extensions.Configuration.FileExtensions.dll
|
||||
2026-07-04T12:59:07.2997681Z -rwxr--r-- 1 root root 27984 May 20 19:36 Microsoft.Extensions.Configuration.Json.dll
|
||||
2026-07-04T12:59:07.2997903Z -rwxr--r-- 1 root root 44880 May 20 19:29 Microsoft.Extensions.Configuration.dll
|
||||
2026-07-04T12:59:07.2997993Z -rwxr--r-- 1 root root 65872 May 20 19:29 Microsoft.Extensions.DependencyInjection.Abstractions.dll
|
||||
2026-07-04T12:59:07.2998083Z -rwxr--r-- 1 root root 95568 May 20 19:30 Microsoft.Extensions.DependencyInjection.dll
|
||||
2026-07-04T12:59:07.2998186Z -rwxr--r-- 1 root root 31056 May 20 19:32 Microsoft.Extensions.Diagnostics.Abstractions.dll
|
||||
2026-07-04T12:59:07.2998272Z -rwxr--r-- 1 root root 36176 May 20 19:34 Microsoft.Extensions.Diagnostics.dll
|
||||
2026-07-04T12:59:07.2998461Z -rwxr--r-- 1 root root 23376 May 20 19:29 Microsoft.Extensions.FileProviders.Abstractions.dll
|
||||
2026-07-04T12:59:07.2998731Z -rwxr--r-- 1 root root 45392 May 20 19:33 Microsoft.Extensions.FileProviders.Physical.dll
|
||||
2026-07-04T12:59:07.2998823Z -rwxr--r-- 1 root root 47952 May 20 19:30 Microsoft.Extensions.FileSystemGlobbing.dll
|
||||
2026-07-04T12:59:07.2998932Z -rwxr--r-- 1 root root 93008 May 20 19:37 Microsoft.Extensions.Http.dll
|
||||
2026-07-04T12:59:07.2999016Z -rwxr--r-- 1 root root 19576 Mar 25 2023 Microsoft.Extensions.Localization.Abstractions.dll
|
||||
2026-07-04T12:59:07.2999099Z -rwxr--r-- 1 root root 31872 Mar 25 2023 Microsoft.Extensions.Localization.dll
|
||||
2026-07-04T12:59:07.2999184Z -rwxr--r-- 1 root root 66896 May 20 19:29 Microsoft.Extensions.Logging.Abstractions.dll
|
||||
2026-07-04T12:59:07.2999292Z -rwxr--r-- 1 root root 51536 May 20 19:33 Microsoft.Extensions.Logging.dll
|
||||
2026-07-04T12:59:07.2999529Z -rwxr--r-- 1 root root 21840 May 20 19:33 Microsoft.Extensions.Options.ConfigurationExtensions.dll
|
||||
2026-07-04T12:59:07.2999620Z -rwxr--r-- 1 root root 65360 May 20 19:30 Microsoft.Extensions.Options.dll
|
||||
2026-07-04T12:59:07.2999737Z -rwxr--r-- 1 root root 44880 May 20 19:29 Microsoft.Extensions.Primitives.dll
|
||||
2026-07-04T12:59:07.2999839Z -rwxr--r-- 1 root root 43344 May 20 20:19 Microsoft.Extensions.Validation.dll
|
||||
2026-07-04T12:59:07.2999920Z -rwxr--r-- 1 root root 19296 Jun 2 20:51 Microsoft.IdentityModel.Abstractions.dll
|
||||
2026-07-04T12:59:07.3000007Z -rwxr--r-- 1 root root 172856 Jun 2 20:52 Microsoft.IdentityModel.JsonWebTokens.dll
|
||||
2026-07-04T12:59:07.3000449Z -rwxr--r-- 1 root root 38200 Jun 2 20:51 Microsoft.IdentityModel.Logging.dll
|
||||
2026-07-04T12:59:07.3000602Z -rwxr--r-- 1 root root 407352 Jun 2 20:51 Microsoft.IdentityModel.Tokens.dll
|
||||
2026-07-04T12:59:07.3000692Z -rwxr--r-- 1 root root 24912 May 20 20:20 Microsoft.JSInterop.WebAssembly.dll
|
||||
2026-07-04T12:59:07.3000807Z -rwxr--r-- 1 root root 75088 May 20 20:19 Microsoft.JSInterop.dll
|
||||
2026-07-04T12:59:07.3000896Z -rwxr--r-- 1 root root 428880 May 20 18:30 Microsoft.VisualBasic.Core.dll
|
||||
2026-07-04T12:59:07.3000979Z -rwxr--r-- 1 root root 17232 May 20 18:31 Microsoft.VisualBasic.dll
|
||||
2026-07-04T12:59:07.3001230Z -rwxr--r-- 1 root root 15696 May 20 18:29 Microsoft.Win32.Primitives.dll
|
||||
2026-07-04T12:59:07.3001364Z -rwxr--r-- 1 root root 33104 May 20 18:29 Microsoft.Win32.Registry.dll
|
||||
2026-07-04T12:59:07.3001452Z -rwxr--r-- 1 root root 9108480 Sep 14 2023 MudBlazor.dll
|
||||
2026-07-04T12:59:07.3001536Z -rwxr--r-- 1 root root 15184 May 20 18:31 System.AppContext.dll
|
||||
2026-07-04T12:59:07.3001639Z -rwxr--r-- 1 root root 15184 May 20 18:30 System.Buffers.dll
|
||||
2026-07-04T12:59:07.3001876Z -rwxr--r-- 1 root root 88912 May 20 18:29 System.Collections.Concurrent.dll
|
||||
2026-07-04T12:59:07.3002141Z -rwxr--r-- 1 root root 251216 May 20 18:29 System.Collections.Immutable.dll
|
||||
2026-07-04T12:59:07.3002230Z -rwxr--r-- 1 root root 47952 May 20 18:29 System.Collections.NonGeneric.dll
|
||||
2026-07-04T12:59:07.3002356Z -rwxr--r-- 1 root root 47952 May 20 18:29 System.Collections.Specialized.dll
|
||||
2026-07-04T12:59:07.3011962Z -rwxr--r-- 1 root root 112976 May 20 18:28 System.Collections.dll
|
||||
2026-07-04T12:59:07.3012179Z -rwxr--r-- 1 root root 102736 May 20 18:31 System.ComponentModel.Annotations.dll
|
||||
2026-07-04T12:59:07.3012338Z -rwxr--r-- 1 root root 16720 May 20 18:31 System.ComponentModel.DataAnnotations.dll
|
||||
2026-07-04T12:59:07.3012446Z -rwxr--r-- 1 root root 26448 May 20 18:29 System.ComponentModel.EventBasedAsync.dll
|
||||
2026-07-04T12:59:07.3012536Z -rwxr--r-- 1 root root 42320 May 20 18:29 System.ComponentModel.Primitives.dll
|
||||
2026-07-04T12:59:07.3012797Z -rwxr--r-- 1 root root 316752 May 20 18:31 System.ComponentModel.TypeConverter.dll
|
||||
2026-07-04T12:59:07.3012938Z -rwxr--r-- 1 root root 16208 May 20 18:29 System.ComponentModel.dll
|
||||
2026-07-04T12:59:07.3013028Z -rwxr--r-- 1 root root 19280 May 20 18:31 System.Configuration.dll
|
||||
2026-07-04T12:59:07.3013114Z -rwxr--r-- 1 root root 54096 May 20 18:31 System.Console.dll
|
||||
2026-07-04T12:59:07.3013209Z -rwxr--r-- 1 root root 23376 May 20 18:31 System.Core.dll
|
||||
2026-07-04T12:59:07.3013292Z -rwxr--r-- 1 root root 1018192 May 20 18:31 System.Data.Common.dll
|
||||
2026-07-04T12:59:07.3013385Z -rwxr--r-- 1 root root 15696 May 20 18:31 System.Data.DataSetExtensions.dll
|
||||
2026-07-04T12:59:07.3013632Z -rwxr--r-- 1 root root 25424 May 20 18:31 System.Data.dll
|
||||
2026-07-04T12:59:07.3013742Z -rwxr--r-- 1 root root 16208 May 20 18:31 System.Diagnostics.Contracts.dll
|
||||
2026-07-04T12:59:07.3013844Z -rwxr--r-- 1 root root 15696 May 20 18:30 System.Diagnostics.Debug.dll
|
||||
2026-07-04T12:59:07.3013934Z -rwxr--r-- 1 root root 202576 May 20 18:29 System.Diagnostics.DiagnosticSource.dll
|
||||
2026-07-04T12:59:07.3014028Z -rwxr--r-- 1 root root 22864 May 20 18:29 System.Diagnostics.FileVersionInfo.dll
|
||||
2026-07-04T12:59:07.3014112Z -rwxr--r-- 1 root root 56656 May 20 18:29 System.Diagnostics.Process.dll
|
||||
2026-07-04T12:59:07.3014195Z -rwxr--r-- 1 root root 25936 May 20 18:29 System.Diagnostics.StackTrace.dll
|
||||
2026-07-04T12:59:07.3014435Z -rwxr--r-- 1 root root 31568 May 20 18:31 System.Diagnostics.TextWriterTraceListener.dll
|
||||
2026-07-04T12:59:07.3014564Z -rwxr--r-- 1 root root 15184 May 20 18:30 System.Diagnostics.Tools.dll
|
||||
2026-07-04T12:59:07.3014656Z -rwxr--r-- 1 root root 58704 May 20 18:29 System.Diagnostics.TraceSource.dll
|
||||
2026-07-04T12:59:07.3014763Z -rwxr--r-- 1 root root 16208 May 20 18:28 System.Diagnostics.Tracing.dll
|
||||
2026-07-04T12:59:07.3014864Z -rwxr--r-- 1 root root 64848 May 20 18:29 System.Drawing.Primitives.dll
|
||||
2026-07-04T12:59:07.3014949Z -rwxr--r-- 1 root root 20304 May 20 18:31 System.Drawing.dll
|
||||
2026-07-04T12:59:07.3015044Z -rwxr--r-- 1 root root 16208 May 20 18:30 System.Dynamic.Runtime.dll
|
||||
2026-07-04T12:59:07.3015280Z -rwxr--r-- 1 root root 97104 May 20 18:29 System.Formats.Asn1.dll
|
||||
2026-07-04T12:59:07.3015397Z -rwxr--r-- 1 root root 38736 May 20 18:29 System.Formats.Tar.dll
|
||||
2026-07-04T12:59:07.3015481Z -rwxr--r-- 1 root root 15696 May 20 18:30 System.Globalization.Calendars.dll
|
||||
2026-07-04T12:59:07.3015567Z -rwxr--r-- 1 root root 15184 May 20 18:30 System.Globalization.Extensions.dll
|
||||
2026-07-04T12:59:07.3015663Z -rwxr--r-- 1 root root 15696 May 20 18:30 System.Globalization.dll
|
||||
2026-07-04T12:59:07.3015747Z -rwxr--r-- 1 root root 28496 May 20 18:29 System.IO.Compression.Brotli.dll
|
||||
2026-07-04T12:59:07.3015831Z -rwxr--r-- 1 root root 15184 May 20 18:31 System.IO.Compression.FileSystem.dll
|
||||
2026-07-04T12:59:07.3016040Z -rwxr--r-- 1 root root 53584 May 20 18:29 System.IO.Compression.ZipFile.dll
|
||||
2026-07-04T12:59:07.3016293Z -rwxr--r-- 1 root root 167760 May 20 18:31 System.IO.Compression.dll
|
||||
2026-07-04T12:59:07.3016382Z -rwxr--r-- 1 root root 32080 May 20 18:29 System.IO.FileSystem.AccessControl.dll
|
||||
2026-07-04T12:59:07.3016467Z -rwxr--r-- 1 root root 23888 May 20 18:29 System.IO.FileSystem.DriveInfo.dll
|
||||
2026-07-04T12:59:07.3016562Z -rwxr--r-- 1 root root 15184 May 20 18:30 System.IO.FileSystem.Primitives.dll
|
||||
2026-07-04T12:59:07.3016652Z -rwxr--r-- 1 root root 33104 May 20 18:29 System.IO.FileSystem.Watcher.dll
|
||||
2026-07-04T12:59:07.3016895Z -rwxr--r-- 1 root root 15696 May 20 18:30 System.IO.FileSystem.dll
|
||||
2026-07-04T12:59:07.3016989Z -rwxr--r-- 1 root root 35152 May 20 18:30 System.IO.IsolatedStorage.dll
|
||||
2026-07-04T12:59:07.3017098Z -rwxr--r-- 1 root root 50000 May 20 18:31 System.IO.MemoryMappedFiles.dll
|
||||
2026-07-04T12:59:07.3017197Z -rwxr--r-- 1 root root 78160 May 20 18:29 System.IO.Pipelines.dll
|
||||
2026-07-04T12:59:07.3017283Z -rwxr--r-- 1 root root 23376 May 20 18:29 System.IO.Pipes.AccessControl.dll
|
||||
2026-07-04T12:59:07.3017385Z -rwxr--r-- 1 root root 42320 May 20 18:29 System.IO.Pipes.dll
|
||||
2026-07-04T12:59:07.3017475Z -rwxr--r-- 1 root root 15696 May 20 18:30 System.IO.UnmanagedMemoryStream.dll
|
||||
2026-07-04T12:59:07.3017708Z -rwxr--r-- 1 root root 15696 May 20 18:30 System.IO.dll
|
||||
2026-07-04T12:59:07.3017795Z -rwxr--r-- 1 root root 92000 Jun 2 20:51 System.IdentityModel.Tokens.Jwt.dll
|
||||
2026-07-04T12:59:07.3017911Z -rwxr--r-- 1 root root 456528 May 20 18:29 System.Linq.AsyncEnumerable.dll
|
||||
2026-07-04T12:59:07.3018007Z -rwxr--r-- 1 root root 575312 May 20 18:29 System.Linq.Expressions.dll
|
||||
2026-07-04T12:59:07.3018088Z -rwxr--r-- 1 root root 223056 May 20 18:31 System.Linq.Parallel.dll
|
||||
2026-07-04T12:59:07.3018173Z -rwxr--r-- 1 root root 78672 May 20 18:31 System.Linq.Queryable.dll
|
||||
2026-07-04T12:59:07.3018253Z -rwxr--r-- 1 root root 201040 May 20 18:29 System.Linq.dll
|
||||
2026-07-04T12:59:07.3018473Z -rwxr--r-- 1 root root 55632 May 20 18:28 System.Memory.dll
|
||||
2026-07-04T12:59:07.3018562Z -rwxr--r-- 1 root root 56144 May 20 18:30 System.Net.Http.Json.dll
|
||||
2026-07-04T12:59:07.3018718Z -rwxr--r-- 1 root root 296272 May 20 18:31 System.Net.Http.dll
|
||||
2026-07-04T12:59:07.3018879Z -rwxr--r-- 1 root root 56144 May 20 18:30 System.Net.HttpListener.dll
|
||||
2026-07-04T12:59:07.3019013Z -rwxr--r-- 1 root root 105296 May 20 18:31 System.Net.Mail.dll
|
||||
2026-07-04T12:59:07.3019285Z -rwxr--r-- 1 root root 24400 May 20 18:31 System.Net.NameResolution.dll
|
||||
2026-07-04T12:59:07.3019384Z -rwxr--r-- 1 root root 42320 May 20 18:29 System.Net.NetworkInformation.dll
|
||||
2026-07-04T12:59:07.3019468Z -rwxr--r-- 1 root root 27984 May 20 18:29 System.Net.Ping.dll
|
||||
2026-07-04T12:59:07.3019549Z -rwxr--r-- 1 root root 107344 May 20 18:31 System.Net.Primitives.dll
|
||||
2026-07-04T12:59:07.3019646Z -rwxr--r-- 1 root root 39248 May 20 18:30 System.Net.Quic.dll
|
||||
2026-07-04T12:59:07.3019725Z -rwxr--r-- 1 root root 65872 May 20 18:30 System.Net.Requests.dll
|
||||
2026-07-04T12:59:07.3019808Z -rwxr--r-- 1 root root 114512 May 20 18:30 System.Net.Security.dll
|
||||
2026-07-04T12:59:07.3019892Z -rwxr--r-- 1 root root 40784 May 20 18:29 System.Net.ServerSentEvents.dll
|
||||
2026-07-04T12:59:07.3020121Z -rwxr--r-- 1 root root 15184 May 20 18:31 System.Net.ServicePoint.dll
|
||||
2026-07-04T12:59:07.3020212Z -rwxr--r-- 1 root root 74576 May 20 18:29 System.Net.Sockets.dll
|
||||
2026-07-04T12:59:07.3020295Z -rwxr--r-- 1 root root 56144 May 20 18:31 System.Net.WebClient.dll
|
||||
2026-07-04T12:59:07.3020488Z -rwxr--r-- 1 root root 33104 May 20 18:29 System.Net.WebHeaderCollection.dll
|
||||
2026-07-04T12:59:07.3020573Z -rwxr--r-- 1 root root 21840 May 20 18:31 System.Net.WebProxy.dll
|
||||
2026-07-04T12:59:07.3020657Z -rwxr--r-- 1 root root 52560 May 20 18:31 System.Net.WebSockets.Client.dll
|
||||
2026-07-04T12:59:07.3020744Z -rwxr--r-- 1 root root 108880 May 20 18:31 System.Net.WebSockets.dll
|
||||
2026-07-04T12:59:07.3020970Z -rwxr--r-- 1 root root 17232 May 20 18:31 System.Net.dll
|
||||
2026-07-04T12:59:07.3021057Z -rwxr--r-- 1 root root 15696 May 20 18:28 System.Numerics.Vectors.dll
|
||||
2026-07-04T12:59:07.3021143Z -rwxr--r-- 1 root root 15184 May 20 18:31 System.Numerics.dll
|
||||
2026-07-04T12:59:07.3021232Z -rwxr--r-- 1 root root 41296 May 20 18:29 System.ObjectModel.dll
|
||||
2026-07-04T12:59:07.3021315Z -rwxr--r-- 1 root root 4880208 May 20 18:19 System.Private.CoreLib.dll
|
||||
2026-07-04T12:59:07.3021397Z -rwxr--r-- 1 root root 859472 May 20 18:31 System.Private.DataContractSerialization.dll
|
||||
2026-07-04T12:59:07.3021490Z -rwxr--r-- 1 root root 105808 May 20 18:28 System.Private.Uri.dll
|
||||
2026-07-04T12:59:07.3021829Z -rwxr--r-- 1 root root 153936 May 20 18:30 System.Private.Xml.Linq.dll
|
||||
2026-07-04T12:59:07.3021924Z -rwxr--r-- 1 root root 3106128 May 20 18:30 System.Private.Xml.dll
|
||||
2026-07-04T12:59:07.3022020Z -rwxr--r-- 1 root root 38224 May 20 18:31 System.Reflection.DispatchProxy.dll
|
||||
2026-07-04T12:59:07.3022121Z -rwxr--r-- 1 root root 15696 May 20 18:29 System.Reflection.Emit.ILGeneration.dll
|
||||
2026-07-04T12:59:07.3022214Z -rwxr--r-- 1 root root 15696 May 20 18:29 System.Reflection.Emit.Lightweight.dll
|
||||
2026-07-04T12:59:07.3022300Z -rwxr--r-- 1 root root 133456 May 20 18:29 System.Reflection.Emit.dll
|
||||
2026-07-04T12:59:07.3022534Z -rwxr--r-- 1 root root 15184 May 20 18:30 System.Reflection.Extensions.dll
|
||||
2026-07-04T12:59:07.3022623Z -rwxr--r-- 1 root root 503632 May 20 18:29 System.Reflection.Metadata.dll
|
||||
2026-07-04T12:59:07.3022707Z -rwxr--r-- 1 root root 15696 May 20 18:28 System.Reflection.Primitives.dll
|
||||
2026-07-04T12:59:07.3022794Z -rwxr--r-- 1 root root 24400 May 20 18:31 System.Reflection.TypeExtensions.dll
|
||||
2026-07-04T12:59:07.3022902Z -rwxr--r-- 1 root root 16208 May 20 18:31 System.Reflection.dll
|
||||
2026-07-04T12:59:07.3022986Z -rwxr--r-- 1 root root 15184 May 20 18:31 System.Resources.Reader.dll
|
||||
2026-07-04T12:59:07.3023069Z -rwxr--r-- 1 root root 15696 May 20 18:30 System.Resources.ResourceManager.dll
|
||||
2026-07-04T12:59:07.3023153Z -rwxr--r-- 1 root root 26960 May 20 18:31 System.Resources.Writer.dll
|
||||
2026-07-04T12:59:07.3023377Z -rwxr--r-- 1 root root 15184 May 20 18:30 System.Runtime.CompilerServices.Unsafe.dll
|
||||
2026-07-04T12:59:07.3023472Z -rwxr--r-- 1 root root 17232 May 20 18:30 System.Runtime.CompilerServices.VisualC.dll
|
||||
2026-07-04T12:59:07.3023557Z -rwxr--r-- 1 root root 17744 May 20 18:30 System.Runtime.Extensions.dll
|
||||
2026-07-04T12:59:07.3023656Z -rwxr--r-- 1 root root 15696 May 20 18:30 System.Runtime.Handles.dll
|
||||
2026-07-04T12:59:07.3023754Z -rwxr--r-- 1 root root 89936 May 20 18:31 System.Runtime.InteropServices.JavaScript.dll
|
||||
2026-07-04T12:59:07.3023845Z -rwxr--r-- 1 root root 15184 May 20 18:31 System.Runtime.InteropServices.RuntimeInformation.dll
|
||||
2026-07-04T12:59:07.3023932Z -rwxr--r-- 1 root root 64848 May 20 18:29 System.Runtime.InteropServices.dll
|
||||
2026-07-04T12:59:07.3024221Z -rwxr--r-- 1 root root 17232 May 20 18:29 System.Runtime.Intrinsics.dll
|
||||
2026-07-04T12:59:07.3024393Z -rwxr--r-- 1 root root 15696 May 20 18:29 System.Runtime.Loader.dll
|
||||
2026-07-04T12:59:07.3024484Z -rwxr--r-- 1 root root 145232 May 20 18:29 System.Runtime.Numerics.dll
|
||||
2026-07-04T12:59:07.3024579Z -rwxr--r-- 1 root root 65872 May 20 18:29 System.Runtime.Serialization.Formatters.dll
|
||||
2026-07-04T12:59:07.3024674Z -rwxr--r-- 1 root root 15696 May 20 18:31 System.Runtime.Serialization.Json.dll
|
||||
2026-07-04T12:59:07.3024767Z -rwxr--r-- 1 root root 23376 May 20 18:30 System.Runtime.Serialization.Primitives.dll
|
||||
2026-07-04T12:59:07.3025038Z -rwxr--r-- 1 root root 16720 May 20 18:31 System.Runtime.Serialization.Xml.dll
|
||||
2026-07-04T12:59:07.3025127Z -rwxr--r-- 1 root root 17232 May 20 18:31 System.Runtime.Serialization.dll
|
||||
2026-07-04T12:59:07.3025213Z -rwxr--r-- 1 root root 44880 May 20 18:28 System.Runtime.dll
|
||||
2026-07-04T12:59:07.3025296Z -rwxr--r-- 1 root root 58192 May 20 18:29 System.Security.AccessControl.dll
|
||||
2026-07-04T12:59:07.3025393Z -rwxr--r-- 1 root root 55120 May 20 18:29 System.Security.Claims.dll
|
||||
2026-07-04T12:59:07.3025477Z -rwxr--r-- 1 root root 17232 May 20 18:31 System.Security.Cryptography.Algorithms.dll
|
||||
2026-07-04T12:59:07.3025565Z -rwxr--r-- 1 root root 16208 May 20 18:31 System.Security.Cryptography.Cng.dll
|
||||
2026-07-04T12:59:07.3025922Z -rwxr--r-- 1 root root 16208 May 20 18:31 System.Security.Cryptography.Csp.dll
|
||||
2026-07-04T12:59:07.3026035Z -rwxr--r-- 1 root root 15696 May 20 18:31 System.Security.Cryptography.Encoding.dll
|
||||
2026-07-04T12:59:07.3026508Z -rwxr--r-- 1 root root 15696 May 20 18:31 System.Security.Cryptography.OpenSsl.dll
|
||||
2026-07-04T12:59:07.3026604Z -rwxr--r-- 1 root root 15696 May 20 18:31 System.Security.Cryptography.Primitives.dll
|
||||
2026-07-04T12:59:07.3026700Z -rwxr--r-- 1 root root 16720 May 20 18:31 System.Security.Cryptography.X509Certificates.dll
|
||||
2026-07-04T12:59:07.3026796Z -rwxr--r-- 1 root root 654160 May 20 18:31 System.Security.Cryptography.dll
|
||||
2026-07-04T12:59:07.3026889Z -rwxr--r-- 1 root root 37712 May 20 18:29 System.Security.Principal.Windows.dll
|
||||
2026-07-04T12:59:07.3026988Z -rwxr--r-- 1 root root 15184 May 20 18:31 System.Security.Principal.dll
|
||||
2026-07-04T12:59:07.3027225Z -rwxr--r-- 1 root root 15696 May 20 18:30 System.Security.SecureString.dll
|
||||
2026-07-04T12:59:07.3027312Z -rwxr--r-- 1 root root 18256 May 20 18:31 System.Security.dll
|
||||
2026-07-04T12:59:07.3027399Z -rwxr--r-- 1 root root 16720 May 20 18:31 System.ServiceModel.Web.dll
|
||||
2026-07-04T12:59:07.3027496Z -rwxr--r-- 1 root root 15696 May 20 18:31 System.ServiceProcess.dll
|
||||
2026-07-04T12:59:07.3027578Z -rwxr--r-- 1 root root 742736 May 20 18:29 System.Text.Encoding.CodePages.dll
|
||||
2026-07-04T12:59:07.3027663Z -rwxr--r-- 1 root root 15696 May 20 18:28 System.Text.Encoding.Extensions.dll
|
||||
2026-07-04T12:59:07.3027753Z -rwxr--r-- 1 root root 15696 May 20 18:30 System.Text.Encoding.dll
|
||||
2026-07-04T12:59:07.3027971Z -rwxr--r-- 1 root root 65872 May 20 18:32 System.Text.Encodings.Web.dll
|
||||
2026-07-04T12:59:07.3028064Z -rwxr--r-- 1 root root 649040 May 20 18:30 System.Text.Json.dll
|
||||
2026-07-04T12:59:07.3028143Z -rwxr--r-- 1 root root 384848 May 20 18:30 System.Text.RegularExpressions.dll
|
||||
2026-07-04T12:59:07.3028244Z -rwxr--r-- 1 root root 33616 May 20 18:29 System.Threading.AccessControl.dll
|
||||
2026-07-04T12:59:07.3028326Z -rwxr--r-- 1 root root 66384 May 20 18:29 System.Threading.Channels.dll
|
||||
2026-07-04T12:59:07.3028411Z -rwxr--r-- 1 root root 15696 May 20 18:28 System.Threading.Overlapped.dll
|
||||
2026-07-04T12:59:07.3028493Z -rwxr--r-- 1 root root 185680 May 20 18:29 System.Threading.Tasks.Dataflow.dll
|
||||
2026-07-04T12:59:07.3028587Z -rwxr--r-- 1 root root 15696 May 20 18:30 System.Threading.Tasks.Extensions.dll
|
||||
2026-07-04T12:59:07.3028808Z -rwxr--r-- 1 root root 61264 May 20 18:31 System.Threading.Tasks.Parallel.dll
|
||||
2026-07-04T12:59:07.3028902Z -rwxr--r-- 1 root root 16720 May 20 18:31 System.Threading.Tasks.dll
|
||||
2026-07-04T12:59:07.3028994Z -rwxr--r-- 1 root root 15696 May 20 18:28 System.Threading.Thread.dll
|
||||
2026-07-04T12:59:07.3029076Z -rwxr--r-- 1 root root 15696 May 20 18:28 System.Threading.ThreadPool.dll
|
||||
2026-07-04T12:59:07.3029160Z -rwxr--r-- 1 root root 15184 May 20 18:30 System.Threading.Timer.dll
|
||||
2026-07-04T12:59:07.3029262Z -rwxr--r-- 1 root root 44880 May 20 18:28 System.Threading.dll
|
||||
2026-07-04T12:59:07.3029345Z -rwxr--r-- 1 root root 175952 May 20 18:30 System.Transactions.Local.dll
|
||||
2026-07-04T12:59:07.3029560Z -rwxr--r-- 1 root root 16720 May 20 18:31 System.Transactions.dll
|
||||
2026-07-04T12:59:07.3029651Z -rwxr--r-- 1 root root 15696 May 20 18:30 System.ValueTuple.dll
|
||||
2026-07-04T12:59:07.3029740Z -rwxr--r-- 1 root root 30032 May 20 18:31 System.Web.HttpUtility.dll
|
||||
2026-07-04T12:59:07.3029838Z -rwxr--r-- 1 root root 15184 May 20 18:31 System.Web.dll
|
||||
2026-07-04T12:59:07.3029919Z -rwxr--r-- 1 root root 15696 May 20 18:31 System.Windows.dll
|
||||
2026-07-04T12:59:07.3030002Z -rwxr--r-- 1 root root 15696 May 20 18:31 System.Xml.Linq.dll
|
||||
2026-07-04T12:59:07.3030083Z -rwxr--r-- 1 root root 21840 May 20 18:30 System.Xml.ReaderWriter.dll
|
||||
2026-07-04T12:59:07.3030164Z -rwxr--r-- 1 root root 16208 May 20 18:31 System.Xml.Serialization.dll
|
||||
2026-07-04T12:59:07.3030381Z -rwxr--r-- 1 root root 15696 May 20 18:31 System.Xml.XDocument.dll
|
||||
2026-07-04T12:59:07.3030479Z -rwxr--r-- 1 root root 15696 May 20 18:31 System.Xml.XPath.XDocument.dll
|
||||
2026-07-04T12:59:07.3030561Z -rwxr--r-- 1 root root 15696 May 20 18:30 System.Xml.XPath.dll
|
||||
2026-07-04T12:59:07.3030650Z -rwxr--r-- 1 root root 15696 May 20 18:31 System.Xml.XmlDocument.dll
|
||||
2026-07-04T12:59:07.3030739Z -rwxr--r-- 1 root root 17744 May 20 18:31 System.Xml.XmlSerializer.dll
|
||||
2026-07-04T12:59:07.3030822Z -rwxr--r-- 1 root root 23376 May 20 18:31 System.Xml.dll
|
||||
2026-07-04T12:59:07.3030913Z -rwxr--r-- 1 root root 50000 May 20 18:32 System.dll
|
||||
2026-07-04T12:59:07.3030995Z -rw-r--r-- 1 root root 156672 Jul 4 12:56 TaxBaik.Application.dll
|
||||
2026-07-04T12:59:07.3031231Z -rw-r--r-- 1 root root 38468 Jul 4 12:56 TaxBaik.Application.pdb
|
||||
2026-07-04T12:59:07.3031318Z -rw-r--r-- 1 root root 37888 Jul 4 12:56 TaxBaik.Domain.dll
|
||||
2026-07-04T12:59:07.3031411Z -rw-r--r-- 1 root root 24100 Jul 4 12:56 TaxBaik.Domain.pdb
|
||||
2026-07-04T12:59:07.3031494Z -rw-r--r-- 1 root root 713216 Jul 4 12:58 TaxBaik.Web.Client.dll
|
||||
2026-07-04T12:59:07.3031583Z -rw-r--r-- 1 root root 373136 Jul 4 12:58 TaxBaik.Web.Client.pdb
|
||||
2026-07-04T12:59:07.3031768Z -rw-r--r-- 1 root root 2546 Jul 4 12:58 TaxBaik.Web.Client.runtimeconfig.json
|
||||
2026-07-04T12:59:07.3032037Z -rw-r--r-- 1 root root 1007340 Jul 4 12:58 TaxBaik.Web.Client.staticwebassets.endpoints.json
|
||||
2026-07-04T12:59:07.3032142Z -rw-r--r-- 1 root root 78064 Jul 4 12:58 TaxBaik.Web.Client.staticwebassets.runtime.json
|
||||
2026-07-04T12:59:07.3032230Z -rwxr--r-- 1 root root 16208 May 20 18:31 WindowsBase.dll
|
||||
2026-07-04T12:59:07.3032313Z -rwxr--r-- 1 root root 37898 May 20 18:42 dotnet.js
|
||||
2026-07-04T12:59:07.3032406Z -rwxr--r-- 1 root root 51818 May 20 18:42 dotnet.js.map
|
||||
2026-07-04T12:59:07.3032485Z -rwxr--r-- 1 root root 145050 May 20 18:43 dotnet.native.js
|
||||
2026-07-04T12:59:07.3032565Z -rwxr--r-- 1 root root 3002101 May 20 18:43 dotnet.native.wasm
|
||||
2026-07-04T12:59:07.3032781Z -rwxr--r-- 1 root root 198479 May 20 18:42 dotnet.runtime.js
|
||||
2026-07-04T12:59:07.3032873Z -rwxr--r-- 1 root root 276757 May 20 18:42 dotnet.runtime.js.map
|
||||
2026-07-04T12:59:07.3032955Z -rwxr--r-- 1 root root 956416 Apr 2 19:04 icudt_CJK.dat
|
||||
2026-07-04T12:59:07.3033032Z -rwxr--r-- 1 root root 550832 Apr 2 19:04 icudt_EFIGS.dat
|
||||
2026-07-04T12:59:07.3033111Z -rwxr--r-- 1 root root 1107168 Apr 2 19:04 icudt_no_CJK.dat
|
||||
2026-07-04T12:59:07.3033190Z -rwxr--r-- 1 root root 59728 May 20 18:31 mscorlib.dll
|
||||
2026-07-04T12:59:07.3033269Z -rwxr--r-- 1 root root 100688 May 20 18:32 netstandard.dll
|
||||
2026-07-04T12:59:07.3033348Z drwxr-xr-x 3 root root 4096 Jul 4 12:58 wwwroot
|
||||
2026-07-04T12:59:07.3071176Z total 4352
|
||||
2026-07-04T12:59:07.3071587Z drwxr-xr-x 8 root root 4096 Jul 4 12:58 .
|
||||
2026-07-04T12:59:07.3071872Z drwxr-xr-x 3 root root 4096 Jul 4 12:58 ..
|
||||
2026-07-04T12:59:07.3071979Z -rw-r--r-- 1 root root 196 Jul 4 12:58 .NETCoreApp,Version=v10.0.AssemblyAttributes.cs
|
||||
2026-07-04T12:59:07.3072092Z -rw-r--r-- 1 root root 137 Jul 4 12:58 EmbeddedAttribute.cs
|
||||
2026-07-04T12:59:07.3072373Z -rw-r--r-- 1 root root 0 Jul 4 12:58 TaxBaik..C36EE7CA.Up2Date
|
||||
2026-07-04T12:59:07.3072471Z -rw-r--r-- 1 root root 1008 Jul 4 12:58 TaxBaik.Web.Client.AssemblyInfo.cs
|
||||
2026-07-04T12:59:07.3072582Z -rw-r--r-- 1 root root 65 Jul 4 12:58 TaxBaik.Web.Client.AssemblyInfoInputs.cache
|
||||
2026-07-04T12:59:07.3072703Z -rw-r--r-- 1 root root 16355 Jul 4 12:58 TaxBaik.Web.Client.GeneratedMSBuildEditorConfig.editorconfig
|
||||
2026-07-04T12:59:07.3072810Z -rw-r--r-- 1 root root 433 Jul 4 12:58 TaxBaik.Web.Client.GlobalUsings.g.cs
|
||||
2026-07-04T12:59:07.3072898Z -rw-r--r-- 1 root root 0 Jul 4 12:58 TaxBaik.Web.Client.MvcApplicationPartsAssemblyInfo.cache
|
||||
2026-07-04T12:59:07.3073143Z -rw-r--r-- 1 root root 27329 Jul 4 12:58 TaxBaik.Web.Client.assets.cache
|
||||
2026-07-04T12:59:07.3073238Z -rw-r--r-- 1 root root 20456 Jul 4 12:58 TaxBaik.Web.Client.csproj.AssemblyReference.cache
|
||||
2026-07-04T12:59:07.3073326Z -rw-r--r-- 1 root root 65 Jul 4 12:58 TaxBaik.Web.Client.csproj.CoreCompileInputs.cache
|
||||
2026-07-04T12:59:07.3073433Z -rw-r--r-- 1 root root 136894 Jul 4 12:58 TaxBaik.Web.Client.csproj.FileListAbsolute.txt
|
||||
2026-07-04T12:59:07.3073537Z -rw-r--r-- 1 root root 713216 Jul 4 12:58 TaxBaik.Web.Client.dll
|
||||
2026-07-04T12:59:07.3073640Z -rw-r--r-- 1 root root 65 Jul 4 12:58 TaxBaik.Web.Client.genruntimeconfig.cache
|
||||
2026-07-04T12:59:07.3073730Z -rw-r--r-- 1 root root 373136 Jul 4 12:58 TaxBaik.Web.Client.pdb
|
||||
2026-07-04T12:59:07.3073973Z -rw-r--r-- 1 root root 276 Jul 4 12:58 ValidatableTypeAttribute.cs
|
||||
2026-07-04T12:59:07.3074072Z -rw-r--r-- 1 root root 3 Jul 4 12:58 blazor.build.boot-extension.json
|
||||
2026-07-04T12:59:07.3074158Z drwxr-xr-x 2 root root 20480 Jul 4 12:58 compressed
|
||||
2026-07-04T12:59:07.3074258Z -rw-r--r-- 1 root root 93392 Jul 4 12:58 dotnet.js
|
||||
2026-07-04T12:59:07.3074339Z -rw-r--r-- 1 root root 276577 Jul 4 12:58 rbcswa.dswa.cache.json
|
||||
2026-07-04T12:59:07.3074420Z drwxr-xr-x 2 root root 4096 Jul 4 12:58 ref
|
||||
2026-07-04T12:59:07.3074500Z drwxr-xr-x 2 root root 4096 Jul 4 12:58 refint
|
||||
2026-07-04T12:59:07.3074734Z -rw-r--r-- 1 root root 327 Jul 4 12:58 rjimswa.dswa.cache.json
|
||||
2026-07-04T12:59:07.3074829Z -rw-r--r-- 1 root root 3434 Jul 4 12:58 rjsmcshtml.dswa.cache.json
|
||||
2026-07-04T12:59:07.3074912Z -rw-r--r-- 1 root root 3434 Jul 4 12:58 rjsmrazor.dswa.cache.json
|
||||
2026-07-04T12:59:07.3074999Z -rw-r--r-- 1 root root 4208 Jul 4 12:58 rpswa.dswa.cache.json
|
||||
2026-07-04T12:59:07.3075082Z drwxr-xr-x 2 root root 4096 Jul 4 12:58 staticwebassets
|
||||
2026-07-04T12:59:07.3075161Z -rw-r--r-- 1 root root 1007340 Jul 4 12:58 staticwebassets.build.endpoints.json
|
||||
2026-07-04T12:59:07.3075247Z -rw-r--r-- 1 root root 1564906 Jul 4 12:58 staticwebassets.build.json
|
||||
2026-07-04T12:59:07.3075331Z -rw-r--r-- 1 root root 44 Jul 4 12:58 staticwebassets.build.json.cache
|
||||
2026-07-04T12:59:07.3075530Z -rw-r--r-- 1 root root 78064 Jul 4 12:58 staticwebassets.development.json
|
||||
2026-07-04T12:59:07.3075619Z -rw-r--r-- 1 root root 0 Jul 4 12:58 swae.build.ex.cache
|
||||
2026-07-04T12:59:07.3075707Z drwxr-xr-x 2 root root 4096 Jul 4 12:58 tmp-webcil
|
||||
2026-07-04T12:59:07.3075785Z drwxr-xr-x 2 root root 20480 Jul 4 12:58 webcil
|
||||
2026-07-04T13:01:22.5627758Z ✓ Publish Web elapsed: 193s
|
||||
2026-07-04T13:01:22.5646529Z -rw-r--r-- 1 root root 2.2M Jul 4 13:01 ./publish-logs/publish-web.binlog
|
||||
2026-07-04T13:01:22.6880698Z ::group::Run set -e
|
||||
2026-07-04T13:01:22.6881027Z set -e
|
||||
2026-07-04T13:01:22.6881197Z mkdir -p ./publish-logs
|
||||
2026-07-04T13:01:22.6881283Z # Proxy is not part of the solution restore graph, so restore it once
|
||||
2026-07-04T13:01:22.6881372Z # here before publishing to avoid NETSDK1004 in CI.
|
||||
2026-07-04T13:01:22.6881469Z dotnet restore src/TaxBaik.Proxy/
|
||||
2026-07-04T13:01:22.6881543Z dotnet build src/TaxBaik.Proxy/TaxBaik.Proxy.csproj -c Release --no-restore
|
||||
2026-07-04T13:01:22.6881631Z start=$(date +%s)
|
||||
2026-07-04T13:01:22.6881698Z dotnet publish src/TaxBaik.Proxy/ \
|
||||
2026-07-04T13:01:22.6881795Z -c Release \
|
||||
2026-07-04T13:01:22.6882017Z -o ./publish/proxy \
|
||||
2026-07-04T13:01:22.6882108Z --no-restore \
|
||||
2026-07-04T13:01:22.6882193Z --no-build \
|
||||
2026-07-04T13:01:22.6882265Z -p:PublishReadyToRun=false \
|
||||
2026-07-04T13:01:22.6882334Z -p:PerformanceSummary=true \
|
||||
2026-07-04T13:01:22.6882404Z -clp:Summary \
|
||||
2026-07-04T13:01:22.6882495Z -bl:./publish-logs/publish-proxy.binlog
|
||||
2026-07-04T13:01:22.6882564Z end=$(date +%s)
|
||||
2026-07-04T13:01:22.6882635Z echo "✓ Publish Proxy elapsed: $((end - start))s"
|
||||
2026-07-04T13:01:22.6882738Z ls -lh ./publish-logs/publish-proxy.binlog
|
||||
2026-07-04T13:01:22.6882814Z shell: bash --noprofile --norc -e -o pipefail {0}
|
||||
2026-07-04T13:01:22.6882907Z ::endgroup::
|
||||
2026-07-04T13:01:24.3061500Z Determining projects to restore...
|
||||
2026-07-04T13:01:24.9616018Z Restored /workspace/***/taxbaik/src/TaxBaik.Proxy/TaxBaik.Proxy.csproj (in 156 ms).
|
||||
2026-07-04T13:01:26.9924481Z TaxBaik.Proxy -> /workspace/***/taxbaik/src/TaxBaik.Proxy/bin/Release/net10.0/TaxBaik.Proxy.dll
|
||||
2026-07-04T13:01:27.0997744Z
|
||||
2026-07-04T13:01:27.1028989Z Build succeeded.
|
||||
2026-07-04T13:01:27.1047131Z 0 Warning(s)
|
||||
2026-07-04T13:01:27.1047398Z 0 Error(s)
|
||||
2026-07-04T13:01:27.1047541Z
|
||||
2026-07-04T13:01:27.1047621Z Time Elapsed 00:00:01.55
|
||||
2026-07-04T13:01:28.9796929Z TaxBaik.Proxy -> /workspace/***/taxbaik/publish/proxy/
|
||||
2026-07-04T13:01:29.0117388Z
|
||||
2026-07-04T13:01:29.0137929Z Build succeeded.
|
||||
2026-07-04T13:01:29.0142336Z 0 Warning(s)
|
||||
2026-07-04T13:01:29.0144041Z 0 Error(s)
|
||||
2026-07-04T13:01:29.0147048Z
|
||||
2026-07-04T13:01:29.0149487Z Time Elapsed 00:00:01.04
|
||||
2026-07-04T13:01:29.0760476Z ✓ Publish Proxy elapsed: 2s
|
||||
2026-07-04T13:01:29.0761097Z -rw-r--r-- 1 root root 335K Jul 4 13:01 ./publish-logs/publish-proxy.binlog
|
||||
2026-07-04T13:01:29.2472841Z ::group::Run set -e
|
||||
2026-07-04T13:01:29.2473270Z set -e
|
||||
2026-07-04T13:01:29.2473414Z JWT_SECRET_KEY="***"
|
||||
2026-07-04T13:01:29.2473519Z TELEGRAM_BOT_TOKEN="***"
|
||||
2026-07-04T13:01:29.2473612Z TELEGRAM_CHAT_ID="***"
|
||||
2026-07-04T13:01:29.2473691Z TELEGRAM_INQUIRY_CHAT_ID=""
|
||||
2026-07-04T13:01:29.2473786Z TELEGRAM_SYSTEM_CHAT_ID=""
|
||||
2026-07-04T13:01:29.2473869Z [ -z "$JWT_SECRET_KEY" ] && { echo "Missing TAXBAIK_JWT_SECRET_KEY" >&2; exit 1; }
|
||||
2026-07-04T13:01:29.2473974Z [ -z "$TELEGRAM_BOT_TOKEN" ] && { echo "Missing TAXBAIK_TELEGRAM_BOT_TOKEN" >&2; exit 1; }
|
||||
2026-07-04T13:01:29.2474063Z [ -z "$TELEGRAM_CHAT_ID" ] && { echo "Missing TAXBAIK_TELEGRAM_CHAT_ID" >&2; exit 1; }
|
||||
2026-07-04T13:01:29.2474147Z [ -z "$TELEGRAM_INQUIRY_CHAT_ID" ] && TELEGRAM_INQUIRY_CHAT_ID="$TELEGRAM_CHAT_ID"
|
||||
2026-07-04T13:01:29.2474240Z [ -z "$TELEGRAM_SYSTEM_CHAT_ID" ] && TELEGRAM_SYSTEM_CHAT_ID="-5585148480"
|
||||
2026-07-04T13:01:29.2474325Z JWT_SECRET_KEY="$JWT_SECRET_KEY" \
|
||||
2026-07-04T13:01:29.2474401Z TELEGRAM_BOT_TOKEN="$TELEGRAM_BOT_TOKEN" \
|
||||
2026-07-04T13:01:29.2474541Z TELEGRAM_CHAT_ID="$TELEGRAM_CHAT_ID" \
|
||||
2026-07-04T13:01:29.2474616Z TELEGRAM_INQUIRY_CHAT_ID="$TELEGRAM_INQUIRY_CHAT_ID" \
|
||||
2026-07-04T13:01:29.2474692Z TELEGRAM_SYSTEM_CHAT_ID="$TELEGRAM_SYSTEM_CHAT_ID" \
|
||||
2026-07-04T13:01:29.2474783Z python3 -c '
|
||||
2026-07-04T13:01:29.2474854Z import json, os, pathlib
|
||||
2026-07-04T13:01:29.2474933Z pathlib.Path("./publish/appsettings.Production.json").write_text(
|
||||
2026-07-04T13:01:29.2475012Z json.dumps({
|
||||
2026-07-04T13:01:29.2475107Z "Jwt": {"SecretKey": os.environ["JWT_SECRET_KEY"]},
|
||||
2026-07-04T13:01:29.2475203Z "Telegram": {
|
||||
2026-07-04T13:01:29.2475275Z "BotToken": os.environ["TELEGRAM_BOT_TOKEN"],
|
||||
2026-07-04T13:01:29.2475367Z "ChatId": os.environ["TELEGRAM_CHAT_ID"],
|
||||
2026-07-04T13:01:29.2475442Z "InquiryChatId": os.environ["TELEGRAM_INQUIRY_CHAT_ID"],
|
||||
2026-07-04T13:01:29.2475513Z "SystemChatId": os.environ["TELEGRAM_SYSTEM_CHAT_ID"]
|
||||
2026-07-04T13:01:29.2475584Z }
|
||||
2026-07-04T13:01:29.2475671Z }, ensure_ascii=False, indent=2),
|
||||
2026-07-04T13:01:29.2475739Z encoding="utf-8"
|
||||
2026-07-04T13:01:29.2475812Z )'
|
||||
2026-07-04T13:01:29.2475895Z test -s ./publish/appsettings.Production.json || { echo "appsettings.Production.json is empty" >&2; exit 1; }
|
||||
2026-07-04T13:01:29.2475995Z shell: bash --noprofile --norc -e -o pipefail {0}
|
||||
2026-07-04T13:01:29.2476277Z ::endgroup::
|
||||
2026-07-04T13:01:29.5445729Z ::group::Run test -s ./publish/proxy/TaxBaik.Proxy.dll || { echo "TaxBaik.Proxy.dll missing" >&2; exit 1; }
|
||||
2026-07-04T13:01:29.5446479Z test -s ./publish/proxy/TaxBaik.Proxy.dll || { echo "TaxBaik.Proxy.dll missing" >&2; exit 1; }
|
||||
2026-07-04T13:01:29.5446656Z test -s ./publish/proxy/TaxBaik.Proxy.runtimeconfig.json || { echo "TaxBaik.Proxy.runtimeconfig.json missing" >&2; exit 1; }
|
||||
2026-07-04T13:01:29.5446772Z shell: bash --noprofile --norc -e -o pipefail {0}
|
||||
2026-07-04T13:01:29.5446901Z ::endgroup::
|
||||
2026-07-04T13:01:29.7396976Z ::group::Run mkdir -p ./publish/db && cp -r db/migrations ./publish/db/ || true
|
||||
2026-07-04T13:01:29.7397352Z mkdir -p ./publish/db && cp -r db/migrations ./publish/db/ || true
|
||||
2026-07-04T13:01:29.7397484Z shell: bash --noprofile --norc -e -o pipefail {0}
|
||||
2026-07-04T13:01:29.7397589Z ::endgroup::
|
||||
2026-07-04T13:01:30.0169769Z ::group::Run bash scripts/validate_migrations.sh db/migrations
|
||||
2026-07-04T13:01:30.0170284Z bash scripts/validate_migrations.sh db/migrations
|
||||
2026-07-04T13:01:30.0170495Z shell: bash --noprofile --norc -e -o pipefail {0}
|
||||
2026-07-04T13:01:30.0170623Z ::endgroup::
|
||||
2026-07-04T13:01:30.1873738Z Duplicate version check passed.
|
||||
2026-07-04T13:01:30.3350394Z ::group::Run bash scripts/validate_kst_timestamps.sh
|
||||
2026-07-04T13:01:30.3350747Z bash scripts/validate_kst_timestamps.sh
|
||||
2026-07-04T13:01:30.3350854Z shell: bash --noprofile --norc -e -o pipefail {0}
|
||||
2026-07-04T13:01:30.3350983Z ::endgroup::
|
||||
2026-07-04T13:01:30.4075762Z KST timestamp harness passed.
|
||||
2026-07-04T13:01:30.5339955Z ::group::Run COMMIT_HASH=$(git rev-parse --short HEAD)
|
||||
2026-07-04T13:01:30.5340420Z COMMIT_HASH=$(git rev-parse --short HEAD)
|
||||
2026-07-04T13:01:30.5340542Z BUILD_TIME=$(TZ=Asia/Seoul date +'%Y-%m-%d %H:%M:%S KST')
|
||||
2026-07-04T13:01:30.5340635Z mkdir -p ./publish/wwwroot
|
||||
2026-07-04T13:01:30.5340751Z printf '{\n "version": "%s",\n "built": "%s"\n}\n' "$COMMIT_HASH" "$BUILD_TIME" > ./publish/wwwroot/version.json
|
||||
2026-07-04T13:01:30.5340864Z echo "✓ Build: $COMMIT_HASH @ $BUILD_TIME"
|
||||
2026-07-04T13:01:30.5340985Z shell: bash --noprofile --norc -e -o pipefail {0}
|
||||
2026-07-04T13:01:30.5341090Z ::endgroup::
|
||||
2026-07-04T13:01:30.5999292Z ✓ Build: c00d237 @ 2026-07-04 22:01:30 KST
|
||||
2026-07-04T13:01:30.7264039Z ::group::Run mkdir -p ~/.ssh
|
||||
2026-07-04T13:01:30.7264431Z mkdir -p ~/.ssh
|
||||
2026-07-04T13:01:30.7264604Z SSH_KEY_B64="***"
|
||||
2026-07-04T13:01:30.7264799Z SSH_KEY_RAW="***"
|
||||
2026-07-04T13:01:30.7265065Z if [ -n "$SSH_KEY_B64" ]; then
|
||||
2026-07-04T13:01:30.7265231Z printf '%s' "$SSH_KEY_B64" | base64 -d > ~/.ssh/id_ed25519
|
||||
2026-07-04T13:01:30.7265382Z elif [ -n "$SSH_KEY_RAW" ]; then
|
||||
2026-07-04T13:01:30.7265513Z if printf '%s' "$SSH_KEY_RAW" | grep -q 'BEGIN .*PRIVATE KEY'; then
|
||||
2026-07-04T13:01:30.7265665Z printf '%b\n' "$SSH_KEY_RAW" > ~/.ssh/id_ed25519
|
||||
2026-07-04T13:01:30.7265813Z else
|
||||
2026-07-04T13:01:30.7265955Z printf '%s' "$SSH_KEY_RAW" | base64 -d > ~/.ssh/id_ed25519
|
||||
2026-07-04T13:01:30.7266448Z fi
|
||||
2026-07-04T13:01:30.7266624Z else
|
||||
2026-07-04T13:01:30.7266747Z echo "Missing DEPLOY_SSH_KEY_B64 or DEPLOY_SSH_KEY" >&2; exit 1
|
||||
2026-07-04T13:01:30.7266892Z fi
|
||||
2026-07-04T13:01:30.7267009Z sed -i 's/\r$//' ~/.ssh/id_ed25519
|
||||
2026-07-04T13:01:30.7267138Z chmod 600 ~/.ssh/id_ed25519
|
||||
2026-07-04T13:01:30.7267222Z ssh-keyscan -H "***" >> ~/.ssh/known_hosts 2>/dev/null || true
|
||||
2026-07-04T13:01:30.7267363Z shell: bash --noprofile --norc -e -o pipefail {0}
|
||||
2026-07-04T13:01:30.7267559Z ::endgroup::
|
||||
2026-07-04T13:01:31.3915758Z ::group::Run cp deploy_gb.sh ./publish/deploy_gb.sh
|
||||
2026-07-04T13:01:31.3916326Z cp deploy_gb.sh ./publish/deploy_gb.sh
|
||||
2026-07-04T13:01:31.3916448Z mkdir -p ./publish/scripts
|
||||
2026-07-04T13:01:31.3916532Z cp scripts/validate_migrations.sh ./publish/scripts/validate_migrations.sh
|
||||
2026-07-04T13:01:31.3916629Z chmod +x ./publish/scripts/validate_migrations.sh
|
||||
2026-07-04T13:01:31.3916706Z tar -czf taxbaik_deploy.tgz -C ./publish .
|
||||
2026-07-04T13:01:31.3916791Z echo "✓ Package: $(du -sh taxbaik_deploy.tgz | cut -f1)"
|
||||
2026-07-04T13:01:31.3916894Z shell: bash --noprofile --norc -e -o pipefail {0}
|
||||
2026-07-04T13:01:31.3916989Z ::endgroup::
|
||||
2026-07-04T13:01:34.5550762Z ✓ Package: 23M
|
||||
2026-07-04T13:01:34.6596310Z ::group::Run set -e
|
||||
2026-07-04T13:01:34.6596644Z set -e
|
||||
2026-07-04T13:01:34.6596755Z export TAXBAIK_DEPLOY_FROM_CI=1
|
||||
2026-07-04T13:01:34.6596854Z TIMESTAMP=$(TZ=Asia/Seoul date +%Y%m%d_%H%M%S)
|
||||
2026-07-04T13:01:34.6596935Z COMMIT=$(git rev-parse --short HEAD)
|
||||
2026-07-04T13:01:34.6597016Z DEPLOY_HOST="***"
|
||||
2026-07-04T13:01:34.6597103Z DEPLOY_USER="***"
|
||||
2026-07-04T13:01:34.6597300Z TELEGRAM_BOT_TOKEN="***"
|
||||
2026-07-04T13:01:34.6597384Z TELEGRAM_SYSTEM_CHAT_ID=""
|
||||
2026-07-04T13:01:34.6597460Z TELEGRAM_CHAT_ID="${TELEGRAM_SYSTEM_CHAT_ID:--5585148480}"
|
||||
2026-07-04T13:01:34.6597556Z
|
||||
2026-07-04T13:01:34.6597626Z send_telegram() {
|
||||
2026-07-04T13:01:34.6597703Z local text="$1"
|
||||
2026-07-04T13:01:34.6597779Z if [ -z "$TELEGRAM_BOT_TOKEN" ]; then
|
||||
2026-07-04T13:01:34.6597884Z echo "Skipping Telegram notification: missing TAXBAIK_TELEGRAM_BOT_TOKEN" >&2
|
||||
2026-07-04T13:01:34.6597983Z return 0
|
||||
2026-07-04T13:01:34.6598050Z fi
|
||||
2026-07-04T13:01:34.6598129Z
|
||||
2026-07-04T13:01:34.6598193Z curl -fsS -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \
|
||||
2026-07-04T13:01:34.6598278Z -d "chat_id=${TELEGRAM_CHAT_ID}" \
|
||||
2026-07-04T13:01:34.6598353Z --data-urlencode "text=${text}" \
|
||||
2026-07-04T13:01:34.6598442Z -d "parse_mode=HTML" >/dev/null || true
|
||||
2026-07-04T13:01:34.6598515Z }
|
||||
2026-07-04T13:01:34.6598583Z
|
||||
2026-07-04T13:01:34.6598655Z notify_failure() {
|
||||
2026-07-04T13:01:34.6598728Z local exit_code=$?
|
||||
2026-07-04T13:01:34.6598798Z send_telegram "❌ <b>TaxBaik 배포 실패</b>
|
||||
2026-07-04T13:01:34.6598890Z
|
||||
2026-07-04T13:01:34.6598968Z 커밋: <code>${COMMIT}</code>
|
||||
2026-07-04T13:01:34.6599054Z 시간: <code>${TIMESTAMP}</code>
|
||||
2026-07-04T13:01:34.6599132Z 단계: CI/CD deploy"
|
||||
2026-07-04T13:01:34.6599222Z exit "$exit_code"
|
||||
2026-07-04T13:01:34.6599293Z }
|
||||
2026-07-04T13:01:34.6599355Z
|
||||
2026-07-04T13:01:34.6599452Z trap notify_failure ERR
|
||||
2026-07-04T13:01:34.6599539Z
|
||||
2026-07-04T13:01:34.6599604Z echo "=== Deploying TaxBaik $COMMIT ($TIMESTAMP) ==="
|
||||
2026-07-04T13:01:34.6599681Z
|
||||
2026-07-04T13:01:34.6599753Z # 1. 아티팩트 업로드
|
||||
2026-07-04T13:01:34.6599828Z scp -i ~/.ssh/id_ed25519 -o StrictHostKeyChecking=yes \
|
||||
2026-07-04T13:01:34.6599908Z taxbaik_deploy.tgz "$DEPLOY_USER@$DEPLOY_HOST:/tmp/taxbaik_${TIMESTAMP}.tgz"
|
||||
2026-07-04T13:01:34.6599993Z
|
||||
2026-07-04T13:01:34.6600070Z # 2. 서버에서 배포 + 헬스 체크 (SSH 1회 연결로 처리, Green-Blue 지원)
|
||||
2026-07-04T13:01:34.6600159Z ssh -i ~/.ssh/id_ed25519 -o StrictHostKeyChecking=yes \
|
||||
2026-07-04T13:01:34.6600240Z -o ServerAliveInterval=10 \
|
||||
2026-07-04T13:01:34.6600323Z "$DEPLOY_USER@$DEPLOY_HOST" TAXBAIK_DEPLOY_FROM_CI=1 bash << REMOTE
|
||||
2026-07-04T13:01:34.6600403Z set -e
|
||||
2026-07-04T13:01:34.6600473Z DEPLOY_HOME="/home/***"
|
||||
2026-07-04T13:01:34.6600555Z DEPLOY_DIR="\$DEPLOY_HOME/deployments/taxbaik_${TIMESTAMP}"
|
||||
2026-07-04T13:01:34.6600671Z TIMESTAMP="${TIMESTAMP}"
|
||||
2026-07-04T13:01:34.6600741Z COMMIT="${COMMIT}"
|
||||
2026-07-04T13:01:34.6600810Z
|
||||
2026-07-04T13:01:34.6600871Z echo "--- [1/5] 압축 해제 ---"
|
||||
2026-07-04T13:01:34.6600946Z mkdir -p "\$DEPLOY_DIR"
|
||||
2026-07-04T13:01:34.6601017Z tar -xzf "/tmp/taxbaik_\${TIMESTAMP}.tgz" -C "\$DEPLOY_DIR"
|
||||
2026-07-04T13:01:34.6601091Z rm -f "/tmp/taxbaik_\${TIMESTAMP}.tgz"
|
||||
2026-07-04T13:01:34.6601165Z
|
||||
2026-07-04T13:01:34.6601224Z echo "--- [2/5] 운영 설정 검증 ---"
|
||||
2026-07-04T13:01:34.6601302Z test -s "\$DEPLOY_DIR/appsettings.Production.json" \
|
||||
2026-07-04T13:01:34.6601375Z || { echo "FATAL: appsettings.Production.json 없음" >&2; exit 1; }
|
||||
2026-07-04T13:01:34.6601452Z test -s "\$DEPLOY_DIR/proxy/TaxBaik.Proxy.dll" \
|
||||
2026-07-04T13:01:34.6601525Z || { echo "FATAL: TaxBaik.Proxy.dll 없음" >&2; exit 1; }
|
||||
2026-07-04T13:01:34.6601608Z
|
||||
2026-07-04T13:01:34.6601676Z echo "--- [3/5] 마이그레이션 사전 검증 ---"
|
||||
2026-07-04T13:01:34.6601750Z test -x "\$DEPLOY_DIR/scripts/validate_migrations.sh" \
|
||||
2026-07-04T13:01:34.6601824Z || { echo "FATAL: validate_migrations.sh 없음" >&2; exit 1; }
|
||||
2026-07-04T13:01:34.6602022Z "\$DEPLOY_DIR/scripts/validate_migrations.sh" "\$DEPLOY_DIR/db/migrations" "postgresql://taxbaik:taxbaik123@localhost:5432/taxbaikdb"
|
||||
2026-07-04T13:01:34.6602131Z
|
||||
2026-07-04T13:01:34.6602198Z echo "--- [4/5] Green-Blue 배포 실행 ---"
|
||||
2026-07-04T13:01:34.6602279Z chmod +x "\$DEPLOY_DIR/deploy_gb.sh"
|
||||
2026-07-04T13:01:34.6602356Z "\$DEPLOY_DIR/deploy_gb.sh" "\$DEPLOY_DIR"
|
||||
2026-07-04T13:01:34.6602427Z
|
||||
2026-07-04T13:01:34.6602489Z echo "--- [4.5/5] Nginx 설정 검증 ---"
|
||||
2026-07-04T13:01:34.6602561Z # 실제 로드되는 파일은 sites-enabled/의 심볼릭 링크 대상만이다.
|
||||
2026-07-04T13:01:34.6602644Z # sites-available/에 다른 파일(예: default)이 있어도 sites-enabled에
|
||||
2026-07-04T13:01:34.6602727Z # 링크되어 있지 않으면 nginx는 그 내용을 절대 읽지 않는다.
|
||||
2026-07-04T13:01:34.6602804Z NGINX_CONF=""
|
||||
2026-07-04T13:01:34.6602878Z for f in /etc/nginx/sites-enabled/*; do
|
||||
2026-07-04T13:01:34.6602947Z if [ -e "\$f" ] && grep -q "location /taxbaik" "\$f" 2>/dev/null; then
|
||||
2026-07-04T13:01:34.6603031Z NGINX_CONF=\$(readlink -f "\$f")
|
||||
2026-07-04T13:01:34.6603100Z break
|
||||
2026-07-04T13:01:34.6603168Z fi
|
||||
2026-07-04T13:01:34.6603237Z done
|
||||
2026-07-04T13:01:34.6603297Z
|
||||
2026-07-04T13:01:34.6603360Z if [ -z "\$NGINX_CONF" ]; then
|
||||
2026-07-04T13:01:34.6603428Z echo "❌ FATAL: sites-enabled/ 안에서 'location /taxbaik'를 정의한 파일을 찾을 수 없음" >&2
|
||||
2026-07-04T13:01:34.6603520Z echo " sites-available/에 파일을 수정해도 sites-enabled에 심볼릭 링크되어 있지 않으면 반영되지 않는다." >&2
|
||||
2026-07-04T13:01:34.6603616Z exit 1
|
||||
2026-07-04T13:01:34.6603681Z fi
|
||||
2026-07-04T13:01:34.6603749Z echo "실제 로드되는 설정 파일: \$NGINX_CONF"
|
||||
2026-07-04T13:01:34.6603826Z
|
||||
2026-07-04T13:01:34.6603893Z # 불변식: '/'와 '/taxbaik' location 모두 반드시 127.0.0.1:5001 (TaxBaik.Proxy)을
|
||||
2026-07-04T13:01:34.6603979Z # 가리켜야 한다. 5003/5004를 직접 하드코딩하면 Green-Blue 포트 전환 시
|
||||
2026-07-04T13:01:34.6604062Z # 죽은 포트를 가리키게 되어 502/404가 발생한다 (실제 발생했던 장애).
|
||||
2026-07-04T13:01:34.6604145Z if grep -E "proxy_pass\s+http://127\.0\.0\.1:500[34]" "\$NGINX_CONF" > /dev/null 2>&1; then
|
||||
2026-07-04T13:01:34.6604235Z echo "❌ FATAL: \$NGINX_CONF 가 포트 5003/5004를 직접 참조함 (Green-Blue 전환 시 502 발생)" >&2
|
||||
2026-07-04T13:01:34.6604332Z echo " 수정: sudo sed -i 's|127.0.0.1:500[34]|127.0.0.1:5001|g' \$NGINX_CONF && sudo nginx -t && sudo systemctl reload nginx" >&2
|
||||
2026-07-04T13:01:34.6604421Z exit 1
|
||||
2026-07-04T13:01:34.6604491Z fi
|
||||
2026-07-04T13:01:34.6604558Z
|
||||
2026-07-04T13:01:34.6604619Z # proxy_pass에 URI(끝 슬래시)가 있으면 nginx가 요청 경로를 재작성하며,
|
||||
2026-07-04T13:01:34.6607916Z # location 접두사와 슬래시 개수가 안 맞으면 백엔드로 이중 슬래시(//)가
|
||||
2026-07-04T13:01:34.6608040Z # 전달되어 404가 발생한다 (실제 발생했던 장애). 접두사 location에서는
|
||||
2026-07-04T13:01:34.6608133Z # proxy_pass에 URI를 붙이지 않는다.
|
||||
2026-07-04T13:01:34.6608233Z if grep -E "location\s+/taxbaik\s*\{" -A 1 "\$NGINX_CONF" | grep -qE "proxy_pass\s+http://127\.0\.0\.1:5001/;"; then
|
||||
2026-07-04T13:01:34.6608347Z echo "❌ FATAL: location /taxbaik 의 proxy_pass 에 불필요한 trailing slash가 있음 (이중 슬래시로 인한 404 위험)" >&2
|
||||
2026-07-04T13:01:34.6608443Z exit 1
|
||||
2026-07-04T13:01:34.6608513Z fi
|
||||
2026-07-04T13:01:34.6608578Z
|
||||
2026-07-04T13:01:34.6608647Z echo "✓ Nginx 설정 검증 통과 (실제 로드 파일 확인 + 포트 5001 고정 + trailing slash 없음)"
|
||||
2026-07-04T13:01:34.6608736Z
|
||||
2026-07-04T13:01:34.6608799Z echo "--- [5/5] 헬스 체크 (최대 60초) ---"
|
||||
2026-07-04T13:01:34.6608888Z ATTEMPTS=20
|
||||
2026-07-04T13:01:34.6608968Z for i in \$(seq 1 \$ATTEMPTS); do
|
||||
2026-07-04T13:01:34.6609044Z STATUS=\$(curl -sf -o /dev/null -w '%{http_code}' http://127.0.0.1:5001/taxbaik/healthz 2>/dev/null || echo "000")
|
||||
2026-07-04T13:01:34.6609143Z if [ "\$STATUS" = "200" ]; then
|
||||
2026-07-04T13:01:34.6609218Z echo "✓ [1/6] 헬스 체크 완료"
|
||||
2026-07-04T13:01:34.6609291Z
|
||||
2026-07-04T13:01:34.6609353Z # 검증 1: 메인 페이지 로드. curl -L + -w 는 리다이렉트 체인의 상태코드를
|
||||
2026-07-04T13:01:34.6609448Z # 이어붙이므로, 첫 응답 코드만 받아 200/3xx를 허용한다.
|
||||
2026-07-04T13:01:34.6609531Z MAIN_STATUS=\$(curl -fsS -o /dev/null -w '%{http_code}' http://127.0.0.1:5001/taxbaik/ 2>/dev/null || echo "000")
|
||||
2026-07-04T13:01:34.6609626Z if ! printf '%s' "\$MAIN_STATUS" | grep -Eq '^(200|301|302|307|308)$'; then
|
||||
2026-07-04T13:01:34.6609715Z echo "❌ 메인 페이지 로드 실패 (상태: \$MAIN_STATUS)" >&2
|
||||
2026-07-04T13:01:34.6609801Z exit 1
|
||||
2026-07-04T13:01:34.6609869Z fi
|
||||
2026-07-04T13:01:34.6609935Z echo "✓ [2/6] 메인 페이지 로드 완료"
|
||||
2026-07-04T13:01:34.6610016Z
|
||||
2026-07-04T13:01:34.6610077Z # 검증 2: CSS 파일 로드
|
||||
2026-07-04T13:01:34.6610147Z CSS_STATUS=\$(curl -sf -o /dev/null -w '%{http_code}' http://127.0.0.1:5001/taxbaik/css/admin.css 2>/dev/null || echo "000")
|
||||
2026-07-04T13:01:34.6610236Z if [ "\$CSS_STATUS" != "200" ]; then
|
||||
2026-07-04T13:01:34.6610312Z echo "❌ CSS 파일 로드 실패 (상태: \$CSS_STATUS)" >&2
|
||||
2026-07-04T13:01:34.6610402Z exit 1
|
||||
2026-07-04T13:01:34.6610470Z fi
|
||||
2026-07-04T13:01:34.6610544Z echo "✓ [3/6] CSS 파일 로드 완료"
|
||||
2026-07-04T13:01:34.6610621Z
|
||||
2026-07-04T13:01:34.6610685Z # 검증 3: 버전 정보. 파일 존재만 보면 5001이 잘못된 구 프로세스를
|
||||
2026-07-04T13:01:34.6610769Z # 가리키는 장애를 놓치므로, HTTP 응답이 이번 커밋인지 확인한다.
|
||||
2026-07-04T13:01:34.6610858Z if [ ! -s "\$DEPLOY_DIR/wwwroot/version.json" ]; then
|
||||
2026-07-04T13:01:34.6610939Z echo "❌ version.json 누락" >&2
|
||||
2026-07-04T13:01:34.6611026Z exit 1
|
||||
2026-07-04T13:01:34.6611104Z fi
|
||||
2026-07-04T13:01:34.6611170Z VERSION_JSON=\$(curl -fsS http://127.0.0.1:5001/taxbaik/version.json 2>/dev/null || true)
|
||||
2026-07-04T13:01:34.6611253Z if ! printf '%s' "\$VERSION_JSON" | grep -q "\"version\": \"\$COMMIT\""; then
|
||||
2026-07-04T13:01:34.6611332Z echo "❌ 5001 프록시가 이번 배포 버전을 제공하지 않음" >&2
|
||||
2026-07-04T13:01:34.6611415Z echo " expected: \$COMMIT" >&2
|
||||
2026-07-04T13:01:34.6611490Z echo " actual: \$VERSION_JSON" >&2
|
||||
2026-07-04T13:01:34.6611563Z echo " 확인: 5001 포트가 TaxBaik.Proxy.dll인지, /home/***/taxbaik_port가 새 포트인지 점검" >&2
|
||||
2026-07-04T13:01:34.6611663Z exit 1
|
||||
2026-07-04T13:01:34.6611729Z fi
|
||||
2026-07-04T13:01:34.6611794Z echo "✓ [4/6] 버전 정보 확인 완료"
|
||||
2026-07-04T13:01:34.6612039Z
|
||||
2026-07-04T13:01:34.6612103Z # 검증 4: 5001 프록시 확인
|
||||
2026-07-04T13:01:34.6612177Z if ! ss -tlnp | grep -q ':5001 '; then
|
||||
2026-07-04T13:01:34.6612250Z echo "❌ 5001 프록시가 실행 중이 아님" >&2
|
||||
2026-07-04T13:01:34.6612338Z exit 1
|
||||
2026-07-04T13:01:34.6612405Z fi
|
||||
2026-07-04T13:01:34.6612476Z echo "✓ [5/6] 5001 프록시 확인 완료"
|
||||
2026-07-04T13:01:34.6612558Z
|
||||
2026-07-04T13:01:34.6612622Z # 검증 5: 관리자 로그인 페이지
|
||||
2026-07-04T13:01:34.6612698Z LOGIN_STATUS=\$(curl -fsSL -o /dev/null -w '%{http_code}' http://127.0.0.1:5001/taxbaik/admin/login 2>/dev/null || echo "000")
|
||||
2026-07-04T13:01:34.6612790Z if [ "\$LOGIN_STATUS" != "200" ]; then
|
||||
2026-07-04T13:01:34.6612871Z echo "❌ 관리자 로그인 페이지 로드 실패 (상태: \$LOGIN_STATUS)" >&2
|
||||
2026-07-04T13:01:34.6612964Z exit 1
|
||||
2026-07-04T13:01:34.6613037Z fi
|
||||
2026-07-04T13:01:34.6613099Z echo "✓ [6/6] 관리자 페이지 로드 완료"
|
||||
2026-07-04T13:01:34.6613182Z
|
||||
2026-07-04T13:01:34.6613243Z echo "✓ 서비스 정상 (시도 \$i/\$ATTEMPTS)"
|
||||
2026-07-04T13:01:34.6613322Z # 구 배포 디렉토리 정리 (최근 5개 보존)
|
||||
2026-07-04T13:01:34.6613401Z ls -1dt \$DEPLOY_HOME/deployments/taxbaik_* 2>/dev/null \
|
||||
2026-07-04T13:01:34.6613481Z | tail -n +6 | xargs rm -rf 2>/dev/null || true
|
||||
2026-07-04T13:01:34.6613557Z exit 0
|
||||
2026-07-04T13:01:34.6613622Z fi
|
||||
2026-07-04T13:01:34.6613692Z if [ "\$i" -eq "\$ATTEMPTS" ]; then
|
||||
2026-07-04T13:01:34.6613767Z echo "=== FATAL: 서비스가 \$ATTEMPTS회 시도 후에도 응답하지 않음 ===" >&2
|
||||
2026-07-04T13:01:34.6613858Z echo "--- 5001 listener ---" >&2
|
||||
2026-07-04T13:01:34.6613947Z ss -tlnp 2>/dev/null | grep ':5001 ' >&2 || true
|
||||
2026-07-04T13:01:34.6614022Z echo "--- active port file ---" >&2
|
||||
2026-07-04T13:01:34.6614102Z cat "\$DEPLOY_HOME/taxbaik_port" >&2 || true
|
||||
2026-07-04T13:01:34.6614174Z echo "--- 신규 앱 로그 ---" >&2
|
||||
2026-07-04T13:01:34.6614245Z ACTIVE_PORT=\$(cat "\$DEPLOY_HOME/taxbaik_port" 2>/dev/null | tr -d '[:space:]' || true)
|
||||
2026-07-04T13:01:34.6614329Z if [ -n "\$ACTIVE_PORT" ] && [ -s "\$DEPLOY_DIR/web_\${ACTIVE_PORT}.log" ]; then
|
||||
2026-07-04T13:01:34.6614409Z tail -n 80 "\$DEPLOY_DIR/web_\${ACTIVE_PORT}.log" >&2
|
||||
2026-07-04T13:01:34.6614489Z else
|
||||
2026-07-04T13:01:34.6614554Z ls -la "\$DEPLOY_DIR" >&2 || true
|
||||
2026-07-04T13:01:34.6614626Z fi
|
||||
2026-07-04T13:01:34.6614690Z echo "--- proxy 로그 ---" >&2
|
||||
2026-07-04T13:01:34.6614764Z tail -n 80 "\$DEPLOY_HOME/taxbaik_proxy.log" >&2 || true
|
||||
2026-07-04T13:01:34.6614848Z exit 1
|
||||
2026-07-04T13:01:34.6614917Z fi
|
||||
2026-07-04T13:01:34.6614986Z echo " 대기 중... (\$i/\$ATTEMPTS, HTTP \$STATUS)"
|
||||
2026-07-04T13:01:34.6615062Z sleep 3
|
||||
2026-07-04T13:01:34.6615131Z done
|
||||
2026-07-04T13:01:34.6615194Z REMOTE
|
||||
2026-07-04T13:01:34.6615256Z
|
||||
2026-07-04T13:01:34.6615315Z echo "✓ 배포 완료: taxbaik_${TIMESTAMP} @ $DEPLOY_HOST"
|
||||
2026-07-04T13:01:34.6615389Z
|
||||
2026-07-04T13:01:34.6615454Z echo "--- 실제 공개 도메인 종단 간 검증 (Nginx/Cloudflare 경유, 최대 3회 재시도) ---"
|
||||
2026-07-04T13:01:34.6615540Z ROOT_URL="https://www.taxbaik.com/" \
|
||||
2026-07-04T13:01:34.6615611Z ADMIN_URL="https://www.taxbaik.com/taxbaik/admin/login" \
|
||||
2026-07-04T13:01:34.6615686Z PUBLIC_MARKER="백원숙 세무회계" \
|
||||
2026-07-04T13:01:34.6615758Z PUBLIC_FORBIDDEN="관리자" \
|
||||
2026-07-04T13:01:34.6615829Z ADMIN_MARKER="관리자 로그인" \
|
||||
2026-07-04T13:01:34.6615900Z MAX_RETRIES=3 \
|
||||
2026-07-04T13:01:34.6615976Z RETRY_SLEEP_SECONDS=5 \
|
||||
2026-07-04T13:01:34.6616044Z bash ./scripts/taxbaik-smoke.sh
|
||||
2026-07-04T13:01:34.6616322Z echo "✓ 실제 공개 도메인 전체 정상"
|
||||
2026-07-04T13:01:34.6616404Z
|
||||
2026-07-04T13:01:34.6616463Z send_telegram "✅ <b>TaxBaik 배포 완료</b>
|
||||
2026-07-04T13:01:34.6616542Z
|
||||
2026-07-04T13:01:34.6616609Z 커밋: <code>${COMMIT}</code>
|
||||
2026-07-04T13:01:34.6616695Z 시간: <code>${TIMESTAMP}</code>
|
||||
2026-07-04T13:01:34.6616774Z 대상: <code>${DEPLOY_HOST}</code>
|
||||
2026-07-04T13:01:34.6616853Z 채널: <code>${TELEGRAM_CHAT_ID}</code>"
|
||||
2026-07-04T13:01:34.6616931Z shell: bash --noprofile --norc -e -o pipefail {0}
|
||||
2026-07-04T13:01:34.6617030Z ::endgroup::
|
||||
2026-07-04T13:01:34.7483080Z === Deploying TaxBaik c00d237 (20260704_220134) ===
|
||||
2026-07-04T13:01:36.0560253Z --- [1/5] 압축 해제 ---
|
||||
2026-07-04T13:01:36.3575256Z --- [2/5] 운영 설정 검증 ---
|
||||
2026-07-04T13:01:36.3575950Z --- [3/5] 마이그레이션 사전 검증 ---
|
||||
2026-07-04T13:01:36.9509044Z Migration dry-run validation passed.
|
||||
2026-07-04T13:01:36.9509614Z --- [4/5] Green-Blue 배포 실행 ---
|
||||
2026-07-04T13:01:36.9593876Z ===== 🚀 TaxBaik Green/Blue Deployment Script =====
|
||||
2026-07-04T13:01:37.1157726Z Active Port: 5004
|
||||
2026-07-04T13:01:37.1158178Z Target Port: 5003
|
||||
2026-07-04T13:01:37.1158370Z Deploy Directory: /home/***/deployments/taxbaik_20260704_220134
|
||||
2026-07-04T13:01:37.4066841Z === Starting New App on Port 5003 ===
|
||||
2026-07-04T13:01:39.4311374Z === Health Checking Port 5003 ===
|
||||
2026-07-04T13:01:39.5507815Z Waiting for health check... (1/20, Status: 000000)
|
||||
2026-07-04T13:01:42.1278558Z ✓ Health check passed on port 5003 (Attempt 2/20)
|
||||
2026-07-04T13:01:42.1279137Z === Switching Traffic to Port 5003 ===
|
||||
2026-07-04T13:01:42.1312702Z ✓ Traffic routed to 5003 (via TaxBaik.Proxy on 5001)
|
||||
2026-07-04T13:01:42.1313191Z === Stopping Old App on Port 5004 ===
|
||||
2026-07-04T13:01:42.2456799Z Killing old process PID: 4150110
|
||||
2026-07-04T13:01:42.2457355Z ✓ Old process terminated
|
||||
2026-07-04T13:01:42.2457639Z === Cleaning Up Old Deployments ===
|
||||
2026-07-04T13:01:42.2824667Z ✓ Cleanup completed
|
||||
2026-07-04T13:01:42.2825372Z ===== ✅ Green/Blue Deployment Completed Successfully =====
|
||||
2026-07-04T13:01:42.2827207Z --- [4.5/5] Nginx 설정 검증 ---
|
||||
2026-07-04T13:01:42.2917525Z 실제 로드되는 설정 파일: /etc/nginx/sites-available/taxbaik-domains.conf
|
||||
2026-07-04T13:01:42.2977470Z ✓ Nginx 설정 검증 통과 (실제 로드 파일 확인 + 포트 5001 고정 + trailing slash 없음)
|
||||
2026-07-04T13:01:42.2978026Z --- [5/5] 헬스 체크 (최대 60초) ---
|
||||
2026-07-04T13:01:42.3668351Z ✓ [1/6] 헬스 체크 완료
|
||||
2026-07-04T13:01:42.8378177Z ✓ [2/6] 메인 페이지 로드 완료
|
||||
2026-07-04T13:01:42.8839270Z ✓ [3/6] CSS 파일 로드 완료
|
||||
2026-07-04T13:01:42.8929193Z ✓ [4/6] 버전 정보 확인 완료
|
||||
2026-07-04T13:01:42.9068857Z ✓ [5/6] 5001 프록시 확인 완료
|
||||
2026-07-04T13:01:42.9149449Z ✓ [6/6] 관리자 페이지 로드 완료
|
||||
2026-07-04T13:01:42.9150100Z ✓ 서비스 정상 (시도 1/20)
|
||||
2026-07-04T13:01:42.9248052Z ✓ 배포 완료: taxbaik_20260704_220134 @ ***
|
||||
2026-07-04T13:01:42.9248637Z --- 실제 공개 도메인 종단 간 검증 (Nginx/Cloudflare 경유, 최대 3회 재시도) ---
|
||||
2026-07-04T13:01:43.3724216Z ✓ https://www.taxbaik.com/ -> HTTP 200
|
||||
2026-07-04T13:01:43.6877712Z ✓ https://www.taxbaik.com/taxbaik/ -> HTTP 200
|
||||
2026-07-04T13:01:44.0126645Z retrying... (1/3)
|
||||
2026-07-04T13:01:44.0128053Z ✗ https://www.taxbaik.com/taxbaik/admin/login -> body missing required marker: 관리자 로그인
|
||||
2026-07-04T13:01:49.3854327Z ✓ https://www.taxbaik.com/ -> HTTP 200
|
||||
2026-07-04T13:01:49.6485192Z ✓ https://www.taxbaik.com/taxbaik/ -> HTTP 200
|
||||
2026-07-04T13:01:50.0153135Z ✗ https://www.taxbaik.com/taxbaik/admin/login -> body missing required marker: 관리자 로그인
|
||||
2026-07-04T13:01:50.0153696Z retrying... (2/3)
|
||||
2026-07-04T13:01:55.2722916Z ✓ https://www.taxbaik.com/ -> HTTP 200
|
||||
2026-07-04T13:01:55.6800476Z ✓ https://www.taxbaik.com/taxbaik/ -> HTTP 200
|
||||
2026-07-04T13:01:55.9334729Z ✗ https://www.taxbaik.com/taxbaik/admin/login -> body missing required marker: 관리자 로그인
|
||||
2026-07-04T13:01:55.9335523Z ✗ smoke verification failed
|
||||
2026-07-04T13:01:56.2238319Z ❌ Failure - Main Deploy & verify on server
|
||||
2026-07-04T13:01:56.2411008Z exitcode '1': failure
|
||||
2026-07-04T13:01:56.2699164Z evaluating expression 'success()'
|
||||
2026-07-04T13:01:56.2699697Z expression 'success()' evaluated to 'false'
|
||||
2026-07-04T13:01:56.2699842Z Skipping step 'Setup .NET' due to 'success()'
|
||||
2026-07-04T13:01:56.2953445Z evaluating expression 'always()'
|
||||
2026-07-04T13:01:56.2953918Z expression 'always()' evaluated to 'true'
|
||||
2026-07-04T13:01:56.2954039Z ⭐ Run Post Checkout code
|
||||
2026-07-04T13:01:56.2954206Z Writing entry to tarball workflow/outputcmd.txt len:0
|
||||
2026-07-04T13:01:56.2954356Z Writing entry to tarball workflow/statecmd.txt len:0
|
||||
2026-07-04T13:01:56.2954454Z Writing entry to tarball workflow/pathcmd.txt len:0
|
||||
2026-07-04T13:01:56.2954553Z Writing entry to tarball workflow/envs.txt len:0
|
||||
2026-07-04T13:01:56.2954636Z Writing entry to tarball workflow/SUMMARY.md len:0
|
||||
2026-07-04T13:01:56.2954724Z Extracting content to '/var/run/act'
|
||||
2026-07-04T13:01:56.2989360Z run post step for 'Checkout code'
|
||||
2026-07-04T13:01:56.2989963Z executing remote job container: [node /var/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/dist/index.js]
|
||||
2026-07-04T13:01:56.3354078Z 🐳 docker exec cmd=[node /var/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/dist/index.js] user= workdir=
|
||||
2026-07-04T13:01:56.3354506Z Exec command '[node /var/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/dist/index.js]'
|
||||
2026-07-04T13:01:56.3354997Z Working directory '/workspace/***/taxbaik'
|
||||
2026-07-04T13:01:56.5634391Z [command]/usr/bin/git version
|
||||
2026-07-04T13:01:56.5708822Z git version 2.54.0
|
||||
2026-07-04T13:01:56.5752988Z ***
|
||||
2026-07-04T13:01:56.5772894Z Temporarily overriding HOME='/tmp/29afdc3d-d383-4267-8baa-f87edca3a0e3' before making global git config changes
|
||||
2026-07-04T13:01:56.5782158Z Adding repository directory to the temporary git global config as a safe directory
|
||||
2026-07-04T13:01:56.5785763Z [command]/usr/bin/git config --global --add safe.directory /workspace/***/taxbaik
|
||||
2026-07-04T13:01:56.5856624Z [command]/usr/bin/git config --local --name-only --get-regexp core\.sshCommand
|
||||
2026-07-04T13:01:56.5933870Z [command]/usr/bin/git submodule foreach --recursive sh -c "git config --local --name-only --get-regexp 'core\.sshCommand' && git config --local --unset-all 'core.sshCommand' || :"
|
||||
2026-07-04T13:01:56.6622902Z [command]/usr/bin/git config --local --name-only --get-regexp http\.http\:\/\/gitea\:3000\/\.extraheader
|
||||
2026-07-04T13:01:56.6697660Z http.http://gitea:3000/.extraheader
|
||||
2026-07-04T13:01:56.6698844Z [command]/usr/bin/git config --local --unset-all http.http://gitea:3000/.extraheader
|
||||
2026-07-04T13:01:56.6735534Z [command]/usr/bin/git submodule foreach --recursive sh -c "git config --local --name-only --get-regexp 'http\.http\:\/\/gitea\:3000\/\.extraheader' && git config --local --unset-all 'http.http://gitea:3000/.extraheader' || :"
|
||||
2026-07-04T13:01:56.6986910Z [command]/usr/bin/git config --local --name-only --get-regexp ^includeIf\.gitdir:
|
||||
2026-07-04T13:01:56.7046662Z [command]/usr/bin/git submodule foreach --recursive git config --local --show-origin --name-only --get-regexp remote.origin.url
|
||||
2026-07-04T13:01:56.8590734Z ✅ Success - Post Checkout code
|
||||
2026-07-04T13:01:56.8750550Z Cleaning up container for job build-and-deploy
|
||||
2026-07-04T13:01:57.5600546Z Removed container: 545d203ee9994ebf9eca9357f948b418a29c0a80f13c2d69e6b64ca3d86d4b5b
|
||||
2026-07-04T13:01:57.5657268Z 🐳 docker volume rm GITEA-ACTIONS-TASK-1552-WORKFLOW-TaxBaik-CI-CD-JOB-build-and-de-ab2d1e70739a3063f448d1e9d12be855fbd02428b801ec092546b895bdf5c88d
|
||||
2026-07-04T13:01:57.6930032Z 🐳 docker volume rm GITEA-ACTIONS-TASK-1552-WORKFLOW-TaxBaik-CI-CD-JOB-build-and-de-ab2d1e70739a3063f448d1e9d12be855fbd02428b801ec092546b895bdf5c88d-env
|
||||
2026-07-04T13:01:57.9092324Z 🏁 Job failed
|
||||
2026-07-04T13:01:57.9175317Z Job 'build-and-deploy' failed
|
||||
+947
@@ -0,0 +1,947 @@
|
||||
2026-07-04T13:08:38.6554157Z hz-prod-runner(version:v0.6.1) received task 1556 of job build-and-deploy, be triggered by event: push
|
||||
2026-07-04T13:08:38.6560694Z workflow prepared
|
||||
2026-07-04T13:08:38.6561481Z evaluating expression 'success()'
|
||||
2026-07-04T13:08:38.6562225Z expression 'success()' evaluated to 'true'
|
||||
2026-07-04T13:08:38.6562395Z 🚀 Start image=docker.gitea.com/runner-images:ubuntu-latest
|
||||
2026-07-04T13:08:38.6674615Z 🐳 docker pull image=docker.gitea.com/runner-images:ubuntu-latest platform= username= forcePull=false
|
||||
2026-07-04T13:08:38.6674898Z 🐳 docker pull docker.gitea.com/runner-images:ubuntu-latest
|
||||
2026-07-04T13:08:38.6934471Z Image exists? true
|
||||
2026-07-04T13:08:38.7467353Z 🐳 docker create image=docker.gitea.com/runner-images:ubuntu-latest platform= entrypoint=["/bin/sleep" "10800"] cmd=[] network="gitea_default"
|
||||
2026-07-04T13:08:38.8585037Z Created container name=GITEA-ACTIONS-TASK-1556-WORKFLOW-TaxBaik-CI-CD-JOB-build-and-de-6297a4cd6c76440083f1331088cff47725d01c4362935c7b8a69a7f486001683 id=08d700f8667717ce78a305700f60f13b228a0d19241928b95a601d63d13f1a22 from image docker.gitea.com/runner-images:ubuntu-latest (platform: )
|
||||
2026-07-04T13:08:38.8585489Z ENV ==> [RUNNER_TOOL_CACHE=/opt/hostedtoolcache RUNNER_OS=Linux RUNNER_ARCH=X64 RUNNER_TEMP=/tmp LANG=C.UTF-8]
|
||||
2026-07-04T13:08:38.8585657Z 🐳 docker run image=docker.gitea.com/runner-images:ubuntu-latest platform= entrypoint=["/bin/sleep" "10800"] cmd=[] network="gitea_default"
|
||||
2026-07-04T13:08:38.8585889Z Starting container: 08d700f8667717ce78a305700f60f13b228a0d19241928b95a601d63d13f1a22
|
||||
2026-07-04T13:08:39.2059504Z Started container: 08d700f8667717ce78a305700f60f13b228a0d19241928b95a601d63d13f1a22
|
||||
2026-07-04T13:08:39.5671051Z Writing entry to tarball workflow/event.json len:11400
|
||||
2026-07-04T13:08:39.5672116Z Writing entry to tarball workflow/envs.txt len:0
|
||||
2026-07-04T13:08:39.5672345Z Extracting content to '/var/run/act/'
|
||||
2026-07-04T13:08:39.7909910Z ☁ git clone 'https://github.com/actions/checkout' # ref=v4
|
||||
2026-07-04T13:08:39.7910353Z cloning https://github.com/actions/checkout to /root/.cache/act/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab
|
||||
2026-07-04T13:08:40.5222042Z Unable to pull refs/heads/v4: non-fast-forward update
|
||||
2026-07-04T13:08:40.5222639Z Cloned https://github.com/actions/checkout to /root/.cache/act/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab
|
||||
2026-07-04T13:08:40.5397717Z Checked out v4
|
||||
2026-07-04T13:08:40.5648995Z ☁ git clone 'https://github.com/actions/setup-dotnet' # ref=v4
|
||||
2026-07-04T13:08:40.5649331Z cloning https://github.com/actions/setup-dotnet to /root/.cache/act/2d637816dd86ec9ff59dad9ec3547bf90b88133b3029538a91ec96ac7f316336
|
||||
2026-07-04T13:08:41.3353541Z Unable to pull refs/heads/v4: worktree contains unstaged changes
|
||||
2026-07-04T13:08:41.3354242Z Cloned https://github.com/actions/setup-dotnet to /root/.cache/act/2d637816dd86ec9ff59dad9ec3547bf90b88133b3029538a91ec96ac7f316336
|
||||
2026-07-04T13:08:41.3732622Z Checked out v4
|
||||
2026-07-04T13:08:41.3997670Z evaluating expression ''
|
||||
2026-07-04T13:08:41.3998237Z expression '' evaluated to 'true'
|
||||
2026-07-04T13:08:41.3998357Z ⭐ Run Main Checkout code
|
||||
2026-07-04T13:08:41.3998550Z Writing entry to tarball workflow/outputcmd.txt len:0
|
||||
2026-07-04T13:08:41.3998709Z Writing entry to tarball workflow/statecmd.txt len:0
|
||||
2026-07-04T13:08:41.3998815Z Writing entry to tarball workflow/pathcmd.txt len:0
|
||||
2026-07-04T13:08:41.3998915Z Writing entry to tarball workflow/envs.txt len:0
|
||||
2026-07-04T13:08:41.3999013Z Writing entry to tarball workflow/SUMMARY.md len:0
|
||||
2026-07-04T13:08:41.3999103Z Extracting content to '/var/run/act'
|
||||
2026-07-04T13:08:41.4023168Z ::group::Run Checkout code
|
||||
2026-07-04T13:08:42.0698698Z ::add-matcher::/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/dist/problem-matcher.json
|
||||
2026-07-04T13:08:42.0698864Z Syncing repository: ***/taxbaik
|
||||
2026-07-04T13:08:42.0714107Z ::group::Getting Git version info
|
||||
2026-07-04T13:08:42.0715274Z Working directory is '/workspace/***/taxbaik'
|
||||
2026-07-04T13:08:42.0813488Z [command]/usr/bin/git version
|
||||
2026-07-04T13:08:42.1412694Z git version 2.54.0
|
||||
2026-07-04T13:08:42.1489424Z ::endgroup::
|
||||
2026-07-04T13:08:42.1524768Z Temporarily overriding HOME='/tmp/64c0d9fe-44ec-41c5-8f88-dcd0485d7c0b' before making global git config changes
|
||||
2026-07-04T13:08:42.1525123Z Adding repository directory to the temporary git global config as a safe directory
|
||||
2026-07-04T13:08:42.1525253Z [command]/usr/bin/git config --global --add safe.directory /workspace/***/taxbaik
|
||||
2026-07-04T13:08:42.1590003Z Deleting the contents of '/workspace/***/taxbaik'
|
||||
2026-07-04T13:08:42.1590570Z ::group::Initializing the repository
|
||||
2026-07-04T13:08:42.1590675Z [command]/usr/bin/git init /workspace/***/taxbaik
|
||||
2026-07-04T13:08:42.1680066Z hint: Using 'master' as the name for the initial branch. This default branch name
|
||||
2026-07-04T13:08:42.1680584Z hint: will change to "main" in Git 3.0. To configure the initial branch name
|
||||
2026-07-04T13:08:42.1680710Z hint: to use in all of your new repositories, which will suppress this warning,
|
||||
2026-07-04T13:08:42.1680801Z hint: call:
|
||||
2026-07-04T13:08:42.1680880Z hint:
|
||||
2026-07-04T13:08:42.1680980Z hint: git config --global init.defaultBranch <name>
|
||||
2026-07-04T13:08:42.1681071Z hint:
|
||||
2026-07-04T13:08:42.1681353Z hint: Names commonly chosen instead of 'master' are 'main', 'trunk' and
|
||||
2026-07-04T13:08:42.1681463Z hint: 'development'. The just-created branch can be renamed via this command:
|
||||
2026-07-04T13:08:42.1681549Z hint:
|
||||
2026-07-04T13:08:42.1681622Z hint: git branch -m <name>
|
||||
2026-07-04T13:08:42.1681695Z hint:
|
||||
2026-07-04T13:08:42.1681776Z hint: Disable this message with "git config set advice.defaultBranchName false"
|
||||
2026-07-04T13:08:42.1691762Z Initialized empty Git repository in /workspace/***/taxbaik/.git/
|
||||
2026-07-04T13:08:42.1718730Z [command]/usr/bin/git remote add origin http://gitea:3000/***/taxbaik
|
||||
2026-07-04T13:08:42.2405296Z ::endgroup::
|
||||
2026-07-04T13:08:42.2405503Z ::group::Disabling automatic garbage collection
|
||||
2026-07-04T13:08:42.2405602Z [command]/usr/bin/git config --local gc.auto 0
|
||||
2026-07-04T13:08:42.2600234Z ::endgroup::
|
||||
2026-07-04T13:08:42.2600432Z ::group::Setting up auth
|
||||
2026-07-04T13:08:42.2600526Z [command]/usr/bin/git config --local --name-only --get-regexp core\.sshCommand
|
||||
2026-07-04T13:08:42.2601015Z [command]/usr/bin/git submodule foreach --recursive sh -c "git config --local --name-only --get-regexp 'core\.sshCommand' && git config --local --unset-all 'core.sshCommand' || :"
|
||||
2026-07-04T13:08:42.3294805Z [command]/usr/bin/git config --local --name-only --get-regexp http\.http\:\/\/gitea\:3000\/\.extraheader
|
||||
2026-07-04T13:08:42.3295538Z [command]/usr/bin/git submodule foreach --recursive sh -c "git config --local --name-only --get-regexp 'http\.http\:\/\/gitea\:3000\/\.extraheader' && git config --local --unset-all 'http.http://gitea:3000/.extraheader' || :"
|
||||
2026-07-04T13:08:42.4647996Z [command]/usr/bin/git config --local --name-only --get-regexp ^includeIf\.gitdir:
|
||||
2026-07-04T13:08:42.4681744Z [command]/usr/bin/git submodule foreach --recursive git config --local --show-origin --name-only --get-regexp remote.origin.url
|
||||
2026-07-04T13:08:42.5097122Z [command]/usr/bin/git config --local http.http://gitea:3000/.extraheader AUTHORIZATION: basic ***
|
||||
2026-07-04T13:08:42.5184896Z ::endgroup::
|
||||
2026-07-04T13:08:42.5187349Z ::group::Fetching the repository
|
||||
2026-07-04T13:08:42.5205386Z [command]/usr/bin/git -c protocol.version=2 fetch --no-tags --prune --no-recurse-submodules --depth=1 origin +333089a6ea2284626a1a10a877058a9def750ad3:refs/remotes/origin/master
|
||||
2026-07-04T13:08:47.6131300Z From http://gitea:3000/***/taxbaik
|
||||
2026-07-04T13:08:47.6131974Z * [new ref] 333089a6ea2284626a1a10a877058a9def750ad3 -> origin/master
|
||||
2026-07-04T13:08:47.6164024Z ::endgroup::
|
||||
2026-07-04T13:08:47.6164301Z ::group::Determining the checkout info
|
||||
2026-07-04T13:08:47.6168754Z ::endgroup::
|
||||
2026-07-04T13:08:47.6188678Z [command]/usr/bin/git sparse-checkout disable
|
||||
2026-07-04T13:08:47.6323220Z [command]/usr/bin/git config --local --unset-all extensions.worktreeConfig
|
||||
2026-07-04T13:08:47.6323803Z ::group::Checking out the ref
|
||||
2026-07-04T13:08:47.6323899Z [command]/usr/bin/git checkout --progress --force -B master refs/remotes/origin/master
|
||||
2026-07-04T13:08:48.1763334Z Reset branch 'master'
|
||||
2026-07-04T13:08:48.1769611Z branch 'master' set up to track 'origin/master'.
|
||||
2026-07-04T13:08:48.1822986Z ::endgroup::
|
||||
2026-07-04T13:08:48.1884733Z [command]/usr/bin/git log -1 --format=%H
|
||||
2026-07-04T13:08:48.1902386Z 333089a6ea2284626a1a10a877058a9def750ad3
|
||||
2026-07-04T13:08:48.1995091Z ::remove-matcher owner=checkout-git::
|
||||
2026-07-04T13:08:48.2008513Z ::endgroup::
|
||||
2026-07-04T13:08:48.2323342Z ::group::Run Setup .NET
|
||||
2026-07-04T13:08:48.2323598Z with:
|
||||
2026-07-04T13:08:48.2323697Z dotnet-version: 10.0
|
||||
2026-07-04T13:08:48.9667505Z (node:140) [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead.
|
||||
2026-07-04T13:08:48.9668330Z (Use `node --trace-deprecation ...` to show where the warning was created)
|
||||
2026-07-04T13:08:48.9702655Z [command]/run/act/actions/2d637816dd86ec9ff59dad9ec3547bf90b88133b3029538a91ec96ac7f316336/externals/install-dotnet.sh --skip-non-versioned-files --runtime dotnet --channel LTS
|
||||
2026-07-04T13:08:49.4224845Z dotnet-install: Attempting to download using aka.ms link https://builds.dotnet.microsoft.com/dotnet/Runtime/10.0.9/dotnet-runtime-10.0.9-linux-x64.tar.gz
|
||||
2026-07-04T13:08:49.7177686Z dotnet-install: Remote file https://builds.dotnet.microsoft.com/dotnet/Runtime/10.0.9/dotnet-runtime-10.0.9-linux-x64.tar.gz size is 36606251 bytes.
|
||||
2026-07-04T13:08:49.7179656Z dotnet-install: Extracting archive from https://builds.dotnet.microsoft.com/dotnet/Runtime/10.0.9/dotnet-runtime-10.0.9-linux-x64.tar.gz
|
||||
2026-07-04T13:08:50.6028043Z dotnet-install: Downloaded file size is 36606251 bytes.
|
||||
2026-07-04T13:08:50.6028507Z dotnet-install: The remote and local file sizes are equal.
|
||||
2026-07-04T13:08:50.6399542Z dotnet-install: Installed version is 10.0.9
|
||||
2026-07-04T13:08:50.6459911Z dotnet-install: Adding to current process PATH: `/usr/share/dotnet`. Note: This change will be visible only when sourcing script.
|
||||
2026-07-04T13:08:50.6477526Z dotnet-install: Note that the script does not resolve dependencies during installation.
|
||||
2026-07-04T13:08:50.6478058Z dotnet-install: To check the list of dependencies, go to https://learn.microsoft.com/dotnet/core/install, select your operating system and check the "Dependencies" section.
|
||||
2026-07-04T13:08:50.6478222Z dotnet-install: Installation finished successfully.
|
||||
2026-07-04T13:08:50.6506707Z [command]/run/act/actions/2d637816dd86ec9ff59dad9ec3547bf90b88133b3029538a91ec96ac7f316336/externals/install-dotnet.sh --skip-non-versioned-files --channel 10.0
|
||||
2026-07-04T13:08:51.0353902Z dotnet-install: Attempting to download using aka.ms link https://builds.dotnet.microsoft.com/dotnet/Sdk/10.0.301/dotnet-sdk-10.0.301-linux-x64.tar.gz
|
||||
2026-07-04T13:08:52.0300218Z dotnet-install: Remote file https://builds.dotnet.microsoft.com/dotnet/Sdk/10.0.301/dotnet-sdk-10.0.301-linux-x64.tar.gz size is 235086718 bytes.
|
||||
2026-07-04T13:08:52.0317772Z dotnet-install: Extracting archive from https://builds.dotnet.microsoft.com/dotnet/Sdk/10.0.301/dotnet-sdk-10.0.301-linux-x64.tar.gz
|
||||
2026-07-04T13:08:59.5506054Z dotnet-install: Downloaded file size is 235086718 bytes.
|
||||
2026-07-04T13:08:59.5506825Z dotnet-install: The remote and local file sizes are equal.
|
||||
2026-07-04T13:08:59.8080972Z dotnet-install: Installed version is 10.0.301
|
||||
2026-07-04T13:08:59.8139788Z dotnet-install: Adding to current process PATH: `/usr/share/dotnet`. Note: This change will be visible only when sourcing script.
|
||||
2026-07-04T13:08:59.8140246Z dotnet-install: Note that the script does not resolve dependencies during installation.
|
||||
2026-07-04T13:08:59.8140426Z dotnet-install: To check the list of dependencies, go to https://learn.microsoft.com/dotnet/core/install, select your operating system and check the "Dependencies" section.
|
||||
2026-07-04T13:08:59.8140561Z dotnet-install: Installation finished successfully.
|
||||
2026-07-04T13:08:59.8174439Z ##[add-matcher]/run/act/actions/2d637816dd86ec9ff59dad9ec3547bf90b88133b3029538a91ec96ac7f316336/.github/csc.json
|
||||
2026-07-04T13:08:59.8350339Z ::endgroup::
|
||||
2026-07-04T13:08:59.9412562Z ::group::Run dotnet restore src/TaxBaik.sln
|
||||
2026-07-04T13:08:59.9412908Z dotnet restore src/TaxBaik.sln
|
||||
2026-07-04T13:08:59.9413029Z shell: bash --noprofile --norc -e -o pipefail {0}
|
||||
2026-07-04T13:08:59.9413130Z ::endgroup::
|
||||
2026-07-04T13:09:00.1537332Z
|
||||
2026-07-04T13:09:00.1546845Z Welcome to .NET 10.0!
|
||||
2026-07-04T13:09:00.1547560Z ---------------------
|
||||
2026-07-04T13:09:00.1547817Z SDK Version: 10.0.301
|
||||
2026-07-04T13:09:00.1547981Z
|
||||
2026-07-04T13:09:00.1548154Z Telemetry
|
||||
2026-07-04T13:09:00.1548294Z ---------
|
||||
2026-07-04T13:09:00.1548841Z The .NET tools collect usage data in order to help us improve your experience. It is collected by Microsoft and shared with the community. You can opt-out of telemetry by setting the DOTNET_CLI_TELEMETRY_OPTOUT environment variable to '1' or 'true' using your favorite shell.
|
||||
2026-07-04T13:09:00.1549162Z
|
||||
2026-07-04T13:09:00.1549585Z Read more about .NET CLI Tools telemetry: https://aka.ms/dotnet-cli-telemetry
|
||||
2026-07-04T13:09:00.6622541Z
|
||||
2026-07-04T13:09:00.6623265Z ----------------
|
||||
2026-07-04T13:09:00.6623468Z Installed an ASP.NET Core HTTPS development certificate.
|
||||
2026-07-04T13:09:00.6623644Z To trust the certificate, run 'dotnet dev-certs https --trust'
|
||||
2026-07-04T13:09:00.6626639Z Learn about HTTPS: https://aka.ms/dotnet-https
|
||||
2026-07-04T13:09:00.6626890Z
|
||||
2026-07-04T13:09:00.6627133Z ----------------
|
||||
2026-07-04T13:09:00.6627328Z Write your first app: https://aka.ms/dotnet-hello-world
|
||||
2026-07-04T13:09:00.6627504Z Find out what's new: https://aka.ms/dotnet-whats-new
|
||||
2026-07-04T13:09:00.6627646Z Explore documentation: https://aka.ms/dotnet-docs
|
||||
2026-07-04T13:09:00.6627822Z Report issues and find source on GitHub: https://github.com/dotnet/core
|
||||
2026-07-04T13:09:00.6628151Z Use 'dotnet --help' to see available commands or visit: https://aka.ms/dotnet-cli
|
||||
2026-07-04T13:09:00.6628340Z --------------------------------------------------------------------------------------
|
||||
2026-07-04T13:09:02.6012944Z Determining projects to restore...
|
||||
2026-07-04T13:09:04.0511847Z Restored /workspace/***/taxbaik/src/TaxBaik.Domain/TaxBaik.Domain.csproj (in 271 ms).
|
||||
2026-07-04T13:09:06.1918401Z Restored /workspace/***/taxbaik/src/TaxBaik.Application/TaxBaik.Application.csproj (in 2.12 sec).
|
||||
2026-07-04T13:09:09.3041520Z Restored /workspace/***/taxbaik/src/TaxBaik.Web/TaxBaik.Web.csproj (in 3.12 sec).
|
||||
2026-07-04T13:09:13.6409709Z Restored /workspace/***/taxbaik/src/TaxBaik.Application.Tests/TaxBaik.Application.Tests.csproj (in 9.9 sec).
|
||||
2026-07-04T13:09:13.6728450Z Restored /workspace/***/taxbaik/src/TaxBaik.Infrastructure/TaxBaik.Infrastructure.csproj (in 16 ms).
|
||||
2026-07-04T13:09:14.9637234Z Restored /workspace/***/taxbaik/src/TaxBaik.Web.Client/TaxBaik.Web.Client.csproj (in 5.65 sec).
|
||||
2026-07-04T13:09:15.1745606Z ::group::Run dotnet build src/TaxBaik.sln -c Release --no-restore -p:ContinuousIntegrationBuild=true
|
||||
2026-07-04T13:09:15.1745957Z dotnet build src/TaxBaik.sln -c Release --no-restore -p:ContinuousIntegrationBuild=true
|
||||
2026-07-04T13:09:15.1746276Z shell: bash --noprofile --norc -e -o pipefail {0}
|
||||
2026-07-04T13:09:15.1746395Z ::endgroup::
|
||||
2026-07-04T13:09:28.2755616Z TaxBaik.Domain -> /workspace/***/taxbaik/src/TaxBaik.Domain/bin/Release/net10.0/TaxBaik.Domain.dll
|
||||
2026-07-04T13:09:35.8522427Z TaxBaik.Application -> /workspace/***/taxbaik/src/TaxBaik.Application/bin/Release/net10.0/TaxBaik.Application.dll
|
||||
2026-07-04T13:09:38.3840271Z TaxBaik.Infrastructure -> /workspace/***/taxbaik/src/TaxBaik.Infrastructure/bin/Release/net10.0/TaxBaik.Infrastructure.dll
|
||||
2026-07-04T13:10:15.0436919Z TaxBaik.Web.Client -> /workspace/***/taxbaik/src/TaxBaik.Web.Client/bin/Debug/net10.0/TaxBaik.Web.Client.dll
|
||||
2026-07-04T13:10:15.0505753Z TaxBaik.Web.Client (Blazor output) -> /workspace/***/taxbaik/src/TaxBaik.Web.Client/bin/Debug/net10.0/wwwroot
|
||||
2026-07-04T13:10:41.1192020Z TaxBaik.Web -> /workspace/***/taxbaik/src/TaxBaik.Web/bin/Release/net10.0/TaxBaik.Web.dll
|
||||
2026-07-04T13:10:45.6178031Z TaxBaik.Application.Tests -> /workspace/***/taxbaik/src/TaxBaik.Application.Tests/bin/Release/net10.0/TaxBaik.Application.Tests.dll
|
||||
2026-07-04T13:10:45.8443066Z
|
||||
2026-07-04T13:10:45.8499388Z Build succeeded.
|
||||
2026-07-04T13:10:45.8499658Z 0 Warning(s)
|
||||
2026-07-04T13:10:45.8500271Z 0 Error(s)
|
||||
2026-07-04T13:10:45.8505126Z
|
||||
2026-07-04T13:10:45.8505708Z Time Elapsed 00:01:29.84
|
||||
2026-07-04T13:10:46.2580548Z ::group::Run dotnet test src/TaxBaik.sln -c Release --no-build
|
||||
2026-07-04T13:10:46.2581178Z dotnet test src/TaxBaik.sln -c Release --no-build
|
||||
2026-07-04T13:10:46.2581333Z shell: bash --noprofile --norc -e -o pipefail {0}
|
||||
2026-07-04T13:10:46.2581440Z ::endgroup::
|
||||
2026-07-04T13:10:53.7014852Z Test run for /workspace/***/taxbaik/src/TaxBaik.Application.Tests/bin/Release/net10.0/TaxBaik.Application.Tests.dll (.NETCoreApp,Version=v10.0)
|
||||
2026-07-04T13:10:55.4193961Z A total of 1 test files matched the specified pattern.
|
||||
2026-07-04T13:11:01.1818213Z
|
||||
2026-07-04T13:11:01.2027434Z Passed! - Failed: 0, Passed: 26, Skipped: 0, Total: 26, Duration: 215 ms - TaxBaik.Application.Tests.dll (net10.0)
|
||||
2026-07-04T13:11:01.5260692Z ::group::Run set -e
|
||||
2026-07-04T13:11:01.5261048Z set -e
|
||||
2026-07-04T13:11:01.5261347Z mkdir -p ./publish-logs
|
||||
2026-07-04T13:11:01.5261580Z web_log="./publish-logs/publish-web.log"
|
||||
2026-07-04T13:11:01.5261735Z start=$(date +%s)
|
||||
2026-07-04T13:11:01.5261874Z # Web.Client needs a Release static-web-assets manifest for Web publish.
|
||||
2026-07-04T13:11:01.5264309Z # Build it explicitly so publish can reuse the prepared outputs.
|
||||
2026-07-04T13:11:01.5264584Z dotnet build src/TaxBaik.Web.Client/TaxBaik.Web.Client.csproj -c Release --no-restore -p:ContinuousIntegrationBuild=true
|
||||
2026-07-04T13:11:01.5264710Z # Build the Web host in Release as well so publish has the same inputs
|
||||
2026-07-04T13:11:01.5264809Z # the server uses in production.
|
||||
2026-07-04T13:11:01.5264941Z dotnet build src/TaxBaik.Web/TaxBaik.Web.csproj -c Release --no-restore -p:ContinuousIntegrationBuild=true
|
||||
2026-07-04T13:11:01.5265051Z echo "--- Web.Client Release artifacts ---"
|
||||
2026-07-04T13:11:01.5265194Z ls -la src/TaxBaik.Web.Client/bin/Release/net10.0 || true
|
||||
2026-07-04T13:11:01.5265350Z ls -la src/TaxBaik.Web.Client/obj/Release/net10.0 || true
|
||||
2026-07-04T13:11:01.5265523Z if ! dotnet publish src/TaxBaik.Web/ \
|
||||
2026-07-04T13:11:01.5265624Z -c Release \
|
||||
2026-07-04T13:11:01.5265698Z -o ./publish \
|
||||
2026-07-04T13:11:01.5265799Z --no-restore \
|
||||
2026-07-04T13:11:01.5265890Z -p:SelfContained=false \
|
||||
2026-07-04T13:11:01.5266026Z -p:PublishReadyToRun=false \
|
||||
2026-07-04T13:11:01.5266373Z -p:PerformanceSummary=true \
|
||||
2026-07-04T13:11:01.5266489Z -clp:Summary \
|
||||
2026-07-04T13:11:01.5266568Z -bl:"./publish-logs/publish-web.binlog" >"$web_log" 2>&1; then
|
||||
2026-07-04T13:11:01.5266685Z echo "=== Publish Web failed; tailing log ==="
|
||||
2026-07-04T13:11:01.5266803Z tail -n 120 "$web_log" || true
|
||||
2026-07-04T13:11:01.5266930Z exit 1
|
||||
2026-07-04T13:11:01.5267050Z fi
|
||||
2026-07-04T13:11:01.5267183Z end=$(date +%s)
|
||||
2026-07-04T13:11:01.5267283Z echo "✓ Publish Web elapsed: $((end - start))s"
|
||||
2026-07-04T13:11:01.5267379Z ls -lh ./publish-logs/publish-web.binlog
|
||||
2026-07-04T13:11:01.5267482Z shell: bash --noprofile --norc -e -o pipefail {0}
|
||||
2026-07-04T13:11:01.5267673Z ::endgroup::
|
||||
2026-07-04T13:11:04.1670093Z TaxBaik.Domain -> /workspace/***/taxbaik/src/TaxBaik.Domain/bin/Release/net10.0/TaxBaik.Domain.dll
|
||||
2026-07-04T13:11:04.7799852Z TaxBaik.Application -> /workspace/***/taxbaik/src/TaxBaik.Application/bin/Release/net10.0/TaxBaik.Application.dll
|
||||
2026-07-04T13:11:47.5538773Z TaxBaik.Web.Client -> /workspace/***/taxbaik/src/TaxBaik.Web.Client/bin/Release/net10.0/TaxBaik.Web.Client.dll
|
||||
2026-07-04T13:11:47.5567236Z TaxBaik.Web.Client (Blazor output) -> /workspace/***/taxbaik/src/TaxBaik.Web.Client/bin/Release/net10.0/wwwroot
|
||||
2026-07-04T13:11:47.9057412Z
|
||||
2026-07-04T13:11:47.9058068Z Build succeeded.
|
||||
2026-07-04T13:11:47.9058260Z 0 Warning(s)
|
||||
2026-07-04T13:11:47.9058408Z 0 Error(s)
|
||||
2026-07-04T13:11:47.9058651Z
|
||||
2026-07-04T13:11:47.9058789Z Time Elapsed 00:00:45.47
|
||||
2026-07-04T13:11:54.8687412Z TaxBaik.Domain -> /workspace/***/taxbaik/src/TaxBaik.Domain/bin/Release/net10.0/TaxBaik.Domain.dll
|
||||
2026-07-04T13:11:55.0442507Z TaxBaik.Infrastructure -> /workspace/***/taxbaik/src/TaxBaik.Infrastructure/bin/Release/net10.0/TaxBaik.Infrastructure.dll
|
||||
2026-07-04T13:11:56.4376886Z TaxBaik.Application -> /workspace/***/taxbaik/src/TaxBaik.Application/bin/Release/net10.0/TaxBaik.Application.dll
|
||||
2026-07-04T13:12:03.8548417Z TaxBaik.Web.Client -> /workspace/***/taxbaik/src/TaxBaik.Web.Client/bin/Release/net10.0/TaxBaik.Web.Client.dll
|
||||
2026-07-04T13:12:03.8567628Z TaxBaik.Web.Client (Blazor output) -> /workspace/***/taxbaik/src/TaxBaik.Web.Client/bin/Release/net10.0/wwwroot
|
||||
2026-07-04T13:12:11.3956932Z TaxBaik.Web -> /workspace/***/taxbaik/src/TaxBaik.Web/bin/Release/net10.0/TaxBaik.Web.dll
|
||||
2026-07-04T13:12:11.4542625Z
|
||||
2026-07-04T13:12:11.4549811Z Build succeeded.
|
||||
2026-07-04T13:12:11.4563841Z 0 Warning(s)
|
||||
2026-07-04T13:12:11.4564351Z 0 Error(s)
|
||||
2026-07-04T13:12:11.4564531Z
|
||||
2026-07-04T13:12:11.4565085Z Time Elapsed 00:00:20.55
|
||||
2026-07-04T13:12:11.5085624Z --- Web.Client Release artifacts ---
|
||||
2026-07-04T13:12:11.5119963Z total 42060
|
||||
2026-07-04T13:12:11.5120310Z drwxr-xr-x 3 root root 20480 Jul 4 13:11 .
|
||||
2026-07-04T13:12:11.5120457Z drwxr-xr-x 3 root root 4096 Jul 4 13:11 ..
|
||||
2026-07-04T13:12:11.5120569Z -rwxr--r-- 1 root root 55632 May 20 20:19 Microsoft.AspNetCore.Authorization.dll
|
||||
2026-07-04T13:12:11.5120690Z -rwxr--r-- 1 root root 34128 May 20 20:19 Microsoft.AspNetCore.Components.Authorization.dll
|
||||
2026-07-04T13:12:11.5120795Z -rwxr--r-- 1 root root 47952 May 20 20:19 Microsoft.AspNetCore.Components.Forms.dll
|
||||
2026-07-04T13:12:11.5120883Z -rwxr--r-- 1 root root 189264 May 20 20:19 Microsoft.AspNetCore.Components.Web.dll
|
||||
2026-07-04T13:12:11.5120973Z -rwxr--r-- 1 root root 166736 May 20 20:20 Microsoft.AspNetCore.Components.WebAssembly.dll
|
||||
2026-07-04T13:12:11.5121363Z -rwxr--r-- 1 root root 399184 May 20 20:19 Microsoft.AspNetCore.Components.dll
|
||||
2026-07-04T13:12:11.5121478Z -rwxr--r-- 1 root root 16208 May 20 20:18 Microsoft.AspNetCore.Metadata.dll
|
||||
2026-07-04T13:12:11.5121580Z -rwxr--r-- 1 root root 19248 Dec 12 2025 Microsoft.Bcl.Cryptography.dll
|
||||
2026-07-04T13:12:11.5121670Z -rwxr--r-- 1 root root 311632 May 20 18:30 Microsoft.CSharp.dll
|
||||
2026-07-04T13:12:11.5121786Z -rwxr--r-- 1 root root 38192 Oct 24 2025 Microsoft.Extensions.Caching.Abstractions.dll
|
||||
2026-07-04T13:12:11.5121877Z -rwxr--r-- 1 root root 28496 May 20 19:29 Microsoft.Extensions.Configuration.Abstractions.dll
|
||||
2026-07-04T13:12:11.5121982Z -rwxr--r-- 1 root root 43344 May 20 19:32 Microsoft.Extensions.Configuration.Binder.dll
|
||||
2026-07-04T13:12:11.5122101Z -rwxr--r-- 1 root root 28496 May 20 19:34 Microsoft.Extensions.Configuration.FileExtensions.dll
|
||||
2026-07-04T13:12:11.5122192Z -rwxr--r-- 1 root root 27984 May 20 19:36 Microsoft.Extensions.Configuration.Json.dll
|
||||
2026-07-04T13:12:11.5122275Z -rwxr--r-- 1 root root 44880 May 20 19:29 Microsoft.Extensions.Configuration.dll
|
||||
2026-07-04T13:12:11.5122369Z -rwxr--r-- 1 root root 65872 May 20 19:29 Microsoft.Extensions.DependencyInjection.Abstractions.dll
|
||||
2026-07-04T13:12:11.5122474Z -rwxr--r-- 1 root root 95568 May 20 19:30 Microsoft.Extensions.DependencyInjection.dll
|
||||
2026-07-04T13:12:11.5125085Z -rwxr--r-- 1 root root 31056 May 20 19:32 Microsoft.Extensions.Diagnostics.Abstractions.dll
|
||||
2026-07-04T13:12:11.5125391Z -rwxr--r-- 1 root root 36176 May 20 19:34 Microsoft.Extensions.Diagnostics.dll
|
||||
2026-07-04T13:12:11.5125556Z -rwxr--r-- 1 root root 23376 May 20 19:29 Microsoft.Extensions.FileProviders.Abstractions.dll
|
||||
2026-07-04T13:12:11.5125898Z -rwxr--r-- 1 root root 45392 May 20 19:33 Microsoft.Extensions.FileProviders.Physical.dll
|
||||
2026-07-04T13:12:11.5126291Z -rwxr--r-- 1 root root 47952 May 20 19:30 Microsoft.Extensions.FileSystemGlobbing.dll
|
||||
2026-07-04T13:12:11.5126508Z -rwxr--r-- 1 root root 93008 May 20 19:37 Microsoft.Extensions.Http.dll
|
||||
2026-07-04T13:12:11.5126638Z -rwxr--r-- 1 root root 19576 Mar 25 2023 Microsoft.Extensions.Localization.Abstractions.dll
|
||||
2026-07-04T13:12:11.5126726Z -rwxr--r-- 1 root root 31872 Mar 25 2023 Microsoft.Extensions.Localization.dll
|
||||
2026-07-04T13:12:11.5126838Z -rwxr--r-- 1 root root 66896 May 20 19:29 Microsoft.Extensions.Logging.Abstractions.dll
|
||||
2026-07-04T13:12:11.5127279Z -rwxr--r-- 1 root root 51536 May 20 19:33 Microsoft.Extensions.Logging.dll
|
||||
2026-07-04T13:12:11.5127448Z -rwxr--r-- 1 root root 21840 May 20 19:33 Microsoft.Extensions.Options.ConfigurationExtensions.dll
|
||||
2026-07-04T13:12:11.5127557Z -rwxr--r-- 1 root root 65360 May 20 19:30 Microsoft.Extensions.Options.dll
|
||||
2026-07-04T13:12:11.5127641Z -rwxr--r-- 1 root root 44880 May 20 19:29 Microsoft.Extensions.Primitives.dll
|
||||
2026-07-04T13:12:11.5127762Z -rwxr--r-- 1 root root 43344 May 20 20:19 Microsoft.Extensions.Validation.dll
|
||||
2026-07-04T13:12:11.5128076Z -rwxr--r-- 1 root root 19296 Jun 2 20:51 Microsoft.IdentityModel.Abstractions.dll
|
||||
2026-07-04T13:12:11.5128260Z -rwxr--r-- 1 root root 172856 Jun 2 20:52 Microsoft.IdentityModel.JsonWebTokens.dll
|
||||
2026-07-04T13:12:11.5128386Z -rwxr--r-- 1 root root 38200 Jun 2 20:51 Microsoft.IdentityModel.Logging.dll
|
||||
2026-07-04T13:12:11.5128474Z -rwxr--r-- 1 root root 407352 Jun 2 20:51 Microsoft.IdentityModel.Tokens.dll
|
||||
2026-07-04T13:12:11.5128571Z -rwxr--r-- 1 root root 24912 May 20 20:20 Microsoft.JSInterop.WebAssembly.dll
|
||||
2026-07-04T13:12:11.5128871Z -rwxr--r-- 1 root root 75088 May 20 20:19 Microsoft.JSInterop.dll
|
||||
2026-07-04T13:12:11.5129071Z -rwxr--r-- 1 root root 428880 May 20 18:30 Microsoft.VisualBasic.Core.dll
|
||||
2026-07-04T13:12:11.5129181Z -rwxr--r-- 1 root root 17232 May 20 18:31 Microsoft.VisualBasic.dll
|
||||
2026-07-04T13:12:11.5129269Z -rwxr--r-- 1 root root 15696 May 20 18:29 Microsoft.Win32.Primitives.dll
|
||||
2026-07-04T13:12:11.5129378Z -rwxr--r-- 1 root root 33104 May 20 18:29 Microsoft.Win32.Registry.dll
|
||||
2026-07-04T13:12:11.5129670Z -rwxr--r-- 1 root root 9108480 Sep 14 2023 MudBlazor.dll
|
||||
2026-07-04T13:12:11.5129849Z -rwxr--r-- 1 root root 15184 May 20 18:31 System.AppContext.dll
|
||||
2026-07-04T13:12:11.5129943Z -rwxr--r-- 1 root root 15184 May 20 18:30 System.Buffers.dll
|
||||
2026-07-04T13:12:11.5130058Z -rwxr--r-- 1 root root 88912 May 20 18:29 System.Collections.Concurrent.dll
|
||||
2026-07-04T13:12:11.5130158Z -rwxr--r-- 1 root root 251216 May 20 18:29 System.Collections.Immutable.dll
|
||||
2026-07-04T13:12:11.5130255Z -rwxr--r-- 1 root root 47952 May 20 18:29 System.Collections.NonGeneric.dll
|
||||
2026-07-04T13:12:11.5130647Z -rwxr--r-- 1 root root 47952 May 20 18:29 System.Collections.Specialized.dll
|
||||
2026-07-04T13:12:11.5130743Z -rwxr--r-- 1 root root 112976 May 20 18:28 System.Collections.dll
|
||||
2026-07-04T13:12:11.5130828Z -rwxr--r-- 1 root root 102736 May 20 18:31 System.ComponentModel.Annotations.dll
|
||||
2026-07-04T13:12:11.5130924Z -rwxr--r-- 1 root root 16720 May 20 18:31 System.ComponentModel.DataAnnotations.dll
|
||||
2026-07-04T13:12:11.5131645Z -rwxr--r-- 1 root root 26448 May 20 18:29 System.ComponentModel.EventBasedAsync.dll
|
||||
2026-07-04T13:12:11.5131859Z -rwxr--r-- 1 root root 42320 May 20 18:29 System.ComponentModel.Primitives.dll
|
||||
2026-07-04T13:12:11.5131956Z -rwxr--r-- 1 root root 316752 May 20 18:31 System.ComponentModel.TypeConverter.dll
|
||||
2026-07-04T13:12:11.5132341Z -rwxr--r-- 1 root root 16208 May 20 18:29 System.ComponentModel.dll
|
||||
2026-07-04T13:12:11.5132542Z -rwxr--r-- 1 root root 19280 May 20 18:31 System.Configuration.dll
|
||||
2026-07-04T13:12:11.5132640Z -rwxr--r-- 1 root root 54096 May 20 18:31 System.Console.dll
|
||||
2026-07-04T13:12:11.5132722Z -rwxr--r-- 1 root root 23376 May 20 18:31 System.Core.dll
|
||||
2026-07-04T13:12:11.5132831Z -rwxr--r-- 1 root root 1018192 May 20 18:31 System.Data.Common.dll
|
||||
2026-07-04T13:12:11.5132916Z -rwxr--r-- 1 root root 15696 May 20 18:31 System.Data.DataSetExtensions.dll
|
||||
2026-07-04T13:12:11.5133215Z -rwxr--r-- 1 root root 25424 May 20 18:31 System.Data.dll
|
||||
2026-07-04T13:12:11.5133381Z -rwxr--r-- 1 root root 16208 May 20 18:31 System.Diagnostics.Contracts.dll
|
||||
2026-07-04T13:12:11.5133484Z -rwxr--r-- 1 root root 15696 May 20 18:30 System.Diagnostics.Debug.dll
|
||||
2026-07-04T13:12:11.5133567Z -rwxr--r-- 1 root root 202576 May 20 18:29 System.Diagnostics.DiagnosticSource.dll
|
||||
2026-07-04T13:12:11.5133677Z -rwxr--r-- 1 root root 22864 May 20 18:29 System.Diagnostics.FileVersionInfo.dll
|
||||
2026-07-04T13:12:11.5134356Z -rwxr--r-- 1 root root 56656 May 20 18:29 System.Diagnostics.Process.dll
|
||||
2026-07-04T13:12:11.5134723Z -rwxr--r-- 1 root root 25936 May 20 18:29 System.Diagnostics.StackTrace.dll
|
||||
2026-07-04T13:12:11.5135379Z -rwxr--r-- 1 root root 31568 May 20 18:31 System.Diagnostics.TextWriterTraceListener.dll
|
||||
2026-07-04T13:12:11.5135651Z -rwxr--r-- 1 root root 15184 May 20 18:30 System.Diagnostics.Tools.dll
|
||||
2026-07-04T13:12:11.5135753Z -rwxr--r-- 1 root root 58704 May 20 18:29 System.Diagnostics.TraceSource.dll
|
||||
2026-07-04T13:12:11.5135850Z -rwxr--r-- 1 root root 16208 May 20 18:28 System.Diagnostics.Tracing.dll
|
||||
2026-07-04T13:12:11.5136781Z -rwxr--r-- 1 root root 64848 May 20 18:29 System.Drawing.Primitives.dll
|
||||
2026-07-04T13:12:11.5137551Z -rwxr--r-- 1 root root 20304 May 20 18:31 System.Drawing.dll
|
||||
2026-07-04T13:12:11.5137863Z -rwxr--r-- 1 root root 16208 May 20 18:30 System.Dynamic.Runtime.dll
|
||||
2026-07-04T13:12:11.5138051Z -rwxr--r-- 1 root root 97104 May 20 18:29 System.Formats.Asn1.dll
|
||||
2026-07-04T13:12:11.5138659Z -rwxr--r-- 1 root root 38736 May 20 18:29 System.Formats.Tar.dll
|
||||
2026-07-04T13:12:11.5138889Z -rwxr--r-- 1 root root 15696 May 20 18:30 System.Globalization.Calendars.dll
|
||||
2026-07-04T13:12:11.5139082Z -rwxr--r-- 1 root root 15184 May 20 18:30 System.Globalization.Extensions.dll
|
||||
2026-07-04T13:12:11.5139781Z -rwxr--r-- 1 root root 15696 May 20 18:30 System.Globalization.dll
|
||||
2026-07-04T13:12:11.5139966Z -rwxr--r-- 1 root root 28496 May 20 18:29 System.IO.Compression.Brotli.dll
|
||||
2026-07-04T13:12:11.5140482Z -rwxr--r-- 1 root root 15184 May 20 18:31 System.IO.Compression.FileSystem.dll
|
||||
2026-07-04T13:12:11.5140771Z -rwxr--r-- 1 root root 53584 May 20 18:29 System.IO.Compression.ZipFile.dll
|
||||
2026-07-04T13:12:11.5141448Z -rwxr--r-- 1 root root 167760 May 20 18:31 System.IO.Compression.dll
|
||||
2026-07-04T13:12:11.5141798Z -rwxr--r-- 1 root root 32080 May 20 18:29 System.IO.FileSystem.AccessControl.dll
|
||||
2026-07-04T13:12:11.5141987Z -rwxr--r-- 1 root root 23888 May 20 18:29 System.IO.FileSystem.DriveInfo.dll
|
||||
2026-07-04T13:12:11.5142508Z -rwxr--r-- 1 root root 15184 May 20 18:30 System.IO.FileSystem.Primitives.dll
|
||||
2026-07-04T13:12:11.5142806Z -rwxr--r-- 1 root root 33104 May 20 18:29 System.IO.FileSystem.Watcher.dll
|
||||
2026-07-04T13:12:11.5142973Z -rwxr--r-- 1 root root 15696 May 20 18:30 System.IO.FileSystem.dll
|
||||
2026-07-04T13:12:11.5143485Z -rwxr--r-- 1 root root 35152 May 20 18:30 System.IO.IsolatedStorage.dll
|
||||
2026-07-04T13:12:11.5143788Z -rwxr--r-- 1 root root 50000 May 20 18:31 System.IO.MemoryMappedFiles.dll
|
||||
2026-07-04T13:12:11.5143959Z -rwxr--r-- 1 root root 78160 May 20 18:29 System.IO.Pipelines.dll
|
||||
2026-07-04T13:12:11.5144578Z -rwxr--r-- 1 root root 23376 May 20 18:29 System.IO.Pipes.AccessControl.dll
|
||||
2026-07-04T13:12:11.5144792Z -rwxr--r-- 1 root root 42320 May 20 18:29 System.IO.Pipes.dll
|
||||
2026-07-04T13:12:11.5146319Z -rwxr--r-- 1 root root 15696 May 20 18:30 System.IO.UnmanagedMemoryStream.dll
|
||||
2026-07-04T13:12:11.5146840Z -rwxr--r-- 1 root root 15696 May 20 18:30 System.IO.dll
|
||||
2026-07-04T13:12:11.5146996Z -rwxr--r-- 1 root root 92000 Jun 2 20:51 System.IdentityModel.Tokens.Jwt.dll
|
||||
2026-07-04T13:12:11.5147089Z -rwxr--r-- 1 root root 456528 May 20 18:29 System.Linq.AsyncEnumerable.dll
|
||||
2026-07-04T13:12:11.5147186Z -rwxr--r-- 1 root root 575312 May 20 18:29 System.Linq.Expressions.dll
|
||||
2026-07-04T13:12:11.5147308Z -rwxr--r-- 1 root root 223056 May 20 18:31 System.Linq.Parallel.dll
|
||||
2026-07-04T13:12:11.5147710Z -rwxr--r-- 1 root root 78672 May 20 18:31 System.Linq.Queryable.dll
|
||||
2026-07-04T13:12:11.5147887Z -rwxr--r-- 1 root root 201040 May 20 18:29 System.Linq.dll
|
||||
2026-07-04T13:12:11.5148086Z -rwxr--r-- 1 root root 55632 May 20 18:28 System.Memory.dll
|
||||
2026-07-04T13:12:11.5148201Z -rwxr--r-- 1 root root 56144 May 20 18:30 System.Net.Http.Json.dll
|
||||
2026-07-04T13:12:11.5148334Z -rwxr--r-- 1 root root 296272 May 20 18:31 System.Net.Http.dll
|
||||
2026-07-04T13:12:11.5148477Z -rwxr--r-- 1 root root 56144 May 20 18:30 System.Net.HttpListener.dll
|
||||
2026-07-04T13:12:11.5148679Z -rwxr--r-- 1 root root 105296 May 20 18:31 System.Net.Mail.dll
|
||||
2026-07-04T13:12:11.5148845Z -rwxr--r-- 1 root root 24400 May 20 18:31 System.Net.NameResolution.dll
|
||||
2026-07-04T13:12:11.5148996Z -rwxr--r-- 1 root root 42320 May 20 18:29 System.Net.NetworkInformation.dll
|
||||
2026-07-04T13:12:11.5149136Z -rwxr--r-- 1 root root 27984 May 20 18:29 System.Net.Ping.dll
|
||||
2026-07-04T13:12:11.5149310Z -rwxr--r-- 1 root root 107344 May 20 18:31 System.Net.Primitives.dll
|
||||
2026-07-04T13:12:11.5149483Z -rwxr--r-- 1 root root 39248 May 20 18:30 System.Net.Quic.dll
|
||||
2026-07-04T13:12:11.5149644Z -rwxr--r-- 1 root root 65872 May 20 18:30 System.Net.Requests.dll
|
||||
2026-07-04T13:12:11.5150001Z -rwxr--r-- 1 root root 114512 May 20 18:30 System.Net.Security.dll
|
||||
2026-07-04T13:12:11.5150166Z -rwxr--r-- 1 root root 40784 May 20 18:29 System.Net.ServerSentEvents.dll
|
||||
2026-07-04T13:12:11.5150253Z -rwxr--r-- 1 root root 15184 May 20 18:31 System.Net.ServicePoint.dll
|
||||
2026-07-04T13:12:11.5150402Z -rwxr--r-- 1 root root 74576 May 20 18:29 System.Net.Sockets.dll
|
||||
2026-07-04T13:12:11.5150564Z -rwxr--r-- 1 root root 56144 May 20 18:31 System.Net.WebClient.dll
|
||||
2026-07-04T13:12:11.5150716Z -rwxr--r-- 1 root root 33104 May 20 18:29 System.Net.WebHeaderCollection.dll
|
||||
2026-07-04T13:12:11.5150889Z -rwxr--r-- 1 root root 21840 May 20 18:31 System.Net.WebProxy.dll
|
||||
2026-07-04T13:12:11.5151238Z -rwxr--r-- 1 root root 52560 May 20 18:31 System.Net.WebSockets.Client.dll
|
||||
2026-07-04T13:12:11.5151418Z -rwxr--r-- 1 root root 108880 May 20 18:31 System.Net.WebSockets.dll
|
||||
2026-07-04T13:12:11.5151578Z -rwxr--r-- 1 root root 17232 May 20 18:31 System.Net.dll
|
||||
2026-07-04T13:12:11.5151715Z -rwxr--r-- 1 root root 15696 May 20 18:28 System.Numerics.Vectors.dll
|
||||
2026-07-04T13:12:11.5151802Z -rwxr--r-- 1 root root 15184 May 20 18:31 System.Numerics.dll
|
||||
2026-07-04T13:12:11.5151933Z -rwxr--r-- 1 root root 41296 May 20 18:29 System.ObjectModel.dll
|
||||
2026-07-04T13:12:11.5152093Z -rwxr--r-- 1 root root 4880208 May 20 18:19 System.Private.CoreLib.dll
|
||||
2026-07-04T13:12:11.5152307Z -rwxr--r-- 1 root root 859472 May 20 18:31 System.Private.DataContractSerialization.dll
|
||||
2026-07-04T13:12:11.5152488Z -rwxr--r-- 1 root root 105808 May 20 18:28 System.Private.Uri.dll
|
||||
2026-07-04T13:12:11.5152692Z -rwxr--r-- 1 root root 153936 May 20 18:30 System.Private.Xml.Linq.dll
|
||||
2026-07-04T13:12:11.5152975Z -rwxr--r-- 1 root root 3106128 May 20 18:30 System.Private.Xml.dll
|
||||
2026-07-04T13:12:11.5153163Z -rwxr--r-- 1 root root 38224 May 20 18:31 System.Reflection.DispatchProxy.dll
|
||||
2026-07-04T13:12:11.5153372Z -rwxr--r-- 1 root root 15696 May 20 18:29 System.Reflection.Emit.ILGeneration.dll
|
||||
2026-07-04T13:12:11.5153608Z -rwxr--r-- 1 root root 15696 May 20 18:29 System.Reflection.Emit.Lightweight.dll
|
||||
2026-07-04T13:12:11.5153879Z -rwxr--r-- 1 root root 133456 May 20 18:29 System.Reflection.Emit.dll
|
||||
2026-07-04T13:12:11.5153978Z -rwxr--r-- 1 root root 15184 May 20 18:30 System.Reflection.Extensions.dll
|
||||
2026-07-04T13:12:11.5154089Z -rwxr--r-- 1 root root 503632 May 20 18:29 System.Reflection.Metadata.dll
|
||||
2026-07-04T13:12:11.5154210Z -rwxr--r-- 1 root root 15696 May 20 18:28 System.Reflection.Primitives.dll
|
||||
2026-07-04T13:12:11.5154364Z -rwxr--r-- 1 root root 24400 May 20 18:31 System.Reflection.TypeExtensions.dll
|
||||
2026-07-04T13:12:11.5154528Z -rwxr--r-- 1 root root 16208 May 20 18:31 System.Reflection.dll
|
||||
2026-07-04T13:12:11.5154682Z -rwxr--r-- 1 root root 15184 May 20 18:31 System.Resources.Reader.dll
|
||||
2026-07-04T13:12:11.5154910Z -rwxr--r-- 1 root root 15696 May 20 18:30 System.Resources.ResourceManager.dll
|
||||
2026-07-04T13:12:11.5155087Z -rwxr--r-- 1 root root 26960 May 20 18:31 System.Resources.Writer.dll
|
||||
2026-07-04T13:12:11.5155192Z -rwxr--r-- 1 root root 15184 May 20 18:30 System.Runtime.CompilerServices.Unsafe.dll
|
||||
2026-07-04T13:12:11.5155391Z -rwxr--r-- 1 root root 17232 May 20 18:30 System.Runtime.CompilerServices.VisualC.dll
|
||||
2026-07-04T13:12:11.5155579Z -rwxr--r-- 1 root root 17744 May 20 18:30 System.Runtime.Extensions.dll
|
||||
2026-07-04T13:12:11.5155669Z -rwxr--r-- 1 root root 15696 May 20 18:30 System.Runtime.Handles.dll
|
||||
2026-07-04T13:12:11.5155753Z -rwxr--r-- 1 root root 89936 May 20 18:31 System.Runtime.InteropServices.JavaScript.dll
|
||||
2026-07-04T13:12:11.5155878Z -rwxr--r-- 1 root root 15184 May 20 18:31 System.Runtime.InteropServices.RuntimeInformation.dll
|
||||
2026-07-04T13:12:11.5156045Z -rwxr--r-- 1 root root 64848 May 20 18:29 System.Runtime.InteropServices.dll
|
||||
2026-07-04T13:12:11.5158325Z -rwxr--r-- 1 root root 17232 May 20 18:29 System.Runtime.Intrinsics.dll
|
||||
2026-07-04T13:12:11.5158590Z -rwxr--r-- 1 root root 15696 May 20 18:29 System.Runtime.Loader.dll
|
||||
2026-07-04T13:12:11.5158772Z -rwxr--r-- 1 root root 145232 May 20 18:29 System.Runtime.Numerics.dll
|
||||
2026-07-04T13:12:11.5158922Z -rwxr--r-- 1 root root 65872 May 20 18:29 System.Runtime.Serialization.Formatters.dll
|
||||
2026-07-04T13:12:11.5159089Z -rwxr--r-- 1 root root 15696 May 20 18:31 System.Runtime.Serialization.Json.dll
|
||||
2026-07-04T13:12:11.5159322Z -rwxr--r-- 1 root root 23376 May 20 18:30 System.Runtime.Serialization.Primitives.dll
|
||||
2026-07-04T13:12:11.5159494Z -rwxr--r-- 1 root root 16720 May 20 18:31 System.Runtime.Serialization.Xml.dll
|
||||
2026-07-04T13:12:11.5159654Z -rwxr--r-- 1 root root 17232 May 20 18:31 System.Runtime.Serialization.dll
|
||||
2026-07-04T13:12:11.5159852Z -rwxr--r-- 1 root root 44880 May 20 18:28 System.Runtime.dll
|
||||
2026-07-04T13:12:11.5160010Z -rwxr--r-- 1 root root 58192 May 20 18:29 System.Security.AccessControl.dll
|
||||
2026-07-04T13:12:11.5160171Z -rwxr--r-- 1 root root 55120 May 20 18:29 System.Security.Claims.dll
|
||||
2026-07-04T13:12:11.5160331Z -rwxr--r-- 1 root root 17232 May 20 18:31 System.Security.Cryptography.Algorithms.dll
|
||||
2026-07-04T13:12:11.5249058Z -rwxr--r-- 1 root root 16208 May 20 18:31 System.Security.Cryptography.Cng.dll
|
||||
2026-07-04T13:12:11.5249347Z -rwxr--r-- 1 root root 16208 May 20 18:31 System.Security.Cryptography.Csp.dll
|
||||
2026-07-04T13:12:11.5249535Z -rwxr--r-- 1 root root 15696 May 20 18:31 System.Security.Cryptography.Encoding.dll
|
||||
2026-07-04T13:12:11.5249760Z -rwxr--r-- 1 root root 15696 May 20 18:31 System.Security.Cryptography.OpenSsl.dll
|
||||
2026-07-04T13:12:11.5250033Z -rwxr--r-- 1 root root 15696 May 20 18:31 System.Security.Cryptography.Primitives.dll
|
||||
2026-07-04T13:12:11.5250216Z -rwxr--r-- 1 root root 16720 May 20 18:31 System.Security.Cryptography.X509Certificates.dll
|
||||
2026-07-04T13:12:11.5250674Z -rwxr--r-- 1 root root 654160 May 20 18:31 System.Security.Cryptography.dll
|
||||
2026-07-04T13:12:11.5250967Z -rwxr--r-- 1 root root 37712 May 20 18:29 System.Security.Principal.Windows.dll
|
||||
2026-07-04T13:12:11.5251315Z -rwxr--r-- 1 root root 15184 May 20 18:31 System.Security.Principal.dll
|
||||
2026-07-04T13:12:11.5251503Z -rwxr--r-- 1 root root 15696 May 20 18:30 System.Security.SecureString.dll
|
||||
2026-07-04T13:12:11.5251716Z -rwxr--r-- 1 root root 18256 May 20 18:31 System.Security.dll
|
||||
2026-07-04T13:12:11.5251870Z -rwxr--r-- 1 root root 16720 May 20 18:31 System.ServiceModel.Web.dll
|
||||
2026-07-04T13:12:11.5251960Z -rwxr--r-- 1 root root 15696 May 20 18:31 System.ServiceProcess.dll
|
||||
2026-07-04T13:12:11.5252050Z -rwxr--r-- 1 root root 742736 May 20 18:29 System.Text.Encoding.CodePages.dll
|
||||
2026-07-04T13:12:11.5252249Z -rwxr--r-- 1 root root 15696 May 20 18:28 System.Text.Encoding.Extensions.dll
|
||||
2026-07-04T13:12:11.5252419Z -rwxr--r-- 1 root root 15696 May 20 18:30 System.Text.Encoding.dll
|
||||
2026-07-04T13:12:11.5252574Z -rwxr--r-- 1 root root 65872 May 20 18:32 System.Text.Encodings.Web.dll
|
||||
2026-07-04T13:12:11.5252760Z -rwxr--r-- 1 root root 649040 May 20 18:30 System.Text.Json.dll
|
||||
2026-07-04T13:12:11.5252867Z -rwxr--r-- 1 root root 384848 May 20 18:30 System.Text.RegularExpressions.dll
|
||||
2026-07-04T13:12:11.5253087Z -rwxr--r-- 1 root root 33616 May 20 18:29 System.Threading.AccessControl.dll
|
||||
2026-07-04T13:12:11.5253251Z -rwxr--r-- 1 root root 66384 May 20 18:29 System.Threading.Channels.dll
|
||||
2026-07-04T13:12:11.5253433Z -rwxr--r-- 1 root root 15696 May 20 18:28 System.Threading.Overlapped.dll
|
||||
2026-07-04T13:12:11.5253553Z -rwxr--r-- 1 root root 185680 May 20 18:29 System.Threading.Tasks.Dataflow.dll
|
||||
2026-07-04T13:12:11.5253654Z -rwxr--r-- 1 root root 15696 May 20 18:30 System.Threading.Tasks.Extensions.dll
|
||||
2026-07-04T13:12:11.5253817Z -rwxr--r-- 1 root root 61264 May 20 18:31 System.Threading.Tasks.Parallel.dll
|
||||
2026-07-04T13:12:11.5253978Z -rwxr--r-- 1 root root 16720 May 20 18:31 System.Threading.Tasks.dll
|
||||
2026-07-04T13:12:11.5254084Z -rwxr--r-- 1 root root 15696 May 20 18:28 System.Threading.Thread.dll
|
||||
2026-07-04T13:12:11.5254257Z -rwxr--r-- 1 root root 15696 May 20 18:28 System.Threading.ThreadPool.dll
|
||||
2026-07-04T13:12:11.5254445Z -rwxr--r-- 1 root root 15184 May 20 18:30 System.Threading.Timer.dll
|
||||
2026-07-04T13:12:11.5254568Z -rwxr--r-- 1 root root 44880 May 20 18:28 System.Threading.dll
|
||||
2026-07-04T13:12:11.5254651Z -rwxr--r-- 1 root root 175952 May 20 18:30 System.Transactions.Local.dll
|
||||
2026-07-04T13:12:11.5254735Z -rwxr--r-- 1 root root 16720 May 20 18:31 System.Transactions.dll
|
||||
2026-07-04T13:12:11.5254887Z -rwxr--r-- 1 root root 15696 May 20 18:30 System.ValueTuple.dll
|
||||
2026-07-04T13:12:11.5255064Z -rwxr--r-- 1 root root 30032 May 20 18:31 System.Web.HttpUtility.dll
|
||||
2026-07-04T13:12:11.5255242Z -rwxr--r-- 1 root root 15184 May 20 18:31 System.Web.dll
|
||||
2026-07-04T13:12:11.5255373Z -rwxr--r-- 1 root root 15696 May 20 18:31 System.Windows.dll
|
||||
2026-07-04T13:12:11.5255459Z -rwxr--r-- 1 root root 15696 May 20 18:31 System.Xml.Linq.dll
|
||||
2026-07-04T13:12:11.5255542Z -rwxr--r-- 1 root root 21840 May 20 18:30 System.Xml.ReaderWriter.dll
|
||||
2026-07-04T13:12:11.5255636Z -rwxr--r-- 1 root root 16208 May 20 18:31 System.Xml.Serialization.dll
|
||||
2026-07-04T13:12:11.5255720Z -rwxr--r-- 1 root root 15696 May 20 18:31 System.Xml.XDocument.dll
|
||||
2026-07-04T13:12:11.5255803Z -rwxr--r-- 1 root root 15696 May 20 18:31 System.Xml.XPath.XDocument.dll
|
||||
2026-07-04T13:12:11.5255959Z -rwxr--r-- 1 root root 15696 May 20 18:30 System.Xml.XPath.dll
|
||||
2026-07-04T13:12:11.5256266Z -rwxr--r-- 1 root root 15696 May 20 18:31 System.Xml.XmlDocument.dll
|
||||
2026-07-04T13:12:11.5256385Z -rwxr--r-- 1 root root 17744 May 20 18:31 System.Xml.XmlSerializer.dll
|
||||
2026-07-04T13:12:11.5256471Z -rwxr--r-- 1 root root 23376 May 20 18:31 System.Xml.dll
|
||||
2026-07-04T13:12:11.5256551Z -rwxr--r-- 1 root root 50000 May 20 18:32 System.dll
|
||||
2026-07-04T13:12:11.5256633Z -rw-r--r-- 1 root root 156672 Jul 4 13:09 TaxBaik.Application.dll
|
||||
2026-07-04T13:12:11.5256719Z -rw-r--r-- 1 root root 38472 Jul 4 13:09 TaxBaik.Application.pdb
|
||||
2026-07-04T13:12:11.5256811Z -rw-r--r-- 1 root root 37888 Jul 4 13:09 TaxBaik.Domain.dll
|
||||
2026-07-04T13:12:11.5256910Z -rw-r--r-- 1 root root 24100 Jul 4 13:09 TaxBaik.Domain.pdb
|
||||
2026-07-04T13:12:11.5257068Z -rw-r--r-- 1 root root 713216 Jul 4 13:11 TaxBaik.Web.Client.dll
|
||||
2026-07-04T13:12:11.5257222Z -rw-r--r-- 1 root root 373100 Jul 4 13:11 TaxBaik.Web.Client.pdb
|
||||
2026-07-04T13:12:11.5257428Z -rw-r--r-- 1 root root 2546 Jul 4 13:11 TaxBaik.Web.Client.runtimeconfig.json
|
||||
2026-07-04T13:12:11.5257634Z -rw-r--r-- 1 root root 1007340 Jul 4 13:11 TaxBaik.Web.Client.staticwebassets.endpoints.json
|
||||
2026-07-04T13:12:11.5257811Z -rw-r--r-- 1 root root 78064 Jul 4 13:11 TaxBaik.Web.Client.staticwebassets.runtime.json
|
||||
2026-07-04T13:12:11.5257978Z -rwxr--r-- 1 root root 16208 May 20 18:31 WindowsBase.dll
|
||||
2026-07-04T13:12:11.5258134Z -rwxr--r-- 1 root root 37898 May 20 18:42 dotnet.js
|
||||
2026-07-04T13:12:11.5258235Z -rwxr--r-- 1 root root 51818 May 20 18:42 dotnet.js.map
|
||||
2026-07-04T13:12:11.5258317Z -rwxr--r-- 1 root root 145050 May 20 18:43 dotnet.native.js
|
||||
2026-07-04T13:12:11.5258513Z -rwxr--r-- 1 root root 3002101 May 20 18:43 dotnet.native.wasm
|
||||
2026-07-04T13:12:11.5258681Z -rwxr--r-- 1 root root 198479 May 20 18:42 dotnet.runtime.js
|
||||
2026-07-04T13:12:11.5258817Z -rwxr--r-- 1 root root 276757 May 20 18:42 dotnet.runtime.js.map
|
||||
2026-07-04T13:12:11.5258967Z -rwxr--r-- 1 root root 956416 Apr 2 19:04 icudt_CJK.dat
|
||||
2026-07-04T13:12:11.5259148Z -rwxr--r-- 1 root root 550832 Apr 2 19:04 icudt_EFIGS.dat
|
||||
2026-07-04T13:12:11.5259308Z -rwxr--r-- 1 root root 1107168 Apr 2 19:04 icudt_no_CJK.dat
|
||||
2026-07-04T13:12:11.5259459Z -rwxr--r-- 1 root root 59728 May 20 18:31 mscorlib.dll
|
||||
2026-07-04T13:12:11.5259612Z -rwxr--r-- 1 root root 100688 May 20 18:32 netstandard.dll
|
||||
2026-07-04T13:12:11.5259759Z drwxr-xr-x 3 root root 4096 Jul 4 13:11 wwwroot
|
||||
2026-07-04T13:12:11.5260131Z total 4352
|
||||
2026-07-04T13:12:11.5260302Z drwxr-xr-x 8 root root 4096 Jul 4 13:11 .
|
||||
2026-07-04T13:12:11.5260470Z drwxr-xr-x 3 root root 4096 Jul 4 13:11 ..
|
||||
2026-07-04T13:12:11.5260626Z -rw-r--r-- 1 root root 196 Jul 4 13:11 .NETCoreApp,Version=v10.0.AssemblyAttributes.cs
|
||||
2026-07-04T13:12:11.5260784Z -rw-r--r-- 1 root root 137 Jul 4 13:11 EmbeddedAttribute.cs
|
||||
2026-07-04T13:12:11.5260941Z -rw-r--r-- 1 root root 0 Jul 4 13:11 TaxBaik..C36EE7CA.Up2Date
|
||||
2026-07-04T13:12:11.5261222Z -rw-r--r-- 1 root root 1008 Jul 4 13:11 TaxBaik.Web.Client.AssemblyInfo.cs
|
||||
2026-07-04T13:12:11.5261405Z -rw-r--r-- 1 root root 65 Jul 4 13:11 TaxBaik.Web.Client.AssemblyInfoInputs.cache
|
||||
2026-07-04T13:12:11.5261503Z -rw-r--r-- 1 root root 16355 Jul 4 13:11 TaxBaik.Web.Client.GeneratedMSBuildEditorConfig.editorconfig
|
||||
2026-07-04T13:12:11.5261615Z -rw-r--r-- 1 root root 433 Jul 4 13:11 TaxBaik.Web.Client.GlobalUsings.g.cs
|
||||
2026-07-04T13:12:11.5261703Z -rw-r--r-- 1 root root 0 Jul 4 13:11 TaxBaik.Web.Client.MvcApplicationPartsAssemblyInfo.cache
|
||||
2026-07-04T13:12:11.5261801Z -rw-r--r-- 1 root root 27329 Jul 4 13:11 TaxBaik.Web.Client.assets.cache
|
||||
2026-07-04T13:12:11.5261889Z -rw-r--r-- 1 root root 20456 Jul 4 13:11 TaxBaik.Web.Client.csproj.AssemblyReference.cache
|
||||
2026-07-04T13:12:11.5262031Z -rw-r--r-- 1 root root 65 Jul 4 13:11 TaxBaik.Web.Client.csproj.CoreCompileInputs.cache
|
||||
2026-07-04T13:12:11.5262202Z -rw-r--r-- 1 root root 136894 Jul 4 13:11 TaxBaik.Web.Client.csproj.FileListAbsolute.txt
|
||||
2026-07-04T13:12:11.5262340Z -rw-r--r-- 1 root root 713216 Jul 4 13:11 TaxBaik.Web.Client.dll
|
||||
2026-07-04T13:12:11.5262508Z -rw-r--r-- 1 root root 65 Jul 4 13:11 TaxBaik.Web.Client.genruntimeconfig.cache
|
||||
2026-07-04T13:12:11.5262662Z -rw-r--r-- 1 root root 373100 Jul 4 13:11 TaxBaik.Web.Client.pdb
|
||||
2026-07-04T13:12:11.5262819Z -rw-r--r-- 1 root root 276 Jul 4 13:11 ValidatableTypeAttribute.cs
|
||||
2026-07-04T13:12:11.5263027Z -rw-r--r-- 1 root root 3 Jul 4 13:11 blazor.build.boot-extension.json
|
||||
2026-07-04T13:12:11.5263190Z drwxr-xr-x 2 root root 20480 Jul 4 13:11 compressed
|
||||
2026-07-04T13:12:11.5263357Z -rw-r--r-- 1 root root 93392 Jul 4 13:11 dotnet.js
|
||||
2026-07-04T13:12:11.5263509Z -rw-r--r-- 1 root root 276597 Jul 4 13:11 rbcswa.dswa.cache.json
|
||||
2026-07-04T13:12:11.5263673Z drwxr-xr-x 2 root root 4096 Jul 4 13:11 ref
|
||||
2026-07-04T13:12:11.5263819Z drwxr-xr-x 2 root root 4096 Jul 4 13:11 refint
|
||||
2026-07-04T13:12:11.5263960Z -rw-r--r-- 1 root root 322 Jul 4 13:11 rjimswa.dswa.cache.json
|
||||
2026-07-04T13:12:11.5264120Z -rw-r--r-- 1 root root 3444 Jul 4 13:11 rjsmcshtml.dswa.cache.json
|
||||
2026-07-04T13:12:11.5264302Z -rw-r--r-- 1 root root 3444 Jul 4 13:11 rjsmrazor.dswa.cache.json
|
||||
2026-07-04T13:12:11.5264451Z -rw-r--r-- 1 root root 4223 Jul 4 13:11 rpswa.dswa.cache.json
|
||||
2026-07-04T13:12:11.5264608Z drwxr-xr-x 2 root root 4096 Jul 4 13:11 staticwebassets
|
||||
2026-07-04T13:12:11.5264772Z -rw-r--r-- 1 root root 1007340 Jul 4 13:11 staticwebassets.build.endpoints.json
|
||||
2026-07-04T13:12:11.5265812Z -rw-r--r-- 1 root root 1564906 Jul 4 13:11 staticwebassets.build.json
|
||||
2026-07-04T13:12:11.5266004Z -rw-r--r-- 1 root root 44 Jul 4 13:11 staticwebassets.build.json.cache
|
||||
2026-07-04T13:12:11.5266531Z -rw-r--r-- 1 root root 78064 Jul 4 13:11 staticwebassets.development.json
|
||||
2026-07-04T13:12:11.5266758Z -rw-r--r-- 1 root root 0 Jul 4 13:11 swae.build.ex.cache
|
||||
2026-07-04T13:12:11.5266937Z drwxr-xr-x 2 root root 4096 Jul 4 13:11 tmp-webcil
|
||||
2026-07-04T13:12:11.5267094Z drwxr-xr-x 2 root root 20480 Jul 4 13:11 webcil
|
||||
2026-07-04T13:13:34.6836583Z ✓ Publish Web elapsed: 153s
|
||||
2026-07-04T13:13:34.6849747Z -rw-r--r-- 1 root root 2.2M Jul 4 13:13 ./publish-logs/publish-web.binlog
|
||||
2026-07-04T13:13:34.7923863Z ::group::Run set -e
|
||||
2026-07-04T13:13:34.7924256Z set -e
|
||||
2026-07-04T13:13:34.7924366Z mkdir -p ./publish-logs
|
||||
2026-07-04T13:13:34.7924451Z # Proxy is not part of the solution restore graph, so restore it once
|
||||
2026-07-04T13:13:34.7924551Z # here before publishing to avoid NETSDK1004 in CI.
|
||||
2026-07-04T13:13:34.7924841Z dotnet restore src/TaxBaik.Proxy/
|
||||
2026-07-04T13:13:34.7924955Z dotnet build src/TaxBaik.Proxy/TaxBaik.Proxy.csproj -c Release --no-restore
|
||||
2026-07-04T13:13:34.7925079Z start=$(date +%s)
|
||||
2026-07-04T13:13:34.7925160Z dotnet publish src/TaxBaik.Proxy/ \
|
||||
2026-07-04T13:13:34.7925249Z -c Release \
|
||||
2026-07-04T13:13:34.7925335Z -o ./publish/proxy \
|
||||
2026-07-04T13:13:34.7925418Z --no-restore \
|
||||
2026-07-04T13:13:34.7925492Z --no-build \
|
||||
2026-07-04T13:13:34.7925570Z -p:PublishReadyToRun=false \
|
||||
2026-07-04T13:13:34.7925644Z -p:PerformanceSummary=true \
|
||||
2026-07-04T13:13:34.7925719Z -clp:Summary \
|
||||
2026-07-04T13:13:34.7925787Z -bl:./publish-logs/publish-proxy.binlog
|
||||
2026-07-04T13:13:34.7925861Z end=$(date +%s)
|
||||
2026-07-04T13:13:34.7925930Z echo "✓ Publish Proxy elapsed: $((end - start))s"
|
||||
2026-07-04T13:13:34.7926017Z ls -lh ./publish-logs/publish-proxy.binlog
|
||||
2026-07-04T13:13:34.7926277Z shell: bash --noprofile --norc -e -o pipefail {0}
|
||||
2026-07-04T13:13:34.7926600Z ::endgroup::
|
||||
2026-07-04T13:13:35.6339899Z Determining projects to restore...
|
||||
2026-07-04T13:13:36.1800562Z Restored /workspace/***/taxbaik/src/TaxBaik.Proxy/TaxBaik.Proxy.csproj (in 118 ms).
|
||||
2026-07-04T13:13:37.4722382Z TaxBaik.Proxy -> /workspace/***/taxbaik/src/TaxBaik.Proxy/bin/Release/net10.0/TaxBaik.Proxy.dll
|
||||
2026-07-04T13:13:37.5109572Z
|
||||
2026-07-04T13:13:37.5197832Z Build succeeded.
|
||||
2026-07-04T13:13:37.5199998Z 0 Warning(s)
|
||||
2026-07-04T13:13:37.5200383Z 0 Error(s)
|
||||
2026-07-04T13:13:37.5201755Z
|
||||
2026-07-04T13:13:37.5208860Z Time Elapsed 00:00:00.99
|
||||
2026-07-04T13:13:38.5576411Z TaxBaik.Proxy -> /workspace/***/taxbaik/publish/proxy/
|
||||
2026-07-04T13:13:38.5714883Z
|
||||
2026-07-04T13:13:38.5723906Z Build succeeded.
|
||||
2026-07-04T13:13:38.5727824Z 0 Warning(s)
|
||||
2026-07-04T13:13:38.5729672Z 0 Error(s)
|
||||
2026-07-04T13:13:38.5732538Z
|
||||
2026-07-04T13:13:38.5734158Z Time Elapsed 00:00:00.66
|
||||
2026-07-04T13:13:38.6053620Z ✓ Publish Proxy elapsed: 1s
|
||||
2026-07-04T13:13:38.6067767Z -rw-r--r-- 1 root root 335K Jul 4 13:13 ./publish-logs/publish-proxy.binlog
|
||||
2026-07-04T13:13:38.7226853Z ::group::Run set -e
|
||||
2026-07-04T13:13:38.7227437Z set -e
|
||||
2026-07-04T13:13:38.7227615Z JWT_SECRET_KEY="***"
|
||||
2026-07-04T13:13:38.7227847Z TELEGRAM_BOT_TOKEN="***"
|
||||
2026-07-04T13:13:38.7228015Z TELEGRAM_CHAT_ID="***"
|
||||
2026-07-04T13:13:38.7228313Z TELEGRAM_INQUIRY_CHAT_ID=""
|
||||
2026-07-04T13:13:38.7228460Z TELEGRAM_SYSTEM_CHAT_ID=""
|
||||
2026-07-04T13:13:38.7228660Z [ -z "$JWT_SECRET_KEY" ] && { echo "Missing TAXBAIK_JWT_SECRET_KEY" >&2; exit 1; }
|
||||
2026-07-04T13:13:38.7228859Z [ -z "$TELEGRAM_BOT_TOKEN" ] && { echo "Missing TAXBAIK_TELEGRAM_BOT_TOKEN" >&2; exit 1; }
|
||||
2026-07-04T13:13:38.7229026Z [ -z "$TELEGRAM_CHAT_ID" ] && { echo "Missing TAXBAIK_TELEGRAM_CHAT_ID" >&2; exit 1; }
|
||||
2026-07-04T13:13:38.7229366Z [ -z "$TELEGRAM_INQUIRY_CHAT_ID" ] && TELEGRAM_INQUIRY_CHAT_ID="$TELEGRAM_CHAT_ID"
|
||||
2026-07-04T13:13:38.7229534Z [ -z "$TELEGRAM_SYSTEM_CHAT_ID" ] && TELEGRAM_SYSTEM_CHAT_ID="-5585148480"
|
||||
2026-07-04T13:13:38.7229688Z JWT_SECRET_KEY="$JWT_SECRET_KEY" \
|
||||
2026-07-04T13:13:38.7229858Z TELEGRAM_BOT_TOKEN="$TELEGRAM_BOT_TOKEN" \
|
||||
2026-07-04T13:13:38.7230034Z TELEGRAM_CHAT_ID="$TELEGRAM_CHAT_ID" \
|
||||
2026-07-04T13:13:38.7230350Z TELEGRAM_INQUIRY_CHAT_ID="$TELEGRAM_INQUIRY_CHAT_ID" \
|
||||
2026-07-04T13:13:38.7230502Z TELEGRAM_SYSTEM_CHAT_ID="$TELEGRAM_SYSTEM_CHAT_ID" \
|
||||
2026-07-04T13:13:38.7230685Z python3 -c '
|
||||
2026-07-04T13:13:38.7230820Z import json, os, pathlib
|
||||
2026-07-04T13:13:38.7230963Z pathlib.Path("./publish/appsettings.Production.json").write_text(
|
||||
2026-07-04T13:13:38.7231485Z json.dumps({
|
||||
2026-07-04T13:13:38.7231673Z "Jwt": {"SecretKey": os.environ["JWT_SECRET_KEY"]},
|
||||
2026-07-04T13:13:38.7231827Z "Telegram": {
|
||||
2026-07-04T13:13:38.7231965Z "BotToken": os.environ["TELEGRAM_BOT_TOKEN"],
|
||||
2026-07-04T13:13:38.7232155Z "ChatId": os.environ["TELEGRAM_CHAT_ID"],
|
||||
2026-07-04T13:13:38.7232301Z "InquiryChatId": os.environ["TELEGRAM_INQUIRY_CHAT_ID"],
|
||||
2026-07-04T13:13:38.7232446Z "SystemChatId": os.environ["TELEGRAM_SYSTEM_CHAT_ID"]
|
||||
2026-07-04T13:13:38.7232591Z }
|
||||
2026-07-04T13:13:38.7232753Z }, ensure_ascii=False, indent=2),
|
||||
2026-07-04T13:13:38.7233027Z encoding="utf-8"
|
||||
2026-07-04T13:13:38.7233158Z )'
|
||||
2026-07-04T13:13:38.7233325Z test -s ./publish/appsettings.Production.json || { echo "appsettings.Production.json is empty" >&2; exit 1; }
|
||||
2026-07-04T13:13:38.7233447Z shell: bash --noprofile --norc -e -o pipefail {0}
|
||||
2026-07-04T13:13:38.7233563Z ::endgroup::
|
||||
2026-07-04T13:13:38.9869961Z ::group::Run test -s ./publish/proxy/TaxBaik.Proxy.dll || { echo "TaxBaik.Proxy.dll missing" >&2; exit 1; }
|
||||
2026-07-04T13:13:38.9870335Z test -s ./publish/proxy/TaxBaik.Proxy.dll || { echo "TaxBaik.Proxy.dll missing" >&2; exit 1; }
|
||||
2026-07-04T13:13:38.9870463Z test -s ./publish/proxy/TaxBaik.Proxy.runtimeconfig.json || { echo "TaxBaik.Proxy.runtimeconfig.json missing" >&2; exit 1; }
|
||||
2026-07-04T13:13:38.9870578Z shell: bash --noprofile --norc -e -o pipefail {0}
|
||||
2026-07-04T13:13:38.9870689Z ::endgroup::
|
||||
2026-07-04T13:13:39.1581439Z ::group::Run mkdir -p ./publish/db && cp -r db/migrations ./publish/db/ || true
|
||||
2026-07-04T13:13:39.1581828Z mkdir -p ./publish/db && cp -r db/migrations ./publish/db/ || true
|
||||
2026-07-04T13:13:39.1581945Z shell: bash --noprofile --norc -e -o pipefail {0}
|
||||
2026-07-04T13:13:39.1582057Z ::endgroup::
|
||||
2026-07-04T13:13:39.3269319Z ::group::Run bash scripts/validate_migrations.sh db/migrations
|
||||
2026-07-04T13:13:39.3270166Z bash scripts/validate_migrations.sh db/migrations
|
||||
2026-07-04T13:13:39.3271314Z shell: bash --noprofile --norc -e -o pipefail {0}
|
||||
2026-07-04T13:13:39.3272003Z ::endgroup::
|
||||
2026-07-04T13:13:39.4174922Z Duplicate version check passed.
|
||||
2026-07-04T13:13:39.5287374Z ::group::Run bash scripts/validate_kst_timestamps.sh
|
||||
2026-07-04T13:13:39.5287938Z bash scripts/validate_kst_timestamps.sh
|
||||
2026-07-04T13:13:39.5288057Z shell: bash --noprofile --norc -e -o pipefail {0}
|
||||
2026-07-04T13:13:39.5288185Z ::endgroup::
|
||||
2026-07-04T13:13:39.5845000Z KST timestamp harness passed.
|
||||
2026-07-04T13:13:39.6981958Z ::group::Run COMMIT_HASH=$(git rev-parse --short HEAD)
|
||||
2026-07-04T13:13:39.6982433Z COMMIT_HASH=$(git rev-parse --short HEAD)
|
||||
2026-07-04T13:13:39.6982554Z BUILD_TIME=$(TZ=Asia/Seoul date +'%Y-%m-%d %H:%M:%S KST')
|
||||
2026-07-04T13:13:39.6982657Z mkdir -p ./publish/wwwroot
|
||||
2026-07-04T13:13:39.6982775Z printf '{\n "version": "%s",\n "built": "%s"\n}\n' "$COMMIT_HASH" "$BUILD_TIME" > ./publish/wwwroot/version.json
|
||||
2026-07-04T13:13:39.6982913Z echo "✓ Build: $COMMIT_HASH @ $BUILD_TIME"
|
||||
2026-07-04T13:13:39.6983012Z shell: bash --noprofile --norc -e -o pipefail {0}
|
||||
2026-07-04T13:13:39.6983143Z ::endgroup::
|
||||
2026-07-04T13:13:39.7666694Z ✓ Build: 333089a @ 2026-07-04 22:13:39 KST
|
||||
2026-07-04T13:13:39.8839119Z ::group::Run mkdir -p ~/.ssh
|
||||
2026-07-04T13:13:39.8839443Z mkdir -p ~/.ssh
|
||||
2026-07-04T13:13:39.8839565Z SSH_KEY_B64="***"
|
||||
2026-07-04T13:13:39.8839697Z SSH_KEY_RAW="***"
|
||||
2026-07-04T13:13:39.8839828Z if [ -n "$SSH_KEY_B64" ]; then
|
||||
2026-07-04T13:13:39.8839904Z printf '%s' "$SSH_KEY_B64" | base64 -d > ~/.ssh/id_ed25519
|
||||
2026-07-04T13:13:39.8839987Z elif [ -n "$SSH_KEY_RAW" ]; then
|
||||
2026-07-04T13:13:39.8840064Z if printf '%s' "$SSH_KEY_RAW" | grep -q 'BEGIN .*PRIVATE KEY'; then
|
||||
2026-07-04T13:13:39.8840162Z printf '%b\n' "$SSH_KEY_RAW" > ~/.ssh/id_ed25519
|
||||
2026-07-04T13:13:39.8840244Z else
|
||||
2026-07-04T13:13:39.8840311Z printf '%s' "$SSH_KEY_RAW" | base64 -d > ~/.ssh/id_ed25519
|
||||
2026-07-04T13:13:39.8840401Z fi
|
||||
2026-07-04T13:13:39.8840468Z else
|
||||
2026-07-04T13:13:39.8840542Z echo "Missing DEPLOY_SSH_KEY_B64 or DEPLOY_SSH_KEY" >&2; exit 1
|
||||
2026-07-04T13:13:39.8840634Z fi
|
||||
2026-07-04T13:13:39.8840694Z sed -i 's/\r$//' ~/.ssh/id_ed25519
|
||||
2026-07-04T13:13:39.8840774Z chmod 600 ~/.ssh/id_ed25519
|
||||
2026-07-04T13:13:39.8840843Z ssh-keyscan -H "***" >> ~/.ssh/known_hosts 2>/dev/null || true
|
||||
2026-07-04T13:13:39.8840934Z shell: bash --noprofile --norc -e -o pipefail {0}
|
||||
2026-07-04T13:13:39.8841160Z ::endgroup::
|
||||
2026-07-04T13:13:40.2940162Z ::group::Run cp deploy_gb.sh ./publish/deploy_gb.sh
|
||||
2026-07-04T13:13:40.2940570Z cp deploy_gb.sh ./publish/deploy_gb.sh
|
||||
2026-07-04T13:13:40.2940686Z mkdir -p ./publish/scripts
|
||||
2026-07-04T13:13:40.2940784Z cp scripts/validate_migrations.sh ./publish/scripts/validate_migrations.sh
|
||||
2026-07-04T13:13:40.2941234Z chmod +x ./publish/scripts/validate_migrations.sh
|
||||
2026-07-04T13:13:40.2941324Z tar -czf taxbaik_deploy.tgz -C ./publish .
|
||||
2026-07-04T13:13:40.2941443Z echo "✓ Package: $(du -sh taxbaik_deploy.tgz | cut -f1)"
|
||||
2026-07-04T13:13:40.2941550Z shell: bash --noprofile --norc -e -o pipefail {0}
|
||||
2026-07-04T13:13:40.2941809Z ::endgroup::
|
||||
2026-07-04T13:13:42.8131179Z ✓ Package: 23M
|
||||
2026-07-04T13:13:42.9209145Z ::group::Run set -e
|
||||
2026-07-04T13:13:42.9209698Z set -e
|
||||
2026-07-04T13:13:42.9209813Z export TAXBAIK_DEPLOY_FROM_CI=1
|
||||
2026-07-04T13:13:42.9209918Z TIMESTAMP=$(TZ=Asia/Seoul date +%Y%m%d_%H%M%S)
|
||||
2026-07-04T13:13:42.9210001Z COMMIT=$(git rev-parse --short HEAD)
|
||||
2026-07-04T13:13:42.9210082Z DEPLOY_HOST="***"
|
||||
2026-07-04T13:13:42.9210164Z DEPLOY_USER="***"
|
||||
2026-07-04T13:13:42.9210250Z TELEGRAM_BOT_TOKEN="***"
|
||||
2026-07-04T13:13:42.9210333Z TELEGRAM_SYSTEM_CHAT_ID=""
|
||||
2026-07-04T13:13:42.9210638Z TELEGRAM_CHAT_ID="${TELEGRAM_SYSTEM_CHAT_ID:--5585148480}"
|
||||
2026-07-04T13:13:42.9210723Z
|
||||
2026-07-04T13:13:42.9210789Z send_telegram() {
|
||||
2026-07-04T13:13:42.9210862Z local text="$1"
|
||||
2026-07-04T13:13:42.9210940Z if [ -z "$TELEGRAM_BOT_TOKEN" ]; then
|
||||
2026-07-04T13:13:42.9211349Z echo "Skipping Telegram notification: missing TAXBAIK_TELEGRAM_BOT_TOKEN" >&2
|
||||
2026-07-04T13:13:42.9211471Z return 0
|
||||
2026-07-04T13:13:42.9211617Z fi
|
||||
2026-07-04T13:13:42.9211684Z
|
||||
2026-07-04T13:13:42.9211765Z curl -fsS -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \
|
||||
2026-07-04T13:13:42.9211858Z -d "chat_id=${TELEGRAM_CHAT_ID}" \
|
||||
2026-07-04T13:13:42.9211937Z --data-urlencode "text=${text}" \
|
||||
2026-07-04T13:13:42.9212171Z -d "parse_mode=HTML" >/dev/null || true
|
||||
2026-07-04T13:13:42.9212250Z }
|
||||
2026-07-04T13:13:42.9212314Z
|
||||
2026-07-04T13:13:42.9212381Z notify_failure() {
|
||||
2026-07-04T13:13:42.9212515Z local exit_code=$?
|
||||
2026-07-04T13:13:42.9212586Z send_telegram "❌ <b>TaxBaik 배포 실패</b>
|
||||
2026-07-04T13:13:42.9212681Z
|
||||
2026-07-04T13:13:42.9212751Z 커밋: <code>${COMMIT}</code>
|
||||
2026-07-04T13:13:42.9212978Z 시간: <code>${TIMESTAMP}</code>
|
||||
2026-07-04T13:13:42.9213060Z 단계: CI/CD deploy"
|
||||
2026-07-04T13:13:42.9213158Z exit "$exit_code"
|
||||
2026-07-04T13:13:42.9213233Z }
|
||||
2026-07-04T13:13:42.9213297Z
|
||||
2026-07-04T13:13:42.9213422Z trap notify_failure ERR
|
||||
2026-07-04T13:13:42.9213514Z
|
||||
2026-07-04T13:13:42.9213577Z echo "=== Deploying TaxBaik $COMMIT ($TIMESTAMP) ==="
|
||||
2026-07-04T13:13:42.9213809Z
|
||||
2026-07-04T13:13:42.9213873Z # 1. 아티팩트 업로드
|
||||
2026-07-04T13:13:42.9213948Z scp -i ~/.ssh/id_ed25519 -o StrictHostKeyChecking=yes \
|
||||
2026-07-04T13:13:42.9214028Z taxbaik_deploy.tgz "$DEPLOY_USER@$DEPLOY_HOST:/tmp/taxbaik_${TIMESTAMP}.tgz"
|
||||
2026-07-04T13:13:42.9214119Z
|
||||
2026-07-04T13:13:42.9214188Z # 2. 서버에서 배포 + 헬스 체크 (SSH 1회 연결로 처리, Green-Blue 지원)
|
||||
2026-07-04T13:13:42.9214335Z ssh -i ~/.ssh/id_ed25519 -o StrictHostKeyChecking=yes \
|
||||
2026-07-04T13:13:42.9214557Z -o ServerAliveInterval=10 \
|
||||
2026-07-04T13:13:42.9214642Z "$DEPLOY_USER@$DEPLOY_HOST" TAXBAIK_DEPLOY_FROM_CI=1 bash << REMOTE
|
||||
2026-07-04T13:13:42.9214727Z set -e
|
||||
2026-07-04T13:13:42.9214797Z DEPLOY_HOME="/home/***"
|
||||
2026-07-04T13:13:42.9214873Z DEPLOY_DIR="\$DEPLOY_HOME/deployments/taxbaik_${TIMESTAMP}"
|
||||
2026-07-04T13:13:42.9214973Z TIMESTAMP="${TIMESTAMP}"
|
||||
2026-07-04T13:13:42.9215054Z COMMIT="${COMMIT}"
|
||||
2026-07-04T13:13:42.9215187Z
|
||||
2026-07-04T13:13:42.9215400Z echo "--- [1/5] 압축 해제 ---"
|
||||
2026-07-04T13:13:42.9215489Z mkdir -p "\$DEPLOY_DIR"
|
||||
2026-07-04T13:13:42.9215560Z tar -xzf "/tmp/taxbaik_\${TIMESTAMP}.tgz" -C "\$DEPLOY_DIR"
|
||||
2026-07-04T13:13:42.9215639Z rm -f "/tmp/taxbaik_\${TIMESTAMP}.tgz"
|
||||
2026-07-04T13:13:42.9215721Z
|
||||
2026-07-04T13:13:42.9215783Z echo "--- [2/5] 운영 설정 검증 ---"
|
||||
2026-07-04T13:13:42.9215859Z test -s "\$DEPLOY_DIR/appsettings.Production.json" \
|
||||
2026-07-04T13:13:42.9215934Z || { echo "FATAL: appsettings.Production.json 없음" >&2; exit 1; }
|
||||
2026-07-04T13:13:42.9216271Z test -s "\$DEPLOY_DIR/proxy/TaxBaik.Proxy.dll" \
|
||||
2026-07-04T13:13:42.9216359Z || { echo "FATAL: TaxBaik.Proxy.dll 없음" >&2; exit 1; }
|
||||
2026-07-04T13:13:42.9216437Z
|
||||
2026-07-04T13:13:42.9216510Z echo "--- [3/5] 마이그레이션 사전 검증 ---"
|
||||
2026-07-04T13:13:42.9216586Z test -x "\$DEPLOY_DIR/scripts/validate_migrations.sh" \
|
||||
2026-07-04T13:13:42.9216660Z || { echo "FATAL: validate_migrations.sh 없음" >&2; exit 1; }
|
||||
2026-07-04T13:13:42.9216745Z "\$DEPLOY_DIR/scripts/validate_migrations.sh" "\$DEPLOY_DIR/db/migrations" "postgresql://taxbaik:taxbaik123@localhost:5432/taxbaikdb"
|
||||
2026-07-04T13:13:42.9216847Z
|
||||
2026-07-04T13:13:42.9217059Z echo "--- [4/5] Green-Blue 배포 실행 ---"
|
||||
2026-07-04T13:13:42.9217221Z chmod +x "\$DEPLOY_DIR/deploy_gb.sh"
|
||||
2026-07-04T13:13:42.9217308Z "\$DEPLOY_DIR/deploy_gb.sh" "\$DEPLOY_DIR"
|
||||
2026-07-04T13:13:42.9217386Z
|
||||
2026-07-04T13:13:42.9217447Z echo "--- [4.5/5] Nginx 설정 검증 ---"
|
||||
2026-07-04T13:13:42.9217520Z # 실제 로드되는 파일은 sites-enabled/의 심볼릭 링크 대상만이다.
|
||||
2026-07-04T13:13:42.9217605Z # sites-available/에 다른 파일(예: default)이 있어도 sites-enabled에
|
||||
2026-07-04T13:13:42.9218091Z # 링크되어 있지 않으면 nginx는 그 내용을 절대 읽지 않는다.
|
||||
2026-07-04T13:13:42.9218382Z NGINX_CONF=""
|
||||
2026-07-04T13:13:42.9218490Z for f in /etc/nginx/sites-enabled/*; do
|
||||
2026-07-04T13:13:42.9218573Z if [ -e "\$f" ] && grep -q "location /taxbaik" "\$f" 2>/dev/null; then
|
||||
2026-07-04T13:13:42.9218869Z NGINX_CONF=\$(readlink -f "\$f")
|
||||
2026-07-04T13:13:42.9218950Z break
|
||||
2026-07-04T13:13:42.9219016Z fi
|
||||
2026-07-04T13:13:42.9219102Z done
|
||||
2026-07-04T13:13:42.9219233Z
|
||||
2026-07-04T13:13:42.9219312Z if [ -z "\$NGINX_CONF" ]; then
|
||||
2026-07-04T13:13:42.9219390Z echo "❌ FATAL: sites-enabled/ 안에서 'location /taxbaik'를 정의한 파일을 찾을 수 없음" >&2
|
||||
2026-07-04T13:13:42.9219652Z echo " sites-available/에 파일을 수정해도 sites-enabled에 심볼릭 링크되어 있지 않으면 반영되지 않는다." >&2
|
||||
2026-07-04T13:13:42.9219759Z exit 1
|
||||
2026-07-04T13:13:42.9219835Z fi
|
||||
2026-07-04T13:13:42.9219908Z echo "실제 로드되는 설정 파일: \$NGINX_CONF"
|
||||
2026-07-04T13:13:42.9219986Z
|
||||
2026-07-04T13:13:42.9220131Z # 불변식: '/'와 '/taxbaik' location 모두 반드시 127.0.0.1:5001 (TaxBaik.Proxy)을
|
||||
2026-07-04T13:13:42.9220223Z # 가리켜야 한다. 5003/5004를 직접 하드코딩하면 Green-Blue 포트 전환 시
|
||||
2026-07-04T13:13:42.9220456Z # 죽은 포트를 가리키게 되어 502/404가 발생한다 (실제 발생했던 장애).
|
||||
2026-07-04T13:13:42.9220549Z if grep -E "proxy_pass\s+http://127\.0\.0\.1:500[34]" "\$NGINX_CONF" > /dev/null 2>&1; then
|
||||
2026-07-04T13:13:42.9220651Z echo "❌ FATAL: \$NGINX_CONF 가 포트 5003/5004를 직접 참조함 (Green-Blue 전환 시 502 발생)" >&2
|
||||
2026-07-04T13:13:42.9220753Z echo " 수정: sudo sed -i 's|127.0.0.1:500[34]|127.0.0.1:5001|g' \$NGINX_CONF && sudo nginx -t && sudo systemctl reload nginx" >&2
|
||||
2026-07-04T13:13:42.9220848Z exit 1
|
||||
2026-07-04T13:13:42.9220987Z fi
|
||||
2026-07-04T13:13:42.9221466Z
|
||||
2026-07-04T13:13:42.9221529Z # proxy_pass에 URI(끝 슬래시)가 있으면 nginx가 요청 경로를 재작성하며,
|
||||
2026-07-04T13:13:42.9221630Z # location 접두사와 슬래시 개수가 안 맞으면 백엔드로 이중 슬래시(//)가
|
||||
2026-07-04T13:13:42.9221713Z # 전달되어 404가 발생한다 (실제 발생했던 장애). 접두사 location에서는
|
||||
2026-07-04T13:13:42.9221797Z # proxy_pass에 URI를 붙이지 않는다.
|
||||
2026-07-04T13:13:42.9221894Z if grep -E "location\s+/taxbaik\s*\{" -A 1 "\$NGINX_CONF" | grep -qE "proxy_pass\s+http://127\.0\.0\.1:5001/;"; then
|
||||
2026-07-04T13:13:42.9221999Z echo "❌ FATAL: location /taxbaik 의 proxy_pass 에 불필요한 trailing slash가 있음 (이중 슬래시로 인한 404 위험)" >&2
|
||||
2026-07-04T13:13:42.9222362Z exit 1
|
||||
2026-07-04T13:13:42.9222534Z fi
|
||||
2026-07-04T13:13:42.9222682Z
|
||||
2026-07-04T13:13:42.9222792Z echo "✓ Nginx 설정 검증 통과 (실제 로드 파일 확인 + 포트 5001 고정 + trailing slash 없음)"
|
||||
2026-07-04T13:13:42.9223150Z
|
||||
2026-07-04T13:13:42.9223222Z echo "--- [5/5] 헬스 체크 (최대 60초) ---"
|
||||
2026-07-04T13:13:42.9223338Z ATTEMPTS=20
|
||||
2026-07-04T13:13:42.9223421Z for i in \$(seq 1 \$ATTEMPTS); do
|
||||
2026-07-04T13:13:42.9223498Z STATUS=\$(curl -sf -o /dev/null -w '%{http_code}' http://127.0.0.1:5001/taxbaik/healthz 2>/dev/null || echo "000")
|
||||
2026-07-04T13:13:42.9223594Z if [ "\$STATUS" = "200" ]; then
|
||||
2026-07-04T13:13:42.9223668Z echo "✓ [1/6] 헬스 체크 완료"
|
||||
2026-07-04T13:13:42.9223743Z
|
||||
2026-07-04T13:13:42.9223966Z # 검증 1: 메인 페이지 로드. curl -L + -w 는 리다이렉트 체인의 상태코드를
|
||||
2026-07-04T13:13:42.9224061Z # 이어붙이므로, 첫 응답 코드만 받아 200/3xx를 허용한다.
|
||||
2026-07-04T13:13:42.9224147Z MAIN_STATUS=\$(curl -fsS -o /dev/null -w '%{http_code}' http://127.0.0.1:5001/taxbaik/ 2>/dev/null || echo "000")
|
||||
2026-07-04T13:13:42.9224234Z if ! printf '%s' "\$MAIN_STATUS" | grep -Eq '^(200|301|302|307|308)$'; then
|
||||
2026-07-04T13:13:42.9224320Z echo "❌ 메인 페이지 로드 실패 (상태: \$MAIN_STATUS)" >&2
|
||||
2026-07-04T13:13:42.9224399Z exit 1
|
||||
2026-07-04T13:13:42.9224479Z fi
|
||||
2026-07-04T13:13:42.9224543Z echo "✓ [2/6] 메인 페이지 로드 완료"
|
||||
2026-07-04T13:13:42.9224985Z
|
||||
2026-07-04T13:13:42.9225055Z # 검증 2: CSS 파일 로드
|
||||
2026-07-04T13:13:42.9225135Z CSS_STATUS=\$(curl -sf -o /dev/null -w '%{http_code}' http://127.0.0.1:5001/taxbaik/css/admin.css 2>/dev/null || echo "000")
|
||||
2026-07-04T13:13:42.9225232Z if [ "\$CSS_STATUS" != "200" ]; then
|
||||
2026-07-04T13:13:42.9225309Z echo "❌ CSS 파일 로드 실패 (상태: \$CSS_STATUS)" >&2
|
||||
2026-07-04T13:13:42.9225387Z exit 1
|
||||
2026-07-04T13:13:42.9225462Z fi
|
||||
2026-07-04T13:13:42.9225738Z echo "✓ [3/6] CSS 파일 로드 완료"
|
||||
2026-07-04T13:13:42.9225831Z
|
||||
2026-07-04T13:13:42.9225896Z # 검증 3: 버전 정보. 파일 존재만 보면 5001이 잘못된 구 프로세스를
|
||||
2026-07-04T13:13:42.9225979Z # 가리키는 장애를 놓치므로, HTTP 응답이 이번 커밋인지 확인한다.
|
||||
2026-07-04T13:13:42.9226283Z if [ ! -s "\$DEPLOY_DIR/wwwroot/version.json" ]; then
|
||||
2026-07-04T13:13:42.9226386Z echo "❌ version.json 누락" >&2
|
||||
2026-07-04T13:13:42.9226486Z exit 1
|
||||
2026-07-04T13:13:42.9226563Z fi
|
||||
2026-07-04T13:13:42.9226631Z VERSION_JSON=\$(curl -fsS http://127.0.0.1:5001/taxbaik/version.json 2>/dev/null || true)
|
||||
2026-07-04T13:13:42.9226716Z if ! printf '%s' "\$VERSION_JSON" | grep -q "\"version\": \"\$COMMIT\""; then
|
||||
2026-07-04T13:13:42.9226807Z echo "❌ 5001 프록시가 이번 배포 버전을 제공하지 않음" >&2
|
||||
2026-07-04T13:13:42.9226891Z echo " expected: \$COMMIT" >&2
|
||||
2026-07-04T13:13:42.9227320Z echo " actual: \$VERSION_JSON" >&2
|
||||
2026-07-04T13:13:42.9227405Z echo " 확인: 5001 포트가 TaxBaik.Proxy.dll인지, /home/***/taxbaik_port가 새 포트인지 점검" >&2
|
||||
2026-07-04T13:13:42.9227514Z exit 1
|
||||
2026-07-04T13:13:42.9227584Z fi
|
||||
2026-07-04T13:13:42.9227652Z echo "✓ [4/6] 버전 정보 확인 완료"
|
||||
2026-07-04T13:13:42.9228126Z
|
||||
2026-07-04T13:13:42.9228198Z # 검증 4: 5001 프록시 확인
|
||||
2026-07-04T13:13:42.9228275Z if ! ss -tlnp | grep -q ':5001 '; then
|
||||
2026-07-04T13:13:42.9228357Z echo "❌ 5001 프록시가 실행 중이 아님" >&2
|
||||
2026-07-04T13:13:42.9228453Z exit 1
|
||||
2026-07-04T13:13:42.9229006Z fi
|
||||
2026-07-04T13:13:42.9229214Z echo "✓ [5/6] 5001 프록시 확인 완료"
|
||||
2026-07-04T13:13:42.9229372Z
|
||||
2026-07-04T13:13:42.9230132Z # 검증 5: 관리자 로그인 페이지
|
||||
2026-07-04T13:13:42.9230248Z LOGIN_STATUS=\$(curl -fsSL -o /dev/null -w '%{http_code}' http://127.0.0.1:5001/taxbaik/admin/login 2>/dev/null || echo "000")
|
||||
2026-07-04T13:13:42.9230348Z if [ "\$LOGIN_STATUS" != "200" ]; then
|
||||
2026-07-04T13:13:42.9230444Z echo "❌ 관리자 로그인 페이지 로드 실패 (상태: \$LOGIN_STATUS)" >&2
|
||||
2026-07-04T13:13:42.9230527Z exit 1
|
||||
2026-07-04T13:13:42.9230609Z fi
|
||||
2026-07-04T13:13:42.9231221Z echo "✓ [6/6] 관리자 페이지 로드 완료"
|
||||
2026-07-04T13:13:42.9231399Z
|
||||
2026-07-04T13:13:42.9231522Z echo "✓ 서비스 정상 (시도 \$i/\$ATTEMPTS)"
|
||||
2026-07-04T13:13:42.9231640Z # 구 배포 디렉토리 정리 (최근 5개 보존)
|
||||
2026-07-04T13:13:42.9232008Z ls -1dt \$DEPLOY_HOME/deployments/taxbaik_* 2>/dev/null \
|
||||
2026-07-04T13:13:42.9232103Z | tail -n +6 | xargs rm -rf 2>/dev/null || true
|
||||
2026-07-04T13:13:42.9232179Z exit 0
|
||||
2026-07-04T13:13:42.9232248Z fi
|
||||
2026-07-04T13:13:42.9232315Z if [ "\$i" -eq "\$ATTEMPTS" ]; then
|
||||
2026-07-04T13:13:42.9232391Z echo "=== FATAL: 서비스가 \$ATTEMPTS회 시도 후에도 응답하지 않음 ===" >&2
|
||||
2026-07-04T13:13:42.9232495Z echo "--- 5001 listener ---" >&2
|
||||
2026-07-04T13:13:42.9232780Z ss -tlnp 2>/dev/null | grep ':5001 ' >&2 || true
|
||||
2026-07-04T13:13:42.9232863Z echo "--- active port file ---" >&2
|
||||
2026-07-04T13:13:42.9232938Z cat "\$DEPLOY_HOME/taxbaik_port" >&2 || true
|
||||
2026-07-04T13:13:42.9233014Z echo "--- 신규 앱 로그 ---" >&2
|
||||
2026-07-04T13:13:42.9233103Z ACTIVE_PORT=\$(cat "\$DEPLOY_HOME/taxbaik_port" 2>/dev/null | tr -d '[:space:]' || true)
|
||||
2026-07-04T13:13:42.9233190Z if [ -n "\$ACTIVE_PORT" ] && [ -s "\$DEPLOY_DIR/web_\${ACTIVE_PORT}.log" ]; then
|
||||
2026-07-04T13:13:42.9233273Z tail -n 80 "\$DEPLOY_DIR/web_\${ACTIVE_PORT}.log" >&2
|
||||
2026-07-04T13:13:42.9233657Z else
|
||||
2026-07-04T13:13:42.9233743Z ls -la "\$DEPLOY_DIR" >&2 || true
|
||||
2026-07-04T13:13:42.9233817Z fi
|
||||
2026-07-04T13:13:42.9233890Z echo "--- proxy 로그 ---" >&2
|
||||
2026-07-04T13:13:42.9233962Z tail -n 80 "\$DEPLOY_HOME/taxbaik_proxy.log" >&2 || true
|
||||
2026-07-04T13:13:42.9234041Z exit 1
|
||||
2026-07-04T13:13:42.9234471Z fi
|
||||
2026-07-04T13:13:42.9234554Z echo " 대기 중... (\$i/\$ATTEMPTS, HTTP \$STATUS)"
|
||||
2026-07-04T13:13:42.9234634Z sleep 3
|
||||
2026-07-04T13:13:42.9234703Z done
|
||||
2026-07-04T13:13:42.9234766Z REMOTE
|
||||
2026-07-04T13:13:42.9234830Z
|
||||
2026-07-04T13:13:42.9234892Z echo "✓ 배포 완료: taxbaik_${TIMESTAMP} @ $DEPLOY_HOST"
|
||||
2026-07-04T13:13:42.9235262Z
|
||||
2026-07-04T13:13:42.9235341Z echo "--- 실제 공개 도메인 종단 간 검증 (Nginx/Cloudflare 경유, 최대 3회 재시도) ---"
|
||||
2026-07-04T13:13:42.9235432Z ROOT_URL="https://www.taxbaik.com/" \
|
||||
2026-07-04T13:13:42.9235507Z ADMIN_URL="https://www.taxbaik.com/taxbaik/admin/login" \
|
||||
2026-07-04T13:13:42.9236287Z PUBLIC_MARKER="백원숙 세무회계" \
|
||||
2026-07-04T13:13:42.9236462Z PUBLIC_FORBIDDEN="관리자" \
|
||||
2026-07-04T13:13:42.9236552Z ADMIN_MARKER="관리자 로그인" \
|
||||
2026-07-04T13:13:42.9236627Z MAX_RETRIES=3 \
|
||||
2026-07-04T13:13:42.9236949Z RETRY_SLEEP_SECONDS=5 \
|
||||
2026-07-04T13:13:42.9237040Z bash ./scripts/taxbaik-smoke.sh
|
||||
2026-07-04T13:13:42.9237112Z echo "✓ 실제 공개 도메인 전체 정상"
|
||||
2026-07-04T13:13:42.9237191Z
|
||||
2026-07-04T13:13:42.9237252Z send_telegram "✅ <b>TaxBaik 배포 완료</b>
|
||||
2026-07-04T13:13:42.9237340Z
|
||||
2026-07-04T13:13:42.9237406Z 커밋: <code>${COMMIT}</code>
|
||||
2026-07-04T13:13:42.9237491Z 시간: <code>${TIMESTAMP}</code>
|
||||
2026-07-04T13:13:42.9237760Z 대상: <code>${DEPLOY_HOST}</code>
|
||||
2026-07-04T13:13:42.9237851Z 채널: <code>${TELEGRAM_CHAT_ID}</code>"
|
||||
2026-07-04T13:13:42.9237934Z shell: bash --noprofile --norc -e -o pipefail {0}
|
||||
2026-07-04T13:13:42.9238036Z ::endgroup::
|
||||
2026-07-04T13:13:42.9795782Z === Deploying TaxBaik 333089a (20260704_221342) ===
|
||||
2026-07-04T13:13:43.7626459Z --- [1/5] 압축 해제 ---
|
||||
2026-07-04T13:13:44.0253437Z --- [2/5] 운영 설정 검증 ---
|
||||
2026-07-04T13:13:44.0253925Z --- [3/5] 마이그레이션 사전 검증 ---
|
||||
2026-07-04T13:13:44.2657668Z Migration dry-run validation passed.
|
||||
2026-07-04T13:13:44.2662938Z --- [4/5] Green-Blue 배포 실행 ---
|
||||
2026-07-04T13:13:44.2760328Z ===== 🚀 TaxBaik Green/Blue Deployment Script =====
|
||||
2026-07-04T13:13:44.2794538Z Active Port: 5003
|
||||
2026-07-04T13:13:44.2795229Z Target Port: 5004
|
||||
2026-07-04T13:13:44.2795392Z Deploy Directory: /home/***/deployments/taxbaik_20260704_221342
|
||||
2026-07-04T13:13:44.3288519Z === Starting New App on Port 5004 ===
|
||||
2026-07-04T13:13:46.3487972Z === Health Checking Port 5004 ===
|
||||
2026-07-04T13:13:46.6511505Z ✓ Health check passed on port 5004 (Attempt 1/20)
|
||||
2026-07-04T13:13:46.6512033Z === Switching Traffic to Port 5004 ===
|
||||
2026-07-04T13:13:46.6515819Z ✓ Traffic routed to 5004 (via TaxBaik.Proxy on 5001)
|
||||
2026-07-04T13:13:46.6516256Z === Stopping Old App on Port 5003 ===
|
||||
2026-07-04T13:13:46.6678440Z Killing old process PID: 4173589
|
||||
2026-07-04T13:13:46.6679064Z ✓ Old process terminated
|
||||
2026-07-04T13:13:46.6679183Z === Cleaning Up Old Deployments ===
|
||||
2026-07-04T13:13:46.6852709Z ✓ Cleanup completed
|
||||
2026-07-04T13:13:46.6853598Z ===== ✅ Green/Blue Deployment Completed Successfully =====
|
||||
2026-07-04T13:13:46.6856960Z --- [4.5/5] Nginx 설정 검증 ---
|
||||
2026-07-04T13:13:46.6948415Z 실제 로드되는 설정 파일: /etc/nginx/sites-available/taxbaik-domains.conf
|
||||
2026-07-04T13:13:46.6964877Z ✓ Nginx 설정 검증 통과 (실제 로드 파일 확인 + 포트 5001 고정 + trailing slash 없음)
|
||||
2026-07-04T13:13:46.6967831Z --- [5/5] 헬스 체크 (최대 60초) ---
|
||||
2026-07-04T13:13:46.7499335Z ✓ [1/6] 헬스 체크 완료
|
||||
2026-07-04T13:13:47.0229067Z ✓ [2/6] 메인 페이지 로드 완료
|
||||
2026-07-04T13:13:47.0535305Z ✓ [3/6] CSS 파일 로드 완료
|
||||
2026-07-04T13:13:47.0710321Z ✓ [4/6] 버전 정보 확인 완료
|
||||
2026-07-04T13:13:47.0814650Z ✓ [5/6] 5001 프록시 확인 완료
|
||||
2026-07-04T13:13:47.2865630Z ✓ [6/6] 관리자 페이지 로드 완료
|
||||
2026-07-04T13:13:47.2869510Z ✓ 서비스 정상 (시도 1/20)
|
||||
2026-07-04T13:13:47.2972427Z ✓ 배포 완료: taxbaik_20260704_221342 @ ***
|
||||
2026-07-04T13:13:47.2972968Z --- 실제 공개 도메인 종단 간 검증 (Nginx/Cloudflare 경유, 최대 3회 재시도) ---
|
||||
2026-07-04T13:13:47.7455427Z ✓ https://www.taxbaik.com/ -> HTTP 200
|
||||
2026-07-04T13:13:48.0299000Z ✓ https://www.taxbaik.com/taxbaik/ -> HTTP 200
|
||||
2026-07-04T13:13:48.3635834Z ✓ https://www.taxbaik.com/taxbaik/admin/login -> HTTP 200
|
||||
2026-07-04T13:13:48.3636757Z ✓ public/admin smoke passed
|
||||
2026-07-04T13:13:48.3636921Z ✓ 실제 공개 도메인 전체 정상
|
||||
2026-07-04T13:13:48.9939879Z evaluating expression 'success()'
|
||||
2026-07-04T13:13:48.9940693Z expression 'success()' evaluated to 'true'
|
||||
2026-07-04T13:13:48.9940826Z ⭐ Run Post Setup .NET
|
||||
2026-07-04T13:13:48.9941279Z Writing entry to tarball workflow/outputcmd.txt len:0
|
||||
2026-07-04T13:13:48.9941448Z Writing entry to tarball workflow/statecmd.txt len:0
|
||||
2026-07-04T13:13:48.9941556Z Writing entry to tarball workflow/pathcmd.txt len:0
|
||||
2026-07-04T13:13:48.9941653Z Writing entry to tarball workflow/envs.txt len:0
|
||||
2026-07-04T13:13:48.9941730Z Writing entry to tarball workflow/SUMMARY.md len:0
|
||||
2026-07-04T13:13:48.9941978Z Extracting content to '/var/run/act'
|
||||
2026-07-04T13:13:49.0352151Z run post step for 'Setup .NET'
|
||||
2026-07-04T13:13:49.0353027Z executing remote job container: [node /var/run/act/actions/2d637816dd86ec9ff59dad9ec3547bf90b88133b3029538a91ec96ac7f316336/dist/cache-save/index.js]
|
||||
2026-07-04T13:13:49.0709594Z 🐳 docker exec cmd=[node /var/run/act/actions/2d637816dd86ec9ff59dad9ec3547bf90b88133b3029538a91ec96ac7f316336/dist/cache-save/index.js] user= workdir=
|
||||
2026-07-04T13:13:49.0710046Z Exec command '[node /var/run/act/actions/2d637816dd86ec9ff59dad9ec3547bf90b88133b3029538a91ec96ac7f316336/dist/cache-save/index.js]'
|
||||
2026-07-04T13:13:49.0710678Z Working directory '/workspace/***/taxbaik'
|
||||
2026-07-04T13:13:50.0657531Z (node:1547) [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead.
|
||||
2026-07-04T13:13:50.0658705Z (Use `node --trace-deprecation ...` to show where the warning was created)
|
||||
2026-07-04T13:13:50.0821237Z ✅ Success - Post Setup .NET
|
||||
2026-07-04T13:13:50.1480311Z evaluating expression 'always()'
|
||||
2026-07-04T13:13:50.1481264Z expression 'always()' evaluated to 'true'
|
||||
2026-07-04T13:13:50.1481402Z ⭐ Run Post Checkout code
|
||||
2026-07-04T13:13:50.1481857Z Writing entry to tarball workflow/outputcmd.txt len:0
|
||||
2026-07-04T13:13:50.1482072Z Writing entry to tarball workflow/statecmd.txt len:0
|
||||
2026-07-04T13:13:50.1482171Z Writing entry to tarball workflow/pathcmd.txt len:0
|
||||
2026-07-04T13:13:50.1482287Z Writing entry to tarball workflow/envs.txt len:0
|
||||
2026-07-04T13:13:50.1482592Z Writing entry to tarball workflow/SUMMARY.md len:0
|
||||
2026-07-04T13:13:50.1482764Z Extracting content to '/var/run/act'
|
||||
2026-07-04T13:13:50.1505527Z run post step for 'Checkout code'
|
||||
2026-07-04T13:13:50.1506785Z executing remote job container: [node /var/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/dist/index.js]
|
||||
2026-07-04T13:13:50.1812860Z 🐳 docker exec cmd=[node /var/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/dist/index.js] user= workdir=
|
||||
2026-07-04T13:13:50.1813559Z Exec command '[node /var/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/dist/index.js]'
|
||||
2026-07-04T13:13:50.1814311Z Working directory '/workspace/***/taxbaik'
|
||||
2026-07-04T13:13:50.4028369Z [command]/usr/bin/git version
|
||||
2026-07-04T13:13:50.4086493Z git version 2.54.0
|
||||
2026-07-04T13:13:50.4124253Z ***
|
||||
2026-07-04T13:13:50.4143368Z Temporarily overriding HOME='/tmp/799355da-5b84-4d6e-aa99-c0ced6989bd6' before making global git config changes
|
||||
2026-07-04T13:13:50.4145671Z Adding repository directory to the temporary git global config as a safe directory
|
||||
2026-07-04T13:13:50.4156595Z [command]/usr/bin/git config --global --add safe.directory /workspace/***/taxbaik
|
||||
2026-07-04T13:13:50.4203190Z [command]/usr/bin/git config --local --name-only --get-regexp core\.sshCommand
|
||||
2026-07-04T13:13:50.4509074Z [command]/usr/bin/git submodule foreach --recursive sh -c "git config --local --name-only --get-regexp 'core\.sshCommand' && git config --local --unset-all 'core.sshCommand' || :"
|
||||
2026-07-04T13:13:50.4819706Z [command]/usr/bin/git config --local --name-only --get-regexp http\.http\:\/\/gitea\:3000\/\.extraheader
|
||||
2026-07-04T13:13:50.4856539Z http.http://gitea:3000/.extraheader
|
||||
2026-07-04T13:13:50.4896853Z [command]/usr/bin/git config --local --unset-all http.http://gitea:3000/.extraheader
|
||||
2026-07-04T13:13:50.4958619Z [command]/usr/bin/git submodule foreach --recursive sh -c "git config --local --name-only --get-regexp 'http\.http\:\/\/gitea\:3000\/\.extraheader' && git config --local --unset-all 'http.http://gitea:3000/.extraheader' || :"
|
||||
2026-07-04T13:13:50.5348711Z [command]/usr/bin/git config --local --name-only --get-regexp ^includeIf\.gitdir:
|
||||
2026-07-04T13:13:50.5381463Z [command]/usr/bin/git submodule foreach --recursive git config --local --show-origin --name-only --get-regexp remote.origin.url
|
||||
2026-07-04T13:13:50.5732977Z ✅ Success - Post Checkout code
|
||||
2026-07-04T13:13:50.5817281Z Cleaning up container for job build-and-deploy
|
||||
2026-07-04T13:13:51.3647302Z Removed container: 08d700f8667717ce78a305700f60f13b228a0d19241928b95a601d63d13f1a22
|
||||
2026-07-04T13:13:51.3662848Z 🐳 docker volume rm GITEA-ACTIONS-TASK-1556-WORKFLOW-TaxBaik-CI-CD-JOB-build-and-de-6297a4cd6c76440083f1331088cff47725d01c4362935c7b8a69a7f486001683
|
||||
2026-07-04T13:13:51.5102730Z 🐳 docker volume rm GITEA-ACTIONS-TASK-1556-WORKFLOW-TaxBaik-CI-CD-JOB-build-and-de-6297a4cd6c76440083f1331088cff47725d01c4362935c7b8a69a7f486001683-env
|
||||
2026-07-04T13:13:51.8129561Z 🏁 Job succeeded
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user