Companies 테이블로 ADO.NET, Dapper, EF Core 데이터 입출력 구현하기
이 문서는 ASP.NET Core MVC 10.0 환경에서 Companies 테이블을 생성하고, 세 가지 데이터 접근 기술(ADO.NET, Dapper, Entity Framework Core)을 사용하여 데이터를 입력하고 조회하는 방법을 단계별로 설명합니다.
소스 코드 경로
최종 적용된 프로젝트: https://github.com/VisualAcademy/DotNetNote
강의 촬영 시 사용한 예제: https://github.com/VisualAcademy/CompanyApp
준비 사항
초기 강의 영상에서 사용한 머티리얼 디자인(Material Design) 관련 코드는 현재 사용하지 않습니다. 대신 Bootstrap 5를 사용하여 UI를 구성합니다.
또한 강의 초기 버전은 .NET 6 이하 기반이었으나, 현재는 .NET 10 이상 및 Visual Studio 2026 이상을 기준으로 진행합니다. 이 문서의 단계를 최신 환경에 맞게 그대로 따라 하시면 됩니다.
1. 데이터베이스 테이블 생성
먼저 SQL Server에서 Companies 테이블을 생성합니다. 이 테이블은 회사 정보를 저장하기 위한 기본 구조를 가지며, 간단하게 회사 이름을 입력하고 관리할 수 있도록 설계합니다.
처음 실습할 때는 복잡한 테이블보다는 단순한 구조로 여러 번 연습합니다. 테이블 생성과 매핑 과정에 익숙해진 후 다양한 열을 포함한 복합 구조로 확장합니다.
파일 경로
DotNetNote\DotNetNote.SqlServer\dbo\Tables\Companies.sql
-- ==========================================
-- Table: Companies
-- 회사 정보 저장용 테이블
-- ==========================================
CREATE TABLE [dbo].[Companies]
(
[Id] BIGINT NOT NULL PRIMARY KEY Identity(1, 1), -- 고유 ID (자동 증가)
[Name] NVarChar(255) Not Null -- 회사 이름
)
GO
- Id: 각 회사를 구분하는 고유 식별자(자동 증가).
- Name: 회사 이름(필수).
NOTE
본 예제는 최소 구조로 시작합니다. 실무에서는 주소, 연락처, 대표자명, 설립일 등의 열을 추가해 확장할 수 있습니다. 핵심은 전반 흐름을 단순 구조로 익힌 뒤 점진 확장입니다.
2. 모델 개체 정의
데이터베이스 테이블과 1:1로 매핑되는 모델 개체를 정의합니다. 이 개체는 ADO.NET, Dapper, EF Core에서 공통으로 사용됩니다.
파일 경로
DotNetNote.Models\Companies\CompanyModel.cs
namespace DotNetNote.Models.Companies;
/// <summary>
/// Company, CompanyDomain, ...
/// </summary>
public class CompanyModel
{
public long Id { get; set; } // 테이블 PK (BIGINT, Identity)
public string Name { get; set; } // 회사 이름(필수)
}
이 개체를 통해 데이터베이스의 Companies 테이블과 코드 간 매핑이 이루어지며, 데이터를 개체 형태로 손쉽게 다룰 수 있습니다.
3. DbContext 구성
Entity Framework Core 사용을 위해 DbContext를 구성합니다. 이 구성은 데이터베이스 연결 및 테이블 매핑을 담당합니다.
파일 경로
DotNetNote.Models\Companies\CompanyContext.cs
//Install-Package Microsoft.EntityFrameworkCore
//Install-Package Microsoft.EntityFrameworkCore.SqlServer
//Install-Package Microsoft.EntityFrameworkCore.Tools
//Install-Package Microsoft.Data.SqlClient
using Microsoft.EntityFrameworkCore;
namespace DotNetNote.Models.Companies;
public class CompanyContext : DbContext
{
public CompanyContext()
{
}
public CompanyContext(DbContextOptions<CompanyContext> options)
: base(options)
{
}
public DbSet<CompanyModel> Companies { get; set; } // 테이블 매핑
}
CompanyContext: EF Core의 핵심 구성 요소로 연결과 변경 추적을 담당합니다.DbSet<CompanyModel>:Companies테이블과 매핑됩니다.- ASP.NET Core MVC 10.0에서는 연결 문자열을
Program.cs에서 주입하므로OnConfiguring()은 필요하지 않습니다.
4. 리포지토리 인터페이스 정의
데이터 입출력 로직의 일관성을 위해 ICompanyRepository 인터페이스를 정의합니다. 이 인터페이스는 IBreadShop<T>를 상속해 CRUD 메서드를 포함합니다.
파일 경로
DotNetNote.Models\Companies\ICompanyRepository.cs
using Dul.Data;
namespace DotNetNote.Models.Companies
{
public interface ICompanyRepository : IBreadShop<CompanyModel>
{
}
}
참고: Dul.Data.IBreadShop<T>
public interface IBreadShop<T> where T : class
{
T Browse(int id);
List<T> Read();
bool Edit(T model);
T Add(T model);
bool Delete(int id);
List<T> Search(string query);
int Has();
IEnumerable<T> Ordering(OrderOption orderOption);
List<T> Paging(int pageNumber, int pageSize);
}
이 구조를 사용하면 데이터 접근 기술을 바꾸더라도 상위 계층 코드를 수정하지 않고 손쉽게 교체할 수 있습니다.
5. ADO.NET 리포지토리 구현
ADO.NET은 기본 데이터 접근 방식으로, SQL을 직접 작성하고 SqlConnection, SqlCommand로 제어합니다.
파일 경로
DotNetNote.Models\Companies\CompanyRepositoryAdo.cs
using Dul.Data;
using Microsoft.Data.SqlClient;
using System;
using System.Collections.Generic;
using System.Data;
namespace DotNetNote.Models.Companies;
public class CompanyRepositoryAdo(string connectionString) : ICompanyRepository
{
public CompanyModel Add(CompanyModel model)
{
using var con = new SqlConnection(connectionString);
con.Open();
var cmd = new SqlCommand("Insert Into Companies (Name) Values(@Name);", con);
cmd.Parameters.Add(new SqlParameter("@Name", SqlDbType.NVarChar, 50) { Value = model.Name });
cmd.ExecuteNonQuery();
return model;
}
public List<CompanyModel> Read()
{
using var con = new SqlConnection(connectionString);
con.Open();
var cmd = new SqlCommand("Select Top 1000 Id, Name From Companies", con);
var da = new SqlDataAdapter(cmd);
var ds = new DataSet();
da.Fill(ds, "Companies");
var companies = new List<CompanyModel>();
foreach (DataRow row in ds.Tables[0].Rows)
{
companies.Add(new CompanyModel
{
Id = Convert.ToInt32(row["Id"]),
Name = row["Name"].ToString()
});
}
return companies;
}
// 필요 시 추가 구현
public CompanyModel Browse(int id) => throw new NotImplementedException();
public bool Delete(int id) => throw new NotImplementedException();
public bool Edit(CompanyModel model) => throw new NotImplementedException();
public int Has() => throw new NotImplementedException();
public IEnumerable<CompanyModel> Ordering(OrderOption orderOption) => throw new NotImplementedException();
public List<CompanyModel> Paging(int pageNumber, int pageSize) => throw new NotImplementedException();
public List<CompanyModel> Search(string query) => throw new NotImplementedException();
}
특징: 직관적이나 코드량이 많고 유지보수 비용이 큽니다. 대신 SQL 제어가 세밀합니다.
6. Dapper 리포지토리 구현
Dapper는 마이크로 ORM으로, SQL과 개체 매핑을 간결하게 처리합니다.
파일 경로
DotNetNote.Models\Companies\CompanyRepositoryDapper.cs
using Dapper;
using Dul.Data;
using Microsoft.Data.SqlClient;
using System.Collections.Generic;
using System.Data;
using System.Linq;
namespace DotNetNote.Models.Companies;
public class CompanyRepositoryDapper : ICompanyRepository
{
private readonly IDbConnection db;
public CompanyRepositoryDapper(string connectionString)
{
db = new SqlConnection(connectionString);
}
public CompanyModel Add(CompanyModel model)
{
var sql = "Insert Into Companies (Name) Values (@Name); Select Cast(SCOPE_IDENTITY() As Int);";
model.Id = db.Query<int>(sql, model).Single();
return model;
}
public List<CompanyModel> Read()
{
string sql = "Select * From Companies";
return db.Query<CompanyModel>(sql).ToList();
}
public CompanyModel Browse(int id) => throw new NotImplementedException();
public bool Delete(int id) => throw new NotImplementedException();
public bool Edit(CompanyModel model) => throw new NotImplementedException();
public int Has() => throw new NotImplementedException();
public IEnumerable<CompanyModel> Ordering(OrderOption orderOption) => throw new NotImplementedException();
public List<CompanyModel> Paging(int pageNumber, int pageSize) => throw new NotImplementedException();
public List<CompanyModel> Search(string query) => throw new NotImplementedException();
}
특징: SQL은 직접 작성하지만 개체 매핑이 자동이라 코드가 간결하고 성능이 우수합니다.
7. Entity Framework Core 리포지토리 구현
EF Core는 ORM으로, LINQ를 사용해 SQL 없이 데이터 조작이 가능합니다.
파일 경로
DotNetNote.Models\Companies\CompanyRepositoryEntityFramework.cs
using Dul.Data;
using System;
using System.Collections.Generic;
using System.Linq;
namespace DotNetNote.Models.Companies;
public class CompanyRepositoryEntityFramework(CompanyContext context) : ICompanyRepository
{
public CompanyModel Add(CompanyModel model)
{
context.Companies.Add(model);
context.SaveChanges();
return model;
}
public List<CompanyModel> Read()
{
return context.Companies.ToList();
}
public CompanyModel Browse(int id) => throw new NotImplementedException();
public bool Delete(int id) => throw new NotImplementedException();
public bool Edit(CompanyModel model) => throw new NotImplementedException();
public int Has() => throw new NotImplementedException();
public IEnumerable<CompanyModel> Ordering(OrderOption orderOption) => throw new NotImplementedException();
public List<CompanyModel> Paging(int pageNumber, int pageSize) => throw new NotImplementedException();
public List<CompanyModel> Search(string query) => throw new NotImplementedException();
}
특징: 가장 표준적이며 유지보수가 쉽습니다. 생산성이 높습니다.
8. 컨트롤러 작성
컨트롤러는 요청을 받아 리포지토리를 통해 데이터를 처리하고 View에 전달합니다. Index()는 입력 폼 테스트용, Manage()는 실제 DB 연동용입니다.
파일 경로
DotNetNote\Controllers\CompaniesController.cs
using DotNetNote.Models.Companies;
namespace DotNetNote.Controllers;
public class CompaniesController(ICompanyRepository repository) : Controller
{
public IActionResult Index() => View();
[HttpPost]
public IActionResult Index(string name)
{
ViewBag.Message = $"{name}을 입력했습니다.";
return View();
}
[HttpGet]
public IActionResult Manage()
{
var companies = repository.Read();
return View(companies);
}
[HttpPost]
public IActionResult Manage(string name)
{
repository.Add(new CompanyModel() { Name = name });
return RedirectToAction(nameof(Manage));
}
}
9. View 구성
Index는 단순 입력 테스트, Manage는 목록 표시와 추가를 처리합니다.
파일 경로
Views\Companies\Manage.cshtml
@model List<DotNetNote.Models.Companies.CompanyModel>
<h1>Company Management</h1>
<form action="/Companies/Manage" method="post">
<input type="text" name="name" placeholder="회사명을 입력하세요" />
<input type="submit" value="추가" />
</form>
<table border="1" cellpadding="5">
<thead>
<tr>
<th>번호</th>
<th>회사명</th>
</tr>
</thead>
<tbody>
@foreach (var c in Model)
{
<tr>
<td>@c.Id</td>
<td>@c.Name</td>
</tr>
}
</tbody>
</table>
10. Program.cs 구성
MVC 서비스, DbContext, 리포지토리를 등록합니다. 최종 실행은 EF Core 구현을 사용합니다.
파일 경로
DotNetNote\Program.cs
using DotNetNote.Models.Companies;
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
// MVC 서비스 등록
builder.Services.AddControllersWithViews();
// DbContext 등록
builder.Services.AddDbContext<CompanyContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
// 리포지토리 등록 (EF Core 기준)
builder.Services.AddScoped<ICompanyRepository, CompanyRepositoryEntityFramework>();
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();
11. appsettings.json 구성
연결 문자열을 설정해 데이터베이스와 연결합니다.
{
"ConnectionStrings": {
"DefaultConnection": "Server=(localdb)\\MSSQLLocalDB;Database=DotNetNote;Trusted_Connection=True;MultipleActiveResultSets=true"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}
12. 실행 및 테스트
- SQL Server에서
Companies테이블을 생성합니다. appsettings.json의 연결 문자열을 환경에 맞게 수정합니다.Program.cs에서CompanyRepositoryEntityFramework등록 여부를 확인합니다.- 애플리케이션을 실행합니다.
- 브라우저에서
/Companies/Manage로 접속합니다. - 회사명을 입력하고 “추가”를 클릭합니다.
- 목록에 입력한 회사명이 표시되는지 확인합니다.
13. 마무리
이 예제는 ASP.NET Core MVC 10.0 프로젝트에서 하나의 테이블을 중심으로 세 가지 데이터 접근 기술(ADO.NET, Dapper, EF Core) 을 비교·구현하는 실습입니다. 각 방식의 구조와 차이를 직접 확인하며, 리포지토리 DI 구성을 바꿔 다른 접근 방식으로도 쉽게 전환할 수 있습니다.