본문 바로가기
Framework/Spring Boot

[Spring Boot] JPA(Hibernate) 기초: 객체와 관계형 DB의 패러다임 불일치를 부수다

반응형

과거의 자바(Java) 백엔드 개발자들은 하루 종일 무엇을 했을까요? "SQL 작성 기계"였습니다. 게시판 하나를 만들려 해도 INSERT INTO board VALUES..., SELECT * FROM board WHERE... 등 길고 지루한 SQL 쿼리문을 수백 줄씩 타이핑해야 했습니다. 만약 중간에 DB 테이블에 컬럼이 하나라도 추가되면, 그 수백 줄의 쿼리문을 일일이 다 찾아다니며 수정하는 끔찍한 '노가다'의 연속이었죠. 게다가 객체 지향 언어인 자바(Java)와 관계형 데이터베이스(RDB)는 태생적으로 사상(Paradigm)이 너무나도 달랐습니다. 자바는 '상속'과 '참조'를 사랑하지만, DB는 그냥 2차원 표(Table)일 뿐이니까요. 이 극악무도한 '패러다임의 불일치'를 박살내고, 개발자가 순수하게 '자바 객체'만 다루면 알아서 뒤에서 SQL을 짜주고 DB에 넣어주는 마법의 기술이 등장했습니다. 바로 JPA(Java Persistence API)와 Hibernate(하이버네이트)입니다.

이번 포스팅에서는 "백엔드 개발의 패러다임을 바꾼 혁명!" JPA의 구동 원리와 실무 CRUD 기본기에 대하여 다뤄보겠습니다.

✨ 1. ORM과 JPA, 그리고 Hibernate의 관계

우리가 흔히 JPA라고 부르는 기술의 정체성을 헷갈려하시는 분들이 많습니다. 용어부터 확실히 정리하고 갑시다.

 

  • ORM (Object-Relational Mapping): 객체(Object)와 RDB의 테이블(Relation)을 매핑(Mapping)하는 '개념이자 패러다임' 그 자체입니다.
  • JPA (Java Persistence API): 자바 진영에서 이 ORM 기술을 쓰기 위해 만든 '표준 인터페이스(규칙서)'입니다. 즉, "자바에서 ORM을 쓰려면 인터페이스 이름은 이렇게 짓고, 룰은 이렇게 해라"라고 문서로만 정의해 놓은 껍데기입니다.
  • Hibernate (하이버네이트): JPA라는 인터페이스 규칙서를 바탕으로, 실제로 피와 살을 붙여 코드로 '구현체'를 만든 대장 격 라이브러리입니다. (실무에서 JPA를 쓴다는 말은 99% Hibernate를 쓴다는 뜻과 같습니다.)

🧱 2. Entity(엔티티) 설계: 객체를 테이블로 변신시키다

JPA를 사용하면 더 이상 SQL로 CREATE TABLE을 칠 필요가 없습니다. 순수한 자바 클래스를 하나 만들고 @Entity 딱지를 붙이면, 스프링 부트가 실행될 때 JPA가 이 클래스를 스캔하여 알아서 DB에 테이블을 만들어 줍니다!

import jakarta.persistence.*;
import lombok.*;

@Entity // 🌟 "JPA야, 이 클래스를 보고 DB 테이블을 만들어 줘!"
@Table(name = "member") // 실제 DB 테이블 이름 지정 (생략 시 클래스명과 동일)
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED) // 기본 생성자는 필수 (JPA 프록시 기술용)
public class MemberEntity {

    @Id // 🌟 이 필드가 PK(Primary Key)임을 명시
    @GeneratedValue(strategy = GenerationType.IDENTITY) // MySQL의 AUTO_INCREMENT 옵션
    private Long id;

    @Column(nullable = false, length = 50) // DB 컬럼 제약조건 설정 (NOT NULL, VARCHAR(50))
    private String email;

    @Column(nullable = false)
    private String name;

    @Builder // 실무에선 객체 생성 시 생성자 대신 빌더 패턴을 사랑합니다.
    public MemberEntity(String email, String name) {
        this.email = email;
        this.name = name;
    }
}

🎩 3. 영속성 컨텍스트(Persistence Context): 1차 캐시의 마법

JPA를 관통하는 가장 핵심적이고 어려운 단어가 바로 '영속성 컨텍스트'입니다. 이는 애플리케이션과 데이터베이스 사이에 존재하는 '가상의 캐시 메모리(임시 저장소)'입니다.

만약 우리가 멤버 객체를 조회(SELECT)하고, 곧바로 이름을 "철수"에서 "영희"로 바꾸는(UPDATE) 자바 코드를 짰다고 가정해 봅시다. JPA는 DB에 곧바로 SQL을 때리지 않습니다.

  1. 처음 조회할 때 DB에서 데이터를 가져와 영속성 컨텍스트(1차 캐시)에 보관합니다.
  2. 개발자가 자바 객체(Entity)의 setName("영희")를 호출하여 값을 바꿉니다. (이때 Update SQL을 치지 않습니다!)
  3. 트랜잭션(Transaction)이 정상적으로 끝나는 시점(Commit)에, JPA가 1차 캐시의 원본 데이터와 현재 객체의 상태를 비교(Dirty Checking, 더티 체킹)합니다.
  4. 바뀐 점이 발견되면, 그제야 JPA가 알아서 UPDATE member SET name='영희'... 쿼리를 만들어 DB에 한 방에 날려버립니다!

이러한 메커니즘 덕분에 우리는 마치 자바의 일반 List 컬렉션에서 객체를 꺼내서 값을 수정하는 것과 완전히 동일한 느낌으로 데이터베이스를 다룰 수 있게 됩니다. 개발 생산성이 미친 듯이 올라가는 마법입니다.


🎯 4. 마무리 및 다음 단계

지금까지 지긋지긋한 SQL 쿼리 작성의 노가다에서 우리를 해방시키고, 객체 지향적인 순수 자바 코딩만으로 데이터베이스를 우아하게 통제할 수 있게 해주는 혁명적 기술, JPA(Hibernate)와 영속성 컨텍스트의 핵심 원리에 대해 다루어 보았습니다. 단일 테이블을 다루는 것은 이제 껌이 되었습니다.

그런데 실무 프로젝트는 단일 테이블로 돌아가지 않습니다. 회원(Member)이 여러 개의 주문(Order)을 하고, 하나의 카테고리에 여러 상품(Product)이 속하는 등 복잡한 '관계(Relationship)'가 얽혀 있습니다. 여기서 초보자들이 JPA를 포기하고 다시 MyBatis로 도망가는 사태가 발생합니다. 바로 그 악명 높은 '연관관계 매핑'과 'N+1 쿼리 성능 폭발 문제' 때문입니다. 이어지는 09단계 포스팅에서는 "JPA 실무의 진정한 진입장벽이자 면접 단골 질문 1순위!" 1:N, N:1 연관관계 매핑의 완벽한 설계 공식과, 성능을 잡아먹는 N+1 문제의 위협 및 해결책에 대해 피 터지게 파헤쳐 보겠습니다!

반응형