Skip to content
Merged
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
14 changes: 14 additions & 0 deletions .github/ISSUE_TEMPLATE/백엔드-이슈.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
name: 백엔드 이슈
about: 백엔드와 관련된 이슈
title: "[백엔드] "
labels: Backend
assignees: ''

---

# 이슈 내용


# 작업 목록
- [ ]
39 changes: 39 additions & 0 deletions api-user/src/main/java/com/example/apiuser/ApiUserApplication.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.example.apiuser;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;

@EnableJpaAuditing
@SpringBootApplication(scanBasePackages = {
"com.example.apiuser",
"com.nowait.auth"
})
@EntityScan(basePackages = {
"com.example.menu.entity", // domain-menu
"com.example.domainstore.entity", // domain-store
"com.example.domaintoken.entity",
"com.nowaiting.user.entity",
"com.nowait.domainbookmark.entity",
"com.nowait.domainreservation.entity",
"com.nowait.domainorder.entity",
"com.nowait.domainorder.entity"
Comment on lines +21 to +22
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

엔티티 스캔 중복 제거 필요

"com.nowait.domainorder.entity" 패키지가 21번과 22번 라인에 중복으로 선언되어 있습니다.

 	"com.nowait.domainbookmark.entity",
 	"com.nowait.domainreservation.entity",
 	"com.nowait.domainorder.entity",
-	"com.nowait.domainorder.entity"
📝 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
"com.nowait.domainorder.entity",
"com.nowait.domainorder.entity"
"com.nowait.domainbookmark.entity",
"com.nowait.domainreservation.entity",
"com.nowait.domainorder.entity"
🤖 Prompt for AI Agents
In api-user/src/main/java/com/example/apiuser/ApiUserApplication.java at lines
21 to 22, the package "com.nowait.domainorder.entity" is declared twice in the
entity scan configuration. Remove the duplicate entry so that the package is
only listed once to avoid redundancy.

})
@EnableJpaRepositories(basePackages = {
"com.example.menu.repository",
"com.nowaiting.user.repository",
"com.example.domainstore.repository",
"com.example.domaintoken.repository",
"com.nowait.domainbookmark.repository",
"com.nowait.domainorder.repository",
"com.nowait.domainreservation.repository"
})
public class ApiUserApplication {

public static void main(String[] args) {
SpringApplication.run(ApiUserApplication.class, args);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package com.example.apiuser.bookmark.controller;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
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;

import com.example.apiuser.bookmark.dto.BookmarkCreateResponse;
import com.example.apiuser.bookmark.service.BookmarkService;
import com.nowait.auth.dto.CustomOAuth2User;
import com.nowaiting.common.api.ApiUtils;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
@Tag(name = "Bookmark API", description = "북마크 API")
@RestController
@RequestMapping("/bookmarks")
@RequiredArgsConstructor
public class BookmarkController {
private final BookmarkService bookmarkService;

@PostMapping("/{storeId}")
@Operation(summary = "북마크 생성", description = "특정 주점에 대한 북마크 생성")
@ApiResponse(responseCode = "201", description = "북마크 생성")
public ResponseEntity<?> createBookmark(@PathVariable Long storeId,@AuthenticationPrincipal CustomOAuth2User customOAuth2User) {
BookmarkCreateResponse response = bookmarkService.createBookmark(storeId,customOAuth2User);

return ResponseEntity
.status(HttpStatus.CREATED)
.body(
ApiUtils.success(
response
)
);
}
@GetMapping
@Operation(summary = "북마크 조회", description = "내가 북마크한 주점 조회")
@ApiResponse(responseCode = "200", description = "북마크 조회")
public ResponseEntity<?> getAllBookmarks(@AuthenticationPrincipal CustomOAuth2User customOAuth2User) {
return ResponseEntity
.ok()
.body(
ApiUtils.success(
bookmarkService.getBookmarks(customOAuth2User)
)
);
}
@DeleteMapping("/{bookmarkId}")
@Operation(summary = "북마크 삭제", description = "특정 주점에 대한 북마크 삭제")
@ApiResponse(responseCode = "200", description = "북마크 삭제")
public ResponseEntity<?> deleteBookmark(@PathVariable Long bookmarkId, @AuthenticationPrincipal CustomOAuth2User customOAuth2User) {
return ResponseEntity
.ok()
.body(
ApiUtils.success(
bookmarkService.deleteBookmark(bookmarkId,customOAuth2User)
)
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.example.apiuser.bookmark.dto;

import com.nowait.domainbookmark.entity.Bookmark;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;

@Getter
@AllArgsConstructor
@Builder
public class BookmarkCreateResponse {
private Long bookmarkId;
private Long userId;
private Long storeId;

public static BookmarkCreateResponse fromEntity(Bookmark bookmark) {
return BookmarkCreateResponse.builder()
.bookmarkId(bookmark.getId())
.userId(bookmark.getUser().getId())
.storeId(bookmark.getStore().getStoreId())
.build();
}
}
Comment on lines +9 to +24
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

코드 중복을 제거하여 DRY 원칙을 준수해주세요.

BookmarkCreateResponseBookmarkGetResponse가 동일한 구조와 필드를 가지고 있습니다:

  • 동일한 필드들: bookmarkId, userId, storeId
  • 동일한 fromEntity 메서드 구현
  • 동일한 Lombok 어노테이션 사용

다음 방법 중 하나를 고려해보세요:

방법 1: 공통 클래스 사용

// BookmarkResponse로 통합
public class BookmarkResponse {
    private Long bookmarkId;
    private Long userId;
    private Long storeId;
    // ... 동일한 구현
}

방법 2: 공통 인터페이스 또는 추상 클래스

public abstract class BaseBookmarkResponse {
    protected Long bookmarkId;
    protected Long userId;
    protected Long storeId;
    // ... 공통 구현
}

이렇게 하면 코드 중복을 제거하고 유지보수성을 향상시킬 수 있습니다.

🤖 Prompt for AI Agents
In
api-user/src/main/java/com/example/apiuser/bookmark/dto/BookmarkCreateResponse.java
lines 9 to 24, the BookmarkCreateResponse class duplicates the same fields and
fromEntity method as BookmarkGetResponse, violating the DRY principle. To fix
this, refactor by creating a common class (e.g., BookmarkResponse) or an
abstract base class that contains the shared fields and fromEntity method, then
have both BookmarkCreateResponse and BookmarkGetResponse extend or use this
common class to eliminate duplication and improve maintainability.

Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.example.apiuser.bookmark.dto;

import com.nowait.domainbookmark.entity.Bookmark;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;

@Getter
@AllArgsConstructor
@Builder
public class BookmarkGetResponse {
private Long bookmarkId;
private Long userId;
private Long storeId;

public static BookmarkGetResponse fromEntity(Bookmark bookmark) {
return BookmarkGetResponse.builder()
.bookmarkId(bookmark.getId())
.userId(bookmark.getUser().getId())
.storeId(bookmark.getStore().getStoreId())
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package com.example.apiuser.bookmark.service;

import java.util.List;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.example.apiuser.bookmark.dto.BookmarkCreateResponse;
import com.example.apiuser.bookmark.dto.BookmarkGetResponse;
import com.example.domainstore.entity.Store;
import com.example.domainstore.repository.StoreRepository;
import com.nowait.auth.dto.CustomOAuth2User;
import com.nowait.domainbookmark.entity.Bookmark;
import com.nowait.domainbookmark.repository.BookmarkRepository;
import com.nowaiting.user.entity.User;
import com.nowaiting.user.repository.UserRepository;

import jakarta.persistence.EntityNotFoundException;
import lombok.RequiredArgsConstructor;

@Service
@RequiredArgsConstructor
public class BookmarkService {
private final BookmarkRepository bookmarkRepository;
private final StoreRepository storeRepository;
private final UserRepository userRepository;
@Transactional
public BookmarkCreateResponse createBookmark(Long storeId, CustomOAuth2User customOAuth2User) {
parameterValidation(storeId, customOAuth2User);
Store store = storeRepository.findById(storeId)
.orElseThrow(() -> new EntityNotFoundException(storeId + " store not found."));
User user = userRepository.findById(customOAuth2User.getUserId())
.orElseThrow(() -> new EntityNotFoundException("User not found"));

if (bookmarkRepository.existsByUserAndStore(user, store)) {
throw new IllegalArgumentException("already bookmarked");
}

Bookmark bookmark = Bookmark.builder()
.store(store)
.user(user)
.build();

return BookmarkCreateResponse.fromEntity(bookmarkRepository.save(bookmark));
}

@Transactional(readOnly = true)
public List<BookmarkGetResponse> getBookmarks(CustomOAuth2User customOAuth2User) {
User user = userRepository.findById(customOAuth2User.getUserId())
.orElseThrow(() -> new EntityNotFoundException("User not found"));
return bookmarkRepository.findAllByUser(user)
.stream()
.map(BookmarkGetResponse::fromEntity)
.toList();
}

@Transactional
public String deleteBookmark(Long bookmarkId, CustomOAuth2User customOAuth2User) {
parameterValidation(bookmarkId, customOAuth2User);
Bookmark bookmark = bookmarkRepository.findById(bookmarkId)
.orElseThrow(() -> new EntityNotFoundException(bookmarkId + " bookmark not found."));
if (bookmark.getUser().getId() != customOAuth2User.getUserId()) {
throw new IllegalArgumentException("you can only delete your own bookmark");
}
Comment on lines +62 to +64
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

사용자 권한 검증에서 타입 안전성 문제가 있습니다.

!= 연산자를 사용한 Long 타입 비교는 객체 참조 비교가 될 수 있어 예상치 못한 결과를 초래할 수 있습니다. equals() 메서드나 Objects.equals()를 사용해야 합니다.

-if (bookmark.getUser().getId() != customOAuth2User.getUserId()) {
+if (!Objects.equals(bookmark.getUser().getId(), customOAuth2User.getUserId())) {
 	throw new IllegalArgumentException("you can only delete your own bookmark");
 }

파일 상단에 java.util.Objects import를 추가해야 합니다.

🤖 Prompt for AI Agents
In
api-user/src/main/java/com/example/apiuser/bookmark/service/BookmarkService.java
around lines 62 to 64, the code uses '!=' to compare Long objects for user ID
equality, which can cause incorrect results due to reference comparison. Replace
the '!=' operator with 'Objects.equals()' to safely compare the Long values.
Also, add an import statement for 'java.util.Objects' at the top of the file to
use this method.

bookmarkRepository.delete(bookmark);
return "Bookmark ID " + bookmarkId + " deleted.";
}

private static void parameterValidation(Long storeId, CustomOAuth2User customOAuth2User) {
// 파라미터 유효성 검사
if (storeId == null || storeId < 0) {
throw new IllegalArgumentException("storeId must be a positive number");
}
if (customOAuth2User == null || customOAuth2User.getUserId() == null) {
throw new IllegalArgumentException("UserInfo is required");
}
}
Comment on lines +69 to +77
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

매개변수 검증 로직을 개선할 수 있습니다.

현재 검증 로직은 중복 코드를 포함하고 있으며, 메서드명이 용도를 명확히 나타내지 않습니다. 또한 storeId가 0인 경우에 대한 처리가 애매합니다.

-private static void parameterValidation(Long storeId, CustomOAuth2User customOAuth2User) {
+private static void validateParameters(Long id, CustomOAuth2User customOAuth2User) {
 	// 파라미터 유효성 검사
-	if (storeId == null || storeId < 0) {
-		throw new IllegalArgumentException("storeId must be a positive number");
+	if (id == null || id <= 0) {
+		throw new IllegalArgumentException("ID must be a positive number");
 	}
 	if (customOAuth2User == null || customOAuth2User.getUserId() == null) {
 		throw new IllegalArgumentException("UserInfo is required");
 	}
 }

메서드 호출 부분도 업데이트해야 합니다.

📝 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
private static void parameterValidation(Long storeId, CustomOAuth2User customOAuth2User) {
// 파라미터 유효성 검사
if (storeId == null || storeId < 0) {
throw new IllegalArgumentException("storeId must be a positive number");
}
if (customOAuth2User == null || customOAuth2User.getUserId() == null) {
throw new IllegalArgumentException("UserInfo is required");
}
}
private static void validateParameters(Long id, CustomOAuth2User customOAuth2User) {
// 파라미터 유효성 검사
if (id == null || id <= 0) {
throw new IllegalArgumentException("ID must be a positive number");
}
if (customOAuth2User == null || customOAuth2User.getUserId() == null) {
throw new IllegalArgumentException("UserInfo is required");
}
}
🤖 Prompt for AI Agents
In
api-user/src/main/java/com/example/apiuser/bookmark/service/BookmarkService.java
around lines 69 to 77, the parameterValidation method has redundant checks and
an unclear method name, and it does not clearly handle the case when storeId is
zero. Rename the method to clearly reflect its purpose, refactor the validation
logic to avoid duplication by separating checks or using helper methods, and
explicitly define whether storeId zero is valid or invalid (adjust the condition
accordingly). After refactoring, update all calls to this method to use the new
method name and signature if changed.

}
Loading