웹과 모바일 앱 개발을 쉽고 빠르게 할 수 있도록 도와주는 클라우드 기반 백엔드 서비스.
BaaS를 사용하면 복잡한 백엔드 시스템을 직접 관리하지 않아도 됨.
그 결과 프론트엔드 개발에 더 집중 가능함.
웹 애플리케이션은 크게 세 부분으로 구성.
프론트엔드 개발에 집중하고 싶어도 서버 설정, 보안, 인증까지 직접 처리해야 한다면 부담 큼.
서버 구축 없이 필요한 기능을 클라우드에서 바로 사용 가능함.
Supabase는 PostgreSQL 기반의 오픈 소스 BaaS 플랫폼.
관계형 데이터를 기반으로 하면서 실시간 데이터 변경 감지 기능 제공.
관계형 데이터베이스 사용 시 데이터 간 관계 표현 용이함. 데이터 변경 시 UI가 자동으로 반영됨.
Supabase는 관계형 데이터베이스를 사용함.
실무에서는 데이터 일관성과 정확성이 중요하며 대부분의 기업 시스템은 관계형 데이터베이스 사용함.
-- 주문과 연관된 고객 정보를 함께 조회
SELECT orders.order_id, customers.name
FROM orders
JOIN customers ON orders.customer_id = customers.id
WHERE customers.region = 'Asia';
Firebase도 실시간 기능 제공하지만 Supabase는 관계형 데이터베이스의 장점과 결합하여 제공.
실시간 데이터 반영은 현대 웹 애플리케이션에서 필수 요소.
예를 들어 주식 거래, 실시간 대시보드, 협업 도구 등 실시간 데이터 처리가 중요한 어플리케이션 개발에 매우 유용.
// 제품 가격 변경을 실시간으로 구독하는 코드
import { supabase } from './supabaseClient';
function subscribeToPriceChanges() {
supabase
.from('products')
.on('UPDATE', payload => {
alert(`Price updated! New price: ${payload.new.price}`);
})
.subscribe();
}
npm install @supabase/supabase-js
import { createClient } from "@supabase/supabase-js";
// project url
const SUPABASE_API_URL = "YOUR_SUPABASE_API_URL";
// anon public key
const SUPABASE_API_KEY = "YOUR_SUPABASE_API_KEY";
const supabase = createClient(SUPABASE_API_URL, SUPABASE_API_KEY);
export default supabase;
설치 후 Supabase 클라이언트 초기화하는 코드 작성.
이를 위해 Supabase 프로젝트의 URL과 API 키가 필요함.
Supabase 콘솔에서 직접 데이터베이스에 데이터 입력 가능.
웹사이트 프로젝트 대시보드에서 Table Editor를 사용해 행 추가 가능.
useEffect와 useState를 사용하여 데이터 조회 처리. 조회된 데이터 화면에 출력.
// src/App.jsx
import "./App.css";
import FetchData from "./components/FetchData";
function App() {
return (
<>
<h1>Supabase</h1>
<FetchData />
</>
);
}
export default App;
// src/components/FetchData.jsx
import { useEffect, useState } from "react";
import supabase from "../supabaseClient";
const FetchData = () => {
const [users, setUsers] = useState([]);
useEffect(() => {
const fetchData = async () => {
const { data, error } = await supabase.from("NACAMP_SAMPLE").select("*");
if (error) {
console.log("error => ", error);
} else {
console.log("data => ", data);
setUsers(data);
}
};
fetchData();
}, []);
return (
<div>
<h3>유저정보</h3>
{users.map((user) => {
return (
<div
key={user.id}
style={{
border: "1px solid black",
}}
>
<h5>아이디 : {user.id}</h5>
<h5>이름 : {user.name}</h5>
<h5>나이 : {user.age}</h5>
<h5>주소 : {user.address}</h5>
</div>
);
})}
</div>
);
};
export default FetchData;
입력 폼을 통해 데이터 추가 가능. insert 메서드 사용.
// src/App.jsx
import "./App.css";
import AddData from "./components/AddData";
import FetchData from "./components/FetchData";
function App() {
return (
<>
<h1>Supabase</h1>
<FetchData />
<AddData />
</>
);
}
export default App;
// src/components/AddData.jsx
import React, { useState } from "react";
import supabase from "../supabaseClient";
const AddData = () => {
const [name, setName] = useState("");
const [age, setAge] = useState(0);
const [address, setAddress] = useState("");
const handleAdd = async () => {
const { data, error } = await supabase.from("NACAMP_SAMPLE").insert({
name,
age,
address,
});
if (error) {
console.log("error => ", error);
} else {
alert("데이터가 정상적으로 입력됐습니다.");
console.log("data => ", data);
}
};
return (
<div
style={{
border: "1px solid red",
}}
>
<h2>데이터 추가 로직</h2>
<div>
이름 :{" "}
<input
type="text"
value={name}
onChange={(e) => {
setName(e.target.value);
}}
/>
</div>
<div>
나이 :{" "}
<input
type="number"
value={age}
onChange={(e) => {
setAge(e.target.value);
}}
/>
</div>
<div>
주소 :{" "}
<input
type="text"
value={address}
onChange={(e) => {
setAddress(e.target.value);
}}
/>
</div>
<button onClick={handleAdd}>등록</button>
</div>
);
};
export default AddData;
특정 ID 기준 데이터 수정 가능. update와 eq 조건 사용.
// src/App.jsx
import "./App.css";
import AddData from "./components/AddData";
import FetchData from "./components/FetchData";
import UpdateData from "./components/UpdateData";
function App() {
return (
<>
<h1>Supabase</h1>
<FetchData />
<UpdateData />
<AddData />
</>
);
}
export default App;
// src/components/UpdateData.jsx
import React, { useState } from "react";
import supabase from "../supabaseClient";
const UpdateData = () => {
const [targetId, setTargetId] = useState(0);
const [address, setAddress] = useState("");
const handleChange = async () => {
const { error } = await supabase
.from("NACAMP_SAMPLE")
.update({
address,
})
.eq("id", targetId);
if (error) {
console.log("error => ", error);
}
};
return (
<div
style={{
border: "1px solid blue",
}}
>
<h2>데이터 수정 로직</h2>
아이디 :{" "}
<input
type="number"
value={targetId}
onChange={(e) => setTargetId(e.target.value)}
/>
<br />
수정주소 :{" "}
<input
type="text"
value={address}
onChange={(e) => setAddress(e.target.value)}
/>
<button onClick={handleChange}>변경</button>
</div>
);
};
export default UpdateData;
ID 기준 데이터 삭제 처리. delete 메서드 사용.
// src/App.jsx
import "./App.css";
import AddData from "./components/AddData";
import DeleteData from "./components/DeleteData";
import FetchData from "./components/FetchData";
import UpdateData from "./components/UpdateData";
function App() {
return (
<>
<h1>Supabase</h1>
<DeleteData />
<FetchData />
<UpdateData />
<AddData />
</>
);
}
export default App;
// src/components/DeleteData.jsx
import React, { useState } from "react";
import supabase from "../supabaseClient";
const DeleteData = () => {
const [targetId, setTargetId] = useState(0);
const handleDelete = async () => {
const { error } = await supabase
.from("NACAMP_SAMPLE")
.delete()
.eq("id", targetId);
if (error) {
console.log("error => ", error);
}
};
return (
<div
style={{
border: "1px solid blue",
}}
>
<h2>데이터 삭제 로직</h2>
아이디 :{" "}
<input
type="number"
value={targetId}
onChange={(e) => setTargetId(e.target.value)}
/>
<button onClick={handleDelete}>삭제</button>
</div>
);
};
export default DeleteData;