< SQLD >

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

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

 

< ADsP > 

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

< 복습 >

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

< 부상 >

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

< 휴식 >

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

< 대학 강의 >

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

< 앞으로의 계획 >

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

 

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

< 프로젝트 목표 및 개념 >

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

그렇게 정리를 했고 제조업체의 생산관리를 도와주는 웹 애플리케이션을 만들고 싶었고, 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 호환성이 안맞는 것 같다. 계속 찾아보고 있는데 문제 해결이 된다면 좋을 것 같다.

재밌어 보여서 구매했다. 당분간 닭가슴살 1개씩만 먹어야겠다.... 돈이 아까워서라도 꼭 완독 해야겠다.. 내돈 내산이 아프지만 효과는 제일 큰 것 같다....😭

'일상' 카테고리의 다른 글

근황  (0) 2023.07.12
2023 07/01~2023 07/07 MES Project  (0) 2023.07.07
AWS 16만원 청구...  (0) 2023.06.15
Slid Application 사용 리뷰 및 인터뷰  (0) 2023.06.15

FastCampus의 AWS 강의를 열심히 수강했다. 강의에서는 다양한 AWS 서비스를 활용하는 방법에 대해 배웠고, 취미로 하고 있는 프로젝트에 적용을 해보고자 했었다. 강의가 모두 끝난 후, AWS의 모든 서비스를 정확하게 비활성화했다고 생각했다. 그런데, 계정에 16만원의 Billing Charge가 있었다.

[요금 청구 사유]

청구 내역을 살펴보니, 사용하지 않았다고 생각한 서비스에 대한 비용이 청구되었다. 먼저 AWS의 공식 문서를 참고하여 청구 관련 정책을 확인했다. 또한, Google에서 비슷한 사례를 검색해보았다, 다행히 나와 비슷한 사례가 많았다.

특히, https://velog.io/@kimphysicsman/AWS-%EC%9A%94%EA%B8%88-%EC%B2%AD%EA%B5%AC-%EA%B5%AC%EC%A0%9C%EB%B0%9B%EA%B8%B0 해당 블로그에서 지금 겪은 상황과 매우 비슷한 사례를 발견했다. 이를 참고로 하여 AWS Customer Support에 문의했고, 문제를 신속하게 해결해주었다.

이번 경험을 통해, 문제 상황에 직면했을 때 적극적으로 대응하고 필요한 정보를 탐색하는 것의 중요성을 다시 한번 깨달았다.

만약 비슷한 문제에 직면하셨다면, AWS 문서를 확인하고, 비슷한 사례를 검색하여 해결 방법을 찾는 것이 도움이 될 수 있겠다.

 

너무 당황해서 사진은 없으나, 중요한 것은 인스턴스 종료와 비활성화를 확인하고 또 확인해야 한다. 또 혹시 모르니 계정을 해지 했다. 계정을 새로 만들어서 정말 필요한 인스턴스만 쓰도록 해야겠다.

AWS Customer Support 팀에 감사드립니다.

※ 이 리뷰는 직접 구매하여 사용한 경험을 바탕으로 작성되었으며, 어떠한 광고나 수익 목적이 없습니다. 따라서 내용이 주관적일 수 있습니다.

Slid Application은 메모 작성을 도와주는 앱으로, 내게 큰 도움을 주었다. 사람들은 자연스럽게 망각하기 마련인데, 이를 최소화하고자 메모 작성을 습관화하고 있다. 이 때문에 Notion과 Slid Application을 활용하여 코딩 강의를 메모하거나 필요한 내용들을 기록하고 있다.

2023년 1월 20일 즈음, 유튜브 광고를 통해 Slid를 처음 알게 되었다. 그 전까지는 주로 Notion을 사용하고 있었으나, Slid가 갖고 있는 Notion이 제공하지 않는 독특한 기능들 때문에 현재는 두 앱을 병행하여 사용하고 있다. (Notion에 대한 리뷰는 다음 기회에 작성할 예정이다.)

< Slid의 장점 >
Slid를 사용하면서 느낀 점 중 하나는 그 사용성이다. 과거, 2022년 11월 21일부터 2023년 5월 중순까지, 나는 아침 6시부터 밤 12시까지 거의 밥도 먹지 않고 공부를 했었다. Notion을 사용하면서 인터페이스가 나에게는 다소 복잡해 보였다. 그런데 Slid를 발견하고 사용해보니, 폴더 형식이 간단하고 색상도 지정할 수 있어서, 나만의 중요도를 표시하는 데 편리했다. 또한 인터페이스가 친숙하게 느껴져 빠르게 메모를 작성할 수 있었고, 이를 통해 강의나 공부한 내용을 체계적으로 기록하고 복습할 수 있었다.

특히 흥미로웠던 기능 중 하나는 음성을 텍스트로 변환해주는 기능이었다. 이 기능은 하루에 많은 양의 메모를 작성할 때 피로를 줄여주는 데 큰 도움이 되었다. 또한 60초 동안의 영상 녹화 기능도 메모 작성에 있어 편리했다.

추가적으로, Slid는 웹 사이트 뿐만 아니라 스마트폰에서도 사용할 수 있어서 이동 중에도 메모를 복습하는 데 유용했다.

< Slid의 단점 >
그러나 Slid의 음성을 텍스트로 변환하는 기능이 나의 현재 회원 등급에서는 사용할 수 없어 아쉬웠다.

< Slid 팀과의 인터뷰 및 대표님과의 만남 >
4월 말쯤, Slid 팀으로부터 연락이 왔고 인터뷰를 요청받았다. 이 인터뷰는 Slid의 우수 사용자들을 대상으로 진행되었다.

그 당시에 Slid를 하루에 18시간씩 사용하면서 여러 궁금증이 있었는데 이 기회를 통해 필요한 기능이나 궁금증을 직접 팀에게 전달할 수 있어 좋았다.

또한, Slid의 대표님과도 만날 기회가 주어졌다. 대표님과의 만남은 나에게 큰 동기부여가 되었고, 많은 것을 배울 수 있었다. 이 경험을 통해 더 열심히 노력하여 선한 영향력을 가진 개발자가 되겠다고 다짐했다.

 

 

폴더 안에 여러가지 메모들을 넣을 수 있다. 즉 폴더는 주제라고 생각하면 된다. 또한 색깔을 내마음대로 해서 중요도를 표시 할 수 있다.
밑의 108개의 노트는 정리해야되는데... 너무 많아서 아무리 정리하고 정리해도 끝이 없기도 하고, 검색하는게 편해서 몇 개 옮기다가 방치 상태. 저것들 날 잡고 정리를 해야겠다.

 

+ Recent posts