안녕하세요. 개발자 Jindory입니다.
오늘은 Spring 서버 Start시 초기 데이터를 셋팅하는 방법에 대해서 작성해보고자 합니다.
# 글 작성 이유
김영한님의 '실전 스프링 부트와 JPA 활용 2'편을 보다가 API 테스트 시 기초 데이터를 셋팅하는 방법을 보고 정리해보고자 합니다. 저 또한 API 테스트 시 기초 데이터가 필요한 경우가 있는데, 테스트 할 때마다 SQL로 데이터를 입력하거나, API로 데이터를 생성하는 수고로움을 덜 수 있는 방법이라서 한번 정리해 보고자 합니다.
# 초기 데이터 생성 Component를 위한 설정
application.properties
Spring Boot 2.4부터 생긴 설정으로 현재 활성화된 프로필을 정의하기 위해서 설정하는 정보입니다.
spring.profiles.active에 자신이 원하는 프로필을 설정하며 test, dev, local, prod, uat 등 자신이 원하는 프로필을 작성하면 됩니다.
spring.profiles.active = dev
나머지 설정에 대해서 자세한 설명은 여기를 참고하시면 될것 같습니다.
# Database Setting
# MySQL 설정
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# DB Source URL
spring.datasource.url=jdbc:mysql://localhost:3306/sports?useSSL=false&useUnicode=true&serverTimezone=Asia/Seoul&allowPublicKeyRetrieval=true
# DB username
spring.datasource.username=
# DB password
spring.datasource.password=
# DB Connection Test
spring.datasource.connection-test-query=SELECT 1
# local DB setting
spring.profiles.active=local
# JPA Setting
# JPA 사용시 사용 DB를 mysql로 설정
spring.jpa.database = mysql
# JPA로 쿼리 생성시 MySQL로 생성되도록 설정
spring.jpa.database-platform = org.hibernate.dialect.MySQL5InnoDBDialect
# JPA DDL 고유기능 사용 유무
spring.jpa.generate-ddl = false
# true 설정시 JPA 쿼리문 확인 가능
spring.jpa.show-sql=true
# DDL(create, alter, drop) 정의시 DB의 고유 기능을 사용할 수 있다.
spring.jpa.hibernate.ddl-auto= create
# JPA의 구현체인 Hibernate가 동작하면서 발생한 SQL의 가독성을 높여준다.
spring.jpa.properties.hibernate.format_sql=true
# hibernate logging
logging.level.org.hibernate = info
# 데이터를 만들기 위한 Entity, Repository, Service, Controller 생성
계정을 생성하는 Entity, Repository, Service Controller를 만들어 보겠습니다.
Account Entity
Email, Password, DisplayName, age, privateYn 속성을 가진 Entity를 만듭니다.
package jpabook.jpashop.domain;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.PrePersist;
import javax.persistence.Table;
import lombok.Getter;
import lombok.NoArgsConstructor;
@Getter
@Entity
@Table(name="ACCOUNT")
@NoArgsConstructor
public class Account {
@Id
private String email;
@Column(name = "PASSWORD", nullable = false, length = 50)
private String password;
@Column(length = 20,columnDefinition =" varchar(255) default 'guest'")
private String displayName;
@Column(length = 3,columnDefinition ="integer default 15")
private int age;
@Column(length = 1,columnDefinition ="char(1) default 'N'")
private char privateYn;
@PrePersist
public void prePersist() {
// displayName이 null이면 default값으로 매핑 되도록 설정
if(this.displayName == null) {
this.displayName = "member";
}
else {
this.displayName = this.displayName;
}
// age이 0이면 default값으로 매핑 되도록 설정
if(this.age == 0) {
this.age = 999;
}
else {
this.age = this.age;
}
// privateYn이 '\u0000'이면 default값으로 매핑 되도록 설정
if(this.privateYn == '\u0000') {
this.privateYn = 'F';
}
else {
this.privateYn = this.privateYn;
}
}
public Account(String email,String pw,String displayName,int age,char privateYn) {
this.email = email;
this.pw = pw;
this.displayName = displayName;
this.age = age;
this.privateYn = privateYn;
}
public Account(String email,String pw,String displayName,int age) {
this.email = email;
this.pw = pw;
this.displayName = displayName;
this.age = age;
}
public Account(String email,String pw,String displayName) {
this.email = email;
this.pw = pw;
this.displayName = displayName;
}
public Account(String email,String pw) {
this.email = email;
this.pw = pw;
}
}
Account Repository
JpaRepository를 상속 받아서 AccountRepository를 생성한다.
package jpabook.jpashop.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import jpabook.jpashop.domain.Account;
public interface AccountRepository extends JpaRepository<Account,String> {
}
Account Service
기본적인 CRUD 기능이 제공되도록 Service를 구현합니다.
package jpabook.jpashop.service;
import java.util.List;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import jpabook.jpashop.domain.Account;
import jpabook.jpashop.repository.AccountRepository;
import lombok.RequiredArgsConstructor;
@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class AccountService {
private final AccountRepository accountRepository;
public Account searchAccount(String email){
return accountRepository.findById(email).orElse(null);
}
public List<Account> searchAccountAll(){
return accountRepository.findAll();
}
public Account createAccount(Account account) {
return accountRepository.save(account);
}
public Account updateAccount(Account account,String email) {
Account rtnEntity = accountRepository.findById(email).orElse(null);
rtnEntity.updateEntity(account);
return rtnEntity;
}
public String deleteAccount(String email) {
accountRepository.deleteById(email);
return email;
}
}
Account Controller
Service와 마찬가지로 CRUD 중심으로 구현하며, API 호출이 가능하도록 @RestController 어노테이션을 만듭니다.
package jpabook.jpashop.api;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import jpabook.jpashop.domain.Account;
import jpabook.jpashop.service.AccountService;
import lombok.RequiredArgsConstructor;
@RestController
@RequiredArgsConstructor
public class AccountController {
private final AccountService accountService;
@GetMapping("searchAccount")
public ResponseEntity<?> searchAccount(@RequestParam("email")String email){
return new ResponseEntity<>(accountService.searchAccount(null),HttpStatus.OK);
}
@GetMapping("searchAccountAll")
public ResponseEntity<?> searchAccountAll(){
return new ResponseEntity<>(accountService.searchAccountAll(),HttpStatus.OK);
}
@PostMapping("createAccount")
public ResponseEntity<?> createAccount(@RequestBody Account account){
return new ResponseEntity<>(accountService.createAccount(null),HttpStatus.OK);
}
@PutMapping("updateAccount")
public ResponseEntity<?> updateAccount(@RequestBody Account account,@RequestParam("email")String email){
return new ResponseEntity<>(accountService.updateAccount(account, email),HttpStatus.OK);
}
@DeleteMapping("deleteAccount")
public ResponseEntity<?> deleteAccount(@RequestParam String email){
return new ResponseEntity<>(accountService.deleteAccount(email),HttpStatus.OK);
}
}
# 초기 데이터를 생성하기 위한 Init Database 만들기
InitDb라는 class를 만들고 Spring에서 Component로 등록하고 local 서버가 실행될때만 초기 데이터를 생성해야하므로, @Profile정보를 설정합니다.(만일 이 정보를 설정하지 않으면, Test Code 실행시에도 초기데이터 생성이 실행되어 테스트 케이스가 다 실패할 수도 있다.)
또한 @PostContruct 어노테이션을 선언하여, 빈 생성시 특정 메서드가 실행될 수 있도록 등록합니다.
메서드에서 서버가 실행되었을때, 생성할 데이터를 만드는 소스코드를 작성하면 서버 시작시 초기 데이터가 셋팅이 됩니다.
package jpabook.jpashop;
import javax.annotation.PostConstruct;
import javax.persistence.EntityManager;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import jpabook.jpashop.domain.Account;
import jpabook.jpashop.domain.Address;
import lombok.RequiredArgsConstructor;
@Component
@Profile("local")
@RequiredArgsConstructor
public class InitDb {
private final InitService initService;
@PostConstruct
public void init() {
initService.initDb();
}
@Component
@Transactional
@RequiredArgsConstructor
static class InitService {
private final EntityManager em;
public void initDb() {
Account account1 = new Account("user1@gmail.com","qwer1234","member1",25,'Y');
Account account2 = new Account("user2@gmail.com","qwer1234","member2",38,'N');
Account account3 = new Account("user3@gmail.com","qwer1234","member3",17,'N');
Account account4 = new Account("user4@gmail.com","qwer1234","member4",49,'Y');
Account account5 = new Account("user5@gmail.com","qwer1234","member5",52,'N');
em.persist(account1);
em.persist(account2);
em.persist(account3);
em.persist(account4);
em.persist(account5);
}
}
Post맨으로 데이터를 호출하면 아래와 같이 초기 데이터가 생성되어 있음을 확인 할 수 있습니다.
이렇게 Spring 서버 Start시 초기 데이터를 셋팅하는 방법에 대해서 알아봤습니다.
혹시라도 정정할 내용이나 추가적으로 필요하신 정보가 있다면 댓글 남겨주시면 감사하겠습니다.
오늘도 Jindory 블로그에 방문해주셔서 감사합니다.
[참고]
'개발 > Spring' 카테고리의 다른 글
[Spring] Component Scan이란? (2) | 2024.05.27 |
---|---|
[Spring] Transaction Propagation Model과 Isolation Level (0) | 2024.02.11 |
[Spring] 의존성 주입의 정의 및 의존성 주입 3가지 방식 (생성자 주입, 수정자 주입, 필드 주입) (0) | 2022.04.30 |
[Spring Boot] Thymeleaf 사용하기 (0) | 2022.03.06 |
[Spring] @RequestParam과 @PathVariable의 차이는? (0) | 2022.02.20 |