Spring 게시판 03 - 연관관계 설정

yuns·2022년 10월 13일

Board 매핑하기

board의 username을 User에서 가져오도록 만들어본다

User.java

하나의 유저는 여러 개의 게시판을 만들 수 있다.
그러므로 OneToMany를 사용하고, Board를 list로 가져온다.

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.sparta.BoardAPI2.dto.UserRequestDto;
import lombok.*;

import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;

@Entity //db테이블과 일대일로 매핑
@Table(name = "users") // 테이블명 지정
@Getter
@Setter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class User {
    @Id
    @Column(name = "user_id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long userId;

    @Column(length = 50, unique = true)
    private String username;

    @Column(length = 100)
    private String password;

    @JsonIgnore
    @OneToMany(mappedBy = "user")
    List<Board> board = new ArrayList<>();

    public User(UserRequestDto requestDto) {
        this.username = requestDto.getUsername();
        this.password = requestDto.getPassword();
    }
}

Board.java

  1. 필요한 엔티티를 매핑한다
    Board - User : Board는 여러개를 하나의 유저가 가질 수 있으므로 ManyToOne이다.
    Board - Comment : Board하나는 여러 개의 게시글을 가질 수 있으므로 OneToMany이다.

  2. 생성자에 매개변수로 필요한 엔티티를 추가한다.

package com.sparta.BoardAPI2.entity;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.sparta.BoardAPI2.dto.BoardRequestDto;
import com.sparta.BoardAPI2.security.UserDetailsImpl;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;

@Setter
@Getter // get 함수를 일괄적으로 만들어줍니다.
@NoArgsConstructor // 기본 생성자를 만들어줍니다.
@Entity // DB 테이블 역할을 합니다.
public class Board extends Timestamped {
    // 글 고유 아이디
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Id
    @Column(name = "boardId")
    private Long id;

    // 글 제목
    @Column(nullable = false)
    private String title;

    // 글 내용
    @Column(nullable = false)
    private String content;

    // 비밀번호
    @Column(nullable = false)
    private String password;

    @ManyToOne
    @JoinColumn(name = "userId")
    private User user;

    @JsonIgnore
    @OneToMany(mappedBy = "board")
    List<Comment> comments = new ArrayList<>();

    // requestDto 정보를 가져와서 entity 만들 때 사용 (user매핑)
    public Board(User user, BoardRequestDto requestDto) {
        this.title = requestDto.getTitle();
        this.content = requestDto.getContent();
        this.password = requestDto.getPassword();
        this.user = user;
    }

    // 업데이트 메소드
    public void update(BoardRequestDto requestDto) {
        this.title = requestDto.getTitle();
        this.content = requestDto.getContent();
        this.password = requestDto.getPassword();
    }
}

BoardRequestDto.java

package com.sparta.BoardAPI2.dto;

import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@NoArgsConstructor
@Getter
@Setter
public class BoardRequestDto {

    private String title;

    private String content;

    private String password;
}

BoardResponseDto.java

import com.sparta.BoardAPI2.entity.Board;
import com.sparta.BoardAPI2.entity.User;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.time.LocalDateTime;

@NoArgsConstructor
@Getter
public class BoardResponseDto {

    private String title;

    private String content;

    private LocalDateTime createdAt;

    private LocalDateTime modifiedAt;

    private String username;

    // board의 정보를 받아 boardResponseDto 생성
    public BoardResponseDto(User user, Board board) {
        this.username = user.getUsername();
        this.title = board.getTitle();
        this.content = board.getContent();
        this.createdAt = board.getModifiedAt();
        this.modifiedAt = board.getCreatedAt();
    }

    public BoardResponseDto(Board board) {
        this.title = board.getTitle();
        this.content = board.getContent();
        this.createdAt = board.getModifiedAt();
        this.modifiedAt = board.getCreatedAt();
        this.username = board.getUser().getUsername();
    }
}

BoardListResponseDto.java

생성자에서 User을 받을 필요가 없는 이유는, Board에 user이 매핑되어있기 때문에 board.getUser()로 가져오면 되기 때문이다.

import com.sparta.BoardAPI2.entity.Board;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.time.LocalDateTime;
@NoArgsConstructor
@AllArgsConstructor
@Getter
public class BoardListResponseDto {
    // 제목
    private String title;

    // 작성자명
    private String username;

    private LocalDateTime createdAt;

    private LocalDateTime modifiedAt;

    // Entity -> dto
    public BoardListResponseDto(Board board) {
        this.username = board.getUser().getUsername();
        this.title = board.getTitle();
        this.createdAt = board.getModifiedAt();
        this.modifiedAt = board.getCreatedAt();
    }
}

BoardService.java

import com.sparta.BoardAPI2.dto.BoardListResponseDto;
import com.sparta.BoardAPI2.dto.BoardRequestDto;
import com.sparta.BoardAPI2.dto.BoardResponseDto;
import com.sparta.BoardAPI2.entity.Board;
import com.sparta.BoardAPI2.entity.User;
import com.sparta.BoardAPI2.repository.BoardRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import javax.transaction.Transactional;
import java.util.List;

@RequiredArgsConstructor
@Service
public class BoardService {
    private final BoardRepository boardRepository;

    // 글 생성 (user 매핑)
    public BoardResponseDto createBoard(User user, BoardRequestDto requestDto) {
        Board board = new Board(user, requestDto);
        boardRepository.save(board);
        return new BoardResponseDto(user, board);
    }

    // 글 목록 가져오기
    public List<BoardListResponseDto> findAllBoard() {
        return boardRepository.findAllByOrderByModifiedAtDesc();
    }

    // 글 하나 가져오기
    public BoardResponseDto findOneBoard(Long id) {
        Board board = boardRepository.findById(id).orElseThrow(
                () -> new IllegalArgumentException("게시글이 없습니다")
        );
        return new BoardResponseDto(board);
    }

    // 글 수정
    @Transactional
    public Long updateBoard(User user, Long id, BoardRequestDto requestDto) {
        // 어떤 게시판인지 찾기
        Board board = boardRepository.findById(id).orElseThrow(
                () -> new IllegalArgumentException("해당 게시글이 존재하지 않습니다.")
        );

        // 게시판의 username과 로그인 유저의 username 비교
        if (user.getUsername().equals(board.getUser().getUsername())) {
            board.update(requestDto);
            return board.getId();
        } else {
            throw new IllegalArgumentException("작성자만 수정할 수 있습니다");
        }
    }

    // 삭제
    @Transactional
    public Long deleteBoard(User user, Long id) {
        // 어떤 게시판인지 찾기
        Board board = boardRepository.findById(id).orElseThrow(
                () -> new IllegalArgumentException("해당 게시글이 존재하지 않습니다.")
        );

        // 게시판의 username과 로그인 유저의 username 비교
        if (user.getUsername().equals(board.getUser().getUsername())) {
            boardRepository.deleteById(id);
            return id;
        } else {
            throw new IllegalArgumentException("작성자만 수정할 수 있습니다");
        }
    }

    // 비밀번호 일치 확인
    public boolean checkPassword(Long id, String inputPassword) {
        Board board = boardRepository.findById(id).orElseThrow(
                () -> new IllegalArgumentException("해당 아이디가 존재하지 않습니다.")
        );
        if (inputPassword.equals(board.getPassword())) {
            return true;
        } else {
            return false;
        }
    }
}

BoardController.java

@AuthenticationPrincipal 어노테이션으로 UserDetailsImpl에서 로그인한 유저 정보를 가져올 수 있다.

UserDetailsImpl에서 필드와 생성자로 User의 데이터를 연결시켰기 때문에, userDetails.getUser()로 유저 데이터를 가져올 수 있다.

import com.sparta.BoardAPI2.dto.BoardListResponseDto;
import com.sparta.BoardAPI2.dto.BoardRequestDto;
import com.sparta.BoardAPI2.dto.BoardResponseDto;
import com.sparta.BoardAPI2.entity.Board;
import com.sparta.BoardAPI2.security.UserDetailsImpl;
import com.sparta.BoardAPI2.service.BoardService;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController // JSON으로 데이터를 주고받음을 선언합니다.
public class BoardController {
    private final BoardService boardService;

    public BoardController(BoardService boardService) {
        this.boardService = boardService;
    }

    // 글 등록
    @PostMapping("/boards")
    public BoardResponseDto createBoard(@AuthenticationPrincipal UserDetailsImpl userDetails, @RequestBody BoardRequestDto requestDto) {

        return boardService.createBoard(userDetails.getUser(), requestDto);
    }

    // 글 등록
//    @PostMapping("/boards")
//    public BoardResponseDto createBoard(@RequestBody BoardRequestDto requestDto){
//        BoardResponseDto board = boardService.createBoard(requestDto);
//        return board;
//    }

    // 전체 목록 조회
    @GetMapping("/boards-list")
    public List<BoardListResponseDto> getAllBoards() {
        return boardService.findAllBoard();
    }

    // 글 하나 조회
    @GetMapping("/boards/{id}")
    public BoardResponseDto getOneBoard(@PathVariable Long id) {
        return boardService.findOneBoard(id);
    }

    // 글 수정
    @PutMapping("/boards/{id}")
    public Long updateBoard(@AuthenticationPrincipal UserDetailsImpl userDetails, @PathVariable Long id, @RequestBody BoardRequestDto requestDto) {
        return boardService.updateBoard(userDetails.getUser(), id, requestDto);
    }

    // 글 삭제
    @DeleteMapping("/boards/{id}")
    public Long deleteBoard(@AuthenticationPrincipal UserDetailsImpl userDetails, @PathVariable Long id) {
        return boardService.deleteBoard(userDetails.getUser(), id);
    }

    // 비밀번호 확인
    @GetMapping("/boards/check/{id}/{inputPassword}")
    public boolean checkPassword(@PathVariable Long id, @PathVariable String inputPassword) {
        return boardService.checkPassword(id, inputPassword);
    }
}

Comment 매핑하기

Comment.java

board의 id를 가져오도록 board를 조인시켜준다.

import com.sparta.BoardAPI2.dto.CommentRequestDto;
import lombok.Getter;
import lombok.NoArgsConstructor;

import javax.persistence.*;

@Getter
@NoArgsConstructor
@Entity
public class Comment {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

//    @Column(nullable = false)
//    private Long boardId;

    @Column(nullable = false)
    private String content;

    @ManyToOne
    @JoinColumn(name = "boardId")
    private Board board;

    public Comment(CommentRequestDto requestDto, Board board) {
        this.content = requestDto.getContent();
//        this.boardId = requestDto.getBoardId();
        this.board = board;
    }

    public void update(CommentRequestDto requestDto) {
        this.content = requestDto.getContent();
    }
}

CommentRequestDto.java

작성 내용과 board의 id를 받는다.
id는 comment에서 저장했던 board를 get시켜서 id를 또 get한다.

import com.sparta.BoardAPI2.entity.Comment;
import lombok.Getter;
import lombok.NoArgsConstructor;

@NoArgsConstructor
@Getter
public class CommentRequestDto {

    private String content;

    private Long boardId;

    public CommentRequestDto(Comment comment) {
        this.content = comment.getContent();
        this.boardId = comment.getBoard().getId();
    }
}

CommentResponseDto.java

응답을 보낼 데이터 중 어떤 데이터를 보여줄지 선택해서 필드에 넣는다.

생성자를 만들 때, comment.getUser.getUsername() 처럼 만들었던 comment entity에 있는 user과 board에 접근해서 각각의 getter를 사용할 수 있다.

import com.sparta.BoardAPI2.entity.Comment;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

@NoArgsConstructor
@AllArgsConstructor
@Getter
public class CommentResponseDto {

    private Long boardId;

    private String content;

    public CommentResponseDto(Comment comment) {
        this.content = comment.getContent();
        this.boardId = comment.getBoard().getId();
    }
}

CommentRepository.java

commentRepositoryDto에서 boardId로 찾을것이기 때문에 만들어준다

import com.sparta.BoardAPI2.dto.CommentResponseDto;
import com.sparta.BoardAPI2.entity.Comment;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.util.List;


@Repository
public interface CommentRepository extends JpaRepository<Comment, Long> {
    List<CommentResponseDto> findAllByBoardId(Long boardId);
}

CommentService.java

댓글 등록 메소드

  • requestDto와 id를 매개변수로 받는다.
  • BoardRepository에서 id를 기준으로 찾는다.
  • 찾은 board로 comment객체를 새로 만든다
  • repository에 만든 객체를 저장한다
  • responseDto로도 만들어준다
import com.sparta.BoardAPI2.dto.CommentRequestDto;
import com.sparta.BoardAPI2.dto.CommentResponseDto;
import com.sparta.BoardAPI2.entity.Board;
import com.sparta.BoardAPI2.entity.Comment;
import com.sparta.BoardAPI2.repository.BoardRepository;
import com.sparta.BoardAPI2.repository.CommentRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import javax.transaction.Transactional;
import java.util.List;

@RequiredArgsConstructor
@Service
public class CommentService {
    private final CommentRepository commentRepository;
    private final BoardRepository boardRepository;

    // 댓글 등록 기본
//    @Transactional
//    public CommentResponseDto createComment(CommentRequestDto requestDto) {
//        Comment comment = new Comment(requestDto);
//        commentRepository.save(comment);
//        CommentResponseDto commentResponseDto = new CommentResponseDto(comment);
//        return commentResponseDto;
//    }

    // 댓글 등록 board 매핑
    @Transactional
    public CommentResponseDto createComment(CommentRequestDto requestDto, Long id) {
        Board board = boardRepository.findById(id).orElseThrow(
                () -> new IllegalArgumentException("조회 실패"));
        Comment comment = new Comment(requestDto,board);
        commentRepository.save(comment);
        CommentResponseDto commentResponseDto = new CommentResponseDto(comment);
        return commentResponseDto;
    }

    // 전체 댓글 조회
    @Transactional
    public List<CommentResponseDto> readAllComments(Long id) {
        return commentRepository.findAllByBoardId(id);
    }
}

CommentController.java

  • CommentService를 가져온다

댓글 등록

  • requestDto와 id(PathVariable로 경로의 {}에 해당하는 id값을 가져온다.)를 매개변수로 받는다
  • createComment메소드로 service에서 댓글을 생성한다

댓글 목록 조회

  • 경로의 {}id값을 매개변수로 받는다
import com.sparta.BoardAPI2.dto.CommentRequestDto;
import com.sparta.BoardAPI2.dto.CommentResponseDto;
import com.sparta.BoardAPI2.service.CommentService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RequiredArgsConstructor
@RestController
public class CommentController {
    private final CommentService commentService;

    // 댓글 등록
    @PostMapping("/boards/{id}/comments")
    public CommentResponseDto createBoard(@RequestBody CommentRequestDto requestDto, @PathVariable Long id) {
        return commentService.createComment(requestDto, id);
    }

    // 댓글 목록 조회
    @GetMapping("/boards/{id}/comments-list")
    public List<CommentResponseDto> getAllComments(@PathVariable Long id) {
        return commentService.readAllComments(id);
    }
}

Injection of autowired dependencies failed 에러

문제 상황 : 어플리케이션 실행이 안 됨
해결 : SecurityConfig 관련 파일이 두개였음, 하나를 삭제

  • annotation을 잘 붙였는지 확인
  • 겹치는 부분이 있는지 확인

1개의 댓글

comment-user-thumbnail
2024년 5월 10일

UserRequestDto 클래스 작성 부탁드려요~^^

답글 달기