베이직 목(24.12.12) 5주차 Database와 ORM(Prisma)
허승우 튜터님
Database란 데이터를 체계적으로 저장하고 관리하기 위한 시스템.
Relational Database(RDB, 관계형 데이터베이스)
데이터를 테이블 형식으로 저장하며, 각 테이블은 행(Row)과 열(Column)로 구성됩니다.
테이블 간의 관계를 정의하여 데이터를 효율적으로 관리하고 중복을 최소화합니다.
DB는 도서관
책장이 테이블
데이터가 책
책을 조회 = 데이터를 조회
서로 관련된 데이터(예: 도서와 대출 기록)는 도서관 시스템 내에서 연결(테이블 간 관계)되어 있습니다
열(col)이 컬럼, 행(row)이 데이터
🔹id :
- 중복된 데이터가 있어도 구분할 수 있음
ex) 동명 이인
-필요한 데이터만 빠르게 조회 가능
RDB의 주요 용어
SQL: Structured Query Language의 약자로, 데이터를 저장, 수정, 조회하는 데 사용하는 언어입니다.
🔹 Primary Key: 각 행을 고유하게 식별하는 열.
ex) id
🔹 Foreign Key: 다른 테이블과의 관계를 정의하는 열. 일반적으로 고유한 값을 가집니다.
다른 테이블에 있는 값을 조회하기 위해 내 테이블과 이어주는 키
🔹Join : Foreign Key를 이용해 다른테이블을 이어 사용합니다.
inner join, left join, right join, full outer join
Join을 사용하는 경우
- 데이터 중복 제거: 테이블을 분리하여 저장했으므로, 관계를 맺어 조합이 필요.
- 데이터 통합: 쪼개진 데이터를 한 번에 조회.
- 효율적인 관리: 테이블 간 관계를 명확히 유지하여 데이터 무결성 보장.
- 복잡한 쿼리 처리: 하나의 쿼리로 다차원 데이터를 가져올 수 있음.
join 기본 형태 :
select *
FROM 테이블a as a join 테이블b as b on a.id = b.aId -- 외래키로 잇기
ex)
SELECT orders_table.order_name, user_table.name -- 컬럼 선택
FROM orders_table JOIN user_table ON orders_table.user_id =user_table.id; -- FK 이용하여 JOIN
inner join 교집합 :
테이블a inner join 테이블b on 조건;
left join 집합A : 앞에 있는 테이블에 연결된 값이 있는 것 들만 가져옴. 테이블b의 값이 있을수도 없을 수도 있음
테이블a left join 테이블b on 조건;
테이블a 가 한국의 도시 테이블이고 테이블b가 화석이 발견된 장소 테이블이라 할 때
- 화석이 발견되지 않은 지역만 추출 (b.fossil 값이 NULL)
SELECT *
FROM A left join B on A.postId = B.postId
WHERE B.fossil IS NULL;
- 화석이 발견된 지역만 추출 (b.fossil 값이 NOT NULL)
SELECT *
FROM A left join B on A.postId = B.postId
WHERE B.fossil IS NOT NULL;
right join 집합B : 뒤에 있는 테이블에 연결된 값이 있는 것 들만 가져옴. 테이블a의 값이 있을수도 없을 수도 있음
테이블a right join 테이블b on 조건;
full outer join : RIGHT JOIN과 LEFT JOIN의 결과를 합
MySQL에서는 full outer join을 지원하지 않아 right join과 left join을 합한것으로 쿼리를 짜야한다.
SELECT *
FROM A FULL OUTER JOIN B on A.postId = B.postId
그 외에 join
Join의 종류와 간단한 설명, 사용법
1. Equi Join (등가 조인)
- 설명: 두 테이블의 컬럼 값이 같은 데이터만 반환.
- 사용법:
SELECT Project.project_name, Employee.full_name, Employee.department FROM Project, Employee WHERE Project.employee_id = Employee.employee_id;
- 사용 이유: PK와 FK 관계를 기반으로 데이터를 연결할 때.
2. Non-Equi Join (비등가 조인)
- 설명: 컬럼 값이 아닌 다른 조건으로 조인.
- 사용법:
SELECT Employee.full_name, Project.project_name FROM Employee, Project WHERE Employee.salary > 2500000;
- 사용 이유: 숫자, 범위 등 다른 조건으로 관계를 정의할 때.
3. Natural Join (자연 조인)
- 설명: 공통 컬럼 이름을 기준으로 조인하며, 중복 컬럼 제거.
- 사용법:
SELECT * FROM Address NATURAL JOIN Employee;
- 사용 이유: 중복 컬럼 없이 깔끔한 결과가 필요할 때.
4. Outer Join (외부 조인)
4.1. Left Outer Join
- 설명: 왼쪽 테이블의 모든 데이터와 오른쪽 테이블의 매칭 데이터를 가져오며, 매칭이 없으면 NULL로 반환.
- 사용법:
SELECT * FROM Employee E LEFT JOIN Address A ON E.employee_id = A.employee_id;
4.2. Right Outer Join
- 설명: 오른쪽 테이블의 모든 데이터와 왼쪽 테이블의 매칭 데이터를 가져오며, 매칭이 없으면 NULL로 반환.
- 사용법:
SELECT * FROM Address A RIGHT JOIN Employee E ON A.employee_id = E.employee_id;
4.3. Full Outer Join (완전 외부 조인)
- 설명: 두 테이블의 모든 데이터를 가져오며, 매칭이 없으면 NULL로 반환.
- 사용법:
SELECT *
FROM Employee E LEFT JOIN Address A ON E.employee_id = A.employee_id
UNION
SELECT *
FROM Address A RIGHT JOIN Employee E ON A.employee_id = E.employee_id;
- 사용 이유: 양쪽 데이터를 모두 포함해야 할 때.
5. Self Join (셀프 조인)
- 설명: 같은 테이블을 두 번 조인하여 참조.
- 사용법:
SELECT E.employee_id, E.full_name, M.full_name AS manager_name FROM Employee E LEFT JOIN Employee M ON E.manager = M.employee_id;
- 사용 이유: 계층적 데이터나 관계를 표현할 때.
6. Semi Join (세미 조인)
- 설명: 조건을 만족하는 데이터가 다른 테이블에 존재하는지 여부만 확인.
- 사용법:
SELECT E.employee_id, E.full_name FROM Employee E WHERE EXISTS ( SELECT 1 FROM Project P WHERE P.employee_id = E.employee_id );
- 사용 이유: 연결된 데이터 여부만 확인할 때.
Object-Relational Mapping (ORM)
ORM은 프로그래밍 언어에서 데이터베이스를 조작할 수 있도록 돕는 도구로 SQL 쿼리를 직접 작성하지 않고도 데이터베이스와 상호작용이 가능합니다.
비유: ORM은 통역사. 사용자가 말하는 내용을 데이터베이스가 이해할 수 있도록 번역해주는 역할
언어별로 다양함
Prisma : Nodejs에서 사용하는 대표적인 ORM
🔹Prisma CLI 설치: 프로젝트 초기화 및 설정.
# 설치
npm install prisma @prisma/client
npx prisma init
내 프로젝트 폴더 이름
├── prisma
│ └── schema.prisma
├── .env
├── .gitignore
├── package.json
└── pakcage_lock.json
🔹 기본 틀
Prisma의 데이터베이스 URL 구현하기
데이터베이스 엔진: mysql
마스터 사용자 이름: root
마스터 암호: aaaa4321
RDS 엔드포인트: express-database.clx5rpjtu59t.ap-northeast-2.rds.amazonaws.com
Port 번호: 3306
사용할 DB 이름: prisma_crud
🔹 기본 설정
schema.prisma 파일에서 데이터베이스의 유형과 연결 정보를 설정합니다.
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "mysql" // 사용하는 DB종류로 수정하세요
url = env("DATABASE_URL") // .env 파일의 DATABASE_URL값에 연결합니다.
}
DATABASE_URL은 .env 파일에 정의합니다.
// "mysql://<계정>:<비밀번호>@<DB주소>:<포트>/<db이름>
DATABASE_URL="mysql://root:1234@localhost:3306/test?schema=public“
🔹 스키마 파일 사용하기
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
}
model User {
id Int @id @default(autoincrement()) // Primary Key
name String
gender String
createdAt DateTime @default(now())
orders Order[] // 관계 정의
}
model Order {
orderId Int @id @default(autoincrement())
orderName String
userId Int
author User @relation(fields: [userId], references: [id])
}
🔹 DB에 적용하기
npx prisma db push // 이건 지양
// 이걸로 쓰기. 마이그레이션 폴더애 생성,수정 기록이 남음
npx prisma migrate dev --name <마이그레이션 이름>
- 마이그레이션 결과
마이그레이션을 하면 이 모델을 만들 수 있는 실제 sql 파인을 만들어줌
변경사항에 대해 히스토리가 남아서 관리 용이
📦prisma
┣ 📂migrations
┃ ┣ 📂<생성일자>_<마이그레이션 이름>
┃ ┃ ┗ 📜migration.sql
┃ ┗ 📜migration_lock.toml
┗ 📜schema.prismaon
-- migration.sql
-- CreateTable
CREATE TABLE `User` (
`id` INTEGER NOT NULL AUTO_INCREMENT,
`name` VARCHAR(191) NOT NULL,
`gender` VARCHAR(191) NOT NULL,
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- CreateTable
CREATE TABLE `Order` (
`orderId` INTEGER NOT NULL AUTO_INCREMENT,
`orderName` VARCHAR(191) NOT NULL,
`userId` INTEGER NOT NULL,
PRIMARY KEY (`orderId`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- AddForeignKey
ALTER TABLE `Order` ADD CONSTRAINT `Order_userId_fkey` FOREIGN KEY (`userId`) REFERENCES `User`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
Prisma Method 사용: 애플리케이션에서 데이터베이스와 상호작용.
🔹create : 데이터 생성
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
const newUser = await prisma.user.create({
data: {
name: "김지웅",
gender: "남자"
}
});
🔹 nested write :
- 데이터 생성 시 연관된 데이터를 같이 생성
- 자동으로 트렌젝션 처리
const newUser = await prisma.user.create({
data: {
name: "김지웅",
gender: "남자",
orders: {
create: {
orderName: "아메리카노",
}
}
}
});
🔹 findUnique:
- unique로 설정된 column값을 가지고 조회하는 경우 사용.
- 속도가 빠름
- findFerst는 컬럼이 unique아닌 경우에만 사용하기
const users = await prisma.user.findUnique({
where: {
id: 1,
},
})
🔹 findMany:
- 여러가지 column을 가지고 여러개의 데이터를 조회할때 사용
- 전체 조회
const users = await prisma.user.findMany();
const users = await prisma.user.findMany();
- 조건 조회
const users = await prisma.user.findMany({
where: {
gender: "남자"
}
});
- include 조회 (join)
- 테이블a as a join 테이블b as b on a.id = b.aId 가 include로 실행됨. 무거우니 필요할 때만 사용
const orders = await prisma.order.findMany({
include: {
user: true
},
});
//
const orders = await prisma.order.findMany({
include: {
user: true,
},
});
🔹 findFirst :
- findMany와 같지만 맨 처음 조회 된 1개만 조회
- 태이블.findMany에 limit이 붙은 형태. 무거움. 유니크 있는건 .findUnique로 사용!
const users = await prisma.user.findFirst({
where: {
gender: "남자"
}
});
SQL문을 이용한 로우쿼리(프리즈마에서 제공)
🔹 SQL문법을 이용한 Raw쿼리 사용법
// prisma
const newUser = await prisma.user.create({
data: {
name: "김지웅",
gender: "남자"
}
});
// raw query
await prisma.$executeRaw`
INSERT INTO "User" ("name", "gender")
VALUES ('김지웅', '남자');
`;
🔹 Transaction을 사용한 원자성 보장방법
// nested write 사용
const newUser = await prisma.user.create({
data: {
name: "김지웅",
gender: "남자",
orders: {
create: {
orderName: "아메리카노",
}
}
}
});
// transaction 사용
await prisma.$transaction(async (tx) => {
// 첫 번째 작업: 사용자 생성
const newUser = await tx.user.create({
data: {
name: "김지웅",
gender: "남자"
},
});
// 두 번째 작업: 새로운 사용자의 주문 생성
await tx.order.create({
data: {
orderName: "아메리카노",
userId: newUser.id,
},
});
console.log("모든 작업이 성공적으로 완료되었습니다.");
});
❓join이랑 트렌젝션 중에 뭐가 더 빠르고 메모리 덜쓸까?
1. JOIN
- 사용 목적: 여러 테이블의 데이터를 한 번에 조회.
- 장점: 빠르고 네트워크 요청 최소화. 메모리 효율적.
- 단점: 복잡한 JOIN은 성능 저하 및 메모리 사용 증가 가능.
- 추천 상황: 데이터 조회.
2. TRANSACTION
- 사용 목적: 여러 작업(조회, 수정, 생성, 삭제)을 묶어서 원자성을 보장.
- 장점: 데이터 무결성 및 롤백 가능.
- 단점: 락(lock)으로 인한 성능 저하, 메모리 사용량 증가.
- 추천 상황: 종속적인 작업이 필요한 데이터 변경.
결론
- 데이터 조회: JOIN이 더 빠르고 메모리 효율적.
- 데이터 수정/생성/삭제: 종속성이 있으면 TRANSACTION필수.
- 성능 최적화를 위해 JOIN은 필요한 필드만 선택적으로 가져오고, TRANSACTION은 꼭 필요한 경우에만 사용.
숙제
저번주 express과제에서 했던 메모리 저장방식을 DB저장방식으로 변경하기
https://teamsparta.notion.site/5-Database-ORM-Prisma-55844e67285d45ccbc8b2566826773d2
※ 요약
1. Database 개념
- Database: 데이터를 체계적으로 저장하고 관리하는 시스템.
- Relational Database (RDB):
- 데이터를 테이블(행과 열) 형식으로 저장.
- 테이블 간 관계(Foreign Key)를 통해 효율적 관리 및 중복 최소화.
2. RDB 주요 용어
- SQL: 데이터베이스를 조작하는 표준 언어.
- Primary Key: 각 행을 고유하게 식별하는 열.
- Foreign Key: 다른 테이블과의 관계를 정의하는 열.
- Join: 두 테이블을 연결하여 데이터를 조회.
Join 종류:
- Inner Join: 두 테이블의 교집합 데이터 조회.
- Left Join: 왼쪽 테이블의 모든 데이터와 조건이 맞는 오른쪽 테이블 데이터 조회.
- Right Join: 오른쪽 테이블의 모든 데이터와 조건이 맞는 왼쪽 테이블 데이터 조회.
- Full Outer Join: 두 테이블의 모든 데이터를 합침. (MySQL은 지원 X)
3. ORM (Object-Relational Mapping)
- ORM: SQL 없이 프로그래밍 언어로 DB 조작.
- Prisma (Node.js에서 자주 사용):
- 설정: npm install prisma @prisma/client, npx prisma init.
- 데이터베이스 연결 정보는 .env 파일에서 설정.
- Schema 정의: schema.prisma에서 데이터 모델 정의.
4. Prisma 주요 기능
- Create: 데이터 생성.
const newUser = await prisma.user.create({ data: { name: "김지웅", gender: "남자" }, });
- Nested Write: 데이터 생성과 관련 데이터 생성.
- findUnique: Primary Key나 Unique Column을 기반으로 데이터 조회.
- findMany: 다중 조건 데이터를 조회하거나 전체 조회.
- findFirst: 조건에 맞는 첫 번째 데이터 조회.
5. Raw Query & Transaction
- Raw Query: SQL 문법을 그대로 사용하는 방법.
await prisma.$executeRaw` INSERT INTO "User" ("name", "gender") VALUES ('김지웅', '남자'); `;
- Transaction: 작업 묶음의 원자성을 보장.
await prisma.$transaction(async (tx) => { const newUser = await tx.user.create({ data: { name: "김지웅", gender: "남자" } }); await tx.order.create({ data: { orderName: "아메리카노", userId: newUser.id } }); });
6. Join vs Transaction
- Join:
- 목적: 여러 테이블 데이터 조회.
- 장점: 빠르고 네트워크 요청 감소.
- 단점: 복잡한 쿼리 시 성능 저하 가능.
- 추천: 데이터 조회 작업.
- Transaction:
- 목적: 여러 작업(조회, 수정, 생성)을 묶음으로 처리.
- 장점: 원자성 보장 및 데이터 무결성 확보.
- 단점: 락(lock)으로 인한 성능 저하 가능.
- 추천: 데이터 변경 작업.
※Tip
◆ 색인 = 인덱싱
참고
Prisma에서 트랜잭션과 다양한 부가기능 : https://dodote10.tistory.com/625
join1: https://velog.io/@pixelstudio/RDBMSMysql-JOIN%EC%97%90-%EB%8C%80%ED%95%B4%EC%84%9C
join2: https://velog.io/@leesomyoung/MySQL-Join%EC%9D%98-%EC%A2%85%EB%A5%98
'내일배움캠프_게임서버(202410) > 분반 수업 Basic-A' 카테고리의 다른 글
교육과정 틀기 (0) | 2024.12.19 |
---|---|
basic 4주차 과제 (3) | 2024.12.07 |
4주차 Express, Restful API - 숙제중 (2) | 2024.12.05 |
베이직 241203 화 - 숙제 해설 (0) | 2024.12.03 |
과제하기 24.11.28.목 Class / Promise 이해하고 활용하기 (0) | 2024.11.28 |