Chapter 3. DB 프로그래밍 / Entity Framework (ORM)

개발하는 운동인·2025년 10월 27일

데이터 베이스 프로그래밍 방법

  • 프로그래밍을 통해 db를 연결하여 db를 조작할 줄 알아야 함

ADO.NET

  • 가장 기본적이고 전통적임

  • 사용이 편리함

  • DB와 직접 연결하고 SQL 실행

  • 성능 빠르고, 세밀한 제어 가능

  • 저수준 함수(매핑이 되어 있음)이므로, DB에 대한 이해도가 높아질 수 있음.

  • 하지만, 코드를 직접 작성해야 하고, 코드 장황해지고 유지보수가 힘들 수 있음.

    • DB 와 연결하기 위한 표준 라이브러리 .
      클래스 집합체. 사용자가 편리하게 API 모아 놓음
    • 주요 기능 : DB연결, SQL 명령어 실행 , 결과 조회 및 데이터 셋 관리
  • 동작구조. ADO.NET의 생명주기

    1. 애플리케이션이 SqlConnection으로 DB에 연결
    2. SqlCommand로 SQL 명령 전달 후 실행
    3. DB가 결과 반환 -> SqlDataReader 또는 DataSet으로 가져옴
    4. 연결종료
      SqlDataReader : Forward - only 읽기 전용 결과
      SqlDataAdapter : DB <-> DataSet 간 데이터 전송
      DataSet / DataTable : 메모리 내 데이터 저장소(비연결형)
  • 프로그램의 .dll 파일은 메모리에 올라가는데, SqlConnection 사용할 때 namespace 정의를 해야 동작이 가능하다.


👍 SqlConnection

    1. 아래 Connection String 내용 복사
    1. SqlConnection 연결 코드 작성
namespace ConsoleApp2
{
    class Program
    {
        static void Main(string[] args)
        {
            string connectString = "Data Source=(localdb)\\MSSQLLocalDB;Integrated Security=True;Persist Security Info=False;Pooling=False;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=True;Application Name=\"SQL Server Management Studio\";Command Timeout=30";

            using (SqlConnection connection = new SqlConnection(connectString))
            {
                connection.Open();
            }
        }
    }
}
    1. 그냥 위 코드 처럼 작성하면 오류가 나타날 것이다.
  • using 선언

  • Nuget 패키지에서

  • 아래 패키지 다운로드 하면

  • 아래 참조 파일인 .dll이 나타난다.


👍 SqlCommand

  • DB에 날릴 수 있는 쿼리들을 작동해줄 수 있는 클래스
  • 여기에서 원하는 DB를 조작할 수 있다.
  • 파라메타 넣는 법 cmd.Parameters.AddWithValue("@email","wls6189@naver.com")
  • 쿼리 실행
    Int rows = cmd.ExcuteNonQuery(); //내가 작성한 쿼리문이 실행이 된다.
    • ExectueNonQuery - 데이터 조작(Insert,UPDATE,DELETE)
    • ExecuteScalar - 단일 값 반환
      -첫번재 행의 첫번째 컬럼 값을 반환 . ex) COUNT,MAX,MIN 같은 집계 함수
    • ExecuteReader - 다중 행 조회
      결과를 순방향(특정 지점을 읽을 수 없음), 읽기 전용으로 가져옴
      속도가 가장 빠른 조회 방법

👍 SqlDataReader (연결형 조회)

👍 SqlDataAdpter

📝 DB 환경 설정

  • 새 쿼리를 만들고 UserCount라는 테이블을 새로 생성 하고 컬럼 작성 후, INSERT까지 해본다.
IF DB_ID('Test01') IS NULL
BEGIN
	CREATE DATABASE Test01
END
GO
USE Test01;
GO

IF OBJECT_ID('dbo.UserAccount','U') IS NOT NULL DROP TABLE dho.UserAccount;
GO
CREATE TABLE dbo.UserCount
(
	ID INT IDENTITY(1,1) PRIMARY KEY,
	Email NVARCHAR(100) NOT NULL UNIQUE,
	[Password] NVARCHAR(100) NOT NULL,
	CreatedAt DATETIME2(0) NOT NULL DEFAULT SYSUTCDATETIME()
);
GO

INSERT INTO dbo.UserCount(Email,[Password]) VALUES
('wls6189@naver.com','wls6189'),
('vksej1235@gmail.com','wls1235');
GO

실제 예제

    1. 연결 테스트
using System;
using System.Collections.Generic;
using Microsoft.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp2
{
    class Program
    {
        private const string connectString = "Data Source=(localdb)\\MSSQLLocalDB;Integrated Security=True;Persist Security Info=False;Pooling=False;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=True;Application Name=\"SQL Server Management Studio\";Command Timeout=30";

        static void Main(string[] args)
        {
            //1. 연결 테스트
            ConnectionDBTest();

        }

        static void ConnectionDBTest()
         {
            using (SqlConnection connection = new SqlConnection(connectString))
            {
                connection.Open();
                Console.WriteLine("DB 연결 완");

                const string sql = @"INSERT INTO dbo.UserCount";
            }
        }
    }
}
    1. DB에 데이터 삽입
  • 중요한 것은 SQL문을 어떻게 잘 작성하느냐가 중요하다. 조회 후 데이터 삽입하는 방식으로 SQL문을 작성한다.

  • 또한 SqlConnection으로 DB에 연결(Open)해주고, SqlCommand를 통해서 SQL문을 이용하여 DB 실행

  • 처음엔 삽입한 데이터가 중복됐는지 확인 후, 중복이 없으면 삽입하는 방식

    1. 단일 값 조회 - ExcecuteScaler을 이용한다.
  • ExcecuteScaler() 메서드는 쿼리 결과에서 첫번째 행의 첫번째 열의 값을 반환한다. 단일 값을 가져올 때 가장 적합하다. 번외로 ExecuteReader () 메서드는 여러 행/ 열 테이블을 조회하고, ExectueNonQueary()는 데이터 변경하여 쿼리를 실행한다.

  • SQL문을 아래 처럼 작성한 이유는 COUNT(*)는 테이블의 전체 행 개수를 반환하는 집계 함수이다. 여기서는 UserCount 테이블에 몇 명의 사용자가 등록되어 있는지 사용자 수를 세기 위해 이 쿼리를 사용하였다.

    1. 다중 조회
  • 다중 조회를 할 때 ExecuteReader() 메서드가 적합하다고 했었다.

  • sql문을 보면 UserCount 테이블에서 모든 행(ID,Email,Password) 컬럼을 ID 기준으로 정렬해서 가져온다.

  • 마찬가지로 SqlConnection을 열어서 DB에 연결 한다

  • SqlCommand 생성하여 ExcecuteReader()를 통해 쿼리를 실행 하고, 결과 집합을 reader에 저장한다.

  • while문을 통해 reader.Read() 하여 다음 행이 있다면 true, 없다면 false 처리 후 GetInt, GetString,GetDateTime을 통해 각 타입을 가져오고 출력

    1. 이메일 변경 및 비밀번호 변경
  • 인자로 전달 받은 email,newPassword를 cmd의 파라미터로 값들을 추가한다.

  • 추가한 것을 실제 실행하기 위해 ExcecuteNonQuery(); 를 호출

    1. 이메일 삭제
  • 위 5번 과정과 동일한 방식이다.

콘솔 결과

프로그래밍 기법

  • Using 블록과 예외처리
  • DB연결/자원은 반드시 닫아야 함 -> using 블록으로 자동 해제.
  • 예외 발생 시 try - catch로 처리 -> 습관처럼 해야 함
  • SQL Injecion 방지 - 파라미터 바인딩을 해줘야 함
  • 직접 문자열 연결 하는 방식으로 하지 않고, URL 파라미터에 SQL 구문을 집어 넣어 의도와 다른 SQL이 실행되도록 만들어야 함
  • 👍 입력값 검증(불필요한 특수문자 차단, 이메일, 숫자, 날짜 등 정확히 입력했는지에 대한 처리) -> 습관처럼 해야 함
  • 권한 최소화 : DB 계정에 DROP TABLE, ALTER 같은 권한을 주지 않기

Entity Framwork

  • ORM : DB 테이블 <-> C# 객체 매핑
  • 유저라는 클래스를 만들고 매핑하게 되면 자동으로 DB에 연결할 수 있ㄷ.
  • 실제, LINQ 문법 사용하므로, LINQ를 잘 모르면 어려울 수 있다. 하지만, LINQ를 사용하면 SQL 직접 작성 줄어듦.
  • 유지보수와 확장성이 뛰어남.
  • 매핑 하므로, 성능은 무겁고 ADO.NET보다 떨어질 수 있음

Dapper (Micro ORM)

  • Entity Framwork의 Micro 버전
  • NuGet으로 설치 가능 , 경량 ORM. NUGET은 실제 라이브러리를 올려 놓는 기능.
  • 빠르고 가볍고, SQL은 직접 작성해야 함

오래된 프로젝트의 데이터 베이스는 ODBCC/OleDB를 사용하는 경우 종종 있다.

현업에서 NoSQL 드라이버를 자주 사용한다.

  • MongoDB, Redis등 전용

ORM

  • 객체 지향 프로그래밍 언어의 클래스와 DB의 테이블을 자동으로 매핑 해주는 기술
  • DB에 저장을 하려면, 내가 만든 클래스 또는 프로그래밍들을 매핑해줘야 한다. 즉, 내가 만든 DATA를 저장해야 유저들이 게임에 접속했을 때 저장된 DATA를 불러올 수 있다.

ORM의 핵심 아이디어 : 매핑 방식

  • C#의 class <-> SQL의 테이블
  • C#의 프로퍼티 <-> sql의 컬럼
  • c#의 object <-> sql의 로우
  • ORM은 데이터 저장/조회/수정/삭제를 SQL이 아닌 객체 조작으로 처리하고, DB 설계보다는 모델 중심의 개발

장점

  • 쿼리문 작성 실수를 줄이고, 유지보수가 쉬워진다.

동작구조 (ORM 매핑)

  • 객체 생성 -> ORM이 SQL 변환 -> DB 실행 -> 결과를 객체로 반환
  • 즉, C# 클래스 객체 <-> SQL 테이블/데이터
var user = new User {Name = "kj" , Age = 25};
dB.Add(user);
dB.SaveChanges();  //INSERT INTO Users(Name,Age) VALUES ("AK",32);
  • 위 코드를 보면 DB에 유저 객체를 Add하고, DB에 값을 Insert한다.

.NET ORM의 EF Core

  • Entity Framework Core
  • MS에서 개발한 .NET 공식 ORM 프레임워크 => ORM의 동작을 지원하는 주요 클래스의 집합
  • EF6의 후속 버전
  • .NET 8까지 계속 업데이트 중
  • LINQ -> SQL 변환한 엔진을 내장한다.

주요 특징

  • 다중 DB를 지원, SQLite , MySql 등

핵심 기능(Migration 기능) : 코드를 짜면 자동으로 스키마를 관리한다. (버전별 DB 업데이트 가능). 버전의 내용이 달라진게 있으면 스키마를 자동으로 업데이트 해준다.

  • LINQ 쿼리 지원 : C# 코드로 데이터 조회 가능
var users = db.Users.Where(u => u.Age > 20).ToList();
- 스키마의 User테이블에서 Age가 20이상일 때 리스트로 변환하여 users에 저장
  • DB 테이블과 매핑되는 c#클래스를 만든다 -> 1. Entity Class (컬럼을 만듦)
  • DB와 Entity 클래스 간의 연결/세션 관리 -> 2. DB Context
  • 실제 DB 테이블을 표현하는 속성 -> 3. DB Set (LINQ 쿼리로 표현)
  • 모델 변경 시 DB 스키마 자동 업데이트(코드 -> DB 구조 자동 반영) -> 4. Migration

프레임워크를 따라해서 만드는 것은 굉장히 좋다

실무에서 .NET ORM을 자주 사용한다.

1. Entity Class 만들기

  • 프로퍼티 생성(컬럼을 생성한다). 단, 프로퍼티 생성할 때 PK를 지정해야 하는데, 프로퍼티 이름이 ID라면 자동으로 PK를 준다.

2. DB Context

  • EF Core의 중심이고, Entity Class들과 DB 연결 세션을 도와주고 관리한다.
  • CRUD 명령 수행. 여기서 CRUD는 추가,업데이트,삭제 등을 수행한다
  • 트랜잭션, Migration과 연동
public DBSet<UserAccount> UserAccounts {get; set;}
  • UserAccount는 내가 만들었던 엔티티 클래스. UserAccounts 라는 이름의 테이블로 사용한다

3. DB Set (일종의 DB 테이블)

LINQ

  • C# 코드 안에서 SQL과 유사한 쿼리 구문을 사용할 수 있게 하는 문법 확장.
  • C# 문법으로 데이터 필터링/정렬/집게 그룹화 가능

LINQ는 지연 방식으로 진행된다.

  • 효율성을 위해. 각 쿼리를 바로 날리지 않고 생성했다가 마지막에 쿼리를 날리는 방식임. 쿼리를 계속 날리면 DB에 부하가 걸린다.
    1. LINQ로 쿼리 정의
    1. 실행 메서드 호출 ( ToList,First,Count등)
    1. EF Core가 식을 sql로 번역
    1. sql server에 전송 후 결과 반환

기본 문법

  • 메서드 체이닝 방식
  • 쿼리 표현식 방식

메서드 체이닝 방식 -주로 많이 사용

  • ToList() 전까지 쿼리를 생성만하고, ToList() 이후에는 쿼리를 날린다.
var list = DB이름.테이블이름.WHERE().OrederBy().ToList();
var list1 = DB이름.테이블이름.WHERE().OrederByDescending().ToList();

Projection (필드 선택 최적화)

  • 불필요한 컬럼을 제외하고 필요한 데이터만 가져오는 방법
  • 전송량 절감

N+1 문제를 InClude / ThenInclude 사용

  • 쿼리 수 감소

AsNoTracking() 읽기 전용 쿼리 최적화

  • 성능 향상

ToListAsync() 비동기 처리

  • IO 효율 개선

EFCore Sample 시작 전 환경설정

  • 현재 비쥬얼스튜디오 Net 버전

  • Nuget 패키지 관리에 들어가서 아래 패키지 다운로드




시작

    1. 엔티티 클래스

    1. DB Context
  • DBContext를 상속 받기 위해 아래 using문을 선언해야 함

    1. DB Set
  • UserAccount의 엔티티 클래스를 DB의 테이블로 지정

  • Character의 엔티티 클래스를 DB의 테이블로 지정

  • 내가 짠 LINQ를 SQL서버에 전송하기 위해 매핑하는 작업을 한다. UseSqlServer 함수 안에 로컬 SQL Server 연결 문자열을 적는다.


  • ex) 유저가 로그인하면 db에 저장 등 , 네트워크와 클라이언트를 분리하여 유저가 로그인 하면 db에 저장 하는식

0개의 댓글