[Spring Boot] aLog Project - 등록/수정/조회 API 구현하기

김광현·2023년 9월 1일
0

Project - aLog

목록 보기
3/12
post-thumbnail

[Spring Boot] aLog Project - JPA 사용하기 에 이어서 등록/수정/조회 API를 구현합니다. 🏆


💻 등록

PostsApiController.java

controller 패키지에 PostsApiController.java 클래스를 만듭니다.

package com.aLog.controller;

@RequiredArgsConstructor
@RestController
public class PostsApiController {
    
    private final PostsService postsService;
    
    @PostMapping("/api/v1/posts")
    public Long save(@RequestBody PostsSaveRequestDto requestDto) {
        return postsService.save(requestDto);
    }
}

PostsService.java

service 패키지를 생성한 후 PostsService 클래스를 만듭니다.

package com.aLog.service;

@RequiredArgsConstructor
@Service
public class PostsService {
    
    private final PostsRepository postsRepository;
    
    @Transactional
    public Long save(PostsSaveRequestDto requestDto) {
        return postsRepository.sava(requestDto.toEntity()).getId();
    }
}

⚠️ 스프링에선 Bean을 주입받는 방식이 @Autowired, setter, 생성자가 있지만, 가장 권장하는 방식은 '생성자'로 주입받는 방식입니다.

@RequiredArgsConstructor에서 final이 선언된 모든 필드를 인자값으로 하는 생성자를 생성해줍니다.


PostsSaveRequestDto.java

dto 패키지에 PostsSaveRequestDto.java 클래스를 만듭니다.

package com.aLog.dto;

@Getter
@NoArgsConstructor
public class PostsSaveRequestDto {
    
    private String title;
    private String content;
    private String author;
    
    @Builder
    public PostsSaveRequestDto(String title, String content, String author) {
        this.title = title;
        this.content = content;
        this.author = author;
    }
    
    public Posts toEntity() {
        return Posts.builder()
                .title(title)
                .content(content)
                .author(author)
                .build();
    }
}

⚠️ Entity 클래스Request/Response 클래스로 사용해서는 안 됩니다.


✅ Entity 클래스를 Request/Response 클래스로 사용해서는 안되는 간략한 이유

엔티티는 데이터베이스 영속성(persistent)의 목적으로 사용되는 객체이고
요청, 응답을 전달하는 클래스로 함께 사용될 경우 변경될 가능성이 크며,
데이터베이스에 직접적인 영향을 줘서 DTO를 분리합니다.

자세한 내용이 궁금하신 분은 [Spring Boot] Entity 클래스를 Request/Response 클래스로 사용해서는 안되는 이유 글을 참고해 주시길 바랍니다.


PostsApiControllerTest.java

테스트 패키지 중 controller 패키지에 PostsApiControllerTest.java를 생성합니다.

package com.aLog.controller;

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class PostsApiControllerTest {
    
    @LocalServerPort
    private int port;
    
    @Autowired
    private TestRestTemplate restTemplate;
    
    @Autowired
    private PostsRepository postsRepository;
    
    @After
    public void tearDown() throws Exception {
        postsRepository.deleteAll();
    }
    
    @Test
    public void Posts_등록된다() throws Exception {
        String title = "title";
        String content = "content";
        PostsSaveRequestDto requestDto = PostsSaveRequestDto.builder()
                .title(title)
                .content(content)
                .author("author")
                .build();
        
        String url = "http://localhost:" + port + "/api/v1/posts";
        
        ResponseEntity<Long> responseEntity = restTemplate.postForEntity(url, requestDto, Long.class);
        
        assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK);
        assertThat(responseEntity.getBody()).isGreaterThan(0L);

        List<Posts> all = postsRepository.findAll();
        assertThat(all.get(0).getTitle()).isEqualTo(title);
        assertThat(all.get(0).getContent()).isEqualTo(content);
    }
}


💻 수정/조회

PostsApiController.java

package com.aLog.controller;

@RequiredArgsConstructor
@RestController
public class PostsApiController {

    ...
    
    @PutMapping("/api/v1/posts/{id}")
    public Long update(@PathVariable Long id, @RequestBody PostsUpdateRequestDto requestDto) {
        return postsService.update(id, requestDto);
    }
    
    @GetMapping("/api/v1/posts/{id}")
    public PostsReponseDto findById (@PathVariable Long id) {
        return postsService.findById(id);
    }
}

PostsResponseDto.java

package com.aLog.dto;

@Getter
public class PostsResponseDto {
    
    private Long id;
    private String title;
    private String content;
    private String author;
    
    public PostsResponseDto(Posts entity) {
        this.id = entity.getId();
        this.title = entity.getTitle();
        this.content = entity.getContent();
        this.author = entity.getAuthor();
    }
}

PostsUpdateRequestDto.java

package com.aLog.dto;

@Getter
@NoArgsConstructor
public class PostsUpdateRequestDto {
    private String title;
    private String content;
    
    @Builder
    public PostsUpdateRequestDto(String title, String content) {
        this.title = title;
        this.content = content;
    }
}

Posts.java

package com.aLog.domain.posts;

@Getter
@NoArgsConstructor
@Entity
public class Posts {

    ...
    
    public void update(String title, String content) {
        this.title = title;
        this.content = content;
    }
}

PostsService

package com.aLog.service;

@RequiredArgsConstructor
@Service
public class PostsService {

    ...
    
    @Transactional
    public Long update(Long id, PostsUpdateRequestDto requestDto) {
        Posts posts = postsRepository.findById(id)
                .orElseThrow(() -> new IllegalArgumentException("해당 게시글이 없습니다. id="+ id));
        
        posts.update(requestDto.getTitle(), requestDto.getContent());
        
        return id;
    }
    
    public PostsResponseDto findById (Long id) {
        Posts entity = postsRepository.findById(id)
                .orElseThrow(() -> new IllegalArgumentException("해당 게시글이 없습니다. id"+ id));
        
        return new PostsResponseDto(entity);
    }
}

PostsApiControllerTest.java

package com.aLog.controller;

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class PostsApiControllerTest {

    ...

    @Test
    public void Posts_수정된다() throws Exception {
        Posts savedPosts = postsRepository.save(Posts.builder()
                .title("title")
                .content("content")
                .author("author")
                .build());

        Long updateId = savedPosts.getId();
        String expectedTitle = "title2";
        String expectedContent = "content2";

        PostsUpdateRequestDto requestDto = PostsUpdateRequestDto.builder()
                .title(expectedTitle)
                .content(expectedContent)
                .build();

        String url = "http://localhost:" + port + "/api/v1/posts/" + updateId;

        HttpEntity<PostsUpdateRequestDto> requestEntity = new HttpEntity<>(requestDto);

        ResponseEntity<Long> responseEntity = restTemplate.
                exchange(url, HttpMethod.PUT, requestEntity, Long.class);

        assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK);
        assertThat(responseEntity.getBody()).isGreaterThan(0L);

        List<Posts> all = postsRepository.findAll();
        assertThat(all.get(0).getTitle()).isEqualTo(expectedTitle);
        assertThat(all.get(0).getContent()).isEqualTo(expectedContent);
    }
}


application.yml

H2 데이터베이스를 직접 접근하기 위한 코드를 작성해줍니다.

...
h2:
  console:
    enabled: true

✔ Spring Data Jpa를 이용하여 관계형 데이터베이스를 객체지향적으로 관리하는 방법에 대해 알아봤습니다.

profile
🅸nterest 🆂olving 🆃horough 🅹udgment

0개의 댓글