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

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

  • https://www.notion.so/WorkManagementSystem-ea38095ae6a846c791f91a82aee003b4?pvs=4
  • 3. ๊ณ ๊ธ‰ ๋””์ž์ธ ํŒจํ„ด๊ณผ ํ…Œ์ŠคํŠธ ๊ธฐ๋ฒ•๊ณ ๊ธ‰ ๋””์ž์ธ ํŒจํ„ด์˜ ์ ์šฉ1. ๋ฆฌํฌ์ง€ํ† ๋ฆฌ ํŒจํ„ด (Repository Pattern)
    csharp์ฝ”๋“œ ๋ณต์‚ฌ
    // IWorkInstructionRepository.cs
    public interface IWorkInstructionRepository
    {
        Task<IEnumerable<WorkInstruction>> GetWorkInstructionsAsync();
        Task<WorkInstruction> GetWorkInstructionByTaskNameAsync(string taskName);
        Task AddWorkInstructionAsync(WorkInstruction workInstruction);
        Task UpdateWorkInstructionAsync(WorkInstruction workInstruction);
        Task DeleteWorkInstructionAsync(string taskName);
    }
    
    // WorkInstructionRepository.cs
    public class WorkInstructionRepository : IWorkInstructionRepository
    {
        private readonly AppDbContext _context;
    
        public WorkInstructionRepository(AppDbContext context)
        {
            _context = context;
        }
    
        public async Task<IEnumerable<WorkInstruction>> GetWorkInstructionsAsync()
        {
            return await _context.WorkInstructions.ToListAsync();
        }
    
        public async Task<WorkInstruction> GetWorkInstructionByTaskNameAsync(string taskName)
        {
            return await _context.WorkInstructions.FirstOrDefaultAsync(w => w.TaskName == taskName);
        }
    
        public async Task AddWorkInstructionAsync(WorkInstruction workInstruction)
        {
            _context.WorkInstructions.Add(workInstruction);
            await _context.SaveChangesAsync();
        }
    
        public async Task UpdateWorkInstructionAsync(WorkInstruction workInstruction)
        {
            _context.WorkInstructions.Update(workInstruction);
            await _context.SaveChangesAsync();
        }
    
        public async Task DeleteWorkInstructionAsync(string taskName)
        {
            var workInstruction = await _context.WorkInstructions.FirstOrDefaultAsync(w => w.TaskName == taskName);
            if (workInstruction != null)
            {
                _context.WorkInstructions.Remove(workInstruction);
                await _context.SaveChangesAsync();
            }
        }
    }
    
    
    2. ์„œ๋น„์Šค ๋ ˆ์ด์–ด ํŒจํ„ด (Service Layer Pattern)
    csharp์ฝ”๋“œ ๋ณต์‚ฌ
    public class WorkInstructionService
    {
        private readonly IWorkInstructionRepository _repository;
        private readonly ILogger<WorkInstructionService> _logger;
    
        public WorkInstructionService(IWorkInstructionRepository repository, ILogger<WorkInstructionService> logger)
        {
            _repository = repository;
            _logger = logger;
        }
    
        public async Task<IEnumerable<WorkInstruction>> GetWorkInstructionsAsync()
        {
            return await _repository.GetWorkInstructionsAsync();
        }
    
        public async Task<WorkInstruction> GetWorkInstructionByTaskNameAsync(string taskName)
        {
            return await _repository.GetWorkInstructionByTaskNameAsync(taskName);
        }
    
        public async Task AddWorkInstructionAsync(WorkInstruction workInstruction)
        {
            await _repository.AddWorkInstructionAsync(workInstruction);
        }
    
        public async Task UpdateWorkInstructionAsync(WorkInstruction workInstruction)
        {
            await _repository.UpdateWorkInstructionAsync(workInstruction);
        }
    
        public async Task DeleteWorkInstructionAsync(string taskName)
        {
            await _repository.DeleteWorkInstructionAsync(taskName);
        }
    }
    
    
    ํ…Œ์ŠคํŠธ ๊ธฐ๋ฒ•์˜ ์ ์šฉ1. ์œ ๋‹› ํ…Œ์ŠคํŠธ (Unit Testing)
    csharp์ฝ”๋“œ ๋ณต์‚ฌ
    public class WorkInstructionServiceTests
    {
        private readonly Mock<IWorkInstructionRepository> _mockRepository;
        private readonly Mock<ILogger<WorkInstructionService>> _mockLogger;
        private readonly WorkInstructionService _service;
    
        public WorkInstructionServiceTests()
        {
            _mockRepository = new Mock<IWorkInstructionRepository>();
            _mockLogger = new Mock<ILogger<WorkInstructionService>>();
            _service = new WorkInstructionService(_mockRepository.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" }
            };
            _mockRepository.Setup(repo => repo.GetWorkInstructionsAsync()).ReturnsAsync(data);
    
            // Act
            var result = await _service.GetWorkInstructionsAsync();
    
            // Assert
            Assert.Equal(2, result.Count());
            Assert.Equal("Task1", result.First().TaskName);
        }
    }
    
    
    2. ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ (Integration Testing)
    csharp์ฝ”๋“œ ๋ณต์‚ฌ
    public class WorkInstructionControllerTests
    {
        private readonly WorkInstructionController _controller;
        private readonly Mock<WorkInstructionService> _mockService;
    
        public WorkInstructionControllerTests()
        {
            _mockService = new Mock<WorkInstructionService>();
            _controller = new WorkInstructionController(_mockService.Object);
        }
    
        [Fact]
        public async Task Get_ReturnsOkResult_WithListOfWorkInstructions()
        {
            // Arrange
            var instructions = new List<WorkInstruction>
            {
                new WorkInstruction { TaskName = "Task1", Content = "Content1" },
                new WorkInstruction { TaskName = "Task2", Content = "Content2" }
            };
            _mockService.Setup(service => service.GetWorkInstructionsAsync()).ReturnsAsync(instructions);
    
            // Act
            var result = await _controller.Get();
    
            // Assert
            var okResult = Assert.IsType<OkObjectResult>(result.Result);
            var returnValue = Assert.IsType<List<WorkInstruction>>(okResult.Value);
            Assert.Equal(2, returnValue.Count);
        }
    }
    
    
    ๊ฒฐ๋ก 
  • ๊ณ ๊ธ‰ ๋””์ž์ธ ํŒจํ„ด๊ณผ ํ…Œ์ŠคํŠธ ๊ธฐ๋ฒ•์˜ ์ ์šฉ์€ ์ฝ”๋“œ์˜ ๊ฐ€๋…์„ฑ, ์œ ์ง€ ๋ณด์ˆ˜์„ฑ, ์žฌ์‚ฌ์šฉ์„ฑ์„ ๋†’์—ฌ์ค๋‹ˆ๋‹ค. ๋ฆฌํฌ์ง€ํ† ๋ฆฌ ํŒจํ„ด๊ณผ ์„œ๋น„์Šค ๋ ˆ์ด์–ด ํŒจํ„ด์„ ์‚ฌ์šฉํ•˜์—ฌ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ๊น”๋”ํ•˜๊ฒŒ ๋ถ„๋ฆฌํ•˜๊ณ , ์œ ๋‹› ํ…Œ์ŠคํŠธ์™€ ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ๋ฅผ ํ†ตํ•ด ๊ฐ ๊ณ„์ธต์˜ ๊ธฐ๋Šฅ์„ ๊ฒ€์ฆํ•จ์œผ๋กœ์จ ํ”„๋กœ์ ํŠธ์˜ ์‹ ๋ขฐ์„ฑ์„ ๋†’์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ ํฌ์ŠคํŒ…์—์„œ๋Š” ์ด๋Ÿฌํ•œ ํŒจํ„ด๊ณผ ๊ธฐ๋ฒ•์„ ์‹ค์ œ ํ”„๋กœ์ ํŠธ์— ์–ด๋–ป๊ฒŒ ์ ์šฉํ–ˆ๋Š”์ง€์— ๋Œ€ํ•ด ๊ตฌ์ฒด์ ์ธ ์‚ฌ๋ก€๋ฅผ ํ†ตํ•ด ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.
  • ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ๋Š” ์—ฌ๋Ÿฌ ๋ชจ๋“ˆ์ด ํ†ตํ•ฉ๋œ ์ƒํƒœ์—์„œ์˜ ๊ธฐ๋Šฅ์„ ๊ฒ€์ฆํ•ฉ๋‹ˆ๋‹ค. ์•„๋ž˜๋Š” **WorkInstructionController**์˜ ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ ์˜ˆ์ œ์ž…๋‹ˆ๋‹ค.
  • ์œ ๋‹› ํ…Œ์ŠคํŠธ๋Š” ๊ฐœ๋ณ„ ๋ชจ๋“ˆ์˜ ๊ธฐ๋Šฅ์„ ๊ฒ€์ฆํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ์•„๋ž˜๋Š” **WorkInstructionService**์˜ ์œ ๋‹› ํ…Œ์ŠคํŠธ ์˜ˆ์ œ์ž…๋‹ˆ๋‹ค.
  • ๊ณ ๊ธ‰ ๋””์ž์ธ ํŒจํ„ด์„ ์ ์šฉํ•œ ํ›„, ๊ฐ ๊ณ„์ธต์˜ ๊ธฐ๋Šฅ์„ ํ…Œ์ŠคํŠธํ•˜๋Š” ๋ฐฉ๋ฒ•๋„ ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค. ์œ ๋‹› ํ…Œ์ŠคํŠธ์™€ ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ๋ฅผ ํ†ตํ•ด ์ฝ”๋“œ์˜ ์‹ ๋ขฐ์„ฑ์„ ๋†’์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์„œ๋น„์Šค ๋ ˆ์ด์–ด ํŒจํ„ด์€ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ์บก์Šํ™”ํ•˜์—ฌ ์ฝ”๋“œ์˜ ์œ ์ง€ ๋ณด์ˆ˜์„ฑ์„ ๋†’์ด๊ณ , ์žฌ์‚ฌ์šฉ์„ฑ์„ ์ฆ๋Œ€์‹œํ‚ค๋Š” ๋ฐ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ด ํŒจํ„ด์€ ๋ฆฌํฌ์ง€ํ† ๋ฆฌ ํŒจํ„ด๊ณผ ํ•จ๊ป˜ ์ž์ฃผ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.
  • ๋ฆฌํฌ์ง€ํ† ๋ฆฌ ํŒจํ„ด์€ ๋ฐ์ดํ„ฐ ์ ‘๊ทผ์„ ์บก์Šํ™”ํ•˜์—ฌ ์ฝ”๋“œ์˜ ํ…Œ์ŠคํŠธ ๊ฐ€๋Šฅ์„ฑ์„ ๋†’์ด๊ณ , ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€์˜ ์˜์กด์„ฑ์„ ์ค„์ด๋Š” ๋ฐ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • ํ”„๋กœ์ ํŠธ๊ฐ€ ์ง„ํ–‰๋จ์— ๋”ฐ๋ผ ์š”๊ตฌ ์‚ฌํ•ญ์ด ๋ณต์žกํ•ด์ง€๊ณ , ์ฝ”๋“œ๋ฒ ์ด์Šค๊ฐ€ ์ปค์ง€๋ฉด์„œ ๋””์ž์ธ ํŒจํ„ด์˜ ์ค‘์š”์„ฑ์€ ๋”์šฑ ์ปค์กŒ์Šต๋‹ˆ๋‹ค. ์ด ํฌ์ŠคํŒ…์—์„œ๋Š” ํ”„๋กœ์ ํŠธ์—์„œ ์‚ฌ์šฉํ•œ ๋ช‡ ๊ฐ€์ง€ ๊ณ ๊ธ‰ ๋””์ž์ธ ํŒจํ„ด์„ ์†Œ๊ฐœํ•˜๊ณ , ์ด๋ฅผ ์–ด๋–ป๊ฒŒ ๊ตฌํ˜„ํ–ˆ๋Š”์ง€์— ๋Œ€ํ•ด ์„ค๋ช…ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.
  • 3. ๊ณ ๊ธ‰ ๋””์ž์ธ ํŒจํ„ด๊ณผ ํ…Œ์ŠคํŠธ ๊ธฐ๋ฒ•
  • 4. API ์˜ค๋ฅ˜ ํ•ด๊ฒฐ ๋ฐ ๋””๋ฒ„๊น… ๊ฒฝํ—˜ ๊ณต์œ ํ”„๋กœ์ ํŠธ ์ง„ํ–‰ ์ค‘, API ์˜ค๋ฅ˜๋Š” ํ”ผํ•  ์ˆ˜ ์—†๋Š” ๋ฌธ์ œ ์ค‘ ํ•˜๋‚˜์˜€์Šต๋‹ˆ๋‹ค. ์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” API ๊ตฌํ˜„ ๊ณผ์ •์—์„œ ๋ฐœ์ƒํ–ˆ๋˜ ์ฃผ์š” ์˜ค๋ฅ˜์™€ ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•œ ๋””๋ฒ„๊น… ๊ณผ์ •์„ ๊ณต์œ ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.1. CS1061 ์˜ค๋ฅ˜: ํด๋ž˜์Šค ๋˜๋Š” ์†์„ฑ ๋ˆ„๋ฝWorkInstructionService ํด๋ž˜์Šค์— DeleteWorkInstruction, GetWorkInstructions, AddWorkInstruction ๋“ฑ์˜ ๋ฉ”์„œ๋“œ ์ •์˜๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ, AppDbContext ํด๋ž˜์Šค์— WorkInstructions ์†์„ฑ์ด ์—†์Šต๋‹ˆ๋‹ค.
    1. ํด๋ž˜์Šค ์ •์˜ ๋ฐ ๋ฉ”์„œ๋“œ ํ™•์ธ: WorkInstructionService ํด๋ž˜์Šค์— ํ•„์š”ํ•œ ๋ชจ๋“  ๋ฉ”์„œ๋“œ๊ฐ€ ์ •์˜๋˜์–ด ์žˆ๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
    2. ์ƒ์„ฑ์ž ๋งค๊ฐœ๋ณ€์ˆ˜ ํ™•์ธ: **WorkInstructionService**๋ฅผ ์ธ์Šคํ„ด์Šคํ™”ํ•  ๋•Œ AppDbContext ์ธ์Šคํ„ด์Šค๋ฅผ ์ „๋‹ฌํ•˜๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
    3. Using ๋ฌธ ๋ฐ ์ฐธ์กฐ ํ™•์ธ: ํŒŒ์ผ ์ƒ๋‹จ์— ํ•„์š”ํ•œ ๋„ค์ž„์ŠคํŽ˜์ด์Šค์— ๋Œ€ํ•œ using ๋ฌธ์ด ์žˆ๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
    csharp์ฝ”๋“œ ๋ณต์‚ฌ
    public class WorkInstructionService
    {
        private readonly AppDbContext _context;
    
        public WorkInstructionService(AppDbContext context)
        {
            _context = context;
        }
    
        public async Task<IEnumerable<WorkInstruction>> GetWorkInstructionsAsync()
        {
            return await _context.WorkInstructions.ToListAsync();
        }
    
        public async Task AddWorkInstructionAsync(WorkInstruction workInstruction)
        {
            _context.WorkInstructions.Add(workInstruction);
            await _context.SaveChangesAsync();
        }
    
        public async Task UpdateWorkInstructionAsync(WorkInstruction workInstruction)
        {
            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();
            }
        }
    
        public async Task DeleteWorkInstructionAsync(string taskName)
        {
            var workInstruction = await _context.WorkInstructions.FirstOrDefaultAsync(w => w.TaskName == taskName);
            if (workInstruction != null)
            {
                _context.WorkInstructions.Remove(workInstruction);
                await _context.SaveChangesAsync();
            }
        }
    }
    
    
    csharp์ฝ”๋“œ ๋ณต์‚ฌ
    public class AppDbContext : DbContext
    {
        public DbSet<WorkInstruction> WorkInstructions { get; set; }
    
        public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
    
        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));
            }
        }
    }
    
    
    2. CS7036 ์˜ค๋ฅ˜: ์ƒ์„ฑ์ž ๋งค๊ฐœ๋ณ€์ˆ˜ ๋ˆ„๋ฝ**WorkInstructionService**์˜ ์ƒ์„ฑ์ž๊ฐ€ **context**๋ผ๋Š” AppDbContext ํƒ€์ž…์˜ ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ํ•„์š”๋กœ ํ•˜์ง€๋งŒ ์ด๋ฅผ ์ œ๊ณตํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.์„œ๋น„์Šค์˜ ์ƒ์„ฑ์ž๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ AppDbContext ์ธ์Šคํ„ด์Šค๋ฅผ ์ „๋‹ฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.3. 500 ๋‚ด๋ถ€ ์„œ๋ฒ„ ์˜ค๋ฅ˜: ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๊ฒฐ ๋ฌธ์ œ500 ๋‚ด๋ถ€ ์„œ๋ฒ„ ์˜ค๋ฅ˜๋Š” ์ฃผ๋กœ ์„œ๋ฒ„์—์„œ ๋ฐœ์ƒํ•˜๋Š” ์˜ˆ์™ธ๋กœ ์ธํ•ด ์‘๋‹ต์ด ์‹คํŒจํ•˜๋Š” ๊ฒฝ์šฐ์ž…๋‹ˆ๋‹ค. ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๊ฒฐ ๋ฌธ์ž์—ด ๋˜๋Š” ์„ค์ • ๋ฌธ์ œ๋กœ ์ธํ•ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    1. ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๊ฒฐ ๋ฌธ์ž์—ด ํ™•์ธ: appsettings.json ๋˜๋Š” **AppDbContext**์—์„œ ์‚ฌ์šฉํ•œ ์—ฐ๊ฒฐ ๋ฌธ์ž์—ด์ด ์˜ฌ๋ฐ”๋ฅธ์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
    2. SQLModels ๋ฐ ์„ค์ • ํ™•์ธ: SQLModels/AppDbContext.cs ํŒŒ์ผ์˜ ์„ค์ •์„ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
    json์ฝ”๋“œ ๋ณต์‚ฌ
    {
      "ConnectionStrings": {
        "DefaultConnection": "Server=localhost;Database=plc_data_model1;User=root;Password=Alejd1785!;"
      },
      "Logging": {
        "LogLevel": {
          "Default": "Information",
          "Microsoft": "Warning",
          "Microsoft.Hosting.Lifetime": "Information"
        }
      },
      "AllowedHosts": "*"
    }
    
    
    csharp์ฝ”๋“œ ๋ณต์‚ฌ
    public class AppDbContext : DbContext
    {
        public DbSet<WorkInstruction> WorkInstructions { get; set; }
    
        public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
    
        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. ๋กœ๊น…์„ ํ†ตํ•ด ์˜ค๋ฅ˜ ์œ„์น˜ ํŒŒ์•…: **ILogger**๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ ๋ฉ”์„œ๋“œ์˜ ์‹œ์ž‘๊ณผ ๋์— ๋กœ๊ทธ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ , ์˜ˆ์™ธ ๋ฐœ์ƒ ์‹œ ์ƒ์„ธํ•œ ์ •๋ณด๋ฅผ ๋กœ๊น…ํ•˜์—ฌ ๋ฌธ์ œ์˜ ์›์ธ์„ ์ถ”์ ํ–ˆ์Šต๋‹ˆ๋‹ค.
    2. ๋‹จ๊ณ„๋ณ„ ์ฝ”๋“œ ๊ฒ€ํ† : ๊ฐ ๋‹จ๊ณ„๋ณ„๋กœ ์ฝ”๋“œ๋ฅผ ๊ฒ€ํ† ํ•˜๋ฉฐ ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ ‘๊ทผ, ์˜์กด์„ฑ ์ฃผ์ž… ๋“ฑ์˜ ๊ณผ์ •์—์„œ ๋ˆ„๋ฝ๋œ ๋ถ€๋ถ„์ด ์—†๋Š”์ง€ ํ™•์ธํ–ˆ์Šต๋‹ˆ๋‹ค.
    3. ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๊ฒฐ ํ…Œ์ŠคํŠธ: ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๊ฒฐ ๋ฌธ์ž์—ด์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ง์ ‘ ์—ฐ๊ฒฐํ•ด๋ณด๊ณ , ์ฟผ๋ฆฌ๋ฅผ ์‹คํ–‰ํ•˜์—ฌ ์—ฐ๊ฒฐ์ด ์ •์ƒ์ ์œผ๋กœ ์ด๋ฃจ์–ด์ง€๋Š”์ง€ ํ™•์ธํ–ˆ์Šต๋‹ˆ๋‹ค.
    4. ์œ ๋‹› ํ…Œ์ŠคํŠธ ๋ฐ ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ ์ž‘์„ฑ: ์„œ๋น„์Šค์™€ ์ปจํŠธ๋กค๋Ÿฌ์— ๋Œ€ํ•œ ์œ ๋‹› ํ…Œ์ŠคํŠธ์™€ ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ•˜์—ฌ ์ฝ”๋“œ๊ฐ€ ์˜ˆ์ƒ๋Œ€๋กœ ๋™์ž‘ํ•˜๋Š”์ง€ ํ™•์ธํ–ˆ์Šต๋‹ˆ๋‹ค.
    csharp์ฝ”๋“œ ๋ณต์‚ฌ
    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()
        {
            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);
    
            var result = await _service.GetWorkInstructionsAsync();
    
            Assert.Equal(2, result.Count());
            Assert.Equal("Task1", result.First().TaskName);
        }
    }
    
    
    ๊ฒฐ๋ก 
  • ์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” API ๊ตฌํ˜„ ๊ณผ์ •์—์„œ ๋ฐœ์ƒํ•œ ์ฃผ์š” ์˜ค๋ฅ˜์™€ ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•œ ๋””๋ฒ„๊น… ๊ฒฝํ—˜์„ ๊ณต์œ ํ–ˆ์Šต๋‹ˆ๋‹ค. ์˜ค๋ฅ˜์˜ ์›์ธ์„ ํŒŒ์•…ํ•˜๊ณ , ์ ์ ˆํ•œ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์„ ์ ์šฉํ•จ์œผ๋กœ์จ ์•ˆ์ •์ ์ธ API๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ ํฌ์ŠคํŒ…์—์„œ๋Š” ๊ณ ๊ธ‰ ๋””์ž์ธ ํŒจํ„ด๊ณผ ํ…Œ์ŠคํŠธ ๊ธฐ๋ฒ•์˜ ์ ์šฉ ์‚ฌ๋ก€๋ฅผ ๋” ์ž์„ธํžˆ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.
  • ๋””๋ฒ„๊น… ๊ณผ์ •์—์„œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋‹จ๊ณ„๋“ค์„ ๊ฑฐ์ณค์Šต๋‹ˆ๋‹ค:
  • ์˜ค๋ฅ˜ ํ•ด๊ฒฐ:
  • ์˜ค๋ฅ˜ ๋‚ด์šฉ:
  • csharp์ฝ”๋“œ ๋ณต์‚ฌ public class WorkInstructionServiceCtrl : Controller { private readonly WorkInstructionService _workInstructionService; public WorkInstructionServiceCtrl(AppDbContext context) { _workInstructionService = new WorkInstructionService(context); } [HttpGet] public async Task<IActionResult> Get() { var instructions = await _workInstructionService.GetWorkInstructionsAsync(); return Ok(instructions); } [HttpPost] public async Task<IActionResult> Post([FromBody] WorkInstruction workInstruction) { await _workInstructionService.AddWorkInstructionAsync(workInstruction); return CreatedAtAction(nameof(Get), new { id = workInstruction.TaskName }, workInstruction); } [HttpPut("{taskName}")] public async Task<IActionResult> Put(string taskName, [FromBody] WorkInstruction workInstruction) { await _workInstructionService.UpdateWorkInstructionAsync(workInstruction); return NoContent(); } [HttpDelete("{taskName}")] public async Task<IActionResult> Delete(string taskName) { await _workInstructionService.DeleteWorkInstructionAsync(taskName); return NoContent(); } }
  • ์˜ค๋ฅ˜ ํ•ด๊ฒฐ:
  • ์˜ค๋ฅ˜ ๋‚ด์šฉ:
  • ์˜ค๋ฅ˜ ํ•ด๊ฒฐ:
  • ์˜ค๋ฅ˜ ๋‚ด์šฉ:
  • ์ฃผ์š” ์˜ค๋ฅ˜ ๋ฐ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•
  • 4. API ์˜ค๋ฅ˜ ํ•ด๊ฒฐ ๋ฐ ๋””๋ฒ„๊น… ๊ฒฝํ—˜ ๊ณต์œ 
  • 5. API ์˜ค๋ฅ˜ ํ•ด๊ฒฐ ๋ฐ ๋””๋ฒ„๊น… ๊ฒฝํ—˜ ๊ณต์œ  - Part 2์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” ์•ž์„œ ์†Œ๊ฐœํ•œ API ์˜ค๋ฅ˜ ํ•ด๊ฒฐ ๋ฐ ๋””๋ฒ„๊น… ๊ณผ์ •์˜ ํ›„์†ํŽธ์œผ๋กœ, ์ข€ ๋” ๊ตฌ์ฒด์ ์ธ ์‚ฌ๋ก€์™€ ์ถ”๊ฐ€์ ์ธ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์„ ๊ณต์œ ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ํŠนํžˆ, ์„œ๋ฒ„ ์˜ค๋ฅ˜์™€ ์˜์กด์„ฑ ๋ฌธ์ œ๋ฅผ ์ค‘์ ์ ์œผ๋กœ ๋‹ค๋ฃจ๊ฒ ์Šต๋‹ˆ๋‹ค.3. 500 ๋‚ด๋ถ€ ์„œ๋ฒ„ ์˜ค๋ฅ˜500 ๋‚ด๋ถ€ ์„œ๋ฒ„ ์˜ค๋ฅ˜๋Š” ์ฃผ๋กœ ์„œ๋ฒ„์—์„œ ๋ฐœ์ƒํ•˜๋Š” ์˜ˆ์™ธ๋กœ ์ธํ•ด ์‘๋‹ต์ด ์‹คํŒจํ•˜๋Š” ๊ฒฝ์šฐ์ž…๋‹ˆ๋‹ค.
    1. ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๊ฒฐ ๋ฌธ์ž์—ด ํ™•์ธ: appsettings.json ๋˜๋Š” **AppDbContext**์—์„œ ์‚ฌ์šฉํ•œ ์—ฐ๊ฒฐ ๋ฌธ์ž์—ด์ด ์˜ฌ๋ฐ”๋ฅธ์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
    2. WorkInstructionService ํด๋ž˜์Šค์˜ ๊ตฌํ˜„ ํ™•์ธ: ์ด ์„œ๋น„์Šค ํด๋ž˜์Šค์˜ ๋ฉ”์„œ๋“œ๋“ค์ด ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ •์˜๋˜์–ด ์žˆ๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
    3. ์˜์กด์„ฑ ์ฃผ์ž…(DI) ์„ค์ • ํ™•์ธ: **Program.cs**์—์„œ **AppDbContext**์™€ **WorkInstructionService**๊ฐ€ ์ œ๋Œ€๋กœ ๋“ฑ๋ก๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
    ์˜ˆ์‹œ ์ฝ”๋“œ: appsettings.json์˜ˆ์‹œ ์ฝ”๋“œ: SQLModels/AppDbContext.cs์˜ˆ์‹œ ์ฝ”๋“œ: WorkManagementSystem/WMSrvs/WorkInstructionService.cs4. ์˜์กด์„ฑ ๋ฌธ์ œ ๋ฐ ํ•ด๊ฒฐ์„œ๋น„์Šค์˜ ์ƒ์„ฑ์ž์—์„œ ํ•„์š”ํ•œ ์˜์กด์„ฑ์ด ์ฃผ์ž…๋˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.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"),
                            new MySqlServerVersion(new Version(8, 0, 25))
                        ));
    
                    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");
                    });
                });
            });
    
    
    5. ์ถ”๊ฐ€์ ์ธ ๋””๋ฒ„๊น… ๋ฐ ํ…Œ์ŠคํŠธ์˜ˆ๊ธฐ์น˜ ์•Š์€ ์˜ˆ์™ธ๋‚˜ ์˜ค๋ฅ˜ ๋ฐœ์ƒ ์‹œ ๋””๋ฒ„๊น… ๋ฐ ํ…Œ์ŠคํŠธ๋ฅผ ํ†ตํ•ด ์›์ธ์„ ํŒŒ์•…ํ•ฉ๋‹ˆ๋‹ค.
    1. ๋กœ๊น… ๋ฐ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ: ์ฝ”๋“œ์— ์ถฉ๋ถ„ํ•œ ๋กœ๊น…์„ ์ถ”๊ฐ€ํ•˜๊ณ , ์˜ˆ์™ธ ๋ฐœ์ƒ ์‹œ ๋กœ๊ทธ๋ฅผ ๊ธฐ๋กํ•ฉ๋‹ˆ๋‹ค.
    2. ๋‹จ์œ„ ํ…Œ์ŠคํŠธ ์ž‘์„ฑ: ๊ฐ ๋ฉ”์„œ๋“œ์˜ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ•˜์—ฌ ์˜ˆ์ƒ๋œ ๋™์ž‘์„ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
    3. ์ •์  ์ฝ”๋“œ ๋ถ„์„ ๋„๊ตฌ ์‚ฌ์šฉ: SonarQube, ReSharper, StyleCop ๋“ฑ์˜ ๋„๊ตฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ฝ”๋“œ์˜ ํ’ˆ์งˆ์„ ๊ฒ€์‚ฌํ•ฉ๋‹ˆ๋‹ค.
    ์˜ˆ์‹œ ์ฝ”๋“œ: ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๋งˆ๋ฌด๋ฆฌ
  • ์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” API ๊ตฌํ˜„ ๊ณผ์ •์—์„œ ๋ฐœ์ƒํ–ˆ๋˜ ์ฃผ์š” ์˜ค๋ฅ˜์™€ ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•œ ๋””๋ฒ„๊น… ๋ฐฉ๋ฒ•์„ ๊ณต์œ ํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ฐ์ข… ์˜ค๋ฅ˜๋Š” ๊ฐœ๋ฐœ ๊ณผ์ •์—์„œ ๋ถˆ๊ฐ€ํ”ผํ•˜๊ฒŒ ๋ฐœ์ƒํ•˜์ง€๋งŒ, ์ด๋ฅผ ํ†ตํ•ด ์‹œ์Šคํ…œ์„ ๋”์šฑ ๊ฒฌ๊ณ ํ•˜๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ ํฌ์ŠคํŒ…์—์„œ๋Š” ๋”์šฑ ์‹ฌํ™”๋œ ๊ณ ๊ธ‰ ๋””๋ฒ„๊น… ๊ธฐ๋ฒ•๊ณผ ํ…Œ์ŠคํŠธ ๋ฐฉ๋ฒ•์„ ๋‹ค๋ฃจ๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.
  • csharp์ฝ”๋“œ ๋ณต์‚ฌ using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; 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 WorkInstructionService _service; public WorkInstructionServiceTests() { _mockContext = new Mock<AppDbContext>(); _service = new WorkInstructionService(_mockContext.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); } }
  • ์˜ค๋ฅ˜ ํ•ด๊ฒฐ:
  • ์˜ค๋ฅ˜ ๋‚ด์šฉ:
  • ์˜ˆ์‹œ ์ฝ”๋“œ: Program.cs
  • ์˜ค๋ฅ˜ ํ•ด๊ฒฐ:
  • ์˜ค๋ฅ˜ ๋‚ด์šฉ:
  • csharp์ฝ”๋“œ ๋ณต์‚ฌ using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using WindowsFormsApp1Core.SQLModels; using WindowsFormsApp1Core.WorkManagementSystem.WMModels; namespace WindowsFormsApp1Core.WorkManagementSystem.WMSrvs { public class WorkInstructionService { private readonly AppDbContext _context; public WorkInstructionService(AppDbContext context) { _context = context; } public async Task<IEnumerable<WorkInstruction>> GetWorkInstructionsAsync() { return await _context.WorkInstructions.ToListAsync(); } public async Task AddWorkInstructionAsync(WorkInstruction instruction) { _context.WorkInstructions.Add(instruction); await _context.SaveChangesAsync(); } public async Task UpdateWorkInstructionAsync(WorkInstruction instruction) { _context.Entry(instruction).State = EntityState.Modified; await _context.SaveChangesAsync(); } public async Task DeleteWorkInstructionAsync(string taskName) { var instruction = await _context.WorkInstructions.FindAsync(taskName); if (instruction != null) { _context.WorkInstructions.Remove(instruction); await _context.SaveChangesAsync(); } } public async Task<WorkInstruction> GetWorkInstructionByTaskNameAsync(string taskName) { return await _context.WorkInstructions.FindAsync(taskName); } } }
  • 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)); } } } }
  • json์ฝ”๋“œ ๋ณต์‚ฌ { "ConnectionStrings": { "DefaultConnection": "Server=localhost;Database=plc_data_model1;User=root;Password=Alejd1785!;" }, "Logging": { "LogLevel": { "Default": "Information", "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information" } }, "AllowedHosts": "*" }
  • ์˜ค๋ฅ˜ ํ•ด๊ฒฐ:
  • ์˜ค๋ฅ˜ ๋‚ด์šฉ:
  • ์ถ”๊ฐ€์ ์ธ ์ฃผ์š” ์˜ค๋ฅ˜ ๋ฐ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•
  • 5. API ์˜ค๋ฅ˜ ํ•ด๊ฒฐ ๋ฐ ๋””๋ฒ„๊น… ๊ฒฝํ—˜ ๊ณต์œ  - Part 2
  • 6. API ์˜ค๋ฅ˜ ํ•ด๊ฒฐ ๋ฐ ๋””๋ฒ„๊น… ๊ฒฝํ—˜ ๊ณต์œ  - Part 3์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” ์ด์ „ ๊ธ€์—์„œ ๋‹ค๋ฃฌ API ์˜ค๋ฅ˜ ํ•ด๊ฒฐ ๋ฐ ๋””๋ฒ„๊น… ๊ณผ์ •์„ ๋ฐ”ํƒ•์œผ๋กœ ๋” ๊นŠ์ด ๋“ค์–ด๊ฐ€๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ํŠนํžˆ, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜๊ณผ ๋กœ๊ทธ ๋ถ„์„์„ ํ†ตํ•ด ๋ฐœ์ƒํ•˜๋Š” ๋ฌธ์ œ๋“ค์„ ํ•ด๊ฒฐํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.1. ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์„ค์ •ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•: Entity Framework Core๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์„ ์„ค์ •ํ•˜๊ณ , ์ด๋ฅผ ํ†ตํ•ด ์Šคํ‚ค๋งˆ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ ์šฉํ•ฉ๋‹ˆ๋‹ค.
    csharp์ฝ”๋“œ ๋ณต์‚ฌ
    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));
            }
        }
    }
    
    
    ์˜ˆ์‹œ ์ฝ”๋“œ: Program.cs2. ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์ถ”๊ฐ€ ๋ฐ ์—…๋ฐ์ดํŠธํ•ด๊ฒฐ ๋ฐฉ๋ฒ•: Entity Framework Core์˜ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์Šคํ‚ค๋งˆ๋ฅผ ์ž๋™์œผ๋กœ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค.
    1. ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์ถ”๊ฐ€
    2. powershell์ฝ”๋“œ ๋ณต์‚ฌ Add-Migration InitialCreate
    3. ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—…๋ฐ์ดํŠธ
    4. powershell์ฝ”๋“œ ๋ณต์‚ฌ Update-Database
    ๋กœ๊ทธ ๋ถ„์„๋ฌธ์ œ: ์˜ค๋ฅ˜ ๋ฐœ์ƒ ์‹œ ์›์ธ์„ ํŒŒ์•…ํ•˜๊ธฐ ์–ด๋ ต์Šต๋‹ˆ๋‹ค.์˜ˆ์‹œ ์ฝ”๋“œ: WorkInstructionService๋งˆ๋ฌด๋ฆฌ
  • ์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์„ค์ •๊ณผ ๋กœ๊ทธ ๋ถ„์„์„ ํ†ตํ•ด ๋ฐœ์ƒํ•˜๋Š” ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ์•Œ์•„๋ณด์•˜์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ ํฌ์ŠคํŒ…์—์„œ๋Š” ๋”์šฑ ์‹ฌํ™”๋œ ํ…Œ์ŠคํŠธ ๊ธฐ๋ฒ•๊ณผ ๊ณ ๊ธ‰ ๋””์ž์ธ ํŒจํ„ด์„ ํ™œ์šฉํ•œ API ๊ฐœ๋ฐœ ๋ฐฉ๋ฒ•์„ ๋‹ค๋ฃจ๊ฒ ์Šต๋‹ˆ๋‹ค.
  • csharp์ฝ”๋“œ ๋ณต์‚ฌ using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using WindowsFormsApp1Core.SQLModels; using WindowsFormsApp1Core.WorkManagementSystem.WMModels; 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; } } } }
  • ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•: ์ถฉ๋ถ„ํ•œ ๋กœ๊น…์„ ํ†ตํ•ด ๋ฌธ์ œ ๋ฐœ์ƒ ์‹œ ๋กœ๊ทธ๋ฅผ ํ™•์ธํ•˜์—ฌ ์›์ธ์„ ํŒŒ์•…ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • 3. ๋กœ๊น… ์„ค์ •
  • ๋ช…๋ น์–ด:
  • ๋ฌธ์ œ: ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๊ตฌ์กฐ๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค ์ˆ˜๋™์œผ๋กœ ์—…๋ฐ์ดํŠธํ•˜๋Š” ๊ฒƒ์€ ๋ฒˆ๊ฑฐ๋กญ๊ณ  ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • 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"); }); }); });
  • ์˜ˆ์‹œ ์ฝ”๋“œ: AppDbContext
  • ๋ฌธ์ œ: ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์Šคํ‚ค๋งˆ ๋ณ€๊ฒฝ ์‹œ, ๊ธฐ์กด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€์˜ ๋ถˆ์ผ์น˜๋กœ ์ธํ•ด ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜
  • 6. API ์˜ค๋ฅ˜ ํ•ด๊ฒฐ ๋ฐ ๋””๋ฒ„๊น… ๊ฒฝํ—˜ ๊ณต์œ  - Part 3
  • 7. API ์˜ค๋ฅ˜ ํ•ด๊ฒฐ ๋ฐ ๋””๋ฒ„๊น… ๊ฒฝํ—˜ ๊ณต์œ  - Part 4์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” ์ง€๋‚œ๋ฒˆ์— ๋‹ค๋ฃฌ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜๊ณผ ๋กœ๊ทธ ๋ถ„์„์— ์ด์–ด, ๋”์šฑ ์‹ฌํ™”๋œ ํ…Œ์ŠคํŠธ ๊ธฐ๋ฒ•๊ณผ ๊ณ ๊ธ‰ ๋””์ž์ธ ํŒจํ„ด์„ ํ™œ์šฉํ•œ API ๊ฐœ๋ฐœ ๋ฐฉ๋ฒ•์„ ํƒ๊ตฌํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.1. ์œ ๋‹› ํ…Œ์ŠคํŠธ ์ž‘์„ฑ ๋ฐ ์‹คํ–‰ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•: ์œ ๋‹› ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ•˜์—ฌ ๊ฐ ๊ธฐ๋Šฅ์ด ์˜๋„ํ•œ ๋Œ€๋กœ ๋™์ž‘ํ•˜๋Š”์ง€ ๊ฒ€์ฆํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์ฝ”๋“œ์˜ ์‹ ๋ขฐ์„ฑ์„ ๋†’์ด๊ณ  ์œ ์ง€๋ณด์ˆ˜๋ฅผ ์šฉ์ดํ•˜๊ฒŒ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    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);
        }
    }
    
    
    2. ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ ์ž‘์„ฑ ๋ฐ ์‹คํ–‰ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•: ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ๋ฅผ ํ†ตํ•ด ์‹œ์Šคํ…œ์˜ ์—ฌ๋Ÿฌ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ํ•จ๊ป˜ ๋™์ž‘ํ•  ๋•Œ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ๋ฌธ์ œ๋ฅผ ์‚ฌ์ „์— ๋ฐœ๊ฒฌํ•ฉ๋‹ˆ๋‹ค.
    csharp์ฝ”๋“œ ๋ณต์‚ฌ
    using System.Net.Http;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Mvc.Testing;
    using Xunit;
    
    public class WorkInstructionIntegrationTests : IClassFixture<WebApplicationFactory<Startup>>
    {
        private readonly HttpClient _client;
    
        public WorkInstructionIntegrationTests(WebApplicationFactory<Startup> factory)
        {
            _client = factory.CreateClient();
        }
    
        [Fact]
        public async Task Get_ReturnsSuccessStatusCode()
        {
            // Act
            var response = await _client.GetAsync("/api/WorkInstruction");
    
            // Assert
            response.EnsureSuccessStatusCode();
        }
    }
    
    
    ๊ณ ๊ธ‰ ๋””์ž์ธ ํŒจํ„ด๋ฌธ์ œ: ๋ฐ์ดํ„ฐ ์•ก์„ธ์Šค ๋กœ์ง๊ณผ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์ด ์„ž์—ฌ ์ฝ”๋“œ๊ฐ€ ๋ณต์žกํ•ด์ง‘๋‹ˆ๋‹ค.์˜ˆ์‹œ ์ฝ”๋“œ: IWorkInstructionRepository์˜ˆ์‹œ ์ฝ”๋“œ: WorkInstructionRepository4. ์„œ๋น„์Šค ํŒจํ„ดํ•ด๊ฒฐ ๋ฐฉ๋ฒ•: ์„œ๋น„์Šค ํŒจํ„ด์„ ์ ์šฉํ•˜์—ฌ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ๋ณ„๋„์˜ ์„œ๋น„์Šค ํด๋ž˜์Šค๋กœ ๋ถ„๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
    csharp์ฝ”๋“œ ๋ณต์‚ฌ
    public class WorkInstructionService
    {
        private readonly IWorkInstructionRepository _repository;
        private readonly ILogger<WorkInstructionService> _logger;
    
        public WorkInstructionService(IWorkInstructionRepository repository, ILogger<WorkInstructionService> logger)
        {
            _repository = repository;
            _logger = logger;
        }
    
        public async Task<IEnumerable<WorkInstruction>> GetWorkInstructionsAsync()
        {
            try
            {
                return await _repository.GetWorkInstructionsAsync();
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error fetching work instructions");
                throw;
            }
        }
    
        public async Task AddWorkInstructionAsync(WorkInstruction workInstruction)
        {
            try
            {
                await _repository.AddWorkInstructionAsync(workInstruction);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error adding work instruction");
                throw;
            }
        }
    
        public async Task UpdateWorkInstructionAsync(WorkInstruction workInstruction)
        {
            try
            {
                await _repository.UpdateWorkInstructionAsync(workInstruction);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error updating work instruction");
                throw;
            }
        }
    
        public async Task DeleteWorkInstructionAsync(string taskName)
        {
            try
            {
                await _repository.DeleteWorkInstructionAsync(taskName);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error deleting work instruction");
                throw;
            }
        }
    
        public async Task<WorkInstruction> GetWorkInstructionByTaskNameAsync(string taskName)
        {
            try
            {
                return await _repository.GetWorkInstructionByTaskNameAsync(taskName);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error fetching work instruction by task name");
                throw;
            }
        }
    }
    
    
    ๋งˆ๋ฌด๋ฆฌ
  • ์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” ์œ ๋‹› ํ…Œ์ŠคํŠธ์™€ ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ•˜๊ณ  ์‹คํ–‰ํ•˜๋Š” ๋ฐฉ๋ฒ•, ๊ทธ๋ฆฌ๊ณ  ๋ฆฌํฌ์ง€ํ† ๋ฆฌ ํŒจํ„ด๊ณผ ์„œ๋น„์Šค ํŒจํ„ด์„ ์ ์šฉํ•˜์—ฌ ์ฝ”๋“œ์˜ ๊ฐ€๋…์„ฑ๊ณผ ์œ ์ง€๋ณด์ˆ˜์„ฑ์„ ๋†’์ด๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ๋‹ค๋ฃจ์—ˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ ํฌ์ŠคํŒ…์—์„œ๋Š” ๋”์šฑ ์‹ฌํ™”๋œ ๊ณ ๊ธ‰ ๋””์ž์ธ ํŒจํ„ด๊ณผ ํ…Œ์ŠคํŒ… ๊ธฐ๋ฒ•์„ ํ†ตํ•ด API ๊ฐœ๋ฐœ์„ ์ตœ์ ํ™”ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.
  • ์˜ˆ์‹œ ์ฝ”๋“œ: WorkInstructionService
  • ๋ฌธ์ œ: ์ปจํŠธ๋กค๋Ÿฌ์— ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์ด ๊ณผ๋„ํ•˜๊ฒŒ ํฌํ•จ๋˜์–ด ์ฝ”๋“œ๊ฐ€ ๋ณต์žกํ•ด์ง‘๋‹ˆ๋‹ค.
  • csharp์ฝ”๋“œ ๋ณต์‚ฌ public class WorkInstructionRepository : IWorkInstructionRepository { private readonly AppDbContext _context; public WorkInstructionRepository(AppDbContext context) { _context = context; } public async Task<IEnumerable<WorkInstruction>> GetWorkInstructionsAsync() { return await _context.WorkInstructions.ToListAsync(); } public async Task AddWorkInstructionAsync(WorkInstruction workInstruction) { _context.WorkInstructions.Add(workInstruction); await _context.SaveChangesAsync(); } public async Task UpdateWorkInstructionAsync(WorkInstruction workInstruction) { 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(); } } public async Task DeleteWorkInstructionAsync(string taskName) { var workInstruction = await _context.WorkInstructions.FirstOrDefaultAsync(w => w.TaskName == taskName); if (workInstruction != null) { _context.WorkInstructions.Remove(workInstruction); await _context.SaveChangesAsync(); } } public async Task<WorkInstruction> GetWorkInstructionByTaskNameAsync(string taskName) { return await _context.WorkInstructions.FirstOrDefaultAsync(w => w.TaskName == taskName); } }
  • csharp์ฝ”๋“œ ๋ณต์‚ฌ public interface IWorkInstructionRepository { Task<IEnumerable<WorkInstruction>> GetWorkInstructionsAsync(); Task AddWorkInstructionAsync(WorkInstruction workInstruction); Task UpdateWorkInstructionAsync(WorkInstruction workInstruction); Task DeleteWorkInstructionAsync(string taskName); Task<WorkInstruction> GetWorkInstructionByTaskNameAsync(string taskName); }
  • ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•: ๋ฆฌํฌ์ง€ํ† ๋ฆฌ ํŒจํ„ด์„ ์ ์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ ์•ก์„ธ์Šค ๋กœ์ง์„ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์œผ๋กœ๋ถ€ํ„ฐ ๋ถ„๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
  • 3. ๋ฆฌํฌ์ง€ํ† ๋ฆฌ ํŒจํ„ด
  • ์˜ˆ์‹œ ์ฝ”๋“œ: WorkInstructionIntegrationTests
  • ๋ฌธ์ œ: ์‹œ์Šคํ…œ์˜ ์—ฌ๋Ÿฌ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ํ•จ๊ป˜ ๋™์ž‘ํ•  ๋•Œ ๋ฐœ์ƒํ•˜๋Š” ๋ฌธ์ œ๋ฅผ ์‚ฌ์ „์— ๋ฐœ๊ฒฌํ•˜๊ธฐ ์–ด๋ ต์Šต๋‹ˆ๋‹ค.
  • ์˜ˆ์‹œ ์ฝ”๋“œ: WorkInstructionServiceTests
  • ๋ฌธ์ œ: ๊ฐ ๊ธฐ๋Šฅ์ด ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ๋™์ž‘ํ•˜๋Š”์ง€ ๊ฒ€์ฆํ•˜๊ธฐ ์–ด๋ ต์Šต๋‹ˆ๋‹ค.
  • ํ…Œ์ŠคํŠธ ๊ธฐ๋ฒ•
  • 7. API ์˜ค๋ฅ˜ ํ•ด๊ฒฐ ๋ฐ ๋””๋ฒ„๊น… ๊ฒฝํ—˜ ๊ณต์œ  - Part 4
    1. API ์˜ค๋ฅ˜ ํ•ด๊ฒฐ ๋ฐ ๋””๋ฒ„๊น… ๊ฒฝํ—˜ ๊ณต์œ  - Part 5
    8. API ์˜ค๋ฅ˜ ํ•ด๊ฒฐ ๋ฐ ๋””๋ฒ„๊น… ๊ฒฝํ—˜ ๊ณต์œ  - Part 5์˜์กด์„ฑ ์ฃผ์ž… (Dependency Injection)1. ์ƒ์„ฑ์ž ์ฃผ์ž…ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•: ์ƒ์„ฑ์ž ์ฃผ์ž…์„ ํ†ตํ•ด ์˜์กด์„ฑ์„ ์ฃผ์ž…ํ•จ์œผ๋กœ์จ ๊ฒฐํ•ฉ๋„๋ฅผ ๋‚ฎ์ถ”๊ณ , ํ…Œ์ŠคํŠธ ์‹œ ๋ชจํ‚น(Mock) ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.
    csharp์ฝ”๋“œ ๋ณต์‚ฌ
    public class WorkInstructionService
    {
        private readonly IWorkInstructionRepository _repository;
        private readonly ILogger<WorkInstructionService> _logger;
    
        public WorkInstructionService(IWorkInstructionRepository repository, ILogger<WorkInstructionService> logger)
        {
            _repository = repository;
            _logger = logger;
        }
    
        // ๋ฉ”์„œ๋“œ ์ •์˜ ์ƒ๋žต...
    }
    
    
    2. ๋ฉ”์„œ๋“œ ์ฃผ์ž…ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•: ๋ฉ”์„œ๋“œ ์ฃผ์ž…์„ ํ†ตํ•ด ํŠน์ • ๋ฉ”์„œ๋“œ์—์„œ๋งŒ ํ•„์š”ํ•œ ์˜์กด์„ฑ์„ ์ฃผ์ž…ํ•ฉ๋‹ˆ๋‹ค.
    csharp์ฝ”๋“œ ๋ณต์‚ฌ
    public class WorkInstructionController : ControllerBase
    {
        private readonly ILogger<WorkInstructionController> _logger;
        private WorkInstructionService _service;
    
        public WorkInstructionController(ILogger<WorkInstructionController> logger)
        {
            _logger = logger;
        }
    
        [HttpPost]
        public async Task<ActionResult<WorkInstruction>> Post(WorkInstruction workInstruction, [FromServices] WorkInstructionService service)
        {
            _service = service;
            await _service.AddWorkInstructionAsync(workInstruction);
            return CreatedAtAction(nameof(Get), new { id = workInstruction.TaskName }, workInstruction);
        }
    
        // ๋‹ค๋ฅธ ๋ฉ”์„œ๋“œ ์ •์˜ ์ƒ๋žต...
    }
    
    
    3. ์†์„ฑ ์ฃผ์ž…ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•: ์†์„ฑ ์ฃผ์ž…์„ ํ†ตํ•ด ํ•„์š”ํ•œ ์˜์กด์„ฑ์„ ์†์„ฑ์— ์ฃผ์ž…ํ•ฉ๋‹ˆ๋‹ค.
    csharp์ฝ”๋“œ ๋ณต์‚ฌ
    public class WorkInstructionController : ControllerBase
    {
        private readonly ILogger<WorkInstructionController> _logger;
    
        [FromServices]
        public WorkInstructionService Service { get; set; }
    
        public WorkInstructionController(ILogger<WorkInstructionController> logger)
        {
            _logger = logger;
        }
    
        [HttpPost]
        public async Task<ActionResult<WorkInstruction>> Post(WorkInstruction workInstruction)
        {
            await Service.AddWorkInstructionAsync(workInstruction);
            return CreatedAtAction(nameof(Get), new { id = workInstruction.TaskName }, workInstruction);
        }
    
        // ๋‹ค๋ฅธ ๋ฉ”์„œ๋“œ ์ •์˜ ์ƒ๋žต...
    }
    
    
    ๊ณ ๊ธ‰ ๋””๋ฒ„๊น… ๊ธฐ๋ฒ•๋ฌธ์ œ: ๋””๋ฒ„๊น… ์‹œ ํ•„์š”ํ•œ ๋กœ๊ทธ ์ •๋ณด๊ฐ€ ๋ถ€์กฑํ•˜๊ฑฐ๋‚˜ ๋„ˆ๋ฌด ๋งŽ์•„ ๋””๋ฒ„๊น…์ด ์–ด๋ ต์Šต๋‹ˆ๋‹ค.์˜ˆ์‹œ ์ฝ”๋“œ: appsettings.json5. ๋ถ„์‚ฐ ํŠธ๋ ˆ์ด์‹ฑํ•ด๊ฒฐ ๋ฐฉ๋ฒ•: ๋ถ„์‚ฐ ํŠธ๋ ˆ์ด์‹ฑ์„ ํ†ตํ•ด ์„œ๋น„์Šค ๊ฐ„์˜ ํ˜ธ์ถœ์„ ์ถ”์ ํ•˜๊ณ , ๊ฐ ์š”์ฒญ์˜ ์ „์ฒด ๊ฒฝ๋กœ๋ฅผ ์‹œ๊ฐํ™”ํ•ฉ๋‹ˆ๋‹ค.
    csharp์ฝ”๋“œ ๋ณต์‚ฌ
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddOpenTelemetryTracing(builder => builder
            .AddAspNetCoreInstrumentation()
            .AddHttpClientInstrumentation()
            .AddJaegerExporter());
    }
    
    
    ๊ฒฐ๋ก 
  • ์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” ์˜์กด์„ฑ ์ฃผ์ž…์˜ ๋‹ค์–‘ํ•œ ํŒจํ„ด๊ณผ ๊ณ ๊ธ‰ ๋””๋ฒ„๊น… ๊ธฐ๋ฒ•์„ ์‚ดํŽด๋ณด์•˜์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ํŒจํ„ด๊ณผ ๊ธฐ๋ฒ•์„ ํ†ตํ•ด ์ฝ”๋“œ์˜ ์œ ์ง€๋ณด์ˆ˜์„ฑ๊ณผ ํ…Œ์ŠคํŠธ ๊ฐ€๋Šฅ์„ฑ์„ ํฌ๊ฒŒ ํ–ฅ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ ํฌ์ŠคํŒ…์—์„œ๋Š” ์ด๋Ÿฌํ•œ ๊ธฐ๋ฒ•๋“ค์„ ์‹ค์ œ ํ”„๋กœ์ ํŠธ์— ์ ์šฉํ•œ ์‚ฌ๋ก€๋ฅผ ํ†ตํ•ด ์‹ค์งˆ์ ์ธ ํ™œ์šฉ ๋ฐฉ๋ฒ•์„ ์†Œ๊ฐœํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.
  • ์˜ˆ์‹œ ์ฝ”๋“œ: Startup.cs
  • ๋ฌธ์ œ: ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค ํ™˜๊ฒฝ์—์„œ ๊ฐ ์„œ๋น„์Šค ๊ฐ„์˜ ํ˜ธ์ถœ์„ ์ถ”์ ํ•˜๊ธฐ ์–ด๋ ต์Šต๋‹ˆ๋‹ค.
  • json์ฝ”๋“œ ๋ณต์‚ฌ { "Logging": { "LogLevel": { "Default": "Information", "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information" } } }
  • ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•: ์ ์ ˆํ•œ ๋กœ๊ทธ ๋ ˆ๋ฒจ์„ ์„ค์ •ํ•˜์—ฌ ๋””๋ฒ„๊น…์— ํ•„์š”ํ•œ ์ •๋ณด๋ฅผ ํšจ์œจ์ ์œผ๋กœ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
  • 4. ๋กœ๊ทธ ๋ ˆ๋ฒจ ์„ค์ •
  • ์˜ˆ์‹œ ์ฝ”๋“œ: WorkInstructionController
  • ๋ฌธ์ œ: ์†์„ฑ์— ์˜์กด์„ฑ์„ ์ฃผ์ž…ํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ, ์ƒ์„ฑ์ž ์ฃผ์ž…์ด ์ ํ•ฉํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์˜ˆ์‹œ ์ฝ”๋“œ: WorkInstructionController
  • ๋ฌธ์ œ: ํŠน์ • ๋ฉ”์„œ๋“œ์—์„œ๋งŒ ํ•„์š”ํ•œ ์˜์กด์„ฑ์„ ์ฃผ์ž…ํ•˜๊ณ  ์‹ถ์„ ๋•Œ, ์ƒ์„ฑ์ž ์ฃผ์ž…์€ ๋ถˆํ•„์š”ํ•œ ์˜์กด์„ฑ์„ ํฌํ•จํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์˜ˆ์‹œ ์ฝ”๋“œ: WorkInstructionService
  • ๋ฌธ์ œ: ๊ฐ์ฒด์˜ ์˜์กด์„ฑ์„ ์ง์ ‘ ์ƒ์„ฑํ•˜๋ฉด, ์ฝ”๋“œ์˜ ๊ฒฐํ•ฉ๋„๊ฐ€ ๋†’์•„์ง€๊ณ  ํ…Œ์ŠคํŠธ๊ฐ€ ์–ด๋ ค์›Œ์ง‘๋‹ˆ๋‹ค.
  • ์˜์กด์„ฑ ์ฃผ์ž…์€ ๊ฐ์ฒด ๊ฐ„์˜ ๊ฒฐํ•ฉ๋„๋ฅผ ๋‚ฎ์ถ”๊ณ , ๊ฐ์ฒด์˜ ์ƒ์„ฑ๊ณผ ์ƒ๋ช…์ฃผ๊ธฐ๋ฅผ ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ๊ด€๋ฆฌํ•˜๋„๋ก ํ•˜์—ฌ ์ฝ”๋“œ์˜ ์œ ์—ฐ์„ฑ๊ณผ ํ…Œ์ŠคํŠธ ๊ฐ€๋Šฅ์„ฑ์„ ๋†’์ด๋Š” ๋””์ž์ธ ํŒจํ„ด์ž…๋‹ˆ๋‹ค.
  • ์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” ์ง€๋‚œ ํฌ์ŠคํŒ…์—์„œ ๋‹ค๋ฃฌ ์œ ๋‹› ํ…Œ์ŠคํŠธ์™€ ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ, ๋ฆฌํฌ์ง€ํ† ๋ฆฌ ํŒจํ„ด ๋ฐ ์„œ๋น„์Šค ํŒจํ„ด์„ ๋ฐ”ํƒ•์œผ๋กœ ๋”์šฑ ์‹ฌํ™”๋œ ๊ณ ๊ธ‰ ๋””์ž์ธ ํŒจํ„ด๊ณผ ๋””๋ฒ„๊น… ๊ธฐ๋ฒ•์„ ํƒ๊ตฌํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ํŠนํžˆ ์˜์กด์„ฑ ์ฃผ์ž…๊ณผ ๊ด€๋ จ๋œ ๋””์ž์ธ ํŒจํ„ด์„ ํ†ตํ•ด ์ฝ”๋“œ์˜ ์œ ์ง€๋ณด์ˆ˜์„ฑ๊ณผ ํ…Œ์ŠคํŠธ ๊ฐ€๋Šฅ์„ฑ์„ ํ–ฅ์ƒ์‹œํ‚ค๋Š” ๋ฐฉ๋ฒ•์„ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.
  • 9. ํ”„๋กœ์ ํŠธ ์ ์šฉ ์‚ฌ๋ก€ - Part 6์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” ์ด์ „ ํฌ์ŠคํŒ…์—์„œ ๋‹ค๋ฃฌ ์˜์กด์„ฑ ์ฃผ์ž… ๋ฐ ๊ณ ๊ธ‰ ๋””๋ฒ„๊น… ๊ธฐ๋ฒ•์„ ์‹ค์ œ ํ”„๋กœ์ ํŠธ์— ์–ด๋–ป๊ฒŒ ์ ์šฉํ–ˆ๋Š”์ง€์— ๋Œ€ํ•œ ์‚ฌ๋ก€๋ฅผ ๊ณต์œ ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ์ด ์‚ฌ๋ก€๋ฅผ ํ†ตํ•ด ์˜์กด์„ฑ ์ฃผ์ž…์˜ ๋‹ค์–‘ํ•œ ํŒจํ„ด๊ณผ ๊ณ ๊ธ‰ ๋””๋ฒ„๊น… ๊ธฐ๋ฒ•์ด ์–ด๋–ป๊ฒŒ ํ”„๋กœ์ ํŠธ์˜ ์œ ์ง€๋ณด์ˆ˜์„ฑ๊ณผ ํ…Œ์ŠคํŠธ ๊ฐ€๋Šฅ์„ฑ์„ ํ–ฅ์ƒ์‹œํ‚ค๋Š”์ง€ ๊ตฌ์ฒด์ ์œผ๋กœ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.์ €๋Š” ํ˜„์žฌ ํŠน์ • ์ž‘์—… ์ง€์‹œ์„œ(Work Instructions)๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ์‹œ์Šคํ…œ์„ ๊ฐœ๋ฐœํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ์‹œ์Šคํ…œ์€ ์‚ฌ์šฉ์ž์—๊ฒŒ ์ž‘์—… ์ง€์‹œ์„œ๋ฅผ ์ถ”๊ฐ€, ์ˆ˜์ •, ์‚ญ์ œํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋ฉฐ, ์ด๋ฅผ ํ†ตํ•ด ์ž‘์—…์˜ ํšจ์œจ์„ฑ๊ณผ ์ •ํ™•์„ฑ์„ ๋†’์ด๋Š” ๊ฒƒ์„ ๋ชฉํ‘œ๋กœ ํ•ฉ๋‹ˆ๋‹ค.1. ์ƒ์„ฑ์ž ์ฃผ์ž… ์ ์šฉ์˜ˆ์‹œ ์ฝ”๋“œ: WorkInstructionService2. ๋ฉ”์„œ๋“œ ์ฃผ์ž… ์ ์šฉ์˜ˆ์‹œ ์ฝ”๋“œ: WorkInstructionController3. ์†์„ฑ ์ฃผ์ž… ์ ์šฉ์˜ˆ์‹œ ์ฝ”๋“œ: WorkInstructionController๊ณ ๊ธ‰ ๋””๋ฒ„๊น… ๊ธฐ๋ฒ• ์ ์šฉ ์‚ฌ๋ก€ํ”„๋กœ์ ํŠธ ์ดˆ๊ธฐ์—๋Š” ๋กœ๊ทธ ๋ ˆ๋ฒจ์„ ์ ์ ˆํžˆ ์„ค์ •ํ•˜์ง€ ์•Š์•„ ๋””๋ฒ„๊น…์— ์–ด๋ ค์›€์ด ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ์ดํ›„ ๋กœ๊ทธ ๋ ˆ๋ฒจ์„ ์„ธ๋ถ„ํ™”ํ•˜๊ณ , ๊ฐ ๋ ˆ๋ฒจ์— ๋งž๋Š” ๋กœ๊ทธ๋ฅผ ๋‚จ๊ธฐ๋„๋ก ๊ฐœ์„ ํ–ˆ์Šต๋‹ˆ๋‹ค.
    json์ฝ”๋“œ ๋ณต์‚ฌ
    {
      "Logging": {
        "LogLevel": {
          "Default": "Information",
          "Microsoft": "Warning",
          "Microsoft.Hosting.Lifetime": "Information"
        }
      }
    }
    
    
    ์˜ˆ์‹œ ์ฝ”๋“œ: WorkInstructionService5. ๋ถ„์‚ฐ ํŠธ๋ ˆ์ด์‹ฑ ๋„์ž…์˜ˆ์‹œ ์ฝ”๋“œ: Startup.cs๊ฒฐ๋ก 
  • ์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” ์˜์กด์„ฑ ์ฃผ์ž…๊ณผ ๊ณ ๊ธ‰ ๋””๋ฒ„๊น… ๊ธฐ๋ฒ•์„ ์‹ค์ œ ํ”„๋กœ์ ํŠธ์— ์ ์šฉํ•œ ์‚ฌ๋ก€๋ฅผ ๊ณต์œ ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ํŒจํ„ด๊ณผ ๊ธฐ๋ฒ•์„ ํ†ตํ•ด ์ฝ”๋“œ์˜ ์œ ์ง€๋ณด์ˆ˜์„ฑ๊ณผ ํ…Œ์ŠคํŠธ ๊ฐ€๋Šฅ์„ฑ์„ ํฌ๊ฒŒ ํ–ฅ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ ํฌ์ŠคํŒ…์—์„œ๋Š” ์ด๋Ÿฌํ•œ ๊ธฐ๋ฒ•๋“ค์„ ์ ์šฉํ•œ ๊ฒฐ๊ณผ์™€ ์•ž์œผ๋กœ์˜ ๊ฐœ์„  ๋ฐฉํ–ฅ์„ ๋…ผ์˜ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.
  • csharp์ฝ”๋“œ ๋ณต์‚ฌ public void ConfigureServices(IServiceCollection services) { services.AddOpenTelemetryTracing(builder => builder .AddAspNetCoreInstrumentation() .AddHttpClientInstrumentation() .AddJaegerExporter()); }
  • ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค ๊ฐ„์˜ ํ˜ธ์ถœ์„ ์ถ”์ ํ•˜๊ธฐ ์œ„ํ•ด ๋ถ„์‚ฐ ํŠธ๋ ˆ์ด์‹ฑ์„ ๋„์ž…ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ๊ฐ ์„œ๋น„์Šค์˜ ํ˜ธ์ถœ ๊ฒฝ๋กœ๋ฅผ ์‹œ๊ฐํ™”ํ•˜๊ณ , ๋ฌธ์ œ ๋ฐœ์ƒ ์ง€์ ์„ ์‹ ์†ํ•˜๊ฒŒ ํŒŒ์•…ํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.
  • csharp์ฝ”๋“œ ๋ณต์‚ฌ public class WorkInstructionService { private readonly IWorkInstructionRepository _repository; private readonly ILogger<WorkInstructionService> _logger; public WorkInstructionService(IWorkInstructionRepository repository, ILogger<WorkInstructionService> logger) { _repository = repository; _logger = logger; } public async Task<IEnumerable<WorkInstruction>> GetWorkInstructionsAsync() { _logger.LogInformation("Fetching all work instructions"); try { var instructions = await _repository.GetAllAsync(); _logger.LogInformation($"Fetched {instructions.Count()} work instructions"); return instructions; } catch (Exception ex) { _logger.LogError(ex, "Error fetching work instructions"); throw; } } // ์ถ”๊ฐ€์ ์ธ ๋ฉ”์„œ๋“œ ์ƒ๋žต... }
  • ์˜ˆ์‹œ ์ฝ”๋“œ: appsettings.json
  • 4. ๋กœ๊ทธ ๋ ˆ๋ฒจ ์„ค์ • ๋ฐ ํ™œ์šฉ
  • csharp์ฝ”๋“œ ๋ณต์‚ฌ public class WorkInstructionController : ControllerBase { private readonly ILogger<WorkInstructionController> _logger; [FromServices] public WorkInstructionService Service { get; set; } public WorkInstructionController(ILogger<WorkInstructionController> logger) { _logger = logger; } [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"); } } // ์ถ”๊ฐ€์ ์ธ ๋ฉ”์„œ๋“œ ์ƒ๋žต... }
  • ํ•„์š”ํ•œ ๊ฒฝ์šฐ ์†์„ฑ ์ฃผ์ž… ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•˜์—ฌ ์˜์กด์„ฑ์„ ์ฃผ์ž…ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด ๋ฐฉ์‹์€ ํด๋ž˜์Šค ๋‚ด ํŠน์ • ์†์„ฑ์— ์˜์กด์„ฑ์„ ์ฃผ์ž…ํ•  ์ˆ˜ ์žˆ์–ด ์œ ์—ฐํ•œ ๊ตฌ์กฐ๋ฅผ ์ œ๊ณตํ–ˆ์Šต๋‹ˆ๋‹ค.
  • csharp์ฝ”๋“œ ๋ณต์‚ฌ public class WorkInstructionController : ControllerBase { private readonly ILogger<WorkInstructionController> _logger; public WorkInstructionController(ILogger<WorkInstructionController> logger) { _logger = logger; } [HttpPost] public async Task<ActionResult<WorkInstruction>> Post(WorkInstruction workInstruction, [FromServices] WorkInstructionService service) { 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"); } } // ์ถ”๊ฐ€์ ์ธ ๋ฉ”์„œ๋“œ ์ƒ๋žต... }
  • ํŠน์ • ๋ฉ”์„œ๋“œ์—์„œ๋งŒ ํ•„์š”ํ•œ ์˜์กด์„ฑ์„ ์ฃผ์ž…ํ•˜๊ธฐ ์œ„ํ•ด ๋ฉ”์„œ๋“œ ์ฃผ์ž… ๋ฐฉ์‹์„ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ํ•„์š”ํ•œ ์˜์กด์„ฑ๋งŒ ์ฃผ์ž…ํ•  ์ˆ˜ ์žˆ์–ด ์ฝ”๋“œ์˜ ํšจ์œจ์„ฑ์„ ๋†’์˜€์Šต๋‹ˆ๋‹ค.
  • csharp์ฝ”๋“œ ๋ณต์‚ฌ public class WorkInstructionService { private readonly IWorkInstructionRepository _repository; private readonly ILogger<WorkInstructionService> _logger; public WorkInstructionService(IWorkInstructionRepository repository, ILogger<WorkInstructionService> logger) { _repository = repository; _logger = logger; } public async Task<IEnumerable<WorkInstruction>> GetWorkInstructionsAsync() { try { return await _repository.GetAllAsync(); } catch (Exception ex) { _logger.LogError(ex, "Error fetching work instructions"); throw; } } // ์ถ”๊ฐ€์ ์ธ ๋ฉ”์„œ๋“œ ์ƒ๋žต... }
  • ํ”„๋กœ์ ํŠธ ์ดˆ๋ฐ˜์—๋Š” ๋Œ€๋ถ€๋ถ„์˜ ์˜์กด์„ฑ์„ ์ƒ์„ฑ์ž ์ฃผ์ž… ๋ฐฉ์‹์œผ๋กœ ์ฒ˜๋ฆฌํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ์˜์กด์„ฑ ์ฃผ์ž…์˜ ๊ฐ€์žฅ ์ผ๋ฐ˜์ ์ธ ๋ฐฉ๋ฒ•์œผ๋กœ, ํด๋ž˜์Šค ๊ฐ„์˜ ๊ฒฐํ•ฉ๋„๋ฅผ ๋‚ฎ์ถ”๊ณ , ์˜์กด์„ฑ์„ ๋ช…ํ™•ํ•˜๊ฒŒ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์–ด ์ฝ”๋“œ์˜ ๊ฐ€๋…์„ฑ๊ณผ ์œ ์ง€๋ณด์ˆ˜์„ฑ์„ ๋†’์˜€์Šต๋‹ˆ๋‹ค.
  • ์˜์กด์„ฑ ์ฃผ์ž… ์ ์šฉ ์‚ฌ๋ก€
  • ํ”„๋กœ์ ํŠธ ๊ฐœ์š”
  • 9. ํ”„๋กœ์ ํŠธ ์ ์šฉ ์‚ฌ๋ก€ - Part 6
  • 10. ํ”„๋กœ์ ํŠธ ์ ์šฉ ์‚ฌ๋ก€ - Part 7์ด์–ด์„œ, ์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” ์•ž์„œ ์–ธ๊ธ‰ํ•œ ์˜์กด์„ฑ ์ฃผ์ž…๊ณผ ๊ณ ๊ธ‰ ๋””๋ฒ„๊น… ๊ธฐ๋ฒ•์„ ์ ์šฉํ•œ ๊ตฌ์ฒด์ ์ธ ์‚ฌ๋ก€๋“ค์„ ํ†ตํ•ด, ์–ด๋–ป๊ฒŒ ํ”„๋กœ์ ํŠธ์˜ ์„ฑ๋Šฅ๊ณผ ์•ˆ์ •์„ฑ์„ ๊ฐœ์„ ํ–ˆ๋Š”์ง€ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ํŠนํžˆ, ๋ถ„์‚ฐ ํŠธ๋ ˆ์ด์‹ฑ ๋„์ž… ์ดํ›„์˜ ์ฃผ์š” ์„ฑ๊ณผ์™€ ๋„์ „์— ๋Œ€ํ•ด ์ž์„ธํžˆ ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.1. ๋ฌธ์ œ ๋ฐœ์ƒ ์ง€์  ํŒŒ์•…์˜ ์šฉ์ด์„ฑ์˜ˆ์‹œ ์ฝ”๋“œ: Startup.cs2. ์„ฑ๋Šฅ ์ตœ์ ํ™”์˜ˆ์‹œ ์ฝ”๋“œ: PerformanceService.cs์ฃผ์š” ๋„์ „ ๊ณผ์ œ๋ถ„์‚ฐ ํŠธ๋ ˆ์ด์‹ฑ ๋„์ž… ์ดˆ๊ธฐ์—๋Š” Jaeger์™€ ๊ฐ™์€ ๋„๊ตฌ๋ฅผ ์„ค์ •ํ•˜๋Š” ๊ณผ์ •์ด ๋‹ค์†Œ ๋ณต์žกํ–ˆ์Šต๋‹ˆ๋‹ค. ํŠนํžˆ, ๋„คํŠธ์›Œํฌ ์„ค์ •๊ณผ ๋ณด์•ˆ ๊ด€๋ จ ์ด์Šˆ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๋ฐ ๋งŽ์€ ์‹œ๊ฐ„์ด ์†Œ์š”๋˜์—ˆ์Šต๋‹ˆ๋‹ค.
    yaml์ฝ”๋“œ ๋ณต์‚ฌ
    version: '3.7'
    services:
      jaeger:
        image: jaegertracing/all-in-one:1.22
        ports:
          - "5775:5775/udp"
          - "6831:6831/udp"
          - "6832:6832/udp"
          - "5778:5778"
          - "16686:16686"
          - "14268:14268"
          - "14250:14250"
          - "9411:9411"
    
    
    2. ๋ฐ์ดํ„ฐ ๋ถ„์„์˜ ์–ด๋ ค์›€์˜ˆ์‹œ ์ฝ”๋“œ: grafana-dashboards.yml์ ์šฉ ๊ฒฐ๊ณผ๋ถ„์‚ฐ ํŠธ๋ ˆ์ด์‹ฑ ๋„์ž…๊ณผ ์„ฑ๋Šฅ ์ตœ์ ํ™”๋ฅผ ํ†ตํ•ด ์„œ๋น„์Šค์˜ ๊ฐ€์šฉ์„ฑ์ด ํฌ๊ฒŒ ํ–ฅ์ƒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ์‚ฌ์šฉ์ž ๊ฒฝํ—˜ ๊ฐœ์„ ์œผ๋กœ ์ด์–ด์กŒ์œผ๋ฉฐ, ์‹œ์Šคํ…œ ์žฅ์•  ๋ฐœ์ƒ ๋นˆ๋„๊ฐ€ ๊ฐ์†Œํ–ˆ์Šต๋‹ˆ๋‹ค.๋ฌธ์ œ ๋ฐœ์ƒ ์‹œ ์‹ ์†ํ•˜๊ฒŒ ์›์ธ์„ ํŒŒ์•…ํ•˜๊ณ  ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์–ด ๊ฐœ๋ฐœ ๋ฐ ์šด์˜ ํšจ์œจ์„ฑ์ด ์ฆ๋Œ€๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ํŒ€์˜ ์ƒ์‚ฐ์„ฑ์„ ๋†’์ด๊ณ , ๋” ๋‚˜์€ ํ’ˆ์งˆ์˜ ์ฝ”๋“œ๋ฅผ ์ œ๊ณตํ•˜๋Š” ๋ฐ ๊ธฐ์—ฌํ–ˆ์Šต๋‹ˆ๋‹ค.์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” ๋ถ„์‚ฐ ํŠธ๋ ˆ์ด์‹ฑ ๋„์ž… ํ›„ ํ”„๋กœ์ ํŠธ์— ๋ฏธ์นœ ๊ธ์ •์ ์ธ ์˜ํ–ฅ์„ ์‚ดํŽด๋ณด์•˜์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๊ธฐ๋ฒ•๋“ค์€ ๋ณต์žกํ•œ ์‹œ์Šคํ…œ์—์„œ ๋ฌธ์ œ๋ฅผ ์‹ ์†ํ•˜๊ฒŒ ํŒŒ์•…ํ•˜๊ณ  ํ•ด๊ฒฐํ•˜๋Š” ๋ฐ ์ค‘์š”ํ•œ ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค. ๋‹ค์Œ ํฌ์ŠคํŒ…์—์„œ๋Š” ํ…Œ์ŠคํŠธ ์ž๋™ํ™”์™€ CI/CD ๋„์ž…์„ ํ†ตํ•ด ํ”„๋กœ์ ํŠธ์˜ ํ’ˆ์งˆ์„ ์–ด๋–ป๊ฒŒ ํ–ฅ์ƒ์‹œ์ผฐ๋Š”์ง€์— ๋Œ€ํ•ด ๋…ผ์˜ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.
  • ๊ฒฐ๋ก 
  • 2. ๊ฐœ๋ฐœ ๋ฐ ์šด์˜ ํšจ์œจ์„ฑ ์ฆ๋Œ€
  • 1. ์„œ๋น„์Šค ๊ฐ€์šฉ์„ฑ ํ–ฅ์ƒ
  • yaml์ฝ”๋“œ ๋ณต์‚ฌ apiVersion: 1 providers: - name: 'default' orgId: 1 folder: '' type: file disableDeletion: false updateIntervalSeconds: 10 options: path: /var/lib/grafana/dashboards
  • ํŠธ๋ ˆ์ด์‹ฑ ๋ฐ์ดํ„ฐ๋ฅผ ๋ถ„์„ํ•˜๋Š” ๋ฐ ํ•„์š”ํ•œ ๋„๊ตฌ์™€ ๊ธฐ์ˆ ์ด ํ•„์š”ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด Grafana์™€ ๊ฐ™์€ ์‹œ๊ฐํ™” ๋„๊ตฌ๋ฅผ ๋„์ž…ํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด๋‹ค ์‰ฝ๊ฒŒ ๋ถ„์„ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ์˜ˆ์‹œ ์ฝ”๋“œ: docker-compose.yml
  • 1. ์ดˆ๊ธฐ ์„ค์ •์˜ ๋ณต์žก์„ฑ
  • csharp์ฝ”๋“œ ๋ณต์‚ฌ public class PerformanceService { private readonly IPerformanceRepository _repository; private readonly ILogger<PerformanceService> _logger; public PerformanceService(IPerformanceRepository repository, ILogger<PerformanceService> logger) { _repository = repository; _logger = logger; } public async Task OptimizeServiceAsync() { _logger.LogInformation("Starting performance optimization"); try { var data = await _repository.GetPerformanceDataAsync(); // ์„ฑ๋Šฅ ์ตœ์ ํ™” ๋กœ์ง ์ถ”๊ฐ€ _logger.LogInformation("Performance optimization completed"); } catch (Exception ex) { _logger.LogError(ex, "Error during performance optimization"); throw; } } }
  • ํŠธ๋ ˆ์ด์‹ฑ ๋ฐ์ดํ„ฐ๋ฅผ ๋ถ„์„ํ•˜์—ฌ ์‹œ์Šคํ…œ ์„ฑ๋Šฅ์„ ์ตœ์ ํ™”ํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ํŠน์ • ์„œ๋น„์Šค ํ˜ธ์ถœ์ด ๋ณ‘๋ชฉ ํ˜„์ƒ์„ ์ผ์œผํ‚ค๋Š” ๊ฒฝ์šฐ๋ฅผ ๋ฐœ๊ฒฌํ•˜๊ณ  ์ด๋ฅผ ์ตœ์ ํ™”ํ•˜์—ฌ ์ „์ฒด ์‹œ์Šคํ…œ์˜ ์‘๋‹ต ์‹œ๊ฐ„์„ ๊ฐœ์„ ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • csharp์ฝ”๋“œ ๋ณต์‚ฌ public void ConfigureServices(IServiceCollection services) { services.AddOpenTelemetryTracing(builder => builder .AddAspNetCoreInstrumentation() .AddHttpClientInstrumentation() .AddJaegerExporter(options => { options.AgentHost = "localhost"; options.AgentPort = 6831; })); }
  • ๋ถ„์‚ฐ ํŠธ๋ ˆ์ด์‹ฑ ๋„์ž… ์ดํ›„, ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค ๊ฐ„์˜ ํ˜ธ์ถœ ๊ฒฝ๋กœ๋ฅผ ์‹œ๊ฐํ™”ํ•จ์œผ๋กœ์จ ๋ฌธ์ œ ๋ฐœ์ƒ ์ง€์ ์„ ์‹ ์†ํ•˜๊ฒŒ ํŒŒ์•…ํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ํŠนํžˆ ๋ณต์žกํ•œ ์‹œ์Šคํ…œ์—์„œ ๋ฌธ์ œ์˜ ๊ทผ๋ณธ ์›์ธ์„ ์ฐพ๋Š” ๋ฐ ํฐ ๋„์›€์ด ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.
  • ๋ถ„์‚ฐ ํŠธ๋ ˆ์ด์‹ฑ ๋„์ž… ํ›„ ์„ฑ๊ณผ
  • 10. ํ”„๋กœ์ ํŠธ ์ ์šฉ ์‚ฌ๋ก€ - Part 7
  • 11. ํ”„๋กœ์ ํŠธ ์ ์šฉ ์‚ฌ๋ก€ - Part 8์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” ํ…Œ์ŠคํŠธ ์ž๋™ํ™”์™€ CI/CD ๋„์ž…์„ ํ†ตํ•ด ํ”„๋กœ์ ํŠธ์˜ ํ’ˆ์งˆ๊ณผ ํšจ์œจ์„ฑ์„ ์–ด๋–ป๊ฒŒ ํ–ฅ์ƒ์‹œ์ผฐ๋Š”์ง€์— ๋Œ€ํ•ด ๋…ผ์˜ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ์ด ๊ณผ์ •์—์„œ ์šฐ๋ฆฌ๊ฐ€ ์ง๋ฉดํ•œ ๋„์ „ ๊ณผ์ œ์™€ ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•œ ๋„๊ตฌ ๋ฐ ๊ธฐ๋ฒ•์— ๋Œ€ํ•ด ์ž์„ธํžˆ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.1. ๋‹จ์œ„ ํ…Œ์ŠคํŠธ(Unit Testing)์˜ˆ์‹œ ์ฝ”๋“œ: WorkInstructionServiceTests.cs2. ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ(Integration Testing)์˜ˆ์‹œ ์ฝ”๋“œ: WorkInstructionIntegrationTests.csCI/CD ๋„์ž…CI ๋„๊ตฌ๋กœ GitHub Actions๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ฝ”๋“œ๋ฅผ ํ‘ธ์‹œํ•  ๋•Œ๋งˆ๋‹ค ์ž๋™์œผ๋กœ ๋นŒ๋“œํ•˜๊ณ  ํ…Œ์ŠคํŠธ๋ฅผ ์‹คํ–‰ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์ฝ”๋“œ ํ’ˆ์งˆ์„ ์ง€์†์ ์œผ๋กœ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.
    yaml์ฝ”๋“œ ๋ณต์‚ฌ
    name: CI
    
    on:
      push:
        branches:
          - main
      pull_request:
        branches:
          - main
    
    jobs:
      build:
        runs-on: ubuntu-latest
    
        steps:
        - uses: actions/checkout@v2
        - name: Setup .NET Core
          uses: actions/setup-dotnet@v1
          with:
            dotnet-version: 5.0.x
        - name: Install dependencies
          run: dotnet restore
        - name: Build
          run: dotnet build --no-restore
        - name: Test
          run: dotnet test --no-restore --verbosity normal
    
    
    2. Continuous Deployment (CD)์˜ˆ์‹œ ์ฝ”๋“œ: deploy.sh์ฃผ์š” ์„ฑ๊ณผ ๋ฐ ๋„์ „ ๊ณผ์ œํ…Œ์ŠคํŠธ ์ž๋™ํ™”๋ฅผ ํ†ตํ•ด ํ…Œ์ŠคํŠธ ์ปค๋ฒ„๋ฆฌ์ง€๋ฅผ ๋Œ€ํญ ํ–ฅ์ƒ์‹œ์ผฐ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ์ฝ”๋“œ ํ’ˆ์งˆ์„ ๋†’์ด๊ณ , ๋ฒ„๊ทธ๋ฅผ ์‚ฌ์ „์— ๋ฐฉ์ง€ํ•˜๋Š” ๋ฐ ํฐ ๋„์›€์ด ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.CI/CD ๋„์ž…์œผ๋กœ ๋ฐฐํฌ ์†๋„๊ฐ€ ํฌ๊ฒŒ ํ–ฅ์ƒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ๊ฐœ๋ฐœ ์ฃผ๊ธฐ๋ฅผ ๋‹จ์ถ•ํ•˜๊ณ , ์‚ฌ์šฉ์ž์—๊ฒŒ ๋” ๋น ๋ฅด๊ฒŒ ์—…๋ฐ์ดํŠธ๋ฅผ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ–ˆ์Šต๋‹ˆ๋‹ค.CI/CD ํŒŒ์ดํ”„๋ผ์ธ์„ ์„ค์ •ํ•˜๋Š” ์ดˆ๊ธฐ ๋‹จ๊ณ„์—์„œ ๋งŽ์€ ์‹œ๊ฐ„๊ณผ ๋…ธ๋ ฅ์ด ํ•„์š”ํ–ˆ์Šต๋‹ˆ๋‹ค. ํŠนํžˆ, ๋‹ค์–‘ํ•œ ํ™˜๊ฒฝ์—์„œ์˜ ์„ค์ •๊ณผ ๋ณด์•ˆ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๋ฐ ์–ด๋ ค์›€์ด ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” ํ…Œ์ŠคํŠธ ์ž๋™ํ™”์™€ CI/CD ๋„์ž…์„ ํ†ตํ•ด ํ”„๋กœ์ ํŠธ์˜ ํ’ˆ์งˆ๊ณผ ํšจ์œจ์„ฑ์„ ์–ด๋–ป๊ฒŒ ํ–ฅ์ƒ์‹œ์ผฐ๋Š”์ง€ ์‚ดํŽด๋ณด์•˜์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๋„๊ตฌ์™€ ๊ธฐ๋ฒ•์„ ํ†ตํ•ด ์ฝ”๋“œ ํ’ˆ์งˆ์„ ์œ ์ง€ํ•˜๊ณ , ์‹ ์†ํ•˜๊ฒŒ ๊ธฐ๋Šฅ์„ ๋ฐฐํฌํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ ํฌ์ŠคํŒ…์—์„œ๋Š” ์‚ฌ์šฉ์ž ํ”ผ๋“œ๋ฐฑ์„ ๋ฐ˜์˜ํ•œ ๊ธฐ๋Šฅ ๊ฐœ์„  ์‚ฌ๋ก€์— ๋Œ€ํ•ด ๋…ผ์˜ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.
  • ๊ฒฐ๋ก 
  • 3. ์ดˆ๊ธฐ ์„ค์ •์˜ ๋ณต์žก์„ฑ
  • 2. ๋ฐฐํฌ ์†๋„ ํ–ฅ์ƒ
  • 1. ํ…Œ์ŠคํŠธ ์ปค๋ฒ„๋ฆฌ์ง€ ํ–ฅ์ƒ
  • bash์ฝ”๋“œ ๋ณต์‚ฌ #!/bin/bash set -e # Build the project dotnet publish -c Release -o out # Stop the existing application systemctl stop myapp # Deploy the new application cp -r out/* /var/www/myapp # Start the application systemctl start myapp echo "Deployment completed successfully"
  • CD ํŒŒ์ดํ”„๋ผ์ธ์„ ํ†ตํ•ด ํ…Œ์ŠคํŠธ๋ฅผ ํ†ต๊ณผํ•œ ์ฝ”๋“œ๋ฅผ ์ž๋™์œผ๋กœ ๋ฐฐํฌํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ๊ณผ ์ˆ˜์ • ์‚ฌํ•ญ์„ ์‹ ์†ํ•˜๊ฒŒ ์‚ฌ์šฉ์ž์—๊ฒŒ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.
  • ์˜ˆ์‹œ ์ฝ”๋“œ: .github/workflows/ci.yml
  • 1. Continuous Integration (CI)
  • csharp์ฝ”๋“œ ๋ณต์‚ฌ using System.Net.Http; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc.Testing; using Xunit; public class WorkInstructionIntegrationTests : IClassFixture<WebApplicationFactory<Startup>> { private readonly HttpClient _client; public WorkInstructionIntegrationTests(WebApplicationFactory<Startup> factory) { _client = factory.CreateClient(); } [Fact] public async Task GetWorkInstructions_ReturnsSuccessStatusCode() { // Act var response = await _client.GetAsync("/api/WorkInstruction"); // Assert response.EnsureSuccessStatusCode(); var responseString = await response.Content.ReadAsStringAsync(); Assert.Contains("Task1", responseString); } }
  • ๋‹จ์œ„ ํ…Œ์ŠคํŠธ์™€ ํ•จ๊ป˜ ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ๋ฅผ ๋„์ž…ํ•˜์—ฌ ์„œ๋น„์Šค ๊ฐ„์˜ ์ƒํ˜ธ์ž‘์šฉ์„ ๊ฒ€์ฆํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์‹œ์Šคํ…œ์˜ ๋‹ค์–‘ํ•œ ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ํ†ตํ•ฉ๋˜๊ณ  ํ˜‘๋ ฅํ•˜๋Š”์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.
  • 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); } }
  • ํ”„๋กœ์ ํŠธ์˜ ํ•ต์‹ฌ ๊ธฐ๋Šฅ์„ ๊ฒ€์ฆํ•˜๊ธฐ ์œ„ํ•ด ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์ฝ”๋“œ ๋ณ€๊ฒฝ ์‹œ ๊ธฐ์กด ๊ธฐ๋Šฅ์ด ์˜๋„ํ•œ ๋Œ€๋กœ ๋™์ž‘ํ•˜๋Š”์ง€ ์‹ ์†ํ•˜๊ฒŒ ํ™•์ธํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.
  • ํ…Œ์ŠคํŠธ ์ž๋™ํ™” ๋„์ž…
  • 11. ํ”„๋กœ์ ํŠธ ์ ์šฉ ์‚ฌ๋ก€ - Part 8
  • 12. ํ”„๋กœ์ ํŠธ ์ ์šฉ ์‚ฌ๋ก€ - Part 9์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” ์‚ฌ์šฉ์ž ํ”ผ๋“œ๋ฐฑ์„ ๋ฐ˜์˜ํ•˜์—ฌ ๊ธฐ๋Šฅ์„ ๊ฐœ์„ ํ•œ ์‚ฌ๋ก€์— ๋Œ€ํ•ด ๋…ผ์˜ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž ํ”ผ๋“œ๋ฐฑ์€ ์‹œ์Šคํ…œ์˜ ์‹ค์ œ ์‚ฌ์šฉ์„ฑ์„ ํ–ฅ์ƒ์‹œํ‚ค๋Š” ์ค‘์š”ํ•œ ์š”์†Œ๋กœ ์ž‘์šฉํ–ˆ์œผ๋ฉฐ, ์ด๋ฅผ ํ†ตํ•ด ์‚ฌ์šฉ์ž ๊ฒฝํ—˜(UX)์„ ๊ฐœ์„ ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.1. ํ”ผ๋“œ๋ฐฑ ์ˆ˜์ง‘ ๋„๊ตฌ ์‚ฌ์šฉ์˜ˆ์‹œ: ์„ค๋ฌธ์กฐ์‚ฌ ๋„๊ตฌ๋ฅผ ํ†ตํ•œ ํ”ผ๋“œ๋ฐฑ ์ˆ˜์ง‘2. ํ”ผ๋“œ๋ฐฑ ๋ถ„์„ ๋ฐ ๋ถ„๋ฅ˜์ฃผ์š” ๊ธฐ๋Šฅ ๊ฐœ์„  ์‚ฌํ•ญ์‚ฌ์šฉ์ž๋“ค์ด ๊ฐ€์žฅ ๋ถˆํŽธํ•˜๋‹ค๊ณ  ๋Š๊ผˆ๋˜ ๋ถ€๋ถ„์€ UI/UX์˜€์Šต๋‹ˆ๋‹ค. ํŠนํžˆ, ํŠน์ • ๊ธฐ๋Šฅ์˜ ์ ‘๊ทผ์„ฑ ๋ถ€์กฑ๊ณผ ๋น„์ง๊ด€์ ์ธ ์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ๋ฌธ์ œ๋กœ ์ง€์ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ๊ฐœ์„ ํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ์ž ํ๋ฆ„์„ ์žฌ์„ค๊ณ„ํ•˜๊ณ , ์ ‘๊ทผ์„ฑ์„ ๋†’์ด๊ธฐ ์œ„ํ•œ ์—ฌ๋Ÿฌ UI ์š”์†Œ๋ฅผ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค.
    • ๊ฐœ์„  ์ „
    • ๊ฐœ์„  ํ›„
    2. ๊ธฐ๋Šฅ ์ถ”๊ฐ€ ๋ฐ ์ˆ˜์ •์˜ˆ์‹œ ์ฝ”๋“œ: ๊ฒ€์ƒ‰ ๋ฐ ํ•„ํ„ฐ๋ง ๊ธฐ๋Šฅ3. ์„ฑ๋Šฅ ์ตœ์ ํ™”์˜ˆ์‹œ ์ฝ”๋“œ: ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ฟผ๋ฆฌ ์ตœ์ ํ™”์ฃผ์š” ์„ฑ๊ณผ ๋ฐ ๋„์ „ ๊ณผ์ œ๊ธฐ๋Šฅ ๊ฐœ์„  ์ดํ›„ ์‚ฌ์šฉ์ž ๋งŒ์กฑ๋„๊ฐ€ ํฌ๊ฒŒ ํ–ฅ์ƒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ํŠนํžˆ, UI/UX ๊ฐœ์„ ๊ณผ ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ ์ถ”๊ฐ€๊ฐ€ ํฐ ํ˜ธ์‘์„ ์–ป์—ˆ์Šต๋‹ˆ๋‹ค.์ง€์†์ ์œผ๋กœ ์‚ฌ์šฉ์ž ํ”ผ๋“œ๋ฐฑ์„ ๋ฐ˜์˜ํ•˜๋Š” ๊ณผ์ •์—์„œ ํ”„๋กœ์ ํŠธ์˜ ๋ฐฉํ–ฅ์„ฑ์„ ์œ ์ง€ํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•œ ๋„์ „ ๊ณผ์ œ์˜€์Šต๋‹ˆ๋‹ค. ๋ชจ๋“  ํ”ผ๋“œ๋ฐฑ์„ ์ˆ˜์šฉํ•  ์ˆ˜๋Š” ์—†์—ˆ์ง€๋งŒ, ํ•ต์‹ฌ ๋ฌธ์ œ๋ฅผ ์šฐ์„  ํ•ด๊ฒฐํ•˜๋Š” ์ ‘๊ทผ ๋ฐฉ์‹์„ ํ†ตํ•ด ์‚ฌ์šฉ์ž ์‹ ๋ขฐ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.์„ฑ๋Šฅ ์ตœ์ ํ™”์™€ ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ ๊ตฌํ˜„ ๊ณผ์ •์—์„œ ๊ธฐ์ˆ ์ ์ธ ์–ด๋ ค์›€์ด ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ํŠนํžˆ, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ตœ์ ํ™”์™€ ์บ์‹ฑ ์ „๋žต ๋„์ž…์€ ๋งŽ์€ ์‹œ๊ฐ„๊ณผ ๋…ธ๋ ฅ์„ ํ•„์š”๋กœ ํ–ˆ์Šต๋‹ˆ๋‹ค.์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” ์‚ฌ์šฉ์ž ํ”ผ๋“œ๋ฐฑ์„ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ธฐ๋Šฅ์„ ๊ฐœ์„ ํ•œ ์‚ฌ๋ก€๋ฅผ ์‚ดํŽด๋ณด์•˜์Šต๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž ํ”ผ๋“œ๋ฐฑ์„ ์ ๊ทน์ ์œผ๋กœ ์ˆ˜์ง‘ํ•˜๊ณ  ๋ฐ˜์˜ํ•จ์œผ๋กœ์จ ์‹œ์Šคํ…œ์˜ ์‚ฌ์šฉ์„ฑ์„ ํฌ๊ฒŒ ํ–ฅ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ ํฌ์ŠคํŒ…์—์„œ๋Š” ํ”„๋กœ์ ํŠธ์˜ ๋ณด์•ˆ ๊ฐ•ํ™”๋ฅผ ์œ„ํ•ด ๋„์ž…ํ•œ ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ๋…ผ์˜ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.
  • ๊ฒฐ๋ก 
  • 3. ๊ธฐ์ˆ ์  ๋„์ „ ๊ณผ์ œ
  • 2. ์ง€์†์ ์ธ ํ”ผ๋“œ๋ฐฑ ๋ฐ˜์˜์˜ ์ค‘์š”์„ฑ
  • 1. ์‚ฌ์šฉ์ž ๋งŒ์กฑ๋„ ํ–ฅ์ƒ
  • csharp์ฝ”๋“œ ๋ณต์‚ฌ public async Task<IEnumerable<WorkInstruction>> GetWorkInstructionsAsync() { return await _context.WorkInstructions .AsNoTracking() // ํŠธ๋ž˜ํ‚น ๋น„ํ™œ์„ฑํ™” .ToListAsync(); }
  • ์ผ๋ถ€ ์‚ฌ์šฉ์ž๋Š” ์„ฑ๋Šฅ ๋ฌธ์ œ๋ฅผ ์ง€์ ํ–ˆ์Šต๋‹ˆ๋‹ค. ํŠนํžˆ, ๋ฐ์ดํ„ฐ ๋กœ๋”ฉ ์†๋„์™€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ฐ˜์‘ ์†๋„์— ๋Œ€ํ•œ ๋ถˆ๋งŒ์ด ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ฟผ๋ฆฌ๋ฅผ ์ตœ์ ํ™”ํ•˜๊ณ , ์บ์‹ฑ ์ „๋žต์„ ๋„์ž…ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • csharp์ฝ”๋“œ ๋ณต์‚ฌ public async Task<IEnumerable<WorkInstruction>> SearchWorkInstructionsAsync(string query) { return await _context.WorkInstructions .Where(w => w.TaskName.Contains(query) || w.Content.Contains(query)) .ToListAsync(); } public async Task<IEnumerable<WorkInstruction>> FilterWorkInstructionsAsync(string priority) { return await _context.WorkInstructions .Where(w => w.Priority == priority) .ToListAsync(); }
  • ์‚ฌ์šฉ์ž๋“ค์ด ์ž์ฃผ ์š”์ฒญํ–ˆ๋˜ ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜ ๊ธฐ์กด ๊ธฐ๋Šฅ์„ ๊ฐœ์„ ํ–ˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ์ž‘์—… ์ง€์‹œ์„œ์˜ ๊ฒ€์ƒ‰ ๊ธฐ๋Šฅ๊ณผ ํ•„ํ„ฐ๋ง ๊ธฐ๋Šฅ์„ ๊ฐ•ํ™”ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ์˜ˆ์‹œ: UI ๊ฐœ์„  ์ „ํ›„ ๋น„๊ต
  • 1. UI/UX ๊ฐœ์„ 
  • ์ˆ˜์ง‘๋œ ํ”ผ๋“œ๋ฐฑ์„ ๋ถ„์„ํ•˜์—ฌ ์ฃผ์š” ๋ฌธ์ œ์ ์„ ํŒŒ์•…ํ•˜๊ณ , ์ด๋ฅผ ์šฐ์„ ์ˆœ์œ„์— ๋”ฐ๋ผ ๋ถ„๋ฅ˜ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ž์ฃผ ์–ธ๊ธ‰๋œ ๋ฌธ์ œ์ ์€ ์ฆ‰์‹œ ํ•ด๊ฒฐํ•  ํ•ญ๋ชฉ์œผ๋กœ ์ง€์ •ํ•˜๊ณ , ๋‚˜๋จธ์ง€๋Š” ์ฐจํ›„ ๊ฐœ์„  ๊ณ„ํš์— ๋ฐ˜์˜ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • markdown์ฝ”๋“œ ๋ณต์‚ฌ ### ์‚ฌ์šฉ์ž ์„ค๋ฌธ์กฐ์‚ฌ ์˜ˆ์‹œ 1. ์•ฑ์˜ ์‚ฌ์šฉ์„ฑ์— ๋Œ€ํ•ด ์–ด๋–ป๊ฒŒ ์ƒ๊ฐํ•˜์‹œ๋‚˜์š”? - ๋งค์šฐ ๋ถˆํŽธํ•˜๋‹ค - ๋ถˆํŽธํ•˜๋‹ค - ๋ณดํ†ต์ด๋‹ค - ํŽธ๋ฆฌํ•˜๋‹ค - ๋งค์šฐ ํŽธ๋ฆฌํ•˜๋‹ค 2. ๊ฐ€์žฅ ๋ถˆํŽธํ–ˆ๋˜ ๊ธฐ๋Šฅ์€ ๋ฌด์—‡์ธ๊ฐ€์š”? - [์ž…๋ ฅ๋ž€] 3. ๊ฐœ์„ ์ด ํ•„์š”ํ•œ ๋ถ€๋ถ„์ด ์žˆ๋‹ค๋ฉด ์ ์–ด์ฃผ์„ธ์š”. - [์ž…๋ ฅ๋ž€]
  • ์‚ฌ์šฉ์ž ํ”ผ๋“œ๋ฐฑ์„ ์ฒด๊ณ„์ ์œผ๋กœ ์ˆ˜์ง‘ํ•˜๊ธฐ ์œ„ํ•ด ๋‹ค์–‘ํ•œ ๋„๊ตฌ๋ฅผ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค. ์„ค๋ฌธ์กฐ์‚ฌ ๋„๊ตฌ, ์‚ฌ์šฉ์ž ์ธํ„ฐ๋ทฐ, ๊ทธ๋ฆฌ๊ณ  ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋‚ด ํ”ผ๋“œ๋ฐฑ ๊ธฐ๋Šฅ์„ ํ†ตํ•ด ์‚ฌ์šฉ์ž ์˜๊ฒฌ์„ ๋ชจ์•˜์Šต๋‹ˆ๋‹ค.
  • ์‚ฌ์šฉ์ž ํ”ผ๋“œ๋ฐฑ ์ˆ˜์ง‘ ๋ฐ ๋ถ„์„
  • 12. ํ”„๋กœ์ ํŠธ ์ ์šฉ ์‚ฌ๋ก€ - Part 9
  • 13. ํ”„๋กœ์ ํŠธ ์ ์šฉ ์‚ฌ๋ก€ - Part 10์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” ํ”„๋กœ์ ํŠธ์˜ ๋ณด์•ˆ์„ ๊ฐ•ํ™”ํ•˜๊ธฐ ์œ„ํ•ด ๋„์ž…ํ•œ ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ๋…ผ์˜ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ๋ณด์•ˆ์€ ํŠนํžˆ ์‚ฌ์šฉ์ž ๋ฐ์ดํ„ฐ๋ฅผ ๋ณดํ˜ธํ•˜๊ณ  ์‹œ์Šคํ…œ์˜ ๋ฌด๊ฒฐ์„ฑ์„ ์œ ์ง€ํ•˜๋Š” ๋ฐ ์žˆ์–ด ๋งค์šฐ ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” ๋ณด์•ˆ ๋ฌธ์ œ๋ฅผ ์–ด๋–ป๊ฒŒ ์‹๋ณ„ํ•˜๊ณ  ํ•ด๊ฒฐํ–ˆ๋Š”์ง€, ๊ทธ๋ฆฌ๊ณ  ๋ณด์•ˆ ๊ฐ•ํ™”๋ฅผ ์œ„ํ•ด ๋„์ž…ํ•œ ๋‹ค์–‘ํ•œ ๋ฐฉ๋ฒ•๋“ค์„ ์†Œ๊ฐœํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.1. ๋ณด์•ˆ ๊ฐ์‚ฌ ๋ฐ ์ทจ์•ฝ์  ๋ถ„์„์˜ˆ์‹œ: ๋ณด์•ˆ ๊ฐ์‚ฌ ์ฒดํฌ๋ฆฌ์ŠคํŠธ
    • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ ‘๊ทผ ์ œ์–ด
    • ์‚ฌ์šฉ์ž ์ธ์ฆ ๋ฐ ๊ถŒํ•œ ๊ด€๋ฆฌ
    • ๋ฐ์ดํ„ฐ ์•”ํ˜ธํ™”
    • ๋„คํŠธ์›Œํฌ ๋ณด์•ˆ ์„ค์ •
    • ๋กœ๊ทธ ๋ฐ ๋ชจ๋‹ˆํ„ฐ๋ง ์‹œ์Šคํ…œ
    2. ์™ธ๋ถ€ ๋ณด์•ˆ ํ…Œ์ŠคํŠธ์˜ˆ์‹œ: ํŽœ ํ…Œ์ŠคํŠธ ๋ณด๊ณ ์„œ ์š”์•ฝ
    • SQL ์ธ์ ์…˜ ๊ณต๊ฒฉ ๊ฐ€๋Šฅ์„ฑ
    • XSS(Cross-Site Scripting) ์ทจ์•ฝ์ 
    • CSRF(Cross-Site Request Forgery) ๊ณต๊ฒฉ ๊ฐ€๋Šฅ์„ฑ
    • ์•ฝํ•œ ํŒจ์Šค์›Œ๋“œ ์ •์ฑ…
    ๋ณด์•ˆ ๊ฐ•ํ™” ๋ฐฉ์•ˆ ๋„์ž…๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ ‘๊ทผ์„ ์—„๊ฒฉํžˆ ์ œ์–ดํ•˜๊ธฐ ์œ„ํ•ด ๊ถŒํ•œ์„ ์ตœ์†Œํ™”ํ•˜๊ณ , ์ค‘์š”ํ•œ ๋ฐ์ดํ„ฐ์— ๋Œ€ํ•œ ์ ‘๊ทผ์€ ๊ด€๋ฆฌ์ž๋งŒ ๊ฐ€๋Šฅํ•˜๋„๋ก ์„ค์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๊ฒฐ ๋ฌธ์ž์—ด์„ ์•ˆ์ „ํ•˜๊ฒŒ ๊ด€๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.
    csharp์ฝ”๋“œ ๋ณต์‚ฌ
    var connectionString = Environment.GetEnvironmentVariable("DB_CONNECTION_STRING");
    services.AddDbContext<AppDbContext>(options =>
        options.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString)));
    
    
    2. ์‚ฌ์šฉ์ž ์ธ์ฆ ๋ฐ ๊ถŒํ•œ ๊ด€๋ฆฌ์˜ˆ์‹œ ์ฝ”๋“œ: JWT ์„ค์ •3. ๋ฐ์ดํ„ฐ ์•”ํ˜ธํ™”์˜ˆ์‹œ ์ฝ”๋“œ: ๋ฐ์ดํ„ฐ ์•”ํ˜ธํ™”4. ๋„คํŠธ์›Œํฌ ๋ณด์•ˆ ์„ค์ •์˜ˆ์‹œ: HTTPS ์„ค์ •5. ๋กœ๊ทธ ๋ฐ ๋ชจ๋‹ˆํ„ฐ๋ง ์‹œ์Šคํ…œ์˜ˆ์‹œ: ๋กœ๊ทธ ์„ค์ •์ฃผ์š” ์„ฑ๊ณผ ๋ฐ ๋„์ „ ๊ณผ์ œ๋ณด์•ˆ ๊ฐ์‚ฌ ๋ฐ ์™ธ๋ถ€ ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜์˜ํ•˜์—ฌ ์‹œ์Šคํ…œ์˜ ๋ณด์•ˆ ์ˆ˜์ค€์„ ํฌ๊ฒŒ ํ–ฅ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ํŠนํžˆ, ๋ฐ์ดํ„ฐ ์•”ํ˜ธํ™”์™€ ์‚ฌ์šฉ์ž ์ธ์ฆ ๊ฐ•ํ™”๋Š” ์‚ฌ์šฉ์ž ๋ฐ์ดํ„ฐ ๋ณดํ˜ธ์— ํฐ ๋„์›€์ด ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.๋ณด์•ˆ์€ ํ•œ ๋ฒˆ ์„ค์ •ํ•œ๋‹ค๊ณ  ๋๋‚˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, ์ง€์†์ ์ธ ๊ด€๋ฆฌ์™€ ๊ฐœ์„ ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ์ƒˆ๋กœ์šด ์ทจ์•ฝ์ ์ด ๋ฐœ๊ฒฌ๋  ๋•Œ๋งˆ๋‹ค ์‹ ์†ํ•˜๊ฒŒ ๋Œ€์‘ํ•  ์ˆ˜ ์žˆ๋Š” ์ฒด๊ณ„๋ฅผ ๋งˆ๋ จํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ–ˆ์Šต๋‹ˆ๋‹ค.๋ณด์•ˆ ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•˜๋ฉด์„œ ๋ฐœ์ƒํ•˜๋Š” ์„ฑ๋Šฅ ์ €ํ•˜ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๊ฒƒ์ด ์ฃผ์š” ๋„์ „ ๊ณผ์ œ์˜€์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด ํšจ์œจ์ ์ธ ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์‚ฌ์šฉํ•˜๊ณ , ์„ฑ๋Šฅ ํ…Œ์ŠคํŠธ๋ฅผ ๋ฐ˜๋ณตํ–ˆ์Šต๋‹ˆ๋‹ค.์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” ํ”„๋กœ์ ํŠธ์˜ ๋ณด์•ˆ์„ ๊ฐ•ํ™”ํ•˜๊ธฐ ์œ„ํ•ด ๋„์ž…ํ•œ ๋‹ค์–‘ํ•œ ๋ฐฉ๋ฒ•๋“ค์„ ์‚ดํŽด๋ณด์•˜์Šต๋‹ˆ๋‹ค. ๋ณด์•ˆ์€ ์‚ฌ์šฉ์ž ๋ฐ์ดํ„ฐ๋ฅผ ๋ณดํ˜ธํ•˜๊ณ  ์‹œ์Šคํ…œ์˜ ๋ฌด๊ฒฐ์„ฑ์„ ์œ ์ง€ํ•˜๋Š” ๋ฐ ๋งค์šฐ ์ค‘์š”ํ•˜๋ฉฐ, ์ง€์†์ ์ธ ๊ด€๋ฆฌ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ๋‹ค์Œ ํฌ์ŠคํŒ…์—์„œ๋Š” ํ”„๋กœ์ ํŠธ์˜ ์„ฑ๋Šฅ์„ ์ตœ์ ํ™”ํ•˜๊ธฐ ์œ„ํ•ด ๋„์ž…ํ•œ ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ๋…ผ์˜ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.
  • ๊ฒฐ๋ก 
  • 3. ๊ธฐ์ˆ ์  ๋„์ „ ๊ณผ์ œ
  • 2. ์ง€์†์ ์ธ ๋ณด์•ˆ ์œ ์ง€์˜ ์–ด๋ ค์›€
  • 1. ๋ณด์•ˆ ์ˆ˜์ค€ ํ–ฅ์ƒ
  • csharp์ฝ”๋“œ ๋ณต์‚ฌ services.AddLogging(logging => { logging.ClearProviders(); logging.AddConsole(); logging.AddDebug(); logging.AddEventLog(); });
  • ๋กœ๊ทธ์™€ ๋ชจ๋‹ˆํ„ฐ๋ง ์‹œ์Šคํ…œ์„ ๊ตฌ์ถ•ํ•˜์—ฌ ๋ณด์•ˆ ์ด๋ฒคํŠธ๋ฅผ ์‹ค์‹œ๊ฐ„์œผ๋กœ ๊ฐ์‹œํ•˜๊ณ , ์ด์ƒ ์ง•ํ›„๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ์ฆ‰์‹œ ๋Œ€์‘ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • csharp์ฝ”๋“œ ๋ณต์‚ฌ webBuilder.ConfigureKestrel(serverOptions => { serverOptions.ConfigureHttpsDefaults(listenOptions => { listenOptions.SslProtocols = SslProtocols.Tls12; listenOptions.ServerCertificate = new X509Certificate2("path_to_certificate.pfx", "password"); }); });
  • ๋„คํŠธ์›Œํฌ ๋ ˆ๋ฒจ์—์„œ ๋ณด์•ˆ์„ ๊ฐ•ํ™”ํ•˜๊ธฐ ์œ„ํ•ด ๋ฐฉํ™”๋ฒฝ ์„ค์ •์„ ์žฌ๊ฒ€ํ† ํ•˜๊ณ , HTTPS๋ฅผ ์ ์šฉํ•˜์—ฌ ๋ชจ๋“  ํ†ต์‹ ์„ ์•”ํ˜ธํ™”ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • csharp์ฝ”๋“œ ๋ณต์‚ฌ public string Encrypt(string plainText) { using (var aes = Aes.Create()) { var key = Encoding.UTF8.GetBytes(Configuration["EncryptionKey"]); aes.Key = key.Take(32).ToArray(); aes.IV = key.Take(16).ToArray(); var encryptor = aes.CreateEncryptor(aes.Key, aes.IV); using (var ms = new MemoryStream()) { using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write)) { using (var sw = new StreamWriter(cs)) { sw.Write(plainText); } } return Convert.ToBase64String(ms.ToArray()); } } }
  • ์ค‘์š”ํ•œ ๋ฐ์ดํ„ฐ๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅํ•˜๊ธฐ ์ „์— ์•”ํ˜ธํ™”ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ๋ฐ์ดํ„ฐ๊ฐ€ ์œ ์ถœ๋˜๋”๋ผ๋„ ๋‚ด์šฉ์„ ์•Œ์•„๋ณผ ์ˆ˜ ์—†๊ฒŒ ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • csharp์ฝ”๋“œ ๋ณต์‚ฌ services.AddAuthentication(options => { options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, ValidIssuer = Configuration["Jwt:Issuer"], ValidAudience = Configuration["Jwt:Audience"], IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"])) }; });
  • JWT(Json Web Token)๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์‚ฌ์šฉ์ž ์ธ์ฆ ๋ฐ ๊ถŒํ•œ ๊ด€๋ฆฌ๋ฅผ ๊ฐ•ํ™”ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ๊ฐ ์š”์ฒญ์ด ์ ์ ˆํ•œ ๊ถŒํ•œ์„ ๊ฐ€์ง„ ์‚ฌ์šฉ์ž์ธ์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์˜ˆ์‹œ ์ฝ”๋“œ: ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ์‚ฌ์šฉ
  • 1. ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ ‘๊ทผ ์ œ์–ด
  • ์™ธ๋ถ€ ๋ณด์•ˆ ์ „๋ฌธ๊ฐ€๋ฅผ ์ดˆ์ฒญํ•˜์—ฌ ํŽœ ํ…Œ์ŠคํŠธ(Penetration Test)๋ฅผ ์‹ค์‹œํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์‹ค์ œ ๊ณต๊ฒฉ ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ํ…Œ์ŠคํŠธํ•˜๊ณ , ์‹œ์Šคํ…œ์˜ ๋ณด์•ˆ ์ˆ˜์ค€์„ ํ‰๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ๋จผ์ €, ์‹œ์Šคํ…œ์˜ ๋ณด์•ˆ ์ƒํƒœ๋ฅผ ํ‰๊ฐ€ํ•˜๊ธฐ ์œ„ํ•ด ๋‚ด๋ถ€ ๋ณด์•ˆ ๊ฐ์‚ฌ๋ฅผ ์ˆ˜ํ–‰ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์ž ์žฌ์ ์ธ ์ทจ์•ฝ์ ์„ ์‹๋ณ„ํ•˜๊ณ , ์ด์— ๋Œ€ํ•œ ๋Œ€์‘ ๋ฐฉ์•ˆ์„ ๋งˆ๋ จํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ๋ณด์•ˆ ๋ฌธ์ œ ์‹๋ณ„ ๋ฐ ๋ถ„์„
  • 13. ํ”„๋กœ์ ํŠธ ์ ์šฉ ์‚ฌ๋ก€ - Part 10
  • 14. ํ”„๋กœ์ ํŠธ ์ ์šฉ ์‚ฌ๋ก€ - Part 11์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” ํ”„๋กœ์ ํŠธ์˜ ์„ฑ๋Šฅ์„ ์ตœ์ ํ™”ํ•˜๊ธฐ ์œ„ํ•ด ๋„์ž…ํ•œ ๋‹ค์–‘ํ•œ ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ๋…ผ์˜ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ์„ฑ๋Šฅ ์ตœ์ ํ™”๋Š” ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ๊ฐœ์„ ํ•˜๊ณ  ์‹œ์Šคํ…œ์˜ ํšจ์œจ์„ฑ์„ ๋†’์ด๊ธฐ ์œ„ํ•ด ๋งค์šฐ ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค. ์ด ํฌ์ŠคํŒ…์—์„œ๋Š” ์„ฑ๋Šฅ ๋ฌธ์ œ๋ฅผ ์–ด๋–ป๊ฒŒ ์‹๋ณ„ํ•˜๊ณ  ํ•ด๊ฒฐํ–ˆ๋Š”์ง€, ๊ทธ๋ฆฌ๊ณ  ์ตœ์ ํ™”๋ฅผ ์œ„ํ•ด ๋„์ž…ํ•œ ๋‹ค์–‘ํ•œ ๋ฐฉ๋ฒ•๋“ค์„ ์†Œ๊ฐœํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.1. ์„ฑ๋Šฅ ๋ชจ๋‹ˆํ„ฐ๋ง ๋„๊ตฌ ์‚ฌ์šฉ์˜ˆ์‹œ: Application Insights ์‚ฌ์šฉ2. ๋กœ๋“œ ํ…Œ์ŠคํŠธ ๋ฐ ์ŠคํŠธ๋ ˆ์Šค ํ…Œ์ŠคํŠธ์˜ˆ์‹œ: Apache JMeter ์‚ฌ์šฉ
    • JMeter๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์—ฌ๋Ÿฌ ์‚ฌ์šฉ์ž ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ์‹œ๋ฎฌ๋ ˆ์ด์…˜ํ•˜๊ณ , ๊ฐ ์‹œ๋‚˜๋ฆฌ์˜ค์—์„œ ์‹œ์Šคํ…œ์˜ ์‘๋‹ต ์‹œ๊ฐ„๊ณผ ์ฒ˜๋ฆฌ๋Ÿ‰์„ ์ธก์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.
    • ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ๋ฅผ ๋ถ„์„ํ•˜์—ฌ ์„ฑ๋Šฅ ๋ณ‘๋ชฉ์ด ๋ฐœ์ƒํ•˜๋Š” ์ง€์ ์„ ํ™•์ธํ–ˆ์Šต๋‹ˆ๋‹ค.
    ์„ฑ๋Šฅ ์ตœ์ ํ™” ๋ฐฉ์•ˆ ๋„์ž…๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ฟผ๋ฆฌ์˜ ์„ฑ๋Šฅ์„ ์ตœ์ ํ™”ํ•˜๊ธฐ ์œ„ํ•ด ์ธ๋ฑ์Šค๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ , ์ฟผ๋ฆฌ๋ฅผ ์žฌ๊ตฌ์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ, ์บ์‹œ๋ฅผ ๋„์ž…ํ•˜์—ฌ ๋ฐ˜๋ณต์ ์ธ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์กฐํšŒ๋ฅผ ์ค„์˜€์Šต๋‹ˆ๋‹ค.
    CREATE INDEX idx_task_name ON WorkInstructions(TaskName);
    
    ์˜ˆ์‹œ ์ฝ”๋“œ: ์บ์‹œ ์‚ฌ์šฉ
    public class WorkInstructionService
    {
        private readonly AppDbContext _context;
        private readonly IMemoryCache _cache;
    
        public WorkInstructionService(AppDbContext context, IMemoryCache cache)
        {
            _context = context;
            _cache = cache;
        }
    
        public async Task<IEnumerable<WorkInstruction>> GetWorkInstructionsAsync()
        {
            if (!_cache.TryGetValue("workInstructions", out List<WorkInstruction> workInstructions))
            {
                workInstructions = await _context.WorkInstructions.ToListAsync();
                _cache.Set("workInstructions", workInstructions, TimeSpan.FromMinutes(5));
            }
            return workInstructions;
        }
    }
    
    2. ๋น„๋™๊ธฐ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๋„์ž…์˜ˆ์‹œ ์ฝ”๋“œ: ๋น„๋™๊ธฐ ๋ฉ”์„œ๋“œ3. ๋กœ๋“œ ๋ฐธ๋Ÿฐ์‹ฑ ๋ฐ ์Šค์ผ€์ผ๋ง์˜ˆ์‹œ: Azure Load Balancer ์‚ฌ์šฉ
    • Azure Load Balancer๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์—ฌ๋Ÿฌ ์ธ์Šคํ„ด์Šค์— ํŠธ๋ž˜ํ”ฝ์„ ๋ถ„์‚ฐ์‹œ์ผฐ์Šต๋‹ˆ๋‹ค.
    • ์ž๋™ ์Šค์ผ€์ผ๋ง์„ ์„ค์ •ํ•˜์—ฌ ํŠธ๋ž˜ํ”ฝ ์ฆ๊ฐ€์— ๋Œ€์‘ํ–ˆ์Šต๋‹ˆ๋‹ค.
    4. ์ •์  ํŒŒ์ผ ์บ์‹ฑ ๋ฐ ์••์ถ•์˜ˆ์‹œ ์ฝ”๋“œ: ์ •์  ํŒŒ์ผ ์บ์‹ฑ์˜ˆ์‹œ ์ฝ”๋“œ: Gzip ์••์ถ•5. ํ”„๋ก ํŠธ์—”๋“œ ์ตœ์ ํ™”์˜ˆ์‹œ ์ฝ”๋“œ: React ๋น„๋™๊ธฐ ๋กœ๋“œ์ฃผ์š” ์„ฑ๊ณผ ๋ฐ ๋„์ „ ๊ณผ์ œ๋‹ค์–‘ํ•œ ์ตœ์ ํ™” ๋ฐฉ๋ฒ•์„ ๋„์ž…ํ•œ ๊ฒฐ๊ณผ, ์‹œ์Šคํ…œ์˜ ์„ฑ๋Šฅ์ด ํฌ๊ฒŒ ํ–ฅ์ƒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ํŠนํžˆ, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ฟผ๋ฆฌ ์ตœ์ ํ™”์™€ ์บ์‹œ ๋„์ž…์€ ์‘๋‹ต ์‹œ๊ฐ„์„ ํฌ๊ฒŒ ๋‹จ์ถ•์‹œ์ผฐ์Šต๋‹ˆ๋‹ค.์„ฑ๋Šฅ ์ตœ์ ํ™”๋Š” ์ผํšŒ์„ฑ ์ž‘์—…์ด ์•„๋‹ˆ๋ผ, ์ง€์†์ ์ธ ๊ด€๋ฆฌ์™€ ๊ฐœ์„ ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•  ๋•Œ๋งˆ๋‹ค ์„ฑ๋Šฅ ํ…Œ์ŠคํŠธ๋ฅผ ํ†ตํ•ด ์‹œ์Šคํ…œ์˜ ์„ฑ๋Šฅ์„ ์œ ์ง€ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.์„ฑ๋Šฅ ์ตœ์ ํ™” ๊ณผ์ •์—์„œ ๋ฐœ์ƒํ•˜๋Š” ๊ธฐ์ˆ ์  ๋„์ „ ๊ณผ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๊ฒƒ์ด ์ฃผ์š” ๊ณผ์ œ์˜€์Šต๋‹ˆ๋‹ค. ํŠนํžˆ, ๋น„๋™๊ธฐ ํ”„๋กœ๊ทธ๋ž˜๋ฐ๊ณผ ๋กœ๋“œ ๋ฐธ๋Ÿฐ์‹ฑ์˜ ๋„์ž…์€ ๋งŽ์€ ์‹œ๊ฐ„๊ณผ ๋…ธ๋ ฅ์ด ํ•„์š”ํ–ˆ์Šต๋‹ˆ๋‹ค.์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” ํ”„๋กœ์ ํŠธ์˜ ์„ฑ๋Šฅ์„ ์ตœ์ ํ™”ํ•˜๊ธฐ ์œ„ํ•ด ๋„์ž…ํ•œ ๋‹ค์–‘ํ•œ ๋ฐฉ๋ฒ•๋“ค์„ ์‚ดํŽด๋ณด์•˜์Šต๋‹ˆ๋‹ค. ์„ฑ๋Šฅ ์ตœ์ ํ™”๋Š” ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ๊ฐœ์„ ํ•˜๊ณ  ์‹œ์Šคํ…œ์˜ ํšจ์œจ์„ฑ์„ ๋†’์ด๋Š” ๋ฐ ๋งค์šฐ ์ค‘์š”ํ•˜๋ฉฐ, ์ง€์†์ ์ธ ๊ด€๋ฆฌ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ๋‹ค์Œ ํฌ์ŠคํŒ…์—์„œ๋Š” ํ”„๋กœ์ ํŠธ์˜ ๋ฐฐํฌ ๋ฐ ์šด์˜์— ๋Œ€ํ•ด ๋…ผ์˜ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.
  • ๊ฒฐ๋ก 
  • 3. ๊ธฐ์ˆ ์  ๋„์ „ ๊ณผ์ œ
  • 2. ์ง€์†์ ์ธ ์„ฑ๋Šฅ ๊ด€๋ฆฌ์˜ ํ•„์š”์„ฑ
  • 1. ์„ฑ๋Šฅ ํ–ฅ์ƒ
  • javascript์ฝ”๋“œ ๋ณต์‚ฌ const WorkInstructions = React.lazy(() => import('./WorkInstructions')); function App() { return ( <Suspense fallback={<div>Loading...</div>}> <WorkInstructions /> </Suspense> ); }
  • ํ”„๋ก ํŠธ์—”๋“œ ์ฝ”๋“œ์˜ ์„ฑ๋Šฅ์„ ์ตœ์ ํ™”ํ•˜๊ธฐ ์œ„ํ•ด ๋ถˆํ•„์š”ํ•œ ๋ Œ๋”๋ง์„ ์ตœ์†Œํ™”ํ•˜๊ณ , ๋น„๋™๊ธฐ ๋กœ๋“œ๋ฅผ ๋„์ž…ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • csharp์ฝ”๋“œ ๋ณต์‚ฌ services.AddResponseCompression(options => { options.Providers.Add<GzipCompressionProvider>(); options.EnableForHttps = true; });
  • app.UseStaticFiles(new StaticFileOptions { OnPrepareResponse = ctx => { ctx.Context.Response.Headers.Append("Cache-Control", "public,max-age=600"); } });
  • ์ •์  ํŒŒ์ผ์˜ ์บ์‹ฑ๊ณผ ์••์ถ•์„ ํ†ตํ•ด ๋„คํŠธ์›Œํฌ ํŠธ๋ž˜ํ”ฝ์„ ์ค„์ด๊ณ , ํŽ˜์ด์ง€ ๋กœ๋“œ ์‹œ๊ฐ„์„ ๋‹จ์ถ•ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ๋กœ๋“œ ๋ฐธ๋Ÿฐ์‹ฑ์„ ๋„์ž…ํ•˜์—ฌ ํŠธ๋ž˜ํ”ฝ์„ ์—ฌ๋Ÿฌ ์„œ๋ฒ„๋กœ ๋ถ„์‚ฐ์‹œํ‚ค๊ณ , ์‹œ์Šคํ…œ์˜ ์•ˆ์ •์„ฑ๊ณผ ์ฒ˜๋ฆฌ ๋Šฅ๋ ฅ์„ ํ–ฅ์ƒ์‹œ์ผฐ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ, ํ•„์š”์— ๋”ฐ๋ผ ์„œ๋ฒ„๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ ์ˆ˜ํ‰์ ์œผ๋กœ ์Šค์ผ€์ผ๋งํ–ˆ์Šต๋‹ˆ๋‹ค.
  • public async Task<WorkInstruction> GetWorkInstructionByTaskNameAsync(string taskName) { return await _context.WorkInstructions.FirstOrDefaultAsync(w => w.TaskName == taskName); }
  • ๋น„๋™๊ธฐ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์„ ๋„์ž…ํ•˜์—ฌ I/O ์ž‘์—…์˜ ๋Œ€๊ธฐ ์‹œ๊ฐ„์„ ์ค„์ด๊ณ , ์‹œ์Šคํ…œ์˜ ์‘๋‹ต์„ฑ์„ ํ–ฅ์ƒ์‹œ์ผฐ์Šต๋‹ˆ๋‹ค.
  • services.AddMemoryCache();
  • ์˜ˆ์‹œ ์ฝ”๋“œ: ์ธ๋ฑ์Šค ์ถ”๊ฐ€
  • 1. ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ฟผ๋ฆฌ ์ตœ์ ํ™”
  • ๋กœ๋“œ ํ…Œ์ŠคํŠธ์™€ ์ŠคํŠธ๋ ˆ์Šค ํ…Œ์ŠคํŠธ๋ฅผ ํ†ตํ•ด ์‹œ์Šคํ…œ์˜ ์ตœ๋Œ€ ์ฒ˜๋ฆฌ ์šฉ๋Ÿ‰์„ ํŒŒ์•…ํ•˜๊ณ , ์„ฑ๋Šฅ์ด ์ €ํ•˜๋˜๋Š” ์ง€์ ์„ ์‹๋ณ„ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • services.AddApplicationInsightsTelemetry(Configuration["ApplicationInsights:InstrumentationKey"]);
  • ํ”„๋กœ์ ํŠธ ์ดˆ๊ธฐ ๋‹จ๊ณ„๋ถ€ํ„ฐ ์„ฑ๋Šฅ ๋ชจ๋‹ˆํ„ฐ๋ง ๋„๊ตฌ๋ฅผ ๋„์ž…ํ•˜์—ฌ ์‹œ์Šคํ…œ์˜ ์„ฑ๋Šฅ์„ ์ง€์†์ ์œผ๋กœ ๋ชจ๋‹ˆํ„ฐ๋งํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์‹œ์Šคํ…œ์˜ ๋ณ‘๋ชฉ ์ง€์ ์„ ์‹๋ณ„ํ•˜๊ณ , ์„ฑ๋Šฅ์„ ์ตœ์ ํ™”ํ•  ์ˆ˜ ์žˆ๋Š” ๊ทผ๊ฑฐ๋ฅผ ๋งˆ๋ จํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ์„ฑ๋Šฅ ๋ฌธ์ œ ์‹๋ณ„ ๋ฐ ๋ถ„์„
  • 14. ํ”„๋กœ์ ํŠธ ์ ์šฉ ์‚ฌ๋ก€ - Part 11
  • 15. ํ”„๋กœ์ ํŠธ ์ ์šฉ ์‚ฌ๋ก€ - Part 12์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” ํ”„๋กœ์ ํŠธ์˜ ๋ฐฐํฌ ๋ฐ ์šด์˜์— ๋Œ€ํ•ด ๋‹ค๋ฃจ๊ฒ ์Šต๋‹ˆ๋‹ค. ์„ฑ๊ณต์ ์ธ ๋ฐฐํฌ์™€ ์›ํ™œํ•œ ์šด์˜์„ ์œ„ํ•ด ์ ์šฉํ•œ ๋ฐฉ๋ฒ•๋ก ๊ณผ ๋„๊ตฌ๋“ค, ๊ทธ๋ฆฌ๊ณ  ๋ฐฐํฌ ๊ณผ์ •์—์„œ ๋ฐœ์ƒํ–ˆ๋˜ ๋ฌธ์ œ์™€ ๊ทธ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•๋“ค์„ ์†Œ๊ฐœํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.1. ๋ฐฐํฌ ํŒŒ์ดํ”„๋ผ์ธ ์„ค์ •์˜ˆ์‹œ: GitHub Actions ์‚ฌ์šฉ2. ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ์˜ˆ์‹œ: Azure DevOps์™€ Blue-Green ๋ฐฐํฌ
    • Azure DevOps ํŒŒ์ดํ”„๋ผ์ธ์„ ์‚ฌ์šฉํ•˜์—ฌ Blue-Green ๋ฐฐํฌ ์ „๋žต์„ ์„ค์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.
    • ์ƒˆ๋กœ์šด ๋ฒ„์ „์„ "Green" ํ™˜๊ฒฝ์— ๋ฐฐํฌํ•˜๊ณ , ํ…Œ์ŠคํŠธ ํ›„ "Blue" ํ™˜๊ฒฝ์œผ๋กœ ํŠธ๋ž˜ํ”ฝ์„ ์ „ํ™˜ํ–ˆ์Šต๋‹ˆ๋‹ค.
    ์šด์˜ ๋ฐ ๋ชจ๋‹ˆํ„ฐ๋งํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์—์„œ ๋ฐœ์ƒํ•˜๋Š” ๋ฌธ์ œ๋ฅผ ์‹ ์†ํ•˜๊ฒŒ ๊ฐ์ง€ํ•˜๊ณ  ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋‹ค์–‘ํ•œ ๋กœ๊น… ๋ฐ ๋ชจ๋‹ˆํ„ฐ๋ง ๋„๊ตฌ๋ฅผ ๋„์ž…ํ–ˆ์Šต๋‹ˆ๋‹ค.
    • Application Insights๋ฅผ ํ†ตํ•ด ์‹ค์‹œ๊ฐ„ ๋ชจ๋‹ˆํ„ฐ๋ง๊ณผ ๋กœ๊ทธ ์ˆ˜์ง‘์„ ์ˆ˜ํ–‰ํ–ˆ์Šต๋‹ˆ๋‹ค.
    • Serilog๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋‹ค์–‘ํ•œ ๋กœ๊ทธ ์ถœ๋ ฅ์„ ์„ค์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.
    ์˜ˆ์‹œ ์ฝ”๋“œ: Serilog ์„ค์ •2. ์ž๋™ํ™”๋œ ๊ฒฝ๊ณ  ์„ค์ •์˜ˆ์‹œ: Azure Monitor ๊ฒฝ๊ณ  ์„ค์ •
    • Azure Monitor๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ CPU ์‚ฌ์šฉ๋ฅ , ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰, HTTP ์š”์ฒญ ์˜ค๋ฅ˜ ๋“ฑ์˜ ์ง€ํ‘œ์— ๋Œ€ํ•ด ๊ฒฝ๊ณ ๋ฅผ ์„ค์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.
    • ๊ฒฝ๊ณ ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ์ด๋ฉ”์ผ์ด๋‚˜ SMS๋กœ ์•Œ๋ฆผ์„ ๋ฐ›๋„๋ก ์„ค์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.
    3. ๋ฐฑ์—… ๋ฐ ๋ณต๊ตฌ ๊ณ„ํš์˜ˆ์‹œ: Azure Backup ์‚ฌ์šฉ
    • Azure Backup์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€ ํŒŒ์ผ ์‹œ์Šคํ…œ์˜ ์ •๊ธฐ์ ์ธ ๋ฐฑ์—…์„ ์„ค์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.
    • ๋ณต๊ตฌ ๊ณ„ํš์„ ํ…Œ์ŠคํŠธํ•˜์—ฌ ๋ฐ์ดํ„ฐ ์†์‹ค ์‹œ ์‹ ์†ํ•˜๊ฒŒ ๋ณต๊ตฌํ•  ์ˆ˜ ์žˆ๋„๋ก ์ค€๋น„ํ–ˆ์Šต๋‹ˆ๋‹ค.
    ๋ฐฐํฌ ๊ณผ์ •์—์„œ์˜ ๋ฌธ์ œ ํ•ด๊ฒฐ๋ฐฐํฌ ๊ณผ์ •์—์„œ ๋ฐœ์ƒํ•œ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ๋กœ๊ทธ์™€ ๋ชจ๋‹ˆํ„ฐ๋ง ๋ฐ์ดํ„ฐ๋ฅผ ๋ถ„์„ํ–ˆ์Šต๋‹ˆ๋‹ค. ํŠนํžˆ, ํ™˜๊ฒฝ ์„ค์ • ์˜ค๋ฅ˜๋‚˜ ๋„คํŠธ์›Œํฌ ๋ฌธ์ œ ๋“ฑ์„ ์ค‘์ ์ ์œผ๋กœ ์ ๊ฒ€ํ–ˆ์Šต๋‹ˆ๋‹ค.
    • ๋ฐฐํฌ ๋กœ๊ทธ๋ฅผ ๋ถ„์„ํ•˜์—ฌ ํ™˜๊ฒฝ ์„ค์ • ํŒŒ์ผ์— ์ž˜๋ชป๋œ ์„ค์ •์ด ์žˆ์Œ์„ ๋ฐœ๊ฒฌํ–ˆ์Šต๋‹ˆ๋‹ค.
    • ์„ค์ • ํŒŒ์ผ์„ ์ˆ˜์ •ํ•˜๊ณ , ๋ฐฐํฌ ํŒŒ์ดํ”„๋ผ์ธ์„ ์žฌ์‹คํ–‰ํ•˜์—ฌ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ–ˆ์Šต๋‹ˆ๋‹ค.
    2. ์„ฑ๋Šฅ ์ €ํ•˜ ๋ฌธ์ œ์˜ˆ์‹œ: ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ฟผ๋ฆฌ ์ตœ์ ํ™”
    • ์„ฑ๋Šฅ ๋ชจ๋‹ˆํ„ฐ๋ง ๋„๊ตฌ๋ฅผ ํ†ตํ•ด ํŠน์ • ์ฟผ๋ฆฌ์˜ ์‹คํ–‰ ์‹œ๊ฐ„์ด ๊ธธ์–ด์กŒ์Œ์„ ๋ฐœ๊ฒฌํ–ˆ์Šต๋‹ˆ๋‹ค.
    • ์ฟผ๋ฆฌ๋ฅผ ์ตœ์ ํ™”ํ•˜๊ณ , ์ธ๋ฑ์Šค๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ ์„ฑ๋Šฅ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ–ˆ์Šต๋‹ˆ๋‹ค.
    ์ฃผ์š” ์„ฑ๊ณผ ๋ฐ ๋„์ „ ๊ณผ์ œCI/CD ํŒŒ์ดํ”„๋ผ์ธ์„ ์„ค์ •ํ•˜์—ฌ ์ฝ”๋“œ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์ด ์ž๋™์œผ๋กœ ๋นŒ๋“œ, ํ…Œ์ŠคํŠธ, ๋ฐฐํฌ๋˜๋„๋ก ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ๋ฐฐํฌ ์ฃผ๊ธฐ๋ฅผ ๋‹จ์ถ•ํ•˜๊ณ , ๋ฐฐํฌ ๊ณผ์ •์—์„œ ๋ฐœ์ƒํ•˜๋Š” ์˜ค๋ฅ˜๋ฅผ ์ตœ์†Œํ™”ํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.์‹ค์‹œ๊ฐ„ ๋ชจ๋‹ˆํ„ฐ๋ง๊ณผ ์ž๋™ํ™”๋œ ๊ฒฝ๊ณ  ์‹œ์Šคํ…œ์„ ๋„์ž…ํ•˜์—ฌ, ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์—์„œ ๋ฐœ์ƒํ•˜๋Š” ๋ฌธ์ œ๋ฅผ ์‹ ์†ํ•˜๊ฒŒ ๊ฐ์ง€ํ•˜๊ณ  ๋Œ€์‘ํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.Blue-Green ๋ฐฐํฌ ์ „๋žต์„ ํ†ตํ•ด ์‚ฌ์šฉ์ž์—๊ฒŒ ์„œ๋น„์Šค ์ค‘๋‹จ ์—†์ด ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ์„ ๋ฐฐํฌํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ํฌ๊ฒŒ ํ–ฅ์ƒ์‹œ์ผฐ์Šต๋‹ˆ๋‹ค.์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” ํ”„๋กœ์ ํŠธ์˜ ๋ฐฐํฌ ๋ฐ ์šด์˜์— ๋Œ€ํ•ด ๋…ผ์˜ํ–ˆ์Šต๋‹ˆ๋‹ค. ์„ฑ๊ณต์ ์ธ ๋ฐฐํฌ์™€ ์›ํ™œํ•œ ์šด์˜์„ ์œ„ํ•ด ๋‹ค์–‘ํ•œ ๋ฐฉ๋ฒ•๋ก ๊ณผ ๋„๊ตฌ๋“ค์„ ๋„์ž…ํ•˜๊ณ , ๋ฐฐํฌ ๊ณผ์ •์—์„œ ๋ฐœ์ƒํ•œ ๋ฌธ์ œ๋“ค์„ ํ•ด๊ฒฐํ–ˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ ํฌ์ŠคํŒ…์—์„œ๋Š” ํ”„๋กœ์ ํŠธ์˜ ์ „๋ฐ˜์ ์ธ ํšŒ๊ณ ์™€ ๊ฐœ์„  ์‚ฌํ•ญ์— ๋Œ€ํ•ด ๋…ผ์˜ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.
  • ๊ฒฐ๋ก 
  • 3. ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ ๊ตฌํ˜„
  • 2. ์‹ค์‹œ๊ฐ„ ๋ชจ๋‹ˆํ„ฐ๋ง ๋ฐ ๊ฒฝ๊ณ  ์‹œ์Šคํ…œ
  • 1. ์ž๋™ํ™”๋œ ๋ฐฐํฌ ํŒŒ์ดํ”„๋ผ์ธ
  • ๋ฐฐํฌ ํ›„ ์„ฑ๋Šฅ ์ €ํ•˜๊ฐ€ ๋ฐœ์ƒํ•œ ๊ฒฝ์šฐ, ์›์ธ์„ ๋ถ„์„ํ•˜์—ฌ ํ•ด๊ฒฐํ–ˆ์Šต๋‹ˆ๋‹ค. ํŠนํžˆ, ์ฝ”๋“œ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์ด๋‚˜ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ฟผ๋ฆฌ๋ฅผ ์ค‘์ ์ ์œผ๋กœ ์ ๊ฒ€ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ์˜ˆ์‹œ: ํ™˜๊ฒฝ ์„ค์ • ์˜ค๋ฅ˜ ํ•ด๊ฒฐ
  • 1. ๋ฐฐํฌ ์‹คํŒจ ๋ฌธ์ œ
  • ๋ฐ์ดํ„ฐ ์†์‹ค์„ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ์ •๊ธฐ์ ์ธ ๋ฐฑ์—…์„ ์ˆ˜ํ–‰ํ•˜๊ณ , ๋ณต๊ตฌ ๊ณ„ํš์„ ์ˆ˜๋ฆฝํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ๋ชจ๋‹ˆํ„ฐ๋ง ๋„๊ตฌ๋ฅผ ํ†ตํ•ด ํŠน์ • ๊ธฐ์ค€์„ ์ดˆ๊ณผํ•˜๋Š” ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ์ž๋™์œผ๋กœ ๊ฒฝ๊ณ ๋ฅผ ๋ณด๋‚ด๋„๋ก ์„ค์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • csharp์ฝ”๋“œ ๋ณต์‚ฌ public class Program { public static void Main(string[] args) { Log.Logger = new LoggerConfiguration() .Enrich.FromLogContext() .WriteTo.Console() .WriteTo.ApplicationInsights(TelemetryConverter.Traces) .CreateLogger(); try { Log.Information("Starting up"); CreateHostBuilder(args).Build().Run(); } catch (Exception ex) { Log.Fatal(ex, "Application start-up failed"); } finally { Log.CloseAndFlush(); } } public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .UseSerilog() .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); }); }
  • ์˜ˆ์‹œ: Application Insights์™€ Serilog ์‚ฌ์šฉ
  • 1. ๋กœ๊น… ๋ฐ ๋ชจ๋‹ˆํ„ฐ๋ง ๋„๊ตฌ ๋„์ž…
  • ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ๋ฅผ ํ†ตํ•ด ์‚ฌ์šฉ์ž์—๊ฒŒ ์„œ๋น„์Šค ์ค‘๋‹จ ์—†์ด ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ์„ ๋ฐฐํฌํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด Blue-Green ๋ฐฐํฌ ์ „๋žต์„ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.
  • yaml์ฝ”๋“œ ๋ณต์‚ฌ name: CI/CD Pipeline on: push: branches: - main jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Setup .NET Core uses: actions/setup-dotnet@v1 with: dotnet-version: 5.0.x - name: Restore dependencies run: dotnet restore - name: Build run: dotnet build --configuration Release --no-restore - name: Test run: dotnet test --no-restore --verbosity normal - name: Publish run: dotnet publish --configuration Release --no-build --output ./publish - name: Deploy to Azure Web App uses: azure/webapps-deploy@v2 with: app-name: my-web-app publish-profile: ${{ secrets.AZURE_WEB_APP_PUBLISH_PROFILE }} package: ./publish
  • ํ”„๋กœ์ ํŠธ์˜ ๋ฐฐํฌ๋ฅผ ์ž๋™ํ™”ํ•˜๊ธฐ ์œ„ํ•ด CI/CD ํŒŒ์ดํ”„๋ผ์ธ์„ ์„ค์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์ฝ”๋“œ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์ด ๋น ๋ฅด๊ณ  ์•ˆ์ „ํ•˜๊ฒŒ ๋ฐฐํฌ๋  ์ˆ˜ ์žˆ๋„๋ก ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ๋ฐฐํฌ ์ „๋žต ์ˆ˜๋ฆฝ
  • 15. ํ”„๋กœ์ ํŠธ ์ ์šฉ ์‚ฌ๋ก€ - Part 12
  • 16. ํ”„๋กœ์ ํŠธ ์ ์šฉ ์‚ฌ๋ก€ - Part 13์ด ํฌ์ŠคํŒ…์—์„œ๋Š” ํ”„๋กœ์ ํŠธ ํšŒ๊ณ ์™€ ํ•จ๊ป˜ ํ–ฅํ›„ ๊ฐœ์„  ์‚ฌํ•ญ์— ๋Œ€ํ•ด ๋…ผ์˜ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๋ฉด์„œ ์–ป์€ ๊ตํ›ˆ๊ณผ ๋ฐฐ์šด ์ ๋“ค์„ ๊ณต์œ ํ•˜๊ณ , ์•ž์œผ๋กœ ๋‚˜์•„๊ฐˆ ๋ฐฉํ–ฅ์„ ์ œ์‹œํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.1. ๊ธฐ์ˆ ์  ์„ฑ๊ณผCI/CD ํŒŒ์ดํ”„๋ผ์ธ์„ ํ†ตํ•ด ์ž๋™ํ™”๋œ ๋นŒ๋“œ, ํ…Œ์ŠคํŠธ, ๋ฐฐํฌ ํ”„๋กœ์„ธ์Šค๋ฅผ ๊ตฌ์ถ•ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ๋ฐฐํฌ ์ฃผ๊ธฐ๋ฅผ ๋‹จ์ถ•ํ•˜๊ณ , ์ฝ”๋“œ ํ’ˆ์งˆ์„ ๋†’์ผ ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ํŒŒ์ดํ”„๋ผ์ธ์˜ ์ž๋™ํ™”๋Š” ๋ฐฐํฌ ๊ณผ์ •์—์„œ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ์ธ์  ์˜ค๋ฅ˜๋ฅผ ์ค„์ด๊ณ , ์ผ๊ด€์„ฑ์„ ์œ ์ง€ํ•˜๋Š” ๋ฐ ํฐ ๋„์›€์ด ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.Blue-Green ๋ฐฐํฌ ์ „๋žต์„ ์„ฑ๊ณต์ ์œผ๋กœ ๊ตฌํ˜„ํ•˜์—ฌ, ์„œ๋น„์Šค ์ค‘๋‹จ ์—†์ด ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ์„ ๋ฐฐํฌํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ํ–ฅ์ƒ์‹œํ‚ค๋Š” ๋ฐ ํฌ๊ฒŒ ๊ธฐ์—ฌํ–ˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ, ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ๋ฅผ ํ†ตํ•ด ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ์„ ๋” ์ž์ฃผ, ๋” ์•ˆ์ „ํ•˜๊ฒŒ ๋ฆด๋ฆฌ์ฆˆํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.Application Insights์™€ Serilog๋ฅผ ํ™œ์šฉํ•˜์—ฌ ์‹ค์‹œ๊ฐ„ ๋ชจ๋‹ˆํ„ฐ๋ง๊ณผ ๊ฒฝ๊ณ  ์‹œ์Šคํ…œ์„ ๊ตฌ์ถ•ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์‹œ์Šคํ…œ ์ƒํƒœ๋ฅผ ์‹ค์‹œ๊ฐ„์œผ๋กœ ํŒŒ์•…ํ•˜๊ณ , ๋ฌธ์ œ ๋ฐœ์ƒ ์‹œ ์‹ ์†ํ•˜๊ฒŒ ๋Œ€์‘ํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ๊ฒฝ๊ณ  ์‹œ์Šคํ…œ์€ ์ž ์žฌ์ ์ธ ๋ฌธ์ œ๋ฅผ ์‚ฌ์ „์— ๊ฐ์ง€ํ•˜๊ณ , ์‹ ์†ํ•˜๊ฒŒ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋Š” ๋Šฅ๋ ฅ์„ ์ œ๊ณตํ–ˆ์Šต๋‹ˆ๋‹ค.๋ณต์žกํ•œ ์˜์กด์„ฑ ๊ด€๋ฆฌ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์„ฑ๋Šฅ ์ตœ์ ํ™”ํ…Œ์ŠคํŠธ ์ปค๋ฒ„๋ฆฌ์ง€ ํ™•๋Œ€ํ–ฅํ›„ ๊ฐœ์„  ์‚ฌํ•ญํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์—์„œ์˜ ์„ฑ๋Šฅ์„ ์ง€์†์ ์œผ๋กœ ๋ชจ๋‹ˆํ„ฐ๋งํ•˜๊ณ , ์„ฑ๋Šฅ ์ €ํ•˜๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋„๋ก ์˜ˆ๋ฐฉ ์กฐ์น˜๋ฅผ ๊ฐ•ํ™”ํ•  ํ•„์š”๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด ์ •๊ธฐ์ ์ธ ์„ฑ๋Šฅ ํ…Œ์ŠคํŠธ์™€ ๋ชจ๋‹ˆํ„ฐ๋ง์„ ์ˆ˜ํ–‰ํ•˜๊ณ , ์„ฑ๋Šฅ ๋ฌธ์ œ๋ฅผ ์‹ ์†ํ•˜๊ฒŒ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋Š” ํ”„๋กœ์„ธ์Šค๋ฅผ ๊ตฌ์ถ•ํ•  ๊ณ„ํš์ž…๋‹ˆ๋‹ค.์ฝ”๋“œ ๋ฆฌ๋ทฐ์™€ ์ •์  ์ฝ”๋“œ ๋ถ„์„ ๋„๊ตฌ๋ฅผ ํ™œ์šฉํ•˜์—ฌ ์ฝ”๋“œ ํ’ˆ์งˆ์„ ์ง€์†์ ์œผ๋กœ ๊ฐœ์„ ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ์ฝ”๋“œ ํ’ˆ์งˆ ํ–ฅ์ƒ์„ ์œ„ํ•œ ๊ต์œก๊ณผ ์›Œํฌ์ƒต์„ ์ •๊ธฐ์ ์œผ๋กœ ๊ฐœ์ตœํ•˜๊ณ , ์ฝ”๋“œ ๋ฆฌ๋ทฐ ๋ฌธํ™”๋ฅผ ์ •์ฐฉ์‹œ์ผœ ํŒ€ ์ „์ฒด์˜ ์ฝ”๋“œ ํ’ˆ์งˆ์„ ํ–ฅ์ƒ์‹œํ‚ค๊ฒ ์Šต๋‹ˆ๋‹ค.๋ณด์•ˆ ์ทจ์•ฝ์ ์„ ์ง€์†์ ์œผ๋กœ ๋ชจ๋‹ˆํ„ฐ๋งํ•˜๊ณ , ๋ณด์•ˆ ์—…๋ฐ์ดํŠธ๋ฅผ ์ •๊ธฐ์ ์œผ๋กœ ์ ์šฉํ•˜์—ฌ ๋ณด์•ˆ์„ ๊ฐ•ํ™”ํ•  ๊ณ„ํš์ž…๋‹ˆ๋‹ค. ๋ณด์•ˆ ๊ฒ€ํ† ๋ฅผ ์ •๊ธฐ์ ์œผ๋กœ ์ˆ˜ํ–‰ํ•˜๊ณ , ๋ณด์•ˆ ์ธ์‹ ๊ต์œก์„ ํ†ตํ•ด ํŒ€ ์ „์ฒด์˜ ๋ณด์•ˆ ์˜์‹์„ ๋†’์ด๊ฒ ์Šต๋‹ˆ๋‹ค.๋ฐ๋ธŒ์˜ต์Šค ๋ฌธํ™”์™€ ๋„๊ตฌ๋ฅผ ํ™•์‚ฐํ•˜์—ฌ ๊ฐœ๋ฐœ๊ณผ ์šด์˜์˜ ํšจ์œจ์„ฑ์„ ๋†’์ด๊ฒ ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด ๋ฐ๋ธŒ์˜ต์Šค ๊ด€๋ จ ๊ต์œก๊ณผ ์„ธ๋ฏธ๋‚˜๋ฅผ ๊ฐœ์ตœํ•˜๊ณ , ๋ฐ๋ธŒ์˜ต์Šค ๋„๊ตฌ์˜ ํ™œ์šฉ์„ ์ ๊ทน ์žฅ๋ คํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ, ๋ฐ๋ธŒ์˜ต์Šค ๋„์ž…์„ ํ†ตํ•ด ๊ฐœ๋ฐœ๊ณผ ์šด์˜ ๊ฐ„์˜ ํ˜‘์—…์„ ๊ฐ•ํ™”ํ•˜๊ณ , ๋ฌธ์ œ ํ•ด๊ฒฐ ์†๋„๋ฅผ ๋†’์ด๊ฒ ์Šต๋‹ˆ๋‹ค.์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” ํ”„๋กœ์ ํŠธ ํšŒ๊ณ ์™€ ํ–ฅํ›„ ๊ฐœ์„  ์‚ฌํ•ญ์— ๋Œ€ํ•ด ๋…ผ์˜ํ–ˆ์Šต๋‹ˆ๋‹ค. ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๋ฉด์„œ ์–ป์€ ๊ตํ›ˆ๊ณผ ๋ฐฐ์šด ์ ๋“ค์„ ๋ฐ”ํƒ•์œผ๋กœ, ์•ž์œผ๋กœ ๋” ๋‚˜์€ ํ”„๋กœ์ ํŠธ๋ฅผ ์œ„ํ•ด ๋‚˜์•„๊ฐˆ ๋ฐฉํ–ฅ์„ ์„ค์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ ํฌ์ŠคํŒ…์—์„œ๋Š” ๊ตฌ์ฒด์ ์ธ ๊ธฐ์ˆ  ์Šคํƒ๊ณผ ์‚ฌ์šฉ๋œ ๋„๊ตฌ๋“ค์— ๋Œ€ํ•ด ์ƒ์„ธํžˆ ์†Œ๊ฐœํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.
  • ๊ฒฐ๋ก 
  • 4. ๋ฐ๋ธŒ์˜ต์Šค ๋ฌธํ™” ํ™•์‚ฐ
  • 3. ๋ณด์•ˆ ๊ฐ•ํ™”
  • 2. ์ฝ”๋“œ ํ’ˆ์งˆ ๊ฐœ์„ 
  • 1. ์ง€์†์ ์ธ ์„ฑ๋Šฅ ๊ฐœ์„ 
  • ํ”„๋กœ์ ํŠธ ์ดˆ๊ธฐ์— ํ…Œ์ŠคํŠธ ์ปค๋ฒ„๋ฆฌ์ง€๊ฐ€ ๋ถ€์กฑํ•˜์—ฌ, ์ฝ”๋“œ ๋ณ€๊ฒฝ ์‹œ ์˜ˆ๊ธฐ์น˜ ์•Š์€ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•  ์œ„ํ—˜์ด ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ์œ ๋‹› ํ…Œ์ŠคํŠธ์™€ ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ•˜๊ณ , ํ…Œ์ŠคํŠธ ์ž๋™ํ™” ๋„๊ตฌ๋ฅผ ๋„์ž…ํ•˜์—ฌ ํ…Œ์ŠคํŠธ ์ปค๋ฒ„๋ฆฌ์ง€๋ฅผ ํ™•๋Œ€ํ–ˆ์Šต๋‹ˆ๋‹ค. ํ…Œ์ŠคํŠธ ์ปค๋ฒ„๋ฆฌ์ง€๋ฅผ ์‹œ๊ฐํ™”ํ•˜๊ณ , ๋ชฉํ‘œ ์ปค๋ฒ„๋ฆฌ์ง€๋ฅผ ์„ค์ •ํ•˜์—ฌ ์ง€์†์ ์œผ๋กœ ๊ฐœ์„ ํ•ด ๋‚˜๊ฐ”์Šต๋‹ˆ๋‹ค.
  • ๋ฐฐํฌ ํ›„ ์„ฑ๋Šฅ ์ €ํ•˜ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ฟผ๋ฆฌ๋ฅผ ์ตœ์ ํ™”ํ•˜๋Š” ๋ฐ ๋งŽ์€ ๋…ธ๋ ฅ์„ ๊ธฐ์šธ์˜€์Šต๋‹ˆ๋‹ค. ์ฟผ๋ฆฌ ์ตœ์ ํ™”์™€ ์ธ๋ฑ์Šค ์ถ”๊ฐ€๋ฅผ ํ†ตํ•ด ์„ฑ๋Šฅ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ–ˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ, ์ •๊ธฐ์ ์ธ ์„ฑ๋Šฅ ํ…Œ์ŠคํŠธ๋ฅผ ํ†ตํ•ด ์ž ์žฌ์ ์ธ ์„ฑ๋Šฅ ๋ฌธ์ œ๋ฅผ ์‚ฌ์ „์— ์‹๋ณ„ํ•˜๊ณ , ์ ์ ˆํ•œ ์กฐ์น˜๋ฅผ ์ทจํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ํ”„๋กœ์ ํŠธ ์ดˆ๊ธฐ์— ๋‹ค์–‘ํ•œ ํŒจํ‚ค์ง€์™€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ์˜์กด์„ฑ์„ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ์ด ์–ด๋ ค์› ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด NuGet ํŒจํ‚ค์ง€ ๊ด€๋ฆฌ์™€ ๋ฒ„์ „ ๊ด€๋ฆฌ๋ฅผ ์ฒ ์ €ํžˆ ํ•˜๊ณ , ์˜์กด์„ฑ ์ถฉ๋Œ์„ ํ”ผํ•˜๊ธฐ ์œ„ํ•ด ์ •๊ธฐ์ ์ธ ์—…๋ฐ์ดํŠธ๋ฅผ ์ˆ˜ํ–‰ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ, ์˜์กด์„ฑ ๊ด€๋ฆฌ ๋„๊ตฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์˜์กด์„ฑ ๊ทธ๋ž˜ํ”„๋ฅผ ์‹œ๊ฐํ™”ํ•˜๊ณ , ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ์ง€์ ์„ ์‚ฌ์ „์— ํŒŒ์•…ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • 2. ๊ฐœ๋ฐœ ๊ณผ์ •์—์„œ์˜ ๋„์ „๊ณผ ํ•ด๊ฒฐ
  • ์‹ค์‹œ๊ฐ„ ๋ชจ๋‹ˆํ„ฐ๋ง ๋ฐ ๊ฒฝ๊ณ  ์‹œ์Šคํ…œ
  • ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ ์ „๋žต
  • CI/CD ํŒŒ์ดํ”„๋ผ์ธ ๋„์ž…
  • ํ”„๋กœ์ ํŠธ ํšŒ๊ณ 
  • 16. ํ”„๋กœ์ ํŠธ ์ ์šฉ ์‚ฌ๋ก€ - Part 13
  • 17. ํ”„๋กœ์ ํŠธ ์ ์šฉ ์‚ฌ๋ก€ - Part 14์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” ํ”„๋กœ์ ํŠธ์— ์‚ฌ์šฉ๋œ ๊ธฐ์ˆ  ์Šคํƒ๊ณผ ๋„๊ตฌ๋“ค์— ๋Œ€ํ•ด ์ž์„ธํžˆ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ๊ฐ ๊ธฐ์ˆ ๊ณผ ๋„๊ตฌ๊ฐ€ ํ”„๋กœ์ ํŠธ์—์„œ ์–ด๋–ค ์—ญํ• ์„ ํ–ˆ๋Š”์ง€ ์„ค๋ช…ํ•˜๊ณ , ๊ทธ ์„ ํƒ ์ด์œ ์™€ ์žฅ์ ์„ ์†Œ๊ฐœํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.1. ๋ฐฑ์—”๋“œ ๋ฐ ํ”„๋ก ํŠธ์—”๋“œ: ASP.NET Core์žฅ์ :
    • ๊ณ ์„ฑ๋Šฅ: ๊ฒฝ๋Ÿ‰ํ™”๋˜๊ณ  ์ตœ์ ํ™”๋œ ๋Ÿฐํƒ€์ž„์„ ์ œ๊ณตํ•˜์—ฌ ๋†’์€ ์„ฑ๋Šฅ์„ ์ž๋ž‘ํ•ฉ๋‹ˆ๋‹ค.
    • ํ™•์žฅ์„ฑ: ๋ชจ๋“ˆํ™”๋œ ์•„ํ‚คํ…์ฒ˜ ๋•๋ถ„์— ํ•„์š”์— ๋”ฐ๋ผ ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜ ์ œ๊ฑฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ํฌ๋กœ์Šค ํ”Œ๋žซํผ: ์œˆ๋„์šฐ, ๋ฆฌ๋ˆ…์Šค, ๋งฅOS ๋“ฑ ๋‹ค์–‘ํ•œ ํ”Œ๋žซํผ์—์„œ ์‹คํ–‰ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.
    • ์ผ๊ด€๋œ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ: ๋ฐฑ์—”๋“œ์™€ ํ”„๋ก ํŠธ์—”๋“œ๋ฅผ ๋™์ผํ•œ ๊ธฐ์ˆ  ์Šคํƒ์œผ๋กœ ๊ฐœ๋ฐœํ•˜์—ฌ ์ผ๊ด€์„ฑ์„ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    2. ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค: MySQL์žฅ์ :
    • ๋†’์€ ์„ฑ๋Šฅ: ๋‹ค์–‘ํ•œ ์ตœ์ ํ™” ๊ธฐ๋ฒ•์„ ํ†ตํ•ด ๋น ๋ฅธ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ ์†๋„๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
    • ํ™•์žฅ์„ฑ: ๋Œ€๊ทœ๋ชจ ๋ฐ์ดํ„ฐ๋ฅผ ํšจ์œจ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ์˜คํ”ˆ ์†Œ์Šค: ์ปค๋ฎค๋‹ˆํ‹ฐ ์ง€์›๊ณผ ๋‹ค์–‘ํ•œ ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    ํ”„๋กœ์ ํŠธ์— ์‚ฌ์šฉ๋œ ์ฃผ์š” ๋„๊ตฌGitHub Actions๋Š” ํ”„๋กœ์ ํŠธ์˜ ์ง€์†์  ํ†ตํ•ฉ๊ณผ ์ง€์†์  ๋ฐฐํฌ๋ฅผ ์ž๋™ํ™”ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.
    • ์ž๋™ํ™”: ์ฝ”๋“œ ํ‘ธ์‹œ ์‹œ ์ž๋™์œผ๋กœ ๋นŒ๋“œ, ํ…Œ์ŠคํŠธ, ๋ฐฐํฌ๋ฅผ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.
    • ํ†ตํ•ฉ์„ฑ: GitHub ๋ฆฌํฌ์ง€ํ† ๋ฆฌ์™€ ๋ฐ€์ ‘ํ•˜๊ฒŒ ํ†ตํ•ฉ๋˜์–ด ์„ค์ •๊ณผ ์‚ฌ์šฉ์ด ๊ฐ„ํŽธํ•ฉ๋‹ˆ๋‹ค.
    • ํ™•์žฅ์„ฑ: ๋‹ค์–‘ํ•œ ์ปค๋ฎค๋‹ˆํ‹ฐ ์•ก์…˜์„ ํ™œ์šฉํ•˜์—ฌ CI/CD ํŒŒ์ดํ”„๋ผ์ธ์„ ์†์‰ฝ๊ฒŒ ํ™•์žฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    2. ๋กœ๊น…: Serilog์žฅ์ :
    • ๊ตฌ์กฐํ™”๋œ ๋กœ๊ทธ: JSON ํ˜•์‹์œผ๋กœ ๋กœ๊ทธ๋ฅผ ๊ธฐ๋กํ•˜์—ฌ ๋กœ๊ทธ ๋ถ„์„๊ณผ ๋ชจ๋‹ˆํ„ฐ๋ง์„ ์šฉ์ดํ•˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.
    • ํ™•์žฅ์„ฑ: ๋‹ค์–‘ํ•œ ์‹ฑํฌ(์˜ˆ: ์ฝ˜์†”, ํŒŒ์ผ, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋“ฑ)๋กœ ๋กœ๊ทธ๋ฅผ ์ „์†กํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ์œ ์—ฐ์„ฑ: ๋‹ค์–‘ํ•œ ๋กœ๊ทธ ์ˆ˜์ค€๊ณผ ์กฐ๊ฑด์— ๋”ฐ๋ผ ๋กœ๊ทธ๋ฅผ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    3. ๋ชจ๋‹ˆํ„ฐ๋ง: Application Insights์žฅ์ :
    • ์‹ค์‹œ๊ฐ„ ๋ชจ๋‹ˆํ„ฐ๋ง: ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ƒํƒœ์™€ ์„ฑ๋Šฅ์„ ์‹ค์‹œ๊ฐ„์œผ๋กœ ๋ชจ๋‹ˆํ„ฐ๋งํ•ฉ๋‹ˆ๋‹ค.
    • ์‹ฌ์ธต ๋ถ„์„: ์ƒ์„ธํ•œ ๋กœ๊ทธ์™€ ๋ฉ”ํŠธ๋ฆญ์„ ํ†ตํ•ด ๋ฌธ์ œ๋ฅผ ์‹ ์†ํ•˜๊ฒŒ ํŒŒ์•…ํ•˜๊ณ  ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ํ†ตํ•ฉ์„ฑ: Azure ์„œ๋น„์Šค์™€์˜ ์›ํ™œํ•œ ํ†ตํ•ฉ์„ ์ œ๊ณตํ•˜์—ฌ ์ „์ฒด์ ์ธ ์‹œ์Šคํ…œ ๋ชจ๋‹ˆํ„ฐ๋ง์„ ๊ฐ•ํ™”ํ•ฉ๋‹ˆ๋‹ค.
    ๊ธฐ์ˆ  ์Šคํƒ๊ณผ ๋„๊ตฌ ์„ ํƒ ์ด์œ ๊ฒฐ๋ก 
  • ์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” ํ”„๋กœ์ ํŠธ์— ์‚ฌ์šฉ๋œ ์ฃผ์š” ๊ธฐ์ˆ  ์Šคํƒ๊ณผ ๋„๊ตฌ๋“ค์— ๋Œ€ํ•ด ์‚ดํŽด๋ณด์•˜์Šต๋‹ˆ๋‹ค. ๊ฐ ๊ธฐ์ˆ ๊ณผ ๋„๊ตฌ๊ฐ€ ํ”„๋กœ์ ํŠธ์—์„œ ์–ด๋–ค ์—ญํ• ์„ ํ–ˆ๋Š”์ง€, ๊ทธ๋ฆฌ๊ณ  ๊ทธ ์„ ํƒ ์ด์œ ์™€ ์žฅ์ ์„ ์ดํ•ดํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค. ๋‹ค์Œ ํฌ์ŠคํŒ…์—์„œ๋Š” ํ”„๋กœ์ ํŠธ์˜ ํ…Œ์ŠคํŠธ ์ „๋žต๊ณผ ํ’ˆ์งˆ ๋ณด์ฆ ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ๋‹ค๋ฃจ๊ฒ ์Šต๋‹ˆ๋‹ค.
  • ํ”„๋กœ์ ํŠธ์— ์‚ฌ์šฉ๋œ ๊ธฐ์ˆ ๊ณผ ๋„๊ตฌ๋“ค์€ ์„ฑ๋Šฅ, ํ™•์žฅ์„ฑ, ์•ˆ์ •์„ฑ์„ ๊ณ ๋ คํ•˜์—ฌ ์„ ํƒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ํŠนํžˆ, ํฌ๋กœ์Šค ํ”Œ๋žซํผ ์ง€์›๊ณผ ์ž๋™ํ™”๋œ ๋ฐฐํฌ ํŒŒ์ดํ”„๋ผ์ธ์„ ํ†ตํ•ด ๊ฐœ๋ฐœ๊ณผ ์šด์˜์˜ ํšจ์œจ์„ฑ์„ ๊ทน๋Œ€ํ™”ํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ, ๊ตฌ์กฐํ™”๋œ ๋กœ๊น…๊ณผ ์‹ค์‹œ๊ฐ„ ๋ชจ๋‹ˆํ„ฐ๋ง์„ ํ†ตํ•ด ์‹œ์Šคํ…œ ์ƒํƒœ๋ฅผ ์ง€์†์ ์œผ๋กœ ํŒŒ์•…ํ•˜๊ณ , ๋ฌธ์ œ ๋ฐœ์ƒ ์‹œ ์‹ ์†ํ•˜๊ฒŒ ๋Œ€์‘ํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋ฐ˜์„ ๋งˆ๋ จํ•˜์˜€์Šต๋‹ˆ๋‹ค.
  • Application Insights๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„ฑ๋Šฅ ๋ชจ๋‹ˆํ„ฐ๋ง๊ณผ ๋กœ๊ทธ ๋ถ„์„์„ ์ œ๊ณตํ•˜๋Š” Azure์˜ ๋„๊ตฌ๋กœ, ์‹ค์‹œ๊ฐ„ ๋ชจ๋‹ˆํ„ฐ๋ง๊ณผ ๊ฒฝ๊ณ  ์‹œ์Šคํ…œ์„ ๊ตฌ์ถ•ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.
  • Serilog๋Š” ๊ตฌ์กฐํ™”๋œ ๋กœ๊น…์„ ์ œ๊ณตํ•˜๋Š” ๊ฐ•๋ ฅํ•œ ๋กœ๊น… ํ”„๋ ˆ์ž„์›Œํฌ๋กœ, ํ”„๋กœ์ ํŠธ์˜ ๋กœ๊ทธ ๊ด€๋ฆฌ๋ฅผ ๋‹ด๋‹นํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ์žฅ์ :
  • 1. CI/CD: GitHub Actions
  • MySQL์€ ์•ˆ์ •์ ์ด๊ณ  ์„ฑ๋Šฅ์ด ๋›ฐ์–ด๋‚œ ๊ด€๊ณ„ํ˜• ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋กœ, ํ”„๋กœ์ ํŠธ์˜ ๋ฐ์ดํ„ฐ ์ €์žฅ์†Œ๋กœ ์‚ฌ์šฉ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.
  • ASP.NET Core๋Š” ํฌ๋กœ์Šค ํ”Œ๋žซํผ ์„ฑ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š” ๋ชจ๋“ˆํ™”๋œ ์›น ํ”„๋ ˆ์ž„์›Œํฌ๋กœ, ์ด๋ฒˆ ํ”„๋กœ์ ํŠธ์˜ ๋ฐฑ์—”๋“œ์™€ ํ”„๋ก ํŠธ์—”๋“œ๋ฅผ ๋ชจ๋‘ ๊ตฌ์„ฑํ•˜๋Š” ๋ฐ ํ•ต์‹ฌ์ ์ธ ์—ญํ• ์„ ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ํ”„๋กœ์ ํŠธ์˜ ์ฃผ์š” ๊ธฐ์ˆ  ์Šคํƒ
  • 17. ํ”„๋กœ์ ํŠธ ์ ์šฉ ์‚ฌ๋ก€ - Part 14
  • 18. ํ”„๋กœ์ ํŠธ ์ ์šฉ ์‚ฌ๋ก€ - Part 15์ด์ œ ํ”„๋กœ์ ํŠธ์˜ ํ…Œ์ŠคํŠธ ์ „๋žต๊ณผ ํ’ˆ์งˆ ๋ณด์ฆ ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ์–ด๋–ค ํ…Œ์ŠคํŠธ ๊ธฐ๋ฒ•๊ณผ ๋„๊ตฌ๋ฅผ ์‚ฌ์šฉํ–ˆ๋Š”์ง€, ๊ทธ๋ฆฌ๊ณ  ์ด๋ฅผ ํ†ตํ•ด ์–ด๋–ป๊ฒŒ ํ”„๋กœ์ ํŠธ์˜ ํ’ˆ์งˆ์„ ๋ณด์žฅํ–ˆ๋Š”์ง€ ์„ค๋ช…ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.1. ์œ ๋‹› ํ…Œ์ŠคํŠธ์žฅ์ :
    • ๋น ๋ฅธ ํ”ผ๋“œ๋ฐฑ: ์ฝ”๋“œ ๋ณ€๊ฒฝ ์‹œ ์ฆ‰๊ฐ์ ์ธ ํ”ผ๋“œ๋ฐฑ์„ ์ œ๊ณตํ•˜์—ฌ ๋ฒ„๊ทธ๋ฅผ ์กฐ๊ธฐ์— ๋ฐœ๊ฒฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ๋‹จ์œ„๋ณ„ ๊ฒ€์ฆ: ๊ฐœ๋ณ„ ๋ชจ๋“ˆ์ด ๋…๋ฆฝ์ ์œผ๋กœ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ๋™์ž‘ํ•˜๋Š”์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ์ž๋™ํ™” ์šฉ์ด: CI/CD ํŒŒ์ดํ”„๋ผ์ธ์— ์‰ฝ๊ฒŒ ํ†ตํ•ฉํ•˜์—ฌ ์ž๋™ํ™”๋œ ํ…Œ์ŠคํŠธ๋ฅผ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    2. ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ์žฅ์ :
    • ์‹ค์ œ ํ™˜๊ฒฝ ๊ฒ€์ฆ: ์‹ค์ œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€์˜ ์ƒํ˜ธ์ž‘์šฉ์„ ํ…Œ์ŠคํŠธํ•˜์—ฌ ๋ฐ์ดํ„ฐ ์ผ๊ด€์„ฑ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ์ข…๋‹จ ๊ฐ„ ํ…Œ์ŠคํŠธ: ์‹œ์Šคํ…œ์˜ ๋‹ค์–‘ํ•œ ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ ํ•จ๊ป˜ ๋™์ž‘ํ•˜๋Š”์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ์‹ ๋ขฐ์„ฑ ํ–ฅ์ƒ: ๋‹ค์–‘ํ•œ ๋ชจ๋“ˆ ๊ฐ„์˜ ํ†ตํ•ฉ์„ ๊ฒ€์ฆํ•˜์—ฌ ์‹ ๋ขฐ์„ฑ์„ ๋†’์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    3. ์—”๋“œํˆฌ์—”๋“œ(E2E) ํ…Œ์ŠคํŠธ์žฅ์ :
    • ์‚ฌ์šฉ์ž ์‹œ๋‚˜๋ฆฌ์˜ค ๊ฒ€์ฆ: ์‹ค์ œ ์‚ฌ์šฉ์ž ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ํ…Œ์ŠคํŠธํ•˜์—ฌ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ๊ฒ€์ฆํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ์ „์ฒด ์‹œ์Šคํ…œ ํ…Œ์ŠคํŠธ: ์‹œ์Šคํ…œ์˜ ๋ชจ๋“  ๊ตฌ์„ฑ ์š”์†Œ๊ฐ€ ํ•จ๊ป˜ ๋™์ž‘ํ•˜๋Š”์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ์ž๋™ํ™” ๊ฐ€๋Šฅ: ํ…Œ์ŠคํŠธ ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ์ž๋™ํ™”ํ•˜์—ฌ ๋ฐ˜๋ณต์ ์ธ ํ…Œ์ŠคํŠธ๋ฅผ ํšจ์œจ์ ์œผ๋กœ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    ํ’ˆ์งˆ ๋ณด์ฆ ๋ฐฉ๋ฒ•์ฝ”๋“œ ๋ฆฌ๋ทฐ๋Š” ํŒ€์› ๊ฐ„์˜ ์ฝ”๋“œ ๊ฒ€ํ† ๋ฅผ ํ†ตํ•ด ์ฝ”๋“œ ํ’ˆ์งˆ์„ ๋†’์ด๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค. ๋ชจ๋“  ์ฝ”๋“œ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์€ Pull Request๋ฅผ ํ†ตํ•ด ํŒ€์›๋“ค์˜ ๊ฒ€ํ† ๋ฅผ ๊ฑฐ์ณ ์Šน์ธ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.
    • ์ฝ”๋“œ ํ’ˆ์งˆ ํ–ฅ์ƒ: ๋‹ค์–‘ํ•œ ๊ด€์ ์—์„œ ์ฝ”๋“œ๋ฅผ ๊ฒ€ํ† ํ•˜์—ฌ ์ฝ”๋“œ ํ’ˆ์งˆ์„ ๋†’์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ์ง€์‹ ๊ณต์œ : ํŒ€์› ๊ฐ„์˜ ์ฝ”๋“œ ๋ฆฌ๋ทฐ๋ฅผ ํ†ตํ•ด ์ง€์‹์„ ๊ณต์œ ํ•˜๊ณ  ํ•™์Šตํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ๋ฒ„๊ทธ ๊ฐ์†Œ: ์ฝ”๋“œ ๋ฆฌ๋ทฐ๋ฅผ ํ†ตํ•ด ์ž ์žฌ์ ์ธ ๋ฒ„๊ทธ๋ฅผ ์กฐ๊ธฐ์— ๋ฐœ๊ฒฌํ•˜๊ณ  ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    2. ์ •์  ์ฝ”๋“œ ๋ถ„์„์žฅ์ :
    • ์ž๋™ํ™”๋œ ํ’ˆ์งˆ ๊ฒ€์‚ฌ: ์ฝ”๋“œ๋ฅผ ์ž๋™์œผ๋กœ ๋ถ„์„ํ•˜์—ฌ ํ’ˆ์งˆ ๋ฌธ์ œ๋ฅผ ๋ฐœ๊ฒฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ์ง€์†์ ์ธ ํ†ตํ•ฉ: CI/CD ํŒŒ์ดํ”„๋ผ์ธ์— ํ†ตํ•ฉํ•˜์—ฌ ์ง€์†์ ์œผ๋กœ ์ฝ”๋“œ ํ’ˆ์งˆ์„ ๋ชจ๋‹ˆํ„ฐ๋งํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ๊ฐœ์„  ๊ถŒ์žฅ ์‚ฌํ•ญ: ์ฝ”๋“œ ํ’ˆ์งˆ์„ ๊ฐœ์„ ํ•˜๊ธฐ ์œ„ํ•œ ๊ถŒ์žฅ ์‚ฌํ•ญ์„ ์ œ๊ณตํ•˜์—ฌ ์ฝ”๋“œ ์œ ์ง€ ๋ณด์ˆ˜๋ฅผ ์šฉ์ดํ•˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.
    3. CI/CD ํŒŒ์ดํ”„๋ผ์ธ์žฅ์ :
    • ์ž๋™ํ™”๋œ ์›Œํฌํ”Œ๋กœ์šฐ: ์ฝ”๋“œ ํ‘ธ์‹œ ์‹œ ์ž๋™์œผ๋กœ ๋นŒ๋“œ, ํ…Œ์ŠคํŠธ, ๋ฐฐํฌ๋ฅผ ์ˆ˜ํ–‰ํ•˜์—ฌ ๊ฐœ๋ฐœ ์†๋„๋ฅผ ๋†’์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ์ง€์†์ ์ธ ๋ฐฐํฌ: ์ง€์†์ ์œผ๋กœ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋ฐฐํฌํ•˜์—ฌ ์‚ฌ์šฉ์ž์—๊ฒŒ ์ตœ์‹  ๊ธฐ๋Šฅ์„ ๋น ๋ฅด๊ฒŒ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ๋†’์€ ์‹ ๋ขฐ์„ฑ: ์ž๋™ํ™”๋œ ํ…Œ์ŠคํŠธ๋ฅผ ํ†ตํ•ด ๋ฐฐํฌ ์ „ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์‹ ๋ขฐ์„ฑ์„ ๊ฒ€์ฆํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    ๊ฒฐ๋ก 
  • ์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” ํ”„๋กœ์ ํŠธ์˜ ํ…Œ์ŠคํŠธ ์ „๋žต๊ณผ ํ’ˆ์งˆ ๋ณด์ฆ ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ๋‹ค๋ฃจ์—ˆ์Šต๋‹ˆ๋‹ค. ์œ ๋‹› ํ…Œ์ŠคํŠธ, ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ, E2E ํ…Œ์ŠคํŠธ๋ฅผ ํ†ตํ•ด ๋‹ค์–‘ํ•œ ์ˆ˜์ค€์—์„œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๊ธฐ๋Šฅ์„ ๊ฒ€์ฆํ•˜๊ณ , ์ฝ”๋“œ ๋ฆฌ๋ทฐ, ์ •์  ์ฝ”๋“œ ๋ถ„์„, CI/CD ํŒŒ์ดํ”„๋ผ์ธ์„ ํ†ตํ•ด ์ฝ”๋“œ ํ’ˆ์งˆ์„ ์ง€์†์ ์œผ๋กœ ๋ชจ๋‹ˆํ„ฐ๋งํ•˜๊ณ  ๊ฐœ์„ ํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ ํฌ์ŠคํŒ…์—์„œ๋Š” ํ”„๋กœ์ ํŠธ์˜ ์„ฑ๋Šฅ ์ตœ์ ํ™”์™€ ๊ด€๋ จ๋œ ๋‚ด์šฉ์„ ๋‹ค๋ฃจ๊ฒ ์Šต๋‹ˆ๋‹ค.
  • CI/CD ํŒŒ์ดํ”„๋ผ์ธ์€ ์ฝ”๋“œ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ์ž๋™์œผ๋กœ ๋นŒ๋“œ, ํ…Œ์ŠคํŠธ, ๋ฐฐํฌํ•˜๋Š” ์ž๋™ํ™”๋œ ์›Œํฌํ”Œ๋กœ์šฐ์ž…๋‹ˆ๋‹ค. ์ด๋ฒˆ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” GitHub Actions๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ CI/CD ํŒŒ์ดํ”„๋ผ์ธ์„ ๊ตฌ์ถ•ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ์ •์  ์ฝ”๋“œ ๋ถ„์„ ๋„๊ตฌ๋Š” ์ฝ”๋“œ์˜ ์ž ์žฌ์ ์ธ ๋ฌธ์ œ๋ฅผ ์ž๋™์œผ๋กœ ๋ถ„์„ํ•˜๊ณ  ๊ฒฝ๊ณ ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฒˆ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” SonarQube๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ฝ”๋“œ ํ’ˆ์งˆ์„ ์ง€์†์ ์œผ๋กœ ๋ชจ๋‹ˆํ„ฐ๋งํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ์žฅ์ :
  • 1. ์ฝ”๋“œ ๋ฆฌ๋ทฐ
  • ์—”๋“œํˆฌ์—”๋“œ(E2E) ํ…Œ์ŠคํŠธ๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ „์ฒด ํ๋ฆ„์„ ํ…Œ์ŠคํŠธํ•˜์—ฌ ์‚ฌ์šฉ์ž๊ฐ€ ์‹ค์ œ๋กœ ๊ฒฝํ—˜ํ•˜๋Š” ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ๊ฒ€์ฆํ•˜๋Š” ํ…Œ์ŠคํŠธ์ž…๋‹ˆ๋‹ค. ์ด ํ”„๋กœ์ ํŠธ์—์„œ๋Š” Selenium์„ ์‚ฌ์šฉํ•˜์—ฌ ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ E2E ํ…Œ์ŠคํŠธ๋ฅผ ์ˆ˜ํ–‰ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ๋Š” ์—ฌ๋Ÿฌ ๋ชจ๋“ˆ์ด ํ†ตํ•ฉ๋œ ํ™˜๊ฒฝ์—์„œ ์ƒํ˜ธ ์ž‘์šฉ์ด ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ด๋ฃจ์–ด์ง€๋Š”์ง€ ๊ฒ€์ฆํ•˜๋Š” ํ…Œ์ŠคํŠธ์ž…๋‹ˆ๋‹ค. ์ด ํ”„๋กœ์ ํŠธ์—์„œ๋Š” ์‹ค์ œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€์˜ ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ๋ฅผ ํ†ตํ•ด ๋ฐ์ดํ„ฐ ํ๋ฆ„์„ ๊ฒ€์ฆํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ์œ ๋‹› ํ…Œ์ŠคํŠธ๋Š” ์ฝ”๋“œ์˜ ์ž‘์€ ๋‹จ์œ„(์ฃผ๋กœ ํ•จ์ˆ˜๋‚˜ ๋ฉ”์„œ๋“œ)๋ฅผ ํ…Œ์ŠคํŠธํ•˜์—ฌ ๊ฐœ๋ณ„ ๊ธฐ๋Šฅ์ด ์˜ˆ์ƒ๋Œ€๋กœ ๋™์ž‘ํ•˜๋Š”์ง€ ํ™•์ธํ•˜๋Š” ํ…Œ์ŠคํŠธ์ž…๋‹ˆ๋‹ค. ์ด๋ฒˆ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” xUnit์„ ์‚ฌ์šฉํ•˜์—ฌ ์œ ๋‹› ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ•˜๊ณ  ์‹คํ–‰ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ํ…Œ์ŠคํŠธ ์ „๋žต
  • 18. ํ”„๋กœ์ ํŠธ ์ ์šฉ ์‚ฌ๋ก€ - Part 15
    1. ํ”„๋กœ์ ํŠธ ์ ์šฉ ์‚ฌ๋ก€ - Part 16: ์„ฑ๋Šฅ ์ตœ์ ํ™”์™€ ๋ฆฌ์†Œ์Šค ๊ด€๋ฆฌ
    19. ํ”„๋กœ์ ํŠธ ์ ์šฉ ์‚ฌ๋ก€ - Part 16: ์„ฑ๋Šฅ ์ตœ์ ํ™”์™€ ๋ฆฌ์†Œ์Šค ๊ด€๋ฆฌ์„ฑ๋Šฅ ์ตœ์ ํ™” ์ „๋žตํ”„๋กœํŒŒ์ผ๋ง ๋„๊ตฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ฝ”๋“œ์˜ ์„ฑ๋Šฅ์„ ๋ถ„์„ํ•˜๊ณ  ๋ณ‘๋ชฉ ์ง€์ ์„ ์‹๋ณ„ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฒˆ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” Valgrind์™€ Gprof๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ CPU ์‚ฌ์šฉ๋Ÿ‰๊ณผ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰์„ ๋ถ„์„ํ–ˆ์Šต๋‹ˆ๋‹ค.
    • ๋ณ‘๋ชฉ ์ง€์  ์‹๋ณ„: ์„ฑ๋Šฅ ์ €ํ•˜์˜ ์›์ธ์ด ๋˜๋Š” ์ฝ”๋“œ๋ฅผ ์ฐพ์•„๋‚ด๊ณ  ์ตœ์ ํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ๋ฆฌ์†Œ์Šค ์‚ฌ์šฉ ๋ถ„์„: CPU์™€ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰์„ ๋ถ„์„ํ•˜์—ฌ ๋ฆฌ์†Œ์Šค ๊ด€๋ฆฌ์— ๋„์›€์ด ๋ฉ๋‹ˆ๋‹ค.
    • ์ตœ์ ํ™” ๋Œ€์ƒ ์„ ์ •: ์„ฑ๋Šฅ ์ตœ์ ํ™”๊ฐ€ ํ•„์š”ํ•œ ๋ถ€๋ถ„์„ ๋ช…ํ™•ํžˆ ํŒŒ์•…ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    2. ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ์žฅ์ :
    • ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ๋ฐฉ์ง€: ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๋ฅผ ๋ฐฉ์ง€ํ•˜์—ฌ ์‹œ์Šคํ…œ์˜ ์•ˆ์ •์„ฑ์„ ๋†’์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ ์ตœ์ ํ™”: ๋ถˆํ•„์š”ํ•œ ๋ฉ”๋ชจ๋ฆฌ ํ• ๋‹น์„ ์ค„์—ฌ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ์„ ์ตœ์ ํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ์‹œ์Šคํ…œ ์•ˆ์ •์„ฑ ํ–ฅ์ƒ: ์•ˆ์ •์ ์ธ ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ๋ฅผ ํ†ตํ•ด ์‹œ์Šคํ…œ์˜ ์‹ ๋ขฐ์„ฑ์„ ๋†’์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    3. ์‹ค์‹œ๊ฐ„ ์ฒ˜๋ฆฌ ์ตœ์ ํ™”์žฅ์ :
    • ์‹ค์‹œ๊ฐ„ ์‘๋‹ต์„ฑ ํ–ฅ์ƒ: ํƒœ์Šคํฌ์˜ ์šฐ์„ ์ˆœ์œ„์™€ ์ธํ„ฐ๋ŸฝํŠธ ์ฒ˜๋ฆฌ ์‹œ๊ฐ„์„ ์ตœ์ ํ™”ํ•˜์—ฌ ์‹ค์‹œ๊ฐ„ ์‘๋‹ต์„ฑ์„ ๋†’์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ์‹œ์Šคํ…œ ํšจ์œจ์„ฑ ํ–ฅ์ƒ: ์‹ค์‹œ๊ฐ„ ์„ฑ๋Šฅ์„ ์ตœ์ ํ™”ํ•˜์—ฌ ์‹œ์Šคํ…œ์˜ ์ „์ฒด์ ์ธ ํšจ์œจ์„ฑ์„ ํ–ฅ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ์‚ฌ์šฉ์ž ๊ฒฝํ—˜ ๊ฐœ์„ : ๋น ๋ฅด๊ณ  ์ผ๊ด€๋œ ์‘๋‹ต์„ฑ์„ ์ œ๊ณตํ•˜์—ฌ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ๊ฐœ์„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    ๋ฆฌ์†Œ์Šค ๊ด€๋ฆฌ ๊ธฐ๋ฒ•๋ฉ”๋ชจ๋ฆฌ ํ’€์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ฉ”๋ชจ๋ฆฌ ํ• ๋‹น๊ณผ ํ•ด์ œ๋ฅผ ๊ด€๋ฆฌํ•จ์œผ๋กœ์จ ๋ฉ”๋ชจ๋ฆฌ ์กฐ๊ฐํ™”๋ฅผ ๋ฐฉ์ง€ํ•˜๊ณ  ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ ํšจ์œจ์„ ๋†’์˜€์Šต๋‹ˆ๋‹ค.
    • ๋ฉ”๋ชจ๋ฆฌ ์กฐ๊ฐํ™” ๋ฐฉ์ง€: ๋ฉ”๋ชจ๋ฆฌ ํ’€์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ฉ”๋ชจ๋ฆฌ ์กฐ๊ฐํ™”๋ฅผ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ๋น ๋ฅธ ๋ฉ”๋ชจ๋ฆฌ ํ• ๋‹น: ๋ฉ”๋ชจ๋ฆฌ ํ’€์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ฉ”๋ชจ๋ฆฌ ํ• ๋‹น ์†๋„๋ฅผ ๋น ๋ฅด๊ฒŒ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ ํšจ์œจ ํ–ฅ์ƒ: ๋ฉ”๋ชจ๋ฆฌ ํ’€์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ์˜ ํšจ์œจ์„ฑ์„ ๋†’์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    2. ์ „๋ ฅ ๊ด€๋ฆฌ์žฅ์ :
    • ์ „๋ ฅ ์†Œ๋น„ ๊ฐ์†Œ: ๋ถˆํ•„์š”ํ•œ ์ „๋ ฅ ์†Œ๋น„๋ฅผ ์ค„์—ฌ ์‹œ์Šคํ…œ์˜ ์ „๋ ฅ ํšจ์œจ์„ฑ์„ ๋†’์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ๋ฐฐํ„ฐ๋ฆฌ ์ˆ˜๋ช… ์—ฐ์žฅ: ์ „๋ ฅ ์†Œ๋น„๋ฅผ ์ค„์—ฌ ๋ฐฐํ„ฐ๋ฆฌ ์ˆ˜๋ช…์„ ์—ฐ์žฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ํ™˜๊ฒฝ ์นœํ™”์ : ์ „๋ ฅ ์†Œ๋น„๋ฅผ ์ค„์—ฌ ํ™˜๊ฒฝ์— ๋ฏธ์น˜๋Š” ์˜ํ–ฅ์„ ์ตœ์†Œํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    3. ๋ฆฌ์†Œ์Šค ๋ชจ๋‹ˆํ„ฐ๋ง์žฅ์ :
    • ์‹ค์‹œ๊ฐ„ ๋ชจ๋‹ˆํ„ฐ๋ง: ์‹œ์Šคํ…œ์˜ ๋ฆฌ์†Œ์Šค ์‚ฌ์šฉ๋Ÿ‰์„ ์‹ค์‹œ๊ฐ„์œผ๋กœ ๋ชจ๋‹ˆํ„ฐ๋งํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ๋ฌธ์ œ ์กฐ๊ธฐ ๋ฐœ๊ฒฌ: ๋ฆฌ์†Œ์Šค ์‚ฌ์šฉ๋Ÿ‰์„ ๋ชจ๋‹ˆํ„ฐ๋งํ•˜์—ฌ ๋ฌธ์ œ๋ฅผ ์กฐ๊ธฐ์— ๋ฐœ๊ฒฌํ•˜๊ณ  ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ์ตœ์ ํ™” ๊ธฐํšŒ ์ œ๊ณต: ๋ฆฌ์†Œ์Šค ์‚ฌ์šฉ๋Ÿ‰์„ ๋ถ„์„ํ•˜์—ฌ ์ตœ์ ํ™”ํ•  ๊ธฐํšŒ๋ฅผ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    ๊ฒฐ๋ก 
  • ์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” ์ž„๋ฒ ๋””๋“œ ์‹œ์Šคํ…œ์˜ ์„ฑ๋Šฅ ์ตœ์ ํ™”์™€ ๋ฆฌ์†Œ์Šค ๊ด€๋ฆฌ์— ๋Œ€ํ•ด ๋‹ค๋ฃจ์—ˆ์Šต๋‹ˆ๋‹ค. ์ฝ”๋“œ ํ”„๋กœํŒŒ์ผ๋ง, ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ, ์‹ค์‹œ๊ฐ„ ์ฒ˜๋ฆฌ ์ตœ์ ํ™” ๋“ฑ์˜ ๋ฐฉ๋ฒ•์„ ํ†ตํ•ด ์„ฑ๋Šฅ์„ ์ตœ์ ํ™”ํ•˜๊ณ , ๋ฉ”๋ชจ๋ฆฌ ํ’€, ์ „๋ ฅ ๊ด€๋ฆฌ, ๋ฆฌ์†Œ์Šค ๋ชจ๋‹ˆํ„ฐ๋ง ๋“ฑ์„ ํ†ตํ•ด ๋ฆฌ์†Œ์Šค๋ฅผ ํšจ์œจ์ ์œผ๋กœ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ์ตœ์ ํ™”์™€ ๊ด€๋ฆฌ ๊ธฐ๋ฒ•์„ ํ†ตํ•ด ์ž„๋ฒ ๋””๋“œ ์‹œ์Šคํ…œ์˜ ์•ˆ์ •์„ฑ๊ณผ ์„ฑ๋Šฅ์„ ํฌ๊ฒŒ ํ–ฅ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ ํฌ์ŠคํŒ…์—์„œ๋Š” ๋„คํŠธ์›Œํฌ ํ†ต์‹ ๊ณผ ๋ณด์•ˆ์— ๋Œ€ํ•ด ๋‹ค๋ฃจ๊ฒ ์Šต๋‹ˆ๋‹ค.
  • ์‹ค์‹œ๊ฐ„์œผ๋กœ ์‹œ์Šคํ…œ์˜ ๋ฆฌ์†Œ์Šค ์‚ฌ์šฉ๋Ÿ‰์„ ๋ชจ๋‹ˆํ„ฐ๋งํ•˜์—ฌ ํ•„์š”์— ๋”ฐ๋ผ ์ตœ์ ํ™” ์ž‘์—…์„ ์ˆ˜ํ–‰ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด ๋ฉ”๋ชจ๋ฆฌ, CPU, ๋„คํŠธ์›Œํฌ ์‚ฌ์šฉ๋Ÿ‰์„ ๋ชจ๋‹ˆํ„ฐ๋งํ•˜๋Š” ๋„๊ตฌ๋ฅผ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ์ž„๋ฒ ๋””๋“œ ์‹œ์Šคํ…œ์—์„œ๋Š” ์ „๋ ฅ ์†Œ๋น„๋ฅผ ์ค„์ด๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค. CPU ํด๋Ÿญ ์†๋„๋ฅผ ์กฐ์ •ํ•˜๊ณ , ํ•„์š”ํ•˜์ง€ ์•Š์€ ๋ชจ๋“ˆ์˜ ์ „์›์„ ์ฐจ๋‹จํ•˜์—ฌ ์ „๋ ฅ ์†Œ๋น„๋ฅผ ์ตœ์†Œํ™”ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ์žฅ์ :
  • 1. ๋ฉ”๋ชจ๋ฆฌ ํ’€ ์‚ฌ์šฉ
  • ์‹ค์‹œ๊ฐ„ ์ฒ˜๋ฆฌ๊ฐ€ ์ค‘์š”ํ•œ ์ž„๋ฒ ๋””๋“œ ์‹œ์Šคํ…œ์—์„œ๋Š” ํƒœ์Šคํฌ์˜ ์šฐ์„ ์ˆœ์œ„๋ฅผ ์กฐ์ •ํ•˜๊ณ , ์ธํ„ฐ๋ŸฝํŠธ ์ฒ˜๋ฆฌ ์‹œ๊ฐ„์„ ์ตœ์ ํ™”ํ•˜์—ฌ ์‹ค์‹œ๊ฐ„ ์„ฑ๋Šฅ์„ ๋ณด์žฅํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • ์ž„๋ฒ ๋””๋“œ ์‹œ์Šคํ…œ์—์„œ๋Š” ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰์„ ์ค„์ด๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค. ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜์™€ ๋ถˆํ•„์š”ํ•œ ๋ฉ”๋ชจ๋ฆฌ ํ• ๋‹น์„ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ๋‹ค์–‘ํ•œ ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ ๊ธฐ๋ฒ•์„ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ์žฅ์ :
  • 1. ์ฝ”๋“œ ํ”„๋กœํŒŒ์ผ๋ง
  • ์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” ์ž„๋ฒ ๋””๋“œ ์‹œ์Šคํ…œ์˜ ์„ฑ๋Šฅ ์ตœ์ ํ™”์™€ ๋ฆฌ์†Œ์Šค ๊ด€๋ฆฌ์— ๋Œ€ํ•ด ๋‹ค๋ฃจ๊ฒ ์Šต๋‹ˆ๋‹ค. ์ž„๋ฒ ๋””๋“œ ์‹œ์Šคํ…œ์€ ์ œํ•œ๋œ ์ž์› ๋‚ด์—์„œ ํšจ์œจ์ ์œผ๋กœ ๋™์ž‘ํ•ด์•ผ ํ•˜๋ฏ€๋กœ ์„ฑ๋Šฅ ์ตœ์ ํ™”์™€ ๋ฆฌ์†Œ์Šค ๊ด€๋ฆฌ๋Š” ๋งค์šฐ ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค. ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๋ฉฐ ๊ฒช์—ˆ๋˜ ์„ฑ๋Šฅ ๋ฌธ์ œ์™€ ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•œ ๋ฐฉ๋ฒ•๋“ค์„ ๊ณต์œ ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.
    1. ํ”„๋กœ์ ํŠธ ์ ์šฉ ์‚ฌ๋ก€ - Part 17: ๋„คํŠธ์›Œํฌ ํ†ต์‹ ๊ณผ ๋ณด์•ˆ
    20. ํ”„๋กœ์ ํŠธ ์ ์šฉ ์‚ฌ๋ก€ - Part 17: ๋„คํŠธ์›Œํฌ ํ†ต์‹ ๊ณผ ๋ณด์•ˆ๋„คํŠธ์›Œํฌ ํ†ต์‹  ์ตœ์ ํ™”๋„คํŠธ์›Œํฌ ํ†ต์‹ ์—์„œ๋Š” ๋ฐ์ดํ„ฐ ์ „์†ก์˜ ํšจ์œจ์„ฑ์„ ๋†’์ด๊ธฐ ์œ„ํ•ด ๋‹ค์–‘ํ•œ ๊ธฐ๋ฒ•์„ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค. ๋ฐ์ดํ„ฐ๋ฅผ ์••์ถ•ํ•˜์—ฌ ์ „์†กํ•˜๊ณ , ํŒจํ‚ท ํฌ๊ธฐ๋ฅผ ์ตœ์ ํ™”ํ•˜์—ฌ ๋„คํŠธ์›Œํฌ ๋Œ€์—ญํญ์„ ํšจ์œจ์ ์œผ๋กœ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.
    • ๋Œ€์—ญํญ ์ ˆ์•ฝ: ๋ฐ์ดํ„ฐ๋ฅผ ์••์ถ•ํ•˜์—ฌ ์ „์†กํ•˜๋ฉด ๋„คํŠธ์›Œํฌ ๋Œ€์—ญํญ์„ ์ ˆ์•ฝํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ์ „์†ก ์†๋„ ํ–ฅ์ƒ: ์ตœ์ ํ™”๋œ ํŒจํ‚ท ํฌ๊ธฐ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋ฐ์ดํ„ฐ ์ „์†ก ์†๋„๊ฐ€ ํ–ฅ์ƒ๋ฉ๋‹ˆ๋‹ค.
    • ๋„คํŠธ์›Œํฌ ๋ถ€ํ•˜ ๊ฐ์†Œ: ํšจ์œจ์ ์ธ ๋ฐ์ดํ„ฐ ์ „์†ก์œผ๋กœ ๋„คํŠธ์›Œํฌ ๋ถ€ํ•˜๋ฅผ ์ค„์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    2. ํ”„๋กœํ† ์ฝœ ์„ ํƒ์žฅ์ :
    • ์‹ ๋ขฐ์„ฑ ํ–ฅ์ƒ: TCP/IP ํ”„๋กœํ† ์ฝœ์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ ์ „์†ก์˜ ์‹ ๋ขฐ์„ฑ์„ ๋ณด์žฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ํšจ์œจ์„ฑ ํ–ฅ์ƒ: UDP ํ”„๋กœํ† ์ฝœ์„ ์‚ฌ์šฉํ•˜์—ฌ ์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ ์ „์†ก์˜ ํšจ์œจ์„ฑ์„ ๋†’์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ๋‹ค์–‘ํ•œ ์š”๊ตฌ์‚ฌํ•ญ ์ถฉ์กฑ: ๋‹ค์–‘ํ•œ ๋„คํŠธ์›Œํฌ ํ†ต์‹  ์š”๊ตฌ์‚ฌํ•ญ์„ ์ ์ ˆํ•œ ํ”„๋กœํ† ์ฝœ๋กœ ์ถฉ์กฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    3. ๋„คํŠธ์›Œํฌ ์—ฐ๊ฒฐ ๊ด€๋ฆฌ์žฅ์ :
    • ์•ˆ์ •์„ฑ ํ–ฅ์ƒ: ๋„คํŠธ์›Œํฌ ์—ฐ๊ฒฐ ์ƒํƒœ๋ฅผ ๋ชจ๋‹ˆํ„ฐ๋งํ•˜์—ฌ ์•ˆ์ •์„ฑ์„ ๋†’์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ์ž๋™ ์žฌ์—ฐ๊ฒฐ: ์—ฐ๊ฒฐ์ด ๋Š์–ด์ง€๋ฉด ์ž๋™์œผ๋กœ ์žฌ์—ฐ๊ฒฐํ•˜์—ฌ ์„œ๋น„์Šค ์ค‘๋‹จ์„ ์ตœ์†Œํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ์‚ฌ์šฉ์ž ๊ฒฝํ—˜ ๊ฐœ์„ : ์•ˆ์ •์ ์ธ ๋„คํŠธ์›Œํฌ ์—ฐ๊ฒฐ์„ ์œ ์ง€ํ•˜์—ฌ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ๊ฐœ์„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    ๋ณด์•ˆ ๊ฐ•ํ™” ๊ธฐ๋ฒ•์ „์†ก๋˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์•”ํ˜ธํ™”ํ•˜์—ฌ ๋ฐ์ดํ„ฐ์˜ ๊ธฐ๋ฐ€์„ฑ์„ ๋ณดํ˜ธํ–ˆ์Šต๋‹ˆ๋‹ค. SSL/TLS๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋„คํŠธ์›Œํฌ ํ†ต์‹ ์„ ์•”ํ˜ธํ™”ํ•˜๊ณ , ์ค‘์š”ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ์ „์†กํ–ˆ์Šต๋‹ˆ๋‹ค.
    • ๊ธฐ๋ฐ€์„ฑ ๋ณด์žฅ: ๋ฐ์ดํ„ฐ๋ฅผ ์•”ํ˜ธํ™”ํ•˜์—ฌ ๊ธฐ๋ฐ€์„ฑ์„ ๋ณดํ˜ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ๋ณด์•ˆ ๊ฐ•ํ™”: SSL/TLS๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋„คํŠธ์›Œํฌ ํ†ต์‹ ์˜ ๋ณด์•ˆ์„ ๊ฐ•ํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ๋ฐ์ดํ„ฐ ๋ณดํ˜ธ: ์ „์†ก ์ค‘์ธ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณดํ˜ธํ•˜์—ฌ ๋ถˆ๋ฒ•์ ์ธ ์ ‘๊ทผ์„ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    2. ์ธ์ฆ ๋ฐ ๊ถŒํ•œ ๊ด€๋ฆฌ์žฅ์ :
    • ์ ‘๊ทผ ์ œ์–ด: ์‚ฌ์šฉ์ž ์ธ์ฆ๊ณผ ๊ถŒํ•œ ๊ด€๋ฆฌ๋กœ ์ ‘๊ทผ์„ ์ œ์–ดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ๋ณด์•ˆ ๊ฐ•ํ™”: ๊ถŒํ•œ์ด ์—†๋Š” ์‚ฌ์šฉ์ž์˜ ์ ‘๊ทผ์„ ์ฐจ๋‹จํ•˜์—ฌ ๋ณด์•ˆ์„ ๊ฐ•ํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ์œ ์—ฐํ•œ ๊ด€๋ฆฌ: OAuth์™€ JWT๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์œ ์—ฐํ•˜๊ฒŒ ์ธ์ฆ๊ณผ ๊ถŒํ•œ์„ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    3. ์ทจ์•ฝ์  ์Šค์บ๋‹์žฅ์ :
    • ์ทจ์•ฝ์  ๋ฐœ๊ฒฌ: ์ •๊ธฐ์ ์ธ ์Šค์บ๋‹์œผ๋กœ ๋ณด์•ˆ ์ทจ์•ฝ์ ์„ ๋ฐœ๊ฒฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ์‹ ์†ํ•œ ํŒจ์น˜: ๋ฐœ๊ฒฌ๋œ ์ทจ์•ฝ์ ์„ ์‹ ์†ํ•˜๊ฒŒ ํŒจ์น˜ํ•˜์—ฌ ๋ณด์•ˆ์„ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ๋ณด์•ˆ ๊ฐ•ํ™”: ์ง€์†์ ์ธ ์ทจ์•ฝ์  ๊ด€๋ฆฌ๋ฅผ ํ†ตํ•ด ๋ณด์•ˆ์„ ๊ฐ•ํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    ๊ฒฐ๋ก 
  • ์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” ์ž„๋ฒ ๋””๋“œ ์‹œ์Šคํ…œ์˜ ๋„คํŠธ์›Œํฌ ํ†ต์‹ ๊ณผ ๋ณด์•ˆ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•œ ๋ฐฉ๋ฒ•๋“ค์„ ๋‹ค๋ฃจ์—ˆ์Šต๋‹ˆ๋‹ค. ํšจ์œจ์ ์ธ ๋ฐ์ดํ„ฐ ์ „์†ก, ์ ์ ˆํ•œ ํ”„๋กœํ† ์ฝœ ์„ ํƒ, ๋„คํŠธ์›Œํฌ ์—ฐ๊ฒฐ ๊ด€๋ฆฌ ๋“ฑ์˜ ๊ธฐ๋ฒ•์„ ํ†ตํ•ด ๋„คํŠธ์›Œํฌ ํ†ต์‹ ์„ ์ตœ์ ํ™”ํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ, ๋ฐ์ดํ„ฐ ์•”ํ˜ธํ™”, ์ธ์ฆ ๋ฐ ๊ถŒํ•œ ๊ด€๋ฆฌ, ์ทจ์•ฝ์  ์Šค์บ๋‹ ๋“ฑ์„ ํ†ตํ•ด ์‹œ์Šคํ…œ์˜ ๋ณด์•ˆ์„ ๊ฐ•ํ™”ํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๋ฐฉ๋ฒ•๋“ค์„ ํ†ตํ•ด ์•ˆ์ •์ ์ด๊ณ  ์•ˆ์ „ํ•œ ์ž„๋ฒ ๋””๋“œ ์‹œ์Šคํ…œ์„ ๊ตฌ์ถ•ํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ ํฌ์ŠคํŒ…์—์„œ๋Š” ์ž„๋ฒ ๋””๋“œ ์‹œ์Šคํ…œ์˜ ํ…Œ์ŠคํŠธ์™€ ๋””๋ฒ„๊น…์— ๋Œ€ํ•ด ๋‹ค๋ฃจ๊ฒ ์Šต๋‹ˆ๋‹ค.
  • ์ •๊ธฐ์ ์œผ๋กœ ์‹œ์Šคํ…œ์˜ ๋ณด์•ˆ ์ทจ์•ฝ์ ์„ ์Šค์บ๋‹ํ•˜๊ณ , ๋ฐœ๊ฒฌ๋œ ์ทจ์•ฝ์ ์„ ์‹ ์†ํ•˜๊ฒŒ ํŒจ์น˜ํ•˜์—ฌ ๋ณด์•ˆ์„ ์œ ์ง€ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด OWASP ZAP๊ณผ ๊ฐ™์€ ๋„๊ตฌ๋ฅผ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ์‚ฌ์šฉ์ž ์ธ์ฆ๊ณผ ๊ถŒํ•œ ๊ด€๋ฆฌ๋ฅผ ํ†ตํ•ด ์‹œ์Šคํ…œ์˜ ์ ‘๊ทผ์„ ์ œ์–ดํ•˜๊ณ , ๊ถŒํ•œ์ด ์—†๋Š” ์‚ฌ์šฉ์ž์˜ ์ ‘๊ทผ์„ ์ฐจ๋‹จํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด OAuth์™€ JWT(Json Web Token)๋ฅผ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ์žฅ์ :
  • 1. ๋ฐ์ดํ„ฐ ์•”ํ˜ธํ™”
  • ๋„คํŠธ์›Œํฌ ์—ฐ๊ฒฐ์˜ ์ƒํƒœ๋ฅผ ์‹ค์‹œ๊ฐ„์œผ๋กœ ๋ชจ๋‹ˆํ„ฐ๋งํ•˜๊ณ , ์—ฐ๊ฒฐ์ด ๋Š์–ด์ง€๋ฉด ์ž๋™์œผ๋กœ ์žฌ์—ฐ๊ฒฐํ•˜๋„๋ก ์„ค์ •ํ•˜์—ฌ ์•ˆ์ •์ ์ธ ๋„คํŠธ์›Œํฌ ํ†ต์‹ ์„ ์œ ์ง€ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ์ ์ ˆํ•œ ๋„คํŠธ์›Œํฌ ํ”„๋กœํ† ์ฝœ์„ ์„ ํƒํ•˜์—ฌ ๋ฐ์ดํ„ฐ ์ „์†ก์˜ ์‹ ๋ขฐ์„ฑ๊ณผ ํšจ์œจ์„ฑ์„ ๋†’์˜€์Šต๋‹ˆ๋‹ค. ์ด๋ฒˆ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” TCP/IP์™€ UDP๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋‹ค์–‘ํ•œ ํ†ต์‹  ์š”๊ตฌ์‚ฌํ•ญ์„ ์ถฉ์กฑํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ์žฅ์ :
  • 1. ํšจ์œจ์ ์ธ ๋ฐ์ดํ„ฐ ์ „์†ก
  • ์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” ์ž„๋ฒ ๋””๋“œ ์‹œ์Šคํ…œ์—์„œ ๋„คํŠธ์›Œํฌ ํ†ต์‹ ๊ณผ ๋ณด์•ˆ ๋ฌธ์ œ๋ฅผ ์–ด๋–ป๊ฒŒ ํ•ด๊ฒฐํ–ˆ๋Š”์ง€์— ๋Œ€ํ•ด ๋‹ค๋ฃจ๊ฒ ์Šต๋‹ˆ๋‹ค. ๋„คํŠธ์›Œํฌ ์—ฐ๊ฒฐ์ด ํ•„์ˆ˜์ ์ธ ์ž„๋ฒ ๋””๋“œ ์‹œ์Šคํ…œ์—์„œ๋Š” ๋ฐ์ดํ„ฐ ์ „์†ก์˜ ํšจ์œจ์„ฑ๊ณผ ๋ณด์•ˆ์ด ๋งค์šฐ ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค. ํ”„๋กœ์ ํŠธ์—์„œ ์‚ฌ์šฉํ•œ ๋„คํŠธ์›Œํฌ ํ†ต์‹  ๊ธฐ๋ฒ•๊ณผ ๋ณด์•ˆ ๊ฐ•ํ™”๋ฅผ ์œ„ํ•œ ๋ฐฉ๋ฒ•๋“ค์„ ๊ณต์œ ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.
  • 21. ํ”„๋กœ์ ํŠธ ์ ์šฉ ์‚ฌ๋ก€ - Part 18: ๊ณ ๊ธ‰ ๋””์ž์ธ ํŒจํ„ด๊ณผ ํ…Œ์ŠคํŠธ ๊ธฐ๋ฒ•์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” ์ž„๋ฒ ๋””๋“œ ์‹œ์Šคํ…œ ๊ฐœ๋ฐœ์—์„œ ๊ณ ๊ธ‰ ๋””์ž์ธ ํŒจํ„ด์„ ์ ์šฉํ•˜๊ณ , ์ด๋ฅผ ํ…Œ์ŠคํŠธํ•˜๋Š” ๊ธฐ๋ฒ•์— ๋Œ€ํ•ด ๋‹ค๋ฃจ๊ฒ ์Šต๋‹ˆ๋‹ค. ์ž„๋ฒ ๋””๋“œ ์‹œ์Šคํ…œ์—์„œ๋Š” ์ฝ”๋“œ์˜ ์žฌ์‚ฌ์šฉ์„ฑ๊ณผ ์œ ์ง€๋ณด์ˆ˜์„ฑ์„ ๋†’์ด๊ธฐ ์œ„ํ•ด ๋””์ž์ธ ํŒจํ„ด์„ ํšจ๊ณผ์ ์œผ๋กœ ํ™œ์šฉํ•  ํ•„์š”๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ, ์‹ ๋ขฐ์„ฑ ๋†’์€ ์‹œ์Šคํ…œ์„ ๊ตฌ์ถ•ํ•˜๊ธฐ ์œ„ํ•ด ๋‹ค์–‘ํ•œ ํ…Œ์ŠคํŠธ ๊ธฐ๋ฒ•์„ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” ๋ช‡ ๊ฐ€์ง€ ๋Œ€ํ‘œ์ ์ธ ๋””์ž์ธ ํŒจํ„ด๊ณผ ํ…Œ์ŠคํŠธ ๊ธฐ๋ฒ•์„ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.1. ์‹ฑ๊ธ€ํ†ค ํŒจํ„ด์žฅ์ :
    • ์ž์› ์ ˆ์•ฝ: ํ•˜๋‚˜์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ๊ณต์œ ํ•˜์—ฌ ๋ฉ”๋ชจ๋ฆฌ์™€ ์ž์›์„ ์ ˆ์•ฝํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ๊ธ€๋กœ๋ฒŒ ์ ‘๊ทผ: ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์–ด๋””์„œ๋‚˜ ์ธ์Šคํ„ด์Šค์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ์ƒํƒœ ๊ด€๋ฆฌ ์šฉ์ด: ์ „์—ญ์ ์ธ ์ƒํƒœ ๊ด€๋ฆฌ๋ฅผ ์‰ฝ๊ฒŒ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    ์˜ˆ์‹œ ์ฝ”๋“œ:2. ์˜ต์ €๋ฒ„ ํŒจํ„ด์žฅ์ :
    • ์ด๋ฒคํŠธ ๊ธฐ๋ฐ˜ ์‹œ์Šคํ…œ ๊ตฌํ˜„: ๊ฐ์ฒด ์ƒํƒœ ๋ณ€ํ™”๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ๋Š์Šจํ•œ ๊ฒฐํ•ฉ: ์˜ต์ €๋ฒ„์™€ ์ฃผ์ฒด ๊ฐ„์˜ ๊ฒฐํ•ฉ๋„๋ฅผ ๋‚ฎ์ถฐ ์œ ์—ฐ์„ฑ์„ ๋†’์ž…๋‹ˆ๋‹ค.
    • ํ™•์žฅ์„ฑ: ์ƒˆ๋กœ์šด ์˜ต์ €๋ฒ„๋ฅผ ์‰ฝ๊ฒŒ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    ์˜ˆ์‹œ ์ฝ”๋“œ:3. ํŒฉํ† ๋ฆฌ ํŒจํ„ด์žฅ์ :
    • ๊ฐ์ฒด ์ƒ์„ฑ ๋กœ์ง ์บก์Šํ™”: ๊ฐ์ฒด ์ƒ์„ฑ ๋กœ์ง์„ ๋ถ„๋ฆฌํ•˜์—ฌ ์ฝ”๋“œ์˜ ๊ฐ€๋…์„ฑ์„ ๋†’์ž…๋‹ˆ๋‹ค.
    • ํ™•์žฅ์„ฑ: ์ƒˆ๋กœ์šด ๊ฐ์ฒด ์ƒ์„ฑ ๋กœ์ง์„ ์‰ฝ๊ฒŒ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ์œ ์—ฐ์„ฑ: ๊ตฌ์ฒด์ ์ธ ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ์ฝ”๋“œ์— ์ง์ ‘ ๋ช…์‹œํ•˜์ง€ ์•Š๊ณ  ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    ์˜ˆ์‹œ ์ฝ”๋“œ:ํ…Œ์ŠคํŠธ ๊ธฐ๋ฒ•๋‹จ์œ„ ํ…Œ์ŠคํŠธ๋Š” ์†Œํ”„ํŠธ์›จ์–ด์˜ ๊ฐœ๋ณ„ ๊ตฌ์„ฑ ์š”์†Œ๋‚˜ ๋ชจ๋“ˆ์„ ํ…Œ์ŠคํŠธํ•˜๋Š” ๊ธฐ๋ฒ•์ž…๋‹ˆ๋‹ค. ๊ฐ ํ•จ์ˆ˜๋‚˜ ๋ฉ”์„œ๋“œ๊ฐ€ ์˜ˆ์ƒ๋Œ€๋กœ ๋™์ž‘ํ•˜๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
    • ๋ฒ„๊ทธ ์กฐ๊ธฐ ๋ฐœ๊ฒฌ: ์ฝ”๋“œ ์ž‘์„ฑ ํ›„ ๋ฐ”๋กœ ํ…Œ์ŠคํŠธํ•˜์—ฌ ๋ฒ„๊ทธ๋ฅผ ์กฐ๊ธฐ์— ๋ฐœ๊ฒฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ๋ฆฌํŒฉํ† ๋ง ์ง€์›: ๋ฆฌํŒฉํ† ๋ง ์‹œ ํ…Œ์ŠคํŠธ๋ฅผ ํ†ตํ•ด ์ฝ”๋“œ์˜ ์ •ํ™•์„ฑ์„ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ์ฝ”๋“œ ํ’ˆ์งˆ ํ–ฅ์ƒ: ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋ฉด์„œ ์ฝ”๋“œ์˜ ํ’ˆ์งˆ์„ ๋†’์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    ์˜ˆ์‹œ ์ฝ”๋“œ:2. ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ์žฅ์ :
    • ์‹œ์Šคํ…œ ํ†ตํ•ฉ ๊ฒ€์ฆ: ๋ชจ๋“ˆ ๊ฐ„์˜ ์ƒํ˜ธ์ž‘์šฉ์„ ํ†ตํ•ด ์‹œ์Šคํ…œ ํ†ตํ•ฉ์„ ๊ฒ€์ฆํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ์‹ค์ œ ์‚ฌ์šฉ ์‹œ๋‚˜๋ฆฌ์˜ค ํ…Œ์ŠคํŠธ: ์‹ค์ œ ์‚ฌ์šฉ ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ๋ฐ˜์˜ํ•œ ํ…Œ์ŠคํŠธ๋กœ ์‹œ์Šคํ…œ์˜ ์•ˆ์ •์„ฑ์„ ๋†’์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ์ข…๋‹จ ๊ฐ„ ํ…Œ์ŠคํŠธ: ์‚ฌ์šฉ์ž ๊ด€์ ์—์„œ ์‹œ์Šคํ…œ์˜ ์ข…๋‹จ ๊ฐ„ ๊ธฐ๋Šฅ์„ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    ์˜ˆ์‹œ ์ฝ”๋“œ:3. ํšŒ๊ท€ ํ…Œ์ŠคํŠธ์žฅ์ :
    • ๋ฒ„๊ทธ ์˜ˆ๋ฐฉ: ์ฝ”๋“œ ์ˆ˜์ • ํ›„ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ๋ฒ„๊ทธ๋ฅผ ์˜ˆ๋ฐฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ๊ธฐ๋Šฅ ๊ฒ€์ฆ: ๊ธฐ์กด ๊ธฐ๋Šฅ์ด ์ •์ƒ์ ์œผ๋กœ ์ž‘๋™ํ•˜๋Š”์ง€ ์ง€์†์ ์œผ๋กœ ๊ฒ€์ฆํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    • ํ’ˆ์งˆ ์œ ์ง€: ์ฝ”๋“œ ์ˆ˜์ • ํ›„์—๋„ ์‹œ์Šคํ…œ์˜ ํ’ˆ์งˆ์„ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    ์˜ˆ์‹œ ์ฝ”๋“œ:๊ฒฐ๋ก 
  • ์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” ์ž„๋ฒ ๋””๋“œ ์‹œ์Šคํ…œ ๊ฐœ๋ฐœ์—์„œ ๊ณ ๊ธ‰ ๋””์ž์ธ ํŒจํ„ด์„ ์ ์šฉํ•˜๊ณ , ์ด๋ฅผ ํ…Œ์ŠคํŠธํ•˜๋Š” ๊ธฐ๋ฒ•์— ๋Œ€ํ•ด ๋‹ค๋ฃจ์—ˆ์Šต๋‹ˆ๋‹ค. ์‹ฑ๊ธ€ํ†ค ํŒจํ„ด, ์˜ต์ €๋ฒ„ ํŒจํ„ด, ํŒฉํ† ๋ฆฌ ํŒจํ„ด ๋“ฑ์„ ํ†ตํ•ด ์ฝ”๋“œ์˜ ์žฌ์‚ฌ์šฉ์„ฑ๊ณผ ์œ ์ง€๋ณด์ˆ˜์„ฑ์„ ๋†’์ผ ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ, ๋‹จ์œ„ ํ…Œ์ŠคํŠธ, ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ, ํšŒ๊ท€ ํ…Œ์ŠคํŠธ ๋“ฑ์„ ํ†ตํ•ด ์‹œ์Šคํ…œ์˜ ์‹ ๋ขฐ์„ฑ์„ ๊ฒ€์ฆํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๋ฐฉ๋ฒ•๋“ค์„ ํ†ตํ•ด ์•ˆ์ •์ ์ด๊ณ  ํšจ์œจ์ ์ธ ์ž„๋ฒ ๋””๋“œ ์‹œ์Šคํ…œ์„ ๊ตฌ์ถ•ํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.
  • csharp์ฝ”๋“œ ๋ณต์‚ฌ public class RegressionTests { [Fact] public void ConfigurationManager_ContinuesToReturnCorrectValue_AfterUpdate() { var configManager = ConfigurationManager.Instance; var initialResult = configManager.GetConfig("SomeKey"); // ์—…๋ฐ์ดํŠธ ํ›„์—๋„ ๋™์ผํ•œ ๊ฐ’์ด ๋ฐ˜ํ™˜๋˜๋Š”์ง€ ํ™•์ธ var updatedResult = configManager.GetConfig("SomeKey"); Assert.Equal(initialResult, updatedResult); } }
  • ํšŒ๊ท€ ํ…Œ์ŠคํŠธ๋Š” ์ฝ”๋“œ ๋ณ€๊ฒฝ ํ›„ ๊ธฐ์กด ๊ธฐ๋Šฅ์ด ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ž‘๋™ํ•˜๋Š”์ง€ ํ™•์ธํ•˜๋Š” ํ…Œ์ŠคํŠธ์ž…๋‹ˆ๋‹ค. ์ฝ”๋“œ ์ˆ˜์ • ํ›„ ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ๋ฒ„๊ทธ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.
  • csharp์ฝ”๋“œ ๋ณต์‚ฌ public class SystemTests { [Fact] public void System_InitializesAndProcessesDataCorrectly() { var device = DeviceFactory.CreateDevice("Sensor"); device.Initialize(); var sensor = (SensorDevice)device; sensor.DataChanged("NewData"); // ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ ๊ฒ€์ฆ ๋กœ์ง Assert.True(sensor.ProcessedData == "ExpectedProcessedData"); } }
  • ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ๋Š” ์—ฌ๋Ÿฌ ๋ชจ๋“ˆ์„ ๊ฒฐํ•ฉํ•˜์—ฌ ์‹œ์Šคํ…œ ์ „์ฒด์˜ ๋™์ž‘์„ ํ…Œ์ŠคํŠธํ•˜๋Š” ๊ธฐ๋ฒ•์ž…๋‹ˆ๋‹ค. ๋ชจ๋“ˆ ๊ฐ„์˜ ์ƒํ˜ธ์ž‘์šฉ์„ ๊ฒ€์ฆํ•ฉ๋‹ˆ๋‹ค.
  • csharp์ฝ”๋“œ ๋ณต์‚ฌ public class ConfigurationManagerTests { [Fact] public void GetConfig_ReturnsCorrectValue() { var configManager = ConfigurationManager.Instance; var result = configManager.GetConfig("SomeKey"); Assert.Equal("ExpectedValue", result); } }
  • ์žฅ์ :
  • 1. ๋‹จ์œ„ ํ…Œ์ŠคํŠธ
  • csharp์ฝ”๋“œ ๋ณต์‚ฌ public abstract class Device { public abstract void Initialize(); } public class SensorDevice : Device { public override void Initialize() { // ์„ผ์„œ ์ดˆ๊ธฐํ™” ๋กœ์ง } } public class DeviceFactory { public static Device CreateDevice(string type) { switch (type) { case "Sensor": return new SensorDevice(); default: throw new ArgumentException("Unknown device type"); } } }
  • ํŒฉํ† ๋ฆฌ ํŒจํ„ด์€ ๊ฐ์ฒด ์ƒ์„ฑ ๋กœ์ง์„ ๋ณ„๋„์˜ ํŒฉํ† ๋ฆฌ ํด๋ž˜์Šค์— ์œ„์ž„ํ•˜๋Š” ํŒจํ„ด์ž…๋‹ˆ๋‹ค. ์ž„๋ฒ ๋””๋“œ ์‹œ์Šคํ…œ์—์„œ ๋‹ค์–‘ํ•œ ํ•˜๋“œ์›จ์–ด ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ฒ˜๋ฆฌํ•  ๋•Œ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • csharp์ฝ”๋“œ ๋ณต์‚ฌ public interface IObserver { void Update(string data); } public class Sensor { private readonly List<IObserver> _observers = new List<IObserver>(); public void Attach(IObserver observer) { _observers.Add(observer); } public void Detach(IObserver observer) { _observers.Remove(observer); } public void Notify(string data) { foreach (var observer in _observers) { observer.Update(data); } } public void DataChanged(string newData) { // ๋ฐ์ดํ„ฐ๊ฐ€ ๋ณ€๊ฒฝ๋˜์—ˆ์„ ๋•Œ Notify(newData); } }
  • ์˜ต์ €๋ฒ„ ํŒจํ„ด์€ ๊ฐ์ฒด์˜ ์ƒํƒœ ๋ณ€ํ™”์— ๋”ฐ๋ผ ๋‹ค๋ฅธ ๊ฐ์ฒด๋“ค์ด ํ†ต๋ณด๋ฐ›๊ณ  ์ž๋™์œผ๋กœ ์—…๋ฐ์ดํŠธ๋˜๋„๋ก ํ•˜๋Š” ํŒจํ„ด์ž…๋‹ˆ๋‹ค. ์ž„๋ฒ ๋””๋“œ ์‹œ์Šคํ…œ์—์„œ๋Š” ์„ผ์„œ ๋ฐ์ดํ„ฐ์˜ ๋ณ€ํ™”๋ฅผ ๊ฐ์ง€ํ•˜์—ฌ ์ด๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • csharp์ฝ”๋“œ ๋ณต์‚ฌ public class ConfigurationManager { private static ConfigurationManager _instance; private static readonly object _lock = new object(); private ConfigurationManager() { } public static ConfigurationManager Instance { get { lock (_lock) { if (_instance == null) { _instance = new ConfigurationManager(); } return _instance; } } } public string GetConfig(string key) { // ์„ค์ • ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋กœ์ง } }
  • ์‹ฑ๊ธ€ํ†ค ํŒจํ„ด์€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋‚ด์—์„œ ํŠน์ • ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ํ•˜๋‚˜๋งŒ ์ƒ์„ฑํ•˜๋„๋ก ๋ณด์žฅํ•˜๋Š” ํŒจํ„ด์ž…๋‹ˆ๋‹ค. ์ž„๋ฒ ๋””๋“œ ์‹œ์Šคํ…œ์—์„œ๋Š” ์ž์› ๊ด€๋ฆฌ๋ฅผ ์œ„ํ•ด ์‹ฑ๊ธ€ํ†ค ํŒจํ„ด์„ ์ž์ฃผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • ๊ณ ๊ธ‰ ๋””์ž์ธ ํŒจํ„ด ์ ์šฉ
  • 21. ํ”„๋กœ์ ํŠธ ์ ์šฉ ์‚ฌ๋ก€ - Part 18: ๊ณ ๊ธ‰ ๋””์ž์ธ ํŒจํ„ด๊ณผ ํ…Œ์ŠคํŠธ ๊ธฐ๋ฒ•