얼라리 하고 계정 가입이 안되어서…밤새우면서 뭐지 하고 원인 찾다
알고보니 내 권한 밖의 일이라 결국 오전에 해결할 수 밖에 없던 일이었던것을 알고 멘붕 온 사람?
..That's me.
오늘도 강제 야근인 림졍은 오늘도 챌린지 강의 정리 할겁니다.
가보자고, goyo.
챌린지반 강의 정리 - Database 기초
야 DB... 앞으로 친하게 지내자?
DB(데이터베이스)
데이터를 체계적으로 저장하고 관리하는 시스템
단순히 파일로 저장 X, 대량의 데이터를 빠르고 안전하게 처리할 수 있도록 도와주는 시스템!
DB의 역할
- 데이터를 안전하게 보관
- 데이터를 빠르게 검색 및 수정 가능
- 여러 사용자가 동시 사용 가능.
- 데이터 무결성과 보안을 유지한다.
💡 Tip
- “데이터 무결성”이란 데이터의 정확성과 일관성을 유지하는 특성이다!
DB 종류
1) 관계형 데이터베이스 (RDB)
데이터를 테이블 구조로 저장하고, 테이블 간의 관계를 정의한다.
- RDB의 특징
- 데이터를 행(Row)과 열(Column)로 구조화.
- 데이터를 관리하기 위해 SQL 언어를 사용함.
- ACID(원자성, 일관성, 격리성, 지속성) 특성을 지켜 데이터 안정성을 보장.
예시
1. 학생(students) 테이블
StudentID | Name | Major | Year |
101 | Alice Johnson | Computer Science | 3 |
102 | Bob Smith | Mathematics | 2 |
103 | Charlie Brown | Physics | 4 |
2. 과목(courses) 테이블
CourseID | CourseName | Credits |
201 | Data Structures | 4 |
202 | Linear Algebra | 3 |
203 | Quantum Mechanics | 4 |
3. 수강신청(enrollments) 테이블
EnrollmentID | StudentID | CourseID | Grade |
1 | 101 | 201 | A |
2 | 102 | 202 | B+ |
3 | 103 | 203 | A- |
💡 용어 정리
- Primary Key: 고유하게 식별 가능한 열!
- Foreign Key: 다른 테이블의 기본 키를 참조하는 열.
2) 비관계형 데이터베이스 (NoSQL)
정해진 테이블 구조 없이 데이터를 다양한 형식으로 저장.
- NoSQL의 특징
- 유연한 데이터 구조를 가짐.
- 대규모 데이터와 실시간 처리에 유리.
- JSON 형식의 데이터를 주로 사용.
예시
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+"
}
]
}
💡 특징
- 하나의 문서에 학생 정보와 수강 과목 정보를 중첩 형태로 저장.
- 관계형 데이터베이스와 달리 데이터를 한 번의 쿼리로 가져올 수 있음.
2. 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
}
}
}
💡 특징
- 데이터를 키-값 쌍으로 저장하며, 계층적 구조로 접근.
- 데이터를 동적으로 추가하거나 삭제하기 쉬움.
RDB vs NoSQL
RDB와 NoSQL의 주요 특징
구분 | 관계형 데이터베이스 (RDB) | 비관계형 데이터베이스 (NoSQL) |
데이터 구조 | 정형화된 테이블 구조 (행과 열 기반) | 유연한 데이터 구조 (문서, 키-값, 그래프 등) |
스키마 | 고정된 스키마 필요 | 스키마가 없거나 유연 |
데이터 관계 | 테이블 간 관계를 명확히 정의 | 데이터 간 관계가 없거나 간접적으로 관리 |
확장성 | 수직적 확장 (더 강력한 서버로 업그레이드) | 수평적 확장 (서버 추가로 용량 및 처리 속도 증가) |
데이터 처리 속도 | 복잡한 조건에서도 효과적으로 데이터를 검색 및 처리 가능 | 단순한 데이터 검색, 빠른 읽기 및 쓰기에 적합 |
데이터 일관성 | 높은 데이터 무결성과 일관성을 보장 (*ACID 준수) | 유연한 구조지만 일관성은 데이터베이스 종류에 따라 다름 |
사용 사례 | 금융, ERP, 전자상거래 등 데이터 정확성이 중요한 시스템 | 빅데이터 분석, 소셜 네트워크, IoT, 실시간 처리 시스템 등 |
+) ACID
RDB에서 트랜잭션의 안전성을 보장하는 4가지 속성
- 원자성 (Atomicity): 트랜잭션의 모든 작업이 성공적으로 완료되거나, 하나라도 실패하면 모든 작업이 취소된다.
- 일관성 (Consistency): 트랜잭션 후 데이터베이스는 항상 일관된 상태를 유지해야 한다.
- 격리성 (Isolation): 여러 트랜잭션이 동시에 실행되더라도 서로 간섭하지 않고 독립적으로 처리된다.
- 지속성 (Durability): 트랜잭션이 완료되면 시스템 오류가 발생하더라도 그 결과는 영구적으로 데이터베이스에 저장된다.
RDB와 NoSQL, 뭐가 좋을까?
RDB가 적합한 경우
- 데이터 간 관계가 중요할 때.
- 데이터 정확성과 무결성이 필수일 때.
NoSQL이 적합한 경우
- 대규모 데이터를 처리하거나, 빠른 읽기/쓰기가 필요할 때.
- 데이터 구조가 자주 변경될 때.
이미지 업로드 / URL 처리
왜 DB에 직접 저장하지 않고 URL을 사용하는 걸까?
1) 효율성
- 이미지 크기 문제
이미지를 데이터베이스에 직접 저장하면 데이터 용량이 커져 성능이 느려질 수 있음. - 전송 속도 문제
이미지를 DB에서 읽어서 전송하면 네트워크 대역폭을 과도하게 사용함.
→ URL을 사용하면 CDN(Content Delivery Network)을 통해 빠르게 로드 가능!
2) 저장소 최적화
- awSupabase Storage는 이미지 저장에 특화된 기능을 제공하고,
CDN을 통해 빠른 데이터 전송을 보장함. - 데이터베이스는 텍스트 같은 메타데이터를 관리하고, 이미지는 스토리지에서 관리!
3) 확장성과 유지보수
- 대규모 이미지 데이터를 다룰 때 스토리지가 더 효율적.
- URL만 저장하면 관리가 훨씬 간편하고 비용도 절감 가능.
- Supabase로 이미지 업로드 구현하기
import { supabase } from "../supabase/supabaseClient";
import { v4 as uuidv4 } from "uuid";
export const uploadImage = async (file) => {
try {
// 1. 이미지 업로드
const { data: storageData, error: uploadError } = await supabase.storage
.from("images")
.upload(`uploads/${uuidv4()}.png`, file);
if (uploadError) throw new Error(uploadError.message);
// 2. 업로드된 이미지의 공개 URL 가져오기
const { data: publicUrlData } = supabase.storage
.from("images")
.getPublicUrl(storageData.path);
return publicUrlData.publicUrl;
} catch (error) {
console.error("Error uploading image:", error);
throw error;
}
};
활용 예시
- 사용자 프로필 이미지 업로드
- 게시물 사진 첨부
- 전자상거래 상품 이미지 관리
회원가입과 로그인 처리
왜 비밀번호를 user 테이블에 직접 저장하면 안 되는 걸까?
1) 보안상의 이유
- 비밀번호를 평문으로 저장하면 해킹 시 사용자 정보가 노출될 위험이 큼.
- 비밀번호는 반드시 해시(Hash) 처리 후 저장해야 함.
- 예시: bcrypt, SHA-256
2) 비밀번호 해싱 원리
- Salted Hashing
비밀번호에 랜덤 문자열(Salt)을 추가하여 해싱하면 같은 비밀번호라도 다른 해시 값이 생성됨. - bcrypt는 해싱과 솔팅을 자동으로 처리하므로 안전함.
3) 비밀번호 관리와 인증 분리의 중요성
- 해싱 로직 오류, DB 공격에 취약해질 위험 있음.
- Supabase 같은 인증 서비스는 안전한 인증 흐름을 자동으로 처리해줌.
Supabase 인증 처리 흐름
- 사용자가 이메일/비밀번호 입력 → auth.signUp 또는 auth.signIn 호출
- Supabase가 비밀번호를 해싱하여 저장 + 인증 토큰 생성 후 반환
- 클라이언트가 API 요청 시 이 토큰을 포함 → 인증된 데이터만 접근 가능
Supabase 인증 처리 코드
1) 회원가입
export const signUp = async (email, password) => {
try {
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) 로그인
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) 인증된 사용자 데이터 가져오기
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: 기본 Authentication 서비스만 사용
- 구조: Supabase에서 기본 제공하는 auth.users 테이블만 사용
- 장점: 간단하고 빠르게 회원가입/로그인 구현 가능
- 단점: 다른 테이블과의 join이 어려움
방법 2(추천): Authentication과 Users 테이블 분리
- 구조
- auth.users: 인증 정보 저장
- users: 프로필 정보 저장 (ex. 닉네임, 생년월일)
- 장점: 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;
}
};
마무리 - 아 프로젝트 힘들다.
챌린지반 정리 요약, 3줄로 정리하면 요렇습니당.
이미지? 스토리지 쓰자.
비밀번호? 안전하게 해시로 관리하자.
Supabase? 쓰면 편하고, 잘 쓰면 더 편하다.
와아 ^-^)b 이제 저는 프로젝트 하러갈게요...
(정말 하기싫다. ㅇTL)
KPT 회고
- Keep : 수수수 수빠베이스.. 이제는 조금 알 것 같아요
- Problem : 수면시간이 부족해요.
- Try : ... 언제 잘 수 있죠? ㅠㅡㅠ
'React TIL' 카테고리의 다른 글
[React] Day_46 팀 프로젝트 후기 (1) | 2024.11.22 |
---|---|
[React] Day_45 데일리 정리 (0) | 2024.11.21 |
[React] Day_43 데일리 정리 (1) | 2024.11.18 |
[React] Day_42 개인 프로젝트 후기 (1) | 2024.11.15 |
[React] Day_41 데일리 정리 (3) | 2024.11.14 |