대시보드와 주간보고 기능 업데이트

This commit is contained in:
2026-01-10 14:40:01 +09:00
parent 0dd4b561f0
commit e4627caa4c
26 changed files with 3329 additions and 1720 deletions

View File

@@ -6,8 +6,32 @@ const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY
})
// 문자열 해시 함수 (seed용)
function hashCode(str: string): number {
let hash = 0
for (let i = 0; i < str.length; i++) {
const char = str.charCodeAt(i)
hash = ((hash << 5) - hash) + char
hash = hash & hash // 32bit 정수로 변환
}
return Math.abs(hash)
}
interface QualityScore {
summary: string // 총평 (맨 위)
specificity: { score: number; improvement: string } // 구체성
completeness: { score: number; improvement: string } // 완결성
timeEstimation: { score: number; improvement: string } // 시간산정
planning: { score: number; improvement: string } // 계획성
overall: number // 종합점수
bestPractice: { // 모범 답안
workTasks: string[] // 금주 실적 모범 답안
planTasks: string[] // 차주 계획 모범 답안
}
}
/**
* 주간보고 PMO AI 리뷰
* 주간보고 PMO AI 리뷰 - 작성 품질 점수 + 모범 답안
* POST /api/report/review
*/
export default defineEventHandler(async (event) => {
@@ -77,61 +101,75 @@ export default defineEventHandler(async (event) => {
})
}
// OpenAI PMO 리뷰 요청
const systemPrompt = `당신은 SI 프로젝트의 PMO(Project Management Officer)이자 주간보고 작성 코치입니다.
개발자들이 더 나은 주간보고 작성할 수 있도록 구체적인 피드백과 가이드를해주세요.
// OpenAI 품질 점수 + 모범 답안 요청
const systemPrompt = `당신은 SI 프로젝트의 PMO(Project Management Officer)입니다.
주간보고 작성 품질을 평가하고, 모범 답안을해주세요.
[주간보고 작성의 목적]
- 프로젝트 진행 현황을 명확히 파악
- 일정 지연이나 리스크를 사전에 감지
- 팀원 간 업무 공유 및 협업 촉진
[평가 항목] (각 1~10점)
1. 구체성 (specificity): 작업 내용이 어떤 기능/모듈인지 구체적으로 작성되었는지
2. 완결성 (completeness): 필수 정보 포함 여부
3. 시간산정 (timeEstimation): 작업 시간이 내용 대비 적절하게 배분되었는지
4. 계획성 (planning): 차주 계획이 실현 가능하고 명확한 목표가 있는지
[검토 기준 - 엄격하게 적용]
[완결성 상세 기준] - 엄격하게 적용
- 진행중 작업에 진척률(%)이 없으면 -2점
- 진행중 작업에 완료 예정일이 없으면 -2점
- 완료 작업인데 산출물/결과 언급이 없으면 -1점
- 상태(완료/진행중)가 명확하지 않으면 -1점
1. **실적의 구체성** (가장 중요!)
- "DB 작업", "화면 개발", "API 개발" 같은 모호한 표현 지양
- 좋은 예시: "사용자 관리 테이블 3개(user, role, permission) 설계 및 생성"
- 좋은 예시: "로그인 API 개발 - JWT 토큰 발급, 리프레시 토큰 구현"
- 좋은 예시: "검색 화면 UI 구현 - 필터 조건 5개, 페이징, 엑셀 다운로드"
- 어떤 기능/모듈/화면인지, 무엇을 구체적으로 했는지 명시되어야 함
[계획성 상세 기준] - 엄격하게 적용
- 차주 계획에 예상 소요시간 근거가 없으면 -1점
- 차주 계획에 목표 완료일/산출물이 없으면 -2점
- 단순 "~할 예정", "~진행" 만 있고 구체적 목표가 없으면 -2점
- 실현 가능성이 낮은 과도한 계획이면 -1점
2. **일정의 명확성**
- "진행중"만 있고 완료 예정일이 없으면 부족
- 언제 완료될 예정인지, 진척률은 얼마인지 표기 권장
- 좋은 예시: "사용자 관리 화면 개발 (70% 완료, 1/10 완료 예정)"
[점수 기준]
- 1~3점: 매우 부족 (내용이 거의 없거나 한 단어 수준)
- 4~5점: 부족 (진척률/예정일 누락, 모호한 표현)
- 6~7점: 보통 (기본 내용은 있으나 구체성 부족)
- 8~9점: 양호 (진척률, 예정일, 산출물 모두 명시)
- 10점: 우수 (완벽한 모범 사례)
3. **시간 산정의 적절성**
- 8시간(1일) 이상 작업은 세부 내역이 필요
- 16시간(2일) 이상인데 내용이 한 줄이면 분리 필요
- "회의", "검토" 등은 별도 기재 권장
※ 진행중 작업에 진척률/예정일이 없으면 완결성은 6점 이하로 평가하세요.
※ 차주 계획에 구체적 목표가 없으면 계획성은 6점 이하로 평가하세요.
4. **차주 계획의 실현 가능성**
- 계획이 너무 추상적이면 실행하기 어려움
- 구체적인 목표와 예상 산출물 명시 필요
- 좋은 예시: "결제 모듈 연동 - PG사 API 연동, 결제 테스트 완료 목표"
[모범 답안 작성 규칙]
- 사용자가 작성한 내용을 기반으로 더 구체적으로 보완
- 같은 프로젝트명, 비슷한 작업 내용을 유지하되 구체성 추가
- 진행중인 작업은 반드시 진척률(%)과 완료 예정일 추가
- 시간이 긴 작업은 세부 내역 포함
- 차주 계획은 목표 산출물과 예상 완료일 명시
- 형식: "프로젝트명 / 작업내용 (세부사항, 진척률, 예정일) / 시간h / 상태"
[피드백 작성 규칙]
- 각 Task별로 구체적인 개선 제안 제시
- 잘 작성된 부분은 "✅" 로 인정
- 보완이 필요한 부분은 "📝" 로 개선 방향 제시
- 일정 관련 질문은 "📅" 로 표시
- 리스크/우려사항은 "⚠️" 로 경고
- **반드시 어떻게 수정하면 좋을지 예시를 들어 설명**
- 친절하지만 명확하게, 구체적인 작성 예시를 포함
- 마지막에 전체적인 작성 팁 1-2개 추가
[응답 규칙]
- 반드시 아래 JSON 형식으로만 응답
- summary: 전체적인 총평 (30~60자, 격려 포함)
- improvement: 각 항목별 개선 포인트 (15~30자, 구체적으로)
- bestPractice: 모범 답안 (workTasks, planTasks 배열)
- JSON 외의 텍스트는 절대 포함하지 마세요`
[피드백 톤]
- 비난하지 않고 코칭하는 느낌으로
- "~하면 더 좋겠습니다", "~로 수정해보시면 어떨까요?" 형태로
- 개선점뿐 아니라 잘한 점도 언급`
const userPrompt = `다음 주간보고의 작성 품질을 평가하고, 모범 답안을 만들어주세요.
const userPrompt = `다음 주간보고를 PMO 관점에서 상세히 리뷰해주세요.
특히 실적과 계획이 구체적으로 작성되었는지, 일정이 명확한지 중점적으로 검토해주세요.
모호한 표현이 있다면 어떻게 수정하면 좋을지 예시와 함께 피드백해주세요.
${taskText}
${taskText}`
아래 JSON 형식으로만 응답하세요:
{
"summary": "총평 (격려 포함)",
"specificity": { "score": 숫자, "improvement": "개선포인트" },
"completeness": { "score": 숫자, "improvement": "개선포인트" },
"timeEstimation": { "score": 숫자, "improvement": "개선포인트" },
"planning": { "score": 숫자, "improvement": "개선포인트" },
"overall": 종합점수(소수점1자리),
"bestPractice": {
"workTasks": ["모범답안1", "모범답안2", ...],
"planTasks": ["모범답안1", "모범답안2", ...]
}
}`
try {
// Task 내용 기반 seed 생성 (같은 내용 = 같은 점수)
const seed = hashCode(taskText)
const response = await openai.chat.completions.create({
model: 'gpt-4o-mini',
messages: [
@@ -139,29 +177,52 @@ ${taskText}`
{ role: 'user', content: userPrompt }
],
max_tokens: 1500,
temperature: 0.7
temperature: 0.2, // 낮춰서 일관성 강화
seed: seed // 같은 내용 = 같은 seed = 같은 결과
})
const review = response.choices[0]?.message?.content || '리뷰를 생성할 수 없습니다.'
const content = response.choices[0]?.message?.content || ''
// JSON 파싱
let qualityScore: QualityScore
try {
// JSON 블록 추출 (```json ... ``` 형태 처리)
let jsonStr = content
const jsonMatch = content.match(/```json\s*([\s\S]*?)\s*```/)
if (jsonMatch) {
jsonStr = jsonMatch[1]
} else {
// { } 사이 추출
const braceMatch = content.match(/\{[\s\S]*\}/)
if (braceMatch) {
jsonStr = braceMatch[0]
}
}
qualityScore = JSON.parse(jsonStr)
} catch (parseError) {
console.error('JSON 파싱 실패:', content)
throw new Error('AI 응답을 파싱할 수 없습니다.')
}
const reviewedAt = new Date().toISOString()
// DB에 저장
// DB에 저장 (ai_review에 JSON 문자열로 저장)
await query(`
UPDATE wr_weekly_report
SET ai_review = $1, ai_review_at = $2
WHERE report_id = $3
`, [review, reviewedAt, reportId])
`, [JSON.stringify(qualityScore), reviewedAt, reportId])
return {
success: true,
review,
qualityScore,
reviewedAt
}
} catch (error: any) {
console.error('OpenAI API error:', error)
throw createError({
statusCode: 500,
message: 'AI 리뷰 생성 중 오류가 발생했습니다: ' + error.message
message: 'AI 품질 평가 중 오류가 발생했습니다: ' + error.message
})
}
})