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

2024. 7. 6. 10:31ใ†Development๐Ÿ‘ฉ๐Ÿป‍๐Ÿฆณ/C#

 

( ์ฒซ๋ฒˆ์งธ ํ”„๋กœ์ ํŠธ์—์„  API ๊ตฌํ˜„๋ฐ ๋ฐ์ดํ„ฐ ๋ฒ ์ด์Šค ์—ฐ๋™๊นŒ์ง€ ํ•ด๋ดค์œผ๋ฉฐ ๋‹ค์Œ ํ”„๋กœ์ ํŠธ์˜ ๊ฐ€์ด๋“œ๋ฅผ ์ข€ ๋” ๊ฒฌ๊ณ ํ•˜๊ฒŒ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด ์ถ”๊ฐ€์ ์œผ๋กœ ์ •๋ฆฌํ•ด ๋†จ์Šต๋‹ˆ๋‹ค )

 

10. ์„œ๋น„์Šค ๋ฐ ์ปจํŠธ๋กค๋Ÿฌ ๊ตฌํ˜„: ์˜ค๋ฅ˜ ํ•ด๊ฒฐ

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

์ฃผ์š” ์˜ค๋ฅ˜ ๋ฐ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•

1. CS1061 ์˜ค๋ฅ˜: ํด๋ž˜์Šค์— ์ •์˜๋˜์ง€ ์•Š์€ ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ

์˜ค๋ฅ˜ ์„ค๋ช…:CS1061 ์˜ค๋ฅ˜๋Š” ํด๋ž˜์Šค์— ํ˜ธ์ถœ๋œ ๋ฉ”์„œ๋“œ๊ฐ€ ์ •์˜๋˜์–ด ์žˆ์ง€ ์•Š์„ ๋•Œ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, WorkInstructionService ํด๋ž˜์Šค์—์„œ DeleteWorkInstruction ๋ฉ”์„œ๋“œ๊ฐ€ ์ •์˜๋˜์ง€ ์•Š์•˜๊ฑฐ๋‚˜ AppDbContext ํด๋ž˜์Šค์— WorkInstructions ์†์„ฑ์ด ์—†์„ ๋•Œ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•:

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

์ฝ”๋“œ ์˜ˆ์‹œ:

csharp์ฝ”๋“œ ๋ณต์‚ฌ
public class WorkInstructionService
{
    private readonly AppDbContext _context;

    public WorkInstructionService(AppDbContext context)
    {
        _context = context;
    }

    public void DeleteWorkInstruction(int id)
    {
        var instruction = _context.WorkInstructions.Find(id);
        if (instruction != null)
        {
            _context.WorkInstructions.Remove(instruction);
            _context.SaveChanges();
        }
    }

    // ๋‹ค๋ฅธ ๋ฉ”์„œ๋“œ๋„ ์œ ์‚ฌํ•˜๊ฒŒ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.
}

  1. AppDbContext์— WorkInstructions ์ถ”๊ฐ€
csharp์ฝ”๋“œ ๋ณต์‚ฌ
public class AppDbContext : DbContext
{
    public DbSet<WorkInstruction> WorkInstructions { get; set; }

    // ๋‹ค๋ฅธ DbSet๊ณผ ์„ค์ •
}

2. CS7036 ์˜ค๋ฅ˜: ์ƒ์„ฑ์ž ๋งค๊ฐœ๋ณ€์ˆ˜ ๋ถ€์กฑ

์˜ค๋ฅ˜ ์„ค๋ช…:CS7036 ์˜ค๋ฅ˜๋Š” ํด๋ž˜์Šค์˜ ์ƒ์„ฑ์ž๊ฐ€ ํ•„์š”ํ•œ ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์ œ๊ณต๋ฐ›์ง€ ๋ชปํ•  ๋•Œ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, **WorkInstructionService**์˜ ์ƒ์„ฑ์ž๊ฐ€ AppDbContext ํƒ€์ž…์˜ ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ํ•„์š”๋กœ ํ•˜์ง€๋งŒ ์ด๋ฅผ ์ œ๊ณตํ•˜์ง€ ์•Š์•˜์„ ๋•Œ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•:

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

์ฝ”๋“œ ์˜ˆ์‹œ:

csharp์ฝ”๋“œ ๋ณต์‚ฌ
public class WorkInstructionServiceCtrl : Controller
{
    private readonly WorkInstructionService _workInstructionService;

    public WorkInstructionServiceCtrl(AppDbContext context)
    {
        _workInstructionService = new WorkInstructionService(context);
    }

    // _workInstructionService๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.
}

3. 500 ๋‚ด๋ถ€ ์„œ๋ฒ„ ์˜ค๋ฅ˜

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

ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•:

  1. ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๊ฒฐ ๋ฌธ์ž์—ด ํ™•์ธ
json์ฝ”๋“œ ๋ณต์‚ฌ
{
  "ConnectionStrings": {
    "DefaultConnection": "Server=localhost;Database=plc_data_model1;User=root;Password=Alejd1785!;"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}

  1. ์„œ๋น„์Šค ํด๋ž˜์Šค์˜ ๊ตฌํ˜„ ํ™•์ธ
csharp์ฝ”๋“œ ๋ณต์‚ฌ
public class WorkInstructionService
{
    private readonly AppDbContext _context;
    private readonly ILogger<WorkInstructionService> _logger;

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

    // CRUD ๋ฉ”์„œ๋“œ ๊ตฌํ˜„
}

  1. ์˜์กด์„ฑ ์ฃผ์ž…(DI) ์„ค์ • ํ™•์ธ
csharp์ฝ”๋“œ ๋ณต์‚ฌ
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");
                });
            });
        });

๊ฒฐ๋ก 

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


11. ๋กœ๊น…๊ณผ ๋””๋ฒ„๊น…: ์‹ค์‹œ๊ฐ„ ๋ฌธ์ œ ํ•ด๊ฒฐ

์„œ๋ก : ์ด์ „ ํฌ์ŠคํŒ…์—์„œ๋Š” **WorkInstructionService**์™€ **WorkInstructionController**๋ฅผ ๊ตฌํ˜„ํ•˜๋ฉด์„œ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ์ฃผ์š” ์˜ค๋ฅ˜์™€ ๊ทธ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์„ ๋‹ค๋ฃจ์—ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” ๋กœ๊น…๊ณผ ๋””๋ฒ„๊น…์„ ํ†ตํ•ด ์‹ค์‹œ๊ฐ„์œผ๋กœ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์†Œ๊ฐœํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ๋กœ๊น…๊ณผ ๋””๋ฒ„๊น…์€ ์˜ค๋ฅ˜๋ฅผ ํŒŒ์•…ํ•˜๊ณ  ํ•ด๊ฒฐํ•˜๋Š” ๋ฐ ์žˆ์–ด ํ•„์ˆ˜์ ์ธ ๋„๊ตฌ์ž…๋‹ˆ๋‹ค.

๋กœ๊น… ์„ค์ • ๋ฐ ํ™œ์šฉ

๋กœ๊น…์€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ƒํƒœ๋ฅผ ๊ธฐ๋กํ•˜์—ฌ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ์›์ธ์„ ํŒŒ์•…ํ•˜๋Š” ๋ฐ ๋„์›€์ด ๋ฉ๋‹ˆ๋‹ค. .NET Core์—์„œ๋Š” ILogger ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋กœ๊น…์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

1. ๋กœ๊น… ์„ค์ •

Program.cs ํŒŒ์ผ์—์„œ ๋กœ๊น… ์„œ๋น„์Šค๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

csharp์ฝ”๋“œ ๋ณต์‚ฌ
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();
                services.AddLogging(); // ๋กœ๊น… ์„œ๋น„์Šค ์ถ”๊ฐ€
            });

            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. ๋กœ๊น… ํ™œ์šฉ

WorkInstructionService ํด๋ž˜์Šค์—์„œ **ILogger**๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋กœ๊ทธ๋ฅผ ๊ธฐ๋กํ•ฉ๋‹ˆ๋‹ค.

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()
        {
            _logger.LogInformation("Fetching all work instructions.");
            try
            {
                return await _context.WorkInstructions.ToListAsync();
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error fetching work instructions");
                throw;
            }
        }

        public async Task AddWorkInstructionAsync(WorkInstruction workInstruction)
        {
            _logger.LogInformation("Adding new work instruction: {TaskName}", workInstruction.TaskName);
            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)
        {
            _logger.LogInformation("Updating work instruction: {TaskName}", workInstruction.TaskName);
            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)
        {
            _logger.LogInformation("Deleting work instruction: {TaskName}", 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)
        {
            _logger.LogInformation("Fetching work instruction by task name: {TaskName}", 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;
            }
        }
    }
}

๋””๋ฒ„๊น… ์„ค์ • ๋ฐ ํ™œ์šฉ

๋””๋ฒ„๊น…์€ ์ฝ”๋“œ ์‹คํ–‰์„ ์ค‘๋‹จํ•˜๊ณ  ๋‹จ๊ณ„๋ณ„๋กœ ์‹คํ–‰ ์ƒํƒœ๋ฅผ ์ ๊ฒ€ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค๋‹ˆ๋‹ค. Visual Studio์˜ ๋””๋ฒ„๊ฑฐ๋ฅผ ํ™œ์šฉํ•˜๋ฉด ์ฝ”๋“œ์˜ ํ๋ฆ„์„ ์ดํ•ดํ•˜๊ณ , ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ์ง€์ ์„ ์ •ํ™•ํžˆ ํŒŒ์•…ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

1. ๋””๋ฒ„๊น… ์„ค์ •

Visual Studio์—์„œ ๋””๋ฒ„๊น…์„ ์„ค์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  1. ์ค‘๋‹จ์  ์„ค์ •: ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•  ๊ฒƒ์œผ๋กœ ์˜ˆ์ƒ๋˜๋Š” ์ฝ”๋“œ ์ค„์— ์ค‘๋‹จ์ ์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. ์ค‘๋‹จ์ ์„ ์„ค์ •ํ•˜๋ ค๋ฉด ์ฝ”๋“œ ์ค„ ์™ผ์ชฝ์˜ ํšŒ์ƒ‰ ๋ง‰๋Œ€๋ฅผ ํด๋ฆญํ•ฉ๋‹ˆ๋‹ค.
  2. ๋””๋ฒ„๊น… ์‹œ์ž‘: Visual Studio์—์„œ F5 ํ‚ค๋ฅผ ๋ˆŒ๋Ÿฌ ๋””๋ฒ„๊น… ๋ชจ๋“œ๋กœ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. ๋””๋ฒ„๊น… ๋ชจ๋“œ์—์„œ๋Š” ์ค‘๋‹จ์ ์—์„œ ์ฝ”๋“œ ์‹คํ–‰์ด ์ค‘๋‹จ๋˜๊ณ , ๋ณ€์ˆ˜ ๊ฐ’, ํ˜ธ์ถœ ์Šคํƒ, ๋กœ์ปฌ ๊ฐ’ ๋“ฑ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

2. ๋ณ€์ˆ˜ ๊ฒ€์‚ฌ ๋ฐ ๋‹จ๊ณ„๋ณ„ ์‹คํ–‰

์ค‘๋‹จ์ ์— ๋„๋‹ฌํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • ๋ณ€์ˆ˜ ๊ฐ’ ๊ฒ€์‚ฌ: ๋งˆ์šฐ์Šค๋ฅผ ๋ณ€์ˆ˜ ์œ„์— ์˜ฌ๋ฆฌ๋ฉด ํ˜„์žฌ ๊ฐ’์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๋‹จ๊ณ„๋ณ„ ์‹คํ–‰: F10 ํ‚ค๋ฅผ ๋ˆŒ๋Ÿฌ ํ•œ ์ค„์”ฉ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๊ฑฐ๋‚˜, F11 ํ‚ค๋ฅผ ๋ˆŒ๋Ÿฌ ํ•จ์ˆ˜ ๋‚ด๋ถ€๋กœ ๋“ค์–ด๊ฐ‘๋‹ˆ๋‹ค.
  • ํ˜ธ์ถœ ์Šคํƒ ํ™•์ธ: ํ˜ธ์ถœ ์Šคํƒ ์ฐฝ์„ ํ†ตํ•ด ํ˜„์žฌ ํ•จ์ˆ˜๊ฐ€ ์–ด๋–ป๊ฒŒ ํ˜ธ์ถœ๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ฒฐ๋ก 

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


12. ์œ ๋‹› ํ…Œ์ŠคํŠธ ์ž‘์„ฑ ๋ฐ ์‹คํ–‰

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

์œ ๋‹› ํ…Œ์ŠคํŠธ์˜ ์ค‘์š”์„ฑ

์œ ๋‹› ํ…Œ์ŠคํŠธ๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ํŠน์ • ๊ธฐ๋Šฅ์„ ๋…๋ฆฝ์ ์œผ๋กœ ๊ฒ€์ฆํ•˜๋Š” ํ…Œ์ŠคํŠธ์ž…๋‹ˆ๋‹ค. ์œ ๋‹› ํ…Œ์ŠคํŠธ๋ฅผ ํ†ตํ•ด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์žฅ์ ์„ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

  1. ์ฝ”๋“œ ์‹ ๋ขฐ์„ฑ ์ฆ๊ฐ€: ์ฝ”๋“œ ๋ณ€๊ฒฝ ์‹œ ๊ธฐ๋Šฅ์ด ์ œ๋Œ€๋กœ ์ž‘๋™ํ•˜๋Š”์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  2. ๋ฌธ์„œํ™”: ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋Š” ์ฝ”๋“œ์˜ ๋™์ž‘ ๋ฐฉ์‹์„ ๋ฌธ์„œํ™”ํ•˜๋Š” ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.
  3. ๋ฆฌํŒฉํ† ๋ง ์ง€์›: ์ฝ”๋“œ ๋ฆฌํŒฉํ† ๋ง ์‹œ ๊ธฐ๋Šฅ์ด ๊นจ์ง€์ง€ ์•Š๋„๋ก ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค.

xUnit ๋ฐ Moq ์„ค์น˜

์œ ๋‹› ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ•˜๊ธฐ ์œ„ํ•ด xUnit ๋ฐ Moq ํŒจํ‚ค์ง€๋ฅผ ์„ค์น˜ํ•ฉ๋‹ˆ๋‹ค.

powershell์ฝ”๋“œ ๋ณต์‚ฌ
dotnet add package xunit
dotnet add package xunit.runner.visualstudio
dotnet add package Moq

์œ ๋‹› ํ…Œ์ŠคํŠธ ์ž‘์„ฑ

์œ ๋‹› ํ…Œ์ŠคํŠธ๋Š” ํ”„๋กœ์ ํŠธ์˜ ๊ธฐ๋Šฅ์„ ๊ฒ€์ฆํ•˜๊ธฐ ์œ„ํ•ด ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค. ์•„๋ž˜๋Š” WorkInstructionService ํด๋ž˜์Šค์— ๋Œ€ํ•œ ์œ ๋‹› ํ…Œ์ŠคํŠธ ์˜ˆ์ œ์ž…๋‹ˆ๋‹ค.

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

public class WorkInstructionServiceTests
{
    private readonly Mock<AppDbContext> _mockContext;
    private readonly Mock<ILogger<WorkInstructionService>> _mockLogger;
    private readonly WorkInstructionService _service;

    public WorkInstructionServiceTests()
    {
        _mockContext = new Mock<AppDbContext>();
        _mockLogger = new Mock<ILogger<WorkInstructionService>>();
        _service = new WorkInstructionService(_mockContext.Object, _mockLogger.Object);
    }

    [Fact]
    public async Task GetWorkInstructionsAsync_ReturnsAllInstructions()
    {
        // Arrange
        var data = new List<WorkInstruction>
        {
            new WorkInstruction { TaskName = "Task1", Content = "Content1" },
            new WorkInstruction { TaskName = "Task2", Content = "Content2" }
        };
        var mockSet = new Mock<DbSet<WorkInstruction>>();
        mockSet.As<IQueryable<WorkInstruction>>().Setup(m => m.Provider).Returns(data.AsQueryable().Provider);
        mockSet.As<IQueryable<WorkInstruction>>().Setup(m => m.Expression).Returns(data.AsQueryable().Expression);
        mockSet.As<IQueryable<WorkInstruction>>().Setup(m => m.ElementType).Returns(data.AsQueryable().ElementType);
        mockSet.As<IQueryable<WorkInstruction>>().Setup(m => m.GetEnumerator()).Returns(data.AsQueryable().GetEnumerator());

        _mockContext.Setup(c => c.WorkInstructions).Returns(mockSet.Object);

        // Act
        var result = await _service.GetWorkInstructionsAsync();

        // Assert
        Assert.Equal(2, result.Count());
        Assert.Equal("Task1", result.First().TaskName);
    }

    [Fact]
    public async Task AddWorkInstructionAsync_AddsInstruction()
    {
        // Arrange
        var newInstruction = new WorkInstruction { TaskName = "Task3", Content = "Content3" };
        var mockSet = new Mock<DbSet<WorkInstruction>>();
        _mockContext.Setup(c => c.WorkInstructions).Returns(mockSet.Object);

        // Act
        await _service.AddWorkInstructionAsync(newInstruction);

        // Assert
        mockSet.Verify(m => m.Add(It.IsAny<WorkInstruction>()), Times.Once());
        _mockContext.Verify(c => c.SaveChangesAsync(default), Times.Once());
    }

    [Fact]
    public async Task DeleteWorkInstructionAsync_DeletesInstruction()
    {
        // Arrange
        var data = new List<WorkInstruction>
        {
            new WorkInstruction { TaskName = "Task1", Content = "Content1" },
            new WorkInstruction { TaskName = "Task2", Content = "Content2" }
        };
        var mockSet = new Mock<DbSet<WorkInstruction>>();
        mockSet.As<IQueryable<WorkInstruction>>().Setup(m => m.Provider).Returns(data.AsQueryable().Provider);
        mockSet.As<IQueryable<WorkInstruction>>().Setup(m => m.Expression).Returns(data.AsQueryable().Expression);
        mockSet.As<IQueryable<WorkInstruction>>().Setup(m => m.ElementType).Returns(data.AsQueryable().ElementType);
        mockSet.As<IQueryable<WorkInstruction>>().Setup(m => m.GetEnumerator()).Returns(data.AsQueryable().GetEnumerator());

        _mockContext.Setup(c => c.WorkInstructions).Returns(mockSet.Object);

        // Act
        await _service.DeleteWorkInstructionAsync("Task1");

        // Assert
        mockSet.Verify(m => m.Remove(It.IsAny<WorkInstruction>()), Times.Once());
        _mockContext.Verify(c => c.SaveChangesAsync(default), Times.Once());
    }
}

์œ ๋‹› ํ…Œ์ŠคํŠธ ์‹คํ–‰

Visual Studio๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์œ ๋‹› ํ…Œ์ŠคํŠธ๋ฅผ ์‹คํ–‰ํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค:

  1. ํ…Œ์ŠคํŠธ ํƒ์ƒ‰๊ธฐ ์—ด๊ธฐ: Visual Studio ์ƒ๋‹จ ๋ฉ”๋‰ด์—์„œ Test -> **Test Explorer**๋ฅผ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค.
  2. ํ…Œ์ŠคํŠธ ์‹คํ–‰: Test Explorer ์ฐฝ์—์„œ ๋ชจ๋“  ํ…Œ์ŠคํŠธ๋ฅผ ์„ ํƒํ•˜๊ณ , Run All ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜์—ฌ ํ…Œ์ŠคํŠธ๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.

๋ช…๋ น์ค„์—์„œ ํ…Œ์ŠคํŠธ๋ฅผ ์‹คํ–‰ํ•˜๋ ค๋ฉด ๋‹ค์Œ ๋ช…๋ น์–ด๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค:

powershell์ฝ”๋“œ ๋ณต์‚ฌ
dotnet test

๊ฒฐ๋ก 

์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” ์œ ๋‹› ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ•˜๊ณ  ์‹คํ–‰ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋‹ค๋ฃจ์—ˆ์Šต๋‹ˆ๋‹ค. ์œ ๋‹› ํ…Œ์ŠคํŠธ๋ฅผ ํ†ตํ•ด ์ฝ”๋“œ์˜ ๊ฐœ๋ณ„ ๊ธฐ๋Šฅ์ด ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ๋™์ž‘ํ•˜๋Š”์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ฝ”๋“œ์˜ ์‹ ๋ขฐ์„ฑ์„ ๋†’์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ ํฌ์ŠคํŒ…์—์„œ๋Š” ๋” ๋ณต์žกํ•œ ์‹œ๋‚˜๋ฆฌ์˜ค์— ๋Œ€ํ•œ ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ์™€ ๋ชจ์˜ ๊ฐ์ฒด(Mock Objects)๋ฅผ ์‚ฌ์šฉํ•œ ํ…Œ์ŠคํŠธ ๊ธฐ๋ฒ•์„ ๋‹ค๋ฃจ๊ฒ ์Šต๋‹ˆ๋‹ค.


14. CI/CD ํŒŒ์ดํ”„๋ผ์ธ ์„ค์ •๊ณผ ํ…Œ์ŠคํŠธ ์ž๋™ํ™”

์„œ๋ก : ์ด์ „ ํฌ์ŠคํŒ…์—์„œ๋Š” ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ์™€ ๋ชจ์˜ ๊ฐ์ฒด(Mock Objects)๋ฅผ ์‚ฌ์šฉํ•œ ์œ ๋‹› ํ…Œ์ŠคํŠธ ๊ธฐ๋ฒ•์„ ์‚ดํŽด๋ณด์•˜์Šต๋‹ˆ๋‹ค. ์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” CI/CD(Continuous Integration/Continuous Deployment) ํŒŒ์ดํ”„๋ผ์ธ์„ ์„ค์ •ํ•˜๊ณ  ํ…Œ์ŠคํŠธ ์ž๋™ํ™”๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋‹ค๋ฃจ๊ฒ ์Šต๋‹ˆ๋‹ค. CI/CD ํŒŒ์ดํ”„๋ผ์ธ์„ ํ†ตํ•ด ์ฝ”๋“œ๋ฅผ ์ž๋™์œผ๋กœ ๋นŒ๋“œ, ํ…Œ์ŠคํŠธ, ๋ฐฐํฌํ•˜์—ฌ ๊ฐœ๋ฐœ ํšจ์œจ์„ฑ๊ณผ ์ฝ”๋“œ ํ’ˆ์งˆ์„ ๋†’์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

CI/CD ํŒŒ์ดํ”„๋ผ์ธ์˜ ํ•„์š”์„ฑ

CI/CD ํŒŒ์ดํ”„๋ผ์ธ์€ ์ฝ”๋“œ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ์ž๋™์œผ๋กœ ํ†ตํ•ฉํ•˜๊ณ  ๋ฐฐํฌํ•˜๋Š” ํ”„๋กœ์„ธ์Šค๋ฅผ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ๊ฐœ๋ฐœ ์ฃผ๊ธฐ๋ฅผ ๋‹จ์ถ•ํ•˜๊ณ , ๋ฒ„๊ทธ๋ฅผ ์กฐ๊ธฐ์— ๋ฐœ๊ฒฌํ•˜๋ฉฐ, ์ œํ’ˆ์˜ ์•ˆ์ •์„ฑ์„ ๋†’์ด๋Š” ๋ฐ ์ค‘์š”ํ•œ ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.

  1. ์ž๋™ํ™”๋œ ๋นŒ๋“œ: ์ฝ”๋“œ ๋ณ€๊ฒฝ ์‹œ๋งˆ๋‹ค ์ž๋™์œผ๋กœ ๋นŒ๋“œํ•˜์—ฌ ์ฝ”๋“œ์˜ ์ปดํŒŒ์ผ ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
  2. ์ž๋™ํ™”๋œ ํ…Œ์ŠคํŠธ: ๋นŒ๋“œ ๊ณผ์ •์—์„œ ์ž๋™์œผ๋กœ ํ…Œ์ŠคํŠธ๋ฅผ ์‹คํ–‰ํ•˜์—ฌ ์ฝ”๋“œ์˜ ๊ธฐ๋Šฅ์„ ๊ฒ€์ฆํ•ฉ๋‹ˆ๋‹ค.
  3. ์ž๋™ํ™”๋œ ๋ฐฐํฌ: ์ฝ”๋“œ๊ฐ€ ๋นŒ๋“œ ๋ฐ ํ…Œ์ŠคํŠธ๋ฅผ ํ†ต๊ณผํ•˜๋ฉด ์ž๋™์œผ๋กœ ๋ฐฐํฌํ•˜์—ฌ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ๋น ๋ฅด๊ฒŒ ์‚ฌ์šฉ์ž์—๊ฒŒ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.

GitHub Actions๋ฅผ ์‚ฌ์šฉํ•œ CI/CD ํŒŒ์ดํ”„๋ผ์ธ ์„ค์ •

GitHub Actions๋Š” GitHub์—์„œ ์ œ๊ณตํ•˜๋Š” CI/CD ๋„๊ตฌ๋กœ, ๋ฆฌํฌ์ง€ํ† ๋ฆฌ์— ๋ณ€๊ฒฝ ์‚ฌํ•ญ์ด ๋ฐœ์ƒํ•  ๋•Œ๋งˆ๋‹ค ์ž๋™์œผ๋กœ ์›Œํฌํ”Œ๋กœ์šฐ๋ฅผ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ์€ GitHub Actions๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ CI/CD ํŒŒ์ดํ”„๋ผ์ธ์„ ์„ค์ •ํ•˜๋Š” ์˜ˆ์ œ์ž…๋‹ˆ๋‹ค.

1. GitHub Actions ์›Œํฌํ”Œ๋กœ์šฐ ํŒŒ์ผ ์ƒ์„ฑ

๋ฆฌํฌ์ง€ํ† ๋ฆฌ์˜ .github/workflows ๋””๋ ‰ํ† ๋ฆฌ์— ci-cd-pipeline.yml ํŒŒ์ผ์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

yaml์ฝ”๋“œ ๋ณต์‚ฌ
name: CI/CD Pipeline

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
    - name: Checkout code
      uses: actions/checkout@v2

    - name: Setup .NET Core
      uses: actions/setup-dotnet@v1
      with:
        dotnet-version: 6.0.x

    - name: Restore dependencies
      run: dotnet restore

    - name: Build
      run: dotnet build --configuration Release --no-restore

    - name: Run tests
      run: dotnet test --no-build --verbosity normal

  deploy:
    runs-on: ubuntu-latest
    needs: build

    steps:
    - name: Checkout code
      uses: actions/checkout@v2

    - name: Setup .NET Core
      uses: actions/setup-dotnet@v1
      with:
        dotnet-version: 6.0.x

    - name: Publish
      run: dotnet publish --configuration Release --no-build -o ./publish

    - name: Deploy to Azure
      uses: azure/webapps-deploy@v2
      with:
        app-name: 'YourAppName'
        publish-profile: ${{ secrets.AZURE_WEBAPP_PUBLISH_PROFILE }}
        package: ./publish

ํ…Œ์ŠคํŠธ ์ž๋™ํ™”

ํ…Œ์ŠคํŠธ ์ž๋™ํ™”๋ฅผ ํ†ตํ•ด ๋ชจ๋“  ์ฝ”๋“œ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์ด ํ†ตํ•ฉ๋  ๋•Œ๋งˆ๋‹ค ์ž๋™์œผ๋กœ ํ…Œ์ŠคํŠธ๋ฅผ ์‹คํ–‰ํ•˜๊ณ  ๊ฒฐ๊ณผ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ์ฝ”๋“œ์˜ ์‹ ๋ขฐ์„ฑ์„ ๋†’์ด๊ณ , ๋ฒ„๊ทธ๋ฅผ ์กฐ๊ธฐ์— ๋ฐœ๊ฒฌํ•˜๋Š” ๋ฐ ๋„์›€์ด ๋ฉ๋‹ˆ๋‹ค.

1. ํ…Œ์ŠคํŠธ ํ”„๋กœ์ ํŠธ ์„ค์ •

ํ…Œ์ŠคํŠธ ํ”„๋กœ์ ํŠธ๋ฅผ ์„ค์ •ํ•˜๊ณ , ํ•„์š”ํ•œ ํŒจํ‚ค์ง€๋ฅผ ์„ค์น˜ํ•ฉ๋‹ˆ๋‹ค.

bash์ฝ”๋“œ ๋ณต์‚ฌ
dotnet new xunit -o Tests
dotnet add Tests package Moq
dotnet add Tests package Microsoft.EntityFrameworkCore.InMemory

2. ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ž‘์„ฑ

ํ…Œ์ŠคํŠธ ํ”„๋กœ์ ํŠธ์— ์œ ๋‹› ํ…Œ์ŠคํŠธ์™€ ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.

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

public class WorkInstructionServiceTests
{
    private readonly Mock<AppDbContext> _mockContext;
    private readonly Mock<ILogger<WorkInstructionService>> _mockLogger;
    private readonly WorkInstructionService _service;

    public WorkInstructionServiceTests()
    {
        _mockContext = new Mock<AppDbContext>();
        _mockLogger = new Mock<ILogger<WorkInstructionService>>();
        _service = new WorkInstructionService(_mockContext.Object, _mockLogger.Object);
    }

    [Fact]
    public async Task GetWorkInstructionsAsync_ReturnsAllInstructions()
    {
        // Arrange
        var data = new List<WorkInstruction>
        {
            new WorkInstruction { TaskName = "Task1", Content = "Content1" },
            new WorkInstruction { TaskName = "Task2", Content = "Content2" }
        };
        var mockSet = new Mock<DbSet<WorkInstruction>>();
        mockSet.As<IQueryable<WorkInstruction>>().Setup(m => m.Provider).Returns(data.AsQueryable().Provider);
        mockSet.As<IQueryable<WorkInstruction>>().Setup(m => m.Expression).Returns(data.AsQueryable().Expression);
        mockSet.As<IQueryable<WorkInstruction>>().Setup(m => m.ElementType).Returns(data.AsQueryable().ElementType);
        mockSet.As<IQueryable<WorkInstruction>>().Setup(m => m.GetEnumerator()).Returns(data.AsQueryable().GetEnumerator());

        _mockContext.Setup(c => c.WorkInstructions).Returns(mockSet.Object);

        // Act
        var result = await _service.GetWorkInstructionsAsync();

        // Assert
        Assert.Equal(2, result.Count());
        Assert.Equal("Task1", result.First().TaskName);
    }
}

๊ฒฐ๋ก 

์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” CI/CD ํŒŒ์ดํ”„๋ผ์ธ์„ ์„ค์ •ํ•˜๊ณ , ํ…Œ์ŠคํŠธ ์ž๋™ํ™”๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์‚ดํŽด๋ณด์•˜์Šต๋‹ˆ๋‹ค. GitHub Actions๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ CI/CD ํŒŒ์ดํ”„๋ผ์ธ์„ ์„ค์ •ํ•˜๊ณ , ์ž๋™์œผ๋กœ ๋นŒ๋“œ, ํ…Œ์ŠคํŠธ, ๋ฐฐํฌํ•˜๋Š” ํ”„๋กœ์„ธ์Šค๋ฅผ ๊ตฌ์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค. ํ…Œ์ŠคํŠธ ์ž๋™ํ™”๋ฅผ ํ†ตํ•ด ๋ชจ๋“  ์ฝ”๋“œ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์ด ํ†ตํ•ฉ๋  ๋•Œ๋งˆ๋‹ค ์ž๋™์œผ๋กœ ํ…Œ์ŠคํŠธ๋ฅผ ์‹คํ–‰ํ•˜๊ณ  ๊ฒฐ๊ณผ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๋ฐฉ๋ฒ•์„ ํ†ตํ•ด ์ฝ”๋“œ์˜ ์‹ ๋ขฐ์„ฑ๊ณผ ์œ ์ง€๋ณด์ˆ˜์„ฑ์„ ๋†’์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ ํฌ์ŠคํŒ…์—์„œ๋Š” ์ฝ”๋“œ ํ’ˆ์งˆ์„ ๋†’์ด๋Š” ๋‹ค์–‘ํ•œ ๋„๊ตฌ์™€ ๊ธฐ๋ฒ•์„ ์†Œ๊ฐœํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.


15. ์ฝ”๋“œ ํ’ˆ์งˆ ํ–ฅ์ƒ์„ ์œ„ํ•œ ์ •์  ์ฝ”๋“œ ๋ถ„์„ ๋„๊ตฌ ์‚ฌ์šฉ

์„œ๋ก : ์ด์ „ ํฌ์ŠคํŒ…์—์„œ๋Š” CI/CD ํŒŒ์ดํ”„๋ผ์ธ ์„ค์ •๊ณผ ํ…Œ์ŠคํŠธ ์ž๋™ํ™” ๋ฐฉ๋ฒ•์„ ๋‹ค๋ฃจ์—ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” ์ฝ”๋“œ ํ’ˆ์งˆ์„ ๋†’์ด๊ธฐ ์œ„ํ•œ ์ •์  ์ฝ”๋“œ ๋ถ„์„ ๋„๊ตฌ์˜ ์‚ฌ์šฉ ๋ฐฉ๋ฒ•์„ ์†Œ๊ฐœํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ์ •์  ์ฝ”๋“œ ๋ถ„์„ ๋„๊ตฌ๋Š” ์ฝ”๋“œ์˜ ์ž ์žฌ์ ์ธ ๋ฒ„๊ทธ, ๋ณด์•ˆ ์ทจ์•ฝ์ , ์Šคํƒ€์ผ ๋ฌธ์ œ ๋“ฑ์„ ์‚ฌ์ „์— ๋ฐœ๊ฒฌํ•˜์—ฌ ์ฝ”๋“œ ํ’ˆ์งˆ์„ ํ–ฅ์ƒ์‹œํ‚ค๋Š” ๋ฐ ์ค‘์š”ํ•œ ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.

์ •์  ์ฝ”๋“œ ๋ถ„์„ ๋„๊ตฌ์˜ ํ•„์š”์„ฑ

์ •์  ์ฝ”๋“œ ๋ถ„์„ ๋„๊ตฌ๋Š” ์†Œ์Šค ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜์ง€ ์•Š๊ณ  ๋ถ„์„ํ•˜์—ฌ ์ฝ”๋“œ์˜ ๊ฒฐํ•จ์„ ์ฐพ๋Š” ๋„๊ตฌ์ž…๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ด์ ์„ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

  1. ๋ฒ„๊ทธ ์‚ฌ์ „ ๋ฐœ๊ฒฌ: ์ฝ”๋“œ์˜ ์ž ์žฌ์ ์ธ ๋ฒ„๊ทธ๋ฅผ ์‚ฌ์ „์— ๋ฐœ๊ฒฌํ•˜์—ฌ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  2. ์ฝ”๋“œ ์ผ๊ด€์„ฑ ์œ ์ง€: ์ฝ”๋“œ ์Šคํƒ€์ผ ๊ฐ€์ด๋“œ๋ฅผ ์ค€์ˆ˜ํ•˜์—ฌ ์ผ๊ด€๋œ ์ฝ”๋“œ ๋ฒ ์ด์Šค๋ฅผ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  3. ๋ณด์•ˆ ๊ฐ•ํ™”: ๋ณด์•ˆ ์ทจ์•ฝ์ ์„ ์‚ฌ์ „์— ๋ฐœ๊ฒฌํ•˜์—ฌ ์•ˆ์ „ํ•œ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  4. ์ฝ”๋“œ ์œ ์ง€๋ณด์ˆ˜์„ฑ ํ–ฅ์ƒ: ์ฝ”๋“œ์˜ ๊ฐ€๋…์„ฑ๊ณผ ์œ ์ง€๋ณด์ˆ˜์„ฑ์„ ๋†’์—ฌ ์žฅ๊ธฐ์ ์œผ๋กœ ๊ด€๋ฆฌํ•˜๊ธฐ ์‰ฝ์Šต๋‹ˆ๋‹ค.

SonarQube๋ฅผ ์‚ฌ์šฉํ•œ ์ •์  ์ฝ”๋“œ ๋ถ„์„

SonarQube๋Š” ์ฝ”๋“œ ํ’ˆ์งˆ ๊ด€๋ฆฌ ๋„๊ตฌ๋กœ, ๋‹ค์–‘ํ•œ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด๋ฅผ ์ง€์›ํ•˜๋ฉฐ, ์ฝ”๋“œ์˜ ํ’ˆ์งˆ, ๋ณด์•ˆ, ์œ ์ง€๋ณด์ˆ˜์„ฑ์„ ํ‰๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. SonarQube๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ .NET ํ”„๋กœ์ ํŠธ๋ฅผ ๋ถ„์„ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

1. SonarQube ์„ค์น˜ ๋ฐ ์„ค์ •

SonarQube๋ฅผ ์„ค์น˜ํ•˜๊ณ  ์„ค์ •ํ•˜๋Š” ๊ณผ์ •์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค:

  1. SonarQube ๋‹ค์šด๋กœ๋“œ ๋ฐ ์„ค์น˜: SonarQube ๊ณต์‹ ์›น์‚ฌ์ดํŠธ์—์„œ SonarQube๋ฅผ ๋‹ค์šด๋กœ๋“œํ•˜๊ณ  ์„ค์น˜ํ•ฉ๋‹ˆ๋‹ค.
  2. SonarQube ์„œ๋ฒ„ ์‹คํ–‰: SonarQube ์„œ๋ฒ„๋ฅผ ์‹คํ–‰ํ•˜์—ฌ ์›น ์ธํ„ฐํŽ˜์ด์Šค์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋„๋ก ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
  3. bash์ฝ”๋“œ ๋ณต์‚ฌ # SonarQube ์„œ๋ฒ„ ์‹คํ–‰ ./bin/linux-x86-64/sonar.sh start
  4. ์›น ๋ธŒ๋ผ์šฐ์ €์—์„œ SonarQube ์ ‘์†: ์›น ๋ธŒ๋ผ์šฐ์ €์—์„œ **http://localhost:9000**์— ์ ‘์†ํ•˜์—ฌ SonarQube ๋Œ€์‹œ๋ณด๋“œ์— ์ ‘๊ทผํ•ฉ๋‹ˆ๋‹ค.

2. SonarQube ํ”„๋กœ์ ํŠธ ์„ค์ •

  1. SonarQube ํ† ํฐ ์ƒ์„ฑ: SonarQube ๋Œ€์‹œ๋ณด๋“œ์—์„œ ์ƒˆ๋กœ์šด ํ”„๋กœ์ ํŠธ๋ฅผ ์ƒ์„ฑํ•˜๊ณ , ํ”„๋กœ์ ํŠธ ํ‚ค์™€ ์ด๋ฆ„์„ ์ž…๋ ฅํ•ฉ๋‹ˆ๋‹ค. ํ”„๋กœ์ ํŠธ์— ๋Œ€ํ•œ ์ธ์ฆ ํ† ํฐ์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
  2. SonarScanner ์„ค์น˜: SonarScanner๋Š” ์†Œ์Šค ์ฝ”๋“œ๋ฅผ ๋ถ„์„ํ•˜์—ฌ SonarQube ์„œ๋ฒ„๋กœ ๊ฒฐ๊ณผ๋ฅผ ์ „์†กํ•˜๋Š” ๋„๊ตฌ์ž…๋‹ˆ๋‹ค. ๋‹ค์Œ ๋ช…๋ น์–ด๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ SonarScanner๋ฅผ ์„ค์น˜ํ•ฉ๋‹ˆ๋‹ค.
  3. bash์ฝ”๋“œ ๋ณต์‚ฌ # SonarScanner ์„ค์น˜ dotnet tool install --global dotnet-sonarscanner
  4. SonarQube ์„ค์ • ํŒŒ์ผ ์ž‘์„ฑ: ํ”„๋กœ์ ํŠธ ๋ฃจํŠธ ๋””๋ ‰ํ† ๋ฆฌ์— sonar-project.properties ํŒŒ์ผ์„ ์ƒ์„ฑํ•˜๊ณ , SonarQube ์„œ๋ฒ„์™€ ํ”„๋กœ์ ํŠธ ์ •๋ณด๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
  5. properties์ฝ”๋“œ ๋ณต์‚ฌ # ํ”„๋กœ์ ํŠธ ํ‚ค์™€ ์ด๋ฆ„ ์„ค์ • sonar.projectKey=YourProjectKey sonar.projectName=YourProjectName # SonarQube ์„œ๋ฒ„ URL ์„ค์ • sonar.host.url=http://localhost:9000 # ์†Œ์Šค ์ฝ”๋“œ ๋””๋ ‰ํ† ๋ฆฌ ์„ค์ • sonar.sources=. # ์ธ์ฆ ํ† ํฐ ์„ค์ • sonar.login=YourSonarQubeToken

3. ์ฝ”๋“œ ๋ถ„์„ ๋ฐ ๊ฒฐ๊ณผ ํ™•์ธ

  1. SonarScanner๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ฝ”๋“œ ๋ถ„์„:
  2. bash์ฝ”๋“œ ๋ณต์‚ฌ # SonarScanner๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ฝ”๋“œ ๋ถ„์„ dotnet sonarscanner begin /k:"YourProjectKey" /d:sonar.login="YourSonarQubeToken" dotnet build dotnet sonarscanner end /d:sonar.login="YourSonarQubeToken"
  3. SonarQube ๋Œ€์‹œ๋ณด๋“œ์—์„œ ๊ฒฐ๊ณผ ํ™•์ธ: SonarQube ๋Œ€์‹œ๋ณด๋“œ์—์„œ ํ”„๋กœ์ ํŠธ๋ฅผ ์„ ํƒํ•˜๊ณ , ๋ถ„์„ ๊ฒฐ๊ณผ๋ฅผ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. ์ฝ”๋“œ์˜ ๋ฒ„๊ทธ, ๋ณด์•ˆ ์ทจ์•ฝ์ , ์ฝ”๋“œ ์Šค๋ฉœ ๋“ฑ์„ ์‹œ๊ฐ์ ์œผ๋กœ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

StyleCop์„ ์‚ฌ์šฉํ•œ ์ฝ”๋“œ ์Šคํƒ€์ผ ๊ฒ€์‚ฌ

StyleCop์€ .NET ์ฝ”๋“œ๋ฅผ ๋Œ€์ƒ์œผ๋กœ ์ฝ”๋“œ ์Šคํƒ€์ผ ๊ฐ€์ด๋“œ๋ฅผ ๊ฒ€์‚ฌํ•˜๊ณ , ์ฝ”๋“œ์˜ ์ผ๊ด€์„ฑ์„ ์œ ์ง€ํ•˜๋Š” ๋ฐ ๋„์›€์„ ์ฃผ๋Š” ๋„๊ตฌ์ž…๋‹ˆ๋‹ค.

1. StyleCop ์„ค์น˜

ํ”„๋กœ์ ํŠธ์— StyleCop์„ ์„ค์น˜ํ•ฉ๋‹ˆ๋‹ค.

bash์ฝ”๋“œ ๋ณต์‚ฌ
# StyleCop ์„ค์น˜
dotnet add package StyleCop.Analyzers

2. StyleCop ์„ค์ • ํŒŒ์ผ ์ž‘์„ฑ

ํ”„๋กœ์ ํŠธ ๋ฃจํŠธ ๋””๋ ‰ํ† ๋ฆฌ์— stylecop.json ํŒŒ์ผ์„ ์ƒ์„ฑํ•˜๊ณ , ์Šคํƒ€์ผ ๊ทœ์น™์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

json์ฝ”๋“œ ๋ณต์‚ฌ
{
  "settings": {
    "documentationRules": {
      "companyName": "YourCompanyName",
      "copyrightText": "Copyright © YourCompanyName. All rights reserved."
    }
  }
}

3. ์ฝ”๋“œ ์Šคํƒ€์ผ ๊ฒ€์‚ฌ

Visual Studio ๋˜๋Š” CLI๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ฝ”๋“œ๋ฅผ ๋นŒ๋“œํ•˜๋ฉด StyleCop ๊ทœ์น™์— ๋”ฐ๋ผ ์ฝ”๋“œ ์Šคํƒ€์ผ์„ ๊ฒ€์‚ฌํ•˜๊ณ , ์œ„๋ฐ˜ ์‚ฌํ•ญ์„ ๊ฒฝ๊ณ ๋กœ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค.

๊ฒฐ๋ก 

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


16. ์œ ๋‹› ํ…Œ์ŠคํŠธ ์ž‘์„ฑ ๋ฐ ์‹คํ–‰

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

์œ ๋‹› ํ…Œ์ŠคํŠธ์˜ ์ค‘์š”์„ฑ

์œ ๋‹› ํ…Œ์ŠคํŠธ๋Š” ์†Œํ”„ํŠธ์›จ์–ด ๊ฐœ๋ฐœ์—์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ค‘์š”ํ•œ ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค:

  1. ๋ฒ„๊ทธ ์กฐ๊ธฐ ๋ฐœ๊ฒฌ: ์ฝ”๋“œ์˜ ์ž‘์€ ๋‹จ์œ„์—์„œ ๋ฒ„๊ทธ๋ฅผ ๋ฐœ๊ฒฌํ•˜์—ฌ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  2. ์ฝ”๋“œ ๋ณ€๊ฒฝ์˜ ์•ˆ์ „์„ฑ: ์ฝ”๋“œ ์ˆ˜์ • ์‹œ ๊ธฐ์กด ๊ธฐ๋Šฅ์ด ์ •์ƒ์ ์œผ๋กœ ์ž‘๋™ํ•˜๋Š”์ง€ ๊ฒ€์ฆํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  3. ๋ฌธ์„œํ™”: ์ฝ”๋“œ์˜ ์˜ˆ์ƒ ๋™์ž‘์„ ๋ช…ํ™•ํžˆ ํ•˜์—ฌ ๋ฌธ์„œํ™”์˜ ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.
  4. ๋ฆฌํŒฉํ† ๋ง ์ง€์›: ๋ฆฌํŒฉํ† ๋ง ์‹œ ์ฝ”๋“œ์˜ ๋™์ž‘์„ ๋ณด์žฅํ•˜์—ฌ ์•ˆ์ „ํ•˜๊ฒŒ ๊ตฌ์กฐ๋ฅผ ๊ฐœ์„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

xUnit์„ ์‚ฌ์šฉํ•œ ์œ ๋‹› ํ…Œ์ŠคํŠธ

xUnit์€ .NET ์ฝ”์–ด์—์„œ ๋„๋ฆฌ ์‚ฌ์šฉ๋˜๋Š” ์œ ๋‹› ํ…Œ์ŠคํŠธ ํ”„๋ ˆ์ž„์›Œํฌ์ž…๋‹ˆ๋‹ค. xUnit์„ ์‚ฌ์šฉํ•˜์—ฌ .NET ํ”„๋กœ์ ํŠธ์˜ ์œ ๋‹› ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

1. xUnit ํŒจํ‚ค์ง€ ์„ค์น˜

ํ”„๋กœ์ ํŠธ์— xUnit ํŒจํ‚ค์ง€๋ฅผ ์„ค์น˜ํ•ฉ๋‹ˆ๋‹ค.

bash์ฝ”๋“œ ๋ณต์‚ฌ
# xUnit ๋ฐ ๊ด€๋ จ ํŒจํ‚ค์ง€ ์„ค์น˜
dotnet add package xunit
dotnet add package xunit.runner.visualstudio
dotnet add package Moq

2. ์œ ๋‹› ํ…Œ์ŠคํŠธ ํด๋ž˜์Šค ์ƒ์„ฑ

์œ ๋‹› ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ•  ํด๋ž˜์Šค๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ์ œ๋กœ WorkInstructionService ํด๋ž˜์Šค์˜ ์œ ๋‹› ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

WorkInstructionServiceTests.cs:

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

public class WorkInstructionServiceTests
{
    private readonly Mock<AppDbContext> _mockContext;
    private readonly Mock<ILogger<WorkInstructionService>> _mockLogger;
    private readonly WorkInstructionService _service;

    public WorkInstructionServiceTests()
    {
        _mockContext = new Mock<AppDbContext>();
        _mockLogger = new Mock<ILogger<WorkInstructionService>>();
        _service = new WorkInstructionService(_mockContext.Object, _mockLogger.Object);
    }

    [Fact]
    public async Task GetWorkInstructionsAsync_ReturnsAllInstructions()
    {
        // Arrange
        var data = new List<WorkInstruction>
        {
            new WorkInstruction { TaskName = "Task1", Content = "Content1" },
            new WorkInstruction { TaskName = "Task2", Content = "Content2" }
        }.AsQueryable();

        var mockSet = new Mock<DbSet<WorkInstruction>>();
        mockSet.As<IQueryable<WorkInstruction>>().Setup(m => m.Provider).Returns(data.Provider);
        mockSet.As<IQueryable<WorkInstruction>>().Setup(m => m.Expression).Returns(data.Expression);
        mockSet.As<IQueryable<WorkInstruction>>().Setup(m => m.ElementType).Returns(data.ElementType);
        mockSet.As<IQueryable<WorkInstruction>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());

        _mockContext.Setup(c => c.WorkInstructions).Returns(mockSet.Object);

        // Act
        var result = await _service.GetWorkInstructionsAsync();

        // Assert
        Assert.Equal(2, result.Count());
        Assert.Equal("Task1", result.First().TaskName);
    }

    [Fact]
    public async Task AddWorkInstructionAsync_AddsInstruction()
    {
        // Arrange
        var newInstruction = new WorkInstruction { TaskName = "Task3", Content = "Content3" };
        var mockSet = new Mock<DbSet<WorkInstruction>>();

        _mockContext.Setup(m => m.WorkInstructions).Returns(mockSet.Object);

        // Act
        await _service.AddWorkInstructionAsync(newInstruction);

        // Assert
        mockSet.Verify(m => m.Add(It.IsAny<WorkInstruction>()), Times.Once());
        _mockContext.Verify(m => m.SaveChangesAsync(It.IsAny<CancellationToken>()), Times.Once());
    }

    [Fact]
    public async Task UpdateWorkInstructionAsync_UpdatesExistingInstruction()
    {
        // Arrange
        var existingInstruction = new WorkInstruction { TaskName = "Task1", Content = "Content1" };
        var updatedInstruction = new WorkInstruction { TaskName = "Task1", Content = "UpdatedContent" };

        var data = new List<WorkInstruction> { existingInstruction }.AsQueryable();
        var mockSet = new Mock<DbSet<WorkInstruction>>();
        mockSet.As<IQueryable<WorkInstruction>>().Setup(m => m.Provider).Returns(data.Provider);
        mockSet.As<IQueryable<WorkInstruction>>().Setup(m => m.Expression).Returns(data.Expression);
        mockSet.As<IQueryable<WorkInstruction>>().Setup(m => m.ElementType).Returns(data.ElementType);
        mockSet.As<IQueryable<WorkInstruction>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());

        _mockContext.Setup(m => m.WorkInstructions).Returns(mockSet.Object);

        // Act
        await _service.UpdateWorkInstructionAsync(updatedInstruction);

        // Assert
        Assert.Equal("UpdatedContent", existingInstruction.Content);
        _mockContext.Verify(m => m.SaveChangesAsync(It.IsAny<CancellationToken>()), Times.Once());
    }

    [Fact]
    public async Task DeleteWorkInstructionAsync_DeletesExistingInstruction()
    {
        // Arrange
        var existingInstruction = new WorkInstruction { TaskName = "Task1", Content = "Content1" };

        var data = new List<WorkInstruction> { existingInstruction }.AsQueryable();
        var mockSet = new Mock<DbSet<WorkInstruction>>();
        mockSet.As<IQueryable<WorkInstruction>>().Setup(m => m.Provider).Returns(data.Provider);
        mockSet.As<IQueryable<WorkInstruction>>().Setup(m => m.Expression).Returns(data.Expression);
        mockSet.As<IQueryable<WorkInstruction>>().Setup(m => m.ElementType).Returns(data.ElementType);
        mockSet.As<IQueryable<WorkInstruction>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());

        _mockContext.Setup(m => m.WorkInstructions).Returns(mockSet.Object);

        // Act
        await _service.DeleteWorkInstructionAsync(existingInstruction.TaskName);

        // Assert
        mockSet.Verify(m => m.Remove(It.IsAny<WorkInstruction>()), Times.Once());
        _mockContext.Verify(m => m.SaveChangesAsync(It.IsAny<CancellationToken>()), Times.Once());
    }
}

3. ํ…Œ์ŠคํŠธ ์‹คํ–‰

Visual Studio์—์„œ ํ…Œ์ŠคํŠธ๋ฅผ ์‹คํ–‰ํ•˜๊ฑฐ๋‚˜, CLI๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ…Œ์ŠคํŠธ๋ฅผ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

bash์ฝ”๋“œ ๋ณต์‚ฌ
# CLI์—์„œ ํ…Œ์ŠคํŠธ ์‹คํ–‰
dotnet test

ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ๋Š” ์„ฑ๊ณต ์—ฌ๋ถ€์™€ ํ•จ๊ป˜ ์ƒ์„ธํ•œ ๊ฒฐ๊ณผ๋ฅผ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค.

๊ฒฐ๋ก 

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


17. ๋กœ๊น… ๋ฐ ๋””๋ฒ„๊น…

์„œ๋ก : ์ด์ „ ํฌ์ŠคํŒ…์—์„œ๋Š” ์œ ๋‹› ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ•˜๊ณ  ์‹คํ–‰ํ•˜์—ฌ ์ฝ”๋“œ์˜ ์•ˆ์ •์„ฑ์„ ๊ฒ€์ฆํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์†Œ๊ฐœํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” ๋กœ๊น…๊ณผ ๋””๋ฒ„๊น…์„ ํ†ตํ•ด ์ฝ”๋“œ์˜ ๋ฌธ์ œ๋ฅผ ์ถ”์ ํ•˜๊ณ  ํ•ด๊ฒฐํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ๋กœ๊น…๊ณผ ๋””๋ฒ„๊น…์€ ๋ฌธ์ œ๋ฅผ ์‹ ์†ํ•˜๊ฒŒ ํŒŒ์•…ํ•˜๊ณ  ์ˆ˜์ •ํ•˜๋Š” ๋ฐ ์ค‘์š”ํ•œ ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.

๋กœ๊น…์˜ ์ค‘์š”์„ฑ

๋กœ๊น…์€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์‹คํ–‰ ์ค‘ ๋ฐœ์ƒํ•˜๋Š” ์ด๋ฒคํŠธ๋ฅผ ๊ธฐ๋กํ•˜์—ฌ ๋ฌธ์ œ๋ฅผ ์ถ”์ ํ•˜๋Š” ๋ฐ ์ค‘์š”ํ•œ ๋„๊ตฌ์ž…๋‹ˆ๋‹ค. ๋‹ค์Œ์€ ๋กœ๊น…์˜ ์ฃผ์š” ์ด์ ์ž…๋‹ˆ๋‹ค:

  1. ๋ฌธ์ œ ์ง„๋‹จ: ๋ฐœ์ƒํ•œ ๋ฌธ์ œ์˜ ์›์ธ์„ ์‹ ์†ํ•˜๊ฒŒ ํŒŒ์•…ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  2. ์ด๋ ฅ ๊ธฐ๋ก: ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์‹คํ–‰ ์ด๋ ฅ์„ ๊ธฐ๋กํ•˜์—ฌ ์ถ”ํ›„ ๋ถ„์„์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.
  3. ์‹ค์‹œ๊ฐ„ ๋ชจ๋‹ˆํ„ฐ๋ง: ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ƒํƒœ๋ฅผ ์‹ค์‹œ๊ฐ„์œผ๋กœ ๋ชจ๋‹ˆํ„ฐ๋งํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  4. ๋ฌธ์ œ ์˜ˆ๋ฐฉ: ์ž ์žฌ์ ์ธ ๋ฌธ์ œ๋ฅผ ์กฐ๊ธฐ์— ๋ฐœ๊ฒฌํ•˜์—ฌ ์˜ˆ๋ฐฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

.NET Core์—์„œ ๋กœ๊น… ์„ค์ •

.NET Core์—์„œ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ œ๊ณตํ•˜๋Š” ๋กœ๊น… ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜์—ฌ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๋กœ๊น…์„ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

1. ๋กœ๊น… ์„ค์ • ์ถ”๊ฐ€

Program.cs:

csharp์ฝ”๋“œ ๋ณต์‚ฌ
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.UseStartup<Startup>();
            })
            .ConfigureLogging(logging =>
            {
                logging.ClearProviders();
                logging.AddConsole();
                logging.AddDebug();
            });
}

2. ๋กœ๊น… ์„œ๋น„์Šค ๋“ฑ๋ก

Startup.cs:

csharp์ฝ”๋“œ ๋ณต์‚ฌ
public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllersWithViews();
        services.AddLogging();
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILogger<Startup> logger)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler("/Home/Error");
            app.UseHsts();
        }

        app.UseHttpsRedirection();
        app.UseStaticFiles();

        app.UseRouting();

        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllerRoute(
                name: "default",
                pattern: "{controller=Home}/{action=Index}/{id?}");
        });

        logger.LogInformation("Application started.");
    }
}

๋กœ๊น… ์‚ฌ์šฉ ์˜ˆ์ œ

๋กœ๊น…์„ ์‚ฌ์šฉํ•˜์—ฌ ์ฝ”๋“œ์˜ ์ฃผ์š” ๋ถ€๋ถ„์— ๋กœ๊ทธ ๋ฉ”์‹œ์ง€๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, WorkInstructionService ํด๋ž˜์Šค์— ๋กœ๊ทธ ๋ฉ”์‹œ์ง€๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

WorkInstructionService.cs:

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

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()
        {
            _logger.LogInformation("Fetching all work instructions.");
            try
            {
                return await _context.WorkInstructions.ToListAsync();
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error fetching work instructions");
                throw;
            }
        }

        public async Task AddWorkInstructionAsync(WorkInstruction workInstruction)
        {
            _logger.LogInformation("Adding a new work instruction: {TaskName}", workInstruction.TaskName);
            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)
        {
            _logger.LogInformation("Updating work instruction: {TaskName}", workInstruction.TaskName);
            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)
        {
            _logger.LogInformation("Deleting work instruction: {TaskName}", 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)
        {
            _logger.LogInformation("Fetching work instruction by task name: {TaskName}", 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;
            }
        }
    }
}

๋””๋ฒ„๊น…

๋””๋ฒ„๊น…์€ ์ฝ”๋“œ์˜ ์‹คํ–‰์„ ๋‹จ๊ณ„๋ณ„๋กœ ์ถ”์ ํ•˜์—ฌ ๋ฌธ์ œ๋ฅผ ๋ฐœ๊ฒฌํ•˜๊ณ  ์ˆ˜์ •ํ•˜๋Š” ๊ณผ์ •์ž…๋‹ˆ๋‹ค. Visual Studio์˜ ๋””๋ฒ„๊ฑฐ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ฝ”๋“œ์˜ ํ๋ฆ„์„ ์ดํ•ดํ•˜๊ณ  ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

1. ์ค‘๋‹จ์  ์„ค์ •

์ค‘๋‹จ์ (Breakpoint)์„ ์„ค์ •ํ•˜์—ฌ ํŠน์ • ์ง€์ ์—์„œ ์ฝ”๋“œ ์‹คํ–‰์„ ์ค‘์ง€ํ•˜๊ณ  ๋ณ€์ˆ˜์˜ ๊ฐ’์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

2. ๋‹จ๊ณ„๋ณ„ ์‹คํ–‰

๋””๋ฒ„๊ฑฐ์—์„œ F10 ํ‚ค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ฝ”๋“œ ๋ผ์ธ์„ ํ•œ ์ค„์”ฉ ์‹คํ–‰ํ•˜๊ณ , F11 ํ‚ค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ•จ์ˆ˜ ๋‚ด๋ถ€๋กœ ๋“ค์–ด๊ฐ‘๋‹ˆ๋‹ค.

3. ๋ณ€์ˆ˜ ๊ฐ’ ํ™•์ธ

๋””๋ฒ„๊ฑฐ ์ฐฝ์—์„œ ๋ณ€์ˆ˜์˜ ๊ฐ’์„ ํ™•์ธํ•˜๊ณ , Watch ์ฐฝ์— ๋ณ€์ˆ˜๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ ๊ฐ’์„ ์ถ”์ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ฒฐ๋ก 

์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” ๋กœ๊น…๊ณผ ๋””๋ฒ„๊น…์„ ํ†ตํ•ด ์ฝ”๋“œ์˜ ๋ฌธ์ œ๋ฅผ ์ถ”์ ํ•˜๊ณ  ํ•ด๊ฒฐํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์‚ดํŽด๋ณด์•˜์Šต๋‹ˆ๋‹ค. ๋กœ๊น…์„ ํ†ตํ•ด ์‹คํ–‰ ์ค‘ ๋ฐœ์ƒํ•˜๋Š” ์ด๋ฒคํŠธ๋ฅผ ๊ธฐ๋กํ•˜๊ณ , ๋””๋ฒ„๊น…์„ ํ†ตํ•ด ์ฝ”๋“œ์˜ ํ๋ฆ„์„ ์ดํ•ดํ•˜์—ฌ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ ํฌ์ŠคํŒ…์—์„œ๋Š” ๋” ๋‹ค์–‘ํ•œ ๋””๋ฒ„๊น… ๊ธฐ๋ฒ•๊ณผ ๋„๊ตฌ๋ฅผ ์†Œ๊ฐœํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.


18. ์ฝ”๋“œ ๋ฆฌ๋ทฐ์™€ ์ •์  ์ฝ”๋“œ ๋ถ„์„

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

์ฝ”๋“œ ๋ฆฌ๋ทฐ์˜ ์ค‘์š”์„ฑ

์ฝ”๋“œ ๋ฆฌ๋ทฐ๋Š” ๋‹ค๋ฅธ ๊ฐœ๋ฐœ์ž๋‚˜ ํŒ€์›๋“ค์ด ์ž‘์„ฑํ•œ ์ฝ”๋“œ๋ฅผ ๊ฒ€ํ† ํ•˜๋Š” ๊ณผ์ •์ž…๋‹ˆ๋‹ค. ์ฝ”๋“œ ๋ฆฌ๋ทฐ์˜ ์ฃผ์š” ๋ชฉ์ ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค:

  1. ์ฝ”๋“œ ํ’ˆ์งˆ ํ–ฅ์ƒ: ์ฝ”๋“œ ๋ฆฌ๋ทฐ๋ฅผ ํ†ตํ•ด ์ฝ”๋“œ์˜ ํ’ˆ์งˆ์„ ๋†’์ด๊ณ , ์ž ์žฌ์ ์ธ ๋ฒ„๊ทธ๋ฅผ ์‚ฌ์ „์— ๋ฐœ๊ฒฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  2. ์ง€์‹ ๊ณต์œ : ํŒ€์› ๊ฐ„์˜ ์ฝ”๋“œ ๋ฆฌ๋ทฐ๋ฅผ ํ†ตํ•ด ์ง€์‹์„ ๊ณต์œ ํ•˜๊ณ , ์ฝ”๋“œ ์ž‘์„ฑ ์Šคํƒ€์ผ์„ ํ†ต์ผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  3. ์ตœ์ ํ™”: ์ฝ”๋“œ ๋ฆฌ๋ทฐ๋ฅผ ํ†ตํ•ด ์„ฑ๋Šฅ์„ ์ตœ์ ํ™”ํ•˜๊ณ , ์ฝ”๋“œ์˜ ๊ฐ€๋…์„ฑ์„ ํ–ฅ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  4. ๊ต์œก: ์ฝ”๋“œ ๋ฆฌ๋ทฐ๋Š” ์‹ ์ž… ๊ฐœ๋ฐœ์ž๋‚˜ ๊ฒฝํ—˜์ด ์ ์€ ๊ฐœ๋ฐœ์ž์—๊ฒŒ ์ข‹์€ ํ•™์Šต ๊ธฐํšŒ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

ํšจ๊ณผ์ ์ธ ์ฝ”๋“œ ๋ฆฌ๋ทฐ ๋ฐฉ๋ฒ•

ํšจ๊ณผ์ ์ธ ์ฝ”๋“œ ๋ฆฌ๋ทฐ๋ฅผ ์œ„ํ•ด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

  1. ์ž‘์€ ๋‹จ์œ„๋กœ ๋ฆฌ๋ทฐ: ์ฝ”๋“œ ๋ฆฌ๋ทฐ๋Š” ์ž‘์€ ๋‹จ์œ„๋กœ ์ž์ฃผ ์ˆ˜ํ–‰ํ•˜๋Š” ๊ฒƒ์ด ํšจ๊ณผ์ ์ž…๋‹ˆ๋‹ค. ์ฝ”๋“œ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์ด ์ ์„์ˆ˜๋ก ๋ฆฌ๋ทฐ๊ฐ€ ๋” ๊ฐ„๋‹จํ•˜๊ณ , ๋” ์ž์ฃผ ๋ฆฌ๋ทฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  2. ๋ช…ํ™•ํ•œ ๊ธฐ์ค€ ์„ค์ •: ์ฝ”๋“œ ๋ฆฌ๋ทฐ๋ฅผ ์œ„ํ•œ ๋ช…ํ™•ํ•œ ๊ธฐ์ค€์„ ์„ค์ •ํ•˜์—ฌ ์ผ๊ด€๋œ ๋ฆฌ๋ทฐ๋ฅผ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. ์ฝ”๋“œ ์Šคํƒ€์ผ, ๋„ค์ด๋ฐ ๊ทœ์น™, ์ฃผ์„ ์ž‘์„ฑ ๋“ฑ์˜ ๊ธฐ์ค€์„ ๋ฌธ์„œํ™”ํ•ฉ๋‹ˆ๋‹ค.
  3. ์˜๋ฏธ ์žˆ๋Š” ํ”ผ๋“œ๋ฐฑ ์ œ๊ณต: ํ”ผ๋“œ๋ฐฑ์€ ๊ตฌ์ฒด์ ์ด๊ณ  ๊ฑด์„ค์ ์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋‹จ์ˆœํžˆ "์ฝ”๋“œ๊ฐ€ ์ข‹์ง€ ์•Š๋‹ค"๋Š” ํ”ผ๋“œ๋ฐฑ๋ณด๋‹ค๋Š” "์ด ๋ถ€๋ถ„์„ ์ด๋ ‡๊ฒŒ ์ˆ˜์ •ํ•˜๋ฉด ๋” ์ข‹๊ฒ ๋‹ค"๋Š” ๊ตฌ์ฒด์ ์ธ ์ œ์•ˆ์„ ํ•ฉ๋‹ˆ๋‹ค.
  4. ์ž๋™ํ™” ๋„๊ตฌ ์‚ฌ์šฉ: ์ฝ”๋“œ ๋ฆฌ๋ทฐ๋ฅผ ์ž๋™ํ™”ํ•  ์ˆ˜ ์žˆ๋Š” ๋„๊ตฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ธฐ๋ณธ์ ์ธ ์ฝ”๋“œ ์Šคํƒ€์ผ๊ณผ ํ’ˆ์งˆ ๊ฒ€์‚ฌ๋ฅผ ์ž๋™์œผ๋กœ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

์ •์  ์ฝ”๋“œ ๋ถ„์„ ๋„๊ตฌ

์ •์  ์ฝ”๋“œ ๋ถ„์„ ๋„๊ตฌ๋Š” ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜์ง€ ์•Š๊ณ  ์†Œ์Šค ์ฝ”๋“œ๋ฅผ ๋ถ„์„ํ•˜์—ฌ ์ž ์žฌ์ ์ธ ๋ฌธ์ œ๋ฅผ ๋ฐœ๊ฒฌํ•˜๋Š” ๋„๊ตฌ์ž…๋‹ˆ๋‹ค. ๋‹ค์Œ์€ ๋Œ€ํ‘œ์ ์ธ ์ •์  ์ฝ”๋“œ ๋ถ„์„ ๋„๊ตฌ์ž…๋‹ˆ๋‹ค:

  1. SonarQube: ์˜คํ”ˆ ์†Œ์Šค ์ •์  ์ฝ”๋“œ ๋ถ„์„ ๋„๊ตฌ๋กœ, ์ฝ”๋“œ ํ’ˆ์งˆ์„ ์ง€์†์ ์œผ๋กœ ๊ฒ€์‚ฌํ•˜๊ณ  ๋ชจ๋‹ˆํ„ฐ๋งํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  2. ReSharper: JetBrains์—์„œ ์ œ๊ณตํ•˜๋Š” Visual Studio์šฉ ํ”Œ๋Ÿฌ๊ทธ์ธ์œผ๋กœ, ์ฝ”๋“œ ๋ถ„์„, ๋ฆฌํŒฉํ† ๋ง, ์ฝ”๋“œ ์ƒ์„ฑ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
  3. StyleCop: C# ์ฝ”๋“œ ์Šคํƒ€์ผ ๋ฐ ํ’ˆ์งˆ ๊ฒ€์‚ฌ ๋„๊ตฌ๋กœ, ์ฝ”๋“œ ์Šคํƒ€์ผ์„ ์ผ๊ด€๋˜๊ฒŒ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ •์  ์ฝ”๋“œ ๋ถ„์„ ๋„๊ตฌ ์‚ฌ์šฉ ์˜ˆ์‹œ

SonarQube ์„ค์ • ๋ฐ ์‚ฌ์šฉ:

  1. SonarQube ์„ค์น˜: SonarQube๋ฅผ ์„ค์น˜ํ•˜๊ณ  ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. ์„ค์น˜ ๋ฐฉ๋ฒ•์€ SonarQube ๊ณต์‹ ๋ฌธ์„œ๋ฅผ ์ฐธ์กฐํ•˜์„ธ์š”.
  2. ํ”„๋กœ์ ํŠธ ์„ค์ •: SonarQube ํ”„๋กœ์ ํŠธ๋ฅผ ์ƒ์„ฑํ•˜๊ณ , sonar-project.properties ํŒŒ์ผ์„ ํ”„๋กœ์ ํŠธ ๋ฃจํŠธ ๋””๋ ‰ํ† ๋ฆฌ์— ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
  3. properties์ฝ”๋“œ ๋ณต์‚ฌ sonar.projectKey=my_project_key sonar.projectName=My Project sonar.projectVersion=1.0 sonar.sources=.
  4. SonarQube ์Šค์บ๋„ˆ ์‹คํ–‰: SonarQube ์Šค์บ๋„ˆ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ”„๋กœ์ ํŠธ๋ฅผ ๋ถ„์„ํ•ฉ๋‹ˆ๋‹ค. ๋ช…๋ น์ค„์—์„œ ๋‹ค์Œ ๋ช…๋ น์–ด๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.
  5. sh์ฝ”๋“œ ๋ณต์‚ฌ sonar-scanner

ReSharper ์‚ฌ์šฉ ์˜ˆ์‹œ:

  1. ReSharper ์„ค์น˜: JetBrains ReSharper๋ฅผ Visual Studio์— ์„ค์น˜ํ•ฉ๋‹ˆ๋‹ค.
  2. ์ฝ”๋“œ ๋ถ„์„ ์‹คํ–‰: ReSharper ๋ฉ”๋‰ด์—์„œ "Inspect This"๋ฅผ ์„ ํƒํ•˜์—ฌ ํ˜„์žฌ ํŒŒ์ผ ๋˜๋Š” ํ”„๋กœ์ ํŠธ ์ „์ฒด๋ฅผ ๋ถ„์„ํ•ฉ๋‹ˆ๋‹ค.
  3. ๋ฌธ์ œ ํ•ด๊ฒฐ: ReSharper๊ฐ€ ๋ฐœ๊ฒฌํ•œ ๋ฌธ์ œ๋ฅผ ๊ฒ€ํ† ํ•˜๊ณ , ์ž๋™ ์ˆ˜์ • ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•ฉ๋‹ˆ๋‹ค.

์ฝ”๋“œ ๋ฆฌ๋ทฐ์™€ ์ •์  ์ฝ”๋“œ ๋ถ„์„์˜ ๊ฒฐํ•ฉ

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

๊ฒฐ๋ก 

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


19. ๊ฒฝ๊ณ„ ํ…Œ์ŠคํŠธ์™€ ์ฝ”๋“œ ๋ถ„๋ฆฌ ๋ฐ ๋ชจ๋“ˆํ™”

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

๊ฒฝ๊ณ„ ํ…Œ์ŠคํŠธ์˜ ์ค‘์š”์„ฑ

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

  1. ์—๋Ÿฌ ๋ฐœ๊ฒฌ: ๊ฒฝ๊ณ„ ๊ฐ’์—์„œ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ์—๋Ÿฌ๋ฅผ ์‚ฌ์ „์— ๋ฐœ๊ฒฌํ•˜์—ฌ ์‹œ์Šคํ…œ์˜ ์‹ ๋ขฐ์„ฑ์„ ๋†’์ž…๋‹ˆ๋‹ค.
  2. ์•ˆ์ •์„ฑ ๊ฒ€์ฆ: ๊ฒฝ๊ณ„ ๊ฐ’์„ ํ…Œ์ŠคํŠธํ•จ์œผ๋กœ์จ ์‹œ์Šคํ…œ์ด ๊ทน๋‹จ์ ์ธ ์ƒํ™ฉ์—์„œ๋„ ์•ˆ์ •์ ์œผ๋กœ ๋™์ž‘ํ•˜๋Š”์ง€ ๊ฒ€์ฆํ•ฉ๋‹ˆ๋‹ค.
  3. ํ…Œ์ŠคํŠธ ์ปค๋ฒ„๋ฆฌ์ง€ ํ–ฅ์ƒ: ๊ฒฝ๊ณ„ ๊ฐ’์„ ํฌํ•จํ•œ ๋‹ค์–‘ํ•œ ์ž…๋ ฅ์„ ํ…Œ์ŠคํŠธํ•˜์—ฌ ํ…Œ์ŠคํŠธ ์ปค๋ฒ„๋ฆฌ์ง€๋ฅผ ํ–ฅ์ƒ์‹œํ‚ต๋‹ˆ๋‹ค.

ํšจ๊ณผ์ ์ธ ๊ฒฝ๊ณ„ ํ…Œ์ŠคํŠธ ๋ฐฉ๋ฒ•

ํšจ๊ณผ์ ์ธ ๊ฒฝ๊ณ„ ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•ด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

  1. ๊ฒฝ๊ณ„ ๊ฐ’ ์‹๋ณ„: ํ…Œ์ŠคํŠธํ•  ์ž…๋ ฅ ๋ฐ์ดํ„ฐ์˜ ๊ฒฝ๊ณ„ ๊ฐ’์„ ์‹๋ณ„ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ์ตœ์†Œ๊ฐ’, ์ตœ๋Œ€๊ฐ’, ๊ทธ๋ฆฌ๊ณ  ๊ทธ ์ฃผ๋ณ€์˜ ๊ฐ’๋“ค์„ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค.
  2. ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค ์ž‘์„ฑ: ๊ฐ ๊ฒฝ๊ณ„ ๊ฐ’์„ ํ…Œ์ŠคํŠธํ•˜๋Š” ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋ฅผ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ๋ฐฐ์—ด์˜ ๊ธธ์ด๋ฅผ ํ…Œ์ŠคํŠธํ•  ๋•Œ๋Š” ๊ธธ์ด๊ฐ€ 0์ธ ๊ฒฝ์šฐ, ์ตœ๋Œ€ ๊ธธ์ด์ธ ๊ฒฝ์šฐ, ๊ทธ๋ฆฌ๊ณ  ๊ทธ๋ณด๋‹ค 1 ์ž‘์€ ๊ฒฝ์šฐ์™€ ํฐ ๊ฒฝ์šฐ๋ฅผ ํ…Œ์ŠคํŠธํ•ฉ๋‹ˆ๋‹ค.
  3. ์ž๋™ํ™” ํ…Œ์ŠคํŠธ: ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋ฅผ ์ž๋™ํ™”ํ•˜์—ฌ ์ง€์†์ ์œผ๋กœ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ์ง€์†์ ์ธ ํ†ตํ•ฉ(CI) ํ™˜๊ฒฝ์—์„œ ํŠนํžˆ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.

๊ฒฝ๊ณ„ ํ…Œ์ŠคํŠธ ์˜ˆ์‹œ

๋‹ค์Œ์€ ๋ฐฐ์—ด์˜ ๊ธธ์ด๋ฅผ ํ…Œ์ŠคํŠธํ•˜๋Š” ๊ฒฝ๊ณ„ ํ…Œ์ŠคํŠธ์˜ ์˜ˆ์‹œ์ž…๋‹ˆ๋‹ค:

csharp์ฝ”๋“œ ๋ณต์‚ฌ
using System;
using Xunit;

public class ArrayTests
{
    [Fact]
    public void TestArrayBoundaryValues()
    {
        // ์ตœ์†Œ๊ฐ’ ํ…Œ์ŠคํŠธ
        int[] array = new int[0];
        Assert.Equal(0, array.Length);

        // ์ตœ๋Œ€๊ฐ’ ํ…Œ์ŠคํŠธ (์˜ˆ๋ฅผ ๋“ค์–ด, ์ตœ๋Œ€ ๊ธธ์ด๊ฐ€ 100์ธ ๊ฒฝ์šฐ)
        array = new int[100];
        Assert.Equal(100, array.Length);

        // ๊ทธ๋ณด๋‹ค 1 ์ž‘์€ ๊ฐ’ ํ…Œ์ŠคํŠธ
        array = new int[99];
        Assert.Equal(99, array.Length);

        // ๊ทธ๋ณด๋‹ค 1 ํฐ ๊ฐ’ ํ…Œ์ŠคํŠธ
        array = new int[101];
        Assert.Equal(101, array.Length);
    }
}

์ฝ”๋“œ ๋ถ„๋ฆฌ ๋ฐ ๋ชจ๋“ˆํ™”

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

  1. ์œ ์ง€ ๋ณด์ˆ˜์„ฑ ํ–ฅ์ƒ: ์ฝ”๋“œ๋ฅผ ๋…๋ฆฝ์ ์ธ ๋ชจ๋“ˆ๋กœ ๋‚˜๋ˆ„๋ฉด ๊ฐ ๋ชจ๋“ˆ์„ ๋…๋ฆฝ์ ์œผ๋กœ ์ˆ˜์ •ํ•˜๊ณ  ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ์–ด ์œ ์ง€ ๋ณด์ˆ˜์„ฑ์ด ํ–ฅ์ƒ๋ฉ๋‹ˆ๋‹ค.
  2. ์žฌ์‚ฌ์šฉ์„ฑ ์ฆ๊ฐ€: ๋…๋ฆฝ์ ์ธ ๋ชจ๋“ˆ์€ ๋‹ค๋ฅธ ํ”„๋กœ์ ํŠธ๋‚˜ ์‹œ์Šคํ…œ์—์„œ๋„ ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด ์ฝ”๋“œ์˜ ์žฌ์‚ฌ์šฉ์„ฑ์„ ๋†’์ž…๋‹ˆ๋‹ค.
  3. ๋ณต์žก๋„ ๊ฐ์†Œ: ์‹œ์Šคํ…œ์„ ์ž‘์€ ๋ชจ๋“ˆ๋กœ ๋‚˜๋ˆ„๋ฉด ๊ฐ ๋ชจ๋“ˆ์˜ ๋ณต์žก๋„๊ฐ€ ์ค„์–ด๋“ค์–ด ์ „์ฒด ์‹œ์Šคํ…œ์˜ ๋ณต์žก๋„๋ฅผ ๊ด€๋ฆฌํ•˜๊ธฐ ์‰ฌ์›Œ์ง‘๋‹ˆ๋‹ค.

ํšจ๊ณผ์ ์ธ ์ฝ”๋“œ ๋ถ„๋ฆฌ ๋ฐ ๋ชจ๋“ˆํ™” ๋ฐฉ๋ฒ•

ํšจ๊ณผ์ ์ธ ์ฝ”๋“œ ๋ถ„๋ฆฌ ๋ฐ ๋ชจ๋“ˆํ™”๋ฅผ ์œ„ํ•ด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

  1. ๋‹จ์ผ ์ฑ…์ž„ ์›์น™: ๊ฐ ๋ชจ๋“ˆ์€ ๋‹จ์ผํ•œ ์ฑ…์ž„์„ ๊ฐ€์ ธ์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ๊ฐ ๋ชจ๋“ˆ์ด ํ•˜๋‚˜์˜ ๊ธฐ๋Šฅ๋งŒ์„ ์ˆ˜ํ–‰ํ•˜๋„๋ก ์„ค๊ณ„ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.
  2. ์ธํ„ฐํŽ˜์ด์Šค ์‚ฌ์šฉ: ๋ชจ๋“ˆ ๊ฐ„์˜ ์˜์กด์„ฑ์„ ์ตœ์†Œํ™”ํ•˜๊ธฐ ์œ„ํ•ด ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ๋ชจ๋“ˆ ๊ฐ„์˜ ๊ฒฐํ•ฉ๋„๋ฅผ ๋‚ฎ์ถ”๊ณ  ์œ ์—ฐ์„ฑ์„ ๋†’์ž…๋‹ˆ๋‹ค.
  3. ๋ชจ๋“ˆํ™”๋œ ๊ตฌ์กฐ: ํ”„๋กœ์ ํŠธ๋ฅผ ๋ชจ๋“ˆํ™”๋œ ๊ตฌ์กฐ๋กœ ์„ค๊ณ„ํ•˜์—ฌ ๊ฐ ๋ชจ๋“ˆ์ด ๋…๋ฆฝ์ ์œผ๋กœ ๋™์ž‘ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ๋””๋ ‰ํ† ๋ฆฌ ๊ตฌ์กฐ์™€ ๋„ค์ž„์ŠคํŽ˜์ด์Šค๋ฅผ ํ†ตํ•ด ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ฝ”๋“œ ๋ถ„๋ฆฌ ๋ฐ ๋ชจ๋“ˆํ™” ์˜ˆ์‹œ

๋‹ค์Œ์€ ์ฝ”๋“œ ๋ถ„๋ฆฌ ๋ฐ ๋ชจ๋“ˆํ™”์˜ ์˜ˆ์‹œ์ž…๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ๋Š” ๋ฐ์ดํ„ฐ ์•ก์„ธ์Šค ๋กœ์ง๊ณผ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ๋ถ„๋ฆฌํ•˜์—ฌ ๋ชจ๋“ˆํ™”ํ•ฉ๋‹ˆ๋‹ค.

IDataAccess.cs:

csharp์ฝ”๋“œ ๋ณต์‚ฌ
public interface IDataAccess
{
    void AddData(string data);
    string GetData(int id);
}

DataAccess.cs:

csharp์ฝ”๋“œ ๋ณต์‚ฌ
public class DataAccess : IDataAccess
{
    private readonly Dictionary<int, string> _database = new Dictionary<int, string>();

    public void AddData(string data)
    {
        int id = _database.Count + 1;
        _database.Add(id, data);
    }

    public string GetData(int id)
    {
        _database.TryGetValue(id, out string data);
        return data;
    }
}

BusinessLogic.cs:

csharp์ฝ”๋“œ ๋ณต์‚ฌ
public class BusinessLogic
{
    private readonly IDataAccess _dataAccess;

    public BusinessLogic(IDataAccess dataAccess)
    {
        _dataAccess = dataAccess;
    }

    public void ProcessData(string data)
    {
        _dataAccess.AddData(data);
        string retrievedData = _dataAccess.GetData(1);
        Console.WriteLine($"Processed Data: {retrievedData}");
    }
}

Program.cs:

csharp์ฝ”๋“œ ๋ณต์‚ฌ
public class Program
{
    public static void Main(string[] args)
    {
        IDataAccess dataAccess = new DataAccess();
        BusinessLogic businessLogic = new BusinessLogic(dataAccess);

        businessLogic.ProcessData("Sample Data");
    }
}

๊ฒฐ๋ก 

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


20. ์˜์กด์„ฑ ์ฃผ์ž…๊ณผ ๋””์ž์ธ ํŒจํ„ด์„ ํ†ตํ•œ ์ฝ”๋“œ ํšจ์œจ์„ฑ ํ–ฅ์ƒ

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

์˜์กด์„ฑ ์ฃผ์ž…์˜ ์ค‘์š”์„ฑ

์˜์กด์„ฑ ์ฃผ์ž…(Dependency Injection, DI)์€ ๊ฐ์ฒด ๊ฐ„์˜ ์˜์กด์„ฑ์„ ์™ธ๋ถ€์—์„œ ์ฃผ์ž…ํ•˜์—ฌ ๊ฐ์ฒด ๊ฐ„์˜ ๊ฒฐํ•ฉ๋„๋ฅผ ๋‚ฎ์ถ”๋Š” ๊ธฐ๋ฒ•์ž…๋‹ˆ๋‹ค. ์ด๋Š” ์ฝ”๋“œ์˜ ์œ ์—ฐ์„ฑ๊ณผ ํ…Œ์ŠคํŠธ ์šฉ์ด์„ฑ์„ ๋†’์ด๋Š” ๋ฐ ๋งค์šฐ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค. ์˜์กด์„ฑ ์ฃผ์ž…์˜ ์ฃผ์š” ๋ชฉ์ ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค:

  1. ๊ฒฐํ•ฉ๋„ ๊ฐ์†Œ: ๊ฐ์ฒด ๊ฐ„์˜ ์˜์กด์„ฑ์„ ์™ธ๋ถ€์—์„œ ์ฃผ์ž…ํ•จ์œผ๋กœ์จ ๊ฒฐํ•ฉ๋„๋ฅผ ๋‚ฎ์ถ”๊ณ , ์ฝ”๋“œ ๋ณ€๊ฒฝ์— ๋”ฐ๋ฅธ ์˜ํ–ฅ์„ ์ตœ์†Œํ™”ํ•ฉ๋‹ˆ๋‹ค.
  2. ์œ ์—ฐ์„ฑ ์ฆ๊ฐ€: ์˜์กด์„ฑ์„ ์™ธ๋ถ€์—์„œ ์ฃผ์ž…๋ฐ›๊ธฐ ๋•Œ๋ฌธ์— ๊ฐ์ฒด๋ฅผ ์‰ฝ๊ฒŒ ๊ต์ฒดํ•˜๊ฑฐ๋‚˜ ํ™•์žฅํ•  ์ˆ˜ ์žˆ์–ด ์œ ์—ฐ์„ฑ์ด ์ฆ๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
  3. ํ…Œ์ŠคํŠธ ์šฉ์ด์„ฑ: ์˜์กด์„ฑ์„ ์™ธ๋ถ€์—์„œ ์ฃผ์ž…๋ฐ›๊ธฐ ๋•Œ๋ฌธ์— ๋ชจ์˜ ๊ฐ์ฒด(Mock Object)๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๋ฅผ ์‰ฝ๊ฒŒ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์˜์กด์„ฑ ์ฃผ์ž… ๋ฐฉ๋ฒ•

์˜์กด์„ฑ ์ฃผ์ž…์„ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ํฌ๊ฒŒ ์„ธ ๊ฐ€์ง€๋กœ ๋‚˜๋ˆŒ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

  1. ์ƒ์„ฑ์ž ์ฃผ์ž…: ์ƒ์„ฑ์ž๋ฅผ ํ†ตํ•ด ์˜์กด์„ฑ์„ ์ฃผ์ž…๋ฐ›๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค. ๊ฐ€์žฅ ์ผ๋ฐ˜์ ์ด๋ฉฐ, ์˜์กด์„ฑ์„ ๋ช…ํ™•ํ•˜๊ฒŒ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  2. ์†์„ฑ ์ฃผ์ž…: ์†์„ฑ์„ ํ†ตํ•ด ์˜์กด์„ฑ์„ ์ฃผ์ž…๋ฐ›๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค. ์ฃผ์ž…๋ฐ›์„ ์˜์กด์„ฑ์ด ์„ ํƒ์ ์ผ ๋•Œ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.
  3. ๋ฉ”์„œ๋“œ ์ฃผ์ž…: ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ์˜์กด์„ฑ์„ ์ฃผ์ž…๋ฐ›๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค. ํŠน์ • ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ ์‹œ์ ์— ์˜์กด์„ฑ์„ ์ฃผ์ž…๋ฐ›์„ ๋•Œ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.

์˜์กด์„ฑ ์ฃผ์ž… ์˜ˆ์‹œ

๋‹ค์Œ์€ ์ƒ์„ฑ์ž ์ฃผ์ž…์„ ์‚ฌ์šฉํ•˜์—ฌ ์˜์กด์„ฑ์„ ์ฃผ์ž…๋ฐ›๋Š” ์˜ˆ์‹œ์ž…๋‹ˆ๋‹ค:

IDataAccess.cs:

csharp์ฝ”๋“œ ๋ณต์‚ฌ
public interface IDataAccess
{
    void AddData(string data);
    string GetData(int id);
}

DataAccess.cs:

csharp์ฝ”๋“œ ๋ณต์‚ฌ
public class DataAccess : IDataAccess
{
    private readonly Dictionary<int, string> _database = new Dictionary<int, string>();

    public void AddData(string data)
    {
        int id = _database.Count + 1;
        _database.Add(id, data);
    }

    public string GetData(int id)
    {
        _database.TryGetValue(id, out string data);
        return data;
    }
}

BusinessLogic.cs:

csharp์ฝ”๋“œ ๋ณต์‚ฌ
public class BusinessLogic
{
    private readonly IDataAccess _dataAccess;

    public BusinessLogic(IDataAccess dataAccess)
    {
        _dataAccess = dataAccess;
    }

    public void ProcessData(string data)
    {
        _dataAccess.AddData(data);
        string retrievedData = _dataAccess.GetData(1);
        Console.WriteLine($"Processed Data: {retrievedData}");
    }
}

Program.cs:

csharp์ฝ”๋“œ ๋ณต์‚ฌ
public class Program
{
    public static void Main(string[] args)
    {
        IDataAccess dataAccess = new DataAccess();
        BusinessLogic businessLogic = new BusinessLogic(dataAccess);

        businessLogic.ProcessData("Sample Data");
    }
}

๋””์ž์ธ ํŒจํ„ด์˜ ์ค‘์š”์„ฑ

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

  1. ์ƒ์„ฑ ํŒจํ„ด: ๊ฐ์ฒด ์ƒ์„ฑ๊ณผ ๊ด€๋ จ๋œ ํŒจํ„ด์ž…๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ์‹ฑ๊ธ€ํ†ค ํŒจํ„ด, ํŒฉํ† ๋ฆฌ ํŒจํ„ด ๋“ฑ์ด ์žˆ์Šต๋‹ˆ๋‹ค.
  2. ๊ตฌ์กฐ ํŒจํ„ด: ๊ฐ์ฒด ๊ฐ„์˜ ๊ด€๊ณ„๋ฅผ ๊ตฌ์„ฑํ•˜๋Š” ํŒจํ„ด์ž…๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ์–ด๋Œ‘ํ„ฐ ํŒจํ„ด, ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ํŒจํ„ด ๋“ฑ์ด ์žˆ์Šต๋‹ˆ๋‹ค.
  3. ํ–‰์œ„ ํŒจํ„ด: ๊ฐ์ฒด ๊ฐ„์˜ ์ƒํ˜ธ์ž‘์šฉ์„ ์ •์˜ํ•˜๋Š” ํŒจํ„ด์ž…๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ์˜ต์ €๋ฒ„ ํŒจํ„ด, ์ „๋žต ํŒจํ„ด ๋“ฑ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

๋””์ž์ธ ํŒจํ„ด ์˜ˆ์‹œ

๋‹ค์Œ์€ ์‹ฑ๊ธ€ํ†ค ํŒจํ„ด์„ ์‚ฌ์šฉํ•˜์—ฌ ๋‹จ์ผ ์ธ์Šคํ„ด์Šค๋ฅผ ๋ณด์žฅํ•˜๋Š” ์˜ˆ์‹œ์ž…๋‹ˆ๋‹ค:

Singleton.cs:

csharp์ฝ”๋“œ ๋ณต์‚ฌ
public class Singleton
{
    private static Singleton _instance;
    private static readonly object _lock = new object();

    private Singleton() { }

    public static Singleton Instance
    {
        get
        {
            lock (_lock)
            {
                if (_instance == null)
                {
                    _instance = new Singleton();
                }
                return _instance;
            }
        }
    }

    public void DoSomething()
    {
        Console.WriteLine("Singleton instance doing something.");
    }
}

Program.cs:

csharp์ฝ”๋“œ ๋ณต์‚ฌ
public class Program
{
    public static void Main(string[] args)
    {
        Singleton instance1 = Singleton.Instance;
        Singleton instance2 = Singleton.Instance;

        instance1.DoSomething();
        instance2.DoSomething();

        Console.WriteLine(ReferenceEquals(instance1, instance2)); // True
    }
}

๊ฒฐ๋ก 

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


21. ๊ณ ๊ธ‰ ๋””์ž์ธ ํŒจํ„ด๊ณผ ํ…Œ์ŠคํŠธ ๊ธฐ๋ฒ•

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

๊ณ ๊ธ‰ ๋””์ž์ธ ํŒจํ„ด

1. ํŒฉํ† ๋ฆฌ ๋ฉ”์„œ๋“œ ํŒจํ„ด

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

IProduct.cs:

csharp์ฝ”๋“œ ๋ณต์‚ฌ
public interface IProduct
{
    void Operate();
}

ConcreteProductA.cs:

csharp์ฝ”๋“œ ๋ณต์‚ฌ
public class ConcreteProductA : IProduct
{
    public void Operate()
    {
        Console.WriteLine("ConcreteProductA is operating.");
    }
}

ConcreteProductB.cs:

csharp์ฝ”๋“œ ๋ณต์‚ฌ
public class ConcreteProductB : IProduct
{
    public void Operate()
    {
        Console.WriteLine("ConcreteProductB is operating.");
    }
}

Creator.cs:

csharp์ฝ”๋“œ ๋ณต์‚ฌ
public abstract class Creator
{
    public abstract IProduct FactoryMethod();

    public void Operation()
    {
        var product = FactoryMethod();
        product.Operate();
    }
}

ConcreteCreatorA.cs:

csharp์ฝ”๋“œ ๋ณต์‚ฌ
public class ConcreteCreatorA : Creator
{
    public override IProduct FactoryMethod()
    {
        return new ConcreteProductA();
    }
}

ConcreteCreatorB.cs:

csharp์ฝ”๋“œ ๋ณต์‚ฌ
public class ConcreteCreatorB : Creator
{
    public override IProduct FactoryMethod()
    {
        return new ConcreteProductB();
    }
}

Program.cs:

csharp์ฝ”๋“œ ๋ณต์‚ฌ
public class Program
{
    public static void Main(string[] args)
    {
        Creator creatorA = new ConcreteCreatorA();
        creatorA.Operation();

        Creator creatorB = new ConcreteCreatorB();
        creatorB.Operation();
    }
}

2. ์ „๋žต ํŒจํ„ด

์ „๋žต ํŒจํ„ด์€ ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์บก์Šํ™”ํ•˜์—ฌ ๋Ÿฐํƒ€์ž„์— ๊ต์ฒดํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ํŒจํ„ด์ž…๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์„œ๋กœ ๋‹ค๋ฅธ ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์‰ฝ๊ฒŒ ๊ต์ฒดํ•˜๊ณ  ํ™•์žฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

IStrategy.cs:

csharp์ฝ”๋“œ ๋ณต์‚ฌ
public interface IStrategy
{
    void Execute();
}

ConcreteStrategyA.cs:

csharp์ฝ”๋“œ ๋ณต์‚ฌ
public class ConcreteStrategyA : IStrategy
{
    public void Execute()
    {
        Console.WriteLine("ConcreteStrategyA executed.");
    }
}

ConcreteStrategyB.cs:

csharp์ฝ”๋“œ ๋ณต์‚ฌ
public class ConcreteStrategyB : IStrategy
{
    public void Execute()
    {
        Console.WriteLine("ConcreteStrategyB executed.");
    }
}

Context.cs:

csharp์ฝ”๋“œ ๋ณต์‚ฌ
public class Context
{
    private IStrategy _strategy;

    public void SetStrategy(IStrategy strategy)
    {
        _strategy = strategy;
    }

    public void ExecuteStrategy()
    {
        _strategy.Execute();
    }
}

Program.cs:

csharp์ฝ”๋“œ ๋ณต์‚ฌ
public class Program
{
    public static void Main(string[] args)
    {
        var context = new Context();

        context.SetStrategy(new ConcreteStrategyA());
        context.ExecuteStrategy();

        context.SetStrategy(new ConcreteStrategyB());
        context.ExecuteStrategy();
    }
}

๊ณ ๊ธ‰ ํ…Œ์ŠคํŠธ ๊ธฐ๋ฒ•

1. ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ

ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ๋Š” ์—ฌ๋Ÿฌ ๋ชจ๋“ˆ์„ ํ†ตํ•ฉํ•˜์—ฌ ์ „์ฒด ์‹œ์Šคํ…œ์ด ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ๋™์ž‘ํ•˜๋Š”์ง€ ๊ฒ€์ฆํ•˜๋Š” ํ…Œ์ŠคํŠธ์ž…๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ๋ชจ๋“ˆ ๊ฐ„์˜ ์ƒํ˜ธ์ž‘์šฉ์—์„œ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ๋ฌธ์ œ๋ฅผ ์‚ฌ์ „์— ๋ฐœ๊ฒฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ ์˜ˆ์‹œ:

csharp์ฝ”๋“œ ๋ณต์‚ฌ
public class IntegrationTest
{
    [Fact]
    public async Task Test_Adding_WorkInstruction()
    {
        var options = new DbContextOptionsBuilder<AppDbContext>()
            .UseInMemoryDatabase(databaseName: "TestDatabase")
            .Options;

        using (var context = new AppDbContext(options))
        {
            var service = new WorkInstructionService(context, Mock.Of<ILogger<WorkInstructionService>>());
            var workInstruction = new WorkInstruction
            {
                TaskName = "TestTask",
                Content = "TestContent",
                Date = DateTime.Now,
                Writer = "TestWriter",
                Priority = "High",
                IsCompleted = false
            };

            await service.AddWorkInstructionAsync(workInstruction);
            var result = await service.GetWorkInstructionByTaskNameAsync("TestTask");

            Assert.NotNull(result);
            Assert.Equal("TestTask", result.TaskName);
        }
    }
}

2. ์„ฑ๋Šฅ ํ…Œ์ŠคํŠธ

์„ฑ๋Šฅ ํ…Œ์ŠคํŠธ๋Š” ์‹œ์Šคํ…œ์˜ ์„ฑ๋Šฅ์„ ์ธก์ •ํ•˜๊ณ  ๋ณ‘๋ชฉ ํ˜„์ƒ์„ ์ฐพ์•„๋‚ด๋Š” ํ…Œ์ŠคํŠธ์ž…๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์‹œ์Šคํ…œ์˜ ์‘๋‹ต ์‹œ๊ฐ„, ์ฒ˜๋ฆฌ๋Ÿ‰, ์•ˆ์ •์„ฑ์„ ํ‰๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์„ฑ๋Šฅ ํ…Œ์ŠคํŠธ ๋„๊ตฌ:

  • JMeter: ๋‹ค์–‘ํ•œ ์„ฑ๋Šฅ ํ…Œ์ŠคํŠธ ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ์„ค์ •ํ•˜๊ณ  ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” ์˜คํ”ˆ ์†Œ์Šค ๋„๊ตฌ์ž…๋‹ˆ๋‹ค.
  • LoadRunner: ๋‹ค์–‘ํ•œ ํ”„๋กœํ† ์ฝœ์„ ์ง€์›ํ•˜๋ฉฐ, ๋ณต์žกํ•œ ์„ฑ๋Šฅ ํ…Œ์ŠคํŠธ๋ฅผ ์ž๋™ํ™”ํ•  ์ˆ˜ ์žˆ๋Š” ์ƒ์šฉ ๋„๊ตฌ์ž…๋‹ˆ๋‹ค.

๊ฒฐ๋ก 

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