Kilian
by Kilian

Categories

Tags

Claude Code Configuration for Spring Developer

1. 아키텍처 원칙

1.1 도메인 이해 우선

  • 도메인 파악: 비즈니스 규칙과 핵심 개념을 먼저 이해하고 코드 작성
  • Ubiquitous Language: 도메인 전문가와 같은 언어로 커뮤니케이션
  • 비즈니스 중심: 기술이 아닌 비즈니스 요구사항으로 아키텍처 결정
  • Context 이해: 각 컨텍스트의 경계와 책임을 명확히 정의
  • 불변 규칙 식별: 어떤 조건에서도 변하지 않는 도메인 규칙 파악

1.2 클린 아키텍처 핵심 원칙

  • Dependency Rule: 내부 계층은 외부 계층을 의존하지 않음 (Domain → Service → Infrastructure)
  • DIP (Dependency Inversion Principle): 구체적인 구현이 아닌 추상화(Interface)에 의존
  • Single Responsibility: 각 클래스는 변경 이유가 하나여야 함
  • No Framework Dependency in Domain: 도메인 계층은 프레임워크에 독립적이어야 함
  • Use Case Driven: 비즈니스 요구사항이 아키텍처를 주도

1.3 도메인 객체 원칙 (정보전문가 패턴 & 팩토리 메소드)

기본 원칙

  • 정보전문가 패턴: 객체가 필요한 정보를 모두 가지고 있을 때, 그 객체에 책임을 할당
  • Private 생성자: 모든 도메인 객체는 private 생성자로 직접 인스턴스화 방지
  • 정적 팩토리 메소드: 의미 있는 이름의 팩토리 메소드로만 객체 생성 허용
  • 의도 명확화: 메소드 이름으로 “어떤 행위(What)”를 하는지 표현

정보전문가 패턴 핵심 원칙

  • 책임 할당: 필요한 정보를 가진 객체가 그 정보로 의사결정하고 행동해야 함
  • 응집력 증대: 관련된 데이터와 로직을 같은 객체에 응집
  • 결합도 감소: 외부의 복잡한 로직이 없어져 의존성 감소
  • 캡슐화 강화: 객체의 내부 상태로 모든 판단이 이루어짐

What 기반 함수명 원칙

함수명은 “어떤 행위를 하는가(What)”를 표현하고 “어떻게 구현했는가(How)”에 의존하지 않음

원칙:

  • 구현 독립적: 함수명은 도메인 의도를 표현하고, 구현 기술에 영향받지 않음
  • 행위 중심: 메서드가 무엇을 하는지(변환, 검증, 계산 등)를 명확히 표현
  • 기술 용어 배제: 데이터베이스, 네트워크, 캐시 등의 기술 용어 제거
  • 도메인 언어 사용: Ubiquitous Language로 일관되게 표현

What 기반 네이밍:

  • getTotalPrice() - 어떤 가격을 구하는가 (구현: 합계 계산)
  • isEligibleForDiscount() - 할인 대상인가를 판단 (구현: DB 조회 또는 메모리 검사)
  • register() - 가입한다 (구현: DB 저장, 캐시 갱신 등)
  • calculateShippingCost() - 배송비를 계산한다 (구현: API 호출 또는 공식 적용)
  • applyPromotion() - 프로모션을 적용한다 (구현: 할인율 계산, 이벤트 발행)

피해야 할 How 기반 네이밍:

  • saveToDatabase() - 구현(DB 저장)을 노출
  • fetchFromCache() - 구현(캐시 조회)을 노출
  • callExternalAPI() - 구현(API 호출)을 노출
  • calculateWithFormula() - 구현(공식 사용)을 노출
  • doRequest() - 애매한 기술 용어

팩토리 메소드 What 기반 네이밍 규칙

  • create() - 객체를 생성한다 (기본 행위)
  • register() - 도메인에 등록한다 (가입, 회원가입 등)
  • generate() - 생성한다 (ID, 코드, 토큰 생성)
  • from() - 다른 형태에서 변환한다
  • parse() - 텍스트/포맷을 해석한다
  • restore() - 저장된 상태에서 복원한다 (Repository에서 사용)
  • open(), close(): 도메인 행위를 직접 표현

팩토리 메소드 사용 패턴

  • 기본 생성: 정상적인 도메인 객체 생성 (검증 포함)
  • 조건부 생성: 특정 조건에서의 특수한 생성 (초기값 설정)
  • 상태 복원: 데이터베이스에서 로드한 객체 복원 (검증 제외)
  • 변환: 다른 타입에서 현재 타입으로 변환

팩토리 메소드 선택 기준

  • 단순 객체 생성 → Private 생성자 + 팩토리 메소드
  • 조건부 생성 → 여러 팩토리 메소드 제공

1.4 계층별 역할

  • Domain: 순수 비즈니스 로직, 엔티티, 값 객체, 도메인 이벤트
  • Service: Use Case, 서비스 조율, 트랜잭션 관리
  • Infrastructure: 데이터 접근, 외부 API 연동, 기술적 구현
  • Presentation: HTTP 요청/응답, 라우팅, 예외 처리

1.5 계층별 책임과 역할 정의

Domain 계층 책임

  • 비즈니스 로직 중심: 도메인 규칙을 코드로 표현
  • 불변 규칙 강제: 엔티티 상태 변경 시 비즈니스 규칙 검증
  • 도메인 이벤트 발행: 중요 도메인 변경사항 이벤트로 표현
  • 프레임워크 무관: Spring, JPA 등 기술 스택에 독립적
  • 재사용성: 어떤 애플리케이션에서든 사용 가능한 순수 로직

Service 계층 책임

  • Use Case 구현: 비즈니스 시나리오를 단계별로 구성
  • 조율: 도메인, 인프라 계층 조정
  • 트랜잭션 관리: 원자성 보장
  • 서비스의 데이터 분리
    • 명령 : Command 객체 활용 (CreateOrderCommand)
    • 조회 : Query 객체 활용 (GetOrderQuery)

Infrastructure 계층 책임

  • 구현 세부사항: JPA, 데이터베이스, HTTP 클라이언트 등
  • 기술 선택: 구현 기술을 자유롭게 변경 가능하도록
  • Port 구현: 도메인이 정의한 인터페이스를 기술로 구현
  • 외부 시스템 연동: API 호출, 메시지 큐 등
  • 도메인 모델과 기술의 분리: Entity ↔ Domain Model 매핑

Presentation 계층 책임

  • HTTP 프로토콜 처리: 요청/응답 매핑
  • 입력 검증: 요청 데이터 기본 검증
  • 예외 처리: 도메인 예외를 HTTP 응답으로 변환
  • 라우팅: API 엔드포인트 정의
  • 직렬화/역직렬화: JSON ↔ DTO 변환
  • DTO에서 서비스 모델 매핑: DTO를 Command 및 Query 객체로 매핑

1.5 Port & Adapter 패턴

Port (Interface - Domain이 정의)

  • 도메인이 필요로 하는 외부 세계와의 계약을 정의
  • 도메인 언어와 개념으로 표현
  • 네이밍: 도메인 관점의 동작/역할 명시
    • OrderRepository: 주문을 저장/조회하는 역할
    • NotificationService: 알림을 전송하는 역할
    • PaymentProcessor: 결제를 처리하는 역할
    • UserAuthenticator: 사용자를 인증하는 역할

Adapter (Implementation - Infrastructure가 구현)

  • Port를 구체적인 기술로 구현
  • 기술 선택에 따라 변경 가능
  • 네이밍: 구현의 의도가 명확하게 드러남
    • PostgresOrderRepository: PostgreSQL을 사용한 주문 저장소
    • EmailNotificationService: 이메일 기반 알림 서비스
    • StripePaymentProcessor: Stripe 결제 처리기
    • JwtUserAuthenticator: JWT 기반 사용자 인증
    • RedisOrderRepository: Redis 캐시를 활용한 주문 저장소
    • KafkaEventPublisher: Kafka 기반 이벤트 발행

사용 예시

도메인 계층:
  interface UserRepository
  interface OrderRepository

인프라 계층:
  class JpaUserRepository implements UserRepository
  class JpaOrderRepository implements OrderRepository
  class RedisUserRepository implements UserRepository (캐시 전용)

또는

도메인 계층:
  interface PaymentGateway
  interface EmailSender
  
인프라 계층:
  class StripePaymentGateway implements PaymentGateway
  class GooglePaymentGateway implements PaymentGateway
  class SmtpEmailSender implements EmailSender
  class SendgridEmailSender implements EmailSender

1.6 핵심 디자인 패턴 통합

  • Port & Adapter: 도메인 독립성 유지, 기술 선택 유연화
  • Strategy Pattern: 다양한 알고리즘 런타임 선택
  • Observer/Event: 느슨한 결합을 통한 컨텍스트 간 통신
  • Factory Pattern: 복잡한 객체 생성 로직 캡슐화
  • Repository Pattern: 데이터 접근 추상화

2. 도메인 중심 개발 프로세스

2.1 요구사항 분석 단계

  • 도메인 전문가와 대화: 비즈니스 규칙과 용어 파악
  • 핵심 개념 식별: Aggregate, Entity, Value Object 후보 찾기
  • 경계 정의: 서로 다른 도메인 컨텍스트 식별
  • 불변 규칙 파악: 항상 유지되어야 하는 조건 정의
  • 상태 변화 추적: 엔티티의 생명주기와 상태 전이 이해

2.2 도메인 모델 설계

  • Aggregate 정의: 트랜잭션 경계 및 루트 엔티티 결정
  • Entity vs Value Object: 식별성이 필요한가?
  • 도메인 이벤트: 비즈니스 관점에서 중요한 변경사항 정의
  • 언어 통일: 도메인 전문가 언어로 클래스/메소드명 정의
  • 책임 분배: 각 엔티티의 명확한 책임 정의

2.3 코드 작성 원칙

  • Domain First: 도메인 모델을 먼저 설계 후 기술 구현
  • Business Logic in Domain: 비즈니스 규칙은 Domain 계층에만
  • Framework 후순위: 기술 선택은 도메인 설계 후 결정
  • 테스트 주도 개발: 도메인 로직부터 테스트 작성
  • 역할 기반 검토: 각 클래스가 자신의 책임을 충실하는지 확인

2.4 각 계층별 개발 순서

  1. Domain: 비즈니스 규칙과 엔티티 설계
  2. Port: Domain이 필요한 외부 인터페이스 정의
  3. Service: Use Case 구현, Port 의존성 주입
  4. Infrastructure: Port 구현체 작성 (기술 결정)
  5. Presentation: API 엔드포인트 정의

3. 객체지향 프로그래밍 원칙

3.1 도메인 모델 설계

  • 값 객체(Value Object)를 적극 활용하여 불변성 보장
  • 엔티티에 비즈니스 로직을 포함하여 도메인 지식 응집
  • 도메인 이벤트를 통해 컨텍스트 간 통신
  • 애그리게이트 루트를 정의하여 일관성 경계 명확히
  • Strategy Pattern: 도메인 내 다양한 계산/처리 방식 표현

3.2 Port & Adapter 패턴

  • 도메인이 의존하는 외부 시스템은 Port(Interface)로 정의
  • 구체적인 구현은 Infrastructure에서 Adapter로 작성
  • Port는 도메인 언어로 표현하되, Adapter는 기술 구현에 최적화
  • 기술 변경 시 Adapter만 수정하여 도메인 영향 최소화

3.3 디자인 패턴 활용

Strategy Pattern

  • 런타임에 알고리즘 선택이 필요한 비즈니스 로직
  • 결제 방식, 할인 정책, 배송 규칙 등 다양한 구현 필요 시 활용
  • 각 전략을 별도 클래스로 캡슐화하여 Open/Closed 원칙 준수

Factory Pattern

  • 복잡한 도메인 객체 생성 로직 캡슐화
  • 객체 생성의 복잡성을 한곳에 집중
  • 생성 로직 변경 시 팩토리만 수정하면 됨
  • Builder와 함께 사용하여 유연성 극대화

Observer/Event Pattern

  • 도메인 이벤트로 Aggregate 간 느슨한 결합 달성
  • 주문 생성, 결제 완료 등 중요 비즈니스 이벤트 발행
  • 이벤트 구독자는 도메인 계층과 독립적으로 동작
  • 도메인 이벤트 저장소를 통한 이벤트 소싱 가능

Repository Pattern

  • 데이터 접근 로직을 도메인에서 분리
  • Repository는 인메모리 컬렉션처럼 동작하는 인터페이스 제공
  • 구현체는 JPA, JDBC, MongoDB 등 자유롭게 선택
  • 테스트 시 Mock Repository로 간편하게 단위 테스트

Builder Pattern

  • 선택적 매개변수가 많은 복잡한 객체 생성
  • 생성 단계를 명확하게 표현하여 가독성 향상
  • Lombok의 @Builder 활용으로 보일러플레이트 제거
  • 불변 객체 생성에 적합

도메인 객체 불변성 원칙

기본 규칙:

  • final 키워드 필수: 도메인 객체의 모든 필드는 final로 선언하여 불변성 보장
  • 객체 재할당 불가: 생성 후 상태를 직접 변경할 수 없음
  • 상태 변경의 원칙: 상태 변경이 필요한 경우 새로운 객체를 반환

@Builder 설정 규칙:

  • access = AccessLevel.PRIVATE: Builder 외부 접근 방지로 캡슐화 강화
  • toBuilder = true: 기존 객체로부터 새로운 빌더 생성 가능 (상태 변경)
  • 팩토리 메소드로 빌더 노출: 명확한 의도의 팩토리 메소드를 통해서만 빌더 접근

사용 패턴:

@Builder(access = AccessLevel.PRIVATE, toBuilder = true)
public final class Order {
    private final String orderId;
    private final OrderStatus status;
    private final LocalDateTime createdAt;
    
    // 팩토리 메소드로 빌더 노출
    public static OrderBuilder builder() {
        return new OrderBuilder();
    }
    
    // 상태 변경 메소드 - 새로운 객체 반환
    public Order approve() {
        // 비즈니스 규칙 검증
        if (!canApprove()) {
            throw new InvalidOrderException("주문을 승인할 수 없습니다");
        }
        
        // toBuilder()를 사용해 기존 상태에서 필요한 부분만 수정한 새 객체 반환
        return this.toBuilder()
            .status(OrderStatus.APPROVED)
            .build();
    }
    
    // 상태 변경 메소드 - 새로운 객체 반환
    public Order cancel(String reason) {
        if (!canCancel()) {
            throw new InvalidOrderException("주문을 취소할 수 없습니다");
        }
        
        return this.toBuilder()
            .status(OrderStatus.CANCELLED)
            .build();
    }
    
    private boolean canApprove() {
        return this.status == OrderStatus.PENDING;
    }
    
    private boolean canCancel() {
        return this.status != OrderStatus.SHIPPED && 
               this.status != OrderStatus.DELIVERED;
    }
}

불변성의 이점:

  • 스레드 안전성: 멀티스레드 환경에서 동기화 불필요
  • 버그 감소: 예상치 못한 상태 변경으로 인한 버그 방지
  • 예측 가능성: 객체 상태가 변하지 않으므로 코드 흐름 이해 용이 (CQS)
  • 함수형 프로그래밍: 순수 함수처럼 동작하여 테스트 용이
  • 캐싱 안전성: 불변 객체는 안전하게 캐싱 가능

Value Object Pattern

  • 불변성 보장으로 식별성 불필요
  • 기본값 동등성으로 비교 간편화
  • 업무 로직을 값 객체에 응집 (Money, Percentage 등)

Specification Pattern

  • 복잡한 비즈니스 규칙을 조합 가능하게 캡슐화
  • 쿼리 조건, 유효성 검사 규칙 등 표현
  • 각 스펙을 조합하여 새로운 규칙 생성 가능

3.4 객체 지향 설계 원칙 (SOLID)

  • Single Responsibility: 클래스의 변경 이유는 하나
  • Open/Closed: 확장에는 열려있고 수정에는 닫혀있음
  • Liskov Substitution: 서브타입은 부모타입 계약을 위반하지 않음
  • Interface Segregation: 클라이언트별 특화된 인터페이스 제공
  • Dependency Inversion: 상위 모듈이 하위 모듈에 의존하지 않음

3.5 도메인 주도 설계(DDD)

  • 도메인 언어(Ubiquitous Language) 일관되게 사용
  • 바운디드 컨텍스트로 복잡한 도메인 분할
  • 애그리게이트로 트랜잭션 경계 정의
  • 팩토리 패턴으로 복잡한 객체 생성 로직 캡슐화

4. 최신 Java 언어 기능

4.1 Record (Java 16+)

  • DTO와 Value Object에 Record 활용하여 보일러플레이트 제거
  • 불변성이 기본으로 보장되므로 동시성 안전성 향상
  • 구조적 패턴 매칭과 함께 사용

4.2 Sealed Classes (Java 17+)

  • 도메인 모델의 계층 구조 명시적으로 제한
  • 상속 가능한 서브타입을 사전에 정의하여 예측 가능성 확보
  • 패턴 매칭과 함께 사용하면 더욱 안전한 타입 처리

4.3 Pattern Matching (Java 16+, 지속 개선 중)

  • 타입 체크 후 캐스트하는 단계 제거
  • instanceof 패턴으로 타입 안전성 강화
  • switch 표현식과 함께 사용하여 가독성 및 안정성 향상

4.4 Text Blocks (Java 13+)

  • SQL, JSON, XML 등 멀티라인 문자열 처리 간편화
  • 가독성 및 유지보수성 개선

4.5 Stream API & Functional Style

  • 함수형 프로그래밍으로 선언적 코드 작성
  • 컬렉션 처리의 의도를 명확히 표현
  • 불변성을 강조하여 사이드 이펙트 최소화

4.6 Virtual Threads (Java 21+)

  • 대량의 동시 작업 효율적으로 처리
  • 플랫폼 스레드 대비 메모리 사용량 급격히 감소
  • 간단한 스레드 모델로 비동기 프로그래밍 간편화

8. 코딩 컨벤션

8.1 Naming Convention

  • 도메인 모델: 비즈니스 의미 명확하게 (Order, Customer, Payment)
  • Port/Interface: 도메인 관점의 역할 표현 (OrderRepository, NotificationService, PaymentProcessor)
  • 구현체: 기술 및 구현의 의도가 명확하게 (JpaOrderRepository, EmailNotificationService, StripePaymentProcessor)
  • Service: 동작 기반 네이밍 (CreateOrderService, CancelOrderService)
  • Exception: 비즈니스 관점 표현 (OrderNotFoundException, InvalidOrderException)
  • 변수: 명확하고 설명적인 이름 (order, customerId, totalPrice)

8.2 코드 스타일 원칙

  • 생성자 주입을 기본으로 하여 의존성 명시
  • Optional을 적극 활용하여 Null 안정성 확보
  • Stream API로 선언적 컬렉션 처리
  • Record를 DTO 기본으로, @Getter/@Setter는 최소화
  • 한 메소드는 한 가지 일만 수행하도록 유지

8.3 패키지 구조 원칙

  • 도메인/기능 중심의 패키지 구성
  • 계층별 패키지 분리로 의존성 흐름 명확화
  • 공개 인터페이스는 최소화하여 캡슐화 강화

9. Claude Code 사용 가이드라인

9.1 코드 생성 시 기대사항

  • 클린 아키텍처의 의존성 규칙 준수
  • 도메인 계층은 프레임워크 독립적으로 작성
  • Port & Adapter 패턴으로 기술 선택 유연화
  • 예외는 비즈니스 의미 중심으로 설계
  • 적절한 디자인 패턴 적용으로 유연성과 유지보수성 확보

9.2 리팩토링 시 체크리스트

  • 순환 의존성 확인 및 제거
  • 도메인 로직이 정확한 계층에 위치하는지 검증
  • 과도한 매퍼 및 변환 로직 제거
  • 단일 책임 원칙 위반 여부 확인
  • 디자인 패턴 적용이 과도하지는 않은지 확인 (과설계 방지)

9.3 파일 생성 시 원칙

  • 계층 간 명확한 경계 설정
  • Port는 도메인 관점의 역할, 구현체는 기술 의도로 표현
  • 객체 생성 로직의 복잡성은 팩토리로 캡슐화
  • 비즈니스 규칙은 Strategy, Specification 패턴으로 캡슐화
  • 복잡한 흐름은 Command, Chain of Responsibility 패턴 고려

9.4 Port & Adapter 네이밍 가이드

Port (Interface - Domain에서 정의)

데이터 접근:
  OrderRepository
  UserRepository
  ProductRepository

외부 서비스:
  NotificationService
  PaymentProcessor
  EmailSender
  UserAuthenticator
  WeatherProvider

Adapter (Implementation - Infrastructure에서 구현)

데이터 접근 구현:
  JpaOrderRepository
  PostgresOrderRepository
  RedisOrderRepository
  MongoOrderRepository

외부 서비스 구현:
  EmailNotificationService
  SmsNotificationService
  KakaoNotificationService
  StripePaymentProcessor
  PaypalPaymentProcessor
  SmtpEmailSender
  SendgridEmailSender
  JwtUserAuthenticator
  OAuth2UserAuthenticator
  OpenWeatherProvider
  WeatherApiProvider

9.5 디자인 패턴 선택 기준

  • Strategy: 런타임 알고리즘 선택 필요 시
  • Factory: 객체 생성 복잡도 높을 때
  • Observer: 느슨한 결합 필요한 이벤트 처리 시
  • Repository: 데이터 접근 추상화 필요 시
  • Builder: 선택적 매개변수 많은 복잡 객체
  • Specification: 비즈니스 규칙 조합 필요 시

10. 참고 자료 및 학습

  • Clean Architecture by Robert C. Martin
  • Domain-Driven Design by Eric Evans
  • Building Microservices by Sam Newman
  • Release It! by Michael T. Nygard
  • Design Patterns by Gang of Four
  • Enterprise Integration Patterns by Gregor Hohpe
  • Java Language Features Documentation
  • Spring Framework Documentation