Repository navigation
fix: 탈퇴한 계정에 알림이 계속 발송됐다 - #149
Merged
Merged
Conversation
리포지토리 시크릿이 하나도 설정돼 있지 않다(gh secret list → 0개). 그래서 main 머지마다 Deploy to Production 이 첫 스텝(Check required secrets)에서 죽었다. #143~#147 까지 다섯 번 연속 같은 모양이다. success Test and Build success Security Scan success Build Docker Image failure Deploy to Production ← Check required secrets 컨테이너를 띄우기 전에 멈추므로 운영 인스턴스를 건드리지는 않는다. 문제는 그 반대다 — 아무것도 배포되지 않는데 매번 빨간불로 끝나서 그 빨간불이 정상처럼 보인다. 그 상태가 굳으면 진짜 실패도 눈에 띄지 않는다(#94 가 적은 그대로다). 시크릿 상태를 세 가지로 나눴다. 전부 있음 → 배포한다 하나도 없음 → 건너뛴다. 실패가 아니고, 잡 요약에 "운영 배포를 건너뜀" 과 설정 문서 경로가 남는다. 배포가 안 됐다는 사실이 조용히 지나가지 않는다 일부만 있음 → 실패한다. 호스트는 있는데 키가 없는 식이면 누군가 설정하다 멈춘 것이고 그건 건너뛸 일이 아니다 스테이징도 같은 규칙으로 맞췄다. 문서(docs/operations/deployment-secrets.md)를 추가했다. 개인키를 채팅·이슈·PR 에 붙여넣지 않고 파일에서 바로 읽는 명령(gh secret set PRODUCTION_SSH_KEY < ~/.ssh/...), 배포 전용 키를 따로 만드는 이유, 서버에 미리 있어야 하는 것(docker 그룹, 스왑 2GB), 첫 배포에서 각 스텝이 실패할 때의 의미를 적었다. 아직 남은 것도 적었다 — 교체 순간의 짧은 순단, SSH 키 권한을 command= 로 좁히지 못한 상태, 애플리케이션 환경 변수(.env)가 이 문서 범위 밖인 점. Refs #94
앞 커밋에서 Set up SSH 스텝에 조건을 두 번 넣었다. 같은 키가 두 번이면 GitHub 이 워크플로 파일 자체를 거부한다 — 실행 목록에 "This run likely failed because of a workflow file issue" 만 남고 잡이 하나도 생기지 않는다. yaml.safe_load 는 통과했다. 파이썬 YAML 파서는 중복 키를 뒤 값으로 덮어쓰고 넘어가기 때문이다. 스텝 목록을 실제로 훑어 "조건이 걸려야 하는 스텝에 if 가 있는지" 를 확인하는 쪽으로 검증을 바꿨고, 그 과정에서 deploy-production 의 Set up SSH 에 조건이 빠져 있던 것도 함께 찾았다. Refs #94
탈퇴는 물리 삭제가 아니다(User.withdraw()). 행이 남고 isActive=false, deletedAt 이 찍히며 이메일은 deleted_<id>@deleted.local 로 익명화된다. 그런데 그 사용자가 남긴 대기 등록·알림 설정·푸시 토큰은 그대로 남는다. 그래서 이런 일이 생긴다. 어린이집 대기를 걸어 둔 사람이 탈퇴한다 → 대기 기록은 WAITING 으로 남아 있다 → 그 시설에 자리가 난다 → FacilityVacancyNotifier 가 entry.getUser() 로 알림을 만들고 발송한다 → 탈퇴한 계정의 기기로 푸시가 가고, 익명화된 주소로 메일이 나간다 NotificationDispatcher 는 수신자가 null 인지만 봤다. 발송 경로는 넷이지만(리마인더 배치, 빈자리·마감 알림, 사용자 API, 어드민) 전부 이 디스패처를 거치므로 막는 자리도 여기 하나다 — 경로마다 검사를 넣으면 다음 경로가 생길 때 또 빠진다. 관리자가 비활성화한 계정도 같게 본다. 로그인할 수 없는 계정에 알림을 보낼 이유가 없다. 인앱 알림 레코드 자체는 막지 않는다. 이미 저장된 뒤라 여기서 되돌릴 수 없고, 조회 API 가 인증을 요구하므로 탈퇴 계정은 어차피 볼 수 없다. 테스트로 먼저 확인했다 — 탈퇴·비활성 계정 모두 "Expecting value to be false but was true". 차단이 과하면 아무에게도 알림이 가지 않으므로 "정상 계정은 차단되지 않는다" 도 함께 고정했다. 기존 테스트 하나가 깨졌다. NotificationChannelAvailabilityTest 의 수신자 픽스처가 isActive 를 설정하지 않아 "비활성" 으로 읽혔다. 실제 저장된 사용자는 @PrePersist 가 true 로 채우므로 픽스처를 맞췄고, 왜 명시해야 하는지 주석으로 남겼다. - 전체 테스트 650개, 실패 0
3 of 6 tasks
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.
남은 도메인 경계 위반 237건 중
→ user가 92건으로 가장 큰 덩어리입니다. 각 도메인이 User 로 무엇을 하는지 세어 보다가 발송 경로에서 이걸 발견했습니다.무슨 일이 일어나나
탈퇴는 물리 삭제가 아닙니다(
User.withdraw()— #146에서 양쪽 경로를 통일했습니다). 행이 남고isActive=false,deletedAt이 찍히며 이메일은deleted_<id>@deleted.local로 익명화됩니다. 그런데 그 사용자가 남긴 대기 등록·알림 설정·푸시 토큰은 그대로 남습니다.NotificationDispatcher는 수신자가null인지만 봤습니다.막는 자리는 한 곳
발송 경로는 넷이지만(리마인더 배치, 빈자리·마감 알림, 사용자 API, 어드민) 전부 이 디스패처를 거칩니다. 그래서 여기 하나에 조건을 넣었습니다 — 경로마다 검사를 넣으면 다음 경로가 생길 때 또 빠집니다. (#145 에서 어드민 발송이 디스패처를 아예 거치지 않던 것을 고친 덕에 이제 네 경로가 모두 여기로 모입니다.)
관리자가 비활성화한 계정도 같게 봅니다. 로그인할 수 없는 계정에 알림을 보낼 이유가 없습니다.
인앱 알림 레코드 자체는 막지 않습니다. 이미 저장된 뒤라 여기서 되돌릴 수 없고, 조회 API 가 인증을 요구하므로 탈퇴 계정은 어차피 볼 수 없습니다.
테스트
먼저 실패를 확인했습니다 — 탈퇴·비활성 계정 모두
Expecting value to be false but was true.차단이 과하면 아무에게도 알림이 가지 않으므로 "정상 계정은 차단되지 않는다" 도 함께 고정했습니다.
기존 테스트 하나가 깨졌습니다
NotificationChannelAvailabilityTest의 수신자 픽스처가isActive를 설정하지 않아 "비활성" 으로 읽혔습니다. 실제 저장된 사용자는@PrePersist가true로 채우므로 픽스처를 맞췄고, 왜 명시해야 하는지 주석으로 남겼습니다.숫자