내일배움캠프 React_7기 TIL - 30. 데이터베이스

·2024년 11월 18일
0

1. 데이터베이스란 무엇인가?

  • 정의: 데이터베이스(Database)는 데이터를 체계적으로 저장하고 관리하는 시스템이다. 데이터베이스는 대량의 데이터를 효율적으로 관리하고 처리할 수 있는 기능을 제공한다.
  • 역할:
    • 데이터를 안전하게 보관.
    • 빠르고 정확한 데이터 검색 및 수정.
    • 여러 사용자가 데이터를 동시에 사용할 수 있도록 지원.
    • 데이터 무결성과 보안을 유지.

💡 용어 설명:
데이터 무결성: 데이터의 정확성과 일관성을 유지하는 특성.

데이터베이스의 종류

1. 관계형 데이터베이스 (RDB)

  • 정의: 관계형 데이터베이스는 데이터를 테이블 구조로 저장하며, 테이블 간의 관계를 정의한다.
  • 특징:
    • 데이터를 행(Row)과 열(Column)로 구조화.
    • SQL(Structured Query Language)을 사용하여 데이터를 검색, 삽입, 수정, 삭제.
    • ACID 특성을 준수하여 데이터의 무결성과 일관성을 보장.

💡 SQL: 데이터베이스와 대화하는 언어로, 데이터를 조회, 삽입, 수정, 삭제하는 명령어를 사용한다.

관계형 데이터베이스 예시

  1. 학생(students) 테이블
StudentIDNameMajorYear
101Alice JohnsonComputer Science3
102Bob SmithMathematics2
103Charlie BrownPhysics4
  1. 과목(courses) 테이블
CourseIDCourseNameCredits
201Data Structures4
202Linear Algebra3
203Quantum Mechanics4
  1. 수강신청(enrollments) 테이블
EnrollmentIDStudentIDCourseIDGrade
1101201A
2102202B+
3103203A-

💡 Primary Key(기본 키): 각 테이블에서 행을 고유하게 식별하는 열. 예: students 테이블의 StudentID.
Foreign Key(외래 키): 다른 테이블의 Primary Key를 참조하여 테이블 간 관계를 정의. 예: enrollments 테이블의 StudentID는 students 테이블의 StudentID를 참조.


2. 비관계형 데이터베이스 (NoSQL)

  • 정의: 비관계형 데이터베이스는 고정된 테이블 구조 없이 데이터를 다양한 형식으로 저장한다.
    • 문서(Document), 키-값(Key-Value), 그래프(Graph) 등 다양한 모델을 지원.
  • 특징:
    • 스키마가 없거나 유연함.
    • 데이터 중복을 허용.
    • 수평적 확장이 쉬워 대규모 데이터 처리에 유리.

비관계형 데이터베이스 예시

  1. MongoDB의 문서(Document) 구조

{
  "StudentID": 101,
  "Name": "Alice Johnson",
  "Major": "Computer Science",
  "Year": 3,
  "Courses": [
    {
      "CourseID": 201,
      "CourseName": "Data Structures",
      "Grade": "A"
    },
    {
      "CourseID": 202,
      "CourseName": "Linear Algebra",
      "Grade": "B+"
    }
  ]
}
  1. Firebase의 키-값(Key-Value) 구조

{
  "students": {
    "101": {
      "name": "Alice Johnson",
      "major": "Computer Science",
      "year": 3
    },
    "102": {
      "name": "Bob Smith",
      "major": "Mathematics",
      "year": 2
    }
  },
  "courses": {
    "201": {
      "name": "Data Structures",
      "credits": 4
    },
    "202": {
      "name": "Linear Algebra",
      "credits": 3
    }
  }
}

💡 특징:

  • 데이터를 중첩 형태로 저장하여 여러 관련 데이터를 한 번의 쿼리로 가져올 수 있음.
  • 관계형 데이터베이스와 달리 복잡한 조인 없이 데이터를 간단하게 처리.

3. 관계형 데이터베이스(RDB) vs 비관계형 데이터베이스(NoSQL)

구분관계형 데이터베이스 (RDB)비관계형 데이터베이스 (NoSQL)
데이터 구조정형화된 테이블 구조 (행과 열)유연한 데이터 구조 (문서, 키-값, 그래프 등)
스키마고정된 스키마 필요스키마가 없거나 유연
데이터 관계테이블 간 관계 정의데이터 간 관계가 없거나 간접적
확장성수직적 확장수평적 확장
데이터 처리 속도복잡한 조건에서 효율적 처리단순한 데이터 검색에 빠름
사용 사례금융, ERP, 전자상거래 등빅데이터, IoT, 실시간 처리 시스템 등

ACID란 무엇인가?

  • ACID는 관계형 데이터베이스에서 트랜잭션의 안전성을 보장하는 4가지 속성이다.
  1. 원자성 (Atomicity): 트랜잭션의 모든 작업이 성공적으로 완료되거나, 하나라도 실패하면 모든 작업이 취소된다.
  2. 일관성 (Consistency): 트랜잭션 후 데이터베이스는 항상 일관된 상태를 유지해야 한다.
  3. 격리성 (Isolation): 여러 트랜잭션이 동시에 실행되더라도 서로 간섭하지 않고 독립적으로 처리된다.
  4. 지속성 (Durability): 트랜잭션이 완료되면 시스템 오류가 발생하더라도 그 결과는 영구적으로 데이터베이스에 저장된다.

RDB와 NoSQL의 차이점

구분관계형 데이터베이스 (RDB)비관계형 데이터베이스 (NoSQL)
데이터 구조테이블 기반문서, 키-값, 그래프 등
확장성수직적 확장수평적 확장
데이터 처리 속도복잡한 쿼리에 효율적빠른 읽기 및 쓰기

데이터베이스 선택

  • 관계형 데이터베이스는 데이터의 무결성과 일관성이 중요한 시스템에 적합하다.
    • 예: 은행 시스템, 전자상거래, 학교 관리 시스템.
  • 비관계형 데이터베이스는 대규모 데이터 처리 및 실시간 응답이 중요한 시스템에 적합하다.
    • 예: 소셜 네트워크, IoT, 실시간 데이터 분석.

6. 이미지 업로드와 URL 처리

왜 이미지를 데이터베이스에 직접 저장하지 않고 URL을 사용하는가?

  1. 효율성
    • 이미지 파일의 크기 문제: 이미지 데이터를 데이터베이스에 저장하면 용량이 커져 성능이 저하될 수 있다.
    • 전송 속도 문제: 이미지 파일을 DB에서 직접 읽고 전송하는 것은 네트워크 대역폭을 많이 소모한다. URL을 사용하면 클라이언트가 CDN(Content Delivery Network)에서 이미지를 빠르게 로드할 수 있다.
  2. 저장소 최적화
    • Supabase의 Storage 서비스는 이미지 저장 및 전송에 특화되어 있으며, CDN을 제공하여 빠른 데이터 전송을 보장한다.
    • 데이터베이스는 텍스트 기반 메타데이터 관리에 집중하고, 이미지는 스토리지를 통해 관리된다.
  3. 확장성과 유지보수
    • 대규모 이미지 데이터의 저장, 수정, 삭제가 필요할 경우, 스토리지가 더 적합하다.
    • URL만 저장하면 백업, 복구 및 관리가 간편하고 비용 절감이 가능하다.

7. Supabase로 이미지 업로드 구현하기

  1. 코드 예제

    
    import { supabase } from "../supabase/supabaseClient";
    import { v4 as uuidv4 } from "uuid";
    
    export const uploadImage = async (file) => {
      try {
        // 이미지 업로드
        const { data: storageData, error: uploadError } = await supabase.storage
          .from("images")
          .upload(`uploads/${uuidv4()}.png`, file);
    
        if (uploadError) {
          throw new Error(uploadError.message);
        }
    
        // 공개 URL 가져오기
        const { data: publicUrlData } = supabase.storage
          .from("images")
          .getPublicUrl(storageData.path);
    
        return publicUrlData.publicUrl;
      } catch (error) {
        console.error("Error uploading image:", error);
        throw error;
      }
    };
    
  2. 활용 예시

    • 사용자 프로필 이미지 업로드
    • 게시물에 첨부된 사진 업로드
    • 전자상거래 상품 이미지 관리

2. 회원가입과 로그인 처리

왜 user 테이블에 비밀번호를 직접 저장하면 안 되는가?

  1. 보안상의 이유
    • 비밀번호를 평문으로 저장하면 데이터베이스 해킹 시 사용자의 비밀번호가 노출된다.
    • 비밀번호는 반드시 해시(Hash) 처리 후 저장해야 하며, 해시 함수는 원래 값을 복원할 수 없다. 예시: bcrypt, SHA-256.
  2. 비밀번호 해싱의 원리
    • Salted Hashing: 비밀번호에 랜덤 문자열(Salt)을 추가하여 해싱하면 동일한 비밀번호라도 서로 다른 해시 값이 생성된다.
    • bcrypt는 해싱과 솔팅을 자동으로 처리하여 안전하다.
  3. 비밀번호 관리와 인증을 분리해야 하는 이유
    • 비밀번호 관리 로직을 직접 구현하면 해싱 오류나 데이터베이스 공격에 취약해진다.
    • Supabase 같은 인증 서비스는 안전한 인증 흐름을 자동으로 처리한다.

Supabase 인증 처리 흐름

  1. 사용자가 이메일과 비밀번호를 입력하면 Supabase의 auth.signUp 또는 auth.signIn 메서드를 호출한다.
  2. Supabase는 비밀번호를 해싱하여 저장하고, 인증 토큰을 생성하여 클라이언트에 반환한다.
  3. 클라이언트는 반환된 토큰을 API 요청 시 함께 전송하여 인증된 사용자만 데이터를 처리한다.

Supabase 인증 처리 예시 코드


// 1. 회원가입 함수 (auth.signUp)
export const signUp = async (email, password) => {
  try {
    // Supabase auth.signUp 호출
    const { data, error } = await supabase.auth.signUp({
      email,
      password,
    });

    if (error) {
      throw new Error(`회원가입 실패: ${error.message}`);
    }

    console.log("회원가입 성공:", data);
    return data;
  } catch (err) {
    console.error(err.message);
    return null;
  }
};

// 2. 로그인 함수 (auth.signInWithPassword)
export const signIn = async (email, password) => {
  try {
    const { data, error } = await supabase.auth.signInWithPassword({
      email,
      password,
    });

    if (error) {
      throw new Error(`로그인 실패: ${error.message}`);
    }

    console.log("로그인 성공:", data);
    return data;
  } catch (err) {
    console.error(err.message);
    return null;
  }
};

// 3. 토큰을 이용한 API 요청
export const getUserProfile = async () => {
  try {
    const { data: { session } } = await supabase.auth.getSession();

    if (!session) {
      throw new Error("로그인된 사용자가 없습니다.");
    }

    const { data, error } = await supabase
      .from("profiles")
      .select("*")
      .eq("user_id", session.user.id);

    if (error) {
      throw new Error(`프로필 가져오기 실패: ${error.message}`);
    }

    console.log("사용자 프로필:", data);
    return data;
  } catch (err) {
    console.error(err.message);
    return null;
  }
};

실행 흐름

  1. 사용자가 회원가입signUp 함수가 호출된다.
  2. 사용자가 로그인하면 signIn 함수가 호출되고, 인증 세션 정보가 로컬 스토리지에 저장된다.
  3. 인증된 사용자만 접근 가능한 데이터를 처리할 때 getUserProfile 함수를 호출하여 세션 정보를 기반으로 데이터에 접근한다.

회원 관리 방법

  • 방법 1: Authentication 서비스만 사용
    • Supabase에서 기본적으로 제공하는 auth.users 테이블만 사용한다.
    • 장점: 간단하게 회원가입/로그인 구현 가능.
    • 단점: 데이터베이스 쿼리 시 join이 어려워, 다른 테이블과 사용자 정보를 결합하기 어렵다.
  • 방법 2(추천): Users 테이블과 Authentication 분리
    • auth.users: 인증 정보(이메일, 비밀번호) 저장.
    • users 테이블: 프로필 정보(닉네임, 생년월일 등) 저장.
    • 장점: join 쿼리 활용 가능하고 사용자 프로필 정보를 유연하게 관리할 수 있다.

회원가입 로직 구현 예시


export const signUp = async ({ email, password, displayName }) => {
  try {
    const { user, error: authError } = await supabase.auth.signUp({
      email,
      password,
    });

    if (authError) {
      throw new Error(authError.message);
    }

    const { data: profileData, error: profileError } = await supabase
      .from("users")
      .insert({ id: user.id, displayName });

    if (profileError) {
      throw new Error(profileError.message);
    }

    return profileData;
  } catch (error) {
    console.error("Error during sign-up:", error);
    throw error;
  }
};

요약

  1. 이미지 업로드: 이미지는 스토리지에 저장하고 URL을 데이터베이스에 저장하는 것이 성능, 확장성, 관리 측면에서 효율적이다. Supabase의 Storage와 공개 URL 기능을 활용하여 이미지를 관리할 수 있다.
  2. 회원가입과 로그인: 비밀번호는 해싱 후 저장해야 하며, 이를 위해 Supabase 인증 서비스를 사용하면 안전하고 효율적이다. 사용자 정보 관리 방법은 auth.users만 사용하거나, users 테이블을 추가로 사용하여 관리할 수 있다.
profile
내배캠 React_7기 이수중

0개의 댓글

관련 채용 정보