Skip to content

Commit 2589f11

Browse files
refactor: CLAUDE.md 원칙 위반 정리 및 dead code 제거
- 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>
1 parent fc395c9 commit 2589f11

16 files changed

Lines changed: 106 additions & 114 deletions

AGENTS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ Schema.String.pipe(
120120
| `authenticator.ts` | HMAC-SHA256 auth header |
121121
| `stringifyQuery.ts` | URL query string builder (array handling) |
122122
| `fileToBase64.ts` | File/URL → Base64 |
123-
| `stringDateTrasnfer.ts` | Date parsing with `InvalidDateError` |
123+
| `stringDateTransfer.ts` | Date parsing with `InvalidDateError` |
124124

125125
## Anti-Patterns
126126

src/errors/defaultError.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -109,11 +109,6 @@ export class ClientError extends Data.TaggedError('ClientError')<{
109109
}
110110
}
111111

112-
/** @deprecated Use ClientError instead */
113-
export const ApiError = ClientError;
114-
/** @deprecated Use ClientError instead */
115-
export type ApiError = ClientError;
116-
117112
// Defect(예측되지 않은 예외) — Effect 경계에서 발생하는 비정상 에러
118113
export class UnexpectedDefectError extends Data.TaggedError(
119114
'UnexpectedDefectError',
@@ -156,3 +151,11 @@ URL: ${this.url}
156151
Response: ${this.responseBody?.substring(0, 500) ?? '(empty)'}`;
157152
}
158153
}
154+
155+
export const isErrorResponse = (value: unknown): value is ErrorResponse =>
156+
value != null &&
157+
typeof value === 'object' &&
158+
'errorCode' in value &&
159+
typeof (value as Record<string, unknown>).errorCode === 'string' &&
160+
'errorMessage' in value &&
161+
typeof (value as Record<string, unknown>).errorMessage === 'string';

src/index.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -218,14 +218,6 @@ export class SolapiMessageService {
218218
readonly removeGroup: typeof GroupService.prototype.removeGroup;
219219

220220
// MessageService 위임
221-
/**
222-
* 단일 메시지 발송 기능
223-
* @param message 메시지(문자, 알림톡 등)
224-
* @param appId appstore용 app id
225-
*/
226-
// TODO: temporary remove
227-
readonly sendOne: typeof MessageService.prototype.sendOne;
228-
229221
/**
230222
* 메시지 발송 기능, sendMany 함수보다 개선된 오류 표시 기능등을 제공합니다.
231223
* 한번의 요청으로 최대 10,000건까지 발송할 수 있습니다.

src/lib/defaultFetcher.ts

Lines changed: 82 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import {
33
ApiKeyError,
44
ClientError,
55
DefaultError,
6-
ErrorResponse,
6+
isErrorResponse,
77
NetworkError,
88
ServerError,
99
} from '../errors/defaultError';
@@ -21,27 +21,49 @@ class RetryableError extends Data.TaggedError('RetryableError')<{
2121
}> {}
2222

2323
const handleOkResponse = <R>(res: Response) =>
24-
Effect.tryPromise({
25-
try: async (): Promise<R> => {
26-
const responseText = await res.text();
24+
pipe(
25+
Effect.tryPromise({
26+
try: () => res.text(),
27+
catch: e =>
28+
new DefaultError({
29+
errorCode: 'ParseError',
30+
errorMessage: e instanceof Error ? e.message : String(e),
31+
context: {
32+
responseStatus: res.status,
33+
responseUrl: res.url,
34+
},
35+
}),
36+
}),
37+
Effect.flatMap(responseText => {
2738
if (!responseText) {
2839
if (res.status === 204) {
29-
return {} as R;
40+
return Effect.succeed({} as R);
3041
}
31-
throw new Error('API returned empty response body');
42+
return Effect.fail(
43+
new DefaultError({
44+
errorCode: 'ParseError',
45+
errorMessage: 'API returned empty response body',
46+
context: {
47+
responseStatus: res.status,
48+
responseUrl: res.url,
49+
},
50+
}),
51+
);
3252
}
33-
return JSON.parse(responseText) as R;
34-
},
35-
catch: e =>
36-
new DefaultError({
37-
errorCode: 'ParseError',
38-
errorMessage: e instanceof Error ? e.message : String(e),
39-
context: {
40-
responseStatus: res.status,
41-
responseUrl: res.url,
42-
},
43-
}),
44-
});
53+
return Effect.try({
54+
try: () => JSON.parse(responseText) as R,
55+
catch: e =>
56+
new DefaultError({
57+
errorCode: 'ParseError',
58+
errorMessage: e instanceof Error ? e.message : String(e),
59+
context: {
60+
responseStatus: res.status,
61+
responseUrl: res.url,
62+
},
63+
}),
64+
});
65+
}),
66+
);
4567

4668
const handleClientErrorResponse = (res: Response) =>
4769
pipe(
@@ -65,52 +87,57 @@ const handleClientErrorResponse = (res: Response) =>
6587
url: res.url,
6688
});
6789

68-
let json: Partial<ErrorResponse>;
69-
try {
70-
json = JSON.parse(text) as Partial<ErrorResponse>;
71-
} catch {
72-
return Effect.fail(genericError);
73-
}
74-
75-
return Effect.fail(
76-
json != null && json.errorCode && json.errorMessage
77-
? new ClientError({
78-
errorCode: json.errorCode,
79-
errorMessage: json.errorMessage,
80-
httpStatus: res.status,
81-
url: res.url,
82-
})
83-
: genericError,
90+
return pipe(
91+
Effect.try({
92+
try: () => JSON.parse(text) as unknown,
93+
catch: () => genericError,
94+
}),
95+
Effect.flatMap(json =>
96+
Effect.fail(
97+
isErrorResponse(json)
98+
? new ClientError({
99+
errorCode: json.errorCode,
100+
errorMessage: json.errorMessage,
101+
httpStatus: res.status,
102+
url: res.url,
103+
})
104+
: genericError,
105+
),
106+
),
84107
);
85108
}),
86109
);
87110

88111
/**
89-
* JSON 파싱을 시도하여 적절한 ServerError를 결정하는 순수 함수.
90-
* 모든 경로가 ServerError를 반환한다 (서버 에러 응답이므로 성공 경로 없음).
112+
* JSON 파싱을 시도하여 적절한 ServerError로 실패하는 Effect를 반환.
113+
* 모든 경로가 ServerError로 실패한다 (서버 에러 응답이므로 성공 경로 없음).
91114
*/
92115
function parseServerErrorBody(
93116
text: string,
94117
genericError: ServerError,
95118
makeError: (errorCode: string, errorMessage: string) => ServerError,
96-
): ServerError {
97-
let json: Partial<ErrorResponse>;
98-
try {
99-
json = JSON.parse(text) as Partial<ErrorResponse>;
100-
} catch (parseError) {
101-
if (parseError instanceof SyntaxError) {
102-
return genericError;
103-
}
104-
return makeError(
105-
'ResponseParseError',
106-
parseError instanceof Error ? parseError.message : String(parseError),
107-
);
108-
}
109-
110-
if (json != null && json.errorCode && json.errorMessage) {
111-
return makeError(json.errorCode, json.errorMessage);
112-
}
113-
return genericError;
119+
): Effect.Effect<never, ServerError> {
120+
return pipe(
121+
Effect.try({
122+
try: () => JSON.parse(text) as unknown,
123+
catch: (parseError: unknown) =>
124+
parseError instanceof SyntaxError
125+
? genericError
126+
: makeError(
127+
'ResponseParseError',
128+
parseError instanceof Error
129+
? parseError.message
130+
: String(parseError),
131+
),
132+
}),
133+
Effect.flatMap(json =>
134+
Effect.fail(
135+
isErrorResponse(json)
136+
? makeError(json.errorCode, json.errorMessage)
137+
: genericError,
138+
),
139+
),
140+
);
114141
}
115142

116143
const handleServerErrorResponse = (res: Response) =>
@@ -146,7 +173,7 @@ const handleServerErrorResponse = (res: Response) =>
146173
text.substring(0, 200) || 'Server error occurred',
147174
);
148175

149-
return Effect.fail(parseServerErrorBody(text, genericError, makeError));
176+
return parseServerErrorBody(text, genericError, makeError);
150177
}),
151178
);
152179

src/lib/schemaUtils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import {Schema} from 'effect';
22
import * as Effect from 'effect/Effect';
33
import {BadRequestError, InvalidDateError} from '../errors/defaultError';
4-
import stringDateTransfer, {formatWithTransfer} from './stringDateTrasnfer';
4+
import stringDateTransfer, {formatWithTransfer} from './stringDateTransfer';
55

66
/**
77
* Schema 디코딩 + BadRequestError 변환을 결합한 Effect 헬퍼.

src/models/base/kakao/kakaoOption.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,10 @@ export type BmsChatBubbleType = Schema.Schema.Type<
5353
* - WIDE_ITEM_LIST: header, mainWideItem, subWideItemList 필수
5454
* - COMMERCE: imageId, commerce, buttons 필수
5555
*/
56-
const BMS_REQUIRED_FIELDS: Record<BmsChatBubbleType, ReadonlyArray<string>> = {
56+
const BMS_REQUIRED_FIELDS: Record<
57+
BmsChatBubbleType,
58+
ReadonlyArray<keyof BaseBmsSchemaType>
59+
> = {
5760
TEXT: [],
5861
IMAGE: ['imageId'],
5962
WIDE: ['imageId'],
@@ -107,9 +110,8 @@ const validateBmsRequiredFields = (
107110
): boolean | string => {
108111
const chatBubbleType = bms.chatBubbleType;
109112
const requiredFields = BMS_REQUIRED_FIELDS[chatBubbleType] ?? [];
110-
const bmsRecord = bms as Record<string, unknown>;
111113
const missingFields = requiredFields.filter(
112-
field => bmsRecord[field] === undefined || bmsRecord[field] === null,
114+
field => bms[field] === undefined || bms[field] === null,
113115
);
114116

115117
if (missingFields.length > 0) {

src/models/requests/iam/getBlacksRequest.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {formatWithTransfer} from '@lib/stringDateTrasnfer';
1+
import {formatWithTransfer} from '@lib/stringDateTransfer';
22
import {Schema} from 'effect';
33
import {type DatePayloadType} from '../common/datePayload';
44

src/models/requests/kakao/getKakaoAlimtalkTemplatesRequest.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {formatWithTransfer} from '@lib/stringDateTrasnfer';
1+
import {formatWithTransfer} from '@lib/stringDateTransfer';
22
import {
33
type KakaoAlimtalkTemplateStatus,
44
kakaoAlimtalkTemplateStatusSchema,

src/models/requests/kakao/getKakaoChannelsRequest.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {formatWithTransfer} from '@lib/stringDateTrasnfer';
1+
import {formatWithTransfer} from '@lib/stringDateTransfer';
22
import {Schema} from 'effect';
33
import {type DatePayloadType} from '../common/datePayload';
44

0 commit comments

Comments
 (0)