BoardController에 PostMapping으로 게시글을 저장할 수 있게 추가한다.
[BoardController]
package test.SpringBootBoard.board.controller;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import test.SpringBootBoard.board.dto.BoardDTO;
import test.SpringBootBoard.board.service.BoardService;
import java.io.IOException;
@Controller
@RequiredArgsConstructor
@RequestMapping("/board")
public class BoardController {
private final BoardService boardService;
@GetMapping("/save")
public String saveForm(){
return "save";
}
@PostMapping("/save")
public String save(@ModelAttribute BoardDTO boardDTO) throws IOException {
System.out.println("boardDTO = " + boardDTO);
boardService.save(boardDTO);
return "index";
}
}
게시글을 /board/save로 보낸 후에는 다시 index로 돌아온다.
dto 패키지도 생성한다.
BoardDTO 파일을 만들어준다.
Lombok을 사용해 주었다. 편하다.
[BoardDTO]
package test.SpringBootBoard.board.dto;
import lombok.*;
import org.springframework.web.multipart.MultipartFile;
import java.time.LocalDateTime;
// 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;
private MultipartFile boardFile; // save.html -> Controller 파일 담는 용도
private String originalFileName; // 원본 파일 이름
private String storedFileName; // 서버 저장용 파일 이름
private int fileAttached; // 파일 첨부 여부(첨부 1, 미첨부 0)
public BoardDTO(Long id, String boardWriter, String boardTitle, int boardHits, LocalDateTime boardCreatedTime) {
this.id = id;
this.boardWriter = boardWriter;
this.boardTitle = boardTitle;
this.boardHits = boardHits;
this.boardCreatedTime = boardCreatedTime;
}
}
- entity(domain): 데이터베이스에 쓰일 컬럼과 여러 엔티티 간의 연관관계를 정의.
데이터베이스의 테이블을 하나의 엔티티로 생각해도 무방함.
실제 데이터베이스의 테이블과 1:1로 매핑됨.
이 클래스의 필드는 각 테이블 내부의 컬럼을 의미
- dto: vo(value object)로 불리기도 하며, 계층간 데이터 교환을 위한 객체를 의미.
vo의 경우 read only의 개념을 가지고 있음.
(vo, dto는 거의 같은 의미.)
(계층간 데이터 교환: 클라이언트에서 컨틀롤러로, 컨트롤러에서 서비스로,
각 계층이 구분되어 있는 부분들을 데이터를 옮겨주기 위해서 사용되는 객체)
(엔티티는 데이터베이스와 동일하게 만들어져 있는 클래스.
dto는 엔티티와 같은 필드 값을 갖고 있을 순 있지만 서비스에서 더 빼거나 추가할 수 있기 때문에 데이터베이스 컬럼과는 독립적.
dto와 entity가 좀 햇갈린다.
dto는 클라이언트와 서버 간 데이터 전송을 위해 설계된 객체.
entity는 데이터베이스에 저장되는 객체로, 데이터베이스와 직접적으로 연결.
entity 패키지를 만들고 BoardEntity 파일을 생성한다.
DB의 테이블 역할을 하는 클래스로
스프링 데이터 jpa를 사용하게 되면 별도로 db에 테이블을 생성할 필요가 없어진다고 한다.
와우 신세계...
특정 테이블 이름을 따로 주고 싶다면 테이블 어노테이션을 사용한다.
[ BoardEntity ]
package test.SpringBootBoard.board.entity;
// 서비스, 레포지토리 사이에서만 사용
import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;
import test.SpringBootBoard.board.dto.BoardDTO;
import java.util.ArrayList;
import java.util.List;
// DB의 테이블 역할을 하는 클래스
// 스프링 데이터 jpa를 쓰게 되면 별도로 db에 테이블 생성 필요 X.
// 특정 테이블 이름을 따로 주고 싶다면 테이블 어노테이션 사용
@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;
@Column
private int fileAttached; // 1 or 0
public static BoardEntity toSaveEntity(BoardDTO boardDTO) {
BoardEntity boardEntity = new BoardEntity();
boardEntity.setBoardWriter(boardDTO.getBoardWriter());
boardEntity.setBoardPass(boardDTO.getBoardPass());
boardEntity.setBoardTitle(boardDTO.getBoardTitle());
boardEntity.setBoardContents(boardDTO.getBoardContents());
boardEntity.setBoardHits(0);
boardEntity.setFileAttached(0); // 파일 없음.
return boardEntity;
}
}
시간을 관리하는 entity는 따로 생성했다.
생성 시간과 업데이트 시간을 다루는 클래스이다.
[어노테이션 의미]
- @MappedSuperclass: 해당 클래스의 필드들이 하위 엔티티 클래스에 상속되어 매핑되지만,
부모 클래스 자체는 실제 데이터베이스 테이블과 매핑되지 않는다.
- @EntityListeners(AuditingEntityListener.class): 엔티티 리스너를 등록한다.
AuditingEntityListener를 사용하여 생성 시간과 업데이트 시간을 자동으로 관리하도록 한다.
- @CreationTimestamp: 엔티티가 저장될 때 자동으로 생성 시간을 기록한다.
@CreationTimestamp가 적용된 필드는 읽기 전용이며, updatable = false 설정을 통해 업데이트가 되지 않도록 한다.
- @UpdateTimestamp: 엔티티가 업데이트될 때 자동으로 업데이트 시간을 기록한다.
[BaseEntity]
package test.SpringBootBoard.board.entity;
// 시간정보 다룸
import jakarta.persistence.Column;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.MappedSuperclass;
import lombok.Getter;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import java.time.LocalDateTime;
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
@Getter
public class BaseEntity {
@CreationTimestamp
@Column(updatable = false)
private LocalDateTime createdTime;
@UpdateTimestamp
@Column(insertable = false)
private LocalDateTime updatedTime;
}
service 패키지도 만들어서 BoardService 파일도 생성한다.
[ BoardService ]
package test.SpringBootBoard.board.service;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import test.SpringBootBoard.board.dto.BoardDTO;
import test.SpringBootBoard.board.entity.BoardEntity;
import test.SpringBootBoard.board.repository.BoardRepository;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
// 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);
}
}
서비스에서는 컨트롤러에서 받은 dto를 entity로 변환하고,
필요한 작업을 수행한 뒤에 repository에 entity를 전달한다.
repository 패키지를 만들고 BoardRepository 파일을 생성한다.
spring data jpa에서 제공하는 JpaRepository 인터페이스를 상속받아 정의한
BoardEntity 엔티티를 다루기 위한 커스텀한 메서드를 정의한 BoardRepository 인터페이스이다.
public interface BoardRepository extends JpaRepository<BoardEntity, Long> {
}
spring data jpa에서는 기본적인 CRUD 작업을 지원하는 JpaRepository 인터페이스를 제공한다.
여기서는 JpaRepository<BoardEntity, Long>를 상속받아 BoardEntity 엔티티와
그 엔티티의 식별자 타입인 Long을 사용하는 JpaRepository를 생성하고 있다.
아 그리고 apllication.yml의 jpa 설정도 create에서 update로 바꿔준다.
끝! 이제 실행해보자.
휴 다행히 테이블이 생성되었다는 메시지가 잘 출력되었다.
휴 그리고 디비버에서 데이터베이스를 확인해본 결과 잘 생성되었다.
http://localhost:8092/board/save 로 이동해서 데이터를 작성했다.
글작성 버튼을 누르면 index 메인으로 돌아간다.
그리고 아까 컨트롤러에서 찍어놨던
System.out.println("boardDTO = " + boardDTO);
안에 데이터가 잘 들어온걸 확인할 수 있다.
데이터도 잘 들어왔다.
'Spring Boot' 카테고리의 다른 글
[게시판 만들기 (5)] 게시글 조회 (0) | 2024.01.14 |
---|---|
[게시판 만들기 (4)] 게시글 목록 (0) | 2024.01.14 |
[게시판 만들기 (2)] 게시글 작성_작성 페이지 이동하기 (0) | 2024.01.12 |
[게시판 만들기 (1)] SpringBoot + jpa + mysql 개발 환경 설정 (0) | 2024.01.12 |
[스프링 입문] (5) 회원 관리 예제 - 웹 MVC 개발 (0) | 2024.01.10 |