Comment, Query Parameter

calis_ws·2023년 6월 22일
0
post-custom-banner

Comment 기능 확장

Comment CRUD API

CommentService

package com.example.article;

import com.example.article.dto.CommentDto;
import com.example.article.entity.CommentEntity;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.web.server.ResponseStatusException;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

@Service
@RequiredArgsConstructor
public class CommentService {
    private final ArticleRepository articleRepository;
    private final CommentRepository commentRepository;

    public CommentDto createComment(Long articleId, CommentDto dto) {
        // articleId를 ID로 가진 ArticleEntity 가 존재 하는지?
        if(!articleRepository.existsById(articleId))
            throw new ResponseStatusException(HttpStatus.NOT_FOUND);

        CommentEntity newComment = new CommentEntity();
        newComment.setWriter(dto.getWriter());
        newComment.setContent(dto.getContent());
        newComment.setArticleId(articleId);
        newComment = commentRepository.save(newComment);
        return CommentDto.fromEntity(newComment);
    }

    // 게시글 댓글 전체 조회
    public List<CommentDto> readCommentAll(Long articleId) {
        List<CommentEntity> commentEntities = commentRepository.findAllByArticleId(articleId);
        List<CommentDto> commentList = new ArrayList<>();
        for (CommentEntity entity: commentEntities) {
            commentList.add(CommentDto.fromEntity(entity));
        }

        return commentList;
    }

    // 게시글 댓글 수정
    // 수정하고자 하는 댓글이 지정한 게시글에 있는지 확인할 목적으로 articleId도 첨부한다.
    public CommentDto updateComment(Long articleId, Long commentId, CommentDto dto) {
        // 요청한 댓글이 존재하는지
        Optional<CommentEntity> optionalComment = commentRepository.findById(commentId);
        // 존재하지 않으면 예외 발생
        if (optionalComment.isEmpty()) throw new ResponseStatusException(HttpStatus.NOT_FOUND);

        // 아니면 로직 진행
        CommentEntity comment = optionalComment.get();

        // 대상 댓글이 대상 게시글의 댓글이 맞는지
        if (!articleId.equals(comment.getArticleId())) throw new ResponseStatusException(HttpStatus.BAD_REQUEST);

        comment.setContent(dto.getContent());
        comment.setWriter(dto.getWriter());
        return CommentDto.fromEntity(commentRepository.save(comment));
    }

    // 게시글 댓글 삭제
    public void deleteComment(Long articleId, Long commentId) {
        Optional<CommentEntity> optionalComment = commentRepository.findById(commentId);
        if (optionalComment.isEmpty()) throw new ResponseStatusException(HttpStatus.NOT_FOUND);

        CommentEntity comment = optionalComment.get();
        if (!articleId.equals(comment.getArticleId())) throw new ResponseStatusException(HttpStatus.BAD_REQUEST);

        commentRepository.deleteById(commentId);
    }
}

CommentController

package com.example.article;

import com.example.article.dto.CommentDto;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@Slf4j
@RestController
@RequestMapping("/articles/{articleId}/comments")
@RequiredArgsConstructor
public class CommentController {
    private final CommentService service;

    // POST /articles/{articleId}/comments
    @PostMapping
    public CommentDto create(
            @PathVariable("articleId") Long articleId,
            @RequestBody CommentDto dto
    ) {
        return service.createComment(articleId, dto);
    }

    // 게시글 댓글 전체 조회
    // GET /articles/{articleId}/comments
    @GetMapping
    public List<CommentDto> readAll(@PathVariable("articleId") Long articleId) {
        return service.readCommentAll(articleId);
    }

    // 게시글 댓글 수정
    // PUT /articles/{articleId}/comments/{commentId}
    @PutMapping("/{commentId}")
    public CommentDto update(
            @PathVariable("articleId") Long articleId,
            @PathVariable("commentId") Long commentId,
            @RequestBody CommentDto dto
    ) {
        return service.updateComment(articleId, commentId, dto);
    }

    // 게시글 댓글 삭제
    // DELETE /articles/{articleId}/comments/{commentId}
    @DeleteMapping("/{commentId}")
    public void delete(
            @PathVariable("articleId") Long articleId,
            @PathVariable("commentId") Long commentId
    ) {
        service.deleteComment(articleId, commentId);
    }
}

CommentDto

package com.example.article.dto;

import com.example.article.entity.CommentEntity;
import lombok.Data;

@Data
public class CommentDto {
    private Long id;
    private Long articleId;
    private String writer;
    private String content;

    public static CommentDto fromEntity(CommentEntity entity){
        CommentDto dto = new CommentDto();
        dto.setId(entity.getId());
        dto.setArticleId(entity.getArticleId());
        dto.setWriter(entity.getWriter());
        dto.setContent(entity.getContent());
        return dto;
    }
}

CommentEntity

package com.example.article.entity;

import jakarta.persistence.*;
import lombok.Data;

@Data
@Entity
@Table(name = "comments")
public class CommentEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private Long articleId;
    private String writer;
    private String content;
}

CommentRepository

package com.example.article;

import com.example.article.entity.CommentEntity;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.List;

public interface CommentRepository extends JpaRepository<CommentEntity, Long> {
    // CommentEntity 중 articleId가 id인 CommentEntity 만 반환하는 메소드
    List<CommentEntity> findAllByArticleId(Long id);
}

API TEST

POST

GET

PUT

DELETE

Query Parameter

조회를 할 때, 어떠한 조건을 붙여서 특정한 자원을 조회하기 위한 방법 → 정렬, 필터링 등

경로 뒤에 입력 데이터를 함께 제공하는 형식

? 이후에 key=value 형태로 입력하며, 인자가 많은 경우 & 로 구분한다.

? : 요구하는 자원에 대한 동적인 조건을 전달하는 용도로 사용한다. (페이지, 검색 등)

--1개의 데이터
/board?board_num=12

--다수의 데이터 &로 처리
/board?board_num=12&key=value

QueryController

@Slf4j
@RestController
public class QueryController {
    // GET /path?query=keyword&limit=20
    @GetMapping("/path")
    public Map<String, Object> queryParams(
        @RequestParam(value = "query", defaultValue = "hello") String query,
        @RequestParam(value = "limit", required = false) Integer limit
    ) {
        log.info("query=" + query);
        log.info("limit=" + limit);
        Map<String, Object> response = new HashMap<>();
        response.put("query",query);
        response.put("limit", limit);
        return response;
    }
}

Pagination

대량의 데이터를 여러 페이지로 나누는 기능이다.

JpaRepository를 이용

ArticleRepository

public interface ArticleRepository extends JpaRepository<ArticleEntity, Long> {
    // ID가 큰 순서대로 최상위 20개
    List<ArticleEntity> findTop20ByOrderByIdDesc();
    // ID가 특정 값보다 작은 데이터 중 큰 순서대로 최상위 20개
    List<ArticleEntity> findTop20ByIdLessThanOrderByIdDesc(Long id);
}

ArticleService

public List<ArticleDto> readArticlePaged() {
        List<ArticleDto> articleDtoList = new ArrayList<>();
        for (ArticleEntity entity : repository.findTop20ByOrderByIdDesc()) {
            articleDtoList.add(ArticleDto.fromEntity(entity));
        }
        return articleDtoList;
    }

ArticleController

@GetMapping("/page-test")
    public Page<ArticleEntity> readPageTest() {
        return service.readArticlePaged();
    }

100 ~ 81번 까지 20개의 article이 요청된다.

Pageable를 이용

ArticleService

public Page<ArticleEntity> readArticlePaged() {
        // PagingAndSortingRepository 메소드에 전달하는 용도
        // 조회하고 싶은 페이지의 정보를 담는 객체
        // 20개씩 페이지를 나눌때 0번 페이지를 달라고 요청하는 Pageable
        Pageable pageable = PageRequest.of(0, 20);
        Page<ArticleEntity> articleEntityPage = repository.findAll(pageable);

        List<ArticleDto> articleDtoList = new ArrayList<>();
        for (ArticleEntity entity : articleEntityPage) {
            articleDtoList.add(ArticleDto.fromEntity(entity));
        }

        return articleEntityPage;
    }

1 ~ 20까지의 정보가 표시되고, pageable에 여러 정보가 표시된다.

JPA의 PagingAndSortingRepository 인터페이스를 활용하면 repository를 건드릴 필요가 없다.
이를 사용하기 위해서는 Pageable 객체가 필요하다.
PagingAndSortingRepository 메소드에 전달하기 위한 용도로 사용된다.
조회하고 싶은 페이지의 정보를 담는 객체라고 보면 된다.

Search 기능 구현

특정 문자열이 제목에 포함되어 있는지를 기준으로 간단한 검색 기능을 구현

pageable 이용

ArticleRepository

public interface ArticleRepository
        extends JpaRepository<ArticleEntity, Long> {
    List<ArticleEntity> findTop20ByOrderByIdDesc();
    List<ArticleEntity> findTop20ByIdLessThanOrderByIdDesc(Long id);
    Page<ArticleEntity> findAllByTitleContains(String query, Pageable pageable);
}

ArticleService

public Page<ArticleDto> search(
            String query,
            Integer pageNumber
    ) {
        Pageable pageable = PageRequest.of(
                pageNumber,
                20,
                Sort.by("id").descending());
        return repository.findAllByTitleContains(query, pageable)
                .map(ArticleDto::fromEntity);
    }

ArticleController

@GetMapping("/search")
    public Page<ArticleDto> search(
            @RequestParam("query") String query,
            @RequestParam(value = "page", defaultValue = "0")
            Integer pageNumber
     ) {
        return service.search(query, pageNumber);
    }

출처 : 멋사 5기 백엔드 위키 7팀 7번방의 회고

인사이트 타임

가장 큰 수 찾기

https://school.programmers.co.kr/learn/courses/30/lessons/120899

class Solution {
    public int[] solution(int[] array) {
        int[] answer = new int[2];
        for (int i = 0; i < array.length; i++) {
            if(answer[0] < array[i]) {
                answer[0] = array[i];
                answer[1] = i;
            }
        }
        return answer;
    }
}

n보다 커질 때까지 더하기

https://school.programmers.co.kr/learn/courses/30/lessons/181884

class Solution {
    public int solution(int[] numbers, int n) {
        int i = 0;
        int sum = 0;
        while (sum <= n) sum += numbers[i++];
        return sum;
    }
}

내적

https://school.programmers.co.kr/learn/courses/30/lessons/70128

class Solution {
    public int solution(int[] a, int[] b) {
        int sum = 0;
        for (int i = 0; i < a.length; i++) sum += a[i] * b[i];
        return sum;
    }
}

easy

review

오전에 배운 이진 탐색 트리는 탐색과 삽입까지 코드를 작성했는데 아마 내일 삭제까지 구현할 것 같아서 작성하지 않았다. 결론은 그냥 어렵다.

오후 스프링 시간은 실로 유구무언. 잘되던 실습이 map 부분에서 막혀서 던지려다가 search 기능은 구현했다. 구현하건 말건 오늘이 위키날이 아님에 다시 한 번 감사할 뿐이다. 컨디션은 나쁘지 않았는데 그냥 내용 자체가 들어오질 않았다. 이거 이러다가 블로그 전체 리뷰가 이해 안된다로 도배가 될 것 같은데 ㅎ

알고리즘은 태환님이 매번 풀만한 난이도로 내주셔서 좋다. 조만간 레벨 1로 올리신다는데 정렬함수도 모르는 내겐 이제 뭐 알고리즘 하차 예고 소식과 다름 없는 것 같다. 이거나 저거나 요즘 아주 멘탈 256등분이다.

profile
반갑습니다람지
post-custom-banner

1개의 댓글

comment-user-thumbnail
2024년 2월 15일

Really great information. This is my first time coming into geometry dash lite contact with these specialized terms. Looking forward to more advanced knowledge.

답글 달기