"데이터를 저장할 때, user.save()가 직관적일까요, 아니면 repository.save(user)가 유지보수에 좋을까요?"
데이터베이스의 테이블(Table)과 객체지향의 객체(Object)를 연결하는 ORM 기술은 크게 두 가지 철학으로 나뉩니다. "객체가 DB 기능까지 다 하는 만능(Smart)이냐", 아니면 **"객체는 데이터만 담고 DB 로직은 분리(Dumb)하느냐"**의 싸움입니다.
1. 방식 1: 액티브 레코드 (Active Record) - "똑똑한 객체"
Ruby on Rails, Django, Node.js(Sequelize, TypeORM 일부) 등 스크립트 언어 진영에서 주로 사용하는 방식입니다. 객체(Entity)가 데이터와 함께 CRUD 메서드(save, delete)를 모두 가지고 있습니다.
특징
- 1:1 매핑: DB 테이블의 Row 하나가 객체 하나와 정확히 일치합니다.
- 직관성: 객체 스스로 "나를 저장해!", "나를 지워!"라고 명령합니다.
코드 예시
Java
// Entity 안에 비즈니스 로직과 DB 로직이 섞여 있음
public class User extends ActiveRecord {
private String name;
private int age;
// 객체 스스로 저장하는 능력 보유
public void register() {
if (this.age < 18) throw new Exception("미성년자");
this.save(); // 부모 클래스(ActiveRecord)의 메서드 호출
}
}
// 사용 (Client)
User user = new User();
user.setName("James");
user.setAge(20);
// "유저야 저장해라" (매우 직관적)
user.register();
장단점
- 장점: 코드가 매우 간결하고 이해하기 쉽습니다. 단순한 CRUD 애플리케이션을 빠르게 개발할 때 최강입니다.
- 단점:
- SRP 위반: 객체가 '데이터 담기'와 'DB 접근'이라는 두 가지 책임을 가집니다.
- 결합도: 도메인 객체가 DB 라이브러리에 강하게 의존하므로 테스트하기 어렵습니다.
2. 방식 2: 데이터 매퍼 (Data Mapper) - "순수한 객체"
Java(Hibernate/JPA), .NET(Entity Framework) 등 엔터프라이즈 진영의 표준입니다. 객체(Entity)는 순수하게 데이터만 담고(POJO), DB 접근 로직은 별도의 매퍼(Repository)가 담당합니다.
특징
- 책임 분리: 도메인 객체는 DB가 존재하는지조차 모릅니다.
- 중재자: Mapper(Repository)가 객체와 DB 사이에서 데이터를 이동시킵니다.
코드 예시 (Spring Data JPA)
Java
// 1. 순수 도메인 객체 (POJO)
@Entity
public class User {
@Id private Long id;
private String name;
// DB 관련 로직(save 등)이 전혀 없음!
}
// 2. 저장소 (Repository)
public interface UserRepository extends JpaRepository<User, Long> {
// DB 접근은 여기서 전담
}
// 3. 사용 (Service)
@Service
@RequiredArgsConstructor
public class UserService {
private final UserRepository userRepository; // 매퍼 주입
public void register(User user) {
// "저장소야, 이 유저를 저장해라" (제3자에게 위임)
userRepository.save(user);
}
}
장단점
- 장점:
- 도메인 순수성: 객체가 특정 프레임워크에 의존하지 않아 단위 테스트가 매우 쉽습니다.
- 유연성: DB 스키마와 도메인 모델이 1:1로 매칭될 필요가 없어 복잡한 비즈니스 로직 구현에 유리합니다.
- 단점: Repository, Service, Entity 등 만들어야 할 클래스가 많고 코드가 길어집니다.
3. 실무 비교: 언제 무엇을 쓰는가?
| 구분 | Active Record (Rails style) | Data Mapper (JPA style) |
| 핵심 철학 | 객체가 곧 DB Row (Smart Model) | 객체와 DB의 완전한 분리 (POJO) |
| 저장 방식 | user.save() | repository.save(user) |
| 복잡도 | 낮음 (빠른 개발) | 높음 (구조적 설계 필요) |
| 테스트 | DB 의존성 때문에 까다로움 | Mocking 쉬움 (순수 자바 테스트 가능) |
| 추천 상황 | 빠른 프로토타이핑, 단순 CRUD, 스타트업 초기 | 복잡한 비즈니스 도메인, 장기 유지보수, 엔터프라이즈 |
결론
"빨리 만들고 끝내야 한다"면 액티브 레코드 패턴이 생산성 면에서 압도적입니다.
하지만 **"비즈니스 로직이 복잡하고, 10년 이상 유지보수해야 하는 시스템"**이라면, 도메인 모델을 DB로부터 보호할 수 있는 데이터 매퍼(JPA) 패턴을 사용하는 것이 정석입니다. 스프링이 JPA를 표준으로 채택한 이유이기도 합니다.
'프로그래밍 > 디자인패턴' 카테고리의 다른 글
| 캐싱 패턴(Caching Pattern) 완벽 정리 (0) | 2025.12.07 |
|---|---|
| 서킷 브레이커 패턴(Circuit Breaker Pattern) 완벽 정리 (1) | 2025.12.07 |
| 사가 패턴(Saga Pattern) 완벽 정리: 코레오그래피(Choreography) vs 오케스트레이션(Orchestration) (0) | 2025.12.07 |
| 페이지네이션 패턴(Pagination Pattern) 완벽 정리 (0) | 2025.12.07 |
| 콜백 패턴(Callback Pattern) 총정리: 동기(Blocking) vs 비동기(Non-blocking) (0) | 2025.12.07 |