[JPA] 1. 프로젝트 환경설정

2025. 5. 4. 01:30·BE/스프링부트와 JPA 활용
728x90

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);
	}
}

동작 확인

  1. 기본 테스트 케이스 실행
  2. 스프링 부트 메인 실행 후 에러페이지로 간단하게 동작 확인(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-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에서 가져오고, 이후에는 캐시에서 반환한다.

동작 흐름

  1. @Transactional 덕분에 테스트는 하나의 트랜잭션 안에서 실행된다.
  2. memberRepository.save(member)를 통해 member 객체를 영속성 컨텍스트에 저장
  3. memberRepository.find(savedId)는 같은 트랜잭션 안에서 실행되므로, JPA는 먼저 영속성 컨텍스트에서 member를 찾고, 바로 반환
  4. 따라서 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)

쿼리 파라미터를 로그로 남기는 외부 라이브러리는 시스템 자원을 사용하므로, 개발 단계에서는 편하게 사용해도 된다. 하지만 운영시스템에 적용하려면 꼭 성능테스트를 하고 사용하는 것이 좋다.

 

이제 어떤 값이 들어갔는지 출력해준다 ~ ✈️ !

 

728x90

'BE > 스프링부트와 JPA 활용' 카테고리의 다른 글

[JPA] 4. 회원 도메인 개발  (0) 2025.05.13
[JPA] 3. 애플리케이션 구현 준비  (0) 2025.05.12
[JPA] 2. 도메인 분석 설계 - 엔티티 설계시 주의점  (0) 2025.05.11
[JPA] @Embedded @Embedable 임베디드 타입이란?  (0) 2025.05.07
[JPA] 2. 도메인 분석 설계 - 요구사항 분석 / 도메인 모델과 테이블 설계 / 엔티티 클래스 개발  (0) 2025.05.04
'BE/스프링부트와 JPA 활용' 카테고리의 다른 글
  • [JPA] 3. 애플리케이션 구현 준비
  • [JPA] 2. 도메인 분석 설계 - 엔티티 설계시 주의점
  • [JPA] @Embedded @Embedable 임베디드 타입이란?
  • [JPA] 2. 도메인 분석 설계 - 요구사항 분석 / 도메인 모델과 테이블 설계 / 엔티티 클래스 개발
DROPDEW
DROPDEW
💻 Developer | 기록하지 않으면 존재하지 않는다
  • DROPDEW
    제 2장 1막
    DROPDEW
  • 전체
    오늘
    어제
    • Dev (417)
      • App·Android (1)
      • BE (44)
        • HTTP 웹 기본 지식 (8)
        • 스프링 입문 - 코드로 배우는 스프링 부트, 웹 .. (12)
        • 스프링부트와 JPA 활용 (11)
        • 스프링부트 시큐리티 & JWT (0)
        • PHP (6)
      • FE·Client (23)
        • HTML (1)
        • React (19)
        • Unity (1)
      • Data (17)
        • AI (7)
        • Bigdata (6)
        • Database (1)
        • 빅데이터분석기사 (2)
      • Infra (0)
      • Activity (0)
        • Education (0)
        • Intern (0)
        • 리모트 인턴십 6기 (0)
        • 구름톤 유니브 4기 (0)
        • SW교육기부단 15기 (0)
      • CS (8)
      • 취준 (13)
        • 자격증 (4)
        • 인적성·NCS (6)
        • 코테·필기·면접 후기 (3)
      • 코테 (270)
        • Algorithm (222)
        • SQL (35)
        • 정리 (13)
      • 인사이트 (27)
        • 회고 (0)
        • 금융경제뉴스 (7)
        • 금융용어·지식 (2)
        • 북마크 (7)
  • 블로그 메뉴

    • 홈
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    그래프탐색
    브루트포스 알고리즘
    오블완
    자료구조
    그리디알고리즘
    투포인터
    다이나믹프로그래밍
    티스토리챌린지
    수학
    그래프이론
    너비우선탐색
    이분탐색
    누적합
    시뮬레이션
    구현
    백준
    매개변수탐색
    최단경로
    문자열
    정렬
  • 최근 댓글

  • 최근 글

  • 250x250
  • hELLO· Designed By정상우.v4.10.3
DROPDEW
[JPA] 1. 프로젝트 환경설정
상단으로

티스토리툴바