티스토리 뷰
1. 이번 목표:
MySQL을 통해 RDBMS를 배워보고 MVC패턴에 대해 알아보자!
2. 배운 내용 요약
RDBMS(Relational Database Management System)
데이터를 테이블 형태로 관리하며, 관계(Relation)를 기반으로 데이터를 저장하고 조회하는 시스템
1. SQL 기본문법
1-1. 데이터 정의어 DDL
데이터베이스와 테이블 구조를 정의하거나 수정하는 명령어
CREATE: 데이터베이스나 테이블 생성
ALTER: 테이블 구조 변경
DROP: 데이터베이스나 테이블 삭제
-- 데이터베이스 생성
CREATE DATABASE mydatabase DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
-- 테이블 생성
CREATE TABLE products(
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50) NOT NULL,
model_number VARCHAR(15) NOT NULL,
series VARCHAR(30) NOT NULL
);
#### 외래키 ####
-- 부모 테이블: departments
CREATE TABLE departments (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50) NOT NULL
);
-- 자식 테이블: employees
CREATE TABLE employees (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50) NOT NULL,
department_id INT,
FOREIGN KEY (department_id) REFERENCES departments(id)
ON DELETE SET NULL
ON UPDATE CASCADE
);
-- 테이블 목록 확인
SHOW TABLES;
-- 테이블 구조 확인
DESC products;
-- 테이블 삭제
DROP TABLE products;
-- 테이블 속성 수정/삭제
ALTER TABLE products ADD new_column VARCHAR(20);
ALTER TABLE products MODIFY new_column INT;
ALTER TABLE products DROP new_column;
1-2. 데이터 조작어 DML
테이블의 데이터를 삽입, 수정, 삭제하는 명령어
INSERT: 데이터 삽입
UPDATE: 데이터 수정
DELETE: 데이터 삭제
-- 데이터 삽입
INSERT INTO users (name, email) VALUES ('John Doe', 'john@example.com');
-- 데이터 수정
UPDATE users SET name = 'Jane Doe' WHERE id = 1;
-- 데이터 삭제
DELETE FROM users WHERE id = 1;
1-3. 데이터 조회어 DQL
테이블에서 데이터를 조회하는 명령어
SELECT: 데이터 조회
GROUP BY: 데이터를 특정 컬럼 기준으로 그룹화하여 집계 연산을 수행
-- 모든 데이터 조회
SELECT * FROM users;
-- 특정 컬럼 조회
SELECT name, email FROM users;
-- 조건부 조회
-- _: 정확히 한 문자와 일치
-- %: 0개 이상의 문자와 일치
SELECT * FROM users WHERE email LIKE '%example.com';
SELECT * FROM users WHERE email LIKE 'j___@example.com';
-- 정렬된 데이터 조회
SELECT * FROM users ORDER BY created_at DESC;
-- 상위 5개 데이터 조회
SELECT * FROM users LIMIT 5;
#### GROUP BY ####
-- 예제 테이블: sales
| id | category | amount |
|-----|-----------|--------|
| 1 | electronics | 100 |
| 2 | clothing | 200 |
| 3 | electronics | 150 |
| 4 | clothing | 300 |
| 5 | groceries | 50 |
-- 카테고리별 총 매출액 계산
SELECT category, SUM(amount) AS total_sales
FROM sales
GROUP BY category;
#### HAVING ####
-- 총 매출액이 100 이상인 카테고리만 조회
SELECT category, SUM(amount) AS total_sales
FROM sales
GROUP BY category
HAVING total_sales >= 100;
#### 집계 함수 ####
-- 카테고리별 주문 수, 최대 주문 금액, 평균 금액
SELECT category,
COUNT(*) AS order_count,
MAX(amount) AS max_amount,
AVG(amount) AS avg_amount
FROM sales
GROUP BY category;
1-4. 데이터 제어어 DCL
사용자 권한을 관리하거나 데이터 접근을 제어하는 명령어
GRANT: 권한 부여
REVOKE: 권한 회수
-- 사용자에게 모든 권한 부여
GRANT ALL PRIVILEGES ON example_db.* TO 'username'@'localhost';
-- 사용자 권한 회수
REVOKE ALL PRIVILEGES ON example_db.* FROM 'username'@'localhost';
2. SQL JOIN
조인은 여러 테이블을 결합하여 데이터를 조회하는 SQL문법.
조인은 데이터를 한 테이블에서만 가져올 수 없을 때 유용
예제
-- 테이블1: Customers
CREATE TABLE Customers (
CustomerID INT PRIMARY KEY,
CustomerName VARCHAR(100)
);
-- 테이블2: Orders
CREATE TABLE Orders (
OrderID INT PRIMARY KEY,
CustomerID INT,
OrderDate DATE,
FOREIGN KEY (CustomerID) REFERENCES Customers(CustomerID)
);
-- 데이터 삽입
INSERT INTO Customers VALUES (1, '홍길동'), (2, '이순신'), (3, '김유신');
INSERT INTO Orders VALUES (101, 1, '2024-12-01'), (102, 2, '2024-12-02');
(1) INNER JOIN
- 두 테이블에서 조건에 일치하는 데이터만 반환.
- 공통된 데이터만 결과로 가져옴.
SELECT 테이블1.컬럼, 테이블2.컬럼
FROM 테이블1
INNER JOIN 테이블2 ON 테이블1.컬럼 = 테이블2.컬럼;
SELECT Customers.CustomerName, Orders.OrderDate
FROM Customers
INNER JOIN Orders ON Customers.CustomerID = Orders.CustomerID;
| CustomerName | OrderDate |
| 홍길동 | 2024-12-01 |
| 이순신 | 2024-12-02 |
(2) LEFT JOIN (또는 LEFT OUTER JOIN)
- 왼쪽 테이블의 모든 데이터와 조건에 맞는 오른쪽 테이블 데이터를 반환.
- 오른쪽 테이블에 일치하지 않는 데이터는 NULL로 표시.
SELECT 테이블1.컬럼, 테이블2.컬럼
FROM 테이블1
LEFT JOIN 테이블2 ON 테이블1.컬럼 = 테이블2.컬럼;
SELECT Customers.CustomerName, Orders.OrderDate
FROM Customers
LEFT JOIN Orders ON Customers.CustomerID = Orders.CustomerID;
| CustomerName | OrderDate |
| 홍길동 | 2024-12-01 |
| 이순신 | 2024-12-02 |
| 김유신 | NULL |
(3) RIGHT JOIN (또는 RIGHT OUTER JOIN)
- 오른쪽 테이블의 모든 데이터와 조건에 맞는 왼쪽 테이블 데이터를 반환.
- 왼쪽 테이블에 일치하지 않는 데이터는 NULL로 표시.
SELECT 테이블1.컬럼, 테이블2.컬럼
FROM 테이블1
RIGHT JOIN 테이블2 ON 테이블1.컬럼 = 테이블2.컬럼;
SELECT Customers.CustomerName, Orders.OrderDate
FROM Customers
RIGHT JOIN Orders ON Customers.CustomerID = Orders.CustomerID;
| CustomerName | OrderDate |
| 홍길동 | 2024-12-01 |
| 이순신 | 2024-12-02 |
(4) FULL OUTER JOIN
- 두 테이블의 모든 데이터를 반환하며, 일치하지 않는 데이터는 NULL로 채움.
- MySQL에서 FULL OUTER JOIN은 직접 지원되지 않으며, UNION을 사용해 구현 가능.
SELECT 테이블1.컬럼, 테이블2.컬럼
FROM 테이블1
LEFT JOIN 테이블2 ON 테이블1.컬럼 = 테이블2.컬럼
UNION
SELECT 테이블1.컬럼, 테이블2.컬럼
FROM 테이블1
RIGHT JOIN 테이블2 ON 테이블1.컬럼 = 테이블2.컬럼;
SELECT Customers.CustomerName, Orders.OrderDate
FROM Customers
LEFT JOIN Orders ON Customers.CustomerID = Orders.CustomerID
UNION
SELECT Customers.CustomerName, Orders.OrderDate
FROM Customers
RIGHT JOIN Orders ON Customers.CustomerID = Orders.CustomerID;
| CustomerName | OrderDate |
| 홍길동 | 2024-12-01 |
| 이순신 | 2024-12-02 |
| 김유신 | NULL |
(5) CROSS JOIN
- 두 테이블의 카테시안 곱(Cartesian Product)을 반환.
- 모든 조합의 데이터를 반환.
SELECT 테이블1.컬럼, 테이블2.컬럼
FROM 테이블1
CROSS JOIN 테이블2;
SELECT Customers.CustomerName, Orders.OrderID
FROM Customers
CROSS JOIN Orders;
| CustomerName | OrderID |
| 홍길동 | 101 |
| 홍길동 | 102 |
| 이순신 | 101 |
| 이순신 | 102 |
| 김유신 | 101 |
| 김유신 | 102 |
3. MVC패턴
1-1. 구성 요소
Model: 데이터와 관련된 로직을 처리. 데이터베이스와의 상호작용, 데이터를 가져오거나 저장하는 역할
View: 사용자에게 보여지는 화면. EJS를 사용하여 동적인 HTML을 생성.
Controller: Model과 View를 연결하며, 사용자의 요청을 처리하고 적절한 View와 데이터를 반환.
project/
├── app.js # 애플리케이션 엔트리 포인트
├── models/ # Model 관련 파일
│ └── todo.js
├── controllers/ # Controller 관련 파일
│ └── todoController.js
├── views/ # View 관련 파일
│ ├── index.ejs
│ └── error.ejs
└── routes/ # 라우팅 관련 파일
└── todoRoutes.js
1-2. app.js: Express 설정
const express = require('express');
const app = express();
const routes = require('./routes/routes');
// 미들웨어 설정
app.set('view engine', 'ejs');
app.use(express.urlencoded({ extended: false }));
// 라우터 연결
app.use('/todos', routes);
// 에러 처리
app.use((req, res) => {
res.status(404).render('error', { message: 'Page not found' });
});
app.listen(8080, () => {
console.log('Server is running on http://localhost:8080');
});
1-3. Model: models./todo.js
let todos = []; // 임시 데이터베이스
module.exports = {
getTodos: () => todos,
addTodo: (task) => todos.push({ id: todos.length + 1, task }),
removeTodo: (id) => {
todos = todos.filter((todo) => todo.id !== id);
},
};
1-4. Controller: controllers/todoController.js
const TodoModel = require('../models/todo');
module.exports = {
getAllTodos: (req, res) => {
const todos = TodoModel.getTodos();
res.render('index', { todos });
},
createTodo: (req, res) => {
const { task } = req.body;
TodoModel.addTodo(task);
res.redirect('/todos');
},
deleteTodo: (req, res) => {
const { id } = req.params;
TodoModel.removeTodo(parseInt(id));
res.redirect('/todos');
},
};
1-5. Routes: routes/routes.js
const express = require('express');
const router = express.Router();
const todoController = require('../controllers/todoController');
// 라우트 정의
router.get('/', todoController.getAllTodos);
router.post('/', todoController.createTodo);
router.post('/delete/:id', todoController.deleteTodo);
module.exports = router;
1-6. Views: views/index.ejs
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Todo List</title>
</head>
<body>
<h1>Todo List</h1>
<ul>
<% todos.forEach(todo => { %>
<li>
<%= todo.task %>
<form action="/todos/delete/<%= todo.id %>" method="post" style="display:inline;">
<button type="submit">Delete</button>
</form>
</li>
<% }); %>
</ul>
<form action="/todos" method="post">
<input type="text" name="task" placeholder="New task" required>
<button type="submit">Add</button>
</form>
</body>
</html>
1-7. 주요 흐름
클라이언트가 /todos 경로에 GET 요청 → Controller가 Model에서 데이터를 가져와 View를 렌더링.
새로운 할 일을 추가하기 위해 POST 요청 → Controller가 Model에 데이터를 추가하고 View를 갱신.
특정 할 일을 삭제하기 위해 DELETE 요청 → Controller가 Model에서 데이터를 삭제하고 View를 갱신.
3. 회고
드디어 너무나도 재밌는 SQL시간이다! 내가 처음 개발을 접해봤던 것도 SQL을 이용해 DB 내역을 조회하는 것으로 관심을 가졌던 터라 더 반갑게 느껴졌다. 이전 회사에 면접을 볼 때도 HAVING과 WHERE의 차이점을 설명하라는 기술면접을 접해서 더 기억에 남는 SQL 수업이었던 것 같다.
또한 나는 문득 EJS와 그냥 JS파일의 차이가 무엇인지 개념적으로 정립이 잘 되지 않았다는 걸 꺠달았다. 검색을 해 보니 EJS는 JavaScript를 보완한다기보다 JavaScript를 활용해 HTML을 동적으로 생성하는 도구라고 한다. 즉, JavaScript는 로직과 동작을 정의하는 프로그래밍 언어이고, EJS는 그런 JavaScript를 활용해 템플릿(HTML)을 동적으로 렌더링할 수 있게 해주는 템플릿 엔진인 것이다. EJS는 JavaScript의 일부 기능(변수 삽입, 조건문, 반복문 등)을 HTML 템플릿 내에서 사용할 수 있게 만들어, 서버에서 동적인 웹 페이지를 생성하는 데 유용하다고 한다. 요약하면 EJS는 JavaScript의 HTML 템플릿 렌더링을 돕는 도구이지, JS를 대체하거나 보완하는 역할을 하는 것은 아니라고 하는데... 나는 왜 이렇게 개념적인 부분에서 헷갈리는지 모르겠다.
라이브러리와 프레임워크의 차이도 계속 되새겨도 헷갈리는 부분이 있다. 리액트는 라이브러리, Next.js는 프레임워크 라는 것도 알고, 프레임워크는 주어진 틀 안에서 개발자가 구축해야 한다는 사실도 알지만 무엇이 라이브러리고 무엇이 프레임워크인지 바로 결론을 내리기는 쉽지가 않다.
- 라이브러리는 개발자가 필요에 따라 호출하여 사용할 수 있는 도구로, 개발자가 흐름을 제어.
- 프레임워크는 애플리케이션 개발의 뼈대를 제공하며, 개발자는 그 프레임워크의 규칙과 구조를 따르게 된다. 프레임워크는 애플리케이션 개발의 "골격"을 정의하고, 그 위에 기능을 추가하는 형태.
라고 ChatGPT가 말해주긴 하는데, 프레임워크는 조금 더 큰 개념으로 라이브러리와 툴을 다 합한 개념으로 생각하면 될 것 같은데 이게 맞는지 모르겠다.
'SeSAC' 카테고리의 다른 글
| [새싹x코딩온] 웹 개발자 부트캠프 과정 6주차 회고(2) | Sequelize (1) | 2024.12.06 |
|---|---|
| [새싹x코딩온] 웹 개발자 부트캠프 과정 6주차 회고(1) | MVC 패턴과 MYSQL 연결 (0) | 2024.12.04 |
| [새싹x코딩온] 웹 개발자 부트캠프 과정 5주차 회고(1) | multer 모듈을 이용한 파일 업로드 (0) | 2024.11.25 |
| [새싹x코딩온] 웹 개발자 부트캠프 과정 4주차 회고(2) | form으로 get, post 요청 보내기 / express 서버에서의 비동기 http통신 (0) | 2024.11.21 |
| [새싹x코딩온] 웹 개발자 부트캠프 과정 4주차 회고(1) | Node.js (Express, ejs), 비동기처리 (1) | 2024.11.18 |
