
Upstage AI Ambassador 2기 과제 - AI Agent 프로젝트 소개

"중간고사 2주 전, 자료구조 슬라이드 150장..."
대학생이라면 누구나 겪는 상황입니다. 시험 기간이 다가오면:
📄 슬라이드 150장
+ 교수님 판서 캡처 30장
+ 강의 녹화본 3시간 분량
+ 내 필기 20페이지
───────────────────────────────────────
= 어디서부터 봐야 할까? 😰
저는 개발자로서, "AI로 이 문제를 해결할 수 없을까?" 라는 생각에서 SlideWise를 시작했습니다.
처음엔 ChatGPT에 PDF를 올려봤습니다.
나: [50페이지 PDF 업로드] "이거 요약해줘"
GPT: "이 문서는 자료구조에 대한 내용으로,
스택, 큐, 트리 등을 다룹니다..."
문제점:
Google의 NotebookLM도 써봤습니다. PDF 자동 분석, 퀴즈 생성, 팟캐스트까지 정말 강력한 도구입니다. 하지만 대학생 시험 준비 관점에서 보면 아쉬운 점이 있었습니다:
| 기능 | NotebookLM | 대학생이 필요한 것 |
|---|---|---|
| 타겟 | 범용 (직장인, 연구자) | 시험 준비에 특화 |
| 분석 단위 | 전체 요약 | 슬라이드별 상세 분석 |
| 퀴즈 | 일반 문제 | 시험 유형별 (객관식/서술형/코딩) |
| 시험 범위 | 전체 문서 | 시험 범위만 선택 가능 |
| 교수 강조점 | 기본 OCR | 손글씨 필기/강조 추출 |
결론: 기존 도구들은 훌륭하지만, 대학생 시험 준비에 특화되지 않았습니다.
"AI에 슬라이드 1장씩 넣었을 때의 디테일을, 단 한 번의 업로드로"
SlideWise는 PDF 강의 슬라이드를 업로드하면 AI가 자동으로:

대학 시험은 크게 4가지 유형이 있고, 각 유형마다 공부 방법이 다릅니다:
| 시험 유형 | 특징 | 학습 전략 |
|---|---|---|
| 객관식 | 4지선다, OX | 오답 선지 함정 파악, 정확한 정의 암기 |
| 서술형 | 단답형, 논술 | 답안 구조화, 필수 키워드 포함 |
| 코딩 | 알고리즘 구현 | 시간복잡도, Edge case 체크 |
| 혼합형 | 다양한 유형 | 유형별 전략 병행 |
예시: "해시 함수 설계 원리" 슬라이드
객관식 시험일 때:
🎯 오답 선지 함정
❌ "해시 함수는 중복을 허용하지 않는다"
→ 충돌은 발생할 수 있음! (함정)
🔑 정의 정확도
"충돌 최소화" vs "충돌 제거"
→ "최소화"가 정확한 표현
서술형 시험일 때:
📝 예상 문제
Q. 해시 함수 설계 시 고려해야 할 3가지 원리를 설명하시오.
✍️ 답안 작성 구조 (20점 만점)
[1단계 - 2점] 서론: 해시 함수의 정의
[2단계 - 12점] 3가지 원리 각 4점
[3단계 - 3점] 구체적 예시
[4단계 - 3점] 결론
🔑 필수 키워드 (안 쓰면 감점!)
✓ "충돌 최소화"
✓ "균일 분포"
✓ "시간복잡도 O(1)"
기존 AI 도구: "전체 요약 한 번"
SlideWise: "전체 맥락 파악 → 슬라이드별 심층 분석"
Phase 1 (자동): PDF 업로드 시 즉시 실행
├── 전체 요약 (200-300자)
├── 토픽 그룹핑 (3-7개)
├── 중요 개념 추출 (10-20개)
└── 학습 전략 제안
Phase 2 (온디맨드): 사용자가 슬라이드 선택 시 실행
├── 핵심 개념 설명 (300-500자)
├── 다이어그램 요소별 해석
├── 구체적 예시 3개
├── 자주 하는 실수 2-3개
├── 시험 대비 전략
├── 연습 문제 2개
└── 빠른 복습 카드 3-5개
이 구조의 장점:
슬라이드를 보다가 궁금한 점이 생기면 바로 질문할 수 있습니다.
👤 사용자: "해시 충돌 해결 방법 중 체이닝과 오픈 어드레싱의 차이가 뭐야?"
🤖 AI 튜터: "현재 보고 계신 슬라이드 11의 내용을 기반으로 설명드릴게요.
**체이닝 (Chaining)**
- 같은 버킷에 연결 리스트로 저장
- 장점: 삭제가 간단
- 단점: 추가 메모리 필요
**오픈 어드레싱 (Open Addressing)**
- 다른 빈 버킷을 찾아 저장
- 장점: 메모리 효율적
- 단점: 클러스터링 발생 가능
📌 시험 Tip: 서술형에서 둘의 '장단점 비교' 자주 출제됩니다!"
일반적인 AI 분석 방식은 전체 문서를 한 번에 요약하는 One-Pass입니다.
// ❌ One-Pass 방식 - 표면적 분석
const analysis = await solar.chat({
prompt: "이 150페이지 슬라이드 전부 분석해줘"
})
// 결과: "이 강의는 자료구조를 다룹니다..." (너무 추상적)
One-Pass의 문제점:
SlideWise는 Two-Pass 방식을 채택했습니다:
┌─────────────────────────────────────────────────────┐
│ Phase 1: 전체 맥락 파악 (1회 API 호출) │
│ ├── 모든 슬라이드 텍스트를 한 번에 분석 │
│ ├── 과목 전체 흐름 이해 │
│ ├── 토픽별 그룹핑 │
│ └── slideContents 저장 (Phase 2에서 사용) │
└─────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────┐
│ Phase 2: 슬라이드별 심층 분석 (N회 API 호출) │
│ ├── 사용자가 특정 슬라이드 선택 시 실행 │
│ ├── Phase 1의 맥락 정보 참조 │
│ ├── 300-500자 상세 설명 │
│ ├── 시험 유형별 맞춤 전략 │
│ └── 분석 결과 캐싱 (재요청 시 즉시 반환) │
└─────────────────────────────────────────────────────┘
Phase 1은 PDF 업로드 즉시 자동으로 실행됩니다.
// app/api/analyze/route.ts (간략화)
async function processPhase1(analysisId: string, filePath: string, examType: string, subject: string) {
// 1. Upstage Document Parse로 PDF 텍스트 추출
const parseResult = await parseDocument(filePath)
const slideContents = parseResult.elements.map(e => e.text)
// 2. Solar Pro로 전체 맥락 분석
const overviewPrompt = `
과목: ${subject}
시험 유형: ${examType}
전체 슬라이드: ${slideContents.length}장
[슬라이드 내용]
${slideContents.map((s, i) => `[${i+1}] ${s}`).join('\n')}
다음을 JSON으로 분석:
1. overallSummary: 전체 요약 (200-300자)
2. topics: 토픽 그룹핑 (3-7개)
3. importantConcepts: 핵심 개념 (10-20개)
4. studyStrategy: 학습 전략
`
const overview = await callSolarAPI(overviewPrompt)
// 3. 결과 저장 (slideContents 포함 - Phase 2에서 사용)
await supabase.from('analyses').update({
analysis_result: {
...overview,
slideContents, // Phase 2에서 개별 슬라이드 텍스트 사용
detailedSlideAnalyses: []
},
status: 'completed'
})
}
Phase 1 결과 예시:
{
"overallSummary": "이 강의는 자료구조의 핵심인 해시 테이블을 다룹니다. 해시 함수 설계 원리부터 충돌 해결 방법, 실제 구현까지 단계적으로 학습합니다.",
"topics": [
{ "title": "해시 함수 기초", "slideNumbers": [1, 2, 3, 4], "examWeight": "중" },
{ "title": "해시 함수 설계", "slideNumbers": [5, 6, 7, 8, 9, 10, 11], "examWeight": "상" },
{ "title": "충돌 해결", "slideNumbers": [12, 13, 14, 15], "examWeight": "상" }
],
"importantConcepts": [
{ "concept": "해시 함수", "importance": "★★★", "examLikelihood": "상" },
{ "concept": "충돌(Collision)", "importance": "★★★", "examLikelihood": "상" },
{ "concept": "체이닝", "importance": "★★", "examLikelihood": "중" }
],
"slideContents": ["슬라이드1 텍스트...", "슬라이드2 텍스트...", ...]
}
Phase 2는 사용자가 특정 슬라이드의 "분석하기" 버튼을 클릭할 때만 실행됩니다.
[UI 스크린샷 삽입 위치 - 분석하기 버튼]
슬라이드 선택 후 "이 슬라이드 분석하기" 버튼을 클릭하는 화면
// app/api/analyze/slide/route.ts (간략화)
export async function POST(request: Request) {
const { analysisId, slideNumber } = await request.json()
// 1. 이미 분석된 슬라이드인지 확인 (캐싱)
const existing = analysisResult.detailedSlideAnalyses?.find(
d => d.slideNumber === slideNumber
)
if (existing?.conceptExplanation) {
return NextResponse.json({ slideAnalysis: existing, cached: true })
}
// 2. Phase 1에서 저장한 슬라이드 텍스트 가져오기
const slideContent = analysisResult.slideContents[slideNumber - 1]
// 3. 심층 분석 실행
const slideAnalysis = await analyzeSlideInDepth(
slideNumber,
slideContent,
subject,
examType,
analysisResult // Phase 1 맥락 정보 참조
)
// 4. 결과 저장
await saveSlideAnalysis(analysisId, slideAnalysis)
return NextResponse.json({ slideAnalysis, cached: false })
}
Phase 2의 핵심은 시험 유형별 맞춤 프롬프트입니다.
// 시험 유형별 프롬프트 분기
function getExamTypeSpecificPrompt(examType: string): string {
switch (examType) {
case 'mcq': // 객관식
return `
- 오답 선지로 자주 나오는 개념들
- 정확히 알아야 할 키워드
- "A와 B의 차이를 묻는 문제 대비"
`
case 'essay': // 서술형
return `
- 답안 작성 구조 (정의 → 설명 → 예시)
- 반드시 포함해야 할 키워드
- 예상 배점 (정의 5점, 설명 10점, 예시 5점)
- 모범 답안 예시 (200자 내외)
`
case 'coding': // 코딩
return `
- 구현 시 주의사항
- 시간복잡도/공간복잡도 분석
- Edge case 체크리스트
- 핵심 코드 스니펫
`
default: // 혼합형
return `
- 객관식으로 나올 확률과 대비 방법
- 서술형으로 나올 확률과 대비 방법
`
}
}
Phase 2 결과 예시 (슬라이드 11: 해시 함수 설계 원리):
{
"slideNumber": 11,
"title": "해시 함수 설계 원리",
"conceptExplanation": "해시 함수 설계의 핵심은 '충돌 최소화'입니다. 좋은 해시 함수는 입력값을 테이블 전체에 균일하게 분포시켜야 합니다. 예를 들어, 학번을 해시 키로 사용할 때 단순히 앞 두 자리(입학년도)만 사용하면 같은 학번대 학생들이 모두 같은 버킷에 몰립니다. 대신 전체 학번을 사용하고, 테이블 크기 M을 소수로 선택하면 분포가 균일해집니다...",
"examples": [
{
"title": "나쁜 해시 함수 예시",
"content": "M=10일 때, h(k) = k mod 10. 키가 20, 30, 40이면 모두 버킷 0에 저장됨 → 충돌 다발",
"code": "function badHash(key) { return key % 10; }"
},
{
"title": "좋은 해시 함수 예시",
"content": "M=7(소수)일 때, h(k) = k mod 7. 키 15→1, 22→1, 30→2로 분산됨",
"code": "function goodHash(key) { return key % 7; }"
}
],
"commonMistakes": [
"❌ '해시 함수는 충돌이 없다' → 충돌은 발생할 수 있으며, 최소화가 목표",
"❌ 'M은 아무 수나 가능하다' → M은 소수를 권장 (균일 분포)"
],
"examAnalysis": {
"likelihood": "상",
"questionTypes": ["정의 묻기", "좋은/나쁜 해시 함수 비교", "M 선택 이유"],
"answerStructure": "1) 해시 함수 정의 (3점)\n2) 3가지 설계 원리 (9점)\n3) 구체적 예시 (3점)",
"keyTerms": [
{ "term": "충돌 최소화", "definition": "다른 키가 같은 버킷에 매핑되는 것을 줄임", "mustInclude": true },
{ "term": "균일 분포", "definition": "모든 버킷에 키가 고르게 분산", "mustInclude": true }
],
"modelAnswer": "해시 함수는 키를 해시 테이블의 인덱스로 변환하는 함수이다. 좋은 해시 함수의 조건은 첫째 충돌 최소화, 둘째 균일 분포, 셋째 O(1) 시간복잡도이다. 예를 들어 M=7(소수)일 때 h(k)=k mod 7은 키를 고르게 분산시킨다."
},
"practiceQuestions": [
{
"question": "해시 테이블 크기 M을 소수로 선택하는 이유를 설명하시오.",
"answer": "소수를 사용하면 키가 테이블 전체에 균일하게 분포되어 충돌이 감소한다.",
"explanation": "M이 2의 거듭제곱이면 하위 비트만 사용되어 패턴이 생기고, 소수면 모든 비트가 활용됨",
"difficulty": "중급"
}
],
"quickReviewCards": [
{ "front": "좋은 해시 함수의 3가지 조건은?", "back": "충돌 최소화, 균일 분포, O(1) 시간복잡도" },
{ "front": "M을 소수로 선택하는 이유는?", "back": "균일 분포를 위해" }
]
}
SlideWise는 Upstage의 두 가지 핵심 API를 활용합니다.
PDF를 분석하려면 먼저 텍스트를 추출해야 합니다. Upstage Document Parse API는 단순 텍스트뿐 아니라 손글씨 필기까지 인식합니다.
[슬라이드 이미지 예시]
┌─────────────────────────────────────┐
│ 해시 함수 설계 원리 │ ← 인쇄체
│ • 충돌 최소화 │ ← 인쇄체
│ │
│ M은 소수로! ★★★ │ ← 손글씨 필기
│ 시험 꼭 나옴 │ ← 손글씨 필기
└─────────────────────────────────────┘
코드 예시:
// lib/upstage.ts
export async function parseDocument(filePath: string) {
const formData = new FormData()
formData.append('document', file)
const response = await fetch(
'https://api.upstage.ai/v1/document-digitization',
{
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.UPSTAGE_API_KEY}`
},
body: formData
}
)
return response.json()
// 결과: { elements: [{ text: "해시 함수 설계 원리...", page: 1 }, ...] }
}
일반 PDF 파서 vs Upstage Document Parse:
| 기능 | 일반 PDF 파서 | Upstage Document Parse |
|---|---|---|
| 인쇄체 | ✅ | ✅ |
| 손글씨 필기 | ❌ | ✅ |
| 강조 표시 (★, 밑줄) | ❌ | ✅ |
| 표/다이어그램 구조 | 부분 지원 | ✅ |
왜 중요한가?: 대학 강의에서 교수님의 손글씨 필기나 강조 표시는 종종 시험에 나올 핵심 내용을 담고 있습니다. 이를 놓치면 중요한 정보를 잃게 됩니다.
분석에는 Upstage의 Solar Pro 모델을 사용합니다.
// Solar Pro API 호출
const response = await fetch(
'https://api.upstage.ai/v1/solar/chat/completions',
{
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.UPSTAGE_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: 'solar-pro',
messages: [
{ role: 'system', content: systemPrompt },
{ role: 'user', content: userPrompt }
],
response_format: { type: 'json_object' }, // JSON 응답 보장
temperature: 0.4 // 일관성 있는 응답
})
}
)
Solar Pro를 선택한 이유:
| 항목 | 설명 |
|---|---|
| 한국어 이해도 | 대학 전공 용어, 한국어 문맥 파악이 정확 |
| JSON 안정성 | response_format: { type: 'json_object' } 지원으로 파싱 오류 최소화 |
| 속도 | 슬라이드 1개당 약 2-3초 |
| 비용 효율 | 고품질 대비 경제적 |
┌──────────────────────────────────────────────────────────┐
│ SlideWise │
└──────────────────────────────────────────────────────────┘
│
┌───────────────┴───────────────┐
▼ ▼
┌─────────────────────┐ ┌─────────────────────┐
│ Document Parse API │ │ Solar Pro API │
│ (PDF 텍스트 추출) │ │ (AI 분석/챗봇) │
├─────────────────────┤ ├─────────────────────┤
│ • OCR 기반 추출 │ │ • Phase 1: 전체 분석 │
│ • 손글씨 인식 │ │ • Phase 2: 상세 분석 │
│ • 페이지별 분리 │ │ • 챗봇 응답 │
│ • 표/다이어그램 구조 │ │ • 퀴즈 생성/채점 │
└─────────────────────┘ └─────────────────────┘
분석 결과는 3개 탭으로 구조화되어 목적에 맞게 학습할 수 있습니다.
목적: 슬라이드 내용을 완전히 이해하기
| 섹션 | 내용 |
|---|---|
| 핵심 개념 | 300-500자 상세 설명, 왜 중요한지, 실생활 비유 |
| 다이어그램 해석 | 그림/수식의 각 요소 설명 |
| 구체적 예시 | 실제 숫자/코드로 3가지 예시 |
| 자주 하는 실수 | 학생들이 흔히 틀리는 포인트 |
| 관련 개념 | 다른 슬라이드와의 연결 |


목적: 시험에서 고득점 받기
| 섹션 | 내용 |
|---|---|
| 출제 확률 | 상/중/하로 표시 |
| 예상 문제 유형 | "정의 묻기", "비교 문제" 등 |
| 답안 작성 구조 | 점수 배분까지 포함된 가이드 |
| 필수 키워드 | 답안에 반드시 포함할 용어 |
| 오답 패턴 | 학생들이 자주 쓰는 잘못된 답 |
| 모범 답안 | 150-200자 예시 답안 |


목적: 실전 연습
| 섹션 | 내용 |
|---|---|
| 연습 문제 | 2개 (난이도 표시, 정답/해설 포함) |
| 빠른 복습 카드 | 플래시카드 3-5개 |
| 퀴즈 생성 | 원하는 문제 수/유형으로 퀴즈 생성 |

슬라이드를 보다가 궁금한 점이 생기면 바로 질문할 수 있습니다.

특징:
// 챗봇 API 호출
const response = await fetch('/api/chat/ask', {
method: 'POST',
body: JSON.stringify({
analysisId,
message: "체이닝과 오픈 어드레싱 차이점이 뭐야?",
slideContext: currentSlide // 현재 슬라이드 번호
})
})
분석된 슬라이드를 기반으로 맞춤형 퀴즈를 생성합니다.



퀴즈 유형:
// 퀴즈 생성
const quiz = await fetch('/api/quiz/generate', {
method: 'POST',
body: JSON.stringify({
analysisId,
quizType: 'mcq', // 객관식
questionCount: 5 // 5문제
})
})
// 퀴즈 제출 및 채점
const result = await fetch('/api/quiz/submit', {
method: 'POST',
body: JSON.stringify({
quizId,
answers: userAnswers
})
})
// 결과: { score: 80, feedback: [...], wrongAnswers: [...] }
퀴즈가 분석 내용을 반영하는 방법:
Phase 2에서 분석된 detailedSlideAnalyses의 내용을 퀴즈 생성 프롬프트에 포함시킵니다:
// app/api/quiz/generate/route.ts
const detailedAnalyses = analysisResult?.detailedSlideAnalyses || []
const slideDetails = detailedAnalyses.map((slide) => `
슬라이드 ${slide.slideNumber}: ${slide.title}
- 핵심 개념: ${slide.conceptExplanation}
- 예시: ${slide.examples?.map(e => e.content).join(', ')}
- 핵심 용어: ${slide.examAnalysis?.keyTerms?.map(k => k.term).join(', ')}
`).join('\n')
const context = `
과목 요약: ${overallSummary}
주요 토픽: ${topicTitles}
중요 개념: ${conceptList}
슬라이드별 상세 내용:
${slideDetails}
`
┌─────────────────────────────────────────────────────────────┐
│ Frontend (Next.js 15) │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Upload │ → │ Analysis │ → │ Quiz │ │ Chat │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
└─────────────────────────┬───────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Backend (Next.js API Routes) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ /api/analyze → Phase 1 전체 분석 │ │
│ │ /api/analyze/slide → Phase 2 슬라이드별 분석 │ │
│ │ /api/chat/ask → AI 챗봇 응답 │ │
│ │ /api/quiz/generate → 퀴즈 생성 │ │
│ │ /api/quiz/submit → 퀴즈 채점 │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────┬───────────────────────────────────┘
│
┌───────────────┼───────────────┐
▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Upstage │ │ Supabase │ │ Vercel │
│ APIs │ │ │ │ │
├──────────────┤ ├──────────────┤ ├──────────────┤
│ • Doc Parse │ │ • PostgreSQL │ │ • 호스팅 │
│ • Solar Pro │ │ • Auth │ │ • Serverless │
│ │ │ • Storage │ │ • Edge │
└──────────────┘ └──────────────┘ └──────────────┘
| 영역 | 기술 | 버전 | 용도 |
|---|---|---|---|
| Framework | Next.js | 15.x | App Router, API Routes |
| UI | React | 19.x | 컴포넌트 기반 UI |
| Language | TypeScript | 5.x | 타입 안정성 |
| Styling | Tailwind CSS | 4.x | 유틸리티 기반 스타일링 |
| Components | shadcn/ui | - | Radix UI 기반 컴포넌트 |
| State | TanStack Query | 5.x | 서버 상태 관리 |
| Form | react-hook-form + Zod | - | 폼 상태 + 검증 |
| Database | Supabase (PostgreSQL) | - | DB + Auth + Storage |
| AI | Upstage APIs | - | Doc Parse + Solar Pro |
| Deploy | Vercel | - | Serverless 배포 |
Next.js 15부터 params가 Promise로 변경되었습니다. React 19의 use() hook을 활용합니다:
// ✅ React 19 방식
import { use } from 'react'
export default function AnalysisPage({
params
}: {
params: Promise<{ id: string }>
}) {
const { id } = use(params) // Promise를 동기적으로 unwrap
// ...
}
Vercel의 무료 티어는 함수 실행 시간이 10초로 제한됩니다. 이를 해결하기 위해:
// app/api/analyze/route.ts
export const maxDuration = 60 // 60초까지 허용
| 효과 | 설명 |
|---|---|
| 학습 시간 단축 | 150장 슬라이드를 1장씩 읽는 대신, AI가 핵심만 추출 |
| 맞춤형 학습 | 시험 유형(객관식/서술형)에 맞는 전략 제공 |
| 실전 대비 | 예상 문제, 모범 답안으로 실전 연습 |
| 24시간 과외 | AI 챗봇으로 언제든 질문 가능 |
📊 목표 지표
├── 시험 준비 시간: 기존 대비 50% 단축
├── 핵심 개념 파악률: 90% 이상
├── 사용자 만족도: 4.5/5.0 이상
└── 재사용률: 70% 이상
SlideWise의 Two-Pass 분석 시스템은 다양한 교육 시나리오로 확장 가능합니다:
[확장 예시: 정보처리기사]
├── 기출문제 패턴 분석
├── 출제 빈도 기반 학습 우선순위
├── 오답 노트 자동 생성
└── 합격 예측 점수 제공
[확장 예시: 신입사원 교육]
├── 사내 매뉴얼 분석
├── 직무별 핵심 내용 추출
├── 이해도 테스트 자동 생성
└── 학습 진도 대시보드
[확장 예시: TOEIC/TOEFL]
├── 지문 난이도 분석
├── 핵심 어휘 추출
├── 문제 유형별 전략
└── 약점 집중 학습
[확장 예시: 수능 대비]
├── 교과서 단원별 분석
├── 수능 기출 연계 분석
├── 오개념 교정
└── 등급 예측
| 기능 | 설명 | 필요 기술 |
|---|---|---|
| 강의 영상 분석 | YouTube 강의 자막 분석 | STT, 영상 처리 |
| 필기 인식 강화 | 손글씨 노트 직접 업로드 | OCR 고도화 |
| 협업 학습 | 스터디 그룹 공유 기능 | 실시간 동기화 |
| 학습 분석 | 개인화된 학습 패턴 분석 | ML/Analytics |
문제: Vercel 무료 티어의 함수 실행 시간 제한 (10초)
증상: 슬라이드가 많은 PDF 분석 시 타임아웃 발생
해결: Two-Pass 아키텍처로 분리
Before (One-Pass):
├── 모든 슬라이드 한 번에 분석
├── 150장 × 3초 = 450초 → 타임아웃!
└── ❌ 실패
After (Two-Pass):
├── Phase 1: 전체 요약만 (30초 이내)
├── Phase 2: 사용자가 선택한 슬라이드만 (10초 이내)
└── ✅ 성공
문제: commonMistakes 배열에 문자열과 객체가 혼재
증상: React 렌더링 오류
해결: 타입 정규화
// 렌더링 시 타입 체크
{commonMistakes.map((mistake, i) => (
<li key={i}>
{typeof mistake === 'string'
? mistake
: (mistake as any)?.pattern || JSON.stringify(mistake)}
</li>
))}
문제: 슬라이드 선택 시 "분석하기" 버튼이 표시되지 않음
원인: 조건부 렌더링 분기 오류
해결: 분기 로직 수정
// Before (잘못된 분기)
{activeSlide ? (
<Summary /> // 버튼 없음
) : hasDetailedAnalysis ? (
<FullContent />
) : (
<AnalyzeButton />
)}
// After (수정)
{hasDetailedAnalysis ? (
<FullContent />
) : (
<AnalyzeButton /> // 분석 안 된 슬라이드면 버튼 표시
)}
문제: **텍스트**를 형태로 한글이 붙으면 파싱 실패
// ❌ 파싱 안 됨
"**해시 함수**는 중요합니다"
// ✅ 파싱 됨
"**해시 함수** 는 중요합니다"
해결: 전처리 함수 적용
function preprocessMarkdown(text: string): string {
return text
.replace(/\*\*([^*]+)\*\*([가-힣a-zA-Z])/g, '**$1** $2')
.replace(/`([^`]+)`([가-힣a-zA-Z])/g, '`$1` $2')
}
| 항목 | 내용 |
|---|---|
| 문제 정의 | 대학생 시험 준비 시 슬라이드 분석의 비효율성 |
| 해결 방안 | Two-Pass AI 분석 + 시험 유형별 맞춤 전략 |
| 핵심 기술 | Upstage Document Parse + Solar Pro, Next.js 15 |
| 기대효과 | 학습 시간 50% 단축, 맞춤형 시험 대비 |
Upstage AI Ambassador 2기 과제로 제작되었습니다.
Made with ❤️ by @hseo1o2