Repository navigation
Conversation
수정은 요청의 수정 시각이 저장된 값과 다르면 NOTICE_UPDATE_CONFLICT로 거부한다. Notice에 수정 시각을 노출하고, 삭제는 소프트 삭제로 처리한다.
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars. ⚙️ Run configuration
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 |
github-actions
Bot
requested review from
leegain1,
sangrae2325,
tnals0924 and
xeoxxn
October 9, 2026 15:18
tnals0924
approved these changes
Oct 11, 2026
| } | ||
|
|
||
| private void requireFilesExist(List<Long> imageIds, List<Long> attachmentIds) { | ||
| Set<Long> fileIds = new LinkedHashSet<>(imageIds); |
Member
There was a problem hiding this comment.
LinkedHashSet 자료구조를 사용한 이유가 있나요?
Collaborator
Author
There was a problem hiding this comment.
파일 ID의 중복을 막기 위해 Set을 사용했습니다. 다만 현재 구조에서는 넣은 순서를 기억할 필요가 없기 때문에 LinkedHashSet 대신 HashSet으로 수정했습니다!
| AdminApiUser apiUser, | ||
| @Valid @RequestBody NoticeCreateRequest request | ||
| ) { | ||
| Notice notice = adminNoticeWriteUseCase.create(request.toCommand(apiUser.userId())); |
Member
There was a problem hiding this comment.
apiUser.userId()는 Command 객체 안에 넣지 말고, usecase의 별도 인자로 받는 게 응집, 가독성 측면에서 좋을 거 같은데 어떻게 생각하시나요?
Collaborator
Author
There was a problem hiding this comment.
동의합니다! 반영해서 수정했습니다~
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
#️⃣연관된 이슈
🎯 해결하려는 문제가 무엇인가요?
운영진 콘솔에서 공지를 등록·수정·삭제하고 상단 고정을 바꾸는 관리자 API를 추가합니다. 지금은 학생 앱의 공지 조회 API만 있어서 공지를 넣을 방법이 DB 직접 입력뿐입니다.
POST /v1/admin/noticesPUT /v1/admin/notices/{noticeId}DELETE /v1/admin/notices/{noticeId}PATCH /v1/admin/notices/{noticeId}/pin❓ 왜 해결해야 하나요?
학생 앱이 읽는 데이터를 운영진이 직접 관리할 수 없는 상태입니다. 관리자 콘솔(준비 중)이 붙으려면 이 API가 먼저 있어야 합니다.
⭐ 어떻게 해결했나요?
SecurityConfig가/v1/admin/**를 ADMIN으로 1차 인가하고, 컨트롤러의AdminApiUser리졸버가 ADMIN role을 한 번 더 확인합니다. 부서 제한(@RequireDepartment)은 두지 않았습니다.AdminNoticeWriteUseCase가 두 Service를 조합합니다. 새 공지는 고정되지 않은 상태로 만들어지고, 작성자는 요청 본문이 아니라 토큰의 관리자 ID입니다.updatedAt(KST 기준 epoch 밀리초)이 저장된updated_at과 다르면 409NOTICE_UPDATE_CONFLICT로 거부합니다.updated_at이 초 단위 DATETIME이라 같은 초로 맞춰 비교합니다.is_deleted만 바꾸는 소프트 삭제입니다. 연결된 파일은 지우지 않습니다.{pinned}값으로 설정/해제합니다. 이미 그 상태여도 200입니다.Notice도메인이updatedAt을 갖게 했고,NoticeCreateCommand·NoticeUpdateCommand를 추가했습니다.admin-api가core:domain:file에 의존합니다(파일 존재 확인).🧩 이 PR의 한계 & 트레이드오프
updatedAt을 받아올 조회 API가 없습니다. 관리자 상세 조회가 없고 학생 앱 상세 응답에도updatedAt이 없어서, 이 PR만으로는 프론트가 수정 시각을 알 수 없습니다. 조회 API가 같은 방식(KST 기준 epoch 밀리초)으로 내려줘야 합니다. 어긋나면 수정이 계속 409가 됩니다.updated_at을 바꿉니다. 다른 관리자가 고정을 바꾸면 열려 있던 수정 화면은 409를 받습니다.noticeId만 줘서, 이어서 다시 수정하려면 공지를 다시 조회해야 합니다.NOTICE_IMAGE/NOTICE_ATTACHMENT)는 검사하지 않습니다.⛓️ 기존 기능에 미치는 영향
Notice.of(...)에updatedAt인자가 추가됐습니다(호출처는NoticeJpaEntity.toDomain한 곳). 학생 앱 공지 조회 동작은 그대로입니다.NoticeService·NoticeRepository에 메서드가 추가됐습니다./v1/admin/**아래에만 추가됩니다.🔀 Edge Case & 실패 시나리오
INVALID_INPUTcategory가 enum에 없는 값INVALID_INPUT("요청 본문 형식이 올바르지 않습니다.")null이거나null원소 포함INVALID_INPUTupdatedAt누락INVALID_INPUTFILE_NOT_FOUNDNOTICE_NOT_FOUNDupdatedAt이 저장된 값과 다름NOTICE_UPDATE_CONFLICTcreatedBy가 와도 무시합니다(부트 기본값이 알 수 없는 필드를 허용).📋 검토한 대안과 선택 이유
createdBy로 받기: 명세에는 있었지만 다른 관리자 이름으로 쓸 수 있어 제외하고 토큰의 관리자 ID를 씁니다.SELECT ... FOR UPDATE)으로 막기: 정확하지만 현재 코드베이스에 없는 패턴이고 락 정책을 정해야 해서 제외했습니다. 필요해지면findById를 잠금 조회로 바꾸면 됩니다.@Version컬럼: 명세가 수정 시각(updatedAt)을 받도록 되어 있고 마이그레이션이 필요해 제외했습니다.FileService를 직접 호출: 공지와 파일이 서로 다른 도메인 조합이라 컨벤션(6-1절)에 따라 UseCase를 두었습니다. 삭제·고정은 단일 도메인이라 컨트롤러가 직접 호출합니다.is_deleted를 기준으로 하고 기존 공지 설계가 소프트 삭제라 맞췄습니다.💬 리뷰 포인트
[r]AdminNoticeController에 부서 제한이 없습니다. ADMIN이면 누구나 공지를 다룹니다. 홍보부 등으로 제한할지 정해 주세요(@RequireDepartment한 줄).[r]updatedAt변환이 KST 가정입니다(NoticeUpdateRequest). DB와 앱의 타임존 설정이 코드에 없어서, 조회 API를 만들 때 같은 변환을 써야 합니다.[c]NoticeServiceImpl.delete는entity.delete()를 변경 감지로 저장하므로@Transactional이 필수입니다. dev에서 실제로 삭제가 반영되는지 확인해 주세요.[c]본문 길이 제한 16,000자(TEXT 컬럼 65,535바이트 기준)가 에디터 사용 방식에 맞는지 확인해 주세요. base64 이미지를 본문에 넣는 에디터라면 너무 작습니다.[a]명세와 다른 점: 검증 오류 코드가INVALID_INPUT이고(명세는VALIDATION_ERROR),FILE_NOT_FOUND메시지가 기존 문구("존재하지 않는 파일입니다.")입니다.