Skip to content

refactor: 프로젝트 정합성 관련 수정 - #70

Merged
chazy-d merged 13 commits into
developfrom
feature/project-api-alignment-refactor
Jul 25, 2026
Merged

refactor: 프로젝트 정합성 관련 수정#70
chazy-d merged 13 commits into
developfrom
feature/project-api-alignment-refactor

Conversation

@chazy-d

@chazy-d chazy-d commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

🔗 관련 이슈 (Related Issue)

  • 없음

📝 작업 내용

주요 검토 파일:

  • src/main/java/com/slatto/domain/project/controller/ProjectController.java
  • src/main/java/com/slatto/domain/project/controller/ProjectFileController.java
  • src/main/java/com/slatto/domain/project/controller/ProjectNoticeController.java
  • src/main/java/com/slatto/domain/project/service/ProjectService.java
  • src/main/java/com/slatto/domain/project/service/ProjectFileService.java
  • src/main/java/com/slatto/domain/project/service/ProjectNoticeService.java
  • src/main/java/com/slatto/domain/project/entity/ProjectPin.java
  • src/main/java/com/slatto/domain/project/entity/ProjectNoticeRead.java

1. 프로젝트 목록 카드 응답 보강

프로젝트 목록 화면에서 필요한 카드 표시용 필드를 보강했습니다.

  • roleNames
  • previewImageUrl
  • deadlineProgressPercent
  • lastActivityAt
  • memberPreviewImageUrls
  • memberCount
  • isPinned
  • pinnedAt
  • myPermission

프로젝트 수정/삭제 가능 여부는 별도 boolean 대신 myPermission으로 판단할 수 있도록 정리했습니다.


2. 프로젝트 고정 API 추가

사용자별 프로젝트 고정을 처리할 수 있도록 API를 추가했습니다.

  • POST /api/v1/projects/{projectId}/pin
  • DELETE /api/v1/projects/{projectId}/pin

프로젝트 고정은 사용자별 상태이므로 project_pin 테이블로 분리했습니다.


3. 파일 고정 API 추가

파일 목록에서 고정 상태를 별도로 변경할 수 있도록 API를 추가했습니다.

  • POST /api/v1/projects/{projectId}/files/{fileId}/pin
  • DELETE /api/v1/projects/{projectId}/files/{fileId}/pin

파일 업로드/수정 request에서는 isPinned을 받지 않고, 고정/해제는 별도 API로 처리하도록 정리했습니다.


4. 공지 읽음 상태 추가

프로젝트 공지 목록에서 사용자별 읽음 여부를 표시할 수 있도록 읽음 상태를 추가했습니다.

  • 공지 목록 응답에 isRead 추가
  • PATCH /api/v1/projects/{projectId}/notices/{noticeId}/read 추가
  • 사용자별 공지 읽음 상태는 project_notice_read 테이블에서 관리

공지사항 파일 첨부는 이번 범위에 포함하지 않았습니다.


5. API 필드 정합성 보정

프로젝트 파트에서 혼동될 수 있던 필드를 정리했습니다.

  • 프로젝트 상세 응답의 역할 필드를 roleNames로 통일
  • 파일 업로드/수정 request의 isPinned 제거
  • 파일 응답에는 isPinned, isFinal 유지

6. 고정 항목 cursor pagination 보정

프로젝트와 파일 목록이 고정 항목 우선 정렬을 사용하므로, cursor pagination 조건도 정렬 기준에 맞게 보정했습니다.

  • 프로젝트 목록: 사용자별 ProjectPin.pinnedAt 기준 cursor 처리
  • 파일 목록: ProjectFile.pinnedAt 기준 cursor 처리
  • 고정 항목과 일반 항목이 섞여도 다음 페이지가 누락되지 않도록 보정

✅ 검증

  • ./gradlew compileJava
  • ./gradlew test

💬 To Reviewers

이번 PR은 S3 파일 API 이후 화면/API 명세와 맞지 않는 부분을 보정하는 작업이였습니다!

✅ PR 체크리스트

  • PR 제목은 커밋 컨벤션을 따랐습니다.
  • 관련 이슈를 연결했습니다.
  • 변경 사항에 대한 테스트를 진행했습니다.

Summary by CodeRabbit

  • 새로운 기능

    • 프로젝트와 프로젝트 파일을 고정하거나 고정 해제할 수 있습니다.
    • 프로젝트 목록과 상세 화면에서 고정 상태 및 고정 시각을 확인할 수 있습니다.
    • 프로젝트 공지를 읽음 처리할 수 있으며, 공지별 읽음 상태가 표시됩니다.
    • 프로젝트 목록에 역할, 미리보기 이미지, 권한 정보가 추가됩니다.
  • 변경 사항

    • 프로젝트 파일 업로드 및 수정 시 고정 상태를 직접 변경하는 기능이 제거되었습니다.
    • 프로젝트와 파일 목록의 고정 항목 정렬 및 페이지 이동이 개선되었습니다.
    • 기타 프로젝트 유형이 일관된 이름으로 표시됩니다.

@chazy-d chazy-d self-assigned this Jul 25, 2026
@chazy-d chazy-d added the refactor 기능 변경 없는 코드 개선 label Jul 25, 2026
@coderabbitai

coderabbitai Bot commented Jul 25, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 917da11b-1885-4497-a4b6-942948bbba97

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

프로젝트와 파일에 핀 생성·해제 API와 핀 상태 응답이 추가되었습니다. 프로젝트 목록은 핀·역할·미리보기 정보를 포함하며, 공지에는 사용자별 읽음 기록과 읽음 처리 API가 추가되었습니다. 비디오 기타 카테고리명은 고정 문자열로 변경되었습니다.

Changes

프로젝트 핀

Layer / File(s) Summary
프로젝트 핀 계약과 저장소
src/main/java/com/slatto/domain/project/entity/ProjectPin.java, src/main/java/com/slatto/domain/project/repository/ProjectPinRepository.java, src/main/java/com/slatto/domain/project/dto/Project*Response.java, src/main/resources/db/migration/002-project-pin.sql
프로젝트 핀 엔티티·테이블·저장소와 목록 및 상세 응답의 핀, 역할, 미리보기, 권한 필드가 추가되었습니다.
프로젝트 목록과 핀 상태 매핑
src/main/java/com/slatto/domain/project/service/ProjectService.java, src/main/java/com/slatto/domain/project/repository/ProjectMemberRepository.java, src/main/java/com/slatto/domain/project/converter/ProjectConverter.java, src/main/java/com/slatto/domain/video/repository/VideoRepository.java
프로젝트 조회가 핀 시각 기반 커서와 배치 메타데이터 조회를 사용하고, 목록·상세 응답에 핀 상태를 반영합니다.
프로젝트 핀 전환 API
src/main/java/com/slatto/domain/project/controller/ProjectController.java, src/main/java/com/slatto/domain/project/service/ProjectService.java
프로젝트 핀 생성 및 해제를 위한 POST/DELETE 엔드포인트와 트랜잭션 서비스가 추가되었습니다.

프로젝트 파일 핀

Layer / File(s) Summary
파일 핀 계약과 커서 조회
src/main/java/com/slatto/domain/project/dto/ProjectFile*Request.java, src/main/java/com/slatto/domain/project/dto/ProjectFilePinResponse.java, src/main/java/com/slatto/domain/project/repository/ProjectFileRepository.java
파일 요청에서 isPinned가 제거되고 전용 핀 응답 DTO와 핀 시각 기반 커서 조건이 추가되었습니다.
파일 핀 전환 서비스
src/main/java/com/slatto/domain/project/service/ProjectFileService.java
파일 업로드·수정에서 핀 변경을 제거하고, 별도 핀 생성·해제와 핀 커서 처리를 추가했습니다.
파일 핀 API
src/main/java/com/slatto/domain/project/controller/ProjectFileController.java
파일 핀 생성 및 해제를 위한 POST/DELETE 엔드포인트가 추가되었습니다.

프로젝트 공지 읽음 처리

Layer / File(s) Summary
공지 읽음 데이터 모델과 저장소
src/main/java/com/slatto/domain/project/entity/ProjectNoticeRead.java, src/main/java/com/slatto/domain/project/repository/ProjectNoticeReadRepository.java, src/main/resources/db/migration/003-project-notice-read.sql
사용자별 공지 읽음 엔티티, 유일 제약, 테이블과 조회 메서드가 추가되었습니다.
공지 읽음 상태 처리
src/main/java/com/slatto/domain/project/service/ProjectNoticeService.java, src/main/java/com/slatto/domain/project/dto/ProjectNoticeResponse.java, src/main/java/com/slatto/domain/project/dto/ProjectNoticeReadResponse.java
공지 목록에 읽음 여부를 반영하고, 읽음 기록을 생성하거나 읽은 시각을 갱신하도록 변경했습니다.
공지 읽음 API
src/main/java/com/slatto/domain/project/controller/ProjectNoticeController.java
공지 읽음 처리를 위한 PATCH 엔드포인트가 추가되었습니다.

비디오 카테고리 라벨

Layer / File(s) Summary
카테고리명 해석 변경
src/main/java/com/slatto/domain/video/service/VideoService.java
기타 카테고리가 커스텀 타입명 대신 기타로 반환되도록 변경되었습니다.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

프로젝트 핀 처리

sequenceDiagram
  participant ProjectController
  participant ProjectService
  participant ProjectPinRepository
  participant project_pin
  ProjectController->>ProjectService: pinProject(projectId, currentUserId)
  ProjectService->>ProjectPinRepository: findByUserIdAndProjectId(...)
  ProjectService->>project_pin: save or delete ProjectPin
  ProjectService-->>ProjectController: ProjectPinResponse
Loading

공지 읽음 처리

sequenceDiagram
  participant ProjectNoticeController
  participant ProjectNoticeService
  participant ProjectNoticeReadRepository
  participant project_notice_read
  ProjectNoticeController->>ProjectNoticeService: readProjectNotice(projectId, noticeId, currentUserId)
  ProjectNoticeService->>ProjectNoticeReadRepository: findByNoticeIdAndUserId(...)
  ProjectNoticeService->>project_notice_read: save or refresh ProjectNoticeRead
  ProjectNoticeService-->>ProjectNoticeController: ProjectNoticeReadResponse
Loading

Possibly related PRs

  • SLAT-TO/SLAT-TO-BE#19: ProjectService의 프로젝트 접근 검증 및 상세 조회 경로와 연결됩니다.
  • SLAT-TO/SLAT-TO-BE#25: 동일한 프로젝트 공지 컨트롤러·서비스 계층을 확장합니다.
  • SLAT-TO/SLAT-TO-BE#67: 프로젝트 파일 API와 isPinned 요청 모델 변경 경로가 직접 연결됩니다.

Suggested labels: feature

Suggested reviewers: sangwon02, guingguing, young0206, kohseoyoung

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive 제목이 프로젝트 정합성 보정이라는 주제는 맞지만, 변경 범위가 너무 포괄적이고 구체성이 부족합니다. 프로젝트 목록 카드, 고정 API, 공지 읽음 상태, cursor 보정 등을 포함한 더 구체적인 제목으로 바꿔주세요.
✅ Passed checks (3 passed)
Check name Status Explanation
Description check ✅ Passed 작업 내용과 검증 결과가 구체적이며 템플릿의 핵심 정보는 대부분 담겨 있지만, 관련 이슈 링크와 체크리스트는 비어 있습니다.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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 (1)
src/main/java/com/slatto/domain/project/service/ProjectService.java (1)

103-112: 🚀 Performance & Scalability | 🔵 Trivial | 🏗️ Heavy lift

목록 항목별 조회를 배치화하세요.

현재 항목마다 countActiveMembersgetMemberPreviewImageUrls를 호출합니다. 최대 50개 목록에서 최대 100개의 추가 쿼리가 발생하므로, 멤버 수와 미리보기 URL도 프로젝트 ID 기준 일괄 조회로 바꾸는 편이 좋습니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/com/slatto/domain/project/service/ProjectService.java` around
lines 103 - 112, Update the project list assembly around the stream mapping and
replace per-item countActiveMembers and getMemberPreviewImageUrls calls with
project-ID-based batch lookups prepared before mapping. Reuse the resulting maps
by project ID when calling projectConverter.toSummary, preserving the existing
values and behavior for each project.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/main/java/com/slatto/domain/project/service/ProjectNoticeService.java`:
- Line 112: Update the response construction in updateProjectNotice to use the
current user’s actual read status instead of always passing false to toResponse.
Reuse the existing read-status lookup and preserve consistency with the notice
list; only clear the user’s read record if that is the established intended
behavior for updates.
- Around line 131-141: ProjectNoticeService의 읽음 기록 생성이 findByNoticeIdAndUserId 후
save로 분리되어 동시 요청에서 중복 키 충돌이 발생합니다. 해당 읽음 처리 로직을 DB upsert 또는 중복 키 발생 시 기존 레코드를
재조회·재시도하는 원자적 방식으로 변경하세요. 동일 공지와 사용자의 동시 PATCH 요청이 모두 성공하고 하나의 읽음 기록으로 통합되는 동시성
통합 테스트도 추가하세요.

In `@src/main/java/com/slatto/domain/project/service/ProjectService.java`:
- Around line 144-145: Update the project-pin creation flow in ProjectService
around projectPinRepository.findByUserIdAndProjectId so concurrent requests are
idempotent: use an atomic database upsert or insert-ignore operation, then
retrieve and return the existing or newly created ProjectPin when a unique-key
conflict occurs. Ensure duplicate requests for the same user and project do not
propagate a constraint exception.

---

Nitpick comments:
In `@src/main/java/com/slatto/domain/project/service/ProjectService.java`:
- Around line 103-112: Update the project list assembly around the stream
mapping and replace per-item countActiveMembers and getMemberPreviewImageUrls
calls with project-ID-based batch lookups prepared before mapping. Reuse the
resulting maps by project ID when calling projectConverter.toSummary, preserving
the existing values and behavior for each project.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: ea5869d9-1380-4c22-abcd-0c7ea6a8ab2c

📥 Commits

Reviewing files that changed from the base of the PR and between 10662bc and 46a3ac1.

📒 Files selected for processing (25)
  • src/main/java/com/slatto/domain/project/controller/ProjectController.java
  • src/main/java/com/slatto/domain/project/controller/ProjectFileController.java
  • src/main/java/com/slatto/domain/project/controller/ProjectNoticeController.java
  • src/main/java/com/slatto/domain/project/converter/ProjectConverter.java
  • src/main/java/com/slatto/domain/project/dto/ProjectDetailResponse.java
  • src/main/java/com/slatto/domain/project/dto/ProjectFilePinResponse.java
  • src/main/java/com/slatto/domain/project/dto/ProjectFileUpdateRequest.java
  • src/main/java/com/slatto/domain/project/dto/ProjectFileUploadRequest.java
  • src/main/java/com/slatto/domain/project/dto/ProjectListResponse.java
  • src/main/java/com/slatto/domain/project/dto/ProjectNoticeReadResponse.java
  • src/main/java/com/slatto/domain/project/dto/ProjectNoticeResponse.java
  • src/main/java/com/slatto/domain/project/dto/ProjectPinResponse.java
  • src/main/java/com/slatto/domain/project/entity/ProjectNoticeRead.java
  • src/main/java/com/slatto/domain/project/entity/ProjectPin.java
  • src/main/java/com/slatto/domain/project/repository/ProjectFileRepository.java
  • src/main/java/com/slatto/domain/project/repository/ProjectMemberRepository.java
  • src/main/java/com/slatto/domain/project/repository/ProjectNoticeReadRepository.java
  • src/main/java/com/slatto/domain/project/repository/ProjectPinRepository.java
  • src/main/java/com/slatto/domain/project/service/ProjectFileService.java
  • src/main/java/com/slatto/domain/project/service/ProjectNoticeService.java
  • src/main/java/com/slatto/domain/project/service/ProjectService.java
  • src/main/java/com/slatto/domain/video/repository/VideoRepository.java
  • src/main/java/com/slatto/domain/video/service/VideoService.java
  • src/main/resources/db/migration/002-project-pin.sql
  • src/main/resources/db/migration/003-project-notice-read.sql
💤 Files with no reviewable changes (2)
  • src/main/java/com/slatto/domain/project/dto/ProjectFileUpdateRequest.java
  • src/main/java/com/slatto/domain/project/dto/ProjectFileUploadRequest.java

projectNotice.update(request.getTitle(), request.getContent());

return toResponse(projectNotice);
return toResponse(projectNotice, false);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

git ls-files 'src/main/java/com/slatto/domain/project/service/ProjectNoticeService.java' \
  'src/main/java/com/slatto/domain/project/repository/*' \
  'src/main/java/com/slatto/domain/project/*Read*' \
  'src/main/java/com/slatto/domain/project/dto/*' \
  'src/main/java/com/slatto/domain/project/controller/*'

echo '--- outline ProjectNoticeService ---'
ast-grep outline src/main/java/com/slatto/domain/project/service/ProjectNoticeService.java --view expanded || true

echo '--- nearby lines ---'
cat -n src/main/java/com/slatto/domain/project/service/ProjectNoticeService.java | sed -n '1,220p'

echo '--- search read state usages ---'
rg -n "isRead|findByNoticeIdAndUserId|readAt|ProjectNoticeRead|noticeIdAndUserId|toResponse\\(" src/main/java/com/slatto/domain/project -S

Repository: SLAT-TO/SLAT-TO-BE

Length of output: 19168


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- ProjectNoticeService relevant methods ---'
sed -n '1,220p' src/main/java/com/slatto/domain/project/service/ProjectNoticeService.java | nl -ba | sed -n '1,220p'

echo '--- repository and entity definitions ---'
for f in \
  src/main/java/com/slatto/domain/project/repository/ProjectNoticeReadRepository.java \
  src/main/java/com/slatto/domain/project/entity/ProjectNoticeRead.java \
  src/main/java/com/slatto/domain/project/dto/ProjectNoticeResponse.java
do
  if [ -f "$f" ]; then
    echo "### $f"
    nl -ba "$f" | sed -n '1,220p'
  fi
done

echo '--- direct search for update response path ---'
rg -n "return toResponse\\(|toResponse\\(.*isRead|isRead =" src/main/java/com/slatto/domain/project/service/ProjectNoticeService.java -n -S

Repository: SLAT-TO/SLAT-TO-BE

Length of output: 242


수정 응답의 isRead를 현재 상태와 맞추세요.
updateProjectNotice()는 항상 false를 내려서, 이미 읽은 사용자가 공지를 수정하면 응답과 목록의 읽음 상태가 어긋납니다. 현재 사용자 기준 읽음 여부를 조회해 반환하거나, 수정 시 읽음 레코드를 초기화할 의도라면 함께 정리해야 합니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/com/slatto/domain/project/service/ProjectNoticeService.java` at
line 112, Update the response construction in updateProjectNotice to use the
current user’s actual read status instead of always passing false to toResponse.
Reuse the existing read-status lookup and preserve consistency with the notice
list; only clear the user’s read record if that is the established intended
behavior for updates.

Comment on lines +131 to +141
ProjectNoticeRead projectNoticeRead = projectNoticeReadRepository.findByNoticeIdAndUserId(
noticeId,
currentUserId
)
.map(existingRead -> {
existingRead.refreshReadAt();
return existingRead;
})
.orElseGet(() -> projectNoticeReadRepository.save(
ProjectNoticeRead.create(projectNotice, currentMember.getUser())
));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== ProjectNoticeService slice ==\n'
sed -n '110,170p' src/main/java/com/slatto/domain/project/service/ProjectNoticeService.java

printf '\n== Search for unique constraint / table definition ==\n'
rg -n "uk_project_notice_read_notice_user|project_notice_read" src/main resources . -g '!target' -g '!build' -g '!dist'

printf '\n== Search for tests / upsert / concurrency handling ==\n'
rg -n "refreshReadAt|findByNoticeIdAndUserId|ProjectNoticeRead.create|concurrent|race|duplicate|unique constraint|upsert" src/test src/main -g '!target' -g '!build' -g '!dist'

Repository: SLAT-TO/SLAT-TO-BE

Length of output: 4858


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== ProjectNoticeRead entity ==\n'
sed -n '1,220p' src/main/java/com/slatto/domain/project/entity/ProjectNoticeRead.java

printf '\n== ProjectNoticeRead repository ==\n'
sed -n '1,220p' src/main/java/com/slatto/domain/project/repository/ProjectNoticeReadRepository.java

printf '\n== Transaction / isolation / duplicate-key handling search ==\n'
rg -n "`@Transactional`|Isolation|Deadlock|DuplicateKey|DataIntegrityViolation|ConstraintViolation|upsert|ON DUPLICATE|retry" src/main/java src/test src/main/resources -g '!target' -g '!build' -g '!dist'

Repository: SLAT-TO/SLAT-TO-BE

Length of output: 9286


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== VideoService duplicate-key handling ==\n'
sed -n '140,190p' src/main/java/com/slatto/domain/video/service/VideoService.java

printf '\n== NotificationSettingRepository upsert pattern ==\n'
sed -n '1,120p' src/main/java/com/slatto/domain/notification/repository/NotificationSettingRepository.java

printf '\n== GlobalExceptionHandler ==\n'
sed -n '1,220p' src/main/java/com/slatto/global/exception/GlobalExceptionHandler.java

printf '\n== Tests for ProjectNoticeRead / readProjectNotice ==\n'
rg -n "ProjectNoticeRead|readProjectNotice|project_notice_read" src/test -g '!target' -g '!build' -g '!dist'

Repository: SLAT-TO/SLAT-TO-BE

Length of output: 7041


동시 읽음 처리를 원자적으로 바꾸세요. src/main/java/com/slatto/domain/project/service/ProjectNoticeService.java:131-141에서 findByNoticeIdAndUserIdsave로 분기하면 동일 사용자/공지의 동시 PATCH가 둘 다 미존재로 판단해 uk_project_notice_read_notice_user 충돌로 한 요청이 실패합니다. DB upsert 또는 중복 키 재조회/재시도 방식으로 바꾸고, 동시 요청 통합 테스트를 추가하세요.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/com/slatto/domain/project/service/ProjectNoticeService.java`
around lines 131 - 141, ProjectNoticeService의 읽음 기록 생성이 findByNoticeIdAndUserId
후 save로 분리되어 동시 요청에서 중복 키 충돌이 발생합니다. 해당 읽음 처리 로직을 DB upsert 또는 중복 키 발생 시 기존 레코드를
재조회·재시도하는 원자적 방식으로 변경하세요. 동일 공지와 사용자의 동시 PATCH 요청이 모두 성공하고 하나의 읽음 기록으로 통합되는 동시성
통합 테스트도 추가하세요.

Comment on lines +144 to +145
ProjectPin projectPin = projectPinRepository.findByUserIdAndProjectId(currentUserId, projectId)
.orElseGet(() -> projectPinRepository.save(ProjectPin.create(currentMember.getUser(), project)));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

동시 고정 요청을 멱등하게 처리하세요.

조회 후 저장은 원자적이지 않습니다. 같은 사용자와 프로젝트에 대한 동시 POST 요청은 둘 다 미존재 상태를 확인한 뒤 uk_project_pin_user_project 충돌로 한 요청이 500 오류가 됩니다. DB upsert/insert-ignore 후 재조회하는 방식 등으로 기존 핀을 반환하도록 처리하세요.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/com/slatto/domain/project/service/ProjectService.java` around
lines 144 - 145, Update the project-pin creation flow in ProjectService around
projectPinRepository.findByUserIdAndProjectId so concurrent requests are
idempotent: use an atomic database upsert or insert-ignore operation, then
retrieve and return the existing or newly created ProjectPin when a unique-key
conflict occurs. Ensure duplicate requests for the same user and project do not
propagate a constraint exception.

@chazy-d
chazy-d merged commit 1459219 into develop Jul 25, 2026
2 checks passed
@guingguing
guingguing deleted the feature/project-api-alignment-refactor branch August 11, 2026 10:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

refactor 기능 변경 없는 코드 개선

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant