728x90
반응형
728x170
▶ Models/Movie.cs
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace TestProject.Models
{
/// <summary>
/// 영화
/// </summary>
public class Movie
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Property
////////////////////////////////////////////////////////////////////////////////////////// Public
#region ID - ID
/// <summary>
/// ID
/// </summary>
public int ID { get; set; }
#endregion
#region 제목 - Title
/// <summary>
/// 제목
/// </summary>
[Required]
[StringLength(60, MinimumLength = 3)]
public string Title { get; set; }
#endregion
#region 릴리즈 일자 - ReleaseDate
/// <summary>
/// 릴리즈 일자
/// </summary>
[DataType(DataType.Date)]
[Display(Name = "Release Date")]
public DateTime ReleaseDate { get; set; }
#endregion
#region 장르 - Genre
/// <summary>
/// 장르
/// </summary>
[Required]
[StringLength(30)]
public string Genre { get; set; }
#endregion
#region 가격 - Price
/// <summary>
/// 가격
/// </summary>
[DataType(DataType.Currency)]
[Column(TypeName = "decimal(18, 2)")]
[Range(1, 100)]
public decimal Price { get; set; }
#endregion
#region 등급 - Rating
/// <summary>
/// 등급
/// </summary>
[Required]
[StringLength(5)]
[RegularExpression(@"^[A-Z]+[a-zA-Z0-9""'\s-]*$")]
public string Rating { get; set; }
#endregion
}
}
728x90
▶ Models/MovieData.cs
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Linq;
using TestProject.Data;
namespace TestProject.Models
{
/// <summary>
/// 영화 데이터
/// </summary>
public static class MovieData
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 초기화하기 - Initialize(serviceProvider)
/// <summary>
/// 초기화하기
/// </summary>
/// <param name="serviceProvider">서비스 제공자</param>
public static void Initialize(IServiceProvider serviceProvider)
{
DbContextOptions<DatabaseContext> options = serviceProvider.GetRequiredService<DbContextOptions<DatabaseContext>>();
using(DatabaseContext context = new DatabaseContext(options))
{
if(context.Movie.Any())
{
return;
}
context.Movie.AddRange
(
new Movie
{
Title = "When Harry Met Sally",
ReleaseDate = DateTime.Parse("1989-2-12"),
Genre = "Romantic Comedy",
Price = 7.99M,
Rating = "A"
},
new Movie
{
Title = "Ghostbusters ",
ReleaseDate = DateTime.Parse("1984-3-13"),
Genre = "Comedy",
Price = 8.99M,
Rating = "A"
},
new Movie
{
Title = "Ghostbusters 2",
ReleaseDate = DateTime.Parse("1986-2-23"),
Genre = "Comedy",
Price = 9.99M,
Rating = "A"
},
new Movie
{
Title = "Rio Bravo",
ReleaseDate = DateTime.Parse("1959-4-15"),
Genre = "Western",
Price = 3.99M,
Rating = "A"
}
);
context.SaveChanges();
}
}
#endregion
}
}
300x250
▶ Models/MovieGenre.cs
using Microsoft.AspNetCore.Mvc.Rendering;
using System.Collections.Generic;
namespace TestProject.Models
{
/// <summary>
/// 영화 장르
/// </summary>
public class MovieGenre
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Property
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 영화 리스트 - MovieList
/// <summary>
/// 영화 리스트
/// </summary>
public List<Movie> MovieList { get; set; }
#endregion
#region 장르 선택 리스트 - GenreSelectList
/// <summary>
/// 장르 선택 리스트
/// </summary>
public SelectList GenreSelectList { get; set; }
#endregion
#region 검색 장르 - SearchGenre
/// <summary>
/// 검색 장르
/// </summary>
public string SearchGenre { get; set; }
#endregion
#region 검색 문자열 - SearchString
/// <summary>
/// 검색 문자열
/// </summary>
public string SearchString { get; set; }
#endregion
}
}
▶ Data/DatabaseContext.cs
using Microsoft.EntityFrameworkCore;
using TestProject.Models;
namespace TestProject.Data
{
/// <summary>
/// 데이터베이스 컨텍스트
/// </summary>
public class DatabaseContext : DbContext
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Property
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 영화 - Movie
/// <summary>
/// 영화
/// </summary>
public DbSet<Movie> Movie { get; set; }
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 생성자 - DatabaseContext(options)
/// <summary>
/// 생성자
/// </summary>
/// <param name="options">옵션</param>
public DatabaseContext(DbContextOptions<DatabaseContext> options) : base(options)
{
}
#endregion
}
}
▶ appsettings.json
{
"Logging" :
{
"LogLevel" :
{
"Default" : "Information",
"Microsoft" : "Warning",
"Microsoft.Hosting.Lifetime" : "Information"
}
},
"AllowedHosts" : "*",
"ConnectionStrings" :
{
"DefaultConnection" : "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=TestDB;Integrated Security=True;"
}
}
▶ 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 TestProject.Data;
namespace TestProject
{
/// <summary>
/// 시작
/// </summary>
public class Startup
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Property
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 구성 - Configuration
/// <summary>
/// 구성
/// </summary>
public IConfiguration Configuration { get; }
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 생성자 - Startup(configuration)
/// <summary>
/// 생성자
/// </summary>
/// <param name="configuration">구성</param>
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 서비스 컬렉션 구성하기 - ConfigureServices(services)
/// <summary>
/// 서비스 컬렉션 구성하기
/// </summary>
/// <param name="services">서비스 컬렉션</param>
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<DatabaseContext>
(
options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))
);
services.AddControllersWithViews();
}
#endregion
#region 구성하기 - Configure(app, environment)
/// <summary>
/// 구성하기
/// </summary>
/// <param name="app">애플리케이션 빌더</param>
/// <param name="environment">웹 호스트 환경</param>
public void Configure(IApplicationBuilder app, IWebHostEnvironment environment)
{
if(environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints
(
endpoints =>
{
endpoints.MapControllerRoute
(
name : "default",
pattern : "{controller=Home}/{action=Index}/{id?}"
);
}
);
}
#endregion
}
}
▶ Program.cs
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using TestProject.Models;
namespace TestProject
{
/// <summary>
/// 프로그램
/// </summary>
public class Program
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Static
//////////////////////////////////////////////////////////////////////////////// Public
#region 프로그램 시작하기 - Main(argumentArray)
/// <summary>
/// 프로그램 시작하기
/// </summary>
/// <param name="argumentArray">인자 배열</param>
public static void Main(string[] argumentArray)
{
IHost host = CreateHostBuilder(argumentArray).Build();
using(IServiceScope scope = host.Services.CreateScope())
{
IServiceProvider serviceProvider = scope.ServiceProvider;
try
{
MovieData.Initialize(serviceProvider);
}
catch(Exception exception)
{
ILogger<Program> logger = serviceProvider.GetRequiredService<ILogger<Program>>();
logger.LogError(exception, "데이터베이스에 데이터 추가시 에러가 발생했습니다.");
}
}
host.Run();
}
#endregion
#region 호스트 빌더 생성하기 - CreateHostBuilder(argumentArray)
/// <summary>
/// 호스트 빌더 생성하기
/// </summary>
/// <param name="argumentArray">인자 배열</param>
/// <returns>호스트 빌더</returns>
public static IHostBuilder CreateHostBuilder(string[] argumentArray) =>
Host.CreateDefaultBuilder(argumentArray)
.ConfigureWebHostDefaults
(
builder =>
{
builder.UseStartup<Startup>();
}
);
#endregion
}
}
▶ Controllers/MovieController.cs
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using TestProject.Data;
using TestProject.Models;
namespace TestProject.Controllers
{
/// <summary>
/// 영화 컨트롤러
/// </summary>
public class MovieController : Controller
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Field
////////////////////////////////////////////////////////////////////////////////////////// Private
#region Field
/// <summary>
/// 컨텍스트
/// </summary>
private readonly DatabaseContext context;
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 생성자 - MovieController(context)
/// <summary>
/// 생성자
/// </summary>
/// <param name="context">컨텍스트</param>
public MovieController(DatabaseContext context)
{
this.context = context;
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 인덱스 페이지 처리하기 - Index(searchGenre, searchString)
/// <summary>
/// 인덱스 페이지 처리하기
/// </summary>
/// <param name="searchGenre">검색 장르</param>
/// <param name="searchString">검색 문자열</param>
/// <returns>액션 결과 태스크</returns>
public async Task<IActionResult> Index(string searchGenre, string searchString)
{
var genreQueryable = from movie in this.context.Movie
orderby movie.Genre
select movie.Genre;
var movieQueryable = from movie in this.context.Movie
select movie;
if(!string.IsNullOrEmpty(searchString))
{
movieQueryable = movieQueryable.Where(s => s.Title.Contains(searchString));
}
if(!string.IsNullOrEmpty(searchGenre))
{
movieQueryable = movieQueryable.Where(x => x.Genre == searchGenre);
}
MovieGenre movieGenre = new MovieGenre
{
GenreSelectList = new SelectList(await genreQueryable.Distinct().ToListAsync()),
MovieList = await movieQueryable.ToListAsync()
};
return View(movieGenre);
}
#endregion
#region 상세 페이지 처리하기 - Details(id)
/// <summary>
/// 상세 페이지 처리하기
/// </summary>
/// <param name="id">ID</param>
/// <returns>액션 결과 태스크</returns>
public async Task<IActionResult> Details(int? id)
{
if(id == null)
{
return NotFound();
}
Movie movie = await this.context.Movie.FirstOrDefaultAsync(m => m.ID == id);
if(movie == null)
{
return NotFound();
}
return View(movie);
}
#endregion
#region 생성 페이지 처리하기 - Create()
/// <summary>
/// 생성 페이지 처리하기
/// </summary>
/// <returns>액션 결과</returns>
public IActionResult Create()
{
return View();
}
#endregion
#region 생성하기 - Create(movie)
/// <summary>
/// 생성하기
/// </summary>
/// <param name="movie">영화</param>
/// <returns>액션 결과 태스크</returns>
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("ID,Title,ReleaseDate,Genre,Price,Rating")] Movie movie)
{
if(ModelState.IsValid)
{
this.context.Add(movie);
await this.context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(movie);
}
#endregion
#region 편집 페이지 처리하기 - Edit(id)
/// <summary>
/// 편집 페이지 처리하기
/// </summary>
/// <param name="id">ID</param>
/// <returns>액션 결과 태스크</returns>
public async Task<IActionResult> Edit(int? id)
{
if(id == null)
{
return NotFound();
}
Movie movie = await this.context.Movie.FindAsync(id);
if(movie == null)
{
return NotFound();
}
return View(movie);
}
#endregion
#region 편집 페이지 처리하기 - Edit(int id, movie)
/// <summary>
/// 편집 페이지 처리하기
/// </summary>
/// <param name="id">ID</param>
/// <param name="movie">영화</param>
/// <returns>액션 결과 태스크</returns>
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("ID,Title,ReleaseDate,Genre,Price,Rating")] Movie movie)
{
if(id != movie.ID)
{
return NotFound();
}
if(ModelState.IsValid)
{
try
{
this.context.Update(movie);
await this.context.SaveChangesAsync();
}
catch(DbUpdateConcurrencyException)
{
if(!MovieExists(movie.ID))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index));
}
return View(movie);
}
#endregion
#region 삭제 페이지 처리하기 - Delete(id)
/// <summary>
/// 삭제 페이지 처리하기
/// </summary>
/// <param name="id">ID</param>
/// <returns>액션 결과 페이지</returns>
public async Task<IActionResult> Delete(int? id)
{
if(id == null)
{
return NotFound();
}
Movie movie = await this.context.Movie.FirstOrDefaultAsync(m => m.ID == id);
if(movie == null)
{
return NotFound();
}
return View(movie);
}
#endregion
#region 삭제 완료시 페이지 처리하기 - DeleteConfirmed(id)
/// <summary>
/// 삭제 완료시 페이지 처리하기
/// </summary>
/// <param name="id">ID</param>
/// <returns>액션 결과 태스크</returns>
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> DeleteConfirmed(int id)
{
Movie movie = await this.context.Movie.FindAsync(id);
this.context.Movie.Remove(movie);
await this.context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
#endregion
////////////////////////////////////////////////////////////////////////////////////////// Private
#region 영화 존재 여부 구하기 - MovieExists(id)
/// <summary>
/// 영화 존재 여부 구하기
/// </summary>
/// <param name="id">ID</param>
/// <returns>영화 존재 여부</returns>
private bool MovieExists(int id)
{
return this.context.Movie.Any(e => e.ID == id);
}
#endregion
}
}
▶ Views/Movie/Index.cshtml
@model TestProject.Models.MovieGenre
@{
ViewData["Title"] = "Index";
}
<h1>Index</h1>
<p>
<a asp-action="Create">Create New</a>
</p>
<form asp-controller="Movie" asp-action="Index" method="get">
<p>
<select asp-for="SearchGenre" asp-items="Model.GenreSelectList">
<option value="">All</option>
</select>
Title : <input type="text" asp-for="SearchString" />
<input type="submit" value="Filter" />
</p>
</form>
<table class="table">
<thead>
<tr>
<th>
@Html.DisplayNameFor(model => model.MovieList[0].Title)
</th>
<th>
@Html.DisplayNameFor(model => model.MovieList[0].ReleaseDate)
</th>
<th>
@Html.DisplayNameFor(model => model.MovieList[0].Genre)
</th>
<th>
@Html.DisplayNameFor(model => model.MovieList[0].Price)
</th>
<th>
@Html.DisplayNameFor(model => model.MovieList[0].Rating)
</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach(Movie movie in Model.MovieList)
{
<tr>
<td>
@Html.DisplayFor(modelItem => movie.Title)
</td>
<td>
@Html.DisplayFor(modelItem => movie.ReleaseDate)
</td>
<td>
@Html.DisplayFor(modelItem => movie.Genre)
</td>
<td>
@Html.DisplayFor(modelItem => movie.Price)
</td>
<td>
@Html.DisplayFor(modelItem => movie.Rating)
</td>
<td>
<a asp-action="Edit" asp-route-id="@movie.ID">Edit</a> |
<a asp-action="Details" asp-route-id="@movie.ID">Details</a> |
<a asp-action="Delete" asp-route-id="@movie.ID">Delete</a>
</td>
</tr>
}
</tbody>
</table>
▶ Views/Movie/Create.cshtml
@model TestProject.Models.Movie
@{
ViewData["Title"] = "Create";
}
<h1>Create</h1>
<h4>MovieModel</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Title" class="control-label"></label>
<input asp-for="Title" class="form-control" />
<span asp-validation-for="Title" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ReleaseDate" class="control-label"></label>
<input asp-for="ReleaseDate" class="form-control" />
<span asp-validation-for="ReleaseDate" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Genre" class="control-label"></label>
<input asp-for="Genre" class="form-control" />
<span asp-validation-for="Genre" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Price" class="control-label"></label>
<input asp-for="Price" class="form-control" />
<span asp-validation-for="Price" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Rating" class="control-label"></label>
<input asp-for="Rating" class="form-control" />
<span asp-validation-for="Rating" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
▶ Views/Movie/Edit.cshtml
@model TestProject.Models.Movie
@{
ViewData["Title"] = "Edit";
}
<h1>Edit</h1>
<h4>MovieModel</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Edit">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input type="hidden" asp-for="ID" />
<div class="form-group">
<label asp-for="Title" class="control-label"></label>
<input asp-for="Title" class="form-control" />
<span asp-validation-for="Title" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="ReleaseDate" class="control-label"></label>
<input asp-for="ReleaseDate" class="form-control" />
<span asp-validation-for="ReleaseDate" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Genre" class="control-label"></label>
<input asp-for="Genre" class="form-control" />
<span asp-validation-for="Genre" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Price" class="control-label"></label>
<input asp-for="Price" class="form-control" />
<span asp-validation-for="Price" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Rating" class="control-label"></label>
<input asp-for="Rating" class="form-control" />
<span asp-validation-for="Rating" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Save" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
▶ Views/Movie/Delete.cshtml
@model TestProject.Models.Movie
@{
ViewData["Title"] = "Delete";
}
<h1>Delete</h1>
<h3>Are you sure you want to delete this?</h3>
<div>
<h4>MovieModel</h4>
<hr />
<dl class="row">
<dt class = "col-sm-2">
@Html.DisplayNameFor(model => model.Title)
</dt>
<dd class = "col-sm-10">
@Html.DisplayFor(model => model.Title)
</dd>
<dt class = "col-sm-2">
@Html.DisplayNameFor(model => model.ReleaseDate)
</dt>
<dd class = "col-sm-10">
@Html.DisplayFor(model => model.ReleaseDate)
</dd>
<dt class = "col-sm-2">
@Html.DisplayNameFor(model => model.Genre)
</dt>
<dd class = "col-sm-10">
@Html.DisplayFor(model => model.Genre)
</dd>
<dt class = "col-sm-2">
@Html.DisplayNameFor(model => model.Price)
</dt>
<dd class = "col-sm-10">
@Html.DisplayFor(model => model.Price)
</dd>
<dt class = "col-sm-2">
@Html.DisplayNameFor(model => model.Rating)
</dt>
<dd class = "col-sm-10">
@Html.DisplayFor(model => model.Rating)
</dd>
</dl>
<form asp-action="Delete">
<input type="hidden" asp-for="ID" />
<input type="submit" value="Delete" class="btn btn-danger" /> |
<a asp-action="Index">Back to List</a>
</form>
</div>
▶ Views/Movie/Details.cshtml
@model TestProject.Models.Movie
@{
ViewData["Title"] = "Details";
}
<h1>Details</h1>
<div>
<h4>MovieModel</h4>
<hr />
<dl class="row">
<dt class = "col-sm-2">
@Html.DisplayNameFor(model => model.Title)
</dt>
<dd class = "col-sm-10">
@Html.DisplayFor(model => model.Title)
</dd>
<dt class = "col-sm-2">
@Html.DisplayNameFor(model => model.ReleaseDate)
</dt>
<dd class = "col-sm-10">
@Html.DisplayFor(model => model.ReleaseDate)
</dd>
<dt class = "col-sm-2">
@Html.DisplayNameFor(model => model.Genre)
</dt>
<dd class = "col-sm-10">
@Html.DisplayFor(model => model.Genre)
</dd>
<dt class = "col-sm-2">
@Html.DisplayNameFor(model => model.Price)
</dt>
<dd class = "col-sm-10">
@Html.DisplayFor(model => model.Price)
</dd>
<dt class = "col-sm-2">
@Html.DisplayNameFor(model => model.Rating)
</dt>
<dd class = "col-sm-10">
@Html.DisplayFor(model => model.Rating)
</dd>
</dl>
</div>
<div>
<a asp-action="Edit" asp-route-id="@Model.ID">Edit</a> |
<a asp-action="Index">Back to List</a>
</div>
※ TestDB 데이터베이스 생성
1. 비주얼 스튜디오를 실행한다.
2. 비주얼 스튜디오에서 [보기] / [SQL Server 개체 탐색기] 메뉴를 클릭한다.
3. [SQL Server 개체 탐색기]에서 [SQL Server] / [(localdb)MSSQLLocalDB...] / [데이터베이스] 노드 위에서 마우스 오른쪽 버튼을 클릭한다.
4. 컨텍스트 메뉴에서 [새 데이터베이스 추가] 메뉴를 클릭한다.
5. [데이터베이스 만들기] 대화 상자에서 아래와 같이 입력하고 [확인] 버튼을 클릭한다.
6. 비주얼 스튜디오에서 [도구] / [NuGet 패키지 관리자] / [패키지 관리자 콘솔] 메뉴를 클릭한다.
7. [패키지 관리자 콘솔]에서 아래 명령을 실행한다.
Update-Database
728x90
반응형
그리드형(광고전용)
댓글을 달아 주세요