나만의 학습 기록

최종 목적은 기술 블로그💩

백엔드 공부

더 깊은 데이터베이스 지식 (2) - ORM

밈밍민믹 2026. 4. 15. 18:07

오늘 날씨가 엄청 따뜻해졌어요. 평상시에도 일찍 일어나기 힘들었는데, 날이 따뜻해지니 더 일어나기가 힘들더라고요. 아직 4월인데 금방 여름이 될 것 같습니다. 저번 시간에는 트랜잭션과 ACID에 대해서 간략하게 알아보았습니다. 오늘은 ORM에 대하여 알아보도록 할까요


ORM

Object-Relational Mapping의 약자로 객체-관계 매핑이라고 부른다.

객체 지향 프로그래밍 언어의 객체와 관계형 데이터베이스의 데이터를 자동으로 연결(매핑)하는 기술입니다.

 

그렇다면 ORM의 필요성을 이해하기 위해서는 먼저 객체지향과 관계형 데이터베이스의 차이점을 알아볼 필요가 있습니다.

객체지향과 관계형 DB의 차이

항목 객체지향(OOP) 관계형 DB(RDBMS)
기본 단위 객체 테이블
구성 요소 속성과 메서드로 구성 컬럼과 행으로 구성
데이터 표현 방식 객체 자체로 데이터를 표현 테이블에 데이터를 행과 열 형태로 저장
관계 표현 방식 객체 참조로 관계를 맺음 외래 키로 관계를 맺음
상속 상속 개념이 존재함 일반적인 테이블 구조에는 상속 개념이 없음
설계 방식 현실 세계의 개체를 객체로 모델링 데이터를 정규화하여 테이블로 설계
목적 프로그램 내부에서 객체 지향적으로 문제를 해결 데이터를 저장, 조회, 수정, 삭제하며 일관성 있게 관리

 

Java에서 회원을 표시할 경우

객체지향의 예시

public class User{
	private Long id;
    private String name;
    private String email;
    
    public User(Long id, String name, String email){
    	this.id = id;
        this.name = name;
        this.email = email;
     }
     
     public String getName(){
     	return name;
     }
}

 

User user = new User(1L, "홍길동", "test@test.com");

 

관계형 DB의 예시

CREATE TABLE users(
	id BIGINT PRIMARY KEY,
    name VARCHAR(100),
    email VARCHAR(100)
);

 

이처럼 객체지향에서는 회원 정보를 User와 같은 클래스와 객체로 표현하고, 관계형 데이터베이스에서는 이를 users 테이블의 행과 열 테이블의 형태로 저장합니다. 즉, 같은 데이터를 다루더라도 객체지향은 객체 중심, 관계형 데이터베이스는 테이블 중심으로 표현합니다.

따라서 두 방식은 관계를 표현하고 데이터를 다루는 구조가 다르기에 객체와 테이블 사이를 연결해주는 ORM이 필요합니다. 

 

ORM의 장단점

장점

  1. SQL을 직접 작성하지 않고, 사용하는 언어로 데이터베이스에 접근할 수 있다.
    • ORM을 사용하면 SQL 쿼리를 직접 작성할 필요 없이 자바와 같은 프로그래밍 언어를 통해 데이터베이스 작업을 수행할 수 있습니다. 이는 개발 생산성을 높이고, 코드의 가독성을 향상시킵니다.
  2. 객체지향적으로 코드를 작성할 수 있기 때문에 비즈니스 로직에만 집중할 수 있다.
    • ORM은 데이터베이스 작업을 객체지향 프로그래밍 방식으로 처리할 수 있게 해주므로 개발자는 비즈니스 로직에만 집중할 수 있다. 데이터베이스 작업이 애플리케이션의 다른 부분과 자연스럽게 통합된다.
  3. 데이터베이스 시스템이 추상화됐기에 방언(MySQL, Oracle 등) 전환을 한다고 해도 추가 작업이 거의 없다.
    • ORM은 데이터베이스 독립성을 제공하여, MySQL, Oracle 등과 같은 다른 데이터베이스로 변경할 때 추가 작업이 최소화된다. 즉, 데이터베이스 간에 이식성을 높여준다.
  4. 매핑하는 정보가 명확하기 때문에 ERD에 대한 의존도를 낮출 수 있고 유지보수에 유리한 점이 많다.
    • ORM은 클래스와 테이블 간의 매핑 정보를 명확하게 정의할 수 있어, 데이터베이스의 ERD에 대한 의존도를 낮추고, 코드 변경에 유연하게 대응할 수 있다.

단점

  1. 프로젝트의 복잡성이 커질수록 사용 난이도가 올라간다.
    • 프로젝트가 복잡해질수록 ORM 설정과 사용이 어려워질 수 있다. 특히, 복잡한 데이터 모델링과 성능 튜닝이 필요할 때 ORM의 사용이 복잡해진다.
  2. 복잡하고 무거운 쿼리는 ORM으로 해결이 불가능한 경우가 많다.
    • ORM은 단순한 CRUD 작업에 적합하지만, 복잡한 조인이나 성능이 중요한 쿼리에는 한계가 있을 수 있다. 이럴 경우에는 직접 SQL을 작성하는 것이 더 효율적일 수 있다.

 

JPA

자바 애플리케이션에서 관계형 데이터베이스를 사용하는 방식을 표준화한 ORM 인터페이스의 모음

예시

import jakarta.persistence.*;

@Entity
@Table(name = "users")
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;
    private String email;

    protected User() {
    }

    public User(String name, String email) {
        this.name = name;
        this.email = email;
    }

    public Long getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public String getEmail() {
        return email;
    }
}

 

JPA를 이용한 저장 예시

import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import jakarta.transaction.Transactional;
import org.springframework.stereotype.Service;

@Service
public class UserJpaService {

    @PersistenceContext
    private EntityManager entityManager;

    @Transactional
    public void saveUser() {
        User user = new User("홍길동", "hong@test.com");
        entityManager.persist(user);

        System.out.println("저장된 사용자: " + user.getName() + ", " + user.getEmail());
        // 예상 출력 -> 저장된 사용자: 홍길동, hong@test.com
    }
}

 

 

 

Hibernate

자바 언어를 위한 가장 인기 있는 개체 관계 매핑 프레임워크로, 자바 객체와 데이터베이스 테이블 간의 매핑을 자동화하고 SQL 쿼리 작성 부담을 줄여줍니다.

persist()가 실제로 동작할 때 내부적으로 SQL을 생성합니다. 위의 코드를 실행하면 아래와 같이 내부적으로 생성됩니다.

insert 
into
    users
    (email, name) 
values
    (?, ?)

 

파라미터 값까지 확인하면 실제로는 삽입하라는 의미입니다.

insert into users (email, name) values ('hong@test.com', '홍길동');

즉, 자바 객체를 보고 관계형 DB가 이해할 수 있는 SQL로 변환하는 것입니다.

 

Spring Data JPA

Spring 환경에서 JPA를 더 편리하게 사용할 수 있도록 도와주는 기술입니다.

개발자는 Repository 인터페이스만 작성하면 CRUD 기능을 손쉽게 사용할 수 있으며, 반복적인 데이터 접근 코드를 크게 줄일 수 있습니다.

 

Spring Boot에서의 사용 예시

import org.springframework.data.jpa.repository.JpaRepository;

public interface UserRepository extends JpaRepository<User, Long> {
	// save(), findBuId(), findAll(), deleteById()와 같은 메서드를 직접 구현하지 않아도 된다.
}

 

이처럼 Repository를 생성하고 Service 계층에서 이를 호출해 사용하면, 개발자는 복잡한 SQL을 직접 작성하지 않고도 객체 중심으로 데이터베이스를 다룰 수 있습니다. 이는 ORM이 제공하는 대표적인 편의성 중 하나입니다.

 

 


오늘은 ORM에 대해 간략하게 정리해보았습니다. 저 역시 Spring Boot에서 Repository를 통해 JPA를 자주 사용해왔지만, 실제로 사용하는 것에 비해 ORM의 개념과 동작 원리에 대해서는 충분히 이해하지 못하고 있었다는 점을 다시 느끼게 되었습니다. JPA는 save(), findById()와 같은 메서드를 통해 데이터 접근을 편리하게 만들어주지만, 모든 상황에서 항상 효율적인 것은 아닙니다. 특히 객체와 관계형 데이터베이스를 매핑하는 과정에서 의도하지 않은 추가 쿼리가 발생할 수 있으며, 이로 인해 대표적으로 N+1 문제와 같은 성능 이슈가 나타날 수 있습니다. 다음 글에서는 ORM을 사용할 때 자주 언급되는 문제 중 하나인 N+1 문제에 대해 자세히 알아보겠습니다. 수고하셨습니다.