[스프링부트 #5] 스프링 기반 DB 통신의 핵심

DB와 통신할 때는 쿼리 즉, 어떤 데이터를 가져올지 명령하는 SQL문과 DB에서 어떻게 데이터를 응답해주는지 결과 데이터 구조를 명확히 알고 있기위해서 정리하였습니다.
도경원's avatar
Aug 05, 2025
[스프링부트 #5] 스프링 기반 DB 통신의 핵심
DB와 통신할 때는 쿼리 즉, 어떤 데이터를 가져올지 명령하는 SQL문과 DB에서 어떻게 데이터를 응답해주는지 결과 데이터 구조를 명확히 알고 있어야 한다.

1. JDBC 기반 통신 방식

notion image
  • DB에서 결과를 가져오면 ResultSet 형태로 반환된다.
  • 개발자가 직접 파싱해서 Java 객체(User 등)로 변환해야 한다. 요즘 기술력으로는 이 과정은 귀찮고 반복적이다.
  • JDBC는 상대적으로 저수준이라고 볼 수 있다.
💬
즉, 데이터 껍데기(ResultSet)비즈니스 로직 객체(User) 사이를 직접 연결해야 한다.
  • 첫 번째 파싱: DB에서 ResultSet 형태로 받아옴
  • 두 번째 파싱: ResultSet → 자바 객체(User)로 수동 매핑

2. JPA

  • JPA는 SQL 작성 없이 메서드 호출로 쿼리를 수행한다.
  • DB 결과를 엔티티 객체에 자동으로 매핑해준다.
// 예: JPA는 아래와 같은 코드를 자동으로 처리해줌 User user = userRepository.findById(1L);
💬
즉, JPA는 첫 번째 파싱(ResultSet)두 번째 파싱(User 클래스 채우기) 까지 자동화한다.
  • 첫 번째 파싱: JPA가 SQL 실행 → ResultSet을 내부적으로 처리
  • 두 번째 파싱: ResultSet 데이터를 기반으로 User 객체 자동 생성 및 필드 자동 주입
JPA에서 Entity 클래스는 반드시 파라미터 없는 생성자가 있어야 한다.
Hibernate는 이 생성자를 통해 객체를 만든다 (리플렉션 기반이기 때문).

3. 조인(Join) 상황의 예외

  • 조인 결과는 하나의 테이블이 아니라 여러 테이블이 합쳐진 새로운 구조이다.
  • 이 구조는 기존 엔티티(User, Post 등)와 1:1로 매칭되지 않는다.
  • 그래서 JPA는 조인된 결과를 엔티티에 매핑해주지 않는다.
Entity 는 반드시 실제 존재하는 DB 테이블과 필드가 정확히 일치해야 하며, 조인된 결과처럼 여러 테이블이 섞인 구조는 절대 엔티티로 만들 수 없다. 그런 구조는 DTO를 써서 직접 Object Mapping(OM) 해야 한다.
  • 조인 상황에서는 직접 파싱해서 원하는 DTO로 만들어야 한다. 이를 Object Mapping(OM) 이라고 부른다.
public class UserPostDTO { private String username; private String postTitle; }
  • 이 DTO는 DB 테이블이 아니라 조인 결과를 담기 위한 커스텀 객체다.
  • 엔티티(Entity) 는 반드시 DB 테이블과 구조가 1:1이어야 하고, 조인 결과를 담는 DTO는 엔티티가 아니다.
OM은 DB와 상관없이 객체끼리 데이터를 주고받는 구조, 단순 복사/변환 역할만 함 (DB는 신경 안 씀)
ORM은 객체를 DB에 저장하거나 불러오도록 자동화시켜주는 기술이다.
스프링에서는 ModelMapper, MapStruct 등이 OM 도구고, JPA, Hibernate는 ORM 도구다.

4. 핵심 요약

구분
설명
JDBC
쿼리 + 파싱 모두 수작업
JPA
쿼리 + 엔티티 매핑 자동
조인 결과
JPA가 매핑 못함, 직접 DTO로 매핑
OM (Object Mapping)
조인 결과를 DTO로 파싱하는 작업
ORM
JPA 포함 개념. 객체와 관계형 DB 매핑 전체를 포괄함

5. 실습 예제

Object[]를 수동으로 Entity로 매핑하는 방법 (findAll, findById) Board.class를 사용한 자동 매핑 방식 (findAllV2, findByIdV2)

Board.java - Entity 클래스

notion image
💬
항목
설명
@Entity
이 클래스는 JPA가 관리하는 객체, 즉 DB 테이블과 매핑되는 클래스
@Table(name = "board_tb")
실제 매핑될 테이블명 지정
@Id, @GeneratedValue
PK이고, 자동 증가 전략(IDENTITY) 사용
생성자 오버로딩
기본 생성자 + 필드를 직접 세팅하는 생성자 (DTO 역할도 가능)
@Getter
Lombok으로 모든 필드의 Getter 자동 생성

BoardRepository.java – 직접 EntityManager를 이용한 DB 접근

notion image
  • EntityManager를 직접 주입 받아 JPQL / NativeQuery로 SQL 작성
  • @Repository: 스프링 컴포넌트 스캔 대상 + 예외 변환 기능 자동 적용됨
  • @RequiredArgsConstructor: final 필드인 em을 자동 생성자로 주입받음

메서드 분석 _ findAll() → 직접 매핑 방식

notion image
  • ativeQuery: 직접 SQL 사용
  • 리턴 결과는 List<Object[]> 형태
  • Object[]의 순서를 정확히 알아야 함 → 매우 불편하고 타입 안정성 낮음
수작업으로 엔티티 객체로 수동 매핑

메서드 분석 _ findAllV2() → 자동 매핑 방식

notion image
  • 두 번째 파라미터로 Board.class 전달 시, JPA가 자동으로 매핑
  • 컬럼 이름 ↔ 필드 이름이 정확히 일치해야 함 (alias 주의)
  • 깔끔하고 실무에서 더 선호됨

6. Spring Data JPA 환경에서 직접 작성한 Repository 메서드들을 검증 실습

notion image
  • @DataJpaTest는 JPA 관련 컴포넌트만 로딩해서 테스트. 트랜잭션 자동 롤백됨 (DB 깨끗하게 유지), 기본적으로 Spring Data JPA 인터페이스 기반 리포지토리만 자동 등록
  • 네가 만든 EntityManager 기반 커스텀 Repository는 수동 등록해야 한다 → @Import 필요
notion image
  • 직접 SQL + Object[]수동 매핑 방식
  • 데이터가 잘 매핑되어 있는지 System.out.println으로 확인
검증 포인트
  • 결과가 List<Object[]>였던 것을 잘 가공해 Board 객체로 바뀌었는가?
  • null, 캐스팅 오류가 없는가?
notion image
  • Object[] 기반 수동 매핑 방식
  • 위와 동일하게 id 존재 여부에 따라 분기 처리
검증 포인트
  • null 체크 + 수동 매핑의 정확성
  • Object[]의 순서가 쿼리 컬럼 순서와 일치해야 한다는 점
이 테스트는 JPA 기반으로 직접 SQL을 작성하고 결과를 수동 또는 자동으로 매핑하는 방식에 대한 확실한 감 잡기 실습이며, JDBC와의 비교 이해, Spring Data JPA로 나아가기 위한 기반 지식이 모두 포함되어 있다.
Share article

Gyeongwon's blog