Skip to content

[#277] 노트 수정 동시성 처리#278

Merged
ohige01 merged 5 commits intomainfrom
fix/277/writeNote
Jan 23, 2026
Merged

[#277] 노트 수정 동시성 처리#278
ohige01 merged 5 commits intomainfrom
fix/277/writeNote

Conversation

@ohige01
Copy link
Member

@ohige01 ohige01 commented Jan 20, 2026

🚀 관련 이슈

🔑 주요 변경사항

1. 비관적 락(Pessimistic Lock) 적용

NoteRepository.java

@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("SELECT n FROM Note n WHERE n.noteId = :noteId")
Optional<Note> findByIdWithLock(@Param("noteId") Long noteId);
  • PESSIMISTIC_WRITE 락을 사용하여 노트 조회 시점부터 트랜잭션 종료까지 해당 레코드에 대한 배타적 잠금 획득
  • 동시에 같은 노트에 접근하려는 다른 트랜잭션은 대기 상태로 전환

2. 버전 관리 필드 추가

Note.java (Entity)

@ColumnDefault("0")
private Long version;
  • 노트 엔티티에 version 필드를 추가하여 각 수정마다 버전 번호 증가
  • 클라이언트는 노트 조회 시 버전 정보를 받고, 저장 요청 시 해당 버전을 함께 전송

3. 버전 검증 로직 추가

NoteService.java

public void checkVersion(Note note, Long version){
    if (!Objects.equals(note.getVersion(), version))
        throw new BadRequestException(ErrorResponseStatus.INCORRECT_VERSION);
}
  • 저장 요청 시 클라이언트가 전송한 버전과 DB의 현재 버전 비교
  • 버전 불일치 시 INCORRECT_VERSION(2015) 에러 반환

4. Facade 서비스 도입

NoteFacadeService.java (신규 생성)

@Transactional
public boolean writeNoteFacade(User user, String mode, Long noteId,
        String name, Node node, Long version, List<MultipartFile> images){

    // 1. 비관적 락으로 노트 조회
    Note note = noteService.getNoteByIdWithLock(noteId);

    // 2. 소유권 및 버전 검증
    noteService.checkOwnership(user, note);
    noteService.checkVersion(note, version);

    // 3. 카드 삭제 및 재생성 로직
    if (cardModuleService.existsByNote(note) && mode.equals("standard")) {
        cardModuleService.deleteAllCardsByNoteId(note);
        cardModuleService.deleteAllImageCardsByNoteId(note);
    }

    // 4. 버전 증가
    note.setVersion((version + 1) % Long.MAX_VALUE);

    return noteService.writeNote(note, node, images);
}
  • 트랜잭션 범위 내에서 락 획득 → 검증 → 수정 → 버전 증가가 원자적으로 수행
  • Controller에서 비즈니스 로직 분리하여 단일 책임 원칙 준수

5. API 요청 스펙 변경

NoteRequest.java

@NotNull
Long noteId;
@NotNull
Long version;  // 추가됨
@NotNull
String name;
  • 클라이언트는 노트 저장 요청 시 반드시 version 값을 포함해야 함(노트 내용 조회 api에서 수집 가능)

✔️ 체크 리스트

  • Merge 하려는 브랜치가 올바른가? (main branch에 실수로 PR 생성 금지)
  • 작업한 API에 대해 적절한 예외처리가 이루어졌는가?
  • 작업한 API에 대해 적절한 로그 메시지가 작성되었는가? (ControllerService에서 log.error 활용)
  • Merge 하려는 PR 및 Commit들을 로컬에서 실행했을 때 에러가 발생하지 않았는가?

↗️ 개선 사항

문제 해결 방법 결과
Lost Update 비관적 락 + 버전 체크 동시 수정 시 순차 처리 보장
Race Condition 트랜잭션 내 락 유지 데이터 정합성 보장
충돌 감지 불가 버전 관리 클라이언트에게 충돌 알림 가능
Client A                    Server                    Client B
   |                          |                          |
   |---(1) writeNote v1------>|                          |
   |                          |<--(2) writeNote v1-------|
   |                          |                          |
   |                     [Lock 획득 - A]                  |
   |                          |                          |
   |                      [B 대기중 ]                      |
   |                          |                          |
   |                     [A 처리 완료, v2로 증가]            |
   |<---(3) Success v2--------|                          |
   |                          |                          |
   |                     [Lock 해제, B 획득]               |
   |                          |                          |
   |                     [버전 체크: v1 != v2 → 실패]       |
   |                          |---(4) INCORRECT_VERSION->|

📔 참고 자료

이슈 #277 수정 과정 참고

@ohige01 ohige01 requested review from afflogy and leegaarden January 20, 2026 07:49
@ohige01 ohige01 self-assigned this Jan 20, 2026
@ohige01 ohige01 added the bug Something isn't working label Jan 20, 2026
@ohige01 ohige01 linked an issue Jan 20, 2026 that may be closed by this pull request
4 tasks
@ohige01 ohige01 added the Feature 기능 개발 label Jan 20, 2026
Copy link
Member

@leegaarden leegaarden left a comment

Choose a reason for hiding this comment

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

컨트롤러단 간결해진 부분 좋습니다. 수고 많으셨습니다!

Copy link
Member

@leegaarden leegaarden left a comment

Choose a reason for hiding this comment

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

확인했습니다 수고 많으셨습니다!

Copy link
Collaborator

@afflogy afflogy left a comment

Choose a reason for hiding this comment

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

확인했습니다. 수고하셨어요.

@ohige01 ohige01 merged commit 11915f8 into main Jan 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working Feature 기능 개발

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FIX] 노트 수정 api 동시성 문제 수정

3 participants