diff --git a/src/api/vocabApi.js b/src/api/vocabApi.js new file mode 100644 index 0000000..4de64ec --- /dev/null +++ b/src/api/vocabApi.js @@ -0,0 +1,30 @@ +import axios from 'axios' + +const vocabApi = axios.create({ + baseURL: import.meta.env.VITE_VOCAB_API_URL, + timeout: 10000, + headers: { + 'Content-Type': 'application/json', + }, +}) + +// Request interceptor +vocabApi.interceptors.request.use( + (config) => { + return config + }, + (error) => { + return Promise.reject(error) + } +) + +// Response interceptor +vocabApi.interceptors.response.use( + (response) => response.data, + (error) => { + console.error('Vocab API Error:', error.response?.data || error.message) + return Promise.reject(error) + } +) + +export default vocabApi diff --git a/src/domains/vocab/constants/vocabConstants.js b/src/domains/vocab/constants/vocabConstants.js new file mode 100644 index 0000000..60484e8 --- /dev/null +++ b/src/domains/vocab/constants/vocabConstants.js @@ -0,0 +1,95 @@ +// 학습 레벨 +export const LEVELS = { + BEGINNER: 'BEGINNER', + INTERMEDIATE: 'INTERMEDIATE', + ADVANCED: 'ADVANCED', +} + +export const LEVEL_LABELS = { + [LEVELS.BEGINNER]: '초급', + [LEVELS.INTERMEDIATE]: '중급', + [LEVELS.ADVANCED]: '고급', +} + +export const LEVEL_COLORS = { + [LEVELS.BEGINNER]: 'success', + [LEVELS.INTERMEDIATE]: 'warning', + [LEVELS.ADVANCED]: 'error', +} + +// 카테고리 +export const CATEGORIES = { + DAILY: 'DAILY', + BUSINESS: 'BUSINESS', + ACADEMIC: 'ACADEMIC', +} + +export const CATEGORY_LABELS = { + [CATEGORIES.DAILY]: '일상', + [CATEGORIES.BUSINESS]: '비즈니스', + [CATEGORIES.ACADEMIC]: '학술', +} + +// 학습 상태 +export const WORD_STATUS = { + NEW: 'NEW', + LEARNING: 'LEARNING', + REVIEWING: 'REVIEWING', + MASTERED: 'MASTERED', +} + +export const WORD_STATUS_LABELS = { + [WORD_STATUS.NEW]: '새 단어', + [WORD_STATUS.LEARNING]: '학습 중', + [WORD_STATUS.REVIEWING]: '복습 중', + [WORD_STATUS.MASTERED]: '암기 완료', +} + +export const WORD_STATUS_COLORS = { + [WORD_STATUS.NEW]: 'default', + [WORD_STATUS.LEARNING]: 'warning', + [WORD_STATUS.REVIEWING]: 'info', + [WORD_STATUS.MASTERED]: 'success', +} + +// 난이도 +export const DIFFICULTY = { + EASY: 'EASY', + NORMAL: 'NORMAL', + HARD: 'HARD', +} + +export const DIFFICULTY_LABELS = { + [DIFFICULTY.EASY]: '쉬움', + [DIFFICULTY.NORMAL]: '보통', + [DIFFICULTY.HARD]: '어려움', +} + +// 시험 유형 +export const TEST_TYPES = { + KOREAN_TO_ENGLISH: 'KOREAN_TO_ENGLISH', + ENGLISH_TO_KOREAN: 'ENGLISH_TO_KOREAN', +} + +export const TEST_TYPE_LABELS = { + [TEST_TYPES.KOREAN_TO_ENGLISH]: '한국어 → 영어', + [TEST_TYPES.ENGLISH_TO_KOREAN]: '영어 → 한국어', +} + +// TTS 음성 +export const VOICE_TYPES = { + MALE: 'MALE', + FEMALE: 'FEMALE', +} + +export const VOICE_LABELS = { + [VOICE_TYPES.MALE]: '남성', + [VOICE_TYPES.FEMALE]: '여성', +} + +// 일일 학습 목표 +export const DAILY_GOAL = { + NEW_WORDS: 50, + REVIEW_WORDS: 5, + TOTAL: 55, +} diff --git a/src/domains/vocab/index.js b/src/domains/vocab/index.js new file mode 100644 index 0000000..05c63ec --- /dev/null +++ b/src/domains/vocab/index.js @@ -0,0 +1,5 @@ +// Services +export * from './services/vocabService' + +// Constants +export * from './constants/vocabConstants' diff --git a/src/domains/vocab/services/vocabService.js b/src/domains/vocab/services/vocabService.js new file mode 100644 index 0000000..1014b98 --- /dev/null +++ b/src/domains/vocab/services/vocabService.js @@ -0,0 +1,91 @@ +import vocabApi from '../../../api/vocabApi' + +/** + * 단어 관리 API (#65) + */ +export const wordService = { + // 단어 목록 조회 + getList: ({ level, category, limit = 20, cursor } = {}) => + vocabApi.get('/vocab/words', { params: { level, category, limit, cursor } }), + + // 단어 검색 + search: ({ q, limit = 20, cursor } = {}) => + vocabApi.get('/vocab/words/search', { params: { q, limit, cursor } }), + + // 단어 상세 조회 + getDetail: (wordId) => + vocabApi.get(`/vocab/words/${wordId}`), +} + +/** + * 일일 학습 API (#66) + */ +export const dailyService = { + // 오늘의 학습 단어 조회 + getWords: (userId) => + vocabApi.get(`/vocab/daily/${userId}`), + + // 단어 학습 완료 표시 + markLearned: (userId, wordId, isCorrect) => + vocabApi.post(`/vocab/daily/${userId}/words/${wordId}/learned`, { isCorrect }), +} + +/** + * 사용자 단어 학습 상태 API (#67) + */ +export const userWordService = { + // 학습 상태 조회 + getList: (userId, { status, limit = 20, cursor } = {}) => + vocabApi.get(`/vocab/users/${userId}/words`, { params: { status, limit, cursor } }), + + // 학습 결과 업데이트 (정답/오답) + update: (userId, wordId, isCorrect) => + vocabApi.put(`/vocab/users/${userId}/words/${wordId}`, { isCorrect }), + + // 단어 태그 변경 (북마크/즐겨찾기/난이도) + updateTag: (userId, wordId, { bookmarked, favorite, difficulty }) => + vocabApi.put(`/vocab/users/${userId}/words/${wordId}/tag`, { bookmarked, favorite, difficulty }), +} + +/** + * 시험 API (#68) + */ +export const testService = { + // 시험 시작 + start: (userId, { wordCount = 20, level, type = 'ENGLISH_TO_KOREAN' } = {}) => + vocabApi.post(`/vocab/test/${userId}/start`, { wordCount, level, type }), + + // 답안 제출 + submit: (userId, testId, answers) => + vocabApi.post(`/vocab/test/${userId}/submit`, { testId, answers }), + + // 시험 결과 조회 + getResults: (userId, { limit = 20, cursor } = {}) => + vocabApi.get(`/vocab/test/${userId}/results`, { params: { limit, cursor } }), +} + +/** + * 통계 API (#69) + */ +export const statsService = { + // 전체 학습 통계 + getOverall: (userId) => + vocabApi.get(`/vocab/stats/${userId}`), + + // 일별 학습 통계 + getDaily: (userId, { limit = 30 } = {}) => + vocabApi.get(`/vocab/stats/${userId}/daily`, { params: { limit } }), + + // 약점 분석 + getWeakness: (userId) => + vocabApi.get(`/vocab/stats/${userId}/weakness`), +} + +/** + * 음성 API (TTS) (#70) + */ +export const voiceService = { + // 단어 발음 합성 + synthesize: (wordId, text, voice = 'FEMALE') => + vocabApi.post('/vocab/voice/synthesize', { wordId, text, voice }), +}