Conversation
📝 WalkthroughWalkthrough쿠폰 엔티티를 JPA 엔티티로 매핑하고 Lombok을 적용했으며, 애플리케이션 초기 데이터 주입, REST 컨트롤러·서비스·레포지토리 계층과 H2 인메모리 데이터소스 설정이 추가되었습니다. 쿠폰 발급은 컨트롤러 → 서비스 → 레포지토리 → DB 순으로 처리됩니다. Changes
Sequence Diagram(s)sequenceDiagram
participant Client as Client
participant Controller as "CouponController\n(POST /api/coupon/issue)"
participant Service as "CouponService"
participant Repo as "CouponRepository (JPA)"
participant DB as "H2 Database"
Client->>Controller: POST /api/coupon/issue
Controller->>Service: issue()
Service->>Repo: findById(1L)
Repo->>DB: SELECT ... FROM coupon WHERE id=1
DB-->>Repo: Coupon row
Repo-->>Service: Coupon entity
Service->>Service: decreaseStock()
Service->>Repo: save(coupon)
Repo->>DB: UPDATE coupon SET stock=...
DB-->>Repo: OK
Repo-->>Service: saved entity
Service-->>Controller: return
Controller-->>Client: "쿠폰 발급 성공"
예상 코드 리뷰 노력🎯 3 (Moderate) | ⏱️ ~25 분 시
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@src/main/java/com/jisung0509/couponEvent/domain/coupon/Coupon.java`:
- Around line 12-15: The Coupon JPA entity class lacks the required no-args
constructor; add Lombok's `@NoArgsConstructor` with an appropriate visibility
(public or protected) to the Coupon class declaration so JPA can instantiate it
(i.e., annotate the Coupon class with `@NoArgsConstructor` and ensure Lombok is
available), keeping the existing `@Entity` and `@Getter` annotations intact.
- Around line 17-22: The Coupon entity is missing optimistic locking and a
controlled way to mutate stock: add a `@Version` field (e.g., private Long
version) to the Coupon class to enable JPA optimistic locking, and implement
explicit business methods to modify stock (for example a decreaseStock(int
amount) or adjustStock(int delta) on Coupon) that validate availability and
update the stock field instead of exposing it publicly; reference the existing
id and stock fields when locating where to add the version field and the new
business method.
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@src/main/java/com/jisung0509/couponEvent/service/CouponService.java`:
- Around line 16-20: The current read-then-decrease in CouponService uses
couponRepository.findById(...) which allows lost updates under concurrency; add
a pessimistic lock repository method (e.g., declare Optional<Coupon>
findByIdForUpdate(Long id) annotated with `@Lock`(LockModeType.PESSIMISTIC_WRITE)
and a matching `@Query`) and replace the service call to
couponRepository.findById(...) with couponRepository.findByIdForUpdate(id), then
call coupon.decreaseStock() and persist/flush as before; ensure you import
jakarta.persistence.LockModeType and
org.springframework.data.jpa.repository.Lock and handle the Optional the same
way as before.
In `@src/main/resources/application.yaml`:
- Around line 2-25: Move development-only H2 and JPA settings out of the default
configuration into a dedicated dev profile (e.g., application-local.yaml or
application-dev.yaml): create the new profile file containing keys
datasource.url, h2.console.enabled/path, username/password,
jpa.hibernate.ddl-auto (set to create), and jpa.show-sql; then update the main
application.yaml to use safe production defaults (remove H2 console enablement,
require a password or omit in prod, set jpa.hibernate.ddl-auto to validate or
none, and set show-sql: false) and document activating the dev profile
(spring.profiles.active: local or via environment) so dev settings are only
loaded when that profile is active.
🧹 Nitpick comments (1)
src/main/java/com/jisung0509/couponEvent/service/CouponService.java (1)
5-15: Spring@Transactional사용을 권장합니다Spring Boot에서는
org.springframework.transaction.annotation.Transactional이 전파/격리/읽기 전용 옵션 측면에서 더 적합합니다.🔧 수정 제안
-import jakarta.transaction.Transactional; +import org.springframework.transaction.annotation.Transactional;
| Coupon coupon = couponRepository.findById(1L) | ||
| .orElseThrow(() -> new IllegalArgumentException("쿠폰이 존재하지 않습니다.")); | ||
|
|
||
| coupon.decreaseStock(); | ||
|
|
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
git ls-files | grep -E "Coupon|coupon" | head -20Repository: chojisung/couponEvent
Length of output: 558
🏁 Script executed:
cat -n src/main/java/com/jisung0509/couponEvent/service/CouponService.java | sed -n '1,30p'Repository: chojisung/couponEvent
Length of output: 1045
🏁 Script executed:
find . -name "Coupon.java" -o -name "CouponRepository.java" | head -10Repository: chojisung/couponEvent
Length of output: 210
🏁 Script executed:
cat -n src/main/java/com/jisung0509/couponEvent/domain/coupon/Coupon.javaRepository: chojisung/couponEvent
Length of output: 1108
🏁 Script executed:
cat -n src/main/java/com/jisung0509/couponEvent/reposotiory/CouponRepository.javaRepository: chojisung/couponEvent
Length of output: 361
동시 발급 시 재고 손실 방지를 위해 비관적 잠금 추가 필요
현재 코드는 조회 후 감소 작업 사이에 보호 메커니즘이 없어서 동시 요청 시 재고 업데이트가 손실될 수 있습니다. 예를 들어, 재고가 10개일 때 두 개의 동시 요청이 모두 10을 읽고 각각 9로 저장하면 재고는 8이 아닌 9가 됩니다. 비관적 잠금을 적용하여 읽기 시점에 행을 잠그세요.
수정 제안
import jakarta.persistence.LockModeType;
import org.springframework.data.jpa.repository.Lock;
import org.springframework.data.jpa.repository.Query;
`@Lock`(LockModeType.PESSIMISTIC_WRITE)
`@Query`("select c from Coupon c where c.id = :id")
Optional<Coupon> findByIdForUpdate(`@Param`("id") Long id);그리고 CouponService에서:
- Coupon coupon = couponRepository.findById(1L)
+ Coupon coupon = couponRepository.findByIdForUpdate(1L)
.orElseThrow(() -> new IllegalArgumentException("쿠폰이 존재하지 않습니다."));🤖 Prompt for AI Agents
In `@src/main/java/com/jisung0509/couponEvent/service/CouponService.java` around
lines 16 - 20, The current read-then-decrease in CouponService uses
couponRepository.findById(...) which allows lost updates under concurrency; add
a pessimistic lock repository method (e.g., declare Optional<Coupon>
findByIdForUpdate(Long id) annotated with `@Lock`(LockModeType.PESSIMISTIC_WRITE)
and a matching `@Query`) and replace the service call to
couponRepository.findById(...) with couponRepository.findByIdForUpdate(id), then
call coupon.decreaseStock() and persist/flush as before; ensure you import
jakarta.persistence.LockModeType and
org.springframework.data.jpa.repository.Lock and handle the Optional the same
way as before.
| datasource: | ||
| # H2 데이터베이스는 기본적으로 메모리(jdbc:h2:mem:)에 띄워집니다. | ||
| # testdb는 데이터베이스 이름입니다. | ||
| url: jdbc:h2:mem:testdb | ||
| driver-class-name: org.h2.Driver | ||
| username: sa | ||
| password: # 비밀번호는 없습니다 (비워둠) | ||
|
|
||
| # H2 콘솔(웹 화면)을 활성화하는 설정입니다. | ||
| h2: | ||
| console: | ||
| enabled: true | ||
| # 접속 경로: localhost:8080/h2-console 이 접속 가능하도록 설정 | ||
| path: /h2-console | ||
|
|
||
| # JPA 관련 설정 | ||
| jpa: | ||
| hibernate: | ||
| # (중요) 'create': 앱 실행할 때마다 테이블 지우고 새로 만듭니다. | ||
| # 데이터가 날아가는 게 싫으면 'update'로 바꾸세요. | ||
| # 개발 중에는 'create'가 편합니다. | ||
| ddl-auto: create | ||
| # (중요) 쿼리를 실행할 때 콘솔에 SQL 로그를 보여줍니다. ("왜 에러가 났지?" 할 때 유용함) | ||
| show-sql: true |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# Find all application.yaml and profile-specific files
find . -type f -name "application*.yaml" -o -name "application*.yml" | head -20Repository: chojisung/couponEvent
Length of output: 102
🏁 Script executed:
# Check the main application.yaml file
cat -n src/main/resources/application.yaml 2>/dev/null || echo "File not found at expected path"Repository: chojisung/couponEvent
Length of output: 986
🏁 Script executed:
# Look for Spring Boot profile configurations across the project
fd -e yaml -e yml | xargs grep -l "spring:" 2>/dev/null | head -10Repository: chojisung/couponEvent
Length of output: 100
🏁 Script executed:
# Check project structure to understand the setup
ls -la src/main/resources/ 2>/dev/null || echo "Directory not found"Repository: chojisung/couponEvent
Length of output: 241
개발용 DB/콘솔 설정을 프로파일로 격리하세요
현재 H2 콘솔 활성화, 무비밀번호, ddl-auto: create, show-sql 등이 기본 프로파일에 적용되어 있습니다. 프로파일 분리가 없는 상태에서 이 설정들이 모든 환경(개발, 스테이징, 운영)에 로드되므로, 운영 환경에서 데이터 유실 및 콘솔 노출 위험이 매우 큽니다. application-local.yaml 또는 application-dev.yaml을 별도로 생성하여 이동하고, 기본 설정에서는 운영 환경에 적합한 값으로 설정하는 것이 필수입니다.
🔧 수정 제안 (local 프로파일로 한정)
spring:
+ config:
+ activate:
+ on-profile: local
datasource:
# H2 데이터베이스는 기본적으로 메모리(jdbc:h2:mem:)에 띄워집니다.
# testdb는 데이터베이스 이름입니다.
url: jdbc:h2:mem:testdb🤖 Prompt for AI Agents
In `@src/main/resources/application.yaml` around lines 2 - 25, Move
development-only H2 and JPA settings out of the default configuration into a
dedicated dev profile (e.g., application-local.yaml or application-dev.yaml):
create the new profile file containing keys datasource.url,
h2.console.enabled/path, username/password, jpa.hibernate.ddl-auto (set to
create), and jpa.show-sql; then update the main application.yaml to use safe
production defaults (remove H2 console enablement, require a password or omit in
prod, set jpa.hibernate.ddl-auto to validate or none, and set show-sql: false)
and document activating the dev profile (spring.profiles.active: local or via
environment) so dev settings are only loaded when that profile is active.
쿠폰 발급 동시성 이슈를 확인하기 위해서 엔티티를 설계했습니다.
Summary by CodeRabbit
New Features
Chores
✏️ Tip: You can customize this high-level summary in your review settings.