본문 바로가기

Heute lerne ich/Java

[스프링 부트 게시판 프로젝트] 게시글 작성

01. 게시판 프로젝트 소개

# 게시판 주요 기능

1. 글 쓰기(/board/save)

2. 글 목록 (/board/)

3. 글 조회(/board/{id})

4. 글 수정(/board/update/{id})

5. 글 삭제(/board/delete/{id})

6. 페이징 처리(/board/paging)


02. application.yml 세팅

- dependencies

 

- application.yml

# 서버 포트 설정
server:
  port: 8092

# database 연동 설정
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/test?serverTimeZone=Asia/Seoul&characterEncoding=UTF-8
    username: root
    password: 1234
  thymeleaf:
    cache: false

# spring data jpa 설정
  jpa:
    database-platform: org.hibernate.dialect.MySQLDialect
    open-in-view: false
    show-sql: true
    hibernate:
      ddl-auto: create

 

전의 회원프로젝트와 같이 application.yml을 설정해준다.

포트 번호를 8092로 설정해두었기 때문에 localhost:8092로 접속한다.

 

ddl-auto 는 한 번 실행 후 create에서 update로 변경한다.


03. 게시글 작성 : (1) 작성 페이지 이동하기

- index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>index</title>
</head>
<body>
    <button onclick="saveReq()">글 작성</button>
</body>
<script>
    const saveReq=() => {
        location.href="/board/save"
    }
</script>
</html>

 

index 페이지에서 "글 작성" 버튼을 생성한다.

글 작성 버튼을 클릭하면 /board/save로 url이 이동한다.

 

function saveReq() { }const saveReq = () => { } 는 같은 역할을 한다. (함수 호출)

const ~ 는 ES6 방식의 함수 호출이며, 자바 스크립트 최신 문법으로 자주 쓰인다.

 

 

-BoardController

@Controller
@RequiredArgsConstructor
@RequestMapping("/board")
public class BoardController {
    private final BoardService boardService;

    @GetMapping("/save")
    public String saveForm(){
        return "save";
    }

 

/board/save를 get해올 때, save.html을 반환한다.

이때 @GetMapping("/board/save")를 사용해도 되지만, ("/board")를 먼저 붙이고, 메소드 마다 다른 url을 합칠 수 있다.

 

위의 코드처럼 @RequestMapping("/board")를 사용하고, 하단 GetMapping("/save")를 사용하면, 해당 url은 /board/save가 된다.

또 다른 Mapping을 BoardController에 추가하여도, 앞에 "/board"가 붙게 된다.

 

 

- save.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Save</title>
</head>
<body>
<!-- action속성: 목적지(서버주소), method속성: http request method(get, post) -->
<form action="/board/save" method="post" enctype="multipart/form-data">
    writer: <input type="text" name="boardWriter"> <br>
    pass: <input type="text" name="boardPass"> <br>
    title: <input type="text" name="boardTitle"> <br>
    contents: <textarea name="boardContents" cols="30" rows="10"></textarea> <br>
    <input type="submit" value="글작성">
</form>
</body>
</html>

 

작성자, 비밀번호, 제목, 내용과 제출 버튼이 있는 페이지를 작성한다.

내용은 textarea로 선언하여 input type="text"보다 넓은 칸을 쓸 수 있게 만든다.


03. 게시글 작성 : (2) 게시글 작성 완료

- BoardController

@Controller
@RequiredArgsConstructor
@RequestMapping("/board")
public class BoardController {
    private final BoardService boardService;

    @GetMapping("/save")
    public String saveForm(){
        return "save";
    }

    @PostMapping("/save")
    // 같은 주소, 다른 method 는 가능하지만 같은 주소 같은 method 는 불가능
    public String save(@ModelAttribute BoardDTO boardDTO){
        System.out.println("boardDTO = " + boardDTO);
        boardService.save(boardDTO);
        return "index";
    }
}

 

BoardController에 PostMapping을 추가한다.

이때 @GetMapping("/save")와 url이 동일하다.

이처럼 같은 주소, 다른 method는 가능하지만, 같은 주소, 같은 method는 Ambiguous mapping 에러가 발생한다.

 

/board/save에 post요청이 들어오면 boardService.save(boardDTO)를 호출한다.

 

 

- BoardDTO

// DTO(Data Transfer Object), VO, Bean <----> (다른 목적) Entity
@Getter
@Setter
@ToString
@NoArgsConstructor // 기본 생성자
@AllArgsConstructor // 모든 필드를 매개변수로 하는 생성자
public class BoardDTO {
    private Long id;
    private String boardWriter;
    private String boardPass;
    private String boardTitle;
    private String boardContents;
    private int boardHits;
    private LocalDateTime boardCreatedTime;
    private LocalDateTime boardUpdatedTime;
}

 

데이터 전송을 위한 객체, DTO를 만들어준다.

사용자가 입력할 boardWriter, boardPass, boardTitle, boardContents를 선언한다.

그리고 데이터베이스에 저장할 나머지 조회수, 생성 시간, 수정 시간 또한 선언한다.

 

 

-BoardEntity

// DB의 테이블 역할을 하는 클래스
@Entity
@Getter
@Setter
@Table(name = "board_table")
public class BoardEntity extends BaseEntity{
    @Id // pk 컬럼 지정. 필수
    @GeneratedValue(strategy = GenerationType.IDENTITY) // auto_increment
    private Long id;

    @Column(length = 20, nullable = false) // 크기 20, not null
    private String boardWriter;

    @Column // 크기 255, null 가능
    private String boardPass;

    @Column
    private String boardTitle;

    @Column(length = 500)
    private String boardContents;

    @Column
    private int boardHits;

    public static BoardEntity toSaveEntity(BoardDTO boardDTO){
        BoardEntity boardEntity = new BoardEntity();
        boardEntity.setBoardWriter(boardDTO.getBoardWriter());
        boardEntity.setBoardPass(boardDTO.getBoardPass());
        boardEntity.setBoardContents(boardDTO.getBoardContents());
        boardEntity.setBoardTitle(boardDTO.getBoardTitle());
        boardEntity.setBoardHits(0);
        return boardEntity;
    }
}

 

Entity를 작성한다. DB의 테이블 역할을 하는 클래스이다.

@Column 후 아무것도 쓰지 않으면 기본적으로 크기가 255, null이 가능한 컬럼이다.

@Column(length = 20, nullable = false)은 크기가 20, not null인 컬럼이다.

 

DTO를 받아 Entity로 반환하는 함수, toSaveEntity를 작성한다.

 

 

- BaseEntity

@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
@Getter
public class BaseEntity {
    @CreationTimestamp
    @Column(updatable = false)
    private LocalDateTime createdTime;

    @UpdateTimestamp
    @Column(insertable = false)
    private LocalDateTime updatedTime;
}

 

생성 시간과 수정 시간은 게시판 외에 다른 곳에서도 공통적으로 사용될 가능성이 있기 때문에

BaseEntity로 따로 선언해둔다.

BoardEntity에서 extends BaseEntity를 통해 상속받아 사용한다.

 

 

- BoardRepository

public interface BoardRepository extends JpaRepository<BoardEntity, Long> {
}

 

! 인터페이스로 작성한다.

JpaRepository<접근할 Entity, pk컬럼의 타입>을 상속받는다.

 

 

- BoardService

// DTO -> Entity (Entity Class)
// Entity -> DTO (DTO Class)


@Service
@RequiredArgsConstructor
public class BoardService {
    private final BoardRepository boardRepository;
    public void save(BoardDTO boardDTO) {
        BoardEntity boardEntity = BoardEntity.toSaveEntity(boardDTO);
        boardRepository.save(boardEntity);
    }
}

 

boardRespostiory를 사용하기 위해 @RequiredArgsConstructor을 추가한다.

 

BoardEntity에서 선언했던 toSaveEntity를 사용하여, DTO를 Entity로 변경한다.

변경된 Entity를 Repository에 저장(save)한다.

(Repository.save를 사용하기 위해서는 Entity객체가 들어가야 하기 때문)

 

보통 DTO -> Entity 변환은 Entity Class에서 정의하고,

그 반대의 경우 DTO Class에서 정의한다.

 

 

- 결과

글 작성을 클릭하면

 

데이터 베이스에 created_time과 더불어 id, writer, contents, pass, title이 저장된 것을 볼 수 있다.

 


 

해당 글은 코딩레시피님의 스프링 부트 쉽게 해보기 : 게시판 프로젝트 영상을 보고 공부한 글입니다.

 

https://youtu.be/YshcPPHClR4?si=IeT6iSwHJGLs_jgL