JSON Web Token(JWT) 적용
패키지 추가

패키지 추가 Microsoft.AspNetCore.Authentication.JwtBearer Microsoft.IdentityModel.Tokens System.IdentityModel.Tokens.Jwt Microsoft.AspNetCore.Identity.EntityFrameworkCore
appsettings.json 변경


appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"NZWalksConnectionString": "Server=[서버],[포트];Database=[DB이름];User ID=[DB아이디];Password=[DB비];Trusted_Connection=True;TrustServerCertificate=True"
},
"Jwt": {
"key": "TestCustomKey",
"Issuer": "https://localhost:7256",
"Audience": "https://localhost:7256"
}
}
서비스 등록을 통한 의존성 주입(Dependency Injection, DI)
Program.cs
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.EntityFrameworkCore; // Entity Framework Core 기능을 사용하기 위한 네임스페이스
using Microsoft.IdentityModel.Tokens;
using NZWalks.API.Data;
using NZWalks.API.Mappings;
using NZWalks.API.Models.Domain;
using NZWalks.API.Repositories;
using System.Text; // 데이터 컨텍스트를 사용하기 위한 네임스페이스
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")));
builder.Services.AddScoped<IRegionRepository, SQLRegionRepository>(); // Main DB Region
builder.Services.AddScoped<IWalkRepository, SQLWalkRepository>(); // Main DB Walk
// builder.Services.AddScoped<IRegionRepository, InMemoryRegionRepository>(); // Test DB (In Memory)
builder.Services.AddAutoMapper(typeof(AutoMapperProfiles));
// JWT 인증 설정 추가
builder.Services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) // JWT Bearer 인증 스키마 사용
.AddJwtBearer(options => // JWT Bearer 인증 설정
options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
{
ValidateIssuer = true, // 발행자(issuer) 유효성 검사 활성화
ValidateAudience = true, // 수신자(audience) 유효성 검사 활성화
ValidateLifetime = true, // 토큰의 유효 기간 검사 활성화
ValidateIssuerSigningKey = true, // 서명 키 유효성 검사 활성화
ValidIssuer = builder.Configuration["Jwt:Issuer"], // 유효한 발행자 설정
ValidAudience = builder.Configuration["Jwt:Audience"], // 유효한 수신자 설정
IssuerSigningKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"])) // 서명 키 설정
});
var app = builder.Build(); // 애플리케이션 빌드
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
// 개발 환경에서만 Swagger를 사용하도록 설정
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection(); // HTTP 요청을 HTTPS로 리디렉션
app.UseAuthentication(); // JWT 인증 미들웨어 추가
// 이 코드는 JWT 인증을 처리하여 사용자 요청을 검증함
app.UseAuthorization(); // 권한 부여 미들웨어 사용
// 인증된 사용자의 권한을 검증함
app.MapControllers(); // 컨트롤러에 대한 요청을 매핑
app.Run(); // 애플리케이션 실행


Controller에 [Authorize] 적용

using Microsoft.AspNetCore.Mvc;
using NZWalks.API.Data;
using NZWalks.API.Models.Domain;
using NZWalks.API.Models.DTO;
using AutoMapper;
using NZWalks.API.CustomActionFilter;
using Microsoft.AspNetCore.Authorization;
namespace NZWalks.API.Controllers
{
[Route("api/[controller]")] // 컨트롤러의 기본 Route를 설정. [controller]는 컨트롤러 이름으로 대체됨.
[ApiController] // API 컨트롤러 특성 적용
[Authorize] // 인증된 사용자만 접급 가능
public class RegionsController : ControllerBase
{
private readonly NZWalksDbcontext dbcontext;
private readonly IRegionRepository regionRepository;
private readonly IMapper mapper;
public RegionsController(NZWalksDbcontext dbContext, IRegionRepository regionRepository, IMapper mapper)
{
this.dbcontext = dbContext;
this.regionRepository = regionRepository;
this.mapper = mapper;
}
// GET ALL REGIONS
// GET: https://localhost:1234/api/regions
[HttpGet]
public async Task<IActionResult> GetAll()
{
// Get Data From Database - Domain models
// var regionDomain = await dbcontext.Regions.ToListAsync(); // Change Async
var regionDomain = await regionRepository.GetAllAsync();
// 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
// });
// }
// Map Domain Models to DTOs
var regionDto = mapper.Map<List<RegionDto>>(regionDomain);
return Ok(regionDto);
}
// GET SINGLE REGION (Get Region By ID)
// GET: https://localhost:1234/api/regions/{id}
[HttpGet]
[Route("{id:guid}")]
public async Task<IActionResult> GetById([FromRoute]Guid id)
{
// Get Data From Database - Domain models
// var region = dbcontext.Regions.Find(id);
// var regionDomain = await dbcontext.Regions.FirstOrDefaultAsync(x => x.Id == id);
var regionDomain = await regionRepository.GetByIdAsync(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
// };
var regionDto = mapper.Map<RegionDto>(regionDomain);
return Ok(regionDto);
}
// POST To Create New Region
// POST : https://localhost:1234/api/regions
[HttpPost]
[ValidateModel]
public async Task<IActionResult> Create([FromBody] AddRegionRequestDto addRegionRequestDto)
{
/* [ValidateModel] 적용전
if (ModelState.IsValid) // [ValidateModel]
{
// [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
//};
// Map or Convert DTO to Domain Model
var regionDomainModel = mapper.Map<Region>(addRegionRequestDto);
// Use Domain Model to create Region
// await dbcontext.Regions.AddAsync(regionDomainModel);
// await dbcontext.SaveChangesAsync();
await regionRepository.CreateAsync(regionDomainModel);
// Map Domain model back to DTO
//var regionDto = new RegionDto
//{
// Id = regionDomainModel.Id,
// Code = addRegionRequestDto.Code,
// Name = regionDomainModel.Name,
// RegionImageUrl = regionDomainModel.RegionImageUrl
//};
var regionDto = mapper.Map<RegionDto>(regionDomainModel);
// 201 Created 응답으로 생성된 DTO 반환
return CreatedAtAction(nameof(GetById), new { id = regionDto.Id }, regionDto);
// CreatedAtAction은 ASP.NET Core에서 HTTP POST 요청 후 201 Created 상태 코드를 반환할 때 사용되는 메서드
// 새로운 리소스를 생성한 후, 해당 리소스에 접근할 수 있는 URI를 함께 제공하여 클라이언트가 리소스를 쉽게 찾을 수 있음
}
else
{
return BadRequest(ModelState);
}
*/
// [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
//};
// Map or Convert DTO to Domain Model
var regionDomainModel = mapper.Map<Region>(addRegionRequestDto);
// Use Domain Model to create Region
// await dbcontext.Regions.AddAsync(regionDomainModel);
// await dbcontext.SaveChangesAsync();
await regionRepository.CreateAsync(regionDomainModel);
// Map Domain model back to DTO
//var regionDto = new RegionDto
//{
// Id = regionDomainModel.Id,
// Code = addRegionRequestDto.Code,
// Name = regionDomainModel.Name,
// RegionImageUrl = regionDomainModel.RegionImageUrl
//};
var regionDto = mapper.Map<RegionDto>(regionDomainModel);
// 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}")]
[ValidateModel]
public async Task<IActionResult> Update([FromRoute] Guid id, [FromBody] UpdateRegionRequestDto updateRegionRequestDto)
{
/* [ValidateModel] 적용 전
if (ModelState.IsValid)
{
// Map
//var regionDomainModel = new Region
//{
// Code = updateRegionRequestDto.Code,
// Name = updateRegionRequestDto.Name,
// RegionImageUrl = updateRegionRequestDto.RegionImageUrl
//};
var regionDomainModel = mapper.Map<Region>(updateRegionRequestDto);
// Check if region exists
// var regionDomainModel = await dbcontext.Regions.FirstOrDefaultAsync(x => x.Id == id);
regionDomainModel = await regionRepository.UpdateAsync(id, regionDomainModel);
if (regionDomainModel == null)
{
return NotFound();
}
//// Map DTO to Domain model
//regionDomainModel.Code = updateRegionRequestDto.Code;
//regionDomainModel.Name = updateRegionRequestDto.Name;
//regionDomainModel.RegionImageUrl = updateRegionRequestDto.RegionImageUrl;
//await dbcontext.SaveChangesAsync();
// Convert Domain Model to DTO
//var regionDto = new RegionDto
//{
// Id = regionDomainModel.Id,
// Code = regionDomainModel.Code,
// Name = regionDomainModel.Name,
// RegionImageUrl = regionDomainModel.RegionImageUrl
//};
var regionDto = mapper.Map<RegionDto>(regionDomainModel);
return Ok(regionDto);
}
else
{
return BadRequest(ModelState);
}
*/
// Map
//var regionDomainModel = new Region
//{
// Code = updateRegionRequestDto.Code,
// Name = updateRegionRequestDto.Name,
// RegionImageUrl = updateRegionRequestDto.RegionImageUrl
//};
var regionDomainModel = mapper.Map<Region>(updateRegionRequestDto);
// Check if region exists
// var regionDomainModel = await dbcontext.Regions.FirstOrDefaultAsync(x => x.Id == id);
regionDomainModel = await regionRepository.UpdateAsync(id, regionDomainModel);
if (regionDomainModel == null)
{
return NotFound();
}
//// Map DTO to Domain model
//regionDomainModel.Code = updateRegionRequestDto.Code;
//regionDomainModel.Name = updateRegionRequestDto.Name;
//regionDomainModel.RegionImageUrl = updateRegionRequestDto.RegionImageUrl;
//await dbcontext.SaveChangesAsync();
// Convert Domain Model to DTO
//var regionDto = new RegionDto
//{
// Id = regionDomainModel.Id,
// Code = regionDomainModel.Code,
// Name = regionDomainModel.Name,
// RegionImageUrl = regionDomainModel.RegionImageUrl
//};
var regionDto = mapper.Map<RegionDto>(regionDomainModel);
return Ok(regionDto);
}
// Delete Region
// Delete https://localhost:1234/api/regions/{id}
[HttpDelete]
[Route("{id:guid}")]
public async Task<IActionResult> Update([FromRoute] Guid id)
{
//var regionDomainModel = await dbcontext.Regions.FirstOrDefaultAsync(x => x.Id == id);
var regionDomainModel = await regionRepository.DeleteAsync(id);
if (regionDomainModel == null)
{
return NotFound();
}
// dbcontext.Regions.Remove(regionDomainModel);
// await dbcontext.SaveChangesAsync();
// 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
//};
var regionDto = mapper.Map<Region>(regionDomainModel);
return Ok(regionDto);
}
}
}


인증을 위한 DB 생성 및 연결
연결문자열 추가
appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"NZWalksConnectionString": "Server=[서버],[포트];Database=[DB이름];User ID=[DB아이디];Password=[DB비];Trusted_Connection=True;TrustServerCertificate=True",
"NZWalksAuthConnectionString": "Server=[서버],[포트];Database=[DB이름];User ID=[DB아이디];Password=[DB비];Trusted_Connection=True;TrustServerCertificate=True"
},
"Jwt": {
"key": "TestCustomKey",
"Issuer": "https://localhost:7256",
"Audience": "https://localhost:7256"
}
}
NZWalksDbcontext.cs 생성
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
namespace NZWalks.API.Data
{
public class NZWalksAuthDbcontext : IdentityDbContext
{
public NZWalksAuthDbcontext(DbContextOptions options) : base(options)
{
}
}
}
의존성 주입(Dependency Injection, DI)
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.EntityFrameworkCore; // Entity Framework Core 기능을 사용하기 위한 네임스페이스
using Microsoft.IdentityModel.Tokens;
using NZWalks.API.Data;
using NZWalks.API.Mappings;
using NZWalks.API.Models.Domain;
using NZWalks.API.Repositories;
using System.Text; // 데이터 컨텍스트를 사용하기 위한 네임스페이스
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")));
// DbContext 서비스를 추가하여 SQL Server를 사용하도록 설정
builder.Services.AddDbContext<NZWalksAuthDbcontext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("NZWalksAuthConnectionString")));
builder.Services.AddScoped<IRegionRepository, SQLRegionRepository>(); // Main DB Region
builder.Services.AddScoped<IWalkRepository, SQLWalkRepository>(); // Main DB Walk
// builder.Services.AddScoped<IRegionRepository, InMemoryRegionRepository>(); // Test DB (In Memory)
builder.Services.AddAutoMapper(typeof(AutoMapperProfiles));
// JWT 인증 설정 추가
builder.Services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) // JWT Bearer 인증 스키마 사용
.AddJwtBearer(options => // JWT Bearer 인증 설정
options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
{
ValidateIssuer = true, // 발행자(issuer) 유효성 검사 활성화
ValidateAudience = true, // 수신자(audience) 유효성 검사 활성화
ValidateLifetime = true, // 토큰의 유효 기간 검사 활성화
ValidateIssuerSigningKey = true, // 서명 키 유효성 검사 활성화
ValidIssuer = builder.Configuration["Jwt:Issuer"], // 유효한 발행자 설정
ValidAudience = builder.Configuration["Jwt:Audience"], // 유효한 수신자 설정
IssuerSigningKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"])) // 서명 키 설정
});
var app = builder.Build(); // 애플리케이션 빌드
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
// 개발 환경에서만 Swagger를 사용하도록 설정
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection(); // HTTP 요청을 HTTPS로 리디렉션
app.UseAuthentication(); // JWT 인증 미들웨어 추가
// 이 코드는 JWT 인증을 처리하여 사용자 요청을 검증함
app.UseAuthorization(); // 권한 부여 미들웨어 사용
// 인증된 사용자의 권한을 검증함
app.MapControllers(); // 컨트롤러에 대한 요청을 매핑
app.Run(); // 애플리케이션 실행






NZWalksDbcontext.cs
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using NZWalks.API.Models.Domain;
namespace NZWalks.API.Data
{
public class NZWalksAuthDbcontext : IdentityDbContext
{
public NZWalksAuthDbcontext(DbContextOptions<NZWalksAuthDbcontext> options) : base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
var readerRoleId = "4597cc15-43f1-481c-904a-27097ce8f294";
var writerRoleId = "4b9e9601-65e4-4b6d-9a30-bda7550c73e6";
var roles = new List<IdentityRole>
{
new IdentityRole
{
Id = readerRoleId,
ConcurrencyStamp = readerRoleId,
Name = "Reader",
NormalizedName = "Reader".ToUpper()
},
new IdentityRole
{
Id = writerRoleId,
ConcurrencyStamp = writerRoleId,
Name = "writer",
NormalizedName = "writer".ToUpper()
},
};
// Seed difficulties to the database
modelBuilder.Entity<IdentityRole>().HasData(roles);
// Add-Migration "Creating Auth Database" -Context "NZWalksAuthDbcontext"
// Update-Database
}
}
}
패키지 관리자 콘솔에 명령어 입력
Add-Migration "Creating Auth Database" -Context "NZWalksAuthDbcontext" Update-Database -Context "NZWalksAuthDbcontext"

Identity Core 서비스를 추가 및 옵션
Program.cs
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore; // Entity Framework Core 기능을 사용하기 위한 네임스페이스
using Microsoft.IdentityModel.Tokens;
using NZWalks.API.Data;
using NZWalks.API.Mappings;
using NZWalks.API.Models.Domain;
using NZWalks.API.Repositories;
using System.Text; // 데이터 컨텍스트를 사용하기 위한 네임스페이스
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")));
// DbContext 서비스를 추가하여 SQL Server를 사용하도록 설정
builder.Services.AddDbContext<NZWalksAuthDbcontext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("NZWalksAuthConnectionString")));
builder.Services.AddScoped<IRegionRepository, SQLRegionRepository>(); // Main DB Region
builder.Services.AddScoped<IWalkRepository, SQLWalkRepository>(); // Main DB Walk
// builder.Services.AddScoped<IRegionRepository, InMemoryRegionRepository>(); // Test DB (In Memory)
builder.Services.AddAutoMapper(typeof(AutoMapperProfiles));
// Identity Core 서비스를 추가하여 IdentityUser를 사용하도록 설정
builder.Services.AddIdentityCore<IdentityUser>()
// IdentityRole을 추가하여 역할 관리를 지원
.AddRoles<IdentityRole>()
// 데이터 보호 토큰 제공자를 추가하여 "NZWalks"라는 이름의 토큰 제공자 설정
.AddTokenProvider<DataProtectorTokenProvider<IdentityUser>>("NZWalks")
// Entity Framework 스토어를 사용하여 NZWalksAuthDbcontext를 통한 저장소 관리
.AddEntityFrameworkStores<NZWalksAuthDbcontext>()
// 기본 토큰 제공자 추가
.AddDefaultTokenProviders();
// Identity 옵션을 구성하여 비밀번호 정책 설정
builder.Services.Configure<IdentityOptions>(options =>
{
options.Password.RequireDigit = false; // 비밀번호에 숫자 필요 없음
options.Password.RequireLowercase = false; // 비밀번호에 소문자 필요 없음
options.Password.RequireNonAlphanumeric = false; // 비밀번호에 알파벳 이외의 문자 필요 없음
options.Password.RequireUppercase = false; // 비밀번호에 대문자 필요 없음
options.Password.RequiredLength = 6; // 비밀번호 최소 길이 6자
options.Password.RequiredUniqueChars = 1; // 비밀번호에 최소 1개의 고유 문자 필요
});
// JWT 인증 설정 추가
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) // JWT Bearer 인증 스키마 사용
.AddJwtBearer(options => // JWT Bearer 인증 설정
options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
{
ValidateIssuer = true, // 발행자(issuer) 유효성 검사 활성화
ValidateAudience = true, // 수신자(audience) 유효성 검사 활성화
ValidateLifetime = true, // 토큰의 유효 기간 검사 활성화
ValidateIssuerSigningKey = true, // 서명 키 유효성 검사 활성화
ValidIssuer = builder.Configuration["Jwt:Issuer"], // 유효한 발행자 설정
ValidAudience = builder.Configuration["Jwt:Audience"], // 유효한 수신자 설정
IssuerSigningKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"])) // 서명 키 설정
});
var app = builder.Build(); // 애플리케이션 빌드
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
// 개발 환경에서만 Swagger를 사용하도록 설정
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection(); // HTTP 요청을 HTTPS로 리디렉션
app.UseAuthentication(); // JWT 인증 미들웨어 추가
// 이 코드는 JWT 인증을 처리하여 사용자 요청을 검증함
app.UseAuthorization(); // 권한 부여 미들웨어 사용
// 인증된 사용자의 권한을 검증함
app.MapControllers(); // 컨트롤러에 대한 요청을 매핑
app.Run(); // 애플리케이션 실행

인증 컨트롤러 만들기

AuthController.cs
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using NZWalks.API.Models.DTO;
namespace NZWalks.API.Controllers
{
[Route("api/[controller]")] // 컨트롤러의 기본 경로를 설정. [controller]는 컨트롤러 이름으로 대체됨.
[ApiController] // API 컨트롤러 특성 적용
public class AuthController : ControllerBase
{
private readonly UserManager<IdentityUser> userManager;
// UserManager를 주입받아 초기화
public AuthController(UserManager<IdentityUser> userManager)
{
this.userManager = userManager;
}
// Post: /api/Auth/Register
[HttpPost]
[Route("Register")]
public async Task<IActionResult> Register([FromBody] RegisterRequestDto registerRequestDto)
{
// 새로운 IdentityUser 생성
var identityUser = new IdentityUser
{
UserName = registerRequestDto.Username,
Email = registerRequestDto.Username
};
// UserManager를 사용하여 사용자 생성
var identityResult = await userManager.CreateAsync(identityUser, registerRequestDto.Password);
if (identityResult.Succeeded)
{
// 사용자에게 역할(Role) 추가
if (registerRequestDto.Roles != null && registerRequestDto.Roles.Any())
{
identityResult = await userManager.AddToRolesAsync(identityUser, registerRequestDto.Roles);
if (identityResult.Succeeded)
{
return Ok("User was registered! Please login."); // 사용자 등록 성공 메시지 반환
}
}
}
return BadRequest("Something went wrong!"); // 오류 발생 시 반환 메시지
}
}
}
RegisterRequestDto.cs
using System.ComponentModel.DataAnnotations;
namespace NZWalks.API.Models.DTO
{
public class RegisterRequestDto
{
[Required]
[DataType(DataType.EmailAddress)]
public string Username { get; set; }
[Required]
[DataType(DataType.EmailAddress)]
public string Password { get; set; }
public string[] Roles { get; set; }
}
}






Login 기능 만들기
AuthController.cs
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using NZWalks.API.Models.DTO;
namespace NZWalks.API.Controllers
{
[Route("api/[controller]")] // 컨트롤러의 기본 경로를 설정. [controller]는 컨트롤러 이름으로 대체됨.
[ApiController] // API 컨트롤러 특성 적용
public class AuthController : ControllerBase
{
private readonly UserManager<IdentityUser> userManager;
// UserManager를 주입받아 초기화
public AuthController(UserManager<IdentityUser> userManager)
{
this.userManager = userManager;
}
// Post: /api/Auth/Register
[HttpPost]
[Route("Register")]
public async Task<IActionResult> Register([FromBody] RegisterRequestDto registerRequestDto)
{
// 새로운 IdentityUser 생성
var identityUser = new IdentityUser
{
UserName = registerRequestDto.Username,
Email = registerRequestDto.Username
};
// UserManager를 사용하여 사용자 생성
var identityResult = await userManager.CreateAsync(identityUser, registerRequestDto.Password);
if (identityResult.Succeeded)
{
// 사용자에게 역할(Role) 추가
if (registerRequestDto.Roles != null && registerRequestDto.Roles.Any())
{
identityResult = await userManager.AddToRolesAsync(identityUser, registerRequestDto.Roles);
if (identityResult.Succeeded)
{
return Ok("User was registered! Please login."); // 사용자 등록 성공 메시지 반환
}
}
}
return BadRequest("Something went wrong!"); // 오류 발생 시 반환 메시지
}
// Post: /api/Auth/Login
[HttpPost]
[Route("Login")]
public async Task<IActionResult> Login([FromBody] LoginRequestDto loginRequestDto)
{
// 사용자 이메일로 사용자 찾기
var user = await userManager.FindByEmailAsync(loginRequestDto.Username);
if (user != null)
{
// 사용자의 비밀번호 확인
var checkPasswordResult = await userManager.CheckPasswordAsync(user, loginRequestDto.Password);
if (checkPasswordResult)
{
// 토큰 생성 로직
return Ok(); // 성공 응답
}
}
return BadRequest("Username or Password incorrect"); // 사용자 이름 또는 비밀번호가 잘못된 경우 오류 응답
}
}
}
LoginRequestDto.cs
using System.ComponentModel.DataAnnotations;
namespace NZWalks.API.Models.DTO
{
public class LoginRequestDto
{
[Required]
[DataType(DataType.EmailAddress)]
public string Username { get; set; }
[Required]
[DataType(DataType.Password)]
public string Password { get; set; }
}
}


JWT 토큰 생성 및 적용
JWT 토큰 생성
TokenRepository.cs
using Microsoft.AspNetCore.Identity;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
namespace NZWalks.API.Repositories
{
public class TokenRepository : ITokenRepository
{
private readonly IConfiguration configuration;
// IConfiguration 주입받아 초기화
public TokenRepository(IConfiguration configuration)
{
this.configuration = configuration;
}
// JWT 토큰 생성 메서드
public string CreateJWTToken(IdentityUser user, List<string> roles)
{
// 클레임 생성
// Claim은 사용자의 특정 속성이나 권한을 나타내는 정보 조각
// JWT 토큰에서는 이러한 클레임을 사용하여 사용자의 신원과 권한을 나타냄
var claims = new List<Claim>
{
new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
new Claim(ClaimTypes.Email, user.Email),
new Claim(ClaimTypes.Name, user.UserName)
};
// 역할(Role)을 클레임에 추가
foreach (var role in roles)
{
claims.Add(new Claim(ClaimTypes.Role, role));
}
// 대칭 보안 키 생성 (Jwt:Key 사용)
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configuration["Jwt:Key"]));
// 서명 자격 증명 생성 (HmacSha256 알고리즘 사용)
var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
// JWT 토큰 생성
var token = new JwtSecurityToken(
configuration["Jwt:Issuer"], // 유효한 발행자
configuration["Jwt:Audience"], // 유효한 수신자
claims, // 클레임
expires: DateTime.Now.AddMinutes(15), // 토큰 만료 시간 (현재 시간부터 15분 후)
signingCredentials: credentials // 서명 자격 증명
);
// 토큰 문자열로 반환
return new JwtSecurityTokenHandler().WriteToken(token);
}
}
}
ITokenRepository.cs
using Microsoft.AspNetCore.Identity;
namespace NZWalks.API.Repositories
{
public interface ITokenRepository
{
string CreateJWTToken(IdentityUser user, List<string> roles)
}
}
JWT 토큰 적용
program.cs 수정
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore; // Entity Framework Core 기능을 사용하기 위한 네임스페이스
using Microsoft.IdentityModel.Tokens;
using NZWalks.API.Data;
using NZWalks.API.Mappings;
using NZWalks.API.Models.Domain;
using NZWalks.API.Repositories;
using System.Text; // 데이터 컨텍스트를 사용하기 위한 네임스페이스
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")));
// DbContext 서비스를 추가하여 SQL Server를 사용하도록 설정
builder.Services.AddDbContext<NZWalksAuthDbcontext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("NZWalksAuthConnectionString")));
builder.Services.AddScoped<IRegionRepository, SQLRegionRepository>(); // Main DB Region
builder.Services.AddScoped<IWalkRepository, SQLWalkRepository>(); // Main DB Walk
builder.Services.AddScoped<ITokenRepository, TokenRepository>(); // Token - Controller에서 사용가능
// builder.Services.AddScoped<IRegionRepository, InMemoryRegionRepository>(); // Test DB (In Memory)
builder.Services.AddAutoMapper(typeof(AutoMapperProfiles));
// Identity Core 서비스를 추가하여 IdentityUser를 사용하도록 설정
builder.Services.AddIdentityCore<IdentityUser>()
// IdentityRole을 추가하여 역할 관리를 지원
.AddRoles<IdentityRole>()
// 데이터 보호 토큰 제공자를 추가하여 "NZWalks"라는 이름의 토큰 제공자 설정
.AddTokenProvider<DataProtectorTokenProvider<IdentityUser>>("NZWalks")
// Entity Framework 스토어를 사용하여 NZWalksAuthDbcontext를 통한 저장소 관리
.AddEntityFrameworkStores<NZWalksAuthDbcontext>()
// 기본 토큰 제공자 추가
.AddDefaultTokenProviders();
// Identity 옵션을 구성하여 비밀번호 정책 설정
builder.Services.Configure<IdentityOptions>(options =>
{
options.Password.RequireDigit = false; // 비밀번호에 숫자 필요 없음
options.Password.RequireLowercase = false; // 비밀번호에 소문자 필요 없음
options.Password.RequireNonAlphanumeric = false; // 비밀번호에 알파벳 이외의 문자 필요 없음
options.Password.RequireUppercase = false; // 비밀번호에 대문자 필요 없음
options.Password.RequiredLength = 6; // 비밀번호 최소 길이 6자
options.Password.RequiredUniqueChars = 1; // 비밀번호에 최소 1개의 고유 문자 필요
});
// JWT 인증 설정 추가
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) // JWT Bearer 인증 스키마 사용
.AddJwtBearer(options => // JWT Bearer 인증 설정
options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
{
ValidateIssuer = true, // 발행자(issuer) 유효성 검사 활성화
ValidateAudience = true, // 수신자(audience) 유효성 검사 활성화
ValidateLifetime = true, // 토큰의 유효 기간 검사 활성화
ValidateIssuerSigningKey = true, // 서명 키 유효성 검사 활성화
ValidIssuer = builder.Configuration["Jwt:Issuer"], // 유효한 발행자 설정
ValidAudience = builder.Configuration["Jwt:Audience"], // 유효한 수신자 설정
IssuerSigningKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"])) // 서명 키 설정
});
var app = builder.Build(); // 애플리케이션 빌드
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
// 개발 환경에서만 Swagger를 사용하도록 설정
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection(); // HTTP 요청을 HTTPS로 리디렉션
app.UseAuthentication(); // JWT 인증 미들웨어 추가
// 이 코드는 JWT 인증을 처리하여 사용자 요청을 검증함
app.UseAuthorization(); // 권한 부여 미들웨어 사용
// 인증된 사용자의 권한을 검증함
app.MapControllers(); // 컨트롤러에 대한 요청을 매핑
app.Run(); // 애플리케이션 실행


AuthController.cs
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using NZWalks.API.Models.DTO;
using NZWalks.API.Repositories;
namespace NZWalks.API.Controllers
{
[Route("api/[controller]")] // 컨트롤러의 기본 경로를 설정. [controller]는 컨트롤러 이름으로 대체됨.
[ApiController] // API 컨트롤러 특성 적용
public class AuthController : ControllerBase
{
private readonly UserManager<IdentityUser> userManager;
private readonly ITokenRepository tokenRepository;
// UserManager와 ITokenRepository를 주입받아 초기화
public AuthController(UserManager<IdentityUser> userManager, ITokenRepository tokenRepository)
{
this.userManager = userManager;
this.tokenRepository = tokenRepository;
}
// Post: /api/Auth/Register
[HttpPost]
[Route("Register")]
public async Task<IActionResult> Register([FromBody] RegisterRequestDto registerRequestDto)
{
// 새로운 IdentityUser 생성
var identityUser = new IdentityUser
{
UserName = registerRequestDto.Username,
Email = registerRequestDto.Username
};
// UserManager를 사용하여 사용자 생성
var identityResult = await userManager.CreateAsync(identityUser, registerRequestDto.Password);
if (identityResult.Succeeded)
{
// 사용자에게 역할(Role) 추가
if (registerRequestDto.Roles != null && registerRequestDto.Roles.Any())
{
identityResult = await userManager.AddToRolesAsync(identityUser, registerRequestDto.Roles);
if (identityResult.Succeeded)
{
return Ok("User was registered! Please login."); // 사용자 등록 성공 메시지 반환
}
}
}
return BadRequest("Something went wrong!"); // 오류 발생 시 반환 메시지
}
// Post: /api/Auth/Login
[HttpPost]
[Route("Login")]
public async Task<IActionResult> Login([FromBody] LoginRequestDto loginRequestDto)
{
// 사용자 이메일로 사용자 찾기
var user = await userManager.FindByEmailAsync(loginRequestDto.Username);
if (user != null)
{
// 사용자의 비밀번호 확인
var checkPasswordResult = await userManager.CheckPasswordAsync(user, loginRequestDto.Password);
if (checkPasswordResult)
{
// 사용자 역할(Role) 가져오기
var roles = await userManager.GetRolesAsync(user);
if (roles != null)
{
// 토큰 생성 로직
var jwtToken = tokenRepository.CreateJWTToken(user, roles.ToList());
var response = new LoginResposeDto
{
JwtToken = jwtToken
};
return Ok(response); // 생성된 토큰을 포함한 응답 반환
}
}
}
return BadRequest("Username or Password incorrect"); // 사용자 이름 또는 비밀번호가 잘못된 경우 오류 응답
}
}
}
LoginResposeDto.cs
namespace NZWalks.API.Models.DTO
{
public class LoginResposeDto
{
public string JwtToken { get; set; }
}
}






RegionsController.cs – Role에 따른 접근으로 변경
using Microsoft.AspNetCore.Mvc;
using NZWalks.API.Data;
using NZWalks.API.Models.Domain;
using NZWalks.API.Models.DTO;
using AutoMapper;
using NZWalks.API.CustomActionFilter;
using Microsoft.AspNetCore.Authorization;
namespace NZWalks.API.Controllers
{
[Route("api/[controller]")] // 컨트롤러의 기본 Route를 설정. [controller]는 컨트롤러 이름으로 대체됨.
[ApiController] // API 컨트롤러 특성 적용
public class RegionsController : ControllerBase
{
private readonly NZWalksDbcontext dbcontext;
private readonly IRegionRepository regionRepository;
private readonly IMapper mapper;
public RegionsController(NZWalksDbcontext dbContext, IRegionRepository regionRepository, IMapper mapper)
{
this.dbcontext = dbContext;
this.regionRepository = regionRepository;
this.mapper = mapper;
}
// GET ALL REGIONS
// GET: https://localhost:1234/api/regions
[HttpGet]
[Authorize(Roles = "Reader, writer")] // Reader, writer에서 인증된 사용자만 접근 가능
public async Task<IActionResult> GetAll()
{
// Get Data From Database - Domain models
// var regionDomain = await dbcontext.Regions.ToListAsync(); // Change Async
var regionDomain = await regionRepository.GetAllAsync();
// 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
// });
// }
// Map Domain Models to DTOs
var regionDto = mapper.Map<List<RegionDto>>(regionDomain);
return Ok(regionDto);
}
// GET SINGLE REGION (Get Region By ID)
// GET: https://localhost:1234/api/regions/{id}
[HttpGet]
[Route("{id:guid}")]
[Authorize(Roles = "Reader")] // Reader에서 인증된 사용자만 접근 가능
public async Task<IActionResult> GetById([FromRoute]Guid id)
{
// Get Data From Database - Domain models
// var region = dbcontext.Regions.Find(id);
// var regionDomain = await dbcontext.Regions.FirstOrDefaultAsync(x => x.Id == id);
var regionDomain = await regionRepository.GetByIdAsync(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
// };
var regionDto = mapper.Map<RegionDto>(regionDomain);
return Ok(regionDto);
}
// POST To Create New Region
// POST : https://localhost:1234/api/regions
[HttpPost]
[ValidateModel]
[Authorize(Roles = "writer")] // writer에서 인증된 사용자만 접근 가능
public async Task<IActionResult> Create([FromBody] AddRegionRequestDto addRegionRequestDto)
{
/* [ValidateModel] 적용전
if (ModelState.IsValid) // [ValidateModel]
{
// [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
//};
// Map or Convert DTO to Domain Model
var regionDomainModel = mapper.Map<Region>(addRegionRequestDto);
// Use Domain Model to create Region
// await dbcontext.Regions.AddAsync(regionDomainModel);
// await dbcontext.SaveChangesAsync();
await regionRepository.CreateAsync(regionDomainModel);
// Map Domain model back to DTO
//var regionDto = new RegionDto
//{
// Id = regionDomainModel.Id,
// Code = addRegionRequestDto.Code,
// Name = regionDomainModel.Name,
// RegionImageUrl = regionDomainModel.RegionImageUrl
//};
var regionDto = mapper.Map<RegionDto>(regionDomainModel);
// 201 Created 응답으로 생성된 DTO 반환
return CreatedAtAction(nameof(GetById), new { id = regionDto.Id }, regionDto);
// CreatedAtAction은 ASP.NET Core에서 HTTP POST 요청 후 201 Created 상태 코드를 반환할 때 사용되는 메서드
// 새로운 리소스를 생성한 후, 해당 리소스에 접근할 수 있는 URI를 함께 제공하여 클라이언트가 리소스를 쉽게 찾을 수 있음
}
else
{
return BadRequest(ModelState);
}
*/
// [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
//};
// Map or Convert DTO to Domain Model
var regionDomainModel = mapper.Map<Region>(addRegionRequestDto);
// Use Domain Model to create Region
// await dbcontext.Regions.AddAsync(regionDomainModel);
// await dbcontext.SaveChangesAsync();
await regionRepository.CreateAsync(regionDomainModel);
// Map Domain model back to DTO
//var regionDto = new RegionDto
//{
// Id = regionDomainModel.Id,
// Code = addRegionRequestDto.Code,
// Name = regionDomainModel.Name,
// RegionImageUrl = regionDomainModel.RegionImageUrl
//};
var regionDto = mapper.Map<RegionDto>(regionDomainModel);
// 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}")]
[ValidateModel]
[Authorize(Roles = "writer")] // writer에서 인증된 사용자만 접근 가능
public async Task<IActionResult> Update([FromRoute] Guid id, [FromBody] UpdateRegionRequestDto updateRegionRequestDto)
{
/* [ValidateModel] 적용 전
if (ModelState.IsValid)
{
// Map
//var regionDomainModel = new Region
//{
// Code = updateRegionRequestDto.Code,
// Name = updateRegionRequestDto.Name,
// RegionImageUrl = updateRegionRequestDto.RegionImageUrl
//};
var regionDomainModel = mapper.Map<Region>(updateRegionRequestDto);
// Check if region exists
// var regionDomainModel = await dbcontext.Regions.FirstOrDefaultAsync(x => x.Id == id);
regionDomainModel = await regionRepository.UpdateAsync(id, regionDomainModel);
if (regionDomainModel == null)
{
return NotFound();
}
//// Map DTO to Domain model
//regionDomainModel.Code = updateRegionRequestDto.Code;
//regionDomainModel.Name = updateRegionRequestDto.Name;
//regionDomainModel.RegionImageUrl = updateRegionRequestDto.RegionImageUrl;
//await dbcontext.SaveChangesAsync();
// Convert Domain Model to DTO
//var regionDto = new RegionDto
//{
// Id = regionDomainModel.Id,
// Code = regionDomainModel.Code,
// Name = regionDomainModel.Name,
// RegionImageUrl = regionDomainModel.RegionImageUrl
//};
var regionDto = mapper.Map<RegionDto>(regionDomainModel);
return Ok(regionDto);
}
else
{
return BadRequest(ModelState);
}
*/
// Map
//var regionDomainModel = new Region
//{
// Code = updateRegionRequestDto.Code,
// Name = updateRegionRequestDto.Name,
// RegionImageUrl = updateRegionRequestDto.RegionImageUrl
//};
var regionDomainModel = mapper.Map<Region>(updateRegionRequestDto);
// Check if region exists
// var regionDomainModel = await dbcontext.Regions.FirstOrDefaultAsync(x => x.Id == id);
regionDomainModel = await regionRepository.UpdateAsync(id, regionDomainModel);
if (regionDomainModel == null)
{
return NotFound();
}
//// Map DTO to Domain model
//regionDomainModel.Code = updateRegionRequestDto.Code;
//regionDomainModel.Name = updateRegionRequestDto.Name;
//regionDomainModel.RegionImageUrl = updateRegionRequestDto.RegionImageUrl;
//await dbcontext.SaveChangesAsync();
// Convert Domain Model to DTO
//var regionDto = new RegionDto
//{
// Id = regionDomainModel.Id,
// Code = regionDomainModel.Code,
// Name = regionDomainModel.Name,
// RegionImageUrl = regionDomainModel.RegionImageUrl
//};
var regionDto = mapper.Map<RegionDto>(regionDomainModel);
return Ok(regionDto);
}
// Delete Region
// Delete https://localhost:1234/api/regions/{id}
[HttpDelete]
[Route("{id:guid}")]
[Authorize(Roles = "writer")] // writer에서 인증된 사용자만 접근 가능
public async Task<IActionResult> Update([FromRoute] Guid id)
{
//var regionDomainModel = await dbcontext.Regions.FirstOrDefaultAsync(x => x.Id == id);
var regionDomainModel = await regionRepository.DeleteAsync(id);
if (regionDomainModel == null)
{
return NotFound();
}
// dbcontext.Regions.Remove(regionDomainModel);
// await dbcontext.SaveChangesAsync();
// 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
//};
var regionDto = mapper.Map<Region>(regionDomainModel);
return Ok(regionDto);
}
}
}




Swagger 적용
Program.cs
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore; // Entity Framework Core 기능을 사용하기 위한 네임스페이스
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using NZWalks.API.Data;
using NZWalks.API.Mappings;
using NZWalks.API.Models.Domain;
using NZWalks.API.Repositories;
using System.Text; // 데이터 컨텍스트를 사용하기 위한 네임스페이스
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 문서를 자동 생성
// Swagger 생성을 위한 서비스 추가
builder.Services.AddSwaggerGen(options => {
// 제목과 버전을 정의하여 Swagger 문서 설정
options.SwaggerDoc("v1", new Microsoft.OpenApi.Models.OpenApiInfo
{
Title = "NZ Walks API",
Version = "v1"
});
// JWT Bearer 보안 정의 추가
options.AddSecurityDefinition(JwtBearerDefaults.AuthenticationScheme,
new Microsoft.OpenApi.Models.OpenApiSecurityScheme()
{
Name = "Authorization", // 인증 헤더 이름
In = ParameterLocation.Header, // 인증 정보의 위치 (헤더)
Type = SecuritySchemeType.ApiKey, // 인증 유형 (API 키)
Scheme = JwtBearerDefaults.AuthenticationScheme // 스키마 설정
}
);
// 정의된 보안 스키마를 사용한 보안 요구 사항 추가
options.AddSecurityRequirement(new OpenApiSecurityRequirement {
{
new OpenApiSecurityScheme {
Reference = new OpenApiReference {
Type = ReferenceType.SecurityScheme, // 참조 유형 설정
Id = JwtBearerDefaults.AuthenticationScheme // 참조 ID 설정
},
Scheme = "Oauth2", // 스키마 설정
Name = JwtBearerDefaults.AuthenticationScheme, // 스키마 이름
In = ParameterLocation.Header // 위치 설정
},
new List<string>()
}
});
});
// DbContext 서비스를 추가하여 SQL Server를 사용하도록 설정
builder.Services.AddDbContext<NZWalksDbcontext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("NZWalksConnectionString")));
// DbContext 서비스를 추가하여 SQL Server를 사용하도록 설정
builder.Services.AddDbContext<NZWalksAuthDbcontext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("NZWalksAuthConnectionString")));
builder.Services.AddScoped<IRegionRepository, SQLRegionRepository>(); // Main DB Region
builder.Services.AddScoped<IWalkRepository, SQLWalkRepository>(); // Main DB Walk
builder.Services.AddScoped<ITokenRepository, TokenRepository>(); // Token - Controller에서 사용가능
// builder.Services.AddScoped<IRegionRepository, InMemoryRegionRepository>(); // Test DB (In Memory)
builder.Services.AddAutoMapper(typeof(AutoMapperProfiles));
// Identity Core 서비스를 추가하여 IdentityUser를 사용하도록 설정
builder.Services.AddIdentityCore<IdentityUser>()
// IdentityRole을 추가하여 역할 관리를 지원
.AddRoles<IdentityRole>()
// 데이터 보호 토큰 제공자를 추가하여 "NZWalks"라는 이름의 토큰 제공자 설정
.AddTokenProvider<DataProtectorTokenProvider<IdentityUser>>("NZWalks")
// Entity Framework 스토어를 사용하여 NZWalksAuthDbcontext를 통한 저장소 관리
.AddEntityFrameworkStores<NZWalksAuthDbcontext>()
// 기본 토큰 제공자 추가
.AddDefaultTokenProviders();
// Identity 옵션을 구성하여 비밀번호 정책 설정
builder.Services.Configure<IdentityOptions>(options =>
{
options.Password.RequireDigit = false; // 비밀번호에 숫자 필요 없음
options.Password.RequireLowercase = false; // 비밀번호에 소문자 필요 없음
options.Password.RequireNonAlphanumeric = false; // 비밀번호에 알파벳 이외의 문자 필요 없음
options.Password.RequireUppercase = false; // 비밀번호에 대문자 필요 없음
options.Password.RequiredLength = 6; // 비밀번호 최소 길이 6자
options.Password.RequiredUniqueChars = 1; // 비밀번호에 최소 1개의 고유 문자 필요
});
// JWT 인증 설정 추가
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) // JWT Bearer 인증 스키마 사용
.AddJwtBearer(options => // JWT Bearer 인증 설정
options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
{
ValidateIssuer = true, // 발행자(issuer) 유효성 검사 활성화
ValidateAudience = true, // 수신자(audience) 유효성 검사 활성화
ValidateLifetime = true, // 토큰의 유효 기간 검사 활성화
ValidateIssuerSigningKey = true, // 서명 키 유효성 검사 활성화
ValidIssuer = builder.Configuration["Jwt:Issuer"], // 유효한 발행자 설정
ValidAudience = builder.Configuration["Jwt:Audience"], // 유효한 수신자 설정
IssuerSigningKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"])) // 서명 키 설정
});
var app = builder.Build(); // 애플리케이션 빌드
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
// 개발 환경에서만 Swagger를 사용하도록 설정
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection(); // HTTP 요청을 HTTPS로 리디렉션
app.UseAuthentication(); // JWT 인증 미들웨어 추가
// 이 코드는 JWT 인증을 처리하여 사용자 요청을 검증함
app.UseAuthorization(); // 권한 부여 미들웨어 사용
// 인증된 사용자의 권한을 검증함
app.MapControllers(); // 컨트롤러에 대한 요청을 매핑
app.Run(); // 애플리케이션 실행








