Loading...
Skip to Content

비즈니스 로직을 SQL에 관리하는 시스템과 도메인 클래스에서 관리하는 시스템의 차이점 Part2

 지난 '비즈니스 로직을 SQL에 관리하는 시스템과 도메인 클래스에서 관리하는 시스템의 차이점'을 이어서 알아보겠습니다.

직원을 계약직과 정규직으로 분류하고, 세금 계산 로직이 계약직의 경우에만 다를 때, 이를 다형성(polymorphism)으로 처리하기 위해 엔티티를 변경하는 방법을 살펴보겠습니다. 이를 위해 Employee 클래스를 추상 클래스로 변경하고, 계약직(ContractEmployee)과 정규직(PermanentEmployee)에 대한 서브클래스를 만듭니다. 공통 로직은 Employee 클래스에서 처리하고, 세금 계산 로직과 같이 차이가 있는 부분은 각 서브클래스에서 오버라이드하여 구현합니다.

Employee 클래스 (추상 클래스)

public abstract class Employee { protected Long id; protected Double baseSalary; protected Double finalSalary; // 기타 공통 속성 public Employee(Long id, Double baseSalary) { this.id = id; this.baseSalary = baseSalary; } // 기본 급여 계산 로직 (공통) public void calculatePayroll() { double taxDeduction = calculateTaxDeduction(); finalSalary = baseSalary - taxDeduction; } // 세금 계산은 서브클래스에서 구현 protected abstract double calculateTaxDeduction(); // Getters and Setters 생략 }

ContractEmployee 클래스

public class ContractEmployee extends Employee { public ContractEmployee(Long id, Double baseSalary) { super(id, baseSalary); } @Override protected double calculateTaxDeduction() { // 계약직의 세금 계산 로직 return baseSalary * 0.05; // 예시: 기본 급여의 5% } }

PermanentEmployee 클래스

public class PermanentEmployee extends Employee { public PermanentEmployee(Long id, Double baseSalary) { super(id, baseSalary); } @Override protected double calculateTaxDeduction() { // 정규직의 세금 계산 로직 return baseSalary * 0.1; // 예시: 기본 급여의 10% } }

이 구조를 통해, 모든 직원(Employee)은 기본 급여 계산 로직을 공유하면서도, 계약직과 정규직의 세금 계산 로직을 각각 다르게 구현할 수 있습니다. 다형성을 활용하여, calculateTaxDeduction 메서드를 추상 메서드로 정의하고, 이를 서브클래스에서 구체적으로 구현함으로써, 세금 계산 로직의 차이점을 관리합니다.

이 방식은 코드의 재사용성과 유지보수성을 향상시키며, 각 직원 유형의 세금 계산 로직을 쉽게 추가하거나 변경할 수 있는 유연성을 제공합니다.

 폴리폴피즘의 효과를 스토어드 프로시져에서도 얻을 수 있는 방법

Stored Procedure에서 직접적인 다형성(Polymorphism)을 구현하는 것은 프로그래밍 언어에서 지원하는 객체 지향적 다형성과는 다소 차이가 있습니다. 그러나, SQL 및 PL/SQL 같은 절차적 언어에서도 조건문, 함수, 그리고 서브 프로그램을 통해 유사한 효과를 얻을 수 있는 방법이 있습니다. 이러한 방법은 코드의 재사용성을 높이고, 유지보수를 용이하게 할 수 있도록 도와줍니다.

조건문을 이용한 접근

계약직과 정규직에 따른 세금 계산 로직의 차이를 처리하기 위해, Stored Procedure 내에서 단순히 조건문을 사용하는 방법입니다. 이 방법은 다형성의 원리를 직접적으로 구현하는 것은 아니지만, 서로 다른 로직 경로를 제공함으로써 유사한 효과를 낼 수 있습니다.

CREATE OR REPLACE PROCEDURE calculate_employee_tax(employee_id IN NUMBER) AS employee_type CHAR(1); base_salary NUMBER; tax_deduction NUMBER; BEGIN -- 직원 유형과 기본급 조회 SELECT e.type, e.base_salary INTO employee_type, base_salary FROM employees e WHERE e.id = employee_id; -- 직원 유형에 따라 세금 계산 IF employee_type = 'C' THEN -- 계약직 tax_deduction := base_salary * 0.05; ELSIF employee_type = 'P' THEN -- 정규직 tax_deduction := base_salary * 0.1; ELSE tax_deduction := 0; END IF; -- 세금 공제 업데이트 등의 후속 처리 UPDATE employees SET tax = tax_deduction WHERE id = employee_id; COMMIT; END; /

동적 SQL을 사용한 접근

동적 SQL을 사용하여, 직원 유형에 따라 다른 프로시저를 호출하는 방식으로 다형성과 유사한 효과를 구현할 수 있습니다. 이 방법은 더 유연하지만, 복잡성과 오류 가능성이 증가할 수 있습니다.

CREATE OR REPLACE PROCEDURE calculate_tax_for_employee(employee_id IN NUMBER) AS employee_type CHAR(1); tax_calculation_sql VARCHAR2(1000); BEGIN -- 직원 유형 조회 SELECT e.type INTO employee_type FROM employees e WHERE e.id = employee_id; -- 유형에 따라 다른 세금 계산 로직 호출 IF employee_type = 'C' THEN tax_calculation_sql := 'CALL calculate_tax_contract(:1)'; ELSIF employee_type = 'P' THEN tax_calculation_sql := 'CALL calculate_tax_permanent(:1)'; ELSE RAISE_APPLICATION_ERROR(-20001, 'Unknown Employee Type'); END IF; -- 동적 SQL 실행 EXECUTE IMMEDIATE tax_calculation_sql USING employee_id; END; /

이 방법들은 Stored Procedure 내에서 조건에 따라 다른 처리 경로를 제공함으로써, 다형성과 유사한 효과를 달성할 수 있습니다. 그러나 객체 지향 프로그래밍 언어에서 제공하는 직접적이고 선언적인 다형성을 완전히 대체할 수는 없습니다. 상황과 요구 사항에 따라 적절한 방법을 선택하여 사용해야 합니다.

 조건문을 사용하는 접근법과 동적 SQL을 사용하는 접근법 사이의 차이점과 이와 관련하여 "관심사의 분리(Separation of Concerns, SoC)" 개념이 어떻게 적용될 수 있는지에 대해

조건문을 사용하는 접근법

이 접근법에서는 단일 Stored Procedure 내에서 IF-ELSEIF 조건문을 사용하여 직원 유형에 따른 세금 계산 로직을 구현합니다. 이 경우, 모든 로직이 하나의 프로시저 내에 내장되어 있어, 로직 변경이 필요할 때 해당 프로시저를 수정해야 합니다. 이 방식은 구현이 간단하고 직관적이지만, 다음과 같은 단점이 있습니다:

  1. 확장성: 새로운 직원 유형이 추가되거나 세금 계산 로직이 변경될 경우, 프로시저를 수정해야 하며, 이는 복잡성을 증가시킬 수 있습니다.
  2. 관심사의 분리: 모든 세금 계산 로직이 하나의 프로시저 내에 존재하기 때문에, 세금 계산 로직의 변화가 다른 부분에 영향을 줄 수 있으며, 관심사의 분리가 잘 되지 않습니다.

동적 SQL을 사용하는 접근법

동적 SQL을 사용하는 방식에서는 직원 유형에 따라 다른 프로시저를 동적으로 호출합니다. 이 방식은 별도의 프로시저에서 각 유형별 세금 계산 로직을 관리할 수 있게 하여, 관심사의 분리를 촉진합니다. 동적 SQL 접근법의 주요 이점은 다음과 같습니다:

  1. 확장성: 새로운 직원 유형이 추가되거나 특정 유형의 세금 계산 로직이 변경될 경우, 해당 유형의 세금 계산 프로시저만 수정하면 됩니다. 이는 확장성과 유지보수성을 향상시킵니다.
  2. 관심사의 분리: 각 세금 계산 로직을 별도의 프로시저로 분리하여 관리함으로써, 각 로직의 변경이 다른 부분에 미치는 영향을 최소화합니다. 이는 코드의 모듈성을 증가시키고, 특정 로직에 대한 수정이나 확장이 용이하게 만듭니다.

결론적으로, 동적 SQL을 사용하는 접근법은 관심사의 분리 원칙을 더 잘 적용할 수 있으며, 이는 각 세금 계산 로직의 독립적인 관리와 수정을 용이하게 합니다. 이는 소프트웨어 설계의 중요 원칙 중 하나인 관심사의 분리를 통해 시스템의 유지보수성과 확장성을 높이는 데 기여합니다.

 결론

다음은 Stored Procedure와 Java 객체를 사용하여 비즈니스 로직을 구현할 때의 주요 차이점을 비교한 표입니다. 이 표는 다형성을 활용하고 관심사의 분리(Separation of Concerns, SoC)를 적용하는 측면에서 두 접근 방식을 평가합니다.

기준 Stored Procedure Java 객체 비고
다형성 제한적. 동적 SQL 또는 조건문을 통한 유사 다형성 제공. 높음. 상속과 인터페이스를 통한 직접적인 다형성 지원. Java의 객체 지향 특성은 복잡한 비즈니스 로직을 다루기에 더 적합할 수 있습니다.
관심사의 분리 가능하지만 관리가 복잡. 각 로직을 분리하여 관리하기 위해 추가 작업 필요. 높음. 클래스와 메서드 단위로 관심사 분리 용이. Java에서는 클래스와 패키지를 통해 관심사를 명확히 분리하고, 책임을 관리하기 더 용이합니다.
확장성 중간. 새로운 로직 추가 시 프로시저 수정이 필요할 수 있음. 높음. 새로운 클래스 추가나 기존 클래스 확장으로 쉽게 확장 가능. Java 객체 모델은 확장에 더 유연하며, Open/Closed 원칙을 따르기 쉽습니다.
유지보수성 낮음. 로직 변경 시 테스트와 배포가 어려울 수 있음. 높음. 유닛 테스트와 CI/CD를 통한 쉬운 유지보수. Java 환경은 리팩토링과 유닛 테스트가 용이하며, IDE와 다양한 도구를 통한 지원이 풍부합니다.
테스트 용이성 낮음. 별도의 데이터베이스 설정이 필요하고, 격리된 테스트가 어려움. 높음. 유닛 테스트와 모의 객체(Mock) 사용으로 격리된 테스트 용이. Java는 JUnit, Mockito 등을 사용한 격리된 테스트 및 TDD 개발을 지원합니다.
데이터베이스 독립성 낮음. 특정 데이터베이스의 SQL 문법과 기능에 의존적. 높음. JDBC, JPA 등을 통해 데이터베이스 독립적인 개발 가능. Java 애플리케이션은 데이터베이스 교체가 상대적으로 용이하며, 데이터베이스 접근 코드 재사용성이 높습니다.
성능 최적화와 튜닝 높음. 데이터베이스 서버 내부에서 실행되어 네트워크 오버헤드 감소. 중간. ORM 사용 시 성능 최적화가 필요할 수 있음. Stored Procedure는 데이터베이스와 밀접하게 작동하기 때문에, 특정 케이스에서 성능 이점을 제공할 수 있습니다.

OCP(Open/Closed Principle)는 소프트웨어 엔티티(클래스, 모듈, 함수 등)는 확장에는 열려 있어야 하지만, 변경에는 닫혀 있어야 한다는 객체 지향 설계 원칙 중 하나입니다. 즉, 기존의 코드를 변경하지 않으면서도 시스템의 기능을 확장할 수 있어야 한다는 뜻입니다.

OCP 위배한 경우

  1. Stored Procedure 사용 시: 만약 계약직과 정규직의 세금 계산 로직을 단일 Stored Procedure 내의 조건문으로 처리한다면, 새로운 직원 유형이 추가될 때마다 이 Stored Procedure를 수정해야 합니다. 예를 들어, 프리랜서 직원 유형이 추가되고, 이들에 대한 세금 계산 로직이 필요하다면, 기존 Stored Procedure에 또 다른 조건문을 추가해야 합니다. 이러한 방식은 OCP를 위배하는 것으로, 기존 코드의 변경을 필요로 하며, 시스템의 확장성을 저해합니다.

OCP를 지킨 경우

  1. Java 객체 모델 사용 시: 위에서 설명한 대로 Employee를 추상 클래스로 정의하고, 계약직(ContractEmployee)과 정규직(PermanentEmployee)을 구현한 경우를 생각해 봅시다. 이 구조에서 새로운 직원 유형(예: 프리랜서)이 추가되어야 한다면, 단순히 Employee를 상속받는 새로운 클래스(예: FreelanceEmployee)를 생성하고, 세금 계산 로직을 그 안에 구현합니다. 이 경우, 기존의 코드(Employee, ContractEmployee, PermanentEmployee)를 변경할 필요 없이 새로운 기능을 시스템에 추가할 수 있습니다. 이러한 방식은 OCP 원칙을 잘 따르는 것으로, 기존의 코드 변경 없이 확장이 가능하여 유지보수와 시스템의 확장성을 향상시킵니다.

결론

OCP 원칙을 따르는 설계는 시스템의 유연성, 확장성, 그리고 유지보수성을 크게 향상시킬 수 있습니다. Java 객체 모델을 사용하는 경우, 다형성과 상속 같은 객체 지향 프로그래밍의 기본 원리를 활용하여 OCP 원칙을 적용하기 용이합니다. 반면, Stored Procedure를 사용하는 접근 방식은 특정 상황에서 성능상의 이점을 제공할 수 있지만, 코드의 확장성과 유지보수성 측면에서는 OCP 원칙을 따르기 어려울 수 있습니다. 따라서, 설계 단계에서 이러한 원칙들을 고려하여 프로젝트의 요구 사항과 장기적인 관점에서 가장 적합한 접근 방식을 선택하는 것이 중요합니다.