Mengimplementasikan User Profile Features dengan Claude Code
Learn about implementing user profile features using Claude Code. Includes practical code examples.
penggunaprofil機能 dengan Claude Code: pembangunan
penggunaprofil SNS、SaaS、コミュニティサイト dll.多く aplikasi diperlukanな機能.Claude Code 使えば、アバターupload、profilEdit、publikasihalaman 含む完全な機能 efisien implementasi bisa dilakukan.
データモデル
> penggunaprofil機能 buatkan.
> アバターgambar upload、tampilan名・自己perkenalan・SNSリンク Edit、
> publikasiprofilhalaman implementasikan.
// src/types/profile.ts
export interface UserProfile {
id: string;
userId: string;
displayName: string;
bio: string;
avatarUrl?: string;
location?: string;
website?: string;
socialLinks: {
twitter?: string;
github?: string;
linkedin?: string;
};
isPublic: boolean;
createdAt: Date;
updatedAt: Date;
}
profilpembaruanAPI
// src/app/api/profile/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { auth } from '@/lib/auth';
import { prisma } from '@/lib/prisma';
import { z } from 'zod';
const profileSchema = z.object({
displayName: z.string().min(1).max(50),
bio: z.string().max(500).optional(),
location: z.string().max(100).optional(),
website: z.string().url().optional().or(z.literal('')),
socialLinks: z.object({
twitter: z.string().optional(),
github: z.string().optional(),
linkedin: z.string().optional(),
}).optional(),
isPublic: z.boolean().optional(),
});
export async function PUT(request: NextRequest) {
const session = await auth();
if (!session?.user) {
return NextResponse.json({ error: '認証 diperlukan す' }, { status: 401 });
}
const body = await request.json();
const validated = profileSchema.parse(body);
const profile = await prisma.profile.upsert({
where: { userId: session.user.id },
update: { ...validated, updatedAt: new Date() },
create: { ...validated, userId: session.user.id },
});
return NextResponse.json(profile);
}
export async function GET(request: NextRequest) {
const session = await auth();
if (!session?.user) {
return NextResponse.json({ error: '認証 diperlukan す' }, { status: 401 });
}
const profile = await prisma.profile.findUnique({
where: { userId: session.user.id },
});
return NextResponse.json(profile);
}
アバターupload
// src/app/api/profile/avatar/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { auth } from '@/lib/auth';
import { uploadToS3 } from '@/lib/storage';
import sharp from 'sharp';
export async function POST(request: NextRequest) {
const session = await auth();
if (!session?.user) {
return NextResponse.json({ error: '認証 diperlukan す' }, { status: 401 });
}
const formData = await request.formData();
const file = formData.get('avatar') as File;
if (!file) {
return NextResponse.json({ error: 'file diperlukan す' }, { status: 400 });
}
// fileサイズチェック(5MB上限)
if (file.size > 5 * 1024 * 1024) {
return NextResponse.json({ error: 'fileサイズ 5MB以下 ください' }, { status: 400 });
}
const buffer = Buffer.from(await file.arrayBuffer());
// gambar リサイズ・optimasi
const optimized = await sharp(buffer)
.resize(256, 256, { fit: 'cover' })
.webp({ quality: 80 })
.toBuffer();
const key = `avatars/${session.user.id}.webp`;
const url = await uploadToS3(optimized, key, 'image/webp');
await prisma.profile.update({
where: { userId: session.user.id },
data: { avatarUrl: url },
});
return NextResponse.json({ avatarUrl: url });
}
profilEditform
// src/components/ProfileForm.tsx
'use client';
import { useState, useRef } from 'react';
import { UserProfile } from '@/types/profile';
export function ProfileForm({ profile }: { profile: UserProfile }) {
const [form, setForm] = useState(profile);
const [saving, setSaving] = useState(false);
const fileInputRef = useRef<HTMLInputElement>(null);
const handleAvatarChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
if (!file) return;
const formData = new FormData();
formData.append('avatar', file);
const res = await fetch('/api/profile/avatar', { method: 'POST', body: formData });
const data = await res.json();
setForm((prev) => ({ ...prev, avatarUrl: data.avatarUrl }));
};
const handleSave = async () => {
setSaving(true);
await fetch('/api/profile', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(form),
});
setSaving(false);
};
return (
<div className="max-w-2xl mx-auto space-y-6">
<div className="flex items-center gap-6">
<div
onClick={() => fileInputRef.current?.click()}
className="w-24 h-24 rounded-full bg-gray-200 overflow-hidden cursor-pointer hover:opacity-80"
>
{form.avatarUrl ? (
<img src={form.avatarUrl} alt="アバター" className="w-full h-full object-cover" />
) : (
<div className="w-full h-full flex items-center justify-center text-gray-400 text-3xl">+</div>
)}
</div>
<input ref={fileInputRef} type="file" accept="image/*" onChange={handleAvatarChange} className="hidden" />
<div>
<p className="font-medium dark:text-white">プロフィール画像</p>
<p className="text-sm text-gray-500">JPG, PNG, WebP(最大5MB)</p>
</div>
</div>
<div className="space-y-4">
<div>
<label className="block text-sm font-medium mb-1 dark:text-gray-300">表示名</label>
<input
value={form.displayName}
onChange={(e) => setForm({ ...form, displayName: e.target.value })}
className="w-full border rounded-lg px-4 py-2 dark:bg-gray-800 dark:border-gray-700"
/>
</div>
<div>
<label className="block text-sm font-medium mb-1 dark:text-gray-300">自己紹介</label>
<textarea
value={form.bio}
onChange={(e) => setForm({ ...form, bio: e.target.value })}
className="w-full border rounded-lg px-4 py-2 h-24 dark:bg-gray-800 dark:border-gray-700"
maxLength={500}
/>
<p className="text-xs text-gray-400 mt-1">{form.bio.length}/500</p>
</div>
<button onClick={handleSave} disabled={saving} className="bg-blue-600 text-white px-6 py-2 rounded-lg disabled:opacity-50">
{saving ? 'penyimpanan中...' : 'penyimpanan'}
</button>
</div>
</div>
);
}
Artikel Terkait
gambarupload 詳細 fileuploadimplementasi、validasi 設計 formvalidasi juga bisa dijadikan referensi.
gambarpemrosesanlibrarysharp 公式dokumen(sharp.pixelplumbing.com) juga あわせてごkonfirmasi.
Related Posts
Cara Mempercepat Side Project dengan Claude Code [Dengan Contoh]
Pelajari cara mempercepat project development personal secara drastis menggunakan Claude Code. Dilengkapi contoh nyata dan workflow praktis dari ide hingga deployment.
Cara Mengotomatisasi Refactoring dengan Claude Code
Pelajari cara mengotomatisasi code refactoring secara efisien menggunakan Claude Code. Dilengkapi prompt praktis dan pola refactoring konkret untuk project nyata.
Panduan Lengkap Konfigurasi CORS dengan Claude Code
Pelajari tentang panduan lengkap konfigurasi CORS menggunakan Claude Code. Dilengkapi tips praktis dan contoh kode.