코드로 배우는 스프링 부트 웹 프로젝트 - 남가람북스
코드로 배우는 스프링부트 웹 프로젝트 - 남가람북스 https://www.namgarambooks.co.kr/27 11. 코드로 배우는 스프링 웹 프로젝트 [개정판]_소스 코드 [소스 코드 링크 주소] 업데이트 된 소스 코드는 아래
soohykeee.tistory.com
기능 | URL | GET/POST | 기능 | Redirect URL |
목록 | /guestbook/list | GET | 목록/페이징/검색 | |
등록 | /guestbook/register | GET | 입력 화면 | |
/guestbook/register | POST | 등록처리 | /guestbook/list | |
조회 | /guestbook/read | GET | 조회 화면 | |
수정 | /guestbook/modify | GET | 수정/삭제 가능 화면 | |
/guestbook/modify | POST | 수정 처리 | /guestbook/read | |
삭제 | /guestbook\/remove | POST | 삭제 처리 | /guestbook/list |
프로젝트의 기본 구조는 다음과 같다.
- 브라우저에서 들어오는 Request는 GuestbookController라는 객체로 처리한다.
- GuestbookController는 GuestbookService 타입을 주입받는 구조로 만들고, 이를 이용해서 원하는 작업을 처리한다.
- GuestbookRepository는 Spring Data JPA를 이용해서 구성하고, GuestbookServiceImpl Class에 주입해서 사용한다.
- 마지막 결과는 Thymeleaf를 이용해서 레이아웃 템플릿을 활용해서 처리한다.
- 브라우저에서 전달되는 Request는 GuestbookController에서 DTO의 형태로 처리가 된다.
- GuestbookRepository는 Entity 타입을 이용하므로 중간에 Service 계층에서는 DTO와 Entity의 변환을 처리한다.
build.gradle의 일부로 의존성에 Spring Data JPA, Lombok, Spring Web, Thymeleaf, Spring Boot Dev Tools, H2, Database를 추가해준다.
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
runtimeOnly 'com.h2database:h2'
annotationProcessor 'org.projectlombok:lombok'
providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
compile group: 'org.thymeleaf.extras', name: 'thymeleaf-extras-java8time'
}
application.yml에 데이터베이스관련 설정과 로그를 확인하기 위한 설정도 작성해준다.
server:
port: 8080
servlet:
context-path: /
encoding:
charset: UTF-8
enabled: true
force: true
spring:
datasource:
driver-class-name: org.h2.Driver
url: jdbc:h2:tcp://localhost/~/guestBookDemo
username: sa
password:
jpa:
properties:
hibernate:
format_sql: true
hibernate:
ddl-auto: none #create update none
naming:
physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
show-sql: true
엔티티를 처리할 때, 등록 수정과 같은 공통으로 추가되고 변경되는 컬럼을 빼서 BaseEntity로 생성한다.
엔티티 객체의 등록 시간과, 최종 수정 시간을 저장하게되는 BaseEntity 클래스를 추상클래스로 생성하고, 모든 Entity가 BaseEntity를 상속받도록 한다.
@MappedSuperclass 어노테이션을 사용하게되면, 해당 클래스는 테이블로 저장이 되지 않는다. 모든 Entity에서 BaseEntity를 상속받아 사용하게 할 것이므로, BaseEntity는 테이블로 저장이 되면 안된다. 따라서 해당 어노테이션을 사용해준다.
package com.example.guestbookdemo.entity;
import lombok.Getter;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import javax.persistence.Column;
import javax.persistence.EntityListeners;
import javax.persistence.MappedSuperclass;
import java.time.LocalDateTime;
@MappedSuperclass
@EntityListeners(value = {AuditingEntityListener.class})
@Getter
abstract class BaseEntity {
@CreatedDate
@Column(name = "regDate", updatable = false)
private LocalDateTime regDate;
@LastModifiedDate
@Column(name = "modDate")
private LocalDateTime modDate;
}
JPA를 이용하면서 AuditionEntityListener를 활성화시키기 위해서는 프로젝트에 @EnableJpaAuditinig 설정을 추가해줘야한다.
package com.example.guestbookdemo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
@EnableJpaAuditing
@SpringBootApplication
public class GuestBookDemoApplication {
public static void main(String[] args) {
SpringApplication.run(GuestBookDemoApplication.class, args);
}
}
controller하단에 GuestbookController와, repository하단에 GuestbookRepository를 생성해준다.
package com.example.guestbookdemo.entity;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.*;
@Entity
@NoArgsConstructor
@AllArgsConstructor
@Data
@Builder
public class Guestbook extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long gno;
@Column(length = 100, nullable = false)
private String title;
@Column(length = 1500, nullable = false)
private String content;
@Column(length = 50, nullable = false)
private String writer;
}
package com.example.guestbookdemo.repository;
import com.example.guestbookdemo.entity.Guestbook;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
public interface GuestbookRepository extends JpaRepository<Guestbook, Long>, QuerydslPredicateExecutor<Guestbook> {
}
GuestbookRepository의 경우 querydsl을 사용할것이기 때문에, 아직 querydsl 설정은 하지 않았지만 설정할 예정이니 미리 상속을 받아주었다.
여기까지하고 querydsl을 사용하기 위해 설정을 해주었다. 책에 있는 내용으로 설정시, 버전이 맞지않아 오류가 발생한다.
https://www.inflearn.com/questions/355723
위의 주소를 통해 querydsl 설정에 오류없이 해결하였다. 추가해준 부분에 주석을 통해 표시를 해두었다.
/*추가해준 부분*/
buildscript {
ext {
queryDslVersion = "5.0.0"
}
}
plugins {
id 'org.springframework.boot' version '2.7.5'
id 'io.spring.dependency-management' version '1.0.15.RELEASE'
id 'java'
id 'war'
id "com.ewerk.gradle.plugins.querydsl" version "1.0.10" /*추가해준 부분*/
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
runtimeOnly 'com.h2database:h2'
annotationProcessor 'org.projectlombok:lombok'
providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation "com.querydsl:querydsl-jpa:${queryDslVersion}" /*추가해준 부분*/
implementation "com.querydsl:querydsl-apt:${queryDslVersion}" /*추가해준 부분*/
implementation 'com.github.gavlyukovskiy:p6spy-spring-boot-starter:1.5.8'
}
tasks.named('test') {
useJUnitPlatform()
}
/*추가해준 부분*/
def querydslDir = "$buildDir/generated/querydsl"
querydsl {
jpa = true
querydslSourcesDir = querydslDir
}
sourceSets {
main.java.srcDir querydslDir
}
compileQuerydsl{
options.annotationProcessorPath = configurations.querydsl
}
configurations {
compileOnly {
extendsFrom annotationProcessor
}
querydsl.extendsFrom compileClasspath
}
해당 설정을 완료하고, compileQuerydsl을 실행하면 미리 querydslDir을 설정한 경로에 querydsl파일이 생성이 된다.
제대로 H2와 연결이 되었는지, Test코드를 통해 gusetbook에 데이터를 쌓아보겠다.
해당 디렉토리에 GuestbookRepositoryTests 자바 파일을 생성하고, 총 300개의 테스트파일을 저장하는 테스트 코드를 작성했다. 설정이 제대로 되었고, 코드가 정확하다면 데이터베이스에 데이터가 저장이 될것이다.
package com.example.guestbookdemo.repository;
import com.example.guestbookdemo.entity.Guestbook;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.stream.IntStream;
@SpringBootTest
public class GuestbookRepositoryTests {
@Autowired
private GuestbookRepository guestbookRepository;
@Test
public void insertGuestbook() {
IntStream.rangeClosed(1,300).forEach( i -> {
Guestbook guestbook = Guestbook.builder()
.title("Title..." + i)
.content("Content..." + i)
.writer("user" + (i % 10))
.build();
System.out.println(guestbookRepository.save(guestbook));
});
}
}
H2 데이터베이스에 접속해서 쿼리 조회한결과 정상적으로 저장이 된것을 확인할 수 있다. 또한 BaseEntity의 regdate, moddate 등록, 수정일을 저장하는 인스턴스에도 정상적으로 값이 저장된것을 확인할 수 있다.