☁️ goormTIL | Web #44

매루·2025년 11월 11일

goormTIL

목록 보기
42/67
post-thumbnail

📅 2025-11-11

➡️ axios, Supabase에 대해 새롭게 알게 된 것 또는 헷갈리는 부분 정리


🔎 학습 리마인드

📌 axios

💡 인스턴스 생성하기

  • Axios 인스턴스를 사용하면 공통 설정을 한 곳에서 관리할 수 있음
    // src/api/client.js
    import axios from "axios";
    
    // 공통 설정을 미리 적용한 axios 인스턴스 생성
    const client = axios.create({
      baseURL: "http://localhost:4000",
      headers: {
        "Content-Type": "application/json",
      },
    });
    
    export default client;
    // App.jsx
    import { useEffect } from "react";
    import client from "./api/client";
    
    export default function App() {
      useEffect(() => {
        client
          .get("/cafe")
          .then((res) => console.log(res.data))
          .catch((err) => console.error("요청 실패:", err));
      }, []);
    
      return <div>Axios Custom Instance 예제</div>;
    }

💡 Interceptor

  • 요청과 응답 사이에서 공통 로직을 처리할 수 있는 중간 훅(Hook)
  • 주로 요청 헤더 추가, 인증 처리, 에러 로깅, 응답 전처리 등에 사용
  • 동작 시점
    1. 요청 전(request) : 요청이 서버로 전송되기 전

    2. 응답 후(response) : 응답이 .then이나 .catch로 넘어가기 전

      import axios from "axios";
      
      const instance = axios.create({
        baseURL: "http://localhost:4000",
      });
      
      instance.interceptors.request.use(
        function (config) {
          // 요청을 보내기 전 수행
          console.log("인터셉트 요청 성공!");
          return config;
        },
        function (error) {
          // 오류 요청을 보내기 전 수행
          console.log("인터셉트 요청 오류!");
          return Promise.reject(error);
        }
      );
      
      instance.interceptors.response.use(
        function (response) {
          console.log("인터셉트 응답 받았습니다");
          // 정상 응답
          return response;
        },
        function (error) {
          console.log("인터셉트 응답을 못받았습다");
          return Promise.reject(error);
        }
      );
      
      export default instance;

📌 SupaBase

💡 BaaS (Backend as a Service)

  • 클라우드 기반 백엔드 서비스로, 서버 없이 빠르게 앱 개발 가능
  • 인증, 데이터베이스, 스토리지 등 백엔드 기능을 API로 제공

💡 웹 서비스의 세가지 핵심 구성

구성요소설명
Frontend사용자가 보는 화면 (React 등)
Backend데이터 처리, 인증, 비즈니스 로직 담당
Database실제 데이터를 저장하는 저장소

💡 장단점

장점

  • 빠른 개발: 서버 코드를 직접 작성하지 않아도 필요한 기능을 바로 연결 가능
  • 유지보수 관리: 서버관리, 보안 업데이트를 신경 쓸 필요가 거의 없음
  • 자동 확장성: 사용량 증가에 따라 서버가 자동으로 확장되어 안정적인 서비스 제공 기능

단점

  • 제한된 유연성: 제공되는 기능 외의 맞춤형 요구사항 구현은 어려움
  • 비용 예측 어려움: 트래픽 증가 시 요금이 빠르게 늘어날 수 있음
  • 플랫폼 종속성: 한 BaaS에 의존하면 다른 플랫폼으로의 이전이 까다로움

💡 데이터베이스 기본 개념

  • DB: 데이터를 구조적으로 저장하고 관리하는 시스템
  • DBMS: 데이터베이스를 관리하는 소프트웨어 (MySQL, PostgreSQL, MongoDB 등)
  • Schema: 데이터의 구조 (컬럼, 타입, 제약조건 등)

💡 NoSQL vs. SQL

구분NoSQLSQL
구조JSON / key-value테이블 기반
스키마유연함 (필드를 자유롭게 추가 가능)고정 구조 (엄격한 구조)
관계없음외래키(Foreign Key)로 연결
예시MongoDB, Firebase 등MySQL, PostgreSQL, Oracle, SQLite 등

💡 Supabase

  • PostgreSQL 기반의 오픈소스 BaaS
  • Firebase 대안으로 주목받는 서비스
  • 관계형 DB + 실시간 기능 + 인증/스토리지 지원
기능설명
관계형 데이터 관리SQL, JOIN, Foreign Key, Transaction 지원
실시간 데이터 반응DB 변경 시 UI 자동 갱신 가능
인증/스토리지 내장회원가입, 로그인, 파일 업로드 쉽게 구현 가능
오픈소스무료로 시작 가능, 자가 호스팅도 가능

💡 React에서 Supabase 사용하기

  1. 설치

    yarn add @supabase/supabase-js
  1. 프로젝트 생성 후, 테이블 만들고 데이터 넣기

    🔗 https://supabase.com/

  1. 초기화 파일
    • 설치 후 프로젝트 루트에 Supabase 클라이언트를 초기화하는 파일 생성
      // src/supabaseClient.js
      import { createClient } from "@supabase/supabase-js";
      
      // Supabase 프로젝트 설정 정보
      const SUPABASE_URL = "YOUR_SUPABASE_URL";
      const SUPABASE_ANON_KEY = "YOUR_SUPABASE_ANON_KEY";
      
      // 클라이언트 생성
      const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY);
      
      export default supabase;
      • API KEY 위치


💡 실습

🔗 https://supabase.com/docs/reference/javascript/introduction

  • 데이터 불러오기 (Read)
    import "./App.css";
    import FetchData from "./components/FetchData";
    
    function App() {
      return (
        <>
          <h1>Supabase 데이터 조회</h1>
          <FetchData />
        </>
      );
    }
    
    export default App;
    import { useEffect, useState } from "react";
    import supabase from "../supabaseClient";
    
    const FetchData = () => {
      const [users, setUsers] = useState([]);
    
      useEffect(() => {
        const loadUsers = async () => {
          const { data, error } = await supabase.from("goorm").select("*");
          if (error) {
            console.error("데이터 불러오기 실패:", error.message);
            return;
          }
          setUsers(data);
        };
    
        loadUsers();
      }, []);
    
      return (
        <div>
          <h3>유저 목록</h3>
          {users.map((user) => (
            <div
              key={user.id}
              style={{ border: "1px solid #aaa", padding: "8px", marginBottom: "6px" }}
            >
              <p>아이디: {user.id}</p>
              <p>이름: {user.name}</p>
              <p>나이: {user.age}</p>
              <p>주소: {user.address}</p>
            </div>
          ))}
        </div>
      );
    };
    
    export default FetchData;
  • 데이터 추가 (Create)
    import "./App.css";
    import AddData from "./components/AddData";
    import FetchData from "./components/FetchData";
    
    function App() {
      return (
        <>
          <h1>Supabase</h1>
          <FetchData />
          <AddData />
        </>
      );
    }
    
    export default App;
    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("goorm").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;
  • 데이터 수정 (Update)
    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;
    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("goorm")
          .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;
  • 데이터 삭제 (Delete)
    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;
    import React, { useState } from "react";
    import supabase from "../supabaseClient";
    
    const DeleteData = () => {
      const [targetId, setTargetId] = useState(0);
    
      const handleDelete = async () => {
        const { error } = await supabase
          .from("goorm")
          .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;

0개의 댓글