From e7e01d0cd85fcc67192e198c9f8707fbcc3d6280 Mon Sep 17 00:00:00 2001 From: Claude Code Date: Fri, 26 Jun 2026 16:46:36 +0900 Subject: [PATCH] =?UTF-8?q?=EB=A7=88=EC=9D=B4=EA=B7=B8=EB=A0=88=EC=9D=B4?= =?UTF-8?q?=EC=85=98=20=EB=B0=8F=20=EB=B3=B4=EC=95=88=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - MigrationRunner: 이미 존재하는 테이블에 대한 "relation already exists" 오류 처리 - V002, V003 마이그레이션: ON CONFLICT DO NOTHING으로 멱등성 보장 - Web, Admin Program.cs: app.UseAntiforgery() 미들웨어 추가 (anti-forgery 토큰 검증) 변경사항: - 마이그레이션 재실행 시에도 안전하게 처리 - 폼 제출 시 CSRF 공격 방지 - 관리자 로그인 페이지 405 에러 해결 Co-Authored-By: Claude Haiku 4.5 --- TaxBaik.Admin/Program.cs | 1 + TaxBaik.Infrastructure/Data/MigrationRunner.cs | 11 +++++++++++ TaxBaik.Web/Program.cs | 1 + db/migrations/V002__SeedData.sql | 6 ++++-- db/migrations/V003__SeedAdminAndBlogPosts.sql | 6 ++++-- 5 files changed, 21 insertions(+), 4 deletions(-) diff --git a/TaxBaik.Admin/Program.cs b/TaxBaik.Admin/Program.cs index 4351ab2..93b7bb1 100644 --- a/TaxBaik.Admin/Program.cs +++ b/TaxBaik.Admin/Program.cs @@ -44,6 +44,7 @@ app.UseStaticFiles(); app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); +app.UseAntiforgery(); app.MapRazorComponents() .AddInteractiveServerRenderMode(); diff --git a/TaxBaik.Infrastructure/Data/MigrationRunner.cs b/TaxBaik.Infrastructure/Data/MigrationRunner.cs index 0400828..994d164 100644 --- a/TaxBaik.Infrastructure/Data/MigrationRunner.cs +++ b/TaxBaik.Infrastructure/Data/MigrationRunner.cs @@ -122,6 +122,17 @@ public class MigrationRunner Console.WriteLine($"✓ Migration {migration.Version} executed"); } + catch (Npgsql.PostgresException pgEx) when (pgEx.SqlState == "42P07") // relation already exists + { + // Already executed previously; mark as done + Console.WriteLine($"ℹ Migration {migration.Version} already applied"); + using var insertCmd = conn.CreateCommand(); + insertCmd.CommandText = + "INSERT INTO schema_migrations (version, description) VALUES (@version, @description) ON CONFLICT (version) DO NOTHING;"; + insertCmd.Parameters.AddWithValue("@version", migration.Version); + insertCmd.Parameters.AddWithValue("@description", migration.Description); + await insertCmd.ExecuteNonQueryAsync(); + } catch (Exception ex) { Console.WriteLine($"✗ Migration {migration.Version} failed: {ex.Message}"); diff --git a/TaxBaik.Web/Program.cs b/TaxBaik.Web/Program.cs index 34e96d4..3a392c9 100644 --- a/TaxBaik.Web/Program.cs +++ b/TaxBaik.Web/Program.cs @@ -29,6 +29,7 @@ app.UsePathBase("/taxbaik"); app.UseResponseCompression(); app.UseStaticFiles(); app.UseRouting(); +app.UseAntiforgery(); if (!app.Environment.IsDevelopment()) { diff --git a/db/migrations/V002__SeedData.sql b/db/migrations/V002__SeedData.sql index 472527f..fbf2f87 100644 --- a/db/migrations/V002__SeedData.sql +++ b/db/migrations/V002__SeedData.sql @@ -5,11 +5,13 @@ INSERT INTO categories (name, slug, sort_order) VALUES ('부동산 세금', 'real-estate-tax', 2), ('종합소득세', 'income-tax', 3), ('부가가치세', 'vat', 4), - ('가족자산·증여', 'family-asset', 5); + ('가족자산·증여', 'family-asset', 5) +ON CONFLICT (slug) DO NOTHING; INSERT INTO site_settings (key, value) VALUES ('site.title', '백원숙 세무회계 | 사업자·부동산·증여 세무 상담'), ('site.description', '사업자 기장, 부동산 양도세·증여세, 종합소득세 전문 상담. 온라인 맞춤 상담 제공.'), ('kakao.channel.url', ''), ('phone.main', ''), - ('consultation.fee.text','상담료 7만~20만 원, 계약 체결 시 일부 차감'); + ('consultation.fee.text','상담료 7만~20만 원, 계약 체결 시 일부 차감') +ON CONFLICT (key) DO NOTHING; diff --git a/db/migrations/V003__SeedAdminAndBlogPosts.sql b/db/migrations/V003__SeedAdminAndBlogPosts.sql index 8540147..a6581a4 100644 --- a/db/migrations/V003__SeedAdminAndBlogPosts.sql +++ b/db/migrations/V003__SeedAdminAndBlogPosts.sql @@ -1,7 +1,8 @@ -- 초기 관리자 계정 (비밀번호: admin123) -- bcrypt hash: $2a$11$N9qo8uLOickgx2ZMRZoMye (실제 환경에서는 강력한 암호 사용) INSERT INTO admin_users (username, password_hash, created_at) -VALUES ('admin', '$2a$11$N9qo8uLOickgx2ZMRZoMye6IjfQTp5emXyqhT3jrDZWCqYIxJkAOq', NOW()); +VALUES ('admin', '$2a$11$N9qo8uLOickgx2ZMRZoMye6IjfQTp5emXyqhT3jrDZWCqYIxJkAOq', 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) @@ -75,4 +76,5 @@ VALUES '증여세를 절감하는 전략적인 증여 방법을 소개합니다.', NOW(), NOW() -); +) +ON CONFLICT (slug) DO NOTHING;