C# mes ํ”„๋กœ์ ํŠธ ๋ฐฑ์—”๋“œ ๊ตฌํ˜„ ์ •๋ฆฌ ( -1- )

2024. 7. 5. 19:55ใ†Development๐Ÿ‘ฉ๐Ÿป‍๐Ÿฆณ/C#

Team4-master (1).zip
0.51MB
WorkInstructionAPI.zip
0.01MB

ํ”„๋กœ์ ํŠธ ๋งˆ์ง€๋ง‰ ๊ตฌํ˜„ ์‚ฌํ•ญ์ด๋ฉฐ API ์—ฐ๋™ ๋ฐ ๋ฐ์ดํ„ฐ ๋ฒ ์ด์Šค ์—ฐ๋™์€ 

com์„ ์ด์šฉํ•œ ํ†ต์‹ ์šฐํšŒ ๋ฐฉ๋ฒ•์„ ์“ฐ๊ธฐ ์ด์ „ ๋ฒ„์ „์—์„œ ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค

์‹ค์ œ API ์—ฐ๋™ ํ…Œ์ŠคํŠธ ์ดํ›„ ์„ฑ๊ณตํ•œ ํ…Œ์ŠคํŠธ ํ™”๋ฉด์ž…๋‹ˆ๋‹ค.


1. ํ”„๋กœ์ ํŠธ ๊ฐœ์š” ๋ฐ ์ดˆ๊ธฐ ์„ค์ •

์•ˆ๋…•ํ•˜์„ธ์š”! ์˜ค๋Š˜์€ ์ œ๊ฐ€ ์ตœ๊ทผ ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๋ฉด์„œ ๊ฒช์—ˆ๋˜ ๋ฐฑ์—”๋“œ ๊ด€๋ จ ๊ตฌํ˜„ ์‚ฌํ•ญ๊ณผ ์—๋Ÿฌ ํ•ด๊ฒฐ ๊ณผ์ •์„ ๊ณต์œ ํ•˜๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฒˆ ํฌ์ŠคํŒ…์€ ์‹œ๋ฆฌ์ฆˆ ์ค‘ ์ฒซ ๋ฒˆ์งธ๋กœ, ํ”„๋กœ์ ํŠธ ๊ฐœ์š”์™€ ์ดˆ๊ธฐ ์„ค์ •์— ๋Œ€ํ•ด ๋‹ค๋ฃฐ ์˜ˆ์ •์ž…๋‹ˆ๋‹ค. ์ด ๊ณผ์ •์—์„œ ๋ฐœ์ƒํ•œ ๋ฌธ์ œ๋“ค๊ณผ ๊ทธ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์„ ๋‹จ๊ณ„๋ณ„๋กœ ์„ค๋ช…ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

ํ”„๋กœ์ ํŠธ ๊ฐœ์š”: ์ด๋ฒˆ ํ”„๋กœ์ ํŠธ๋Š” ์ž‘์—… ์ง€์‹œ์„œ ๊ด€๋ฆฌ ์‹œ์Šคํ…œ์„ ๊ตฌ์ถ•ํ•˜๋Š” ๊ฒƒ์ด ๋ชฉํ‘œ์˜€์Šต๋‹ˆ๋‹ค. ์ด ์‹œ์Šคํ…œ์€ ๋‹ค์–‘ํ•œ ์ž‘์—… ์ง€์‹œ์„œ๋ฅผ ์ƒ์„ฑ, ์กฐํšŒ, ์ˆ˜์ •, ์‚ญ์ œํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ๋ฐฑ์—”๋“œ๋Š” ASP.NET Core์™€ Entity Framework Core๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€ ์ƒํ˜ธ ์ž‘์šฉํ•˜๋„๋ก ์„ค๊ณ„ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋Š” MySQL์„ ์‚ฌ์šฉํ•˜์˜€๊ณ , ์ฃผ์š” ํ…Œ์ด๋ธ”์€ **WorkInstruction**์ด๋ผ๋Š” ์ž‘์—… ์ง€์‹œ์„œ๋ฅผ ์ €์žฅํ•˜๋Š” ํ…Œ์ด๋ธ”์ด์—ˆ์Šต๋‹ˆ๋‹ค.

์ดˆ๊ธฐ ์„ค์ •

1. ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ ๋ฐ ๊ธฐ๋ณธ ๊ตฌ์กฐ ์„ค์ •: ์ฒซ ๋‹จ๊ณ„๋กœ, ASP.NET Core ํ”„๋กœ์ ํŠธ๋ฅผ ์ƒ์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด ๊ณผ์ •์—์„œ ๊ธฐ๋ณธ์ ์ธ ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ๋ฅผ ์„ค์ •ํ•˜๊ณ , ํ•„์š”ํ•œ ํŒจํ‚ค์ง€๋ฅผ ์„ค์น˜ํ–ˆ์Šต๋‹ˆ๋‹ค.

bash์ฝ”๋“œ ๋ณต์‚ฌ
dotnet new webapi -n WorkManagementSystem
cd WorkManagementSystem
dotnet add package Microsoft.EntityFrameworkCore
dotnet add package Pomelo.EntityFrameworkCore.MySql
dotnet add package Microsoft.EntityFrameworkCore.Tools
dotnet add package Microsoft.Extensions.Logging

์ดํ›„, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€์˜ ์—ฐ๊ฒฐ์„ ์„ค์ •ํ•˜๊ธฐ ์œ„ํ•ด appsettings.json ํŒŒ์ผ์— MySQL ์—ฐ๊ฒฐ ๋ฌธ์ž์—ด์„ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค.

json์ฝ”๋“œ ๋ณต์‚ฌ
{
  "ConnectionStrings": {
    "DefaultConnection": "Server=localhost;Database=plc_data_model1;User=root;Password=Alejd1785!;"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}

2. ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ปจํ…์ŠคํŠธ ์„ค์ •: ๋‹ค์Œ์œผ๋กœ, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ปจํ…์ŠคํŠธ ํด๋ž˜์Šค๋ฅผ ์ƒ์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค. AppDbContext ํด๋ž˜์Šค๋Š” Entity Framework Core์—์„œ DbContext๋ฅผ ์ƒ์†๋ฐ›์•„ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€์˜ ์ƒํ˜ธ์ž‘์šฉ์„ ๋‹ด๋‹นํ•ฉ๋‹ˆ๋‹ค.

csharp์ฝ”๋“œ ๋ณต์‚ฌ
using Microsoft.EntityFrameworkCore;
using WindowsFormsApp1Core.WorkManagementSystem.WMModels;

namespace WindowsFormsApp1Core.SQLModels
{
    public class AppDbContext : DbContext
    {
        public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }

        public DbSet<WorkInstruction> WorkInstructions { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            if (!optionsBuilder.IsConfigured)
            {
                string connectionString = "Server=localhost;Database=plc_data_model1;User=root;Password=Alejd1785!;";
                optionsBuilder.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString));
            }
        }
    }
}

3. WorkInstruction ๋ชจ๋ธ ์ •์˜:WorkInstruction ๋ชจ๋ธ์€ ์ž‘์—… ์ง€์‹œ์„œ์˜ ๊ตฌ์กฐ๋ฅผ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค. ์ด ํด๋ž˜์Šค๋Š” ์ž‘์—… ์ง€์‹œ์„œ์˜ ๋‹ค์–‘ํ•œ ์†์„ฑ์„ ํฌํ•จํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

csharp์ฝ”๋“œ ๋ณต์‚ฌ
using System;
using System.ComponentModel.DataAnnotations;

namespace WindowsFormsApp1Core.WorkManagementSystem.WMModels
{
    public class WorkInstruction
    {
        [Key]
        public string TaskName { get; set; }
        public string Content { get; set; }
        public DateTime Date { get; set; }
        public string Writer { get; set; }
        public string Priority { get; set; }
        public bool IsCompleted { get; set; }
    }
}

4. DI ์„ค์ • ๋ฐ ์„œ๋น„์Šค ๋“ฑ๋ก: ์ด์ œ Program.cs ํŒŒ์ผ์—์„œ **AppDbContext**์™€ **WorkInstructionService**๋ฅผ DI ์ปจํ…Œ์ด๋„ˆ์— ๋“ฑ๋กํ•˜์—ฌ, ํ”„๋กœ์ ํŠธ ์ „์ฒด์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

csharp์ฝ”๋“œ ๋ณต์‚ฌ
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.EntityFrameworkCore;
using WindowsFormsApp1Core.SQLModels;
using WindowsFormsApp1Core.WorkManagementSystem.WMSrvs;
using Microsoft.Extensions.Logging;

public static class Program
{
    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.ConfigureServices((context, services) =>
                {
                    services.AddDbContext<AppDbContext>(options =>
                        options.UseMySql(
                            context.Configuration.GetConnectionString("DefaultConnection"),
                            ServerVersion.AutoDetect(context.Configuration.GetConnectionString("DefaultConnection"))
                        ));
                    services.AddScoped<WorkInstructionService>();
                    services.AddControllersWithViews();
                });
                webBuilder.Configure((context, app) =>
                {
                    if (context.HostingEnvironment.IsDevelopment())
                    {
                        app.UseDeveloperExceptionPage();
                    }
                    else
                    {
                        app.UseExceptionHandler("/Home/Error");
                        app.UseHsts();
                    }
                    app.UseHttpsRedirection();
                    app.UseStaticFiles();
                    app.UseRouting();
                    app.UseAuthorization();
                    app.UseEndpoints(endpoints =>
                    {
                        endpoints.MapControllers();
                    });
                });
            });
}

5. ์„œ๋น„์Šค ํด๋ž˜์Šค ๊ตฌํ˜„:WorkInstructionService ํด๋ž˜์Šค๋Š” ์‹ค์ œ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค. ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ์ž‘์—… ์ง€์‹œ์„œ๋ฅผ ์ƒ์„ฑ, ์กฐํšŒ, ์ˆ˜์ •, ์‚ญ์ œํ•˜๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ํฌํ•จํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

csharp์ฝ”๋“œ ๋ณต์‚ฌ
using System.Collections.Generic;
using System.Threading.Tasks;
using WindowsFormsApp1Core.SQLModels;
using WindowsFormsApp1Core.WorkManagementSystem.WMModels;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;

namespace WindowsFormsApp1Core.WorkManagementSystem.WMSrvs
{
    public class WorkInstructionService
    {
        private readonly AppDbContext _context;
        private readonly ILogger<WorkInstructionService> _logger;

        public WorkInstructionService(AppDbContext context, ILogger<WorkInstructionService> logger)
        {
            _context = context;
            _logger = logger;
        }

        public async Task<IEnumerable<WorkInstruction>> GetWorkInstructionsAsync()
        {
            try
            {
                return await _context.WorkInstructions.ToListAsync();
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error fetching work instructions");
                throw;
            }
        }

        public async Task AddWorkInstructionAsync(WorkInstruction workInstruction)
        {
            try
            {
                _context.WorkInstructions.Add(workInstruction);
                await _context.SaveChangesAsync();
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error adding work instruction");
                throw;
            }
        }

        public async Task UpdateWorkInstructionAsync(WorkInstruction workInstruction)
        {
            try
            {
                var existing = await _context.WorkInstructions.FirstOrDefaultAsync(w => w.TaskName == workInstruction.TaskName);
                if (existing != null)
                {
                    existing.Content = workInstruction.Content;
                    existing.Date = workInstruction.Date;
                    existing.Writer = workInstruction.Writer;
                    existing.Priority = workInstruction.Priority;
                    existing.IsCompleted = workInstruction.IsCompleted;
                    await _context.SaveChangesAsync();
                }
                else
                {
                    _logger.LogWarning("Work instruction not found: {TaskName}", workInstruction.TaskName);
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error updating work instruction");
                throw;
            }
        }

        public async Task DeleteWorkInstructionAsync(string taskName)
        {
            try
            {
                var workInstruction = await _context.WorkInstructions.FirstOrDefaultAsync(w => w.TaskName == taskName);
                if (workInstruction != null)
                {
                    _context.WorkInstructions.Remove(workInstruction);
                    await _context.SaveChangesAsync();
                }
                else
                {
                    _logger.LogWarning("Work instruction not found: {TaskName}", taskName);
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error deleting work instruction");
                throw;
            }
        }

        public async Task<WorkInstruction> GetWorkInstructionByTaskNameAsync(string taskName)
        {
            try
            {
                return await _context.WorkInstructions.FirstOrDefaultAsync(w => w.TaskName == taskName);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error fetching work instruction by task name");
                throw;
            }
        }
    }
}

6. ์ปจํŠธ๋กค๋Ÿฌ ๊ตฌํ˜„: ๋งˆ์ง€๋ง‰์œผ๋กœ, WorkInstructionController ํด๋ž˜์Šค๋ฅผ ์ž‘์„ฑํ•˜์—ฌ ํด๋ผ์ด์–ธํŠธ ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ์ด ์ปจํŠธ๋กค๋Ÿฌ๋Š” RESTful API๋ฅผ ์ œ๊ณตํ•˜์—ฌ ์ž‘์—… ์ง€์‹œ์„œ๋ฅผ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

csharp์ฝ”๋“œ ๋ณต์‚ฌ
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Threading.Tasks;
using WindowsFormsApp1Core.WorkManagementSystem.WMModels;
using WindowsFormsApp1Core.WorkManagementSystem.WMSrvs;
using Microsoft.Extensions.Logging;

namespace WindowsFormsApp1Core.Dashboard.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    public class WorkInstructionController : ControllerBase
    {
        private readonly WorkInstructionService _service;
        private readonly ILogger<WorkInstructionController> _logger;

        public WorkInstructionController(AppDbContext context, ILogger<WorkInstructionController> logger)
        {
            _service = new WorkInstructionService(context, logger);
            _logger = logger;
        }

        [HttpGet]
        public async Task<ActionResult<IEnumerable<WorkInstruction>>> Get()
        {
            try
            {
                var workInstructions = await _service.GetWorkInstructionsAsync();
                return Ok(workInstructions);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error fetching work instructions");
                return StatusCode(500, "Internal server error");
            }
        }

        [HttpPost]
        public async Task<ActionResult<WorkInstruction>> Post(WorkInstruction workInstruction)
        {
            try
            {
                await _service.AddWorkInstructionAsync(workInstruction);
                return CreatedAtAction(nameof(Get), new { id = workInstruction.TaskName }, workInstruction);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error adding work instruction");
                return StatusCode(500, "Internal server error");
            }
        }

        [HttpPut("{taskName}")]
        public async Task<IActionResult> Put(string taskName, WorkInstruction workInstruction)
        {
            try
            {
                var existingWorkInstruction = await _service.GetWorkInstructionByTaskNameAsync(taskName);
                if (existingWorkInstruction == null)
                {
                    return NotFound();
                }

                await _service.UpdateWorkInstructionAsync(workInstruction);
                return NoContent();
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error updating work instruction");
                return StatusCode(500, "Internal server error");
            }
        }

        [HttpDelete("{taskName}")]
        public async Task<IActionResult> Delete(string taskName)
        {
            try
            {
                var workInstruction = await _service.GetWorkInstructionByTaskNameAsync(taskName);
                if (workInstruction == null)
                {
                    return NotFound();
                }

                await _service.DeleteWorkInstructionAsync(taskName);
                return NoContent();
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error deleting work instruction");
                return StatusCode(500, "Internal server error");
            }
        }
    }
}

๊ฒฐ๋ก :

์ด์™€ ๊ฐ™์ด ํ”„๋กœ์ ํŠธ์˜ ์ดˆ๊ธฐ ์„ค์ •์„ ์™„๋ฃŒํ–ˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ ํฌ์ŠคํŒ…์—์„œ๋Š” ๋ณธ๊ฒฉ์ ์œผ๋กœ ๋ฐœ์ƒํ•œ ์˜ค๋ฅ˜์™€ ๊ทธ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์„ ๋‹ค๋ฃจ๊ฒ ์Šต๋‹ˆ๋‹ค. ์ด ๊ณผ์ •์—์„œ Entity Framework Core์™€ ๊ด€๋ จ๋œ ๋‹ค์–‘ํ•œ ๋ฌธ์ œ์™€ ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๊ณผ์ •์„ ํ†ตํ•ด ์—ฌ๋Ÿฌ๋ถ„๋„ ๋น„์Šทํ•œ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋„๋ก ๋•๊ฒ ์Šต๋‹ˆ๋‹ค.

 


2. ์—๋Ÿฌ ๋ถ„์„ ๋ฐ ํ•ด๊ฒฐ (API)

ํ”„๋กœ์ ํŠธ ์ง„ํ–‰ ์ค‘์— ๋งˆ์ฃผํ•œ API ๊ด€๋ จ ์˜ค๋ฅ˜๋ฅผ ๋ถ„์„ํ•˜๊ณ  ํ•ด๊ฒฐํ•˜๋Š” ๊ณผ์ •์„ ๊ณต์œ ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ํ”„๋กœ์ ํŠธ ์ดˆ๊ธฐ ์„ค์ • ์ดํ›„, ์‹ค์ œ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•˜๋ฉด์„œ ๋‹ค์–‘ํ•œ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋Š”๋ฐ, ์ด ๊ณผ์ •์—์„œ ๋ฐฐ์šฐ๊ณ  ํ•ด๊ฒฐํ•œ ๋‚ด์šฉ์„ ๋‹จ๊ณ„๋ณ„๋กœ ์ •๋ฆฌํ•˜์˜€์Šต๋‹ˆ๋‹ค.

์—๋Ÿฌ ๋ถ„์„

1. CS1061 ์˜ค๋ฅ˜:

์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€: 'WorkInstructionService' does not contain a definition for 'DeleteWorkInstruction' and no accessible extension method 'DeleteWorkInstruction' accepting a first argument of type 'WorkInstructionService' could be found.

์›์ธ ๋ถ„์„:

  • WorkInstructionService ํด๋ž˜์Šค์— DeleteWorkInstruction, GetWorkInstructions, AddWorkInstruction ๋“ฑ์˜ ๋ฉ”์„œ๋“œ ์ •์˜๊ฐ€ ์—†๋‹ค๋Š” ๋ฉ”์‹œ์ง€์ž…๋‹ˆ๋‹ค.
  • AppDbContext ํด๋ž˜์Šค์— WorkInstructions ์†์„ฑ์ด ์—†๋‹ค๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

2. CS7036 ์˜ค๋ฅ˜:

์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€: There is no argument given that corresponds to the required formal parameter 'context' of 'WorkInstructionService.WorkInstructionService(AppDbContext)'.

์›์ธ ๋ถ„์„:

  • **WorkInstructionService**์˜ ์ƒ์„ฑ์ž๊ฐ€ **context**๋ผ๋Š” AppDbContext ํƒ€์ž…์˜ ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ํ•„์š”๋กœ ํ•˜์ง€๋งŒ, ์ด๋ฅผ ์ œ๊ณตํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.

์˜ค๋ฅ˜ ํ•ด๊ฒฐ ๋‹จ๊ณ„

๋‹จ๊ณ„ 1: ํด๋ž˜์Šค ์ •์˜ ๋ฐ ๋ฉ”์„œ๋“œ ํ™•์ธ

  • WorkInstructionService ํด๋ž˜์Šค์— ๋ชจ๋“  ํ•„์š”ํ•œ ๋ฉ”์„œ๋“œ(DeleteWorkInstruction, GetWorkInstructions, AddWorkInstruction ๋“ฑ)๊ฐ€ ์ •์˜๋˜์–ด ์žˆ๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
  • AppDbContext ํด๋ž˜์Šค์— WorkInstructions DbSet์ด ์ •์˜๋˜์–ด ์žˆ๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.

๋‹จ๊ณ„ 2: ์ƒ์„ฑ์ž ๋งค๊ฐœ๋ณ€์ˆ˜ ํ™•์ธ

  • **WorkInstructionService**๋ฅผ ์ธ์Šคํ„ด์Šคํ™”ํ•  ๋•Œ AppDbContext ์ธ์Šคํ„ด์Šค๋ฅผ ์ „๋‹ฌํ•˜๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.

๋‹จ๊ณ„ 3: Using ๋ฌธ ๋ฐ ์ฐธ์กฐ ํ™•์ธ

  • ํŒŒ์ผ ์ƒ๋‹จ์— ํ•„์š”ํ•œ ๋„ค์ž„์ŠคํŽ˜์ด์Šค์— ๋Œ€ํ•œ using ๋ฌธ์ด ์žˆ๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.

ํŒŒ์ผ ๊ฒ€ํ† 

1. WorkInstructionService.cs

์šฐ์„  WorkInstructionService ํด๋ž˜์Šค์— ๋ฉ”์„œ๋“œ๋“ค์ด ์ •์˜๋˜์–ด ์žˆ๋Š”์ง€ ํ™•์ธํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

csharp์ฝ”๋“œ ๋ณต์‚ฌ
using System.Collections.Generic;
using System.Threading.Tasks;
using WindowsFormsApp1Core.SQLModels;
using WindowsFormsApp1Core.WorkManagementSystem.WMModels;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;

namespace WindowsFormsApp1Core.WorkManagementSystem.WMSrvs
{
    public class WorkInstructionService
    {
        private readonly AppDbContext _context;
        private readonly ILogger<WorkInstructionService> _logger;

        public WorkInstructionService(AppDbContext context, ILogger<WorkInstructionService> logger)
        {
            _context = context;
            _logger = logger;
        }

        public async Task<IEnumerable<WorkInstruction>> GetWorkInstructionsAsync()
        {
            try
            {
                return await _context.WorkInstructions.ToListAsync();
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error fetching work instructions");
                throw;
            }
        }

        public async Task AddWorkInstructionAsync(WorkInstruction workInstruction)
        {
            try
            {
                _context.WorkInstructions.Add(workInstruction);
                await _context.SaveChangesAsync();
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error adding work instruction");
                throw;
            }
        }

        public async Task UpdateWorkInstructionAsync(WorkInstruction workInstruction)
        {
            try
            {
                var existing = await _context.WorkInstructions.FirstOrDefaultAsync(w => w.TaskName == workInstruction.TaskName);
                if (existing != null)
                {
                    existing.Content = workInstruction.Content;
                    existing.Date = workInstruction.Date;
                    existing.Writer = workInstruction.Writer;
                    existing.Priority = workInstruction.Priority;
                    existing.IsCompleted = workInstruction.IsCompleted;
                    await _context.SaveChangesAsync();
                }
                else
                {
                    _logger.LogWarning("Work instruction not found: {TaskName}", workInstruction.TaskName);
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error updating work instruction");
                throw;
            }
        }

        public async Task DeleteWorkInstructionAsync(string taskName)
        {
            try
            {
                var workInstruction = await _context.WorkInstructions.FirstOrDefaultAsync(w => w.TaskName == taskName);
                if (workInstruction != null)
                {
                    _context.WorkInstructions.Remove(workInstruction);
                    await _context.SaveChangesAsync();
                }
                else
                {
                    _logger.LogWarning("Work instruction not found: {TaskName}", taskName);
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error deleting work instruction");
                throw;
            }
        }

        public async Task<WorkInstruction> GetWorkInstructionByTaskNameAsync(string taskName)
        {
            try
            {
                return await _context.WorkInstructions.FirstOrDefaultAsync(w => w.TaskName == taskName);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error fetching work instruction by task name");
                throw;
            }
        }
    }
}

2. AppDbContext.cs

์ด์ œ AppDbContext ํด๋ž˜์Šค์— WorkInstructions DbSet์ด ์ •์˜๋˜์–ด ์žˆ๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.

csharp์ฝ”๋“œ ๋ณต์‚ฌ
using Microsoft.EntityFrameworkCore;
using WindowsFormsApp1Core.WorkManagementSystem.WMModels;

namespace WindowsFormsApp1Core.SQLModels
{
    public class AppDbContext : DbContext
    {
        public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }

        public DbSet<WorkInstruction> WorkInstructions { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            if (!optionsBuilder.IsConfigured)
            {
                string connectionString = "Server=localhost;Database=plc_data_model1;User=root;Password=Alejd1785!;";
                optionsBuilder.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString));
            }
        }
    }
}

3. WorkInstructionServiceCtrl.cs

WorkInstructionService ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•  ๋•Œ **AppDbContext**๋ฅผ ์ „๋‹ฌํ•˜๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.

csharp์ฝ”๋“œ ๋ณต์‚ฌ
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Threading.Tasks;
using WindowsFormsApp1Core.WorkManagementSystem.WMModels;
using WindowsFormsApp1Core.WorkManagementSystem.WMSrvs;
using Microsoft.Extensions.Logging;

namespace WindowsFormsApp1Core.Dashboard.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    public class WorkInstructionController : ControllerBase
    {
        private readonly WorkInstructionService _service;
        private readonly ILogger<WorkInstructionController> _logger;

        public WorkInstructionController(AppDbContext context, ILogger<WorkInstructionController> logger)
        {
            _service = new WorkInstructionService(context, logger);
            _logger = logger;
        }

        [HttpGet]
        public async Task<ActionResult<IEnumerable<WorkInstruction>>> Get()
        {
            try
            {
                var workInstructions = await _service.GetWorkInstructionsAsync();
                return Ok(workInstructions);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error fetching work instructions");
                return StatusCode(500, "Internal server error");
            }
        }

        [HttpPost]
        public async Task<ActionResult<WorkInstruction>> Post(WorkInstruction workInstruction)
        {
            try
            {
                await _service.AddWorkInstructionAsync(workInstruction);
                return CreatedAtAction(nameof(Get), new { id = workInstruction.TaskName }, workInstruction);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error adding work instruction");
                return StatusCode(500, "Internal server error");
            }
        }

        [HttpPut("{taskName}")]
        public async Task<IActionResult> Put(string taskName, WorkInstruction workInstruction)
        {
            try
            {
                var existingWorkInstruction = await _service.GetWorkInstructionByTaskNameAsync(taskName);
                if (existingWorkInstruction == null)
                {
                    return NotFound();
                }

                await _service.UpdateWorkInstructionAsync(workInstruction);
                return NoContent();
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error updating work instruction");
                return StatusCode(500, "Internal server error");
            }
        }

        [HttpDelete("{taskName}")]
        public async Task<IActionResult> Delete(string taskName)
        {
            try
            {
                var workInstruction = await _service.GetWorkInstructionByTaskNameAsync(taskName);
                if (workInstruction == null)
                {
                    return NotFound();
                }

                await _service.DeleteWorkInstructionAsync(taskName);
                return NoContent();
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error deleting work instruction");
                return StatusCode(500, "Internal server error");
            }
        }
    }
}

500 ๋‚ด๋ถ€ ์„œ๋ฒ„ ์˜ค๋ฅ˜ ํ•ด๊ฒฐ

๋ฌธ์ œ: 500 ๋‚ด๋ถ€ ์„œ๋ฒ„ ์˜ค๋ฅ˜๋Š” ์ฃผ๋กœ ์„œ๋ฒ„์—์„œ ๋ฐœ์ƒํ•˜๋Š” ์˜ˆ์™ธ๋กœ ์ธํ•ด ์‘๋‹ต์ด ์‹คํŒจํ•˜๋Š” ๊ฒฝ์šฐ์ž…๋‹ˆ๋‹ค. ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋ช‡ ๊ฐ€์ง€ ์ ๊ฒ€ ์‚ฌํ•ญ์„ ํ™•์ธํ•ด ๋ณด์•˜์Šต๋‹ˆ๋‹ค.

ํ™•์ธํ•  ํ•ญ๋ชฉ๋“ค:

  1. ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๊ฒฐ ๋ฌธ์ž์—ด:
    • appsettings.json ๋˜๋Š” **AppDbContext**์—์„œ ์‚ฌ์šฉํ•œ ์—ฐ๊ฒฐ ๋ฌธ์ž์—ด์ด ์˜ฌ๋ฐ”๋ฅธ์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
  2. WorkInstructionService ํด๋ž˜์Šค์˜ ๊ตฌํ˜„:
    • ์ด ์„œ๋น„์Šค ํด๋ž˜์Šค์˜ ๋ฉ”์„œ๋“œ๋“ค์ด ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ •์˜๋˜์–ด ์žˆ๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
  3. ์˜์กด์„ฑ ์ฃผ์ž…(DI) ์„ค์ •:
    • **Program.cs**์—์„œ **AppDbContext**์™€ **WorkInstructionService**๊ฐ€ ์ œ๋Œ€๋กœ ๋“ฑ๋ก๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.

๊ฒฐ๋ก 

์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” API ๊ด€๋ จ ์˜ค๋ฅ˜๋ฅผ ๋ถ„์„ํ•˜๊ณ  ํ•ด๊ฒฐํ•˜๋Š” ๊ณผ์ •์„ ์‚ดํŽด๋ณด์•˜์Šต๋‹ˆ๋‹ค. ์—๋Ÿฌ ๋ฉ”์‹œ์ง€๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๋ฌธ์ œ๋ฅผ ๋ถ„์„ํ•˜๊ณ , ํ•„์š”ํ•œ ์ˆ˜์ • ์‚ฌํ•ญ์„ ์ ์šฉํ•˜์—ฌ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ฐฐ์› ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ ํฌ์ŠคํŒ…์—์„œ๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ๋ฐ ๊ธฐํƒ€ ๋ฐœ์ƒํ•œ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๊ณผ์ •์„ ๋‹ค๋ฃจ๊ฒ ์Šต๋‹ˆ๋‹ค.

 


4. ์˜์กด์„ฑ ์„ค์น˜ ๋ฐ ์„ค์ •

์ด ํฌ์ŠคํŒ…์—์„œ๋Š” ํ”„๋กœ์ ํŠธ์— ํ•„์š”ํ•œ ์˜์กด์„ฑ ์„ค์น˜ ๋ฐ ์„ค์ • ๋ฐฉ๋ฒ•์„ ๋‹ค๋ฃจ๊ฒ ์Šต๋‹ˆ๋‹ค. ํŠนํžˆ, NuGet ํŒจํ‚ค์ง€๋ฅผ ์„ค์น˜ํ•˜๊ณ , ํ”„๋กœ์ ํŠธ์˜ ์„ค์ • ํŒŒ์ผ๋“ค์„ ์ˆ˜์ •ํ•˜๋Š” ๊ณผ์ •์„ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ์„ค์ •์€ ํ”„๋กœ์ ํŠธ๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ๋นŒ๋“œ๋˜๊ณ  ์‹คํ–‰๋˜๊ธฐ ์œ„ํ•ด ํ•„์ˆ˜์ ์ž…๋‹ˆ๋‹ค.

NuGet ํŒจํ‚ค์ง€ ์„ค์น˜

ํ”„๋กœ์ ํŠธ์—์„œ ์‚ฌ์šฉํ•  NuGet ํŒจํ‚ค์ง€๋ฅผ ์„ค์น˜ํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์—์„œ๋Š” Pomelo.EntityFrameworkCore.MySql, Microsoft.EntityFrameworkCore, Microsoft.EntityFrameworkCore.Tools ํŒจํ‚ค์ง€๋ฅผ ์„ค์น˜ํ•ฉ๋‹ˆ๋‹ค.

1. ํŒจํ‚ค์ง€ ์„ค์น˜ ๋ช…๋ น

ํŒจํ‚ค์ง€ ๊ด€๋ฆฌ ์ฝ˜์†”์—์„œ ๋‹ค์Œ ๋ช…๋ น์„ ์‹คํ–‰ํ•˜์—ฌ ํ•„์š”ํ•œ ํŒจํ‚ค์ง€๋ฅผ ์„ค์น˜ํ•ฉ๋‹ˆ๋‹ค.

powershell์ฝ”๋“œ ๋ณต์‚ฌ
Install-Package Pomelo.EntityFrameworkCore.MySql
Install-Package Microsoft.EntityFrameworkCore
Install-Package Microsoft.EntityFrameworkCore.Tools

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๊ฒฐ ์„ค์ •

1. appsettings.json ์„ค์ •

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๊ฒฐ ๋ฌธ์ž์—ด์„ ์„ค์ •ํ•˜์—ฌ ํ”„๋กœ์ ํŠธ๊ฐ€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

json์ฝ”๋“œ ๋ณต์‚ฌ
{
  "ConnectionStrings": {
    "DefaultConnection": "Server=localhost;Database=plc_data_model1;User=root;Password=Alejd1785!;"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}

AppDbContext ํด๋ž˜์Šค ์„ค์ •

1. AppDbContext.cs ์ˆ˜์ •

AppDbContext ํด๋ž˜์Šค์— WorkInstructions DbSet์„ ์ถ”๊ฐ€ํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋ชจ๋ธ์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

csharp์ฝ”๋“œ ๋ณต์‚ฌ
using Microsoft.EntityFrameworkCore;
using WindowsFormsApp1Core.WorkManagementSystem.WMModels;

namespace WindowsFormsApp1Core.SQLModels
{
    public class AppDbContext : DbContext
    {
        public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }

        public DbSet<WorkInstruction> WorkInstructions { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            if (!optionsBuilder.IsConfigured)
            {
                string connectionString = "Server=localhost;Database=plc_data_model1;User=root;Password=Alejd1785!;";
                optionsBuilder.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString));
            }
        }
    }
}

์˜์กด์„ฑ ์ฃผ์ž… ์„ค์ •

1. Program.cs ์ˆ˜์ •

ํ”„๋กœ์ ํŠธ์˜ DI ์ปจํ…Œ์ด๋„ˆ์— **AppDbContext**์™€ **WorkInstructionService**๋ฅผ ๋“ฑ๋กํ•ฉ๋‹ˆ๋‹ค.

csharp์ฝ”๋“œ ๋ณต์‚ฌ
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.EntityFrameworkCore;
using WindowsFormsApp1Core.SQLModels;
using Microsoft.Extensions.Logging;

namespace WorkManagementSystem
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.ConfigureServices((context, services) =>
                    {
                        services.AddDbContext<AppDbContext>(options =>
                            options.UseMySql(
                                context.Configuration.GetConnectionString("DefaultConnection"),
                                ServerVersion.AutoDetect(context.Configuration.GetConnectionString("DefaultConnection"))
                            ));

                        services.AddScoped<WorkInstructionService>();
                        services.AddLogging();

                        services.AddControllersWithViews();
                        services.AddRazorPages();
                        services.AddServerSideBlazor();
                        services.AddSyncfusionBlazor();
                    });

                    webBuilder.Configure((context, app) =>
                    {
                        var env = context.HostingEnvironment;

                        if (env.IsDevelopment())
                        {
                            app.UseDeveloperExceptionPage();
                        }
                        else
                        {
                            app.UseExceptionHandler("/Home/Error");
                            app.UseHsts();
                        }

                        app.UseHttpsRedirection();
                        app.UseStaticFiles();
                        app.UseRouting();
                        app.UseAuthorization();

                        app.UseEndpoints(endpoints =>
                        {
                            endpoints.MapControllers();
                            endpoints.MapBlazorHub();
                            endpoints.MapRazorPages();
                            endpoints.MapFallbackToPage("/_Host");
                        });
                    });
                });
    }
}

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ๋ฐ ์—…๋ฐ์ดํŠธ

1. ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์‹คํ–‰

Entity Framework Core๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์„ ์‹คํ–‰ํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์Šคํ‚ค๋งˆ๋ฅผ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค.

powershell์ฝ”๋“œ ๋ณต์‚ฌ
Add-Migration InitialCreate
Update-Database

2. ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ดˆ๊ธฐํ™”

Update-Database ๋ช…๋ น์„ ์‹คํ–‰ํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์ตœ์‹  ์ƒํƒœ๋กœ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์Šคํ‚ค๋งˆ๊ฐ€ ํ”„๋กœ์ ํŠธ์˜ ๋ชจ๋ธ๊ณผ ์ผ์น˜ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

๊ฒฐ๋ก 

์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” ํ”„๋กœ์ ํŠธ์˜ ๋ฐฑ์—”๋“œ ์„ค์ •์„ ์œ„ํ•œ ์˜์กด์„ฑ ์„ค์น˜์™€ ์„ค์ • ๋ฐฉ๋ฒ•์„ ๋‹ค๋ฃจ์—ˆ์Šต๋‹ˆ๋‹ค. NuGet ํŒจํ‚ค์ง€๋ฅผ ์„ค์น˜ํ•˜๊ณ , ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๊ฒฐ์„ ์„ค์ •ํ•˜๋ฉฐ, DI ์ปจํ…Œ์ด๋„ˆ์— ํ•„์š”ํ•œ ์„œ๋น„์Šค๋ฅผ ๋“ฑ๋กํ•˜๋Š” ๊ณผ์ •์„ ์‚ดํŽด๋ณด์•˜์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ์„ค์ •์ด ์™„๋ฃŒ๋˜๋ฉด ํ”„๋กœ์ ํŠธ๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ์‹คํ–‰๋˜๊ณ  ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€ ์ƒํ˜ธ์ž‘์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๋‹ค์Œ ํฌ์ŠคํŒ…์—์„œ๋Š” ํ”„๋กœ์ ํŠธ์˜ ํ”„๋ก ํŠธ์—”๋“œ์™€ ๊ด€๋ จ๋œ ์„ค์ • ๋ฐ ๊ตฌํ˜„ ์‚ฌํ•ญ์„ ๋‹ค๋ฃฐ ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.


5. WorkInstructionService ํด๋ž˜์Šค์™€ ์ปจํŠธ๋กค๋Ÿฌ ๊ตฌํ˜„

์„œ๋ก : ์ด ํฌ์ŠคํŒ…์—์„œ๋Š” WorkInstructionService ํด๋ž˜์Šค์™€ ์ด๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋‹ค๋ฃน๋‹ˆ๋‹ค. WorkInstructionService ํด๋ž˜์Šค๋Š” ์ž‘์—… ์ง€์‹œ์„œ์˜ CRUD ์ž‘์—…์„ ๋‹ด๋‹นํ•˜๋ฉฐ, ์ปจํŠธ๋กค๋Ÿฌ๋Š” ์ด๋Ÿฌํ•œ ์„œ๋น„์Šค๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ํด๋ผ์ด์–ธํŠธ ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

WorkInstructionService ํด๋ž˜์Šค ๊ตฌํ˜„

WorkInstructionService ํด๋ž˜์Šค๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ์ž‘์—… ์ง€์‹œ์„œ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค.

1. WorkInstructionService.cs

csharp์ฝ”๋“œ ๋ณต์‚ฌ
using System.Collections.Generic;
using System.Threading.Tasks;
using WindowsFormsApp1Core.SQLModels;
using WindowsFormsApp1Core.WorkManagementSystem.WMModels;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;

namespace WindowsFormsApp1Core.WorkManagementSystem.WMSrvs
{
    public class WorkInstructionService
    {
        private readonly AppDbContext _context;
        private readonly ILogger<WorkInstructionService> _logger;

        public WorkInstructionService(AppDbContext context, ILogger<WorkInstructionService> logger)
        {
            _context = context;
            _logger = logger;
        }

        public async Task<IEnumerable<WorkInstruction>> GetWorkInstructionsAsync()
        {
            try
            {
                return await _context.WorkInstructions.ToListAsync();
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error fetching work instructions");
                throw;
            }
        }

        public async Task AddWorkInstructionAsync(WorkInstruction workInstruction)
        {
            try
            {
                _context.WorkInstructions.Add(workInstruction);
                await _context.SaveChangesAsync();
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error adding work instruction");
                throw;
            }
        }

        public async Task UpdateWorkInstructionAsync(WorkInstruction workInstruction)
        {
            try
            {
                var existing = await _context.WorkInstructions.FirstOrDefaultAsync(w => w.TaskName == workInstruction.TaskName);
                if (existing != null)
                {
                    existing.Content = workInstruction.Content;
                    existing.Date = workInstruction.Date;
                    existing.Writer = workInstruction.Writer;
                    existing.Priority = workInstruction.Priority;
                    existing.IsCompleted = workInstruction.IsCompleted;
                    await _context.SaveChangesAsync();
                }
                else
                {
                    _logger.LogWarning("Work instruction not found: {TaskName}", workInstruction.TaskName);
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error updating work instruction");
                throw;
            }
        }

        public async Task DeleteWorkInstructionAsync(string taskName)
        {
            try
            {
                var workInstruction = await _context.WorkInstructions.FirstOrDefaultAsync(w => w.TaskName == taskName);
                if (workInstruction != null)
                {
                    _context.WorkInstructions.Remove(workInstruction);
                    await _context.SaveChangesAsync();
                }
                else
                {
                    _logger.LogWarning("Work instruction not found: {TaskName}", taskName);
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error deleting work instruction");
                throw;
            }
        }

        public async Task<WorkInstruction> GetWorkInstructionByTaskNameAsync(string taskName)
        {
            try
            {
                return await _context.WorkInstructions.FirstOrDefaultAsync(w => w.TaskName == taskName);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error fetching work instruction by task name");
                throw;
            }
        }
    }
}

WorkInstructionController ํด๋ž˜์Šค ๊ตฌํ˜„

์ปจํŠธ๋กค๋Ÿฌ๋Š” **WorkInstructionService**๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํด๋ผ์ด์–ธํŠธ ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

2. WorkInstructionController.cs

csharp์ฝ”๋“œ ๋ณต์‚ฌ
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Threading.Tasks;
using WindowsFormsApp1Core.WorkManagementSystem.WMModels;
using WindowsFormsApp1Core.WorkManagementSystem.WMSrvs;
using Microsoft.Extensions.Logging;

namespace WindowsFormsApp1Core.Dashboard.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    public class WorkInstructionController : ControllerBase
    {
        private readonly WorkInstructionService _service;
        private readonly ILogger<WorkInstructionController> _logger;

        public WorkInstructionController(WorkInstructionService service, ILogger<WorkInstructionController> logger)
        {
            _service = service;
            _logger = logger;
        }

        [HttpGet]
        public async Task<ActionResult<IEnumerable<WorkInstruction>>> Get()
        {
            try
            {
                var workInstructions = await _service.GetWorkInstructionsAsync();
                return Ok(workInstructions);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error fetching work instructions");
                return StatusCode(500, "Internal server error");
            }
        }

        [HttpPost]
        public async Task<ActionResult<WorkInstruction>> Post(WorkInstruction workInstruction)
        {
            try
            {
                await _service.AddWorkInstructionAsync(workInstruction);
                return CreatedAtAction(nameof(Get), new { id = workInstruction.TaskName }, workInstruction);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error adding work instruction");
                return StatusCode(500, "Internal server error");
            }
        }

        [HttpPut("{taskName}")]
        public async Task<IActionResult> Put(string taskName, WorkInstruction workInstruction)
        {
            try
            {
                var existingWorkInstruction = await _service.GetWorkInstructionByTaskNameAsync(taskName);
                if (existingWorkInstruction == null)
                {
                    return NotFound();
                }

                await _service.UpdateWorkInstructionAsync(workInstruction);
                return NoContent();
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error updating work instruction");
                return StatusCode(500, "Internal server error");
            }
        }

        [HttpDelete("{taskName}")]
        public async Task<IActionResult> Delete(string taskName)
        {
            try
            {
                var workInstruction = await _service.GetWorkInstructionByTaskNameAsync(taskName);
                if (workInstruction == null)
                {
                    return NotFound();
                }

                await _service.DeleteWorkInstructionAsync(taskName);
                return NoContent();
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error deleting work instruction");
                return StatusCode(500, "Internal server error");
            }
        }
    }
}

๊ฒฐ๋ก 

์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” WorkInstructionService ํด๋ž˜์Šค์™€ ์ด๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋‹ค๋ฃจ์—ˆ์Šต๋‹ˆ๋‹ค. WorkInstructionService ํด๋ž˜์Šค๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ์ž‘์—… ์ง€์‹œ์„œ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ํฌํ•จํ•˜๊ณ  ์žˆ์œผ๋ฉฐ, ์ปจํŠธ๋กค๋Ÿฌ๋Š” ์ด๋Ÿฌํ•œ ์„œ๋น„์Šค๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ํด๋ผ์ด์–ธํŠธ ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ๋‹ค์Œ ํฌ์ŠคํŒ…์—์„œ๋Š” ํ”„๋กœ์ ํŠธ์˜ ํ”„๋ก ํŠธ์—”๋“œ์™€ ๊ด€๋ จ๋œ ์„ค์ • ๋ฐ ๊ตฌํ˜„ ์‚ฌํ•ญ์„ ๋‹ค๋ฃฐ ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.


6. ์˜์กด์„ฑ ์ฃผ์ž… ๋ฐ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์„ค์ •

์„œ๋ก : ์ด ํฌ์ŠคํŒ…์—์„œ๋Š” ์˜์กด์„ฑ ์ฃผ์ž…(DI)๊ณผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์„ค์ •์„ ๋‹ค๋ฃน๋‹ˆ๋‹ค. DI๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๊ตฌ์„ฑ ์š”์†Œ๋ฅผ ์„œ๋กœ ๋ถ„๋ฆฌํ•˜์—ฌ ์œ ์ง€ ๊ด€๋ฆฌ์™€ ํ…Œ์ŠคํŠธ๋ฅผ ์šฉ์ดํ•˜๊ฒŒ ํ•˜๊ณ , ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์„ค์ •์€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€ ์ƒํ˜ธ ์ž‘์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ตฌ์„ฑํ•˜๋Š” ์ค‘์š”ํ•œ ๋‹จ๊ณ„์ž…๋‹ˆ๋‹ค.

์˜์กด์„ฑ ์ฃผ์ž… ์„ค์ •

์˜์กด์„ฑ ์ฃผ์ž…์€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ํด๋ž˜์Šค๋“ค์ด ํ•„์š”ํ•œ ์ข…์†์„ฑ์„ ์™ธ๋ถ€์—์„œ ์ฃผ์ž…๋ฐ›๋„๋ก ํ•˜๋Š” ํŒจํ„ด์ž…๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ํด๋ž˜์Šค ๊ฐ„์˜ ๊ฒฐํ•ฉ๋„๋ฅผ ๋‚ฎ์ถ”๊ณ  ์œ ์—ฐ์„ฑ์„ ๋†’์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

1. Program.cs

Program.cs ํŒŒ์ผ์—์„œ ์„œ๋น„์Šค์™€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ปจํ…์ŠคํŠธ๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

csharp์ฝ”๋“œ ๋ณต์‚ฌ
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.EntityFrameworkCore;
using WindowsFormsApp1Core.SQLModels;
using Syncfusion.Blazor;
using System;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using WindowsFormsApp1Core.WorkManagementSystem.WMSrvs;

namespace WorkManagementSystem
{
    static class Program
    {
        [STAThread]
        static void Main(string[] args)
        {
            Application.SetHighDpiMode(HighDpiMode.SystemAware);
            Application.SetCompatibleTextRenderingDefault(false);

            var host = CreateHostBuilder(args).Build();
            host.Start();

            Application.Run(new FormMain());
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.ConfigureServices((context, services) =>
                    {
                        services.AddDbContext<AppDbContext>(options =>
                            options.UseMySql(
                                context.Configuration.GetConnectionString("DefaultConnection"),
                                ServerVersion.AutoDetect(context.Configuration.GetConnectionString("DefaultConnection"))
                            ));

                        // Add WorkInstructionService to the DI container
                        services.AddScoped<WorkInstructionService>();

                        services.AddControllersWithViews();
                        services.AddRazorPages();
                        services.AddServerSideBlazor();
                        services.AddSyncfusionBlazor();
                    });

                    webBuilder.Configure((context, app) =>
                    {
                        var env = context.HostingEnvironment;

                        if (env.IsDevelopment())
                        {
                            app.UseDeveloperExceptionPage();
                        }
                        else
                        {
                            app.UseExceptionHandler("/Home/Error");
                            app.UseHsts();
                        }

                        app.UseHttpsRedirection();
                        app.UseStaticFiles();
                        app.UseRouting();
                        app.UseAuthorization();

                        app.UseEndpoints(endpoints =>
                        {
                            endpoints.MapControllers();
                            endpoints.MapBlazorHub();
                            endpoints.MapRazorPages();
                            endpoints.MapFallbackToPage("/_Host");
                        });
                    });
                });
    }
}

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์„ค์ •

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์„ค์ •์€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€ ์ƒํ˜ธ ์ž‘์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ตฌ์„ฑํ•˜๋Š” ์ค‘์š”ํ•œ ๋‹จ๊ณ„์ž…๋‹ˆ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด appsettings.json ํŒŒ์ผ๊ณผ AppDbContext ํด๋ž˜์Šค๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

2. appsettings.json

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๊ฒฐ ๋ฌธ์ž์—ด์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

json์ฝ”๋“œ ๋ณต์‚ฌ
{
  "ConnectionStrings": {
    "DefaultConnection": "Server=localhost;Database=plc_data_model1;User=root;Password=Alejd1785!;"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}

3. AppDbContext.cs

AppDbContext ํด๋ž˜์Šค๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€์˜ ์—ฐ๊ฒฐ์„ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

csharp์ฝ”๋“œ ๋ณต์‚ฌ
using Microsoft.EntityFrameworkCore;
using WindowsFormsApp1Core.WorkManagementSystem.WMModels;

namespace WindowsFormsApp1Core.SQLModels
{
    public class AppDbContext : DbContext
    {
        public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }

        public DbSet<PlcDataModel> PlcDatas { get; set; }
        public DbSet<Table1> Table1s { get; set; }
        public DbSet<Table2> Table2s { get; set; }
        public DbSet<WorkInstruction> WorkInstructions { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            if (!optionsBuilder.IsConfigured)
            {
                string connectionString = "Server=localhost;Database=plc_data_model1;User=root;Password=Alejd1785!;";
                optionsBuilder.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString));
            }
        }
    }
}

๊ฒฐ๋ก 

์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” ์˜์กด์„ฑ ์ฃผ์ž…๊ณผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์„ค์ • ๋ฐฉ๋ฒ•์„ ๋‹ค๋ฃจ์—ˆ์Šต๋‹ˆ๋‹ค. ์˜์กด์„ฑ ์ฃผ์ž…์„ ํ†ตํ•ด ํด๋ž˜์Šค ๊ฐ„์˜ ๊ฒฐํ•ฉ๋„๋ฅผ ๋‚ฎ์ถ”๊ณ  ์œ ์—ฐ์„ฑ์„ ๋†’์˜€์œผ๋ฉฐ, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์„ค์ •์„ ํ†ตํ•ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€ ์›ํ™œํ•˜๊ฒŒ ์ƒํ˜ธ ์ž‘์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ตฌ์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ ํฌ์ŠคํŒ…์—์„œ๋Š” ํ”„๋กœ์ ํŠธ์˜ ์œ ๋‹› ํ…Œ์ŠคํŠธ์™€ ๋””๋ฒ„๊น… ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ๋‹ค๋ฃฐ ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.


7. ์„œ๋น„์Šค์™€ ์ปจํŠธ๋กค๋Ÿฌ ๊ตฌํ˜„

์„œ๋ก : ์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” **WorkInstructionService**์™€ **WorkInstructionController**๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๊ณผ์ •์„ ๋‹ค๋ฃน๋‹ˆ๋‹ค. ์ด ๊ณผ์ •์€ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง๊ณผ API ์—”๋“œํฌ์ธํŠธ๋ฅผ ๋ถ„๋ฆฌํ•˜์—ฌ ์ฝ”๋“œ์˜ ๊ฐ€๋…์„ฑ๊ณผ ์œ ์ง€ ๋ณด์ˆ˜์„ฑ์„ ๋†’์ด๋Š” ์ค‘์š”ํ•œ ๋‹จ๊ณ„์ž…๋‹ˆ๋‹ค.

์„œ๋น„์Šค ํด๋ž˜์Šค ๊ตฌํ˜„

์„œ๋น„์Šค ํด๋ž˜์Šค๋Š” ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. WorkInstructionService ํด๋ž˜์Šค๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€ ์ƒํ˜ธ ์ž‘์šฉํ•˜๋ฉฐ, CRUD ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

1. WorkInstructionService ํด๋ž˜์Šค

๋‹ค์Œ์€ WorkInstructionService ํด๋ž˜์Šค์˜ ๊ตฌํ˜„ ์˜ˆ์ œ์ž…๋‹ˆ๋‹ค.

csharp์ฝ”๋“œ ๋ณต์‚ฌ
using System.Collections.Generic;
using System.Threading.Tasks;
using WindowsFormsApp1Core.SQLModels;
using WindowsFormsApp1Core.WorkManagementSystem.WMModels;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;

namespace WindowsFormsApp1Core.WorkManagementSystem.WMSrvs
{
    public class WorkInstructionService
    {
        private readonly AppDbContext _context;
        private readonly ILogger<WorkInstructionService> _logger;

        public WorkInstructionService(AppDbContext context, ILogger<WorkInstructionService> logger)
        {
            _context = context;
            _logger = logger;
        }

        public async Task<IEnumerable<WorkInstruction>> GetWorkInstructionsAsync()
        {
            try
            {
                return await _context.WorkInstructions.ToListAsync();
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error fetching work instructions");
                throw;
            }
        }

        public async Task AddWorkInstructionAsync(WorkInstruction workInstruction)
        {
            try
            {
                _context.WorkInstructions.Add(workInstruction);
                await _context.SaveChangesAsync();
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error adding work instruction");
                throw;
            }
        }

        public async Task UpdateWorkInstructionAsync(WorkInstruction workInstruction)
        {
            try
            {
                var existing = await _context.WorkInstructions.FirstOrDefaultAsync(w => w.TaskName == workInstruction.TaskName);
                if (existing != null)
                {
                    existing.Content = workInstruction.Content;
                    existing.Date = workInstruction.Date;
                    existing.Writer = workInstruction.Writer;
                    existing.Priority = workInstruction.Priority;
                    existing.IsCompleted = workInstruction.IsCompleted;
                    await _context.SaveChangesAsync();
                }
                else
                {
                    _logger.LogWarning("Work instruction not found: {TaskName}", workInstruction.TaskName);
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error updating work instruction");
                throw;
            }
        }

        public async Task DeleteWorkInstructionAsync(string taskName)
        {
            try
            {
                var workInstruction = await _context.WorkInstructions.FirstOrDefaultAsync(w => w.TaskName == taskName);
                if (workInstruction != null)
                {
                    _context.WorkInstructions.Remove(workInstruction);
                    await _context.SaveChangesAsync();
                }
                else
                {
                    _logger.LogWarning("Work instruction not found: {TaskName}", taskName);
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error deleting work instruction");
                throw;
            }
        }

        public async Task<WorkInstruction> GetWorkInstructionByTaskNameAsync(string taskName)
        {
            try
            {
                return await _context.WorkInstructions.FirstOrDefaultAsync(w => w.TaskName == taskName);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error fetching work instruction by task name");
                throw;
            }
        }
    }
}

์ปจํŠธ๋กค๋Ÿฌ ํด๋ž˜์Šค ๊ตฌํ˜„

์ปจํŠธ๋กค๋Ÿฌ ํด๋ž˜์Šค๋Š” HTTP ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•˜๊ณ , ์„œ๋น„์Šค ํด๋ž˜์Šค์™€ ์ƒํ˜ธ ์ž‘์šฉํ•˜์—ฌ ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ์‘๋‹ต์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

2. WorkInstructionController ํด๋ž˜์Šค

๋‹ค์Œ์€ WorkInstructionController ํด๋ž˜์Šค์˜ ๊ตฌํ˜„ ์˜ˆ์ œ์ž…๋‹ˆ๋‹ค.

csharp์ฝ”๋“œ ๋ณต์‚ฌ
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Threading.Tasks;
using WindowsFormsApp1Core.WorkManagementSystem.WMModels;
using WindowsFormsApp1Core.WorkManagementSystem.WMSrvs;
using Microsoft.Extensions.Logging;

namespace WindowsFormsApp1Core.Dashboard.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    public class WorkInstructionController : ControllerBase
    {
        private readonly WorkInstructionService _service;
        private readonly ILogger<WorkInstructionController> _logger;

        public WorkInstructionController(WorkInstructionService service, ILogger<WorkInstructionController> logger)
        {
            _service = service;
            _logger = logger;
        }

        [HttpGet]
        public async Task<ActionResult<IEnumerable<WorkInstruction>>> Get()
        {
            try
            {
                var workInstructions = await _service.GetWorkInstructionsAsync();
                return Ok(workInstructions);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error fetching work instructions");
                return StatusCode(500, "Internal server error");
            }
        }

        [HttpPost]
        public async Task<ActionResult<WorkInstruction>> Post(WorkInstruction workInstruction)
        {
            try
            {
                await _service.AddWorkInstructionAsync(workInstruction);
                return CreatedAtAction(nameof(Get), new { id = workInstruction.TaskName }, workInstruction);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error adding work instruction");
                return StatusCode(500, "Internal server error");
            }
        }

        [HttpPut("{taskName}")]
        public async Task<IActionResult> Put(string taskName, WorkInstruction workInstruction)
        {
            try
            {
                var existingWorkInstruction = await _service.GetWorkInstructionByTaskNameAsync(taskName);
                if (existingWorkInstruction == null)
                {
                    return NotFound();
                }

                await _service.UpdateWorkInstructionAsync(workInstruction);
                return NoContent();
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error updating work instruction");
                return StatusCode(500, "Internal server error");
            }
        }

        [HttpDelete("{taskName}")]
        public async Task<IActionResult> Delete(string taskName)
        {
            try
            {
                var workInstruction = await _service.GetWorkInstructionByTaskNameAsync(taskName);
                if (workInstruction == null)
                {
                    return NotFound();
                }

                await _service.DeleteWorkInstructionAsync(taskName);
                return NoContent();
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error deleting work instruction");
                return StatusCode(500, "Internal server error");
            }
        }
    }
}

๊ฒฐ๋ก 

์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” **WorkInstructionService**์™€ **WorkInstructionController**์˜ ๊ตฌํ˜„ ๊ณผ์ •์„ ๋‹ค๋ฃจ์—ˆ์Šต๋‹ˆ๋‹ค. ์„œ๋น„์Šค ํด๋ž˜์Šค๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€ ์ƒํ˜ธ ์ž‘์šฉํ•˜๋ฉฐ, ์ปจํŠธ๋กค๋Ÿฌ ํด๋ž˜์Šค๋Š” HTTP ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•˜๊ณ  ์„œ๋น„์Šค๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ์‘๋‹ต์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ๋‹ค์Œ ํฌ์ŠคํŒ…์—์„œ๋Š” ํ”„๋กœ์ ํŠธ์˜ ์œ ๋‹› ํ…Œ์ŠคํŠธ์™€ ๋””๋ฒ„๊น… ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ๋‹ค๋ฃฐ ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.


8. ์˜์กด์„ฑ ์ฃผ์ž… ์„ค์ • ๋ฐ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๊ฒฐ

์„œ๋ก : ์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” **WorkInstructionService**์™€ **AppDbContext**๋ฅผ ํ”„๋กœ์ ํŠธ์— ์˜์กด์„ฑ ์ฃผ์ž…(DI)ํ•˜์—ฌ ์„ค์ •ํ•˜๊ณ  ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€์˜ ์—ฐ๊ฒฐ์„ ์„ค์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋‹ค๋ฃน๋‹ˆ๋‹ค. ์˜ฌ๋ฐ”๋ฅธ DI ์„ค์ •์€ ์ฝ”๋“œ์˜ ๋ชจ๋“ˆํ™”์™€ ํ…Œ์ŠคํŠธ ๊ฐ€๋Šฅ์„ฑ์„ ๋†’์ด๋Š” ์ค‘์š”ํ•œ ๋‹จ๊ณ„์ž…๋‹ˆ๋‹ค.

์˜์กด์„ฑ ์ฃผ์ž… ์„ค์ •

์˜์กด์„ฑ ์ฃผ์ž…์€ ๊ฐ์ฒด ๊ฐ„์˜ ์˜์กด์„ฑ์„ ์„ค์ •ํ•˜๋Š” ๋””์ž์ธ ํŒจํ„ด์œผ๋กœ, ๊ฐ์ฒด ์ƒ์„ฑ๊ณผ ๋ผ์ดํ”„์‚ฌ์ดํด ๊ด€๋ฆฌ๋ฅผ ํ”„๋ ˆ์ž„์›Œํฌ์— ๋งก๊ธฐ๋Š” ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์ฝ”๋“œ์˜ ๊ฒฐํ•ฉ๋„๋ฅผ ๋‚ฎ์ถ”๊ณ  ํ…Œ์ŠคํŠธ ๊ฐ€๋Šฅ์„ฑ์„ ๋†’์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

1. Program.cs ํŒŒ์ผ ์„ค์ •

Program.cs ํŒŒ์ผ์—์„œ ์„œ๋น„์Šค์™€ DbContext๋ฅผ DI ์ปจํ…Œ์ด๋„ˆ์— ๋“ฑ๋กํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด CreateHostBuilder ๋ฉ”์„œ๋“œ์—์„œ ํ•„์š”ํ•œ ์„œ๋น„์Šค๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

csharp์ฝ”๋“œ ๋ณต์‚ฌ
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.EntityFrameworkCore;
using WindowsFormsApp1Core.SQLModels;
using Syncfusion.Blazor;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using WindowsFormsApp1Core.WorkManagementSystem.WMSrvs;

namespace WorkManagementSystem
{
    static class Program
    {
        [STAThread]
        static void Main(string[] args)
        {
            Application.SetHighDpiMode(HighDpiMode.SystemAware);
            Application.SetCompatibleTextRenderingDefault(false);

            var host = CreateHostBuilder(args).Build();
            host.Start();

            Application.Run(new FormMain());
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.ConfigureServices((context, services) =>
                    {
                        services.AddDbContext<AppDbContext>(options =>
                            options.UseMySql(
                                context.Configuration.GetConnectionString("DefaultConnection"),
                                ServerVersion.AutoDetect(context.Configuration.GetConnectionString("DefaultConnection"))
                            ));
                        services.AddScoped<WorkInstructionService>();
                        services.AddControllersWithViews();
                        services.AddRazorPages();
                        services.AddServerSideBlazor();
                        services.AddSyncfusionBlazor();
                    });

                    webBuilder.Configure((context, app) =>
                    {
                        var env = context.HostingEnvironment;

                        if (env.IsDevelopment())
                        {
                            app.UseDeveloperExceptionPage();
                        }
                        else
                        {
                            app.UseExceptionHandler("/Home/Error");
                            app.UseHsts();
                        }

                        app.UseHttpsRedirection();
                        app.UseStaticFiles();
                        app.UseRouting();
                        app.UseAuthorization();

                        app.UseEndpoints(endpoints =>
                        {
                            endpoints.MapControllers();
                            endpoints.MapBlazorHub();
                            endpoints.MapRazorPages();
                            endpoints.MapFallbackToPage("/_Host");
                        });
                    });
                });
    }
}

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๊ฒฐ ์„ค์ •

2. appsettings.json ํŒŒ์ผ ์„ค์ •

๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๊ฒฐ ๋ฌธ์ž์—ด์€ appsettings.json ํŒŒ์ผ์— ์ €์žฅ๋ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€์˜ ์—ฐ๊ฒฐ ์ •๋ณด๋ฅผ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

json์ฝ”๋“œ ๋ณต์‚ฌ
{
  "ConnectionStrings": {
    "DefaultConnection": "Server=localhost;Database=plc_data_model1;User=root;Password=Alejd1785!;"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}

DbContext ์„ค์ •

3. AppDbContext ํด๋ž˜์Šค ์„ค์ •

AppDbContext ํด๋ž˜์Šค๋Š” Entity Framework Core๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€ ์ƒํ˜ธ ์ž‘์šฉํ•˜๋Š” ํด๋ž˜์Šค์ž…๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ๋Š” ํ•„์š”ํ•œ DbSet์„ ์ •์˜ํ•˜๊ณ , ์—ฐ๊ฒฐ ๋ฌธ์ž์—ด์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

csharp์ฝ”๋“œ ๋ณต์‚ฌ
using Microsoft.EntityFrameworkCore;
using WindowsFormsApp1Core.WorkManagementSystem.WMModels;

namespace WindowsFormsApp1Core.SQLModels
{
    public class AppDbContext : DbContext
    {
        public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }

        public DbSet<PlcDataModel> PlcDatas { get; set; }
        public DbSet<Table1> Table1s { get; set; }
        public DbSet<Table2> Table2s { get; set; }
        public DbSet<WorkInstruction> WorkInstructions { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            if (!optionsBuilder.IsConfigured)
            {
                string connectionString = "Server=localhost;Database=plc_data_model1;User=root;Password=Alejd1785!;";
                optionsBuilder.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString));
            }
        }
    }
}

๊ฒฐ๋ก 

์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” **WorkInstructionService**์™€ **AppDbContext**๋ฅผ DI ์ปจํ…Œ์ด๋„ˆ์— ๋“ฑ๋กํ•˜๊ณ , ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€์˜ ์—ฐ๊ฒฐ์„ ์„ค์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋‹ค๋ฃจ์—ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์„œ๋น„์Šค์™€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๊ฐ„์˜ ์ƒํ˜ธ ์ž‘์šฉ์„ ์›ํ™œํ•˜๊ฒŒ ํ•˜์˜€์œผ๋ฉฐ, ์˜์กด์„ฑ ์ฃผ์ž…์„ ํ†ตํ•ด ์ฝ”๋“œ์˜ ๋ชจ๋“ˆํ™”์™€ ํ…Œ์ŠคํŠธ ๊ฐ€๋Šฅ์„ฑ์„ ๋†’์˜€์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ ํฌ์ŠคํŒ…์—์„œ๋Š” ํ”„๋กœ์ ํŠธ์˜ ์œ ๋‹› ํ…Œ์ŠคํŠธ์™€ ๋””๋ฒ„๊น… ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ๋‹ค๋ฃฐ ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.


9. ์„œ๋น„์Šค ๋ฐ ์ปจํŠธ๋กค๋Ÿฌ ๊ตฌํ˜„

์„œ๋ก : ์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” **WorkInstructionService**์™€ **WorkInstructionController**์˜ ๊ตฌ์ฒด์ ์ธ ๊ตฌํ˜„ ๋ฐฉ๋ฒ•์„ ๋‹ค๋ฃจ๊ฒ ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์ž‘์—… ์ง€์‹œ์„œ๋ฅผ ์ƒ์„ฑ, ์ฝ๊ธฐ, ์—…๋ฐ์ดํŠธ, ์‚ญ์ œ(CRUD)ํ•˜๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๊ณ , ๊ฐ ๋ฉ”์„œ๋“œ๊ฐ€ ์–ด๋–ป๊ฒŒ ๋™์ž‘ํ•˜๋Š”์ง€ ์„ค๋ช…ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

์„œ๋น„์Šค ๊ตฌํ˜„

WorkInstructionService ํด๋ž˜์Šค๋Š” **AppDbContext**๋ฅผ ํ†ตํ•ด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€ ์ƒํ˜ธ ์ž‘์šฉํ•˜๋ฉฐ, ์ž‘์—… ์ง€์‹œ์„œ์˜ CRUD ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ์ด ํด๋ž˜์Šค๋Š” ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ํ˜ธ์ถœ๋˜์–ด ์ž‘์—… ์ง€์‹œ์„œ ๊ด€๋ จ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

1. WorkInstructionService ํด๋ž˜์Šค

csharp์ฝ”๋“œ ๋ณต์‚ฌ
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using WindowsFormsApp1Core.SQLModels;
using WindowsFormsApp1Core.WorkManagementSystem.WMModels;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;

namespace WindowsFormsApp1Core.WorkManagementSystem.WMSrvs
{
    public class WorkInstructionService
    {
        private readonly AppDbContext _context;
        private readonly ILogger<WorkInstructionService> _logger;

        public WorkInstructionService(AppDbContext context, ILogger<WorkInstructionService> logger)
        {
            _context = context;
            _logger = logger;
        }

        public async Task<IEnumerable<WorkInstruction>> GetWorkInstructionsAsync()
        {
            try
            {
                return await _context.WorkInstructions.ToListAsync();
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error fetching work instructions");
                throw;
            }
        }

        public async Task AddWorkInstructionAsync(WorkInstruction workInstruction)
        {
            try
            {
                _context.WorkInstructions.Add(workInstruction);
                await _context.SaveChangesAsync();
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error adding work instruction");
                throw;
            }
        }

        public async Task UpdateWorkInstructionAsync(WorkInstruction workInstruction)
        {
            try
            {
                var existing = await _context.WorkInstructions.FirstOrDefaultAsync(w => w.TaskName == workInstruction.TaskName);
                if (existing != null)
                {
                    existing.Content = workInstruction.Content;
                    existing.Date = workInstruction.Date;
                    existing.Writer = workInstruction.Writer;
                    existing.Priority = workInstruction.Priority;
                    existing.IsCompleted = workInstruction.IsCompleted;
                    await _context.SaveChangesAsync();
                }
                else
                {
                    _logger.LogWarning("Work instruction not found: {TaskName}", workInstruction.TaskName);
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error updating work instruction");
                throw;
            }
        }

        public async Task DeleteWorkInstructionAsync(string taskName)
        {
            try
            {
                var workInstruction = await _context.WorkInstructions.FirstOrDefaultAsync(w => w.TaskName == taskName);
                if (workInstruction != null)
                {
                    _context.WorkInstructions.Remove(workInstruction);
                    await _context.SaveChangesAsync();
                }
                else
                {
                    _logger.LogWarning("Work instruction not found: {TaskName}", taskName);
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error deleting work instruction");
                throw;
            }
        }

        public async Task<WorkInstruction> GetWorkInstructionByTaskNameAsync(string taskName)
        {
            try
            {
                return await _context.WorkInstructions.FirstOrDefaultAsync(w => w.TaskName == taskName);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error fetching work instruction by task name");
                throw;
            }
        }
    }
}

์ปจํŠธ๋กค๋Ÿฌ ๊ตฌํ˜„

WorkInstructionController ํด๋ž˜์Šค๋Š” API ์—”๋“œํฌ์ธํŠธ๋ฅผ ์ œ๊ณตํ•˜๋ฉฐ, ์„œ๋น„์Šค ํด๋ž˜์Šค๋ฅผ ํ†ตํ•ด ์ž‘์—… ์ง€์‹œ์„œ์˜ CRUD ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ๊ฐ ๋ฉ”์„œ๋“œ๋Š” HTTP ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•˜๊ณ , ์ ์ ˆํ•œ ์‘๋‹ต์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

2. WorkInstructionController ํด๋ž˜์Šค

csharp์ฝ”๋“œ ๋ณต์‚ฌ
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Threading.Tasks;
using WindowsFormsApp1Core.WorkManagementSystem.WMModels;
using WindowsFormsApp1Core.WorkManagementSystem.WMSrvs;
using Microsoft.Extensions.Logging;

namespace WindowsFormsApp1Core.Dashboard.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    public class WorkInstructionController : ControllerBase
    {
        private readonly WorkInstructionService _service;
        private readonly ILogger<WorkInstructionController> _logger;

        public WorkInstructionController(WorkInstructionService service, ILogger<WorkInstructionController> logger)
        {
            _service = service;
            _logger = logger;
        }

        [HttpGet]
        public async Task<ActionResult<IEnumerable<WorkInstruction>>> Get()
        {
            try
            {
                var workInstructions = await _service.GetWorkInstructionsAsync();
                return Ok(workInstructions);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error fetching work instructions");
                return StatusCode(500, "Internal server error");
            }
        }

        [HttpPost]
        public async Task<ActionResult<WorkInstruction>> Post(WorkInstruction workInstruction)
        {
            try
            {
                await _service.AddWorkInstructionAsync(workInstruction);
                return CreatedAtAction(nameof(Get), new { id = workInstruction.TaskName }, workInstruction);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error adding work instruction");
                return StatusCode(500, "Internal server error");
            }
        }

        [HttpPut("{taskName}")]
        public async Task<IActionResult> Put(string taskName, WorkInstruction workInstruction)
        {
            try
            {
                var existingWorkInstruction = await _service.GetWorkInstructionByTaskNameAsync(taskName);
                if (existingWorkInstruction == null)
                {
                    return NotFound();
                }

                await _service.UpdateWorkInstructionAsync(workInstruction);
                return NoContent();
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error updating work instruction");
                return StatusCode(500, "Internal server error");
            }
        }

        [HttpDelete("{taskName}")]
        public async Task<IActionResult> Delete(string taskName)
        {
            try
            {
                var workInstruction = await _service.GetWorkInstructionByTaskNameAsync(taskName);
                if (workInstruction == null)
                {
                    return NotFound();
                }

                await _service.DeleteWorkInstructionAsync(taskName);
                return NoContent();
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error deleting work instruction");
                return StatusCode(500, "Internal server error");
            }
        }
    }
}

๊ฒฐ๋ก 

์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” **WorkInstructionService**์™€ **WorkInstructionController**์˜ ๊ตฌํ˜„์„ ํ†ตํ•ด ์ž‘์—… ์ง€์‹œ์„œ์˜ CRUD ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋‹ค๋ฃจ์—ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ํ”„๋กœ์ ํŠธ์˜ ํ•ต์‹ฌ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•˜๊ณ , ์˜ฌ๋ฐ”๋ฅธ DI ์„ค์ •๊ณผ ์„œ๋น„์Šค-์ปจํŠธ๋กค๋Ÿฌ ๊ตฌ์กฐ๋ฅผ ์ดํ•ดํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ ํฌ์ŠคํŒ…์—์„œ๋Š” ํ”„๋กœ์ ํŠธ์˜ ์œ ๋‹› ํ…Œ์ŠคํŠธ์™€ ๋””๋ฒ„๊น… ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ๋‹ค๋ฃฐ ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.