Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import com.example.booklog.domain.booklog.view.BookView;
import com.example.booklog.domain.library.books.entity.Books;
import com.example.booklog.domain.library.books.repository.BooksRepository;
import com.example.booklog.global.common.apiPayload.code.status.ErrorStatus;
import com.example.booklog.global.common.apiPayload.exception.GeneralException;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

Expand All @@ -23,7 +25,7 @@ public boolean existsById(Long bookId) {
@Override
public BookView findBook(Long bookId) {
Books b = booksRepository.findById(bookId)
.orElseThrow(() -> new IllegalArgumentException("책 없음. bookId=" + bookId));
.orElseThrow(() -> new GeneralException(ErrorStatus.BOOK_NOT_FOUND));

return new BookViewImpl(
b.getId(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import com.example.booklog.domain.booklog.view.AuthorView;
import com.example.booklog.domain.users.entity.Users;
import com.example.booklog.domain.users.repository.UsersRepository;
import com.example.booklog.global.common.apiPayload.code.status.ErrorStatus;
import com.example.booklog.global.common.apiPayload.exception.GeneralException;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

Expand All @@ -24,7 +26,7 @@ public boolean existsById(Long userId) {
@Override
public AuthorView findAuthorSummary(Long userId) {
Users u = usersRepository.findById(userId)
.orElseThrow(() -> new IllegalArgumentException("유저 없음. userId=" + userId));
.orElseThrow(() -> new GeneralException(ErrorStatus.USER_NOT_FOUND));

// summary에서는 email/followedByMe는 null 허용
return new AuthorViewImpl(
Expand All @@ -39,7 +41,7 @@ public AuthorView findAuthorSummary(Long userId) {
@Override
public AuthorView findAuthorDetail(Long userId, Long viewerId) {
Users u = usersRepository.findById(userId)
.orElseThrow(() -> new IllegalArgumentException("유저 없음. userId=" + userId));
.orElseThrow(() -> new GeneralException(ErrorStatus.USER_NOT_FOUND));

// 팔로우 도메인이 아직 없으면 우선 null 또는 false로 둠
Boolean followedByMe = null; // 또는 false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import com.example.booklog.aws.s3.AmazonS3Manager;
import com.example.booklog.domain.booklog.dto.BooklogImageUploadResponse;
import com.example.booklog.global.common.Uuid;
import com.example.booklog.global.common.apiPayload.code.status.ErrorStatus;
import com.example.booklog.global.common.apiPayload.exception.GeneralException;
import com.example.booklog.global.common.repository.UuidRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Profile;
Expand All @@ -26,12 +28,12 @@ public class BooklogImageService {

public BooklogImageUploadResponse uploadBooklogImage(Long userId, MultipartFile file) {
if (file == null || file.isEmpty()) {
throw new IllegalArgumentException("업로드할 파일이 없습니다.");
throw new GeneralException(ErrorStatus.FILE_REQUIRED);
}

String contentType = file.getContentType();
if (contentType == null || !ALLOWED_CONTENT_TYPES.contains(contentType)) {
throw new IllegalArgumentException("이미지 파일만 업로드할 수 있습니다. (jpg/png/webp)");
throw new GeneralException(ErrorStatus.UNSUPPORTED_IMAGE_TYPE);
}
// 1) UUID 발급/저장 (프로젝트에서 쓰는 Uuid 테이블/레포 그대로 활용)
Uuid uuid = uuidRepository.save(Uuid.create());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
import com.example.booklog.domain.booklog.view.TagView;
import com.example.booklog.domain.tags.entity.TagCategory;
import com.example.booklog.domain.tags.repository.TagsRepository;
import com.example.booklog.global.common.apiPayload.code.status.ErrorStatus;
import com.example.booklog.global.common.apiPayload.exception.GeneralException;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
Expand Down Expand Up @@ -62,27 +64,27 @@ public BooklogPostCreateResponse create(Long userId, BooklogPostCreateRequest re

private void validateBooklogTagRules(List<Long> tagIds) {
if (tagIds == null || tagIds.isEmpty()) {
throw new IllegalArgumentException("태그는 최소 1개 이상 선택해야 합니다.");
throw new GeneralException(ErrorStatus.TAG_MIN_ONE_REQUIRED);
}

var tags = tagsRepository.findAllById(tagIds);

if (tags.size() != tagIds.size()) {
throw new IllegalArgumentException("존재하지 않는 태그가 포함되어 있습니다.");
throw new GeneralException(ErrorStatus.TAG_NOT_FOUND_INCLUDED);
}

long moodCount = tags.stream().filter(t -> t.getCategory() == TagCategory.MOOD).count();
long styleCount = tags.stream().filter(t -> t.getCategory() == TagCategory.STYLE).count();
long immersionCount = tags.stream().filter(t -> t.getCategory() == TagCategory.IMMERSION).count();

if (moodCount < 1 || moodCount > 2) throw new IllegalArgumentException("MOOD 태그는 1~2개 선택해야 합니다.");
if (styleCount < 1 || styleCount > 2) throw new IllegalArgumentException("STYLE 태그는 1~2개 선택해야 합니다.");
if (immersionCount != 1) throw new IllegalArgumentException("IMMERSION 태그는 반드시 1개 선택해야 합니다.");
if (moodCount < 1 || moodCount > 2) throw new GeneralException(ErrorStatus.MOOD_TAG_COUNT_INVALID);
if (styleCount < 1 || styleCount > 2) throw new GeneralException(ErrorStatus.STYLE_TAG_COUNT_INVALID);
if (immersionCount != 1) throw new GeneralException(ErrorStatus.IMMERSION_TAG_COUNT_INVALID);
}

private void validateImageLimit(List<String> imageUrls) {
if (imageUrls != null && imageUrls.size() > 8) {
throw new IllegalArgumentException("이미지는 최대 8장까지 가능합니다.");
throw new GeneralException(ErrorStatus.IMAGE_MAX_8);
}
}

Expand Down Expand Up @@ -198,7 +200,7 @@ public BooklogFeedResponse getFeed(Long userId, BooklogFeedQuery query, Pageable
public BooklogDetailResponse getDetail(Long userId, Long postId) {

BooklogPost post = postRepository.findByIdAndStatus(postId, BooklogStatus.PUBLISHED)
.orElseThrow(() -> new IllegalArgumentException("게시글을 찾을 수 없습니다."));
.orElseThrow(() -> new GeneralException(ErrorStatus.POST_NOT_FOUND));

// 조회수 +1
postRepository.increaseViewCount(postId, BooklogStatus.PUBLISHED);
Expand Down Expand Up @@ -252,10 +254,10 @@ public BooklogRecommendationResponse getRecommendations(Long userId, Long postId
public void softDelete(Long userId, Long postId) {

BooklogPost post = postRepository.findById(postId)
.orElseThrow(() -> new IllegalArgumentException("게시글을 찾을 수 없습니다."));
.orElseThrow(() -> new GeneralException(ErrorStatus.POST_NOT_FOUND));

if (!post.getUserId().equals(userId)) {
throw new IllegalArgumentException("삭제 권한이 없습니다.");
throw new GeneralException(ErrorStatus.POST_NOT_FOUND);
}

int updated = postRepository.softDelete(
Expand All @@ -266,7 +268,7 @@ public void softDelete(Long userId, Long postId) {
);

if (updated == 0) {
throw new IllegalArgumentException("이미 삭제되었거나 삭제할 수 없습니다.");
throw new GeneralException(ErrorStatus.POST_ALREADY_DELETED_OR_CANNOT_DELETE);
}
}

Expand All @@ -276,7 +278,7 @@ public BookmarkToggleResult toggleBookmark(Long userId, Long postId) {

// 1) 게시글 존재 + PUBLISHED 확인
BooklogPost post = postRepository.findByIdAndStatus(postId, BooklogStatus.PUBLISHED)
.orElseThrow(() -> new IllegalArgumentException("게시글을 찾을 수 없습니다."));
.orElseThrow(() -> new GeneralException(ErrorStatus.POST_NOT_FOUND));

// 2) 이미 북마크한 row가 있는지 확인
var existing = bookmarkRepository.findByUserIdAndPostId(userId, postId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
import com.example.booklog.domain.booklog.view.TagView;
import com.example.booklog.domain.tags.entity.Tags;
import com.example.booklog.domain.tags.repository.TagsRepository;
import com.example.booklog.global.common.apiPayload.code.status.ErrorStatus;
import com.example.booklog.global.common.apiPayload.exception.GeneralException;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
Expand Down Expand Up @@ -142,7 +144,7 @@ private List<Long> safeList(List<Long> v) {
public BooklogRecommendationResponse buildRecommendations(Long postId) {

BooklogPost base = postRepository.findByIdAndStatus(postId, BooklogStatus.PUBLISHED)
.orElseThrow(() -> new IllegalArgumentException("게시글을 찾을 수 없습니다."));
.orElseThrow(() -> new GeneralException(ErrorStatus.POST_NOT_FOUND));

// 1) base post의 tagIds
List<TagView> baseTags = findTagsByPostId(postId);
Expand Down Expand Up @@ -198,10 +200,10 @@ static class SimpleTagView implements TagView {
@Override
public void validateCreateRequest(Long userId, Long bookId) {
if (!userReadPort.existsById(userId)) {
throw new IllegalArgumentException("존재하지 않는 사용자입니다. userId=" + userId);
throw new GeneralException(ErrorStatus.USER_NOT_FOUND);
}
if (!bookReadPort.existsById(bookId)) {
throw new IllegalArgumentException("존재하지 않는 책입니다. bookId=" + bookId);
throw new GeneralException(ErrorStatus.BOOK_NOT_FOUND);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import com.example.booklog.domain.library.books.entity.Authors;
import com.example.booklog.domain.library.books.repository.AuthorsRepository;
import com.example.booklog.domain.library.books.service.client.WikidataClient;
import com.example.booklog.global.common.apiPayload.code.status.ErrorStatus;
import com.example.booklog.global.common.apiPayload.exception.GeneralException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
Expand All @@ -25,10 +27,10 @@ public class AuthorEnrichmentService {
@Transactional
public void enrichAuthorByName(String authorName) {
String normalized = authorName == null ? "" : authorName.trim();
if (normalized.isBlank()) throw new IllegalArgumentException("authorName is blank");
if (normalized.isBlank()) throw new GeneralException(ErrorStatus.AUTHOR_NAME_REQUIRED);

Authors author = authorsRepository.findByName(normalized)
.orElseThrow(() -> new IllegalArgumentException("Author not found: " + normalized));
.orElseThrow(() -> new GeneralException(ErrorStatus.AUTHOR_NOT_FOUND));

if (author.hasWikidataId()) return;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import com.example.booklog.domain.library.books.entity.Books;
import com.example.booklog.domain.users.entity.Users;
import com.example.booklog.global.common.BaseEntity;
import com.example.booklog.global.common.apiPayload.code.status.ErrorStatus;
import com.example.booklog.global.common.apiPayload.exception.GeneralException;
import jakarta.persistence.*;
import lombok.*;

Expand Down Expand Up @@ -82,7 +84,7 @@ public void updateProgress(Integer currentPage, Integer progressPercent) {
아래 메소드 3개는 사용자의 페이지 입력관련 메소드
*/
public void updatePageCountSnapshot(Integer totalPage) {
if (totalPage == null || totalPage < 1) throw new IllegalArgumentException("총 페이지는 1 이상이어야 합니다.");
if (totalPage == null || totalPage < 1) throw new GeneralException(ErrorStatus.TOTAL_PAGE_INVALID);
this.pageCountSnapshot = totalPage;
recalcProgressPercent();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import com.example.booklog.domain.library.shelves.repository.BookshelvesRepository;
import com.example.booklog.domain.users.entity.Users;
import com.example.booklog.domain.users.repository.UsersRepository;
import com.example.booklog.global.common.apiPayload.code.status.ErrorStatus;
import com.example.booklog.global.common.apiPayload.exception.GeneralException;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
Expand All @@ -27,10 +29,10 @@ public class BookshelvesService {
@Transactional
public BookshelfCreateResponse create(Long userId, BookshelfCreateRequest req) {
Users user = usersRepository.findById(userId)
.orElseThrow(() -> new IllegalArgumentException("유저 없음"));
.orElseThrow(() -> new GeneralException(ErrorStatus.BOOK_NOT_FOUND));
Comment on lines 31 to +32
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

User-not-found is mapped to the wrong error code.

findById(userId) currently throws BOOK_NOT_FOUND, which will mislead clients and breaks the unified error contract. This should be USER_NOT_FOUND.

✅ Suggested fix
-        Users user = usersRepository.findById(userId)
-                .orElseThrow(() -> new GeneralException(ErrorStatus.BOOK_NOT_FOUND));
+        Users user = usersRepository.findById(userId)
+                .orElseThrow(() -> new GeneralException(ErrorStatus.USER_NOT_FOUND));
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Users user = usersRepository.findById(userId)
.orElseThrow(() -> new IllegalArgumentException("유저 없음"));
.orElseThrow(() -> new GeneralException(ErrorStatus.BOOK_NOT_FOUND));
Users user = usersRepository.findById(userId)
.orElseThrow(() -> new GeneralException(ErrorStatus.USER_NOT_FOUND));
🤖 Prompt for AI Agents
In
`@booklog/src/main/java/com/example/booklog/domain/library/shelves/service/BookshelvesService.java`
around lines 31 - 32, In BookshelvesService replace the incorrect error mapping
when loading a user: change the
usersRepository.findById(userId).orElseThrow(...) to throw new
GeneralException(ErrorStatus.USER_NOT_FOUND) instead of
ErrorStatus.BOOK_NOT_FOUND so that the user-not-found case uses the correct
ErrorStatus constant (refer to Users usersRepository.findById in
BookshelvesService and the GeneralException/ErrorStatus types to implement the
change).


if (bookshelvesRepository.existsByUser_IdAndName(userId, req.name())) {
throw new IllegalStateException("이미 존재하는 서재명입니다.");
throw new GeneralException(ErrorStatus.DUPLICATE_SHELF_NAME);
}

boolean isPublic = (req.isPublic() != null) && req.isPublic();
Expand Down Expand Up @@ -92,7 +94,7 @@ public List<BookshelfListItemResponse> list(Long userId) {
@Transactional
public void update(Long userId, Long shelfId, BookshelfUpdateRequest req) {
Bookshelves shelf = bookshelvesRepository.findByIdAndUser_Id(shelfId, userId)
.orElseThrow(() -> new IllegalArgumentException("서재 없음 또는 내 서재 아님"));
.orElseThrow(() -> new GeneralException(ErrorStatus.SHELF_NOT_FOUND_OR_NOT_OWNED));

if (req.name() != null && !req.name().isBlank()) shelf.updateName(req.name());
if (req.isPublic() != null) shelf.updatePublic(req.isPublic());
Expand All @@ -103,7 +105,7 @@ public void update(Long userId, Long shelfId, BookshelfUpdateRequest req) {
@Transactional
public void delete(Long userId, Long shelfId) {
Bookshelves shelf = bookshelvesRepository.findByIdAndUser_Id(shelfId, userId)
.orElseThrow(() -> new IllegalArgumentException("서재 없음 또는 내 서재 아님"));
.orElseThrow(() -> new GeneralException(ErrorStatus.SHELF_NOT_FOUND_OR_NOT_OWNED));

// ✅ 라이브러리(user_books)는 건드리지 않음
bookshelfItemsRepository.deleteByShelfId(shelfId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import com.example.booklog.domain.library.shelves.entity.UserBooks;
import com.example.booklog.domain.library.shelves.repository.ReadingLogsRepository;
import com.example.booklog.domain.library.shelves.repository.UserBooksRepository;
import com.example.booklog.global.common.apiPayload.code.status.ErrorStatus;
import com.example.booklog.global.common.apiPayload.exception.GeneralException;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
Expand All @@ -25,7 +27,7 @@ public class ReadingLogsService {
@Transactional
public ReadingLogResponse create(Long userId, Long userBookId, ReadingLogSaveRequest req) {
UserBooks ub = userBooksRepository.findByUser_IdAndId(userId, userBookId)
.orElseThrow(() -> new IllegalArgumentException("저장 도서 없음"));
.orElseThrow(() -> new GeneralException(ErrorStatus.USER_BOOK_NOT_FOUND));


// prevCurrent 계산 (일단 유지)
Expand Down Expand Up @@ -67,7 +69,7 @@ public ReadingLogResponse create(Long userId, Long userBookId, ReadingLogSaveReq
@Transactional
public ReadingLogResponse update(Long userId, Long logId, ReadingLogSaveRequest req) {
ReadingLogs log = readingLogsRepository.findOwned(userId, logId)
.orElseThrow(() -> new IllegalArgumentException("독서 기록 없음/권한 없음"));
.orElseThrow(() -> new GeneralException(ErrorStatus.READING_LOG_NOT_FOUND_OR_FORBIDDEN));

UserBooks ub = log.getUserBook();

Expand All @@ -77,7 +79,7 @@ public ReadingLogResponse update(Long userId, Long logId, ReadingLogSaveRequest
recalcLogsAndUserBook(ub);

ReadingLogs updated = readingLogsRepository.findById(logId)
.orElseThrow(() -> new IllegalStateException("수정된 로그 조회 실패"));
.orElseThrow(() -> new GeneralException(ErrorStatus.READING_LOG_UPDATED_FETCH_FAILED));

return new ReadingLogResponse(
updated.getId(),
Expand All @@ -92,7 +94,7 @@ public ReadingLogResponse update(Long userId, Long logId, ReadingLogSaveRequest
@Transactional
public void delete(Long userId, Long logId) {
ReadingLogs log = readingLogsRepository.findOwned(userId, logId)
.orElseThrow(() -> new IllegalArgumentException("독서 기록 없음/권한 없음"));
.orElseThrow(() -> new GeneralException(ErrorStatus.READING_LOG_NOT_FOUND_OR_FORBIDDEN));

UserBooks ub = log.getUserBook();
readingLogsRepository.delete(log);
Expand Down
Loading