-
Notifications
You must be signed in to change notification settings - Fork 3
feat : 코딩 테스트 코드 자동 저장(Draft) 기능 및 동시성 제어 구현 #204
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
- 도메인 분리: Submission과 별도로 Draft 도메인 패키지 신설 - DB 설계: User+Problem+Language 복합 유니크 키 및 Version 컬럼 추가 - 동시성 제어: - JPA Optimistic Locking(@Version) 적용 - 요청 버전과 DB 버전을 비교하는 명시적 검증 로직 구현 - 충돌 시 409 Conflict 예외 처리 - API 구현: - POST /api/drafts (자동 저장/업데이트) - GET /api/drafts (임시 저장 내역 조회)
- 기존에 저장된 값이 없다면 null 값을 리턴하도록 함
Walkthrough새로운 Draft(임시 저장) 기능을 구현합니다. 사용자는 코드를 임시로 저장하고 조회할 수 있으며, 낙관적 잠금을 통한 동시성 제어와 접근 권한 검증을 포함합니다. Changes
Sequence Diagram(s)sequenceDiagram
actor Client
participant Controller as DraftController
participant Service as DraftService
participant DomainService as DraftDomainService
participant Repository as DraftRepository
participant Database as Database
rect rgba(100, 200, 100, 0.2)
Note over Client,Database: 임시 저장 (autoSave) 흐름
Client->>Controller: POST /api/drafts<br/>(problemId, languageId, code, version)
Controller->>Service: autoSave(userId, request)
Service->>Service: 사용자, 문제, 언어 엔티티 조회
Service->>DomainService: autoSave(user, problem, language,<br/>code, version)
DomainService->>Repository: findByUserAndProblemAndLanguage()
Repository->>Database: 기존 Draft 조회
alt Draft 존재
Database-->>Repository: Draft 반환
Repository-->>DomainService: Draft
DomainService->>DomainService: 버전 확인<br/>(제공된 version == stored version?)
alt 버전 일치
DomainService->>DomainService: updateCode(newCode)
DomainService->>Repository: saveAndFlush(draft)
Repository->>Database: UPDATE draft
else 버전 불일치
DomainService-->>Service: DraftException<br/>(DRAFT_VERSION_CONFLICT)
Service-->>Controller: 409 Conflict
end
else Draft 미존재
DomainService->>Repository: save(new Draft)
Repository->>Database: INSERT draft
end
Database-->>Repository: Draft (저장됨)
Repository-->>DomainService: Draft
DomainService-->>Service: Draft
Service->>Service: DraftResponse.from(draft)
Service-->>Controller: DraftResponse
Controller-->>Client: 200 OK + DraftResponse
end
rect rgba(100, 150, 200, 0.2)
Note over Client,Database: 임시 저장 조회 (getDraft) 흐름
Client->>Controller: GET /api/drafts<br/>(problemId, languageId)
Controller->>Service: getDraft(userId, problemId, languageId)
Service->>Service: 사용자, 문제, 언어 엔티티 조회
Service->>DomainService: getDraft(user, problem, language)
DomainService->>Repository: findByUserAndProblemAndLanguage()
Repository->>Database: 조회
Database-->>Repository: Optional<Draft>
Repository-->>DomainService: Optional<Draft>
DomainService-->>Service: Optional<Draft>
Service->>Service: 존재 여부 확인
alt Draft 존재
Service->>Service: DraftResponse.from(draft)
Service-->>Controller: Optional(DraftResponse)
else Draft 미존재
Service-->>Controller: Optional.empty()
end
Controller-->>Client: 200 OK<br/>(DraftResponse 또는 null)
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
🧹 Nitpick comments (4)
src/main/java/org/ezcode/codetest/domain/draft/model/entity/Draft.java (2)
64-71: Builder에서 version 파라미터 노출을 제거하는 것을 권장합니다.Builder를 통해 version을 수동으로 설정할 수 있는데, 이는 JPA의 @Version 자동 관리 메커니즘과 충돌할 수 있습니다. version은 JPA가 관리해야 하므로 생성자에서 제외하는 것이 안전합니다.
🔎 권장 수정안
@Builder -public Draft(User user, Problem problem, Language language, String code, Long version) { +public Draft(User user, Problem problem, Language language, String code) { this.user = user; this.problem = problem; this.language = language; this.code = code; - this.version = version; }
60-62: 코드 검증 로직 추가를 고려하세요.updateCode 메서드가 단순 세터로 구현되어 있습니다. 코드 길이 검증(예: 100,000자 제한)이나 null 체크를 추가하면 도메인 무결성이 향상됩니다.
🔎 제안 수정안
public void updateCode(String newCode) { + if (newCode != null && newCode.length() > 100000) { + throw new IllegalArgumentException("코드는 최대 100,000자까지 입력 가능합니다."); + } this.code = newCode; }src/main/java/org/ezcode/codetest/domain/draft/service/DraftDomainService.java (1)
24-51: 수동 버전 검증과 JPA 낙관적 락의 중복 전략을 명확히 하세요.Line 32의 수동 버전 체크는 "fail-fast" 접근법이지만, Line 26과 Line 37 사이에 발생하는 실제 race condition은 방지하지 못합니다. JPA의 @Version만이 flush 시점에 동시 수정을 감지할 수 있습니다.
현재 구현:
- 수동 체크: 클라이언트가 오래된 버전으로 요청 시 즉시 실패 (fail-fast)
- JPA 체크: 실제 동시 수정 시 ObjectOptimisticLockingFailureException 발생
이 이중 검증 전략이 의도된 것이라면 주석으로 명확히 문서화하거나, 불필요하다면 수동 체크를 제거하고 JPA 낙관적 락만 사용하는 것을 권장합니다.
🔎 제안 1: 전략 문서화
if (existingDraftOpt.isPresent()) { Draft draft = existingDraftOpt.get(); + // Fail-fast: 클라이언트가 명백히 오래된 버전을 보낸 경우 즉시 거부 + // (실제 동시성 제어는 JPA @Version이 flush 시점에 처리) if (version != null && !Objects.equals(version, draft.getVersion())) { throw new DraftException(DraftExceptionCode.DRAFT_VERSION_CONFLICT); }🔎 제안 2: 수동 체크 제거 (JPA만 사용)
if (existingDraftOpt.isPresent()) { Draft draft = existingDraftOpt.get(); - // 명시적 버전 검증: 클라이언트가 보낸 version과 DB의 version이 일치해야 함 - if (version != null && !draft.getVersion().equals(version)) { - throw new DraftException(DraftExceptionCode.DRAFT_VERSION_CONFLICT); - } - draft.updateCode(code); draftRepository.saveAndFlush(draft); + // JPA @Version이 동시 수정을 감지하여 ObjectOptimisticLockingFailureException 발생src/main/java/org/ezcode/codetest/application/draft/servcie/DraftService.java (1)
43-53: 이중 버전 충돌 검증 메커니즘 명확히 하기현재 버전 충돌은 두 계층에서 감지됩니다:
DraftDomainService의 명시적 버전 체크 (라인 31-34): 요청 버전과 DB 버전 비교 후 즉시DraftException발생DraftService의ObjectOptimisticLockingFailureException처리 (라인 51-52): JPA 낙관적 락 예외 catch이 이중 방어 전략은 TOCTOU(Time-of-Check to Time-of-Use) 시나리오를 방어합니다. 명시적 체크(라인 32)와 실제 저장(라인 37) 사이에 다른 스레드가 Draft를 수정할 수 있기 때문에, JPA의
@Version낙관적 락은 필수적인 안전망입니다.이 의도를 팀과 공유하기 위해 catch 블록에 주석을 추가하는 것을 권장합니다:
} catch (ObjectOptimisticLockingFailureException e) { + // DraftDomainService의 명시적 체크 후에도 발생 가능한 TOCTOU 시나리오 방어 throw new DraftException(DraftExceptionCode.DRAFT_VERSION_CONFLICT); }
📜 Review details
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (11)
src/main/java/org/ezcode/codetest/application/draft/dto/request/DraftSaveRequest.javasrc/main/java/org/ezcode/codetest/application/draft/dto/response/DraftResponse.javasrc/main/java/org/ezcode/codetest/application/draft/servcie/DraftService.javasrc/main/java/org/ezcode/codetest/domain/draft/exception/DraftException.javasrc/main/java/org/ezcode/codetest/domain/draft/exception/DraftExceptionCode.javasrc/main/java/org/ezcode/codetest/domain/draft/model/entity/Draft.javasrc/main/java/org/ezcode/codetest/domain/draft/repository/DraftRepository.javasrc/main/java/org/ezcode/codetest/domain/draft/service/DraftDomainService.javasrc/main/java/org/ezcode/codetest/infrastructure/persistence/repository/draft/DraftJpaRepository.javasrc/main/java/org/ezcode/codetest/infrastructure/persistence/repository/draft/DraftRepositoryImpl.javasrc/main/java/org/ezcode/codetest/presentation/draft/DraftController.java
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-06-15T04:37:29.231Z
Learnt from: chat26666
Repo: ezcode-my/backend PR: 64
File: src/main/java/org/ezcode/codetest/infrastructure/persistence/repository/game/EncounterChoiceRepositoryImpl.java:0-0
Timestamp: 2025-06-15T04:37:29.231Z
Learning: EncounterChoiceRepositoryImpl in src/main/java/org/ezcode/codetest/infrastructure/persistence/repository/game/EncounterChoiceRepositoryImpl.java is intentionally a skeleton implementation that is work-in-progress and will be completed later.
Applied to files:
src/main/java/org/ezcode/codetest/domain/draft/repository/DraftRepository.javasrc/main/java/org/ezcode/codetest/infrastructure/persistence/repository/draft/DraftJpaRepository.javasrc/main/java/org/ezcode/codetest/infrastructure/persistence/repository/draft/DraftRepositoryImpl.java
📚 Learning: 2025-07-02T12:05:54.917Z
Learnt from: Kimminu7
Repo: ezcode-my/backend PR: 133
File: src/test/java/org/ezcode/codetest/domain/problem/service/ProblemDomainServiceTest.java:92-99
Timestamp: 2025-07-02T12:05:54.917Z
Learning: ProblemDomainService의 removeProblem 메서드는 DB에서 Problem을 삭제한 후 Elasticsearch에서도 해당 ProblemSearchDocument를 찾아서 삭제합니다. 만약 Elasticsearch에서 문서를 찾지 못하면 ProblemException(PROBLEM_NOT_FOUND)을 던지므로, 테스트에서는 problem.getId()와 searchRepository.findById() 모두 적절하게 mock해야 합니다.
Applied to files:
src/main/java/org/ezcode/codetest/domain/draft/service/DraftDomainService.java
🧬 Code graph analysis (4)
src/main/java/org/ezcode/codetest/application/draft/servcie/DraftService.java (1)
src/main/java/org/ezcode/codetest/domain/draft/service/DraftDomainService.java (1)
Slf4j(17-57)
src/main/java/org/ezcode/codetest/domain/draft/exception/DraftExceptionCode.java (1)
src/main/java/org/ezcode/codetest/domain/draft/exception/DraftException.java (1)
Getter(9-21)
src/main/java/org/ezcode/codetest/domain/draft/service/DraftDomainService.java (1)
src/main/java/org/ezcode/codetest/application/draft/servcie/DraftService.java (1)
Slf4j(24-68)
src/main/java/org/ezcode/codetest/domain/draft/exception/DraftException.java (1)
src/main/java/org/ezcode/codetest/common/base/exception/BaseException.java (1)
BaseException(5-12)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build
🔇 Additional comments (13)
src/main/java/org/ezcode/codetest/domain/draft/exception/DraftException.java (1)
10-21: LGTM! 표준 예외 패턴을 올바르게 구현했습니다.BaseException을 확장하여 ResponseCode 기반의 일관된 예외 처리를 지원합니다. 구조가 명확하고 구현이 정확합니다.
src/main/java/org/ezcode/codetest/infrastructure/persistence/repository/draft/DraftJpaRepository.java (1)
11-14: LGTM! Spring Data JPA 표준 패턴을 따릅니다.메서드 명명 규칙에 따라 복합 키 조회를 위한 쿼리가 자동 생성됩니다. 구현이 올바릅니다.
src/main/java/org/ezcode/codetest/application/draft/dto/response/DraftResponse.java (1)
7-22: LGTM! 명확한 DTO 매핑 구조입니다.from(Draft) 정적 팩토리 메서드가 연관 엔티티에서 ID를 추출하여 응답 DTO를 구성합니다. 구현이 정확합니다.
src/main/java/org/ezcode/codetest/domain/draft/repository/DraftRepository.java (1)
10-17: LGTM! 도메인 레이어 인터페이스가 명확합니다.saveAndFlush 메서드가 노출되어 있어 낙관적 락의 버전을 즉시 반환할 수 있습니다(PR 목표에 부합).
src/main/java/org/ezcode/codetest/domain/draft/model/entity/Draft.java (2)
23-36: LGTM! 엔티티 구조가 올바릅니다.복합 유니크 제약 조건이 적절히 설정되어 사용자-문제-언어별 단일 Draft를 보장합니다. LAZY 로딩 전략도 성능상 적절합니다.
57-58: LGTM! 낙관적 락을 위한 @Version 필드가 올바르게 설정되었습니다.JPA가 자동으로 버전을 관리하여 동시성 제어를 제공합니다.
src/main/java/org/ezcode/codetest/domain/draft/service/DraftDomainService.java (1)
53-56: LGTM! 조회 메서드가 명확합니다.저장된 Draft를 조회하는 로직이 간결하고 정확합니다.
src/main/java/org/ezcode/codetest/presentation/draft/DraftController.java (1)
33-49: LGTM! POST 엔드포인트가 올바르게 구현되었습니다.@Valid 검증과 인증 처리가 적절하며, Swagger 문서화도 명확합니다. 버전 충돌 시 409 응답이 반환됩니다(DraftService에서 DraftException 발생).
src/main/java/org/ezcode/codetest/application/draft/dto/request/DraftSaveRequest.java (1)
6-18: 필드 검증이 적절하게 설계되었습니다.version 필드가 새 엔티티의 경우 null을 허용하도록 설정되어 있으며, 코드 크기 제한(100,000자)이 올바르게 적용되었습니다. Draft 엔티티의 code 필드에 @lob 어노테이션이 적용되어 있고, MySQL의 LONGTEXT 타입은 4GB 이상의 용량을 지원하므로 100,000자 제한을 충분히 수용할 수 있습니다.
src/main/java/org/ezcode/codetest/domain/draft/exception/DraftExceptionCode.java (2)
16-25: LGTM!낙관적 락 충돌 처리를 위한
DRAFT_VERSION_CONFLICT(409)와 권한 검증을 위한DRAFT_ACCESS_DENIED(403)가 적절하게 정의되어 있습니다. 사용자 친화적인 한국어 메시지도 잘 작성되었습니다.
13-13:DRAFT_NOT_FOUND예외 코드는 사용되지 않고 있습니다.
DRAFT_NOT_FOUND는 정의되어 있지만 코드베이스에서 전혀 사용되지 않고 있습니다.DraftDomainService.getDraft()는Optional<Draft>을 반환하고,DraftController.getDraft()에서는 데이터가 없을 경우ResponseEntity.ok().body(null)로 200 OK 상태로 응답합니다. 이는 PR 목표와 일치합니다.미사용 예외 코드를 제거하거나 향후 사용 계획이 있다면 명확히 문서화하는 것을 검토하세요.
src/main/java/org/ezcode/codetest/infrastructure/persistence/repository/draft/DraftRepositoryImpl.java (1)
14-37: LGTM!리포지토리 구현이 깔끔하게 작성되었습니다.
saveAndFlush메서드를 사용하여 낙관적 락의 버전을 즉시 반환할 수 있도록 구현한 점이 PR 목표와 일치합니다.src/main/java/org/ezcode/codetest/application/draft/servcie/DraftService.java (1)
58-67: LGTM!조회 메서드가 적절하게 구현되었습니다.
@Transactional(readOnly = true)를 사용하여 읽기 전용 트랜잭션으로 최적화했으며,Optional을 반환하여 데이터가 없는 경우를 우아하게 처리합니다. PR 목표에 명시된 "조회 시 데이터 없으면 빈 객체 반환" 전략과 일치합니다.
src/main/java/org/ezcode/codetest/application/draft/servcie/DraftService.java
Outdated
Show resolved
Hide resolved
src/main/java/org/ezcode/codetest/domain/draft/service/DraftDomainService.java
Outdated
Show resolved
Hide resolved
src/main/java/org/ezcode/codetest/presentation/draft/DraftController.java
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (1)
src/main/java/org/ezcode/codetest/presentation/draft/DraftController.java (1)
33-49: DraftException의 HTTP 상태 코드가 올바르게 반환되는지 확인하세요.이전 리뷰에서 지적된 대로,
GlobalExceptionHandler가DraftException(또는BaseException)을 처리하지 않아DRAFT_VERSION_CONFLICT가 의도한 409 Conflict 대신 400 Bad Request로 반환될 수 있습니다. Line 39의 API 문서에서는 409를 명시하고 있으므로,GlobalExceptionHandler에BaseException을 처리하는@ExceptionHandler메서드를 추가하여 각 예외의getHttpStatus()를 올바르게 반환하도록 해야 합니다.다음 스크립트로 GlobalExceptionHandler에 BaseException 핸들러가 추가되었는지 확인하세요:
#!/bin/bash # GlobalExceptionHandler에서 BaseException 처리 여부 확인 rg -A 10 "@ExceptionHandler.*BaseException" src/main/java/org/ezcode/codetest/common/base/exception/GlobalExceptionHandler.java
🧹 Nitpick comments (1)
src/main/java/org/ezcode/codetest/domain/draft/service/DraftDomainService.java (1)
41-51: 새 Draft 생성 시 버전 파라미터 검증을 고려하세요.클라이언트가 존재하지 않는 Draft에 대해
version != null을 전송하는 경우, 이는 클라이언트 상태가 불일치함을 나타낼 수 있습니다. 현재는 이 값이 조용히 무시되지만, 클라이언트의 잘못된 가정을 조기에 감지하기 위해version != null인 경우 예외를 던지는 것을 고려해보세요.🔎 검증 로직 추가 제안
} else { + // 새 엔티티 생성 시 클라이언트가 버전을 보냈다면 상태 불일치 + if (version != null) { + throw new DraftException(DraftExceptionCode.DRAFT_VERSION_CONFLICT); + } + // 새 엔티티인 경우 Draft draft = Draft.builder() .user(user) .problem(problem) .language(language) .code(code) .build(); return draftRepository.saveAndFlush(draft); }
📜 Review details
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
src/main/java/org/ezcode/codetest/application/draft/service/DraftService.javasrc/main/java/org/ezcode/codetest/domain/draft/service/DraftDomainService.javasrc/main/java/org/ezcode/codetest/presentation/draft/DraftController.java
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-07-02T12:05:54.917Z
Learnt from: Kimminu7
Repo: ezcode-my/backend PR: 133
File: src/test/java/org/ezcode/codetest/domain/problem/service/ProblemDomainServiceTest.java:92-99
Timestamp: 2025-07-02T12:05:54.917Z
Learning: ProblemDomainService의 removeProblem 메서드는 DB에서 Problem을 삭제한 후 Elasticsearch에서도 해당 ProblemSearchDocument를 찾아서 삭제합니다. 만약 Elasticsearch에서 문서를 찾지 못하면 ProblemException(PROBLEM_NOT_FOUND)을 던지므로, 테스트에서는 problem.getId()와 searchRepository.findById() 모두 적절하게 mock해야 합니다.
Applied to files:
src/main/java/org/ezcode/codetest/domain/draft/service/DraftDomainService.java
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build
🔇 Additional comments (3)
src/main/java/org/ezcode/codetest/domain/draft/service/DraftDomainService.java (1)
54-57: 구현이 명확하고 정확합니다.단순 조회 로직이 올바르게 구현되었습니다.
src/main/java/org/ezcode/codetest/application/draft/service/DraftService.java (2)
35-56: 낙관적 락 예외 처리가 올바르게 구현되었습니다.Line 51-52의
ObjectOptimisticLockingFailureException처리는DraftDomainService의 명시적 버전 체크(Line 33)를 통과한 후 발생할 수 있는 레이스 컨디션을 방어합니다. 두 단계의 예외 처리(도메인 레이어의 Fail-Fast + 애플리케이션 레이어의 JPA 예외)는 동시성 제어를 위한 올바른 방어적 프로그래밍 패턴입니다.
58-67: 읽기 전용 트랜잭션과 Optional 매핑이 적절합니다.
readOnly = true트랜잭션 설정과Optional.map을 통한 DTO 변환이 깔끔하게 구현되었습니다.
| return response | ||
| .map(ResponseEntity::ok) | ||
| .orElse(ResponseEntity.ok().body(null)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Draft 미존재 시 응답 형식이 PR 목표와 다릅니다.
PR 목표에서는 "조회 시 데이터 없으면 404 대신 빈 문자열("")과 version: null 반환"이라고 명시되어 있지만, 현재 구현은 body(null)을 반환하여 응답 본문이 완전히 비어있습니다.
클라이언트가 일관된 응답 구조를 기대한다면, Draft가 없을 때 DraftResponse 객체를 빈 값들(code="", version=null)로 반환하는 것이 더 나은 선택일 수 있습니다.
🔎 일관된 응답 구조 제안
DraftResponse에 정적 팩토리 메서드를 추가:
// DraftResponse.java에 추가
public static DraftResponse empty() {
return DraftResponse.builder()
.code("")
.version(null)
.build();
}그런 다음 컨트롤러를 수정:
return response
.map(ResponseEntity::ok)
- .orElse(ResponseEntity.ok().body(null));
+ .orElseGet(() -> ResponseEntity.ok(DraftResponse.empty()));Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In src/main/java/org/ezcode/codetest/presentation/draft/DraftController.java
around lines 73-75, the controller returns ResponseEntity.ok().body(null) when
the draft is missing; change this to return a DraftResponse instance with code =
"" and version = null so the response shape is consistent. Add a static factory
method DraftResponse.empty() in DraftResponse (or construct the object inline)
that returns code "" and version null, and use
response.map(ResponseEntity::ok).orElse(ResponseEntity.ok(DraftResponse.empty()))
so clients always receive the expected JSON structure.
작업 내용
Submission)과 분리된 별도의 도메인(Draft)을 설계하여 데이터 성격을 명확히 했습니다.변경 사항
Domain Layer
Draft엔티티 생성User+Problem+Language3가지 조합의 Composite Unique Constraint 적용 (언어별 저장 지원)@Version필드 추가 (JPA 낙관적 락 적용)DraftRepository추가:findByUserAndProblemAndLanguage조회 메서드 구현Service Layer
DraftService,DraftDomainService구현saveAndFlush를 사용하여 갱신된 버전 정보를 즉시 반환하도록 구현빈 문자열("")과version: null을 반환하여 프론트엔드 처리 간소화Controller Layer
POST /api/v1/drafts: 자동 저장 API (Upsert)ObjectOptimisticLockingFailureException발생 시409 Conflict예외 코드로 매핑하여 반환Configuration
SecurityConfig: 로컬 테스트 환경을 위한 CORS 설정 추가 (AllowedOriginPatterns적용)트러블 슈팅
문제 1: 동시 수정 시 데이터 덮어쓰기 문제
@Version을 이용한 **낙관적 락(Optimistic Lock)**을 도입. 버전 불일치 시409 Conflict를 반환하고, 클라이언트에서 "불러오기 vs 덮어쓰기"를 선택하도록 유도함.문제 2: 초기 진입 시 404 예외 처리의 모호함
EntityNotFoundException이 발생, 프론트엔드에서 이를 에러로 처리해야 하는 불편함 존재참고 사항
코드 리뷰 전 확인 체크리스트
type :)Summary by CodeRabbit
릴리스 노트
✏️ Tip: You can customize this high-level summary in your review settings.