Skip to content

refactor/feat: Post·PostImage B 구조 적용(트랜잭션 분리) — create · update 동시 반영 + 테스트 픽스처 안정화#135

Merged
hwijae33 merged 12 commits intodevfrom
feat-posts
Oct 21, 2025
Merged

refactor/feat: Post·PostImage B 구조 적용(트랜잭션 분리) — create · update 동시 반영 + 테스트 픽스처 안정화#135
hwijae33 merged 12 commits intodevfrom
feat-posts

Conversation

@hwijae33
Copy link
Copy Markdown
Collaborator

요약

  • 구조(B): 업로드(S3)는 TX 밖, DB 변경은 짧은 TX, 삭제 정리는 afterCommit, 일부 삭제는 REQUIRES_NEW로 실패 격리.
  • 대상 메서드: PostService.createPost, PostService.updatePost 둘 다 B 구조로 전환.
  • 효과(로그 기반):
    • updatePost (B_pre → B_post, 각 7회): 총합 평균 142.55 → 130.90ms (↓8.2%), 중앙값 ↓11.6%, p95 ↓8.4%, S3 업로드 ↓15.1%, TX 내부 합 55.09 → 53.18ms (↓3.5%)
    • createPost (B_pre → B_post, 단일 런): 총합 평균 479.45 → 193.74ms (↓59.6%), S3 업로드 ↓52.9%, TX 내부 합 141.81 → 34.62ms (↓75.6%)

용어 정의

  • A 구조(레거시 단일 TX): 업로드·DB변경·삭제를 한 @transactional 안에서 처리
  • B 구조(트랜잭션 분리):
    1. uploadImagesOnly()업로드 선행(TX 밖)
    2. DB-only TX에서 URL 저장/선택삭제/썸네일/텍스트
    3. afterCommit 훅으로 S3 삭제 정리, rollback 훅으로 업로드분 보상
    4. 일부 삭제는 @Transactional(REQUIRES_NEW)
  • B_pre / B_post: 동일 B 구조설정 적용 전 / 설정 적용 후 측정값

작업 내용

1) 테스트 픽스처 안정화 (test)

  • 닉네임 UNIQUE(23505) 회피: 모든 픽스처 닉네임 UUID suffix로 생성
  • PW NOT NULL(23502) 대응: password 필수값 세팅
  • 검증 보정: 고정 닉네임 비교 → 생성값/접두어 기반 비교
  • 영향: comments / postImages / postLikes / posts 엔티티·리포지토리 테스트 전반

2) PostImageService 분해 & 유틸 (refactor)

  • uploadImagesOnly() : S3 업로드 전용(TX 밖 I/O)
  • saveImageUrls() : URL만 DB 반영(짧은 TX)
  • cleanupUploadedOnRollback() : TX 실패 시 업로드분 보상 삭제
  • deleteS3ByUrlsQuietly() : S3 삭제 실패 예외 삼키고 warn 로깅

3) createPost — B 구조 적용 (feat/refactor)

  • 업로드 선행(uploadImagesOnly) → TX 내 post 저장 + saveImageUrls
  • 첫 이미지 썸네일 유지(기존 정책 유지)
  • TX 내 작업 최소화로 커넥션 점유 축소

4) updatePost — B 구조 적용 (feat/refactor)

  • 업로드 선행 → TX 내 mutateImagesAndRefresh 일괄 처리
    • 흐름: cleanupUploadedOnRollback 등록 → deleteSelectedImagesDbOnly(DB-only, 일부 REQUIRES_NEW)

      saveImageUrlsrefreshThumbnailIfNeeded(남은 이미지 < 2면 예외)

      applyTextIfChanged(무변경 시 no-op save)

5) 트랜잭션 경계/전파 정리

  • @Transactional을 퍼블릭 엔트리포인트(updatePost)로 이관(기존 updatePostTx 제거)
  • deleteSelectedImagesDbOnly@Transactional(REQUIRES_NEW) 적용(상위 롤백 영향 최소화)

6) 조회/삭제 일관성

  • 조회: getPostById / findByStoreId / findByStoreIdWithCommentsAndLikes@Transactional(readOnly = true)
  • 전체 삭제 deleteAllByPost: DB-first → afterCommit S3 정리
  • 정리: 미사용 findFirstImageByPostId 제거, findImagesByPostId readOnly 지정

성능 영향 (B 구조 — 설정 전 vs 설정 후)

A. PostService.updatePostB_pre → B_post (각 7회)

지표 B_pre B_post 개선율
총합 평균(ms) 142.55 130.90 ↓8.2%
중앙값(ms) 146.61 129.60 ↓11.6%
p95(ms) 168.97 154.76 ↓8.4%
S3 업로드 평균(ms) 70.75 60.06 ↓15.1%
이미지 삭제(DB)(ms) 16.19 15.57 ↓3.8%
URL 저장(DB)(ms) 13.90 13.61 ↓2.1%
썸네일/검증(ms) 7.58 7.66 ↑1.1%
afterCommit(ms) 16.71 17.65 ↑5.6%
TX 내부 합(ms) 55.09 53.18 ↓3.5%

B. PostService.createPostB_pre → B_post (단일 런)

지표 B_pre B_post 개선율
총합 평균(ms) 479.45 193.74 ↓59.6%
S3 업로드 평균(ms) 337.64 159.11 ↓52.9%
post insert(DB)(ms) 117.73 23.83 ↓79.8%
postImages save(DB)(ms) 24.05 10.79 ↓55.2%
TX 내부 합(ms) 141.81 34.62 ↓75.6%

메모: createPost는 단일 런 기준. 신뢰도 강화를 위해 update와 동일하게 7회/케이스 수집 권장.


테스트 포인트

  • 생성: (최소 2장 규칙) 업로드 선행 → DB URL 반영 → 썸네일 설정 확인
  • 수정: 선택삭제(DB-only) + 신규 업로드 + 썸네일 재평가 + 텍스트 무변경(no-op) 경로
  • 롤백 보상: TX 실패 시 선업로드 객체 S3 정리 확인
  • 삭제: deleteAllByPost DB-first → afterCommit S3 경로
  • 조회: readOnly 경계 내 LAZY 접근 정상 동작

참고 사항

  • 설계 배경/의도/수치: 트랜잭션 범위를 줄여서 최적화 기록 — 핀업 적용기(Velog)
  • 로깅 키워드: S3_DELETE_QUIET, POST_IMG_CLEANUP_ON_ROLLBACK
  • 측정 기준: StepWatch 구간 + p6spy SQL 구간 + JpaTransactionManager 시작~커밋 로그 간격

관련 이슈

  • Close #이슈번호

- 모든 테스트 픽스처에 nickname을 UUID suffix로 생성하여 UNIQUE(23505) 충돌 방지
- password 컬럼 필수값 추가 세팅으로 무결성 위반(23502) 예방
- 관련 테스트(assert) 보정: 고정 닉네임 비교 → 생성값/접두어 기반 비교로 안정화
- 영향 범위: comments/postImages/postLikes/posts의 Entity/Repository 테스트 전반
- 업로드 전용 uploadImagesOnly()로 S3 I/O를 TX 밖으로 분리
- DB 전용 saveImageUrls()로 URL만 짧게 반영
- cleanupUploadedOnRollback()로 TX 롤백 시 업로드분 S3 정리
- deleteS3ByUrlsQuietly() 유틸 도입(예외 삼키고 warn 로깅)
- uploadImagesOnly로 업로드 선행(트랜잭션 유지하되 DB 커넥션 미점유)
- cleanupUploadedOnRollback 등록 후 post/saveImageUrls로 URL만 DB 반영
- 첫 이미지로 썸네일 설정 로직 유지
- deleteSelectedImagesDbOnly(postId, urls): DB-only 삭제, 실제 삭제된 URL 반환
- deleteS3QuietlyAfterCommit(urls): 커밋 이후 S3 정리(예외 삼킴)
- deleteSelectedImages(...): 래퍼 → DB-only 후 afterCommit S3 호출
- findFirstImageByPostId 제거(미사용)
- findImagesByPostId @transactional(readOnly = true) 추가
- deleteAllByPost: DB 먼저 삭제 → 커밋 후 S3 정리(deleteS3QuietlyAfterCommit)로 전환
- updatePost: 업로드 선행(uploadImagesOnly) 후 TX 내 mutateImagesAndRefresh로 일괄 처리
  · cleanupUploadedOnRollback, deleteSelectedImagesDbOnly(DB-only), saveImageUrls(URL DB 저장)
  · 썸네일 refreshThumbnailIfNeeded(남은 이미지 < 2면 예외), 기존 유지/필요 시 갱신
  · 텍스트는 썸네일 결정 이후 applyTextIfChanged, 변경 없으면 save 스킵(no-op)
- 조회: getPostById / findByStoreId / findByStoreIdWithCommentsAndLikes에 @transactional(readOnly = true) 추가
  · dirty checking 완화 + LAZY 접근 트랜잭션 경계 보장
- title/content가 실제로 달라질 때만 값을 갱신하고 변경 여부(boolean) 반환
- updatePost에서 텍스트 무변경 시 save 스킵 가능해져 JPA flush/쓰기 부하 감소
… — 퍼블릭 엔트리포인트에서 트랜잭션 시작하도록 정리
…IRES_NEW) 적용 — 이미지 삭제를 독립 트랜잭션으로 격리해 상위 롤백 영향 최소화
… 세분화로 테스트 보정 - create/update 오케스트레이션·트랜잭션·이미지 변이 단계 분리 및 플래그 도입 - PostImageService 메서드 분리(uploadImagesOnly/saveImageUrls/deleteSelectedImagesDbOnly 등) - savePostImages 의존 제거 후 신규 API 기준 스텁·검증 교체 - 통합 테스트 “진짜 흐름” 적용(최소 2장 규칙/썸네일 재평가) - AppLogger NPE·불필요 스터빙 제거
@hwijae33 hwijae33 self-assigned this Oct 21, 2025
@github-actions
Copy link
Copy Markdown

github-actions bot commented Oct 21, 2025

✅ 테스트 통과!

Copy link
Copy Markdown
Collaborator

@whoamixzerone whoamixzerone 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
Copy Markdown
Collaborator Author

@hwijae33 hwijae33 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
Copy Markdown
Collaborator

@whoamixzerone whoamixzerone left a comment

Choose a reason for hiding this comment

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

고생했어요!

@hwijae33 hwijae33 merged commit 92bb4f6 into dev Oct 21, 2025
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants