보통 프로젝트를 할 때, 자바를 이용한다면 저는 거의 스프링을 주로 씁니다. 또한 스프링을 쓰면 JPA도 거의 필수적으로 사용하게 됩니다.
거기다 Data JPA를 쓰게 되면 다음처럼 JPA Entity가 생성됩니다.
@Entity
@Table(name = "user")
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long memberId;
...
}
이후 UserEntity에 메서드를 추가하거나 할 수 있습니다. 그런데 이 클래스가 Entity일까요?
📌 Entity?
우선 Entity를 3가지 종류로 나누어보겠습니다.
- Domain Entity
- DB Entity
- JPA Entity
Domain Entity
도메인은 간단히 설명하면 ‘비즈니스 영역’이라고 할 수 있습니다. 은행을 사용하는 소프트웨어를 예로 들면, Account가 사용될 수 있습니다. 이는 식별자가 존재하고 이 도메인 모델에 특화된 비즈니스 로직을 가지고 라이프사이클을 가질 수 있습니다. 이러한 도메인 모델을 Domain Entity라고 합니다. 즉, 우리가 해결하려는 문제의 객체라고 생각할 수 있습니다.
DB Entity
DB Entity는 단순히 데이터베이스에서 사용되는 객체입니다. 즉 정보를 담고 있는 하나의 row라고 할 수 있습니다.
JPA Entity
JPA Entity는 관계형 데이터베이스에 있는 데이터를 객체로 매핑하는 데 사용되는 클래스입니다. 따라서 JPA Entity는 DB Entity와 가깝다고 할 수 있습니다. JPA Entity는 관계형 데이터베이스에 사용되는 용어를 어노테이션으로 그대로 사용하기도 하고, 관계형 데이터베이스에 저장된 객체를 매핑하기 때문입니다.
📌 도메인과 인프라
주로 우리가 프로젝트를 하게 되면, DB와 JPA는 인프라로 분리할 수 있습니다. 어떤 DB를 쓸지와 JPA는 세부 기술 영역이기 때문입니다. 예를 들어, 멤버의 팀을 바꾸는 로직이 있다고 가정해 보겠습니다.
@Service
@RequiredArgsConstructor
public class MemberService {
private final MemberRepository memberRepository;
// Member와 Team은 JPA Entity
public void changeTeam(Long memberId, Long teamId) {
Member member = memberRepository.findById(memberId);
member.changeTeam(teamId);
}
}
위 로직은 잘못 작성되었을 수 있지만, 예시를 위한 간단한 코드입니다.
MemberRepository는 자바 코드로 이루어진 인터페이스이고, 구현체인 MemberRepositoryImpl은 필드로 JPA 리포지토리를 가진 상태로 각 행동이 구현되어 있습니다.
여기서 Member가 JPA Entity로 되어있다면, Service는 JPA에 종속적인 코드가 됩니다. 그렇다면 Service를 세부 기술과 뗴어놓으려면 어떻게 해야 할까요?
Service에서 JPA Entity대신 Domain Entity를 사용한다면, 위와 같은 상황은 사라지게 됩니다.
@Service
@RequiredArgsConstructor
public class MemberService {
private final MemberRepository memberRepository;
// Member와 Team은 Domain Entity
public void changeTeam(Long memberId, Long teamId) {
Member member = memberRepository.findById(memberId);
member.changeTeam(teamId);
memberRepository.update(member);
}
}
public class Member {
private Long memberId;
...
}
여기서 Member는 단순히 Java 코드로만 이루어져 있습니다. 즉 Service는 세부 기술이 바뀌어도 사실상 코드는 변함이 없고, 팀을 바꾼다는 행위 또한 변함없는 것과 같습니다. JPA를 마이바티스로 바꾸거나, DB를 바꾼다고 하더라도 Service의 코드는 수정되지 않습니다. 결국 세부 기술을 바꾼다는 것에 맞게 인프라 계층을 수정하면 됩니다.
실제로 프로젝트에서 MySQL에서 Redis로 옮길 때 인프라 계층만 수정하여 비교적 수월하게 옮길 수 있었습니다.
📌 그러면 무조건 분리하는 게 좋을까?
위처럼 코드를 작성하면 분명 세부 기술과 비즈니스 로직이 분리된다는 장점이 있습니다. Service는 순수한 자바 코드로 작성할 수도 있습니다. 그렇다면 장점만 있을까요?
다른 기술?
사실 대부분 프로젝트에서 JPA를 주로 쓰곤 합니다. 우리의 프로젝트가 JPA보다 더 나은 기술이 나와서, 또는 다른 목적에 의해 기술이 변경될 이유가 생길까요? 정말 길게 유지하는 프로젝트가 아닌 이상 그러기는 쉽지 않을 것입니다.
개발 시간 소요
분리한다는 것은 그만큼 시간이 소요되는 작업입니다. 시간적 여유가 있는 프로젝트도 있을 수 있지만, 그렇지 않다면 시간적인 부분도 고려해야 합니다. 간단하게 끝낼 수 있는 작업도 인터페이스로 분리하고 구현체를 따로 작성해야 합니다.
📌 결국 트레이드오프..!
결국 우리는 트레이드오프를 고려해야 합니다. 각 선택엔 장단점이 존재합니다. 현재 진행하고 있는 프로젝트 규모나 상황에 따라 팀 내에서 적절한 선택을 할 필요가 있습니다. 정말 단순한 프로젝트에서 이를 적용하는 것은 ‘굳이..?’싶은 작업이기도 합니다. 하지만 서비스 요구사항이 복잡할수록 프로젝트 규모는 커지게 되고 이 상황에서 분리시키지 않는다면 해당 기술에 상당히 종속적인 프로젝트가 됩니다. ‘해결하면 된 거 아닌가?’라고 생각할 수 있지만, 우리는 유지보수하기 좋은 코드를 작성할 필요도 존재합니다.
이번 프로젝트에서 이를 처음 적용하여 개발하였는데, 비즈니스 영역이 견고해진다는 느낌을 받았습니다. 그리고 데이터 중심적인 사고로 접근하기보다는 객체지향적으로 먼저 생각하게 되는 부분도 있었습니다. 하지만 팀원 모두 익숙지 않아서 시간이 꽤 소요되었고, 짧은 기간 내 개발을 해야 했기에 쉽지 않았습니다. 그러다 보니 테스트 코드 작성하기가 쉽지 않았고 후반에는 거의 테스트를 작성하지 못했습니다. 무조건적으로 프로젝트 내에 분리된 구조, 더 좋은 기술을 추구하기보다 상황에 맞게 적용해야 할 필요성을 느꼈습니다. 무엇보다 ‘요구사항에 맞춰 돌아가는 코드’를 작성하는 게 우선이지 않을까 싶습니다. 일정과 시간을 고려하여 적합한 아키텍처와 기술을 적용할 필요성을 느낄 수 있었습니다.
'아키텍처' 카테고리의 다른 글
Kafka를 이용한 이벤트 기반 아키텍처 맛보기 (0) | 2025.02.18 |
---|---|
레이어 아키텍처 제대로 적용하기 (2) | 2024.11.24 |