1. 프로젝트 생성
- 스프링 부트 스타터(https://start.spring.io/)
- Project: Gradle - Groovy Project
- 사용 기능: web, thymeleaf, jpa, h2, lombok, validation
- groupId: jpabook
- artifactId: jpashop
롬복 적용
Lombok
: 반복적인 Java 코드를 간단한 어노테이션으로 자동 생성해주는 라이브러리
@Getter, @Setter, @Builder, @NoArgsConstructor 등 어노테이션만 붙이면 필수 메서드나 생성자 코드를 자동으로 만들어주기 때문에 코드가 깔끔해진다.
1. Settings > plugin > lombok 검색 실행 (재시작)
2. Settings > Annotation Processors 검색 > Enable annotation processing 체크
3. 임의의 테스트 클래스를 만들고 @Getter, @Setter 확인
package jpabook.jpashop;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class Hello {
private String data;
}
package jpabook.jpashop;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import javax.swing.*;
@SpringBootApplication
public class JpashopApplication {
public static void main(String[] args) {
SpringApplication.run(JpashopApplication.class, args);
Hello hello = new Hello();
hello.setData("Hello");
String data = hello.getData();
System.out.println("data = "+data);
SpringApplication.run(JpashopApplication.class, args);
}
}
동작 확인
- 기본 테스트 케이스 실행
- 스프링 부트 메인 실행 후 에러페이지로 간단하게 동작 확인(http://localhost:8080)
2. 라이브러리 확인
gradle 의존관계 보기
프로젝트 폴더 > ./gradlew dependencies —configuration compileClasspath
스프링 부트 라이브러리 알아보기
- spring-boot-starter-web
- spring-boot-starter-tomcat: 톰캣 (웹서버)
- spring-webmvc: 스프링 웹 MVC
- spring-boot-starter-thymeleaf: 타임리프 템플릿 엔진(View)
- spring-boot-starter-data-jpa
- spring-boot-starter-aop
- spring-boot-starter-jdbc
- HikariCP 커넥션 풀 (부트 2.0 기본)
- hibernate + JPA: 하이버네이트 + JPA
- spring-data-jpa: 스프링 데이터 JPA
- spring-boot-starter(공통): 스프링 부트 + 스프링 코어 + 로깅
- spring-boot
- spring-core
- spring-boot-starter-logging
- logback, slf4j
- spring-boot
테스트 라이브러리
- spring-boot-starter-test
- junit: 테스트 프레임워크
- mockito: 목 라이브러리
- assertj: 테스트 코드를 좀 더 편하게 작성하게 도와주는 라이브러리
- spring-test: 스프링 통합 테스트 지원
- 핵심 라이브러리
- 스프링 MVC
- 스프링 ORM
- JPA, 하이버네이트
- 스프링 데이터 JPA
- 기타 라이브러리
- H2 데이터베이스 클라이언트
- 커넥션 풀: 부트 기본은 HikariCP
- WEB(thymeleaf)
- 로깅 SLF4J & LogBack
스프링 데이터 JPA는 스프링과 JPA를 먼저 이해하고 사용해야 하는 응용기술이다.
3. View 환경 설정
thymeleaf 템플릿 엔진
- thymeleaf 공식 사이트: https://www.thymeleaf.org/
- 스프링 공식 튜토리얼: https://spring.io/guides/gs/serving-web-content/
- 스프링부트 메뉴얼: https://docs.spring.io/spring-boot/reference/web/
- servlet.html#web.servlet.spring-mvc.template-engines
- 스프링 부트 thymeleaf viewName 매핑
resources:templates/+{ViewName}+.html
package jpabook.jpashop;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class HelloController {
// Model: 스프링 UI, 컨트롤러에서 모델에 데이터를 실어서 뷰로 넘길 수 있다.
@GetMapping("hello")
public String Hello(Model model){
model.addAttribute("data", "hello!!!");
return "hello"; //return은 화면 이름을 리턴
}
}
- 스프링 부트 thymeleaf 에서는 ViewName을 자동으로 매핑해준다. (templates/+{ViewName}+.html)
- 그래서 return "hello"를 했을 때, templates/hello.html 화면으로 리턴되는 것.
Spring-boot-devtools
implementation 'org.springframework.boot:spring-boot-devtools'
spring-boot-devtools 라이브러리를 추가해주며, 'html'파일을 컴파일만 해주면 서버 재시작 없이 View 파일 변경 가능
Build > Recomplie 'XXX.html'
4. H2 데이터베이스 설치
spring.application.name=jpashop
spring.datasource.url=jdbc:h2:tcp://localhost/~/jpashop
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa
spring.h2.console.enabled=true
- application.properties에 위에 코드처럼 설정해두고,
- JDBC URL은 맨 처음에는 파일모드인 jdbc:h2:~/jpashop으로 연결해주고,
- 연결된 이후에는 네트워크 모드 tcp인 jdbc:h2:tcp://localhost/~/jpashop로 연결해주면 된다.
5. JPA와 DB 설정, 동작 확인
application.properties를 application.yml 파일로 변경하면 아래와 같이 작성하게 된다.
spring:
datasource:
url: jdbc:h2:tcp://localhost/~/jpashop
username: sa
password:
driver-class-name: org.h2.Driver
jpa:
hibernate:
ddl-auto: create
properties:
hibernate:
# show_sql: true
format_sql: true
logging:
level:
org.hibernate.SQL: debug
application.yml` 같은 `yml` 파일은 띄어쓰기(스페이스) 2칸으로 계층을 만든다. 따라서 띄어쓰기 2칸을 필수로 적어주어야 한다. 예를 들어서 아래의 `datasource` 는 `spring:` 하위에 있고 앞에 띄어쓰기 2칸이 있으므로`spring.datasource` 가 된다. 다음 코드에 주석으로 띄어쓰기를 적어두었다.
spring.jpa.hibernate.ddl-auto
create | 실행 시 기존 테이블 삭제 후 새로 생성. 초기 개발에 적합(⚠️ create는 테스트 용도로만 사용, 데이터가 모두 삭제된다.) |
update | 변경 사항만 반영 (데이터 유지) |
validate | 매핑 검증만 수행. 테이블은 변경하지 않음 |
none | 아무 작업도 수행하지 않음 |
- spring.jpa.hibernate.ddl-auto: create: 이 옵션은 애플리케이션 실행 시점에 테이블을 drop 하고, 다시 생성한다.
spring.jpa.properties.hibernate
- format_sql: true
→ SQL 문을 보기 좋게 들여쓰기해서 출력 - show_sql: true
→ SQL을 System.out에 직접 출력. 옵션은 `System.out` 에 하이버네이트 실행 SQL을 남긴다. 로그에 남기려면 아래의 로그 설정을 권장.
logging.level.org.hibernate.sql: debug
참고: 모든 로그 출력은 가급적 로거를 통해 남겨야 한다.
Hibernate가 실행하는 SQL 쿼리를 로그 파일이나 콘솔에 출력. 로그로 관리하기 위해 show_sql을 주석처리 한 것.
package jpabook.jpashop;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import lombok.Getter;
import lombok.Setter;
@Entity
@Getter @Setter
public class Member {
@Id
@GeneratedValue
private Long id;
private String username;
}
package jpabook.jpashop;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import org.springframework.stereotype.Repository;
@Repository
public class MemberRepository {
@PersistenceContext
private EntityManager em;
public Long save(Member member){
em.persist(member);
return member.getId();
}
public Member find(Long id){
return em.find(Member.class, id);
}
}
EntityManager란?
EntityManager는 JPA(Java Persistence API)에서 엔티티를 저장, 조회, 수정, 삭제하는 등의 기능을 제공하는 중앙 객체. 즉, SQL 없이 객체 중심으로 데이터베이스를 다룰 수 있게 해주는 JPA의 핵심 도구
JPA를 사용하기 위해서는 Database 구조와 맵핑된 JPA Entity 들을 먼저 생성하게 된다. 모든 JPA의 동작은 이 Entity들을 기준으로 돌아가게 되는데, 이 때 Entity들을 관리하는 역할을 하는것이 바로 EntityManager
package jpabook.jpashop;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.Rollback;
import org.springframework.transaction.annotation.Transactional;
import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest
public class MemberRepositoryTest {
@Autowired MemberRepository memberRepository;
@Test
@Transactional
@Rollback(value = false)
@DisplayName("test Member")
public void testMember() throws Exception {
// given
Member member = new Member();
member.setUsername("memberA");
// when
Long savedId = memberRepository.save(member);
Member findMember = memberRepository.find(savedId);
// then
Assertions.assertThat(findMember.getId()).isEqualTo(member.getId());
Assertions.assertThat(findMember.getUsername()).isEqualTo(member.getUsername());
Assertions.assertThat(findMember).isEqualTo(member);
}
}
영속성 컨텍스트란? 그리고 왜 findMember == member일까?
Spring Boot + JPA로 개발하다 보면 종종 다음과 같은 상황을 마주하게 된다.
Assertions.assertThat(findMember).isEqualTo(member); // true!
왜 save()한 객체와 find()로 다시 조회한 객체가 같을까?
영속성 컨텍스트란?
영속성 컨텍스트(Persistence Context)는 엔티티를 저장하는 JPA의 1차 캐시를 말한다.
즉, JPA가 관리하는 엔티티 객체들이 모여 있는 "저장소"
- EntityManager가 하나의 영속성 컨텍스트를 유지한다.
- 같은 트랜잭션 안에서는 같은 EntityManager가 사용되며,
- 같은 식별자를 가진 엔티티는 한 번만 DB에서 가져오고, 이후에는 캐시에서 반환한다.
동작 흐름
- @Transactional 덕분에 테스트는 하나의 트랜잭션 안에서 실행된다.
- memberRepository.save(member)를 통해 member 객체를 영속성 컨텍스트에 저장
- memberRepository.find(savedId)는 같은 트랜잭션 안에서 실행되므로, JPA는 먼저 영속성 컨텍스트에서 member를 찾고, 바로 반환
- 따라서 findMember == member → true
즉, 영속성 컨텍스트는 JPA가 관리하는 1차 캐시 공간으로, @Transactional 같은 트랜잭션일 경우 DB 대신 캐시에서 엔티티를 반환한다. JPA는 영속성 컨텍스트에서 member를 찾아 반환하기 때문에 find()로 조회한 객체와 save()한 객체가 동일 참조!
만약, @Transactional 없이 테스트를 실행할 경우 save()와 find()는 서로 다른 트랜잭션에서 작동하게 되기 때문에 member != findMember가 될 수 있다. 왜냐하면 캐시에서 반환하는 것이 아니라 DB에서 조회한 다른 인스턴스가 반환되기 때문.
그런데, 쿼리가 조회될 때 values로 어떤 값이 들어갔는지 (?, ?)로 알 수 없다.
해결하기 위해서는 로그 설정을 추가해줘야 한다.
쿼리 파라미터 로그 남기기
org.hibernate.orm.jdbc.bind: trace
application.yml 파일에 SQL 실행 파라미터를 로그로 남기게 설정한다.
implementation 'com.github.gavlyukovskiy:p6spy-spring-boot-starter:1.10.0'
스프링 부트를 사용하면 이 라이브러리만 추가하면 된다. (https://github.com/gavlyukovskiy/spring-boot-data-source-decorator)
쿼리 파라미터를 로그로 남기는 외부 라이브러리는 시스템 자원을 사용하므로, 개발 단계에서는 편하게 사용해도 된다. 하지만 운영시스템에 적용하려면 꼭 성능테스트를 하고 사용하는 것이 좋다.
이제 어떤 값이 들어갔는지 출력해준다 ~ ✈️ !
'Backend > 스프링 부트와 JPA 활용' 카테고리의 다른 글
[JPA] 2. 도메인 분석 설계 (0) | 2025.05.04 |
---|