회원 가입과 로그인 등 회원 관리를 구현하려면 백엔드와 데이터베이스(DB)가 필요하다.
하지만 이러한 기능을 직접 구현하는 데는 많은 시간이 걸린다.
다행히도, 이러한 작업을 쉽게 구현할 수 있는 플랫폼들이 있다.
superbase란
Supabase는 Firebase와 유사한 기능을 제공하는 오픈 소스 백엔드 플랫폼이며 웹 및 모바일 애플리케이션을 쉽고 빠르게 구축할 수 있도록 다양한 도구와 서비스를 제공한다.
쉽게말해 BaaS (Backend as a Service)로써 백엔드(+DB) 기능을 제공한다.
프로필사진변경 기능구현하기
1. Supabase 설정
Supabase를 사용하여 파일을 저장하고 사용자 프로필을 관리할 수 있습니다. Supabase는 백엔드 서비스로, 데이터베이스와 스토리지 기능을 제공합니다.
2. Supabase 클라이언트 설정
먼저, Supabase 클라이언트를 설정하여 프로젝트에서 Supabase와 통신할 수 있도록 한다.
supabaseClient.js
import { createClient } from '@supabase/supabase-js';
const supabaseKey = 'your-supabase-anon-key';
export const supabase = createClient(supabaseUrl, supabaseKey);
Supabase 클라이언트를 설정하여 프로젝트에서 Supabase API를 사용할 수 있게 합니다.
3. 파일 업로드 기능 추가 및 모달 수정
프로필 수정 모달에서 파일 업로드 입력을 추가한다. 사용자가 파일을 선택하면, 해당 파일을 Supabase 스토리지에 업로드하고, 업로드된 파일의 URL을 사용자 프로필에 저장한다.
MyPageModal 컴포넌트
import React, { useState } from 'react';
import { supabase } from '../supabaseClient';
export default function MyPageModal({ isVisible, onClose }) {
const [file, setFile] = useState(null);
const handleFileChange = (e) => {
setFile(e.target.files[0]);
};
const handleSubmit = async (e) => {
e.preventDefault();
if (!file) return;
// Upload file to Supabase
const { data, error } = await supabase.storage
.from('profiles')
.upload(`public/${file.name}`, file);
if (error) {
console.error('Error uploading file:', error);
return;
}
const fileUrl = supabase.storage
.from('profiles')
.getPublicUrl(`public/${file.name}`).publicURL;
// Update user profile with new image URL
const { error: updateError } = await supabase
.from('profiles')
.update({ avatar_url: fileUrl })
.eq('id', 'user-id'); // Update with the correct user ID
if (updateError) {
console.error('Error updating profile:', updateError);
} else {
onClose(); // Close the modal on success
}
};
if (!isVisible) return null;
return (
<div className="fixed inset-0 flex items-center justify-center z-50">
<div className="bg-white p-5 rounded shadow-lg">
<h2 className="text-xl mb-4">프로필 수정</h2>
<form onSubmit={handleSubmit}>
<div>
<label className="block mb-2">프로필 사진 업로드</label>
<input type="file" onChange={handleFileChange} />
</div>
<button type="submit" className="mt-4 p-2 bg-blue-500 text-white rounded">저장</button>
<button type="button" onClick={onClose} className="mt-4 ml-2 p-2 bg-gray-500 text-white rounded">취소</button>
</form>
</div>
</div>
);
}
- 파일 선택: 사용자가 파일을 선택할 수 있도록 <input type="file" /> 요소를 추가
- 파일 업로드: supabase.storage.from('profiles').upload() 메서드를 사용하여 파일을 Supabase 스토리지에 업로드
- URL 저장: 업로드된 파일의 URL을 Supabase 데이터베이스에 사용자 프로필의 avatar_url 필드에 저장
4. 마이페이지 컴포넌트 수정
마이페이지 컴포넌트에서 MyPageModal을 호출하고, 프로필 정보를 Supabase에서 가져와서 렌더링한다.
MyPage 컴포넌트
import React, { useState, useEffect } from 'react';
import MyPageModal from '../components/MyPageModal';
import { supabase } from '../supabaseClient';
export default function MyPage() {
const [showModal, setShowModal] = useState(false);
const [profile, setProfile] = useState(null);
useEffect(() => {
const fetchProfile = async () => {
const { data, error } = await supabase
.from('profiles')
.select('*')
.eq('id', 'user-id') // Fetch profile for the logged-in user
.single();
if (error) {
console.error('Error fetching profile:', error);
} else {
setProfile(data);
}
};
fetchProfile();
}, []);
return (
<>
<div>헤더</div>
{/* 프로필 사진 */}
<div className="container mx-auto py-40 flex gap-20">
<div className="relative">
<div className="h-full rounded-full overflow-hidden object-contain h-48 w-48">
<img
src={profile?.avatar_url || "default-avatar-url"}
alt="프로필 사진"
/>
</div>
</div>
{/* 프로필 내용 */}
<div className="my-auto flex flex-col gap-3">
<h1 className="text-3xl font-medium">{profile?.name || "홍길동"}</h1>
<p className="text-gray-400">{profile?.status || "즐거운 날입니다."}</p>
<div className="flex item-stretch gap-4 ">
<button
data-modal-target="default-modal"
data-modal-toggle="default-modal"
className="p-2 ring-1 ring-gray-400 rounded-full"
onClick={() => setShowModal(true)}
>
프로필 수정
</button>
<button className="p-2 ring-1 ring-gray-400 rounded-full">비밀번호 변경</button>
</div>
<MyPageModal isVisible={showModal} onClose={() => setShowModal(false)} />
</div>
</div>
{/* 내가 쓴 리뷰 */}
<div>
<div className="container mx-auto">
<h1 className="text-3xl font-medium">내가 작성한 리뷰</h1>
</div>
</div>
<div className="p-5 flex flex-wrap">
{/* 리뷰 내용 반복 렌더링 */}
</div>
<div>푸더</div>
</>
);
}
- 프로필 정보 가져오기: useEffect 훅을 사용하여 컴포넌트가 마운트될 때 Supabase에서 사용자 프로필 정보를 가져오기
- 프로필 사진 표시: profile?.avatar_url을 사용하여 사용자의 프로필 사진을 표시
- 프로필 수정 모달 호출: "프로필 수정" 버튼을 클릭하면 모달을 표시하고, 모달에서 파일 업로드와 프로필 수정을 처리
코드 흐름 설명
- Supabase 클라이언트 설정: supabaseClient.js 파일에서 Supabase 클라이언트를 설정하여 프로젝트에서 Supabase API를 사용할 수 있게 한다.
- MyPage 컴포넌트:
- useEffect를 사용하여 컴포넌트가 마운트될 때 Supabase에서 사용자 프로필 정보를 가져와 profile 상태에 저장한다.
- 프로필 사진과 프로필 정보를 화면에 표시한다.
- "프로필 수정" 버튼을 클릭하면 showModal 상태를 true로 설정하여 MyPageModal을 표시한다.
- MyPageModal 컴포넌트:
- 사용자가 파일을 선택하면 handleFileChange 함수가 호출되어 선택한 파일을 file 상태에 저장한다.
- 사용자가 "저장" 버튼을 클릭하면 handleSubmit 함수가 호출되어 파일을 Supabase 스토리지에 업로드하고, 업로드된 파일의 URL을 Supabase 데이터베이스에 사용자 프로필의 avatar_url 필드에 저장한다.
- 프로필 수정이 완료되면 모달을 닫고, 업데이트된 프로필 정보를 MyPage 컴포넌트에서 다시 표시한다.
이렇게 하여
사용자가 마이페이지에서 프로필 사진을 수정할 수 있고,
수정된 사진은 Supabase 스토리지에 저장되며,
사용자 프로필에 반영된다.