요즘 팀원들과 코드 리뷰를 하다 보면, 단순히 레이어를 나눴다는 이유로 안심하고 넘어가는 경우가 꽤 많습니다. 그런데 정말 제대로 나눈 걸까요? 이번 글에서는 우리가 흔히 사용하는 레이어드 아키텍처(Layered Architecture) 구조에 대해 다시 생각해보고, 실무에서 종종 마주치는 싱크홀 안티패턴(Sinkhole Anti-pattern)에 대해서도 정리해보려고 합니다. 모두가 사용하는 구조이기에, 더 잘 쓰는 게 중요하다고 생각해요.
레이어드 아키텍처는 말 그대로 소프트웨어를 '층'으로 나눠 관심사를 분리한 구조입니다. 관심사라는 건 같은 목적이나 책임을 가진 로직들을 하나의 묶음으로 본다는 개념입니다. 대표적으로 아래와 같은 3계층 구조가 가장 많이 사용되죠:
이렇게 계층을 나누면 어떤 장점이 있을까요?
하지만, '나눈다'는 건 결국 추상화의 문제입니다. 추상화는 잘하면 명확한 구조가 되지만, 잘못하면 '불필요한 중간 과정'만 늘어나는 결과가 됩니다.
싱크홀(Sinkhole) 안티패턴은 이름처럼 요청이 중간 계층에서 그냥 '빠져버리는' 현상이에요. 즉, 중간 계층(주로 Service나 UseCase)이 아무 일도 안 하면서 하위 계층으로 요청을 단순 전달만 하는 구조입니다.
예를 들어:
@Service
public class OrderService {
private final OrderDao orderDao;
public OrderService(OrderDao orderDao) {
this.orderDao = orderDao;
}
// 그냥 DAO 호출만 하는 경우
public Order getOrder(Long id) {
return orderDao.getById(id);
}
}
겉으로 보면 문제 없어 보이지만, 실은 이 Service 계층은 존재 의미가 없습니다. 이건 추상화가 아니라 불필요한 중계일 뿐이에요.
CPU와 메모리는 결국 이 "아무 일도 안 하는 코드"를 실행하느라 낭비되고, 코드 구조는 더 복잡해지고, 팀은 이 계층이 무슨 역할을 하는지 혼란스러워합니다.
꼭 그렇지도 않습니다. 오히려 서비스 계층이 비어 있어도 괜찮은 경우도 있습니다:
중요한 건 "일관성 없는 예외"를 만드는 것이지, "초기에는 비어 있더라도 명확한 역할이 부여된 계층"은 오히려 팀에게 방향성을 줍니다.
저는 팀원들에게 아래와 같이 리뷰를 남기곤 합니다:
"이 Service는 지금 역할이 없습니다. 그대로 두어도 되지만, 꼭 필요한 계층인지 한 번 다시 고민해보면 좋겠습니다."
"지금은 단순 전달만 하지만, 추후 Validation이나 Logging 로직이 붙는다면 이 위치가 적절할 수 있겠네요."
아래는 싱크홀에서 벗어난 Service 예시입니다:
@Service
public class OrderService {
private final OrderDao orderDao;
private final PaymentValidator paymentValidator;
public OrderService(OrderDao orderDao, PaymentValidator validator) {
this.orderDao = orderDao;
this.paymentValidator = validator;
}
public Order getValidatedOrder(Long id) {
Order order = orderDao.getById(id);
if (!paymentValidator.isValid(order)) {
throw new IllegalArgumentException("유효하지 않은 결제 정보입니다.");
}
return order;
}
}
또는 다음과 같은 형태도 많죠:
@Service
public class EmailSenderService {
private final MailProviderClient client;
public void sendWelcomeEmail(User user) {
String body = "Welcome, " + user.getName();
client.sendEmail(user.getEmail(), body);
}
}
이런 코드처럼 비즈니스 중심의 책임이 명확하게 포함되면, 해당 계층은 더 이상 단순 전달이 아니라고 생각합니다.
싱크홀 안티패턴은 단순히 'Service가 비어있다'가 아니라, 의미 없는 계층이 남용되는 현상을 말합니다. 이를 방지하기 위해선 다음 기준을 명확히 가져가야 합니다:
결국 구조는 팀을 위한 약속입니다. 단순히 패턴을 따라가는 것이 아니라, 팀이 이해하고 유지할 수 있는 구조를 설계하는 것이 진짜 실무 아키텍처라고 생각해요.