홈으로 돌아가기

백엔드 개발에서의 UseCase: 비즈니스 로직 구조화

이 기사는 백엔드 애플리케이션에서 비즈니스 시나리오를 설명하는 UseCase 패턴을 설명합니다. 구조, 이점, 제한 사항, PHP와 Laravel에서의 구현 예제, 테스트 및 코드 구성 포함.

UseCase: 개발자를 위한 코드에서 비즈니스 프로세스 설명 방법
Advertisement 728x90

UseCase 패턴: 백엔드 비즈니스 로직 구조화

UseCase는 애플리케이션에서 비즈니스 시나리오를 정의하는 아키텍처 패턴입니다. 주문 생성이나 사용자 등록 같은 구체적인 시스템 동작 중심으로 코드를 구성하며, 추상적인 기술 작업이 아닌 비즈니스 행동을 중심으로 합니다. 이 접근법은 가독성을 높이고, 테스트를 간편하게 하며, 특히 복잡한 백엔드 시스템에서 프로젝트를 효과적으로 확장할 수 있게 합니다.

UseCase란 무엇이며 왜 사용하나요?

UseCase는 애플리케이션 계층에서 완전한 비즈니스 시나리오를 나타냅니다. 도메인 서비스와 엔티티에 비즈니스 규칙을 위임하며 일련의 단계를 조율합니다. 예를 들어, 데이터베이스에 "쓰기" 같은 저수준 작업 대신 "사용자가 주문 생성"을 설명합니다. 프로젝트가 커지면서 컨트롤러와 서비스에 비즈니스 로직이 흩어지는 것을 방지합니다. 명확한 시나리오 경계가 없으면 코드 이해와 유지보수가 어려워집니다.

UseCase의 주요 이점:

Google AdInline article slot
  • 명확한 구조: Service나 Manager 같은 모호한 추상화가 아닌 명시적인 비즈니스 기능으로 시스템 정의.
  • 높은 가독성: 비즈니스 로직을 반영한 아키텍처로 "이 앱이 무엇을 할 수 있나?"에 쉽게 답변.
  • 쉬운 테스트: 시나리오 수준의 통합 테스트로 전체 사용자 여정을 검증하며 비즈니스 프로세스 파괴 없음.

한계와 모범 사례

하나의 UseCase에서 다른 UseCase를 호출하지 마세요. 숨겨진 의존성을 만들고 재사용 가능한 로직 블록으로 변질될 수 있습니다. 복잡한 시나리오에서 순환 없고 명시적이라면 괜찮습니다. 재사용 로직은 여러 UseCase에서 쓸 수 있는 전용 서비스나 도메인 컴포넌트로 추출하세요.

UseCase 호출 위치:

  • 컨트롤러
  • 콘솔 명령어
  • 이벤트 리스너

UseCase는 앱 비즈니스 로직의 진입점으로, 외부 상호작용과 시스템을 중재합니다.

Google AdInline article slot

서비스만으로는 부족한 이유

간단한 프로젝트에서는 서비스가 잘 작동하지만, 비즈니스 로직이 커지면 문제가 생깁니다. 주문 생성의 기본 "데이터베이스 저장"이 재고 확인, 할인 적용, 잔액 차감, 알림 발송으로 팽창합니다. 의존성, 메서드, 부작용으로 부푼 갓 오브젝트가 되어 코드 읽기와 테스트가 어려워집니다.

UseCase는 간단한 규칙을 따릅니다: 하나의 UseCase = 하나의 비즈니스 프로세스. 중복 로직은 공유 서비스나 도메인 컴포넌트로 옮겨 복잡도 증가에도 코드 관리가 쉽습니다.

PHP와 Laravel에서의 UseCase 구조

전형적인 UseCase는 세 부분으로 구성:

Google AdInline article slot
  • Handler: 시나리오 단계와 조건 조율, 도메인 비즈니스 로직 없음.
  • DataInput: 로직 없는 입력 데이터 객체—타입화되고 불변인 데이터 선호.
  • DataOutput: 명확한 계약 정의 결과 객체 (예: DTO).

예시: 주문 생성

class StoreOrderDataInput
{
    public function __construct(
        public readonly int $userId,
        public readonly array $items,
    ) {}
}

class StoreOrderDataOutput
{
    public function __construct(
        public readonly int $orderId,
        public readonly string $status,
    ) {}
}

class StoreOrderHandler
{
    public function __construct(
        private OrderRepository $orders
    ) {}

    public function handle(StoreOrderDataInput $input): StoreOrderDataOutput
    {
        $order = new Order(
            userId: $input->userId,
        );

        $orderSaved = $this->orders->save($order);

        return new StoreOrderDataOutput(
            orderId: $orderSaved->id,
            status: $orderSaved->status
        );
    }
}

프로젝트에서의 UseCase 구성과 테스트

Laravel에서는 app/UseCases/V1처럼 버전 관리 구조로 저장해 계약 변경 안전성 확보. 하위 디렉토리는 "시스템이 무엇을 할 수 있나?"를 반영—예: StoreOrder, CancelOrder, RegisterUser. 기술 세부가 아닌 비즈니스 기능 반영.

UseCase 테스트는 시나리오 수준 통합 테스트로 간단. StoreOrderHandler 테스트 예시:

use Tests\TestCase;
use Illuminate\Foundation\Testing\RefreshDatabase;

class StoreOrderFeatureTest extends TestCase
{
    use RefreshDatabase;

    public function testUserCanCreateOrder(): void
    {
        $user = User::factory()->create();

        $payload = [
            'items' => [
                ['id' => 10, 'qty' => 2],
                ['id' => 15, 'qty' => 1],
            ],
        ];

        $response = $this->actingAs($user)
            ->postJson('/api/orders', $payload);

        $response->assertStatus(200);
        $response->assertJsonStructure([
            'orderId',
            'status'
        ]);
        $this->assertDatabaseHas('orders', [
            'user_id' => $user->id
        ]);
    }
}

주요 요약

  • UseCase는 비즈니스 시나리오 정의, 기술 작업 아님으로 앱 구조 깔끔.
  • 대형 프로젝트 로직 확산 해결, 코드 유지보수 용이.
  • 하나의 UseCase = 하나의 비즈니스 프로세스, 공유 로직은 서비스에.
  • 통합 테스트로 테스트 간편, 전체 시나리오 커버.
  • 프로젝트 구조가 비즈니스 기능 반영, 온보딩 속도 향상.

— Editorial Team

Advertisement 728x90

다음 읽기