- REST
- 실습
- 도메인 모델링(Domain Modeling)
- Adding Entity Framework Core Packages (Nuget)
- DbContext Class
- Adding ConnectionString To The Database In Appsettings.Json
- Understanding Dependency Injection & Injecting DbContext Into Our Application
- Understanding Dependency Injection & Injecting DbContext Into Our Application
- Create New Controller (Regions Controller)
- Change Methods To Use DTOs
- Create Region Action Method
- Update Region Action Method
- Delete Region Action Method
REST
REST(REpresentational State Transfer)는 웹 서비스 설계의 Architecture Style 입니다.
REST는 리소스를 나타내는 URL과 리소스 상태를 조작하기 위한 HTTP 메서드(POST, GET, PUT, DELETE … )를 사용하여
분산 시스템을 간단하고 효율적으로 구축하도록 도와줍니다.
주요 개념
- 리소스 (Resource):
- 모든 리소스는 고유한 URL을 통해 식별합니다.
- 리소스는 서버의 상태나 객체를 나타냅니다.
- HTTP 메서드, Verbs:
- GET: 서버에서 리소스를 조회합니다.
- POST: 서버에 새로운 리소스를 생성합니다.
- PUT: 서버의 기존 리소스를 업데이트합니다.
- DELETE: 서버에서 리소스를 삭제합니다.
- PATCH: 서버의 기존 리소스를 부분적으로 업데이트하는 데 사용됩니다.
- 표현 (Representation):
- 리소스의 상태는 다양한 형식(JSON, XML, HTML 등…)으로 클라이언트에 제공될 수 있습니다.
- 무상태성 (Statelessness):
- 각 요청은 독립적이며 서버는 클라이언트의 이전 요청을 기억하지 않습니다.
- 모든 필요한 상태 정보는 요청에 포함되어야 합니다.
- 캐싱 (Caching):
- 응답은 캐시 가능하며, 이를 통해 성능을 향상시킬 수 있습니다.
- 서버의 응답에 적절한 캐싱 헤더를 포함하여 클라이언트가 응답을 캐시할 수 있도록 합니다.
.NET Core가 지원하는 기능 (REST)
HTTP Verbs
HTTP 메서드는 웹 애플리케이션에서 리소스와 상호작용하는 방법을 정의
- GET: 리소스 조회
- POST: 새 리소스 생성
- PUT: 기존 리소스 업데이트
- DELETE: 리소스 삭제
- PATCH: 리소스의 일부 업데이트
- OPTION
Routing
라우팅은 URL 경로를 서버의 코드와 매핑하는 과정입니다:
- 경로 정의:
URL이 컨트롤러 및 액션 메서드에 어떻게 매핑되는지 정의합니다. - ASP.NET Core:
Startup.cs
파일에서 라우팅 규칙을 설정하고, 컨트롤러와 액션 메서드로 요청을 매핑합니다.
using Microsoft.AspNetCore.Http; // HTTP 관련 기능을 위한 네임스페이스 using Microsoft.AspNetCore.Mvc; // MVC 관련 기능을 위한 네임스페이스 namespace NZWalks.API.Controllers { // URL 경로: https://localhost:portnumber/api/Students [Route("api/[controller]")] // 컨트롤러의 기본 라우트를 설정. [controller]는 컨트롤러 이름으로 대체됨. [ApiController] // API 컨트롤러 특성 적용 public class StudentsController : ControllerBase // ControllerBase를 상속하여 API 컨트롤러 생성 { // HTTP GET 메서드: https://localhost:portnumber/api/Students [HttpGet] public IActionResult GetAllStudents() // 모든 학생 이름을 반환하는 메서드 { // 학생 이름 배열을 생성 (임시) string[] StudentsNames = new string[] { "lycos0", "lycos1", "lycos2", "lycos3", "lycos4" }; return Ok(StudentsNames); // 200 OK 응답으로 학생 이름 배열 반환 } } }
Model Binding
모델 바인딩은 클라이언트의 요청 데이터를 컨트롤러 메서드의 파라미터나 모델 객체에 자동으로 매핑하는 과정입니다:
- 자동 바인딩:
폼 데이터, 쿼리 문자열, 경로 데이터를 C# 객체로 변환합니다. - 유효성 검사:
바인딩된 데이터에 대해 유효성 검사를 수행하여 올바른 데이터만 처리합니다.
Content Negotiation
Content Negotiation은 서버가 클라이언트의 Accept
헤더를 기반으로 응답 콘텐츠 유형을 결정하는 과정입니다:
- 클라이언트 요청:
클라이언트는Accept
헤더를 통해 원하는 콘텐츠 유형을 알립니다. - 서버 응답:
서버는 JSON, XML 등 다양한 형식으로 응답을 반환합니다.
Response Types
응답 유형은 서버가 클라이언트에게 반환하는 다양한 데이터의 형식을 정의합니다:
- JSON:
일반적으로 사용되는 데이터 형식으로, 키-값 쌍을 포함합니다. - XML:
구조화된 데이터 형식으로, 태그를 사용하여 데이터 요소를 포함합니다. - HTML:
웹 페이지를 렌더링하기 위한 마크업 언어입니다. - Plain Text:
단순 텍스트 형식으로 데이터를 반환합니다. - File:
파일 다운로드를 위한 응답 형식입니다.
실습
도메인 모델링(Domain Modeling)
도메인 모델링은 애플리케이션의 특정 도메인(문제 영역)을 표현하는 모델을 정의하는 과정
비즈니스 로직과 데이터를 구조화하고, 데이터베이스와의 상호작용을 관리하는 데 사용
데이터베이스 설계와 밀접한 관련이 있으며, ORM(Object-Relational Mapping) 프레임워크를 사용하는 경우
데이터베이스 테이블과 애플리케이션의 객체 간의 매핑을 정의하는 데 중요
Walk
속성 | 타입 | 설명 |
---|---|---|
Id | Guid | 걷기 경로의 고유 식별자 |
Name | string | 걷기 경로의 이름 |
Description | string | 걷기 경로의 설명 |
LengthInKm | double | 걷기 경로의 길이 (킬로미터 단위) |
WalkImageUrl | string? | 걷기 경로의 이미지 URL (nullable) |
DifficultyId | Guid | 난이도 식별자 |
RegionId | Guid | 지역 식별자 |
Difficulty | Difficulty | 난이도 (Navigation property – ORM 프레임워크에서 중요한 역할을 함) |
Region | Region | 지역 (Navigation property – ORM 프레임워크에서 중요한 역할을 함) |
namespace NZWalks.API.Models.Domain { public class Walk { public Guid Id { get; set; } public string Name { get; set; } public string Description { get; set; } public double LengthInKm { get; set; } public string? WalkImageUrl { get; set; } // Nullable public Guid DifficultyId { get; set; } public Guid RegionId { get; set; } // Navigation properties // Navigation properties은 ORM(Object-Relational Mapping) 프레임워크, // 특히 Entity Framework Core에서 매우 중요한 역할을 합니다. // 이들은 데이터베이스의 외래 키 관계를 나타내고, // 테이블 간의 관계를 더 쉽게 탐색할 수 있도록 해줍니다. public Difficulty Difficulty { get; set; } public Region Region { get; set; } } }
Region
속성 | 타입 | 설명 |
---|---|---|
Id | Guid | 지역의 고유 식별자 |
Code | string | 지역 코드 |
Name | string | 지역 이름 |
RegionImageUrl | string? | 지역의 이미지 URL (nullable) |
namespace NZWalks.API.Models.Domain { public class Region { public Guid Id { get; set; } public string Code { get; set; } public string Name { get; set; } public string? RegionImageUrl { get; set; } // Nullable } }
Difficulty
속성 | 타입 | 설명 |
---|---|---|
Id | Guid | 난이도의 고유 식별자 |
Name | string | 난이도의 이름 |
namespace NZWalks.API.Models.Domain { public class Difficulty { public Guid Id { get; set; } public string Name { get; set; } } }
Adding Entity Framework Core Packages (Nuget)
프로젝트에
EntityFrameworkCore.SqlServer / EntityFrameworkCore.Tools Package 추가
DbContext Class
Entity Framework Core (EF Core)에서 가장 중요한 클래스
애플리케이션과 데이터베이스 간의 세션을 관리하며, 데이터베이스 작업(조회, 삽입, 업데이트, 삭제)을 수행하는 데 사용
주요 기능
- 데이터베이스 연결:
데이터베이스와의 연결을 관리합니다. - 데이터 모델 매핑:
C# 클래스와 데이터베이스 테이블 간의 매핑을 정의합니다. - CRUD 작업:
데이터베이스에 대해 조회, 삽입, 업데이트, 삭제 작업을 수행합니다. - 변경 추적:
Entity의 상태 변화를 추적하여 데이터베이스에 저장할 수 있도록 합니다. - 쿼리 작성:
LINQ를 사용하여 데이터베이스 쿼리를 작성하고 실행합니다.
using Microsoft.EntityFrameworkCore; // Entity Framework Core 기능을 사용하기 위한 네임스페이스 using NZWalks.API.Models.Domain; // 도메인 모델을 사용하기 위한 네임스페이스 namespace NZWalks.API.Data { public class NZWalksDbcontext : DbContext // DbContext를 상속하여 데이터베이스 컨텍스트 클래스 생성 { // 생성자에서 DbContextOptions를 받아 기본 생성자에 전달 // : base(dbContextOptions) -> 부모 클래스인 DbContext의 생성자를 호출 public NZWalksDbcontext(DbContextOptions dbContextOptions) : base(dbContextOptions) { } // DbSet 속성은 데이터베이스 테이블에 매핑됨 public DbSet<Difficulty> Difficulties { get; set; } // Difficulty 엔티티에 대한 DbSet 정의 public DbSet<Region> Regions { get; set; } // Region 엔티티에 대한 DbSet 정의 public DbSet<Walk> Walks { get; set; } // Walk 엔티티에 대한 DbSet 정의 } }
Adding ConnectionString To The Database In Appsettings.Json
{ "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } }, "AllowedHosts": "*", "ConnectionStrings": { "NZWalksConnectionString": "Server=서버,포트번호;Database=NZWalksDb;User ID=your_username;Password=your_password;TrustServerCertificate=True" } }
Understanding Dependency Injection & Injecting DbContext Into Our Application
Dependency Injection (DI)는 클래스 간의 종속성을 관리하는 방법으로 컴포넌트가 필요로 하는 의존성을 주입받는 방식
DI를 사용하면 코드의 재사용성과 테스트 가능성을 높이고, 의존성 관계를 명시적으로 관리
DI를 통해 DbContext
를 주입하면, 데이터베이스 연결과 관련된 설정을 중앙 집중식으로 관리할 수 있고, 코드의 가독성과 유지보수성을 높일 수 있음
using Microsoft.EntityFrameworkCore; // Entity Framework Core 기능을 사용하기 위한 네임스페이스 using NZWalks.API.Data; // 데이터 컨텍스트를 사용하기 위한 네임스페이스 var builder = WebApplication.CreateBuilder(args); // 웹 애플리케이션 빌더 객체 생성 // Add services to the container. builder.Services.AddControllers(); // 컨트롤러 서비스를 추가하여 MVC 패턴을 지원 // Swagger/OpenAPI 설정에 대한 더 많은 정보를 얻으려면 https://aka.ms/aspnetcore/swashbuckle 참고 builder.Services.AddEndpointsApiExplorer(); // API 탐색기를 추가하여 API 문서를 자동 생성 builder.Services.AddSwaggerGen(); // Swagger 생성을 위한 서비스 추가 // DbContext 서비스를 추가하여 SQL Server를 사용하도록 설정 builder.Services.AddDbContext<NZWalksDbcontext>(options => options.UseSqlServer(builder.Configuration.GetConnectionString("NZWalksConnectionString"))); var app = builder.Build(); // 애플리케이션 빌드 // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { // 개발 환경에서만 Swagger를 사용하도록 설정 app.UseSwagger(); app.UseSwaggerUI(); } app.UseHttpsRedirection(); // HTTP 요청을 HTTPS로 리디렉션 app.UseAuthorization(); // 권한 부여 미들웨어 사용 app.MapControllers(); // 컨트롤러에 대한 요청을 매핑 app.Run(); // 애플리케이션 실행
Understanding Dependency Injection & Injecting DbContext Into Our Application
Add-Migration “Initial Migration”
데이터베이스의 스키마(구조)에 대한 변경 사항을 추적하고 기록하는 “마이그레이션”을 생성
PM> Add-Migration "Initial Migration" Build started... Build succeeded. To undo this action, use Remove-Migration.
20241026224957_Initial Migration.cs
using System; using Microsoft.EntityFrameworkCore.Migrations; #nullable disable namespace NZWalks.API.Migrations { /// <inheritdoc /> public partial class InitialMigration : Migration { /// <inheritdoc /> protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.CreateTable( name: "Difficulties", columns: table => new { Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false), Name = table.Column<string>(type: "nvarchar(max)", nullable: false) }, constraints: table => { table.PrimaryKey("PK_Difficulties", x => x.Id); }); migrationBuilder.CreateTable( name: "Regions", columns: table => new { Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false), Code = table.Column<string>(type: "nvarchar(max)", nullable: false), Name = table.Column<string>(type: "nvarchar(max)", nullable: false), RegionImageUrl = table.Column<string>(type: "nvarchar(max)", nullable: true) }, constraints: table => { table.PrimaryKey("PK_Regions", x => x.Id); }); migrationBuilder.CreateTable( name: "Walks", columns: table => new { Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false), Name = table.Column<string>(type: "nvarchar(max)", nullable: false), Description = table.Column<string>(type: "nvarchar(max)", nullable: false), LengthInKm = table.Column<double>(type: "float", nullable: false), WalkImageUrl = table.Column<string>(type: "nvarchar(max)", nullable: true), DifficultyId = table.Column<Guid>(type: "uniqueidentifier", nullable: false), RegionId = table.Column<Guid>(type: "uniqueidentifier", nullable: false) }, constraints: table => { table.PrimaryKey("PK_Walks", x => x.Id); table.ForeignKey( name: "FK_Walks_Difficulties_DifficultyId", column: x => x.DifficultyId, principalTable: "Difficulties", principalColumn: "Id", onDelete: ReferentialAction.Cascade); table.ForeignKey( name: "FK_Walks_Regions_RegionId", column: x => x.RegionId, principalTable: "Regions", principalColumn: "Id", onDelete: ReferentialAction.Cascade); }); migrationBuilder.CreateIndex( name: "IX_Walks_DifficultyId", table: "Walks", column: "DifficultyId"); migrationBuilder.CreateIndex( name: "IX_Walks_RegionId", table: "Walks", column: "RegionId"); } /// <inheritdoc /> protected override void Down(MigrationBuilder migrationBuilder) { migrationBuilder.DropTable( name: "Walks"); migrationBuilder.DropTable( name: "Difficulties"); migrationBuilder.DropTable( name: "Regions"); } } }
Update-Database
마이그레이션 파일에 정의된 변경 사항을 실제 데이터베이스에 적용
모든 마이그레이션을 차례대로 실행하고, 데이터베이스를 최신 상태로 업데이트
이 과정에서 데이터베이스에 테이블이 생성되고, 관계가 설정되며, 필요한 모든 변경 사항이 적용
PM> Update-Database Build started... Build succeeded. Microsoft.EntityFrameworkCore.Database.Command[20101] Executed DbCommand (2,215ms) [Parameters=[], CommandType='Text', CommandTimeout='60'] CREATE DATABASE [NZWalksDb]; Microsoft.EntityFrameworkCore.Database.Command[20101] Executed DbCommand (1,448ms) [Parameters=[], CommandType='Text', CommandTimeout='60'] IF SERVERPROPERTY('EngineEdition') <> 5 BEGIN ALTER DATABASE [NZWalksDb] SET READ_COMMITTED_SNAPSHOT ON; END; Microsoft.EntityFrameworkCore.Database.Command[20101] Executed DbCommand (4ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] SELECT 1 Microsoft.EntityFrameworkCore.Migrations[20411] Acquiring an exclusive lock for migration application. See https://aka.ms/efcore-docs-migrations-lock for more information if this takes too long. Acquiring an exclusive lock for migration application. See https://aka.ms/efcore-docs-migrations-lock for more information if this takes too long. Microsoft.EntityFrameworkCore.Database.Command[20101] Executed DbCommand (30ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] DECLARE @result int; EXEC @result = sp_getapplock @Resource = '__EFMigrationsLock', @LockOwner = 'Session', @LockMode = 'Exclusive'; SELECT @result Microsoft.EntityFrameworkCore.Database.Command[20101] Executed DbCommand (57ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] IF OBJECT_ID(N'[__EFMigrationsHistory]') IS NULL BEGIN CREATE TABLE [__EFMigrationsHistory] ( [MigrationId] nvarchar(150) NOT NULL, [ProductVersion] nvarchar(32) NOT NULL, CONSTRAINT [PK___EFMigrationsHistory] PRIMARY KEY ([MigrationId]) ); END; Microsoft.EntityFrameworkCore.Database.Command[20101] Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] SELECT 1 Microsoft.EntityFrameworkCore.Database.Command[20101] Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] SELECT OBJECT_ID(N'[__EFMigrationsHistory]'); Microsoft.EntityFrameworkCore.Database.Command[20101] Executed DbCommand (224ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] SELECT [MigrationId], [ProductVersion] FROM [__EFMigrationsHistory] ORDER BY [MigrationId]; Microsoft.EntityFrameworkCore.Migrations[20402] Applying migration '20241026224957_Initial Migration'. Applying migration '20241026224957_Initial Migration'. Microsoft.EntityFrameworkCore.Database.Command[20101] Executed DbCommand (3ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] CREATE TABLE [Difficulties] ( [Id] uniqueidentifier NOT NULL, [Name] nvarchar(max) NOT NULL, CONSTRAINT [PK_Difficulties] PRIMARY KEY ([Id]) ); Microsoft.EntityFrameworkCore.Database.Command[20101] Executed DbCommand (3ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] CREATE TABLE [Regions] ( [Id] uniqueidentifier NOT NULL, [Code] nvarchar(max) NOT NULL, [Name] nvarchar(max) NOT NULL, [RegionImageUrl] nvarchar(max) NULL, CONSTRAINT [PK_Regions] PRIMARY KEY ([Id]) ); Microsoft.EntityFrameworkCore.Database.Command[20101] Executed DbCommand (4ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] CREATE TABLE [Walks] ( [Id] uniqueidentifier NOT NULL, [Name] nvarchar(max) NOT NULL, [Description] nvarchar(max) NOT NULL, [LengthInKm] float NOT NULL, [WalkImageUrl] nvarchar(max) NULL, [DifficultyId] uniqueidentifier NOT NULL, [RegionId] uniqueidentifier NOT NULL, CONSTRAINT [PK_Walks] PRIMARY KEY ([Id]), CONSTRAINT [FK_Walks_Difficulties_DifficultyId] FOREIGN KEY ([DifficultyId]) REFERENCES [Difficulties] ([Id]) ON DELETE CASCADE, CONSTRAINT [FK_Walks_Regions_RegionId] FOREIGN KEY ([RegionId]) REFERENCES [Regions] ([Id]) ON DELETE CASCADE ); Microsoft.EntityFrameworkCore.Database.Command[20101] Executed DbCommand (2ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] CREATE INDEX [IX_Walks_DifficultyId] ON [Walks] ([DifficultyId]); Microsoft.EntityFrameworkCore.Database.Command[20101] Executed DbCommand (2ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] CREATE INDEX [IX_Walks_RegionId] ON [Walks] ([RegionId]); Microsoft.EntityFrameworkCore.Database.Command[20101] Executed DbCommand (3ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion]) VALUES (N'20241026224957_Initial Migration', N'9.0.0-rc.2.24474.1'); Microsoft.EntityFrameworkCore.Database.Command[20101] Executed DbCommand (3ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] DECLARE @result int; EXEC @result = sp_releaseapplock @Resource = '__EFMigrationsLock', @LockOwner = 'Session'; SELECT @result Done. PM>
Create New Controller (Regions Controller)
실습을 위한 새로운 Controller 생성
using Microsoft.AspNetCore.Mvc; // ASP.NET Core MVC 기능을 사용하기 위한 네임스페이스 using NZWalks.API.Models.Domain; // 도메인 모델을 사용하기 위한 네임스페이스 namespace NZWalks.API.Controllers { // URL 경로: https://localhost:1234/api/regions [Route("api/[controller]")] // 컨트롤러의 기본 라우트를 설정. [controller]는 컨트롤러 이름으로 대체됨. [ApiController] // API 컨트롤러 특성 적용 public class RegionsController : ControllerBase // ControllerBase를 상속하여 API 컨트롤러 생성 { // 모든 지역 정보를 가져오기 위한 GET 메서드 [HttpGet] public IActionResult GetAll() { // 임시 지역 정보 리스트를 생성 var regions = new List<Region>() { new Region { Id = Guid.NewGuid(), Name = "Auckland Region", Code = "AKL", RegionImageUrl = "https://en.wikipedia.org/wiki/File:Auckland_skyline_-_May_2024_(2).jpg" }, new Region { Id = Guid.NewGuid(), Name = "Wellington Region", Code = "WLG", RegionImageUrl = "https://en.wikipedia.org/wiki/File:Wellington_Panorama_View.jpg" }, new Region { Id = Guid.NewGuid(), Name = "Seoul Region", Code = "KR-11", RegionImageUrl = "https://en.wikipedia.org/wiki/File:Seoul_(175734251)_(cropped).jpg" } }; // 200 OK 응답으로 지역 정보 리스트 반환 return Ok(regions); } } }
Dbcontext를 이용한 방법으로 변경
using Microsoft.AspNetCore.Mvc; using NZWalks.API.Data; using NZWalks.API.Models.Domain; namespace NZWalks.API.Controllers { [Route("api/[controller]")] // [Route("api/regions")] / https://localhost:1234/api/regions [ApiController] public class RegionsController : ControllerBase { private readonly NZWalksDbcontext dbcontext; public RegionsController(NZWalksDbcontext dbContext) { this.dbcontext = dbContext; } // GET ALL REGIONS [HttpGet] public IActionResult GetAll() { var region = this.dbcontext.Regions.ToList(); return Ok(region); } } }
샘플 데이터 입력 및 컨트롤러 수정
USE NZWalksDb; Select * From Regions; INSERT INTO Regions (Id, Code, Name, RegionImageUrl) VALUES (NEWID(), 'AKL', 'Auckland Region', 'https://en.wikipedia.org/wiki/File:Auckland_skyline_-_May_2024_(2).jpg'), (NEWID(), 'WLG', 'Wellington Region', 'https://en.wikipedia.org/wiki/File:Wellington_Panorama_View.jpg'), (NEWID(), 'KR-11', 'Seoul Region', 'https://en.wikipedia.org/wiki/File:Seoul_(175734251)_(cropped).jpg'), (NEWID(), 'NYC', 'New York City', 'https://en.wikipedia.org/wiki/File:New_York_City_skyline.jpg'), (NEWID(), 'TKY', 'Tokyo Region', 'https://en.wikipedia.org/wiki/File:Tokyo_skyline.jpg');
RegionsController.cs
using Microsoft.AspNetCore.Mvc; using NZWalks.API.Data; using NZWalks.API.Models.Domain; namespace NZWalks.API.Controllers { [Route("api/[controller]")] // 컨트롤러의 기본 Route를 설정. [controller]는 컨트롤러 이름으로 대체됨. [ApiController] // API 컨트롤러 특성 적용 public class RegionsController : ControllerBase { private readonly NZWalksDbcontext dbcontext; public RegionsController(NZWalksDbcontext dbContext) { this.dbcontext = dbContext; } // GET ALL REGIONS // GET: https://localhost:1234/api/regions [HttpGet] public IActionResult GetAll() { var region = dbcontext.Regions.ToList(); return Ok(region); } // GET SINGLE REGION (Get Region By ID) // GET: https://localhost:1234/api/regions/{id} [HttpGet] [Route("{id:guid}")] public IActionResult GetById([FromRoute]Guid id) { // var region = dbcontext.Regions.Find(id); var region = dbcontext.Regions.FirstOrDefault(x => x.Id == id); if (region == null) { return NotFound(); } else { return Ok(region); } } } }
Change Methods To Use DTOs
using NZWalks.API.Models.Domain; namespace NZWalks.API.Models.DTO { public class RegionDto { public string Code { get; set; } public string Name { get; set; } public string? RegionImageUrl { get; set; } // Nullable public RegionDto(Region region) { this.Code = region.Code; this.Name = region.Name; this.RegionImageUrl = region.RegionImageUrl; } } }
namespace NZWalks.API.Models.Domain { public class Region { public Guid Id { get; set; } public string Code { get; set; } public string Name { get; set; } public string? RegionImageUrl { get; set; } // Nullable } }
using Microsoft.AspNetCore.Mvc; using NZWalks.API.Data; using NZWalks.API.Models.Domain; using NZWalks.API.Models.DTO; namespace NZWalks.API.Controllers { [Route("api/[controller]")] // 컨트롤러의 기본 Route를 설정. [controller]는 컨트롤러 이름으로 대체됨. [ApiController] // API 컨트롤러 특성 적용 public class RegionsController : ControllerBase { private readonly NZWalksDbcontext dbcontext; public RegionsController(NZWalksDbcontext dbContext) { this.dbcontext = dbContext; } // GET ALL REGIONS // GET: https://localhost:1234/api/regions [HttpGet] public IActionResult GetAll() { // Get Data From Database - Domain models var regionDomain = dbcontext.Regions.ToList(); // Map Domain Models to DTOs var regionDto = new List<RegionDto>(); foreach (var region in regionDomain) { regionDto.Add(new RegionDto(region)); } return Ok(regionDto); } // GET SINGLE REGION (Get Region By ID) // GET: https://localhost:1234/api/regions/{id} [HttpGet] [Route("{id:guid}")] public IActionResult GetById([FromRoute]Guid id) { // Get Data From Database - Domain models // var region = dbcontext.Regions.Find(id); var regionDomain = dbcontext.Regions.FirstOrDefault(x => x.Id == id); if (regionDomain == null) { return NotFound(); } // Map Domain Models to DTOs var regionDto = new RegionDto(regionDomain); return Ok(regionDto); } } }
Create Region Action Method
namespace NZWalks.API.Models.DTO { public class AddRegionRequestDto { public string Code { get; set; } public string Name { get; set; } public string? RegionImageUrl { get; set; } // Nullable } }
using NZWalks.API.Models.Domain; namespace NZWalks.API.Models.DTO { public class RegionDto { public Guid Id { get; set; } public string Code { get; set; } public string Name { get; set; } public string? RegionImageUrl { get; set; } // Nullable } }
using Microsoft.AspNetCore.Mvc; // ASP.NET Core MVC 기능을 사용하기 위한 네임스페이스 using NZWalks.API.Data; // 데이터 컨텍스트를 사용하기 위한 네임스페이스 using NZWalks.API.Models.Domain; // 도메인 모델을 사용하기 위한 네임스페이스 using NZWalks.API.Models.DTO; // DTO를 사용하기 위한 네임스페이스 namespace NZWalks.API.Controllers { // URL 경로: https://localhost:1234/api/regions [Route("api/[controller]")] // 컨트롤러의 기본 Route를 설정. [controller]는 컨트롤러 이름으로 대체됨. [ApiController] // API 컨트롤러 특성 적용 public class RegionsController : ControllerBase // ControllerBase를 상속하여 API 컨트롤러 생성 { private readonly NZWalksDbcontext dbcontext; // 생성자에서 DbContext를 주입받아 설정 public RegionsController(NZWalksDbcontext dbContext) { this.dbcontext = dbContext; } // 모든 지역 정보를 가져오기 위한 GET 메서드 // GET: https://localhost:1234/api/regions [HttpGet] public IActionResult GetAll() { // 데이터베이스에서 지역 정보 가져오기 - 도메인 모델 var regionDomain = dbcontext.Regions.ToList(); // 도메인 모델을 DTO로 매핑 var regionDto = new List<RegionDto>(); foreach (var region in regionDomain) { regionDto.Add(new RegionDto() { Id = region.Id, Code = region.Code, Name = region.Name, RegionImageUrl = region.RegionImageUrl }); } return Ok(regionDto); // 200 OK 응답으로 DTO 리스트 반환 } // 단일 지역 정보를 ID로 가져오기 위한 GET 메서드 // GET: https://localhost:1234/api/regions/{id} [HttpGet] [Route("{id:guid}")] public IActionResult GetById([FromRoute] Guid id) { // 데이터베이스에서 ID로 지역 정보 가져오기 - 도메인 모델 var regionDomain = dbcontext.Regions.FirstOrDefault(x => x.Id == id); if (regionDomain == null) { return NotFound(); // ID에 해당하는 지역이 없으면 404 Not Found 반환 } // 도메인 모델을 DTO로 매핑 var regionDto = new RegionDto() { Id = regionDomain.Id, Code = regionDomain.Code, Name = regionDomain.Name, RegionImageUrl = regionDomain.RegionImageUrl }; return Ok(regionDto); // 200 OK 응답으로 DTO 반환 } // 새로운 지역 정보를 생성하기 위한 POST 메서드 // POST: https://localhost:1234/api/regions [HttpPost] public IActionResult Create([FromBody] AddRegionRequestDto addRegionRequestDto) { // DTO를 도메인 모델로 변환 var regionDomainModel = new Region { Code = addRegionRequestDto.Code, Name = addRegionRequestDto.Name, RegionImageUrl = addRegionRequestDto.RegionImageUrl }; // 도메인 모델을 사용하여 지역 정보 생성 dbcontext.Regions.Add(regionDomainModel); dbcontext.SaveChanges(); // 도메인 모델을 다시 DTO로 매핑 var regionDto = new RegionDto { Id = regionDomainModel.Id, Code = addRegionRequestDto.Code, Name = regionDomainModel.Name, RegionImageUrl = regionDomainModel.RegionImageUrl }; // 201 Created 응답으로 생성된 DTO 반환 return CreatedAtAction(nameof(GetById), new { id = regionDto.Id }, regionDto); } } }
CreatedAtAction은 ASP.NET Core에서 HTTP POST 요청 후 201 Created 상태 코드를 반환할 때 사용되는 메서드
새로운 리소스를 생성한 후, 해당 리소스에 접근할 수 있는 URI를 함께 제공하여 클라이언트가 리소스를 쉽게 찾을 수 있게 해줌
주요 역할
- 201 Created 상태 코드 반환:
리소스가 성공적으로 생성되었음을 알립니다. - 리소스 위치 URI 제공:
새로 생성된 리소스에 접근할 수 있는 URI를 반환하여 클라이언트가 해당 리소스를 쉽게 찾을 수 있게 합니다. - 리소스 데이터 반환:
새로 생성된 리소스의 데이터를 응답 본문에 포함시킵니다.
return CreatedAtAction(nameof(GetById), new { id = regionDto.Id}, regionDto);
Update Region Action Method
using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using NZWalks.API.Data; using NZWalks.API.Models.Domain; using NZWalks.API.Models.DTO; using static System.Net.WebRequestMethods; using System; namespace NZWalks.API.Controllers { [Route("api/[controller]")] // 컨트롤러의 기본 Route를 설정. [controller]는 컨트롤러 이름으로 대체됨. [ApiController] // API 컨트롤러 특성 적용 public class RegionsController : ControllerBase { private readonly NZWalksDbcontext dbcontext; public RegionsController(NZWalksDbcontext dbContext) { this.dbcontext = dbContext; } // GET ALL REGIONS // GET: https://localhost:1234/api/regions [HttpGet] public IActionResult GetAll() { // Get Data From Database - Domain models var regionDomain = dbcontext.Regions.ToList(); // Map Domain Models to DTOs var regionDto = new List<RegionDto>(); foreach (var region in regionDomain) { regionDto.Add(new RegionDto() { Id = region.Id, Code = region.Code, Name = region.Name, RegionImageUrl = region.RegionImageUrl }); } return Ok(regionDto); } // GET SINGLE REGION (Get Region By ID) // GET: https://localhost:1234/api/regions/{id} [HttpGet] [Route("{id:guid}")] public IActionResult GetById([FromRoute]Guid id) { // Get Data From Database - Domain models // var region = dbcontext.Regions.Find(id); var regionDomain = dbcontext.Regions.FirstOrDefault(x => x.Id == id); if (regionDomain == null) { return NotFound(); } // Map Domain Models to DTOs var regionDto = new RegionDto() { Id = regionDomain.Id, Code = regionDomain.Code, Name = regionDomain.Name, RegionImageUrl = regionDomain.RegionImageUrl }; return Ok(regionDto); } // POST To Create New Region // POST : https://localhost:1234/api/regions [HttpPost] public IActionResult Create([FromBody] AddRegionRequestDto addRegionRequestDto) { // [Require] // string Code, Name // string? RegionImageUrl // Map or Convert DTO to Domain Model var regionDomainModel = new Region { Code = addRegionRequestDto.Code, Name = addRegionRequestDto.Name, RegionImageUrl = addRegionRequestDto.RegionImageUrl }; // Use Domain Model to create Region dbcontext.Regions.Add(regionDomainModel); dbcontext.SaveChanges(); // Map Domain model back to DTO var regionDto = new RegionDto { Id = regionDomainModel.Id, Code = addRegionRequestDto.Code, Name = regionDomainModel.Name, RegionImageUrl = regionDomainModel.RegionImageUrl }; // 201 Created 응답으로 생성된 DTO 반환 return CreatedAtAction(nameof(GetById), new { id = regionDto.Id}, regionDto); // CreatedAtAction은 ASP.NET Core에서 HTTP POST 요청 후 201 Created 상태 코드를 반환할 때 사용되는 메서드 // 새로운 리소스를 생성한 후, 해당 리소스에 접근할 수 있는 URI를 함께 제공하여 클라이언트가 리소스를 쉽게 찾을 수 있음 } // Update Region // PUT: https://localhost:1234/api/regions/{id} [HttpPut] [Route("{id:guid}")] public IActionResult Update([FromRoute] Guid id, [FromBody] UpdateRegionRequestDto updateRegionRequestDto) { // Check if region exists var regionDomainModel = dbcontext.Regions.FirstOrDefault(x => x.Id == id); if (regionDomainModel == null) { return NotFound(); } // Map DTO to Domain model regionDomainModel.Code = updateRegionRequestDto.Code; regionDomainModel.Name = updateRegionRequestDto.Name; regionDomainModel.RegionImageUrl = updateRegionRequestDto.RegionImageUrl; dbcontext.SaveChanges(); // Convert Domain Model to DTO var regionDto = new RegionDto { Id = regionDomainModel.Id, Code = regionDomainModel.Code, Name = regionDomainModel.Name, RegionImageUrl = regionDomainModel.RegionImageUrl }; return Ok(regionDto); } } }
Delete Region Action Method
using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using NZWalks.API.Data; using NZWalks.API.Models.Domain; using NZWalks.API.Models.DTO; using static System.Net.WebRequestMethods; using System; namespace NZWalks.API.Controllers { [Route("api/[controller]")] // 컨트롤러의 기본 Route를 설정. [controller]는 컨트롤러 이름으로 대체됨. [ApiController] // API 컨트롤러 특성 적용 public class RegionsController : ControllerBase { private readonly NZWalksDbcontext dbcontext; public RegionsController(NZWalksDbcontext dbContext) { this.dbcontext = dbContext; } // GET ALL REGIONS // GET: https://localhost:1234/api/regions [HttpGet] public IActionResult GetAll() { // Get Data From Database - Domain models var regionDomain = dbcontext.Regions.ToList(); // Map Domain Models to DTOs var regionDto = new List<RegionDto>(); foreach (var region in regionDomain) { regionDto.Add(new RegionDto() { Id = region.Id, Code = region.Code, Name = region.Name, RegionImageUrl = region.RegionImageUrl }); } return Ok(regionDto); } // GET SINGLE REGION (Get Region By ID) // GET: https://localhost:1234/api/regions/{id} [HttpGet] [Route("{id:guid}")] public IActionResult GetById([FromRoute]Guid id) { // Get Data From Database - Domain models // var region = dbcontext.Regions.Find(id); var regionDomain = dbcontext.Regions.FirstOrDefault(x => x.Id == id); if (regionDomain == null) { return NotFound(); } // Map Domain Models to DTOs var regionDto = new RegionDto() { Id = regionDomain.Id, Code = regionDomain.Code, Name = regionDomain.Name, RegionImageUrl = regionDomain.RegionImageUrl }; return Ok(regionDto); } // POST To Create New Region // POST : https://localhost:1234/api/regions [HttpPost] public IActionResult Create([FromBody] AddRegionRequestDto addRegionRequestDto) { // [Require] // string Code, Name // string? RegionImageUrl // Map or Convert DTO to Domain Model var regionDomainModel = new Region { Code = addRegionRequestDto.Code, Name = addRegionRequestDto.Name, RegionImageUrl = addRegionRequestDto.RegionImageUrl }; // Use Domain Model to create Region dbcontext.Regions.Add(regionDomainModel); dbcontext.SaveChanges(); // Map Domain model back to DTO var regionDto = new RegionDto { Id = regionDomainModel.Id, Code = addRegionRequestDto.Code, Name = regionDomainModel.Name, RegionImageUrl = regionDomainModel.RegionImageUrl }; // 201 Created 응답으로 생성된 DTO 반환 return CreatedAtAction(nameof(GetById), new { id = regionDto.Id}, regionDto); // CreatedAtAction은 ASP.NET Core에서 HTTP POST 요청 후 201 Created 상태 코드를 반환할 때 사용되는 메서드 // 새로운 리소스를 생성한 후, 해당 리소스에 접근할 수 있는 URI를 함께 제공하여 클라이언트가 리소스를 쉽게 찾을 수 있음 } // Update Region // PUT: https://localhost:1234/api/regions/{id} [HttpPut] [Route("{id:guid}")] public IActionResult Update([FromRoute] Guid id, [FromBody] UpdateRegionRequestDto updateRegionRequestDto) { // Check if region exists var regionDomainModel = dbcontext.Regions.FirstOrDefault(x => x.Id == id); if (regionDomainModel == null) { return NotFound(); } // Map DTO to Domain model regionDomainModel.Code = updateRegionRequestDto.Code; regionDomainModel.Name = updateRegionRequestDto.Name; regionDomainModel.RegionImageUrl = updateRegionRequestDto.RegionImageUrl; dbcontext.SaveChanges(); // Convert Domain Model to DTO var regionDto = new RegionDto { Id = regionDomainModel.Id, Code = regionDomainModel.Code, Name = regionDomainModel.Name, RegionImageUrl = regionDomainModel.RegionImageUrl }; return Ok(regionDto); } // Delete Region // Delete https://localhost:1234/api/regions/{id} [HttpDelete] [Route("{id:guid}")] public IActionResult Update([FromRoute] Guid id) { var regionDomainModel = dbcontext.Regions.FirstOrDefault(x => x.Id == id); if (regionDomainModel == null) { return NotFound(); } dbcontext.Regions.Remove(regionDomainModel); dbcontext.SaveChanges(); // return deleted Region back // Convert Domain Model to DTO var regionDto = new RegionDto { Id = regionDomainModel.Id, Code = regionDomainModel.Code, Name = regionDomainModel.Name, RegionImageUrl = regionDomainModel.RegionImageUrl }; return Ok(regionDto); } } }