Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: 잡포스트 리팩토링 #77

Closed
wants to merge 10 commits into from
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.*;

import java.util.List;
Expand Down Expand Up @@ -66,9 +67,13 @@ public ResponseEntity<List<ApplyDTO>> findAll(Member member) {
)
}
)
// @PostMapping
// public ResponseEntity<ApplyDTO> registerApply(@RequestBody ApplyDTO applyDTO) {
// return ResponseEntity.ok(applyService.register(applyDTO));
// }
@PostMapping
public ResponseEntity<ApplyDTO> registerApply(@RequestBody ApplyDTO applyDTO) {
return ResponseEntity.ok(applyService.register(applyDTO));
public ResponseEntity<?> registerApply(@RequestBody ApplyDTO applyDTO) {
return applyService.register(applyDTO);
}

@Operation(
Expand Down Expand Up @@ -101,8 +106,9 @@ public ResponseEntity<ApplyDTO> registerApply(@RequestBody ApplyDTO applyDTO) {
schema = @Schema(type = "integer")
)
@GetMapping("/{id}")
public ResponseEntity<ApplyDTO> readApply(@PathVariable("id") Long id) {
return ResponseEntity.ok(applyService.read(id));
public ResponseEntity<ApplyDTO> readApply(@PathVariable("id") Long id, @AuthenticationPrincipal Member member) {
ApplyDTO applyDTO = applyService.readByIdAndMember(id, member);
return ResponseEntity.ok(applyDTO);
}

@Operation(
Expand Down
Copy link
Collaborator

Choose a reason for hiding this comment

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

MethodArgumentNotValidException, ConstraintViolationException은 GlobalControllerAdvice에서 처리하고 있으므로 제외해야 할 것 같습니다.

Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.prgrms2.java.bitta.apply.controller.advice;

import com.prgrms2.java.bitta.apply.exception.ApplyTaskException;
import jakarta.validation.ConstraintViolationException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.util.Map;

@RestControllerAdvice
public class ApplyControllerAdvice {

@ExceptionHandler(ApplyTaskException.class)
public ResponseEntity<Map<String, String>> handleApplyTaskException(ApplyTaskException e) {
return ResponseEntity.status(e.getCode())
.body(Map.of("error", e.getMessage()));
}

@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Map<String, String>> handleValidationException(MethodArgumentNotValidException e) {
return ResponseEntity.status(e.getStatusCode())
.body(Map.of("error", e.getMessage()));
}

@ExceptionHandler(ConstraintViolationException.class)
public ResponseEntity<Map<String, String>> handleConstraintViolationException(ConstraintViolationException e) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(Map.of("error", e.getMessage()));
}
}

9 changes: 9 additions & 0 deletions src/main/java/com/prgrms2/java/bitta/apply/dto/ApplyDTO.java
Copy link
Collaborator

Choose a reason for hiding this comment

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

jobPostId와 memberId의 @NotNull 메시지가 동일한 이유가 궁금합니다.
또한 @Min()으로 설정해주셨는데, 관련 에러 메시지도 명시해주셨으면 합니다.

Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package com.prgrms2.java.bitta.apply.dto;

import com.prgrms2.java.bitta.apply.entity.Apply;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
Expand All @@ -15,14 +18,20 @@
@Schema(title = "지원서 DTO", description = "지원서 요청 및 응답에 사용하는 DTO입니다.")
public class ApplyDTO {
@Schema(title = "지원서 ID (PK)", description = "지원서의 고유 ID 입니다.", example = "1", minimum = "1")
@Min(1)
private Long id;

@Schema(title = "일거리 ID (FK)", description = "일거리의 고유 ID 입니다.", example = "1", minimum = "1")
@Min(1)
@NotNull(message = "게시글의 ID가 필요합니다")
private Long jobPostId;

@Schema(title = "회원 ID (FK)", description = "회원의 고유 ID 입니다.", example = "1", minimum = "1")
@Min(1)
@NotNull(message = "게시글의 ID가 필요합니다")
private Long memberId;

@Schema(title = "지원서 생성일시", description = "지원서가 생성된 날짜 및 시간입니다.", example = "2023-09-24T14:45:00")
private LocalDateTime appliedAt;

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,9 @@ public interface ApplyRepository extends JpaRepository<Apply, Long> {

@Query("SELECT a FROM Apply a WHERE a.id = :id")
Optional<ApplyDTO> getApplyDTO(@Param("id") Long id);

@Query("DELETE FROM Apply a WHERE a.id = :id")
int deleteByIdAndReturnCount(Long id);

Optional<Apply> findByIdAndMember(Long id, Member member);
}
YooJHyun marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,19 @@

import com.prgrms2.java.bitta.apply.dto.ApplyDTO;
import com.prgrms2.java.bitta.member.entity.Member;
import org.springframework.http.ResponseEntity;

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

public interface ApplyService {
List<ApplyDTO> readAll(Member member);

ApplyDTO register(ApplyDTO applyDTO);
ResponseEntity<Map<String, Object>> register(ApplyDTO applyDTO);

void delete(Long id);

ApplyDTO read(Long id);

ApplyDTO readByIdAndMember(Long id, Member member);
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@
import com.prgrms2.java.bitta.member.entity.Member;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;

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

@Slf4j
@Service
Expand All @@ -33,12 +33,29 @@ public List<ApplyDTO> readAll(Member member) {
return applies.stream().map(this::entityToDto).toList();
}

// @Override
// public ApplyDTO register(ApplyDTO applyDTO) {
// try {
// Apply apply = dtoToEntity(applyDTO);
// apply = applyRepository.save(apply);
// return entityToDto(apply);
// } catch (Exception e) {
// log.error(e.getMessage());
// throw ApplyException.NOT_REGISTERED.get();
// }
// }

@Override
public ApplyDTO register(ApplyDTO applyDTO) {
public ResponseEntity<Map<String, Object>> register(ApplyDTO applyDTO) {
try {
Apply apply = dtoToEntity(applyDTO);
apply = applyRepository.save(apply);
return entityToDto(apply);

Map<String, Object> response = new HashMap<>();
response.put("message", apply.getMember().getId() + "님 지원 완료");
response.put("data", entityToDto(apply));

return ResponseEntity.ok(response);
} catch (Exception e) {
log.error(e.getMessage());
throw ApplyException.NOT_REGISTERED.get();
Expand All @@ -47,13 +64,7 @@ public ApplyDTO register(ApplyDTO applyDTO) {

@Override
public void delete(Long id) {
Optional<Apply> deleteApply = applyRepository.findById(id);
Apply apply = deleteApply.orElseThrow(ApplyException.NOT_FOUND::get);

try {
applyRepository.delete(apply);
} catch (Exception e) {
log.error(e.getMessage());
if (applyRepository.deleteByIdAndReturnCount(id) == 0) {
throw ApplyException.NOT_REMOVED.get();
}
}
Expand All @@ -64,6 +75,14 @@ public ApplyDTO read(Long id) {
return applyDTO.orElseThrow(ApplyException.NOT_FOUND::get);
}

@Override
public ApplyDTO readByIdAndMember(Long id, Member member) {
Apply apply = applyRepository.findByIdAndMember(id, member)
.orElseThrow(ApplyException.NOT_FOUND::get);
return entityToDto(apply);
}


private Apply dtoToEntity(ApplyDTO applyDTO) {
return Apply.builder()
.id(applyDTO.getId())
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.prgrms2.java.bitta.apply.util;

import com.prgrms2.java.bitta.apply.dto.ApplyDTO;
import com.prgrms2.java.bitta.apply.entity.Apply;
import com.prgrms2.java.bitta.apply.repository.ApplyRepository;
import lombok.RequiredArgsConstructor;
Expand Down
Copy link
Collaborator

Choose a reason for hiding this comment

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

데이터 검증을 사용하는 어노테이션에는 @Valid@Validated가 있습니다.
약간 수정이 필요한 부분이 있는 것 같습니다. 아래 링크를 참고하여 수정해주셨으면 합니다.
필요하시다면 예시로 FeedController를 참고해주세요.
https://medium.com/sjk5766/valid-vs-validated-%EC%A0%95%EB%A6%AC-5665043cd64b

Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
package com.prgrms2.java.bitta.jobpost.controller;

import com.prgrms2.java.bitta.jobpost.dto.JobPostDTO;
import com.prgrms2.java.bitta.jobpost.dto.PageRequestDTO;
import com.prgrms2.java.bitta.jobpost.service.JobPostService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.constraints.Min;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.data.domain.Page;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.util.List;
Expand All @@ -21,7 +25,7 @@
@Tag(name = "일거리 API 컨트롤러", description = "일거리와 관련된 REST API를 제공하는 컨틀롤러입니다.")
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/v1/jobpost")
@RequestMapping("/api/v1/job-post")
@Log4j2
public class JobPostController {
private final JobPostService jobPostService;
Expand All @@ -48,9 +52,14 @@ public class JobPostController {
)
}
)
// @GetMapping
// public ResponseEntity<List<JobPostDTO>> findAll() {
// return ResponseEntity.ok(jobPostService.getList());
// }

@GetMapping
public ResponseEntity<List<JobPostDTO>> findAll() {
return ResponseEntity.ok(jobPostService.getList());
public ResponseEntity<Page<JobPostDTO>> getList(@Validated PageRequestDTO pageRequestDTO) {
return ResponseEntity.ok(jobPostService.getList(pageRequestDTO));
}

@Operation(
Expand All @@ -76,7 +85,7 @@ public ResponseEntity<List<JobPostDTO>> findAll() {
}
)
@PostMapping
public ResponseEntity<JobPostDTO> registerJobPost(@RequestBody JobPostDTO jobPostDTO) {
public ResponseEntity<JobPostDTO> registerJobPost(@Validated @RequestBody JobPostDTO jobPostDTO) {
return ResponseEntity.ok(jobPostService.register(jobPostDTO));
}

Expand Down Expand Up @@ -110,7 +119,7 @@ public ResponseEntity<JobPostDTO> registerJobPost(@RequestBody JobPostDTO jobPos
schema = @Schema(type = "integer")
)
@GetMapping("/{id}")
public ResponseEntity<JobPostDTO> readJobPost(@PathVariable("id") Long id) {
public ResponseEntity<JobPostDTO> readJobPost(@PathVariable("id") @Min(1) Long id) {
return ResponseEntity.ok(jobPostService.read(id));
}

Expand Down Expand Up @@ -152,7 +161,7 @@ public ResponseEntity<JobPostDTO> readJobPost(@PathVariable("id") Long id) {
schema = @Schema(type = "integer")
)
@PutMapping("/{id}")
public ResponseEntity<JobPostDTO> modifyJobPost(@RequestBody JobPostDTO jobPostDTO, @PathVariable("id") Long id) {
public ResponseEntity<JobPostDTO> modifyJobPost(@RequestBody JobPostDTO jobPostDTO, @Min(1) @PathVariable("id") Long id) {
return ResponseEntity.ok(jobPostService.modify(jobPostDTO));
}

Expand Down Expand Up @@ -194,8 +203,8 @@ public ResponseEntity<JobPostDTO> modifyJobPost(@RequestBody JobPostDTO jobPostD
schema = @Schema(type = "integer")
)
@DeleteMapping("/{id}")
public ResponseEntity<Map<String, String>> deleteJobPost(@PathVariable("id") Long id) {
public ResponseEntity<Map<String, String>> deleteJobPost(@PathVariable("id") @Min(1) Long id) {
jobPostService.remove(id);
return ResponseEntity.ok(Map.of("message", "success"));
return ResponseEntity.ok(Map.of("message", "delete success"));
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

마찬가지로 MethodArgumentNotValidException, ConstraintViolationException은 GlobalControllerAdvice에서 처리하고 있으므로 제외해야 할 것 같습니다. 만약 여러 ControllerAdvice가 같은 Exception을 처리하게 될 경우, 잘 모르겠지만 Ambiguous 관련 오류가 발생할 것 같습니다.

Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.prgrms2.java.bitta.jobpost.controller.advice;

import com.prgrms2.java.bitta.jobpost.exception.JobPostTaskException;
import jakarta.validation.ConstraintViolationException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.util.Map;
import java.util.stream.Collectors;

@RestControllerAdvice
public class JobPostControllerAdvice {
@ExceptionHandler(JobPostTaskException.class)
public ResponseEntity<Map<String, String>> handleArgsException(JobPostTaskException e) {
return ResponseEntity.status(e.getCode())
.body(Map.of("error", e.getMessage()));
}

@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Map<String, String>> handleValidationException(MethodArgumentNotValidException e) {
String errors = e.getBindingResult().getAllErrors().stream()
.map(ObjectError::getDefaultMessage)
.collect(Collectors.joining(", "));

return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(Map.of("error", errors));
}

@ExceptionHandler(ConstraintViolationException.class)
public ResponseEntity<Map<String, String>> handleArgsException(ConstraintViolationException e) {
return ResponseEntity.badRequest()
.body(Map.of("error", e.getMessage()));
}

}
16 changes: 15 additions & 1 deletion src/main/java/com/prgrms2/java/bitta/jobpost/dto/JobPostDTO.java
Copy link
Collaborator

Choose a reason for hiding this comment

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

startDate와 endDate의 @FutureOrPresent 날짜 범위가 잘못 설정되어 있는 것 같습니다.
startDate와 endDate는 커스텀 가능하게 하는 게 낫지 않을까요?
후에 해당 게시글을 참고할 때 DTO에서 오류가 발생할 수 있으니까요.

Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
import com.prgrms2.java.bitta.jobpost.entity.PayStatus;
import com.prgrms2.java.bitta.member.entity.Member;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.FutureOrPresent;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
Expand All @@ -20,33 +24,43 @@
@Schema(title = "일거리 DTO", description = "일거리의 요청 및 응답에 사용하는 DTO입니다.")
public class JobPostDTO {
@Schema(title = "일거리 ID (PK)", description = "일거리의 고유 ID 입니다.", example = "1")
@Min(1)
private Long id;

@Schema(title = "회원 ID (FK)", description = "회원의 고유 ID 입니다.", example = "1")
private Long userId;
@Min(1)
@NotNull(message = "회원 ID가 필요합니다")
private Long memberId;

@Schema(title = "일거리 제목", description = "일거리 제목입니다.", example = "Job Title")
@NotNull(message = "제목 입력은 필수적 입니다")
private String title;

@Schema(title = "일거리 내용", description = "일거리 내용입니다.", example = "Job Content")
@NotNull(message = "설명 입력은 필수적 입니다")
private String description;

@Schema(title = "출근지", description = "출근 지역입니다.", example = "SEOUL")
@NotNull(message = "지역명은 필수적으로 입력해야 합니다")
private Location location;

@Schema(title = "지불 유형", description = "지불 유형입니다.", example = "FREE")
@NotNull(message = "급여 여부는 필수적으로 입력해야 합니다")
private PayStatus payStatus;

@Schema(title = "일거리 수정일시", description = "일거리가 수정된 날짜 및 시간입니다.", example = "2023-09-24T14:45:00")
private LocalDateTime updateAt;

@Schema(title = "시작일", description = "일이 시작하는 날짜입니다.", example = "2023-09-24")
@FutureOrPresent(message = "시작 일자는 오늘부터 설정해야 합니다")
private LocalDate startDate;

@Schema(title = "종료일", description = "일이 종료되는 날짜입니다.", example = "2023-09-24")
@FutureOrPresent(message = "종료 일자는 시작일자와 동일하거나 이후 일자로 설정해야 합니다")
private LocalDate endDate;

@Schema(title = "종료여부", description = "일이 종료되었는지 여부입니다.", example = "true")
@JsonProperty("isClosed")
private boolean isClosed;

}
Loading