Files
web-page/src/pages/portfolio/usePortfolioApi.js
gahusb a6dd2ef747 feat(portfolio): 포트폴리오 페이지 전체 구현
- 3탭 구조: 프로필&경력, 프로젝트, 자기소개
- 비밀번호 인증 → 편집 모드
- 클립보드 복사, PDF 내보내기 (window.print)
- 사이버펑크 테마 CSS, 모바일 반응형

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-27 14:37:25 +09:00

101 lines
4.3 KiB
JavaScript

import { useState, useCallback } from 'react';
const BASE = '/api/profile';
async function apiFetch(path, options = {}) {
const res = await fetch(`${BASE}${path}`, {
headers: { 'Content-Type': 'application/json', ...options.headers },
...options,
});
if (!res.ok) {
const err = await res.json().catch(() => ({ detail: res.statusText }));
throw new Error(err.detail || res.statusText);
}
return res.json();
}
export default function usePortfolioApi() {
const [token, setToken] = useState(null);
const [authError, setAuthError] = useState('');
const authHeaders = token ? { Authorization: `Bearer ${token}` } : {};
const login = useCallback(async (password) => {
setAuthError('');
try {
const data = await apiFetch('/auth', {
method: 'POST',
body: JSON.stringify({ password }),
});
setToken(data.token);
return true;
} catch (err) {
setAuthError(err.message);
return false;
}
}, []);
const logout = useCallback(() => setToken(null), []);
// ── Public ──
const fetchPublic = useCallback(() => apiFetch('/public'), []);
// ── Profile ──
const fetchProfile = useCallback(() =>
apiFetch('/profile', { headers: authHeaders }), [token]);
const saveProfile = useCallback((data) =>
apiFetch('/profile', { method: 'PUT', headers: authHeaders, body: JSON.stringify(data) }), [token]);
// ── Careers ──
const fetchCareers = useCallback(() =>
apiFetch('/careers', { headers: authHeaders }), [token]);
const addCareer = useCallback((data) =>
apiFetch('/careers', { method: 'POST', headers: authHeaders, body: JSON.stringify(data) }), [token]);
const editCareer = useCallback((id, data) =>
apiFetch(`/careers/${id}`, { method: 'PUT', headers: authHeaders, body: JSON.stringify(data) }), [token]);
const removeCareer = useCallback((id) =>
apiFetch(`/careers/${id}`, { method: 'DELETE', headers: authHeaders }), [token]);
// ── Projects ──
const fetchProjects = useCallback(() =>
apiFetch('/projects', { headers: authHeaders }), [token]);
const addProject = useCallback((data) =>
apiFetch('/projects', { method: 'POST', headers: authHeaders, body: JSON.stringify(data) }), [token]);
const editProject = useCallback((id, data) =>
apiFetch(`/projects/${id}`, { method: 'PUT', headers: authHeaders, body: JSON.stringify(data) }), [token]);
const removeProject = useCallback((id) =>
apiFetch(`/projects/${id}`, { method: 'DELETE', headers: authHeaders }), [token]);
// ── Skills ──
const fetchSkills = useCallback(() =>
apiFetch('/skills', { headers: authHeaders }), [token]);
const addSkill = useCallback((data) =>
apiFetch('/skills', { method: 'POST', headers: authHeaders, body: JSON.stringify(data) }), [token]);
const editSkill = useCallback((id, data) =>
apiFetch(`/skills/${id}`, { method: 'PUT', headers: authHeaders, body: JSON.stringify(data) }), [token]);
const removeSkill = useCallback((id) =>
apiFetch(`/skills/${id}`, { method: 'DELETE', headers: authHeaders }), [token]);
// ── Introductions ──
const fetchIntros = useCallback(() =>
apiFetch('/introductions', { headers: authHeaders }), [token]);
const addIntro = useCallback((data) =>
apiFetch('/introductions', { method: 'POST', headers: authHeaders, body: JSON.stringify(data) }), [token]);
const editIntro = useCallback((id, data) =>
apiFetch(`/introductions/${id}`, { method: 'PUT', headers: authHeaders, body: JSON.stringify(data) }), [token]);
const removeIntro = useCallback((id) =>
apiFetch(`/introductions/${id}`, { method: 'DELETE', headers: authHeaders }), [token]);
const setMainIntro = useCallback((id) =>
apiFetch(`/introductions/${id}/main`, { method: 'PATCH', headers: authHeaders }), [token]);
return {
token, authError, login, logout,
fetchPublic,
fetchProfile, saveProfile,
fetchCareers, addCareer, editCareer, removeCareer,
fetchProjects, addProject, editProject, removeProject,
fetchSkills, addSkill, editSkill, removeSkill,
fetchIntros, addIntro, editIntro, removeIntro, setMainIntro,
};
}