This commit is contained in:
djeon 2025-10-22 17:41:22 +09:00
commit 038fa40c2d
5 changed files with 173 additions and 271 deletions

View File

@ -0,0 +1,11 @@
{
"permissions": {
"allow": [
"Bash(git add:*)",
"Bash(git commit:*)",
"Bash(git push)"
],
"deny": [],
"ask": []
}
}

View File

@ -6,27 +6,22 @@ title AI Service 내부 시퀀스 - 결정사항제안
participant "SuggestionController" as Controller participant "SuggestionController" as Controller
participant "DecisionSuggestionService" as Service participant "DecisionSuggestionService" as Service
participant "LLMClient" as LLM participant "LLMClient" as LLM
participant "SuggestionRepository" as Repo
participant "TranscriptRepository" as TranscriptRepo participant "TranscriptRepository" as TranscriptRepo
database "Azure OpenAI<<E>>" as OpenAI database "Azure OpenAI<<E>>" as OpenAI
database "Redis Cache<<E>>" as Cache
database "PostgreSQL<<E>>" as DB database "PostgreSQL<<E>>" as DB
== 실시간 결정사항 제안 요청 == == 실시간 결정사항 제안 요청 ==
note over Controller note over Controller
TranscriptService로부터 호출 TranscriptService로부터 호출
또는 API 직접 호출: (회의록 자동작성 프로세스 내부)
POST /api/ai/suggestions/decision
Body: {
"meetingId": "{meetingId}",
"transcriptText": "최근 대화 내용"
}
end note end note
Controller -> Service: suggestDecisions(meetingId, transcriptText) Controller -> Service: suggestDecisions(meetingId, transcriptText)
activate Service activate Service
== 회의 맥락 및 이전 결정 조회 == == 회의 맥락 조회 ==
Service -> TranscriptRepo: getMeetingContext(meetingId) Service -> TranscriptRepo: getMeetingContext(meetingId)
activate TranscriptRepo activate TranscriptRepo
@ -40,17 +35,15 @@ deactivate DB
TranscriptRepo --> Service: meetingContext TranscriptRepo --> Service: meetingContext
deactivate TranscriptRepo deactivate TranscriptRepo
Service -> Repo: getPreviousDecisions(meetingId) Service -> Cache: GET decisions:{meetingId}
activate Repo activate Cache
note right
이전에 감지한 결정사항 조회
(중복 제거용)
end note
Repo -> DB: SELECT content FROM ai_suggestions\nWHERE meeting_id = {meetingId}\nAND suggestion_type = 'DECISION'\nAND status = 'APPLIED' Cache --> Service: previousDecisions
activate DB deactivate Cache
DB --> Repo: 이미 확정된 결정사항
deactivate DB
Repo --> Service: previousDecisions
deactivate Repo
== LLM 기반 결정사항 패턴 감지 == == LLM 기반 결정사항 패턴 감지 ==
@ -69,7 +62,7 @@ note right
사용자 프롬프트: 사용자 프롬프트:
- 회의 참석자: {participants} - 회의 참석자: {participants}
- 이미 확정된 결정: {previousDecisions} - 이미 감지한 결정: {previousDecisions}
- 현재 대화 내용: {transcriptText} - 현재 대화 내용: {transcriptText}
지시사항: 지시사항:
@ -140,7 +133,7 @@ Service -> Service: 결정사항 검증
note right note right
검증 기준: 검증 기준:
- 신뢰도 70% 이상만 선택 - 신뢰도 70% 이상만 선택
- 중복 제거 (이미 확정된 결정) - 중복 제거 (이미 감지한 결정)
- 명확성 검증 - 명확성 검증
* 주어, 목적어가 명확한가? * 주어, 목적어가 명확한가?
* 결정 내용이 구체적인가? * 결정 내용이 구체적인가?
@ -155,46 +148,32 @@ loop 각 제안마다
추가 정보: 추가 정보:
- 생성 시각 - 생성 시각
- 회의 진행 시점 (분) - 회의 진행 시점 (분)
- 원문 위치 (라인 번호) - 원문 위치 정보
- 상태: PENDING - 고유 ID (UUID)
- 관련 논의사항 참조
end note end note
end end
== 제안 저장 == == 임시 캐시 저장 (선택적) ==
loop 각 검증된 제안마다 Service -> Cache: APPEND decisions:{meetingId}
activate Cache
Service -> Repo: saveSuggestion(meetingId, decision)
activate Repo
Repo -> DB: INSERT INTO ai_suggestions
activate DB
note right note right
저장 데이터: Redis에 임시 저장:
- meeting_id - Key: decisions:{meetingId}
- suggestion_type: 'DECISION' - Value: JSON array (제안 목록)
- content: {decision 내용} - TTL: 2시간 (회의 시간)
- category: 결정 카테고리 - APPEND로 기존 목록에 추가
- decision_maker: 결정자
- participants: 참여자 목록 (JSON) 목적:
- confidence_score: 0.0-1.0 - 중복 감지용
- extracted_from: 원문 - 재접속 시 복원용
- context: 결정 배경
- status: PENDING
- created_at
end note end note
DB --> Repo: suggestionId Cache --> Service: 저장 완료
deactivate DB deactivate Cache
Repo --> Service: suggestionId == 응답 반환 ==
deactivate Repo
end
== 응답 구성 ==
Service -> Service: 응답 데이터 구성 Service -> Service: 응답 데이터 구성
note right note right
@ -208,20 +187,22 @@ note right
"decisionMaker": "김철수", "decisionMaker": "김철수",
"confidence": 0.85, "confidence": 0.85,
"extractedFrom": "원문 발췌", "extractedFrom": "원문 발췌",
"context": "결정 배경 설명", "context": "결정 배경 설명"
"canApply": true
} }
], ],
"totalCount": 제안 개수, "totalCount": 제안 개수,
"displayHint": "오른쪽 탭 '결정사항' 섹션" "timestamp": "생성 시각"
} }
end note end note
Service --> Controller: 결정사항 제안 생성 완료 Service --> Controller: 결정사항 제안 목록
deactivate Service deactivate Service
Controller --> Controller: 200 OK 응답 반환 Controller --> Controller: 이벤트 데이터에 포함하여 반환
note right note right
TranscriptSummaryCreated 이벤트에
decisionSuggestions 필드로 포함
프론트엔드 처리: 프론트엔드 처리:
- 오른쪽 "추천" 탭의 "결정사항" 섹션 표시 - 오른쪽 "추천" 탭의 "결정사항" 섹션 표시
- "적용" 버튼 활성화 - "적용" 버튼 활성화
@ -234,49 +215,36 @@ end note
note over Controller note over Controller
사용자가 "적용" 버튼 클릭 시: 사용자가 "적용" 버튼 클릭 시:
PUT /api/ai/suggestions/{suggestionId}/apply 프론트엔드에서 직접 Meeting Service 호출
end note
Controller -> Service: applySuggestion(suggestionId, meetingId) PUT /api/meetings/{meetingId}/transcript
activate Service Body: {
"addDecisionSection": {
"content": "결정 내용",
"category": "기술",
"decisionMaker": "김철수"
}
}
Service -> Repo: updateSuggestionStatus(suggestionId, "APPLIED") Meeting Service에서 회의록의
activate Repo
Repo -> DB: UPDATE ai_suggestions\nSET status = 'APPLIED',\napplied_at = NOW()
activate DB
DB --> Repo: 업데이트 완료
deactivate DB
Repo --> Service: 완료
deactivate Repo
Service -> Service: 결정사항을 회의록에 추가
note right
Meeting Service API 호출하여
"결정사항" 섹션에 항목 추가 "결정사항" 섹션에 항목 추가
end note end note
Service --> Controller: 적용 완료
deactivate Service
Controller --> Controller: 200 OK
note over Controller, DB note over Controller, DB
처리 시간: 처리 시간:
- 맥락 조회: 100-200ms - 맥락 조회: 100-200ms
- LLM 패턴 감지: 2-3초 - LLM 패턴 감지: 2-3초
- 검증 및 필터링: 100-200ms - 검증 및 필터링: 100-200ms
- 저장 처리: 200-300ms - 캐시 저장: 50-100ms
총 처리 시간: 약 3-4 총 처리 시간: 약 2.5-3.5초
제안 정책: 특징:
- 신뢰도 70% 이상만 제안 - DB 영구 저장 없음 (임시 데이터)
- 명확한 결정 표현 우선 - Redis 캐시만 활용
- 중복 제거 (이미 확정된 것 제외) * 중복 감지용
- 카테고리별로 최대 10개까지 * 재접속 복원용
- 실시간으로 계속 감지 - 프론트엔드 메모리에서 관리
- "적용" 시에만 회의록에 반영
end note end note
@enduml @enduml

View File

@ -6,21 +6,16 @@ title AI Service 내부 시퀀스 - 논의사항제안
participant "SuggestionController" as Controller participant "SuggestionController" as Controller
participant "DiscussionSuggestionService" as Service participant "DiscussionSuggestionService" as Service
participant "LLMClient" as LLM participant "LLMClient" as LLM
participant "SuggestionRepository" as Repo
participant "TranscriptRepository" as TranscriptRepo participant "TranscriptRepository" as TranscriptRepo
database "Azure OpenAI<<E>>" as OpenAI database "Azure OpenAI<<E>>" as OpenAI
database "Redis Cache<<E>>" as Cache
database "PostgreSQL<<E>>" as DB database "PostgreSQL<<E>>" as DB
== 실시간 논의사항 제안 요청 == == 실시간 논의사항 제안 요청 ==
note over Controller note over Controller
TranscriptService로부터 호출 TranscriptService로부터 호출
또는 API 직접 호출: (회의록 자동작성 프로세스 내부)
POST /api/ai/suggestions/discussion
Body: {
"meetingId": "{meetingId}",
"transcriptText": "최근 대화 내용"
}
end note end note
Controller -> Service: suggestDiscussionTopics(meetingId, transcriptText) Controller -> Service: suggestDiscussionTopics(meetingId, transcriptText)
@ -139,42 +134,30 @@ loop 각 제안마다
- 생성 시각 - 생성 시각
- 제안 신뢰도 점수 - 제안 신뢰도 점수
- 회의 진행 시점 (분) - 회의 진행 시점 (분)
- 상태: PENDING - 고유 ID (UUID)
end note end note
end end
== 제안 저장 == == 임시 캐시 저장 (선택적) ==
loop 각 검증된 제안마다 Service -> Cache: SET suggestions:discussion:{meetingId}
activate Cache
Service -> Repo: saveSuggestion(meetingId, suggestion)
activate Repo
Repo -> DB: INSERT INTO ai_suggestions
activate DB
note right note right
저장 데이터: Redis에 임시 저장:
- meeting_id - Key: suggestions:discussion:{meetingId}
- suggestion_type: 'DISCUSSION' - Value: JSON array (제안 목록)
- content: {topic, reason} - TTL: 2시간 (회의 시간)
- priority: HIGH/MEDIUM/LOW
- related_agenda: "안건 항목" 목적:
- estimated_time: 예상 시간 - 재접속 시 복원용
- confidence_score: 0.0-1.0 - WebSocket 재연결 대응
- status: PENDING
- created_at
end note end note
DB --> Repo: suggestionId Cache --> Service: 저장 완료
deactivate DB deactivate Cache
Repo --> Service: suggestionId == 응답 반환 ==
deactivate Repo
end
== 응답 구성 ==
Service -> Service: 응답 데이터 구성 Service -> Service: 응답 데이터 구성
note right note right
@ -187,20 +170,22 @@ note right
"reason": "제안 이유", "reason": "제안 이유",
"priority": "HIGH", "priority": "HIGH",
"relatedAgenda": "관련 안건", "relatedAgenda": "관련 안건",
"estimatedTime": 10, "estimatedTime": 10
"canApply": true
} }
], ],
"totalCount": 제안 개수, "totalCount": 제안 개수,
"displayHint": "오른쪽 탭에 표시" "timestamp": "생성 시각"
} }
end note end note
Service --> Controller: 논의사항 제안 생성 완료 Service --> Controller: 논의사항 제안 목록
deactivate Service deactivate Service
Controller --> Controller: 200 OK 응답 반환 Controller --> Controller: 이벤트 데이터에 포함하여 반환
note right note right
TranscriptSummaryCreated 이벤트에
discussionSuggestions 필드로 포함
프론트엔드 처리: 프론트엔드 처리:
- 오른쪽 "추천" 탭에 표시 - 오른쪽 "추천" 탭에 표시
- "적용" 버튼 활성화 - "적용" 버튼 활성화
@ -214,48 +199,33 @@ end note
note over Controller note over Controller
사용자가 "적용" 버튼 클릭 시: 사용자가 "적용" 버튼 클릭 시:
PUT /api/ai/suggestions/{suggestionId}/apply 프론트엔드에서 직접 Meeting Service 호출
end note
Controller -> Service: applySuggestion(suggestionId, meetingId) PUT /api/meetings/{meetingId}/transcript
activate Service Body: {
"addDiscussionSection": {
"topic": "논의 주제",
"content": ""
}
}
Service -> Repo: updateSuggestionStatus(suggestionId, "APPLIED") Meeting Service에서 회의록에
activate Repo
Repo -> DB: UPDATE ai_suggestions\nSET status = 'APPLIED',\napplied_at = NOW()
activate DB
DB --> Repo: 업데이트 완료
deactivate DB
Repo --> Service: 완료
deactivate Repo
Service -> Service: 논의사항을 회의록에 추가하는 로직 실행
note right
Meeting Service에 API 호출하여
새로운 논의 섹션 추가 새로운 논의 섹션 추가
end note end note
Service --> Controller: 적용 완료
deactivate Service
Controller --> Controller: 200 OK
note over Controller, DB note over Controller, DB
처리 시간: 처리 시간:
- 맥락 정보 조회: 100-200ms - 맥락 정보 조회: 100-200ms
- LLM 제안 생성: 2-3초 - LLM 제안 생성: 2-3초
- 검증 및 필터링: 100-200ms - 검증 및 필터링: 100-200ms
- 저장 처리: 200-300ms - 캐시 저장: 50-100ms
총 처리 시간: 약 3-4 총 처리 시간: 약 2.5-3.5초
제안 정책: 특징:
- 최대 5개까지만 제안 - DB 영구 저장 없음 (임시 데이터)
- 우선순위 HIGH가 1개 이상 - Redis 캐시만 활용 (재접속 복원용)
- 이미 논의한 주제는 제외 - 프론트엔드 메모리에서 관리
- 제안은 회의 중 언제든 생성 가능 - "적용" 시에만 회의록에 반영
end note end note
@enduml @enduml

View File

@ -6,21 +6,16 @@ title AI Service 내부 시퀀스 - 실시간Todo추출
participant "SuggestionController" as Controller participant "SuggestionController" as Controller
participant "RealtimeTodoService" as Service participant "RealtimeTodoService" as Service
participant "LLMClient" as LLM participant "LLMClient" as LLM
participant "SuggestionRepository" as Repo
participant "TranscriptRepository" as TranscriptRepo participant "TranscriptRepository" as TranscriptRepo
database "Azure OpenAI<<E>>" as OpenAI database "Azure OpenAI<<E>>" as OpenAI
database "Redis Cache<<E>>" as Cache
database "PostgreSQL<<E>>" as DB database "PostgreSQL<<E>>" as DB
== 실시간 액션아이템 추출 요청 == == 실시간 액션아이템 추출 요청 ==
note over Controller note over Controller
TranscriptService로부터 호출 TranscriptService로부터 호출
또는 API 직접 호출: (회의록 자동작성 프로세스 내부)
POST /api/ai/suggestions/action-item
Body: {
"meetingId": "{meetingId}",
"transcriptText": "최근 대화 내용"
}
end note end note
Controller -> Service: extractRealtimeActionItems(meetingId, transcriptText) Controller -> Service: extractRealtimeActionItems(meetingId, transcriptText)
@ -40,17 +35,15 @@ deactivate DB
TranscriptRepo --> Service: meetingContext TranscriptRepo --> Service: meetingContext
deactivate TranscriptRepo deactivate TranscriptRepo
Service -> Repo: getPreviousActionItems(meetingId) Service -> Cache: GET action-items:{meetingId}
activate Repo activate Cache
note right
이전에 추출한 액션아이템 조회
(중복 제거용)
end note
Repo -> DB: SELECT content FROM ai_suggestions\nWHERE meeting_id = {meetingId}\nAND suggestion_type = 'ACTION_ITEM'\nAND status IN ('PENDING', 'APPLIED') Cache --> Service: previousActionItems
activate DB deactivate Cache
DB --> Repo: 이미 추출된 액션아이템
deactivate DB
Repo --> Service: previousActionItems
deactivate Repo
== LLM 기반 액션아이템 패턴 감지 == == LLM 기반 액션아이템 패턴 감지 ==
@ -169,47 +162,32 @@ loop 각 제안마다
추가 정보: 추가 정보:
- 생성 시각 - 생성 시각
- 회의 진행 시점 (분) - 회의 진행 시점 (분)
- 원문 위치 (라인 번호) - 원문 위치 정보
- 상태: PENDING - 고유 ID (UUID)
- 관련 결정사항 참조
- 관련 논의 섹션 참조
end note end note
end end
== 제안 저장 == == 임시 캐시 저장 (선택적) ==
loop 각 검증된 제안마다 Service -> Cache: APPEND action-items:{meetingId}
activate Cache
Service -> Repo: saveSuggestion(meetingId, actionItem)
activate Repo
Repo -> DB: INSERT INTO ai_suggestions
activate DB
note right note right
저장 데이터: Redis에 임시 저장:
- meeting_id - Key: action-items:{meetingId}
- suggestion_type: 'ACTION_ITEM' - Value: JSON array (제안 목록)
- content: 할 일 내용 - TTL: 2시간 (회의 시간)
- assignee: 담당자 - APPEND로 기존 목록에 추가
- due_date: 마감일
- priority: HIGH/MEDIUM/LOW 목적:
- confidence_score: 0.0-1.0 - 중복 감지용
- extracted_from: 원문 - 재접속 시 복원용
- related_decision_id: 관련 결정
- status: PENDING
- created_at
end note end note
DB --> Repo: suggestionId Cache --> Service: 저장 완료
deactivate DB deactivate Cache
Repo --> Service: suggestionId == 응답 반환 ==
deactivate Repo
end
== 응답 구성 ==
Service -> Service: 응답 데이터 구성 Service -> Service: 응답 데이터 구성
note right note right
@ -224,20 +202,22 @@ note right
"priority": "HIGH", "priority": "HIGH",
"confidence": 0.85, "confidence": 0.85,
"extractedFrom": "원문 발췌", "extractedFrom": "원문 발췌",
"relatedDecision": "decision-uuid", "relatedDecision": "decision-uuid"
"canApply": true
} }
], ],
"totalCount": 제안 개수, "totalCount": 제안 개수,
"displayHint": "오른쪽 탭 '액션아이템' 섹션" "timestamp": "생성 시각"
} }
end note end note
Service --> Controller: 액션아이템 제안 생성 완료 Service --> Controller: 액션아이템 제안 목록
deactivate Service deactivate Service
Controller --> Controller: 200 OK 응답 반환 Controller --> Controller: 이벤트 데이터에 포함하여 반환
note right note right
TranscriptSummaryCreated 이벤트에
actionItemSuggestions 필드로 포함
프론트엔드 처리: 프론트엔드 처리:
- 오른쪽 "추천" 탭의 "액션아이템" 섹션 표시 - 오른쪽 "추천" 탭의 "액션아이템" 섹션 표시
- "적용" 버튼 활성화 - "적용" 버튼 활성화
@ -251,36 +231,14 @@ end note
note over Controller note over Controller
사용자가 "적용" 버튼 클릭 시: 사용자가 "적용" 버튼 클릭 시:
PUT /api/ai/suggestions/{suggestionId}/apply 프론트엔드에서 직접 Meeting Service 호출
Body: {
"assignee": "김철수" (수정 가능),
"dueDate": "2025-02-01" (수정 가능)
}
end note
Controller -> Service: applySuggestion(suggestionId, updateData) POST /api/meetings/{meetingId}/todos
activate Service
Service -> Repo: updateSuggestionStatus(suggestionId, "APPLIED")
activate Repo
Repo -> DB: UPDATE ai_suggestions\nSET status = 'APPLIED',\napplied_at = NOW(),\nassignee = {updateData.assignee},\ndue_date = {updateData.dueDate}
activate DB
DB --> Repo: 업데이트 완료
deactivate DB
Repo --> Service: 완료
deactivate Repo
Service -> Service: Meeting Service에 Todo 생성 요청
note right
POST /meetings/{meetingId}/todos
Body: { Body: {
"content": "할 일 내용", "content": "할 일 내용",
"assignee": "담당자", "assignee": "김철수",
"dueDate": "마감일", "dueDate": "2025-02-01",
"priority": "우선순위", "priority": "HIGH",
"relatedSection": "관련 회의록 섹션" "relatedSection": "관련 회의록 섹션"
} }
@ -290,31 +248,26 @@ note right
- 담당자에게 알림 발송 - 담당자에게 알림 발송
end note end note
Service --> Controller: 적용 완료
deactivate Service
Controller --> Controller: 200 OK
note over Controller, DB note over Controller, DB
처리 시간: 처리 시간:
- 맥락 조회: 100-200ms - 맥락 조회: 100-200ms
- LLM 패턴 감지: 1-2초 - LLM 패턴 감지: 1-2초
- 검증 및 필터링: 100-200ms - 검증 및 필터링: 100-200ms
- 저장 처리: 200-300ms - 캐시 저장: 50-100ms
총 처리 시간: 약 2-3 총 처리 시간: 약 1.5-2.5
제안 정책: 특징:
- 신뢰도 70% 이상만 제안 - DB 영구 저장 없음 (임시 데이터)
- 명확한 액션 표현 우선 - Redis 캐시만 활용
- 중복 제거 (유사도 90% 기준) * 중복 감지용
- 실시간으로 계속 감지 * 재접속 복원용
- 최대 20개까지 누적 표시 - 프론트엔드 메모리에서 관리
- 적용된 것은 회색 처리 - "적용" 시에만 Todo 생성
차이점 (회의 종료 후 Todo 추출과): 차이점 (회의 종료 후 Todo 추출과):
- 실시간: 5초마다 즉시 추출 - 실시간: 5초마다 즉시 추출, 임시 제안
- 종료 후: 전체 회의록 기반 종합 추출 - 종료 후: 전체 회의록 기반 종합 추출, 자동 생성
- 실시간은 "후보"로 제시 - 실시간은 "후보"로 제시, 사용자 선택
- 종료 후는 "확정" 추출 후 자동 생성 - 종료 후는 "확정" 추출 후 자동 생성
end note end note

View File

@ -82,7 +82,7 @@
<!-- Header --> <!-- Header -->
<header class="header"> <header class="header">
<div class="header-left"> <div class="header-left">
<button class="icon-btn" onclick="history.back()"></button> <button class="back-btn" onclick="history.back()"></button>
<h1 class="header-title">검증 완료</h1> <h1 class="header-title">검증 완료</h1>
</div> </div>
</header> </header>
@ -134,7 +134,7 @@
<span class="text-caption text-muted">2명 검증 완료</span> <span class="text-caption text-muted">2명 검증 완료</span>
</div> </div>
</div> </div>
<button class="btn-ghost btn-sm" onclick="viewSection(0)">보기</button> <button class="btn btn-secondary btn-sm" onclick="viewSection(0)">보기</button>
</div> </div>
<!-- 섹션 2 - 검증 완료 + 잠금 --> <!-- 섹션 2 - 검증 완료 + 잠금 -->
@ -154,7 +154,7 @@
<span class="text-caption text-muted">3명 검증 완료 · 잠금됨</span> <span class="text-caption text-muted">3명 검증 완료 · 잠금됨</span>
</div> </div>
</div> </div>
<button class="btn-ghost btn-sm" onclick="unlockSection(1)">잠금해제</button> <button class="btn btn-secondary btn-sm" onclick="unlockSection(1)">잠금해제</button>
</div> </div>
<!-- 섹션 3 - 미검증 --> <!-- 섹션 3 - 미검증 -->
@ -169,7 +169,7 @@
<span class="text-caption text-muted">1명 검증 완료</span> <span class="text-caption text-muted">1명 검증 완료</span>
</div> </div>
</div> </div>
<button class="btn-primary btn-sm" onclick="verifySection(2)">검증하기</button> <button class="btn btn-primary btn-sm" onclick="verifySection(2)">검증하기</button>
</div> </div>
<!-- 섹션 4 - 미검증 --> <!-- 섹션 4 - 미검증 -->
@ -181,7 +181,7 @@
<span class="text-caption text-muted">아직 검증되지 않음</span> <span class="text-caption text-muted">아직 검증되지 않음</span>
</div> </div>
</div> </div>
<button class="btn-primary btn-sm" onclick="verifySection(3)">검증하기</button> <button class="btn btn-primary btn-sm" onclick="verifySection(3)">검증하기</button>
</div> </div>
</div> </div>
@ -209,8 +209,8 @@
</div> </div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button class="btn-ghost" onclick="closeModal('sectionModal')">닫기</button> <button class="btn btn-secondary btn-sm" onclick="closeModal('sectionModal')">닫기</button>
<button class="btn-primary" onclick="editSection()">편집</button> <button class="btn btn-primary btn-sm" onclick="editSection()">편집</button>
</div> </div>
</div> </div>
</div> </div>
@ -229,8 +229,8 @@
</div> </div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button class="btn-ghost" onclick="closeModal('verifyModal')">취소</button> <button class="btn btn-secondary btn-sm" onclick="closeModal('verifyModal')">취소</button>
<button class="btn-primary" onclick="confirmVerification()">검증 완료</button> <button class="btn btn-primary btn-sm" onclick="confirmVerification()">검증 완료</button>
</div> </div>
</div> </div>
</div> </div>
@ -244,13 +244,13 @@
</div> </div>
<div class="modal-body"> <div class="modal-body">
<p class="text-small mb-md">이 섹션의 잠금을 해제하시겠습니까?</p> <p class="text-small mb-md">이 섹션의 잠금을 해제하시겠습니까?</p>
<div class="card" style="background: var(--warning); color: var(--white);"> <div class="card" style="background: transparent; color: var(--error); border: 1px solid var(--error);">
<p class="text-small font-medium">⚠️ 잠금 해제 시 다른 참석자들이 내용을 수정할 수 있습니다.</p> <p class="text-small font-medium">⚠️ 잠금 해제 시 다른 참석자들이 내용을 수정할 수 있습니다.</p>
</div> </div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button class="btn-ghost" onclick="closeModal('unlockModal')">취소</button> <button class="btn btn-sm" style="background: transparent; color: var(--error); border: 1px solid var(--error);" onclick="closeModal('unlockModal')">취소</button>
<button class="btn-error" onclick="confirmUnlock()">잠금 해제</button> <button class="btn btn-error btn-sm" onclick="confirmUnlock()">잠금 해제</button>
</div> </div>
</div> </div>
</div> </div>