데이터를 저장 및 보존하는 시스템
데이터 베이스의 종류
관계형 데이터 베이스 시스템 (RDBMS, Relational Database Management System)
비관계형 데이터 베이스 시스템 (NoSQL, Non-relational Database Mangement System)
- 테이블들의 스키마(schema)와 테이블들의 관계를 미리 구현해야 하는 필요가 없이 데이터가 들어오는 그대로 저장
- 저장되는 데이터의 구조에 따라 달라집니다.
- Key Value DB : Key와 Value의 쌍으로 데이터가 저장되는 가장 단순한 형태의 솔루션
- Wide Columnar Store : Big Table DB라고하며, Key, Value에서 발전된 형태로 Column Family 데이터 모델을 사용
- Document DB : JSON, XML과 같은 Collection 데이터 모델 구조를 채택
- Graph DB : Nodes, Relationship, Key-Value 데이터 모델을 채용
- 장점
- 저장하는 데이터의 구조 변화에 유연하다.
- 시스템 확장하기 쉬워 스케일 아웃 방식이 가능하다.
- 방대한 양의 데이터를 저장하는데 유리
- 단점
- 데이터의 완전성이 덜 보장된다.
- 트랜젝션이 안되거나 되더라도 불안하다.
Structured Query Language이며, 관계현 데이터베이스에서 데이터를 읽거나 생성 및 수정하기 위해 사용되는 언어이며, 아래는 Mysql에서 기본적인 CRUD(Create, Read, Update, Delete)를 위한 기본적인 Query 명령어입니다. Query에서 column이나 table이름같은 고유한 값을 제외한 명령어들은 대/소문자 상관 없이 쓸수 있습니다.
SELECT
// table_name에서 column1, column2를 가져옵니다.
SELECT column1, column2 from table_name
// table_name에서 모든(*) column을 가져옵니다.
SELECT * from table_name
INSERT
// table_name에 column1에 column1_value를 column2에 column2_value를 넣습니다.
INSERT INTO table_name(column1, column2) VALUES (column1_value, column2_value)
UPDATE
// table_name에 column2가 value2인(WHERE 뒤가 조건문) row들에서 column1에 value1으로 update합니다.
UPDATE table_name SET column1 = value1 WHERE column2 = value2
DELETE
// table_name에 column1이 value1인 row들을 전부 지웁니다.
DELETE FROM table_name WHERE column1 = value1
JOIN
// table2에서 tabel1의 id와 table2에서 table1_id가 같은 값을 table1에 붙인 후 table1.column1과 table2.column2를 출력합니다.
SELECT table1.column1, table2.column2 FROM table1 JOIN table2 ON table1.id = table2.table1_id
sudo apt update
sudo apt install mariadb-server
# 보안상 해야하면 remove 할 것들은 전부 remove하세요
$> mysql_secure_installation
# 만약 remote에서 mysql을 접속하고 싶을때
# 파일에서 bind_address=127.0.0.1을 0.0.0.0으로 변경합니다.
sudo vi /etc/mysql/my.cnf
sudo service mysql restart
# 보안상 root는 remote를 못하게 막았기 때문에 원격으로 붙을 수 있는 계정을 생성해야합니다.
$> mysql -u root -p
# 사용할 스키마명 생성
mysql> CREATE database 스키마명;
# 생성한 스키마를 사용할 유저 생성
mysql> CREATE user '계정아이디'@localhost identified by '비밀번호';
mysql> CREATE user '계정아이디'@'%' identified by '비밀번호';
mysql> GRANT all privileges on '스키마명'.* to '계정아이디'@'%' identified by '비밀번호' with grant option;
mysql> flush privileges;
miniter에서 사용할 DB를 만들어 보겠습니다. 위에서 스키마명을 miniter로 하고 진행하면 됩니다.
mysql> use miniter;
mysql> CREATE TABLE users (
id INT NOT NULL AUTO_INCREMENT,
name VARCHAR(255) NOT NULL,
email VARCHAR(80) NOT NULL,
hashed_password VARCHAR(255) NOT NULL,
profile VARCHAR(2000) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (id),
UNIQUE KEY email (email)
) DEFAULT CHARSET=utf8mb4;
mysql> CREATE TABLE users_follow_list (
user_id INT NOT NULL,
follow_user_id INT NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (user_id, follow_user_id),
CONSTRAINT users_follow_list_user_id_fkey FOREIGN KEY (user_id) REFERENCES users(id),
CONSTRAINT users_follow_list_follow_user_id_fkey FOREIGN KEY (follow_user_id) REFERENCES users(id)
) DEFAULT CHARSET=utf8mb4;
mysql> CREATE TABLE tweets(
id INT NOT NULL AUTO_INCREMENT,
user_id INT NOT NULL,
tweet VARCHAR(300) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
CONSTRAINT tweets_user_id_fkey FOREIGN KEY (user_id) REFERENCES users(id)
)DEFAULT CHARSET=utf8mb4;
# 이미 테이블을 만들어서 charset을 바꾸고 싶다면 다음과 같이 쓰면됩니다.
mysql> ALTER TABLE users convert to CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
mysql> ALTER TABLE users_follow_list convert to CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
mysql> ALTER TABLE tweets convert to CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci
# 위와 같이 할 때 Specified key was too long; max key length is 767 bytes 이러한 에러가 날 경우 다음과 같이 변경합니다.
# users에 email VARCHAR(255) NOT NULL를 email VARCHAR(80) NOT NULL 로 변경합니다.
# config.py
# user, password는 DB의 id와 password 입니다.
db = {
'user': '',
'password': '',
'host': 'localhost',
'port': 3306,
'database': 'miniter'
}
DB_URL = f"mysql+mysqlconnector://{db['user']}:{db['password']}@{db['host']}:{db['port']}/{db['database']}?charset=utf8"
# util.py
from flask.json import JSONEncoder
class CustomJSONEncoder(JSONEncoder):
def default(self, obj):
if isinstance(obj, set):
return list(obj)
return JSONEncoder.default(self, obj)
# api.py
from flask import current_app
from sqlalchemy import text
def get_user(user_id):
user = current_app.database.execute(text("""
SELECT
id, name, email, profile
FROM
users
WHERE id = :user_id
"""), {'user_id': user_id}).fetchone()
return {
'id' : user['id'],
'name' : user['name'],
'email' : user['email'],
'profile' : user['profile']
} if user else None
def insert_user(user):
return current_app.database.execute(text("""
INSERT INTO users (
name,
email,
profile,
hashed_password
) VALUES (
:name,
:email,
:profile,
:password
)
"""), user).lastrowid
def insert_tweet(user_tweet):
return current_app.database.execute(text("""
INSERT INTO tweets (
user_id,
tweet
) VALUES (
:id,
:tweet
)
"""), user_tweet).rowcount
def insert_follow(user_follow):
return current_app.database.execute(text("""
INSERT INTO users_follow_list (
user_id,
follow_user_id
) VALUES (
:id,
:follow
)
"""), user_follow).rowcount
def insert_unfollow(user_unfollow):
return current_app.database.execute(text("""
DELETE FROM user_follow_list
WHERE user_id = :id
AND follow_user_id = :unfollow
"""), user_unfollow).rowcount
def get_timeline(user_id):
timeline = current_app.database.execute(text("""
SELECT
t.user_id,
t.tweet
FROM tweets t
LEFT JOIN users_follow_list ufl ON ufl.user_id = :user_id
WHERE t.user_id = :user_id OR t.user_id = ufl.follow_user_id
"""),{
"user_id": user_id
}).fetchall()
return [{
'user_id': tweet['user_id'],
'tweet': tweet['tweet']
} for tweet in timeline]
# app.py
from flask import Flask, jsonify, request
from sqlalchemy import create_engine
from util import *
from api import *
def create_app(test_config=None):
app = Flask(__name__)
app.json_encoder = CustomJSONEncoder
if test_config is None:
app.config.from_pyfile("config.py")
else:
app.config.update(test_config)
database = create_engine(app.config['DB_URL'], encoding='utf-8', max_overflow = 0)
app.database = database
@app.route('/', methods=['GET'])
def index():
return "Hello Flask"
@app.route('/ping', methods=['GET'])
def ping():
return "pong"
@app.route("/sign-up", methods=['POST'])
def sign_up():
new_user = request.json
new_user_id = insert_user(new_user)
new_user = get_user(new_user_id)
return jsonify(new_user)
@app.route('/tweet', methods=['POST'])
def tweet():
user_tweet = request.json
tweet = user_tweet['tweet']
if len(tweet) > 300:
return '300자를 초과했습니다.', 400
insert_tweet(user_tweet)
return '', 200
@app.route('/follow', methods=['POST'])
def follow():
payload = request.json
insert_follow(payload)
return '', 200
@app.route('/unfollow', methods=['POST'])
def unfollow():
payload = request.json
insert_unfollow(payload)
return '', 200
@app.route('/timeline/<int:user_id>', methods=['GET'])
def timeline(user_id):
return jsonify({
'user_id': user_id,
'timeline': get_timeline(user_id)
})
return app
if __name__ == '__main__':
app = create_app()
app.run(host='0.0.0.0', port=5000, debug=True)