diff --git a/src/main/java/UMC/career_mate/domain/recruit/Recruit.java b/src/main/java/UMC/career_mate/domain/recruit/Recruit.java index 4e648b7..8bd4706 100644 --- a/src/main/java/UMC/career_mate/domain/recruit/Recruit.java +++ b/src/main/java/UMC/career_mate/domain/recruit/Recruit.java @@ -28,7 +28,6 @@ public class Recruit { // 채용 공고 목록 조회 시 아래 4개만 반환 private String companyName; // 회사명 private String title; // 공고 제목 - private String imageUrl; // 공고 이미지 private LocalDateTime deadLine; // 마감일 // 요약 페이지 조회 시 아래 데이터 추가 반환 diff --git a/src/main/java/UMC/career_mate/domain/recruit/controller/RecruitController.java b/src/main/java/UMC/career_mate/domain/recruit/controller/RecruitController.java index 4a9159e..fbc25ea 100644 --- a/src/main/java/UMC/career_mate/domain/recruit/controller/RecruitController.java +++ b/src/main/java/UMC/career_mate/domain/recruit/controller/RecruitController.java @@ -29,22 +29,34 @@ public class RecruitController { private final RecruitCommandService recruitCommandService; private final RecruitQueryService recruitQueryService; - /** - * TODO: MemberEducationLevel, EducationStatus 멤버 완성 삭제 후 서비스에서 처리 - */ - @Operation(summary = "추천 채용 공고 조회 API", description = "추천 채용 공고를 조회하는 API입니다.") + @Operation( + summary = "추천 채용 공고 조회 API", + description = """ + 추천 채용 공고를 조회하는 API입니다.\n\n + page의 값은 1부터 시작이고, 기본 값은 1입니다.\n\n + size의 기본값은 6입니다.\n\n + 정렬의 경우\n\n + 전체 (기본 값) -> POSTING_DESC\n\n + 마감 빠른 순 -> DEADLINE_ASC\n\n + 마감 늦은 순 -> DEADLINE_DESC + """) @GetMapping public ApiResponse>> getRecommendRecruitList( @RequestParam(defaultValue = "1", required = false) int page, @RequestParam(defaultValue = "6", required = false) int size, - @RequestParam RecruitSortType recruitSortType, + @RequestParam(defaultValue = "POSTING_DESC", required = false) RecruitSortType recruitSortType, @LoginMember Member member ) { return ApiResponse.onSuccess( recruitQueryService.getRecommendRecruitList(page, size, recruitSortType, member)); } - @Operation(summary = "채용 공고 요약 페이지 조회 API", description = "채용 공고 요약 페이지를 조회하는 API입니다.") + @Operation( + summary = "채용 공고 요약 페이지 조회 API", + description = """ + 채용 공고 요약 페이지를 조회하는 API입니다.\n\n + recruitId : 조회하려는 채용 공고 pk 값 + """) @GetMapping("/{recruitId}") public ResponseEntity> getRecruitInfo(@PathVariable Long recruitId, @LoginMember Member member) { return ResponseEntity.ok( diff --git a/src/main/java/UMC/career_mate/domain/recruit/converter/RecruitConverter.java b/src/main/java/UMC/career_mate/domain/recruit/converter/RecruitConverter.java index fcde1a3..0db232b 100644 --- a/src/main/java/UMC/career_mate/domain/recruit/converter/RecruitConverter.java +++ b/src/main/java/UMC/career_mate/domain/recruit/converter/RecruitConverter.java @@ -6,8 +6,10 @@ import UMC.career_mate.domain.recruit.dto.response.RecruitInfoDTO; import UMC.career_mate.domain.recruit.enums.EducationLevel; import java.time.Instant; +import java.time.LocalDate; import java.time.LocalDateTime; import java.time.ZoneId; +import java.time.temporal.ChronoUnit; import java.util.Arrays; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringEscapeUtils; @@ -22,7 +24,6 @@ public static Recruit toRecruit(Job job, String recruitUrl, String companyInfoUr return Recruit.builder() .companyName(job.company().detail().name()) .title(job.position().title()) - .imageUrl(null) .deadLine(LocalDateTime.ofInstant(Instant.ofEpochSecond(job.expirationTimestamp()), ZoneId.systemDefault())) .companyInfoUrl(companyInfoUrl) @@ -46,20 +47,19 @@ public static Recruit toRecruit(Job job, String recruitUrl, String companyInfoUr .build(); } - public static RecommendRecruitDTO toRecommendRecruitDTO(Recruit recruit) { + public static RecommendRecruitDTO toRecommendRecruitDTO(Recruit recruit, boolean isScraped) { return RecommendRecruitDTO.builder() .recruitId(recruit.getId()) .companyName(recruit.getCompanyName()) - .imageUrl(recruit.getImageUrl()) .title(recruit.getTitle()) - .deadLine(recruit.getDeadLine()) + .deadLine(formatDeadLine(recruit)) + .isScraped(isScraped) .experienceLevelCode(recruit.getExperienceLevelCode()) .experienceLevelMin(recruit.getExperienceLevelMin()) .experienceLevelMax(recruit.getExperienceLevelMax()) .experienceLevelName(recruit.getExperienceLevelName()) .educationLevelCode(recruit.getEducationLevelCode()) .educationLevelName(recruit.getEducationLevelName()) - .openingDate(recruit.getOpeningDate()) .postingDate(recruit.getPostingDate()) .build(); } @@ -68,17 +68,25 @@ public static RecruitInfoDTO toRecruitInfoDTO(String comment, Recruit recruit) { return RecruitInfoDTO.builder() .comment(comment) .companyName(recruit.getCompanyName()) - .title(recruit.getTitle()) - .industryName(recruit.getIndustryName()) - .region(recruit.getRegion()) .employmentName(recruit.getEmploymentName()) .experienceLevelName(recruit.getExperienceLevelName()) .educationLevelName(recruit.getEducationLevelName()) .salaryName(recruit.getSalaryName()) - .deadLine(recruit.getDeadLine()) - .jobNames(recruit.getJobNames()) + .region(recruit.getRegion()) .companyInfoUrl(recruit.getCompanyInfoUrl()) .recruitUrl(recruit.getRecruitUrl()) .build(); } + + private static String formatDeadLine(Recruit recruit) { + LocalDate today = LocalDate.now(); + LocalDate targetDate = recruit.getDeadLine().toLocalDate(); + long daysBetween = ChronoUnit.DAYS.between(today, targetDate); + + if (daysBetween == 0) { + return "오늘 마감"; + } + + return "D-" + daysBetween; + } } diff --git a/src/main/java/UMC/career_mate/domain/recruit/dto/response/RecommendRecruitDTO.java b/src/main/java/UMC/career_mate/domain/recruit/dto/response/RecommendRecruitDTO.java index 5d1096e..7d86844 100644 --- a/src/main/java/UMC/career_mate/domain/recruit/dto/response/RecommendRecruitDTO.java +++ b/src/main/java/UMC/career_mate/domain/recruit/dto/response/RecommendRecruitDTO.java @@ -7,9 +7,9 @@ public record RecommendRecruitDTO( Long recruitId, String companyName, - String imageUrl, String title, - LocalDateTime deadLine, + String deadLine, + boolean isScraped, /** * TODO: 아래로는 (필터링, 정렬) 잘 되는지 확인용 데이터, 나중에 삭제 예정 @@ -20,7 +20,6 @@ public record RecommendRecruitDTO( String experienceLevelName, Integer educationLevelCode, // 학력 코드 0(학력무관), 1(고등학교졸업), 2(대학졸업(2,3년)), 3(대학졸업(4년)), 4(석사졸업), 5(박사졸업) 등 String educationLevelName, - LocalDateTime openingDate, LocalDateTime postingDate ) { diff --git a/src/main/java/UMC/career_mate/domain/recruit/dto/response/RecruitInfoDTO.java b/src/main/java/UMC/career_mate/domain/recruit/dto/response/RecruitInfoDTO.java index b50ce10..f1c49dc 100644 --- a/src/main/java/UMC/career_mate/domain/recruit/dto/response/RecruitInfoDTO.java +++ b/src/main/java/UMC/career_mate/domain/recruit/dto/response/RecruitInfoDTO.java @@ -1,22 +1,16 @@ package UMC.career_mate.domain.recruit.dto.response; -import java.time.LocalDateTime; -import java.util.List; import lombok.Builder; @Builder public record RecruitInfoDTO( String comment, String companyName, - String title, - String industryName, - String region, String employmentName, String experienceLevelName, String educationLevelName, String salaryName, - LocalDateTime deadLine, - List jobNames, + String region, String companyInfoUrl, String recruitUrl ) { diff --git a/src/main/java/UMC/career_mate/domain/recruit/enums/RecruitSortType.java b/src/main/java/UMC/career_mate/domain/recruit/enums/RecruitSortType.java index 02fbda6..11656ca 100644 --- a/src/main/java/UMC/career_mate/domain/recruit/enums/RecruitSortType.java +++ b/src/main/java/UMC/career_mate/domain/recruit/enums/RecruitSortType.java @@ -9,10 +9,7 @@ public enum RecruitSortType { DEADLINE_ASC("deadLine", "asc"), // 마감일 빠른 순 DEADLINE_DESC("deadLine", "desc"), // 마감일 늦은 순 - OPENING_ASC("openingDate", "asc"), // 원서 접수 시작일 빠른 순 - OPENING_DESC("openingDate", "desc"), // 원서 접수 시작일 늦은 순 - POSTING_ASC("postingDate", "asc"), // 사람인에 채용 공고 작성일 빠른 순 - POSTING_DESC("postingDate", "desc"); // 사람인에 채용 공고 작성일 늦은 순 + POSTING_DESC("postingDate", "desc"); // 사람인에 채용 공고 작성일 늦은 순 (전체) private final String field; private final String direction; diff --git a/src/main/java/UMC/career_mate/domain/recruit/repository/querydsl/RecruitQueryRepositoryImpl.java b/src/main/java/UMC/career_mate/domain/recruit/repository/querydsl/RecruitQueryRepositoryImpl.java index 638eb5a..40142c0 100644 --- a/src/main/java/UMC/career_mate/domain/recruit/repository/querydsl/RecruitQueryRepositoryImpl.java +++ b/src/main/java/UMC/career_mate/domain/recruit/repository/querydsl/RecruitQueryRepositoryImpl.java @@ -117,10 +117,7 @@ private boolean isEnglish(String keyword) { private OrderSpecifier createOrderSpecifier(RecruitSortType recruitSortType) { return switch (recruitSortType) { case DEADLINE_ASC -> new OrderSpecifier(Order.ASC, recruit.deadLine); - case OPENING_ASC -> new OrderSpecifier(Order.ASC, recruit.openingDate); - case POSTING_ASC -> new OrderSpecifier(Order.ASC, recruit.postingDate); case DEADLINE_DESC -> new OrderSpecifier(Order.DESC, recruit.deadLine); - case OPENING_DESC -> new OrderSpecifier(Order.DESC, recruit.openingDate); case POSTING_DESC -> new OrderSpecifier(Order.DESC, recruit.postingDate); }; } diff --git a/src/main/java/UMC/career_mate/domain/recruit/service/RecruitQueryService.java b/src/main/java/UMC/career_mate/domain/recruit/service/RecruitQueryService.java index b38a130..621ac9d 100644 --- a/src/main/java/UMC/career_mate/domain/recruit/service/RecruitQueryService.java +++ b/src/main/java/UMC/career_mate/domain/recruit/service/RecruitQueryService.java @@ -11,9 +11,11 @@ import UMC.career_mate.domain.recruit.enums.EducationLevel; import UMC.career_mate.domain.recruit.enums.RecruitSortType; import UMC.career_mate.domain.recruit.repository.RecruitRepository; +import UMC.career_mate.domain.recruitScrap.repository.RecruitScrapRepository; import UMC.career_mate.global.common.PageResponseDTO; import UMC.career_mate.global.response.exception.GeneralException; import UMC.career_mate.global.response.exception.code.CommonErrorCode; +import java.util.Set; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; @@ -29,6 +31,7 @@ public class RecruitQueryService { private final RecruitRepository recruitRepository; private final ChatGptService chatGptService; + private final RecruitScrapRepository recruitScrapRepository; public PageResponseDTO> getRecommendRecruitList(int page, int size, RecruitSortType recruitSortType, Member member) { @@ -48,7 +51,7 @@ public PageResponseDTO> getRecommendRecruitList(int pa filterCondition.recruitKeyword(), educationLevel, filterCondition.careerYear(), recruitSortType, pageRequest); - return createPageResponseDTO(page, size, findRecruitPage); + return createPageResponseDTO(page, size, findRecruitPage, member); } public RecruitInfoDTO findRecruitInfo(Member member, Long recruitId) { @@ -64,12 +67,15 @@ public RecruitInfoDTO findRecruitInfo(Member member, Long recruitId) { } private PageResponseDTO> createPageResponseDTO(int page, - int size, Page findRecruitPage) { + int size, Page findRecruitPage, Member member) { + Set scrapedRecruitIds = recruitScrapRepository.findRecruitIdsByMember(member); + boolean hasNext = findRecruitPage.getSize() == size + 1; List recommendRecruitDTOList = findRecruitPage.stream() .limit(size) - .map(recruit -> RecruitConverter.toRecommendRecruitDTO(recruit)) + .map(recruit -> RecruitConverter.toRecommendRecruitDTO(recruit, + scrapedRecruitIds.contains(recruit.getId()))) .toList(); return PageResponseDTO.>builder() diff --git a/src/main/java/UMC/career_mate/domain/recruitScrap/controller/RecruitScrapController.java b/src/main/java/UMC/career_mate/domain/recruitScrap/controller/RecruitScrapController.java new file mode 100644 index 0000000..7b1977f --- /dev/null +++ b/src/main/java/UMC/career_mate/domain/recruitScrap/controller/RecruitScrapController.java @@ -0,0 +1,65 @@ +package UMC.career_mate.domain.recruitScrap.controller; + +import UMC.career_mate.domain.member.Member; +import UMC.career_mate.domain.recruitScrap.dto.response.RecruitScrapResponseDTO; +import UMC.career_mate.domain.recruitScrap.service.RecruitScrapCommandService; +import UMC.career_mate.domain.recruitScrap.service.RecruitScrapQueryService; +import UMC.career_mate.global.annotation.LoginMember; +import UMC.career_mate.global.response.ApiResponse; +import io.swagger.v3.oas.annotations.Operation; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/scrap/recruits") +public class RecruitScrapController { + + private final RecruitScrapCommandService recruitScrapCommandService; + private final RecruitScrapQueryService recruitScrapQueryService; + + @Operation( + summary = "채용 공고 스크랩 API", + description = """ + 채용 공고를 스크랩하는 API입니다.\n\n + recruitId : 스크랩하려는 채용 공고 pk 값 + """) + @PostMapping("/{recruitId}") + public ApiResponse createRecruitScrap(@LoginMember Member member, + @PathVariable Long recruitId) { + recruitScrapCommandService.saveRecruitScrap(member, recruitId); + return ApiResponse.onSuccess("스크랩 완료"); + } + + @Operation( + summary = "채용 공고 스크랩 삭제 API", + description = """ + 스크랩을 삭제하는 API입니다.\n\n + recruitId : 삭제하려는 스크랩의 채용 공고 pk 값 + """) + @DeleteMapping("/{recruitId}") + public ApiResponse deleteRecruitScrap(@LoginMember Member member, + @PathVariable Long recruitId) { + recruitScrapCommandService.deleteRecruitScrap(member, recruitId); + return ApiResponse.onSuccess("스크랩 삭제 완료"); + } + + @Operation( + summary = "채용 공고 스크랩 목록 조회 API", + description = """ + 스크랩한 채용 공고 목록을 조회하는 API입니다.\n\n + 로그인 인증만을 요구합니다. + """) + @GetMapping + public ApiResponse> getRecruitScrapList( + @LoginMember Member member) { + return ApiResponse.onSuccess(recruitScrapQueryService.findRecruitScrapList(member)); + } + +} diff --git a/src/main/java/UMC/career_mate/domain/recruitScrap/converter/RecruitScrapConverter.java b/src/main/java/UMC/career_mate/domain/recruitScrap/converter/RecruitScrapConverter.java new file mode 100644 index 0000000..1ab4405 --- /dev/null +++ b/src/main/java/UMC/career_mate/domain/recruitScrap/converter/RecruitScrapConverter.java @@ -0,0 +1,44 @@ +package UMC.career_mate.domain.recruitScrap.converter; + +import UMC.career_mate.domain.member.Member; +import UMC.career_mate.domain.recruit.Recruit; +import UMC.career_mate.domain.recruitScrap.RecruitScrap; +import UMC.career_mate.domain.recruitScrap.dto.response.RecruitScrapResponseDTO; +import java.time.LocalDate; +import java.time.temporal.ChronoUnit; + +public class RecruitScrapConverter { + + public static RecruitScrap toEntity(Member member, Recruit recruit) { + return RecruitScrap.builder() + .member(member) + .recruit(recruit) + .build(); + } + + public static RecruitScrapResponseDTO toRecruitScrapResponseDTO(Recruit recruit) { + return RecruitScrapResponseDTO.builder() + .recruitId(recruit.getId()) + .companyName(recruit.getCompanyName()) + .title(recruit.getTitle()) + .deadLine(formatDeadLine(recruit)) + .isScraped(true) + .build(); + } + + private static String formatDeadLine(Recruit recruit) { + LocalDate today = LocalDate.now(); + LocalDate targetDate = recruit.getDeadLine().toLocalDate(); + long daysBetween = ChronoUnit.DAYS.between(today, targetDate); + + if (daysBetween == 0) { + return "오늘 마감"; + } + + if (daysBetween > 0) { + return "D-" + daysBetween; + } + + return "마감"; + } +} diff --git a/src/main/java/UMC/career_mate/domain/recruitScrap/dto/response/RecruitScrapResponseDTO.java b/src/main/java/UMC/career_mate/domain/recruitScrap/dto/response/RecruitScrapResponseDTO.java new file mode 100644 index 0000000..27059f5 --- /dev/null +++ b/src/main/java/UMC/career_mate/domain/recruitScrap/dto/response/RecruitScrapResponseDTO.java @@ -0,0 +1,15 @@ +package UMC.career_mate.domain.recruitScrap.dto.response; + +import java.time.LocalDateTime; +import lombok.Builder; + +@Builder +public record RecruitScrapResponseDTO( + Long recruitId, + String companyName, + String title, + String deadLine, + boolean isScraped +) { + +} diff --git a/src/main/java/UMC/career_mate/domain/recruitScrap/repository/RecruitScrapRepository.java b/src/main/java/UMC/career_mate/domain/recruitScrap/repository/RecruitScrapRepository.java new file mode 100644 index 0000000..eb44108 --- /dev/null +++ b/src/main/java/UMC/career_mate/domain/recruitScrap/repository/RecruitScrapRepository.java @@ -0,0 +1,24 @@ +package UMC.career_mate.domain.recruitScrap.repository; + +import UMC.career_mate.domain.member.Member; +import UMC.career_mate.domain.recruit.Recruit; +import UMC.career_mate.domain.recruitScrap.RecruitScrap; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +public interface RecruitScrapRepository extends JpaRepository { + + boolean existsByMemberAndRecruit(Member member, Recruit recruit); + + Optional findByMemberAndRecruitId(Member member, Long recruitId); + + @Query("select rs from RecruitScrap rs join fetch rs.recruit where rs.member = :member") + List findByMember(@Param("member") Member member); + + @Query("select rs.recruit.id from RecruitScrap rs where rs.member = :member") + Set findRecruitIdsByMember(@Param("member") Member member); +} diff --git a/src/main/java/UMC/career_mate/domain/recruitScrap/service/RecruitScrapCommandService.java b/src/main/java/UMC/career_mate/domain/recruitScrap/service/RecruitScrapCommandService.java new file mode 100644 index 0000000..856cf73 --- /dev/null +++ b/src/main/java/UMC/career_mate/domain/recruitScrap/service/RecruitScrapCommandService.java @@ -0,0 +1,45 @@ +package UMC.career_mate.domain.recruitScrap.service; + +import UMC.career_mate.domain.member.Member; +import UMC.career_mate.domain.recruit.Recruit; +import UMC.career_mate.domain.recruit.repository.RecruitRepository; +import UMC.career_mate.domain.recruitScrap.RecruitScrap; +import UMC.career_mate.domain.recruitScrap.converter.RecruitScrapConverter; +import UMC.career_mate.domain.recruitScrap.repository.RecruitScrapRepository; +import UMC.career_mate.global.response.exception.GeneralException; +import UMC.career_mate.global.response.exception.code.CommonErrorCode; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +@Transactional +public class RecruitScrapCommandService { + + private final RecruitScrapRepository recruitScrapRepository; + private final RecruitRepository recruitRepository; + + public void saveRecruitScrap(Member member, Long recruitId) { + Recruit recruit = recruitRepository.findById(recruitId).orElseThrow( + () -> new GeneralException(CommonErrorCode.NOT_FOUND_RECRUIT) + ); + + if (recruitScrapRepository.existsByMemberAndRecruit(member, recruit)) { + throw new GeneralException(CommonErrorCode.DUPLICATE_RECRUIT_SCRAP); + } + + RecruitScrap recruitScrap = RecruitScrapConverter.toEntity(member, recruit); + + recruitScrapRepository.save(recruitScrap); + } + + public void deleteRecruitScrap(Member member, Long recruitId) { + RecruitScrap recruitScrap = recruitScrapRepository.findByMemberAndRecruitId(member, + recruitId).orElseThrow( + () -> new GeneralException(CommonErrorCode.NOT_FOUND_RECRUIT_SCRAP) + ); + + recruitScrapRepository.delete(recruitScrap); + } +} diff --git a/src/main/java/UMC/career_mate/domain/recruitScrap/service/RecruitScrapQueryService.java b/src/main/java/UMC/career_mate/domain/recruitScrap/service/RecruitScrapQueryService.java new file mode 100644 index 0000000..f924060 --- /dev/null +++ b/src/main/java/UMC/career_mate/domain/recruitScrap/service/RecruitScrapQueryService.java @@ -0,0 +1,32 @@ +package UMC.career_mate.domain.recruitScrap.service; + +import UMC.career_mate.domain.member.Member; +import UMC.career_mate.domain.recruitScrap.RecruitScrap; +import UMC.career_mate.domain.recruitScrap.converter.RecruitScrapConverter; +import UMC.career_mate.domain.recruitScrap.dto.response.RecruitScrapResponseDTO; +import UMC.career_mate.domain.recruitScrap.repository.RecruitScrapRepository; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class RecruitScrapQueryService { + + private final RecruitScrapRepository recruitScrapRepository; + + public List findRecruitScrapList(Member member) { + List recruitScrapList = recruitScrapRepository.findByMember(member); + + if (recruitScrapList.isEmpty()) { + return List.of(); + } + + return recruitScrapList.stream() + .map(RecruitScrap::getRecruit) + .map(RecruitScrapConverter::toRecruitScrapResponseDTO) + .toList(); + } +} diff --git a/src/main/java/UMC/career_mate/global/response/exception/code/CommonErrorCode.java b/src/main/java/UMC/career_mate/global/response/exception/code/CommonErrorCode.java index 0203de9..96d643e 100644 --- a/src/main/java/UMC/career_mate/global/response/exception/code/CommonErrorCode.java +++ b/src/main/java/UMC/career_mate/global/response/exception/code/CommonErrorCode.java @@ -52,7 +52,12 @@ public enum CommonErrorCode implements ErrorCode { // ContentScrap 도메인 NOT_FOUND_CONTENT(400, "ESC001", "해당 콘텐츠를 찾을 수 없습니다."), DUPLICATE_SCRAP(400, "ESC002", "이미 스크랩된 콘텐츠입니다."), - NOT_FOUND_SCRAP(400, "ESC003", "스크랩이 존재하지 않습니다."); + NOT_FOUND_SCRAP(400, "ESC003", "스크랩이 존재하지 않습니다."), + + // RecruitScrap 도메인 + DUPLICATE_RECRUIT_SCRAP(400, "ERSC001", "이미 스크랩된 채용 공고입니다."), + NOT_FOUND_RECRUIT_SCRAP(400, "ERSC002", "해당 채용 공고 스크랩을 찾을 수 없습니다."), + ; private final int status;