< SQLD >

요즘 SQLD 자격증을 취득을 위해 일명 '노랭이'라는 책을 구매해서 공부하고 있다. 취득 후 취득 후기 글을 작성하고 싶다.

9월에 시험이 있는데, 지금부터 배울 겸...? 앉으나 서나 요거를 보고 있다..ㅋㅋ 

 

< ADsP > 

ADsP라는 자격증도 10월에 시험 일정이 있길래 취득하기로 했다. 이것은 누나가 최근에 자격증을 취득한 것인데 추천을 받아 빅데이터 분야도 공부할 겸 취득을 하고 싶어 공부하기로 했다.

< 복습 >

내가 작성한 블로그 글들을 보고 C#, Developer지식 부분을 복습을 하고 있다. 내가 작성해도 정말 잘 작성한 것 같다. 공부가 정말 많이 되는 것 같다. 

< 부상 >

운동은 저번주에 갔다가 어깨 통증이 있었는데, '어깨 충돌 증후군'이라는 진단을 받고 일주일 정도 운동을 쉬었다. 내일부터 다시 운동을 가기로 했는데, 부상 관리를 잘해야겠다는 생각이 들었다.

< 휴식 >

휴가 기간에 내가 가장 좋아하는 그것이 알고 싶다와 꼬꼬무 밀린 것을 보았다.

< 대학 강의 >

컴퓨터공학을 전공하고 있다. 개발자 다운 면모를 갖추기 위해 또 궁금했던 학문을 알고 싶어 수강 신청해 학습하고 있다. 이번 학기에는 인공지능과 사물인터넷(IOT), 컴퓨터구조, 멀티미디어개론, 사회복지학개론, 운영체제, 컴퓨터네트워크 이렇게 8과목을 수강 신청해서 학습하고 있다. 내가 모르는 이론적인 부분뿐만 아니라 실습적인 부분도 알 수 있어서 좋았다. 또 교수님들이 훌륭하시고 교재 부분도 훌륭하게 작성되어서 매우 만족도가 높다. 

< 앞으로의 계획 >

이번 년도는 20대의 마지막인 만큼, 정말 2023년을 죽어도 후회하지 않게 열심히 달려보자라고 다짐했었고. 1초라도 더 공부하려고 최선의 발버둥을 쳤었다. 나 자신에게 목숨을 걸었다고 자신할 만큼 매 순간순간 진지 했었고, 평일은 15~17시간, 주말엔 12시간 이상 꾸준히 공부를 했었다. 그 결과 좋은 결과가 있었다.(공부하다가 쌍코피가 난 것은 생전 처음이었다!!!) 여기서 안주하지 않고, 계속 코딩 공부를 해서 29살을 멋있게, 열정 있게 장식하고 싶고 30대에도 20대 못지않게 열정 있게 꾸준히 정진해서 스페셜 리스트가 되고 싶다. 다만 문자 그대로 목숨을 걸고 공부하고 나고 느낀 것은 자동차도 휴식이 필요하듯, 휴식이 필요하다는 것을 느꼈다. 늘 그랬듯이 가장 중요한 꺾이지 않는 마음을 가지고, 항상 초심을 잃지 않도록 늘 가슴속에 새기고, 나태해지지 말고, 잘했을 때 자만하지 말고, 못했을 때 기죽지 말고, 말보단 행동으로 열심히 하고, 소중한 사람들에게 잘하도록 하자.  

 

< 디컴파일의 개념 >

먼저, 디컴파일을 이해하기 위해서는 컴파일에 대해 알아야 한다. 컴파일이란, 개발자가 작성한 소스 코드(고급언어)를 컴퓨터가 이해할 수 있는 기계어(저급언어)로 변환하는 과정을 의미한다.

그렇다면 디컴파일은 무엇일까? 말 그대로 '디컴파일'은 컴파일의 반대 과정이다. 즉, 컴퓨터가 이해하는 기계어를 다시 사람이 이해할 수 있는 고급언어로 변환하는 과정을 말한다.

< 왜 디컴파일이 필요한가? >

그럼 왜 이런 과정이 필요한 것일까? 주로 다음의 경우에 디컴파일이 사용된다.

소스 코드를 잃어버린 경우: 원본 소스 코드가 없더라도 컴파일된 실행 파일로부터 소스 코드를 복구할 수 있다.

소프트웨어 리버스 엔지니어링: 기계어로 변환된 프로그램을 분석해 그 작동 원리를 파악하거나 보안 취약점을 찾는데 사용된다.

< 실습 >

실습에 앞서, EXE파일,DLL파일의 실 소유자의 동의가 필요하기에 라이센스의 디컴파일 금지조항을 반드시 확인해야 한다. 아니면 불법 복제 및 소스코드 무단 도용 시, 법적 소송이 발생할 수 있기에 반드시 동의를 구하고 디컴파일 해야한다.

해당 실습에서는 코드를 블러 처리 한다.

 

우선 새로운 프로젝트를 하나 만들도록 한다. 

참조 추가를 해서, 원하는 exe,DLL파일을 추가한다.

찾아보기 탭에서 원하는 exe,dll파일을 가져온다음, 확인을 누른다.

이후, 도구 -> 옵션 -> 텍스트 편집기 -> 고급 탭에서  디 컴파일된 원본으로의 이동을 사용하도록 설정을 체크 해준다.

기존 Form1에서 해당 디컴파일된 코드이름을 불러와 정의로 이동을 한다. 예 : 디컴파일된 이름이 Hello이면

.하면 여러 클래스가 보일 것인데, 거기서 특정 불러오고 싶은 클래스를 가져온 다음, 정의로 이동을 하면 된다. 

정의로 이동을 할 때, 아래와 같이 창이 뜬다면 동의를 받고 확인을 누르자.

그럼 참조 추가한 파일의 원본 코드가 보일 것이다. 다만 cs파일의 소스코드와 디자인파일의 소스코드가 혼합되어 있을 수 있기에 분리작업을 통하여 코드를 완성해내면 된다.

 

이후 실행을 하면, exe파일의 원본 실행을 알 수 있다.

 

일주일간의 휴식을 가지면서, 하고싶은 프로젝트가 있어서 만들어 보았다.

< 프로젝트 목표 및 개념 >

프로젝트를 계획하면서는 제조 공정에서 발생하는 기업의 다양한 요구사항을 반영하려 노력했다. 스스로 다른 기업에서 생성 관리를 도와주는 앱을 쓴다면 어떤 기능들이 필요할까 고민했고 정보가 없었으나 편의점 아르바이트를 하면서 물류를 받고 정리할 때를 생각하며 구상을 하니,  우선 생산 계획 플랜이 있어야 할 것이며, 지시가 있어야 만들어 질 것이고, 제품 정보가 있어야 할 것이며, 생산 결과가 있어야 하며,이후 그것을 추적할 수 있어야 한다.

그렇게 정리를 했고 제조업체의 생산관리를 도와주는 웹 애플리케이션을 만들고 싶었고, ASP.NET Core(NET6.0)를 사용해 개발하였다.



우선 모델은 다음과 같다.

ProductionPlan: 생산 계획을 나타내는 모델이다. 특정 제품에 대한 계획된 생산 일정과 수량 등을 관리한다.

namespace SimpleMES.Models
{
    public class ProductionPlan
    {
        public int ProductionPlanId { get; set; }
        public DateTime PlanDate { get; set; }
        public int Quantity { get; set; }
        public int ProductId { get; set; }
        public Product Product { get; set; }
    }
}

 

Product: 제품에 대한 정보를 나타내는 모델이다. 제품의 ID, 이름, 코드, 설명, 단위 가격 등이 포함되어 있다.

namespace SimpleMES.Models
{
    public class Product
    {
        public int ProductId { get; set; }
        public string ProductName { get; set; }
        public string ProductCode { get; set; }
        public string Description { get; set; }
        public decimal UnitPrice { get; set; }
        public ICollection<ProductionPlan> ProductionPlans { get; set; }
        public ICollection<WorkOrder> WorkOrders { get; set; }
        public ICollection<ProductionResult> ProductionResults { get; set; }
    }
}

 

WorkOrder: 작업 지시를 나타내는 모델이다. 특정 제품에 대한 작업 지시 내용을 관리한다.

namespace SimpleMES.Models
{
    public class WorkOrder
    {
        public int WorkOrderId { get; set; }
        public DateTime OrderDate { get; set; }
        public int OrderQuantity { get; set; }
        public int ProductId { get; set; }
        public Product Product { get; set; }
    }
}

ProductionResult: 생산 결과를 나타내는 모델이다. 특정 제품에 대한 실제 생산 결과를 관리한다.

namespace SimpleMES.Models
{
    public class ProductionResult
    {
        public int ProductionResultId { get; set; }
        public DateTime ResultDate { get; set; }
        public int ProducedQuantity { get; set; }
        public int ProductId { get; set; }
        public Product Product { get; set; }
    }
}

 

LotTracking: 제품 묶음을 추적하는 모델이다. 특정 제품의 묶음을 추적할 수 있게 한다.

namespace SimpleMES.Models
{
    public class LotTracking
    {
        public int LotTrackingId { get; set; }
        public string LotCode { get; set; }
        public DateTime LotDate { get; set; }
        public int ProductId { get; set; }
        public Product Product { get; set; }
        public ICollection<ProductionResult> ProductionResults { get; set; }
    }
}


그리고 이 모델들은 ProductionDbContext 클래스에서 관리된다. 이 클래스는 DbContext를 상속받아  데이터베이스 연결 및 쿼리를 처리한다.

using Microsoft.EntityFrameworkCore;

namespace SimpleMES.Models
{
    public class ProductionDbContext : DbContext
    {
        public ProductionDbContext(DbContextOptions<ProductionDbContext> options)
            : base(options)
        {
        }

        public DbSet<Product> Products { get; set; }
        public DbSet<ProductionPlan> ProductionPlans { get; set; }
        public DbSet<WorkOrder> WorkOrders { get; set; }
        public DbSet<ProductionResult> ProductionResults { get; set; }
        public DbSet<LotTracking> LotTrackings { get; set; }
    }
}

 

< 유효성 검사를 위한 Helper 클래스 >

ValidationHelper라는 정적 클래스를 생성했다. 이 클래스에서는 위의 모델의 속성들에 대한 유효성 검사 메서드를 제공한다.

 

 

using SimpleMES.Models;

public static class ValidationHelper
{
    // 제품 유효성 검사 함수
    public static bool ValidateProduct(Product product)
    {
        // 제품 이름이 없는 경우 false 반환
        if (string.IsNullOrEmpty(product.ProductName))
        {
            return false;
        }

        // 제품 코드가 없는 경우 false 반환
        if (string.IsNullOrEmpty(product.ProductCode))
        {
            return false;
        }

        // 제품 단위 가격이 0 이하인 경우 false 반환
        if (product.UnitPrice <= 0)
        {
            return false;
        }

        // 모든 검사를 통과한 경우 true 반환
        return true;
    }

    // 로트 트래킹 유효성 검사 함수
    public static bool ValidateLotTracking(LotTracking lotTracking)
    {
        // 로트 코드가 없는 경우 false 반환
        if (string.IsNullOrEmpty(lotTracking.LotCode))
        {
            return false;
        }

        // 로트 날짜가 없거나 미래의 날짜인 경우 false 반환
        if (lotTracking.LotDate == null || lotTracking.LotDate > DateTime.Now)
        {
            return false;
        }

        // 모든 검사를 통과한 경우 true 반환
        return true;
    }

    // 생산 계획 유효성 검사 함수
    public static bool ValidateProductionPlan(ProductionPlan productionPlan)
    {
        // 계획 날짜가 없거나 미래의 날짜인 경우 false 반환
        if (productionPlan.PlanDate == null || productionPlan.PlanDate > DateTime.Now)
        {
            return false;
        }

        // 계획된 수량이 0 이하인 경우 false 반환
        if (productionPlan.Quantity <= 0)
        {
            return false;
        }

        // 모든 검사를 통과한 경우 true 반환
        return true;
    }

    // 생산 결과 유효성 검사 함수
    public static bool ValidateProductionResult(ProductionResult productionResult)
    {
        // 결과 날짜가 없거나 미래의 날짜인 경우 false 반환
        if (productionResult.ResultDate == null || productionResult.ResultDate > DateTime.Now)
        {
            return false;
        }

        // 생산된 수량이 0 이하인 경우 false 반환
        if (productionResult.ProducedQuantity <= 0)
        {
            return false;
        }

        // 모든 검사를 통과한 경우 true 반환
        return true;
    }

    // 작업 지시 유효성 검사 함수
    public static bool ValidateWorkOrder(WorkOrder workOrder)
    {
        // 주문 날짜가 없거나 미래의 날짜인 경우 false 반환
        if (workOrder.OrderDate == null || workOrder.OrderDate > DateTime.Now)
        {
            return false;
        }

        // 주문 수량이 0 이하인 경우 false 반환
        if (workOrder.OrderQuantity <= 0)
        {
            return false;
        }

        // 모든 검사를 통과한 경우 true 반환
        return true;
    }
}


ValidateProduct: 제품 이름과 제품 코드가 null이 아니며, 단위 가격이 0 이상인지 확인한더.

ValidateLotTracking: Lot 코드가 null이 아니며, Lot 날짜가 현재 시간 이전인지 확인한다.

ValidateProductionPlan: 계획 날짜가 현재 시간 이전이며, 수량이 0 이상인지 확인한다.

ValidateProductionResult: 결과 날짜가 현재 시간 이전이며, 생산 수량이 0 이상인지 확인한다.

ValidateWorkOrder: 주문 날짜가 현재 시간 이전이며, 주문 수량이 0 이상인지 확인한다.

이렇게 각각의 유효성 검사 메서드를 사용하면, 사용자로부터 받은 데이터가 요구사항을 충족하는지 확인할 수 있다.


< 데이터베이스 연결 설정 >

데이터베이스 연결 설정은 appsettings.json 파일에 저장되어 있다. "DefaultConnection" 문자열을 통해 데이터베이스 서버의 위치, 데이터베이스 이름, 그리고 연결 옵션들을 설정하였다. 이 설정은 ProductionDbContext에서 사용되며, 애플리케이션과 데이터베이스 사이의 연결을 관리한다.

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "DefaultConnection": "Server=LAPTOP-G9PM4O7F\\SQLEXPRESS;Database=ProductionDb;Trusted_Connection=True;MultipleActiveResultSets=true"
  }
}

< Repository >

먼저, Repository 클래스는 데이터베이스와의 상호작용을 추상화하는 역할을 한다. 

5개의 Repository 클래스는 각각의 Repository 클래스는 모델에 대한 CRUD(Create, Read, Update, Delete) 연산을 제공한다. 

[LotTrackingRepository]
LotTrackingRepository는 LotTracking 모델에 대한 데이터 액세스를 제공한다. GetAll, GetById, Add, Update, Delete 이렇게 5개의 메서드를 포함하고 있다.

using SimpleMES.Models;

public class LotTrackingRepository
{
    private readonly ProductionDbContext _context;

    public LotTrackingRepository(ProductionDbContext context)
    {
        _context = context;
    }

    public IEnumerable<LotTracking> GetAll()
    {
        return _context.LotTrackings.ToList();
    }

    public LotTracking GetById(int id)
    {
        return _context.LotTrackings.Find(id);
    }

    public void Add(LotTracking lotTracking)
    {
        _context.LotTrackings.Add(lotTracking);
        _context.SaveChanges();
    }

    public void Update(LotTracking lotTracking)
    {
        _context.LotTrackings.Update(lotTracking);
        _context.SaveChanges();
    }

    public void Delete(int id)
    {
        var lotTracking = _context.LotTrackings.Find(id);
        if (lotTracking != null)
        {
            _context.LotTrackings.Remove(lotTracking);
            _context.SaveChanges();
        }
    }
}

[ProductionPlanRepository]
ProductionPlanRepository는 ProductionPlan 모델에 대한 데이터 액세스를 제공한다. LotTrackingRepository와 마찬가지로 5개의 메서드를 포함하고 있다.

using SimpleMES.Models;

public class ProductionPlanRepository
{
    private readonly ProductionDbContext _context;

    public ProductionPlanRepository(ProductionDbContext context)
    {
        _context = context;
    }

    public IEnumerable<ProductionPlan> GetAll()
    {
        return _context.ProductionPlans.ToList();
    }

    public ProductionPlan GetById(int id)
    {
        return _context.ProductionPlans.Find(id);
    }

    public void Add(ProductionPlan productionPlan)
    {
        _context.ProductionPlans.Add(productionPlan);
        _context.SaveChanges();
    }

    public void Update(ProductionPlan productionPlan)
    {
        _context.ProductionPlans.Update(productionPlan);
        _context.SaveChanges();
    }

    public void Delete(int id)
    {
        var productionPlan = _context.ProductionPlans.Find(id);
        if (productionPlan != null)
        {
            _context.ProductionPlans.Remove(productionPlan);
            _context.SaveChanges();
        }
    }
}

[ProductionResultRepository]
ProductionResultRepository는 ProductionResult 모델에 대한 데이터 액세스를 제공한다.

using SimpleMES.Models;

public class ProductionResultRepository
{
    private readonly ProductionDbContext _context;

    public ProductionResultRepository(ProductionDbContext context)
    {
        _context = context;
    }

    public IEnumerable<ProductionResult> GetAll()
    {
        return _context.ProductionResults.ToList();
    }

    public ProductionResult GetById(int id)
    {
        return _context.ProductionResults.Find(id);
    }

    public void Add(ProductionResult productionResult)
    {
        _context.ProductionResults.Add(productionResult);
        _context.SaveChanges();
    }

    public void Update(ProductionResult productionResult)
    {
        _context.ProductionResults.Update(productionResult);
        _context.SaveChanges();
    }

    public void Delete(int id)
    {
        var productionResult = _context.ProductionResults.Find(id);
        if (productionResult != null)
        {
            _context.ProductionResults.Remove(productionResult);
            _context.SaveChanges();
        }
    }
}

[ProductRepository]

using SimpleMES.Models;

public class ProductRepository
{
    private readonly ProductionDbContext _context;

    public ProductRepository(ProductionDbContext context)
    {
        _context = context;
    }

    public IEnumerable<Product> GetAll()
    {
        return _context.Products.ToList();
    }

    public Product GetById(int id)
    {
        return _context.Products.Find(id);
    }

    public void Add(Product product)
    {
        _context.Products.Add(product);
        _context.SaveChanges();
    }

    public void Update(Product product)
    {
        _context.Products.Update(product);
        _context.SaveChanges();
    }

    public void Delete(int id)
    {
        var product = _context.Products.Find(id);
        if (product != null)
        {
            _context.Products.Remove(product);
            _context.SaveChanges();
        }
    }
}

[WorkOrderRepository]

using SimpleMES.Models;

public class WorkOrderRepository
{
    private readonly ProductionDbContext _context;

    public WorkOrderRepository(ProductionDbContext context)
    {
        _context = context;
    }

    public IEnumerable<WorkOrder> GetAll()
    {
        return _context.WorkOrders.ToList();
    }

    public WorkOrder GetById(int id)
    {
        return _context.WorkOrders.Find(id);
    }

    public void Add(WorkOrder workOrder)
    {
        _context.WorkOrders.Add(workOrder);
        _context.SaveChanges();
    }

    public void Update(WorkOrder workOrder)
    {
        _context.WorkOrders.Update(workOrder);
        _context.SaveChanges();
    }

    public void Delete(int id)
    {
        var workOrder = _context.WorkOrders.Find(id);
        if (workOrder != null)
        {
            _context.WorkOrders.Remove(workOrder);
            _context.SaveChanges();
        }
    }
}

< Service >

Service 클래스는 애플리케이션의 비즈니스 로직을 포함하고 있다. 이 클래스는 Repository 클래스를 통해 데이터에 액세스하고, 비즈니스 규칙을 적용하여 애플리케이션의 핵심 기능을 제공한다.
5개 모델에 대한 Service 클래스를 구현하였다. 각각의 Service 클래스는 모델에 대한 비즈니스 로직을 제공한다.

 

[LotTrackingService]

public class LotTrackingService
{
    //데이터베이스와의 통신을 담당
    private readonly LotTrackingRepository _repository;

    public LotTrackingService(LotTrackingRepository repository)
    {
        _repository = repository;
    }

    // GetAll : 모든 LotTracking 객체를 조회하여 반환
    public IEnumerable<LotTracking> GetAll()
    {
        return _repository.GetAll();
    }

    // GetById : 메서드는 주어진 id에 해당하는 LotTracking 객체를 조회하여 반환
    public LotTracking GetById(int id)
    {
        return _repository.GetById(id);
    }

    // 유효성 검사를 먼저, 이를 통과한 경우에만 _repository의 Add를 호출하여 객체를 추가
    public void Add(LotTracking lotTracking)
    {
        if (!ValidationHelper.ValidateLotTracking(lotTracking))
        {
            throw new ArgumentException("Invalid lot tracking data");
        }

        _repository.Add(lotTracking);
    }

    // 유효성 검사를 먼저, 이를 통과한 경우에만 _repository의 Update를 호출하여 객체를 업데이트
    public void Update(LotTracking lotTracking)
    {
        if (!ValidationHelper.ValidateLotTracking(lotTracking))
        {
            throw new ArgumentException("Invalid lot tracking data");
        }

        _repository.Update(lotTracking);
    }

    //주어진 id에 해당하는 LotTracking 객체삭제
    public void Delete(int id)
    {
        _repository.Delete(id);
    }
}

[ProductionPlanService]

using SimpleMES.Models;

public class ProductionPlanService
{
    private readonly ProductionPlanRepository _repository;

    public ProductionPlanService(ProductionPlanRepository repository)
    {
        _repository = repository;
    }

    public IEnumerable<ProductionPlan> GetAll()
    {
        return _repository.GetAll();
    }

    public ProductionPlan GetById(int id)
    {
        return _repository.GetById(id);
    }

    public void Add(ProductionPlan productionPlan)
    {
        if (!ValidationHelper.ValidateProductionPlan(productionPlan))
        {
            throw new ArgumentException("Invalid production plan data");
        }

        _repository.Add(productionPlan);
    }

    public void Update(ProductionPlan productionPlan)
    {
        if (!ValidationHelper.ValidateProductionPlan(productionPlan))
        {
            throw new ArgumentException("Invalid production plan data");
        }

        _repository.Update(productionPlan);
    }

    public void Delete(int id)
    {
        _repository.Delete(id);
    }
}

[ProductionResultService]

using SimpleMES.Models;

public class ProductionResultService
{
    private readonly ProductionResultRepository _repository;

    public ProductionResultService(ProductionResultRepository repository)
    {
        _repository = repository;
    }

    public IEnumerable<ProductionResult> GetAll()
    {
        return _repository.GetAll();
    }

    public ProductionResult GetById(int id)
    {
        return _repository.GetById(id);
    }

    public void Add(ProductionResult productionResult)
    {
        if (!ValidationHelper.ValidateProductionResult(productionResult))
        {
            throw new ArgumentException("Invalid production result data");
        }

        _repository.Add(productionResult);
    }

    public void Update(ProductionResult productionResult)
    {
        if (!ValidationHelper.ValidateProductionResult(productionResult))
        {
            throw new ArgumentException("Invalid production result data");
        }

        _repository.Update(productionResult);
    }

    public void Delete(int id)
    {
        _repository.Delete(id);
    }
}

[ProductService]

using SimpleMES.Models;

public class ProductService
{
    private readonly ProductRepository _repository;

    public ProductService(ProductRepository repository)
    {
        _repository = repository;
    }

    public IEnumerable<Product> GetAll()
    {
        return _repository.GetAll();
    }

    public Product GetById(int id)
    {
        return _repository.GetById(id);
    }

    public void Add(Product product)
    {
        if (!ValidationHelper.ValidateProduct(product))
        {
            throw new ArgumentException("Invalid product data");
        }

        _repository.Add(product);
    }

    public void Update(Product product)
    {
        if (!ValidationHelper.ValidateProduct(product))
        {
            throw new ArgumentException("Invalid product data");
        }

        _repository.Update(product);
    }

    public void Delete(int id)
    {
        _repository.Delete(id);
    }
}

[WorkOrderService]

using SimpleMES.Models;

public class WorkOrderService
{
    private readonly WorkOrderRepository _repository;

    public WorkOrderService(WorkOrderRepository repository)
    {
        _repository = repository;
    }

    public IEnumerable<WorkOrder> GetAll()
    {
        return _repository.GetAll();
    }

    public WorkOrder GetById(int id)
    {
        return _repository.GetById(id);
    }

    public void Add(WorkOrder workOrder)
    {
        if (!ValidationHelper.ValidateWorkOrder(workOrder))
        {
            throw new ArgumentException("Invalid work order data");
        }

        _repository.Add(workOrder);
    }

    public void Update(WorkOrder workOrder)
    {
        if (!ValidationHelper.ValidateWorkOrder(workOrder))
        {
            throw new ArgumentException("Invalid work order data");
        }

        _repository.Update(workOrder);
    }

    public void Delete(int id)
    {
        _repository.Delete(id);
    }
}

< Controller >

// ProductsController.cs
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using SimpleMES.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace SimpleMES.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    public class ProductsController : ControllerBase
    {
        private readonly ProductionDbContext _context;

        public ProductsController(ProductionDbContext context)
        {
            _context = context;
        }
  // [HttpGet] 어트리뷰트를 사용하여 이 메서드를 GET 요청에 바인딩
    // 모든 Product 객체를 비동기로 조회하여 반환
        [HttpGet]
        public async Task<ActionResult<IEnumerable<Product>>> GetProducts()
        {
            try
            {
                return await _context.Products.ToListAsync();
            }
            catch (Exception ex)
            {
                return StatusCode(500, $"Error fetching products: {ex.Message}");
            }
        }


        [HttpGet("{id}")]
        public async Task<ActionResult<Product>> GetProduct(int id)
        {
            var product = await _context.Products.FindAsync(id);

            if (product == null)
            {
                return NotFound();
            }

            return product;
        }
 // [Authorize] : 이 엔드포인트가 인증된 사용자만에게 허용.
        [Authorize]
        [HttpPut("{id}")]
        public async Task<IActionResult> UpdateProduct(int id, [FromBody] Product product)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }

            if (id != product.ProductId)
            {
                return BadRequest();
            }

            _context.Entry(product).State = EntityState.Modified;

            try
            {
                await _context.SaveChangesAsync();
            }
            catch (DbUpdateConcurrencyException)
            {
                if (!ProductExists(id))
                {
                    return NotFound();
                }
                else
                {
                    throw;
                }
            }

            return NoContent();
        }

        [Authorize]
        [HttpPost]
        public async Task<ActionResult<Product>> AddProduct([FromBody] Product product)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(ModelState);
            }

            try
            {
                _context.Products.Add(product);
                await _context.SaveChangesAsync();

                return CreatedAtAction(nameof(GetProduct), new { id = product.ProductId }, product);
            }
            catch (Exception ex)
            {
                return StatusCode(500, $"Error adding product: {ex.Message}");
            }
        }

        [Authorize]
        [HttpDelete("{id}")]
        public async Task<IActionResult> DeleteProduct(int id)
        {
            var product = await _context.Products.FindAsync(id);
            if (product == null)
            {
                return NotFound();
            }

            _context.Products.Remove(product);
            await _context.SaveChangesAsync();

            return NoContent();
        }

        private bool ProductExists(int id)
        {
            return _context.Products.Any(e => e.ProductId == id);
        }
    }
}

< Startup.cs >

// Startup.cs
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using SimpleMES.Models;

namespace SimpleMES
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext<ProductionDbContext>(options =>
                options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))
            );

            services.AddCors(options =>
            {
                options.AddDefaultPolicy(builder =>
                {
                    builder.AllowAnyOrigin()
                           .AllowAnyHeader()
                           .AllowAnyMethod();
                });
            });

            services.AddControllers();
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseHttpsRedirection();
            app.UseRouting();

            app.UseCors();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
    }
}

 

 

이제 여기서 React 코드를 만들어서 상호작용하는 코드를 완성했다. Axios를 사용해서 하고 있는데 잘 해결이 안되고 있다. 코드는 잘 짰는데 localhost 3000 port와 7223port cros 해결이 잘 안되는 것 같고.. 뭔가 Version 호환성이 안맞는 것 같다. 계속 찾아보고 있는데 문제 해결이 된다면 좋을 것 같다.

서브네팅은 이름 그대로 '서브넷(subnet)'을 만드는 과정이다. 그렇다면, 서브넷이 무엇인지 알아봅자. '서브넷'은 '네트워크의 작은 부분'을 의미한다. 여기서 '네트워크'란 '여러 컴퓨터가 연결된 것'을 의미한다. 즉, 서브네팅은 큰 네트워크를 여러 개의 작은 네트워크로 분할하는 것을 말한다.

< 왜 서브네팅을 해야 할까? >

 왜 ,네트워크를 나누려고 할까요? 이것을 이해하려면, 큰 아파트 단지에 살고 있다고 가정해보자. 아파트 단지 전체가 하나의 큰 '네트워크'라고 할 수 있다. 이 아파트 단지는 수천 개의 가구와 사람들로 이루어져 있을 수 있다. 우체부가 우편을 배달할 때, 아파트 단지 전체를 하나의 큰 네트워크로 보고 모든 문을 하나씩 찾아다니면서 우편물을 배달하려고 하면 어떨까? 그렇게 하면 효율성이 떨어지고 시간이 많이 걸릴 것이다.

이 문제를 해결하기 위해 아파트 단지는 여러 개의 빌딩으로 나누어져 있다. 각 빌딩에는 고유한 번호가 있고, 각 빌딩 안에는 여러 개의 가구(몇 호)가 있다. 이제 우체부는 '빌딩 번호와  가구'를 이용해 효율적으로 우편물을 배달할 수 있다. 이렇게 큰 아파트 단지를 여러 개의 빌딩으로 나누는 것은 '서브네팅'에 비유할 수 있다.

 

[IP 주소와 서브넷 마스크]
이제 네트워크의 기본적인 구조를 이해했으니, IP 주소와 서브넷 마스크에 대해 알아보겠다.

IP 주소는 인터넷에 연결된 모든 장치에 할당된 고유한 식별자다. 집 주소 처럼, 컴퓨터나 스마트폰 등의 장치도 IP 주소를 가지고 있다. 이 IP 주소를 통해 데이터가 올바른 위치로 전송된다.

서브넷 마스크는 IP 주소를 네트워크 주소와 호스트 주소로 분리하는 데 사용된다. '네트워크 주소'는  아파트 단지 또는 빌딩 번호에 해당하며, '호스트 주소'는 각각의 가구 번호에 해당한다. 서브넷 마스크는 이 두 부분을 구분하는 데 도움을 준다.

< 서브네팅 방법 >

서브네팅을 하는 방법은 다양하다. IP 주소와 서브넷 마스크를 이해하면, 그런 방법을 쉽게 이해할 수 있다.

먼저 IP 주소를 취한다. 예를 들어, '192.168.1.1'이라는 IP 주소를 가진 컴퓨터가 있다고 가정해보자.

이제 서브넷 마스크를 적용한다. 일반적으로 가장 자주 사용되는 서브넷 마스크는 '255.255.255.0'다.

이 서브넷 마스크를 적용하면, IP 주소의 '192.168.1' 부분이 네트워크 주소가 되고, '.1' 부분이 호스트 주소가 된다.

이제 이 네트워크에서 추가로 분할을 하고 싶다면, 서브넷 마스크를 변경하여 분할을 할 수 있다. 예를 들어, 서브넷 마스크를 '255.255.255.128'로 변경하면, '192.168.1.0'부터 '192.168.1.127'까지의 주소와 '192.168.1.128'부터 '192.168.1.255'까지의 주소로 네트워크를 두 개로 분할할 수 있다.

< 서브네팅의 이점 >

서브네팅은 네트워크 관리를 더 효율적으로 하고, 네트워크 트래픽을 줄이며, 보안을 강화하는 등 많은 이점이 있다.

효율적인 네트워크 관리: 서브네팅을 통해 네트워크를 여러 개의 작은 부분으로 나눌 수 있으므로, 네트워크의 관리가 더욱 효율적이게 된다. 위에서 언급한 아파트 단지의 예를 생각해보자. 아파트 단지를 여러 개의 빌딩으로 나눔으로써, 각 빌딩의 관리가 더욱 용이해졌을 것이다.

네트워크 트래픽 감소: 서브네팅은 네트워크의 트래픽을 효과적으로 분산시킨다. 큰 네트워크에서는 많은 데이터가 서로를 통과하므로, 트래픽이 많이 발생하게 된다. 하지만 서브네팅을 통해 네트워크를 여러 부분으로 나누면, 각 부분에서 발생하는 트래픽을 별도로 관리할 수 있게 된다.

보안 강화 : 서브네팅은 네트워크의 보안도 강화시킨다. 네트워크가 여러 부분으로 나뉘면, 한 부분에서 문제가 발생해도 다른 부분으로 쉽게 퍼져나가지 않는다. 이는 바이러스, 해킹 등의 위협으로부터 네트워크를 보호하는 데 도움이 된다.

'Network' 카테고리의 다른 글

인터네트워킹(InterNetworking)  (0) 2023.06.21
LAN,MAN,WAN  (0) 2023.06.20
데이터 통신 시스템  (0) 2023.06.20
컴퓨터 네트워크란?  (0) 2023.06.20
2.네트워크 구성  (0) 2023.05.12

[Flags]는 특별한 목적을 위해 사용되는 특성(attribute)이다. 주로 enum 형식을 비트 연산을 위한 플래그 집합으로 사용할 때 붙인다. 이를 통해 여러 가지 상태를 한 변수에 저장하고, 비트 단위의 연산을 수행할 수 있다.

< [Flags]의 정의와 사용 시기 >

[Flags] 속성은 enum(열거형)에 대해 선언되며, 각각의 값이 비트 수준에서 조합될 수 있음을 나타낸다. 이는 주로 '플래그(flag)' 역할을 하는 enum을 정의할 때 사용된다. 이렇게 선언된 enum 값은 비트 OR 연산자(|)를 이용해 합쳐지거나, 비트 AND 연산자(&)를 통해 특정 플래그가 활성화되었는지 확인할 수 있다.

[Flags]
public enum MyFlags
{
    None = 0,   // 0000
    Flag1 = 1,  // 0001
    Flag2 = 2,  // 0010
    Flag3 = 4,  // 0100
    Flag4 = 8   // 1000
}

 

각 플래그는 2의 거듭제곱을 사용하므로, 각 플래그가 고유한 비트를 차지한다. 이렇게 설정함으로써 각 플래그를 조합하여 사용할 수 있다.
[[Flags]의 사용 예]

MyFlags activeFlags = MyFlags.Flag1 | MyFlags.Flag3;

// Flag1 and Flag3 are active
Console.WriteLine(activeFlags.HasFlag(MyFlags.Flag1)); // prints True
Console.WriteLine(activeFlags.HasFlag(MyFlags.Flag3)); // prints True

// Flag2 and Flag4 are not active
Console.WriteLine(activeFlags.HasFlag(MyFlags.Flag2)); // prints False
Console.WriteLine(activeFlags.HasFlag(MyFlags.Flag4)); // prints False

 

위 코드에서는 MyFlags.Flag1와 MyFlags.Flag3를 OR 연산을 통해 activeFlags에 저장한다. 이후 HasFlag 메소드를 통해 각 플래그가 활성화되었는지를 확인할 수 있다.

< [Flags] 사용 시 주의사항 >

[Flags]를 사용할 때는 두 가지 주요한 주의사항이 있다.
플래그의 값이 2의 거듭제곱이어야 한다. 이렇게 설정해야 각 플래그가 고유한 비트를 차지할 수 있다.
None 값은 항상 0을 사용해야 한다. 이는 아무런 플래그도 설정되지 않았음을 나타내는 값으로, 해당 플래그를 모두 클리어하는데 사용된다.

+ Recent posts