Server/Spring Boot

[Spring Boot] Toy Project κ²Œμ‹œνŒ λ§Œλ“€κΈ° (3) - 쑰회, μ‚­μ œ

Nellie29 2023. 12. 18. 00:28


1️⃣ ResponseDto 생성

πŸ’š BoardReadDto

  • κ²Œμ‹œκΈ€ 단독 쑰회 μ‹œ μ‚¬μš©
package com.gdsc_teamb.servertoyproject.dto.boardDto;

import com.gdsc_teamb.servertoyproject.domain.post.domain.PostEntity;
import com.gdsc_teamb.servertoyproject.domain.user.domain.UserEntity;
import lombok.Getter;

// κ²Œμ‹œκΈ€ 쑰회 μ‹œ μ‚¬μš©
@Getter
public class BoardReadDto {

    private Long id; // κ²Œμ‹œκΈ€ ID
    private String title; // κ²Œμ‹œκΈ€ 제λͺ©
    private String content; // κ²Œμ‹œκΈ€ λ‚΄μš©
    private String nickname; // μž‘μ„±μž 정보

    // BoardReadDto 의 μƒμ„±μž
    public BoardReadDto(PostEntity entity){
        this.id=entity.getId();
        this.content=entity.getContent();
        this.title=entity.getTitle();
        this.nickname=entity.getUser().getNickname();
    }
}

πŸ’š BoardListDto

  • κ²Œμ‹œκΈ€ λͺ©λ‘ 쑰회 μ‹œ μ‚¬μš©
package com.gdsc_teamb.servertoyproject.dto.boardDto;

import com.gdsc_teamb.servertoyproject.domain.post.domain.PostEntity;
import com.gdsc_teamb.servertoyproject.domain.user.domain.UserEntity;
import lombok.Getter;

import java.time.LocalDateTime;

// κ²Œμ‹œκΈ€ λͺ©λ‘ 쑰회 μ‹œ μ‚¬μš©
@Getter
public class BoardListDto {

    private Long id; // κ²Œμ‹œκΈ€ ID
    private String title; // κ²Œμ‹œκΈ€ 제λͺ©
    private String nickname; // μž‘μ„±μž λ‹‰λ„€μž„
    private LocalDateTime updated_at; // μˆ˜μ • 일자

    // BoardListDto 의 μƒμ„±μž
    public BoardListDto(PostEntity entity){
        this.id=entity.getId();
        this.title=entity.getTitle();
        this.nickname=entity.getUser().getNickname(); // UserEntity μ—μ„œ nickname κ°€μ Έμ˜€κΈ°
        this.updated_at=entity.getUpdated_at();
    }
}

2️⃣ Service 생성

πŸ’š BoardService

package com.gdsc_teamb.servertoyproject.service;

import com.gdsc_teamb.servertoyproject.domain.post.domain.PostEntity;
import com.gdsc_teamb.servertoyproject.domain.repository.PostRepository;
import com.gdsc_teamb.servertoyproject.domain.user.domain.UserEntity;
import com.gdsc_teamb.servertoyproject.domain.user.domain.UserRepository;
import com.gdsc_teamb.servertoyproject.dto.boardDto.BoardDto;
import com.gdsc_teamb.servertoyproject.dto.boardDto.BoardListDto;
import com.gdsc_teamb.servertoyproject.dto.boardDto.BoardReadDto;
import com.gdsc_teamb.servertoyproject.dto.boardDto.BoardUpdateDto;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor

public class BoardService {
    private final PostRepository postRepository;
    private final UserRepository userRepository;

    @Transactional
    public ResponseEntity<Object> findById (Long id) {
        try {
            // boardRepository μ—μ„œ μ£Όμ–΄μ§„ id에 ν•΄λ‹Ήν•˜λŠ” κ²Œμ‹œκΈ€μ„ λ°μ΄ν„°λ² μ΄μŠ€μ—μ„œ 쑰회
            PostEntity post = postRepository.findById(id)
                    .orElseThrow(() -> new IllegalArgumentException());
            return ResponseEntity.ok(new BoardReadDto(post));
        } catch (Exception e) {
            return ResponseEntity.badRequest().body("κ²Œμ‹œκΈ€ μ‘°νšŒμ— μ‹€νŒ¨ν–ˆμŠ΅λ‹ˆλ‹€. " + e.getMessage());
        }

    }
    
    @Transactional (readOnly=true)
    public List<BoardListDto> findAllByOrderByIdDesc(){
        return postRepository.findAllByOrderByIdDesc().stream()
                // BoardRepository 의 findAllDesc λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•˜μ—¬ κ²Œμ‹œκΈ€μ„ λ‚΄λ¦Όμ°¨μˆœμœΌλ‘œ 쑰회 (쿼리 κΈ°λŠ₯)
                .map(BoardListDto::new)// 각 κ²Œμ‹œκΈ€μ„ BoardListDto 둜 λ³€ν™˜
                .collect(Collectors.toList()); // 이후 리슀트둜 μˆ˜μ§‘ν•˜μ—¬ λ°˜ν™˜
    }

    @Transactional
    public ResponseEntity<Object> deletePost(Long id){
        try{
            // boardRepository μ—μ„œ μ£Όμ–΄μ§„ id에 ν•΄λ‹Ήν•˜λŠ” κ²Œμ‹œκΈ€μ„ λ°μ΄ν„°λ² μ΄μŠ€μ—μ„œ 쑰회
            PostEntity post= postRepository.findById(id)
                    .orElseThrow(() -> new IllegalArgumentException("ν•΄λ‹Ή κ²Œμ‹œκΈ€μ΄ μ—†μŠ΅λ‹ˆλ‹€. id="+id));
            // JpaRepository μ—μ„œ κΈ°λ³Έ μ œκ³΅λ˜λŠ” delete λ©”μ„œλ“œ
            // μ£Όμ–΄μ§„ post μ—”ν‹°ν‹°λ₯Ό λ°μ΄ν„°λ² μ΄μŠ€μ—μ„œ μ‚­μ œν•˜λŠ” μ—­ν• 
            postRepository.delete(post);
            return ResponseEntity.ok("κ²Œμ‹œκΈ€μ΄ μ„±κ³΅μ μœΌλ‘œ μ‚­μ œλ˜μ—ˆμŠ΅λ‹ˆλ‹€.");
        }catch (Exception e){
            return ResponseEntity.badRequest().body("κ²Œμ‹œκΈ€ μ‚­μ œμ— μ‹€νŒ¨ν–ˆμŠ΅λ‹ˆλ‹€. " + e.getMessage());
        }

    }


}

πŸ“Œ findById()

  • κ²Œμ‹œκΈ€ μ„ΈλΆ€ λ‚΄μš© 쑰회
  • idλŠ” μ‘°νšŒν•  κ²Œμ‹œκΈ€μ˜ IDλ₯Ό λ‹΄μŒ
  • 쑰회된 κ²Œμ‹œκΈ€μ˜ μ„ΈλΆ€ λ‚΄μš©μ„ 담은 BoardReadDtoλ₯Ό return

πŸ“Œ findAllByOrderByIdDesc()

  • κ²Œμ‹œκΈ€ λͺ©λ‘ 쑰회
  • κ²Œμ‹œκΈ€ λͺ©λ‘μ„ 담은 List<BoardListDto> return

πŸ“Œ deletePost()

  • κ²Œμ‹œκΈ€ μ‚­μ œ
  • idλŠ” μ‚­μ œν•  κ²Œμ‹œκΈ€μ˜ ID

3️⃣ Controller 생성

πŸ’š BoardController

package com.gdsc_teamb.servertoyproject.controller;

import com.gdsc_teamb.servertoyproject.dto.boardDto.BoardDto;
import com.gdsc_teamb.servertoyproject.dto.boardDto.BoardListDto;
import com.gdsc_teamb.servertoyproject.dto.boardDto.BoardReadDto;
import com.gdsc_teamb.servertoyproject.dto.boardDto.BoardUpdateDto;
import com.gdsc_teamb.servertoyproject.service.BoardService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequiredArgsConstructor

public class BoardController {
    private final BoardService boardService;

    @GetMapping("/api/boards/{id}")
    public ResponseEntity<Object> findById (@PathVariable Long id){
        return boardService.findById(id);
    }

    @GetMapping("/api/boards")
    public List<BoardListDto> findAllDesc() {
        return boardService.findAllByOrderByIdDesc();
    }

    @DeleteMapping("/api/boards/{id}")
    public Long delete(@PathVariable Long id){
        boardService.deletePost(id);
        return id;
    }
}

πŸ“Œ findById()

  • HTTP GET μš”μ²­μ„ 톡해 νŠΉμ • ID의 κ²Œμ‹œκΈ€μ„ 쑰회
  • λ§€κ°œλ³€μˆ˜: μ‘°νšŒν•  κ²Œμ‹œκΈ€μ˜ IDλ₯Ό μ „λ‹¬λ°›μŒ
  • @PathVariable을 톡해 κ²½λ‘œμ—μ„œ μΆ”μΆœλœ κ²Œμ‹œκΈ€μ˜ IDλ₯Ό 전달 λ°›μŒ
  • λ°˜ν™˜ κ°’: 쑰회된 κ²Œμ‹œκΈ€μ˜ μ„ΈλΆ€ 정보가 λ‹΄κΈ΄ DTOλ₯Ό λ°˜ν™˜

πŸ“Œ findAllDesc()

  • HTTP GET μš”μ²­μ„ 톡해 λͺ¨λ“  κ²Œμ‹œκΈ€ λͺ©λ‘μ„ 쑰회
  • λ°˜ν™˜ κ°’: 쑰회된 λͺ¨λ“  κ²Œμ‹œκΈ€μ˜ λͺ©λ‘μ΄ λ‹΄κΈ΄ DTO 듀을 리슀트둜 λ°˜ν™˜

πŸ“Œ delete()

  • HTTP DELETE μš”μ²­μ„ 톡해 νŠΉμ • ID의 κ²Œμ‹œκΈ€μ„ μ‚­μ œ
  • λ§€κ°œλ³€μˆ˜: μ‚­μ œν•  κ²Œμ‹œκΈ€μ˜ IDλ₯Ό 전달 λ°›μŒ
  • @PathVariable 을 톡해 κ²½λ‘œμ—μ„œ μΆ”μΆœλœ κ²Œμ‹œκΈ€μ˜ IDλ₯Ό 전달 λ°›μŒ
  • λ°˜ν™˜ κ°’: μ‚­μ œκ°€ μ™„λ£Œλœ κ²Œμ‹œκΈ€μ˜ IDλ₯Ό λ°˜ν™˜

4️⃣ TestCode

πŸ’š BoardControllerTest

  • κ²Œμ‹œκΈ€ μ‚­μ œ 톡신이 잘 μ΄λ£¨μ–΄μ§€λŠ”μ§€μ— λŒ€ν•œ ν…ŒμŠ€νŠΈ μ½”λ“œ
  • MocMvc μ‚¬μš©ν•˜μ—¬ 톡신
package com.gdsc_teamb.servertoyproject.controller;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.gdsc_teamb.servertoyproject.domain.post.domain.HeartEntity;
import com.gdsc_teamb.servertoyproject.domain.post.domain.PostEntity;
import com.gdsc_teamb.servertoyproject.domain.repository.PostRepository;
import com.gdsc_teamb.servertoyproject.domain.user.domain.UserRepository;
import com.gdsc_teamb.servertoyproject.domain.user.domain.UserEntity;
import com.gdsc_teamb.servertoyproject.dto.boardDto.BoardDto;
import com.gdsc_teamb.servertoyproject.dto.boardDto.BoardUpdateDto;
import jakarta.transaction.Transactional;
import org.junit.jupiter.api.*;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.test.web.server.LocalServerPort;
import org.springframework.http.*;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.servlet.MockMvc;

import java.util.List;
import java.util.Map;
import java.util.Optional;

import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.test.util.AssertionErrors.fail;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;


@ExtendWith(SpringExtension.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
@Transactional
class BoardControllerTest {
    // ν…ŒμŠ€νŠΈμ— μ‚¬μš©ν•  μ„œλ²„μ˜ 랜덀 포트 번호λ₯Ό ν• λ‹Ή
    @LocalServerPort
    private int port;
    @Autowired
    private PostRepository postRepository;
    @Autowired
    private UserRepository userRepository;
    @Autowired
    private MockMvc mockMvc;

    @Test
    @DisplayName("κ²Œμ‹œλ¬Ό μ‚­μ œ ν…ŒμŠ€νŠΈ")
    public void deletePost() throws Exception{
        UserEntity savedUser = userRepository.save(UserEntity.builder()
                .email("abc@abc.com")
                .password("password1234")
                .nickname("nickname")
                .phone("01012345678")
                .build());

        PostEntity savedPost = postRepository.save(PostEntity.builder()
                .title("title")
                .content("content")
                .user(savedUser)
                .build());

        Long deleteId = savedPost.getId();
        String url="http://localhost:" + port + "/api/boards/"+deleteId;

        // when
        mockMvc.perform(delete(url))
                .andExpect(status().isOk());

        // then
        // μ‚­μ œν•œ 데이터 쑰회
        Optional<PostEntity> deletedPost = postRepository.findById(savedPost.getId());
        assertThat(deletedPost).isNotPresent();
    }
}