Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,15 @@ dependencies {
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
implementation 'jakarta.validation:jakarta.validation-api:3.0.2'
implementation 'org.hibernate.validator:hibernate-validator:8.0.1.Final'
implementation 'org.springframework.boot:spring-boot-starter-validation'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'

testCompileOnly 'org.projectlombok:lombok'
testAnnotationProcessor 'org.projectlombok:lombok'

}

tasks.named('test') {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package com.gsm._8th.class4.backed.task._1._1.domain.ariticle.controller;
import com.gsm._8th.class4.backed.task._1._1.domain.ariticle.controller.request.ArticlesPatchRequestDto;
import com.gsm._8th.class4.backed.task._1._1.domain.ariticle.controller.request.ArticlesRequestDto;
import com.gsm._8th.class4.backed.task._1._1.domain.ariticle.controller.response.ArticlesResponseDto;
import com.gsm._8th.class4.backed.task._1._1.domain.ariticle.entity.ArticlesEntity;
import com.gsm._8th.class4.backed.task._1._1.domain.ariticle.repository.Articlesrepository;
import com.gsm._8th.class4.backed.task._1._1.domain.ariticle.service.ArticlesService;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

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

@RestController
@RequestMapping("/articles")
@RequiredArgsConstructor
public class ArticlesController {
private final ArticlesService articlesService;
private final Articlesrepository articlesrepository;

@GetMapping
public ResponseEntity<List<ArticlesResponseDto>> getAriticles() {
List<ArticlesResponseDto> articles = articlesService.getAll();
return ResponseEntity.ok(articles);
}
@GetMapping("/{id}")
public ResponseEntity<ArticlesResponseDto> getArticles(@PathVariable Long id) {
if(!articlesService.existsById(id)) {
return ResponseEntity.notFound().build();
}
ArticlesResponseDto articles = articlesService.getOne(id);
return ResponseEntity.ok(articles);
}
@PostMapping
public ResponseEntity<ArticlesResponseDto> createArticles(@Valid @RequestBody ArticlesRequestDto articles) {
ArticlesResponseDto article = articlesService.saveArticle(articles);
return ResponseEntity.status(201).body(article);
}
@PatchMapping("/{id}")
public ResponseEntity<ArticlesResponseDto> update(
@PathVariable Long id, @Valid @RequestBody ArticlesPatchRequestDto request) {
ArticlesResponseDto update = articlesService.updateArticle(id, request);
return ResponseEntity.ok(update);
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteArticles(@PathVariable Long id) {
articlesService.deleteById(id);
return ResponseEntity.noContent().build();
}
Comment on lines +23 to +51
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

각 메서드 사이에 개행해주세요




}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.gsm._8th.class4.backed.task._1._1.domain.ariticle.controller.request;

import jakarta.validation.constraints.NotBlank;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;


@Getter
@NoArgsConstructor
@AllArgsConstructor
public class ArticlesPatchRequestDto {
@NotBlank(message = "제목은 비어 있을 수 없습니다.")
private String title;
@NotBlank(message = "내용은 비어 있을 수 없습니다.")
private String content;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.gsm._8th.class4.backed.task._1._1.domain.ariticle.controller.request;
import lombok.AllArgsConstructor;

import lombok.Getter;
import lombok.NoArgsConstructor;
import jakarta.validation.constraints.NotBlank;

@Getter
@AllArgsConstructor
@NoArgsConstructor
public class ArticlesRequestDto {
@NotBlank(message = "제목은 비어 있을 수 없습니다.")
private String title;
@NotBlank(message = "내용은 비어 있을 수 없습니다.")
private String content;


}

Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.gsm._8th.class4.backed.task._1._1.domain.ariticle.controller.response;

import lombok.Builder;
import lombok.Getter;

@Getter
@Builder
public class ArticlesResponseDto {
private String title;
private String content;


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.gsm._8th.class4.backed.task._1._1.domain.ariticle.entity;

import com.gsm._8th.class4.backed.task._1._1.global.entity.BaseIdxEntity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@AllArgsConstructor
@NoArgsConstructor
@Entity
@Table(name = "Article")
@Getter
@Setter
public class ArticlesEntity extends BaseIdxEntity {
@Column(nullable = false)
private String title;

@Column(nullable = false , columnDefinition = "TEXT")
private String content;
public void update(String title, String content) {
if(title != null)
{
this.title = title;
}
if(content != null){
this.content = content;
}
}
Comment on lines +24 to +32
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

update 메서드에서 이러한 null 검사를 하기보단 애초에 서비스 로직에서 null검사를 하는것이 더 깔끔하지 않을까요?

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.gsm._8th.class4.backed.task._1._1.domain.ariticle.repository;

import com.gsm._8th.class4.backed.task._1._1.domain.ariticle.entity.ArticlesEntity;
import org.springframework.data.jpa.repository.JpaRepository;

public interface Articlesrepository extends JpaRepository<ArticlesEntity, Long> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.gsm._8th.class4.backed.task._1._1.domain.ariticle.service;

import com.gsm._8th.class4.backed.task._1._1.domain.ariticle.controller.request.ArticlesPatchRequestDto;
import com.gsm._8th.class4.backed.task._1._1.domain.ariticle.controller.request.ArticlesRequestDto;
import com.gsm._8th.class4.backed.task._1._1.domain.ariticle.controller.response.ArticlesResponseDto;
import jakarta.validation.Valid;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public interface ArticlesService {
// 게시글 모든 글 조회
List<ArticlesResponseDto> getAll();
//단일 게시글 조회
ArticlesResponseDto getOne(Long id);
//게시글 생성
ArticlesResponseDto saveArticle(ArticlesRequestDto request);
//게시글 수정
ArticlesResponseDto updateArticle(Long id, @Valid ArticlesPatchRequestDto request);
//게시글 삭제
void deleteById(Long id);
//게시글 존재 여부 확인
boolean existsById(Long id);
//수정 요청 데이터 확인
boolean hasValidPatchData(@Valid ArticlesPatchRequestDto request);
//유효성 검사
boolean checkRequest(@Valid ArticlesRequestDto request);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package com.gsm._8th.class4.backed.task._1._1.domain.ariticle.service;

import com.gsm._8th.class4.backed.task._1._1.domain.ariticle.controller.request.ArticlesPatchRequestDto;
import com.gsm._8th.class4.backed.task._1._1.domain.ariticle.controller.request.ArticlesRequestDto;
import com.gsm._8th.class4.backed.task._1._1.domain.ariticle.controller.response.ArticlesResponseDto;
import com.gsm._8th.class4.backed.task._1._1.domain.ariticle.entity.ArticlesEntity;

import com.gsm._8th.class4.backed.task._1._1.domain.ariticle.repository.Articlesrepository;
import jakarta.persistence.Entity;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

@Service
@RequiredArgsConstructor

public class ArticlesServiceImpl implements ArticlesService {

private final Articlesrepository articlesrepository;

@Override
@Transactional(readOnly = true)
public List<ArticlesResponseDto> getAll(){
List<ArticlesEntity> entities = articlesrepository.findAll();
List<ArticlesResponseDto> dtos = entities.stream()
.map(entity -> convertToResponseDto(entity))
.collect(Collectors.toList());
Collections.reverse(dtos);
return dtos;


}
@Override
@Transactional(readOnly = true)
public ArticlesResponseDto getOne(Long id) {
Optional<ArticlesEntity> result = articlesrepository.findById(id);

if(result.isPresent()){
ArticlesEntity entity = result.get();
return convertToResponseDto(entity);
}
else {
return null;
}

}

@Override
@Transactional
public ArticlesResponseDto saveArticle(ArticlesRequestDto request) {
if(!checkRequest(request)){
throw new IllegalArgumentException("잘못된 요청입니다");
}
ArticlesEntity entity = new ArticlesEntity(request.getTitle(), request.getContent());
ArticlesEntity savedEntity = articlesrepository.save(entity);
return convertToResponseDto(savedEntity);
}
@Override
@Transactional
public ArticlesResponseDto updateArticle(Long id, ArticlesPatchRequestDto request) {
ArticlesEntity entity = articlesrepository.findById(id)
.orElseThrow(() -> new IllegalArgumentException("해당글은 존재하지않습니다"));

if(!hasValidPatchData(request)){
throw new IllegalArgumentException("수정할 데이터가 없습니다.");

}
Optional.ofNullable(request.getTitle()).ifPresent(entity::setTitle);
Optional.ofNullable(request.getContent()).ifPresent(entity::setContent);
ArticlesEntity savedEntity = articlesrepository.save(entity);
return convertToResponseDto(savedEntity);
}

public void deleteById(Long id) {
ArticlesEntity entity = articlesrepository.findById(id)
.orElseThrow(() -> new IllegalArgumentException("삭제할 글이 존재하지 않습니다."));
Comment on lines +67 to +81
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IllegalArgumentException을 발행하는 것이 아니라 커스텀 예외 클래스를 구현 후 GlobalExceptionHandler를 구현하여 이를 캐치하는 구조로 만들면 좋지 않을까요?

articlesrepository.delete(entity);
}

@Override
public boolean existsById(Long id) {

return articlesrepository.existsById(id);
}

@Override
public boolean hasValidPatchData(ArticlesPatchRequestDto request) {
return existsTitle(request) || existsContent(request);
}

@Override
public boolean checkRequest(ArticlesRequestDto request) {
// title과 content가 모두 null이 아니고 비어있지 않은지 확인
return request.getTitle() != null && !request.getTitle().trim().isEmpty() &&
request.getContent() != null && !request.getContent().trim().isEmpty();
}


private boolean existsTitle(ArticlesPatchRequestDto request) { // 타입 변경
return request.getTitle() != null;
}

private boolean existsContent(ArticlesPatchRequestDto request) { // 타입 변경
return request.getContent() != null;
}

private ArticlesResponseDto convertToResponseDto(ArticlesEntity entity) {
return ArticlesResponseDto.builder()
.title(entity.getTitle())
.content(entity.getContent())
.build();
}

}



Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@

import jakarta.persistence.*;
import lombok.Getter;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;

@Getter
@MappedSuperclass
abstract class BaseIdxEntity {
@Component
public abstract class BaseIdxEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "idx", nullable = false, updatable = false, insertable = false, unique = true)
Expand Down