ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Spring Boot] JPA 게시판 만들기(1) - 기본 설정 및 CRUD 구현
    Spring Boot/2.3.x - JPA 게시판 만들기 2020. 6. 25. 11:32
    반응형

    Spring Data Jpa와 H2 Database를 사용하여 게시글을 조회, 등록, 수정, 삭제하는 API를 구현하겠습니다.

     

    1.Dependency 설정

    pom.xml

    더보기

     

     

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.3.1.RELEASE</version>
            <relativePath />
        </parent>
        <groupId>com.rest.api</groupId>
        <artifactId>jpa-sample</artifactId>
        <version>1.0</version>
        <name>jpa-sample</name>
        <description>JPA SAMPLE</description>
        <packaging>war</packaging>
     
        <properties>
            <java.version>1.8</java.version>
        </properties>
     
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <!-- JPA -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-jpa</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-tomcat</artifactId>
                <scope>provided</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-devtools</artifactId>
                <scope>runtime</scope>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
                <exclusions>
                    <exclusion>
                        <groupId>org.junit.vintage</groupId>
                        <artifactId>junit-vintage-engine</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
     
            <!-- H2 Database -->
            <dependency>
                <groupId>com.h2database</groupId>
                <artifactId>h2</artifactId>
                <version>1.4.192</version>
            </dependency>
     
            <!-- Lombok -->
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
            </dependency>
     
        </dependencies>
     
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
     
    </project>
    cs

     

     

     

    2.Properties 설정

    application.properties

    더보기

     

     

     

     

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    #H2 Database 설정
    spring.datasource.url=jdbc:h2:~/jpa-sample;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
    spring.datasource.driverClassName=org.h2.Driver
    spring.datasource.username=sa
    spring.datasource.password=
     
    #Hibernate가 실행하는 모든 SQL문을  콘솔로 출력
    spring.jpa.properties.hibernate.show_sql=true
    #SQL문을 가독성 있게 출력
    spring.jpa.properties.hibernate.format_sql=true
    #SQL문 이외에 추가적인 정보를 출력
    spring.jpa.properties.hibernate.use_sql_comments=true
    #MySQL InnoDB Dialect 설정
    spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
    #SQL문 중 물음표로 표기되는 BindParameter의 값를 출력
    logging.level.org.hibernate.type.descriptor.sql=trace
    cs

     

     

     

     

     

    3.Entity 구현

    JPA에서 제공하는 어노테이션@Entity 클래스와 테이블과 매핑한다고 JPA에게 전달하며, @Entity 어노테이션이 선언된 클래스를 엔티티 클래스라고 합니다.@Table 엔티티 클래스에 매핑할 테이블 정보를 전달하며, 생략 시 클래스명을 테이블명으로 매핑합니다.@Id 엔티티 클래스의 필드를 테이블에 기본 키(PK, Primary key)로 매핑합니다.@GeneratedValue 생성 전략(

    strategy)에 따라 기본 키를 지정합니다.

    AUTO(default) - JPA가 자동으로 생성 전략을 결정합니다.IDENTIT - 기본키 생성을 데이터베이스에 위임한다. 예를 들어 MySQL의 경우 AUTO_INCREMENT를 사용하여 기본키를 생성합니다.SEQUENCE - 데이터베이스의 특별한 오브젝트 시퀀스를 사용하여 기본키를 생성합니다.TABLE - 데이터베이스에 키 생성 전용 테이블을 하나 만들고 이를 사용하여 기본키를 생성합니다.@Column 엔티티 클래스의 필드를 컬럼에 매핑하며, 생략 시 필드명을 컬럼에 매핑합니다.@Lob

     

     엔티티 클래스의 필드를 데이터베이스 BLOB, CLOB, TEXT 타입과 맵핑합니다.
    Lombok에서 제공하는 어노테이션

    @NoArgsConstructor

    기본 생성자를 자동으로 추가합니다.@Getter

     

    클래스 내 모든 필드의 Getter 메소드를 자동 생성합니다.

    @Builder해당 클래스의 빌더 패턴 클래스를 생성합니다.생성자 상단에 선언 시 생성자에 포함된 필드만 빌더에 포함합니다.

    Board.java

    더보기

     

     

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    package com.rest.api.board.domain;
     
    import javax.persistence.Column;
    import javax.persistence.Entity;
    import javax.persistence.GeneratedValue;
    import javax.persistence.GenerationType;
    import javax.persistence.Id;
    import javax.persistence.Lob;
    import javax.persistence.Table;
     
    import lombok.Builder;
    import lombok.Getter;
    import lombok.NoArgsConstructor;
     
    @Getter
    @NoArgsConstructor
    @Entity
    @Table(name = "tbl_board")               
    public class Board {
     
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        @Column(name = "board_seq")
        private Long boardSeq;
     
        @Lob
        @Column(name = "board_subject")
        private String boardSubject;
     
        @Column(name = "board_writer")
        private String boardWriter;
     
        @Lob
        @Column(name = "board_content")
        private String boardContent;
     
        @Builder
        public Board(String boardSubject, String boardWriter, String boardContent) {
            this.boardSubject = boardSubject;
            this.boardWriter = boardWriter;
            this.boardContent = boardContent;
        }
     
        public void update(String boardSubject, String boardContent) {
            this.boardSubject = boardSubject;
            this.boardContent = boardContent;
        }
    }
    cs

     

     

     

    4.Dto 구현

     Entity 클래스를 변경하는 경우 DB Layer에 영향을 주기 때문에, View Layer에서 사용할 Request/Response Dto를 추가하세요.

    BoardSaveRequestDto.java

    더보기

     

     

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    package com.rest.api.board.dto;
     
    import com.rest.api.board.domain.Board;
     
    import lombok.Builder;
    import lombok.Getter;
    import lombok.NoArgsConstructor;
     
    @Getter
    @NoArgsConstructor
    public class BoardSaveRequestDto {
     
        private String boardSubject;
        private String boardWriter;
        private String boardContent;
     
        @Builder
        public BoardSaveRequestDto(String boardSubject, String boardWriter, String boardContent) {
            this.boardSubject = boardSubject;
            this.boardWriter = boardWriter;
            this.boardContent = boardContent;
        }
     
        public Board toEntity() {
            return Board.builder()
                        .boardSubject(boardSubject)
                        .boardWriter(boardWriter)
                        .boardContent(boardContent)
                        .build();
        }
    }
    cs

     

     

    BoardUpdateRequestDto.java

    더보기
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    package com.rest.api.board.dto;
     
    import lombok.Builder;
    import lombok.Getter;
    import lombok.NoArgsConstructor;
     
    @Getter
    @NoArgsConstructor
    public class BoardUpdateRequestDto {
     
        private String boardSubject;
        private String boardContent;
     
        @Builder
        public BoardUpdateRequestDto(String boardSubject, String boardContent) {
            this.boardSubject = boardSubject;
            this.boardContent = boardContent;
        }
    }
    cs

    BoardResponseDto.java

    더보기

     

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    package com.rest.api.board.dto;
     
    import com.rest.api.board.domain.Board;
     
    import lombok.Getter;
     
    @Getter
    public class BoardResponseDto {
     
        private Long boardSeq;
        private String boardSubject;
        private String boardWriter;
        private String boardContent;
     
        public BoardResponseDto(Board board) {
            this.boardSeq = board.getBoardSeq();
            this.boardSubject = board.getBoardSubject();
            this.boardWriter = board.getBoardWriter();
            this.boardContent = board.getBoardContent();
        }
    }
    cs

     

    5.Repository 구현

     Spring Data JPA는 Repository 계층의 반복되는 작업을 피하기 위해 JpaRepository 인터페이스를 제공합니다. 

    JpaRepository 인터페이스에는 자주 사용되는 기본적인 CRUD 메서드가 선언되어 있는데, 개발자는 Repository 클래스를 생성하지 않고 인터페이스를 생성하여 JpaRepository 인터페이스를 상속받으면 JpaRepository에서 선언한 메서드들을 사용 할 수 있게 됩니다. 또한 JpaRepository에서 제공하는 메서드 외에 필요한 쿼리를 수행하기 위한 메서드를 직접 정의할 수도 있습니다.

    BoardRepository.java

    더보기
    1
    2
    3
    4
    5
    6
    7
    8
    9
    package com.rest.api.board.repository;
     
    import org.springframework.data.jpa.repository.JpaRepository;
     
    import com.rest.api.board.domain.Board;
     
    public interface BoardRepository extends JpaRepository<Board, Long> {
     
    }
    cs

     

    6.Service 구현

    BoardService.java

    더보기

     

     

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    package com.rest.api.board.service;
     
    import java.util.List;
    import java.util.stream.Collectors;
     
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;
     
    import com.rest.api.board.domain.Board;
    import com.rest.api.board.dto.BoardSaveRequestDto;
    import com.rest.api.board.dto.BoardResponseDto;
    import com.rest.api.board.dto.BoardUpdateRequestDto;
    import com.rest.api.board.repository.BoardRepository;
     
    import lombok.RequiredArgsConstructor;
     
    @RequiredArgsConstructor
    @Service
    public class BoardService {
     
        private final BoardRepository boardRepository;
     
        /** 게시글 - 목록 조회 */
        @Transactional(readOnly = true)
        public List<BoardResponseDto> findAll() {
            
            return boardRepository.findAll()
                                  .stream()
                                  .map(BoardResponseDto::new)
                                  .collect(Collectors.toList());
        }
     
        /** 게시글 - 상세 조회 */
        @Transactional(readOnly = true)
        public BoardResponseDto findById(Long boardSeq) {
     
            Board board = boardRepository.findById(boardSeq)
                                         .orElseThrow(() -> new IllegalAccessError("[boardSeq=" + boardSeq + "] 해당 게시글이 존재하지 않습니다."));
     
            return new BoardResponseDto(board);
        }
     
        /** 게시글 - 등록 */
        @Transactional
        public Long save(BoardSaveRequestDto boardSaveRequestDto) {
     
            return boardRepository.save(boardSaveRequestDto.toEntity())
                                  .getBoardSeq();
        }
     
        /** 게시글 - 수정 */
        @Transactional
        public Long update(Long boardSeq, BoardUpdateRequestDto boardUpdateRequestDto) {
     
            Board board = boardRepository.findById(boardSeq)
                                         .orElseThrow(() -> new IllegalAccessError("[boardSeq=" + boardSeq + "] 해당 게시글이 존재하지 않습니다."));
     
            board.update(boardUpdateRequestDto.getBoardSubject(), boardUpdateRequestDto.getBoardContent());
     
            return boardSeq;
        }
     
        /** 게시글 - 삭제 */
        @Transactional
        public void delete(Long boardSeq) {
     
            Board board = boardRepository.findById(boardSeq)
                                         .orElseThrow(() -> new IllegalAccessError("[boardSeq=" + boardSeq + "] 해당 게시글이 존재하지 않습니다."));
     
            boardRepository.delete(board);
        }
    }
    cs

     

     

     

    7.Controller 구현

    BoardController.java

    더보기

     

     

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    package com.rest.api.board.controller;
     
    import java.util.List;
     
    import org.springframework.http.HttpStatus;
    import org.springframework.http.MediaType;
    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.PathVariable;
    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.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
     
    import com.rest.api.board.dto.BoardSaveRequestDto;
    import com.rest.api.board.dto.BoardResponseDto;
    import com.rest.api.board.dto.BoardUpdateRequestDto;
    import com.rest.api.board.service.BoardService;
     
    import lombok.RequiredArgsConstructor;
     
    @RequiredArgsConstructor
    @RequestMapping("/api/v1/board")
    @RestController
    public class BoardController {
     
        private final BoardService boardService;
     
        /** 게시글 - 목록 조회  */
        @GetMapping(value = "", produces = { MediaType.APPLICATION_JSON_VALUE })
        public ResponseEntity<List<BoardResponseDto>> findAll() {
     
            List<BoardResponseDto> boardResponseDtoList = boardService.findAll();
     
            return new ResponseEntity<List<BoardResponseDto>>(boardResponseDtoList, HttpStatus.OK);
        }
     
        /** 게시글 - 상세 조회 */
        @GetMapping(value = "/{boardSeq}", produces = { MediaType.APPLICATION_JSON_VALUE })
        public ResponseEntity<BoardResponseDto> findById(@PathVariable("boardSeq") Long boardSeq) {
     
            BoardResponseDto boardResponseDto = boardService.findById(boardSeq);
     
            return new ResponseEntity<BoardResponseDto>(boardResponseDto, HttpStatus.OK);
        }
     
        /** 게시글 - 등록 */
        @PostMapping(value = "", produces = { MediaType.APPLICATION_JSON_VALUE })
        public ResponseEntity<Long> save(@RequestBody BoardSaveRequestDto boardSaveRequestDto) {
     
            Long savedBoardSeq = boardService.save(boardSaveRequestDto);
     
            return new ResponseEntity<Long>(savedBoardSeq, HttpStatus.CREATED);
        }
     
        /** 게시글 - 수정 */
        @PutMapping(value = "/{boardSeq}", produces = { MediaType.APPLICATION_JSON_VALUE })
        public ResponseEntity<Long> update(@PathVariable("boardSeq") Long boardSeq, @RequestBody BoardUpdateRequestDto boardUpdateRequestDto) {
     
            Long updatedboardSeq = boardService.update(boardSeq, boardUpdateRequestDto);
     
            return new ResponseEntity<Long>(updatedboardSeq, HttpStatus.CREATED);
        }
     
        /** 게시글 - 삭제 */
        @DeleteMapping(value = "/{boardSeq}", produces = { MediaType.APPLICATION_JSON_VALUE })
        public ResponseEntity<Long> delete(@PathVariable("boardSeq") Long boardSeq) {
     
            boardService.delete(boardSeq);
     
            return new ResponseEntity<Long>(boardSeq, HttpStatus.NO_CONTENT);
        }
    }
    cs

     

     

     

    8.SQL Script

     src/main/resources에 import.sql 파일이 있으면 서버 실행 시에 해당 스크립트를 자동 실행합니다.import.sql

    더보기
    1
    2
    3
    4
    5
    INSERT INTO TBL_BOARD ( BOARD_SEQ, BOARD_SUBJECT, BOARD_WRITER, BOARD_CONTENT ) VALUES ( 1'게시글_제목_01''게시글_작성자_01''게시글_내용_01' );
     
    INSERT INTO TBL_BOARD ( BOARD_SEQ, BOARD_SUBJECT, BOARD_WRITER, BOARD_CONTENT ) VALUES ( 2'게시글_제목_02''게시글_작성자_02''게시글_내용_02' );
     
    INSERT INTO TBL_BOARD ( BOARD_SEQ, BOARD_SUBJECT, BOARD_WRITER, BOARD_CONTENT ) VALUES ( 3'게시글_제목_03''게시글_작성자_03''게시글_내용_03' );
    cs

     

    9.프로젝트 구조

     

    Github Repository - https://github.com/tychejin1218/jpa-sample

    반응형

    댓글

Designed by Tistory.