어제 상품을 판매하는 애플리케이션을 만들기로 하고, myShop이라는 새 프로젝트를 생성했다.
Spring Boot Dev Tools, Spring Web, Thymeleaf, Lombok, Spring Data JPA, MySQL Driver, H2 Database 등의 Dependencies를 추가해 줬었고, 어제 Lombok에 대해서만 알아 보았다.
오늘은 본격적으로 애플리케이션을 구성해 보았다.
pom.xml 수정
스프링 부트 버전을 2.7.1로 낮추어야 해서 pom.xml을 조금 수정해주었다.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>myShop</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name/>
<description/>
<url/>
<licenses>
<license/>
</licenses>
<developers>
<developer/>
</developers>
<scm>
<connection/>
<developerConnection/>
<tag/>
<url/>
</scm>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.0.33</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
application.properties 설정
기본 생성된 resource 안 application.properties 파일에 설정 정보를 넣어준다.
#애플리케이션 포트 설정
server.port = 80
#MySQL 연결 설정
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/[스키마 이름]
spring.datasource.username=
spring.datasource.password=
#실행되는 쿼리 콘솔 출력
spring.jpa.properties.hibernate.show_sql=true
#콘솔창에 출력되는 쿼리를 가독성이 좋게 포맷팅
spring.jpa.properties.hibernate.format_sql=true
#쿼리에 물음표로 출력되는 바인드 파라미터 출력
logging.level.org.hibernate.type.descriptor.sql=trace
spring.jpa.hibernate.ddl-auto=update
spring.jpa.database-platform=org.hibernate.dialect.MySQL8Dialect
url, username, password는 본인 DB 서버에 맞는 정보를 넣으면 된다.
엔티티 설계
enum 클래스
constant 패키지를 만들고 SELL과 SOLD_OUT 타입을 가지는 enum 클래스를 정의해준다.
package com.example.myShop.constant;
public enum ItemSellStatus {
SELL, SOLD_OUT
}
상품 엔티티
entity 패키지를 만들고 Item 엔티티를 만든다.
package com.example.myShop.entity;
import com.example.myShop.constant.ItemSellStatus;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import javax.persistence.*;
import java.time.LocalDateTime;
@Entity
@Table(name="t_item")
@Getter
@Setter
@ToString
public class Item {
@Id
@Column(name="id")
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id; // 상품코드
@Column (nullable = false, length=50)
private String itemName; // 상품명
@Column (name="price", nullable=false)
private int price; // 가격
@Column (nullable = false)
private int stockNumber; // 재고수량
@Lob
@Column (nullable = false)
private String itemDetail; // 상품상세설명
@Enumerated(EnumType.STRING)
private ItemSellStatus itemSellStatus; // 상품판매상태
private LocalDateTime regTime; // 등록시간
private LocalDateTime updateTime; // 수정시간
}
애플리케이션을 실행하면 t_item 테이블 생성 확인이 가능하다.

Repository 설계
Repository 인터페이스
DAO 역할을 하는 Repository 인터페이스를 설계한다.
우선repository 패키지를 만들고 JpaRepository를 상속받는 ItemRepository 인터페이스를 생성하면 된다.
package com.example.myShop.repository;
import com.example.myShop.entity.Item;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
public interface ItemRepository extends JpaRepository<Item, Long> {
}
JpaRepository를 상속 받았기 때문에 기본적인 CRUD 기능을 사용할 수 있는 상태이다.
테스트 환경 설정
테스트 코드 작성을 위한 환경을 구성한다.
application-test.properties 파일을 만들어 테스트 전용 h2 DB 설정을 해준다.
# Datasource 설정
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.url=jdbc:h2:mem:test
spring.datasource.username=sa
spring.datasource.password=
# H2 데이터베이스 방언 설정
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
테스트 코드 작성
ItemRepository에 대한 Test 코드를 작성한다.
package com.example.myShop.repository;
// import 문 생략
@SpringBootTest
@TestPropertySource(locations = "classpath:application-test.properties")
class ItemRepositoryTest {
@Autowired
ItemRepository itemRepository;
@Test
@DisplayName("상품 저장 테스트")
public void createItemTest() {
Item item = new Item();
item.setItemName("테스트 상품");
item.setPrice(10000);
item.setItemDetail("테스트 상품 상세 설명");
item.setItemSellStatus(ItemSellStatus.SELL);
item.setStockNumber(100);
item.setRegTime(LocalDateTime.now());
item.setUpdateTime(LocalDateTime.now());
Item saveItem = itemRepository.save(item);
System.out.println(saveItem);
}
}
Spring Data JPA는 인터페이스만 작성하면 런타임 시점에 자바의 Dynamic Proxy를 이용해서 객체를 동적으로 생성 해준다.
그래서 itemRepository.save() 메서드를 사용할 수 있는 것.
테스트를 통과했다.

데이터 조회
데이터 조회 방식은 3가지가 있다.
- 쿼리 메서드
- 메서드 이름만으로 JPA가 자동으로 JPQL을 생성해주는 방식
- @Query
- JPQL 혹은 Native SQL을 직접 명시적으로 작성
- Querydsl
- 코드로 쿼리를 동적으로 생성할 수 있는 타입 안전한 DSL (도메인 전용 언어)
오늘은 쿼리 메서드, @Query 방식만 학습했다. 내일 이어서 Querydsl도 학습할 예정이다.
쿼리 메서드를 이용한 상품 조회
ItemRepository에 상품명으로 조회하는 쿼리 메서드를 작성한다.
package com.example.myShop.repository;
import com.example.myShop.entity.Item;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import java.util.List;
public interface ItemRepository extends JpaRepository<Item, Long> {
List<Item> findByItemName(String itemName);
}
그러면 파라미터로 들어간 itemName에 해당하는 Item 객체를 List 형태로 반환해준다.
메서드만 만들면 JPA가 알아서 쿼리 만들어서 실행하고 실행 결과를 반환해주는 것이다.
테스트 코드를 추가해 테스트 해본다.
package com.example.myShop.repository;
// import 생략
@SpringBootTest
@TestPropertySource(locations = "classpath:application-test.properties")
class ItemRepositoryTest {
@Autowired
ItemRepository itemRepository;
public void createItemList() {
for (int i = 1; i <= 5; i++) {
Item item = new Item();
item.setItemName("테스트 상품" + i);
item.setPrice(10000 + i);
item.setItemDetail("테스트 상품 상세 설명" + i);
item.setItemSellStatus(ItemSellStatus.SELL);
item.setStockNumber(100);
item.setRegTime(LocalDateTime.now());
item.setUpdateTime(LocalDateTime.now());
Item saveItem = itemRepository.save(item);
}
}
@Test
@DisplayName("상품명 조회 테스트")
public void findByItemNameTest() {
this.createItemList();
List<Item> itemList = itemRepository.findByItemName("테스트 상품1");
for (Item item : itemList) {
System.out.println(item);
}
}
}
테스트를 위해 5개의 상품을 저장하는 createItemList() 메서드를 만들어주고 상품명 조회 테스트를 하고 확인을 위해 println문으로 item을 콘솔에 찍어봤다.
테스트 코드를 실행하면 insert문 5개로 상품을 먼저 저장하고, select 문을 실행한다.

콘솔 아래쪽 내용을 보면 Item(id=1, itemName=테스트 상품1, price=10001, stockNumber=100, itemDetail=테스트 상품 상세 설명1, itemSellStatus=SELL, regTime=2026-04-01T09:31:01.193720, updateTime=2026-04-01T09:31:01.193720) 라고 뜨면서 println한 내용이 뜬다.
@Query
SQL과 유사한 JPQL 객체지향 쿼리 언어를 통해 복잡한 쿼리도 처리가 가능하다.
JPQL: 엔티티 객체를 대상으로 쿼리를 수행. 객체를 대상으로 검색하는 객체지향 쿼리.
package com.example.myShop.repository;
import com.example.myShop.entity.Item;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import java.util.List;
public interface ItemRepository extends JpaRepository<Item, Long> {
// 쿼리 메서드를 이용한 상품 조회
List<Item> findByItemName(String itemName);
// @Query를 이용한 상품 조회
// 객체 기준으로 조회하기 때문에 테이블 이름이 아닌 엔티티 이름(Item)으로 조회함
@Query("select i from Item i where i.itemDetail like " +
"%:itemDetail% order by i.price desc")
List<Item> findByItemDetail(@Param("itemDetail") String itemDetail);
// 기존 DB 쿼리 사용 시 nativeQuery 지정 (default는 false)
@Query(value = "select * from t_item i where i.item_detail like " +
"%:itemDetail% order by i.price desc", nativeQuery = true)
List<Item> findByItemDetailNative(@Param("itemDetail") String itemDetail);
}
이렇게 findByItemDetail(), findByItemDetailNative() 메서드를 생성해 주었다.
이제 테스트 코드를 작성한다.
package com.example.myShop.repository;
// import 생략
@SpringBootTest
@TestPropertySource(locations = "classpath:application-test.properties")
class ItemRepositoryTest {
@Autowired
ItemRepository itemRepository;
public void createItemList() {
for (int i = 1; i <= 5; i++) {
Item item = new Item();
item.setItemName("테스트 상품" + i);
item.setPrice(10000 + i);
item.setItemDetail("테스트 상품 상세 설명" + i);
item.setItemSellStatus(ItemSellStatus.SELL);
item.setStockNumber(100);
item.setRegTime(LocalDateTime.now());
item.setUpdateTime(LocalDateTime.now());
Item saveItem = itemRepository.save(item);
}
}
@Test
@DisplayName("@Query를 이용한 상품 조회 테스트")
public void findByItemDetailTest() {
this.createItemList();
List<Item> itemList = itemRepository.findByItemDetail("테스트 상품 상세 설명");
for (Item item : itemList) {
System.out.println(item);
}
}
@Test
@DisplayName("nativeQuery 속성을 이용한 상품 조회 테스트")
public void findByItemDetailByNative() {
this.createItemList();
List<Item> itemList = itemRepository.findByItemDetailNative("테스트 상품 상세 설명");
for (Item item : itemList) {
System.out.println(item);
}
}
}
테스트 실행 결과 성공했음을 알 수 있다.

❕느낀점
오늘부터 본격적으로 스프링 부트가 시작되는 느낌이다. 엔티티를 만들고 레포지토리도 만들어서 테스트를 통해 데이터 조회까지 해보니 재밌었다. 테스트를 위해 h2 DB를 사용했는데 이건 처음 사용해봤다. 자바 기반의 경량형 RDBMS라는 것 밖에는 정확한 정보는 모르겠다. 실습하면서 차차 알아가야겠다.
'Spring Boot' 카테고리의 다른 글
| [TIL-260330] Spring Boot 기초: 개요, 실습, Lombok (0) | 2026.03.31 |
|---|