AgileBoot 백엔드 프레임워크 시작 가이드

프로젝트 개요

AgileBoot 백엔드는 Spring Boot 기반의 엔터프라이즈급 애플리케이션 개발을 위한 구조화된 템플릿입니다. 계층형 아키텍처와 도메인 주도 설계 원칙을 적용하여 대규모 팀 협업과 장기적인 유지보수를 고려한 구조로 설계되었습니다.

코드베이스 구성

프로젝트 루트에서 주요 디렉터리는 다음과 같이 배치됩니다:

agileboot-backend/
├── src/main/java/io/agileboot/
│   ├── BootstrapEntry.java          # 애플리케이션 진입점
│   ├── shared/                     # 공통 유틸리티 및 상수
│   ├── configuration/                # 빈 설정 및 프로퍼티 바인딩
│   ├── interfaces/                 # REST API 엔드포인트
│   ├── application/                # 유스케이스 및 서비스 로직
│   ├── domain/                     # 핵심 비즈니스 엔티티 및 규칙
│   ├── persistence/                # 데이터 접근 계층
│   └── integration/                # 외부 시스템 연동
├── src/main/resources/
│   ├── application.yaml            # 기본 설정
│   ├── application-local.yaml      # 로컬 개발 환경
│   └── db/migration/               # Flyway 스크립트
└── infrastructure/
    ├── docker-compose.yml
    └── kubernetes/

애플리케이션 부트스트랩

진입점 클래스는 표준 Spring Boot 구조를 따르되, 추가적인 빈 등록 전략을 포함합니다:

package io.agileboot;

import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.scheduling.annotation.EnableAsync;

@SpringBootApplication
@EnableCaching
@EnableAsync
public class BootstrapEntry {
    
    public static void main(String[] runtimeArgs) {
        new SpringApplicationBuilder(BootstrapEntry.class)
            .properties("spring.config.name=application,secrets")
            .run(runtimeArgs);
    }
}

환경별 설정 관리

프로파일 기반 설정 분리를 통해 환경 간 차이를 명확히 구분합니다. application.yaml은 공통 설정만 담당하고, 환경별 파일은 오버라이드할 항목만 정의합니다:

# application.yaml (공통 설정)
spring:
  application:
    name: agile-platform
  jackson:
    default-property-inclusion: non_null
    
management:
  endpoints:
    web:
      exposure:
        include: health,metrics,prometheus

---
# application-local.yaml (개발 환경)
spring:
  datasource:
    url: jdbc:postgresql://localhost:5432/agile_dev
    hikari:
      maximum-pool-size: 5
      
logging:
  level:
    io.agileboot: DEBUG

레이어 간 의존성 방향

프로젝트는 내부 의존성 규칙을 엄격히 적용합니다. 상위 계층만 하위 계층을 참조하며, 도메인 계층은 어떤 기술적 의존성도 갖지 않습니다:

// interfaces 레이어: HTTP 요청 처리
@RestController
@RequestMapping("/api/v1/inventory")
public class StockController {
    
    private final StockManageUseCase stockService;
    
    @PostMapping("/adjustment")
    public ResponseEntity<StockResult> modifyQuantity(
            @RequestBody @Valid StockAdjustmentRequest payload) {
        var command = payload.toCommand();
        var outcome = stockService.executeAdjustment(command);
        return ResponseEntity.ok(StockResult.from(outcome));
    }
}

// application 레이어: 트랜잭션 및 유스케이스 조율
@Service
@Transactional
public class StockManageUseCase {
    
    private final InventoryRepository repository;
    private final EventPublisher eventPublisher;
    
    public AdjustmentOutcome executeAdjustment(AdjustStockCommand cmd) {
        var item = repository.findBySku(cmd.sku())
            .orElseThrow(() -> new StockNotFoundException(cmd.sku()));
            
        item.decrease(cmd.quantity(), cmd.reason());
        repository.save(item);
        
        eventPublisher.publish(new StockLevelChanged(item.getSku(), item.getAvailable()));
        return AdjustmentOutcome.success(item.getAvailable());
    }
}

데이터 영속성 설정

MyBatis Plus와 함께 데이터베이스 중립적인 매퍼 인터페이스를 정의합니다:

@Mapper
public interface ProductCatalogMapper {
    
    @Select("""
        SELECT p.id, p.sku_code, p.name, c.category_name 
        FROM products p 
        JOIN categories c ON p.category_id = c.id 
        WHERE p.status = #{status} 
        AND p.created_at > #{since}
        ORDER BY p.priority DESC
    """)
    List<ProductSummary> selectActiveProducts(
        @Param("status") ProductStatus status,
        @Param("since") OffsetDateTime since);
    
    @Insert("""
        INSERT INTO products (sku_code, name, category_id, unit_price, status)
        VALUES (#{sku}, #{name}, #{categoryId}, #{price}, #{status})
    """)
    @Options(useGeneratedKeys = true, keyProperty = "id")
    int insertNewProduct(ProductEntity record);
}

컨테이너화 및 배포

멀티스테이지 빌드를 적용한 컨테이너 이미지는 최소 런타임만 포함합니다:

# Dockerfile
FROM eclipse-temurin:21-jdk-alpine AS builder
WORKDIR /workspace
COPY pom.xml .
COPY src ./src
RUN --mount=type=cache,target=/root/.m2 mvn package -DskipTests

FROM eclipse-temurin:21-jre-alpine
RUN addgroup -S runtime && adduser -S runtime -G runtime
USER runtime
COPY --from=builder /workspace/target/*.jar app.jar
ENTRYPOINT ["java", "-XX:+UseContainerSupport", "-XX:MaxRAMPercentage=75.0", "-jar", "/app.jar"]

태그: Spring Boot MyBatis Plus DDD Layered Architecture docker

6월 21일 20:24에 게시됨