1. Domain Model 생성
Image.cs
using System.ComponentModel.DataAnnotations.Schema;
namespace NZWalks.API.Models.Domain
{
public class Image
{
public Guid Id { get; set; } // 이미지의 고유 식별자
[NotMapped] // Entity Framework가 해당 속성을 데이터베이스 테이블에 매핑하지 않도록 지정
public IFormFile File { get; set; } // File Data 객체를 나타내는 IFormFile 객체
public string Filename { get; set; } // 파일 이름
public string? FileDescription { get; set; } // 파일 설명 (nullable)
public string FileExtension { get; set; } // 파일 확장자 (예: .jpg, .png)
public long FileSizeInBytes { get; set; } // 파일 크기 (바이트 단위)
public string FilePath { get; set; } // 파일 저장 경로
}
}

2. DBContext 생성

// Package Manager Console Add-Migration "Adding Images Table" -Context "NZWalksDbcontext" Update-Database -Context "NZWalksDbcontext"


3. Image Controller 및 Method 생성
ImageController.cs
using Microsoft.AspNetCore.Mvc;
using NZWalks.API.Models.Domain;
using NZWalks.API.Models.DTO;
using NZWalks.API.Repositories;
namespace NZWalks.API.Controllers
{
[Route("api/[controller]")] // 컨트롤러의 기본 경로를 설정. [controller]는 컨트롤러 이름으로 대체됨.
[ApiController] // API 컨트롤러 특성 적용
public class ImageController : ControllerBase
{
private readonly IImageRepository imageRepository;
// IImageRepository를 주입받아 초기화
public ImageController(IImageRepository imageRepository)
{
this.imageRepository = imageRepository;
}
// POST: /api/Images/Upload
[HttpPost]
[Route("Upload")]
public async Task<IActionResult> Upload([FromForm] ImageUploadRequestDto request)
{
// 파일 업로드 유효성 검사
ValidateFileUpload(request);
if (ModelState.IsValid)
{
// DTO를 도메인 모델로 변환
var imageDomainModel = new Image
{
File = request.File,
FileExtension = Path.GetExtension(request.File.FileName),
FileSizeInBytes = request.File.Length,
Filename = request.File.FileName,
FileDescription = request.FileDescription
};
// 이미지 업로드를 위해 리포지토리 사용
await imageRepository.Uploade(imageDomainModel);
return Ok(imageDomainModel); // 업로드된 이미지 정보 반환
}
return BadRequest(ModelState); // 유효성 검사 실패 시 오류 반환
}
// 파일 업로드 유효성 검사 메서드
private void ValidateFileUpload(ImageUploadRequestDto request)
{
var allowedExtensions = new string[] { ".jpg", ".jpeg", ".Png" }; // 허용되는 확장자 목록
// 파일 확장자 검사
if (!allowedExtensions.Contains(Path.GetExtension(request.File.FileName)))
{
ModelState.AddModelError("file", "Unsupported file extension"); // 지원되지 않는 파일 확장자 오류 추가
}
// 파일 크기 검사 (10MB 초과 여부)
if (request.File.Length > 10485760) // 10MB
{
ModelState.AddModelError("file", "File Size more than 10MB, Please upload a smaller size file"); // 파일 크기 초과 오류 추가
}
}
}
}
ImageUploadRequestDto.cs
using System.ComponentModel.DataAnnotations;
namespace NZWalks.API.Models.DTO
{
public class ImageUploadRequestDto
{
[Required]
public IFormFile File { get; set; }
[Required]
public string Filename { get; set; }
public string? FileDescription { get; set; }
}
}
IImageRepository.cs
using NZWalks.API.Models.Domain;
namespace NZWalks.API.Repositories
{
public interface IImageRepository
{
Task<Image> Uploade(Image image);
}
}
LocalImageRepository.cs
using NZWalks.API.Data; // 데이터 컨텍스트를 사용하기 위한 네임스페이스
using NZWalks.API.Models.Domain; // 도메인 모델을 사용하기 위한 네임스페이스
namespace NZWalks.API.Repositories
{
public class LocalImageRepository : IImageRepository
{
private readonly IWebHostEnvironment webHostEnvironment;
private readonly IHttpContextAccessor httpContextAccessor;
private readonly NZWalksDbcontext dbcontext;
// LocalImageRepository 생성자
// 필요한 종속성들(IWebHostEnvironment, IHttpContextAccessor, NZWalksDbcontext)을 주입받아 초기화
public LocalImageRepository(IWebHostEnvironment webHostEnvironment, IHttpContextAccessor httpContextAccessor, NZWalksDbcontext dbcontext)
{
this.webHostEnvironment = webHostEnvironment;
this.httpContextAccessor = httpContextAccessor;
this.dbcontext = dbcontext;
}
// 이미지 업로드 메서드
public async Task<Image> Uploade(Image image)
{
// 로컬 파일 경로 생성
var localFilePath = Path.Combine(webHostEnvironment.ContentRootPath, "Images", $"{Path.GetFileNameWithoutExtension(image.Filename)}{image.FileExtension}");
// 로컬 경로에 이미지 업로드
using var stream = new FileStream(localFilePath, FileMode.Create);
await image.File.CopyToAsync(stream);
// URL 경로 생성
var urlFilePath = $"{httpContextAccessor.HttpContext.Request.Scheme}://{httpContextAccessor.HttpContext.Request.Host}{httpContextAccessor.HttpContext.Request.PathBase}/Images/{Path.GetFileNameWithoutExtension(image.Filename)}{image.FileExtension}";
image.FilePath = urlFilePath;
// 이미지 정보를 Images 테이블에 추가
await dbcontext.Images.AddAsync(image);
await dbcontext.SaveChangesAsync();
return image; // 업로드된 이미지 반환
}
}
}
Program.cs 변경
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore; // Entity Framework Core 기능을 사용하기 위한 네임스페이스
using Microsoft.Extensions.FileProviders;
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 패턴을 지원
builder.Services.AddHttpContextAccessor(); // HttpContextAccessor를 서비스 컨테이너에 추가
// 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<IImageRepository, LocalImageRepository>(); // Image DB
// builder.Services.AddScoped<IRegionRepository, InMemoryRegionRepository>(); // Test DB (In Memory)
builder.Services.AddAutoMapper(typeof(AutoMapperProfiles)); // AutoMapper 프로필 추가
// 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.UseStaticFiles(new StaticFileOptions {
FileProvider = new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(), "Images")),
RequestPath = "/Images"
});
app.UseAuthorization(); // 권한 부여 미들웨어 사용
// 인증된 사용자의 권한을 검증함
app.MapControllers(); // 컨트롤러에 대한 요청을 매핑
app.Run(); // 애플리케이션 실행










