일정 관리 앱 서버 기능 추가하기 : 댓글 기능 구현

이민호·2024년 6월 4일
0

1단계 : 일정과 댓글의 연관관계

설명

  • 지난 과제에서 만든 일정에 댓글을 추가할 수 있습니다.
  • ERD에도 댓글 모델을 추가합니다.
  • 각 일정에 댓글을 작성할 수 있도록 관련 클래스를 추가하고 연관 관계를 설정합니다.
  • 매핑 관계를 설정합니다. (1:1 or N:1 or N:M)

추가되는 entity : Comment

댓글필드데이터 유형
아이디(고유번호)bigint
댓글내용varchar
사용자 아이디varchar
일정 아이디bigint
작성일자timestamp
  • User

    • 하나의 user는 todo를 여러개 작성할 수 있으므로 OneToMany
    • 하나의 user는 comment를 여러개 작성 할 수 있으므로 OneToMany
  • todo

    • 여러개의 todo가 하나의 user에 의해 작성되므로 ManyToOne
    • 하나의 todo는 여러개의 comment를 가질 수 있으므로 OneToMany
  • comment

    • 여러개의 comment가 하나의 user에 의해 작성되므로 ManyToOne
    • 여러개의 comment가 하나의 todo에 의해 작성되므로 ManyToOne

수정되는 entity : User

사용자 필드데이터 유형
아이디(고유번호)bigint
별명varchar
사용자 이름varchar
비밀번호varchar
권한varchar
생성일timestamp

수정되는 entity : Todo

User 테이블이 생기면서 기존 Todo에 존재하는 password field가 중복된다고 판단.

일정 필드데이터 유형
아이디(고유번호)bigint
사용자 이름varchar
제목varchar
내용varchar
비밀번호varchar
생성일timestamp

Entity 구현 및 수정

구현 : entity.Comment
package io._2minus.todoapp.entity;

import jakarta.persistence.*;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;


@Entity
@Getter
@Setter
@NoArgsConstructor
public class Comment extends Timestamped{

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long commentId;

    @Column(nullable = false)
    private String content;

    @ManyToOne
    @JoinColumn(name = "user_id", nullable = false)
    private User user;

    @ManyToOne
    @JoinColumn(name = "todo_id",nullable = false)
    private Todo todo;

    @Builder
    public Comment(User user, Todo todo, String content) {
        this.user = user;
        this.todo = todo;
        this.content = content;
    }
}
수정 : entity.Todo
package io._2minus.todoapp.entity;

import jakarta.persistence.*;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Getter
@Setter
@Entity
@NoArgsConstructor
@Table(name = "todo")
public class Todo extends Timestamped {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "todo_id", nullable = false)
    private long todoId;

    @Column(nullable = false)
    private String title;

    @Column(nullable = false)
    private String content;

    @Column(nullable = false)
    private String userName;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id", nullable = false)
    private User user;

    @Builder
    public Todo(User user, String title, String content, String userName) {
        this.user = user;
        this.title = title;
        this.content = content;
        this.userName = userName;
    }
}
구현 : entity.User
package io._2minus.todoapp.entity;

import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Entity
@Getter
@Setter
@NoArgsConstructor
@Table(name = "user")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(nullable = false)
    private String nickname;

    @Column(nullable = false, unique = true)
    private String username;

    @Column(nullable = false)
    private String password;
    
    @Column(nullable = false)
    @Enumerated(value = EnumType.STRING)
    private UserRoleEnum role;
    
    public User(String nickname, String username, String password, UserRoleEnum role) {
        this.nickname = nickname;
        this.username = username;
        this.password = password;
        this.role = role;
    }


}

2단계 : 댓글 등록

기능

  • 선택한 일정이 있다면 댓글을 등록합니다.

조건

  • 댓글이 등록되었다면 client에게 반환합니다.
  • 선택한 일정이 DB에 저장되어 있어야 합니다.
  • 댓글을 식별하는 고유번호, 댓글 내용, 댓글을 작성한 사용자 아이디, 댓글이 작성된 일정 아이디, 작성일자를 저장할 수 있습니다.

⚠️ 예외 처리

  • 선택한 일정의 ID를 입력 받지 않은 경우
CommentService.createComment
// todoRepository에서 id로 조회 및 예외처리
Todo todo = todoRepository.findById(dto.getTodo().getTodoId()).orElseThrow(()->
                 new NullPointerException("잘못된 접근입니다."));
  • 댓글 내용이 비어 있는 경우
CommentRequestDTO

// validation으로 예외처리
@NotBlank
private String content;
  • 일정이 DB에 저장되지 않은 경우
CommentService.createComment

// save 후 commentRepository에서 조회 및 예외처리
commentRepository.save(comment);
        return commentRepository.findById(comment.getCommentId()).orElseThrow(()
        -> new IllegalArgumentException("저장에 오류가 발생했습니다."));

기능 구현

repository.CommentRepository
package io._2minus.todoapp.repository;

import io._2minus.todoapp.entity.Comment;
import org.springframework.data.jpa.repository.JpaRepository;

public interface CommentRepository extends JpaRepository<Comment, Long> {
}
service.CommentService.createComment
import io._2minus.todoapp.repository.CommentRepository;
import io._2minus.todoapp.repository.TodoRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
public class CommentService {
    private final CommentRepository commentRepository;
    private final TodoRepository todoRepository;

    @Transactional
    public Comment createComment(Long todoId, User user, CommentRequestDTO dto) {

        Todo todo = todoRepository.findById(todoId).orElseThrow(()->
                 new NullPointerException("잘못된 접근입니다."));

        var comment = dto.toEntity(user, todo);
        commentRepository.save(comment);
        return commentRepository.findById(comment.getCommentId()).orElseThrow(()
        -> new IllegalArgumentException("저장에 오류가 발생했습니다."));
    }
}
controller.CommentController.postComment
@RequestMapping("/v1.0/todo/{todoId}/comments")
@RestController
@RequiredArgsConstructor
public class CommentController {

    public final CommentService commentService;

    @PostMapping
    public ResponseEntity<CommonResponse<CommentResponseDTO>> postComment(@PathVariable Long todoId, @AuthenticationPrincipal UserDetailsImpl userDetails, @RequestBody CommentRequestDTO dto) {
        Comment comment = commentService.createComment(todoId, userDetails.getUser(), dto);
        CommentResponseDTO response = new CommentResponseDTO(comment);
        return ResponseEntity.ok().body(CommonResponse.<CommentResponseDTO>builder()
                .statusCode(HttpStatus.OK.value())
                .msg("생성이 완료되었습니다.")
                .data(response)
                .build());
    }
}

3단계 : 댓글 수정

### 기능
  • 선택한 일정의 댓글을 수정합니다.

조건

  • 댓글이 수정되었다면 수정된 댓글을 반환합니다.
  • 댓글 내용만 수정 가능합니다.
  • 선택한 일정과 댓글이 DB에 저장되어 있어야 합니다.

⚠️ 예외 처리

  • 선택한 일정이나 댓글의 ID를 입력 받지 않은 경우
  • 일정이나 댓글이 DB에 저장되지 않은 경우
  • 선택한 댓글의 사용자가 현재 사용자와 일치하지 않은 경우

위와 다르지 않음.

기능 구현

service.CommentService.updateComment
@Transactional
    public Comment updateComment(Long todoId, User user, Long commentId, CommentRequestDTO dto) {
        Comment comment = checkUserAndGetComment(todoId, user, commentId);
        comment.setContent(dto.getContent());
        return commentRepository.save(comment);

    }
controller.CommentController.putComment
@PutMapping("/{commentId}")
    public ResponseEntity<CommonResponse<CommentResponseDTO>> putComment(@PathVariable Long todoId, @AuthenticationPrincipal UserDetailsImpl userDetails, @PathVariable Long commentId, @RequestBody CommentRequestDTO dto) {
        Comment comment = commentService.updateComment(todoId, userDetails.getUser(), commentId, dto);
        CommentResponseDTO response = new CommentResponseDTO(comment);
        return ResponseEntity.ok().body(CommonResponse.<CommentResponseDTO>builder()
                .statusCode(HttpStatus.OK.value())
                .msg("수정 내용 : " + comment.getContent())
                .data(response)
                .build());
    }

4단계 : 댓글 삭제

### 기능
  • 선택한 일정의 댓글을 삭제합니다.

조건

  • 성공했다는 메시지와 상태 코드 반환하기
  • 선택한 일정과 댓글이 DB에 저장되어 있어야 합니다.

⚠️ 예외 처리

  • 선택한 일정이나 댓글의 ID를 입력받지 않은 경우
  • 일정이나 댓글이 DB에 저장되지 않은 경우
  • 선택한 댓글의 사용자가 현재 사용자와 일치하지 않은 경우

기능 구현

service.CommentService.deleteComment
@Transactional
    public void deleteComment(Long todoId, User user, Long commentId) {
        Comment comment = checkUserAndGetComment(todoId, user, commentId);
        commentRepository.delete(comment);
    }
controller.CommentController.deleteComment
@DeleteMapping("/{commentId}")
    public ResponseEntity<CommonResponse> deleteComment(@PathVariable Long todoId, @AuthenticationPrincipal UserDetailsImpl userDetails, @PathVariable Long commentId, @RequestBody CommentRequestDTO dto) {
        commentService.deleteComment(todoId, userDetails.getUser(), commentId);
        return ResponseEntity.ok().body(CommonResponse.<CommentResponseDTO>builder()
                .statusCode(HttpStatus.OK.value())
                .msg("삭제가 완료되었습니다.")
                .build());
    }
profile
둘뺌

0개의 댓글

관련 채용 정보