0Pricing
C# Academy · 강의

원시 SQL, 저장 프로시저와 보간

FromSqlRaw, FromSqlInterpolated, 저장 프로시저로 원시 SQL을 안전하게 실행하면서 인젝션을 방지합니다.

원시 SQL, 저장 프로시저와 보간은(는) CoddyKit의 무료 C# Academy 강의입니다. 이것은 4개 중 2번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 C# Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. C# Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

EF Core에서 원시 SQL을 사용할 때

대부분의 쿼리는 LINQ로 문제없이 처리할 수 있지만, 복잡한 분석, 공급업체별 함수, 저장 프로시저 또는 LINQ가 비효율적으로 변환하는 성능 중요 쿼리와 같은 일부 시나리오에서는 원시 SQL이 필요합니다.

EF Core는 보안을 유지하면서 원시 SQL을 안전하게 사용하는 방법을 제공합니다.

FromSqlRaw: 안전한 매개 변수화 쿼리

FromSqlRaw는 원시 SQL을 실행하고 결과를 다시 엔터티에 매핑합니다. SQL 삽입을 방지하려면 항상 {0} 자리 표시자를 사용하고 문자열 연결은 절대 사용하지 마십시오.

// SAFE: parameterized
var products = await _db.Products
    .FromSqlRaw("SELECT * FROM Products WHERE Price > {0}", 100m)
    .ToListAsync();

// DANGEROUS: never do this
// FromSqlRaw("SELECT * FROM Products WHERE Price > " + price)

FromSqlInterpolated: 보간 문자열의 안전성

FromSqlInterpolated는 C# 보간 문자열을 받습니다. EF Core는 보간된 값을 SQL 매개 변수로 자동 변환하므로 안전하고 읽기 쉽습니다.

decimal minPrice = 100m;
string category = "Electronics";

var products = await _db.Products
    .FromSqlInterpolated(
        $"SELECT * FROM Products WHERE Price > {minPrice} AND Category = {category}")
    .OrderBy(p => p.Name)
    .ToListAsync();
// Values become @p0, @p1 parameters automatically

FromSql 이후 LINQ 조합

FromSql 이후에 LINQ 연산자를 연결할 수 있습니다. EF Core는 원시 SQL을 하위 쿼리로 감싼 다음 그 위에 LINQ를 적용하므로, 필터링이나 페이지 매김을 추가할 때 유용합니다.

decimal min = 50m;
var page = await _db.Products
    .FromSqlInterpolated($"SELECT * FROM Products WHERE Price > {min}")
    .Where(p => p.IsActive)        // additional LINQ filter
    .OrderBy(p => p.Price)
    .Skip(0).Take(20)              // pagination
    .ToListAsync();
// Generates: SELECT ... FROM (raw SQL) AS p WHERE ...

저장 프로시저 실행

엔터티 행을 반환하는 저장 프로시저는 FromSqlRaw를 통해 호출하십시오. 행을 반환하지 않는 프로시저(예: INSERT/UPDATE)에는 ExecuteSqlRawAsync를 사용하십시오.

// Stored proc returning Products
var products = await _db.Products
    .FromSqlRaw("EXEC GetProductsByCategory {0}", "Books")
    .ToListAsync();

// Stored proc with no return rows
await _db.Database.ExecuteSqlRawAsync(
    "EXEC ArchiveOldOrders {0}",
    DateTime.UtcNow.AddYears(-1));

DML에 ExecuteSqlInterpolated 사용

ExecuteSqlInterpolated는 보간 문자열의 안전성을 적용하여 DML 문(INSERT, UPDATE, DELETE)을 실행합니다. 영향을 받은 행의 수를 반환합니다.

int days = 30;
int affected = await _db.Database.ExecuteSqlInterpolatedAsync(
    $"UPDATE Orders SET Status = 'Expired' WHERE PlacedAt < {DateTime.UtcNow.AddDays(-days)}");

Console.WriteLine($"{affected} orders expired");

엔터티가 아닌 형식 쿼리

SqlQuery<T>(.NET 7 이상)를 사용하면 EF Core 엔터티가 아닌 임의의 스칼라 형식이나 DTO를 쿼리할 수 있습니다. DbSet은 필요하지 않습니다.

// Query a DTO directly — no entity needed
var stats = await _db.Database
    .SqlQuery<ProductStats>(
        $"SELECT Category, COUNT(*) AS Count, AVG(Price) AS AvgPrice FROM Products GROUP BY Category")
    .ToListAsync();

public record ProductStats(string Category, int Count, decimal AvgPrice);

뷰를 위한 키 없는 엔터티 형식

HasNoKey()와 ToView()를 사용하여 데이터베이스 뷰나 원시 쿼리 결과를 키 없는 엔터티 형식에 매핑하십시오. 기본 키 없이도 DbSet처럼 쿼리할 수 있습니다.

public class ProductSalesSummary
{
    public string Category { get; set; } = "";
    public int TotalSold { get; set; }
    public decimal Revenue { get; set; }
}

// In OnModelCreating:
modelBuilder.Entity<ProductSalesSummary>()
    .HasNoKey()
    .ToView("vw_ProductSalesSummary");

// Query:
var summary = await _db.Set<ProductSalesSummary>().ToListAsync();

SQL 삽입 문제 피하기

황금률은 사용자 입력을 SQL 문자열에 절대 연결하지 않는 것입니다. 항상 FromSqlRaw의 자리 표시자, FromSqlInterpolated 또는 EF Core의 LINQ를 통해 매개 변수화 쿼리를 사용하십시오.

// SAFE
var name = userInput;
var result = await _db.Products
    .FromSqlInterpolated($"SELECT * FROM Products WHERE Name = {name}")
    .ToListAsync();

// INJECTION RISK (never do this)
// FromSqlRaw("SELECT * FROM Products WHERE Name = '" + name + "'");

ADO.NET 대체 수단을 통한 출력 매개 변수

EF Core는 저장 프로시저의 출력 매개 변수를 기본적으로 지원하지 않습니다. 이러한 경우에는 _db.Database.GetDbConnection()을 사용하여 ADO.NET으로 전환하십시오.

await _db.Database.OpenConnectionAsync();
var conn = _db.Database.GetDbConnection();
using var cmd = conn.CreateCommand();
cmd.CommandText = "EXEC GetOrderCount @CustomerId, @Count OUTPUT";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(new SqlParameter("@CustomerId", 5));
var outParam = new SqlParameter("@Count", SqlDbType.Int) { Direction = ParameterDirection.Output };
cmd.Parameters.Add(outParam);
await cmd.ExecuteNonQueryAsync();
int count = (int)outParam.Value;

실전: 전문 검색 대체 수단

LINQ로는 데이터베이스별 전문 검색 조건식을 표현할 수 없습니다. FromSqlInterpolated를 사용하여 기본 제공되는 FREETEXT 또는 텍스트 벡터 기능을 활용하십시오.

string query = "keyboard mechanical";
var results = await _db.Products
    .FromSqlInterpolated(
        $"SELECT * FROM Products WHERE FREETEXT(Name, {query})")
    .AsNoTracking()
    .Take(20)
    .ToListAsync();

빠른 확인

매개 변수화 쿼리에서 FromSqlInterpolated가 문자열 연결보다 안전한 이유는 무엇입니까?

복습: EF Core의 원시 SQL

핵심 요점:

  • FromSqlRaw: {0} 자리 표시자를 사용하고 사용자 입력을 절대 연결하지 않음
  • FromSqlInterpolated: 안전한 보간 문자열 → ADO.NET 매개 변수
  • FromSql 이후에 LINQ 연산자를 연결하여 필터링, 정렬, 페이지 매김 추가
  • ExecuteSqlInterpolatedAsync: 안전한 DML 실행
  • SqlQuery<T>: 엔터티가 아닌 DTO 쿼리(.NET 7 이상)
  • 출력 매개 변수나 복잡한 저장 프로시저에는 ADO.NET을 직접 사용

자주 묻는 질문

“원시 SQL, 저장 프로시저와 보간” 강의는 무료인가요?

네 — “원시 SQL, 저장 프로시저와 보간” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 C# Academy 강의 전체를 잠금 해제할 수 있습니다. C# Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

“원시 SQL, 저장 프로시저와 보간”에서 뭘 배우나요?

FromSqlRaw, FromSqlInterpolated, 저장 프로시저로 원시 SQL을 안전하게 실행하면서 인젝션을 방지합니다. 브라우저에서 직접 실행하는 실습 코드로 C# Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

C# Academy을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 C# Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 2번째 강의입니다.

“원시 SQL, 저장 프로시저와 보간” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 C# Academy 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 C# Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. 즉시, 지연, 명시적 로딩
  2. 원시 SQL, 저장 프로시저와 보간
  3. 컴파일된 쿼리와 성능
  4. 전역 쿼리 필터와 소유 엔터티
← C# Academy(으)로 돌아가기