[Practice] 게시판 API 만들기 (7)

2021. 4. 16. 22:07Spring/Practice

1. 문제

  • 게시된 게시글을 최상단에 배치하는 API

 

 

 

2. 풀이

- schema.sql

DROP TABLE IF EXISTS USER;
DROP TABLE IF EXISTS NOTICE;

create table USER (
    ID          BIGINT auto_increment primary key,
    EMAIL       VARCHAR(255),
    NAME        VARCHAR(255),
    PASSWORD    VARCHAR(255),
    PHONE       VARCHAR(255),
    REG_DATE    TIMESTAMP,
    UPDATE_DATE TIMESTAMP,
    STATUS      INTEGER,
    LOCK_YN     BOOLEAN DEFAULT FALSE
);

create table NOTICE (
    ID          BIGINT auto_increment primary key,
    TITLE       VARCHAR(255),
    CONTENTS    VARCHAR(255),

    HITS        INTEGER,
    LIKES       INTEGER,

    REG_DATE    TIMESTAMP,
    UPDATE_DATE TIMESTAMP,
    DELETED     BOOLEAN DEFAULT FALSE,
    DELETED_DATE    TIMESTAMP,

    USER_ID     BIGINT,
    constraint FK_NOTICE_USER_ID foreign key(USER_ID) references USER(ID)
);

create table NOTICE_LIKE (
    ID          BIGINT auto_increment primary key,

    NOTICE_ID   BIGINT,
    constraint FK_NOTICE_LIKE_NOTICE_ID foreign key(NOTICE_ID) references NOTICE(ID),

    USER_ID     BIGINT,
    constraint FK_NOTICE_LIKE_USER_ID foreign key(USER_ID) references USER(ID)
);

create table USER_LOGIN_HISTORY (
    ID          BIGINT auto_increment primary key,
    USER_ID     BIGINT,
    EMAIL       VARCHAR(255),
    NAME        VARCHAR(255),
    LOGIN_DATE  TIMESTAMP,
    IP_ADDR     VARCHAR(255)
);

create table BOARD_TYPE (
    ID          BIGINT auto_increment primary key,
    BOARD_NAME  VARCHAR(255),
    REG_DATE    TIMESTAMP,
    UPDATE_DATE TIMESTAMP,
    USING_YN    BOOLEAN DEFAULT FALSE
);

-- auto-generated definition
create table BOARD
(
    ID            BIGINT auto_increment primary key,
    CONTENTS      VARCHAR(255),
    REG_DATE      TIMESTAMP,
    TITLE         VARCHAR(255),
    BOARD_TYPE_ID BIGINT,
    USER_ID       BIGINT,
    TOP_YN        BOOLEAN DEFAULT FALSE,
    constraint FK_BOARD_BOARD_TYPE_ID foreign key (BOARD_TYPE_ID) references BOARD_TYPE (ID),
    constraint FK_BOARD_USER_ID foreign key (USER_ID) references USER (ID)
);

- data.sql

INSERT INTO USER(ID, EMAIL, PASSWORD, PHONE, REG_DATE, UPDATE_DATE, NAME, STATUS, LOCK_YN) VALUES(1, 'test1@naver.com', '1111', '010-1111-1111', '2021-01-01 01:01:01.000000', null, '테스트1', 1, 0);
INSERT INTO USER(ID, EMAIL, PASSWORD, PHONE, REG_DATE, UPDATE_DATE, NAME, STATUS, LOCK_YN) VALUES(2, 'test2@naver.com', '2222', '010-2222-2222', '2021-01-02 02:02:02.000000', null, '테스트2', 1, 0);
INSERT INTO USER(ID, EMAIL, PASSWORD, PHONE, REG_DATE, UPDATE_DATE, NAME, STATUS, LOCK_YN) VALUES(3, 'test3@naver.com', '3333', '010-3333-3333', '2021-01-03 03:03:03.000000', null, '테스트3', 2, 0);

INSERT INTO NOTICE(ID, CONTENTS, HITS, LIKES, REG_DATE, TITLE, USER_ID) VALUES(1, '내용1', 0, 0, '2021-01-01 01:01:01.000000', '제목1', 1);
INSERT INTO NOTICE(ID, CONTENTS, HITS, LIKES, REG_DATE, TITLE, USER_ID) VALUES(2, '내용2', 0, 0, '2021-01-02 02:02:02.000000', '제목2', 1);
INSERT INTO NOTICE(ID, CONTENTS, HITS, LIKES, REG_DATE, TITLE, USER_ID) VALUES(3, '내용3', 0, 0, '2021-01-03 03:03:03.000000', '제목3', 1);

INSERT INTO NOTICE_LIKE(ID, NOTICE_ID, USER_ID) VALUES(1, 3, 1);
INSERT INTO NOTICE_LIKE(ID, NOTICE_ID, USER_ID) VALUES(2, 2, 1);

INSERT INTO BOARD_TYPE(ID, BOARD_NAME, REG_DATE, USING_YN) VALUES
    (1, '게시판1', '2021-01-01 01:01:01.000000', 1),
    (2, '게시판2', '2021-01-02 02:02:02.000000', 1);

INSERT INTO BOARD(ID, BOARD_TYPE_ID, USER_ID, TITLE, CONTENTS, REG_DATE, TOP_YN) VALUES
    (1, 1, 1, '게시글1', '게시글내용1', '2021-01-01 01:01:01.000000', 0),
    (2, 1, 1, '게시글2', '게시글내용2', '2021-01-02 02:02:02.000000', 0);

- Board.java

package com.example.jpa.sample.board.entity;

import com.example.jpa.sample.user.entity.User;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.*;
import java.time.LocalDateTime;

@AllArgsConstructor
@NoArgsConstructor
@Builder
@Data
@Entity
public class Board {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;

    @ManyToOne
    @JoinColumn
    private User user;

    @ManyToOne
    @JoinColumn
    private BoardType boardType;

    @Column
    private String title;

    @Column
    private String contents;

    @Column
    private LocalDateTime regDate;

    @Column
    private boolean topYn;
}

- BoardService.java

package com.example.jpa.sample.board.service;

import com.example.jpa.sample.board.entity.BoardType;
import com.example.jpa.sample.board.model.BoardTypeCount;
import com.example.jpa.sample.board.model.BoardTypeInput;
import com.example.jpa.sample.board.model.BoardTypeUsing;
import com.example.jpa.sample.board.model.ServiceResult;

import java.util.List;

public interface BoardService {

    ServiceResult addBoard(BoardTypeInput boardTypeInput);

    ServiceResult updateBoard(Long id, BoardTypeInput boardTypeInput);

    ServiceResult deleteBoard(Long id);

    List<BoardType> getAllBoardType();

    ServiceResult setBoardTypeUsing(Long id, BoardTypeUsing boardTypeUsing);

    List<BoardTypeCount> getBoardTypeCount();

    ServiceResult setBoardTop(Long id);
}

- BoardServiceImpl.java

package com.example.jpa.sample.board.service;

import com.example.jpa.sample.board.entity.Board;
import com.example.jpa.sample.board.entity.BoardType;
import com.example.jpa.sample.board.model.BoardTypeCount;
import com.example.jpa.sample.board.model.BoardTypeInput;
import com.example.jpa.sample.board.model.BoardTypeUsing;
import com.example.jpa.sample.board.model.ServiceResult;
import com.example.jpa.sample.board.repository.BoardTypeCustomRepository;
import com.example.jpa.sample.board.repository.BoardRepository;
import com.example.jpa.sample.board.repository.BoardTypeRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;

@RequiredArgsConstructor
@Service
public class BoardServiceImpl implements BoardService {

    private final BoardTypeRepository boardTypeRepository;
    private final BoardRepository boardRepository;
    private final BoardTypeCustomRepository boardTypeCustomRepository;

    @Override
    public ServiceResult addBoard(BoardTypeInput boardTypeInput) {
        BoardType boardType = boardTypeRepository.findByBoardName(boardTypeInput.getName());

        if(boardType != null && boardTypeInput.getName().equals(boardType.getBoardName())) { // 동일한 게시판 제목이 있을 경우
            return ServiceResult.fail("이미 동일한 게시판이 존재합니다.");
        }

        BoardType addBoardType = BoardType.builder()
                .boardName(boardTypeInput.getName())
                .regDate(LocalDateTime.now())
                .build();

        boardTypeRepository.save(addBoardType);

        return ServiceResult.success();
    }

    @Override
    public ServiceResult updateBoard(Long id, BoardTypeInput boardTypeInput) {
        Optional<BoardType> optionalBoardType = boardTypeRepository.findById(id);
        if(!optionalBoardType.isPresent()) {
            return ServiceResult.fail("수정할 게시판 타입이 없습니다.");
        }

        BoardType boardType = optionalBoardType.get();

        if(boardTypeInput.getName().equals(boardType.getBoardName())) { // 동일한 게시판 제목이 있을 경우
            return ServiceResult.fail("수정할 이름이 동일한 게시판명 입니다.");
        }

        boardType.setBoardName(boardTypeInput.getName());
        boardType.setUpdateDate(LocalDateTime.now());
        boardTypeRepository.save(boardType);

        return ServiceResult.success();
    }

    @Override
    public ServiceResult deleteBoard(Long id) {
        Optional<BoardType> optionalBoardType = boardTypeRepository.findById(id);
        if(!optionalBoardType.isPresent()) {
            return ServiceResult.fail("삭제할 게시판 타입이 없습니다.");
        }

        BoardType boardType = optionalBoardType.get();
        if(boardRepository.countByBoardType(boardType) > 0) {
            return ServiceResult.fail("삭제할 게시판 타입의 게시글이 존재합니다.");
        }

        boardTypeRepository.delete(boardType);

        return ServiceResult.success();
    }

    @Override
    public List<BoardType> getAllBoardType() {

        return boardTypeRepository.findAll();
    }

    @Override
    public ServiceResult setBoardTypeUsing(Long id, BoardTypeUsing boardTypeUsing) {

        Optional<BoardType> optionalBoardType = boardTypeRepository.findById(id);
        if(!optionalBoardType.isPresent()) {
            return ServiceResult.fail("삭제할 게시판 타입이 없습니다.");
        }

        BoardType boardType = optionalBoardType.get();

        boardType.setUsingYn(boardTypeUsing.isUsingYn());
        boardTypeRepository.save(boardType);

        return ServiceResult.success();

    }

    @Override
    public List<BoardTypeCount> getBoardTypeCount() {
        return boardTypeCustomRepository.getBoardTypeCount();
    }

    @Override
    public ServiceResult setBoardTop(Long id) {

        Optional<Board> optionalBoard = boardRepository.findById(id);
        if(!optionalBoard.isPresent()) {
            return ServiceResult.fail("게시글이 존재하지 않습니다.");
        }

        Board board = optionalBoard.get();
        if(board.isTopYn()) {
            return ServiceResult.fail("이미 게시글이 최상단에 배체되어 있습니다.");
        }

        board.setTopYn(true);
        boardRepository.save(board);

        return ServiceResult.success();
    }

}

- ApiBoardController.java

package com.example.jpa.sample.board.controller;

import com.example.jpa.sample.board.entity.BoardType;
import com.example.jpa.sample.board.model.BoardTypeCount;
import com.example.jpa.sample.board.model.BoardTypeInput;
import com.example.jpa.sample.board.model.BoardTypeUsing;
import com.example.jpa.sample.board.model.ServiceResult;
import com.example.jpa.sample.board.service.BoardService;
import com.example.jpa.sample.notice.model.ResponseError;
import com.example.jpa.sample.user.model.ResponseMessage;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import java.util.List;

@RequiredArgsConstructor
@RestController
public class ApiBoardController {

    private final BoardService boardService;

    // 문제 1
    @PostMapping("/api/board/type")
    public ResponseEntity<?> addBoardType(@RequestBody @Valid BoardTypeInput boardTypeInput, Errors errors) {

        if(errors.hasErrors()) {
            List<ResponseError> responseErrorList = ResponseError.of(errors.getAllErrors());
            return new ResponseEntity<>(ResponseMessage.fail("입력값이 정확하지 않습니다.", responseErrorList), HttpStatus.BAD_REQUEST);
        }

        ServiceResult result = boardService.addBoard(boardTypeInput);

        if(!result.isResult()) {
            return ResponseEntity.ok().body(ResponseMessage.fail(result.getMessage()));
        }

        return ResponseEntity.ok().build();
    }

    // 문제 2
    @PutMapping("/api/board/type/{id}")
    public ResponseEntity<?> updateBoardType(@PathVariable Long id, @RequestBody @Valid BoardTypeInput boardTypeInput, Errors errors) {

        if(errors.hasErrors()) {
            List<ResponseError> responseErrorList = ResponseError.of(errors.getAllErrors());
            return new ResponseEntity<>(ResponseMessage.fail("입력값이 정확하지 않습니다.", responseErrorList), HttpStatus.BAD_REQUEST);
        }

        ServiceResult result = boardService.updateBoard(id, boardTypeInput);

        if(!result.isResult()) {
            return ResponseEntity.ok().body(ResponseMessage.fail(result.getMessage()));
        }

        return ResponseEntity.ok().build();
    }

    // 문제 3
    @DeleteMapping("/api/board/type/{id}")
    public ResponseEntity<?> deleteBoardType(@PathVariable Long id) {
        ServiceResult result = boardService.deleteBoard(id);

        if(!result.isResult()) {
            return ResponseEntity.ok().body(ResponseMessage.fail(result.getMessage()));
        }

        return ResponseEntity.ok().body(ResponseMessage.success());
    }

    // 문제 4
    @GetMapping("/api/board/type")
    public ResponseEntity<?> boardType() {
        List<BoardType> boardTypeList = boardService.getAllBoardType();

        return ResponseEntity.ok().body(ResponseMessage.success(boardTypeList));
    }

    // 문제 5
    @PatchMapping("/api/board/type/{id}/using")
    public ResponseEntity<?> usingBoardType(@PathVariable Long id, @RequestBody BoardTypeUsing boardTypeUsing) {
        ServiceResult result = boardService.setBoardTypeUsing(id, boardTypeUsing);

        if(!result.isResult()) {
            return ResponseEntity.ok().body(ResponseMessage.fail(result.getMessage()));
        }

        return ResponseEntity.ok().body(ResponseMessage.success());
    }

    // 문제 6
    @GetMapping("/api/board/type/count")
    public ResponseEntity<?> boardTypeCount() {
        List<BoardTypeCount> boardTypeCountList = boardService.getBoardTypeCount();
        return ResponseEntity.ok().body(boardTypeCountList);
    }

    // 문제 7
    @PatchMapping("/api/board/{id}/top")
    public ResponseEntity<?> boardPostTop(@PathVariable Long id) {

        ServiceResult result = boardService.setBoardTop(id);
        return ResponseEntity.ok().body(result);
    }
}
728x90