C# mes ํ๋ก์ ํธ ๋ฐฑ์๋ ๊ตฌํ ์ ๋ฆฌ ๋ฐ ์ถ๊ฐ ๊ฐ์ด๋ ( -3- )
2024. 7. 6. 13:41ใDevelopment๐ฉ๐ป๐ฆณ/C#
- https://www.notion.so/WorkManagementSystem-ea38095ae6a846c791f91a82aee003b4?pvs=4
- 3. ๊ณ ๊ธ ๋์์ธ ํจํด๊ณผ ํ
์คํธ ๊ธฐ๋ฒ๊ณ ๊ธ ๋์์ธ ํจํด์ ์ ์ฉ1. ๋ฆฌํฌ์งํ ๋ฆฌ ํจํด (Repository Pattern)
2. ์๋น์ค ๋ ์ด์ด ํจํด (Service Layer 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(); } } }
ํ ์คํธ ๊ธฐ๋ฒ์ ์ ์ฉ1. ์ ๋ ํ ์คํธ (Unit Testing)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); } }
2. ํตํฉ ํ ์คํธ (Integration 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); } }
๊ฒฐ๋ก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 ์์ฑ์ด ์์ต๋๋ค.
- ํด๋์ค ์ ์ ๋ฐ ๋ฉ์๋ ํ์ธ: WorkInstructionService ํด๋์ค์ ํ์ํ ๋ชจ๋ ๋ฉ์๋๊ฐ ์ ์๋์ด ์๋์ง ํ์ธํฉ๋๋ค.
- ์์ฑ์ ๋งค๊ฐ๋ณ์ ํ์ธ: **WorkInstructionService**๋ฅผ ์ธ์คํด์คํํ ๋ AppDbContext ์ธ์คํด์ค๋ฅผ ์ ๋ฌํ๋์ง ํ์ธํฉ๋๋ค.
- 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(); } } }
2. CS7036 ์ค๋ฅ: ์์ฑ์ ๋งค๊ฐ๋ณ์ ๋๋ฝ**WorkInstructionService**์ ์์ฑ์๊ฐ **context**๋ผ๋ AppDbContext ํ์ ์ ๋งค๊ฐ๋ณ์๋ฅผ ํ์๋ก ํ์ง๋ง ์ด๋ฅผ ์ ๊ณตํ์ง ์์์ต๋๋ค.์๋น์ค์ ์์ฑ์๋ฅผ ํธ์ถํ ๋ AppDbContext ์ธ์คํด์ค๋ฅผ ์ ๋ฌํด์ผ ํฉ๋๋ค.3. 500 ๋ด๋ถ ์๋ฒ ์ค๋ฅ: ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ ๋ฌธ์ 500 ๋ด๋ถ ์๋ฒ ์ค๋ฅ๋ ์ฃผ๋ก ์๋ฒ์์ ๋ฐ์ํ๋ ์์ธ๋ก ์ธํด ์๋ต์ด ์คํจํ๋ ๊ฒฝ์ฐ์ ๋๋ค. ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ ๋ฌธ์์ด ๋๋ ์ค์ ๋ฌธ์ ๋ก ์ธํด ๋ฐ์ํ ์ ์์ต๋๋ค.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)); } } }
- ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ ๋ฌธ์์ด ํ์ธ: appsettings.json ๋๋ **AppDbContext**์์ ์ฌ์ฉํ ์ฐ๊ฒฐ ๋ฌธ์์ด์ด ์ฌ๋ฐ๋ฅธ์ง ํ์ธํฉ๋๋ค.
- 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)); } } }
- ๋ก๊น ์ ํตํด ์ค๋ฅ ์์น ํ์ : **ILogger**๋ฅผ ์ฌ์ฉํ์ฌ ๊ฐ ๋ฉ์๋์ ์์๊ณผ ๋์ ๋ก๊ทธ๋ฅผ ์ถ๊ฐํ๊ณ , ์์ธ ๋ฐ์ ์ ์์ธํ ์ ๋ณด๋ฅผ ๋ก๊น ํ์ฌ ๋ฌธ์ ์ ์์ธ์ ์ถ์ ํ์ต๋๋ค.
- ๋จ๊ณ๋ณ ์ฝ๋ ๊ฒํ : ๊ฐ ๋จ๊ณ๋ณ๋ก ์ฝ๋๋ฅผ ๊ฒํ ํ๋ฉฐ ๋ฉ์๋ ํธ์ถ, ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ ๊ทผ, ์์กด์ฑ ์ฃผ์ ๋ฑ์ ๊ณผ์ ์์ ๋๋ฝ๋ ๋ถ๋ถ์ด ์๋์ง ํ์ธํ์ต๋๋ค.
- ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ ํ ์คํธ: ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ ๋ฌธ์์ด์ ์ฌ์ฉํ์ฌ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ง์ ์ฐ๊ฒฐํด๋ณด๊ณ , ์ฟผ๋ฆฌ๋ฅผ ์คํํ์ฌ ์ฐ๊ฒฐ์ด ์ ์์ ์ผ๋ก ์ด๋ฃจ์ด์ง๋์ง ํ์ธํ์ต๋๋ค.
- ์ ๋ ํ ์คํธ ๋ฐ ํตํฉ ํ ์คํธ ์์ฑ: ์๋น์ค์ ์ปจํธ๋กค๋ฌ์ ๋ํ ์ ๋ ํ ์คํธ์ ํตํฉ ํ ์คํธ๋ฅผ ์์ฑํ์ฌ ์ฝ๋๊ฐ ์์๋๋ก ๋์ํ๋์ง ํ์ธํ์ต๋๋ค.
๊ฒฐ๋ก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 ๋ด๋ถ ์๋ฒ ์ค๋ฅ๋ ์ฃผ๋ก ์๋ฒ์์ ๋ฐ์ํ๋ ์์ธ๋ก ์ธํด ์๋ต์ด ์คํจํ๋ ๊ฒฝ์ฐ์
๋๋ค.
- ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ ๋ฌธ์์ด ํ์ธ: appsettings.json ๋๋ **AppDbContext**์์ ์ฌ์ฉํ ์ฐ๊ฒฐ ๋ฌธ์์ด์ด ์ฌ๋ฐ๋ฅธ์ง ํ์ธํฉ๋๋ค.
- WorkInstructionService ํด๋์ค์ ๊ตฌํ ํ์ธ: ์ด ์๋น์ค ํด๋์ค์ ๋ฉ์๋๋ค์ด ์ฌ๋ฐ๋ฅด๊ฒ ์ ์๋์ด ์๋์ง ํ์ธํฉ๋๋ค.
- ์์กด์ฑ ์ฃผ์ (DI) ์ค์ ํ์ธ: **Program.cs**์์ **AppDbContext**์ **WorkInstructionService**๊ฐ ์ ๋๋ก ๋ฑ๋ก๋์๋์ง ํ์ธํฉ๋๋ค.
5. ์ถ๊ฐ์ ์ธ ๋๋ฒ๊น ๋ฐ ํ ์คํธ์๊ธฐ์น ์์ ์์ธ๋ ์ค๋ฅ ๋ฐ์ ์ ๋๋ฒ๊น ๋ฐ ํ ์คํธ๋ฅผ ํตํด ์์ธ์ ํ์ ํฉ๋๋ค.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"); }); }); });
- ๋ก๊น ๋ฐ ์์ธ ์ฒ๋ฆฌ: ์ฝ๋์ ์ถฉ๋ถํ ๋ก๊น ์ ์ถ๊ฐํ๊ณ , ์์ธ ๋ฐ์ ์ ๋ก๊ทธ๋ฅผ ๊ธฐ๋กํฉ๋๋ค.
- ๋จ์ ํ ์คํธ ์์ฑ: ๊ฐ ๋ฉ์๋์ ๋จ์ ํ ์คํธ๋ฅผ ์์ฑํ์ฌ ์์๋ ๋์์ ํ์ธํฉ๋๋ค.
- ์ ์ ์ฝ๋ ๋ถ์ ๋๊ตฌ ์ฌ์ฉ: 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๋ฅผ ์ฌ์ฉํ์ฌ ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ง์ด๊ทธ๋ ์ด์
์ ์ค์ ํ๊ณ , ์ด๋ฅผ ํตํด ์คํค๋ง ๋ณ๊ฒฝ ์ฌํญ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ์ฉํฉ๋๋ค.
์์ ์ฝ๋: Program.cs2. ๋ง์ด๊ทธ๋ ์ด์ ์ถ๊ฐ ๋ฐ ์ ๋ฐ์ดํธํด๊ฒฐ ๋ฐฉ๋ฒ: 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)); } } }
- ๋ง์ด๊ทธ๋ ์ด์ ์ถ๊ฐ
- powershell์ฝ๋ ๋ณต์ฌ Add-Migration InitialCreate
- ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ ๋ฐ์ดํธ
- powershell์ฝ๋ ๋ณต์ฌ Update-Database
- ์ด๋ฒ ํฌ์คํ ์์๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ง์ด๊ทธ๋ ์ด์ ์ค์ ๊ณผ ๋ก๊ทธ ๋ถ์์ ํตํด ๋ฐ์ํ๋ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ๋ฐฉ๋ฒ์ ๋ํด ์์๋ณด์์ต๋๋ค. ๋ค์ ํฌ์คํ ์์๋ ๋์ฑ ์ฌํ๋ ํ ์คํธ ๊ธฐ๋ฒ๊ณผ ๊ณ ๊ธ ๋์์ธ ํจํด์ ํ์ฉํ 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. ์ ๋ ํ
์คํธ ์์ฑ ๋ฐ ์คํํด๊ฒฐ ๋ฐฉ๋ฒ: ์ ๋ ํ
์คํธ๋ฅผ ์์ฑํ์ฌ ๊ฐ ๊ธฐ๋ฅ์ด ์๋ํ ๋๋ก ๋์ํ๋์ง ๊ฒ์ฆํฉ๋๋ค. ์ด๋ฅผ ํตํด ์ฝ๋์ ์ ๋ขฐ์ฑ์ ๋์ด๊ณ ์ ์ง๋ณด์๋ฅผ ์ฉ์ดํ๊ฒ ํ ์ ์์ต๋๋ค.
2. ํตํฉ ํ ์คํธ ์์ฑ ๋ฐ ์คํํด๊ฒฐ ๋ฐฉ๋ฒ: ํตํฉ ํ ์คํธ๋ฅผ ํตํด ์์คํ ์ ์ฌ๋ฌ ์ปดํฌ๋ํธ๊ฐ ํจ๊ป ๋์ํ ๋ ๋ฐ์ํ ์ ์๋ ๋ฌธ์ ๋ฅผ ์ฌ์ ์ ๋ฐ๊ฒฌํฉ๋๋ค.csharp์ฝ๋ ๋ณต์ฌ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using Moq; using WindowsFormsApp1Core.SQLModels; using WindowsFormsApp1Core.WorkManagementSystem.WMModels; using WindowsFormsApp1Core.WorkManagementSystem.WMSrvs; using Xunit; public class WorkInstructionServiceTests { private readonly Mock<AppDbContext> _mockContext; private readonly Mock<ILogger<WorkInstructionService>> _mockLogger; private readonly WorkInstructionService _service; public WorkInstructionServiceTests() { _mockContext = new Mock<AppDbContext>(); _mockLogger = new Mock<ILogger<WorkInstructionService>>(); _service = new WorkInstructionService(_mockContext.Object, _mockLogger.Object); } [Fact] public async Task GetWorkInstructionsAsync_ReturnsAllInstructions() { // Arrange var data = new List<WorkInstruction> { new WorkInstruction { TaskName = "Task1", Content = "Content1" }, new WorkInstruction { TaskName = "Task2", Content = "Content2" } }; var mockSet = new Mock<DbSet<WorkInstruction>>(); mockSet.As<IQueryable<WorkInstruction>>().Setup(m => m.Provider).Returns(data.AsQueryable().Provider); mockSet.As<IQueryable<WorkInstruction>>().Setup(m => m.Expression).Returns(data.AsQueryable().Expression); mockSet.As<IQueryable<WorkInstruction>>().Setup(m => m.ElementType).Returns(data.AsQueryable().ElementType); mockSet.As<IQueryable<WorkInstruction>>().Setup(m => m.GetEnumerator()).Returns(data.AsQueryable().GetEnumerator()); _mockContext.Setup(c => c.WorkInstructions).Returns(mockSet.Object); // Act var result = await _service.GetWorkInstructionsAsync(); // Assert Assert.Equal(2, result.Count()); Assert.Equal("Task1", result.First().TaskName); } }
๊ณ ๊ธ ๋์์ธ ํจํด๋ฌธ์ : ๋ฐ์ดํฐ ์ก์ธ์ค ๋ก์ง๊ณผ ๋น์ฆ๋์ค ๋ก์ง์ด ์์ฌ ์ฝ๋๊ฐ ๋ณต์กํด์ง๋๋ค.์์ ์ฝ๋: IWorkInstructionRepository์์ ์ฝ๋: WorkInstructionRepository4. ์๋น์ค ํจํดํด๊ฒฐ ๋ฐฉ๋ฒ: ์๋น์ค ํจํด์ ์ ์ฉํ์ฌ ๋น์ฆ๋์ค ๋ก์ง์ ๋ณ๋์ ์๋น์ค ํด๋์ค๋ก ๋ถ๋ฆฌํฉ๋๋ค.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(); } }
๋ง๋ฌด๋ฆฌ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
-
- API ์ค๋ฅ ํด๊ฒฐ ๋ฐ ๋๋ฒ๊น ๊ฒฝํ ๊ณต์ - Part 5
2. ๋ฉ์๋ ์ฃผ์ ํด๊ฒฐ ๋ฐฉ๋ฒ: ๋ฉ์๋ ์ฃผ์ ์ ํตํด ํน์ ๋ฉ์๋์์๋ง ํ์ํ ์์กด์ฑ์ ์ฃผ์ ํฉ๋๋ค.csharp์ฝ๋ ๋ณต์ฌ public class WorkInstructionService { private readonly IWorkInstructionRepository _repository; private readonly ILogger<WorkInstructionService> _logger; public WorkInstructionService(IWorkInstructionRepository repository, ILogger<WorkInstructionService> logger) { _repository = repository; _logger = logger; } // ๋ฉ์๋ ์ ์ ์๋ต... }
3. ์์ฑ ์ฃผ์ ํด๊ฒฐ ๋ฐฉ๋ฒ: ์์ฑ ์ฃผ์ ์ ํตํด ํ์ํ ์์กด์ฑ์ ์์ฑ์ ์ฃผ์ ํฉ๋๋ค.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); } // ๋ค๋ฅธ ๋ฉ์๋ ์ ์ ์๋ต... }
๊ณ ๊ธ ๋๋ฒ๊น ๊ธฐ๋ฒ๋ฌธ์ : ๋๋ฒ๊น ์ ํ์ํ ๋ก๊ทธ ์ ๋ณด๊ฐ ๋ถ์กฑํ๊ฑฐ๋ ๋๋ฌด ๋ง์ ๋๋ฒ๊น ์ด ์ด๋ ต์ต๋๋ค.์์ ์ฝ๋: appsettings.json5. ๋ถ์ฐ ํธ๋ ์ด์ฑํด๊ฒฐ ๋ฐฉ๋ฒ: ๋ถ์ฐ ํธ๋ ์ด์ฑ์ ํตํด ์๋น์ค ๊ฐ์ ํธ์ถ์ ์ถ์ ํ๊ณ , ๊ฐ ์์ฒญ์ ์ ์ฒด ๊ฒฝ๋ก๋ฅผ ์๊ฐํํฉ๋๋ค.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); } // ๋ค๋ฅธ ๋ฉ์๋ ์ ์ ์๋ต... }
๊ฒฐ๋ก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๊ณ ๊ธ ๋๋ฒ๊น
๊ธฐ๋ฒ ์ ์ฉ ์ฌ๋กํ๋ก์ ํธ ์ด๊ธฐ์๋ ๋ก๊ทธ ๋ ๋ฒจ์ ์ ์ ํ ์ค์ ํ์ง ์์ ๋๋ฒ๊น
์ ์ด๋ ค์์ด ์์์ต๋๋ค. ์ดํ ๋ก๊ทธ ๋ ๋ฒจ์ ์ธ๋ถํํ๊ณ , ๊ฐ ๋ ๋ฒจ์ ๋ง๋ ๋ก๊ทธ๋ฅผ ๋จ๊ธฐ๋๋ก ๊ฐ์ ํ์ต๋๋ค.
์์ ์ฝ๋: WorkInstructionService5. ๋ถ์ฐ ํธ๋ ์ด์ฑ ๋์ ์์ ์ฝ๋: Startup.cs๊ฒฐ๋กjson์ฝ๋ ๋ณต์ฌ { "Logging": { "LogLevel": { "Default": "Information", "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information" } } }
- ์ด๋ฒ ํฌ์คํ ์์๋ ์์กด์ฑ ์ฃผ์ ๊ณผ ๊ณ ๊ธ ๋๋ฒ๊น ๊ธฐ๋ฒ์ ์ค์ ํ๋ก์ ํธ์ ์ ์ฉํ ์ฌ๋ก๋ฅผ ๊ณต์ ํ์ต๋๋ค. ์ด๋ฌํ ํจํด๊ณผ ๊ธฐ๋ฒ์ ํตํด ์ฝ๋์ ์ ์ง๋ณด์์ฑ๊ณผ ํ ์คํธ ๊ฐ๋ฅ์ฑ์ ํฌ๊ฒ ํฅ์์ํฌ ์ ์์์ต๋๋ค. ๋ค์ ํฌ์คํ ์์๋ ์ด๋ฌํ ๊ธฐ๋ฒ๋ค์ ์ ์ฉํ ๊ฒฐ๊ณผ์ ์์ผ๋ก์ ๊ฐ์ ๋ฐฉํฅ์ ๋ ผ์ํ๊ฒ ์ต๋๋ค.
- 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์ ๊ฐ์ ๋๊ตฌ๋ฅผ ์ค์ ํ๋ ๊ณผ์ ์ด ๋ค์ ๋ณต์กํ์ต๋๋ค. ํนํ, ๋คํธ์ํฌ ์ค์ ๊ณผ ๋ณด์ ๊ด๋ จ ์ด์๋ฅผ ํด๊ฒฐํ๋ ๋ฐ ๋ง์ ์๊ฐ์ด ์์๋์์ต๋๋ค.
2. ๋ฐ์ดํฐ ๋ถ์์ ์ด๋ ค์์์ ์ฝ๋: grafana-dashboards.yml์ ์ฉ ๊ฒฐ๊ณผ๋ถ์ฐ ํธ๋ ์ด์ฑ ๋์ ๊ณผ ์ฑ๋ฅ ์ต์ ํ๋ฅผ ํตํด ์๋น์ค์ ๊ฐ์ฉ์ฑ์ด ํฌ๊ฒ ํฅ์๋์์ต๋๋ค. ์ด๋ ์ฌ์ฉ์ ๊ฒฝํ ๊ฐ์ ์ผ๋ก ์ด์ด์ก์ผ๋ฉฐ, ์์คํ ์ฅ์ ๋ฐ์ ๋น๋๊ฐ ๊ฐ์ํ์ต๋๋ค.๋ฌธ์ ๋ฐ์ ์ ์ ์ํ๊ฒ ์์ธ์ ํ์ ํ๊ณ ํด๊ฒฐํ ์ ์์ด ๊ฐ๋ฐ ๋ฐ ์ด์ ํจ์จ์ฑ์ด ์ฆ๋๋์์ต๋๋ค. ์ด๋ ํ์ ์์ฐ์ฑ์ ๋์ด๊ณ , ๋ ๋์ ํ์ง์ ์ฝ๋๋ฅผ ์ ๊ณตํ๋ ๋ฐ ๊ธฐ์ฌํ์ต๋๋ค.์ด๋ฒ ํฌ์คํ ์์๋ ๋ถ์ฐ ํธ๋ ์ด์ฑ ๋์ ํ ํ๋ก์ ํธ์ ๋ฏธ์น ๊ธ์ ์ ์ธ ์ํฅ์ ์ดํด๋ณด์์ต๋๋ค. ์ด๋ฌํ ๊ธฐ๋ฒ๋ค์ ๋ณต์กํ ์์คํ ์์ ๋ฌธ์ ๋ฅผ ์ ์ํ๊ฒ ํ์ ํ๊ณ ํด๊ฒฐํ๋ ๋ฐ ์ค์ํ ์ญํ ์ ํฉ๋๋ค. ๋ค์ ํฌ์คํ ์์๋ ํ ์คํธ ์๋ํ์ CI/CD ๋์ ์ ํตํด ํ๋ก์ ํธ์ ํ์ง์ ์ด๋ป๊ฒ ํฅ์์์ผฐ๋์ง์ ๋ํด ๋ ผ์ํ๊ฒ ์ต๋๋ค.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. ๊ฐ๋ฐ ๋ฐ ์ด์ ํจ์จ์ฑ ์ฆ๋
- 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๋ฅผ ์ฌ์ฉํ์ฌ ์ฝ๋๋ฅผ ํธ์ํ ๋๋ง๋ค ์๋์ผ๋ก ๋น๋ํ๊ณ ํ
์คํธ๋ฅผ ์คํํ์ต๋๋ค. ์ด๋ฅผ ํตํด ์ฝ๋ ํ์ง์ ์ง์์ ์ผ๋ก ์ ์งํ ์ ์์์ต๋๋ค.
2. Continuous Deployment (CD)์์ ์ฝ๋: deploy.sh์ฃผ์ ์ฑ๊ณผ ๋ฐ ๋์ ๊ณผ์ ํ ์คํธ ์๋ํ๋ฅผ ํตํด ํ ์คํธ ์ปค๋ฒ๋ฆฌ์ง๋ฅผ ๋ํญ ํฅ์์์ผฐ์ต๋๋ค. ์ด๋ ์ฝ๋ ํ์ง์ ๋์ด๊ณ , ๋ฒ๊ทธ๋ฅผ ์ฌ์ ์ ๋ฐฉ์งํ๋ ๋ฐ ํฐ ๋์์ด ๋์์ต๋๋ค.CI/CD ๋์ ์ผ๋ก ๋ฐฐํฌ ์๋๊ฐ ํฌ๊ฒ ํฅ์๋์์ต๋๋ค. ์ด๋ ๊ฐ๋ฐ ์ฃผ๊ธฐ๋ฅผ ๋จ์ถํ๊ณ , ์ฌ์ฉ์์๊ฒ ๋ ๋น ๋ฅด๊ฒ ์ ๋ฐ์ดํธ๋ฅผ ์ ๊ณตํ ์ ์๊ฒ ํ์ต๋๋ค.CI/CD ํ์ดํ๋ผ์ธ์ ์ค์ ํ๋ ์ด๊ธฐ ๋จ๊ณ์์ ๋ง์ ์๊ฐ๊ณผ ๋ ธ๋ ฅ์ด ํ์ํ์ต๋๋ค. ํนํ, ๋ค์ํ ํ๊ฒฝ์์์ ์ค์ ๊ณผ ๋ณด์ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ๋ฐ ์ด๋ ค์์ด ์์์ต๋๋ค.์ด๋ฒ ํฌ์คํ ์์๋ ํ ์คํธ ์๋ํ์ CI/CD ๋์ ์ ํตํด ํ๋ก์ ํธ์ ํ์ง๊ณผ ํจ์จ์ฑ์ ์ด๋ป๊ฒ ํฅ์์์ผฐ๋์ง ์ดํด๋ณด์์ต๋๋ค. ์ด๋ฌํ ๋๊ตฌ์ ๊ธฐ๋ฒ์ ํตํด ์ฝ๋ ํ์ง์ ์ ์งํ๊ณ , ์ ์ํ๊ฒ ๊ธฐ๋ฅ์ ๋ฐฐํฌํ ์ ์์์ต๋๋ค. ๋ค์ ํฌ์คํ ์์๋ ์ฌ์ฉ์ ํผ๋๋ฐฑ์ ๋ฐ์ํ ๊ธฐ๋ฅ ๊ฐ์ ์ฌ๋ก์ ๋ํด ๋ ผ์ํ๊ฒ ์ต๋๋ค.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
- ๊ฒฐ๋ก
- 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 ์์๋ฅผ ์ถ๊ฐํ์ต๋๋ค.
- ๊ฐ์ ์
- ๊ฐ์ ํ
- ๊ฒฐ๋ก
- 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. ๋ณด์ ๊ฐ์ฌ ๋ฐ ์ทจ์ฝ์ ๋ถ์์์: ๋ณด์ ๊ฐ์ฌ ์ฒดํฌ๋ฆฌ์คํธ
- ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ ๊ทผ ์ ์ด
- ์ฌ์ฉ์ ์ธ์ฆ ๋ฐ ๊ถํ ๊ด๋ฆฌ
- ๋ฐ์ดํฐ ์ํธํ
- ๋คํธ์ํฌ ๋ณด์ ์ค์
- ๋ก๊ทธ ๋ฐ ๋ชจ๋ํฐ๋ง ์์คํ
- SQL ์ธ์ ์ ๊ณต๊ฒฉ ๊ฐ๋ฅ์ฑ
- XSS(Cross-Site Scripting) ์ทจ์ฝ์
- CSRF(Cross-Site Request Forgery) ๊ณต๊ฒฉ ๊ฐ๋ฅ์ฑ
- ์ฝํ ํจ์ค์๋ ์ ์ฑ
2. ์ฌ์ฉ์ ์ธ์ฆ ๋ฐ ๊ถํ ๊ด๋ฆฌ์์ ์ฝ๋: JWT ์ค์ 3. ๋ฐ์ดํฐ ์ํธํ์์ ์ฝ๋: ๋ฐ์ดํฐ ์ํธํ4. ๋คํธ์ํฌ ๋ณด์ ์ค์ ์์: HTTPS ์ค์ 5. ๋ก๊ทธ ๋ฐ ๋ชจ๋ํฐ๋ง ์์คํ ์์: ๋ก๊ทธ ์ค์ ์ฃผ์ ์ฑ๊ณผ ๋ฐ ๋์ ๊ณผ์ ๋ณด์ ๊ฐ์ฌ ๋ฐ ์ธ๋ถ ํ ์คํธ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ์ํ์ฌ ์์คํ ์ ๋ณด์ ์์ค์ ํฌ๊ฒ ํฅ์์ํฌ ์ ์์์ต๋๋ค. ํนํ, ๋ฐ์ดํฐ ์ํธํ์ ์ฌ์ฉ์ ์ธ์ฆ ๊ฐํ๋ ์ฌ์ฉ์ ๋ฐ์ดํฐ ๋ณดํธ์ ํฐ ๋์์ด ๋์์ต๋๋ค.๋ณด์์ ํ ๋ฒ ์ค์ ํ๋ค๊ณ ๋๋๋ ๊ฒ์ด ์๋๋ผ, ์ง์์ ์ธ ๊ด๋ฆฌ์ ๊ฐ์ ์ด ํ์ํฉ๋๋ค. ์๋ก์ด ์ทจ์ฝ์ ์ด ๋ฐ๊ฒฌ๋ ๋๋ง๋ค ์ ์ํ๊ฒ ๋์ํ ์ ์๋ ์ฒด๊ณ๋ฅผ ๋ง๋ จํ๋ ๊ฒ์ด ์ค์ํ์ต๋๋ค.๋ณด์ ๊ธฐ๋ฅ์ ์ถ๊ฐํ๋ฉด์ ๋ฐ์ํ๋ ์ฑ๋ฅ ์ ํ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ๊ฒ์ด ์ฃผ์ ๋์ ๊ณผ์ ์์ต๋๋ค. ์ด๋ฅผ ์ํด ํจ์จ์ ์ธ ์๊ณ ๋ฆฌ์ฆ์ ์ฌ์ฉํ๊ณ , ์ฑ๋ฅ ํ ์คํธ๋ฅผ ๋ฐ๋ณตํ์ต๋๋ค.์ด๋ฒ ํฌ์คํ ์์๋ ํ๋ก์ ํธ์ ๋ณด์์ ๊ฐํํ๊ธฐ ์ํด ๋์ ํ ๋ค์ํ ๋ฐฉ๋ฒ๋ค์ ์ดํด๋ณด์์ต๋๋ค. ๋ณด์์ ์ฌ์ฉ์ ๋ฐ์ดํฐ๋ฅผ ๋ณดํธํ๊ณ ์์คํ ์ ๋ฌด๊ฒฐ์ฑ์ ์ ์งํ๋ ๋ฐ ๋งค์ฐ ์ค์ํ๋ฉฐ, ์ง์์ ์ธ ๊ด๋ฆฌ๊ฐ ํ์ํฉ๋๋ค. ๋ค์ ํฌ์คํ ์์๋ ํ๋ก์ ํธ์ ์ฑ๋ฅ์ ์ต์ ํํ๊ธฐ ์ํด ๋์ ํ ๋ฐฉ๋ฒ์ ๋ํด ๋ ผ์ํ๊ฒ ์ต๋๋ค.csharp์ฝ๋ ๋ณต์ฌ var connectionString = Environment.GetEnvironmentVariable("DB_CONNECTION_STRING"); services.AddDbContext<AppDbContext>(options => options.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString)));
- ๊ฒฐ๋ก
- 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);
2. ๋น๋๊ธฐ ํ๋ก๊ทธ๋๋ฐ ๋์ ์์ ์ฝ๋: ๋น๋๊ธฐ ๋ฉ์๋3. ๋ก๋ ๋ฐธ๋ฐ์ฑ ๋ฐ ์ค์ผ์ผ๋ง์์: Azure Load Balancer ์ฌ์ฉ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; } }
- Azure Load Balancer๋ฅผ ์ฌ์ฉํ์ฌ ์ฌ๋ฌ ์ธ์คํด์ค์ ํธ๋ํฝ์ ๋ถ์ฐ์์ผฐ์ต๋๋ค.
- ์๋ ์ค์ผ์ผ๋ง์ ์ค์ ํ์ฌ ํธ๋ํฝ ์ฆ๊ฐ์ ๋์ํ์ต๋๋ค.
- ๊ฒฐ๋ก
- 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๋ฅผ ์ฌ์ฉํ์ฌ ๋ค์ํ ๋ก๊ทธ ์ถ๋ ฅ์ ์ค์ ํ์ต๋๋ค.
- Azure Monitor๋ฅผ ์ฌ์ฉํ์ฌ CPU ์ฌ์ฉ๋ฅ , ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋, HTTP ์์ฒญ ์ค๋ฅ ๋ฑ์ ์งํ์ ๋ํด ๊ฒฝ๊ณ ๋ฅผ ์ค์ ํ์ต๋๋ค.
- ๊ฒฝ๊ณ ๊ฐ ๋ฐ์ํ๋ฉด ์ด๋ฉ์ผ์ด๋ SMS๋ก ์๋ฆผ์ ๋ฐ๋๋ก ์ค์ ํ์ต๋๋ค.
- Azure Backup์ ์ฌ์ฉํ์ฌ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ํ์ผ ์์คํ ์ ์ ๊ธฐ์ ์ธ ๋ฐฑ์ ์ ์ค์ ํ์ต๋๋ค.
- ๋ณต๊ตฌ ๊ณํ์ ํ ์คํธํ์ฌ ๋ฐ์ดํฐ ์์ค ์ ์ ์ํ๊ฒ ๋ณต๊ตฌํ ์ ์๋๋ก ์ค๋นํ์ต๋๋ค.
- ๋ฐฐํฌ ๋ก๊ทธ๋ฅผ ๋ถ์ํ์ฌ ํ๊ฒฝ ์ค์ ํ์ผ์ ์๋ชป๋ ์ค์ ์ด ์์์ ๋ฐ๊ฒฌํ์ต๋๋ค.
- ์ค์ ํ์ผ์ ์์ ํ๊ณ , ๋ฐฐํฌ ํ์ดํ๋ผ์ธ์ ์ฌ์คํํ์ฌ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ์ต๋๋ค.
- ์ฑ๋ฅ ๋ชจ๋ํฐ๋ง ๋๊ตฌ๋ฅผ ํตํด ํน์ ์ฟผ๋ฆฌ์ ์คํ ์๊ฐ์ด ๊ธธ์ด์ก์์ ๋ฐ๊ฒฌํ์ต๋๋ค.
- ์ฟผ๋ฆฌ๋ฅผ ์ต์ ํํ๊ณ , ์ธ๋ฑ์ค๋ฅผ ์ถ๊ฐํ์ฌ ์ฑ๋ฅ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ์ต๋๋ค.
- ๊ฒฐ๋ก
- 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 ๋ฑ ๋ค์ํ ํ๋ซํผ์์ ์คํ ๊ฐ๋ฅํฉ๋๋ค.
- ์ผ๊ด๋ ๊ฐ๋ฐ ํ๊ฒฝ: ๋ฐฑ์๋์ ํ๋ก ํธ์๋๋ฅผ ๋์ผํ ๊ธฐ์ ์คํ์ผ๋ก ๊ฐ๋ฐํ์ฌ ์ผ๊ด์ฑ์ ์ ์งํ ์ ์์ต๋๋ค.
- ๋์ ์ฑ๋ฅ: ๋ค์ํ ์ต์ ํ ๊ธฐ๋ฒ์ ํตํด ๋น ๋ฅธ ๋ฐ์ดํฐ ์ฒ๋ฆฌ ์๋๋ฅผ ์ ๊ณตํฉ๋๋ค.
- ํ์ฅ์ฑ: ๋๊ท๋ชจ ๋ฐ์ดํฐ๋ฅผ ํจ์จ์ ์ผ๋ก ์ฒ๋ฆฌํ ์ ์์ต๋๋ค.
- ์คํ ์์ค: ์ปค๋ฎค๋ํฐ ์ง์๊ณผ ๋ค์ํ ํ๋ฌ๊ทธ์ธ์ ํ์ฉํ ์ ์์ต๋๋ค.
- ์๋ํ: ์ฝ๋ ํธ์ ์ ์๋์ผ๋ก ๋น๋, ํ ์คํธ, ๋ฐฐํฌ๋ฅผ ์ํํฉ๋๋ค.
- ํตํฉ์ฑ: GitHub ๋ฆฌํฌ์งํ ๋ฆฌ์ ๋ฐ์ ํ๊ฒ ํตํฉ๋์ด ์ค์ ๊ณผ ์ฌ์ฉ์ด ๊ฐํธํฉ๋๋ค.
- ํ์ฅ์ฑ: ๋ค์ํ ์ปค๋ฎค๋ํฐ ์ก์ ์ ํ์ฉํ์ฌ CI/CD ํ์ดํ๋ผ์ธ์ ์์ฝ๊ฒ ํ์ฅํ ์ ์์ต๋๋ค.
- ๊ตฌ์กฐํ๋ ๋ก๊ทธ: JSON ํ์์ผ๋ก ๋ก๊ทธ๋ฅผ ๊ธฐ๋กํ์ฌ ๋ก๊ทธ ๋ถ์๊ณผ ๋ชจ๋ํฐ๋ง์ ์ฉ์ดํ๊ฒ ํฉ๋๋ค.
- ํ์ฅ์ฑ: ๋ค์ํ ์ฑํฌ(์: ์ฝ์, ํ์ผ, ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ฑ)๋ก ๋ก๊ทธ๋ฅผ ์ ์กํ ์ ์์ต๋๋ค.
- ์ ์ฐ์ฑ: ๋ค์ํ ๋ก๊ทธ ์์ค๊ณผ ์กฐ๊ฑด์ ๋ฐ๋ผ ๋ก๊ทธ๋ฅผ ์ค์ ํ ์ ์์ต๋๋ค.
- ์ค์๊ฐ ๋ชจ๋ํฐ๋ง: ์ ํ๋ฆฌ์ผ์ด์ ์ ์ํ์ ์ฑ๋ฅ์ ์ค์๊ฐ์ผ๋ก ๋ชจ๋ํฐ๋งํฉ๋๋ค.
- ์ฌ์ธต ๋ถ์: ์์ธํ ๋ก๊ทธ์ ๋ฉํธ๋ฆญ์ ํตํด ๋ฌธ์ ๋ฅผ ์ ์ํ๊ฒ ํ์ ํ๊ณ ํด๊ฒฐํ ์ ์์ต๋๋ค.
- ํตํฉ์ฑ: Azure ์๋น์ค์์ ์ํํ ํตํฉ์ ์ ๊ณตํ์ฌ ์ ์ฒด์ ์ธ ์์คํ ๋ชจ๋ํฐ๋ง์ ๊ฐํํฉ๋๋ค.
- ์ด๋ฒ ํฌ์คํ ์์๋ ํ๋ก์ ํธ์ ์ฌ์ฉ๋ ์ฃผ์ ๊ธฐ์ ์คํ๊ณผ ๋๊ตฌ๋ค์ ๋ํด ์ดํด๋ณด์์ต๋๋ค. ๊ฐ ๊ธฐ์ ๊ณผ ๋๊ตฌ๊ฐ ํ๋ก์ ํธ์์ ์ด๋ค ์ญํ ์ ํ๋์ง, ๊ทธ๋ฆฌ๊ณ ๊ทธ ์ ํ ์ด์ ์ ์ฅ์ ์ ์ดํดํ๋ ๊ฒ์ด ์ค์ํฉ๋๋ค. ๋ค์ ํฌ์คํ ์์๋ ํ๋ก์ ํธ์ ํ ์คํธ ์ ๋ต๊ณผ ํ์ง ๋ณด์ฆ ๋ฐฉ๋ฒ์ ๋ํด ๋ค๋ฃจ๊ฒ ์ต๋๋ค.
- ํ๋ก์ ํธ์ ์ฌ์ฉ๋ ๊ธฐ์ ๊ณผ ๋๊ตฌ๋ค์ ์ฑ๋ฅ, ํ์ฅ์ฑ, ์์ ์ฑ์ ๊ณ ๋ คํ์ฌ ์ ํ๋์์ต๋๋ค. ํนํ, ํฌ๋ก์ค ํ๋ซํผ ์ง์๊ณผ ์๋ํ๋ ๋ฐฐํฌ ํ์ดํ๋ผ์ธ์ ํตํด ๊ฐ๋ฐ๊ณผ ์ด์์ ํจ์จ์ฑ์ ๊ทน๋ํํ ์ ์์์ต๋๋ค. ๋ํ, ๊ตฌ์กฐํ๋ ๋ก๊น ๊ณผ ์ค์๊ฐ ๋ชจ๋ํฐ๋ง์ ํตํด ์์คํ ์ํ๋ฅผ ์ง์์ ์ผ๋ก ํ์ ํ๊ณ , ๋ฌธ์ ๋ฐ์ ์ ์ ์ํ๊ฒ ๋์ํ ์ ์๋ ๊ธฐ๋ฐ์ ๋ง๋ จํ์์ต๋๋ค.
- Application Insights๋ ์ ํ๋ฆฌ์ผ์ด์ ์ฑ๋ฅ ๋ชจ๋ํฐ๋ง๊ณผ ๋ก๊ทธ ๋ถ์์ ์ ๊ณตํ๋ Azure์ ๋๊ตฌ๋ก, ์ค์๊ฐ ๋ชจ๋ํฐ๋ง๊ณผ ๊ฒฝ๊ณ ์์คํ ์ ๊ตฌ์ถํ๋ ๋ฐ ์ฌ์ฉ๋์์ต๋๋ค.
- Serilog๋ ๊ตฌ์กฐํ๋ ๋ก๊น ์ ์ ๊ณตํ๋ ๊ฐ๋ ฅํ ๋ก๊น ํ๋ ์์ํฌ๋ก, ํ๋ก์ ํธ์ ๋ก๊ทธ ๊ด๋ฆฌ๋ฅผ ๋ด๋นํ์ต๋๋ค.
- ์ฅ์ :
- 1. CI/CD: GitHub Actions
- MySQL์ ์์ ์ ์ด๊ณ ์ฑ๋ฅ์ด ๋ฐ์ด๋ ๊ด๊ณํ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ก, ํ๋ก์ ํธ์ ๋ฐ์ดํฐ ์ ์ฅ์๋ก ์ฌ์ฉ๋์์ต๋๋ค.
- ASP.NET Core๋ ํฌ๋ก์ค ํ๋ซํผ ์ฑ๋ฅ์ ์ ๊ณตํ๋ ๋ชจ๋ํ๋ ์น ํ๋ ์์ํฌ๋ก, ์ด๋ฒ ํ๋ก์ ํธ์ ๋ฐฑ์๋์ ํ๋ก ํธ์๋๋ฅผ ๋ชจ๋ ๊ตฌ์ฑํ๋ ๋ฐ ํต์ฌ์ ์ธ ์ญํ ์ ํ์ต๋๋ค.
- ํ๋ก์ ํธ์ ์ฃผ์ ๊ธฐ์ ์คํ
- 17. ํ๋ก์ ํธ ์ ์ฉ ์ฌ๋ก - Part 14
- 18. ํ๋ก์ ํธ ์ ์ฉ ์ฌ๋ก - Part 15์ด์ ํ๋ก์ ํธ์ ํ
์คํธ ์ ๋ต๊ณผ ํ์ง ๋ณด์ฆ ๋ฐฉ๋ฒ์ ๋ํด ์์๋ณด๊ฒ ์ต๋๋ค. ์ด๋ค ํ
์คํธ ๊ธฐ๋ฒ๊ณผ ๋๊ตฌ๋ฅผ ์ฌ์ฉํ๋์ง, ๊ทธ๋ฆฌ๊ณ ์ด๋ฅผ ํตํด ์ด๋ป๊ฒ ํ๋ก์ ํธ์ ํ์ง์ ๋ณด์ฅํ๋์ง ์ค๋ช
ํ๊ฒ ์ต๋๋ค.1. ์ ๋ ํ
์คํธ์ฅ์ :
- ๋น ๋ฅธ ํผ๋๋ฐฑ: ์ฝ๋ ๋ณ๊ฒฝ ์ ์ฆ๊ฐ์ ์ธ ํผ๋๋ฐฑ์ ์ ๊ณตํ์ฌ ๋ฒ๊ทธ๋ฅผ ์กฐ๊ธฐ์ ๋ฐ๊ฒฌํ ์ ์์ต๋๋ค.
- ๋จ์๋ณ ๊ฒ์ฆ: ๊ฐ๋ณ ๋ชจ๋์ด ๋ ๋ฆฝ์ ์ผ๋ก ์ฌ๋ฐ๋ฅด๊ฒ ๋์ํ๋์ง ํ์ธํ ์ ์์ต๋๋ค.
- ์๋ํ ์ฉ์ด: CI/CD ํ์ดํ๋ผ์ธ์ ์ฝ๊ฒ ํตํฉํ์ฌ ์๋ํ๋ ํ ์คํธ๋ฅผ ์คํํ ์ ์์ต๋๋ค.
- ์ค์ ํ๊ฒฝ ๊ฒ์ฆ: ์ค์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ์ํธ์์ฉ์ ํ ์คํธํ์ฌ ๋ฐ์ดํฐ ์ผ๊ด์ฑ์ ํ์ธํ ์ ์์ต๋๋ค.
- ์ข ๋จ ๊ฐ ํ ์คํธ: ์์คํ ์ ๋ค์ํ ๊ตฌ์ฑ ์์๊ฐ ํจ๊ป ๋์ํ๋์ง ํ์ธํ ์ ์์ต๋๋ค.
- ์ ๋ขฐ์ฑ ํฅ์: ๋ค์ํ ๋ชจ๋ ๊ฐ์ ํตํฉ์ ๊ฒ์ฆํ์ฌ ์ ๋ขฐ์ฑ์ ๋์ผ ์ ์์ต๋๋ค.
- ์ฌ์ฉ์ ์๋๋ฆฌ์ค ๊ฒ์ฆ: ์ค์ ์ฌ์ฉ์ ์๋๋ฆฌ์ค๋ฅผ ํ ์คํธํ์ฌ ์ฌ์ฉ์ ๊ฒฝํ์ ๊ฒ์ฆํ ์ ์์ต๋๋ค.
- ์ ์ฒด ์์คํ ํ ์คํธ: ์์คํ ์ ๋ชจ๋ ๊ตฌ์ฑ ์์๊ฐ ํจ๊ป ๋์ํ๋์ง ํ์ธํ ์ ์์ต๋๋ค.
- ์๋ํ ๊ฐ๋ฅ: ํ ์คํธ ์๋๋ฆฌ์ค๋ฅผ ์๋ํํ์ฌ ๋ฐ๋ณต์ ์ธ ํ ์คํธ๋ฅผ ํจ์จ์ ์ผ๋ก ์ํํ ์ ์์ต๋๋ค.
- ์ฝ๋ ํ์ง ํฅ์: ๋ค์ํ ๊ด์ ์์ ์ฝ๋๋ฅผ ๊ฒํ ํ์ฌ ์ฝ๋ ํ์ง์ ๋์ผ ์ ์์ต๋๋ค.
- ์ง์ ๊ณต์ : ํ์ ๊ฐ์ ์ฝ๋ ๋ฆฌ๋ทฐ๋ฅผ ํตํด ์ง์์ ๊ณต์ ํ๊ณ ํ์ตํ ์ ์์ต๋๋ค.
- ๋ฒ๊ทธ ๊ฐ์: ์ฝ๋ ๋ฆฌ๋ทฐ๋ฅผ ํตํด ์ ์ฌ์ ์ธ ๋ฒ๊ทธ๋ฅผ ์กฐ๊ธฐ์ ๋ฐ๊ฒฌํ๊ณ ์์ ํ ์ ์์ต๋๋ค.
- ์๋ํ๋ ํ์ง ๊ฒ์ฌ: ์ฝ๋๋ฅผ ์๋์ผ๋ก ๋ถ์ํ์ฌ ํ์ง ๋ฌธ์ ๋ฅผ ๋ฐ๊ฒฌํ ์ ์์ต๋๋ค.
- ์ง์์ ์ธ ํตํฉ: CI/CD ํ์ดํ๋ผ์ธ์ ํตํฉํ์ฌ ์ง์์ ์ผ๋ก ์ฝ๋ ํ์ง์ ๋ชจ๋ํฐ๋งํ ์ ์์ต๋๋ค.
- ๊ฐ์ ๊ถ์ฅ ์ฌํญ: ์ฝ๋ ํ์ง์ ๊ฐ์ ํ๊ธฐ ์ํ ๊ถ์ฅ ์ฌํญ์ ์ ๊ณตํ์ฌ ์ฝ๋ ์ ์ง ๋ณด์๋ฅผ ์ฉ์ดํ๊ฒ ํฉ๋๋ค.
- ์๋ํ๋ ์ํฌํ๋ก์ฐ: ์ฝ๋ ํธ์ ์ ์๋์ผ๋ก ๋น๋, ํ ์คํธ, ๋ฐฐํฌ๋ฅผ ์ํํ์ฌ ๊ฐ๋ฐ ์๋๋ฅผ ๋์ผ ์ ์์ต๋๋ค.
- ์ง์์ ์ธ ๋ฐฐํฌ: ์ง์์ ์ผ๋ก ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ฐฐํฌํ์ฌ ์ฌ์ฉ์์๊ฒ ์ต์ ๊ธฐ๋ฅ์ ๋น ๋ฅด๊ฒ ์ ๊ณตํ ์ ์์ต๋๋ค.
- ๋์ ์ ๋ขฐ์ฑ: ์๋ํ๋ ํ ์คํธ๋ฅผ ํตํด ๋ฐฐํฌ ์ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ ๋ขฐ์ฑ์ ๊ฒ์ฆํ ์ ์์ต๋๋ค.
- ์ด๋ฒ ํฌ์คํ ์์๋ ํ๋ก์ ํธ์ ํ ์คํธ ์ ๋ต๊ณผ ํ์ง ๋ณด์ฆ ๋ฐฉ๋ฒ์ ๋ํด ๋ค๋ฃจ์์ต๋๋ค. ์ ๋ ํ ์คํธ, ํตํฉ ํ ์คํธ, E2E ํ ์คํธ๋ฅผ ํตํด ๋ค์ํ ์์ค์์ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ธฐ๋ฅ์ ๊ฒ์ฆํ๊ณ , ์ฝ๋ ๋ฆฌ๋ทฐ, ์ ์ ์ฝ๋ ๋ถ์, CI/CD ํ์ดํ๋ผ์ธ์ ํตํด ์ฝ๋ ํ์ง์ ์ง์์ ์ผ๋ก ๋ชจ๋ํฐ๋งํ๊ณ ๊ฐ์ ํ ์ ์์์ต๋๋ค. ๋ค์ ํฌ์คํ ์์๋ ํ๋ก์ ํธ์ ์ฑ๋ฅ ์ต์ ํ์ ๊ด๋ จ๋ ๋ด์ฉ์ ๋ค๋ฃจ๊ฒ ์ต๋๋ค.
- CI/CD ํ์ดํ๋ผ์ธ์ ์ฝ๋ ๋ณ๊ฒฝ ์ฌํญ์ ์๋์ผ๋ก ๋น๋, ํ ์คํธ, ๋ฐฐํฌํ๋ ์๋ํ๋ ์ํฌํ๋ก์ฐ์ ๋๋ค. ์ด๋ฒ ํ๋ก์ ํธ์์๋ GitHub Actions๋ฅผ ์ฌ์ฉํ์ฌ CI/CD ํ์ดํ๋ผ์ธ์ ๊ตฌ์ถํ์ต๋๋ค.
- ์ ์ ์ฝ๋ ๋ถ์ ๋๊ตฌ๋ ์ฝ๋์ ์ ์ฌ์ ์ธ ๋ฌธ์ ๋ฅผ ์๋์ผ๋ก ๋ถ์ํ๊ณ ๊ฒฝ๊ณ ๋ฅผ ์ ๊ณตํฉ๋๋ค. ์ด๋ฒ ํ๋ก์ ํธ์์๋ SonarQube๋ฅผ ์ฌ์ฉํ์ฌ ์ฝ๋ ํ์ง์ ์ง์์ ์ผ๋ก ๋ชจ๋ํฐ๋งํ์ต๋๋ค.
- ์ฅ์ :
- 1. ์ฝ๋ ๋ฆฌ๋ทฐ
- ์๋ํฌ์๋(E2E) ํ ์คํธ๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ ์ฒด ํ๋ฆ์ ํ ์คํธํ์ฌ ์ฌ์ฉ์๊ฐ ์ค์ ๋ก ๊ฒฝํํ๋ ์๋๋ฆฌ์ค๋ฅผ ๊ฒ์ฆํ๋ ํ ์คํธ์ ๋๋ค. ์ด ํ๋ก์ ํธ์์๋ Selenium์ ์ฌ์ฉํ์ฌ ์น ์ ํ๋ฆฌ์ผ์ด์ ์ E2E ํ ์คํธ๋ฅผ ์ํํ์ต๋๋ค.
- ํตํฉ ํ ์คํธ๋ ์ฌ๋ฌ ๋ชจ๋์ด ํตํฉ๋ ํ๊ฒฝ์์ ์ํธ ์์ฉ์ด ์ฌ๋ฐ๋ฅด๊ฒ ์ด๋ฃจ์ด์ง๋์ง ๊ฒ์ฆํ๋ ํ ์คํธ์ ๋๋ค. ์ด ํ๋ก์ ํธ์์๋ ์ค์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ํตํฉ ํ ์คํธ๋ฅผ ํตํด ๋ฐ์ดํฐ ํ๋ฆ์ ๊ฒ์ฆํ์ต๋๋ค.
- ์ ๋ ํ ์คํธ๋ ์ฝ๋์ ์์ ๋จ์(์ฃผ๋ก ํจ์๋ ๋ฉ์๋)๋ฅผ ํ ์คํธํ์ฌ ๊ฐ๋ณ ๊ธฐ๋ฅ์ด ์์๋๋ก ๋์ํ๋์ง ํ์ธํ๋ ํ ์คํธ์ ๋๋ค. ์ด๋ฒ ํ๋ก์ ํธ์์๋ xUnit์ ์ฌ์ฉํ์ฌ ์ ๋ ํ ์คํธ๋ฅผ ์์ฑํ๊ณ ์คํํ์ต๋๋ค.
- ํ ์คํธ ์ ๋ต
- 18. ํ๋ก์ ํธ ์ ์ฉ ์ฌ๋ก - Part 15
-
- ํ๋ก์ ํธ ์ ์ฉ ์ฌ๋ก - Part 16: ์ฑ๋ฅ ์ต์ ํ์ ๋ฆฌ์์ค ๊ด๋ฆฌ
- ๋ณ๋ชฉ ์ง์ ์๋ณ: ์ฑ๋ฅ ์ ํ์ ์์ธ์ด ๋๋ ์ฝ๋๋ฅผ ์ฐพ์๋ด๊ณ ์ต์ ํํ ์ ์์ต๋๋ค.
- ๋ฆฌ์์ค ์ฌ์ฉ ๋ถ์: CPU์ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋์ ๋ถ์ํ์ฌ ๋ฆฌ์์ค ๊ด๋ฆฌ์ ๋์์ด ๋ฉ๋๋ค.
- ์ต์ ํ ๋์ ์ ์ : ์ฑ๋ฅ ์ต์ ํ๊ฐ ํ์ํ ๋ถ๋ถ์ ๋ช ํํ ํ์ ํ ์ ์์ต๋๋ค.
- ๋ฉ๋ชจ๋ฆฌ ๋์ ๋ฐฉ์ง: ๋ฉ๋ชจ๋ฆฌ ๋์๋ฅผ ๋ฐฉ์งํ์ฌ ์์คํ ์ ์์ ์ฑ์ ๋์ผ ์ ์์ต๋๋ค.
- ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ ์ต์ ํ: ๋ถํ์ํ ๋ฉ๋ชจ๋ฆฌ ํ ๋น์ ์ค์ฌ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ์ ์ต์ ํํ ์ ์์ต๋๋ค.
- ์์คํ ์์ ์ฑ ํฅ์: ์์ ์ ์ธ ๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ๋ฅผ ํตํด ์์คํ ์ ์ ๋ขฐ์ฑ์ ๋์ผ ์ ์์ต๋๋ค.
- ์ค์๊ฐ ์๋ต์ฑ ํฅ์: ํ์คํฌ์ ์ฐ์ ์์์ ์ธํฐ๋ฝํธ ์ฒ๋ฆฌ ์๊ฐ์ ์ต์ ํํ์ฌ ์ค์๊ฐ ์๋ต์ฑ์ ๋์ผ ์ ์์ต๋๋ค.
- ์์คํ ํจ์จ์ฑ ํฅ์: ์ค์๊ฐ ์ฑ๋ฅ์ ์ต์ ํํ์ฌ ์์คํ ์ ์ ์ฒด์ ์ธ ํจ์จ์ฑ์ ํฅ์ํ ์ ์์ต๋๋ค.
- ์ฌ์ฉ์ ๊ฒฝํ ๊ฐ์ : ๋น ๋ฅด๊ณ ์ผ๊ด๋ ์๋ต์ฑ์ ์ ๊ณตํ์ฌ ์ฌ์ฉ์ ๊ฒฝํ์ ๊ฐ์ ํ ์ ์์ต๋๋ค.
- ๋ฉ๋ชจ๋ฆฌ ์กฐ๊ฐํ ๋ฐฉ์ง: ๋ฉ๋ชจ๋ฆฌ ํ์ ์ฌ์ฉํ์ฌ ๋ฉ๋ชจ๋ฆฌ ์กฐ๊ฐํ๋ฅผ ๋ฐฉ์งํ ์ ์์ต๋๋ค.
- ๋น ๋ฅธ ๋ฉ๋ชจ๋ฆฌ ํ ๋น: ๋ฉ๋ชจ๋ฆฌ ํ์ ์ฌ์ฉํ์ฌ ๋ฉ๋ชจ๋ฆฌ ํ ๋น ์๋๋ฅผ ๋น ๋ฅด๊ฒ ํ ์ ์์ต๋๋ค.
- ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ ํจ์จ ํฅ์: ๋ฉ๋ชจ๋ฆฌ ํ์ ์ฌ์ฉํ์ฌ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ์ ํจ์จ์ฑ์ ๋์ผ ์ ์์ต๋๋ค.
- ์ ๋ ฅ ์๋น ๊ฐ์: ๋ถํ์ํ ์ ๋ ฅ ์๋น๋ฅผ ์ค์ฌ ์์คํ ์ ์ ๋ ฅ ํจ์จ์ฑ์ ๋์ผ ์ ์์ต๋๋ค.
- ๋ฐฐํฐ๋ฆฌ ์๋ช ์ฐ์ฅ: ์ ๋ ฅ ์๋น๋ฅผ ์ค์ฌ ๋ฐฐํฐ๋ฆฌ ์๋ช ์ ์ฐ์ฅํ ์ ์์ต๋๋ค.
- ํ๊ฒฝ ์นํ์ : ์ ๋ ฅ ์๋น๋ฅผ ์ค์ฌ ํ๊ฒฝ์ ๋ฏธ์น๋ ์ํฅ์ ์ต์ํํ ์ ์์ต๋๋ค.
- ์ค์๊ฐ ๋ชจ๋ํฐ๋ง: ์์คํ ์ ๋ฆฌ์์ค ์ฌ์ฉ๋์ ์ค์๊ฐ์ผ๋ก ๋ชจ๋ํฐ๋งํ ์ ์์ต๋๋ค.
- ๋ฌธ์ ์กฐ๊ธฐ ๋ฐ๊ฒฌ: ๋ฆฌ์์ค ์ฌ์ฉ๋์ ๋ชจ๋ํฐ๋งํ์ฌ ๋ฌธ์ ๋ฅผ ์กฐ๊ธฐ์ ๋ฐ๊ฒฌํ๊ณ ํด๊ฒฐํ ์ ์์ต๋๋ค.
- ์ต์ ํ ๊ธฐํ ์ ๊ณต: ๋ฆฌ์์ค ์ฌ์ฉ๋์ ๋ถ์ํ์ฌ ์ต์ ํํ ๊ธฐํ๋ฅผ ์ ๊ณตํ ์ ์์ต๋๋ค.
- ์ด๋ฒ ํฌ์คํ ์์๋ ์๋ฒ ๋๋ ์์คํ ์ ์ฑ๋ฅ ์ต์ ํ์ ๋ฆฌ์์ค ๊ด๋ฆฌ์ ๋ํด ๋ค๋ฃจ์์ต๋๋ค. ์ฝ๋ ํ๋กํ์ผ๋ง, ๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ, ์ค์๊ฐ ์ฒ๋ฆฌ ์ต์ ํ ๋ฑ์ ๋ฐฉ๋ฒ์ ํตํด ์ฑ๋ฅ์ ์ต์ ํํ๊ณ , ๋ฉ๋ชจ๋ฆฌ ํ, ์ ๋ ฅ ๊ด๋ฆฌ, ๋ฆฌ์์ค ๋ชจ๋ํฐ๋ง ๋ฑ์ ํตํด ๋ฆฌ์์ค๋ฅผ ํจ์จ์ ์ผ๋ก ๊ด๋ฆฌํ ์ ์์์ต๋๋ค. ์ด๋ฌํ ์ต์ ํ์ ๊ด๋ฆฌ ๊ธฐ๋ฒ์ ํตํด ์๋ฒ ๋๋ ์์คํ ์ ์์ ์ฑ๊ณผ ์ฑ๋ฅ์ ํฌ๊ฒ ํฅ์์ํฌ ์ ์์์ต๋๋ค. ๋ค์ ํฌ์คํ ์์๋ ๋คํธ์ํฌ ํต์ ๊ณผ ๋ณด์์ ๋ํด ๋ค๋ฃจ๊ฒ ์ต๋๋ค.
- ์ค์๊ฐ์ผ๋ก ์์คํ ์ ๋ฆฌ์์ค ์ฌ์ฉ๋์ ๋ชจ๋ํฐ๋งํ์ฌ ํ์์ ๋ฐ๋ผ ์ต์ ํ ์์ ์ ์ํํ์ต๋๋ค. ์ด๋ฅผ ์ํด ๋ฉ๋ชจ๋ฆฌ, CPU, ๋คํธ์ํฌ ์ฌ์ฉ๋์ ๋ชจ๋ํฐ๋งํ๋ ๋๊ตฌ๋ฅผ ์ฌ์ฉํ์ต๋๋ค.
- ์๋ฒ ๋๋ ์์คํ ์์๋ ์ ๋ ฅ ์๋น๋ฅผ ์ค์ด๋ ๊ฒ์ด ์ค์ํฉ๋๋ค. CPU ํด๋ญ ์๋๋ฅผ ์กฐ์ ํ๊ณ , ํ์ํ์ง ์์ ๋ชจ๋์ ์ ์์ ์ฐจ๋จํ์ฌ ์ ๋ ฅ ์๋น๋ฅผ ์ต์ํํ์ต๋๋ค.
- ์ฅ์ :
- 1. ๋ฉ๋ชจ๋ฆฌ ํ ์ฌ์ฉ
- ์ค์๊ฐ ์ฒ๋ฆฌ๊ฐ ์ค์ํ ์๋ฒ ๋๋ ์์คํ ์์๋ ํ์คํฌ์ ์ฐ์ ์์๋ฅผ ์กฐ์ ํ๊ณ , ์ธํฐ๋ฝํธ ์ฒ๋ฆฌ ์๊ฐ์ ์ต์ ํํ์ฌ ์ค์๊ฐ ์ฑ๋ฅ์ ๋ณด์ฅํด์ผ ํฉ๋๋ค.
- ์๋ฒ ๋๋ ์์คํ ์์๋ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋์ ์ค์ด๋ ๊ฒ์ด ์ค์ํฉ๋๋ค. ๋ฉ๋ชจ๋ฆฌ ๋์์ ๋ถํ์ํ ๋ฉ๋ชจ๋ฆฌ ํ ๋น์ ๋ฐฉ์งํ๊ธฐ ์ํด ๋ค์ํ ๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ ๊ธฐ๋ฒ์ ์ฌ์ฉํ์ต๋๋ค.
- ์ฅ์ :
- 1. ์ฝ๋ ํ๋กํ์ผ๋ง
- ์ด๋ฒ ํฌ์คํ ์์๋ ์๋ฒ ๋๋ ์์คํ ์ ์ฑ๋ฅ ์ต์ ํ์ ๋ฆฌ์์ค ๊ด๋ฆฌ์ ๋ํด ๋ค๋ฃจ๊ฒ ์ต๋๋ค. ์๋ฒ ๋๋ ์์คํ ์ ์ ํ๋ ์์ ๋ด์์ ํจ์จ์ ์ผ๋ก ๋์ํด์ผ ํ๋ฏ๋ก ์ฑ๋ฅ ์ต์ ํ์ ๋ฆฌ์์ค ๊ด๋ฆฌ๋ ๋งค์ฐ ์ค์ํฉ๋๋ค. ํ๋ก์ ํธ๋ฅผ ์งํํ๋ฉฐ ๊ฒช์๋ ์ฑ๋ฅ ๋ฌธ์ ์ ์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํ ๋ฐฉ๋ฒ๋ค์ ๊ณต์ ํ๊ฒ ์ต๋๋ค.
-
- ํ๋ก์ ํธ ์ ์ฉ ์ฌ๋ก - Part 17: ๋คํธ์ํฌ ํต์ ๊ณผ ๋ณด์
- ๋์ญํญ ์ ์ฝ: ๋ฐ์ดํฐ๋ฅผ ์์ถํ์ฌ ์ ์กํ๋ฉด ๋คํธ์ํฌ ๋์ญํญ์ ์ ์ฝํ ์ ์์ต๋๋ค.
- ์ ์ก ์๋ ํฅ์: ์ต์ ํ๋ ํจํท ํฌ๊ธฐ๋ฅผ ์ฌ์ฉํ๋ฉด ๋ฐ์ดํฐ ์ ์ก ์๋๊ฐ ํฅ์๋ฉ๋๋ค.
- ๋คํธ์ํฌ ๋ถํ ๊ฐ์: ํจ์จ์ ์ธ ๋ฐ์ดํฐ ์ ์ก์ผ๋ก ๋คํธ์ํฌ ๋ถํ๋ฅผ ์ค์ผ ์ ์์ต๋๋ค.
- ์ ๋ขฐ์ฑ ํฅ์: TCP/IP ํ๋กํ ์ฝ์ ์ฌ์ฉํ์ฌ ๋ฐ์ดํฐ ์ ์ก์ ์ ๋ขฐ์ฑ์ ๋ณด์ฅํ ์ ์์ต๋๋ค.
- ํจ์จ์ฑ ํฅ์: UDP ํ๋กํ ์ฝ์ ์ฌ์ฉํ์ฌ ์ค์๊ฐ ๋ฐ์ดํฐ ์ ์ก์ ํจ์จ์ฑ์ ๋์ผ ์ ์์ต๋๋ค.
- ๋ค์ํ ์๊ตฌ์ฌํญ ์ถฉ์กฑ: ๋ค์ํ ๋คํธ์ํฌ ํต์ ์๊ตฌ์ฌํญ์ ์ ์ ํ ํ๋กํ ์ฝ๋ก ์ถฉ์กฑํ ์ ์์ต๋๋ค.
- ์์ ์ฑ ํฅ์: ๋คํธ์ํฌ ์ฐ๊ฒฐ ์ํ๋ฅผ ๋ชจ๋ํฐ๋งํ์ฌ ์์ ์ฑ์ ๋์ผ ์ ์์ต๋๋ค.
- ์๋ ์ฌ์ฐ๊ฒฐ: ์ฐ๊ฒฐ์ด ๋์ด์ง๋ฉด ์๋์ผ๋ก ์ฌ์ฐ๊ฒฐํ์ฌ ์๋น์ค ์ค๋จ์ ์ต์ํํ ์ ์์ต๋๋ค.
- ์ฌ์ฉ์ ๊ฒฝํ ๊ฐ์ : ์์ ์ ์ธ ๋คํธ์ํฌ ์ฐ๊ฒฐ์ ์ ์งํ์ฌ ์ฌ์ฉ์ ๊ฒฝํ์ ๊ฐ์ ํ ์ ์์ต๋๋ค.
- ๊ธฐ๋ฐ์ฑ ๋ณด์ฅ: ๋ฐ์ดํฐ๋ฅผ ์ํธํํ์ฌ ๊ธฐ๋ฐ์ฑ์ ๋ณดํธํ ์ ์์ต๋๋ค.
- ๋ณด์ ๊ฐํ: SSL/TLS๋ฅผ ์ฌ์ฉํ์ฌ ๋คํธ์ํฌ ํต์ ์ ๋ณด์์ ๊ฐํํ ์ ์์ต๋๋ค.
- ๋ฐ์ดํฐ ๋ณดํธ: ์ ์ก ์ค์ธ ๋ฐ์ดํฐ๋ฅผ ๋ณดํธํ์ฌ ๋ถ๋ฒ์ ์ธ ์ ๊ทผ์ ๋ฐฉ์งํ ์ ์์ต๋๋ค.
- ์ ๊ทผ ์ ์ด: ์ฌ์ฉ์ ์ธ์ฆ๊ณผ ๊ถํ ๊ด๋ฆฌ๋ก ์ ๊ทผ์ ์ ์ดํ ์ ์์ต๋๋ค.
- ๋ณด์ ๊ฐํ: ๊ถํ์ด ์๋ ์ฌ์ฉ์์ ์ ๊ทผ์ ์ฐจ๋จํ์ฌ ๋ณด์์ ๊ฐํํ ์ ์์ต๋๋ค.
- ์ ์ฐํ ๊ด๋ฆฌ: OAuth์ JWT๋ฅผ ์ฌ์ฉํ์ฌ ์ ์ฐํ๊ฒ ์ธ์ฆ๊ณผ ๊ถํ์ ๊ด๋ฆฌํ ์ ์์ต๋๋ค.
- ์ทจ์ฝ์ ๋ฐ๊ฒฌ: ์ ๊ธฐ์ ์ธ ์ค์บ๋์ผ๋ก ๋ณด์ ์ทจ์ฝ์ ์ ๋ฐ๊ฒฌํ ์ ์์ต๋๋ค.
- ์ ์ํ ํจ์น: ๋ฐ๊ฒฌ๋ ์ทจ์ฝ์ ์ ์ ์ํ๊ฒ ํจ์นํ์ฌ ๋ณด์์ ์ ์งํ ์ ์์ต๋๋ค.
- ๋ณด์ ๊ฐํ: ์ง์์ ์ธ ์ทจ์ฝ์ ๊ด๋ฆฌ๋ฅผ ํตํด ๋ณด์์ ๊ฐํํ ์ ์์ต๋๋ค.
- ์ด๋ฒ ํฌ์คํ ์์๋ ์๋ฒ ๋๋ ์์คํ ์ ๋คํธ์ํฌ ํต์ ๊ณผ ๋ณด์ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํ ๋ฐฉ๋ฒ๋ค์ ๋ค๋ฃจ์์ต๋๋ค. ํจ์จ์ ์ธ ๋ฐ์ดํฐ ์ ์ก, ์ ์ ํ ํ๋กํ ์ฝ ์ ํ, ๋คํธ์ํฌ ์ฐ๊ฒฐ ๊ด๋ฆฌ ๋ฑ์ ๊ธฐ๋ฒ์ ํตํด ๋คํธ์ํฌ ํต์ ์ ์ต์ ํํ ์ ์์์ต๋๋ค. ๋ํ, ๋ฐ์ดํฐ ์ํธํ, ์ธ์ฆ ๋ฐ ๊ถํ ๊ด๋ฆฌ, ์ทจ์ฝ์ ์ค์บ๋ ๋ฑ์ ํตํด ์์คํ ์ ๋ณด์์ ๊ฐํํ ์ ์์์ต๋๋ค. ์ด๋ฌํ ๋ฐฉ๋ฒ๋ค์ ํตํด ์์ ์ ์ด๊ณ ์์ ํ ์๋ฒ ๋๋ ์์คํ ์ ๊ตฌ์ถํ ์ ์์์ต๋๋ค. ๋ค์ ํฌ์คํ ์์๋ ์๋ฒ ๋๋ ์์คํ ์ ํ ์คํธ์ ๋๋ฒ๊น ์ ๋ํด ๋ค๋ฃจ๊ฒ ์ต๋๋ค.
- ์ ๊ธฐ์ ์ผ๋ก ์์คํ ์ ๋ณด์ ์ทจ์ฝ์ ์ ์ค์บ๋ํ๊ณ , ๋ฐ๊ฒฌ๋ ์ทจ์ฝ์ ์ ์ ์ํ๊ฒ ํจ์นํ์ฌ ๋ณด์์ ์ ์งํ์ต๋๋ค. ์ด๋ฅผ ์ํด OWASP ZAP๊ณผ ๊ฐ์ ๋๊ตฌ๋ฅผ ์ฌ์ฉํ์ต๋๋ค.
- ์ฌ์ฉ์ ์ธ์ฆ๊ณผ ๊ถํ ๊ด๋ฆฌ๋ฅผ ํตํด ์์คํ ์ ์ ๊ทผ์ ์ ์ดํ๊ณ , ๊ถํ์ด ์๋ ์ฌ์ฉ์์ ์ ๊ทผ์ ์ฐจ๋จํ์ต๋๋ค. ์ด๋ฅผ ์ํด OAuth์ JWT(Json Web Token)๋ฅผ ์ฌ์ฉํ์ต๋๋ค.
- ์ฅ์ :
- 1. ๋ฐ์ดํฐ ์ํธํ
- ๋คํธ์ํฌ ์ฐ๊ฒฐ์ ์ํ๋ฅผ ์ค์๊ฐ์ผ๋ก ๋ชจ๋ํฐ๋งํ๊ณ , ์ฐ๊ฒฐ์ด ๋์ด์ง๋ฉด ์๋์ผ๋ก ์ฌ์ฐ๊ฒฐํ๋๋ก ์ค์ ํ์ฌ ์์ ์ ์ธ ๋คํธ์ํฌ ํต์ ์ ์ ์งํ์ต๋๋ค.
- ์ ์ ํ ๋คํธ์ํฌ ํ๋กํ ์ฝ์ ์ ํํ์ฌ ๋ฐ์ดํฐ ์ ์ก์ ์ ๋ขฐ์ฑ๊ณผ ํจ์จ์ฑ์ ๋์์ต๋๋ค. ์ด๋ฒ ํ๋ก์ ํธ์์๋ TCP/IP์ UDP๋ฅผ ์ฌ์ฉํ์ฌ ๋ค์ํ ํต์ ์๊ตฌ์ฌํญ์ ์ถฉ์กฑํ์ต๋๋ค.
- ์ฅ์ :
- 1. ํจ์จ์ ์ธ ๋ฐ์ดํฐ ์ ์ก
- ์ด๋ฒ ํฌ์คํ ์์๋ ์๋ฒ ๋๋ ์์คํ ์์ ๋คํธ์ํฌ ํต์ ๊ณผ ๋ณด์ ๋ฌธ์ ๋ฅผ ์ด๋ป๊ฒ ํด๊ฒฐํ๋์ง์ ๋ํด ๋ค๋ฃจ๊ฒ ์ต๋๋ค. ๋คํธ์ํฌ ์ฐ๊ฒฐ์ด ํ์์ ์ธ ์๋ฒ ๋๋ ์์คํ ์์๋ ๋ฐ์ดํฐ ์ ์ก์ ํจ์จ์ฑ๊ณผ ๋ณด์์ด ๋งค์ฐ ์ค์ํฉ๋๋ค. ํ๋ก์ ํธ์์ ์ฌ์ฉํ ๋คํธ์ํฌ ํต์ ๊ธฐ๋ฒ๊ณผ ๋ณด์ ๊ฐํ๋ฅผ ์ํ ๋ฐฉ๋ฒ๋ค์ ๊ณต์ ํ๊ฒ ์ต๋๋ค.
- 21. ํ๋ก์ ํธ ์ ์ฉ ์ฌ๋ก - Part 18: ๊ณ ๊ธ ๋์์ธ ํจํด๊ณผ ํ
์คํธ ๊ธฐ๋ฒ์ด๋ฒ ํฌ์คํ
์์๋ ์๋ฒ ๋๋ ์์คํ
๊ฐ๋ฐ์์ ๊ณ ๊ธ ๋์์ธ ํจํด์ ์ ์ฉํ๊ณ , ์ด๋ฅผ ํ
์คํธํ๋ ๊ธฐ๋ฒ์ ๋ํด ๋ค๋ฃจ๊ฒ ์ต๋๋ค. ์๋ฒ ๋๋ ์์คํ
์์๋ ์ฝ๋์ ์ฌ์ฌ์ฉ์ฑ๊ณผ ์ ์ง๋ณด์์ฑ์ ๋์ด๊ธฐ ์ํด ๋์์ธ ํจํด์ ํจ๊ณผ์ ์ผ๋ก ํ์ฉํ ํ์๊ฐ ์์ต๋๋ค. ๋ํ, ์ ๋ขฐ์ฑ ๋์ ์์คํ
์ ๊ตฌ์ถํ๊ธฐ ์ํด ๋ค์ํ ํ
์คํธ ๊ธฐ๋ฒ์ ์ฌ์ฉํด์ผ ํฉ๋๋ค. ์ด๋ฒ ํฌ์คํ
์์๋ ๋ช ๊ฐ์ง ๋ํ์ ์ธ ๋์์ธ ํจํด๊ณผ ํ
์คํธ ๊ธฐ๋ฒ์ ์ดํด๋ณด๊ฒ ์ต๋๋ค.1. ์ฑ๊ธํค ํจํด์ฅ์ :
- ์์ ์ ์ฝ: ํ๋์ ์ธ์คํด์ค๋ฅผ ๊ณต์ ํ์ฌ ๋ฉ๋ชจ๋ฆฌ์ ์์์ ์ ์ฝํ ์ ์์ต๋๋ค.
- ๊ธ๋ก๋ฒ ์ ๊ทผ: ์ ํ๋ฆฌ์ผ์ด์ ์ด๋์๋ ์ธ์คํด์ค์ ์ ๊ทผํ ์ ์์ต๋๋ค.
- ์ํ ๊ด๋ฆฌ ์ฉ์ด: ์ ์ญ์ ์ธ ์ํ ๊ด๋ฆฌ๋ฅผ ์ฝ๊ฒ ํ ์ ์์ต๋๋ค.
- ์ด๋ฒคํธ ๊ธฐ๋ฐ ์์คํ ๊ตฌํ: ๊ฐ์ฒด ์ํ ๋ณํ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ ์ ์์ต๋๋ค.
- ๋์จํ ๊ฒฐํฉ: ์ต์ ๋ฒ์ ์ฃผ์ฒด ๊ฐ์ ๊ฒฐํฉ๋๋ฅผ ๋ฎ์ถฐ ์ ์ฐ์ฑ์ ๋์ ๋๋ค.
- ํ์ฅ์ฑ: ์๋ก์ด ์ต์ ๋ฒ๋ฅผ ์ฝ๊ฒ ์ถ๊ฐํ ์ ์์ต๋๋ค.
- ๊ฐ์ฒด ์์ฑ ๋ก์ง ์บก์ํ: ๊ฐ์ฒด ์์ฑ ๋ก์ง์ ๋ถ๋ฆฌํ์ฌ ์ฝ๋์ ๊ฐ๋ ์ฑ์ ๋์ ๋๋ค.
- ํ์ฅ์ฑ: ์๋ก์ด ๊ฐ์ฒด ์์ฑ ๋ก์ง์ ์ฝ๊ฒ ์ถ๊ฐํ ์ ์์ต๋๋ค.
- ์ ์ฐ์ฑ: ๊ตฌ์ฒด์ ์ธ ํด๋์ค์ ์ธ์คํด์ค๋ฅผ ์ฝ๋์ ์ง์ ๋ช ์ํ์ง ์๊ณ ์์ฑํ ์ ์์ต๋๋ค.
- ๋ฒ๊ทธ ์กฐ๊ธฐ ๋ฐ๊ฒฌ: ์ฝ๋ ์์ฑ ํ ๋ฐ๋ก ํ ์คํธํ์ฌ ๋ฒ๊ทธ๋ฅผ ์กฐ๊ธฐ์ ๋ฐ๊ฒฌํ ์ ์์ต๋๋ค.
- ๋ฆฌํฉํ ๋ง ์ง์: ๋ฆฌํฉํ ๋ง ์ ํ ์คํธ๋ฅผ ํตํด ์ฝ๋์ ์ ํ์ฑ์ ์ ์งํ ์ ์์ต๋๋ค.
- ์ฝ๋ ํ์ง ํฅ์: ํ ์คํธ ์ฝ๋๋ฅผ ์์ฑํ๋ฉด์ ์ฝ๋์ ํ์ง์ ๋์ผ ์ ์์ต๋๋ค.
- ์์คํ ํตํฉ ๊ฒ์ฆ: ๋ชจ๋ ๊ฐ์ ์ํธ์์ฉ์ ํตํด ์์คํ ํตํฉ์ ๊ฒ์ฆํ ์ ์์ต๋๋ค.
- ์ค์ ์ฌ์ฉ ์๋๋ฆฌ์ค ํ ์คํธ: ์ค์ ์ฌ์ฉ ์๋๋ฆฌ์ค๋ฅผ ๋ฐ์ํ ํ ์คํธ๋ก ์์คํ ์ ์์ ์ฑ์ ๋์ผ ์ ์์ต๋๋ค.
- ์ข ๋จ ๊ฐ ํ ์คํธ: ์ฌ์ฉ์ ๊ด์ ์์ ์์คํ ์ ์ข ๋จ ๊ฐ ๊ธฐ๋ฅ์ ํ ์คํธํ ์ ์์ต๋๋ค.
- ๋ฒ๊ทธ ์๋ฐฉ: ์ฝ๋ ์์ ํ ๋ฐ์ํ ์ ์๋ ๋ฒ๊ทธ๋ฅผ ์๋ฐฉํ ์ ์์ต๋๋ค.
- ๊ธฐ๋ฅ ๊ฒ์ฆ: ๊ธฐ์กด ๊ธฐ๋ฅ์ด ์ ์์ ์ผ๋ก ์๋ํ๋์ง ์ง์์ ์ผ๋ก ๊ฒ์ฆํ ์ ์์ต๋๋ค.
- ํ์ง ์ ์ง: ์ฝ๋ ์์ ํ์๋ ์์คํ ์ ํ์ง์ ์ ์งํ ์ ์์ต๋๋ค.
- ์ด๋ฒ ํฌ์คํ ์์๋ ์๋ฒ ๋๋ ์์คํ ๊ฐ๋ฐ์์ ๊ณ ๊ธ ๋์์ธ ํจํด์ ์ ์ฉํ๊ณ , ์ด๋ฅผ ํ ์คํธํ๋ ๊ธฐ๋ฒ์ ๋ํด ๋ค๋ฃจ์์ต๋๋ค. ์ฑ๊ธํค ํจํด, ์ต์ ๋ฒ ํจํด, ํฉํ ๋ฆฌ ํจํด ๋ฑ์ ํตํด ์ฝ๋์ ์ฌ์ฌ์ฉ์ฑ๊ณผ ์ ์ง๋ณด์์ฑ์ ๋์ผ ์ ์์์ต๋๋ค. ๋ํ, ๋จ์ ํ ์คํธ, ํตํฉ ํ ์คํธ, ํ๊ท ํ ์คํธ ๋ฑ์ ํตํด ์์คํ ์ ์ ๋ขฐ์ฑ์ ๊ฒ์ฆํ ์ ์์์ต๋๋ค. ์ด๋ฌํ ๋ฐฉ๋ฒ๋ค์ ํตํด ์์ ์ ์ด๊ณ ํจ์จ์ ์ธ ์๋ฒ ๋๋ ์์คํ ์ ๊ตฌ์ถํ ์ ์์์ต๋๋ค.
- 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: ๊ณ ๊ธ ๋์์ธ ํจํด๊ณผ ํ ์คํธ ๊ธฐ๋ฒ