synapse-sdk 2025.9.1__py3-none-any.whl → 2025.9.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of synapse-sdk might be problematic. Click here for more details.

Files changed (81) hide show
  1. synapse_sdk/devtools/docs/docs/api/clients/annotation-mixin.md +378 -0
  2. synapse_sdk/devtools/docs/docs/api/clients/backend.md +368 -1
  3. synapse_sdk/devtools/docs/docs/api/clients/core-mixin.md +477 -0
  4. synapse_sdk/devtools/docs/docs/api/clients/data-collection-mixin.md +422 -0
  5. synapse_sdk/devtools/docs/docs/api/clients/hitl-mixin.md +554 -0
  6. synapse_sdk/devtools/docs/docs/api/clients/index.md +391 -0
  7. synapse_sdk/devtools/docs/docs/api/clients/integration-mixin.md +571 -0
  8. synapse_sdk/devtools/docs/docs/api/clients/ml-mixin.md +578 -0
  9. synapse_sdk/devtools/docs/docs/plugins/developing-upload-template.md +1463 -0
  10. synapse_sdk/devtools/docs/docs/plugins/export-plugins.md +161 -34
  11. synapse_sdk/devtools/docs/docs/plugins/upload-plugins.md +1497 -213
  12. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/annotation-mixin.md +289 -0
  13. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/backend.md +378 -11
  14. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/core-mixin.md +417 -0
  15. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/data-collection-mixin.md +356 -0
  16. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/hitl-mixin.md +192 -0
  17. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/index.md +391 -0
  18. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/integration-mixin.md +479 -0
  19. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/api/clients/ml-mixin.md +284 -0
  20. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/developing-upload-template.md +1463 -0
  21. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/export-plugins.md +161 -34
  22. synapse_sdk/devtools/docs/i18n/ko/docusaurus-plugin-content-docs/current/plugins/upload-plugins.md +1752 -572
  23. synapse_sdk/devtools/docs/sidebars.ts +7 -0
  24. synapse_sdk/plugins/README.md +1 -2
  25. synapse_sdk/plugins/categories/base.py +7 -0
  26. synapse_sdk/plugins/categories/export/actions/__init__.py +3 -0
  27. synapse_sdk/plugins/categories/export/actions/export/__init__.py +28 -0
  28. synapse_sdk/plugins/categories/export/actions/export/action.py +160 -0
  29. synapse_sdk/plugins/categories/export/actions/export/enums.py +113 -0
  30. synapse_sdk/plugins/categories/export/actions/export/exceptions.py +53 -0
  31. synapse_sdk/plugins/categories/export/actions/export/models.py +74 -0
  32. synapse_sdk/plugins/categories/export/actions/export/run.py +195 -0
  33. synapse_sdk/plugins/categories/export/actions/export/utils.py +187 -0
  34. synapse_sdk/plugins/categories/export/templates/plugin/__init__.py +1 -1
  35. synapse_sdk/plugins/categories/upload/actions/upload/__init__.py +1 -2
  36. synapse_sdk/plugins/categories/upload/actions/upload/action.py +154 -531
  37. synapse_sdk/plugins/categories/upload/actions/upload/context.py +185 -0
  38. synapse_sdk/plugins/categories/upload/actions/upload/factory.py +143 -0
  39. synapse_sdk/plugins/categories/upload/actions/upload/models.py +66 -29
  40. synapse_sdk/plugins/categories/upload/actions/upload/orchestrator.py +182 -0
  41. synapse_sdk/plugins/categories/upload/actions/upload/registry.py +113 -0
  42. synapse_sdk/plugins/categories/upload/actions/upload/steps/__init__.py +1 -0
  43. synapse_sdk/plugins/categories/upload/actions/upload/steps/base.py +106 -0
  44. synapse_sdk/plugins/categories/upload/actions/upload/steps/cleanup.py +62 -0
  45. synapse_sdk/plugins/categories/upload/actions/upload/steps/collection.py +62 -0
  46. synapse_sdk/plugins/categories/upload/actions/upload/steps/generate.py +80 -0
  47. synapse_sdk/plugins/categories/upload/actions/upload/steps/initialize.py +66 -0
  48. synapse_sdk/plugins/categories/upload/actions/upload/steps/metadata.py +101 -0
  49. synapse_sdk/plugins/categories/upload/actions/upload/steps/organize.py +89 -0
  50. synapse_sdk/plugins/categories/upload/actions/upload/steps/upload.py +96 -0
  51. synapse_sdk/plugins/categories/upload/actions/upload/steps/validate.py +61 -0
  52. synapse_sdk/plugins/categories/upload/actions/upload/strategies/__init__.py +1 -0
  53. synapse_sdk/plugins/categories/upload/actions/upload/strategies/base.py +86 -0
  54. synapse_sdk/plugins/categories/upload/actions/upload/strategies/data_unit/__init__.py +1 -0
  55. synapse_sdk/plugins/categories/upload/actions/upload/strategies/data_unit/batch.py +39 -0
  56. synapse_sdk/plugins/categories/upload/actions/upload/strategies/data_unit/single.py +34 -0
  57. synapse_sdk/plugins/categories/upload/actions/upload/strategies/file_discovery/__init__.py +1 -0
  58. synapse_sdk/plugins/categories/upload/actions/upload/strategies/file_discovery/flat.py +233 -0
  59. synapse_sdk/plugins/categories/upload/actions/upload/strategies/file_discovery/recursive.py +253 -0
  60. synapse_sdk/plugins/categories/upload/actions/upload/strategies/metadata/__init__.py +1 -0
  61. synapse_sdk/plugins/categories/upload/actions/upload/strategies/metadata/excel.py +174 -0
  62. synapse_sdk/plugins/categories/upload/actions/upload/strategies/metadata/none.py +16 -0
  63. synapse_sdk/plugins/categories/upload/actions/upload/strategies/upload/__init__.py +1 -0
  64. synapse_sdk/plugins/categories/upload/actions/upload/strategies/upload/async_upload.py +109 -0
  65. synapse_sdk/plugins/categories/upload/actions/upload/strategies/upload/sync.py +43 -0
  66. synapse_sdk/plugins/categories/upload/actions/upload/strategies/validation/__init__.py +1 -0
  67. synapse_sdk/plugins/categories/upload/actions/upload/strategies/validation/default.py +45 -0
  68. synapse_sdk/plugins/categories/upload/actions/upload/utils.py +194 -83
  69. synapse_sdk/plugins/categories/upload/templates/config.yaml +4 -0
  70. synapse_sdk/plugins/categories/upload/templates/plugin/__init__.py +269 -0
  71. synapse_sdk/plugins/categories/upload/templates/plugin/upload.py +71 -27
  72. synapse_sdk/plugins/models.py +7 -0
  73. synapse_sdk/shared/__init__.py +21 -0
  74. {synapse_sdk-2025.9.1.dist-info → synapse_sdk-2025.9.4.dist-info}/METADATA +2 -1
  75. {synapse_sdk-2025.9.1.dist-info → synapse_sdk-2025.9.4.dist-info}/RECORD +79 -28
  76. synapse_sdk/plugins/categories/export/actions/export.py +0 -385
  77. synapse_sdk/plugins/categories/export/enums.py +0 -7
  78. {synapse_sdk-2025.9.1.dist-info → synapse_sdk-2025.9.4.dist-info}/WHEEL +0 -0
  79. {synapse_sdk-2025.9.1.dist-info → synapse_sdk-2025.9.4.dist-info}/entry_points.txt +0 -0
  80. {synapse_sdk-2025.9.1.dist-info → synapse_sdk-2025.9.4.dist-info}/licenses/LICENSE +0 -0
  81. {synapse_sdk-2025.9.1.dist-info → synapse_sdk-2025.9.4.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,417 @@
1
+ ---
2
+ id: core-mixin
3
+ title: CoreClientMixin
4
+ sidebar_position: 12
5
+ ---
6
+
7
+ # CoreClientMixin
8
+
9
+ Synapse 백엔드를 위한 핵심 파일 업로드 및 기본 작업을 제공합니다.
10
+
11
+ ## 개요
12
+
13
+ `CoreClientMixin`은 핵심 시스템 작업, 특히 대용량 파일을 위한 청크 업로드를 포함한 파일 업로드 기능을 처리합니다. 이 믹스인은 `BackendClient`에 자동으로 포함되며 다른 믹스인에서 사용하는 필수 기능을 제공합니다.
14
+
15
+ ## 파일 업로드 작업
16
+
17
+ ### `create_chunked_upload(file_path)`
18
+
19
+ 최적의 성능과 안정성을 위해 청크 업로드를 사용하여 대용량 파일을 업로드합니다.
20
+
21
+ ```python
22
+ from pathlib import Path
23
+
24
+ # 대용량 파일 업로드
25
+ file_path = Path('/path/to/large_dataset.zip')
26
+ result = client.create_chunked_upload(file_path)
27
+ print(f"업로드 완료: {result}")
28
+ print(f"파일 ID: {result['id']}")
29
+ ```
30
+
31
+ **매개변수:**
32
+
33
+ - `file_path` (str | Path): 업로드할 파일의 경로
34
+
35
+ **반환값:**
36
+
37
+ - `dict`: 파일 ID와 메타데이터가 포함된 업로드 결과
38
+
39
+ **기능:**
40
+
41
+ - **50MB 청크**: 성능을 위한 최적 청크 크기 사용
42
+ - **MD5 무결성**: 자동 체크섬 검증
43
+ - **재개 기능**: 중단된 업로드 재개 가능
44
+ - **진행률 추적**: 업로드 진행률 모니터링 지원
45
+ - **오류 복구**: 실패한 청크에 대한 자동 재시도
46
+
47
+ ### 업로드 프로세스 세부사항
48
+
49
+ 청크 업로드 프로세스는 다음과 같이 작동합니다:
50
+
51
+ 1. **파일 분석**: 파일 크기 및 MD5 해시 계산
52
+ 2. **청크 생성**: 파일을 50MB 청크로 분할
53
+ 3. **순차 업로드**: 청크를 하나씩 업로드
54
+ 4. **무결성 검사**: MD5로 각 청크 검증
55
+ 5. **조립**: 서버에서 청크를 최종 파일로 조립
56
+ 6. **검증**: 완성된 파일의 최종 무결성 검사
57
+
58
+ ```python
59
+ import hashlib
60
+ import os
61
+ from pathlib import Path
62
+
63
+ def upload_with_progress(file_path):
64
+ """자세한 진행률 추적과 함께 파일 업로드."""
65
+
66
+ file_path = Path(file_path)
67
+
68
+ # 파일 정보 가져오기
69
+ file_size = os.path.getsize(file_path)
70
+ print(f"업로드 중인 파일: {file_path.name}")
71
+ print(f"파일 크기: {file_size / (1024*1024):.2f} MB")
72
+
73
+ # MD5 계산 (클라이언트에서 자동으로 수행됨)
74
+ hash_md5 = hashlib.md5()
75
+ with open(file_path, "rb") as f:
76
+ for chunk in iter(lambda: f.read(4096), b""):
77
+ hash_md5.update(chunk)
78
+
79
+ print(f"MD5 체크섬: {hash_md5.hexdigest()}")
80
+
81
+ # 청크 업로드로 업로드
82
+ try:
83
+ result = client.create_chunked_upload(file_path)
84
+ print("업로드 성공!")
85
+ return result
86
+ except Exception as e:
87
+ print(f"업로드 실패: {e}")
88
+ raise
89
+
90
+ # 사용법
91
+ upload_result = upload_with_progress('/path/to/large_file.zip')
92
+ ```
93
+
94
+ ## 고급 업로드 시나리오
95
+
96
+ ### 일괄 파일 업로드
97
+
98
+ ```python
99
+ def batch_chunked_upload(file_paths, max_concurrent=3):
100
+ """동시성 제어와 함께 여러 대용량 파일 업로드."""
101
+ import concurrent.futures
102
+ import threading
103
+
104
+ upload_results = []
105
+ failed_uploads = []
106
+
107
+ def upload_single_file(file_path):
108
+ try:
109
+ print(f"업로드 시작: {file_path}")
110
+ result = client.create_chunked_upload(file_path)
111
+ print(f"업로드 완료: {file_path}")
112
+ return {'file_path': file_path, 'result': result, 'status': 'success'}
113
+ except Exception as e:
114
+ print(f"업로드 실패: {file_path} - {e}")
115
+ return {'file_path': file_path, 'error': str(e), 'status': 'failed'}
116
+
117
+ # 동시 업로드를 위해 ThreadPoolExecutor 사용
118
+ with concurrent.futures.ThreadPoolExecutor(max_workers=max_concurrent) as executor:
119
+ future_to_file = {
120
+ executor.submit(upload_single_file, file_path): file_path
121
+ for file_path in file_paths
122
+ }
123
+
124
+ for future in concurrent.futures.as_completed(future_to_file):
125
+ result = future.result()
126
+
127
+ if result['status'] == 'success':
128
+ upload_results.append(result)
129
+ else:
130
+ failed_uploads.append(result)
131
+
132
+ return {
133
+ 'successful': upload_results,
134
+ 'failed': failed_uploads,
135
+ 'total': len(file_paths)
136
+ }
137
+
138
+ # 여러 파일 업로드
139
+ file_list = [
140
+ Path('/data/file1.zip'),
141
+ Path('/data/file2.zip'),
142
+ Path('/data/file3.zip')
143
+ ]
144
+
145
+ batch_results = batch_chunked_upload(file_list, max_concurrent=2)
146
+ print(f"성공한 업로드: {len(batch_results['successful'])}")
147
+ print(f"실패한 업로드: {len(batch_results['failed'])}")
148
+ ```
149
+
150
+ ### 재시도 로직이 있는 업로드
151
+
152
+ ```python
153
+ import time
154
+ from synapse_sdk.clients.exceptions import ClientError
155
+
156
+ def robust_chunked_upload(file_path, max_retries=3, retry_delay=5):
157
+ """향상된 안정성을 위한 재시도 로직이 있는 업로드."""
158
+
159
+ for attempt in range(max_retries):
160
+ try:
161
+ result = client.create_chunked_upload(file_path)
162
+ print(f"{attempt + 1}번째 시도에서 업로드 성공")
163
+ return result
164
+
165
+ except ClientError as e:
166
+ if e.status_code == 413: # 파일이 너무 큼
167
+ print(f"파일 {file_path}이 업로드하기에 너무 큽니다")
168
+ raise
169
+ elif e.status_code == 507: # 저장 공간 부족
170
+ print("서버 저장 공간이 가득 참")
171
+ raise
172
+ elif e.status_code >= 500: # 서버 오류
173
+ if attempt < max_retries - 1:
174
+ print(f"{attempt + 1}번째 시도에서 서버 오류, {retry_delay}초 후 재시도...")
175
+ time.sleep(retry_delay)
176
+ retry_delay *= 2 # 지수 백오프
177
+ else:
178
+ print(f"{max_retries}번의 시도 후 업로드 실패")
179
+ raise
180
+ else:
181
+ print(f"오류로 업로드 실패: {e}")
182
+ raise
183
+
184
+ except OSError as e:
185
+ print(f"파일 시스템 오류: {e}")
186
+ raise
187
+
188
+ except Exception as e:
189
+ if attempt < max_retries - 1:
190
+ print(f"{attempt + 1}번째 시도에서 예상치 못한 오류: {e}")
191
+ time.sleep(retry_delay)
192
+ else:
193
+ print(f"{max_retries}번의 시도 후 오류로 업로드 실패: {e}")
194
+ raise
195
+
196
+ # 안정적인 업로드 사용
197
+ try:
198
+ result = robust_chunked_upload('/path/to/file.zip')
199
+ print(f"파일이 성공적으로 업로드됨: {result['id']}")
200
+ except Exception as e:
201
+ print(f"최종 업로드 실패: {e}")
202
+ ```
203
+
204
+ ### 업로드 진행률 모니터링
205
+
206
+ ```python
207
+ import os
208
+ from tqdm import tqdm
209
+
210
+ class ProgressTracker:
211
+ """파일 업로드를 위한 간단한 진행률 추적기."""
212
+
213
+ def __init__(self, total_size):
214
+ self.total_size = total_size
215
+ self.uploaded_size = 0
216
+ self.progress_bar = tqdm(total=total_size, unit='B', unit_scale=True, desc="업로드 중")
217
+
218
+ def update(self, chunk_size):
219
+ self.uploaded_size += chunk_size
220
+ self.progress_bar.update(chunk_size)
221
+
222
+ if self.uploaded_size >= self.total_size:
223
+ self.progress_bar.close()
224
+
225
+ def get_progress_percentage(self):
226
+ return (self.uploaded_size / self.total_size) * 100 if self.total_size > 0 else 0
227
+
228
+ def upload_with_progress_bar(file_path):
229
+ """시각적 진행률 바와 함께 파일 업로드."""
230
+
231
+ file_path = Path(file_path)
232
+ file_size = os.path.getsize(file_path)
233
+
234
+ # 진행률 추적기 생성
235
+ tracker = ProgressTracker(file_size)
236
+
237
+ try:
238
+ # 참고: 실제 청크 업로드는 청크 수준 진행률을 노출하지 않음
239
+ # 이것은 진행률을 추적할 수 있는 방법에 대한 개념적 예제
240
+ print(f"{file_path.name} 업로드 시작 ({file_size / (1024*1024):.2f} MB)")
241
+
242
+ result = client.create_chunked_upload(file_path)
243
+
244
+ # 진행률 완료 시뮬레이션
245
+ tracker.update(file_size)
246
+ print(f"업로드 완료: {result['id']}")
247
+
248
+ return result
249
+
250
+ except Exception as e:
251
+ tracker.progress_bar.close()
252
+ print(f"업로드 실패: {e}")
253
+ raise
254
+
255
+ # 사용법
256
+ upload_result = upload_with_progress_bar('/path/to/large_file.zip')
257
+ ```
258
+
259
+ ## 파일 검증
260
+
261
+ ### 업로드 전 검증
262
+
263
+ ```python
264
+ def validate_file_for_upload(file_path, max_size_gb=10):
265
+ """업로드를 시도하기 전에 파일을 검증."""
266
+
267
+ file_path = Path(file_path)
268
+
269
+ # 파일 존재 확인
270
+ if not file_path.exists():
271
+ raise FileNotFoundError(f"파일을 찾을 수 없음: {file_path}")
272
+
273
+ # 파일인지 확인 (디렉토리가 아닌)
274
+ if not file_path.is_file():
275
+ raise ValueError(f"경로가 파일이 아님: {file_path}")
276
+
277
+ # 파일 크기 확인
278
+ file_size = os.path.getsize(file_path)
279
+ max_size_bytes = max_size_gb * 1024 * 1024 * 1024
280
+
281
+ if file_size > max_size_bytes:
282
+ raise ValueError(f"파일이 너무 큼: {file_size / (1024**3):.2f} GB (최대: {max_size_gb} GB)")
283
+
284
+ # 파일 권한 확인
285
+ if not os.access(file_path, os.R_OK):
286
+ raise PermissionError(f"파일을 읽을 수 없음: {file_path}")
287
+
288
+ # 기본 파일 무결성 검사
289
+ try:
290
+ with open(file_path, 'rb') as f:
291
+ f.read(1024) # 첫 1KB 읽기 시도
292
+ except Exception as e:
293
+ raise ValueError(f"파일이 손상된 것으로 보임: {e}")
294
+
295
+ return {
296
+ 'valid': True,
297
+ 'file_size': file_size,
298
+ 'file_path': str(file_path)
299
+ }
300
+
301
+ def safe_chunked_upload(file_path):
302
+ """사전 검증과 함께 업로드."""
303
+
304
+ try:
305
+ # 먼저 파일 검증
306
+ validation = validate_file_for_upload(file_path)
307
+ print(f"파일 검증 통과: {validation['file_size'] / (1024*1024):.2f} MB")
308
+
309
+ # 업로드 진행
310
+ result = client.create_chunked_upload(file_path)
311
+ print(f"업로드 성공: {result['id']}")
312
+
313
+ return result
314
+
315
+ except (FileNotFoundError, ValueError, PermissionError) as e:
316
+ print(f"검증 실패: {e}")
317
+ return None
318
+ except Exception as e:
319
+ print(f"업로드 실패: {e}")
320
+ return None
321
+
322
+ # 사용법
323
+ upload_result = safe_chunked_upload('/path/to/file.zip')
324
+ ```
325
+
326
+ ## 성능 최적화
327
+
328
+ ### 최적화된 업로드 전략
329
+
330
+ ```python
331
+ def optimized_upload_strategy(file_path):
332
+ """파일 특성에 따른 최적 업로드 전략 선택."""
333
+
334
+ file_path = Path(file_path)
335
+ file_size = os.path.getsize(file_path)
336
+
337
+ # 임계값 (바이트 단위)
338
+ SMALL_FILE_THRESHOLD = 10 * 1024 * 1024 # 10MB
339
+ LARGE_FILE_THRESHOLD = 100 * 1024 * 1024 # 100MB
340
+
341
+ if file_size < SMALL_FILE_THRESHOLD:
342
+ print(f"작은 파일 ({file_size / (1024*1024):.2f} MB) - 일반 업로드 사용")
343
+ # 작은 파일의 경우 다른 업로드 방법을 사용할 수 있음
344
+ # CoreClientMixin은 청크 업로드만 제공하므로 이것은 개념적임
345
+ return client.create_chunked_upload(file_path)
346
+
347
+ elif file_size < LARGE_FILE_THRESHOLD:
348
+ print(f"중간 파일 ({file_size / (1024*1024):.2f} MB) - 청크 업로드 사용")
349
+ return client.create_chunked_upload(file_path)
350
+
351
+ else:
352
+ print(f"대용량 파일 ({file_size / (1024*1024):.2f} MB) - 최적화된 청크 업로드 사용")
353
+ # 매우 큰 파일의 경우 추가 최적화를 원할 수 있음
354
+ return robust_chunked_upload(file_path, max_retries=5)
355
+
356
+ # 사용법
357
+ result = optimized_upload_strategy('/path/to/any_size_file.zip')
358
+ ```
359
+
360
+ ## 오류 처리
361
+
362
+ ```python
363
+ from synapse_sdk.clients.exceptions import ClientError
364
+
365
+ def handle_upload_errors():
366
+ """업로드를 위한 포괄적인 오류 처리."""
367
+
368
+ try:
369
+ result = client.create_chunked_upload('/path/to/file.zip')
370
+ return result
371
+
372
+ except FileNotFoundError:
373
+ print("오류: 파일을 찾을 수 없음")
374
+ return None
375
+
376
+ except PermissionError:
377
+ print("오류: 권한이 거부됨 - 파일 권한을 확인하세요")
378
+ return None
379
+
380
+ except ClientError as e:
381
+ if e.status_code == 413:
382
+ print("오류: 업로드하기에 파일이 너무 큼")
383
+ elif e.status_code == 507:
384
+ print("오류: 서버 저장 공간이 가득 �참")
385
+ elif e.status_code == 429:
386
+ print("오류: 요청 제한 - 너무 많은 요청")
387
+ elif e.status_code >= 500:
388
+ print(f"오류: 서버 오류 ({e.status_code})")
389
+ else:
390
+ print(f"오류: 클라이언트 오류 ({e.status_code}): {e}")
391
+ return None
392
+
393
+ except OSError as e:
394
+ print(f"오류: 운영 체제 오류: {e}")
395
+ return None
396
+
397
+ except MemoryError:
398
+ print("오류: 업로드를 위한 메모리 부족")
399
+ return None
400
+
401
+ except Exception as e:
402
+ print(f"오류: 예상치 못한 오류: {e}")
403
+ return None
404
+
405
+ # 오류 처리 사용
406
+ upload_result = handle_upload_errors()
407
+ if upload_result:
408
+ print(f"업로드 성공: {upload_result['id']}")
409
+ else:
410
+ print("업로드 실패")
411
+ ```
412
+
413
+ ## 참고
414
+
415
+ - [BackendClient](./backend.md) - 메인 백엔드 클라이언트
416
+ - [DataCollectionClientMixin](./data-collection-mixin.md) - 청크 업로드를 사용하는 데이터 수집 작업
417
+ - [MLClientMixin](./ml-mixin.md) - 청크 업로드를 사용하는 ML 모델 업로드