티스토리 뷰

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가 말해주긴 하는데, 프레임워크는 조금 더 큰 개념으로 라이브러리와 툴을 다 합한 개념으로 생각하면 될 것 같은데 이게 맞는지 모르겠다.

 

 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2026/06   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함