Skip to content

feat: 내 템플릿 수정 API (#52) - #53

Merged
oTAETAEo merged 7 commits into
developfrom
feat/52-template-update
Nov 2, 2026
Merged

oTAETAEo merged 7 commits into
developfrom
feat/52-template-update

Conversation

@oTAETAEo

@oTAETAEo oTAETAEo commented Oct 31, 2026 •

Copy link
Copy Markdown
Contributor

관련 이슈

PR #50(feat/49-template-detail) 위에 쌓은 PR입니다. #50이 병합되면 기준 브랜치를 develop으로 바꿉니다.

작업 내용

명세 11번 PUT /api/v1/templates/{templateId}를 구현했습니다. #50의 getActiveTemplate으로 404를 나누고, 저장은 기존 save()로 해서 유니크 위반을 TEMPLATE409_1로 바꿉니다.

커밋별로 보기

  1. Template에 @DynamicUpdate 추가 (아래 리뷰어에게 1번)
  2. 서비스: TemplateCommandService.update, 자기 자신을 뺀 중복 조회, TemplateUpdateCommand, TemplateUpdateResult
  3. API: PUT /{templateId}, TemplateUpdateRequest, TemplateUpdateResponse, TEMPLATE200_3

동작 규칙

경우 결과
정리 후 세 값이 기존과 같음 200. UPDATE가 나가지 않아 edited_at·updated_at 유지
단축어 대소문자만 변경 수정으로 처리
자기 자신의 단축어 그대로 중복 아님
내 다른 활성 템플릿과 비교 키가 같음 TEMPLATE409_1
휴지통 템플릿이 쓰던 단축어 사용 가능
없는 ID, 다른 회원 템플릿 (휴지통 포함) TEMPLATE404_1
내 템플릿인데 휴지통에 있음 TEMPLATE404_2

수정은 개수가 바뀌지 않으므로 lockUser를 부르지 않고, 같은 필드를 동시에 수정하면 나중 저장이 덮어씁니다.

명세와 달라진 점

  • 명세 11번 에러표의 TEMPLATE404_1 설명에 "휴지통에 있는 템플릿"이 남아 있어 같은 표의 TEMPLATE404_2, 비고와 어긋납니다. #50과 같이 내 휴지통은 TEMPLATE404_2, 다른 회원의 휴지통은 TEMPLATE404_1로 구현했습니다. 이 PR에서 docs/는 고치지 않았습니다
  • 명세 11번 비고는 "동시에 두 곳에서 수정하면 나중 저장이 덮어씁니다"이지만, @DynamicUpdate로 요청마다 바뀐 필드만 쓰므로 서로 다른 필드를 고친 두 수정은 합쳐집니다. A가 이름만, B가 본문만 고치면 둘 다 남습니다. 같은 필드는 나중 저장이 덮어씁니다. 단축어의 shortcut·shortcut_key는 함께 저장되어 어긋나지 않습니다

테스트

  • TemplateUpdateControllerTest (테스트컨테이너 MySQL)
    • 성공: 필드 순서, no-store, DB 값, editedAt·updated_at 갱신, lastUsedAt 유지
    • 같은 값 저장(공백·자모 분리형·CRLF 포함): editedAt·updated_at·암호문 유지
    • 대소문자만 변경, 자기 단축어 유지, 다른 회원의 단축어, 휴지통 단축어
    • 내 다른 활성 템플릿 409_1 (자모 분리형·대소문자 포함)
    • 없는 ID·다른 회원 404_1, 내 휴지통 404_2, 다른 회원의 휴지통 404_1
    • TEMPLATE400_1~7과 검증 순서(이름 → 단축어 → 내용), 토큰 없음 COMMON401_1, 본문 암호화 저장
  • TemplateUpdateUniqueViolationTest: 중복 조회를 통과한 뒤 실제 유니크 위반이 나도 409_1이고 수정이 남지 않음
  • TemplateCommandServiceTest (모키토): 주요 분기, 호출 순서, lockUser를 쓰지 않음
  • TemplateRepositoryTest: 수정 UPDATE가 읽은 뒤 기록된 last_used_at·deleted_at을 되돌리지 않음
  • 전체 589개 통과

확인 체크리스트

  • ./gradlew build 통과
  • 테스트 포함
  • 변경 400줄 안팎 → 731줄 (본 코드 155줄, 테스트 576줄). 테스트가 대부분이고 기능을 나눌 수 없어 그대로 뒀습니다
  • 병합된 마이그레이션 수정 안 함
  • 비밀 값·개인정보 없음
  • global 공통 코드 변경 없음

리뷰어에게

  1. Template에 @DynamicUpdate를 넣었습니다: 없으면 수정의 UPDATE가 전체 컬럼을 써서, 읽은 뒤 기록된 휴지통 이동과 사용 시각을 옛 값으로 되돌립니다. 휴지통으로 옮긴 템플릿이 조용히 복구될 수 있습니다
  2. 남은 경쟁 구간이 있습니다: 수정이 템플릿을 읽은 직후 휴지통으로 옮겨지면 404_2가 아니라 200을 돌려줍니다. 행은 휴지통에 남고 내용만 바뀝니다. 잠금을 쓰지 않는 방침이라 그대로 뒀습니다
  3. 응답 객체를 공용화하지 않았습니다: 상세·생성 응답과 필드가 같지만, 화면마다 각자 둔다는 기존 방침을 따랐습니다. #50이 바뀌어도 영향을 받지 않습니다
  4. 13번이 쓸 수 있습니다: existsByUserIdAndActiveShortcutKeyAndIdNot은 excludeTemplateId가 있을 때 그대로 쓰면 됩니다
  5. CORS 설정이 아직 없습니다: 화면을 붙일 때 PUT도 허용해야 합니다

🤖 Generated with Claude Code

@oTAETAEo oTAETAEo added feature 기능 개발 template 내 템플릿·휴지통 labels Oct 31, 2026
@oTAETAEo oTAETAEo self-assigned this Oct 31, 2026
@oTAETAEo
oTAETAEo marked this pull request as ready for review October 31, 2026 05:22
@oTAETAEo oTAETAEo linked an issue Oct 31, 2026 that may be closed by this pull request
7 tasks

@chjpooh2002 chjpooh2002 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

리뷰 남긴 사항 한 번만 확인 부탁드립니다~!

Comment on lines +108 to +118
Template template = templateQueryService.getActiveTemplate(command.userId(), command.templateId());

TemplateTitle title = TemplateTitle.create(command.title());
Shortcut shortcut = Shortcut.create(command.shortcut());
validateShortcutAvailable(command.userId(), shortcut, template.getId());
TemplateContent content = TemplateContent.create(command.content());

template.edit(title, shortcut, content, LocalDateTime.now(clock));

return TemplateUpdateResult.from(save(template));
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

조회와 저장 사이에 다른 탭에서 휴지통으로 옮기면, 휴지통 템플릿에 수정이 들어가고 200이 나갈 것 같은데 맞을까요?!
창이 아주 짧아서 지금은 허용해도 될 것 같은데, 허용한다면 주석에 한 줄 남겨 주면 좋겠습니다! 막으려면 UPDATE 조건에 deleted_at IS NULL을 거는 방법도 방법 고려해주시면 좋을 거 같아요!

@oTAETAEo oTAETAEo Nov 2, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@chjpooh2002 말씀해 주신 부분으로 다시한번 보니까 일어날수 있는 상황인것 같습니당 👏

... templateQueryService.getActiveTemplate(command.userId(), command.templateId());
해당 라인이 실행이 되어서 조회 후에 마지막 return 사이에 발생할수 있는 문제 인걸로 이해 했습니다

조회와 저장 사이에 휴지통으로 옮기면 200이 동작은 서비스 이용에는 큰 문제는 없을것 같은 생각이기도 하구
@DynamicUpdate로 deleted_at은 되돌아가지 않아서 삭제한 게 다시 풀리지는 않고, 구간이 짧아서 괜찮을 것 같습니다.

deleted_at IS NULL 조건은 변경 감지 대신 @Modifying으로 바꿔야 해서 넣지 않았고, 주석에 남겨 두려고 하는데 다른 의견 있으시면 남겨주세여 !

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

저도 그 판단이 맞다고 봅니다! 휴지통 템플릿은 단축어 점유가 풀려 있어서 충돌도 없고, 복구하면 고친 내용이 남는 정도라 실제 피해가 거의 없네요.
말씀대로 제가 처음 말한 조건부 UPDATE는 변경 감지를 버려야 해서 과했던 것 같아요. 나중에 막아야 하는 상황이 오면 수정용 조회만 FOR UPDATE로 잠그는 방법이 있어서, 변경 감지는 그대로 두고 휴지통 이동이 먼저 끝난 경우 404_2를 낼 수 있어요. 주석에 그 선택지도 같이 적어 두면 좋겠습니다!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

좋습니다 !

chjpooh2002
chjpooh2002 previously approved these changes Nov 2, 2026
Base automatically changed from feat/49-template-detail to develop November 2, 2026 09:07
@oTAETAEo
oTAETAEo dismissed chjpooh2002’s stale review November 2, 2026 09:07

The base branch was changed.

@oTAETAEo
oTAETAEo merged commit b83df1a into develop Nov 2, 2026
@oTAETAEo
oTAETAEo deleted the feat/52-template-update branch November 2, 2026 09:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature 기능 개발 template 내 템플릿·휴지통

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[feat] 내 템플릿 수정 API

2 participants