Bladeren bron

first commit

X\choro 1 jaar geleden
commit
3f58474b37
100 gewijzigde bestanden met toevoegingen van 5860 en 0 verwijderingen
  1. 30 0
      .dockerignore
  2. 2 0
      .gitignore
  3. 25 0
      economy.sln
  4. 232 0
      economy/.editorconfig
  5. 150 0
      economy/Controllers/DayController.cs
  6. 74 0
      economy/Controllers/EmissionController.cs
  7. 64 0
      economy/Controllers/FIFAController.cs
  8. 77 0
      economy/Controllers/FinancialController.cs
  9. 70 0
      economy/Controllers/FlowerController.cs
  10. 73 0
      economy/Controllers/GoldController.cs
  11. 32 0
      economy/Controllers/HomeController.cs
  12. 43 0
      economy/Controllers/LottoController.cs
  13. 71 0
      economy/Controllers/OilController.cs
  14. 100 0
      economy/Controllers/PriceController.cs
  15. 30 0
      economy/Dockerfile
  16. 49 0
      economy/Helpers/Common.cs
  17. 92 0
      economy/Helpers/FlexibleTypeConverter.cs
  18. 51 0
      economy/Models/AnniversaryModel.cs
  19. 74 0
      economy/Models/Clients.cs
  20. 60 0
      economy/Models/EmissionModel.cs
  21. 9 0
      economy/Models/ErrorViewModel.cs
  22. 54 0
      economy/Models/ExchangeModel.cs
  23. 50 0
      economy/Models/FIFAModel.cs
  24. 73 0
      economy/Models/FlowerModel.cs
  25. 61 0
      economy/Models/GoldModel.cs
  26. 51 0
      economy/Models/HolidayModel.cs
  27. 54 0
      economy/Models/InterestModel.cs
  28. 54 0
      economy/Models/InternationalModel.cs
  29. 52 0
      economy/Models/LottoModel.cs
  30. 56 0
      economy/Models/OilModel.cs
  31. 88 0
      economy/Models/Pagination.cs
  32. 82 0
      economy/Models/ProductModel.cs
  33. 19 0
      economy/Models/Request/Days/Anniversary.cs
  34. 19 0
      economy/Models/Request/Days/Holiday.cs
  35. 19 0
      economy/Models/Request/Days/Seasonal.cs
  36. 19 0
      economy/Models/Request/Days/Sundry.cs
  37. 40 0
      economy/Models/Request/FIFA.cs
  38. 29 0
      economy/Models/Request/Financial/Exchange.cs
  39. 29 0
      economy/Models/Request/Financial/Interest.cs
  40. 29 0
      economy/Models/Request/Financial/International.cs
  41. 18 0
      economy/Models/Request/Lotto.cs
  42. 38 0
      economy/Models/Request/Market/Emission.cs
  43. 50 0
      economy/Models/Request/Market/Flower.cs
  44. 38 0
      economy/Models/Request/Market/Gold.cs
  45. 34 0
      economy/Models/Request/Market/Oil.cs
  46. 40 0
      economy/Models/Request/Product/Detail.cs
  47. 23 0
      economy/Models/Request/Product/List.cs
  48. 65 0
      economy/Models/Response/Day/Anniversary.cs
  49. 65 0
      economy/Models/Response/Day/Holiday.cs
  50. 71 0
      economy/Models/Response/Day/Seasonal.cs
  51. 71 0
      economy/Models/Response/Day/Sundry.cs
  52. 110 0
      economy/Models/Response/FIFA.cs
  53. 46 0
      economy/Models/Response/Financial/Exchange.cs
  54. 22 0
      economy/Models/Response/Financial/Interest.cs
  55. 95 0
      economy/Models/Response/Financial/International.cs
  56. 63 0
      economy/Models/Response/Lotto.cs
  57. 85 0
      economy/Models/Response/Market/Emission.cs
  58. 56 0
      economy/Models/Response/Market/Flower.cs
  59. 85 0
      economy/Models/Response/Market/Gold.cs
  60. 67 0
      economy/Models/Response/Market/Oil.cs
  61. 70 0
      economy/Models/Response/Product/Detail.cs
  62. 45 0
      economy/Models/Response/Product/List.cs
  63. 51 0
      economy/Models/SeasonalModel.cs
  64. 51 0
      economy/Models/SundryModel.cs
  65. 80 0
      economy/Models/View.cs
  66. 39 0
      economy/Program.cs
  67. 49 0
      economy/Properties/launchSettings.json
  68. 60 0
      economy/Views/Component/Pagination.cshtml
  69. 91 0
      economy/Views/Day/Anniversary.cshtml
  70. 92 0
      economy/Views/Day/Holiday.cshtml
  71. 14 0
      economy/Views/Day/NavTab.cshtml
  72. 88 0
      economy/Views/Day/Seasonal.cshtml
  73. 88 0
      economy/Views/Day/Sundry.cshtml
  74. 110 0
      economy/Views/Emission/Index.cshtml
  75. 124 0
      economy/Views/FIFA/Index.cshtml
  76. 89 0
      economy/Views/Financial/Exchange.cshtml
  77. 62 0
      economy/Views/Financial/Interest.cshtml
  78. 332 0
      economy/Views/Financial/International.cshtml
  79. 8 0
      economy/Views/Financial/NavTab.cshtml
  80. 103 0
      economy/Views/Flower/Index.cshtml
  81. 110 0
      economy/Views/Gold/Index.cshtml
  82. 8 0
      economy/Views/Home/Index.cshtml
  83. 6 0
      economy/Views/Home/Privacy.cshtml
  84. 94 0
      economy/Views/Lotto/Index.cshtml
  85. 86 0
      economy/Views/Oil/Index.cshtml
  86. 102 0
      economy/Views/Price/Detail.cshtml
  87. 78 0
      economy/Views/Price/List.cshtml
  88. 117 0
      economy/Views/SCSS/style.scss
  89. 25 0
      economy/Views/Shared/Error.cshtml
  90. 87 0
      economy/Views/Shared/_Layout.cshtml
  91. 48 0
      economy/Views/Shared/_Layout.cshtml.css
  92. 2 0
      economy/Views/Shared/_ValidationScriptsPartial.cshtml
  93. 3 0
      economy/Views/_ViewImports.cshtml
  94. 3 0
      economy/Views/_ViewStart.cshtml
  95. 8 0
      economy/appsettings.Development.json
  96. 9 0
      economy/appsettings.json
  97. BIN
      economy/bin/Debug/net8.0/Microsoft.Data.SqlClient.dll
  98. BIN
      economy/bin/Debug/net8.0/Microsoft.EntityFrameworkCore.Abstractions.dll
  99. BIN
      economy/bin/Debug/net8.0/Microsoft.EntityFrameworkCore.Relational.dll
  100. BIN
      economy/bin/Debug/net8.0/Microsoft.EntityFrameworkCore.SqlServer.dll

+ 30 - 0
.dockerignore

@@ -0,0 +1,30 @@
+**/.classpath
+**/.dockerignore
+**/.env
+**/.git
+**/.gitignore
+**/.project
+**/.settings
+**/.toolstarget
+**/.vs
+**/.vscode
+**/*.*proj.user
+**/*.dbmdl
+**/*.jfm
+**/azds.yaml
+**/bin
+**/charts
+**/docker-compose*
+**/Dockerfile*
+**/node_modules
+**/npm-debug.log
+**/obj
+**/secrets.dev.yaml
+**/values.dev.yaml
+LICENSE
+README.md
+!**/.gitignore
+!.git/HEAD
+!.git/config
+!.git/packed-refs
+!.git/refs/heads/**

+ 2 - 0
.gitignore

@@ -0,0 +1,2 @@
+/economy/node_modules
+.vs/

+ 25 - 0
economy.sln

@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.11.35327.3
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "economy", "economy\economy.csproj", "{8588211F-14A5-4019-9CF0-0E52AE26314F}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{8588211F-14A5-4019-9CF0-0E52AE26314F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{8588211F-14A5-4019-9CF0-0E52AE26314F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{8588211F-14A5-4019-9CF0-0E52AE26314F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{8588211F-14A5-4019-9CF0-0E52AE26314F}.Release|Any CPU.Build.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		SolutionGuid = {355E2E2D-79E8-4A14-925C-6DE02B6E7167}
+	EndGlobalSection
+EndGlobal

+ 232 - 0
economy/.editorconfig

@@ -0,0 +1,232 @@
+# 상위 디렉터리에서 .editorconfig 설정을 상속하려면 아래 행을 제거하세요.
+root = true
+
+# C# 파일
+[*.cs]
+
+#### 코어 EditorConfig 옵션 ####
+
+# 들여쓰기 및 간격
+indent_size = 4
+indent_style = space
+tab_width = 4
+
+# 새 줄 기본 설정
+end_of_line = crlf
+insert_final_newline = false
+
+#### .NET 코딩 규칙 ####
+
+# Using 구성
+dotnet_separate_import_directive_groups = false
+dotnet_sort_system_directives_first = false
+file_header_template = unset
+
+# this. 및 Me. 기본 설정
+dotnet_style_qualification_for_event = false
+dotnet_style_qualification_for_field = false
+dotnet_style_qualification_for_method = false
+dotnet_style_qualification_for_property = false
+
+# 언어 키워드 및 BCL 형식 기본 설정
+dotnet_style_predefined_type_for_locals_parameters_members = true
+dotnet_style_predefined_type_for_member_access = true
+
+# 괄호 기본 설정
+dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity
+dotnet_style_parentheses_in_other_binary_operators = always_for_clarity
+dotnet_style_parentheses_in_other_operators = never_if_unnecessary
+dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity
+
+# 한정자 기본 설정
+dotnet_style_require_accessibility_modifiers = for_non_interface_members
+
+# 식 수준 기본 설정
+dotnet_style_coalesce_expression = true
+dotnet_style_collection_initializer = true
+dotnet_style_explicit_tuple_names = true
+dotnet_style_namespace_match_folder = true
+dotnet_style_null_propagation = true
+dotnet_style_object_initializer = true
+dotnet_style_operator_placement_when_wrapping = beginning_of_line
+dotnet_style_prefer_auto_properties = true
+dotnet_style_prefer_collection_expression = when_types_loosely_match
+dotnet_style_prefer_compound_assignment = true
+dotnet_style_prefer_conditional_expression_over_assignment = true
+dotnet_style_prefer_conditional_expression_over_return = true
+dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed
+dotnet_style_prefer_inferred_anonymous_type_member_names = true
+dotnet_style_prefer_inferred_tuple_names = true
+dotnet_style_prefer_is_null_check_over_reference_equality_method = true
+dotnet_style_prefer_simplified_boolean_expressions = true
+dotnet_style_prefer_simplified_interpolation = true
+
+# 필드 기본 설정
+dotnet_style_readonly_field = true
+
+# 매개 변수 기본 설정
+dotnet_code_quality_unused_parameters = all
+
+# 비표시 오류(Suppression) 기본 설정
+dotnet_remove_unnecessary_suppression_exclusions = none
+
+# 새 줄 기본 설정
+dotnet_style_allow_multiple_blank_lines_experimental = true
+dotnet_style_allow_statement_immediately_after_block_experimental = true
+
+#### C# 코딩 규칙 ####
+
+# var 기본 설정
+csharp_style_var_elsewhere = false
+csharp_style_var_for_built_in_types = false
+csharp_style_var_when_type_is_apparent = false
+
+# 식 본문 멤버
+csharp_style_expression_bodied_accessors = true
+csharp_style_expression_bodied_constructors = false
+csharp_style_expression_bodied_indexers = true
+csharp_style_expression_bodied_lambdas = true
+csharp_style_expression_bodied_local_functions = false
+csharp_style_expression_bodied_methods = false
+csharp_style_expression_bodied_operators = false
+csharp_style_expression_bodied_properties = true
+
+# 패턴 일치 기본 설정
+csharp_style_pattern_matching_over_as_with_null_check = true
+csharp_style_pattern_matching_over_is_with_cast_check = true
+csharp_style_prefer_extended_property_pattern = true
+csharp_style_prefer_not_pattern = true
+csharp_style_prefer_pattern_matching = true
+csharp_style_prefer_switch_expression = true
+
+# Null 검사 기본 설정
+csharp_style_conditional_delegate_call = true
+
+# 한정자 기본 설정
+csharp_prefer_static_anonymous_function = true
+csharp_prefer_static_local_function = true
+csharp_preferred_modifier_order = public,private,protected,internal,file,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async
+csharp_style_prefer_readonly_struct = true
+csharp_style_prefer_readonly_struct_member = true
+
+# 코드 블록 기본 설정
+csharp_prefer_braces = true
+csharp_prefer_simple_using_statement = true
+csharp_style_namespace_declarations = block_scoped
+csharp_style_prefer_method_group_conversion = true
+csharp_style_prefer_primary_constructors = true
+csharp_style_prefer_top_level_statements = true
+
+# 식 수준 기본 설정
+csharp_prefer_simple_default_expression = true
+csharp_style_deconstructed_variable_declaration = true
+csharp_style_implicit_object_creation_when_type_is_apparent = true
+csharp_style_inlined_variable_declaration = true
+csharp_style_prefer_index_operator = true
+csharp_style_prefer_local_over_anonymous_function = true
+csharp_style_prefer_null_check_over_type_check = true
+csharp_style_prefer_range_operator = true
+csharp_style_prefer_tuple_swap = true
+csharp_style_prefer_utf8_string_literals = true
+csharp_style_throw_expression = true
+csharp_style_unused_value_assignment_preference = discard_variable
+csharp_style_unused_value_expression_statement_preference = discard_variable
+
+# 'using' 지시문 기본 설정
+csharp_using_directive_placement = outside_namespace
+
+# 새 줄 기본 설정
+csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true
+csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true
+csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true
+csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true
+csharp_style_allow_embedded_statements_on_same_line_experimental = true
+
+#### C# 서식 설정 규칙 ####
+
+# 새 줄 기본 설정
+csharp_new_line_before_catch = true
+csharp_new_line_before_else = true
+csharp_new_line_before_finally = true
+csharp_new_line_before_members_in_anonymous_types = true
+csharp_new_line_before_members_in_object_initializers = true
+csharp_new_line_before_open_brace = all
+csharp_new_line_between_query_expression_clauses = true
+
+# 들여쓰기 기본 설정
+csharp_indent_block_contents = true
+csharp_indent_braces = false
+csharp_indent_case_contents = true
+csharp_indent_case_contents_when_block = true
+csharp_indent_labels = one_less_than_current
+csharp_indent_switch_labels = true
+
+# 공간 기본 설정
+csharp_space_after_cast = false
+csharp_space_after_colon_in_inheritance_clause = true
+csharp_space_after_comma = true
+csharp_space_after_dot = false
+csharp_space_after_keywords_in_control_flow_statements = true
+csharp_space_after_semicolon_in_for_statement = true
+csharp_space_around_binary_operators = before_and_after
+csharp_space_around_declaration_statements = false
+csharp_space_before_colon_in_inheritance_clause = true
+csharp_space_before_comma = false
+csharp_space_before_dot = false
+csharp_space_before_open_square_brackets = false
+csharp_space_before_semicolon_in_for_statement = false
+csharp_space_between_empty_square_brackets = false
+csharp_space_between_method_call_empty_parameter_list_parentheses = false
+csharp_space_between_method_call_name_and_opening_parenthesis = false
+csharp_space_between_method_call_parameter_list_parentheses = false
+csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
+csharp_space_between_method_declaration_name_and_open_parenthesis = false
+csharp_space_between_method_declaration_parameter_list_parentheses = false
+csharp_space_between_parentheses = false
+csharp_space_between_square_brackets = false
+
+# 기본 설정 래핑
+csharp_preserve_single_line_blocks = true
+csharp_preserve_single_line_statements = true
+
+#### 명명 스타일 ####
+
+# 명명 규칙
+
+dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
+dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
+dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
+
+dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
+dotnet_naming_rule.types_should_be_pascal_case.symbols = types
+dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
+
+dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
+dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
+dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
+
+# 기호 사양
+
+dotnet_naming_symbols.interface.applicable_kinds = interface
+dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.interface.required_modifiers = 
+
+dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
+dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.types.required_modifiers = 
+
+dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
+dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.non_field_members.required_modifiers = 
+
+# 명명 스타일
+
+dotnet_naming_style.pascal_case.required_prefix = 
+dotnet_naming_style.pascal_case.required_suffix = 
+dotnet_naming_style.pascal_case.word_separator = 
+dotnet_naming_style.pascal_case.capitalization = pascal_case
+
+dotnet_naming_style.begins_with_i.required_prefix = I
+dotnet_naming_style.begins_with_i.required_suffix = 
+dotnet_naming_style.begins_with_i.word_separator = 
+dotnet_naming_style.begins_with_i.capitalization = pascal_case

+ 150 - 0
economy/Controllers/DayController.cs

@@ -0,0 +1,150 @@
+using Microsoft.AspNetCore.Mvc;
+using economy.Helpers;
+using economy.Models;
+using economy.Models.Anniversary;
+using economy.Models.Holiday;
+using economy.Models.Seasonal;
+using economy.Models.Sundry;
+
+namespace economy.Controllers
+{
+    public class DayController : Controller
+    {
+        private readonly DataGoKR _dataGoKR;
+        private Dictionary<string, string> _queryString;
+
+        public DayController(DataGoKR dataGoKR)
+        {
+            _dataGoKR = dataGoKR;
+        }
+
+        // 공휴일/국경일
+        public async Task<IActionResult> Holiday(Models.Holiday.Request request)
+        {
+            if (!ModelState.IsValid)
+            {
+                return BadRequest(ModelState);
+            }
+
+            HolidayModel holiday = new HolidayModel(_dataGoKR);
+            Models.Holiday.Response itemList = await holiday.GetHoliday(request);
+
+            if (itemList is not null)
+            {
+                itemList.Body.Items.ItemList = itemList.Body.Items.ItemList.Select((row, index) =>
+                {
+                    row.Num += (index + 1);
+                    row.LocDate = Common.StringToDateFormat(row.LocDate);
+                    return row;
+                }).ToList();
+            }
+
+            var viewModel = new View<Models.Holiday.Request, Models.Holiday.Response>();
+            viewModel.Request = request;
+            viewModel.Response = itemList;
+
+            // 출력 연도 지정
+            ViewData["years"] = Enumerable.Range(2000, DateTime.Now.Year - 2000 + 1).Reverse();
+            ViewData["type"] = "Holiday";
+
+            return View(viewModel);
+        }
+
+        // 기념일
+        public async Task<IActionResult> Anniversary(Models.Anniversary.Request request)
+        {
+            if (!ModelState.IsValid)
+            {
+                return BadRequest(ModelState);
+            }
+
+            AnniversaryModel anniversary = new AnniversaryModel(_dataGoKR);
+            Models.Anniversary.Response itemList = await anniversary.GetAnniversary(request);
+
+            if (itemList is not null)
+            {
+                itemList.Body.Items.ItemList = itemList.Body.Items.ItemList.Select((row, index) =>
+                {
+                    row.Num += (index + 1);
+                    row.LocDate = Common.StringToDateFormat(row.LocDate);
+                    return row;
+                }).ToList();
+            }
+
+            var viewModel = new View<Models.Anniversary.Request, Models.Anniversary.Response>();
+            viewModel.Request = request;
+            viewModel.Response = itemList;
+
+            // 출력 연도 지정
+            ViewData["years"] = Enumerable.Range(2000, DateTime.Now.Year - 2000 + 1).Reverse();
+            ViewData["type"] = "Anniversary";
+
+            return View(viewModel);
+        }
+
+        // 24절기
+        public async Task<IActionResult> Seasonal(Models.Seasonal.Request request)
+        {
+            if (!ModelState.IsValid)
+            {
+                return BadRequest(ModelState);
+            }
+
+            SeasonalModel seasonal = new SeasonalModel(_dataGoKR);
+            Models.Seasonal.Response itemList = await seasonal.GetSeasonal(request);
+
+            if (itemList is not null)
+            {
+                itemList.Body.Items.ItemList = itemList.Body.Items.ItemList.Select((row, index) =>
+                {
+                    row.Num += (index + 1);
+                    row.LocDate = Common.StringToDateFormat(row.LocDate);
+                    return row;
+                }).ToList();
+            }
+
+            var viewModel = new View<Models.Seasonal.Request, Models.Seasonal.Response>();
+            viewModel.Request = request;
+            viewModel.Response = itemList;
+
+            // 출력 연도 지정
+            ViewData["years"] = Enumerable.Range(2000, DateTime.Now.Year - 2000 + 1).Reverse();
+            ViewData["type"] = "Seasonal";
+
+            return View(viewModel);
+        }
+
+
+        // 잡절
+        public async Task<IActionResult> Sundry(Models.Sundry.Request request)
+        {
+            if (!ModelState.IsValid)
+            {
+                return BadRequest(ModelState);
+            }
+
+            SundryModel sundry = new SundryModel(_dataGoKR);
+            Models.Sundry.Response itemList = await sundry.GetSundry(request);
+
+            if (itemList is not null)
+            {
+                itemList.Body.Items.ItemList = itemList.Body.Items.ItemList.Select((row, index) =>
+                {
+                    row.Num += (index + 1);
+                    row.LocDate = Common.StringToDateFormat(row.LocDate);
+                    return row;
+                }).ToList();
+            }
+
+            var viewModel = new View<Models.Sundry.Request, Models.Sundry.Response>();
+            viewModel.Request = request;
+            viewModel.Response = itemList;
+
+            // 출력 연도 지정
+            ViewData["years"] = Enumerable.Range(2000, DateTime.Now.Year - 2000 + 1).Reverse();
+            ViewData["type"] = "Sundry";
+
+            return View(viewModel);
+        }
+    }
+}

+ 74 - 0
economy/Controllers/EmissionController.cs

@@ -0,0 +1,74 @@
+using economy.Helpers;
+using economy.Models;
+using economy.Models.Emission;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.Filters;
+using Microsoft.AspNetCore.WebUtilities;
+
+namespace economy.Controllers
+{
+    public class EmissionController : Controller
+    {
+        private readonly DataGoKR _dataGoKR;
+        private Dictionary<string, string> _queryString;
+
+        public EmissionController(DataGoKR dataGoKR)
+        {
+            _dataGoKR = dataGoKR;
+        }
+
+        public override void OnActionExecuting(ActionExecutingContext context)
+        {
+            _queryString = QueryHelpers.ParseQuery(HttpContext.Request.QueryString.Value).ToDictionary(k => k.Key, v => string.Join(",", v.Value));
+
+            ViewBag.QueryString = _queryString;
+
+            base.OnActionExecuting(context);
+        }
+
+        public async Task<IActionResult> Index(Request request)
+        {
+            if (!ModelState.IsValid)
+            {
+                return BadRequest(ModelState);
+            }
+
+            EmissionModel emissionModel = new EmissionModel(_dataGoKR);
+            Response itemList = await emissionModel.GetEmissionPriceInfo(request);
+
+            if (itemList is not null)
+            {
+                int listNum = Common.CalcListNumber(itemList.Body.TotalCount, request.PageNo, request.NumOfRows);
+                itemList.Body.Items.ItemList = itemList.Body.Items.ItemList.Select((row, index) =>
+                {
+                    row.Num = (listNum - index);
+                    row.Clpr = Common.NumberFormat(row.Clpr);
+                    row.Vs = Common.NumberFormat(row.Vs);
+                    row.FltRt = Common.NumberFormat(row.FltRt, "0.##");
+                    row.Mkp = Common.NumberFormat(row.Mkp);
+                    row.Hipr = Common.NumberFormat(row.Hipr);
+                    row.Lopr = Common.NumberFormat(row.Lopr);
+                    row.Trqu = Common.NumberFormat(row.Trqu);
+                    row.TrPrc = Common.NumberFormat(row.TrPrc);
+                    return row;
+                }).ToList();
+            }
+
+            var viewModel = new View<Request, Response>();
+            viewModel.SelectedListPerPage = request.NumOfRows;
+            viewModel.Request = request;
+            viewModel.Response = itemList;
+
+            var queryString = new
+            {
+                sDate = request.StartDate,
+                eDate = request.EndDate
+            };
+
+            var pagination = new Pagination(itemList.Body?.TotalCount, request.PageNo, request.NumOfRows, queryString);
+            viewModel.Pagination = pagination;
+
+            return View(viewModel);
+        }
+    }
+}

+ 64 - 0
economy/Controllers/FIFAController.cs

@@ -0,0 +1,64 @@
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.Filters;
+using Microsoft.AspNetCore.WebUtilities;
+using economy.Models;
+using economy.Models.FIFA;
+
+namespace economy.Controllers
+{
+    public class FIFAController : Controller
+    {
+        private readonly FIFA_API _fifaAPI;
+        private Dictionary<string, string> _queryString;
+
+        public FIFAController(FIFA_API fifaAPI)
+        {
+            _fifaAPI = fifaAPI;
+        }
+
+        public override void OnActionExecuting(ActionExecutingContext context)
+        {
+            _queryString = QueryHelpers.ParseQuery(HttpContext.Request.QueryString.Value).ToDictionary(k => k.Key, v => string.Join(",", v.Value));
+
+            ViewBag.QueryString = _queryString;
+
+            base.OnActionExecuting(context);
+        }
+
+        public async Task<IActionResult> Index(Request request)
+        {
+            if (!ModelState.IsValid)
+            {
+                return BadRequest(ModelState);
+            }
+
+            FIFAModel fifa = new FIFAModel(_fifaAPI);
+            Response itemList = await fifa.GetRanking(request);
+
+            if (itemList is not null)
+            {
+                itemList.Results = itemList.Results
+                  .Select((row, index) =>
+                  {
+                      row.Num += 1;
+                      row.FlagSrc = $"https://api.fifa.com/api/v3/picture/flags-sq-4/{row.IdCountry}";
+                      return row;
+                  })
+                  .ToList();
+
+                string ?type = request.Type.ToString();
+                if (itemList.Results is not null && type != string.Empty)
+                {
+                    itemList.Results = itemList.Results.Where(row => row.ConfederationName == type).ToList();
+                }
+            }
+
+            var viewModel = new View<Request, Response>();
+            viewModel.SelectedListPerPage = request.NumOfRows;
+            viewModel.Request = request;
+            viewModel.Response = itemList;
+
+            return View(viewModel);
+        }
+    }
+}

+ 77 - 0
economy/Controllers/FinancialController.cs

@@ -0,0 +1,77 @@
+using Microsoft.AspNetCore.Mvc;
+using economy.Models;
+using economy.Models.Exchange;
+using economy.Models.Interest;
+using economy.Models.International;
+
+namespace economy.Controllers
+{
+    public class FinancialController : Controller
+    {
+        private readonly KoreaEximGoKR _koreaEximGoKR;
+        private Dictionary<string, string> _queryString;
+
+        public FinancialController(KoreaEximGoKR koreaEximGoKR)
+        {
+            _koreaEximGoKR = koreaEximGoKR;
+        }
+
+        // 환율
+        public async Task<IActionResult> Exchange(Models.Exchange.Request request)
+        {
+            if (!ModelState.IsValid)
+            {
+                return BadRequest(ModelState);
+            }
+
+            ExchangeModel exchange = new ExchangeModel(_koreaEximGoKR);
+            Models.Exchange.Response itemList = await exchange.GetExchange(request);
+
+            var viewModel = new View<Models.Exchange.Request, Models.Exchange.Response>();
+            viewModel.Request = request;
+            viewModel.Response = itemList;
+
+            return View(viewModel);
+        }
+
+        // 대출 금리
+        public async Task<IActionResult> Interest(Models.Interest.Request request)
+        {
+            if (!ModelState.IsValid)
+            {
+                return BadRequest(ModelState);
+            }
+
+            InterestModel interest = new InterestModel(_koreaEximGoKR);
+            Models.Interest.Response itemList = await interest.GetInterestRate(request);
+
+            var viewModel = new View<Models.Interest.Request, Models.Interest.Response>();
+            viewModel.Request = request;
+            viewModel.Response = itemList;
+
+            ViewData["type"] = "Interest";
+
+            return View(viewModel);
+        }
+
+        // 국제 금리
+        public async Task<IActionResult> International(Models.International.Request request)
+        {
+            if (!ModelState.IsValid)
+            {
+                return BadRequest(ModelState);
+            }
+
+            InternationalModel international = new InternationalModel(_koreaEximGoKR);
+            Models.International.Response itemList = await international.GetInternationalRate(request);
+
+            var viewModel = new View<Models.International.Request, Models.International.Response>();
+            viewModel.Request = request;
+            viewModel.Response = itemList;
+
+            ViewData["type"] = "International";
+
+            return View(viewModel);
+        }
+    }
+}

+ 70 - 0
economy/Controllers/FlowerController.cs

@@ -0,0 +1,70 @@
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.Filters;
+using Microsoft.AspNetCore.WebUtilities;
+using economy.Helpers;
+using economy.Models;
+using economy.Models.Flower;
+
+namespace economy.Controllers
+{
+    public class FlowerController : Controller
+    {
+        private readonly FlowerAtOrKR _flowerAtOrKR;
+        private Dictionary<string, string> _queryString;
+
+        public FlowerController(FlowerAtOrKR flowerAtOrKR)
+        {
+            _flowerAtOrKR = flowerAtOrKR;
+        }
+
+        public override void OnActionExecuting(ActionExecutingContext context)
+        {
+            _queryString = QueryHelpers.ParseQuery(HttpContext.Request.QueryString.Value).ToDictionary(k => k.Key, v => string.Join(",", v.Value));
+
+            ViewBag.QueryString = _queryString;
+
+            base.OnActionExecuting(context);
+        }
+
+        public async Task<IActionResult> Index(Request request)
+        {
+            if (!ModelState.IsValid)
+            {
+                return BadRequest(ModelState);
+            }
+
+            FlowerModel flower = new FlowerModel(_flowerAtOrKR);
+            Response itemList = await flower.GetFlowerPriceInfo(request);
+
+            if (itemList is not null)
+            {
+                int listNum = Common.CalcListNumber(itemList.NumberOfRows, request.PageNo, request.NumOfRows);
+                itemList.Items = itemList.Items.Select((row, index) =>
+                {
+                    row.Num = (listNum - index);
+                    row.MaxAmt = Common.NumberFormat(row.MaxAmt);
+                    row.MinAmt = Common.NumberFormat(row.MinAmt);
+                    row.AvgAmt = Common.NumberFormat(row.AvgAmt);
+                    row.TotAmt = Common.NumberFormat(row.TotAmt);
+                    row.TotQty = Common.NumberFormat(row.TotQty);
+                    return row;
+                }).ToList();
+            }
+
+            var viewModel = new View<Request, Response>();
+            viewModel.SelectedListPerPage = request.NumOfRows;
+            viewModel.Request = request;
+            viewModel.Response = itemList;
+
+            var queryString = new
+            {
+                date = request.BaseDate
+            };
+
+            var pagination = new Pagination(itemList?.NumberOfRows, request.PageNo, request.NumOfRows, queryString);
+            viewModel.Pagination = pagination;
+
+            return View(viewModel);
+        }
+    }
+}

+ 73 - 0
economy/Controllers/GoldController.cs

@@ -0,0 +1,73 @@
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.Filters;
+using Microsoft.AspNetCore.WebUtilities;
+using economy.Helpers;
+using economy.Models;
+using economy.Models.Gold;
+
+namespace economy.Controllers
+{
+    public class GoldController : Controller
+    {
+        private readonly DataGoKR _dataGoKR;
+        private Dictionary<string, string> _queryString;
+
+        public GoldController(DataGoKR dataGoKR)
+        {
+            _dataGoKR = dataGoKR;
+        }
+
+        public override void OnActionExecuting(ActionExecutingContext context)
+        {
+            _queryString = QueryHelpers.ParseQuery(HttpContext.Request.QueryString.Value).ToDictionary(k => k.Key, v => string.Join(",", v.Value));
+
+            ViewBag.QueryString = _queryString;
+
+            base.OnActionExecuting(context);
+        }
+
+        public async Task<IActionResult> Index(Request request)
+        {
+            if (!ModelState.IsValid)
+            {
+                return BadRequest(ModelState);
+            }
+
+            GoldModel gold = new GoldModel(_dataGoKR);
+            Response itemList = await gold.GetGoldPriceInfo(request);
+
+            if (itemList is not null)
+            {
+                int listNum = Common.CalcListNumber(itemList.Body.TotalCount, request.PageNo, request.NumOfRows);
+                itemList.Body.Items.ItemList = itemList.Body.Items.ItemList.Select((row, index) =>
+                {
+                    row.Num = (listNum - index);
+                    row.Clpr = Common.NumberFormat(row.Clpr);
+                    row.Vs = Common.NumberFormat(row.Vs);
+                    row.FltRt = Common.NumberFormat(row.FltRt, "0.##");
+                    row.Hipr = Common.NumberFormat(row.Hipr);
+                    row.Lopr = Common.NumberFormat(row.Lopr);
+                    row.Trqu = Common.NumberFormat(row.Trqu);
+                    row.TrPrc = Common.NumberFormat(row.TrPrc);
+                    return row;
+                }).ToList();
+            }
+
+            var viewModel = new View<Request, Response>();
+            viewModel.SelectedListPerPage = request.NumOfRows;
+            viewModel.Request = request;
+            viewModel.Response = itemList;
+
+            var queryString = new
+            {
+                sDate = request.StartDate,
+                eDate = request.EndDate
+            };
+
+            var pagination = new Pagination(itemList.Body?.TotalCount, request.PageNo, request.NumOfRows, queryString);
+            viewModel.Pagination = pagination;
+
+            return View(viewModel);
+        }
+    }
+}

+ 32 - 0
economy/Controllers/HomeController.cs

@@ -0,0 +1,32 @@
+using economy.Models;
+using Microsoft.AspNetCore.Mvc;
+using System.Diagnostics;
+
+namespace economy.Controllers
+{
+    public class HomeController : Controller
+    {
+        private readonly ILogger<HomeController> _logger;
+
+        public HomeController(ILogger<HomeController> logger)
+        {
+            _logger = logger;
+        }
+
+        public IActionResult Index()
+        {
+            return View();
+        }
+
+        public IActionResult Privacy()
+        {
+            return View();
+        }
+
+        [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
+        public IActionResult Error()
+        {
+            return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
+        }
+    }
+}

+ 43 - 0
economy/Controllers/LottoController.cs

@@ -0,0 +1,43 @@
+using Microsoft.AspNetCore.Mvc;
+using economy.Models;
+using economy.Models.Lotto;
+
+namespace economy.Controllers
+{
+    public class LottoController : Controller
+    {
+        private readonly DhlotteryCoKR _dhlotteryCoKR;
+
+        public LottoController(DhlotteryCoKR dhlotteryCoKR)
+        {
+            _dhlotteryCoKR = dhlotteryCoKR;
+        }
+
+        public async Task<IActionResult> Index(Request request)
+        {
+            if (!ModelState.IsValid)
+            {
+                return BadRequest(ModelState);
+            }
+
+            // 주차 간격 계산
+            var last = (ushort)(((DateTime.Now - new DateTime(2002, 12, 7)).TotalDays / 7) + 1);
+            
+            if (request.Number <= 0 && last > 0)
+            {
+                request.Number = last;
+            }
+
+            LottoModel lotto = new LottoModel(_dhlotteryCoKR);
+            Response lottoInfo = await lotto.GetLottoNumber(request);
+
+            var viewModel = new View<Request, Response>();
+            viewModel.Request = request;
+            viewModel.Response = lottoInfo;
+
+            ViewBag.Last = last;
+
+            return View(viewModel);
+        }
+    }
+}

+ 71 - 0
economy/Controllers/OilController.cs

@@ -0,0 +1,71 @@
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.Filters;
+using Microsoft.AspNetCore.WebUtilities;
+using economy.Helpers;
+using economy.Models;
+using economy.Models.Oil;
+
+namespace economy.Controllers
+{
+    public class OilController : Controller
+    {
+        private readonly DataGoKR _dataGoKR;
+        private Dictionary<string, string> _queryString;
+
+        public OilController(DataGoKR dataGoKR)
+        {
+            _dataGoKR = dataGoKR;
+        }
+
+        public override void OnActionExecuting(ActionExecutingContext context)
+        {
+            _queryString = QueryHelpers.ParseQuery(HttpContext.Request.QueryString.Value).ToDictionary(k => k.Key, v => string.Join(",", v.Value));
+
+            ViewBag.QueryString = _queryString;
+
+            base.OnActionExecuting(context);
+        }
+
+        public async Task<IActionResult> Index(Request request)
+        {
+            if (!ModelState.IsValid)
+            {
+                return BadRequest(ModelState);
+            }
+
+            OilModel oil = new OilModel(_dataGoKR);
+            Response itemList = await oil.GetOilPriceInfo(request);
+
+            if (itemList is not null)
+            {
+                int listNum = Common.CalcListNumber(itemList.Body.TotalCount, request.PageNo, request.NumOfRows);
+                itemList.Body.Items.ItemList = itemList.Body.Items.ItemList.Select((row, index) =>
+                {
+                    row.Num = (listNum - index);
+                    row.OilCtg = Common.NumberFormat(row.OilCtg);
+                    row.WtAvgPrcCptn = Common.NumberFormat(row.WtAvgPrcCptn, "0.##");
+                    row.WtAvgPrcDisc = Common.NumberFormat(row.WtAvgPrcDisc);
+                    row.Trqu = Common.NumberFormat(row.Trqu);
+                    row.TrPrc = Common.NumberFormat(row.TrPrc);
+                    return row;
+                }).ToList();
+            }
+
+            var viewModel = new View<Request, Response>();
+            viewModel.SelectedListPerPage = request.NumOfRows;
+            viewModel.Request = request;
+            viewModel.Response = itemList;
+
+            var queryString = new
+            {
+                sDate = request.StartDate,
+                eDate = request.EndDate
+            };
+
+            var pagination = new Pagination(itemList.Body?.TotalCount, request.PageNo, request.NumOfRows, queryString);
+            viewModel.Pagination = pagination;
+
+            return View(viewModel);
+        }
+    }
+}

+ 100 - 0
economy/Controllers/PriceController.cs

@@ -0,0 +1,100 @@
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.WebUtilities;
+using Microsoft.AspNetCore.Mvc.Filters;
+using economy.Models;
+using economy.Helpers;
+using Request = economy.Models.Product;
+using Response = economy.Models.Product;
+using economy.Models.Product;
+
+namespace economy.Controllers
+{
+    public class PriceController : Controller
+    {
+        private readonly DataGoKR _dataGoKR;
+        private ProductModel productModel;
+        private Dictionary<string, string> _queryString;
+
+        public PriceController(DataGoKR dataGoKR)
+        {
+            _dataGoKR = dataGoKR;
+            productModel = new ProductModel(_dataGoKR);
+        }
+
+        public async Task<IActionResult> Index(Request.List.Request request)
+        {
+            return await List(request);
+        }
+
+        public override void OnActionExecuting(ActionExecutingContext context)
+        {
+            _queryString = QueryHelpers.ParseQuery(HttpContext.Request.QueryString.Value).ToDictionary(k => k.Key, v => string.Join(",", v.Value));
+
+            ViewBag.QueryString = _queryString;
+
+            base.OnActionExecuting(context);
+        }
+
+        // 품목별 시세
+        [HttpGet("Price/List")]
+        public async Task<IActionResult> List(Request.List.Request request)
+        {
+            if (!ModelState.IsValid)
+            {
+                return BadRequest(ModelState);
+            }
+
+            Response.List.Response itemList = await productModel.GetPriceItemList(request);
+
+            var viewModel = new View<Request.List.Request, Response.List.Response>();
+            viewModel.SelectedListPerPage = request.NumOfRows;
+            viewModel.Request = request;
+            viewModel.Response = itemList;
+
+            var pagination = new Pagination(itemList.Body?.TotalCount, request.PageNo, request.NumOfRows, null);
+            viewModel.Pagination = pagination;
+
+            return View(viewModel);
+        }
+
+        // 품목 가격 정보
+        [HttpGet("Price/Detail/{item}")]
+        public async Task<IActionResult> Detail([FromRoute] string item, [Bind] Request.Detail.Request request)
+        {
+            request.ItemCode = item;
+
+            if (!ModelState.IsValid)
+            {
+                return BadRequest(ModelState);
+            }
+        
+            Response.Detail.Response itemInfo = await productModel.GetPriceItemInfo(request);
+
+            if (itemInfo.Body is not null)
+            {
+                int listNum = Common.CalcListNumber(itemInfo.Body.TotalCount, request.PageNo, request.NumOfRows);
+                itemInfo.Body.Items.ItemList = itemInfo.Body.Items.ItemList.Select((row, index) =>
+                {
+                    row.Num = (listNum - index);
+                    return row;
+                }).ToList();
+            }
+
+            var viewModel = new View<Response.Detail.Request, Response.Detail.Response>();
+            viewModel.SelectedListPerPage = request.NumOfRows;
+            viewModel.Request = request;
+            viewModel.Response = itemInfo;
+
+            var queryString = new
+            {
+                sDate = request.StartDate,
+                eDate = request.EndDate
+            };
+
+            var pagination = new Pagination(itemInfo.Body?.TotalCount, request.PageNo, request.NumOfRows, queryString);
+            viewModel.Pagination = pagination;
+
+            return View(viewModel);
+        }
+    }
+}

+ 30 - 0
economy/Dockerfile

@@ -0,0 +1,30 @@
+# 디버그 컨테이너를 사용자 지정하는 방법과 Visual Studio 이 Dockerfile을 사용하여 더 빠른 디버깅을 위해 이미지를 빌드하는 방법을 알아보려면 https://aka.ms/customizecontainer를 참조하세요.
+
+# 이 스테이지는 VS에서 빠른 모드로 실행할 때 사용됩니다(디버그 구성의 기본값).
+FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
+USER app
+WORKDIR /app
+EXPOSE 8080
+EXPOSE 8081
+
+
+# 이 스테이지는 서비스 프로젝트를 빌드하는 데 사용됩니다.
+FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
+ARG BUILD_CONFIGURATION=Release
+WORKDIR /src
+COPY ["economy/economy.csproj", "economy/"]
+RUN dotnet restore "./economy/economy.csproj"
+COPY . .
+WORKDIR "/src/economy"
+RUN dotnet build "./economy.csproj" -c $BUILD_CONFIGURATION -o /app/build
+
+# 이 스테이지는 최종 스테이지에 복사할 서비스 프로젝트를 게시하는 데 사용됩니다.
+FROM build AS publish
+ARG BUILD_CONFIGURATION=Release
+RUN dotnet publish "./economy.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
+
+# 이 스테이지는 프로덕션에서 사용되거나 VS에서 일반 모드로 실행할 때 사용됩니다(디버그 구성을 사용하지 않는 경우 기본값).
+FROM base AS final
+WORKDIR /app
+COPY --from=publish /app/publish .
+ENTRYPOINT ["dotnet", "economy.dll"]

+ 49 - 0
economy/Helpers/Common.cs

@@ -0,0 +1,49 @@
+using System.Xml.Serialization;
+
+namespace economy.Helpers
+{
+    public class Common
+    {
+        // XML 데이터 직렬화
+        public static async Task<T> ParseXmlDataAsync<T>(string xmlData) where T : class
+        {
+            var serializer = new XmlSerializer(typeof(T));
+
+            using (var reader = new StringReader(xmlData))
+            {
+                return await Task.Run(() => (T)serializer.Deserialize(reader));
+            }
+        }
+
+        // 시작 번호 조회
+        public static int CalcListNumber(int total, int page, int perPage)
+        {
+            return (total - ((page - 1) * perPage));
+        }
+
+        // 천 단위 구분 형식으로 변환
+        public static string NumberFormat(string s, string format = "N0")
+        {
+            if (int.TryParse(s, out int clprValue))
+            {
+                s = clprValue.ToString(format);
+            }
+            if (double.TryParse(s, out double doubleValue))
+            {
+                return doubleValue.ToString(format);
+            }
+            if (decimal.TryParse(s, out decimal decimalValue))
+            {
+                return decimalValue.ToString(format);
+            }
+
+            return s;
+        }
+
+        // 문자열 형태의 날짜를 포맷 형식으로 변환
+        public static string StringToDateFormat(string s, string format = "yyyy-MM-dd")
+        {
+            return DateTime.ParseExact(s, "yyyyMMdd", null).ToString(format);
+        }
+    }
+}

+ 92 - 0
economy/Helpers/FlexibleTypeConverter.cs

@@ -0,0 +1,92 @@
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+public class FlexibleTypeConverter<T> : JsonConverter<T>
+{
+    public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+    {
+        // Type 'T'에 따라 각 JSON 토큰 타입에 맞춰서 변환 시도
+        switch (reader.TokenType)
+        {
+            case JsonTokenType.String:
+                var strValue = reader.GetString();
+
+                if (typeof(T) == typeof(int) && int.TryParse(strValue, out var intValue))
+                    return (T)(object)intValue;
+
+                if (typeof(T) == typeof(long) && long.TryParse(strValue, out var longValue))
+                    return (T)(object)longValue;
+
+                if (typeof(T) == typeof(double) && double.TryParse(strValue, out var doubleValue))
+                    return (T)(object)doubleValue;
+
+                if (typeof(T) == typeof(bool) && bool.TryParse(strValue, out var boolValue))
+                    return (T)(object)boolValue;
+
+                if (typeof(T) == typeof(string))
+                    return (T)(object)strValue;
+
+                break;
+
+            case JsonTokenType.Number:
+                if (typeof(T) == typeof(int))
+                    return (T)(object)reader.GetInt32();
+
+                if (typeof(T) == typeof(long))
+                    return (T)(object)reader.GetInt64();
+
+                if (typeof(T) == typeof(double))
+                    return (T)(object)reader.GetDouble();
+
+                break;
+
+            case JsonTokenType.True:
+            case JsonTokenType.False:
+                if (typeof(T) == typeof(bool))
+                    return (T)(object)reader.GetBoolean();
+                break;
+
+            case JsonTokenType.Null:
+                if (typeToConvert.IsClass) return default;
+                throw new JsonException($"Cannot convert null to type '{typeof(T)}'.");
+        }
+
+        throw new JsonException($"Unable to convert JSON token to type '{typeof(T)}'.");
+    }
+
+    public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
+    {
+        // Type 'T'에 따라 JSON 타입에 맞춰서 기록
+        if (value == null)
+        {
+            writer.WriteNullValue();
+            return;
+        }
+
+        switch (value)
+        {
+            case int intValue:
+                writer.WriteNumberValue(intValue);
+                break;
+
+            case long longValue:
+                writer.WriteNumberValue(longValue);
+                break;
+
+            case double doubleValue:
+                writer.WriteNumberValue(doubleValue);
+                break;
+
+            case bool boolValue:
+                writer.WriteBooleanValue(boolValue);
+                break;
+
+            case string stringValue:
+                writer.WriteStringValue(stringValue);
+                break;
+
+            default:
+                throw new JsonException($"Unsupported type '{typeof(T)}'.");
+        }
+    }
+}

+ 51 - 0
economy/Models/AnniversaryModel.cs

@@ -0,0 +1,51 @@
+using economy.Helpers;
+
+namespace economy.Models.Anniversary
+{
+    public class AnniversaryModel
+    {
+        private readonly DataGoKR _dataGoKR;
+
+        public AnniversaryModel(DataGoKR dataGoKR)
+        {
+            _dataGoKR = dataGoKR;
+        }
+
+        // 기념일 조회
+        public async Task<Response> GetAnniversary(Request request)
+        {
+            Response parseData = new();
+
+            try
+            {
+                var uriBuilder = new UriBuilder(_dataGoKR.APIUrl)
+                {
+                    Path = "/B090041/openapi/service/SpcdeInfoService/getAnniversaryInfo",
+                    Query = $"ServiceKey={_dataGoKR.APIKey}&numOfRows=100&solYear={request.Year}"
+                };
+
+                var response = await _dataGoKR.httpClient.GetAsync(uriBuilder.Uri);
+
+                if (response.IsSuccessStatusCode)
+                {
+                    var xmlString = await response.Content.ReadAsStringAsync();
+                    parseData = await Common.ParseXmlDataAsync<Response>(xmlString);
+
+
+                    if (parseData.Body is null)
+                    {
+                        return new Response();
+                    }
+                }
+
+                response.EnsureSuccessStatusCode();
+            }
+            catch (HttpRequestException e)
+            {
+                Console.WriteLine($"Request error: {e.Message}");
+            }
+
+            return parseData;
+        }
+    }
+}

+ 74 - 0
economy/Models/Clients.cs

@@ -0,0 +1,74 @@
+using Microsoft.Net.Http.Headers;
+
+namespace economy.Models
+{
+    // 공공데이터 API
+    public class DataGoKR
+    {
+        public readonly HttpClient httpClient;
+        public readonly string APIUrl = "http://apis.data.go.kr";
+        public readonly string APIKey = "vQz8tIxrdhjerG6DE1w1hcVEli5S27LtIsCvx0axiieZmRgOpB4vToQ77VmvknAkIC9YjxlPx2gDZcl06S88Xw%3D%3D";
+
+        public DataGoKR(HttpClient e)
+        {
+            httpClient = e;
+            httpClient.BaseAddress = new Uri(APIUrl);
+        }
+    }
+
+    // 화훼공판장 API
+    public class FlowerAtOrKR
+    {
+        public readonly HttpClient httpClient;
+        public readonly string APIUrl = "https://flower.at.or.kr";
+        public readonly string APIKey = "5F70416B443241BE846D0807744FC489";
+
+        public FlowerAtOrKR(HttpClient e)
+        {
+            httpClient = e;
+            httpClient.BaseAddress = new Uri(APIUrl);
+        }
+    }
+
+    // 한국수출입은행 API
+    public class KoreaEximGoKR
+    {
+        public readonly HttpClient httpClient;
+        public readonly string APIUrl = "https://www.koreaexim.go.kr";
+        public readonly string ExchangeAPIKey = "OiZlh6SjbXGBlfbagekBpi6kAE4UyonP"; // 환율
+        public readonly string InterestAPIKey = "4olJNSqYHe4oVsW50JFXpXyYmDh9O9NN"; // 대출금리
+        public readonly string InternationalAPIKey = "UI3DgPS3oZucxxCThkJQV6eAGprSbLJy"; // 국제금리
+
+        public KoreaEximGoKR(HttpClient e)
+        {
+            httpClient = e;
+            httpClient.BaseAddress = new Uri(APIUrl);
+        }
+    }
+
+    // 동행복권
+    public class DhlotteryCoKR
+    {
+        public readonly HttpClient httpClient;
+        public readonly string APIUrl = "https://www.dhlottery.co.kr";
+
+        public DhlotteryCoKR(HttpClient e)
+        {
+            httpClient = e;
+            httpClient.BaseAddress = new Uri(APIUrl);
+        }
+    }
+
+    // FIFA 순위
+    public class FIFA_API
+    {
+        public readonly HttpClient httpClient;
+        public readonly string APIUrl = "https://api.fifa.com";
+
+        public FIFA_API(HttpClient e)
+        {
+            httpClient = e;
+            httpClient.BaseAddress = new Uri(APIUrl);
+        }
+    }
+}

+ 60 - 0
economy/Models/EmissionModel.cs

@@ -0,0 +1,60 @@
+using System.Text.Json;
+
+namespace economy.Models.Emission
+{
+    public class EmissionModel
+    {
+        private readonly DataGoKR _dataGoKR;
+
+        public EmissionModel(DataGoKR dataGoKR)
+        {
+            _dataGoKR = dataGoKR;
+        }
+
+        // 배출권 시세 조회
+        public async Task<Response> GetEmissionPriceInfo(Request request)
+        {
+            Response parseData = new();
+
+            try
+            {
+                var uriBuilder = new UriBuilder(_dataGoKR.APIUrl)
+                {
+                    Path = "/1160100/service/GetGeneralProductInfoService/getCertifiedEmissionReductionPriceInfo",
+                    Query = $"serviceKey={_dataGoKR.APIKey}&pageNo={request.PageNo}&numOfRows={request.NumOfRows}&beginBasDt={request.StartDate:yyyyMMdd}&endBasDt={request.EndDate:yyyyMMdd}&resultType=json"
+                };
+
+                if (request.LikeSrtnCd is not null)
+                {
+                    uriBuilder.Query += $"&likeSrtnCd={request.LikeSrtnCd}";
+                }
+
+                var response = await _dataGoKR.httpClient.GetAsync(uriBuilder.Uri);
+
+                if (response.IsSuccessStatusCode)
+                {
+                    var jsonString = await response.Content.ReadAsStringAsync();
+
+                    using (var document = JsonDocument.Parse(jsonString))
+                    {
+                        var resEle = document.RootElement.GetProperty("response");
+                        parseData = JsonSerializer.Deserialize<Response>(resEle.GetRawText());
+                    }
+
+                    if (parseData.Body is null)
+                    {
+                        return new Response();
+                    }
+                }
+
+                response.EnsureSuccessStatusCode();
+            }
+            catch (HttpRequestException e)
+            {
+                Console.WriteLine($"Request error: {e.Message}");
+            }
+
+            return parseData;
+        }
+    }
+}

+ 9 - 0
economy/Models/ErrorViewModel.cs

@@ -0,0 +1,9 @@
+namespace economy.Models
+{
+    public class ErrorViewModel
+    {
+        public string? RequestId { get; set; }
+
+        public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
+    }
+}

+ 54 - 0
economy/Models/ExchangeModel.cs

@@ -0,0 +1,54 @@
+using System.Text.Json;
+
+namespace economy.Models.Exchange
+{
+    public class ExchangeModel
+    {
+        private readonly KoreaEximGoKR _koreaEximGoKR;
+
+        public ExchangeModel(KoreaEximGoKR koreaEximGoKR)
+        {
+            _koreaEximGoKR = koreaEximGoKR;
+        }
+
+        // 환율 조회
+        public async Task<Response> GetExchange(Request request)
+        {
+            Response parseData = new();
+
+            try
+            {
+                var uriBuilder = new UriBuilder(_koreaEximGoKR.APIUrl)
+                {
+                    Path = "/site/program/financial/exchangeJSON",
+                    Query = $"authkey={_koreaEximGoKR.ExchangeAPIKey}&searchdate={request.Date:yyyyMMdd}&data=AP01"
+                };
+
+                var response = await _koreaEximGoKR.httpClient.GetAsync(uriBuilder.Uri);
+
+                if (response.IsSuccessStatusCode)
+                {
+                    var jsonString = await response.Content.ReadAsStringAsync();
+
+                    using (var document = JsonDocument.Parse(jsonString))
+                    {
+                        parseData.Items = JsonSerializer.Deserialize<List<Data>>(jsonString);
+                    }
+
+                    if (parseData is null)
+                    {
+                        return new Response();
+                    }
+                }
+
+                response.EnsureSuccessStatusCode();
+            }
+            catch (HttpRequestException e)
+            {
+                Console.WriteLine($"Request error: {e.Message}");
+            }
+
+            return parseData;
+        }
+    }
+}

+ 50 - 0
economy/Models/FIFAModel.cs

@@ -0,0 +1,50 @@
+using System.Text.Json;
+
+namespace economy.Models.FIFA
+{
+    public class FIFAModel
+    {
+        private readonly FIFA_API _fifaAPI;
+
+        public FIFAModel(FIFA_API fifaAPI)
+        {
+            _fifaAPI = fifaAPI;
+        }
+
+        // FIFA 순위
+        public async Task<Response> GetRanking(Request request)
+        {
+            Response parseData = new();
+
+            try
+            {
+                var uriBuilder = new UriBuilder(_fifaAPI.APIUrl)
+                {
+                    Path = "/api/v3/rankings",
+                    Query = $"gender={request.Gender}&count={request.NumOfRows}&language=ko"
+                };
+
+                var response = await _fifaAPI.httpClient.GetAsync(uriBuilder.Uri);
+
+                if (response.IsSuccessStatusCode)
+                {
+                    var jsonString = await response.Content.ReadAsStringAsync();
+                    parseData = JsonSerializer.Deserialize<Response>(jsonString);
+
+                    if (parseData is null)
+                    {
+                        return new Response();
+                    }
+                }
+
+                response.EnsureSuccessStatusCode();
+            }
+            catch (HttpRequestException e)
+            {
+                Console.WriteLine($"Request error: {e.Message}");
+            }
+
+            return parseData;
+        }
+    }
+}

+ 73 - 0
economy/Models/FlowerModel.cs

@@ -0,0 +1,73 @@
+using System.Text.Json;
+
+namespace economy.Models.Flower
+{
+    public class FlowerModel
+    {
+        private readonly FlowerAtOrKR _flowerAtOrKR;
+
+        public FlowerModel(FlowerAtOrKR flowerAtOrKR)
+        {
+            _flowerAtOrKR = flowerAtOrKR;
+        }
+
+        // 석유 시세 조회
+        public async Task<Response> GetFlowerPriceInfo(Request request)
+        {
+            Response parseData = new();
+
+            try
+            {
+                var uriBuilder = new UriBuilder(_flowerAtOrKR.APIUrl)
+                {
+                    Path = "/api/returnData.api",
+                    Query = $"kind=f001&serviceKey={_flowerAtOrKR.APIKey}&currentPage={request.PageNo}&countPerPage={request.NumOfRows}&baseDate={request.BaseDate:yyyy-MM-dd}&flowerGubn={(int)request.Type}&dataType=json"
+                };
+
+                if (request.PumName is not null)
+                {
+                    uriBuilder.Query += $"&pumName={request.PumName}";
+                }
+
+                if (request.GoodName is not null)
+                {
+                    uriBuilder.Query += $"&goodName={request.GoodName}";
+                }
+
+                var response = await _flowerAtOrKR.httpClient.GetAsync(uriBuilder.Uri);
+
+                if (response.IsSuccessStatusCode)
+                {
+                    var jsonString = await response.Content.ReadAsStringAsync();
+
+                    using (var document = JsonDocument.Parse(jsonString))
+                    {
+                        if (document.RootElement.TryGetProperty("response", out var resEle))
+                        {
+                            Console.WriteLine("Found 'response' property.");
+                            Console.WriteLine(resEle.GetRawText()); // response 내부 텍스트 출력
+                            parseData = JsonSerializer.Deserialize<Response>(resEle.GetRawText());
+                        }
+                        else
+                        {
+                            Console.WriteLine("'response' property not found in JSON.");
+                        }
+                    }
+
+                    if (parseData.Items is null)
+                    {
+                        return new Response();
+                    }
+                }
+
+                response.EnsureSuccessStatusCode();
+            }
+            catch (HttpRequestException e)
+            {
+                Console.WriteLine($"Request error: {e.Message}");
+            }
+
+            return parseData;
+        }
+    }
+}

+ 61 - 0
economy/Models/GoldModel.cs

@@ -0,0 +1,61 @@
+using System.Text.Json;
+
+namespace economy.Models.Gold
+{
+    public class GoldModel
+    {
+        private readonly DataGoKR _dataGoKR;
+
+        public GoldModel(DataGoKR dataGoKR)
+        {
+            _dataGoKR = dataGoKR;
+        }
+
+        // 금 시세 조회
+        public async Task<Response> GetGoldPriceInfo(Request request)
+        {
+            Response parseData = new();
+
+            try
+            {
+                var uriBuilder = new UriBuilder(_dataGoKR.APIUrl)
+                {
+                    Path = "/1160100/service/GetGeneralProductInfoService/getGoldPriceInfo",
+                    Query = $"serviceKey={_dataGoKR.APIKey}&pageNo={request.PageNo}&numOfRows={request.NumOfRows}&beginBasDt={request.StartDate:yyyyMMdd}&endBasDt={request.EndDate:yyyyMMdd}&resultType=json"
+                };
+
+                if (request.LikeSrtnCd is not null)
+                {
+                    uriBuilder.Query += $"&likeSrtnCd={request.LikeSrtnCd}";
+                }
+
+                var response = await _dataGoKR.httpClient.GetAsync(uriBuilder.Uri);
+
+                if (response.IsSuccessStatusCode)
+                {
+                    var jsonString = await response.Content.ReadAsStringAsync();
+
+                    using (var document = JsonDocument.Parse(jsonString))
+                    {
+                        var resEle = document.RootElement.GetProperty("response");
+
+                        parseData = JsonSerializer.Deserialize<Response>(resEle.GetRawText());
+                    }
+
+                    if (parseData.Body is null)
+                    {
+                        return new Response();
+                    }
+                }
+
+                response.EnsureSuccessStatusCode();
+            }
+            catch (HttpRequestException e)
+            {
+                Console.WriteLine($"Request error: {e.Message}");
+            }
+
+            return parseData;
+        }
+    }
+}

+ 51 - 0
economy/Models/HolidayModel.cs

@@ -0,0 +1,51 @@
+using economy.Helpers;
+
+namespace economy.Models.Holiday
+{
+    public class HolidayModel
+    {
+        private readonly DataGoKR _dataGoKR;
+
+        public HolidayModel(DataGoKR dataGoKR)
+        {
+            _dataGoKR = dataGoKR;
+        }
+
+        // 국경일 조회
+        public async Task<Response> GetHoliday(Request request)
+        {
+            Response parseData = new();
+
+            try
+            {
+                var uriBuilder = new UriBuilder(_dataGoKR.APIUrl)
+                {
+                    Path = "/B090041/openapi/service/SpcdeInfoService/getHoliDeInfo",
+                    Query = $"ServiceKey={_dataGoKR.APIKey}&numOfRows=100&solYear={request.Year}"
+                };
+
+                var response = await _dataGoKR.httpClient.GetAsync(uriBuilder.Uri);
+
+                if (response.IsSuccessStatusCode)
+                {
+                    var xmlString = await response.Content.ReadAsStringAsync();
+                    parseData = await Common.ParseXmlDataAsync<Response>(xmlString);
+
+
+                    if (parseData.Body is null)
+                    {
+                        return new Response();
+                    }
+                }
+
+                response.EnsureSuccessStatusCode();
+            }
+            catch (HttpRequestException e)
+            {
+                Console.WriteLine($"Request error: {e.Message}");
+            }
+
+            return parseData;
+        }
+    }
+}

+ 54 - 0
economy/Models/InterestModel.cs

@@ -0,0 +1,54 @@
+using System.Text.Json;
+
+namespace economy.Models.Interest
+{
+    public class InterestModel
+    {
+        private readonly KoreaEximGoKR _koreaEximGoKR;
+
+        public InterestModel(KoreaEximGoKR koreaEximGoKR)
+        {
+            _koreaEximGoKR = koreaEximGoKR;
+        }
+
+        // 대출 금리 조회
+        public async Task<Response> GetInterestRate(Request request)
+        {
+            Response parseData = new();
+
+            try
+            {
+                var uriBuilder = new UriBuilder(_koreaEximGoKR.APIUrl)
+                {
+                    Path = "/site/program/financial/interestJSON",
+                    Query = $"authkey={_koreaEximGoKR.InterestAPIKey}&searchdate={request.Date:yyyyMMdd}&data=AP02"
+                };
+
+                var response = await _koreaEximGoKR.httpClient.GetAsync(uriBuilder.Uri);
+
+                if (response.IsSuccessStatusCode)
+                {
+                    var jsonString = await response.Content.ReadAsStringAsync();
+
+                    using (var document = JsonDocument.Parse(jsonString))
+                    {
+                        parseData.Items = JsonSerializer.Deserialize<List<Data>>(jsonString);
+                    }
+
+                    if (parseData.Items is null)
+                    {
+                        return new Response();
+                    }
+                }
+
+                response.EnsureSuccessStatusCode();
+            }
+            catch (HttpRequestException e)
+            {
+                Console.WriteLine($"Request error: {e.Message}");
+            }
+
+            return parseData;
+        }
+    }
+}

+ 54 - 0
economy/Models/InternationalModel.cs

@@ -0,0 +1,54 @@
+using System.Text.Json;
+
+namespace economy.Models.International
+{
+    public class InternationalModel
+    {
+        private readonly KoreaEximGoKR _koreaEximGoKR;
+
+        public InternationalModel(KoreaEximGoKR koreaEximGoKR)
+        {
+            _koreaEximGoKR = koreaEximGoKR;
+        }
+
+        // 국제 금리 조회
+        public async Task<Response> GetInternationalRate(Request request)
+        {
+            Response parseData = new();
+
+            try
+            {
+                var uriBuilder = new UriBuilder(_koreaEximGoKR.APIUrl)
+                {
+                    Path = "/site/program/financial/internationalJSON",
+                    Query = $"authkey={_koreaEximGoKR.InternationalAPIKey}&searchdate={request.Date:yyyyMMdd}&data=AP03"
+                };
+
+                var response = await _koreaEximGoKR.httpClient.GetAsync(uriBuilder.Uri);
+
+                if (response.IsSuccessStatusCode)
+                {
+                    var jsonString = await response.Content.ReadAsStringAsync();
+
+                    using (var document = JsonDocument.Parse(jsonString))
+                    {
+                        parseData = JsonSerializer.Deserialize<Response>(jsonString);
+                    }
+
+                    if (parseData is null)
+                    {
+                        return new Response();
+                    }
+                }
+
+                response.EnsureSuccessStatusCode();
+            }
+            catch (HttpRequestException e)
+            {
+                Console.WriteLine($"Request error: {e.Message}");
+            }
+
+            return parseData;
+        }
+    }
+}

+ 52 - 0
economy/Models/LottoModel.cs

@@ -0,0 +1,52 @@
+using System.Text;
+using System.Text.Json;
+
+namespace economy.Models.Lotto
+{
+    public class LottoModel
+    {
+        private readonly DhlotteryCoKR _dhlotteryCoKR;
+
+        public LottoModel(DhlotteryCoKR dhlotteryCoKR)
+        {
+            _dhlotteryCoKR = dhlotteryCoKR;
+        }
+
+        // 로또 당첨 번호 조회
+        public async Task<Response> GetLottoNumber(Request request)
+        {
+            Response parseData = new();
+
+            try
+            {
+                var uriBuilder = new UriBuilder(_dhlotteryCoKR.APIUrl)
+                {
+                    Path = "/common.do",
+                    Query = $"method=getLottoNumber&drwNo={request.Number}"
+                };
+
+                var response = await _dhlotteryCoKR.httpClient.GetAsync(uriBuilder.Uri);
+
+                if (response.IsSuccessStatusCode)
+                {
+                    var bytes = await response.Content.ReadAsByteArrayAsync();
+                    var jsonString = Encoding.GetEncoding("euc-kr").GetString(bytes);
+                    parseData = JsonSerializer.Deserialize<Response>(jsonString);
+                    
+                    if (parseData is null)
+                    {
+                        return new Response();
+                    }
+                }
+
+                response.EnsureSuccessStatusCode();
+            }
+            catch (HttpRequestException e)
+            {
+                Console.WriteLine($"Request error: {e.Message}");
+            }
+
+            return parseData;
+        }
+    }
+}

+ 56 - 0
economy/Models/OilModel.cs

@@ -0,0 +1,56 @@
+using System.Text.Json;
+
+namespace economy.Models.Oil
+{
+    public class OilModel
+    {
+        private readonly DataGoKR _dataGoKR;
+
+        public OilModel(DataGoKR dataGoKR)
+        {
+            _dataGoKR = dataGoKR;
+        }
+
+        // 석유 시세 조회
+        public async Task<Response> GetOilPriceInfo(Request request)
+        {
+            Response parseData = new();
+
+            try
+            {
+                var uriBuilder = new UriBuilder(_dataGoKR.APIUrl)
+                {
+                    Path = "/1160100/service/GetGeneralProductInfoService/getOilPriceInfo",
+                    Query = $"serviceKey={_dataGoKR.APIKey}&pageNo={request.PageNo}&numOfRows={request.NumOfRows}&beginBasDt={request.StartDate:yyyyMMdd}&endBasDt={request.EndDate:yyyyMMdd}&resultType=json"
+                };
+
+                var response = await _dataGoKR.httpClient.GetAsync(uriBuilder.Uri);
+
+                if (response.IsSuccessStatusCode)
+                {
+                    var jsonString = await response.Content.ReadAsStringAsync();
+
+                    using (var document = JsonDocument.Parse(jsonString))
+                    {
+                        var resEle = document.RootElement.GetProperty("response");
+
+                        parseData = JsonSerializer.Deserialize<Response>(resEle.GetRawText());
+                    }
+
+                    if (parseData.Body is null)
+                    {
+                        return new Response();
+                    }
+                }
+
+                response.EnsureSuccessStatusCode();
+            }
+            catch (HttpRequestException e)
+            {
+                Console.WriteLine($"Request error: {e.Message}");
+            }
+
+            return parseData;
+        }
+    }
+}

+ 88 - 0
economy/Models/Pagination.cs

@@ -0,0 +1,88 @@
+using Microsoft.AspNetCore.WebUtilities;
+using System.Reflection;
+
+namespace economy.Models
+{
+    public class Pagination
+    {
+        public int Page { get; set; } = 0; // 현재 페이지
+        public int TotalRows { get; set; } = 0; // 전체 항목 수
+        public int TotalPage { get; set; } = 0; // 전체 페이지 수
+        public int PerPage = 10;
+
+        public int StartPage = 0; // 현재 페이지의 시작 번호
+        public int EndPage = 0; // 현재 페이지의 마지막 번호
+        public int PageGroupSize = 10; // 한번에 보여줄 페이지 번호 개수
+        public int CurrentPageGroup = 0; // 현재 페이지가 속한 페이지 그룹의 시작번호
+
+        public int GroupStartPage = 0; // 페이지 그룹의 시작 페이지 번호
+        public int GroupEndPage = 0; // 페이지 그룹의 마지막 페이지 번호
+
+        public int PrevGroupPage = 0; // 이전 페이지 그룹의 페이지 번호
+        public int NextGroupPage = 0; // 다음 페이지 그룹 페이지 번호
+
+        private Dictionary<string, string> QueryStringParams;
+
+        public Pagination(int? totalRows, int page, int perPage, object? queryParams) 
+        {
+            Page = page;
+            PerPage = perPage;
+            TotalRows = (totalRows ?? 0);
+            TotalPage = ((int)Math.Ceiling((double)TotalRows / perPage));
+
+            CurrentPageGroup = (int)Math.Ceiling((double)Page / PageGroupSize);
+            StartPage = ((CurrentPageGroup - 1) * PageGroupSize + 1);
+            EndPage = Math.Min((StartPage + PageGroupSize - 1), TotalPage);
+
+            GroupStartPage = (GroupStartPage - PageGroupSize);
+            GroupEndPage = (((Page - 1) / PageGroupSize + 1) * PageGroupSize);
+
+            PrevGroupPage = Math.Max(StartPage - PageGroupSize, 1); // 이전 페이지 그룹의 첫 페이지 계산
+            NextGroupPage = Math.Min(EndPage + 1, TotalPage); // 다음 페이지 그룹의 첫 페이지 계산
+
+            // 객체를 쿼리스트링으로 변환
+            QueryStringParams = ToDictionary(queryParams);
+        }
+
+        // 이전 페이지 여부
+        public bool HasPreviousPage => (Page > 1);
+
+        // 다음 페이지 여부
+        public bool HasNextPage => (Page < TotalPage);
+
+        // 객체를 Dictionary로 변환하는 메서드
+        private Dictionary<string, string> ToDictionary(object? obj)
+        {
+            var dictionary = new Dictionary<string, string>();
+
+            if (obj is null) {
+                return dictionary;
+            }
+
+            var properties = obj.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
+
+            foreach (var prop in properties)
+            {
+                var value = prop.GetValue(obj)?.ToString();
+                if (!string.IsNullOrEmpty(value))
+                {
+                    dictionary.Add(prop.Name, value);
+                }
+            }
+
+            return dictionary;
+        }
+
+        // 최종 쿼리스트링을 반환하는 메서드
+        public string BuildQueryString()
+        {
+            if (QueryStringParams == null || QueryStringParams.Count == 0)
+            {
+                return "";
+            }
+
+            // 쿼리스트링이 있을 경우, AddQueryString으로 변환 후 ? 제거
+            return "&" + QueryHelpers.AddQueryString("", QueryStringParams).TrimStart('?');
+        }
+    }
+}

+ 82 - 0
economy/Models/ProductModel.cs

@@ -0,0 +1,82 @@
+using economy.Helpers;
+
+namespace economy.Models.Product
+{
+    public class ProductModel
+    {
+        private readonly DataGoKR _dataGoKR;
+
+        public ProductModel(DataGoKR dataGoKR)
+        {
+            _dataGoKR = dataGoKR;
+        }
+
+        // 품목별 가격 목록 조회
+        public async Task<List.Response> GetPriceItemList(List.Request request)
+        {
+            List.Response parsedData = new();
+
+            try
+            {
+                var response = await _dataGoKR.httpClient.GetAsync($"/1240000/bpp_openapi/getPriceItemList?ServiceKey={_dataGoKR.APIKey}&pageNo={request.PageNo}&numOfRows={request.NumOfRows}");
+
+                if (response.IsSuccessStatusCode)
+                {
+                    var xmlString = await response.Content.ReadAsStringAsync();
+                    parsedData = await Common.ParseXmlDataAsync<List.Response>(xmlString);
+
+                    if (parsedData.Body is null)
+                    {
+                        return parsedData;
+                    }
+                }
+
+                response.EnsureSuccessStatusCode(); // 예외 발생시킴
+
+            }
+            catch (HttpRequestException e)
+            {
+                Console.WriteLine($"Request error: {e.Message}");
+            }
+
+            return parsedData;
+        }
+
+        // 품목 상세 정보 조회
+        public async Task<Detail.Response> GetPriceItemInfo(Detail.Request request)
+        {
+            Detail.Response parseData = new();
+
+            try
+            {
+                var uriBuilder = new UriBuilder(_dataGoKR.APIUrl)
+                {
+                    Path = "/1240000/bpp_openapi/getPriceInfo",
+                    Query = $"ServiceKey={_dataGoKR.APIKey}&pageNo={request.PageNo}&numOfRows={request.NumOfRows}&startDate={request.StartDate}&endDate={request.EndDate}&itemCode={request.ItemCode}"
+                };
+
+                var response = await _dataGoKR.httpClient.GetAsync(uriBuilder.Uri);
+
+                if (response.IsSuccessStatusCode)
+                {
+                    var xmlString = await response.Content.ReadAsStringAsync();
+                    parseData = await Common.ParseXmlDataAsync<Detail.Response>(xmlString);
+
+
+                    if (parseData.Body is null)
+                    {
+                        return new Detail.Response();
+                    }
+                }
+
+                response.EnsureSuccessStatusCode();
+            }
+            catch (HttpRequestException e)
+            {
+                Console.WriteLine($"Request error: {e.Message}");
+            }
+
+            return parseData;
+        }
+    }
+}

+ 19 - 0
economy/Models/Request/Days/Anniversary.cs

@@ -0,0 +1,19 @@
+using Microsoft.AspNetCore.Mvc;
+using System.ComponentModel.DataAnnotations;
+
+namespace economy.Models.Anniversary
+{
+    /*
+     * 기념일 정보
+     * 예시 ) http://apis.data.go.kr/B090041/openapi/service/SpcdeInfoService/getAnniversaryInfo
+     */
+
+    // 검색요청 변수
+    public class Request
+    {
+        [BindProperty(Name = "year", SupportsGet = true)]
+        [Required(ErrorMessage = "검색년도를 선택해주세요.")]
+        [Range(1900, 2100, ErrorMessage = "유효한 연도를 입력하세요.")]
+        public int Year { get; set; } = DateTime.Now.Year;
+    }
+}

+ 19 - 0
economy/Models/Request/Days/Holiday.cs

@@ -0,0 +1,19 @@
+using Microsoft.AspNetCore.Mvc;
+using System.ComponentModel.DataAnnotations;
+
+namespace economy.Models.Holiday
+{
+    /*
+     * 국경일 정보
+     * 예시 ) http://apis.data.go.kr/B090041/openapi/service/SpcdeInfoService/getHoliDeInfo
+     */
+
+    // 검색요청 변수
+    public class Request
+    {
+        [BindProperty(Name = "year", SupportsGet = true)]
+        [Required(ErrorMessage = "검색년도를 선택해주세요.")]
+        [Range(1900, 2100, ErrorMessage = "유효한 연도를 입력하세요.")]
+        public int Year { get; set; } = DateTime.Now.Year;
+    }
+}

+ 19 - 0
economy/Models/Request/Days/Seasonal.cs

@@ -0,0 +1,19 @@
+using Microsoft.AspNetCore.Mvc;
+using System.ComponentModel.DataAnnotations;
+
+namespace economy.Models.Seasonal
+{
+    /*
+     * 24절기 정보
+     * 예시 ) http://apis.data.go.kr/B090041/openapi/service/SpcdeInfoService/get24DivisionsInfo
+     */
+
+    // 검색요청 변수
+    public class Request
+    {
+        [BindProperty(Name = "year", SupportsGet = true)]
+        [Required(ErrorMessage = "검색년도를 선택해주세요.")]
+        [Range(1900, 2100, ErrorMessage = "유효한 연도를 입력하세요.")]
+        public int Year { get; set; } = DateTime.Now.Year;
+    }
+}

+ 19 - 0
economy/Models/Request/Days/Sundry.cs

@@ -0,0 +1,19 @@
+using Microsoft.AspNetCore.Mvc;
+using System.ComponentModel.DataAnnotations;
+
+namespace economy.Models.Sundry
+{
+    /*
+     * 잡절 정보
+     * 예시 ) http://apis.data.go.kr/B090041/openapi/service/SpcdeInfoService/getSundryDayInfo
+     */
+
+    // 검색요청 변수
+    public class Request
+    {
+        [BindProperty(Name = "year", SupportsGet = true)]
+        [Required(ErrorMessage = "검색년도를 선택해주세요.")]
+        [Range(1900, 2100, ErrorMessage = "유효한 연도를 입력하세요.")]
+        public int Year { get; set; } = DateTime.Now.Year;
+    }
+}

+ 40 - 0
economy/Models/Request/FIFA.cs

@@ -0,0 +1,40 @@
+using Microsoft.AspNetCore.Mvc;
+using System.ComponentModel.DataAnnotations;
+
+namespace economy.Models.FIFA
+{
+    /*
+     * FIFA 순위
+     * 예시 ) https://api.fifa.com/api/v3/rankings?gender=1&count=5&language=ko
+     */
+
+    // 검색요청 변수
+    public class Request
+    {
+        public enum Types
+        {
+            ALL,
+            AFC,
+            CAF,
+            CONCACAF,
+            CONMEBOL,
+            OFC,
+            UEFA
+        }
+
+        [BindProperty(Name = "page", SupportsGet = true)]
+        [Range(1, int.MaxValue, ErrorMessage = "페이지 허용량을 초과하였습니다.")]
+        public int PageNo { get; set; } = 1;
+
+        [BindProperty(Name = "perPage", SupportsGet = true)]
+        [Range(1, int.MaxValue, ErrorMessage = "출력 허용량을 초과하였습니다.")]
+        public int NumOfRows { get; set; } = 10;
+
+        [BindProperty(Name = "gender", SupportsGet = true)]
+        [Range(1, 2, ErrorMessage = "순위 구분이 잘못되었습니다.")]
+        public int Gender { get; set; } = 1;
+
+        [FromQuery(Name = "type")]
+        public Types? Type { get; set; } = null;
+    }
+}

+ 29 - 0
economy/Models/Request/Financial/Exchange.cs

@@ -0,0 +1,29 @@
+using Microsoft.AspNetCore.Mvc;
+using System.ComponentModel.DataAnnotations;
+
+namespace economy.Models.Exchange
+{
+    /*
+     * 한국수출입은행이 제공하는 환율정보
+     * 예시 ) https://www.koreaexim.go.kr/site/program/financial/exchangeJSON?authkey=OiZlh6SjbXGBlfbagekBpi6kAE4UyonP&searchdate=20241025&data=AP01
+     */
+
+    // 검색요청 변수
+    public class Request
+    {
+        private DateOnly _date = DateOnly.FromDateTime(DateTime.Now);
+
+        [BindProperty(Name = "date", SupportsGet = true)]
+        [Required(ErrorMessage = "검색 날짜를 입력해주세요.")]
+        [DataType(DataType.Date)]
+        [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:yyyy-MM-dd}")]
+        public DateOnly Date
+        {
+            get => _date;
+            set => _date = value;
+        }
+
+        // 날짜 형식을 적용하여 문자열로 반환하는 추가 속성
+        public string DateFormatted => _date.ToString("yyyy-MM-dd");
+    }
+}

+ 29 - 0
economy/Models/Request/Financial/Interest.cs

@@ -0,0 +1,29 @@
+using Microsoft.AspNetCore.Mvc;
+using System.ComponentModel.DataAnnotations;
+
+namespace economy.Models.Interest
+{
+    /*
+     * 한국수출입은행이 제공하는 대출금리
+     * 예시 ) https://www.koreaexim.go.kr/site/program/financial/interestJSON?authkey=4olJNSqYHe4oVsW50JFXpXyYmDh9O9NN&searchdate=20241025&data=AP02
+     */
+
+    // 검색요청 변수
+    public class Request
+    {
+        private DateOnly _date = DateOnly.FromDateTime(DateTime.Now);
+
+        [BindProperty(Name = "date", SupportsGet = true)]
+        [Required(ErrorMessage = "검색 날짜를 입력해주세요.")]
+        [DataType(DataType.Date)]
+        [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:yyyy-MM-dd}")]
+        public DateOnly Date
+        {
+            get => _date;
+            set => _date = value;
+        }
+
+        // 날짜 형식을 적용하여 문자열로 반환하는 추가 속성
+        public string DateFormatted => _date.ToString("yyyy-MM-dd");
+    }
+}

+ 29 - 0
economy/Models/Request/Financial/International.cs

@@ -0,0 +1,29 @@
+using Microsoft.AspNetCore.Mvc;
+using System.ComponentModel.DataAnnotations;
+
+namespace economy.Models.International
+{
+    /*
+     * 한국수출입은행이 제공하는 국제금리
+     * 예시 ) https://www.koreaexim.go.kr/site/program/financial/internationalJSON?authkey=UI3DgPS3oZucxxCThkJQV6eAGprSbLJy&searchdate=20241025&data=AP03
+     */
+
+    // 검색요청 변수
+    public class Request
+    {
+        private DateOnly _date = DateOnly.FromDateTime(DateTime.Now);
+
+        [BindProperty(Name = "date", SupportsGet = true)]
+        [Required(ErrorMessage = "검색 날짜를 입력해주세요.")]
+        [DataType(DataType.Date)]
+        [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:yyyy-MM-dd}")]
+        public DateOnly Date
+        {
+            get => _date;
+            set => _date = value;
+        }
+
+        // 날짜 형식을 적용하여 문자열로 반환하는 추가 속성
+        public string DateFormatted => _date.ToString("yyyy-MM-dd");
+    }
+}

+ 18 - 0
economy/Models/Request/Lotto.cs

@@ -0,0 +1,18 @@
+using Microsoft.AspNetCore.Mvc;
+using System.ComponentModel.DataAnnotations;
+
+namespace economy.Models.Lotto
+{
+    /*
+     * 로또 정보
+     * 예시 ) https://www.dhlottery.co.kr/common.do?method=getLottoNumber&drwNo={회차}
+     */
+
+    // 검색요청 변수
+    public class Request
+    {
+        [BindProperty(Name = "number", SupportsGet = true)]
+        [Required(ErrorMessage = "회차를 선택해주세요.")]
+        public ushort Number { get; set; }
+    }
+}

+ 38 - 0
economy/Models/Request/Market/Emission.cs

@@ -0,0 +1,38 @@
+using Microsoft.AspNetCore.Mvc;
+using System.ComponentModel.DataAnnotations;
+
+namespace economy.Models.Emission
+{
+    /*
+     * 탄소배출권시장에 상장된 배출권의 시세 정보를 제공
+     * 예시 ) https://apis.data.go.kr/1160100/service/GetGeneralProductInfoService/getCertifiedEmissionReductionPriceInfo?serviceKey=vQz8tIxrdhjerG6DE1w1hcVEli5S27LtIsCvx0axiieZmRgOpB4vToQ77VmvknAkIC9YjxlPx2gDZcl06S88Xw%3D%3D&resultType=json&beginBasDt=20241010&endBasDt=20241012&pageNo=1&numOfRows=10
+     */
+
+    // 검색요청 변수
+    public class Request
+    {
+        [BindProperty(Name = "page", SupportsGet = true)]
+        [Range(1, int.MaxValue, ErrorMessage = "페이지 허용량을 초과하였습니다.")]
+        public int PageNo { get; set; } = 1;
+
+        [BindProperty(Name = "perPage", SupportsGet = true)]
+        [Range(1, int.MaxValue, ErrorMessage = "출력 허용량을 초과하였습니다.")]
+        public int NumOfRows { get; set; } = 10;
+
+        [BindProperty(Name = "code", SupportsGet = true)]
+        [StringLength(10, ErrorMessage = "품목코드 형식이 잘못되었습니다.")]
+        public string? LikeSrtnCd { get; set; }
+
+        [BindProperty(Name = "sDate", SupportsGet = true)]
+        [Required(ErrorMessage = "검색 시작날짜를 입력해주세요.")]
+        [DataType(DataType.Date)]
+        [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:yyyy-MM-dd}")]
+        public DateTime StartDate { get; set; } = DateTime.Now.AddDays(-5);
+
+        [BindProperty(Name = "eDate", SupportsGet = true)]
+        [Required(ErrorMessage = "검색 종료날짜를 입력해주세요.")]
+        [DataType(DataType.Date)]
+        [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:yyyy-MM-dd}")]
+        public DateTime EndDate { get; set; } = DateTime.Now;
+    }
+}

+ 50 - 0
economy/Models/Request/Market/Flower.cs

@@ -0,0 +1,50 @@
+using Microsoft.AspNetCore.Mvc;
+using System.ComponentModel.DataAnnotations;
+
+namespace economy.Models.Flower
+{
+    // 부류코드
+    public enum SearchType : sbyte
+    {
+        CutFlower = 1,
+        FoliagePlants = 2,
+        Orchilds = 3,
+        SpringOrchilds = 4
+    }
+
+    /*
+     * 화훼 경락 일자별 경매정보
+     * 예시 ) https://flower.at.or.kr/api/returnData.api?kind=f001&serviceKey=sample&baseDate=2018-08-16&flowerGubn=1&dataType=json&countPerPage=999
+     */
+
+    // 검색요청 변수
+    public class Request
+    {
+        [BindProperty(Name = "page", SupportsGet = true)]
+        [Range(1, int.MaxValue, ErrorMessage = "페이지 허용량을 초과하였습니다.")]
+        public int PageNo { get; set; } = 1;
+
+        [BindProperty(Name = "perPage", SupportsGet = true)]
+        [Range(1, int.MaxValue, ErrorMessage = "출력 허용량을 초과하였습니다.")]
+        public int NumOfRows { get; set; } = 10;
+
+        [BindProperty(Name = "date", SupportsGet = true)]
+        [Required(ErrorMessage = "조회 날짜를 입력해주세요.")]
+        [DataType(DataType.Date)]
+        [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:yyyy-MM-dd}")]
+        public DateTime BaseDate { get; set; } = DateTime.Now;
+
+        [BindProperty(Name = "type", SupportsGet = true)]
+        [Required(ErrorMessage = "부류코드를 입력해주세요.")]
+        [EnumDataType(typeof(SearchType), ErrorMessage = "부류코드가 잘못되었습니다.")]
+        public SearchType Type { get; set; } = SearchType.CutFlower;
+
+        [BindProperty(Name = "pName", SupportsGet = true)]
+        [MaxLength(100, ErrorMessage = "품목명은 100자까지 입력가능합니다.")]
+        public string? PumName { get; set; }
+
+        [BindProperty(Name = "gName", SupportsGet = true)]
+        [MaxLength(100, ErrorMessage = "품종명은 100자까지 입력가능합니다.")]
+        public string? GoodName { get; set; }
+    }
+}

+ 38 - 0
economy/Models/Request/Market/Gold.cs

@@ -0,0 +1,38 @@
+using Microsoft.AspNetCore.Mvc;
+using System.ComponentModel.DataAnnotations;
+
+namespace economy.Models.Gold
+{
+    /*
+     * KRX금시장에 상장된 금상품의 시세 정보를 제공	
+     * 예시 ) https://apis.data.go.kr/1160100/service/GetGeneralProductInfoService/getGoldPriceInfo?serviceKey=vQz8tIxrdhjerG6DE1w1hcVEli5S27LtIsCvx0axiieZmRgOpB4vToQ77VmvknAkIC9YjxlPx2gDZcl06S88Xw%3D%3D&resultType=json&beginBasDt=20241010&endBasDt=20241012&pageNo=1&numOfRows=10
+     */
+
+    // 검색요청 변수
+    public class Request
+    {
+        [BindProperty(Name = "page", SupportsGet = true)]
+        [Range(1, int.MaxValue, ErrorMessage = "페이지 허용량을 초과하였습니다.")]
+        public int PageNo { get; set; } = 1;
+
+        [BindProperty(Name = "perPage", SupportsGet = true)]
+        [Range(1, int.MaxValue, ErrorMessage = "출력 허용량을 초과하였습니다.")]
+        public int NumOfRows { get; set; } = 10;
+
+        [BindProperty(Name = "code", SupportsGet = true)]
+        [StringLength(10, ErrorMessage = "품목코드 형식이 잘못되었습니다.")]
+        public string? LikeSrtnCd { get; set; }
+
+        [BindProperty(Name = "sDate", SupportsGet = true)]
+        [Required(ErrorMessage = "검색 시작날짜를 입력해주세요.")]
+        [DataType(DataType.Date)]
+        [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:yyyy-MM-dd}")]
+        public DateTime StartDate { get; set; } = DateTime.Now.AddDays(-5);
+
+        [BindProperty(Name = "eDate", SupportsGet = true)]
+        [Required(ErrorMessage = "검색 종료날짜를 입력해주세요.")]
+        [DataType(DataType.Date)]
+        [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:yyyy-MM-dd}")]
+        public DateTime EndDate { get; set; } = DateTime.Now;
+    }
+}

+ 34 - 0
economy/Models/Request/Market/Oil.cs

@@ -0,0 +1,34 @@
+using Microsoft.AspNetCore.Mvc;
+using System.ComponentModel.DataAnnotations;
+
+namespace economy.Models.Oil
+{
+    /*
+     * 석유전자상거래시장에 상장된 석유상품의 시세 정보를 제공
+     * 예시 ) https://apis.data.go.kr/1160100/service/GetGeneralProductInfoService/getOilPriceInfo?serviceKey=vQz8tIxrdhjerG6DE1w1hcVEli5S27LtIsCvx0axiieZmRgOpB4vToQ77VmvknAkIC9YjxlPx2gDZcl06S88Xw%3D%3D&resultType=json&beginBasDt=20241010&endBasDt=20241012&pageNo=1&numOfRows=10
+     */
+
+    // 검색요청 변수
+    public class Request
+    {
+        [BindProperty(Name = "page", SupportsGet = true)]
+        [Range(1, int.MaxValue, ErrorMessage = "페이지 허용량을 초과하였습니다.")]
+        public int PageNo { get; set; } = 1;
+
+        [BindProperty(Name = "perPage", SupportsGet = true)]
+        [Range(1, int.MaxValue, ErrorMessage = "출력 허용량을 초과하였습니다.")]
+        public int NumOfRows { get; set; } = 10;
+
+        [BindProperty(Name = "sDate", SupportsGet = true)]
+        [Required(ErrorMessage = "검색 시작날짜를 입력해주세요.")]
+        [DataType(DataType.Date)]
+        [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:yyyy-MM-dd}")]
+        public DateTime StartDate { get; set; } = DateTime.Now.AddDays(-5);
+
+        [BindProperty(Name = "eDate", SupportsGet = true)]
+        [Required(ErrorMessage = "검색 종료날짜를 입력해주세요.")]
+        [DataType(DataType.Date)]
+        [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:yyyy-MM-dd}")]
+        public DateTime EndDate { get; set; } = DateTime.Now;
+    }
+}

+ 40 - 0
economy/Models/Request/Product/Detail.cs

@@ -0,0 +1,40 @@
+using Microsoft.AspNetCore.Mvc;
+using System.ComponentModel.DataAnnotations;
+
+namespace economy.Models.Product.Detail
+{
+    /*
+     * 온라인 수집 가격 정보
+     * 품목 상세 정보
+     * 예시 ) http://apis.data.go.kr/1240000/bpp_openapi/getPriceInfo?serviceKey=vQz8tIxrdhjerG6DE1w1hcVEli5S27LtIsCvx0axiieZmRgOpB4vToQ77VmvknAkIC9YjxlPx2gDZcl06S88Xw%3D%3D&itemCode=A011010&startDate=20210220&endDate=20210220&numOfRows=10&pageNo=1
+     */
+
+    // 검색요청 변수
+    public class Request
+    {
+        [BindProperty(Name = "page", SupportsGet = true)]
+        [Range(1, int.MaxValue, ErrorMessage = "페이지 허용량을 초과하였습니다.")]
+        public int PageNo { get; set; } = 1;
+
+        [BindProperty(Name = "perPage", SupportsGet = true)]
+        [Range(1, int.MaxValue, ErrorMessage = "출력 허용량을 초과하였습니다.")]
+        public int NumOfRows { get; set; } = 10;
+
+        [BindProperty(Name = "item", SupportsGet = true)]
+        [Required(ErrorMessage = "품목코드를 입력해주세요.")]
+        [StringLength(7, ErrorMessage = "품목코드 형식이 잘못되었습니다.")]
+        public string ItemCode { get; set; }
+
+        [BindProperty(Name = "sDate", SupportsGet = true)]
+        [Required(ErrorMessage = "검색 시작날짜를 입력해주세요.")]
+        [DataType(DataType.Date)]
+        [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:yyyy-MM-dd}")]
+        public DateOnly StartDate { get; set; } = DateOnly.FromDateTime(DateTime.Now);
+
+        [BindProperty(Name = "eDate", SupportsGet = true)]
+        [Required(ErrorMessage = "검색 종료날짜를 입력해주세요.")]
+        [DataType(DataType.Date)]
+        [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:yyyy-MM-dd}")]
+        public DateOnly EndDate { get; set; } = DateOnly.FromDateTime(DateTime.Now);
+    }
+}

+ 23 - 0
economy/Models/Request/Product/List.cs

@@ -0,0 +1,23 @@
+using Microsoft.AspNetCore.Mvc;
+using System.ComponentModel.DataAnnotations;
+
+namespace economy.Models.Product.List
+{
+    /*
+     * 온라인 수집 가격 정보
+     * 품목별 종류
+     * 예시 ) https://apis.data.go.kr/1240000/bpp_openapi/getPriceItemList?ServiceKey=vQz8tIxrdhjerG6DE1w1hcVEli5S27LtIsCvx0axiieZmRgOpB4vToQ77VmvknAkIC9YjxlPx2gDZcl06S88Xw%3D%3D&pageNo=1&numOfRows=10
+     */
+
+    // 검색요청 변수
+    public class Request
+    {
+        [BindProperty(Name = "page", SupportsGet = true)]
+        [Range(1, int.MaxValue, ErrorMessage = "페이지 허용량을 초과하였습니다.")]
+        public int PageNo { get; set; } = 1;
+
+        [BindProperty(Name = "perPage", SupportsGet = true)]
+        [Range(1, int.MaxValue, ErrorMessage = "출력 허용량을 초과하였습니다.")]
+        public int NumOfRows { get; set; } = 10;
+    }
+}

+ 65 - 0
economy/Models/Response/Day/Anniversary.cs

@@ -0,0 +1,65 @@
+using System.Xml.Serialization;
+
+namespace economy.Models.Anniversary
+{
+    // 검색결과 변수
+    [XmlRoot("response")]
+    public class Response
+    {
+        [XmlElement("header")]
+        public Header Header { get; set; }
+
+        [XmlElement("body")]
+        public Body Body { get; set; }
+    }
+
+    public class Header
+    {
+        [XmlElement("resultCode")]
+        public string ResultCode { get; set; }
+
+        [XmlElement("resultMsg")]
+        public string ResultMsg { get; set; }
+    }
+
+    public class Body
+    {
+        [XmlElement("items")]
+        public Items Items { get; set; }
+
+        [XmlElement("numOfRows")]
+        public int NumOfRows { get; set; }
+
+        [XmlElement("pageNo")]
+        public int PageNo { get; set; }
+
+        [XmlElement("totalCount")]
+        public int TotalCount { get; set; }
+    }
+
+    public class Items
+    {
+        [XmlElement("item")]
+        public List<Item> ItemList { get; set; }
+    }
+
+    public class Item
+    {
+        public int Num { get; set; }
+
+        [XmlElement("dateKind")]
+        public string DateKind { get; set; } // 종류
+
+        [XmlElement("dateName")]
+        public string DateName { get; set; } // 명칭
+
+        [XmlElement("isHoliday")]
+        public string IsHoliday { get; set; } // 공공기관 휴일여부
+
+        [XmlElement("locdate")]
+        public string LocDate { get; set; } // 날짜
+
+        [XmlElement("seq")]
+        public int Seq { get; set; } // 순번
+    }
+}

+ 65 - 0
economy/Models/Response/Day/Holiday.cs

@@ -0,0 +1,65 @@
+using System.Xml.Serialization;
+
+namespace economy.Models.Holiday
+{
+    // 검색결과 변수
+    [XmlRoot("response")]
+    public class Response
+    {
+        [XmlElement("header")]
+        public Header Header { get; set; }
+
+        [XmlElement("body")]
+        public Body Body { get; set; }
+    }
+
+    public class Header
+    {
+        [XmlElement("resultCode")]
+        public string ResultCode { get; set; }
+
+        [XmlElement("resultMsg")]
+        public string ResultMsg { get; set; }
+    }
+
+    public class Body
+    {
+        [XmlElement("items")]
+        public Items Items { get; set; }
+
+        [XmlElement("numOfRows")]
+        public int NumOfRows { get; set; }
+
+        [XmlElement("pageNo")]
+        public int PageNo { get; set; }
+
+        [XmlElement("totalCount")]
+        public int TotalCount { get; set; }
+    }
+
+    public class Items
+    {
+        [XmlElement("item")]
+        public List<Item> ItemList { get; set; }
+    }
+
+    public class Item
+    {
+        public int Num { get; set; }
+
+        [XmlElement("dateKind")]
+        public string DateKind { get; set; } // 종류
+
+        [XmlElement("dateName")]
+        public string DateName { get; set; } // 명칭
+
+        [XmlElement("isHoliday")]
+        public string IsHoliday { get; set; } // 공공기관 휴일여부
+
+        [XmlElement("locdate")]
+        public string LocDate { get; set; } // 날짜
+
+        [XmlElement("seq")]
+        public int Seq { get; set; } // 순번
+    }
+}

+ 71 - 0
economy/Models/Response/Day/Seasonal.cs

@@ -0,0 +1,71 @@
+using System.Xml.Serialization;
+
+namespace economy.Models.Seasonal
+{
+    // 검색결과 변수
+    [XmlRoot("response")]
+    public class Response
+    {
+        [XmlElement("header")]
+        public Header Header { get; set; }
+
+        [XmlElement("body")]
+        public Body Body { get; set; }
+    }
+
+    public class Header
+    {
+        [XmlElement("resultCode")]
+        public string ResultCode { get; set; }
+
+        [XmlElement("resultMsg")]
+        public string ResultMsg { get; set; }
+    }
+
+    public class Body
+    {
+        [XmlElement("items")]
+        public Items Items { get; set; }
+
+        [XmlElement("numOfRows")]
+        public int NumOfRows { get; set; }
+
+        [XmlElement("pageNo")]
+        public int PageNo { get; set; }
+
+        [XmlElement("totalCount")]
+        public int TotalCount { get; set; }
+    }
+
+    public class Items
+    {
+        [XmlElement("item")]
+        public List<Item> ItemList { get; set; }
+    }
+
+    public class Item
+    {
+        public int Num { get; set; }
+
+        [XmlElement("dateKind")]
+        public string DateKind { get; set; } // 종류
+
+        [XmlElement("dateName")]
+        public string DateName { get; set; } // 명칭
+
+        [XmlElement("isHoliday")]
+        public string IsHoliday { get; set; } // 공공기관 휴일여부
+
+        [XmlElement("locdate")]
+        public string LocDate { get; set; } // 날짜
+
+        [XmlElement("seq")]
+        public int Seq { get; set; } // 순번
+
+        [XmlElement("kst")]
+        public int Kst { get; set; } // 한국표준시각
+
+        [XmlElement("sunLongitude")]
+        public int SunLongitude { get; set; } // 태양황경
+    }
+}

+ 71 - 0
economy/Models/Response/Day/Sundry.cs

@@ -0,0 +1,71 @@
+using System.Xml.Serialization;
+
+namespace economy.Models.Sundry
+{
+    // 검색결과 변수
+    [XmlRoot("response")]
+    public class Response
+    {
+        [XmlElement("header")]
+        public Header Header { get; set; }
+
+        [XmlElement("body")]
+        public Body Body { get; set; }
+    }
+
+    public class Header
+    {
+        [XmlElement("resultCode")]
+        public string ResultCode { get; set; }
+
+        [XmlElement("resultMsg")]
+        public string ResultMsg { get; set; }
+    }
+
+    public class Body
+    {
+        [XmlElement("items")]
+        public Items Items { get; set; }
+
+        [XmlElement("numOfRows")]
+        public int NumOfRows { get; set; }
+
+        [XmlElement("pageNo")]
+        public int PageNo { get; set; }
+
+        [XmlElement("totalCount")]
+        public int TotalCount { get; set; }
+    }
+
+    public class Items
+    {
+        [XmlElement("item")]
+        public List<Item> ItemList { get; set; }
+    }
+
+    public class Item
+    {
+        public int Num { get; set; }
+
+        [XmlElement("dateKind")]
+        public string DateKind { get; set; } // 종류
+
+        [XmlElement("dateName")]
+        public string DateName { get; set; } // 명칭
+
+        [XmlElement("isHoliday")]
+        public string IsHoliday { get; set; } // 공공기관 휴일여부
+
+        [XmlElement("locdate")]
+        public string LocDate { get; set; } // 날짜
+
+        [XmlElement("seq")]
+        public int Seq { get; set; } // 순번
+
+        [XmlElement("kst")]
+        public int Kst { get; set; } // 한국표준시각
+
+        [XmlElement("sunLongitude")]
+        public int SunLongitude { get; set; } // 태양황경
+    }
+}

+ 110 - 0
economy/Models/Response/FIFA.cs

@@ -0,0 +1,110 @@
+using System.Text.Json.Serialization;
+
+namespace economy.Models.FIFA
+{
+    public class Response
+    {
+        [JsonPropertyName("ContinuationToken")]
+        public object ContinuationToken { get; set; }
+
+        [JsonPropertyName("ContinuationHash")]
+        public object ContinuationHash { get; set; }
+
+        [JsonPropertyName("Results")]
+        public List<Team> Results { get; set; }
+    }
+
+    public class TeamName
+    {
+        [JsonPropertyName("Locale")]
+        public string Locale { get; set; }
+
+        [JsonPropertyName("Description")]
+        public string Description { get; set; }
+    }
+
+    public class Team
+    {
+        public int Num { get; set; } = 1;
+
+        [JsonPropertyName("IdTeam")]
+        public string IdTeam { get; set; }
+
+        [JsonPropertyName("TeamName")]
+        public List<TeamName> TeamName { get; set; }
+
+        [JsonPropertyName("Gender")]
+        public int Gender { get; set; }
+
+        [JsonPropertyName("IdConfederation")]
+        public string IdConfederation { get; set; }
+
+        [JsonPropertyName("RankingMovement")]
+        public int? RankingMovement { get; set; }
+
+        [JsonPropertyName("RankingMovementString")]
+        public string RankingMovementString { get; set; }
+
+        [JsonPropertyName("ConfederationName")]
+        public string ConfederationName { get; set; }
+
+        [JsonPropertyName("IdCountry")]
+        public string IdCountry { get; set; }
+
+        [JsonPropertyName("Rank")]
+        public int? Rank { get; set; }
+
+        [JsonPropertyName("IdSchedule")]
+        public string IdSchedule { get; set; }
+
+        [JsonPropertyName("PrevRank")]
+        public int? PrevRank { get; set; }
+
+        [JsonPropertyName("EOYRank")]
+        public int EOYRank { get; set; }
+
+        [JsonPropertyName("TotalPoints")]
+        public int TotalPoints { get; set; }
+
+        [JsonPropertyName("DecimalTotalPoints")]
+        public double DecimalTotalPoints { get; set; }
+
+        [JsonPropertyName("PrevPoints")]
+        public int PrevPoints { get; set; }
+
+        [JsonPropertyName("StatusRanked")]
+        public int StatusRanked { get; set; }
+
+        [JsonPropertyName("DecimalPrevPoints")]
+        public double DecimalPrevPoints { get; set; }
+
+        [JsonPropertyName("EOYPoints")]
+        public int EOYPoints { get; set; }
+
+        [JsonPropertyName("DecimalEOYPoints")]
+        public double DecimalEOYPoints { get; set; }
+
+        [JsonPropertyName("Matches")]
+        public int Matches { get; set; }
+
+        [JsonPropertyName("PubDate")]
+        public DateTime PubDate { get; set; }
+
+        [JsonPropertyName("PrePubDate")]
+        public DateTime? PrePubDate { get; set; }
+
+        [JsonPropertyName("EOYPubDate")]
+        public DateTime? EOYPubDate { get; set; }
+
+        [JsonPropertyName("NextPubDate")]
+        public DateTime? NextPubDate { get; set; }
+
+        [JsonPropertyName("Properties")]
+        public Dictionary<string, object> Properties { get; set; }
+
+        [JsonPropertyName("IsUpdateable")]
+        public object IsUpdateable { get; set; }
+
+        public string FlagSrc { get; set; } // 국기 경로
+    }
+}

+ 46 - 0
economy/Models/Response/Financial/Exchange.cs

@@ -0,0 +1,46 @@
+using System.Text.Json.Serialization;
+
+namespace economy.Models.Exchange
+{
+    // 검색결과 변수
+    public class Response
+    {
+        public List<Data> Items { get; set; }
+    }
+
+    public class Data
+    {
+        [JsonPropertyName("result")]
+        public int Result { get; set; }
+
+        [JsonPropertyName("cur_unit")]
+        public string CurUnit { get; set; } // 통화 단위
+
+        [JsonPropertyName("cur_nm")]
+        public string CurNm { get; set; } // 국가/통화명
+
+        [JsonPropertyName("ttb")]
+        public string Ttb { get; set; } // 매입 환율
+
+        [JsonPropertyName("tts")]
+        public string Tts { get; set; } // 매도 환율
+
+        [JsonPropertyName("deal_bas_r")]
+        public string DealBasRate { get; set; } // 기준 환율
+
+        [JsonPropertyName("bkpr")]
+        public string BankSellingRate { get; set; } // 은행 판매율
+
+        [JsonPropertyName("yy_efee_r")]
+        public string YearlyExchangeFeeRate { get; set; } // 연간 환전 수수료율
+
+        [JsonPropertyName("ten_dd_efee_r")]
+        public string TenDayExchangeFeeRate { get; set; } // 10일 환전 수수료율
+
+        [JsonPropertyName("kftc_bkpr")]
+        public string KftcBankSellingRate { get; set; } // 한국 외환 거래소 기준 판매율
+
+        [JsonPropertyName("kftc_deal_bas_r")]
+        public string KftcDealBaseRate { get; set; } // 한국 외환 거래소 기준 기준율
+    }
+}

+ 22 - 0
economy/Models/Response/Financial/Interest.cs

@@ -0,0 +1,22 @@
+using System.Text.Json.Serialization;
+
+namespace economy.Models.Interest
+{
+    // 검색결과 변수
+    public class Response
+    {
+        public List<Data> Items { get; set; }
+    }
+
+    public class Data
+    {
+        [JsonPropertyName("result")]
+        public int Result { get; set; }
+
+        [JsonPropertyName("sfln_intrc_nm")]
+        public string? InterestName { get; set; } // 대출기간
+
+        [JsonPropertyName("int_r")]
+        public string? InterestRate { get; set; } // 고정기준금리
+    }
+}

+ 95 - 0
economy/Models/Response/Financial/International.cs

@@ -0,0 +1,95 @@
+using System.Text.Json.Serialization;
+
+namespace economy.Models.International
+{
+    // 검색결과 변수
+    public class Response
+    {
+        [JsonPropertyName("result")]
+        public int Result { get; set; }
+
+        [JsonPropertyName("cur_fund")]
+        public string? CurFund { get; set; }
+
+        [JsonPropertyName("sfln_intrc_nm")]
+        public string? SflnIntrcNm { get; set; }
+
+        [JsonPropertyName("int_r")]
+        public string? IntR { get; set; }
+
+        [JsonPropertyName("sofr_list")]
+        public List<Data>? SofrList { get; set; }
+
+        [JsonPropertyName("estr_list")]
+        public List<Data>? EstrList { get; set; }
+
+        [JsonPropertyName("euribor_list")]
+        public List<Data>? EuriborList { get; set; }
+
+        [JsonPropertyName("tona_list")]
+        public List<Data>? TonaList { get; set; }
+
+        [JsonPropertyName("tibor_list")]
+        public List<Data>? TiborList { get; set; }
+
+        [JsonPropertyName("swapRfr_list")]
+        public List<Data>? SwapRfrList { get; set; }
+
+        [JsonPropertyName("libor_list")]
+        public List<Data>? LiborList { get; set; }
+
+        [JsonPropertyName("swap_list")]
+        public List<Data>? SwapList { get; set; }
+
+        [JsonPropertyName("cirr_list")]
+        public List<Data>? CirrList { get; set; }
+
+        [JsonPropertyName("new_cirr_list")]
+        public List<Data>? NewCirrList { get; set; }
+    }
+
+    public class Data
+    {
+        [JsonPropertyName("result")]
+        public int Result { get; set; }
+
+        [JsonPropertyName("cur_fund")]
+        public string? CurFund { get; set; } // 통화
+
+        [JsonPropertyName("sfln_intrc_nm")]
+        public string? InterestName { get; set; } // 기간
+
+        [JsonPropertyName("int_r")]
+        public string? InterestRate { get; set; } // 금리
+
+        [JsonPropertyName("sofr_list")]
+        public object? SofrList { get; set; }
+
+        [JsonPropertyName("estr_list")]
+        public object? EstrList { get; set; }
+
+        [JsonPropertyName("euribor_list")]
+        public object? EuriborList { get; set; }
+
+        [JsonPropertyName("tona_list")]
+        public object? TonaList { get; set; }
+
+        [JsonPropertyName("tibor_list")]
+        public object? TiborList { get; set; }
+
+        [JsonPropertyName("swapRfr_list")]
+        public object? SwapRfrList { get; set; }
+
+        [JsonPropertyName("libor_list")]
+        public object? LiborList { get; set; }
+
+        [JsonPropertyName("swap_list")]
+        public object? SwapList { get; set; }
+
+        [JsonPropertyName("cirr_list")]
+        public object? CirrList { get; set; }
+
+        [JsonPropertyName("new_cirr_list")]
+        public object? NewCirrList { get; set; }
+    }
+}

+ 63 - 0
economy/Models/Response/Lotto.cs

@@ -0,0 +1,63 @@
+using System.Text.Json.Serialization;
+
+namespace economy.Models.Lotto
+{
+    public class Response
+    {
+        // 요청 결과
+        [JsonPropertyName("returnValue")]
+        public string ReturnValue { get; set; }
+
+        // 총 판매금액
+        [JsonPropertyName("totSellamnt")]
+        public long TotalSellAmount { get; set; }
+
+        // 추첨일
+        [JsonPropertyName("drwNoDate")]
+        public DateOnly DrawDate { get; set; }
+
+        // 1게임당 당첨금액
+        [JsonPropertyName("firstWinamnt")]
+        public long FirstWinAmount { get; set; }
+
+        // 1등 당첨인원
+        [JsonPropertyName("firstPrzwnerCo")]
+        public int FirstPrizeWinnerCount { get; set; }
+
+        // 1등 총 당첨금액
+        [JsonPropertyName("firstAccumamnt")]
+        public long FirstAccumulatedAmount { get; set; }
+
+        // 회차
+        [JsonPropertyName("drwNo")]
+        public int DrawNumber { get; set; }
+
+        // 번호 1
+        [JsonPropertyName("drwtNo1")]
+        public int DrawnNumber1 { get; set; }
+
+        // 번호 2
+        [JsonPropertyName("drwtNo2")]
+        public int DrawnNumber2 { get; set; }
+
+        // 번호 3
+        [JsonPropertyName("drwtNo3")]
+        public int DrawnNumber3 { get; set; }
+
+        // 번호 4
+        [JsonPropertyName("drwtNo4")]
+        public int DrawnNumber4 { get; set; }
+
+        // 번호 5
+        [JsonPropertyName("drwtNo5")]
+        public int DrawnNumber5 { get; set; }
+
+        // 번호 6
+        [JsonPropertyName("drwtNo6")]
+        public int DrawnNumber6 { get; set; }
+
+        // 보너스 번호
+        [JsonPropertyName("bnusNo")]
+        public int BonusNumber { get; set; }
+    }
+}

+ 85 - 0
economy/Models/Response/Market/Emission.cs

@@ -0,0 +1,85 @@
+using System.Text.Json.Serialization;
+
+namespace economy.Models.Emission
+{
+    // 검색결과 변수
+    public class Response
+    {
+        [JsonPropertyName("header")]
+        public Header Header { get; set; }
+
+        [JsonPropertyName("body")]
+        public Body Body { get; set; }
+    }
+
+    public class Header
+    {
+        [JsonPropertyName("resultCode")]
+        public string ResultCode { get; set; }
+
+        [JsonPropertyName("resultMsg")]
+        public string ResultMsg { get; set; }
+    }
+
+    public class Body
+    {
+        [JsonPropertyName("numOfRows")]
+        public int NumberOfRows { get; set; }
+
+        [JsonPropertyName("pageNo")]
+        public int PageNo { get; set; }
+
+        [JsonPropertyName("totalCount")]
+        public int TotalCount { get; set; }
+
+        [JsonPropertyName("items")]
+        public Items Items { get; set; }
+    }
+
+    public class Items
+    {
+        [JsonPropertyName("item")]
+        public List<Item> ItemList { get; set; }
+    }
+
+    public class Item
+    {
+        public int Num { get; set; }
+
+        [JsonPropertyName("basDt")]
+        public string BasDt { get; set; } // 기준일자
+
+        [JsonPropertyName("srtnCd")]
+        public string SrtnCd { get; set; } // 단축코드
+
+        [JsonPropertyName("isinCd")]
+        public string IsinCd { get; set; } // ISIN코드
+
+        [JsonPropertyName("itmsNm")]
+        public string ItmsNm { get; set; } // 종목명
+
+        [JsonPropertyName("clpr")]
+        public string Clpr { get; set; } // 종가
+
+        [JsonPropertyName("vs")]
+        public string Vs { get; set; } // 대비
+
+        [JsonPropertyName("fltRt")]
+        public string FltRt { get; set; } // 등락률
+
+        [JsonPropertyName("mkp")]
+        public string Mkp { get; set; } // 시가
+
+        [JsonPropertyName("hipr")]
+        public string Hipr { get; set; } // 고가
+
+        [JsonPropertyName("lopr")]
+        public string Lopr { get; set; } // 저가
+
+        [JsonPropertyName("trqu")]
+        public string Trqu { get; set; } // 거래량
+
+        [JsonPropertyName("trPrc")]
+        public string TrPrc { get; set; } // 거래대금
+    }
+}

+ 56 - 0
economy/Models/Response/Market/Flower.cs

@@ -0,0 +1,56 @@
+using System.Text.Json.Serialization;
+
+namespace economy.Models.Flower
+{
+    // 검색결과 변수
+    public class Response
+    {
+        [JsonPropertyName("resultCd")]
+        public string ResultCode { get; set; }
+
+        [JsonPropertyName("resultMsg")]
+        public string ResultMsg { get; set; }
+
+        [JsonPropertyName("numOfRows")]
+        [JsonConverter(typeof(FlexibleTypeConverter<int>))]
+        public int NumberOfRows { get; set; }
+
+        [JsonPropertyName("items")]
+        public List<Item> Items { get; set; }
+    }
+
+    public class Item
+    {
+        public int Num { get; set; }
+
+        [JsonPropertyName("saleDate")]
+        public string SaleDate { get; set; } // 경매일자
+
+        [JsonPropertyName("flowerGubn")]
+        public string FlowerGubn { get; set; } // 화훼부류명
+
+        [JsonPropertyName("pumName")]
+        public string PumName { get; set; } // 품목명
+
+        [JsonPropertyName("goodName")]
+        public string GoodName { get; set; } // 품종명
+
+        [JsonPropertyName("lvNm")]
+        public string LvNm { get; set; } // 등급명
+
+        [JsonPropertyName("maxAmt")]
+        public string MaxAmt { get; set; } // 최고가
+
+        [JsonPropertyName("minAmt")]
+        public string MinAmt { get; set; } // 최저가
+
+        [JsonPropertyName("avgAmt")]
+        public string AvgAmt { get; set; } // 평균가
+
+        [JsonPropertyName("totAmt")]
+        public string TotAmt { get; set; } // 총금액
+
+        [JsonPropertyName("totQty")]
+        public string TotQty { get; set; } // 총물량
+    }
+}

+ 85 - 0
economy/Models/Response/Market/Gold.cs

@@ -0,0 +1,85 @@
+using System.Text.Json.Serialization;
+
+namespace economy.Models.Gold
+{
+    // 검색결과 변수
+    public class Response
+    {
+        [JsonPropertyName("header")]
+        public Header Header { get; set; }
+
+        [JsonPropertyName("body")]
+        public Body Body { get; set; }
+    }
+
+    public class Header
+    {
+        [JsonPropertyName("resultCode")]
+        public string ResultCode { get; set; }
+
+        [JsonPropertyName("resultMsg")]
+        public string ResultMsg { get; set; }
+    }
+
+    public class Body
+    {
+        [JsonPropertyName("numOfRows")]
+        public int NumberOfRows { get; set; }
+
+        [JsonPropertyName("pageNo")]
+        public int PageNo { get; set; }
+
+        [JsonPropertyName("totalCount")]
+        public int TotalCount { get; set; }
+
+        [JsonPropertyName("items")]
+        public Items Items { get; set; }
+    }
+
+    public class Items
+    {
+        [JsonPropertyName("item")]
+        public List<Item> ItemList { get; set; }
+    }
+
+    public class Item
+    {
+        public int Num { get; set; }
+
+        [JsonPropertyName("basDt")]
+        public string BasDt { get; set; } // 기준일자
+
+        [JsonPropertyName("srtnCd")]
+        public string SrtnCd { get; set; } // 단축코드
+
+        [JsonPropertyName("isinCd")]
+        public string IsinCd { get; set; } // ISIN코드
+
+        [JsonPropertyName("itmsNm")]
+        public string ItmsNm { get; set; } // 종목명
+
+        [JsonPropertyName("clpr")]
+        public string Clpr { get; set; } // 종가
+
+        [JsonPropertyName("vs")]
+        public string Vs { get; set; } // 대비
+
+        [JsonPropertyName("fltRt")]
+        public string FltRt { get; set; } // 등락률
+
+        [JsonPropertyName("mkp")]
+        public string Mkp { get; set; } // 시가
+
+        [JsonPropertyName("hipr")]
+        public string Hipr { get; set; } // 고가
+
+        [JsonPropertyName("lopr")]
+        public string Lopr { get; set; } // 저가
+
+        [JsonPropertyName("trqu")]
+        public string Trqu { get; set; } // 거래량
+
+        [JsonPropertyName("trPrc")]
+        public string TrPrc { get; set; } // 거래대금
+    }
+}

+ 67 - 0
economy/Models/Response/Market/Oil.cs

@@ -0,0 +1,67 @@
+using System.Text.Json.Serialization;
+
+namespace economy.Models.Oil
+{
+    // 검색결과 변수
+    public class Response
+    {
+        [JsonPropertyName("header")]
+        public Header Header { get; set; }
+
+        [JsonPropertyName("body")]
+        public Body Body { get; set; }
+    }
+
+    public class Header
+    {
+        [JsonPropertyName("resultCode")]
+        public string ResultCode { get; set; }
+
+        [JsonPropertyName("resultMsg")]
+        public string ResultMsg { get; set; }
+    }
+
+    public class Body
+    {
+        [JsonPropertyName("numOfRows")]
+        public int NumberOfRows { get; set; }
+
+        [JsonPropertyName("pageNo")]
+        public int PageNo { get; set; }
+
+        [JsonPropertyName("totalCount")]
+        public int TotalCount { get; set; }
+
+        [JsonPropertyName("items")]
+        public Items Items { get; set; }
+    }
+
+    public class Items
+    {
+        [JsonPropertyName("item")]
+        public List<Item> ItemList { get; set; }
+    }
+
+    public class Item
+    {
+        public int Num { get; set; }
+
+        [JsonPropertyName("basDt")]
+        public string BasDt { get; set; } // 기준일자
+
+        [JsonPropertyName("oilCtg")]
+        public string OilCtg { get; set; } // 유종구분
+
+        [JsonPropertyName("wtAvgPrcCptn")]
+        public string WtAvgPrcCptn { get; set; } // 가중평균가격 경쟁
+
+        [JsonPropertyName("wtAvgPrcDisc")]
+        public string WtAvgPrcDisc { get; set; } // 가중평균가격 협의
+
+        [JsonPropertyName("trqu")]
+        public string Trqu { get; set; } // 거래량
+
+        [JsonPropertyName("trPrc")]
+        public string TrPrc { get; set; } // 거래대금
+    }
+}

+ 70 - 0
economy/Models/Response/Product/Detail.cs

@@ -0,0 +1,70 @@
+using System.Xml.Serialization;
+
+namespace economy.Models.Product.Detail
+{
+    // 검색결과 변수
+    [XmlRoot(ElementName = "response")]
+    public class Response
+    {
+        [XmlElement(ElementName = "body")]
+        public Body Body { get; set; }
+    }
+
+    public class Body
+    {
+        // 품목코드
+        [XmlElement(ElementName = "ic")]
+        public string Ic { get; set; }
+
+        // 품목명
+        [XmlElement(ElementName = "in")]
+        public string In { get; set; }
+
+        [XmlElement(ElementName = "items")]
+        public Items Items { get; set; }
+
+        [XmlElement(ElementName = "numOfRows")]
+        public int NumberOfRows { get; set; }
+
+        [XmlElement(ElementName = "pageNo")]
+        public int PageNo { get; set; }
+
+        [XmlElement(ElementName = "totalCount")]
+        public int TotalCount { get; set; }
+    }
+
+    public class Items
+    {
+        [XmlElement(ElementName = "item")]
+        public List<Item> ItemList { get; set; }
+    }
+
+    public class Item
+    {
+        public int Num { get; set; }
+
+        // 상품 ID
+        [XmlElement(ElementName = "pi")]
+        public string Pi { get; set; }
+
+        // 상품명
+        [XmlElement(ElementName = "pn")]
+        public string Pn { get; set; }
+
+        // 판매가격
+        [XmlElement(ElementName = "sp")]
+        public int Sp { get; set; }
+
+        // 할인가격
+        [XmlElement(ElementName = "dp")]
+        public int Dp { get; set; }
+
+        // 혜택가격
+        [XmlElement(ElementName = "bp")]
+        public int Bp { get; set; }
+
+        // 가격일자
+        [XmlElement(ElementName = "sd")]
+        public string Sd { get; set; }
+    }
+}

+ 45 - 0
economy/Models/Response/Product/List.cs

@@ -0,0 +1,45 @@
+using System.Xml.Serialization;
+
+namespace economy.Models.Product.List
+{
+    // 검색결과 변수
+    [XmlRoot(ElementName = "response")]
+    public class Response
+    {
+        [XmlElement(ElementName = "body")]
+        public Body Body { get; set; }
+    }
+
+    public class Body
+    {
+        [XmlElement(ElementName = "items")]
+        public Items Items { get; set; }
+
+        [XmlElement(ElementName = "numOfRows")]
+        public int NumberOfRows { get; set; }
+
+        [XmlElement(ElementName = "pageNo")]
+        public int PageNo { get; set; }
+
+        [XmlElement(ElementName = "totalCount")]
+        public int TotalCount { get; set; }
+    }
+
+    public class Items
+    {
+        [XmlElement(ElementName = "item")]
+        public List<Item> ItemList { get; set; }
+    }
+
+    public class Item
+    {
+        [XmlElement(ElementName = "rn")]
+        public int Rn { get; set; }
+
+        [XmlElement(ElementName = "ic")]
+        public string Ic { get; set; }
+
+        [XmlElement(ElementName = "in")]
+        public string In { get; set; }
+    }
+}

+ 51 - 0
economy/Models/SeasonalModel.cs

@@ -0,0 +1,51 @@
+using economy.Helpers;
+
+namespace economy.Models.Seasonal
+{
+    public class SeasonalModel
+    {
+        private readonly DataGoKR _dataGoKR;
+
+        public SeasonalModel(DataGoKR dataGoKR)
+        {
+            _dataGoKR = dataGoKR;
+        }
+
+        // 기념일 조회
+        public async Task<Response> GetSeasonal(Request request)
+        {
+            Response parseData = new();
+
+            try
+            {
+                var uriBuilder = new UriBuilder(_dataGoKR.APIUrl)
+                {
+                    Path = "/B090041/openapi/service/SpcdeInfoService/get24DivisionsInfo",
+                    Query = $"ServiceKey={_dataGoKR.APIKey}&numOfRows=100&solYear={request.Year}"
+                };
+
+                var response = await _dataGoKR.httpClient.GetAsync(uriBuilder.Uri);
+
+                if (response.IsSuccessStatusCode)
+                {
+                    var xmlString = await response.Content.ReadAsStringAsync();
+                    parseData = await Common.ParseXmlDataAsync<Response>(xmlString);
+
+
+                    if (parseData.Body is null)
+                    {
+                        return new Response();
+                    }
+                }
+
+                response.EnsureSuccessStatusCode();
+            }
+            catch (HttpRequestException e)
+            {
+                Console.WriteLine($"Request error: {e.Message}");
+            }
+
+            return parseData;
+        }
+    }
+}

+ 51 - 0
economy/Models/SundryModel.cs

@@ -0,0 +1,51 @@
+using economy.Helpers;
+
+namespace economy.Models.Sundry
+{
+    public class SundryModel
+    {
+        private readonly DataGoKR _dataGoKR;
+
+        public SundryModel(DataGoKR dataGoKR)
+        {
+            _dataGoKR = dataGoKR;
+        }
+
+        // 잡절 조회
+        public async Task<Response> GetSundry(Request request)
+        {
+            Response parseData = new();
+
+            try
+            {
+                var uriBuilder = new UriBuilder(_dataGoKR.APIUrl)
+                {
+                    Path = "/B090041/openapi/service/SpcdeInfoService/getSundryDayInfo",
+                    Query = $"ServiceKey={_dataGoKR.APIKey}&numOfRows=100&solYear={request.Year}"
+                };
+
+                var response = await _dataGoKR.httpClient.GetAsync(uriBuilder.Uri);
+
+                if (response.IsSuccessStatusCode)
+                {
+                    var xmlString = await response.Content.ReadAsStringAsync();
+                    parseData = await Common.ParseXmlDataAsync<Response>(xmlString);
+
+
+                    if (parseData.Body is null)
+                    {
+                        return new Response();
+                    }
+                }
+
+                response.EnsureSuccessStatusCode();
+            }
+            catch (HttpRequestException e)
+            {
+                Console.WriteLine($"Request error: {e.Message}");
+            }
+
+            return parseData;
+        }
+    }
+}

+ 80 - 0
economy/Models/View.cs

@@ -0,0 +1,80 @@
+using Microsoft.AspNetCore.Mvc.Rendering;
+
+namespace economy.Models
+{
+    public class View<TRequest, TResponse>
+    {
+        public TRequest Request { get; set; }
+        public TResponse Response { get; set; }
+
+        // 한 페이지 보여줄 개수
+        /*
+        public enum ListPerPage
+        {
+            [Display(Name = "10개")]
+            Default = 10,
+
+            [Display(Name = "20개")]
+            Second = 20,
+
+            [Display(Name = "30개")]
+            Third = 30,
+
+            [Display(Name = "60개")]
+            Fourth = 60,
+
+            [Display(Name = "80개")]
+            Fifth = 80,
+
+            [Display(Name = "100개")]
+            Sixth = 100
+        }
+
+        public ListPerPage SelectedListPerPage { get; set; } = ListPerPage.Default;
+
+        // 사전에 미리 정의해 놓음
+        public ListPerPage MatchListPerPage(int numOfRows)
+        {
+            var listPerPageMap = new Dictionary<int, ListPerPage>
+            {
+                { 10, ListPerPage.Default },
+                { 20, ListPerPage.Second },
+                { 30, ListPerPage.Third },
+                { 60, ListPerPage.Fourth },
+                { 80, ListPerPage.Fifth },
+                { 100, ListPerPage.Sixth }
+            };
+
+            return listPerPageMap.ContainsKey(numOfRows) ? listPerPageMap[numOfRows] : ListPerPage.Default;
+        }
+        */
+
+        public int SelectedListPerPage { get; set; }
+
+        public List<SelectListItem> ListPerPage { get; } = new List<SelectListItem>
+        {
+            new SelectListItem { Value = "10", Text = "10개" },
+            new SelectListItem { Value = "20", Text = "20개" },
+            new SelectListItem { Value = "30", Text = "30개" },
+            new SelectListItem { Value = "60", Text = "60개" },
+            new SelectListItem { Value = "80", Text = "80개" },
+            new SelectListItem { Value = "100", Text = "100개" },
+            new SelectListItem { Value = "300", Text = "300개" },
+            new SelectListItem { Value = "500", Text = "500개" }
+        };
+
+        public List<SelectListItem> FIFAListPerPage { get; } = new List<SelectListItem>
+        {
+            new SelectListItem { Value = "10", Text = "상위 10" },
+            new SelectListItem { Value = "20", Text = "상위 20" },
+            new SelectListItem { Value = "30", Text = "상위 30" },
+            new SelectListItem { Value = "60", Text = "상위 60" },
+            new SelectListItem { Value = "80", Text = "상위 80" },
+            new SelectListItem { Value = "100", Text = "상위 100" },
+            new SelectListItem { Value = "200", Text = "상위 200" },
+            new SelectListItem { Value = "300", Text = "상위 300" }
+        };
+
+        public Pagination Pagination { get; set; }
+    }
+}

+ 39 - 0
economy/Program.cs

@@ -0,0 +1,39 @@
+using economy.Models;
+using System.Text;
+
+var builder = WebApplication.CreateBuilder(args);
+
+// Add services to the container.
+builder.Services.AddControllersWithViews();
+
+// HttpClient »ç¿ë
+builder.Services.AddHttpClient<DataGoKR>();
+builder.Services.AddHttpClient<FlowerAtOrKR>();
+builder.Services.AddHttpClient<KoreaEximGoKR>();
+builder.Services.AddHttpClient<DhlotteryCoKR>();
+builder.Services.AddHttpClient<FIFA_API>();
+
+Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); // ÀÎÄÚµù µî·Ï
+
+var app = builder.Build();
+
+// Configure the HTTP request pipeline.
+if (!app.Environment.IsDevelopment())
+{
+    app.UseExceptionHandler("/Home/Error");
+    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
+    app.UseHsts();
+}
+
+app.UseHttpsRedirection();
+app.UseStaticFiles();
+
+app.UseRouting();
+
+app.UseAuthorization();
+
+app.MapControllerRoute(
+    name: "default",
+    pattern: "{controller=Home}/{action=Index}/{id?}");
+
+app.Run();

+ 49 - 0
economy/Properties/launchSettings.json

@@ -0,0 +1,49 @@
+{
+  "profiles": {
+    "http": {
+      "commandName": "Project",
+      "launchBrowser": true,
+      "environmentVariables": {
+        "ASPNETCORE_ENVIRONMENT": "Development"
+      },
+      "dotnetRunMessages": true,
+      "applicationUrl": "http://localhost:5027"
+    },
+    "https": {
+      "commandName": "Project",
+      "launchBrowser": true,
+      "environmentVariables": {
+        "ASPNETCORE_ENVIRONMENT": "Development"
+      },
+      "dotnetRunMessages": true,
+      "applicationUrl": "https://localhost:7294;http://localhost:5027"
+    },
+    "IIS Express": {
+      "commandName": "IISExpress",
+      "launchBrowser": true,
+      "environmentVariables": {
+        "ASPNETCORE_ENVIRONMENT": "Development"
+      }
+    },
+    "Container (Dockerfile)": {
+      "commandName": "Docker",
+      "launchBrowser": true,
+      "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}",
+      "environmentVariables": {
+        "ASPNETCORE_HTTPS_PORTS": "8081",
+        "ASPNETCORE_HTTP_PORTS": "8080"
+      },
+      "publishAllPorts": true,
+      "useSSL": true
+    }
+  },
+  "$schema": "http://json.schemastore.org/launchsettings.json",
+  "iisSettings": {
+    "windowsAuthentication": false,
+    "anonymousAuthentication": true,
+    "iisExpress": {
+      "applicationUrl": "http://localhost:51451",
+      "sslPort": 44374
+    }
+  }
+}

+ 60 - 0
economy/Views/Component/Pagination.cshtml

@@ -0,0 +1,60 @@
+@using Microsoft.AspNetCore.WebUtilities
+@model economy.Models.Pagination
+
+@if (Model.TotalRows > 0)
+{
+<nav id="pagination" aria-label="Page navigation">
+    <ul class="pagination">
+
+        @if (Model.Page > Model.PageGroupSize)
+        {
+            <li class="page-item">
+                <a class="page-link" href="?page=@Model.PrevGroupPage&perPage=@Model.PerPage@Model.BuildQueryString()">이전</a>
+            </li>
+        }
+        else
+        {
+            <li class="page-item disabled">
+                <span class="page-link">이전</span>
+            </li>
+        }
+
+        <!-- 페이지 번호 표시 -->
+        @for (int i = Model.StartPage; i <= Model.EndPage; i++)
+        {
+            if (i == Model.Page)
+            {
+                <li class="page-item active">
+                    <span class="page-link">@i</span>
+                </li>
+            }
+            else
+            {
+                <li class="page-item">
+                        <a class="page-link" href="?page=@i&perPage=@Model.PerPage@Model.BuildQueryString()">@i</a>
+                </li>
+            }
+        }
+
+        @if (Model.HasNextPage && Model.NextGroupPage <= Model.TotalPage)
+        {
+            <li class="page-item">
+                    <a class="page-link" href="?page=@Model.NextGroupPage&perPage=@Model.PerPage@Model.BuildQueryString()">다음</a>
+            </li>
+        }
+        else
+        {
+            <li class="page-item disabled">
+                <span class="page-link">다음</span>
+            </li>
+        }
+    </ul>
+</nav>
+}
+
+@section Scripts {
+    <script src="~/js/price.js" asp-append-version="true"></script>
+}
+@section Styles {
+    <link href="~/css/price.css" rel="stylesheet" asp-append-version="true"/>
+}

+ 91 - 0
economy/Views/Day/Anniversary.cshtml

@@ -0,0 +1,91 @@
+@model economy.Models.View<economy.Models.Anniversary.Request, economy.Models.Anniversary.Response>
+
+@{
+    ViewData["Title"] = "기념일";
+}
+
+<div class="container">
+    <h3>기념일</h3>
+
+    @await Html.PartialAsync("NavTab")
+
+    <div class="row mt-3">
+        <div class="col align-self-center">
+            @((Model?.Response?.Body?.TotalCount ?? 0).ToString("N0")) 개
+        </div>
+        <div class="col-auto">
+            @{
+                var years = ViewData["years"] as IEnumerable<int> ?? Enumerable.Empty<int>();
+            }
+            <select name="year" id="year" class="form-select" form="fSearch">
+                @foreach (var year in years)
+                {
+                    if (year == Model.Request?.Year)
+                    {
+                        <option value="@year" selected="selected">@year</option>
+                    }
+                    else
+                    {
+                        <option value="@year">@year</option>
+                    }
+                }
+            </select>
+        </div>
+    </div>
+
+    <div class="table-responsive">
+        <table id="dayAnniversary" class="table table-bordered table-hover">
+            <caption class="caption-top">
+                
+            </caption>
+            <colgroup>
+                <col width="9%"/>
+                <col width="15%"/>
+                <col width="15%"/>
+                <col width="*"/>
+                <col width="20%"/>
+            </colgroup>
+            <thead>
+                <tr>
+                    <th>번호</th>
+                    <th>날짜</th>
+                    <th>종류</th>
+                    <th>명칭</th>
+                    <th>공공기간 휴일 여부</th>
+                </tr>
+            </thead>
+            <tbody>
+                @if (Model.Response.Body != null && Model.Response.Body?.TotalCount > 0)
+                {
+                    @foreach (var row in Model.Response.Body.Items.ItemList)
+                    {
+                        <tr>
+                            <td>@row.Num</td>
+                            <td>@row.LocDate</td>
+                            <td>@row.DateKind</td>
+                            <td>@row.DateName</td>
+                            <td>@row.IsHoliday</td>
+                        </tr>
+                    }
+                }
+                else
+                {
+                    <tr>
+                        <td colspan="5">
+                            No data.
+                        </td>
+                    </tr>
+                }
+            </tbody>
+        </table>
+    </div>
+
+    <form id="fSearch" method="get" accept-charset="UTF-8" rel="search" autocomplete="off" asp-controller="Day" asp-action="Anniversary"></form>
+</div>
+
+@section Scripts {
+    <script src="~/js/day.js" asp-append-version="true"></script>
+}
+@section Styles {
+    <link href="~/css/style.css" rel="stylesheet" asp-append-version="true" />
+}

+ 92 - 0
economy/Views/Day/Holiday.cshtml

@@ -0,0 +1,92 @@
+@model economy.Models.View<economy.Models.Holiday.Request, economy.Models.Holiday.Response>
+
+
+@{
+    ViewData["Title"] = "공휴일/특일";
+}
+
+<div class="container">
+    <h3>공휴일</h3>
+
+    @await Html.PartialAsync("NavTab")
+
+    <div class="row mt-3">
+        <div class="col align-self-center">
+            @((Model?.Response?.Body?.TotalCount ?? 0).ToString("N0")) 개
+        </div>
+        <div class="col-auto">
+            @{
+                var years = ViewData["years"] as IEnumerable<int> ?? Enumerable.Empty<int>();
+            }
+            <select name="year" id="year" class="form-select" form="fSearch">
+                @foreach (var year in years)
+                {
+                    if (year == Model.Request?.Year)
+                    {
+                        <option value="@year" selected="selected">@year</option>
+                    }
+                    else
+                    {
+                        <option value="@year">@year</option>
+                    }
+                }
+            </select>
+        </div>
+    </div>
+
+    <div class="table-responsive">
+        <table id="dayHoliday" class="table table-bordered table-hover">
+            <caption class="caption-top">
+                
+            </caption>
+            <colgroup>
+                <col width="9%"/>
+                <col width="15%"/>
+                <col width="15%"/>
+                <col width="*"/>
+                <col width="20%"/>
+            </colgroup>
+            <thead>
+                <tr>
+                    <th>번호</th>
+                    <th>날짜</th>
+                    <th>종류</th>
+                    <th>명칭</th>
+                    <th>공공기간 휴일 여부</th>
+                </tr>
+            </thead>
+            <tbody>
+                @if (Model.Response.Body != null && Model.Response.Body?.TotalCount > 0)
+                {
+                    @foreach (var row in Model.Response.Body.Items.ItemList)
+                    {
+                        <tr>
+                            <td>@row.Num</td>
+                            <td>@row.LocDate</td>
+                            <td>@row.DateKind</td>
+                            <td>@row.DateName</td>
+                            <td>@row.IsHoliday</td>
+                        </tr>
+                    }
+                }
+                else
+                {
+                    <tr>
+                        <td colspan="5">
+                            No data.
+                        </td>
+                    </tr>
+                }
+            </tbody>
+        </table>
+    </div>
+
+    <form id="fSearch" method="get" accept-charset="UTF-8" rel="search" autocomplete="off" asp-controller="Day" asp-action="Holiday"></form>
+</div>
+
+@section Scripts {
+    <script src="~/js/day.js" asp-append-version="true"></script>
+}
+@section Styles {
+    <link href="~/css/style.css" rel="stylesheet" asp-append-version="true" />
+}

+ 14 - 0
economy/Views/Day/NavTab.cshtml

@@ -0,0 +1,14 @@
+<ul class="nav nav-tabs mt-3">
+    <li class="nav-item">
+        <a class="nav-link @(ViewData["type"].ToString() == "Holiday" ? "active" : "")" asp-controller="Day" asp-action="Holiday" asp-route-year="@Model.Request.Year">공휴일</a>
+    </li>
+    <li class="nav-item">
+        <a class="nav-link @(ViewData["type"].ToString() == "Anniversary" ? "active" : "")" asp-controller="Day" asp-action="Anniversary" asp-route-year="@Model.Request.Year">기념일</a>
+    </li>
+    <li class="nav-item">
+        <a class="nav-link @(ViewData["type"].ToString() == "Seasonal" ? "active" : "")" asp-controller="Day" asp-action="Seasonal" asp-route-year="@Model.Request.Year">24절기</a>
+    </li>
+    <li class="nav-item">
+        <a class="nav-link @(ViewData["type"].ToString() == "Sundry" ? "active" : "")" asp-controller="Day" asp-action="Sundry" asp-route-year="@Model.Request.Year">잡절</a>
+    </li>
+</ul>

+ 88 - 0
economy/Views/Day/Seasonal.cshtml

@@ -0,0 +1,88 @@
+@model economy.Models.View<economy.Models.Seasonal.Request, economy.Models.Seasonal.Response>
+
+@{
+    ViewData["Title"] = "24절기";
+}
+
+<div class="container">
+    <h3>24절기</h3>
+
+    @await Html.PartialAsync("NavTab")
+
+    <div class="row mt-3">
+        <div class="col align-self-center">
+            @((Model?.Response?.Body?.TotalCount ?? 0).ToString("N0")) 개
+        </div>
+        <div class="col-auto">
+            @{
+                var years = ViewData["years"] as IEnumerable<int> ?? Enumerable.Empty<int>();
+            }
+            <select name="year" id="year" class="form-select" form="fSearch">
+                @foreach (var year in years)
+                {
+                    if (year == Model.Request?.Year)
+                    {
+                        <option value="@year" selected="selected">@year</option>
+                    }
+                    else
+                    {
+                        <option value="@year">@year</option>
+                    }
+                }
+            </select>
+        </div>
+    </div>
+
+    <div class="table-responsive">
+        <table id="daySeasonal" class="table table-bordered table-hover">
+            <caption class="caption-top">
+                
+            </caption>
+            <thead>
+                <tr>
+                    <th>번호</th>
+                    <th>날짜</th>
+                    <th>종류</th>
+                    <th>명칭</th>
+                    <th>공공기간 휴일 여부</th>
+                    <th>한국표준시간</th>
+                    <th>태양황경(도)</th>
+                </tr>
+            </thead>
+            <tbody>
+                @if (Model.Response.Body != null && Model.Response.Body?.TotalCount > 0)
+                {
+                    @foreach (var row in Model.Response.Body.Items.ItemList)
+                    {
+                        <tr>
+                            <td>@row.Num</td>
+                            <td>@row.LocDate</td>
+                            <td>@row.DateKind</td>
+                            <td>@row.DateName</td>
+                            <td>@row.IsHoliday</td>
+                            <td>@row.Kst</td>
+                            <td>@row.SunLongitude</td>
+                        </tr>
+                    }
+                }
+                else
+                {
+                    <tr>
+                        <td colspan="7">
+                            No data.
+                        </td>
+                    </tr>
+                }
+            </tbody>
+        </table>
+    </div>
+
+    <form id="fSearch" method="get" accept-charset="UTF-8" rel="search" autocomplete="off" asp-controller="Day" asp-action="Seasonal"></form>
+</div>
+
+@section Scripts {
+    <script src="~/js/day.js" asp-append-version="true"></script>
+}
+@section Styles {
+    <link href="~/css/style.css" rel="stylesheet" asp-append-version="true" />
+}

+ 88 - 0
economy/Views/Day/Sundry.cshtml

@@ -0,0 +1,88 @@
+@model economy.Models.View<economy.Models.Sundry.Request, economy.Models.Sundry.Response>
+
+@{
+    ViewData["Title"] = "잡절";
+}
+
+<div class="container">
+    <h3>잡절</h3>
+
+    @await Html.PartialAsync("NavTab")
+
+    <div class="row mt-3">
+        <div class="col align-self-center">
+            @((Model?.Response?.Body?.TotalCount ?? 0).ToString("N0")) 개
+        </div>
+        <div class="col-auto">
+            @{
+                var years = ViewData["years"] as IEnumerable<int> ?? Enumerable.Empty<int>();
+            }
+            <select name="year" id="year" class="form-select" form="fSearch">
+                @foreach (var year in years)
+                {
+                    if (year == Model.Request?.Year)
+                    {
+                        <option value="@year" selected="selected">@year</option>
+                    }
+                    else
+                    {
+                        <option value="@year">@year</option>
+                    }
+                }
+            </select>
+        </div>
+    </div>
+
+    <div class="table-responsive">
+        <table id="daySundry" class="table table-bordered table-hover">
+            <caption class="caption-top">
+                
+            </caption>
+            <thead>
+                <tr>
+                    <th>번호</th>
+                    <th>날짜</th>
+                    <th>종류</th>
+                    <th>명칭</th>
+                    <th>공공기간 휴일 여부</th>
+                    <th>한국표준시간</th>
+                    <th>태양황경(도)</th>
+                </tr>
+            </thead>
+            <tbody>
+                @if (Model.Response.Body != null && Model.Response.Body?.TotalCount > 0)
+                {
+                    @foreach (var row in Model.Response.Body.Items.ItemList)
+                    {
+                        <tr>
+                            <td>@row.Num</td>
+                            <td>@row.LocDate</td>
+                            <td>@row.DateKind</td>
+                            <td>@row.DateName</td>
+                            <td>@row.IsHoliday</td>
+                            <td>@row.Kst</td>
+                            <td>@row.SunLongitude</td>
+                        </tr>
+                    }
+                }
+                else
+                {
+                    <tr>
+                        <td colspan="7">
+                            No data.
+                        </td>
+                    </tr>
+                }
+            </tbody>
+        </table>
+    </div>
+
+    <form id="fSearch" method="get" accept-charset="UTF-8" rel="search" autocomplete="off" asp-controller="Day" asp-action="Sundry"></form>
+</div>
+
+@section Scripts {
+    <script src="~/js/day.js" asp-append-version="true"></script>
+}
+@section Styles {
+    <link href="~/css/style.css" rel="stylesheet" asp-append-version="true" />
+}

+ 110 - 0
economy/Views/Emission/Index.cshtml

@@ -0,0 +1,110 @@
+@model economy.Models.View<economy.Models.Emission.Request, economy.Models.Emission.Response>
+
+@{
+    ViewData["Title"] = "배출권 시세";
+}
+
+<div class="container">
+    <h3>배출권 시세</h3>
+
+    <div class="row mt-3">
+        <div class="col align-self-center">
+            @((Model?.Response?.Body?.TotalCount ?? 0).ToString("N0")) 개
+        </div>
+        <div class="col-auto">
+            <div class="row g-2">
+                <div class="col">
+                    <input type="date" name="sDate" id="sDate" class="form-control" value="@Model.Request.StartDate.ToString("yyyy-MM-dd")" form="fSearch" />
+                </div>
+                <div class="col">
+                    <input type="date" name="eDate" id="eDate" class="form-control" value="@Model.Request.EndDate.ToString("yyyy-MM-dd")" form="fSearch" />
+                </div>
+                <div class="col">
+                    <select name="perPage" id="perPage" class="form-select" asp-for="SelectedListPerPage" asp-items="Model.ListPerPage" form="fSearch"></select>
+                </div>
+            </div>
+        </div>
+    </div>
+
+    <div class="table-responsive">
+        <table id="emissionPriceInfo" class="table table-bordered table-hover">
+            <caption class="caption-top">
+                
+            </caption>
+            <thead>
+                <tr>
+                    <th>번호</th>
+                    <th>기준일자</th>
+                    <th>종목명</th>
+                    <th>종가</th>
+                    <th>대비</th>
+                    <th>등락률</th>
+                    <th>시가</th>
+                    <th>고가</th>
+                    <th>저가</th>
+                    <th>거래량</th>
+                    <th>거래대금</th>
+                </tr>
+            </thead>
+            <tbody>
+                @if (Model.Response.Body != null && Model.Response.Body?.TotalCount > 0)
+                {
+                    @foreach (var row in Model.Response.Body.Items.ItemList)
+                    {
+                        <tr>
+                            <td>@row.Num</td>
+                            <td>@row.BasDt</td>
+                            <td>
+                                <a asp-controller="Emission" asp-action="Index" asp-route-code="@row.SrtnCd" asp-route-sDate="@Model.Request.StartDate" asp-route-eDate="@Model.Request.EndDate">@row.ItmsNm</a>
+                                <span class="text-primary" data-bs-toggle="tooltip" data-bs-placement="top" data-bs-html="true" data-bs-title="코드: @row.SrtnCd<br/>ISIN: @row.IsinCd">
+                                    <i class="bi bi-exclamation-circle"></i>
+                                </span>
+                            </td>
+                            <td>@row.Clpr</td>
+                            <td>@row.Vs</td>
+                            <td>
+                                @{
+                                    double tmpNum;
+                                    bool isPlus = double.TryParse(row.FltRt, out tmpNum);
+                                }
+                                @if (isPlus && tmpNum > 0)
+                                {
+                                    <span class="text-danger">▲ @row.FltRt</span>
+                                } else{
+                                    <span class="text-primary">▼ @row.FltRt</span>
+                                }
+                            </td>
+                            <td>@row.Mkp</td>
+                            <td>@row.Hipr</td>
+                            <td>@row.Lopr</td>
+                            <td>@row.Trqu</td>
+                            <td>@row.TrPrc</td>
+                        </tr>
+                    }
+                }
+                else
+                {
+                    <tr>
+                        <td colspan="11">
+                            No data.
+                        </td>
+                    </tr>
+                }
+            </tbody>
+        </table>
+    </div>
+
+    <form id="fSearch" method="get" accept-charset="UTF-8" rel="search" autocomplete="off" asp-controller="Emission" asp-action="Index">
+        <input type="hidden" name="page" value="@Model.Request.PageNo" />
+    </form>
+
+    @await Html.PartialAsync("~/Views/Component/Pagination.cshtml", Model.Pagination)
+
+</div>
+
+@section Scripts {
+    <script src="~/js/emission.js" asp-append-version="true"></script>
+}
+@section Styles {
+    <link href="~/css/style.css" rel="stylesheet" asp-append-version="true" />
+}

+ 124 - 0
economy/Views/FIFA/Index.cshtml

@@ -0,0 +1,124 @@
+@model economy.Models.View<economy.Models.FIFA.Request, economy.Models.FIFA.Response>
+
+@{
+    ViewData["Title"] = "FIFA 순위";
+}
+
+<div class="container">
+    <h3>FIFA 순위</h3>
+
+    <div class="row g-2">
+        <div class="col-auto col-form-label">
+            <div class="form-check form-check-inline">
+                <input class="form-check-input" type="radio" name="gender" id="gender_1" value="1" @(Model.Request.Gender == 1 ? "checked" : "") form="fSearch" />
+                <label class="form-check-label" for="gender_1">남자<label>
+            </div>
+            <div class="form-check form-check-inline">
+                <input class="form-check-input" type="radio" name="gender" id="gender_2" value="2" @(Model.Request.Gender == 2 ? "checked" : "") form="fSearch" />
+                <label class="form-check-label" for="gender_2">여자<label>
+            </div>
+        </div>
+    </div>
+
+    <br/>
+
+    <ul class="nav nav-tabs">
+        <li class="nav-item">
+            <a asp-controller="FIFA" asp-action="Index" asp-route-type="" asp-route-gender="@Model.Request.Gender" asp-route-perPage="@Model.Request.NumOfRows" class="nav-link @(Model.Request.Type == economy.Models.FIFA.Request.Types.ALL || Model.Request.Type == null ? "active" : "")">ALL</a>
+        </li>
+        <li class="nav-item">
+            <a asp-controller="FIFA" asp-action="Index" asp-route-type="@economy.Models.FIFA.Request.Types.AFC" asp-route-gender="@Model.Request.Gender"  asp-route-perPage="@Model.Request.NumOfRows" class="nav-link @(Model.Request.Type == economy.Models.FIFA.Request.Types.AFC ? "active" : "")">AFC</a>
+        </li>
+        <li class="nav-item">
+            <a asp-controller="FIFA" asp-action="Index" asp-route-type="@economy.Models.FIFA.Request.Types.CAF" asp-route-gender="@Model.Request.Gender" asp-route-perPage="@Model.Request.NumOfRows"  class="nav-link @(Model.Request.Type == economy.Models.FIFA.Request.Types.CAF ? "active" : "")">CAF</a>
+        </li>
+        <li class="nav-item">
+            <a asp-controller="FIFA" asp-action="Index" asp-route-type="@economy.Models.FIFA.Request.Types.CONCACAF" asp-route-gender="@Model.Request.Gender" asp-route-perPage="@Model.Request.NumOfRows"  class="nav-link @(Model.Request.Type == economy.Models.FIFA.Request.Types.CONCACAF ? "active" : "")">CONCACAF</a>
+        </li>
+        <li class="nav-item">
+            <a asp-controller="FIFA" asp-action="Index" asp-route-type="@economy.Models.FIFA.Request.Types.CONMEBOL" asp-route-gender="@Model.Request.Gender" asp-route-perPage="@Model.Request.NumOfRows"  class="nav-link @(Model.Request.Type == economy.Models.FIFA.Request.Types.CONMEBOL ? "active" : "")">CONMEBOL</a>
+        </li>
+        <li class="nav-item">
+            <a asp-controller="FIFA" asp-action="Index" asp-route-type="@economy.Models.FIFA.Request.Types.OFC" asp-route-gender="@Model.Request.Gender"  asp-route-perPage="@Model.Request.NumOfRows" class="nav-link @(Model.Request.Type == economy.Models.FIFA.Request.Types.OFC ? "active" : "")">OFC</a>
+        </li>
+        <li class="nav-item">
+            <a asp-controller="FIFA" asp-action="Index" asp-route-type="@economy.Models.FIFA.Request.Types.UEFA" asp-route-gender="@Model.Request.Gender"  asp-route-perPage="@Model.Request.NumOfRows" class="nav-link @(Model.Request.Type == economy.Models.FIFA.Request.Types.UEFA ? "active" : "")">UEFA</a>
+        </li>
+    </ul>
+
+    <br/>
+    
+    <div id="fifaRanks" class="table-responsive">
+        <table class="table table-hover table-bordered">
+            <caption class="caption-top">
+                <div class="row mb-1">
+                    <div class="col align-self-center">
+                        총 국가 : @Model.Response.Results.Count()
+                    </div>
+                    <div class="col-auto text-end">
+                        <select name="perPage" id="perPage" class="form-select" asp-for="SelectedListPerPage" asp-items="Model.FIFAListPerPage" form="fSearch"></select>
+                    </div>
+                </div>
+            </caption>
+            <thead>
+                <tr>
+                    <th>순위</th>
+                    <th>국가</th>
+                    <th>전적</th>
+                    <th>국명</th>
+                    <th>총점</th>
+                    <th>이전 순위</th>
+                    <th>변동</th>
+                </tr>
+            </thead>
+            <tbody>
+                @if (@Model?.Response?.Results is not null && @Model.Response.Results.Count() > 0)
+                {
+                    @foreach (var row in @Model.Response.Results)
+                    {
+                    var diff = Math.Abs((row.Rank ?? 0) - (row.PrevRank ?? 0));
+                    <tr>
+                        <td>@row.Rank</td>
+                        <td>
+                            <img src="@row.FlagSrc" alt="@row.IdCountry" class="img-fluid"/>
+                        </td>
+                        <td>@row.Matches</td>
+                        <td>@row.TeamName[0].Description</td>
+                        <td>@row.TotalPoints</td>
+                        <td>@row.PrevRank</td>
+                        <td>
+                            @if (diff > 0)
+                            {
+                                <i class="bi bi-caret-up-fill text-danger"></i>
+                            }
+                            else
+                            {
+                                <i class="bi bi-caret-down-fill text-primary"></i>
+                            }
+                            @diff
+                        </td>
+                    </tr>
+                    }
+                } else {
+                <tr>
+                    <td colspan="7">
+                        No data.
+                    </td>
+                </tr>
+                }
+            </tbody>
+        </table>
+    </div>
+
+    <form id="fSearch" method="get" accept-charset="UTF-8" rel="search" autocomplete="off" asp-controller="FIFA" asp-action="Index">
+        <input type="hidden" name="type" value="@Model.Request.Type" />
+    </form>
+
+</div>
+
+@section Scripts {
+    <script src="~/js/fifa.js" asp-append-version="true"></script>
+}
+@section Styles {
+    <link href="~/css/style.css" rel="stylesheet" asp-append-version="true" />
+}

+ 89 - 0
economy/Views/Financial/Exchange.cshtml

@@ -0,0 +1,89 @@
+@model economy.Models.View<economy.Models.Exchange.Request, economy.Models.Exchange.Response>
+
+@{
+    ViewData["Title"] = "환율";
+}
+
+<div class="container">
+    <h3>환율</h3>
+
+    <div class="row mt-3">
+        <div class="col align-self-center">
+            @(Model?.Response?.Items?.Count().ToString("N0")) 개
+        </div>
+        <div class="col-auto">
+            <input type="date" name="date" id="date" class="form-control" value="@Model.Request.DateFormatted" form="fSearch" />
+        </div>
+    </div>
+
+    <div class="table-responsive">
+        <table id="financialExchange" class="table table-bordered table-hover">
+            <caption class="caption-top">
+                한국수출입은행이 제공하는 환율정보입니다.
+            </caption>
+            <colgroup>
+                <col width="10%"/>
+                <col width="10%"/>
+                <col width="10%"/>
+                <col width="10%"/>
+                <col width="10%"/>
+                <col width="10%"/>
+                <col width="10%"/>
+                <col width="10%"/>
+                <col width="10%"/>
+                <col width="10%"/>
+            </colgroup>
+            <thead>
+                <tr>
+                    <th>통화 단위</th>
+                    <th>국가/통화명</th>
+                    <th>매입 환율</th>
+                    <th>매도 환율</th>
+                    <th>기준 환율</th>
+                    <th>은행 판매율</th>
+                    <th>년환가료율</th>
+                    <th>10일환가료율</th>
+                    <th>서울외국환중개 매매기준율</th>
+                    <th>서울외국환중개 장부가격</th>
+                </tr>
+            </thead>
+            <tbody>
+                @if (Model?.Response?.Items != null && Model?.Response?.Items.Count() > 0)
+                {
+                    @foreach (var row in Model.Response.Items)
+                    {
+                        <tr>
+                            <td>@row.CurUnit</td>
+                            <td>@row.CurNm</td>
+                            <td>@row.Ttb</td>
+                            <td>@row.Tts</td>
+                            <td>@row.DealBasRate</td>
+                            <td>@row.BankSellingRate</td>
+                            <td>@row.YearlyExchangeFeeRate</td>
+                            <td>@row.TenDayExchangeFeeRate</td>
+                            <td>@row.KftcBankSellingRate</td>
+                            <td>@row.KftcDealBaseRate</td>
+                        </tr>
+                    }
+                }
+                else
+                {
+                    <tr>
+                        <td colspan="10">
+                            No data.
+                        </td>
+                    </tr>
+                }
+            </tbody>
+        </table>
+    </div>
+
+    <form id="fSearch" method="get" accept-charset="UTF-8" rel="search" autocomplete="off" asp-controller="Financial" asp-action="Exchange"></form>
+</div>
+
+@section Scripts {
+    <script src="~/js/financial.js" asp-append-version="true"></script>
+}
+@section Styles {
+    <link href="~/css/style.css" rel="stylesheet" asp-append-version="true" />
+}

+ 62 - 0
economy/Views/Financial/Interest.cshtml

@@ -0,0 +1,62 @@
+@model economy.Models.View<economy.Models.Interest.Request, economy.Models.Interest.Response>
+
+@{
+    ViewData["Title"] = "대출 금리";
+}
+
+<div class="container">
+    <h3>대출 금리</h3>
+
+    @await Html.PartialAsync("NavTab")
+
+    <div class="row mt-3 mb-3">
+        <div class="col-auto">
+            <input type="date" name="date" id="date" class="form-control" value="@Model.Request.DateFormatted" form="fSearch" />
+        </div>
+    </div>
+
+    <p>
+        한국수출입은행이 제공하는 대출 금리 정보입니다.
+        각 대출기간에 대한 고정기준금리를 일 기준으로 제공합니다.
+    </p>
+
+    <div class="table-responsive">
+        <table id="financialInterest" class="table table-bordered table-hover w-auto">
+            <thead>
+                <tr>
+                    <th>대출 기간</th>
+                    <th>고정기준 금리</th>
+                </tr>
+            </thead>
+            <tbody>
+                @if (Model.Response?.Items != null && Model.Response?.Items.Count() > 0)
+                {
+                    @foreach (var row in Model.Response.Items)
+                    {
+                        <tr>
+                            <td>@row.InterestName</td>
+                            <td>@row.InterestRate</td>
+                        </tr>
+                    }
+                }
+                else
+                {
+                    <tr>
+                        <td colspan="2">
+                            No data.
+                        </td>
+                    </tr>
+                }
+            </tbody>
+        </table>
+    </div>
+
+    <form id="fSearch" method="get" accept-charset="UTF-8" rel="search" autocomplete="off" asp-controller="Financial" asp-action="Interest"></form>
+</div>
+
+@section Scripts {
+    <script src="~/js/financial.js" asp-append-version="true"></script>
+}
+@section Styles {
+    <link href="~/css/style.css" rel="stylesheet" asp-append-version="true" />
+}

+ 332 - 0
economy/Views/Financial/International.cshtml

@@ -0,0 +1,332 @@
+@model economy.Models.View<economy.Models.International.Request, economy.Models.International.Response>
+
+@{
+    ViewData["Title"] = "국제 금리";
+}
+
+<div class="container">
+    <h3>국제 금리</h3>
+
+    @await Html.PartialAsync("NavTab")
+
+    <div class="row mt-3 mb-3">
+        <div class="col-auto">
+            <input type="date" name="date" id="date" class="form-control" value="@Model.Request.DateFormatted" form="fSearch" />
+        </div>
+    </div>
+
+
+    <p>
+        한국수출입은행이 제공하는 국제 금리 정보입니다.
+        각 대출기간에 대한 고정기준금리를 일 기준으로 제공합니다.
+    </p>
+
+    <div id="financialInternational" class="row row-cols-sm-1 row-cols-md-2 row-cols-lg-3 row-cols-xl-4">
+        <div class="col">
+            <table class="table table-bordered table-hover">
+                <thead>
+                    <tr>
+                        <th>통화</th>
+                        <th>기준</th>
+                        <th>금리</th>
+                    </tr>
+                </thead>
+                <tbody>
+                        @if (Model.Response?.SofrList != null && Model.Response?.SofrList.Count() > 0)
+                    {
+                        @foreach (var row in Model.Response.SofrList)
+                        {
+                            <tr>
+                                <td>@row.CurFund</td>
+                                <td>@row.InterestName</td>
+                                <td>@row.InterestRate</td>
+                            </tr>
+                        }
+                    }
+                    else
+                    {
+                        <tr>
+                            <td colspan="3">
+                                No data.
+                            </td>
+                        </tr>
+                    }
+                </tbody>
+            </table>
+        </div>
+
+        <div class="col">
+            <table class="table table-bordered table-hover">
+                <thead>
+                    <tr>
+                        <th>통화</th>
+                        <th>기준</th>
+                        <th>금리</th>
+                    </tr>
+                </thead>
+                <tbody>
+                        @if (Model.Response?.EstrList != null && Model.Response?.EstrList.Count() > 0)
+                    {
+                        @foreach (var row in Model.Response.EstrList)
+                        {
+                            <tr>
+                                <td>@row.CurFund</td>
+                                <td>@row.InterestName</td>
+                                <td>@row.InterestRate</td>
+                            </tr>
+                        }
+                    }
+                    else
+                    {
+                        <tr>
+                            <td colspan="3">
+                                No data.
+                            </td>
+                        </tr>
+                    }
+                </tbody>
+            </table>
+        </div>
+
+        <div class="col">
+            <table class="table table-bordered table-hover">
+                <thead>
+                    <tr>
+                        <th>통화</th>
+                        <th>기준</th>
+                        <th>금리</th>
+                    </tr>
+                </thead>
+                <tbody>
+                        @if (Model.Response?.EuriborList != null && Model.Response?.EuriborList.Count() > 0)
+                    {
+                        @foreach (var row in Model.Response.EuriborList)
+                        {
+                            <tr>
+                                <td>@row.CurFund</td>
+                                <td>@row.InterestName</td>
+                                <td>@row.InterestRate</td>
+                            </tr>
+                        }
+                    }
+                    else
+                    {
+                        <tr>
+                            <td colspan="3">
+                                No data.
+                            </td>
+                        </tr>
+                    }
+                </tbody>
+            </table>
+        </div>
+
+        <div class="col">
+            <table class="table table-bordered table-hover">
+                <thead>
+                    <tr>
+                        <th>통화</th>
+                        <th>기준</th>
+                        <th>금리</th>
+                    </tr>
+                </thead>
+                <tbody>
+                    @if (Model.Response?.TonaList != null && Model.Response?.TonaList.Count() > 0)
+                    {
+                        @foreach (var row in Model.Response.TonaList)
+                        {
+                            <tr>
+                                <td>@row.CurFund</td>
+                                <td>@row.InterestName</td>
+                                <td>@row.InterestRate</td>
+                            </tr>
+                        }
+                    }
+                    else
+                    {
+                        <tr>
+                            <td colspan="3">
+                                No data.
+                            </td>
+                        </tr>
+                    }
+                </tbody>
+            </table>
+        </div>
+
+        <div class="col">
+            <table class="table table-bordered table-hover">
+                <thead>
+                    <tr>
+                        <th>통화</th>
+                        <th>기준</th>
+                        <th>금리</th>
+                    </tr>
+                </thead>
+                <tbody>
+                    @if (Model.Response?.TiborList != null && Model.Response?.TiborList.Count() > 0)
+                    {
+                        @foreach (var row in Model.Response.TiborList)
+                        {
+                            <tr>
+                                <td>@row.CurFund</td>
+                                <td>@row.InterestName</td>
+                                <td>@row.InterestRate</td>
+                            </tr>
+                        }
+                    }
+                    else
+                    {
+                        <tr>
+                            <td colspan="3">
+                                No data.
+                            </td>
+                        </tr>
+                    }
+                </tbody>
+            </table>
+        </div>
+
+        <div class="col">
+            <table class="table table-bordered table-hover">
+                <thead>
+                    <tr>
+                        <th>통화</th>
+                        <th>기준</th>
+                        <th>금리</th>
+                    </tr>
+                </thead>
+                <tbody>
+                    @if (Model.Response?.SwapRfrList != null && Model.Response?.SwapRfrList.Count() > 0)
+                    {
+                        @foreach (var row in Model.Response.SwapRfrList)
+                        {
+                            <tr>
+                                <td>@row.CurFund</td>
+                                <td>@row.InterestName</td>
+                                <td>@row.InterestRate</td>
+                            </tr>
+                        }
+                    }
+                    else
+                    {
+                        <tr>
+                            <td colspan="3">
+                                No data.
+                            </td>
+                        </tr>
+                    }
+                </tbody>
+            </table>
+        </div>
+
+        <div class="col">
+            <table class="table table-bordered table-hover">
+                <thead>
+                    <tr>
+                        <th>통화</th>
+                        <th>기준</th>
+                        <th>금리</th>
+                    </tr>
+                </thead>
+                <tbody>
+                    @if (Model.Response?.LiborList != null && Model.Response?.LiborList.Count() > 0)
+                    {
+                        @foreach (var row in Model.Response.LiborList)
+                        {
+                            <tr>
+                                <td>@row.CurFund</td>
+                                <td>@row.InterestName</td>
+                                <td>@row.InterestRate</td>
+                            </tr>
+                        }
+                    }
+                    else
+                    {
+                        <tr>
+                            <td colspan="3">
+                                No data.
+                            </td>
+                        </tr>
+                    }
+                </tbody>
+            </table>
+        </div>
+
+        <div class="col">
+            <table class="table table-bordered table-hover">
+                <thead>
+                    <tr>
+                        <th>통화</th>
+                        <th>기준</th>
+                        <th>금리</th>
+                    </tr>
+                </thead>
+                <tbody>
+                    @if (Model.Response?.SwapList != null && Model.Response?.SwapList.Count() > 0)
+                    {
+                        @foreach (var row in Model.Response.SwapList)
+                        {
+                            <tr>
+                                <td>@row.CurFund</td>
+                                <td>@row.InterestName</td>
+                                <td>@row.InterestRate</td>
+                            </tr>
+                        }
+                    }
+                    else
+                    {
+                        <tr>
+                            <td colspan="3">
+                                No data.
+                            </td>
+                        </tr>
+                    }
+                </tbody>
+            </table>
+        </div>
+
+        <div class="col">
+            <table class="table table-bordered table-hover">
+                <thead>
+                    <tr>
+                        <th>통화</th>
+                        <th>기준</th>
+                        <th>금리</th>
+                    </tr>
+                </thead>
+                <tbody>
+                    @if (Model.Response?.CirrList != null && Model.Response?.CirrList.Count() > 0)
+                    {
+                        @foreach (var row in Model.Response.CirrList)
+                        {
+                            <tr>
+                                <td>@row.CurFund</td>
+                                <td>@row.InterestName</td>
+                                <td>@row.InterestRate</td>
+                            </tr>
+                        }
+                    }
+                    else
+                    {
+                        <tr>
+                            <td colspan="3">
+                                No data.
+                            </td>
+                        </tr>
+                    }
+                </tbody>
+            </table>
+        </div>
+    </div>
+    
+
+    <form id="fSearch" method="get" accept-charset="UTF-8" rel="search" autocomplete="off" asp-controller="Financial" asp-action="International"></form>
+</div>
+
+@section Scripts {
+    <script src="~/js/financial.js" asp-append-version="true"></script>
+}
+@section Styles {
+    <link href="~/css/style.css" rel="stylesheet" asp-append-version="true" />
+}

+ 8 - 0
economy/Views/Financial/NavTab.cshtml

@@ -0,0 +1,8 @@
+<ul class="nav nav-tabs mt-3">
+    <li class="nav-item">
+        <a class="nav-link @(ViewData["type"].ToString() == "Interest" ? "active" : "")" asp-controller="Financial" asp-action="Interest" asp-route-date="@Model.Request.Date">대출 금리</a>
+    </li>
+    <li class="nav-item">
+        <a class="nav-link @(ViewData["type"].ToString() == "International" ? "active" : "")" asp-controller="Financial" asp-action="International" asp-route-date="@Model.Request.Date">국제 금리</a>
+    </li>
+</ul>

+ 103 - 0
economy/Views/Flower/Index.cshtml

@@ -0,0 +1,103 @@
+@model economy.Models.View<economy.Models.Flower.Request, economy.Models.Flower.Response>
+
+@{
+    ViewData["Title"] = "화훼 시세";
+}
+
+<div class="container">
+    <h3>화훼 시세</h3>
+
+    <div class="row g-2 mt-3">
+        <div class="col-sm col-md-auto">
+            <input type="search" name="pName" id="pName" class="form-control" value="@Model.Request.PumName" form="fSearch" placeholder="품목명" />
+        </div>
+        <div class="col-sm col-md-auto">
+            <input type="search" name="gName" id="gName" class="form-control" value="@Model.Request.GoodName" form="fSearch" placeholder="품종명" />
+        </div>
+        <div class="col col-md-auto text-center text-md-start">
+            <button type="submit" class="btn btn-secondary" form="fSearch">검색</button>
+        </div>
+    </div>
+
+    <div class="row mt-3">
+        <div class="col align-self-center">
+            @((Model?.Response?.NumberOfRows ?? 0).ToString("N0")) 개
+        </div>
+        <div class="col-auto">
+            <div class="row g-2">
+                <div class="col">
+                    <input type="date" name="date" id="date" class="form-control" value="@Model.Request.BaseDate.ToString("yyyy-MM-dd")" form="fSearch" />
+                </div>
+                <div class="col">
+                    <select name="perPage" id="perPage" class="form-select" asp-for="SelectedListPerPage" asp-items="Model.ListPerPage" form="fSearch"></select>
+                </div>
+            </div>
+        </div>
+    </div>
+
+    <div class="table-responsive">
+        <table id="flowerPrice" class="table table-bordered table-hover">
+            <caption class="caption-top">
+                총금액 단위:원 / 총물량 단위:속
+            </caption>
+            <thead>
+                <tr>
+                    <th>번호</th>
+                    <th>기준일자</th>
+                    <th>부류</th>
+                    <th>품목명</th>
+                    <th>품종명</th>
+                    <th>등급명</th>
+                    <th>최고가</th>
+                    <th>최저가</th>
+                    <th>평균가</th>
+                    <th>총금액</th>
+                    <th>총물량</th>
+                </tr>
+            </thead>
+            <tbody>
+                @if (Model.Response.Items != null && Model.Response?.NumberOfRows > 0)
+                {
+                    @foreach (var row in Model.Response.Items)
+                    {
+                        <tr>
+                            <td>@row.Num</td>
+                            <td>@row.SaleDate</td>
+                            <td>@row.FlowerGubn</td>
+                            <td>@row.PumName</td>
+                            <td>@row.GoodName</td>
+                            <td>@row.LvNm</td>
+                            <td>@row.MaxAmt</td>
+                            <td>@row.MinAmt</td>
+                            <td>@row.AvgAmt</td>
+                            <td>@row.TotAmt</td>
+                            <td>@row.TotQty</td>
+                        </tr>
+                    }
+                }
+                else
+                {
+                    <tr>
+                        <td colspan="11">
+                            No data.
+                        </td>
+                    </tr>
+                }
+            </tbody>
+        </table>
+    </div>
+
+    <form id="fSearch" method="get" accept-charset="UTF-8" rel="search" autocomplete="off" asp-controller="Flower" asp-action="Index">
+        <input type="hidden" name="page" value="@Model.Request.PageNo" />
+    </form>
+
+    @await Html.PartialAsync("~/Views/Component/Pagination.cshtml", Model.Pagination)
+
+</div>
+
+@section Scripts {
+    <script src="~/js/flower.js" asp-append-version="true"></script>
+}
+@section Styles {
+    <link href="~/css/style.css" rel="stylesheet" asp-append-version="true" />
+}

+ 110 - 0
economy/Views/Gold/Index.cshtml

@@ -0,0 +1,110 @@
+@model economy.Models.View<economy.Models.Gold.Request, economy.Models.Gold.Response>
+
+@{
+    ViewData["Title"] = "금 시세";
+}
+
+<div class="container">
+    <h3>금 시세</h3>
+
+    <div class="row mt-3">
+        <div class="col align-self-center">
+            @((Model?.Response?.Body?.TotalCount ?? 0).ToString("N0")) 개
+        </div>
+        <div class="col-auto">
+            <div class="row g-2">
+                <div class="col">
+                    <input type="date" name="sDate" id="sDate" class="form-control" value="@Model.Request.StartDate.ToString("yyyy-MM-dd")" form="fSearch" />
+                </div>
+                <div class="col">
+                    <input type="date" name="eDate" id="eDate" class="form-control" value="@Model.Request.EndDate.ToString("yyyy-MM-dd")" form="fSearch" />
+                </div>
+                <div class="col">
+                    <select name="perPage" id="perPage" class="form-select" asp-for="SelectedListPerPage" asp-items="Model.ListPerPage" form="fSearch"></select>
+                </div>
+            </div>
+        </div>
+    </div>
+
+    <div class="table-responsive">
+        <table id="goldPriceInfo" class="table table-bordered table-hover">
+            <caption class="caption-top">
+                
+            </caption>
+            <thead>
+                <tr>
+                    <th>번호</th>
+                    <th>기준일자</th>
+                    <th>종목명</th>
+                    <th>종가</th>
+                    <th>대비</th>
+                    <th>등락률</th>
+                    <th>시가</th>
+                    <th>고가</th>
+                    <th>저가</th>
+                    <th>거래량</th>
+                    <th>거래대금</th>
+                </tr>
+            </thead>
+            <tbody>
+                @if (Model.Response.Body != null && Model.Response.Body?.TotalCount > 0)
+                {
+                    @foreach (var row in Model.Response.Body.Items.ItemList)
+                    {
+                        <tr>
+                            <td>@row.Num</td>
+                            <td>@row.BasDt</td>
+                            <td>
+                                <a asp-controller="Gold" asp-action="Index" asp-route-code="@row.SrtnCd" asp-route-sDate="@Model.Request.StartDate" asp-route-eDate="@Model.Request.EndDate">@row.ItmsNm</a>
+                                <span class="text-primary" data-bs-toggle="tooltip" data-bs-placement="top" data-bs-html="true" data-bs-title="코드: @row.SrtnCd<br/>ISIN: @row.IsinCd">
+                                    <i class="bi bi-exclamation-circle"></i>
+                                </span>
+                            </td>
+                            <td>@row.Clpr</td>
+                            <td>@row.Vs</td>
+                            <td>
+                                @{
+                                    double tmpNum;
+                                    bool isPlus = double.TryParse(row.FltRt, out tmpNum);
+                                }
+                                @if (isPlus && tmpNum > 0)
+                                {
+                                    <span class="text-danger">▲ @row.FltRt</span>
+                                } else{
+                                    <span class="text-primary">▼ @row.FltRt</span>
+                                }
+                            </td>
+                            <td>@row.Mkp</td>
+                            <td>@row.Hipr</td>
+                            <td>@row.Lopr</td>
+                            <td>@row.Trqu</td>
+                            <td>@row.TrPrc</td>
+                        </tr>
+                    }
+                }
+                else
+                {
+                    <tr>
+                        <td colspan="11">
+                            No data.
+                        </td>
+                    </tr>
+                }
+            </tbody>
+        </table>
+    </div>
+
+    <form id="fSearch" method="get" accept-charset="UTF-8" rel="search" autocomplete="off" asp-controller="Gold" asp-action="Index">
+        <input type="hidden" name="page" value="@Model.Request.PageNo" />
+    </form>
+
+    @await Html.PartialAsync("~/Views/Component/Pagination.cshtml", Model.Pagination)
+
+</div>
+
+@section Scripts {
+    <script src="~/js/gold.js" asp-append-version="true"></script>
+}
+@section Styles {
+    <link href="~/css/style.css" rel="stylesheet" asp-append-version="true" />
+}

+ 8 - 0
economy/Views/Home/Index.cshtml

@@ -0,0 +1,8 @@
+@{
+    ViewData["Title"] = "Home Page";
+}
+
+<div class="text-center">
+    <h1 class="display-4">Welcome</h1>
+    <p>Learn about <a href="https://learn.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
+</div>

+ 6 - 0
economy/Views/Home/Privacy.cshtml

@@ -0,0 +1,6 @@
+@{
+    ViewData["Title"] = "Privacy Policy";
+}
+<h1>@ViewData["Title"]</h1>
+
+<p>Use this page to detail your site's privacy policy.</p>

+ 94 - 0
economy/Views/Lotto/Index.cshtml

@@ -0,0 +1,94 @@
+@model economy.Models.View<economy.Models.Lotto.Request, economy.Models.Lotto.Response>
+
+@{
+    ViewData["Title"] = "로또 회차별 당첨번호";
+
+    var result = Model.Response;
+}
+
+<div class="container">
+    <h3>로또 회차별 당첨번호</h3>
+
+    <dl>
+        <dd>동행복권에서 제공하는 로또 당첨번호입니다.</dd>
+    </dl>
+
+    <div class="row g-2">
+        <label for="date" class="col-auto col-form-label">날짜</label>
+        <div class="col-auto">
+            @{
+                int last = Convert.ToInt32(ViewBag.Last); // 명시적 형변환
+                int selectedNumber = (int)Model.Request.Number;
+            }
+            <select name="number" id="number" class="form-select" form="fSearch">
+                @for (int number = last; number >= 1; number--)
+                {
+                    @if (number == selectedNumber)
+                    {
+                        <option value="@number" selected>@number 회차</option>
+                    }
+                    else
+                    {
+                        <option value="@number">@number 회차</option>
+                    }
+                }
+            </select>
+        </div>
+        <div class="col-auto">
+            <button type="submit" class="btn btn-dark" form="fSearch">검색</button>
+        </div>
+    </div>
+
+    <br/>
+    <div class="table-responsive">
+        <table class="table table-hover table-bordered table-nowrap">
+            <caption class="caption-top">
+                <h5>@ViewBag.Last 회 당첨번호</h5>
+                (@result.DrawDate 추첨)
+            </caption>
+            <thead>
+                <tr class="text-center">
+                    <th colspan="7">당첨번호</th>
+                    <th>보너스</th>
+                </tr>
+            </thead>
+            <tbody>
+                <tr class="text-center">
+                    <td>@result.DrawnNumber1</td>
+                    <td>@result.DrawnNumber2</td>
+                    <td>@result.DrawnNumber3</td>
+                    <td>@result.DrawnNumber4</td>
+                    <td>@result.DrawnNumber5</td>
+                    <td>@result.DrawnNumber6</td>
+                    <td>+</td>
+                    <td>@result.BonusNumber</td>
+                </tr>
+            </tbody>
+            <tfoot>
+                <tr>
+                    <td colspan="4" class="text-center">1등 총 당첨금액</td>
+                    <td colspan="4" class="text-end">@result.FirstAccumulatedAmount.ToString("N0") 원</td>
+                </tr>
+                <tr>
+                    <td colspan="4" class="text-center">1 게임당 당첨금액</td>
+                    <td colspan="4" class="text-end">@result.FirstWinAmount.ToString("N0") 원</td>
+                </tr>
+                <tr>
+                    <td colspan="4" class="text-center">당첨 인원</td>
+                    <td colspan="4" class="text-end">@result.FirstPrizeWinnerCount 명</td>
+                </tr>
+                <tr>
+                    <td colspan="4" class="text-center">총 판매금액</td>
+                    <td colspan="4" class="text-end">@result.TotalSellAmount.ToString("N0") 원</td>
+                </tr>
+            </tfoot>
+        </table>
+        당첨금 지급기한 : 지급개시일로부터 1년 (휴일인 경우 익영업일)
+    </div>
+
+    <form id="fSearch" method="get" accept-charset="UTF-8" rel="search" autocomplete="off" asp-controller="Lotto" asp-action="Index"></form>
+</div>
+
+@section Styles {
+    <link href="~/css/style.css" rel="stylesheet" asp-append-version="true" />
+}

+ 86 - 0
economy/Views/Oil/Index.cshtml

@@ -0,0 +1,86 @@
+@model economy.Models.View<economy.Models.Oil.Request, economy.Models.Oil.Response>
+
+@{
+    ViewData["Title"] = "석유 시세";
+}
+
+<div class="container">
+    <h3>석유 시세</h3>
+
+    <div class="row mt-3">
+        <div class="col align-self-center">
+            @((Model?.Response?.Body?.TotalCount ?? 0).ToString("N0")) 개
+        </div>
+        <div class="col-auto">
+            <div class="row g-2">
+                <div class="col">
+                    <input type="date" name="sDate" id="sDate" class="form-control" value="@Model.Request.StartDate.ToString("yyyy-MM-dd")" form="fSearch" />
+                </div>
+                <div class="col">
+                    <input type="date" name="eDate" id="eDate" class="form-control" value="@Model.Request.EndDate.ToString("yyyy-MM-dd")" form="fSearch" />
+                </div>
+                <div class="col">
+                    <select name="perPage" id="perPage" class="form-select" asp-for="SelectedListPerPage" asp-items="Model.ListPerPage" form="fSearch"></select>
+                </div>
+            </div>
+        </div>
+    </div>
+
+    <div class="table-responsive">
+        <table id="oilPriceInfo" class="table table-bordered table-hover">
+            <caption class="caption-top">
+                
+            </caption>
+            <thead>
+                <tr>
+                    <th>번호</th>
+                    <th>기준일자</th>
+                    <th>유종구분</th>
+                    <th>가중평균가격 경쟁</th>
+                    <th>가중평균가격 협의</th>
+                    <th>거래량</th>
+                    <th>거래대금</th>
+                </tr>
+            </thead>
+            <tbody>
+                @if (Model.Response.Body != null && Model.Response.Body?.TotalCount > 0)
+                {
+                    @foreach (var row in Model.Response.Body.Items.ItemList)
+                    {
+                        <tr>
+                            <td>@row.Num</td>
+                            <td>@row.BasDt</td>
+                            <td>@row.OilCtg</td>
+                            <td>@row.WtAvgPrcCptn</td>
+                            <td>@row.WtAvgPrcDisc</td>
+                            <td>@row.Trqu</td>
+                            <td>@row.TrPrc</td>
+                        </tr>
+                    }
+                }
+                else
+                {
+                    <tr>
+                        <td colspan="11">
+                            No data.
+                        </td>
+                    </tr>
+                }
+            </tbody>
+        </table>
+    </div>
+
+    <form id="fSearch" method="get" accept-charset="UTF-8" rel="search" autocomplete="off" asp-controller="Oil" asp-action="Index">
+        <input type="hidden" name="page" value="@Model.Request.PageNo" />
+    </form>
+
+    @await Html.PartialAsync("~/Views/Component/Pagination.cshtml", Model.Pagination)
+
+</div>
+
+@section Scripts {
+    <script src="~/js/oil.js" asp-append-version="true"></script>
+}
+@section Styles {
+    <link href="~/css/style.css" rel="stylesheet" asp-append-version="true" />
+}

+ 102 - 0
economy/Views/Price/Detail.cshtml

@@ -0,0 +1,102 @@
+@model economy.Models.View<economy.Models.Product.Detail.Request, economy.Models.Product.Detail.Response>
+
+@{
+    ViewData["Title"] = "품목 가격 정보";
+}
+
+<div class="container">
+    <h3>품목 가격 정보</h3>
+
+    <p class="mb-0">
+        @Model.Response.Body?.In | <small>@Model.Response.Body?.Ic</small>
+    </p>
+
+      <div class="row mt-3">
+        <div class="col align-self-center">
+            @((Model?.Response?.Body?.TotalCount ?? 0).ToString("N0")) 개
+        </div>
+        <div class="col-auto">
+            <div class="row g-2">
+                <div class="col">
+                    <input type="date" name="sDate" id="sDate" class="form-control" value="@Model.Request.StartDate" form="fSearch" />
+                </div>
+                <div class="col">
+                    <input type="date" name="eDate" id="eDate" class="form-control" value="@Model.Request.EndDate" form="fSearch" />
+                </div>
+                <div class="col">
+                    <select name="perPage" id="perPage" class="form-select" asp-for="SelectedListPerPage" asp-items="Model.ListPerPage" form="fSearch"></select>
+                </div>
+            </div>
+        </div>
+    </div>
+
+    <div class="table-responsive">
+        <table id="priceItemInfo" class="table table-bordered table-hover">
+            <caption class="caption-top">
+                
+            </caption>
+            @if (Model.Response.Body != null)
+            {
+                <colgroup>
+                    <col width="7%"/>
+                    <col width="10%"/>
+                    <col width="*"/>
+                    <col width="8%"/>
+                    <col width="8%"/>
+                    <col width="8%"/>
+                    <col width="10%"/>
+                </colgroup>
+            }
+            <thead>
+                <tr>
+                    <th>번호</th>
+                    <th>상품 ID</th>
+                    <th>상품 명</th>
+                    <th>판매가격</th>
+                    <th>할인가격</th>
+                    <th>혜택가격</th>
+                    <th>가격일자</th>
+                </tr>
+            </thead>
+            <tbody>
+                @if (Model.Response.Body != null)
+                {
+                    @foreach (var row in Model.Response.Body.Items.ItemList)
+                    {
+                        <tr>
+                            <td>@row.Num</td>
+                            <td>@row.Pi</td>
+                            <td class="text-start">@row.Pn</td>
+                            <td>@row.Sp.ToString("N0")</td>
+                            <td>@row.Dp.ToString("N0")</td>
+                            <td>@row.Bp</td>
+                            <td>@row.Sd</td>
+                        </tr>
+                    }
+                }
+                else
+                {
+                    <tr>
+                        <td colspan="7">
+                            No data.
+                        </td>
+                    </tr>   
+                }
+            </tbody>
+        </table>
+    </div>
+
+    <form id="fSearch" method="get" accept-charset="UTF-8" rel="search" autocomplete="off" asp-controller="Price" asp-action="Detail">
+        <input type="hidden" name="page" value="@Model.Request.PageNo"/>
+    </form>
+
+    @await Html.PartialAsync("~/Views/Component/Pagination.cshtml", Model.Pagination)
+
+</div>
+
+@section Scripts {
+    <script src="~/js/price.js" asp-append-version="true"></script>
+}
+@section Styles {
+    <link href="~/css/style.css" rel="stylesheet" asp-append-version="true"/>
+}

+ 78 - 0
economy/Views/Price/List.cshtml

@@ -0,0 +1,78 @@
+@model economy.Models.View<economy.Models.Product.List.Request, economy.Models.Product.List.Response>
+
+@{
+    ViewData["Title"] = "품목별 시세";
+}
+
+<div class="container">
+    <h3>품목별 시세</h3>
+
+     <div class="row mt-3">
+        <div class="col align-self-center">
+            @((Model?.Response?.Body?.TotalCount ?? 0).ToString("N0")) 개
+        </div>
+        <div class="col-auto">
+            <select name="perPage" id="perPage" class="form-select" asp-for="SelectedListPerPage" asp-items="Model.ListPerPage" form="fSearch"></select>
+        </div>
+    </div>
+
+    <div class="table-responsive">
+        <table id="priceItemList" class="table table-bordered table-hover">
+            <caption class="caption-top">
+               
+            </caption>
+            <colgroup>
+                <col width="10%"/>
+                <col width="30%"/>
+                <col width="*"/>
+                <col width="20%"/>
+            </colgroup>
+            <thead>
+                <tr>
+                    <th>번호</th>
+                    <th>품목코드</th>
+                    <th>품목명</th>
+                    <th>비고</th>
+                </tr>
+            </thead>
+            <tbody>
+                @if (Model.Response.Body != null)
+                {
+                    @foreach (var row in Model.Response.Body.Items.ItemList)
+                    {
+                        <tr>
+                            <td>@row.Rn</td>
+                            <td>@row.Ic</td>
+                            <td>@row.In</td>
+                            <td>
+                                @Html.ActionLink("[가격 정보]", "Detail", "Price", new {id=@row.Ic})
+                            </td>
+                        </tr>
+                    }
+                }
+                else
+                {
+                    <tr>
+                        <td colspan="4">
+                            No data.
+                        </td>
+                    </tr>   
+                }
+            </tbody>
+        </table>
+    </div>
+
+    <form id="fSearch" method="get" accept-charset="UTF-8" rel="search" autocomplete="off" asp-controller="Price" asp-action="List">
+        <input type="hidden" name="page" value="@Model.Request.PageNo" />
+    </form>
+
+    @await Html.PartialAsync("~/Views/Component/Pagination.cshtml", Model.Pagination)
+
+</div>
+
+@section Scripts {
+    <script src="~/js/price.js" asp-append-version="true"></script>
+}
+@section Styles {
+    <link href="~/css/style.css" rel="stylesheet" asp-append-version="true" />
+}

+ 117 - 0
economy/Views/SCSS/style.scss

@@ -0,0 +1,117 @@
+header {
+    nav {
+        > div {
+            > div {
+                cursor: grab;
+                overflow-x: auto;
+                overflow-y: hidden;
+                white-space: nowrap;
+                -webkit-overflow-scrolling: touch;
+                -ms-overflow-style: none;
+                scrollbar-width: none;
+
+                ul {
+                    li {
+                        display: inline-block;
+
+                        a {
+                            user-select: none; /* 텍스트 선택 방지 */
+                            -webkit-user-drag: none; /* 드래그 방지 (웹킷 브라우저용) */
+                            -moz-user-select: none; /* 파이어폭스용 */
+                            -ms-user-select: none; /* IE/엣지용 */
+                        }
+
+                        &:hover {
+                            a {
+                                color: chocolate !important;
+                                text-decoration: underline;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+
+#priceItemList, #priceItemInfo, #goldPriceInfo, #oilPriceInfo, #emissionPriceInfo, #flowerPrice, #dayHoliday, #dayAnniversary, #daySeasonal, #daySundry, #financialExchange {
+    min-width: 740px;
+
+    thead {
+        tr {
+            th {
+                text-align: center;
+                background-color: #eee;
+                vertical-align: middle;
+            }
+        }
+    }
+
+    tbody {
+        tr {
+            td {
+                text-align: center;
+                vertical-align: middle;
+
+                span[data-bs-toggle="tooltip"] {
+                    color: blue;
+                    cursor: pointer;
+                    font-weight: bold;
+                }
+            }
+        }
+    }
+}
+
+#financialInterest, #fifaRanks {
+    thead {
+        tr {
+            th {
+                text-align: center;
+                background-color: #eee;
+                vertical-align: middle;
+            }
+        }
+    }
+
+    tbody {
+        tr {
+            td {
+                text-align: center;
+                vertical-align: middle;
+
+                img {
+                    width: 60%;
+                    max-width: 140px;
+                }
+            }
+        }
+    }
+}
+
+#financialInternational {
+    table {
+        thead {
+            tr {
+                th {
+                    text-align: center;
+                    background-color: #eee;
+                    vertical-align: middle;
+                }
+            }
+        }
+
+        tbody {
+            tr {
+                td {
+                    text-align: center;
+                    vertical-align: middle;
+                }
+            }
+        }
+    }
+}
+
+#pagination {
+    justify-items: center;
+}

+ 25 - 0
economy/Views/Shared/Error.cshtml

@@ -0,0 +1,25 @@
+@model ErrorViewModel
+@{
+    ViewData["Title"] = "Error";
+}
+
+<h1 class="text-danger">Error.</h1>
+<h2 class="text-danger">An error occurred while processing your request.</h2>
+
+@if (Model.ShowRequestId)
+{
+    <p>
+        <strong>Request ID:</strong> <code>@Model.RequestId</code>
+    </p>
+}
+
+<h3>Development Mode</h3>
+<p>
+    Swapping to <strong>Development</strong> environment will display more detailed information about the error that occurred.
+</p>
+<p>
+    <strong>The Development environment shouldn't be enabled for deployed applications.</strong>
+    It can result in displaying sensitive information from exceptions to end users.
+    For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
+    and restarting the app.
+</p>

+ 87 - 0
economy/Views/Shared/_Layout.cshtml

@@ -0,0 +1,87 @@
+<!DOCTYPE html>
+<html lang="ko">
+<head>
+    <meta charset="utf-8" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+    <title>@ViewData["Title"] - Economy</title>
+    
+    <meta http-equiv="refresh" content="30"/>
+    <meta name="description" content="다양한 국제 시세 정보를 확인할 수 있습니다."/>
+    <meta name="keywords" content="각종 주요 광물, 상품 시세 정보"/>
+    <meta name="author" content="김진오"/>
+
+    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
+    <link rel="stylesheet" href="~/css/bundle.css" asp-append-version="true" />
+    <link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
+    <link rel="stylesheet" href="~/css/style.css" asp-append-version="true" />
+    <link rel="stylesheet" href="~/economy.styles.css" asp-append-version="true" />
+
+    @await RenderSectionAsync("Styles", required: false)
+</head>
+<body>
+
+    <header>
+        <nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
+            <div class="container">
+                <a class="navbar-brand" asp-area="" asp-controller="Home" asp-action="Index">Economy API</a>
+                <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
+                    <span class="navbar-toggler-icon"></span>
+                </button>
+                <div id="navMenu" class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
+                    <ul class="navbar-nav flex-grow-1 justify-content-center">
+                        <li class="nav-item">
+                            <a class="nav-link text-dark" asp-area="" asp-controller="Gold" asp-action="Index">금시세</a>
+                        </li>
+                        <li class="nav-item">
+                            <a class="nav-link text-dark" asp-area="" asp-controller="Oil" asp-action="Index">석유시세</a>
+                        </li>
+                        <li class="nav-item">
+                            <a class="nav-link text-dark" asp-area="" asp-controller="Emission" asp-action="Index">배출권시세</a>
+                        </li>
+                        <li class="nav-item">
+                            <a class="nav-link text-dark" asp-area="" asp-controller="Price" asp-action="List">품목별 시세</a>
+                        </li>
+                        <li class="nav-item">
+                            <a class="nav-link text-dark" asp-area="" asp-controller="Flower" asp-action="Index">화훼시세</a>
+                        </li>
+                        <li class="nav-item">
+                            <a class="nav-link text-dark" asp-area="" asp-controller="Day" asp-action="Holiday">공휴일/특일</a>
+                        </li>
+                        <li class="nav-item">
+                            <a class="nav-link text-dark" asp-area="" asp-controller="Financial" asp-action="Exchange">환율</a>
+                        </li>
+                        <li class="nav-item">
+                            <a class="nav-link text-dark" asp-area="" asp-controller="Financial" asp-action="Interest">금리</a>
+                        </li>
+                        <li class="nav-item">
+                            <a class="nav-link text-dark" asp-area="" asp-controller="FIFA" asp-action="Index">FIFA 순위</a>
+                        </li>
+                        <li class="nav-item">
+                            <a class="nav-link text-dark" asp-area="" asp-controller="Lotto" asp-action="Index">로또 당첨결과</a>
+                        </li>
+                    </ul>
+                </div>
+            </div>
+        </nav>
+    </header>
+
+    <div class="container">
+        <main role="main" class="pb-3">
+            @RenderBody()
+        </main>
+    </div>
+
+    <footer class="border-top footer text-muted">
+        <div class="container">
+            &copy; 2023 ~ @DateTime.Now.Year - www.web.or.kr - <a href="https://web.or.kr" ref="author" target="_blank">Goto</a>
+        </div>
+    </footer>
+
+    <script src="~/lib/jquery/dist/jquery.min.js"></script>
+    <script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
+    <script src="~/js/site.js" asp-append-version="true"></script>
+
+    @await RenderSectionAsync("Scripts", required: false)
+
+</body>
+</html>

+ 48 - 0
economy/Views/Shared/_Layout.cshtml.css

@@ -0,0 +1,48 @@
+/* Please see documentation at https://learn.microsoft.com/aspnet/core/client-side/bundling-and-minification
+for details on configuring this project to bundle and minify static web assets. */
+
+a.navbar-brand {
+  white-space: normal;
+  text-align: center;
+  word-break: break-all;
+}
+
+a {
+  color: #0077cc;
+}
+
+.btn-primary {
+  color: #fff;
+  background-color: #1b6ec2;
+  border-color: #1861ac;
+}
+
+.nav-pills .nav-link.active, .nav-pills .show > .nav-link {
+  color: #fff;
+  background-color: #1b6ec2;ㅐ
+  border-color: #1861ac;
+}
+
+.border-top {
+  border-top: 1px solid #e5e5e5;
+}
+.border-bottom {
+  border-bottom: 1px solid #e5e5e5;
+}
+
+.box-shadow {
+  box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .05);
+}
+
+button.accept-policy {
+  font-size: 1rem;
+  line-height: inherit;
+}
+
+.footer {
+  position: absolute;
+  bottom: 0;
+  width: 100%;
+  white-space: nowrap;
+  line-height: 60px;
+}

+ 2 - 0
economy/Views/Shared/_ValidationScriptsPartial.cshtml

@@ -0,0 +1,2 @@
+<script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script>
+<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script>

+ 3 - 0
economy/Views/_ViewImports.cshtml

@@ -0,0 +1,3 @@
+@using economy
+@using economy.Models
+@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

+ 3 - 0
economy/Views/_ViewStart.cshtml

@@ -0,0 +1,3 @@
+@{
+    Layout = "_Layout";
+}

+ 8 - 0
economy/appsettings.Development.json

@@ -0,0 +1,8 @@
+{
+  "Logging": {
+    "LogLevel": {
+      "Default": "Information",
+      "Microsoft.AspNetCore": "Warning"
+    }
+  }
+}

+ 9 - 0
economy/appsettings.json

@@ -0,0 +1,9 @@
+{
+  "Logging": {
+    "LogLevel": {
+      "Default": "Information",
+      "Microsoft.AspNetCore": "Warning"
+    }
+  },
+  "AllowedHosts": "*"
+}

BIN
economy/bin/Debug/net8.0/Microsoft.Data.SqlClient.dll


BIN
economy/bin/Debug/net8.0/Microsoft.EntityFrameworkCore.Abstractions.dll


BIN
economy/bin/Debug/net8.0/Microsoft.EntityFrameworkCore.Relational.dll


BIN
economy/bin/Debug/net8.0/Microsoft.EntityFrameworkCore.SqlServer.dll


Some files were not shown because too many files changed in this diff