SlideShare a Scribd company logo
1 of 44
Legacy
Architecture
기존 방식
< 기존 방식>
기존 개발 흐름
1. 도메인 별로 SQL 구상.
• DB를 상상하며 SQL 실행 순서까지 결정.
2. SQL에 필요한 데이터를 줄 수 있는 화면
개발.
3. 도메인 별로 Repository를 나누고, SQL
작성.
4. SQL 직접 테스트.
5. Layer별로 1번에서 보내는 데이터를 받도록
매개변수를 설정하고 SQL에 매핑.
6. 직접 기능 테스트.
• 사용자 UI 데이터와 SQL이 연결된다.
• Low-Level 모듈인 Repository가 변하기 쉽다.(DIP
원칙 위배)
• SQL간의 결과 데이터를 공유하게 구상하는 것은 불가
능.
• 각각의 SQL들이 필요한 데이터를 모두 매개변수
에 추가. (많은 매개변수의 원인)
• SQL이 매개변수에 종속되기 때문에 메서드 재사용 가
능성이 현저히 떨어짐. (안좋은 Low-Level 모듈)
OOP가 불가능하다.
• OOP의 기본은 비즈니스 로직 수행 시, 로직에 필요한 데이터를 상태(필드)로 가지는 객체에게 명령하는 것인데 상태를
가지는 객체가 없다.
• DB에 접근하는 레이어인 Repository가 비즈니스 로직의 주체가 되어 행동한다.
• UpdateInteract()와 DeleteInteractTag는 DB에 수행하는 SQL을 암시할 뿐 어떤 비즈니스를 수행하는지 알 수
없다.
• BizLogic은 마치 쿼리를 여러 개 모아 놓고 순서를 조율한 것과 같다. 즉, 철저히 SQL 중심의 구조이다.
• 객체가 없기 때문에 데이터를 주고받기 힘들고, 데이터가 필요하면 매개변수를 늘리는 악순환이 반복됩니다.
연관성을 유추하기 힘들다.
• 코드를 짠 개발자는 연관성이 보이겠지만, 유지보수 하는 개발자가 보기엔 각각의 데이터로 보인다.
• 비즈니스 로직의 주체인 Repository의 메서드 명은 비즈니스 로직을 암시하지 못하고 각 데이터는 어디에
쓰여야 하는지 불분명하다.
• Strong-Type언어인 C#에서 컴파일러가 타입으로 매핑의 실수를 잡아주지 못하기 때문에 런타임 에러를
발생시키고 훨씬 많은 시간을 소비한다.
• 애트리뷰트를 사용해서 그것을 예방하지만 안 그래도 긴 호출인자가 더 길어진다.
매개변수가 너무 많다.
• 루틴 매개변수의 수를 7개 정도로 제한한다.
심리학 연구에 따르면 사람들은 일반적으로 한 번에 7개 이상의 정보 묶음을 추적할 수 없다고 한다. 이러한 발견은
여러 규칙에 적용됐으며 같은 논리로 대부분의 사람이 한 번에 7개 이상의 루틴 매개변수를 추적하는 건 쉽지
않다고 이해하면 될 것 같다.
- << Code Complete 2 (스티브 맥코넬) >>
• 매개변수가 너무 많아서 연관성을 유추하기 힘들다.
• 10개 이상의 매개변수가 각 레이어에 중복되어 있어서 휴먼에러가 발생하기 쉽다.
디버깅이 힘들다.
• 사람이 이해하기 쉽고 상태를 조작하기 쉬운 객체 대신, DB에 데이터가 조작되는 것을
상상하며 코딩을 해야한다.
• 비즈니스 객체도 없기 때문에 디버깅을 할 수가 없다.
• 한번의 메서드(비즈니스 로직) 호출이 SQL의 실행을 의미하기 때문에, 데이터를 조작하거나
검증하는 코드와 SQL실행 메서드가 뒤죽박죽 섞이게 되고 이해하기 힘들어진다.
유효성 검사가 힘들다
• 데이터가 중간에 바뀔 위험에 노출 되어있는데, 사용자UI로부터 받은 데이터의 에러를 BizLogic까지
전파시키는 것은 좋지 않다.
DTO & Entity
편리한 개발을 막는 구조적 문제점
• 매개변수가 너무 많아 실수하기 쉽다.
• 비즈니스 객체가 없어 디버깅이 힘들고 OOP가 불가능하다.
• 비즈니스 객체란, Business Logic Layer에서 Business를 수행하기 위해 명령을 받는 객체이다.
• SQL 중심의 코드이기 때문에 이해가 힘들고, 변화에 취약하다.
• 각 SQL들이 하나의 비즈니스 로직이고, 이들은 사용자 UI로부터 받은 데이터에 종속된 SQL문이다.
PresentationLayer의 변화가 SQL 변화로 이어질 확률이 높고, SQL또한 리터럴이어서 실수하기 쉽다.
과거의 개발자들
• 과거 개발자들은 객체지향 언어를 사용함에도 불구하고 OOP를 하지못하고 개발자보다는 SQL Mapper에
가까웠다. OOP를 효과적으로 수행하기 위하여 테이블 데이터를 객체로 만들어 다루는 것을 시도하였고,
그것이 Entity이다.
Entity vs DTO vs VO
• Entity
• 테이블 데이터를 OOP스럽게 다루기 위하여 나온 테이블 데이터 모델링 클래스이자, 비즈니스 개체이다.
• 테이블 데이터를 모델링 했기 때문에 테이블의 key에 매치되는 필드가 Identity이다.
• DTO
• 클라이언트와 서버 사이에서 전송할 데이터를 담은 객체를 의미한다.
• 데이터를 담는 역할만 하는 객체이기 때문에 Identity가 없다.
• VO
• 값을 가지는 객체로 값 객체라고 불린다.
• Equals()로 Identity가 판단된다. 어떤 프로퍼티들을 Identity로 할지는 개발자의 몫이다.
Entity DTO VO
Has Identity Key Field X Specific Field
Has Business O X O
Has Value O O O
그래서 어떻게?
DTO & Entity
< DTO & Entity >
비즈니스 개체를 만들어라.
• Entity클래스를 만들어 비즈니스 로직의 주체가 Repository가 아닌 Entity개체가 되도록 하여
개발자가 SQL Mapper가 아닌 객체를 다루는 개발자가 되도록 하여야 한다.
• OOP가 가능해져서 이해하기 쉽고 디버깅이 쉬운 코드를 작성할 수 있다.
하나의 Repository에 하나의 Entity를 종속 시켜라.
• 하나의 Repository에 하나의 Entity또는 Entity Aggregate를 종속 시켜라.
• 하나의 Entity 혹은 Entity집합에 종속시켜 응집도를 높이자.
• Repository Factory Pattern을 사용하여 모듈화 시킨다.
• 더 이상 Repository는 비즈니스 로직을 책임지지 않는다. 비즈니스 로직은 BizLogic에서 Entity를 통해 수행한다.
Presentation Layer와 Application Layer를 분리하라
• DTO와 Entity를 같이 사용함으로써 Presentation Layer와 Application Layer를 완전 분리 시킨다.
• 변화가 잦은 Presentation Layer로부터 Application Layer를 보호한다.
• DTO는 선택적으로 필요한 데이터만 필드로 갖도록 하여 테이블 컬럼의 노출을 피한다.
• DTO를 사용하는 개발자는 필드로 있는 데이터만 신경 쓰면 된다.
• DTO의 목적과 Entity의 목적은 분명히 다르기에 하나가 다른 하나를 대신하면 안된다.
만약 Entity를 DTO처럼 사용한다면?
• 모든 필드가 오픈 되어 있기 때문에 사용자 UI로부터 받은 데이터가 무엇인지 알 수 없다.
• ORM을 사용한다면 연관관계에 있는 엔터티끼리 순환참조가 발생할 수 있다.
OOP 개발 흐름
1. 필요한 정보를 갖고 있는 Entity 탐색.
2. Repository에 필요한 데이터를 주는 화면
개발(주로 Idx or 조회조건 데이터들)
3. DTO 작성 및 유효성 검증조건 달기.
4. 테스트 코드로 단위 테스트.
5. 서버 구동 후, 직접 기능 테스트
• DTO사용으로 사용자 입력 데이터를 관리할 수 있다.
• Presentation Layer 와 Application Layer를 분리하
여 변화에 유연해 진다.
• 전체적으로 코드의 안정성이 올라간다.
Entity 적용 모습
public void CancelOrder(int orderIdx)
{
// 1)
Order order = _orderRepo.GetOrder(orderIdx);
Billing billing = _billingRepo.GetBilling(orderIdx);
Delivery delivery = _deliveryRepo.GetDelivery(orderIdx);
// 2)
string deliveryStatus = delivery.GetStatus();
// 3)
if(string.Equals(“RUNNING”, deliveryStatus))
{
delivery.Status = “CANCEL”;
deliveryRepo.Update(delivery);
}
// 4)
order.Status = “CANCEL”;
orderRepo.Update(order);
billing.Status = “CANCEL”;
buillingRepo.Update(billing);
}// 참고 : 스프링부트와 aws로 혼자 구현하는 웹서비스
public void CancelOrder(int orderIdx, string deliveryStatus)
{
// 2~3)
if(deliveryStatus.Equals(“RUNNING”, deliveryStatus))
{
deliveryStatus = “CANCEL”;
}
// 4)
_orderRepo.UpdateOrder(int orderIdx, deliveryStatus);
}
1) DB로부터 데이터 조회
2) 배송 취소를 해야 하는지 확인
3) 배송 중이라면 취소로 변경
4) 각 테이블에 취소 상태 Update
• 언뜻 보면 짧아서 더 좋아 보일 수 있다. 하지만 해당 코드의 핵심인
UpdateOrder로는 어떤 비즈니스 기능을 수행하는지 알 수 없다.
• 로직을 이해하기 위해서는 BizLogic이 아니라 Repository 코드를 봐야
한다.
• idx이외에 인자에 종속된 메서드로서 재 사용성이 떨어지고, 변화하기
쉽다.
• 역할이 큰 SQL이 리터럴이기 때문에 실수하기 쉽다.
string sql = @“
UPDATE ORDER
SET DeliveryStatus = @DeliveryStatus
WHERE ORDER.orderIdx = @OrderIdx
UPDATE BILLING
…
”;
• Repository로 부터 비즈니스 개체를 받아 비즈니스 로직을 수행
한다.
• BizLogic만 보면 기능을 이해할 수 있다.
• 논리적인 순서로 비즈니스 객체가 주체가 되어 수행되기 때문에
유지보수가 훨씬 간편해지고, 컴파일러의 도움으로 수정 시 실수
를 줄일 수 있다.
어떤 효과가 있나요?
유지보수가 쉬워진다.
• 원시 타입으로 데이터를 받았다면 PostTitle을 Title로
변경해야 할 때, 13군데에 수정작업을 해야 했다. (테
스트 코드 포함)
• DTO를 사용한다면 IDE의 Refactoring 기능을 사용
하여 한번에 수정이 가능하다.
디버깅이 쉬워진다.
• 비즈니스 객체를 사용함으로써 디버깅이 쉬워진다.
• 코드를 이해하기 쉬워진다.
간단한 유효성 검사가 쉬워진다.
• 데이터를 가지고있는 DTO에서 유효성조건을
관리하기 때문에 직관적이고 관리가 쉽다.
• 응집도를 높여 스파게티 코드를 방지한다.
• 컨트롤러의 애트리뷰트로 사용해 사용하기가 쉽
다.
• WebApiConfig에 필터로서 추가하여 자동화 가
능하다.
DataTable vs DTO
DataTable DTO
장점
 타입에 상관없이 집어넣을 수 있다.
 SqlDataReader에서 변환이 쉽다.
 필요한 정보만 프로퍼티로 가지고 있기 때문에 생각하지 않고 프로퍼티를 사용 가능하다.
 명시적인 타입지정으로 매핑 시 올바르지 않은 데이터가 들어올 경우 빠르게 에러를
감지할 수 있다.
 의미 있는 타입명으로 코드를 더 읽기 쉽게 해준다.
 기본 값을 지정하여 데이터가 없을 경우를 대비할 수 있다.
 엔터티 개체와 같이 사용할 경우 PresentationLayer와 ApplicationLayer를 분리하는
효과가 있다.
단점
 DataTable에 어떤 키가 들어가 있는지 알기 힘들다. 즉,
쿼리수행 결과 데이터를 알기 힘들다.
 DataTable에서 값을 꺼내야 하는 경우 Type Casting
과정에서 에러 발생 가능성이 있다.
 꼭 들어가야 하는 데이터에 null이 들어가도 유효성을
체크하기 어렵다.
 DataTable보다 메모리를 많이 차지한다.
 매핑 과정이 필요하기 때문에 성능적으로 조금 손해를 본다.
 DTO클래스 파일이 많아질 수 있다.
DataTable이 아닌 DTO로!
DTO를 그대로 .aspx파일에서 사용함으로써 쓸데없이 많은 필드 수를 줄
일 수 있다.
• 논리적 의미를 알기 힘든 코드를 작성하지 않아도 된다.
• 문자열 리터럴로 인한 개발자의 실수 가능성을 없앤다.
왜 DTO를 적용하지 않을까?
1. 계층화 된 Complex Json Data를 DTO로 받기가 힘들다.
• Complex Json Data Parse 해결
2. 현재 폴더 구조에서 서로 다른 도메인 폴더에 있는 Controller가 같은 일을 하는 경우 DTO의
위치가 애매해 진다.
3. DTO를 한곳에 모은다면 그 수가 너무 많아진다.
Complex Json Data Parse
< jQuery code> < DTO Class>
• .NET의 컨트롤러 파라미터 바인딩은 타입을 기준으로 프로퍼티를 찾고 여러 개라면 그
중 이름으로 바인딩 한다.
• JsonProperty 애트리뷰트를 사용한다면 타입이 일치하지 않더라도 이름으로 바인딩을
시도할 수 있다.
ORM
개발자들이 한 고민
public void CancelOrder(int orderIdx)
{
// 1)
Order order = _orderRepo.GetOrder(orderIdx);
Billing billing = _billingRepo.GetBilling(orderIdx);
Delivery delivery = _deliveryRepo.GetDelivery(orderIdx);
// 2)
string deliveryStatus = delivery.GetStatus();
if(string.Equals(“RUNNING”, deliveryStatus))
{
delivery.Status = “CANCEL”;
deliveryRepo.Update(delivery);
}
// 3)
order.Status = “CANCEL”;
orderRepo.Update(order);
billing.Status = “CANCEL”;
buillingRepo.Update(billing);
} // 출처 : 스프링부트와 aws로 혼자 구현하는 웹서비스
1. 테이블들의 관계를 객체 사이의 관계로 표현할 수가 없
어 관계를 맺고 있는 엔티티들을 따로따로 가져와야 함.
2. 객체의 상태변화를 개발자가 직접 체크 하여 쿼리를 수
행할지 말지 정해야 하기 때문에 getter를 쓸 수 밖에 없
고 캡슐화가 깨진다.
3. 객체에게 명령하는 것이 아닌 객체의 상태를 직접 조작
하고 쿼리를 수행함.
ORM의 등장 배경
• 데이터를 정형화 하여 관리하려는 RDBS와 사물을 추상화하여 이해하려는 OOP는 서로 존재의 목적이 다르기
때문에 다음의 문제들이 완벽한 OOP의 큰 장애물이 되었다.
1. 객체와 테이블의 identity 차이
• 객체는 보통 HashCode와 타입으로 동일성을 나타낸다.
• 테이블은 Primary키로 나타낸다.
2. 테이블 간의 연관관계와 객체의 참조의 차이
• 테이블은 외래키로 관계를 표현하며 관계의 방향이 없다
• 객체는 참조를 통해 관계를 표현하며 분명한 관계의 방향이 존재한다.
• 아무리 SOLID원칙을 지켜도 근본적인 패러다임 불일치 문제를 해결하지 못해, 각각의 Idx로 엔터티 개체를 얻
고, 엔터티로 부터 데이터를 꺼내 조작하는 캡슐화를 위배할 수 밖에 없었다.
• 두 패러다임의 불일치 문제를 해결하기 위해 개발자들이 머리를 맞대 만들어 낸 것이
ObjectRelationalMapping(ORM)이다.
EntityFramework
< EntityFramework 적용 >
ORM을 통한 코드 변화
public void CancelOrder(int orderIdx)
{
// 1)
Order order = _orderRepo.FindByIdx(orderIdx);
Billing billing = order.Billing;
Delivery delivery = order.Delivery;
// 2~3)
delivery.Cancel();
// 4)
order.Cancel();
billing.Cancel();
} // 출처 : 스프링부트와 aws로 혼자 구현하는 웹서비스
public void CancelOrder(int orderIdx)
{
// 1)
Order order = _orderRepo.GetOrder(orderIdx);
Billing billing = _billingRepo.GetBilling(orderIdx);
Delivery delivery = _deliveryRepo.GetDelivery(orderIdx);
// 2)
string deliveryStatus = delivery.GetStatus();
// 3)
if(string.Equals(“RUNNING”, deliveryStatus))
{
delivery.Status = “CANCEL”;
deliveryRepo.Update(delivery);
}
// 4)
order.Status = “CANCEL”;
orderRepo.Update(order);
billing.Status = “CANCEL”;
buillingRepo.Update(billing);
} // 출처 : 스프링부트와 aws로 혼자 구현하는 웹서비스
• 테이블 간의 관계를 객체로 표현할 수 있게 되었다.
• 영속성 컨텍스트를 통해 Entity들의 변화를 개발자가 아닌 EF가 대신 추적해 준다.
• 개발자는 객체에게 명령만하면 객체가 알아서 자신의 데이터를 직접 조작하고 EF는 변화를 인지해 자동으로 DB에
반영한다.
ORM의 영속성 이용
Repository에게 데이터를 보내고 모든 것을 맡기던 코드에서
PostEntity에게 직접 상태를 변화하라는 명령을 보내는 방식으로 변
경되었다.
OOP 네이밍
Repository로부터 데이터를 꺼내는 느낌인
Get*()에서 EF가 적용된 Repository에게
찾으라고 명령하는 OOP느낌이 강한 네이
밍으로 바꿨다. Entity를 DTO로 간단하게 변경하여 반환한다.
프로젝트에 EF Core 3.1 도입하기 (Entity Class)
• 고객들의 DB에는 뷰만 존재하기 때문에 DbContext가 날리는
쿼리의 테이블이 SQL Server에서 자동으로 뷰를 가리키도록
한다. 만약 테이블이 존재한다면 예상치 못한 에러가 발생할 수
있겠다.
• 뷰에는 TenantIdx가 없기 때문에 필드로 TenantIdx를 넣는다면
유효하지 않은 컬럼 명 에러가 발생한다.
• Idx를 long으로 하면 타입 에러가 발생한다.
• Entity개체의 프로퍼티는 private set을 해도 매핑이 되기 때문
에 private set을 하는 것이 좋다.
프로젝트에 EF Core 3.1 도입하기(DbContext)
• DbContext에서 접근할 엔터티 개체들을 필드로 지정할 수 있다.
• AddInterceptors()를 통해 메서드를 중간에 변형시킬 수 있는 객체를 설정할 수 있다.
프로젝트에 EF Core 3.1 도입하기(DbCommandInterceptor)
• command.CommandText 프로퍼티를 통해 sql을 조작할 수 있다.
프로젝트에 EF Core 3.1
도입하기
• EF Core는 Insert 후 반환되는 Identity와 자신이 예측하고 있는 Identity를 통해 동시성 오류가 발생했는지 체크한다.
• 현재 뷰들은 트리거를 통해 Insert문을 변환하고 있는데 Identity를 반환하지 않아 동시성 오류가 발생한다. Identity를 반
환하는 구문을 추가해야 한다.
< Insert 실행 시 DbContext가 DB에 보내는 쿼리 >
EntityFramework의 실용성
• 현재 프로젝트의 타겟 버전은 .NET Framework 4.7.2로서 EF core 3.1 과 EF 6.4를
사용할 수 있습니다. 해당 버전들은 조회 데이터가 10만 이상이거나 여러 테이블을
조인 시 성능저하가 심하거나 에러가 발생하기 때문에 성능이 중요한 조회에는 사용이
힘들 것으로 보입니다.
• 11월에 .NET 6 배포에 맞춰 EF Core 5 보다 훨씬 버전업한 EF Core 6을 발표할
예정으로 보입니다.
• URL : https://devblogs.microsoft.com/dotnet/announcing-entity-framework-core-6-0-preview-4-
performance-edition/
• 현재 사용중인 DataContext 와 병행이 가능한 것으로 보인다.
Unit Test
xUnit의 LifeCycle과 커스텀한 트랜잭션 전략
< xUnit의 기본 LifeCycle > < 애트리뷰트를 이용한 트랜잭션 전략>
PostRepository의 테스트 코드
• 코드 URL : [보안]
테스트 간의 컨텍스트 공유
Reference : https://xunit.net/docs/shared-context
마치며
• 앞에서 말한 것들은 SQL중심의 코드에서 ORM 적용까지의 과정을 보인 것입니다. 개발이 편해지고 즐거워지기
위해서는 SQL 중심의 코드를 벗어나야 합니다.
• OOP를 하기 위해선 비즈니스 로직의 책임을 Repository가 아니라 비즈니스 객체를 만들어 그것에게 할당해야 합니다.
• 아직 업무를 많이 다뤄보지 않아 제가 말한 것들이 그대로 적용 가능할지는 모르겠습니다. 하지만 팀원분들에게 좋은
영감을 주었으면 좋겠다는 마음으로 열심히 만들었습니다. 봐주셔서 감사합니다.

More Related Content

What's hot

도메인 주도 설계 - 6장 도메인 객체의 생명주기
도메인 주도 설계 - 6장 도메인 객체의 생명주기도메인 주도 설계 - 6장 도메인 객체의 생명주기
도메인 주도 설계 - 6장 도메인 객체의 생명주기JangHyuk You
 
영속성 컨텍스트로 보는 JPA
영속성 컨텍스트로 보는 JPA영속성 컨텍스트로 보는 JPA
영속성 컨텍스트로 보는 JPA경원 이
 
SpringDataJPA - 스프링 캠프
SpringDataJPA - 스프링 캠프SpringDataJPA - 스프링 캠프
SpringDataJPA - 스프링 캠프Younghan Kim
 
Jpa 잘 (하는 척) 하기
Jpa 잘 (하는 척) 하기Jpa 잘 (하는 척) 하기
Jpa 잘 (하는 척) 하기경원 이
 
Hibernate start (하이버네이트 시작하기)
Hibernate start (하이버네이트 시작하기)Hibernate start (하이버네이트 시작하기)
Hibernate start (하이버네이트 시작하기)visual khh
 
Ksug2015 - JPA1, JPA 소개
Ksug2015 - JPA1, JPA 소개Ksug2015 - JPA1, JPA 소개
Ksug2015 - JPA1, JPA 소개Younghan Kim
 
ORM을 활용할 경우의 설계, 개발 과정
ORM을 활용할 경우의 설계, 개발 과정ORM을 활용할 경우의 설계, 개발 과정
ORM을 활용할 경우의 설계, 개발 과정Javajigi Jaesung
 
유지보수 가능한 개발 원칙
유지보수 가능한 개발 원칙유지보수 가능한 개발 원칙
유지보수 가능한 개발 원칙Hyosang Hong
 
Ksug2015 jpa4 객체지향쿼리
Ksug2015 jpa4 객체지향쿼리Ksug2015 jpa4 객체지향쿼리
Ksug2015 jpa4 객체지향쿼리Younghan Kim
 
아꿈사 DDD(Domain-Driven Design) 5장 소프트웨어에서 표현되는 모델
아꿈사 DDD(Domain-Driven Design) 5장 소프트웨어에서 표현되는 모델아꿈사 DDD(Domain-Driven Design) 5장 소프트웨어에서 표현되는 모델
아꿈사 DDD(Domain-Driven Design) 5장 소프트웨어에서 표현되는 모델명환 안
 
Java script 강의자료_ed13
Java script 강의자료_ed13Java script 강의자료_ed13
Java script 강의자료_ed13hungrok
 
MyBatis에서 JPA로
MyBatis에서 JPA로MyBatis에서 JPA로
MyBatis에서 JPA로Dongmin Shin
 
[오픈소스컨설팅]MyBatis Basic
[오픈소스컨설팅]MyBatis Basic[오픈소스컨설팅]MyBatis Basic
[오픈소스컨설팅]MyBatis BasicJi-Woong Choi
 
Jquery javascript_ed10
Jquery javascript_ed10Jquery javascript_ed10
Jquery javascript_ed10hungrok
 
QnA blog using Django - ORM, 회원가입, 로그인/로그아웃
QnA blog using Django - ORM, 회원가입, 로그인/로그아웃QnA blog using Django - ORM, 회원가입, 로그인/로그아웃
QnA blog using Django - ORM, 회원가입, 로그인/로그아웃Kwangyoun Jung
 
토비의 스프링 - DI
토비의 스프링 - DI토비의 스프링 - DI
토비의 스프링 - DIJU Chae
 
Web server page_ed10
Web server page_ed10Web server page_ed10
Web server page_ed10hungrok
 

What's hot (20)

도메인 주도 설계 - 6장 도메인 객체의 생명주기
도메인 주도 설계 - 6장 도메인 객체의 생명주기도메인 주도 설계 - 6장 도메인 객체의 생명주기
도메인 주도 설계 - 6장 도메인 객체의 생명주기
 
영속성 컨텍스트로 보는 JPA
영속성 컨텍스트로 보는 JPA영속성 컨텍스트로 보는 JPA
영속성 컨텍스트로 보는 JPA
 
SpringDataJPA - 스프링 캠프
SpringDataJPA - 스프링 캠프SpringDataJPA - 스프링 캠프
SpringDataJPA - 스프링 캠프
 
Jpa 잘 (하는 척) 하기
Jpa 잘 (하는 척) 하기Jpa 잘 (하는 척) 하기
Jpa 잘 (하는 척) 하기
 
Java JPA
Java JPAJava JPA
Java JPA
 
Hibernate start (하이버네이트 시작하기)
Hibernate start (하이버네이트 시작하기)Hibernate start (하이버네이트 시작하기)
Hibernate start (하이버네이트 시작하기)
 
Ksug2015 - JPA1, JPA 소개
Ksug2015 - JPA1, JPA 소개Ksug2015 - JPA1, JPA 소개
Ksug2015 - JPA1, JPA 소개
 
ORM을 활용할 경우의 설계, 개발 과정
ORM을 활용할 경우의 설계, 개발 과정ORM을 활용할 경우의 설계, 개발 과정
ORM을 활용할 경우의 설계, 개발 과정
 
유지보수 가능한 개발 원칙
유지보수 가능한 개발 원칙유지보수 가능한 개발 원칙
유지보수 가능한 개발 원칙
 
Ksug2015 jpa4 객체지향쿼리
Ksug2015 jpa4 객체지향쿼리Ksug2015 jpa4 객체지향쿼리
Ksug2015 jpa4 객체지향쿼리
 
아꿈사 DDD(Domain-Driven Design) 5장 소프트웨어에서 표현되는 모델
아꿈사 DDD(Domain-Driven Design) 5장 소프트웨어에서 표현되는 모델아꿈사 DDD(Domain-Driven Design) 5장 소프트웨어에서 표현되는 모델
아꿈사 DDD(Domain-Driven Design) 5장 소프트웨어에서 표현되는 모델
 
Java script 강의자료_ed13
Java script 강의자료_ed13Java script 강의자료_ed13
Java script 강의자료_ed13
 
MyBatis에서 JPA로
MyBatis에서 JPA로MyBatis에서 JPA로
MyBatis에서 JPA로
 
[오픈소스컨설팅]MyBatis Basic
[오픈소스컨설팅]MyBatis Basic[오픈소스컨설팅]MyBatis Basic
[오픈소스컨설팅]MyBatis Basic
 
Java script
Java scriptJava script
Java script
 
Jquery javascript_ed10
Jquery javascript_ed10Jquery javascript_ed10
Jquery javascript_ed10
 
javascript03
javascript03javascript03
javascript03
 
QnA blog using Django - ORM, 회원가입, 로그인/로그아웃
QnA blog using Django - ORM, 회원가입, 로그인/로그아웃QnA blog using Django - ORM, 회원가입, 로그인/로그아웃
QnA blog using Django - ORM, 회원가입, 로그인/로그아웃
 
토비의 스프링 - DI
토비의 스프링 - DI토비의 스프링 - DI
토비의 스프링 - DI
 
Web server page_ed10
Web server page_ed10Web server page_ed10
Web server page_ed10
 

Similar to Sql 중심 코드 탈피 발표자료

Jpa 쿼리 포함 자료
Jpa 쿼리 포함 자료Jpa 쿼리 포함 자료
Jpa 쿼리 포함 자료Hyosang Hong
 
클린 코드 part2
클린 코드 part2클린 코드 part2
클린 코드 part2Minseok Jang
 
좌충우돌 ORM 개발기 2012 DAUM DEVON
좌충우돌 ORM 개발기 2012 DAUM DEVON좌충우돌 ORM 개발기 2012 DAUM DEVON
좌충우돌 ORM 개발기 2012 DAUM DEVONYounghan Kim
 
Scala, Spring-Boot, JPA의 불편하면서도 즐거운 동거
Scala, Spring-Boot, JPA의 불편하면서도 즐거운 동거Scala, Spring-Boot, JPA의 불편하면서도 즐거운 동거
Scala, Spring-Boot, JPA의 불편하면서도 즐거운 동거Javajigi Jaesung
 
자바 웹 개발 시작하기 (5주차 : 스프링 프래임워크)
자바 웹 개발 시작하기 (5주차 : 스프링 프래임워크)자바 웹 개발 시작하기 (5주차 : 스프링 프래임워크)
자바 웹 개발 시작하기 (5주차 : 스프링 프래임워크)DK Lee
 
Node.js에서 공공API를 활용해서 개발하기
Node.js에서 공공API를 활용해서 개발하기Node.js에서 공공API를 활용해서 개발하기
Node.js에서 공공API를 활용해서 개발하기Inho Kwon
 
버클리Db 를 이용한 게임 서버 제작
버클리Db 를 이용한 게임 서버 제작버클리Db 를 이용한 게임 서버 제작
버클리Db 를 이용한 게임 서버 제작Vong Sik Kong
 
보다 나은 웹 어플리케이션 설계
보다 나은 웹 어플리케이션 설계보다 나은 웹 어플리케이션 설계
보다 나은 웹 어플리케이션 설계Eb Styles
 
[HaU] 신입 기술 면접 준비 java
[HaU] 신입 기술 면접 준비 java[HaU] 신입 기술 면접 준비 java
[HaU] 신입 기술 면접 준비 java유리 하
 
자바 웹 개발 시작하기 (4주차 : MVC)
자바 웹 개발 시작하기 (4주차 : MVC)자바 웹 개발 시작하기 (4주차 : MVC)
자바 웹 개발 시작하기 (4주차 : MVC)DK Lee
 
All about JDBC Performance Tuning_Wh apm
All about JDBC Performance Tuning_Wh apmAll about JDBC Performance Tuning_Wh apm
All about JDBC Performance Tuning_Wh apm엑셈
 
[Spring]오브젝트와 의존관계
[Spring]오브젝트와 의존관계[Spring]오브젝트와 의존관계
[Spring]오브젝트와 의존관계slowstarter
 
NDC 11 자이언트 서버의 비밀
NDC 11 자이언트 서버의 비밀NDC 11 자이언트 서버의 비밀
NDC 11 자이언트 서버의 비밀승명 양
 
[스프링 스터디 1일차] 템플릿
[스프링 스터디 1일차] 템플릿[스프링 스터디 1일차] 템플릿
[스프링 스터디 1일차] 템플릿AnselmKim
 
2007년 제8회 JCO 컨퍼런스 POJO 프로그래밍 발표 자료
2007년 제8회 JCO 컨퍼런스 POJO 프로그래밍 발표 자료2007년 제8회 JCO 컨퍼런스 POJO 프로그래밍 발표 자료
2007년 제8회 JCO 컨퍼런스 POJO 프로그래밍 발표 자료beom kyun choi
 
Fundamentals of Oracle SQL
Fundamentals of Oracle SQLFundamentals of Oracle SQL
Fundamentals of Oracle SQLJAEGEUN YU
 

Similar to Sql 중심 코드 탈피 발표자료 (20)

Jpa 쿼리 포함 자료
Jpa 쿼리 포함 자료Jpa 쿼리 포함 자료
Jpa 쿼리 포함 자료
 
클린 코드 part2
클린 코드 part2클린 코드 part2
클린 코드 part2
 
좌충우돌 ORM 개발기 2012 DAUM DEVON
좌충우돌 ORM 개발기 2012 DAUM DEVON좌충우돌 ORM 개발기 2012 DAUM DEVON
좌충우돌 ORM 개발기 2012 DAUM DEVON
 
Scala, Spring-Boot, JPA의 불편하면서도 즐거운 동거
Scala, Spring-Boot, JPA의 불편하면서도 즐거운 동거Scala, Spring-Boot, JPA의 불편하면서도 즐거운 동거
Scala, Spring-Boot, JPA의 불편하면서도 즐거운 동거
 
DDD Start Ch#3
DDD Start Ch#3DDD Start Ch#3
DDD Start Ch#3
 
자바 웹 개발 시작하기 (5주차 : 스프링 프래임워크)
자바 웹 개발 시작하기 (5주차 : 스프링 프래임워크)자바 웹 개발 시작하기 (5주차 : 스프링 프래임워크)
자바 웹 개발 시작하기 (5주차 : 스프링 프래임워크)
 
Node.js에서 공공API를 활용해서 개발하기
Node.js에서 공공API를 활용해서 개발하기Node.js에서 공공API를 활용해서 개발하기
Node.js에서 공공API를 활용해서 개발하기
 
버클리Db 를 이용한 게임 서버 제작
버클리Db 를 이용한 게임 서버 제작버클리Db 를 이용한 게임 서버 제작
버클리Db 를 이용한 게임 서버 제작
 
보다 나은 웹 어플리케이션 설계
보다 나은 웹 어플리케이션 설계보다 나은 웹 어플리케이션 설계
보다 나은 웹 어플리케이션 설계
 
dbt 101
dbt 101dbt 101
dbt 101
 
My di container
My di containerMy di container
My di container
 
React js 1
React js   1React js   1
React js 1
 
[HaU] 신입 기술 면접 준비 java
[HaU] 신입 기술 면접 준비 java[HaU] 신입 기술 면접 준비 java
[HaU] 신입 기술 면접 준비 java
 
자바 웹 개발 시작하기 (4주차 : MVC)
자바 웹 개발 시작하기 (4주차 : MVC)자바 웹 개발 시작하기 (4주차 : MVC)
자바 웹 개발 시작하기 (4주차 : MVC)
 
All about JDBC Performance Tuning_Wh apm
All about JDBC Performance Tuning_Wh apmAll about JDBC Performance Tuning_Wh apm
All about JDBC Performance Tuning_Wh apm
 
[Spring]오브젝트와 의존관계
[Spring]오브젝트와 의존관계[Spring]오브젝트와 의존관계
[Spring]오브젝트와 의존관계
 
NDC 11 자이언트 서버의 비밀
NDC 11 자이언트 서버의 비밀NDC 11 자이언트 서버의 비밀
NDC 11 자이언트 서버의 비밀
 
[스프링 스터디 1일차] 템플릿
[스프링 스터디 1일차] 템플릿[스프링 스터디 1일차] 템플릿
[스프링 스터디 1일차] 템플릿
 
2007년 제8회 JCO 컨퍼런스 POJO 프로그래밍 발표 자료
2007년 제8회 JCO 컨퍼런스 POJO 프로그래밍 발표 자료2007년 제8회 JCO 컨퍼런스 POJO 프로그래밍 발표 자료
2007년 제8회 JCO 컨퍼런스 POJO 프로그래밍 발표 자료
 
Fundamentals of Oracle SQL
Fundamentals of Oracle SQLFundamentals of Oracle SQL
Fundamentals of Oracle SQL
 

Sql 중심 코드 탈피 발표자료

  • 3. 기존 개발 흐름 1. 도메인 별로 SQL 구상. • DB를 상상하며 SQL 실행 순서까지 결정. 2. SQL에 필요한 데이터를 줄 수 있는 화면 개발. 3. 도메인 별로 Repository를 나누고, SQL 작성. 4. SQL 직접 테스트. 5. Layer별로 1번에서 보내는 데이터를 받도록 매개변수를 설정하고 SQL에 매핑. 6. 직접 기능 테스트. • 사용자 UI 데이터와 SQL이 연결된다. • Low-Level 모듈인 Repository가 변하기 쉽다.(DIP 원칙 위배) • SQL간의 결과 데이터를 공유하게 구상하는 것은 불가 능. • 각각의 SQL들이 필요한 데이터를 모두 매개변수 에 추가. (많은 매개변수의 원인) • SQL이 매개변수에 종속되기 때문에 메서드 재사용 가 능성이 현저히 떨어짐. (안좋은 Low-Level 모듈)
  • 4. OOP가 불가능하다. • OOP의 기본은 비즈니스 로직 수행 시, 로직에 필요한 데이터를 상태(필드)로 가지는 객체에게 명령하는 것인데 상태를 가지는 객체가 없다. • DB에 접근하는 레이어인 Repository가 비즈니스 로직의 주체가 되어 행동한다. • UpdateInteract()와 DeleteInteractTag는 DB에 수행하는 SQL을 암시할 뿐 어떤 비즈니스를 수행하는지 알 수 없다. • BizLogic은 마치 쿼리를 여러 개 모아 놓고 순서를 조율한 것과 같다. 즉, 철저히 SQL 중심의 구조이다. • 객체가 없기 때문에 데이터를 주고받기 힘들고, 데이터가 필요하면 매개변수를 늘리는 악순환이 반복됩니다.
  • 5. 연관성을 유추하기 힘들다. • 코드를 짠 개발자는 연관성이 보이겠지만, 유지보수 하는 개발자가 보기엔 각각의 데이터로 보인다. • 비즈니스 로직의 주체인 Repository의 메서드 명은 비즈니스 로직을 암시하지 못하고 각 데이터는 어디에 쓰여야 하는지 불분명하다. • Strong-Type언어인 C#에서 컴파일러가 타입으로 매핑의 실수를 잡아주지 못하기 때문에 런타임 에러를 발생시키고 훨씬 많은 시간을 소비한다. • 애트리뷰트를 사용해서 그것을 예방하지만 안 그래도 긴 호출인자가 더 길어진다.
  • 6. 매개변수가 너무 많다. • 루틴 매개변수의 수를 7개 정도로 제한한다. 심리학 연구에 따르면 사람들은 일반적으로 한 번에 7개 이상의 정보 묶음을 추적할 수 없다고 한다. 이러한 발견은 여러 규칙에 적용됐으며 같은 논리로 대부분의 사람이 한 번에 7개 이상의 루틴 매개변수를 추적하는 건 쉽지 않다고 이해하면 될 것 같다. - << Code Complete 2 (스티브 맥코넬) >> • 매개변수가 너무 많아서 연관성을 유추하기 힘들다. • 10개 이상의 매개변수가 각 레이어에 중복되어 있어서 휴먼에러가 발생하기 쉽다.
  • 7. 디버깅이 힘들다. • 사람이 이해하기 쉽고 상태를 조작하기 쉬운 객체 대신, DB에 데이터가 조작되는 것을 상상하며 코딩을 해야한다. • 비즈니스 객체도 없기 때문에 디버깅을 할 수가 없다. • 한번의 메서드(비즈니스 로직) 호출이 SQL의 실행을 의미하기 때문에, 데이터를 조작하거나 검증하는 코드와 SQL실행 메서드가 뒤죽박죽 섞이게 되고 이해하기 힘들어진다.
  • 8. 유효성 검사가 힘들다 • 데이터가 중간에 바뀔 위험에 노출 되어있는데, 사용자UI로부터 받은 데이터의 에러를 BizLogic까지 전파시키는 것은 좋지 않다.
  • 10. 편리한 개발을 막는 구조적 문제점 • 매개변수가 너무 많아 실수하기 쉽다. • 비즈니스 객체가 없어 디버깅이 힘들고 OOP가 불가능하다. • 비즈니스 객체란, Business Logic Layer에서 Business를 수행하기 위해 명령을 받는 객체이다. • SQL 중심의 코드이기 때문에 이해가 힘들고, 변화에 취약하다. • 각 SQL들이 하나의 비즈니스 로직이고, 이들은 사용자 UI로부터 받은 데이터에 종속된 SQL문이다. PresentationLayer의 변화가 SQL 변화로 이어질 확률이 높고, SQL또한 리터럴이어서 실수하기 쉽다.
  • 11. 과거의 개발자들 • 과거 개발자들은 객체지향 언어를 사용함에도 불구하고 OOP를 하지못하고 개발자보다는 SQL Mapper에 가까웠다. OOP를 효과적으로 수행하기 위하여 테이블 데이터를 객체로 만들어 다루는 것을 시도하였고, 그것이 Entity이다.
  • 12. Entity vs DTO vs VO • Entity • 테이블 데이터를 OOP스럽게 다루기 위하여 나온 테이블 데이터 모델링 클래스이자, 비즈니스 개체이다. • 테이블 데이터를 모델링 했기 때문에 테이블의 key에 매치되는 필드가 Identity이다. • DTO • 클라이언트와 서버 사이에서 전송할 데이터를 담은 객체를 의미한다. • 데이터를 담는 역할만 하는 객체이기 때문에 Identity가 없다. • VO • 값을 가지는 객체로 값 객체라고 불린다. • Equals()로 Identity가 판단된다. 어떤 프로퍼티들을 Identity로 할지는 개발자의 몫이다. Entity DTO VO Has Identity Key Field X Specific Field Has Business O X O Has Value O O O
  • 14. DTO & Entity < DTO & Entity >
  • 15. 비즈니스 개체를 만들어라. • Entity클래스를 만들어 비즈니스 로직의 주체가 Repository가 아닌 Entity개체가 되도록 하여 개발자가 SQL Mapper가 아닌 객체를 다루는 개발자가 되도록 하여야 한다. • OOP가 가능해져서 이해하기 쉽고 디버깅이 쉬운 코드를 작성할 수 있다.
  • 16. 하나의 Repository에 하나의 Entity를 종속 시켜라. • 하나의 Repository에 하나의 Entity또는 Entity Aggregate를 종속 시켜라. • 하나의 Entity 혹은 Entity집합에 종속시켜 응집도를 높이자. • Repository Factory Pattern을 사용하여 모듈화 시킨다. • 더 이상 Repository는 비즈니스 로직을 책임지지 않는다. 비즈니스 로직은 BizLogic에서 Entity를 통해 수행한다.
  • 17. Presentation Layer와 Application Layer를 분리하라 • DTO와 Entity를 같이 사용함으로써 Presentation Layer와 Application Layer를 완전 분리 시킨다. • 변화가 잦은 Presentation Layer로부터 Application Layer를 보호한다. • DTO는 선택적으로 필요한 데이터만 필드로 갖도록 하여 테이블 컬럼의 노출을 피한다. • DTO를 사용하는 개발자는 필드로 있는 데이터만 신경 쓰면 된다. • DTO의 목적과 Entity의 목적은 분명히 다르기에 하나가 다른 하나를 대신하면 안된다. 만약 Entity를 DTO처럼 사용한다면? • 모든 필드가 오픈 되어 있기 때문에 사용자 UI로부터 받은 데이터가 무엇인지 알 수 없다. • ORM을 사용한다면 연관관계에 있는 엔터티끼리 순환참조가 발생할 수 있다.
  • 18. OOP 개발 흐름 1. 필요한 정보를 갖고 있는 Entity 탐색. 2. Repository에 필요한 데이터를 주는 화면 개발(주로 Idx or 조회조건 데이터들) 3. DTO 작성 및 유효성 검증조건 달기. 4. 테스트 코드로 단위 테스트. 5. 서버 구동 후, 직접 기능 테스트 • DTO사용으로 사용자 입력 데이터를 관리할 수 있다. • Presentation Layer 와 Application Layer를 분리하 여 변화에 유연해 진다. • 전체적으로 코드의 안정성이 올라간다.
  • 19. Entity 적용 모습 public void CancelOrder(int orderIdx) { // 1) Order order = _orderRepo.GetOrder(orderIdx); Billing billing = _billingRepo.GetBilling(orderIdx); Delivery delivery = _deliveryRepo.GetDelivery(orderIdx); // 2) string deliveryStatus = delivery.GetStatus(); // 3) if(string.Equals(“RUNNING”, deliveryStatus)) { delivery.Status = “CANCEL”; deliveryRepo.Update(delivery); } // 4) order.Status = “CANCEL”; orderRepo.Update(order); billing.Status = “CANCEL”; buillingRepo.Update(billing); }// 참고 : 스프링부트와 aws로 혼자 구현하는 웹서비스 public void CancelOrder(int orderIdx, string deliveryStatus) { // 2~3) if(deliveryStatus.Equals(“RUNNING”, deliveryStatus)) { deliveryStatus = “CANCEL”; } // 4) _orderRepo.UpdateOrder(int orderIdx, deliveryStatus); } 1) DB로부터 데이터 조회 2) 배송 취소를 해야 하는지 확인 3) 배송 중이라면 취소로 변경 4) 각 테이블에 취소 상태 Update • 언뜻 보면 짧아서 더 좋아 보일 수 있다. 하지만 해당 코드의 핵심인 UpdateOrder로는 어떤 비즈니스 기능을 수행하는지 알 수 없다. • 로직을 이해하기 위해서는 BizLogic이 아니라 Repository 코드를 봐야 한다. • idx이외에 인자에 종속된 메서드로서 재 사용성이 떨어지고, 변화하기 쉽다. • 역할이 큰 SQL이 리터럴이기 때문에 실수하기 쉽다. string sql = @“ UPDATE ORDER SET DeliveryStatus = @DeliveryStatus WHERE ORDER.orderIdx = @OrderIdx UPDATE BILLING … ”; • Repository로 부터 비즈니스 개체를 받아 비즈니스 로직을 수행 한다. • BizLogic만 보면 기능을 이해할 수 있다. • 논리적인 순서로 비즈니스 객체가 주체가 되어 수행되기 때문에 유지보수가 훨씬 간편해지고, 컴파일러의 도움으로 수정 시 실수 를 줄일 수 있다.
  • 21. 유지보수가 쉬워진다. • 원시 타입으로 데이터를 받았다면 PostTitle을 Title로 변경해야 할 때, 13군데에 수정작업을 해야 했다. (테 스트 코드 포함) • DTO를 사용한다면 IDE의 Refactoring 기능을 사용 하여 한번에 수정이 가능하다.
  • 22. 디버깅이 쉬워진다. • 비즈니스 객체를 사용함으로써 디버깅이 쉬워진다. • 코드를 이해하기 쉬워진다.
  • 23. 간단한 유효성 검사가 쉬워진다. • 데이터를 가지고있는 DTO에서 유효성조건을 관리하기 때문에 직관적이고 관리가 쉽다. • 응집도를 높여 스파게티 코드를 방지한다. • 컨트롤러의 애트리뷰트로 사용해 사용하기가 쉽 다. • WebApiConfig에 필터로서 추가하여 자동화 가 능하다.
  • 24. DataTable vs DTO DataTable DTO 장점  타입에 상관없이 집어넣을 수 있다.  SqlDataReader에서 변환이 쉽다.  필요한 정보만 프로퍼티로 가지고 있기 때문에 생각하지 않고 프로퍼티를 사용 가능하다.  명시적인 타입지정으로 매핑 시 올바르지 않은 데이터가 들어올 경우 빠르게 에러를 감지할 수 있다.  의미 있는 타입명으로 코드를 더 읽기 쉽게 해준다.  기본 값을 지정하여 데이터가 없을 경우를 대비할 수 있다.  엔터티 개체와 같이 사용할 경우 PresentationLayer와 ApplicationLayer를 분리하는 효과가 있다. 단점  DataTable에 어떤 키가 들어가 있는지 알기 힘들다. 즉, 쿼리수행 결과 데이터를 알기 힘들다.  DataTable에서 값을 꺼내야 하는 경우 Type Casting 과정에서 에러 발생 가능성이 있다.  꼭 들어가야 하는 데이터에 null이 들어가도 유효성을 체크하기 어렵다.  DataTable보다 메모리를 많이 차지한다.  매핑 과정이 필요하기 때문에 성능적으로 조금 손해를 본다.  DTO클래스 파일이 많아질 수 있다.
  • 25. DataTable이 아닌 DTO로! DTO를 그대로 .aspx파일에서 사용함으로써 쓸데없이 많은 필드 수를 줄 일 수 있다. • 논리적 의미를 알기 힘든 코드를 작성하지 않아도 된다. • 문자열 리터럴로 인한 개발자의 실수 가능성을 없앤다.
  • 26. 왜 DTO를 적용하지 않을까? 1. 계층화 된 Complex Json Data를 DTO로 받기가 힘들다. • Complex Json Data Parse 해결 2. 현재 폴더 구조에서 서로 다른 도메인 폴더에 있는 Controller가 같은 일을 하는 경우 DTO의 위치가 애매해 진다. 3. DTO를 한곳에 모은다면 그 수가 너무 많아진다.
  • 27. Complex Json Data Parse < jQuery code> < DTO Class> • .NET의 컨트롤러 파라미터 바인딩은 타입을 기준으로 프로퍼티를 찾고 여러 개라면 그 중 이름으로 바인딩 한다. • JsonProperty 애트리뷰트를 사용한다면 타입이 일치하지 않더라도 이름으로 바인딩을 시도할 수 있다.
  • 28. ORM
  • 29. 개발자들이 한 고민 public void CancelOrder(int orderIdx) { // 1) Order order = _orderRepo.GetOrder(orderIdx); Billing billing = _billingRepo.GetBilling(orderIdx); Delivery delivery = _deliveryRepo.GetDelivery(orderIdx); // 2) string deliveryStatus = delivery.GetStatus(); if(string.Equals(“RUNNING”, deliveryStatus)) { delivery.Status = “CANCEL”; deliveryRepo.Update(delivery); } // 3) order.Status = “CANCEL”; orderRepo.Update(order); billing.Status = “CANCEL”; buillingRepo.Update(billing); } // 출처 : 스프링부트와 aws로 혼자 구현하는 웹서비스 1. 테이블들의 관계를 객체 사이의 관계로 표현할 수가 없 어 관계를 맺고 있는 엔티티들을 따로따로 가져와야 함. 2. 객체의 상태변화를 개발자가 직접 체크 하여 쿼리를 수 행할지 말지 정해야 하기 때문에 getter를 쓸 수 밖에 없 고 캡슐화가 깨진다. 3. 객체에게 명령하는 것이 아닌 객체의 상태를 직접 조작 하고 쿼리를 수행함.
  • 30. ORM의 등장 배경 • 데이터를 정형화 하여 관리하려는 RDBS와 사물을 추상화하여 이해하려는 OOP는 서로 존재의 목적이 다르기 때문에 다음의 문제들이 완벽한 OOP의 큰 장애물이 되었다. 1. 객체와 테이블의 identity 차이 • 객체는 보통 HashCode와 타입으로 동일성을 나타낸다. • 테이블은 Primary키로 나타낸다. 2. 테이블 간의 연관관계와 객체의 참조의 차이 • 테이블은 외래키로 관계를 표현하며 관계의 방향이 없다 • 객체는 참조를 통해 관계를 표현하며 분명한 관계의 방향이 존재한다. • 아무리 SOLID원칙을 지켜도 근본적인 패러다임 불일치 문제를 해결하지 못해, 각각의 Idx로 엔터티 개체를 얻 고, 엔터티로 부터 데이터를 꺼내 조작하는 캡슐화를 위배할 수 밖에 없었다. • 두 패러다임의 불일치 문제를 해결하기 위해 개발자들이 머리를 맞대 만들어 낸 것이 ObjectRelationalMapping(ORM)이다.
  • 32. ORM을 통한 코드 변화 public void CancelOrder(int orderIdx) { // 1) Order order = _orderRepo.FindByIdx(orderIdx); Billing billing = order.Billing; Delivery delivery = order.Delivery; // 2~3) delivery.Cancel(); // 4) order.Cancel(); billing.Cancel(); } // 출처 : 스프링부트와 aws로 혼자 구현하는 웹서비스 public void CancelOrder(int orderIdx) { // 1) Order order = _orderRepo.GetOrder(orderIdx); Billing billing = _billingRepo.GetBilling(orderIdx); Delivery delivery = _deliveryRepo.GetDelivery(orderIdx); // 2) string deliveryStatus = delivery.GetStatus(); // 3) if(string.Equals(“RUNNING”, deliveryStatus)) { delivery.Status = “CANCEL”; deliveryRepo.Update(delivery); } // 4) order.Status = “CANCEL”; orderRepo.Update(order); billing.Status = “CANCEL”; buillingRepo.Update(billing); } // 출처 : 스프링부트와 aws로 혼자 구현하는 웹서비스 • 테이블 간의 관계를 객체로 표현할 수 있게 되었다. • 영속성 컨텍스트를 통해 Entity들의 변화를 개발자가 아닌 EF가 대신 추적해 준다. • 개발자는 객체에게 명령만하면 객체가 알아서 자신의 데이터를 직접 조작하고 EF는 변화를 인지해 자동으로 DB에 반영한다.
  • 33. ORM의 영속성 이용 Repository에게 데이터를 보내고 모든 것을 맡기던 코드에서 PostEntity에게 직접 상태를 변화하라는 명령을 보내는 방식으로 변 경되었다.
  • 34. OOP 네이밍 Repository로부터 데이터를 꺼내는 느낌인 Get*()에서 EF가 적용된 Repository에게 찾으라고 명령하는 OOP느낌이 강한 네이 밍으로 바꿨다. Entity를 DTO로 간단하게 변경하여 반환한다.
  • 35. 프로젝트에 EF Core 3.1 도입하기 (Entity Class) • 고객들의 DB에는 뷰만 존재하기 때문에 DbContext가 날리는 쿼리의 테이블이 SQL Server에서 자동으로 뷰를 가리키도록 한다. 만약 테이블이 존재한다면 예상치 못한 에러가 발생할 수 있겠다. • 뷰에는 TenantIdx가 없기 때문에 필드로 TenantIdx를 넣는다면 유효하지 않은 컬럼 명 에러가 발생한다. • Idx를 long으로 하면 타입 에러가 발생한다. • Entity개체의 프로퍼티는 private set을 해도 매핑이 되기 때문 에 private set을 하는 것이 좋다.
  • 36. 프로젝트에 EF Core 3.1 도입하기(DbContext) • DbContext에서 접근할 엔터티 개체들을 필드로 지정할 수 있다. • AddInterceptors()를 통해 메서드를 중간에 변형시킬 수 있는 객체를 설정할 수 있다.
  • 37. 프로젝트에 EF Core 3.1 도입하기(DbCommandInterceptor) • command.CommandText 프로퍼티를 통해 sql을 조작할 수 있다.
  • 38. 프로젝트에 EF Core 3.1 도입하기 • EF Core는 Insert 후 반환되는 Identity와 자신이 예측하고 있는 Identity를 통해 동시성 오류가 발생했는지 체크한다. • 현재 뷰들은 트리거를 통해 Insert문을 변환하고 있는데 Identity를 반환하지 않아 동시성 오류가 발생한다. Identity를 반 환하는 구문을 추가해야 한다. < Insert 실행 시 DbContext가 DB에 보내는 쿼리 >
  • 39. EntityFramework의 실용성 • 현재 프로젝트의 타겟 버전은 .NET Framework 4.7.2로서 EF core 3.1 과 EF 6.4를 사용할 수 있습니다. 해당 버전들은 조회 데이터가 10만 이상이거나 여러 테이블을 조인 시 성능저하가 심하거나 에러가 발생하기 때문에 성능이 중요한 조회에는 사용이 힘들 것으로 보입니다. • 11월에 .NET 6 배포에 맞춰 EF Core 5 보다 훨씬 버전업한 EF Core 6을 발표할 예정으로 보입니다. • URL : https://devblogs.microsoft.com/dotnet/announcing-entity-framework-core-6-0-preview-4- performance-edition/ • 현재 사용중인 DataContext 와 병행이 가능한 것으로 보인다.
  • 41. xUnit의 LifeCycle과 커스텀한 트랜잭션 전략 < xUnit의 기본 LifeCycle > < 애트리뷰트를 이용한 트랜잭션 전략>
  • 42. PostRepository의 테스트 코드 • 코드 URL : [보안]
  • 43. 테스트 간의 컨텍스트 공유 Reference : https://xunit.net/docs/shared-context
  • 44. 마치며 • 앞에서 말한 것들은 SQL중심의 코드에서 ORM 적용까지의 과정을 보인 것입니다. 개발이 편해지고 즐거워지기 위해서는 SQL 중심의 코드를 벗어나야 합니다. • OOP를 하기 위해선 비즈니스 로직의 책임을 Repository가 아니라 비즈니스 객체를 만들어 그것에게 할당해야 합니다. • 아직 업무를 많이 다뤄보지 않아 제가 말한 것들이 그대로 적용 가능할지는 모르겠습니다. 하지만 팀원분들에게 좋은 영감을 주었으면 좋겠다는 마음으로 열심히 만들었습니다. 봐주셔서 감사합니다.