본문 바로가기

Spring Framework

서비스 추상화

서비스 계층

 

자바 웹 애플리케이션에서 Service 계층은 일반적으로 비즈니스 로직을 처리하는 부분을 의미합니다. 이 계층은 사용자의 요청을 받아들이고 처리하는 Controller 계층과, 데이터를 저장하고 관리하는 Repository 또는 DAO(Data Access Object) 계층 사이에 위치합니다.

Service 계층의 주요 역할은 다음과 같습니다:

1. 비즈니스 로직 처리: 사용자로부터 받은 데이터를 가공하거나, 특정 비즈니스 규칙에 따라 계산하고 처리합니다.
2. 트랜잭션 관리: 여러 데이터베이스 연산을 하나의 작업 단위로 묶어서 처리합니다. 예를 들어, 한 작업 내에서 데이터베이스의 여러 테이블에 데이터를 추가하거나 수정할 때, 모든 작업이 성공적으로 완료되거나 실패할 경우 롤백하는 등의 처리를 합니다.
3. 모델 변환: Controller 계층과 Repository 계층 사이에서 데이터 모델을 변환합니다. 예를 들어, 데이터베이스에서 가져온 엔티티를 DTO(Data Transfer Object)로 변환하여 Controller에 전달할 수 있습니다.
4. 보안 및 검증: 사용자의 요청 데이터에 대한 유효성 검사 또는 인증 및 권한 검사 등의 보안 관련 로직을 처리합니다.

이러한 Service 계층을 통해 애플리케이션의 비즈니스 로직이 Controller나 Repository와 분리되어, 각 계층이 자신의 역할에만 집중할 수 있도록 도와주며, 이는 애플리케이션의 유지보수 및 확장성을 높이는 데 중요한 역할을 합니다.

 

 

자바 웹 애플리케이션에서 Service 계층의 역할을 이해하기 위해, 여러 서비스 계층에 대한 예를 들어 보겠습니다.

간단한 온라인 쇼핑몰 시스템.

이 시스템에는 상품을 관리하고, 주문을 처리하는 기능이 있습니다.

1. 상품 관리 기능 (Product Management)
   - Controller 계층: 사용자의 웹 요청을 받음. 예를 들어, 새로운 상품을 추가하거나, 상품 목록을 조회하는 요청을 처리.
   - Service 계층: 상품 추가, 수정, 조회 등의 비즈니스 로직을 수행.
     - 비즈니스 로직 처리: 새 상품 추가 시, 상품 정보의 유효성을 검사하고, 가격 책정 로직을 실행.
     - 트랜잭션 관리: 상품 정보를 데이터베이스에 저장할 때, 모든 정보가 정확히 저장되도록 트랜잭션을 관리.
   - Repository 계층: 데이터베이스와의 실제 상호작용을 담당. 상품 데이터를 저장하고, 조회하는 역할을 함.

2. 주문 처리 기능 (Order Processing)
   - Controller 계층: 사용자가 상품을 주문하는 요청을 받음.
   - Service 계층: 주문 로직을 수행.
     - 비즈니스 로직 처리: 사용자가 선택한 상품의 재고 확인, 결제 처리, 주문 기록 생성.
     - 트랜잭션 관리: 주문 데이터가 데이터베이스에 정확하게 저장되도록 트랜잭션을 관리. 만약 결제 실패나 재고 부족

       등의  문제가 발생하면, 이전 단계로 롤백.
     - 모델 변환: 주문 데이터를 DTO로 변환하여 Controller에 전달.
     - 보안 및 검증: 주문 요청을 보낸 사용자의 인증 상태를 확인하고, 주문 데이터의 유효성을 검증.

이렇게 Service 계층은 Controller와 Repository 계층 사이에서 중요한 중간 매개체 역할을 하면서, 비즈니스 로직과 데이터 처리 로직을 분리하고, 애플리케이션의 전체적인 구조를 깔끔하고 관리하기 쉽게 만듭니다. 이로 인해 유지보수가 용이하고, 시스템의 확장성과 유연성이 향상됩니다.

 

은행 시스템에서 고객의 계좌 관리와 관련된 기능

은행 시스템에서 고객의 계좌 관리와 관련된 기능을 예로 들어보겠습니다. 이 시스템에는 계좌 생성, 입출금 처리, 계좌 잔액 조회 등의 기능이 포함될 수 있습니다.

1. 계좌 생성 기능 (Account Creation)
   - Controller 계층: 웹 인터페이스를 통해 고객이 새 계좌를 생성하려는 요청을 받습니다.
   - Service 계층: 계좌 생성에 관한 비즈니스 로직을 처리합니다.
     - 비즈니스 로직 처리: 고객 정보의 유효성을 검증하고, 필요한 경우 고객 신원을 확인합니다.
     - 트랜잭션 관리: 새 계좌 정보를 데이터베이스에 저장할 때, 올바른 데이터가 저장되도록 트랜잭션을 관리합니다.
   - Repository 계층: 계좌 정보를 데이터베이스에 실제로 저장하고 관리하는 역할을 합니다.

2. 입출금 처리 기능 (Deposit and Withdrawal)
   - Controller 계층: 고객이 입금 또는 출금 요청을 할 때 이를 받습니다.
   - Service 계층: 입출금 로직을 수행합니다.
     - 비즈니스 로직 처리: 요청된 금액에 따라 계좌 잔액을 업데이트하고, 거래 내역을 기록합니다.
     - 트랜잭션 관리: 여러 데이터베이스 변경 사항을 하나의 작업 단위로 묶어 관리합니다. 예를 들어, 출금 시 잔액이 충분한

       지 검사하고, 부족할 경우 거래를 취소합니다.
   - Repository 계층: 입출금 관련 데이터를 데이터베이스에 저장하고 관리합니다.

3. 계좌 잔액 조회 기능 (Account Balance Inquiry)
   - Controller 계층: 고객의 계좌 잔액 조회 요청을 받습니다.
   - Service 계층: 계좌 잔액 조회 로직을 수행합니다.
     - 모델 변환: 데이터베이스에서 가져온 계좌 정보를 고객에게 표시하기 적합한 형태로 변환합니다.
     - 보안 및 검증: 요청을 한 고객이 해당 계좌에 접근할 권한이 있는지 확인합니다.
   - Repository 계층: 계좌 데이터를 데이터베이스에서 조회합니다.

이 예시에서, Service 계층은 비즈니스 로직의 핵심을 담당하고 있습니다. 이 계층은 사용자의 요청을 처리하고, 필요한 비즈니스 규칙을 적용하며, 데이터베이스와의 상호작용을 관리합니다. 이로써 Controller는 사용자 인터페이스와의 상호작용에, Repository는 데이터 저장 및 검색에 집중할 수 있습니다. 이렇게 계층화된 구조는 애플리케이션의 유지보수와 확장을 용이하게 합니다.

 

의료 시스템

의료 시스템은 환자의 예약 관리, 진료 기록 관리, 처방전 발급 등의 기능을 포함할 수 있습니다.

1. 환자 예약 관리 기능 (Patient Appointment Management)
   - Controller 계층: 환자 또는 의료진이 예약을 하거나 변경하는 웹 요청을 받습니다.
   - Service 계층: 예약 관련 비즈니스 로직을 처리합니다.
     - 비즈니스 로직 처리: 예약 가능 여부를 확인, 특정 시간대에 의사의 스케줄을 검사하고, 환자의 예약 요청을 승인 또는

       거절합니다.
     - 트랜잭션 관리: 예약 정보가 데이터베이스에 정확히 저장되도록 관리합니다.
   - Repository 계층: 예약 데이터를 데이터베이스에 저장하고, 필요한 조회를 수행합니다.

2. 진료 기록 관리 기능 (Medical Record Management)
   - Controller 계층: 의료진이 환자의 진료 기록을 추가하거나 조회하는 요청을 처리합니다.
   - Service 계층: 진료 기록에 관한 비즈니스 로직을 수행합니다.
     - 비즈니스 로직 처리: 새로운 진료 기록을 추가할 때, 환자의 이전 기록과의 일관성을 확인하고, 필요한 정보를

       기록합니다.
     - 트랜잭션 관리: 진료 기록이 정확히 데이터베이스에 저장되도록 합니다.
   - Repository 계층: 진료 기록을 데이터베이스에 저장하고, 필요한 경우 조회합니다.

3. 처방전 발급 기능 (Prescription Issuance)
   - Controller 계층: 의사가 처방전을 발급하는 요청을 받습니다.
   - Service 계층: 처방 관련 로직을 처리합니다.
     - 비즈니스 로직 처리: 처방약의 적절성을 평가하고, 환자의 알레르기 이력이나 다른 약물과의 상호작용을 검토합니다.
     - 모델 변환: 처방 정보를 환자나 약국에 전달하기 위해 적절한 형태로 변환합니다.
     - 보안 및 검증: 처방을 요청한 의사의 인증과 권한을 확인합니다.
   - Repository 계층: 처방전 정보를 데이터베이스에 저장합니다.

이 예시에서 Service 계층은 의료 데이터와 관련된 중요한 비즈니스 로직을 처리하며, 이를 통해 의료 시스템의 정확성과 효율성을 보장합니다. Controller는 사용자 인터페이스와의 상호작용을 처리하고, Repository는 데이터의 저장과 검색을 담당합니다. 이러한 계층화는 시스템의 복잡성을 관리하고, 각 부분의 독립성을 유지하는 데 도움을 줍니다.

 

 

서비스 추상화

 

자바에서 서비스 추상화(Service Abstraction)는 주로 스프링 프레임워크(Spring Framework)와 같은 고수준의 프레임워크에서 사용되는 개념입니다. 이 개념의 핵심은 복잡한 로직이나 기술적인 세부 사항을 숨기고 사용자에게 단순화된 인터페이스를 제공하는 것입니다. 이렇게 하면 개발자는 구현 세부 사항에 대해 걱정하지 않고 비즈니스 로직에 집중할 수 있습니다. 서비스 추상화의 주요 특징은 다음과 같습니다:

1. 인터페이스 기반 프로그래밍: 서비스 추상화는 인터페이스를 통해 구현을 정의합니다. 이렇게 하면 구현 세부 사항을 클라이언트로부터 숨길 수 있으며, 필요에 따라 다른 구현으로 쉽게 교체할 수 있습니다.

2. 분리된 관심사(Separation of Concerns): 서비스 추상화는 비즈니스 로직을 데이터 액세스 레이어나 다른 기술적인 세부 사항으로부터 분리합니다. 이는 코드의 유지보수와 확장성을 향상시킵니다.

3. 유연성과 확장성: 추상화된 서비스는 다양한 환경과 요구 사항에 맞게 쉽게 조정할 수 있습니다. 예를 들어, 데이터베이스 접근 방식이나 외부 서비스 연결 방법을 변경할 때 기존의 비즈니스 로직을 수정할 필요가 없습니다.

4. 테스트 용이성: 서비스 추상화를 사용하면, 실제 서비스 구현 대신 모의 객체(Mock Objects)를 사용하여 유닛 테스트를 쉽게 수행할 수 있습니다.

5. 프레임워크와의 통합: 스프링과 같은 프레임워크는 서비스 추상화를 통해 다양한 기능을 제공합니다. 예를 들어, 트랜잭션 관리, 보안, 로깅 등의 관심사를 서비스 레이어에서 쉽게 관리할 수 있습니다.

서비스 추상화는 복잡한 시스템을 구축할 때 필수적인 요소로, 개발자가 보다 깔끔하고 관리하기 쉬운 코드를 작성할 수 있도록 돕습니다. 이러한 접근 방식은 유지보수성, 확장성 및 시스템의 전체적인 품질 향상에 크게 기여합니다.
 

서비스 추상화 예

서비스 추상화를 이해하기 위해 간단한 예를 들어보겠습니다. 

 

이메일 서비스 추상화

우리가 개발하는 시스템에 사용자의 이메일을 보내는 기능이 필요하다고 가정해봅시다. 이메일 서비스의 구현은 다음과 같은 여러 단계로 나눌 수 있습니다:

1. 인터페이스 정의 (Service Abstraction):
   먼저, EmailService라는 인터페이스를 정의합니다. 이 인터페이스는 sendEmail이라는 메소드를 가지며, 이 메소드는 이메일을 보내는 데 필요한 정보(받는 사람, 제목, 내용 등)를 매개변수로 받습니다.

public interface EmailService {
   void sendEmail(String to, String subject, String body);
}


2. 구체적인 구현:
   이 인터페이스의 구현체인 SmtpEmailService 클래스를 만듭니다. 이 클래스는 실제로 SMTP 프로토콜을 사용해 이메일을 보냅니다.

public class SmtpEmailService implements EmailService {
   public void sendEmail(String to, String subject, String body) {
       // SMTP를 사용하여 이메일 보내기
   }
}


   또는, 다른 방식의 이메일 서비스 구현체를 만들 수도 있습니다. 예를 들어, MockEmailService 클래스는 개발 중에 실제 이메일을 보내지 않고 로그로 기록하는 용도로 사용될 수 있습니다.

public class MockEmailService implements EmailService {
   public void sendEmail(String to, String subject, String body) {
       // 이메일을 실제로 보내는 대신, 로그에 기록
   }
}


3. 클라이언트 코드:
   클라이언트 코드에서는 EmailService 인터페이스에만 의존합니다. 실제 사용되는 구현체는 구성(Configuration) 또는 의존성 주입(Dependency Injection)을 통해 제공됩니다.

public class OrderService {
       private EmailService emailService;

       // 의존성 주입을 통해 EmailService 구현체를 받음
       public OrderService(EmailService emailService) {
           this.emailService = emailService;
       }

       public void processOrder(Order order) {
           // 주문 처리 로직
           // ...
           // 주문 처리 후 이메일 보내기
           emailService.sendEmail(order.getCustomerEmail(), "Order Confirmation", "Your order has been processed.");
       }
   }


이 예에서, 서비스 추상화는 EmailService 인터페이스를 통해 이루어집니다. 이를 통해 실제 이메일을 보내는 로직(SmtpEmailService)과 테스트 중에 사용하는 모의 로직(MockEmailService) 사이를 쉽게 전환할 수 있습니다. 또한, 클라이언트 코드인 OrderService는 이메일 서비스의 구체적인 구현에 대해 전혀 알 필요가 없으며, 오직 EmailService 인터페이스에 정의된 메소드를 사용하기만 하면 됩니다.

 


데이터베이스 관련 서비스 추상화


데이터베이스와 관련하여 서비스 추상화를 설명하기 위해, 데이터 액세스 레이어의 추상화에 초점을 맞추겠습니다. 이 예에서는 자바와 스프링 프레임워크를 사용하는 가상의 웹 애플리케이션을 가정합니다. 서비스 추상화의 핵심은 구체적인 데이터베이스 기술(예: SQL, NoSQL) 또는 구현(예: MySQL, MongoDB)으로부터 비즈니스 로직을 분리하는 것입니다.

1. 인터페이스 정의

 우선, 데이터 액세스를 위한 인터페이스를 정의합니다. 예를 들어, 사용자 데이터에 액세스하는 서비스를 위한 UserRepository 인터페이스를 만듭니다.

public interface UserRepository {
    User findById(Long id);
    void save(User user);
    List<User> findAll();
}


2. 구체적인 구현
 이 인터페이스의 구체적인 구현을 제공합니다. 예를 들어, MySQL 데이터베이스를 사용하는 구현체는 다음과 같습니다.

public class MySqlUserRepository implements UserRepository {
    // 데이터베이스 연결 및 쿼리 실행을 위한 로직

    @Override
    public User findById(Long id) {
        // MySQL 데이터베이스에서 사용자 조회
    }

    @Override
    public void save(User user) {
        // 사용자 정보를 MySQL 데이터베이스에 저장
    }

    @Override
    public List<User> findAll() {
        // 모든 사용자 정보를 조회
    }
}


만약 다른 종류의 데이터베이스를 사용한다면, 해당 데이터베이스에 맞는 다른 구현체를 만들 수 있습니다. 예를 들어, MongoDB를 사용하는 경우 MongoUserRepository를 만들 수 있습니다.

3. 서비스 레이어 구현
서비스 레이어에서는 UserRepository 인터페이스에 의존합니다. 이 레이어는 구체적인 데이터베이스 구현을 알 필요가 없습니다.

@Service
public class UserService {
    private final UserRepository userRepository;

    @Autowired
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public User getUser(Long id) {
        return userRepository.findById(id);
    }

    public void createUser(User user) {
        userRepository.save(user);
    }

    // 기타 비즈니스 로직...
}


4. 의존성 주입을 통한 유연성 제공
 스프링 프레임워크의 의존성 주입(Dependency Injection)을 사용하면, 실행 환경에 따라 다른 UserRepository 구현체를 UserService에 주입할 수 있습니다. 개발 환경에서는 H2와 같은 인메모리 데이터베이스를 사용하는 구현체를, 실제 운영 환경에서는 MySQL을 사용하는 구현체를 사용할 수 있습니다.

이렇게 서비스 추상화를 통해 데이터 액세스 레이어를 추상화함으로써, 데이터베이스 구현이 변경되어도 비즈니스 로직 레이어를 수정할 필요가 없게 됩니다. 또한, 코드의 유지보수성과 테스트 용이성이 향상되며, 다양한 데이터베이스 기술과의 호환성이 증가합니다.

'Spring Framework' 카테고리의 다른 글

Java Dynamic Proxy와 Spring FactoryBean  (0) 2024.04.09
InvocationHandler  (0) 2024.04.09
Service Layer  (0) 2024.04.09
템플릿 콜백 패턴(Template Callback Pattern)  (0) 2024.04.09
전략 패턴  (0) 2024.04.09