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
52 changes: 52 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# ============================================
# Eatsfine Backend - 환경변수 설정 가이드
# ============================================
# 이 파일을 `.env`로 복사한 뒤, 실제 값을 입력하세요.
# cp .env.example .env
#
# ⚠️ 주의: .env 파일은 절대 Git에 커밋하지 마세요!
# ============================================

# ── Database (MySQL) ──────────────────────────
DB_HOST=localhost
DB_PORT=3306
DB_NAME=eatsfine_local
DB_USERNAME=root
DB_PASSWORD=your_password
Comment on lines +11 to +15
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

dotenv-linter 경고: DB 섹션 키 순서

dotenv-linterDB_NAME(N < P)과 DB_PASSWORD(PAS < POR)가 알파벳 순서상 DB_PORT 앞에 와야 한다고 경고합니다. 현재 순서(HOST→PORT→NAME→USERNAME→PASSWORD)가 의미적으로 읽기 쉽다면 무시할 수도 있지만, 린터 일관성을 원할 경우 아래와 같이 정렬하세요.

🔧 제안: 알파벳 순서로 정렬
 # ── Database (MySQL) ──────────────────────────
 DB_HOST=localhost
-DB_PORT=3306
 DB_NAME=eatsfine_local
-DB_USERNAME=root
 DB_PASSWORD=your_password
+DB_PORT=3306
+DB_USERNAME=root
📝 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
DB_HOST=localhost
DB_PORT=3306
DB_NAME=eatsfine_local
DB_USERNAME=root
DB_PASSWORD=your_password
DB_HOST=localhost
DB_NAME=eatsfine_local
DB_PASSWORD=your_password
DB_PORT=3306
DB_USERNAME=root
🧰 Tools
🪛 dotenv-linter (4.0.0)

[warning] 13-13: [UnorderedKey] The DB_NAME key should go before the DB_PORT key

(UnorderedKey)


[warning] 15-15: [UnorderedKey] The DB_PASSWORD key should go before the DB_PORT key

(UnorderedKey)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.env.example around lines 11 - 15, Reorder the DB keys in .env.example into
strict alphabetical order to satisfy dotenv-linter: place DB_HOST, DB_NAME,
DB_PASSWORD, DB_PORT, DB_USERNAME (or whichever alphabetical sequence your
linter expects) by moving the DB_NAME and DB_PASSWORD lines so they come before
DB_PORT; update the block containing DB_HOST, DB_PORT, DB_NAME, DB_USERNAME,
DB_PASSWORD accordingly referencing those exact keys.


# ── Redis ─────────────────────────────────────
REDIS_HOST=localhost
REDIS_PORT=6379

# ── JWT ───────────────────────────────────────
SECRET_KEY=your_jwt_secret_key_must_be_long_enough_32chars

# ── Toss Payments ─────────────────────────────
# 토스페이먼츠 개발자센터에서 테스트 시크릿 키 발급
# https://developers.tosspayments.com
TOSS_WIDGET_SECRET_KEY=test_sk_xxxxxxxxxxxxxxxx

# ── OAuth2 (Google) ───────────────────────────
# Google Cloud Console에서 OAuth 2.0 클라이언트 ID 발급
# https://console.cloud.google.com/apis/credentials
GOOGLE_CLIENT_ID=your_google_client_id
GOOGLE_CLIENT_SECRET=your_google_client_secret

# ── OAuth2 (Kakao) ────────────────────────────
# 카카오 개발자센터에서 앱 키 발급
# https://developers.kakao.com
KAKAO_CLIENT_ID=your_kakao_client_id
KAKAO_CLIENT_SECRET=your_kakao_client_secret

# ── AWS S3 ────────────────────────────────────
# AWS IAM에서 액세스 키 발급 (로컬 개발 시 필수)
# https://console.aws.amazon.com/iam
AWS_ACCESS_KEY_ID=your_aws_access_key_id
AWS_SECRET_ACCESS_KEY=your_aws_secret_access_key
AWS_REGION=ap-northeast-2
AWS_S3_BUCKET=your-bucket-name
AWS_S3_BASE_URL=https://your-bucket-name.s3.ap-northeast-2.amazonaws.com
Comment on lines +44 to +48
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

AWS S3 섹션 키 순서 — dotenv-linter 경고 3건

AWS_ACCESS_KEY_ID·AWS_SECRET_ACCESS_KEY 추가 후 블록 전체 알파벳 순서가 맞지 않아 경고가 발생합니다.

올바른 순서: AWS_ACCESS_KEY_IDAWS_REGIONAWS_S3_BASE_URLAWS_S3_BUCKETAWS_SECRET_ACCESS_KEY

♻️ 제안: 알파벳 순서로 재정렬
 # ── AWS S3 ────────────────────────────────────
 # AWS IAM에서 액세스 키 발급 (로컬 개발 시 필수)
 # https://console.aws.amazon.com/iam
 AWS_ACCESS_KEY_ID=your_aws_access_key_id
-AWS_SECRET_ACCESS_KEY=your_aws_secret_access_key
 AWS_REGION=ap-northeast-2
-AWS_S3_BUCKET=your-bucket-name
 AWS_S3_BASE_URL=https://your-bucket-name.s3.ap-northeast-2.amazonaws.com
+AWS_S3_BUCKET=your-bucket-name
+AWS_SECRET_ACCESS_KEY=your_aws_secret_access_key
🧰 Tools
🪛 dotenv-linter (4.0.0)

[warning] 46-46: [UnorderedKey] The AWS_REGION key should go before the AWS_SECRET_ACCESS_KEY key

(UnorderedKey)


[warning] 47-47: [UnorderedKey] The AWS_S3_BUCKET key should go before the AWS_SECRET_ACCESS_KEY key

(UnorderedKey)


[warning] 48-48: [UnorderedKey] The AWS_S3_BASE_URL key should go before the AWS_S3_BUCKET key

(UnorderedKey)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.env.example around lines 44 - 48, Reorder the AWS S3 environment variables
so they are in alphabetical order by key: place AWS_ACCESS_KEY_ID first, then
AWS_REGION, then AWS_S3_BASE_URL, then AWS_S3_BUCKET, and finally
AWS_SECRET_ACCESS_KEY; keep the existing values unchanged and ensure the keys
AWS_ACCESS_KEY_ID, AWS_REGION, AWS_S3_BASE_URL, AWS_S3_BUCKET, and
AWS_SECRET_ACCESS_KEY are present exactly as listed to satisfy dotenv-linter.


# ── 사업자등록 조회 API ───────────────────────
# 공공데이터포털에서 API 키 발급
BIZ_API_KEY=your_biz_api_key
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ out/
.vscode/

### secret properties files ###
.env
/src/main/resources/application-blue.yml
/src/main/resources/application-green.yml
/src/main/resources/application-local.yml
Expand All @@ -53,3 +54,7 @@ build.gradle

src/main/resources/static/payment-test.html
src/main/resources/static/payment-success.html


.agent/rules
.agent/skills
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
package com.eatsfine.eatsfine.domain.payment.controller;

import com.eatsfine.eatsfine.domain.payment.dto.request.PaymentConfirmDTO;
import com.eatsfine.eatsfine.domain.payment.dto.request.PaymentRequestDTO;
import com.eatsfine.eatsfine.domain.payment.dto.response.PaymentResponseDTO;
import com.eatsfine.eatsfine.domain.payment.service.PaymentService;

import com.eatsfine.eatsfine.global.apiPayload.ApiResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
Expand Down Expand Up @@ -49,18 +53,21 @@ public ApiResponse<PaymentResponseDTO.CancelPaymentResultDTO> cancelPayment(
@Operation(summary = "결제 내역 조회", description = "로그인한 사용자의 결제 내역을 조회합니다.")
@GetMapping
public ApiResponse<PaymentResponseDTO.PaymentListResponseDTO> getPaymentList(
@RequestParam(name = "userId", required = false, defaultValue = "1") Long userId,
@AuthenticationPrincipal UserDetails user,
@RequestParam(name = "page", defaultValue = "1") Integer page,
@RequestParam(name = "limit", defaultValue = "10") Integer limit,
@RequestParam(name = "status", required = false) String status) {
// TODO: userId는 추후 Security Context에서 가져오도록 수정
return ApiResponse.onSuccess(paymentService.getPaymentList(userId, page, limit, status));
String email = user.getUsername();
return ApiResponse.onSuccess(paymentService.getPaymentList(email, page, limit, status));
}

@Operation(summary = "결제 상세 조회", description = "특정 결제 건의 상세 내역을 조회합니다.")
@GetMapping("/{paymentId}")
public ApiResponse<PaymentResponseDTO.PaymentDetailResultDTO> getPaymentDetail(
@AuthenticationPrincipal UserDetails user,
@PathVariable Long paymentId) {
return ApiResponse.onSuccess(paymentService.getPaymentDetail(paymentId));
String email = user.getUsername();
return ApiResponse.onSuccess(paymentService.getPaymentDetail(paymentId, email));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,30 @@
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import java.util.Optional;

public interface PaymentRepository extends JpaRepository<Payment, Long> {
Optional<Payment> findByOrderId(String orderId);
Optional<Payment> findByOrderId(String orderId);

Optional<Payment> findByPaymentKey(String paymentKey);
Optional<Payment> findByPaymentKey(String paymentKey);

Page<Payment> findAllByBooking_User_Id(Long userId, Pageable pageable);
@Query(value = "SELECT p FROM Payment p JOIN FETCH p.booking b JOIN FETCH b.store WHERE b.user.id = :userId", countQuery = "SELECT COUNT(p) FROM Payment p JOIN p.booking b JOIN b.store WHERE b.user.id = :userId")
Page<Payment> findAllByUserIdWithDetails(@Param("userId") Long userId, Pageable pageable);

Page<Payment> findAllByBooking_User_IdAndPaymentStatus(Long userId, PaymentStatus status, Pageable pageable);
@Query(value = "SELECT p FROM Payment p JOIN FETCH p.booking b JOIN FETCH b.store WHERE b.user.id = :userId AND p.paymentStatus = :status", countQuery = "SELECT COUNT(p) FROM Payment p JOIN p.booking b JOIN b.store WHERE b.user.id = :userId AND p.paymentStatus = :status")
Page<Payment> findAllByUserIdAndStatusWithDetails(@Param("userId") Long userId,
@Param("status") PaymentStatus status, Pageable pageable);

@Query(value = "SELECT p FROM Payment p JOIN FETCH p.booking b JOIN FETCH b.store s WHERE s.owner.id = :ownerId", countQuery = "SELECT COUNT(p) FROM Payment p JOIN p.booking b JOIN b.store s WHERE s.owner.id = :ownerId")
Page<Payment> findAllByOwnerIdWithDetails(@Param("ownerId") Long ownerId, Pageable pageable);

@Query(value = "SELECT p FROM Payment p JOIN FETCH p.booking b JOIN FETCH b.store s WHERE s.owner.id = :ownerId AND p.paymentStatus = :status", countQuery = "SELECT COUNT(p) FROM Payment p JOIN p.booking b JOIN b.store s WHERE s.owner.id = :ownerId AND p.paymentStatus = :status")
Page<Payment> findAllByOwnerIdAndStatusWithDetails(@Param("ownerId") Long ownerId,
@Param("status") PaymentStatus status, Pageable pageable);

@Query("SELECT p FROM Payment p JOIN FETCH p.booking b JOIN FETCH b.store s JOIN FETCH s.owner JOIN FETCH b.user WHERE p.id = :paymentId")
Optional<Payment> findByIdWithDetails(@Param("paymentId") Long paymentId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,16 @@
import com.eatsfine.eatsfine.domain.payment.enums.PaymentProvider;
import com.eatsfine.eatsfine.domain.payment.enums.PaymentStatus;
import com.eatsfine.eatsfine.domain.payment.enums.PaymentType;
import com.eatsfine.eatsfine.domain.user.entity.User;
import com.eatsfine.eatsfine.domain.user.enums.Role;
import com.eatsfine.eatsfine.domain.payment.repository.PaymentRepository;
import com.eatsfine.eatsfine.domain.payment.exception.PaymentException;
import com.eatsfine.eatsfine.domain.payment.status.PaymentErrorStatus;
import com.eatsfine.eatsfine.global.apiPayload.code.status.ErrorStatus;
import com.eatsfine.eatsfine.global.apiPayload.exception.GeneralException;
import com.eatsfine.eatsfine.domain.user.repository.UserRepository;
import com.eatsfine.eatsfine.domain.user.exception.UserException;
import com.eatsfine.eatsfine.domain.user.status.UserErrorStatus;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
Expand All @@ -38,6 +43,7 @@ public class PaymentService {

private final PaymentRepository paymentRepository;
private final BookingRepository bookingRepository;
private final UserRepository userRepository;
private final TossPaymentService tossPaymentService;

@Transactional
Expand Down Expand Up @@ -122,7 +128,6 @@ public PaymentResponseDTO.PaymentSuccessResultDTO confirmPayment(PaymentConfirmD
log.info("Booking confirmed for OrderID: {}", dto.orderId());
}


log.info("Payment confirmed for OrderID: {}", dto.orderId());

return new PaymentResponseDTO.PaymentSuccessResultDTO(
Expand All @@ -136,7 +141,6 @@ public PaymentResponseDTO.PaymentSuccessResultDTO confirmPayment(PaymentConfirmD
payment.getReceiptUrl());
}


@Transactional(noRollbackFor = GeneralException.class)
public PaymentResponseDTO.CancelPaymentResultDTO cancelPayment(String paymentKey,
PaymentRequestDTO.CancelPaymentDTO dto) {
Expand All @@ -162,28 +166,30 @@ public PaymentResponseDTO.CancelPaymentResultDTO cancelPayment(String paymentKey
}

@Transactional(readOnly = true)
public PaymentResponseDTO.PaymentListResponseDTO getPaymentList(Long userId, Integer page, Integer limit,
public PaymentResponseDTO.PaymentListResponseDTO getPaymentList(String email, Integer page, Integer limit,
String status) {
// limit 기본값 처리 (만약 null이면 10)
int size = (limit != null) ? limit : 10;
User user = userRepository.findByEmail(email)
.orElseThrow(() -> new UserException(UserErrorStatus.MEMBER_NOT_FOUND));
// limit 기본값 처리 (null이거나 0 이하이면 10)
int size = (limit != null && limit > 0) ? limit : 10;
// page 기본값 처리 (만약 null이면 1, 0보다 작으면 1로 보정). Spring Data는 0-based index이므로 -1
int pageNumber = (page != null && page > 0) ? page - 1 : 0;

Pageable pageable = PageRequest.of(pageNumber, size);

PaymentStatus paymentStatus = parsePaymentStatus(status);

Page<Payment> paymentPage;
if (status != null && !status.isEmpty()) {
PaymentStatus paymentStatus;
try {
paymentStatus = PaymentStatus.valueOf(status.toUpperCase());
} catch (IllegalArgumentException e) {
// 유효하지 않은 status가 들어오면 BadRequest 예외 발생
throw new GeneralException(ErrorStatus._BAD_REQUEST);
}
paymentPage = paymentRepository.findAllByBooking_User_IdAndPaymentStatus(userId, paymentStatus,
pageable);
if (user.getRole() == Role.ROLE_OWNER) {
paymentPage = (paymentStatus != null)
? paymentRepository.findAllByOwnerIdAndStatusWithDetails(user.getId(),
paymentStatus, pageable)
: paymentRepository.findAllByOwnerIdWithDetails(user.getId(), pageable);
} else {
paymentPage = paymentRepository.findAllByBooking_User_Id(userId, pageable);
paymentPage = (paymentStatus != null)
? paymentRepository.findAllByUserIdAndStatusWithDetails(user.getId(),
paymentStatus, pageable)
: paymentRepository.findAllByUserIdWithDetails(user.getId(), pageable);
}
Comment on lines 168 to 193
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

이메일 기반 접근 제어와 역할 분기 로직이 깔끔하게 구현되었습니다.

parsePaymentStatus 헬퍼 추출, limit 기본값/음수 처리, 역할 기반 분기의 삼항 연산자 전환 모두 이전 리뷰 피드백이 잘 반영되었습니다.

한 가지 개선 사항으로, limit에 상한값(예: 100)을 두지 않으면 클라이언트가 극단적으로 큰 값을 전달하여 과도한 메모리 사용 및 쿼리 부하를 유발할 수 있습니다.

♻️ limit 상한 제한 제안
-                int size = (limit != null && limit > 0) ? limit : 10;
+                int size = (limit != null && limit > 0) ? Math.min(limit, 100) : 10;

As per coding guidelines, "캐시 적용 가능성이 있거나, 과도한 연산이 반복되는 구간이 있는지" 검토 항목에 해당합니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/java/com/eatsfine/eatsfine/domain/payment/service/PaymentService.java`
around lines 168 - 193, getPaymentList currently sets size from limit without an
upper bound, allowing clients to request arbitrarily large pages; add a
MAX_PAGE_SIZE constant (e.g., 100) in PaymentService and clamp the computed size
with Math.min(size, MAX_PAGE_SIZE) (replace the existing size computation so it
first applies your existing null/<=0 fallback then caps to MAX_PAGE_SIZE) to
prevent excessive memory/query loads; reference PaymentService.getPaymentList,
the limit parameter, and the size variable when making this change.


List<PaymentResponseDTO.PaymentHistoryResultDTO> payments = paymentPage.getContent().stream()
Expand Down Expand Up @@ -211,10 +217,19 @@ public PaymentResponseDTO.PaymentListResponseDTO getPaymentList(Long userId, Int
}

@Transactional(readOnly = true)
public PaymentResponseDTO.PaymentDetailResultDTO getPaymentDetail(Long paymentId) {
Payment payment = paymentRepository.findById(paymentId)
public PaymentResponseDTO.PaymentDetailResultDTO getPaymentDetail(Long paymentId, String email) {
User user = userRepository.findByEmail(email)
.orElseThrow(() -> new UserException(UserErrorStatus.MEMBER_NOT_FOUND));
Payment payment = paymentRepository.findByIdWithDetails(paymentId)
.orElseThrow(() -> new PaymentException(PaymentErrorStatus._PAYMENT_NOT_FOUND));

boolean isBooker = payment.getBooking().getUser().getId().equals(user.getId());
boolean isStoreOwner = payment.getBooking().getStore().getOwner().getId().equals(user.getId());

if (!isBooker && !isStoreOwner) {
throw new PaymentException(PaymentErrorStatus._PAYMENT_ACCESS_DENIED);
}
Comment on lines 219 to +231
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# 1. Booking 엔티티 파일 찾기 및 user, store 필드의 nullable 설정 확인
echo "=== Booking 엔티티 검색 ==="
fd -e java -i "booking" | grep -i entity
echo ""

# 2. 엔티티 정의 확인
find . -path "*/entity/*" -name "*.java" | grep -i booking | head -5
find . -path "*/entity/*" -name "*.java" | grep -i store | head -5
find . -path "*/entity/*" -name "*.java" | grep -i payment | head -5

Repository: Eatsfine/BE

Length of output: 781


🏁 Script executed:

#!/bin/bash
# Booking 엔티티의 user와 store 필드 정의 확인
echo "=== Booking Entity 분석 ==="
find . -type f -name "Booking.java" -not -path "*/test/*" -not -path "*/.git/*" | while read f; do
  echo "File: $f"
  cat -n "$f" | grep -A 3 -B 1 '@ManyToOne\|@OneToOne\|private.*user\|private.*store\|@JoinColumn'
done

Repository: Eatsfine/BE

Length of output: 577


🏁 Script executed:

#!/bin/bash
# Store 엔티티의 owner 필드 정의 확인
echo "=== Store Entity owner 필드 분석 ==="
find . -type f -name "Store.java" -not -path "*/test/*" | while read f; do
  echo "File: $f"
  cat -n "$f" | grep -A 3 -B 1 'owner\|@ManyToOne.*owner\|@JoinColumn.*owner'
done

Repository: Eatsfine/BE

Length of output: 414


🏁 Script executed:

#!/bin/bash
# findByIdWithDetails 메서드 구현 확인
echo "=== findByIdWithDetails 구현 검색 ==="
rg "findByIdWithDetails" -A 5 -B 2

Repository: Eatsfine/BE

Length of output: 2944


쿼리에서 store의 owner를 함께 로드해야 합니다.

소유권 검증 로직 자체는 올바릅니다. 다만 findByIdWithDetails 쿼리에서 Store.owner를 함께 로드하지 않았기 때문에, payment.getBooking().getStore().getOwner() 호출 시 추가 쿼리가 발생합니다.

현재 쿼리:

SELECT p FROM Payment p JOIN FETCH p.booking b JOIN FETCH b.store JOIN FETCH b.user WHERE p.id = :paymentId

이 쿼리는 b.store는 로드하지만, b.store.owner는 로드하지 않습니다. 따라서 store.owner 접근 시 LazyInitializationException이 발생하거나 추가 N+1 쿼리 문제가 생길 수 있습니다. JOIN FETCH b.store s JOIN FETCH s.owner를 추가하여 owner도 함께 로드해야 합니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/java/com/eatsfine/eatsfine/domain/payment/service/PaymentService.java`
around lines 233 - 245, The ownership check in PaymentService.getPaymentDetail
triggers an extra lazy load because findByIdWithDetails doesn't fetch
Store.owner; update the repository method findByIdWithDetails JPQL to eagerly
load the owner (e.g. add "JOIN FETCH b.store s JOIN FETCH s.owner" or
equivalent) so payment.getBooking().getStore().getOwner() is resolved in the
same query and avoids LazyInitializationException/N+1 queries.


return new PaymentResponseDTO.PaymentDetailResultDTO(
payment.getId(),
payment.getBooking().getId(),
Expand Down Expand Up @@ -306,4 +321,15 @@ public void processWebhook(PaymentWebhookDTO dto) {
log.info("Webhook processed: Payment {} status updated to REFUNDED", data.orderId());
}
}

private PaymentStatus parsePaymentStatus(String status) {
if (status == null || status.isBlank()) {
return null;
}
try {
return PaymentStatus.valueOf(status.toUpperCase());
} catch (IllegalArgumentException e) {
throw new GeneralException(ErrorStatus._BAD_REQUEST);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ public enum PaymentErrorStatus implements BaseErrorCode {
_PAYMENT_INVALID_DEPOSIT(HttpStatus.BAD_REQUEST, "PAYMENT4001", "예약금이 유효하지 않습니다."),
_PAYMENT_INVALID_AMOUNT(HttpStatus.BAD_REQUEST, "PAYMENT4002", "결제 금액이 일치하지 않습니다."),
_PAYMENT_NOT_FOUND(HttpStatus.NOT_FOUND, "PAYMENT4003", "결제 정보를 찾을 수 없습니다."),
_BOOKING_NOT_FOUND(HttpStatus.NOT_FOUND, "BOOKING4001", "예약을 찾을 수 없습니다.");
_BOOKING_NOT_FOUND(HttpStatus.NOT_FOUND, "BOOKING4001", "예약을 찾을 수 없습니다."),
_PAYMENT_ACCESS_DENIED(HttpStatus.FORBIDDEN, "PAYMENT4031", "해당 결제 정보에 접근할 권한이 없습니다.");

private final HttpStatus httpStatus;
private final String code;
Expand Down
10 changes: 6 additions & 4 deletions src/main/resources/application-local.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,16 @@ spring:

payment:
toss:
widget-secret-key: test_gsk_docs_OaPz8L5KdmQXkzRz3y47BMw6
widget-secret-key: ${TOSS_WIDGET_SECRET_KEY}

cloud:
aws:
region: ap-northeast-2
region: ${AWS_REGION}
s3:
bucket: eatsfine-images
base-url: https://eatsfine-images.s3.ap-northeast-2.amazonaws.com
bucket: ${AWS_S3_BUCKET}
base-url: ${AWS_S3_BASE_URL}

api:
service-key: ${BIZ_API_KEY}
jwt:
Comment on lines +73 to 74
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

api: 블록과 jwt: 블록 사이 빈 줄 누락.

파일 내 다른 최상위 섹션들(payment:, cloud:, jwt: 등)은 빈 줄로 구분되어 있는 반면, api.service-key (line 73) 바로 다음에 jwt: (line 74)가 붙어 있어 일관성이 떨어집니다.

✏️ 빈 줄 추가 제안
 api:
   service-key: ${BIZ_API_KEY}
+
 jwt:
   secret: ${SECRET_KEY}
📝 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
service-key: ${BIZ_API_KEY}
jwt:
service-key: ${BIZ_API_KEY}
jwt:
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/resources/application-local.yml` around lines 73 - 74, The YAML is
missing a blank line between the api.service-key entry and the next top-level
jwt: block, breaking top-level section separation consistency; update the file
so there is an empty line between the api.service-key (api:) block and the jwt:
top-level key to match other sections like payment: and cloud:, ensuring
consistent visual separation for top-level YAML blocks.

🧹 Nitpick | 🔵 Trivial

api: 블록과 jwt: 블록 사이 빈 줄 누락.

파일 내 다른 최상위 섹션들(payment:, cloud: 등)은 빈 줄로 구분되어 있으나, api.service-key (Line 73) 바로 다음에 jwt: (Line 74)가 붙어 있어 가독성이 떨어집니다.

✏️ 빈 줄 추가 제안
 api:
   service-key: ${BIZ_API_KEY}
+
 jwt:
   secret: ${SECRET_KEY}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/resources/application-local.yml` around lines 73 - 74, The YAML is
missing a blank line between the api.service-key entry and the next top-level
jwt: block, harming readability; edit the area containing api.service-key (the
`service-key: ${BIZ_API_KEY}` line under the api: block) and insert a single
empty line before the `jwt:` top-level key so top-level sections are
consistently separated.

secret: ${SECRET_KEY}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.bean.override.mockito.MockitoBean;

import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.test.context.ActiveProfiles;
Expand Down Expand Up @@ -41,13 +41,13 @@ class HealthControllerTest {
@Autowired
private MockMvc mockMvc;

@MockBean
@MockitoBean
private JwtAuthenticationFilter jwtAuthenticationFilter;

@MockBean
@MockitoBean
private CustomAuthenticationEntryPoint customAuthenticationEntryPoint;

@MockBean
@MockitoBean
private CustomAccessDeniedHandler customAccessDeniedHandler;

@BeforeEach
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.http.MediaType;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.web.servlet.MockMvc;
Expand Down Expand Up @@ -42,16 +42,16 @@ class InquiryControllerTest {
@Autowired
private MockMvc mockMvc;

@MockBean
@MockitoBean
private InquiryService inquiryService;

@MockBean
@MockitoBean
private JwtAuthenticationFilter jwtAuthenticationFilter;

@MockBean
@MockitoBean
private CustomAuthenticationEntryPoint customAuthenticationEntryPoint;

@MockBean
@MockitoBean
private CustomAccessDeniedHandler customAccessDeniedHandler;

@Autowired
Expand Down
Loading