[Spring] 클린 아키텍처란 무엇일까?

thezz9·2025년 4월 4일
3

"The database is a detail. The web is a detail. The frameworks are details."
Robert C. Martin (Uncle Bob)


클린 아키텍처란?

클린 아키텍처(Clean Architecture)는 시스템의 핵심 규칙과 비즈니스 로직을 외부로부터 분리하고 보호하는 것에 집중하는 아키텍처 설계 패턴이다.

클린 아키텍처는 다음과 같은 문제를 해결하는 데 초점을 맞춘다.

  • 프레임워크에 종속적인 코드
  • UI/DB와 강하게 결합된 로직
  • 테스트하기 어려운 구조
  • 유지보수가 점점 어려워지는 코드베이스

Uncle Bob이 제시한 이 아키텍처는 의존성 규칙을 중심으로, 책임의 분리(SRP)의존성 역전(DIP)을 실천하는 구조다.


구조 개요: 계층과 방향성

클린 아키텍처는 중심을 향하는 동심원 구조로 표현된다. 각 계층은 안쪽 계층에만 의존 가능하고, 밖에서 안으로만 의존성이 흐른다.

의존성 규칙 (Dependency Rule)

"안쪽 계층은 바깥 계층을 모른다."


계층별 상세 설명


1. Entities (엔터티)

  • 가장 핵심적인 비즈니스 규칙
  • 도메인 개념, 도메인 메서드 (비즈니스 규칙을 스스로 수행하는 메서드) 포함
  • 어떤 외부 환경에도 의존하지 않음
  • 수명이 가장 길고, 시스템 전반에서 재사용됨

예: User, Order, Policy, Money, Validator

public class User {
    private final String email;
    private final String password;

    public User(String email, String rawPassword) {
        this.email = email;
        this.password = hash(rawPassword); // 도메인 레벨에서 비밀번호 해싱
    }

    public boolean isValidPassword(String rawPassword) {
        return this.password.equals(hash(rawPassword));
    }

    private String hash(String rawPassword) {
        // 해싱 로직은 단순화
        return "hashed:" + rawPassword;
    }
}

2. Use Cases (유스케이스 / 애플리케이션 서비스)

  • 애플리케이션의 구체적인 동작 흐름
  • 엔터티를 조작해 특정 기능을 수행
  • 외부 시스템과 직접 소통하지 않고, 포트를 통해 간접 연결

예: 회원 가입, 주문 생성, 결제 처리 등

public class SignUpUseCase {
    private final UserRepository userRepository;

    public SignUpUseCase(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public void execute(SignUpCommand command) {
        if (userRepository.existsByEmail(command.email())) {
            throw new DuplicateEmailException();
        }
        User user = new User(command.email(), command.password());
        userRepository.save(user);
    }
}

3. Interface Adapters (인터페이스 어댑터)

  • 외부와 내부 간의 형식 변환을 담당
  • REST Controller, DTO, ViewModel, Repository 구현체 등이 위치
  • 유스케이스 ↔ UI/DB/네트워크 계층을 연결하는 ‘어댑터’ 역할

예: UserController, UserRequestDto, JpaUserRepository

@RestController
public class UserController {
    private final SignUpUseCase signUpUseCase;

    public UserController(SignUpUseCase signUpUseCase) {
        this.signUpUseCase = signUpUseCase;
    }

    @PostMapping("/signup")
    public ResponseEntity<Void> signup(@RequestBody SignUpRequest request) {
        SignUpCommand command = new SignUpCommand(request.email(), request.password());
        signUpUseCase.execute(command);
        return ResponseEntity.ok().build();
    }
}

4. Frameworks & Drivers (프레임워크와 드라이버)

  • 가장 바깥 계층, 외부 기술 요소들로 구성
  • 교체 가능한 기술로 취급하며, 안쪽 계층에서는 존재 자체를 알지 못함

예: Spring, MySQL, Redis, Kafka, SMTP, React

// 실제 DB와 매핑되는 구조. 도메인 객체인 User와는 구분됨.
public interface SpringDataUserRepository extends JpaRepository<UserEntity, Long> {
    boolean existsByEmail(String email);
}

이 계층에선 어떤 프레임워크를 쓰든 상관없다. 클린 아키텍처에서는 이런 기술들이 ‘디테일’이기 때문이다.


의존성 역전 원칙 (Dependency Inversion Principle, DIP)

이 모든 계층 구조의 핵심을 관통하는 원칙은 바로 의존성 역전 원칙(DIP)이다.

  • 상위 수준 모듈은 하위 수준 모듈에 의존하지 않는다.
  • 둘 다 추상화에 의존해야 한다.
  • 추상화는 세부사항에 의존하지 않는다. 세부사항이 추상화에 의존해야 한다.

유스케이스는 오직 Repository 인터페이스(추상화)에만 의존하며, 실제 구현은 인터페이스 어댑터 계층에서 주입된다.
이로 인해 유스케이스는 Spring이나 DB와 같은 인프라 기술의 변화에 영향을 받지 않는다.

예시 다이어그램

예시 코드

// UseCase 계층 - 추상화만 의존
public interface UserRepository {
    void save(User user);
    boolean existsByEmail(String email);
}

// Interface Adapter 계층 - 세부 구현
@Repository
public class JpaUserRepository implements UserRepository {
    private final SpringDataUserRepository jpaRepo;

    public JpaUserRepository(SpringDataUserRepository jpaRepo) {
        this.jpaRepo = jpaRepo;
    }

    public void save(User user) {
        jpaRepo.save(UserEntity.from(user));
    }

    public boolean existsByEmail(String email) {
        return jpaRepo.existsByEmail(email);
    }
}

DIP가 만들어내는 클린 아키텍처의 4가지 특징

Framework 독립성

어떤 프레임워크도 내부 로직을 지배하지 않는다.

UI 독립성

웹이든 앱이든 CLI든 상관없다. UI는 교체 가능한 껍데기일 뿐이다.

DB 독립성

RDB, NoSQL, 파일 시스템, 메시징 시스템과 독립적인 도메인 로직.

테스트 용이성

외부 환경이 없어도, 순수 자바 코드로 유닛 테스트 가능.


클린 아키텍처의 장점

  • 비즈니스 로직의 장기적 생존 가능성 확보
  • 변화에 유연한 구조 (DB, 프레임워크 교체 시 최소 영향)
  • 고립된 테스트, 유연한 배포 전략 가능
  • 유지보수성과 협업 효율 ↑

클린 아키텍처의 단점

  • 초반 설계와 코드량이 많아짐
  • 작은 팀/프로젝트에서는 오버엔지니어링이 될 수 있음
  • 팀원 간 합의된 규칙 없이 구조만 흉내 내면 오히려 코드가 복잡해질 수 있음

실제 적용 시 고려사항

  • 패키지 구조도 의존성 방향을 반영해야 함
  • 모듈 분리(멀티모듈)와 함께 쓰면 더 강력해짐
  • 각 계층마다 명확한 책임 분리와 테스트 전략 수립 필요
  • 인터페이스(포트)를 정의할 때, 추상화 범위를 잘 조절할 것
    • 포트를 너무 세분화하면 오히려 추상화 비용이 커질 수 있음

마무리

클린 아키텍처는 단지 "겹겹이 나눈 계층"이 아니다.
비즈니스 로직을 보호하고, 기술 변화에 유연하게 대처할 수 있도록 설계하는 사고방식이다.

The Clean Code Blog - The Clean Architecture
by Robert C. Martin (Uncle Bob)

profile
개발 취준생

1개의 댓글

comment-user-thumbnail
2025년 4월 4일

내용의 질이 논문급입니다. 대학원 가시는 걸 적극 추천드립니다.

답글 달기