Skip to content

feat: 근로자 보안 링크 SMS 발송 연동 - #134

Merged
BcKmini merged 6 commits into
mainfrom
feat/133-worker-link-sms
Aug 11, 2026
Merged

feat: 근로자 보안 링크 SMS 발송 연동#134
BcKmini merged 6 commits into
mainfrom
feat/133-worker-link-sms

Conversation

@hywznn

@hywznn hywznn commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

왜 필요한가요?

현재 Worker Link는 발급과 수동 전달 완료 기록만 가능해, HR이 링크를 복사해 별도 문자 앱으로 보내야 합니다. 이번 변경은 Server가 선택적으로 SMS Provider를 호출하면서, 외부 발송과 DB 기록이 어긋날 때 같은 문자가 자동 재발송되지 않도록 전달 상태를 관리합니다.

Closes #133

무엇이 바뀌나요?

  • POST /api/v1/worker-links/{workerLinkId}/sms-deliveries API를 추가했습니다.
  • 링크 발급에 사용한 Idempotency-Key와 원본 token hash를 검증합니다.
  • WorkerLinkSmsSender Port와 SOLAPI REST Adapter를 추가했습니다.
  • HMAC 인증, 연결·전체 timeout, 응답 크기 제한과 응답 계약 검증을 적용했습니다.
  • 외부 호출 전에 SENDING을 별도 트랜잭션으로 먼저 저장합니다.
  • Provider가 명확히 거부하면 NOT_SENT, timeout·응답 단절처럼 접수 여부가 불명확하면 REVIEW_REQUIRED로 기록합니다.
  • SENDINGREVIEW_REQUIRED에서는 자동 재발송을 막아 중복 문자를 방지합니다.
  • Provider 접수 후에는 링크 만료를 다시 검사하지 않고 SENT로 기록합니다.
  • 발급 응답의 worker_url은 전체 URL, worker_link_token은 발급 직후만 반환하는 원본 token으로 구분했습니다.
  • V39__extend_worker_link_delivery_status.sql에서 전달 상태 DB 제약을 확장했습니다.
  • 전화번호는 발송 순간에만 사용하며 DB·감사로그에 저장하지 않습니다.

상태 의미

상태 의미 자동 재발송
NOT_SENT 미발송 또는 Provider가 명확히 거부 가능
SENDING Provider 호출이 시작됨 금지
REVIEW_REQUIRED Provider 접수 여부 확인 필요 금지
SENT Provider 접수 또는 HR 수동 전달 기록 기존 결과 반환

SENT는 휴대전화의 최종 수신 성공을 의미하지 않습니다. 기존 /sent 수동 기록도 같은 업무 상태를 사용하므로 이미 SENT인 링크에는 SMS를 추가 발송하지 않습니다.

사용자·개발 영향

  • HR/ADMIN만 발송할 수 있고 다른 사업장 링크는 404로 처리합니다.
  • 기본 Provider는 none이며, 이 상태에서는 성공으로 가장하지 않고 503을 반환합니다.
  • Client는 링크 발급 직후 응답의 worker_link_token을 SMS 요청에 사용해야 합니다.
  • 잘못된 번호로 이미 SENT 처리됐다면 기존 링크를 회전·재발급한 뒤 새 링크를 발송합니다.
  • 실제 발송에는 SOLAPI API Key·Secret과 승인된 발신번호가 필요합니다.

검증

  • ./gradlew clean test — 성공
  • Worker Link·SMS 관련 테스트 재실행 — 성공
  • Provider 호출 전에 DB의 SENDING 상태가 커밋되는지 확인
  • 발송 중 링크가 만료돼도 Provider 접수 성공을 SENT로 기록하는지 확인
  • timeout 후 REVIEW_REQUIRED 전환과 재호출 시 Provider 미호출 확인
  • WireMock으로 SOLAPI HMAC 요청, 2xx, 부분 실패, 503, timeout 검증
  • token 불일치, 사업장 격리, VIEWER 차단, 중복 호출과 감사로그 검증
  • 실제 SOLAPI 계정으로 테스트 번호 발송 요청 — Provider 접수 성공, 실패 건수 0

완료 현황

Server 완료

  • SMS 발송 API와 SOLAPI Adapter 구현
  • 중복 발송 방지를 위한 SENDING·REVIEW_REQUIRED 상태 처리
  • Provider 접수 후 링크 만료 재검사로 인한 상태 불일치 제거
  • 실제 SOLAPI 요청의 Provider 접수 성공 확인
  • feat: 근로자 보안 링크 SMS 발송 연동 #134 병합 head 기준 Server 테스트와 GitHub CI 통과

외부 연동·배포 대기

  • Client가 worker_link_id, worker_link_token, 발급 시 사용한 Idempotency-Key로 SMS API 연결
  • Client의 전달 상태 타입에 SENDING, REVIEW_REQUIRED 추가
  • 배포 Secret에 SOLAPI credential과 승인된 발신번호 등록
  • 데모 Client HTTPS 주소를 WORKER_PORTAL_BASE_URL로 설정
  • 테스트 휴대전화에서 문자 수신 → 모바일 링크 접속 Smoke 수행

hywznn added 3 commits August 11, 2026 01:11
SOLAPI REST Adapter와 발송 API를 추가하고, Provider 접수 성공 후에만 기존 Worker Link를 SENT로 기록합니다. 전화번호와 원본 token은 저장하지 않으며 사업장 격리와 중복 발송 방지를 유지합니다.
Provider 인증 요청, 접수 실패와 timeout, 중복 호출, token 불일치, 사업장 격리, 감사로그 생성을 단위·통합 테스트로 확인합니다.
로컬·데모 환경변수와 실제 수신 Smoke 순서, 개인정보 비저장 원칙, Provider 장애 시 수동 전달 방법을 정리합니다.
@hywznn
hywznn requested review from chaeliki and krestar August 10, 2026 16:13
@hywznn hywznn added the area:server Spring Boot API·도메인·DB·tenant·Task Workflow 영역; Prompt·모델·Provider 구현 제외 label Aug 10, 2026
@hywznn hywznn self-assigned this Aug 10, 2026
@hywznn hywznn added priority:P1 핵심 작업 다음으로 처리할 중요 작업 security:privacy 개인정보·접근권한·토큰·보안 영향이 있는 작업 type:integration 외부 LLM·DB·스토리지 등 시스템 간 연동 작업 status:in-review 구현을 마치고 리뷰 또는 병합을 기다리는 작업 labels Aug 10, 2026
hywznn added 3 commits August 11, 2026 09:01
외부 Provider 호출 전에 SENDING 상태를 별도 트랜잭션으로 확정합니다. 타임아웃처럼 접수 여부가 불명확하면 REVIEW_REQUIRED로 전환해 자동 재발송을 차단하고, Provider가 명확히 거부한 경우에만 NOT_SENT로 되돌립니다. 발급 응답의 전체 URL과 원본 토큰도 분리합니다.
Provider 호출 전 SENDING 커밋, 발송 중 링크 만료, 불명확한 응답의 REVIEW_REQUIRED 전환과 재호출 차단을 검증합니다. Provider 거부와 timeout을 구분하고 PostgreSQL 상태 제약도 확인합니다.
SENT를 휴대전화 수신 성공이 아닌 Provider 접수 또는 수동 전달 기록으로 정의합니다. 불명확한 결과의 자동 재발송 금지와 worker_url·worker_link_token 사용 기준을 문서화합니다.

@BcKmini BcKmini left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

코드 확인했습니다.

  • Idempotency-Key + raw token 이중 검증(MessageDigest.isEqual로 상수시간 비교)이라 다른 링크 오발송 위험이 없어 보입니다.
  • 상태 전이(NOT_SENT → SENDING → SENT/REVIEW_REQUIRED)를 findForUpdate로 행 잠금하고 트랜잭션 분리해서 처리한 게 동시 요청/재시도 상황에서 안전해 보입니다.
  • 전화번호·원본 토큰을 감사로그·일반 로그에 남기지 않는 부분, worker_link_token을 발급 직후에만 노출하는 것도 문서와 코드가 일치합니다.
  • SOLAPI 어댑터는 HMAC-SHA256 서명 + timeout + 응답 크기 제한까지 다 있어서 외부 호출 경계 처리도 꼼꼼합니다.
  • .env.example 기본값이 WORKER_LINK_SMS_PROVIDER=none이라 기존 수동 전달 흐름을 안 건드리는 것도 좋습니다.

사소한 확인차 질문: normalizeKoreanMobile010만 허용하는데(011~019 구형 번호 제외) 문서에도 동일하게 "국내 휴대전화 번호(010...)"로 명시돼 있어서 의도된 MVP 스코프로 이해함 --> 맞나요

CI 전부 통과했고 merge 진행하겠습니다.

@BcKmini
BcKmini marked this pull request as ready for review August 11, 2026 04:00
@BcKmini
BcKmini merged commit f068c09 into main Aug 11, 2026
4 of 5 checks passed
@BcKmini
BcKmini deleted the feat/133-worker-link-sms branch August 11, 2026 04:01
@hywznn

hywznn commented Aug 11, 2026

Copy link
Copy Markdown
Contributor Author

사소한 확인차 질문: normalizeKoreanMobile이 010만 허용하는데(011~019 구형 번호 제외) 문서에도 동일하게 "국내 휴대전화 번호(010...)"로 명시돼 있어서 의도된 MVP 스코프로 이해함 --> 맞나요

일단 제 번호로 보내니까 0104729**** 맞습니다

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:server Spring Boot API·도메인·DB·tenant·Task Workflow 영역; Prompt·모델·Provider 구현 제외 priority:P1 핵심 작업 다음으로 처리할 중요 작업 security:privacy 개인정보·접근권한·토큰·보안 영향이 있는 작업 status:in-review 구현을 마치고 리뷰 또는 병합을 기다리는 작업 type:integration 외부 LLM·DB·스토리지 등 시스템 간 연동 작업

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Worker Link][P1] 근로자 보안 링크 SMS 발송 연동

2 participants