<< 학습 목표 >>

1. JPA와 ORM 프레임워크에 대해서 설명할 수 있다.

2. JPA를 사용했을 때와 사용하지 않았을 때를 비교해 설명할 수 있다.


Servlet을 배우며 JDBC 를 사용해 DB와 통신을 했을 때 처음에는 신기하면서도 유용했지만 JDBC 를 사용하면 할수록 불편한 점들이 생겼음

JDBC에서 제일 불편했던 점은 DB와 통신하기 전 항상 Connection을 연결해야하고 또 DB와 통신이 끝난 후에는 DB 통신과 관련된 자원(Connection, PreparedStatement, Result 등)을 모두 해제해야했었음

Connection을 연결하지 않으면 쿼리를 실행시킬 수 없고 DB 통신과 관련된 자원을 해제하지 않으면 어느 순간 연결 가능한 Connection 수가 한계에 도달해 새로운 Connection을 연결할 수 없게 됨


그 후 SpringFramework, Spring Boot를 배우며 MyBatis를 접했을 때 굉장히 유용하다 느꼈음

MyBatis는 Connection 연결, DB 통신과 관련된 자원 해제를 개발자가 신경쓰지 않아도 됐었고 트랜잭션 또한 애너테이션으로 간단하게 처리할 수 있었음

 

그러나 역시 MyBatis를 사용하면 할수록 불편한 점들이 생겼음

회원 가입 API의 경우 컨트롤러가 클라이언트의 요청을 받으며 클라이언트가 보낸 가입할 회원의 정보를 객체에 저장함

그 후 서비스, DAO를 통해 INSERT 쿼리를 실행시켜 회원 가입을 하는데 이때 객체에 저장된 가입할 회원의 정보를 일일히 다시 꺼내야했었음

이때 오타가 있으면 안됌

SELECT, UPDATE, DELETE 또한 마찬가지며 프로젝트 내 INSERT, UPDATE, DELETE 쿼리가 한 두개가 아니기 때문에 굉장히 불편하고 반복적인 패턴의 코드를 입력하는데 개발 시간의 상당 부분을 소모한 경험이 있을 것

( 이 글을 읽는 JPA를 공부하는 분들 중 취업 준비생이라고 하더라도 MyBatis를 사용해 포트폴리오까진 다 만들어봤을 것 )


이렇게 JDBC, MyBatis에서 불편했던 부분들을 상당 부분 메워줄 수 있는 것이 ORM(Object Relational Mapping) 프레임워크임

JPA는 런타임 시점에 자동으로 SQL을 만들어줌

JPA를 사용하면 개발자는 불편하고 반복적인 패턴의 SQL을 고민할 필요가 없고 작성할 필요도 없음

단지, 필요한 SQL을 생각하고 그에 맞는 JPA 메서드만 호출하면 됨

 

특히, JPA는 데이터를 조회할 때 JOIN 을 대부분 자동으로 처리해주므로 개발자가 SELECT 결과를 매핑하기 위해 고생할 필요가 없어짐

 

JDBC, MyBatis를 사용해 개발하다 보면 프로젝트가 SQL을 중심으로 개발되기 때문에 객체를 중심으로 생각하지 못하고 데이터를 중심으로 생각하게 됨

자바의 클래스를 들어갈 때 어디서든 항상 하는 말을 떠올려보면 JDBC, MyBatis를 사용해 개발하는 방식(데이터를 중심으로 생각하는 개발 방식)이 잘못됐다는 생각이 들 것

 

JPA를 사용해 개발하면 개발자가 SQL을 작성할 필요가 없어지므로 자연스럽게 객체를 중심으로 생각하게 됨


자바의 ORM 프레임워크 표준은 JPA임

Spring Framework 프로젝트도 자바로 이뤄져있기 때문에 SpringFramework 프로젝트에서 JPA를 사용할 수 있음

또한 전자정부프레임워크(eGovFramework)는 Spring Framework를 개량한 프레임워크이기 때문에 역시나 전자정부프레임워크에서도 JPA를 사용할 수 있음


위에서 언급한 JPA를 사용하지 않았을 때 불편한 점을 코드로 알아보자

JPA를 사용하지 않았을 때와 JPA를 사용했을 때를 비교해보면 "JPA가 그래서 필요하구나" 하고 느낄 수 있을 것

 

JPA를 사용해야하는 가장 큰 이유는 패러다임의 불일치

이제 볼 코드는 입력하지 않아도 됨

 

아래와 같은 회원 가입 컨트롤러가 있음

( 최대한 핵심만 보기 위해서 중간에 필요하지만 작은 부분들은 뺐음 )

package com.example.demo.chapter01;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class Sample {
	public static List<MemberDto> memberList = new ArrayList<>();
	
	@PostMapping("/chapter01/member")
	public ResponseEntity<Void> signup(MemberDto newMemberInfo) throws URISyntaxException {
		// ...
		
		newMemberInfo.setIdx(memberList.size()+1);
		
		memberList.add(newMemberInfo);
		
		// ...
		
		URI searchingURI = new URI("/chapter01/member/"+newMemberInfo.getIdx());
		
		return ResponseEntity.created(searchingURI).build();
	}
}

<< 코드 설명 >>

(1). 회원 정보를 저장하기 위한 리스트

(2). 회원 정보 저장 ( 회원 가입 )

 

클라이언트가 보낸 회원 정보를 signup 컨트롤러가 받아 자바의 List 에 저장하고 있음

컨트롤러도 자바고 List도 자바이므로 패러다임의 불일치가 일어날 일이 없음

그래서 그대로 저장할 수 있음

 

그러나 이와 같이 List 등에 저장하면 회원 정보가 사라질 위험이 있으므로 보통은 DB에 저장함

 

 

이번에는 DB에 저장하기 위해 아래와 같은 컨트롤러, DAO, MyBatis 매퍼가 있음

( 최대한 핵심만 보기 위해서 중간에 필요하지만 작은 부분들은 뺐음 )

 

<< 컨트롤러 >>

package com.study;

import java.net.URI;
import java.net.URISyntaxException;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class Sample {
	@Autowired
	private MemberDao memberDao;
	
	@PostMapping("/member")
	public ResponseEntity<Void> signup(MemberDto newMemberInfo) throws URISyntaxException {
		// ...
		
		memberDao.insertMember(newMemberInfo);
		
		// ...
		
		URI searchingURI = new URI("/member/"+newMemberInfo.getIdx());
		
		return ResponseEntity.created(searchingURI).build();
	}
}

 

<< DAO >>

package com.study;

import org.springframework.stereotype.Repository;

@Repository
public interface MemberDao {
	public void insertMember(MemberDto newMemberInfo);
}

 

<< MyBatis 매퍼 >>

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.study.MemberDao">
	<insert id="insertMember" parameterType="com.study.MemberDto" useGeneratedKeys="true" keyProperty="idx">
		INSERT INTO member(id, pw, nickname) VALUES(#{id}, #{pw}, #{nickname});
	</insert>
</mapper>

 

<< 코드 설명 >>

(1). DB에 저장하기 위해 DAO를 불러옴

(2). DAO의 메서드를 호출해 매퍼 파일의 INSERT 쿼리가 동작해 회원 가입이 되도록 했음

 

(1). 전달 받은 회원 정보를 저장하기 위해 멤버 변수명을 일일이 나열 했음

 

Member.xml 매퍼 파일에서 이와 같이 멤버 변수명을 일일이 나열하는 이유는 ?

자바의 객체가 데이터를 저장하는 구조와 테이블이 데이터를 저장하는 구조가 다르므로

이렇게 구조가 다른 두 대상을 패러다임이 일치하지 않는다 라고 표현함

 

위 INSERT 쿼리처럼 자바와 DB의 패러다임이 일치하지 않으므로 개발자가 직접 매퍼 파일에 패러다임을 일치시켜주는  과정을 매핑(Mapping) 이라고 함


매핑 작업이 아무렇지 않을 수 있지만 잘 생각해보면 위처럼 INSERT 쿼리문을 굉장히 많이 작성한 경험이 있을 것임

이때 오타가 있으면 매핑이 제대로 이뤄지지 않아 SQLException이 발생하니 매번 오타를 내지 않기 위해 신경을 써야함

또한 INSERT 쿼리만 매핑 시켜주면 문제가 없겠지만 SELECT, UPDATE, DELETE 쿼리 마다 매핑을 해야하므로 엄청나게 많이, 반복적으로 매핑을 해야했음

 

개발자가 컨트롤러, 서비스를 만들기도 벅찬대 매핑도 신경써야하니 개발 피로도가 굉장히 큼

컨트롤러, 서비스를 대신 만들어주는 기능이 있으면 좋겠지만 아쉽게도 그런 기능은 없고 대신 매핑을 대신해주는 기능이 있음

 

매핑을 대신해주는 기능에 대한 명세가 JPA임

명세라는 말 대신 스펙(Spec)이란 말을 더 많이 사용함

좀 더 쉽게 풀어서 얘기하면 JPA는 "매핑을 대신해주는 기능은 이런 것들을 구현해야한다" 라고 정의 해놓은 것임

 

JPA 스펙을 구현한 것이 하이버네이트(Hibernate), 이클립스링크(EclipseLink), DataNucleus 등임

이 하이버네이트, 이클립스링크, DataNucelus 를 또 다른 말로 ORM 프레임워크라고 표현함 또는 JPA프로바이더(Provider) 라고 표현함

 

JPA(Java Persistence API)를 구현한 ORM 프레임워크를 사용하면 개발자는 INSERT, UPDATE, DELETE, SELECT 쿼리를 작성하기 위해 고민할 필요가 없음

예를 들어 ORM 프레임워크를 사용하면 DB에 데이터를 저장할 때는 ORM 프레임워크가 제공하는 persist 메서드를 호출하면 됨

또한 DB에 저장된 데이터를 조회 할 때는 ORM 프레임워크가 제공하는 find, findAll 등 메서드를 호출하면 됨


우리는 ORM 프레임워크로 하이버네이트 / 6.1 버전을 사용할 것임

 

JPA를 사용해야하는 이유는 지금까지 설명한 이유 말고도 더 있지만 이정도만해도 "JPA를 배워야겠다" 라는 생각이 들 것

728x90
LIST

'JPA > Chaper01' 카테고리의 다른 글

Chapter01 - JPA / 참고한 도서  (0) 2023.06.04