Companies 테이블로 ADO.NET, Dapper, EF Core 데이터 입출력 구현하기

  • 10 minutes to read

이 문서는 ASP.NET Core MVC 10.0 환경에서 Companies 테이블을 생성하고, 세 가지 데이터 접근 기술(ADO.NET, Dapper, Entity Framework Core)을 사용하여 데이터를 입력하고 조회하는 방법을 단계별로 설명합니다.


소스 코드 경로


준비 사항

초기 강의 영상에서 사용한 머티리얼 디자인(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. 실행 및 테스트

  1. SQL Server에서 Companies 테이블을 생성합니다.
  2. appsettings.json의 연결 문자열을 환경에 맞게 수정합니다.
  3. Program.cs에서 CompanyRepositoryEntityFramework 등록 여부를 확인합니다.
  4. 애플리케이션을 실행합니다.
  5. 브라우저에서 /Companies/Manage로 접속합니다.
  6. 회사명을 입력하고 “추가”를 클릭합니다.
  7. 목록에 입력한 회사명이 표시되는지 확인합니다.

13. 마무리

이 예제는 ASP.NET Core MVC 10.0 프로젝트에서 하나의 테이블을 중심으로 세 가지 데이터 접근 기술(ADO.NET, Dapper, EF Core) 을 비교·구현하는 실습입니다. 각 방식의 구조와 차이를 직접 확인하며, 리포지토리 DI 구성을 바꿔 다른 접근 방식으로도 쉽게 전환할 수 있습니다.

VisualAcademy Docs의 모든 콘텐츠, 이미지, 동영상의 저작권은 박용준에게 있습니다. 저작권법에 의해 보호를 받는 저작물이므로 무단 전재와 복제를 금합니다. 사이트의 콘텐츠를 복제하여 블로그, 웹사이트 등에 게시할 수 없습니다. 단, 링크와 SNS 공유, Youtube 동영상 공유는 허용합니다. www.VisualAcademy.com
박용준 강사의 모든 동영상 강의는 데브렉에서 독점으로 제공됩니다. www.devlec.com