원시 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 automaticallyFromSql 이후 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 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.
이 강의의 모든 강의
- 즉시, 지연, 명시적 로딩
- 원시 SQL, 저장 프로시저와 보간
- 컴파일된 쿼리와 성능
- 전역 쿼리 필터와 소유 엔터티