sonamu 0.7.53 → 0.8.0
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.
- package/dist/api/config.d.ts +9 -1
- package/dist/api/config.d.ts.map +1 -1
- package/dist/api/config.js +1 -1
- package/dist/api/sonamu.d.ts +21 -1
- package/dist/api/sonamu.d.ts.map +1 -1
- package/dist/api/sonamu.js +159 -65
- package/dist/auth/plugins/entity-definitions/anonymous.d.ts +10 -0
- package/dist/auth/plugins/entity-definitions/anonymous.d.ts.map +1 -0
- package/dist/auth/plugins/entity-definitions/anonymous.js +23 -0
- package/dist/auth/plugins/entity-definitions/api-key.d.ts +9 -0
- package/dist/auth/plugins/entity-definitions/api-key.d.ts.map +1 -0
- package/dist/auth/plugins/entity-definitions/api-key.js +199 -0
- package/dist/auth/plugins/entity-definitions/index.d.ts +6 -0
- package/dist/auth/plugins/entity-definitions/index.d.ts.map +1 -1
- package/dist/auth/plugins/entity-definitions/index.js +20 -2
- package/dist/auth/plugins/entity-definitions/jwt.d.ts +9 -0
- package/dist/auth/plugins/entity-definitions/jwt.d.ts.map +1 -0
- package/dist/auth/plugins/entity-definitions/jwt.js +67 -0
- package/dist/auth/plugins/entity-definitions/organization.d.ts +9 -0
- package/dist/auth/plugins/entity-definitions/organization.d.ts.map +1 -0
- package/dist/auth/plugins/entity-definitions/organization.js +424 -0
- package/dist/auth/plugins/entity-definitions/passkey.d.ts +10 -0
- package/dist/auth/plugins/entity-definitions/passkey.d.ts.map +1 -0
- package/dist/auth/plugins/entity-definitions/passkey.js +129 -0
- package/dist/auth/plugins/entity-definitions/sso.d.ts +10 -0
- package/dist/auth/plugins/entity-definitions/sso.d.ts.map +1 -0
- package/dist/auth/plugins/entity-definitions/sso.js +110 -0
- package/dist/auth/plugins/entity-definitions/types.d.ts +1 -1
- package/dist/auth/plugins/entity-definitions/types.d.ts.map +1 -1
- package/dist/auth/plugins/entity-definitions/types.js +1 -1
- package/dist/auth/plugins/wrappers/admin.d.ts.map +1 -1
- package/dist/auth/plugins/wrappers/admin.js +2 -4
- package/dist/auth/plugins/wrappers/anonymous.d.ts +18 -0
- package/dist/auth/plugins/wrappers/anonymous.d.ts.map +1 -0
- package/dist/auth/plugins/wrappers/anonymous.js +26 -0
- package/dist/auth/plugins/wrappers/api-key.d.ts +18 -0
- package/dist/auth/plugins/wrappers/api-key.d.ts.map +1 -0
- package/dist/auth/plugins/wrappers/api-key.js +38 -0
- package/dist/auth/plugins/wrappers/index.d.ts +6 -0
- package/dist/auth/plugins/wrappers/index.d.ts.map +1 -1
- package/dist/auth/plugins/wrappers/index.js +7 -1
- package/dist/auth/plugins/wrappers/jwt.d.ts +18 -0
- package/dist/auth/plugins/wrappers/jwt.d.ts.map +1 -0
- package/dist/auth/plugins/wrappers/jwt.js +30 -0
- package/dist/auth/plugins/wrappers/organization.d.ts +18 -0
- package/dist/auth/plugins/wrappers/organization.d.ts.map +1 -0
- package/dist/auth/plugins/wrappers/organization.js +67 -0
- package/dist/auth/plugins/wrappers/passkey.d.ts +18 -0
- package/dist/auth/plugins/wrappers/passkey.d.ts.map +1 -0
- package/dist/auth/plugins/wrappers/passkey.js +32 -0
- package/dist/auth/plugins/wrappers/phone-number.d.ts.map +1 -1
- package/dist/auth/plugins/wrappers/phone-number.js +2 -4
- package/dist/auth/plugins/wrappers/sso.d.ts +853 -0
- package/dist/auth/plugins/wrappers/sso.d.ts.map +1 -0
- package/dist/auth/plugins/wrappers/sso.js +36 -0
- package/dist/auth/plugins/wrappers/two-factor.d.ts.map +1 -1
- package/dist/auth/plugins/wrappers/two-factor.js +2 -4
- package/dist/auth/plugins/wrappers/username.d.ts.map +1 -1
- package/dist/auth/plugins/wrappers/username.js +2 -4
- package/dist/bin/build-config.d.ts +2 -2
- package/dist/bin/build-config.js +6 -7
- package/dist/bin/cli.js +417 -32
- package/dist/bin/fixture.d.ts +27 -0
- package/dist/bin/fixture.d.ts.map +1 -0
- package/dist/bin/fixture.js +245 -0
- package/dist/cache/decorator.d.ts +4 -3
- package/dist/cache/decorator.d.ts.map +1 -1
- package/dist/cache/decorator.js +5 -4
- package/dist/cone/cone-generator.d.ts +33 -0
- package/dist/cone/cone-generator.d.ts.map +1 -0
- package/dist/cone/cone-generator.js +286 -0
- package/dist/database/_batch_update.d.ts.map +1 -1
- package/dist/database/_batch_update.js +16 -2
- package/dist/database/puri-subset.test-d.js +1 -1
- package/dist/database/puri-subset.types.d.ts +1 -1
- package/dist/database/puri-subset.types.d.ts.map +1 -1
- package/dist/database/puri-subset.types.js +1 -1
- package/dist/database/puri.d.ts +4 -0
- package/dist/database/puri.d.ts.map +1 -1
- package/dist/database/puri.js +20 -2
- package/dist/database/upsert-builder.d.ts.map +1 -1
- package/dist/database/upsert-builder.js +19 -3
- package/dist/dict/en.d.ts +15 -0
- package/dist/dict/en.d.ts.map +1 -1
- package/dist/dict/en.js +2 -1
- package/dist/dict/ko.d.ts +15 -0
- package/dist/dict/ko.d.ts.map +1 -1
- package/dist/dict/ko.js +2 -1
- package/dist/dict/rc-keys.d.ts +28 -0
- package/dist/dict/rc-keys.d.ts.map +1 -1
- package/dist/dict/rc-keys.js +31 -1
- package/dist/dict/sd.d.ts.map +1 -1
- package/dist/dict/sd.js +20 -4
- package/dist/entity/entity-manager.d.ts +298 -2
- package/dist/entity/entity-manager.d.ts.map +1 -1
- package/dist/entity/entity-manager.js +4 -1
- package/dist/entity/entity-template-cone.d.ts +14 -0
- package/dist/entity/entity-template-cone.d.ts.map +1 -0
- package/dist/entity/entity-template-cone.js +222 -0
- package/dist/entity/entity.d.ts +47 -2
- package/dist/entity/entity.d.ts.map +1 -1
- package/dist/entity/entity.js +161 -14
- package/dist/ssr/renderer.js +3 -3
- package/dist/syncer/api-parser.js +12 -1
- package/dist/syncer/checksum.d.ts +0 -14
- package/dist/syncer/checksum.d.ts.map +1 -1
- package/dist/syncer/checksum.js +1 -23
- package/dist/syncer/syncer-actions.d.ts.map +1 -1
- package/dist/syncer/syncer-actions.js +8 -2
- package/dist/syncer/syncer.d.ts +1 -1
- package/dist/syncer/syncer.d.ts.map +1 -1
- package/dist/syncer/syncer.js +17 -10
- package/dist/tasks/workflow-manager.d.ts +13 -1
- package/dist/tasks/workflow-manager.d.ts.map +1 -1
- package/dist/tasks/workflow-manager.js +18 -1
- package/dist/template/entity-converter.js +4 -4
- package/dist/template/helpers.d.ts +10 -0
- package/dist/template/helpers.d.ts.map +1 -1
- package/dist/template/helpers.js +48 -1
- package/dist/template/implementations/entry-server.template.d.ts +1 -1
- package/dist/template/implementations/entry-server.template.js +7 -2
- package/dist/template/implementations/generated.template.d.ts.map +1 -1
- package/dist/template/implementations/generated.template.js +5 -1
- package/dist/template/implementations/generated_http.template.d.ts +1 -0
- package/dist/template/implementations/generated_http.template.d.ts.map +1 -1
- package/dist/template/implementations/generated_http.template.js +6 -2
- package/dist/template/implementations/generated_sso.template.d.ts.map +1 -1
- package/dist/template/implementations/generated_sso.template.js +29 -8
- package/dist/template/implementations/queries.template.d.ts.map +1 -1
- package/dist/template/implementations/queries.template.js +9 -1
- package/dist/template/implementations/sd.template.d.ts +1 -1
- package/dist/template/implementations/sd.template.d.ts.map +1 -1
- package/dist/template/implementations/sd.template.js +28 -4
- package/dist/template/implementations/services.template.d.ts.map +1 -1
- package/dist/template/implementations/services.template.js +12 -12
- package/dist/template/implementations/view_form.template.d.ts +11 -7
- package/dist/template/implementations/view_form.template.d.ts.map +1 -1
- package/dist/template/implementations/view_form.template.js +97 -87
- package/dist/template/implementations/view_list.template.d.ts +3 -3
- package/dist/template/implementations/view_list.template.d.ts.map +1 -1
- package/dist/template/implementations/view_list.template.js +115 -109
- package/dist/template/implementations/view_search_input.template.d.ts.map +1 -1
- package/dist/template/implementations/view_search_input.template.js +18 -14
- package/dist/template/zod-converter.d.ts.map +1 -1
- package/dist/template/zod-converter.js +95 -7
- package/dist/testing/_relation-graph.js +1 -1
- package/dist/testing/data-explorer.d.ts +61 -0
- package/dist/testing/data-explorer.d.ts.map +1 -0
- package/dist/testing/data-explorer.js +274 -0
- package/dist/testing/faker-mappings.d.ts +20 -0
- package/dist/testing/faker-mappings.d.ts.map +1 -0
- package/dist/testing/faker-mappings.js +421 -0
- package/dist/testing/fixture-generator.d.ts +161 -0
- package/dist/testing/fixture-generator.d.ts.map +1 -0
- package/dist/testing/fixture-generator.js +954 -0
- package/dist/testing/fixture-manager.d.ts +6 -1
- package/dist/testing/fixture-manager.d.ts.map +1 -1
- package/dist/testing/fixture-manager.js +72 -4
- package/dist/testing/index.d.ts +3 -0
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +4 -1
- package/dist/types/types.d.ts +1520 -26
- package/dist/types/types.d.ts.map +1 -1
- package/dist/types/types.js +136 -22
- package/dist/ui/ai-client.d.ts.map +1 -1
- package/dist/ui/ai-client.js +9 -4
- package/dist/ui/api.d.ts.map +1 -1
- package/dist/ui/api.js +303 -24
- package/dist/ui-web/assets/index-CsUr-_pV.js +254 -0
- package/dist/ui-web/assets/index-T42zzs1K.css +1 -0
- package/dist/ui-web/index.html +2 -2
- package/dist/utils/fs-utils.d.ts +2 -1
- package/dist/utils/fs-utils.d.ts.map +1 -1
- package/dist/utils/fs-utils.js +14 -3
- package/package.json +19 -11
- package/src/api/config.ts +12 -1
- package/src/api/sonamu.ts +179 -65
- package/src/auth/plugins/entity-definitions/anonymous.ts +17 -0
- package/src/auth/plugins/entity-definitions/api-key.ts +93 -0
- package/src/auth/plugins/entity-definitions/index.ts +18 -0
- package/src/auth/plugins/entity-definitions/jwt.ts +35 -0
- package/src/auth/plugins/entity-definitions/organization.ts +215 -0
- package/src/auth/plugins/entity-definitions/passkey.ts +64 -0
- package/src/auth/plugins/entity-definitions/sso.ts +62 -0
- package/src/auth/plugins/entity-definitions/types.ts +11 -1
- package/src/auth/plugins/wrappers/admin.ts +1 -3
- package/src/auth/plugins/wrappers/anonymous.ts +30 -0
- package/src/auth/plugins/wrappers/api-key.ts +42 -0
- package/src/auth/plugins/wrappers/index.ts +6 -0
- package/src/auth/plugins/wrappers/jwt.ts +34 -0
- package/src/auth/plugins/wrappers/organization.ts +73 -0
- package/src/auth/plugins/wrappers/passkey.ts +36 -0
- package/src/auth/plugins/wrappers/phone-number.ts +1 -3
- package/src/auth/plugins/wrappers/sso.ts +40 -0
- package/src/auth/plugins/wrappers/two-factor.ts +1 -3
- package/src/auth/plugins/wrappers/username.ts +1 -3
- package/src/bin/build-config.ts +6 -6
- package/src/bin/cli.ts +452 -31
- package/src/bin/fixture.ts +302 -0
- package/src/cache/decorator.ts +4 -3
- package/src/cone/cone-generator.ts +363 -0
- package/src/database/_batch_update.ts +11 -0
- package/src/database/puri-subset.test-d.ts +13 -13
- package/src/database/puri-subset.types.ts +1 -1
- package/src/database/puri.ts +43 -1
- package/src/database/upsert-builder.ts +16 -2
- package/src/dict/en.ts +1 -0
- package/src/dict/ko.ts +1 -0
- package/src/dict/rc-keys.ts +32 -0
- package/src/dict/sd.ts +23 -3
- package/src/entity/entity-manager.ts +4 -0
- package/src/entity/entity-template-cone.ts +298 -0
- package/src/entity/entity.ts +189 -13
- package/src/shared/app.shared.ts.txt +5 -0
- package/src/shared/web.shared.ts.txt +9 -5
- package/src/skills/project/README.md +21 -0
- package/src/skills/project/architecture.md +373 -0
- package/src/skills/project/business-logic.md +270 -0
- package/src/skills/project/requirements.md +160 -0
- package/src/skills/sonamu/SKILL.md +168 -3
- package/src/skills/sonamu/api.md +102 -0
- package/src/skills/sonamu/database.md +220 -1
- package/src/skills/sonamu/entity-relations.md +89 -1
- package/src/skills/sonamu/fixture-cli.md +501 -0
- package/src/skills/sonamu/frontend.md +214 -0
- package/src/skills/sonamu/i18n.md +95 -0
- package/src/skills/sonamu/model.md +153 -0
- package/src/skills/sonamu/project-init.md +178 -8
- package/src/skills/sonamu/scaffolding.md +112 -0
- package/src/skills/sonamu/subset.md +9 -3
- package/src/skills/sonamu/testing.md +287 -2
- package/src/skills/sonamu/workflow.md +70 -5
- package/src/ssr/renderer.ts +2 -2
- package/src/syncer/api-parser.ts +12 -0
- package/src/syncer/checksum.ts +0 -38
- package/src/syncer/syncer-actions.ts +7 -1
- package/src/syncer/syncer.ts +16 -5
- package/src/tasks/workflow-manager.ts +29 -8
- package/src/template/entity-converter.ts +3 -3
- package/src/template/helpers.ts +49 -0
- package/src/template/implementations/entry-server.template.ts +1 -1
- package/src/template/implementations/generated.template.ts +4 -0
- package/src/template/implementations/generated_http.template.ts +1 -0
- package/src/template/implementations/generated_sso.template.ts +40 -11
- package/src/template/implementations/queries.template.ts +8 -0
- package/src/template/implementations/sd.template.ts +22 -3
- package/src/template/implementations/services.template.ts +11 -10
- package/src/template/implementations/view_form.template.ts +111 -101
- package/src/template/implementations/view_list.template.ts +120 -119
- package/src/template/implementations/view_search_input.template.ts +17 -13
- package/src/template/zod-converter.ts +103 -6
- package/src/testing/_relation-graph.ts +1 -1
- package/src/testing/data-explorer.ts +427 -0
- package/src/testing/faker-mappings.ts +434 -0
- package/src/testing/fixture-generator.ts +1166 -0
- package/src/testing/fixture-manager.ts +91 -6
- package/src/testing/index.ts +3 -0
- package/src/types/types.ts +222 -26
- package/src/ui/ai-client.ts +9 -1
- package/src/ui/api.ts +429 -23
- package/src/utils/fs-utils.ts +14 -1
- package/dist/template/implementations/view_enums_select.template.d.ts +0 -17
- package/dist/template/implementations/view_enums_select.template.d.ts.map +0 -1
- package/dist/template/implementations/view_enums_select.template.js +0 -62
- package/dist/template/implementations/view_id_async_select.template.d.ts +0 -17
- package/dist/template/implementations/view_id_async_select.template.d.ts.map +0 -1
- package/dist/template/implementations/view_id_async_select.template.js +0 -125
- package/dist/ui-web/assets/index-Bd_2AkLb.css +0 -1
- package/dist/ui-web/assets/index-BpSbhQWo.js +0 -225
- package/src/template/implementations/view_enums_select.template.ts +0 -65
- package/src/template/implementations/view_id_async_select.template.ts +0 -139
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
# 아키텍처 설계
|
|
2
|
+
|
|
3
|
+
> 이 문서는 시스템의 전체 구조와 설계 결정을 기록합니다.
|
|
4
|
+
> 개발 과정에서 결정된 아키텍처 사항들을 업데이트합니다.
|
|
5
|
+
|
|
6
|
+
## 시스템 개요
|
|
7
|
+
|
|
8
|
+
### 기술 스택
|
|
9
|
+
|
|
10
|
+
- **Backend**: Sonamu + Node.js + TypeScript
|
|
11
|
+
- **Database**: PostgreSQL
|
|
12
|
+
- **Frontend**: React + TanStack Router + TanStack Query
|
|
13
|
+
- **Auth**: better-auth
|
|
14
|
+
- **Storage**: Local FS / S3 (선택)
|
|
15
|
+
- **Deployment**: (추후 결정)
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## 데이터베이스 설계
|
|
20
|
+
|
|
21
|
+
### 주요 테이블
|
|
22
|
+
|
|
23
|
+
<!--
|
|
24
|
+
예시:
|
|
25
|
+
|
|
26
|
+
**users**
|
|
27
|
+
- id (PK)
|
|
28
|
+
- email (unique)
|
|
29
|
+
- name
|
|
30
|
+
- role (enum: admin, user)
|
|
31
|
+
- created_at
|
|
32
|
+
|
|
33
|
+
**posts**
|
|
34
|
+
- id (PK)
|
|
35
|
+
- title
|
|
36
|
+
- content
|
|
37
|
+
- author_id (FK → users)
|
|
38
|
+
- status (enum: draft, published)
|
|
39
|
+
- created_at
|
|
40
|
+
- updated_at
|
|
41
|
+
-->
|
|
42
|
+
|
|
43
|
+
### 관계도
|
|
44
|
+
|
|
45
|
+
<!--
|
|
46
|
+
ERD 또는 텍스트로 표현
|
|
47
|
+
|
|
48
|
+
예시:
|
|
49
|
+
```
|
|
50
|
+
users 1---N posts
|
|
51
|
+
users 1---N comments
|
|
52
|
+
posts 1---N comments
|
|
53
|
+
users N---M roles (user__roles)
|
|
54
|
+
```
|
|
55
|
+
-->
|
|
56
|
+
|
|
57
|
+
### 인덱스 전략
|
|
58
|
+
|
|
59
|
+
<!--
|
|
60
|
+
예시:
|
|
61
|
+
- users.email: unique index
|
|
62
|
+
- posts.author_id: index (조회 성능)
|
|
63
|
+
- posts.created_at: index (정렬용)
|
|
64
|
+
- posts(status, created_at): composite index (필터+정렬)
|
|
65
|
+
-->
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## API 설계
|
|
70
|
+
|
|
71
|
+
### 엔드포인트 구조
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
/api/{model}/{method}
|
|
75
|
+
|
|
76
|
+
예시:
|
|
77
|
+
GET /api/user/findMany
|
|
78
|
+
GET /api/user/findById?id=1
|
|
79
|
+
POST /api/user/save
|
|
80
|
+
POST /api/user/del
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### 인증/권한
|
|
84
|
+
|
|
85
|
+
<!--
|
|
86
|
+
예시:
|
|
87
|
+
- Public: 공개 API (로그인 불필요)
|
|
88
|
+
- User: 로그인 필요
|
|
89
|
+
- Admin: 관리자 권한 필요
|
|
90
|
+
- Owner: 본인 데이터만 접근 가능
|
|
91
|
+
-->
|
|
92
|
+
|
|
93
|
+
### 에러 응답
|
|
94
|
+
|
|
95
|
+
```json
|
|
96
|
+
{
|
|
97
|
+
"code": 400,
|
|
98
|
+
"message": "잘못된 요청입니다",
|
|
99
|
+
"issues": [
|
|
100
|
+
{
|
|
101
|
+
"path": ["email"],
|
|
102
|
+
"message": "이메일 형식이 올바르지 않습니다"
|
|
103
|
+
}
|
|
104
|
+
]
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## 프론트엔드 구조
|
|
111
|
+
|
|
112
|
+
### 라우팅
|
|
113
|
+
|
|
114
|
+
<!--
|
|
115
|
+
예시:
|
|
116
|
+
```
|
|
117
|
+
/ # 홈
|
|
118
|
+
/login # 로그인
|
|
119
|
+
/admin # 관리자 대시보드
|
|
120
|
+
/admin/users # 사용자 목록
|
|
121
|
+
/admin/users/:id # 사용자 상세/편집
|
|
122
|
+
/admin/posts # 게시글 관리
|
|
123
|
+
```
|
|
124
|
+
-->
|
|
125
|
+
|
|
126
|
+
### 상태 관리
|
|
127
|
+
|
|
128
|
+
- **서버 상태**: TanStack Query (자동 캐싱, 리페칭)
|
|
129
|
+
- **폼 상태**: useTypeForm (Zod 검증)
|
|
130
|
+
- **전역 상태**: Context API (최소화)
|
|
131
|
+
|
|
132
|
+
### 컴포넌트 구조
|
|
133
|
+
|
|
134
|
+
```
|
|
135
|
+
src/
|
|
136
|
+
├── routes/ # 페이지 컴포넌트
|
|
137
|
+
├── components/ # 재사용 컴포넌트
|
|
138
|
+
├── services/ # API Service (자동생성)
|
|
139
|
+
├── contexts/ # Context Providers
|
|
140
|
+
└── lib/ # 유틸리티
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## 보안 설계
|
|
146
|
+
|
|
147
|
+
### 인증
|
|
148
|
+
|
|
149
|
+
- **세션 기반**: better-auth 사용
|
|
150
|
+
- **쿠키**: httpOnly, secure, sameSite
|
|
151
|
+
- **만료 시간**: 24시간 (연장 가능)
|
|
152
|
+
|
|
153
|
+
### 권한 관리
|
|
154
|
+
|
|
155
|
+
<!--
|
|
156
|
+
예시 (연구과제):
|
|
157
|
+
- Role 기반: admin, evaluator, applicant
|
|
158
|
+
- Guard를 통한 API 접근 제어
|
|
159
|
+
- 프론트엔드 라우트 보호
|
|
160
|
+
|
|
161
|
+
예시 (의료 실험):
|
|
162
|
+
- Role 기반: principal_investigator, researcher, monitor, subject
|
|
163
|
+
- 데이터 접근 레벨: full, anonymized, restricted
|
|
164
|
+
- IRB 승인 상태에 따른 기능 제한
|
|
165
|
+
|
|
166
|
+
예시 (커머스):
|
|
167
|
+
- Role 기반: admin, seller, customer
|
|
168
|
+
- 리소스 소유권 검증 (본인 주문/상품만 접근)
|
|
169
|
+
- IP 기반 관리자 접근 제한 (선택)
|
|
170
|
+
|
|
171
|
+
예시 (AI Agent):
|
|
172
|
+
- Role 기반: admin, developer, user
|
|
173
|
+
- 플랜별 기능 제한 (free, pro, enterprise)
|
|
174
|
+
- API 키 기반 인증 (외부 연동)
|
|
175
|
+
|
|
176
|
+
예시 (의사 소통 도구):
|
|
177
|
+
- Role 기반: admin, moderator, member
|
|
178
|
+
- 채널별 권한 설정 (읽기/쓰기/관리)
|
|
179
|
+
- 의료정보 접근 로그 기록 (감사용)
|
|
180
|
+
-->
|
|
181
|
+
|
|
182
|
+
### 데이터 보호
|
|
183
|
+
|
|
184
|
+
<!--
|
|
185
|
+
예시 (일반):
|
|
186
|
+
- 비밀번호: bcrypt 해싱
|
|
187
|
+
- 개인정보: 암호화 저장 (선택)
|
|
188
|
+
- SQL Injection: Knex parameterized query
|
|
189
|
+
- XSS: React 자동 이스케이핑
|
|
190
|
+
|
|
191
|
+
예시 (의료 실험 - HIPAA 준수):
|
|
192
|
+
- PHI(Protected Health Information): AES-256 암호화
|
|
193
|
+
- 데이터 전송: TLS 1.3
|
|
194
|
+
- 감사 로그: 모든 접근 기록 저장 (삭제 불가)
|
|
195
|
+
- 데이터 익명화: 개인식별정보 분리 저장
|
|
196
|
+
|
|
197
|
+
예시 (커머스 - PCI DSS 준수):
|
|
198
|
+
- 카드 정보: PG사에 위임 (직접 저장 금지)
|
|
199
|
+
- 결제 토큰: 일회용 사용 후 폐기
|
|
200
|
+
- 주문 정보: 배송 완료 후 일부 마스킹
|
|
201
|
+
|
|
202
|
+
예시 (AI Agent):
|
|
203
|
+
- 프롬프트 내 민감정보 자동 감지 및 마스킹
|
|
204
|
+
- 대화 히스토리: 사용자별 격리 저장
|
|
205
|
+
- API 키: 해싱 후 저장, 원본은 1회만 표시
|
|
206
|
+
|
|
207
|
+
예시 (의사 소통 도구):
|
|
208
|
+
- 메시지 E2E 암호화 (선택적)
|
|
209
|
+
- 파일: 바이러스 스캔 + 암호화 저장
|
|
210
|
+
- 의료정보 자동 감지 시 강제 암호화
|
|
211
|
+
- 삭제된 메시지: 30일 후 완전 삭제 (right to be forgotten)
|
|
212
|
+
-->
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## 파일 저장소
|
|
217
|
+
|
|
218
|
+
### 업로드 방식
|
|
219
|
+
|
|
220
|
+
<!--
|
|
221
|
+
예시:
|
|
222
|
+
- 개발: Local FS (packages/api/public/uploaded)
|
|
223
|
+
- 프로덕션: S3
|
|
224
|
+
- URL: /api/public/uploaded/{key}
|
|
225
|
+
-->
|
|
226
|
+
|
|
227
|
+
### 파일 관리
|
|
228
|
+
|
|
229
|
+
<!--
|
|
230
|
+
예시 (게시판):
|
|
231
|
+
- 파일명: UUID + 원본파일명
|
|
232
|
+
- 최대 크기: 10MB
|
|
233
|
+
- 허용 확장자: jpg, png, pdf, docx
|
|
234
|
+
- 삭제 정책: 소프트 삭제 (30일 후 영구 삭제)
|
|
235
|
+
|
|
236
|
+
예시 (의료 실험):
|
|
237
|
+
- 파일명: 익명화된 피험자 ID + 타임스탬프
|
|
238
|
+
- 최대 크기: 100MB (DICOM 이미지 등)
|
|
239
|
+
- 허용 확장자: dcm, jpg, png, pdf, xlsx
|
|
240
|
+
- 버전 관리: 모든 버전 보관 (감사 추적)
|
|
241
|
+
- 암호화: AES-256 (at rest)
|
|
242
|
+
|
|
243
|
+
예시 (커머스):
|
|
244
|
+
- 상품 이미지: CDN 업로드, 자동 리사이징 (여러 해상도)
|
|
245
|
+
- 최대 크기: 5MB (이미지), 20MB (기타)
|
|
246
|
+
- 썸네일: 자동 생성 (200x200, 400x400, 800x800)
|
|
247
|
+
- 삭제 정책: 상품 삭제 시 즉시 CDN에서 제거
|
|
248
|
+
|
|
249
|
+
예시 (AI Agent):
|
|
250
|
+
- 업로드 파일: 텍스트 추출 후 임베딩 생성
|
|
251
|
+
- 최대 크기: 10MB
|
|
252
|
+
- 허용 확장자: txt, pdf, docx, md
|
|
253
|
+
- 처리 후 원본 삭제 (임베딩만 보관)
|
|
254
|
+
|
|
255
|
+
예시 (의사 소통 도구):
|
|
256
|
+
- 파일명: UUID (원본명은 메타데이터로 저장)
|
|
257
|
+
- 최대 크기: 50MB
|
|
258
|
+
- 바이러스 스캔: 업로드 시 필수
|
|
259
|
+
- 미리보기: 이미지/PDF 자동 생성
|
|
260
|
+
- 삭제 정책: 메시지 삭제 시 함께 삭제 (7일 유예)
|
|
261
|
+
-->
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
## 성능 최적화
|
|
266
|
+
|
|
267
|
+
### 캐싱 전략
|
|
268
|
+
|
|
269
|
+
<!--
|
|
270
|
+
예시:
|
|
271
|
+
- 정적 데이터: 브라우저 캐시 (1년)
|
|
272
|
+
- API 응답: 필요시 Redis 캐시
|
|
273
|
+
- 쿼리 결과: Sonamu 내장 캐시
|
|
274
|
+
-->
|
|
275
|
+
|
|
276
|
+
### 데이터베이스 최적화
|
|
277
|
+
|
|
278
|
+
<!--
|
|
279
|
+
예시:
|
|
280
|
+
- N+1 문제: Subset의 relation으로 해결
|
|
281
|
+
- 대량 조회: 페이지네이션 필수
|
|
282
|
+
- 집계 쿼리: 인덱스 활용
|
|
283
|
+
-->
|
|
284
|
+
|
|
285
|
+
### 프론트엔드 최적화
|
|
286
|
+
|
|
287
|
+
<!--
|
|
288
|
+
예시:
|
|
289
|
+
- 코드 스플리팅: TanStack Router 자동 처리
|
|
290
|
+
- 이미지 최적화: lazy loading
|
|
291
|
+
- API 호출: debounce, throttle 적용
|
|
292
|
+
-->
|
|
293
|
+
|
|
294
|
+
---
|
|
295
|
+
|
|
296
|
+
## 배포 아키텍처
|
|
297
|
+
|
|
298
|
+
<!--
|
|
299
|
+
예시:
|
|
300
|
+
```
|
|
301
|
+
[Client Browser]
|
|
302
|
+
|
|
|
303
|
+
v
|
|
304
|
+
[Nginx / CloudFront]
|
|
305
|
+
|
|
|
306
|
+
v
|
|
307
|
+
[Node.js Server]
|
|
308
|
+
|
|
|
309
|
+
v
|
|
310
|
+
[PostgreSQL]
|
|
311
|
+
```
|
|
312
|
+
-->
|
|
313
|
+
|
|
314
|
+
### 환경 구성
|
|
315
|
+
|
|
316
|
+
<!--
|
|
317
|
+
예시:
|
|
318
|
+
- Development: localhost:34900
|
|
319
|
+
- Staging: staging.example.com
|
|
320
|
+
- Production: api.example.com
|
|
321
|
+
-->
|
|
322
|
+
|
|
323
|
+
---
|
|
324
|
+
|
|
325
|
+
## 모니터링 & 로깅
|
|
326
|
+
|
|
327
|
+
<!--
|
|
328
|
+
예시:
|
|
329
|
+
- 에러 추적: Sentry (선택)
|
|
330
|
+
- 로그: Winston / Pino
|
|
331
|
+
- 성능: New Relic / Datadog (선택)
|
|
332
|
+
- 헬스체크: /api/health
|
|
333
|
+
-->
|
|
334
|
+
|
|
335
|
+
---
|
|
336
|
+
|
|
337
|
+
## 개발 중 아키텍처 결정
|
|
338
|
+
|
|
339
|
+
<!--
|
|
340
|
+
날짜별로 중요한 아키텍처 결정 기록
|
|
341
|
+
|
|
342
|
+
### 2024-01-15
|
|
343
|
+
- 파일 저장소: S3 사용 결정 (확장성 고려)
|
|
344
|
+
- 이유: 서버 디스크 용량 제한, CDN 연동 용이
|
|
345
|
+
|
|
346
|
+
### 2024-01-20
|
|
347
|
+
- 캐싱: Redis 도입 결정
|
|
348
|
+
- 대상: 대시보드 통계, 검색 결과
|
|
349
|
+
- TTL: 5분
|
|
350
|
+
-->
|
|
351
|
+
|
|
352
|
+
---
|
|
353
|
+
|
|
354
|
+
## 마이그레이션 전략
|
|
355
|
+
|
|
356
|
+
<!--
|
|
357
|
+
예시:
|
|
358
|
+
- 스키마 변경: Sonamu migration 사용
|
|
359
|
+
- 데이터 마이그레이션: 별도 스크립트 작성
|
|
360
|
+
- 롤백 계획: 각 마이그레이션마다 down 함수 구현
|
|
361
|
+
-->
|
|
362
|
+
|
|
363
|
+
---
|
|
364
|
+
|
|
365
|
+
## 확장성 고려사항
|
|
366
|
+
|
|
367
|
+
<!--
|
|
368
|
+
예시:
|
|
369
|
+
- 수평 확장: 무상태(stateless) 서버 설계
|
|
370
|
+
- 데이터베이스: 읽기 복제본 추가 가능
|
|
371
|
+
- 파일 저장소: S3로 무제한 확장
|
|
372
|
+
- 캐시: Redis Cluster 고려
|
|
373
|
+
-->
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
# 비즈니스 로직 상세
|
|
2
|
+
|
|
3
|
+
> 이 문서는 개발 과정에서 정리되는 비즈니스 규칙과 로직을 기록합니다.
|
|
4
|
+
> 대화를 통해 명확해진 내용들을 추가하여 일관된 구현을 유지합니다.
|
|
5
|
+
|
|
6
|
+
## 엔티티별 비즈니스 규칙
|
|
7
|
+
|
|
8
|
+
### [Entity명]
|
|
9
|
+
|
|
10
|
+
#### 상태 전이
|
|
11
|
+
|
|
12
|
+
<!--
|
|
13
|
+
예시:
|
|
14
|
+
- draft → published: 관리자만 가능, 필수 필드 검증
|
|
15
|
+
- published → closed: 마감일 이후 자동 전환
|
|
16
|
+
- closed → archived: 90일 후 자동 전환
|
|
17
|
+
-->
|
|
18
|
+
|
|
19
|
+
#### 검증 규칙
|
|
20
|
+
|
|
21
|
+
<!--
|
|
22
|
+
예시:
|
|
23
|
+
- 제목: 3-100자, 필수
|
|
24
|
+
- 시작일 < 종료일
|
|
25
|
+
- 예산: 0 이상
|
|
26
|
+
- 첨부파일: 최대 10MB, 5개까지
|
|
27
|
+
-->
|
|
28
|
+
|
|
29
|
+
#### 계산 로직
|
|
30
|
+
|
|
31
|
+
<!--
|
|
32
|
+
예시:
|
|
33
|
+
- 총점 = sum(평가항목별 점수 × 가중치)
|
|
34
|
+
- 진행률 = (완료된 마일스톤 / 전체 마일스톤) × 100
|
|
35
|
+
-->
|
|
36
|
+
|
|
37
|
+
#### 권한 규칙
|
|
38
|
+
|
|
39
|
+
<!--
|
|
40
|
+
예시:
|
|
41
|
+
- 생성: 로그인한 모든 사용자
|
|
42
|
+
- 수정: 작성자 본인 또는 관리자
|
|
43
|
+
- 삭제: 관리자만
|
|
44
|
+
- 조회: 공개 상태면 모두, 비공개면 작성자/관리자만
|
|
45
|
+
-->
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## 워크플로우
|
|
50
|
+
|
|
51
|
+
### [프로세스명]
|
|
52
|
+
|
|
53
|
+
<!--
|
|
54
|
+
예시: 연구과제 평가 프로세스
|
|
55
|
+
|
|
56
|
+
1. 공고 발행 (관리자)
|
|
57
|
+
2. 과제 지원 (신청자)
|
|
58
|
+
3. 평가위원 배정 (관리자)
|
|
59
|
+
4. 평가표 작성 (평가위원)
|
|
60
|
+
- 각 항목별 점수 입력
|
|
61
|
+
- 총점 자동 계산
|
|
62
|
+
- 의견 작성
|
|
63
|
+
5. 평가 완료 처리
|
|
64
|
+
- 모든 평가위원 제출 완료 시
|
|
65
|
+
- 평균 점수 계산
|
|
66
|
+
6. 선정 결과 발표 (관리자)
|
|
67
|
+
|
|
68
|
+
예시: 의료 실험 데이터 수집 프로세스
|
|
69
|
+
|
|
70
|
+
1. 프로토콜 승인 (IRB)
|
|
71
|
+
2. 피험자 모집 및 동의서 획득 (연구원)
|
|
72
|
+
3. 베이스라인 데이터 수집 (연구원)
|
|
73
|
+
4. 데이터 검증 (모니터)
|
|
74
|
+
5. 주기적 follow-up 데이터 수집
|
|
75
|
+
- 스케줄 알림 자동 발송
|
|
76
|
+
- 데이터 입력
|
|
77
|
+
- 이상치 자동 플래그
|
|
78
|
+
6. 최종 분석 (연구책임자)
|
|
79
|
+
|
|
80
|
+
예시: 커머스 주문 처리 프로세스
|
|
81
|
+
|
|
82
|
+
1. 장바구니 담기 (고객)
|
|
83
|
+
2. 주문 생성 및 재고 예약 (시스템)
|
|
84
|
+
3. 결제 처리 (PG사 연동)
|
|
85
|
+
4. 결제 확인 및 주문 확정 (시스템)
|
|
86
|
+
5. 상품 준비 (판매자)
|
|
87
|
+
6. 배송 시작 (배송사 연동)
|
|
88
|
+
7. 배송 완료 (배송사 웹훅)
|
|
89
|
+
8. 구매 확정 (고객 또는 자동)
|
|
90
|
+
|
|
91
|
+
예시: AI Agent 대화 세션 프로세스
|
|
92
|
+
|
|
93
|
+
1. 세션 생성 (사용자)
|
|
94
|
+
2. 프롬프트 템플릿 선택 (사용자 또는 자동)
|
|
95
|
+
3. 사용자 메시지 입력
|
|
96
|
+
4. 모델 선택 및 파라미터 설정 (자동)
|
|
97
|
+
5. AI 응답 생성 (외부 API 호출)
|
|
98
|
+
6. 응답 저장 및 표시
|
|
99
|
+
7. 사용량 기록 (토큰 수 계산)
|
|
100
|
+
8. 세션 종료 또는 계속
|
|
101
|
+
|
|
102
|
+
예시: 의사 소통 도구 메시지 전달 프로세스
|
|
103
|
+
|
|
104
|
+
1. 메시지 작성 (의사)
|
|
105
|
+
2. 메시지 타입 감지 (텍스트/이미지/파일/음성)
|
|
106
|
+
3. 의료정보 포함 여부 자동 감지
|
|
107
|
+
4. 민감 정보 암호화 (필요시)
|
|
108
|
+
5. 채널 멤버에게 전송
|
|
109
|
+
6. 실시간 알림 발송 (푸시/이메일)
|
|
110
|
+
7. 읽음 상태 업데이트
|
|
111
|
+
8. 검색 인덱싱
|
|
112
|
+
-->
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## 상태 전이 다이어그램
|
|
117
|
+
|
|
118
|
+
<!--
|
|
119
|
+
Mermaid 다이어그램이나 텍스트로 표현
|
|
120
|
+
|
|
121
|
+
예시:
|
|
122
|
+
```
|
|
123
|
+
[초안] --발행--> [발행됨] --마감--> [마감됨] --보관--> [보관됨]
|
|
124
|
+
| |
|
|
125
|
+
+----삭제--------+
|
|
126
|
+
```
|
|
127
|
+
-->
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## 비즈니스 제약사항
|
|
132
|
+
|
|
133
|
+
<!--
|
|
134
|
+
예시 (연구과제):
|
|
135
|
+
- 한 사용자는 동일 공고에 한 번만 지원 가능
|
|
136
|
+
- 평가위원은 자신의 소속 기관 과제는 평가 불가
|
|
137
|
+
- 평가 시작 후에는 평가 기준 변경 불가
|
|
138
|
+
- 마감일 이후 지원서 수정 불가
|
|
139
|
+
|
|
140
|
+
예시 (의료 실험):
|
|
141
|
+
- IRB 승인 없이는 데이터 수집 불가
|
|
142
|
+
- 피험자는 언제든 참여 철회 가능
|
|
143
|
+
- 프로토콜 일탈은 24시간 내 보고 필수
|
|
144
|
+
- 데이터 삭제 요청시 익명화 처리 (완전 삭제 불가)
|
|
145
|
+
|
|
146
|
+
예시 (커머스):
|
|
147
|
+
- 동일 상품 중복 재고 예약 방지
|
|
148
|
+
- 결제 실패시 재고 예약 자동 해제
|
|
149
|
+
- 판매자는 본인 상품만 수정 가능
|
|
150
|
+
- 리뷰는 구매 확정 후에만 작성 가능
|
|
151
|
+
|
|
152
|
+
예시 (AI Agent):
|
|
153
|
+
- 동시 세션 수 제한 (플랜별)
|
|
154
|
+
- API 호출 실패시 3회 재시도 후 에러
|
|
155
|
+
- 프롬프트 템플릿은 버전 관리 (삭제 불가)
|
|
156
|
+
- 과거 대화는 수정 불가 (읽기 전용)
|
|
157
|
+
|
|
158
|
+
예시 (의사 소통 도구):
|
|
159
|
+
- 채널 멤버만 메시지 접근 가능
|
|
160
|
+
- 삭제된 메시지는 7일간 복구 가능
|
|
161
|
+
- 민감 정보 포함 채널은 외부 초대 불가
|
|
162
|
+
- 파일은 바이러스 검사 후 업로드
|
|
163
|
+
-->
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## 알림 규칙
|
|
168
|
+
|
|
169
|
+
<!--
|
|
170
|
+
예시 (연구과제):
|
|
171
|
+
- 공고 발행 시: 전체 사용자에게 이메일 발송
|
|
172
|
+
- 평가 배정 시: 해당 평가위원에게 알림
|
|
173
|
+
- 평가 마감 3일 전: 미제출 평가위원에게 알림
|
|
174
|
+
- 선정 결과 발표 시: 지원자에게 개별 알림
|
|
175
|
+
|
|
176
|
+
예시 (의료 실험):
|
|
177
|
+
- 방문 예정일 3일 전: 피험자에게 SMS 알림
|
|
178
|
+
- 데이터 이상치 감지: 연구원에게 즉시 알림
|
|
179
|
+
- 프로토콜 일탈 발생: 연구책임자에게 즉시 알림
|
|
180
|
+
- 동의서 만료 30일 전: 연구원에게 갱신 알림
|
|
181
|
+
|
|
182
|
+
예시 (커머스):
|
|
183
|
+
- 주문 확정: 판매자에게 즉시 알림
|
|
184
|
+
- 배송 시작: 고객에게 추적번호 포함 알림
|
|
185
|
+
- 재고 부족 (임계값 이하): 판매자에게 알림
|
|
186
|
+
- 구매 확정: 판매자에게 정산 예정 알림
|
|
187
|
+
|
|
188
|
+
예시 (AI Agent):
|
|
189
|
+
- 사용량 80% 도달: 사용자에게 경고
|
|
190
|
+
- API 오류 발생: 관리자에게 즉시 알림
|
|
191
|
+
- 세션 타임아웃 5분 전: 사용자에게 연장 여부 확인
|
|
192
|
+
- 새 모델 추가: 전체 사용자에게 공지
|
|
193
|
+
|
|
194
|
+
예시 (의사 소통 도구):
|
|
195
|
+
- 새 메시지: 채널 멤버에게 실시간 푸시
|
|
196
|
+
- 멘션 시: 해당 사용자에게 강조 알림
|
|
197
|
+
- 파일 업로드 완료: 업로더에게 알림
|
|
198
|
+
- 읽지 않은 메시지 100개 이상: 일일 요약 이메일
|
|
199
|
+
-->
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## 예외 처리
|
|
204
|
+
|
|
205
|
+
<!--
|
|
206
|
+
예시:
|
|
207
|
+
- 중복 지원 시도: "이미 지원한 공고입니다" 에러
|
|
208
|
+
- 권한 없는 수정: "권한이 없습니다" 에러
|
|
209
|
+
- 마감 후 지원: "마감된 공고입니다" 에러
|
|
210
|
+
- 필수 필드 누락: 구체적인 필드명 포함 에러
|
|
211
|
+
-->
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
## 데이터 일관성
|
|
216
|
+
|
|
217
|
+
<!--
|
|
218
|
+
예시 (연구과제):
|
|
219
|
+
- 과제 삭제 시: 관련 평가 데이터도 함께 삭제 (CASCADE)
|
|
220
|
+
- 사용자 삭제 시: 작성한 콘텐츠는 유지하되 "탈퇴한 사용자"로 표시
|
|
221
|
+
- 평가표 제출 후: 평가 항목 변경 불가 (버전 관리)
|
|
222
|
+
|
|
223
|
+
예시 (의료 실험):
|
|
224
|
+
- 피험자 탈퇴 시: 데이터는 익명화하여 보존
|
|
225
|
+
- 프로토콜 변경 시: 기존 버전도 보관 (감사 추적)
|
|
226
|
+
- 동의서 철회 시: 향후 데이터 수집 중단, 기존 데이터는 익명화
|
|
227
|
+
|
|
228
|
+
예시 (커머스):
|
|
229
|
+
- 상품 삭제 시: 주문 히스토리는 유지 (스냅샷)
|
|
230
|
+
- 주문 취소 시: 재고 복원, 결제 환불 처리
|
|
231
|
+
- 판매자 탈퇴 시: 진행 중인 주문 완료 후 계정 비활성화
|
|
232
|
+
|
|
233
|
+
예시 (AI Agent):
|
|
234
|
+
- 프롬프트 템플릿 삭제 시: 기존 세션은 유지 (soft delete)
|
|
235
|
+
- 사용자 삭제 시: 대화 히스토리 익명화
|
|
236
|
+
- 모델 변경 시: 기존 세션은 기존 모델 설정 유지
|
|
237
|
+
|
|
238
|
+
예시 (의사 소통 도구):
|
|
239
|
+
- 채널 삭제 시: 메시지 30일간 보관 후 영구 삭제
|
|
240
|
+
- 사용자 탈퇴 시: 메시지는 유지하되 "탈퇴한 사용자"로 표시
|
|
241
|
+
- 파일 삭제 시: 참조하는 메시지가 있으면 soft delete
|
|
242
|
+
-->
|
|
243
|
+
|
|
244
|
+
---
|
|
245
|
+
|
|
246
|
+
## 성능 고려사항
|
|
247
|
+
|
|
248
|
+
<!--
|
|
249
|
+
예시:
|
|
250
|
+
- 대시보드 통계: 캐시 적용 (5분 TTL)
|
|
251
|
+
- 검색: 인덱스 적용 필요 필드 명시
|
|
252
|
+
- 파일 업로드: 비동기 처리
|
|
253
|
+
- 대량 데이터: 페이지네이션 필수
|
|
254
|
+
-->
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## 개발 중 논의 내역
|
|
259
|
+
|
|
260
|
+
<!--
|
|
261
|
+
날짜별로 대화 중 정리된 내용 기록
|
|
262
|
+
|
|
263
|
+
### 2024-01-15
|
|
264
|
+
- 평가 점수 계산 방식 확정: 가중치 평균 사용
|
|
265
|
+
- 동점자 처리: 특정 항목 우선순위로 결정
|
|
266
|
+
|
|
267
|
+
### 2024-01-20
|
|
268
|
+
- 첨부파일 저장 방식: S3 사용 결정
|
|
269
|
+
- 파일명 중복 처리: UUID 접두사 추가
|
|
270
|
+
-->
|