프로그래밍/디자인패턴

ORM 패턴(ORM Pattern) 완벽 정리

Jinwookoh 2025. 12. 7. 19:45

"데이터를 저장할 때, 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를 표준으로 채택한 이유이기도 합니다.