유정잉

🎀 스프링 부트 [ 게시판 만들기 ] 본문

개발자 공부/🎀 스프링 공부

🎀 스프링 부트 [ 게시판 만들기 ]

유정♡ 2024. 7. 10. 12:24

[ CRUD 게시판 만들기 ]

   • 목록 화면(번호 1) - 전체 목록을 페이징 처리해서 조회할 수 있고, 제목/내용/작성자 항목으로 검색 과 페이징 처리를 가능하게 합니다.
   • 등록 화면(번호 2) - 새로운 글을 등록할 수 있고, 등록 처리 후 다시 목록 화면으로 이동하게 됩니다.
   • 조회 화면(번호 3) - 목록 화면에서 특정한 글을 선택하면 자동으로 조회 화면으로 이동합니다.

     조회 화면에서는 수정/삭제가 가능한 화면(번호 4)으로 버튼을 클릭해서 이동할 수 있습니다.
   • 수정/삭제 화면(번호 4)- 수정 화면에서 삭제가 가능하고 삭제 후에는 목록 페이지로 이동합니다.

      글을 수정하는 경우에는 다시 조회 화면(번호 2)으로 이동해서 수정된 내용을 확인할 수 있습니다.

기능 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

 

 


1) controller

package org.zerock.guestbook.controller;

import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/guestbook")
@Log4j2
public class GuestbookController {

    @GetMapping({"/","/list"})
    public String list() {
        log.info("list..............");
        return "/guestbook/list";
    }
}

 

2) list.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">

<th:block th:replace="~{/layout/basic :: setContent(~{this :: content} )}">
    <th:block th:fragment="content">
        <h1>GuestBook List Page</h1>
    </th:block>
</th:block>

 

3) BaseEntity  ->  자동으로 처리 되는 날짜/시간 설정 

@MappedSuperclass : 해당 어노테이션이 적용된 클래스는 테이블로 생성되지 않음. 실제 테이블은 BaseEntity를 상속한 엔티티의 클래스로 데이터베이스 테이블이 생성됨

@CreatedDate : JPA에서 엔티티 생성 시간 처리

@LastModifiedDate : 최종 수정 시간을 자동으로 처리. 속성으로 insertable, updateable 등 존재 updateable=false로 지정하면 해당 엔티티 객체를 데이터베이스에 반영할 때 regdate 칼럼값은 변경되지 않는다.

package org.zerock.guestbook.entity;

import jakarta.persistence.Column;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.MappedSuperclass;
import lombok.Getter;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import java.time.LocalDateTime;

@MappedSuperclass
@EntityListeners(value = { AuditingEntityListener.class })
@Getter
public class BaseEntity {

    @CreatedDate
    @Column(name = "regdate", updatable = false)
    private LocalDateTime regDate;

    @LastModifiedDate
    @Column(name ="moddate")
    private LocalDateTime modDate;

}

 

4) GuestbookApplication -> AuditingEntityListener을 활성화 시키기 위해 @EnableJpaAuditing 설정 추가 

package org.zerock.guestbook;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

@SpringBootApplication
@EnableJpaAuditing
public class GuestbookApplication {

    public static void main(String[] args) {
        SpringApplication.run(GuestbookApplication.class, args);
    }

}

 

5) Guestbook  ->  BaseEntity를 상속받아 작성

package org.zerock.guestbook.entity;

import jakarta.persistence.*;
import lombok.*;

@Entity
@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
@ToString
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;
}

 

6) 서버 실행 -> 테이블 생성 된 것 확인 

 

7) GuestbookRepository Interface 생성 -> extends JpaRepository

package org.zerock.guestbook.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import org.zerock.guestbook.entity.Guestbook;

public interface GuestbookRepository extends JpaRepository<Guestbook, Long> {
}

 

8) 동적 쿼리 처리를 위한 Querydsl 설정

    build.gradle에 buildscript 항목 추가, plugins 항목 변경, dependencies 항목에 필요한 라이브러리 추가, 기타 추가 

buildscript {
    ext {
        queryDslVersion = "5.0.0"
    }
}

plugins {
    id 'base'
    id 'java'
    id 'war'
    id 'org.springframework.boot' version '3.3.1'
    id 'io.spring.dependency-management' version '1.1.5'
}

group = 'org.zerock'
version = '0.0.1-SNAPSHOT'

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(17)
    }
}

configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
    implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'mysql:mysql-connector-java:8.0.32'  // MySQL 의존성 추가 및 버전 지정
    compileOnly 'org.projectlombok:lombok'
    developmentOnly 'org.springframework.boot:spring-boot-devtools'
    annotationProcessor 'org.projectlombok:lombok'
    runtimeOnly 'org.springframework.boot:spring-boot-starter-tomcat'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'

    implementation "com.querydsl:querydsl-jpa:${queryDslVersion}"

    annotationProcessor(
            "javax.persistence:javax.persistence-api:2.2",
            "javax.annotation:javax.annotation-api:1.3.2",
            "com.querydsl:querydsl-apt:${queryDslVersion}:jpa"
    )
    testCompileOnly 'org.projectlombok:lombok'
    testAnnotationProcessor 'org.projectlombok:lombok'
}

sourceSets {
    main {
        java {
            srcDirs = ["$projectDir/src/main/java","$projectDir/build/generated"]
        }
    }
}

compileJava.dependsOn('clean')

tasks.named('test') {
    useJUnitPlatform()
}

 

9) 엔티티 테스트 -> 300개의 테스트 데이터 집어 넣기

    -> 생성된 데이터의 regdate(생성시간), moddate(수정시간)이 자동으로 null이 아닌 값으로 채워지는 것 확인

package org.zerock.guestbook.repository;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.zerock.guestbook.entity.Guestbook;

import java.util.stream.IntStream;

@SpringBootTest
public class GuestbookRepositoryTests {

    @Autowired
    private GuestbookRepository guestbookRepository;

    @Test
    public void insertDummies() {
        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));
        });
    }
}

 

 

10) 수정 시간 테스트 -> 엔티티 글래스는 가능하면 setter 관련 기능을 만들지 않는 것이 권장 사항이지만 필요에 따라서는 수정 기능을 만들기도 한다. 

Guestbook 엔티티에 수정 기능 추가 -> Test에 수정 코드 추가 -> DB에 moddate 칼럼 값 변경됐는지 확인 

public void changeTitle(String title){
    this.title = title;
}

public void changeContent(String content){
    this.content = content;
}
@Test
public void updateTest() {
    Optional<Guestbook> result = guestbookRepository.findById(300L); //존재하는 번호로 테스트

    if(result.isPresent()) {
        Guestbook guestbook = result.get();

        guestbook.changeTitle("Changed Title ...");
        guestbook.changeContent("Changed Content ...");

        guestbookRepository.save(guestbook);
    }
}

 

11) Querysl Test 작업

      - 제목/내용/작성자와 같이 단 하나의 항목으로 검색하는 경우

      - 제목 + 내용, 내용 + 작성자, 제목 + 작성자와 같이 2개 항목으로 검색하는 경우

      - 제목 + 내용 + 작성자와 같이 3개의 항목으로 검색하는 경우 

 

 


 

 

 

728x90