Skip to content

release: v6.0.0 — Effect 기반 대규모 마이그레이션 및 BMS 지원#153

Open
Palbahngmiyine wants to merge 74 commits intomasterfrom
beta
Open

release: v6.0.0 — Effect 기반 대규모 마이그레이션 및 BMS 지원#153
Palbahngmiyine wants to merge 74 commits intomasterfrom
beta

Conversation

@Palbahngmiyine
Copy link
Copy Markdown
Member

Summary

beta 브랜치에 누적된 v6.0.0 변경사항을 master(stable)로 승격합니다. npm latest 태그 릴리스 대상입니다.

  • 현재 버전: master 5.5.4 → beta 6.0.0-beta.2
  • 릴리스 버전: 6.0.0 (stable)

⚠ BREAKING CHANGES

  • 전체 API를 Effect 라이브러리 기반으로 마이그레이션
    • Data.TaggedError 기반 에러 처리 통일
    • Effect.gen + Effect.tryPromise 비동기 처리
    • Effect Schema 기반 입력 검증
    • runSafePromise() / runSafeSync() Promise 변환 유틸 도입
  • 모든 public 타입/스키마 외부 export (barrel 패턴 유지)

Features

  • BMS(브랜드 메시지 서비스) 타입/스키마/E2E 테스트 추가
    • WIDE_ITEM_LIST, carousel(feed/commerce), premium video, wide, commerce, image, text 등 전 채팅버블 타입 지원
    • Free-form(BMS_FREE) 및 템플릿 기반(BMS) 발송 예제 추가
  • ClientError / ServerError 클래스 도입으로 에러 분류 명확화
  • Group 생성 시 custom fields 지원
  • AGENTS 문서화(AGENTS.md) 및 Effect 가이드라인 정비

Bug Fixes

  • handleClientErrorResponse / handleServerErrorResponse null/비정형 JSON 방어
  • Kakao 알림톡 템플릿 code nullable, 앱버튼 링크 필수 처리
  • restore default message schema export — BMS 어설션 유지로 downstream 빌드 실패 방지
  • 테스트에 expect.assertions() 추가로 false-green 방지

CI / Release Automation

  • Release workflows를 npm Trusted Publishers OIDC(Node 24) 기반으로 통합
  • release-please component 브랜치(release-please--branches--beta--components--solapi) 체계화
  • supply-chain workflow 강화

Docs / Examples

Test plan

  • beta 브랜치 전체 E2E 테스트 통과(277/277) 이력 확인
  • pnpm build 성공(타입 체크 포함) 이력 확인
  • @effect/language-service diagnostics: 0 errors / 0 warnings
  • master 머지 후 release-please가 6.0.0 PR 자동 생성하는지 확인
  • npm latest 태그로 6.0.0 배포 후 설치/임포트 smoke test
  • TypeDoc 문서 재생성 및 배포

References

  • 마지막 beta 릴리스: solapi-v6.0.0-beta.2
  • 전체 CHANGELOG: CHANGELOG.md (beta 브랜치 기준)

🤖 Generated with Claude Code

Palbahngmiyine and others added 30 commits April 8, 2026 12:33
BREAKING CHANGE: 전체 API를 Effect 라이브러리 기반으로 마이그레이션

- 모든 타입과 스키마를 외부로 export
- Data.TaggedError 기반 에러 처리 통일
- Effect.gen + Effect.tryPromise 비동기 처리
- Effect Schema 기반 입력 검증
- runSafePromise()/runSafeSync() Promise 변환 유틸 도입
- authenticator를 Effect.try로 래핑
- finalize Defect 방지 및 Kakao 입력 검증
- 테스트 환경변수 SOLAPI_ 접두사 통일
- effectErrorHandler, schemaUtils, getGroupsRequest, getMessagesRequest 테스트 추가

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- versioning-strategy: prerelease 추가 (6.0.0-beta.0 형식 생성)
- package.json version을 5.5.4로 복원 (manifest와 일치, release-please가 bump)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ci: beta release-please versioning-strategy 및 버전 복원
release-please config schema의 올바른 속성명은 "versioning"이다.
"versioning-strategy"는 존재하지 않는 속성으로 무시되어
prerelease 버전(6.0.0-beta.0)이 생성되지 않았다.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ci: release-please versioning 속성명 수정 (beta)
"beta"로 설정하면 첫 버전이 6.0.0-beta(카운터 없음)로 생성된다.
"beta.0"으로 설정하면 6.0.0-beta.0부터 시작하고,
이후 bump 시 beta.1, beta.2로 시리얼하게 증가한다.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ci: prerelease-type beta.0으로 변경 — 시리얼 넘버링 (beta)
…omponents--solapi

chore(beta): release solapi 6.0.0-beta.0
release-please가 package.json 업데이트 시 포맷을 변경하여
biome check(lint:ci)에서 포맷 에러가 발생한다.
package.json은 release-please가 관리하므로 formatter 대상에서 제외.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ci: biome formatter에서 package.json 제외
upstream/beta 동기화 시 보존 대상 파일 복원:
- agents: barrel-checker, effect-reviewer, tidy-first
- skills: create-model, gen-e2e-test

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
v6.0.0 Effect 전환에서 남아있던 레거시 패턴을 모두 제거:
- Message, KakaoOption, RcsOption dead class 제거 (호출처 없음)
- MessageType, AdditionalBody, RcsOptionRequest 수동 타입을 Schema 파생 타입으로 교체
- defaultFetcher의 try-catch를 Effect.try 파이프라인으로 변환
- barrel export 개선: VariableValidationError value export, 누락 schema 추가

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- JSON.parse("null") 시 null.errorCode TypeError 방지 (null guard 추가)
- parseServerErrorBody 순수 함수 추출로 에러 결정 로직 명확화
- makeError 팩토리로 ServerError 생성 코드 중복 제거

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- res.json() 대신 res.text() + JSON.parse로 변경하여 파싱 실패 처리
- null, 비객체 JSON, 다른 스키마 JSON에 대한 null guard 추가
- genericError fallback으로 "undefined: undefined" 에러 메시지 방지

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…egacy-cleanup

refactor: 레거시 클래스/수동 타입 제거 및 Effect Schema 전면 전환
- defaultFetcher.ts: Effect.flatMap 내 try-catch를 Effect.try로 전환, throw new Error 제거, isErrorResponse 타입 가드로 as Partial<ErrorResponse> 단언 대체
- defaultError.ts: 미사용 deprecated ApiError alias 제거, isErrorResponse 타입 가드 추가
- index.ts/messageService.ts: sendOne 메소드 삭제
- kakaoOption.ts: as Record<string, unknown> 단언을 keyof BaseBmsSchemaType으로 대체
- defaultService.ts: 내부 전용 타입 불필요 export 제거
- stringDateTrasnfer.ts → stringDateTransfer.ts 파일명 오타 수정 및 전체 import 경로 반영

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- isErrorResponse: 빈 문자열 거부 추가 (기존 truthy 체크 동작 보존), 단일 cast로 간소화
- handleClientErrorResponse: SyntaxError 구분 추가 (parseServerErrorBody와 일관)
- defaultFetcher: toMessage/makeParseError 헬퍼 추출로 반복 제거, 불필요 nested pipe 제거
- isErrorResponse 단위 테스트 19건 추가 (null, undefined, 원시값, 빈 문자열 등)
- examples/: 삭제된 sendOne/sendOneFuture → send로 전환 (6개 파일)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- singleMessageSendingRequestSchema, SingleMessageSendingRequestSchema 제거 (sendOne 전용)
- singleMessageSentResponseSchema, SingleMessageSentResponse 제거 (sendOne 전용)
- barrel export (requests/index.ts, responses/index.ts)에서 해당 항목 제거
- 미사용 messageTypeSchema import 정리 (messageResponses.ts)
- sendMessage.test.ts에서 삭제된 스키마 테스트 3건 제거

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ns-cleanup

refactor: CLAUDE.md 원칙 위반 정리 및 dead code 제거
- 미사용 default export 제거 (fileToBase64, defaultFetcher)
- 빈 섹션 주석 제거 (models/index.ts, responses/index.ts)
- effectErrorHandler 타입 가드 개선: 필드별 as 어설션 → Record 통합
- bmsCommerce NumberOrNumericString 타입 어설션 Why 주석 보강
- deprecated v5 alias 7개 제거 (KakaoAlimtalkTemplateInterface 등)
- bindServices 동적 프로토타입 순회 → 32개 메서드 명시적 .bind() 전환
- Writable<T> 헬퍼 타입 및 DefaultService import 제거
- 타입 어설션 7개 제거 (as unknown as, as never, as Record 등)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- CLAUDE.md/AGENTS.md: bindServices() 참조를 명시적 .bind()로 업데이트
- effectErrorHandler 테스트: non-string _tag, message 없는 tagged defect 케이스 추가
- solapiMessageService 테스트: 32개 메서드 전체 바인딩 검증 테스트 추가

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…de-md-violations

refactor: dead code 제거, deprecated alias 정리, bindServices 명시적 바인딩 전환
- @effect/language-service 설치 및 tsconfig.json plugin 설정
- as 캐스팅 제거: isTaggedDefect 타입 가드 도입, isErrorResponse in 연산자 narrowing
- DRY: DefaultService에 getWithQuery 헬퍼 추출, 6개 서비스 메서드 간소화
- Effect.gen → Effect.flatMap 전환: uploadFile, reserveGroup, addMessagesToGroup
- Effect LSP diagnostic 반영: Schema.decodeUnknown + mapError, Effect.void, yieldable error 직접 yield

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…rvice-optimization

refactor: Effect Language Service 도입 및 타입 안전성·DRY 개선
operatorTypeSchema, dateOperatorTypeSchema, kakaoOptionRequestSchema 등
프로젝트 내부·외부 어디에서도 참조되지 않는 타입/스키마 export를 정리하고,
유일한 export가 모두 dead인 kakaoOptionRequest.ts 파일을 삭제합니다.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
refactor: 미사용 export 및 dead file 제거
Palbahngmiyine and others added 12 commits April 16, 2026 16:54
npm Trusted Publishers OIDC token exchange requires npm CLI 11.5.1+.
Node 18 ships npm 10.8.2 which lacks OIDC support, causing E404 on
publish despite provenance signing succeeding (Sigstore is separate).

Node 24 ships npm 11.x+ with native OIDC support. Only the publish
job is changed — test/lint jobs remain on Node 18 for SDK compatibility.

Also restore --provenance --access public flags per OIDC project
conventions (astral-sh/ruff, Effect-TS/effect).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
fix(ci): use Node 24 for npm Trusted Publishers OIDC support
Re-trigger release-please after deleting solapi-v6.0.0-beta.1
release and tag. The updated release.yml (Node 24, npm 11.x+)
will handle OIDC Trusted Publishers authentication.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ci: trigger beta release with Node 24 OIDC publish
…omponents--solapi

chore(beta): release solapi 6.0.0-beta.2
- Remove unused runSafeSync from effectErrorHandler (dead code)
- Unify path aliases to domain-specific form (@/ -> @errors, @models)
- Add @errors/* path to tsconfig to match documented alias scheme
- Extract retryable error detection helper in defaultFetcher
- Adopt ParseResult TreeFormatter/ArrayFormatter in decodeWithBadRequest
- Remove redundant WHAT/section comments; keep TSDoc and WHY-only

Verified with pnpm lint, pnpm test (277/277), pnpm build, and
@effect/language-service diagnostics (0 errors / 0 warnings / 88 files).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
refactor: remove dead code and align with effect best practices
Refactor commit 0042ae6 는 release-please 기본 설정상 user-facing
commit(feat/fix/perf/deps)이 아니라 release PR 이 자동 생성되지 않았습니다.
Effect ParseResult 포맷터 도입과 runSafeSync 내부 심볼 제거가
포함되어 있어 beta release 로 배포합니다.

Release-As: 6.0.0-beta.3

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
chore: trigger beta release for refactor changes (6.0.0-beta.3)
sendOne/sendOneFuture 메서드는 이미 제거되어 `send`로 통합되었으므로
"send 메소드로도 동일하게 사용가능" 주석 문구는 의미가 사라졌습니다.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
docs(examples): drop stale sendOne note from send_sms comment
@Palbahngmiyine Palbahngmiyine self-assigned this Apr 17, 2026
@Palbahngmiyine
Copy link
Copy Markdown
Member Author

This PR resolves #95 #102

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request executes a comprehensive migration of the SDK to the Effect library, introducing functional programming patterns and schema-based validation. The SolapiMessageService facade was refactored to use explicit method binding, and all domain services and internal utilities were converted to Effect pipelines. Additionally, legacy class-based models were replaced with Effect Schema definitions, and the test suite was expanded to cover these new patterns. Feedback from the review suggests ensuring compatibility for error.cause in older Node.js 16 environments and recommends utilizing Effect Schema for response parsing instead of type assertions to improve runtime safety.

Comment thread src/lib/defaultFetcher.ts
Comment on lines +32 to +40
const isRetryableNetworkError = (error: Error): boolean => {
const cause = error.cause;
const causeCode =
cause && typeof cause === 'object' && 'code' in cause
? String(cause.code)
: '';
const message = `${error.message} ${causeCode}`.toLowerCase();
return RETRYABLE_ERROR_KEYWORDS.some(keyword => message.includes(keyword));
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

error.cause 속성은 Node.js 16.9.0 버전부터 공식 지원됩니다. package.jsonengines 설정이 node >= 16.0.0으로 되어 있어, 16.0.0 ~ 16.8.x 버전 환경에서는 causeundefined일 수 있습니다. 현재 코드는 옵셔널 체이닝 등을 통해 안전하게 처리하고 있으나, 런타임 환경에 따른 주의가 필요합니다.

Comment thread src/lib/defaultFetcher.ts
Comment on lines +66 to +67
const parsed: unknown = JSON.parse(responseText);
return parsed as R;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

JSON.parse 결과를 R 타입으로 강제 형변환(Type Assertion)하고 있습니다. 응답 데이터의 구조적 안전성을 보장하기 위해 추후 모든 응답 모델에 대해 Effect Schema 기반의 디코딩을 적용하는 것을 권장합니다.

Palbahngmiyine and others added 13 commits April 17, 2026 19:31
- getBalance 응답에 lowBalanceAlert, minimumCash, rechargeTo, deposit 등 실서버에 존재하는 필드를 반영해 스키마 확장
- getStatistics, getGroups, getGroup, getBlacks, getBlockGroups, getKakaoChannel, getKakaoAlimtalkTemplate(s) 응답의 드리프트(nullable, 누락 필드, Record→Array 등) 정합화
- MessageTypeRecord에 rcs_itpl/ltpl, fax, voice, bms_* 신규 메시지 타입 추가
- 조회 응답 전용 storedMessageSchema 신설해 발송용 messageSchema와 분리
- DefaultService.requestEffect/getWithQuery에 responseSchema 주입 경로 마련하고, decodeServerResponse 헬퍼 추가로 서버 응답을 런타임 검증해 드리프트 즉시 감지
- 15개 조회 서비스 메서드에 해당 스키마 연결

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
첫 페이지 호출 시 서버가 startKey를 null 또는 미포함으로 반환하는데
getKakaoChannelsResponseSchema와 getKakaoAlimtalkTemplatesResponseSchema의
startKey가 required string으로 남아 있어 런타임 검증이 실패하는 회귀를 수정.
다른 list 응답들과 동일하게 NullishOr로 정렬.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- 전용 ResponseSchemaMismatchError 도입 — 2xx에서 발생하는 스키마 불일치를
  ServerError(200)으로 래핑하던 문제를 분리해 5xx 재시도/알림 분기 오염 방지
- validationErrors(ArrayFormatter) 및 responseBody 보존으로 운영 환경에서도
  드리프트 경로 재현 가능
- decodeServerResponse에 onExcessProperty:'preserve' 적용 — 부분 스키마가
  서버의 미선언 필드를 strip 해버려 소비자 응답에서 silent data loss가
  발생하던 문제 수정
- storedMessageSchema의 autoTypeDetect/replacement/voiceReplied/
  unavailableSenderNumber를 Schema.transform으로 boolean 정규화
- responseSchema 제네릭을 Schema.Schema<R, I, never>로 타이트닝 —
  요구사항 채널이 오염되지 않도록 강제
- decodeServerResponse 8개 유닛 테스트 추가 (fixture 기반 회귀 방어)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- booleanOrZeroOne을 Schema.transformOrFail로 엄격화해 2/-1/NaN 같은
  drift 입력을 silent true로 coerce하지 않고 ResponseSchemaMismatchError로 전파
- stringifyResponseBody가 circular/BigInt로 JSON 실패 시 undefined로
  정보 소실하던 동작을 '[unserializable: reason] toStringTag' 메타데이터로 보완
- ResponseSchemaMismatchError.toString()의 production 경로에서 url과
  validationErrors를 유지(responseBody만 민감 페이로드라 계속 숨김)
- 엣지 케이스 유닛 테스트 추가: boolean transform 거부값, 순환참조 responseBody

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ure fields

Codex 2차 리뷰 발견 사항 반영:
- countForChargeSchema에 rcs_itpl/ltpl, fax, voice, bms_* 13개 필드 추가 —
  messageTypeRecordSchema 확장과 비대칭하여 신규 메시지 타입을 가진 그룹 조회가
  ResponseSchemaMismatchError로 실패하던 문제 수정 (getGroups/getGroup 회귀 방어)
- storedMessageSchema의 unavailableSenderNumber/faxPageCount/voiceDuration/
  voiceReplied를 NullishOr로 완화 — non-FAX/non-VOICE 메시지 행에서 null로
  내려오는 경우 전체 응답 decode 실패하던 문제 수정

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…Error

Codex 3차 리뷰 P2: production에서 ResponseSchemaMismatchError의 responseBody
필드에 원본 응답이 유지되어, toString() 가드와 무관하게 Sentry 등
에러 리포터가 enumerable 필드를 직렬화하면서 PII가 누출될 수 있었음.
ServerError와 동일하게 creation 단계에서 production이면 undefined로 제외.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…tion

silent-failure-hunter 4차 후속:
- ParseResult.ArrayFormatter/TreeFormatter는 실패 메시지에 원본 값(전화번호 등)을
  그대로 문자열로 삽입하므로, production에서는 validationErrors 엔트리를
  `path: [_tag]` 형태로 축약하고 message는 issue 개수 요약으로 대체
- getMessages 등 조회 API는 to/from 등을 query string에 실으므로 production에서
  url의 query 부분을 '?[redacted]'로 마스킹
- 테스트 fixture를 확장해 실제 전화번호 문자열이 message/validationErrors/url
  어디에도 포함되지 않음을 명시적으로 검증

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
silent-failure-hunter 5차:
- NODE_ENV 가드를 safe-by-default로 반전 — staging/unset 등
  비정형 환경에서도 redact를 기본 적용하고, development/test만 verbose 모드
- redactUrlForProduction이 URL fragment(#...)를 먼저 strip 하도록 보완해
  fragment 내부 쿼리 문자열 또한 redact 경로에 포함
- ResponseSchemaMismatchError.toString()에서 이중 가드 제거 — creation 시점에
  이미 필드가 redact된 상태라 단순히 보유 값만 렌더링
- 테스트 시나리오를 'production' → 'staging'으로 교체해 새로운 가드 범위 검증

Codex 4차에서 지적된 BadRequestError/ClientError/ServerError/NetworkError의
동일 PII 누출 패턴은 request/transport 계층 전반의 정책 변경을 요구하므로
이 PR 범위에서 분리 — 별도 후속 작업으로 처리 권장

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
silent-failure-hunter/Codex 5차:
- NODE_ENV gate를 .trim().toLowerCase()로 정규화해 Windows PowerShell 등에서
  'Development' 같은 변형도 verbose 모드로 인식
- redactUrlForProduction을 URL 객체 기반으로 전환해 query string뿐 아니라
  fragment/userinfo(user:password@)까지 함께 redact
- ResponseSchemaMismatchError.toString()에 defense-in-depth 가드 복원 —
  클래스가 public이라 외부에서 직접 생성될 경우에도 production 경로에서
  responseBody가 유출되지 않도록 한다
- storedMessageSchema의 옵션 객체(kakao/rcs/naver/fax/voice)와 배열 필드
  (replacements/log/queues)를 Record/Array of Unknown으로 강화해 최소한의
  구조 계약(object or array)이 타입 수준에서 유지되도록 함
- 테스트: NODE_ENV를 table-driven으로 확장해 production/staging/undefined/빈 문자열
  모두 redact 되고 development/test만 verbose 되는 계약, 대소문자/공백 정규화
  동작, userinfo/fragment redact 동작까지 커버

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ypeRecord

Codex 최종 P2: statusCode 값 타입이 generic Record<string, number>로 바뀌면서
statusCode[code].sms/lms 같은 타입-narrowed 접근이 깨지던 회귀 복원.
서버가 실제 일부 필드만 내려주는 sparse 응답임을 고려해,
messageTypeRecordSchema를 Schema.partial로 감싼 partialMessageTypeRecordSchema를
신설하고 dayPeriod.statusCode value에 사용. 타입 정보는 유지하면서도
한두 필드만 있는 응답도 통과한다.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…dation-beta

fix(responses): sync query API schemas and add runtime response validation
…omponents--solapi

chore(beta): release solapi 6.0.0-beta.3
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant