Tiny Star

🏕️내일배움캠프/📂뉴스 피드 프로젝트(23.06.30)

뉴스 피드 프로젝트 2일차_필수 구현 기능 코드 작성

청크 2023. 7. 3. 22:37

스파르타 코딩클럽_ 내일 배움 캠프 Spring 트랙 6기

[프로젝트] 뉴스 피드 만들기

2023.06.30 ~ 2023.07.07

 

<프로젝트 정보>

개발도구 : IntelliJ

프로그래밍 언어 : Java

데이터베이스 : MySQL

프레임워크 : Spring

와이어프레임 : Figma

ERD 설계 : ERD Cloud

 

<프로젝트 내용>

지난 금요일에 정해진 내용을 바탕으로 각자 맡은 기능을 구현하기 시작했고, 내가 오늘 제작한 건 게시글 작성 API와 게시글 삭제 API를 구현했다.

 

깃허브가 슬슬 익숙해졌기 때문에 프로젝트 할 때는 깃허브를 필수적으로 사용하고 있는데 한 명의 레포지토리를 포크하는 방식이  아닌

팀 대시보드를 만들어 브랜치를 여러개 나누어 작업하는 방식을 오늘 처음 사용해봤다.

우리팀 어쩌면 천재들의 모임일지도..!

팀원 모두 필수 구현사항을 빠르게 완성했기 때문에 내일은 머지하고 새로운 기능을 구현해봐도 좋을 것 같다.

 

아래는 내가 오늘 완성한 코드다.

[Post]

package com.sparta.newspeed.entity;

import com.sparta.newspeed.dto.PostRequestDto;
import jakarta.persistence.*;
import lombok.*;

import java.time.LocalDateTime;

@Entity
@Getter
@Setter
@Table
@NoArgsConstructor
public class Post extends Timestamped{

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    //PK값 자동 생성
    @Column(name = "post_id")
    private Long postId; // 게시글 고유 id
    @Column (nullable = false, length = 500)
    private String title; // 제목
    @Column (nullable = false, length = 10)
    private String author; // 작성자명
    @Column (nullable = false, length = 1000)
    private String contents; // 작성내용


    //setAuthor
    @ManyToOne
    @JoinColumn(name = "user_id")
    private User username;


    public Post(String title, String author, String contents) {
        this.title = title;
        this.author = author;
        this.contents = contents;

    }


}

[PostController]

package com.sparta.newspeed.controller;

import com.sparta.newspeed.dto.PostRequestDto;
import com.sparta.newspeed.dto.PostResponseDto;
import com.sparta.newspeed.security.UserDetailsImpl;
import com.sparta.newspeed.service.PostService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api")
@RequiredArgsConstructor
public class PostController {

    private final PostService postService;

    // 게시글 작성 API
    @PostMapping("/posts")
    public ResponseEntity<PostResponseDto> createdPost(@RequestBody PostRequestDto requestDto, @AuthenticationPrincipal UserDetailsImpl userDetails) {
        PostResponseDto createdPost = postService.createdPost(requestDto, userDetails);
        return ResponseEntity.status(HttpStatus.CREATED).body(createdPost);
    }

    // 선택한 게시글 삭제 API
    @DeleteMapping("/posts/{id}")
    public ResponseEntity<String> deletePost(@PathVariable Long id,@AuthenticationPrincipal UserDetailsImpl userDetails) {
        String message = postService.deletePost(id, userDetails);
        return ResponseEntity.ok(message);
    }
}

[PostRepository]

package com.sparta.newspeed.repository;

import com.sparta.newspeed.entity.Post;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
public interface PostRepository extends JpaRepository<Post, Long> {
    List<Post> findAllByOrderByModifiedAtDesc(); //내림차순 정렬
}

[PostService]

package com.sparta.newspeed.service;

import com.sparta.newspeed.dto.PostRequestDto;
import com.sparta.newspeed.dto.PostResponseDto;
import com.sparta.newspeed.entity.Post;
import com.sparta.newspeed.entity.Timestamped;
import com.sparta.newspeed.entity.User;
import com.sparta.newspeed.jwt.JwtUtil;
import com.sparta.newspeed.repository.PostRepository;
import com.sparta.newspeed.security.UserDetailsImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.server.ResponseStatusException;

@Service
@RequiredArgsConstructor //생성자 주입으로 데이터베이스에 대한 생성자를 생성하지 않아도 됨
public class PostService {
    private final PostRepository postRepository;
    private final JwtUtil jwtUtil;

    //게시글 작성 API
    @Transactional
    public PostResponseDto createdPost(PostRequestDto requestDto, UserDetailsImpl userDetails){
    // JWT 토큰 검증
        if (!jwtUtil.validateToken(requestDto.getToken())) {
            throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "유효하지 않은 토큰입니다.");
        }

        // 사용자 인증
        User currentUser = userDetails.getUser();
        String username = jwtUtil.getUserInfoFromToken(requestDto.getToken()).getSubject();
        if (!username.equals(currentUser.getUsername())) {
            throw new ResponseStatusException(HttpStatus.FORBIDDEN, "게시글 작성 권한이 없습니다.");
        }

        Post post = new Post(requestDto.getTitle(), currentUser.getUsername(), requestDto.getContents());
        Post savedPost = postRepository.save(post);

        return new PostResponseDto(savedPost.getPostId(), savedPost.getTitle(), savedPost.getAuthor(), savedPost.getContents(), savedPost.getCreatedAt());
    }

    //게시글 삭제 API
    @Transactional
    public String deletePost(Long id, UserDetailsImpl userDetails) {
        Post post = postRepository.findById(id)
                .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "게시글을 찾을 수 없습니다."));

        User currentUser = userDetails.getUser();
        if (!post.getAuthor().equals(currentUser.getUsername())) {
            throw new ResponseStatusException(HttpStatus.FORBIDDEN, "작성자만 게시글을 삭제할 수 있습니다.");
        }

        postRepository.delete(post);

        return "게시글이 성공적으로 삭제되었습니다.";
    }
}

[PostRequestDto]

package com.sparta.newspeed.dto;

import lombok.Getter;
import lombok.NoArgsConstructor;


@Getter
@NoArgsConstructor
public class PostRequestDto {
    private String token; // JWT 토큰 추가
    private String title;
    private String username;
    private String contents;



    public PostRequestDto(String title, String username, String contents) {
        this.title = title;
        this.username = username;
        this.contents = contents;
    }

    public String getToken() {
        return token;
    }
}

[PostResponseDto]

package com.sparta.newspeed.dto;

import lombok.Getter;
import java.time.LocalDateTime;


@Getter
public class PostResponseDto {    // 게시물 조회 요청에 대한 응답으로 사용
    private Long postId;
    private String title;
    private String author;
    private String contents;
    private LocalDateTime createdAt;
//    private LocalDateTime modifiedAt; 수정시간 - 상인님 코드

    public PostResponseDto(Long postId, String title, String author, String contents, LocalDateTime createdAt) {
        this.postId = this.postId;
        this.title = this.title;
        this.author = this.author;
        this.contents = this.contents;
        this.createdAt = this.createdAt;
//        this.modifiedAt = modifiedAt;
    }
}