diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..f86113d
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,8 @@
+# 디폴트 무시된 파일
+/shelf/
+/workspace.xml
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
+# 에디터 기반 HTTP 클라이언트 요청
+/httpRequests/
diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml
new file mode 100644
index 0000000..105ce2d
--- /dev/null
+++ b/.idea/inspectionProfiles/profiles_settings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..d56657a
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..1fbe102
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/wixon_blog.iml b/.idea/wixon_blog.iml
new file mode 100644
index 0000000..b594273
--- /dev/null
+++ b/.idea/wixon_blog.iml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app.py b/app.py
new file mode 100644
index 0000000..3324a31
--- /dev/null
+++ b/app.py
@@ -0,0 +1,159 @@
+from flask import Flask, render_template, request, redirect, url_for, flash, session
+import pymysql
+from flask import session, g, jsonify
+import bcrypt
+from bs4 import BeautifulSoup
+from werkzeug.utils import secure_filename
+import os
+import uuid
+
+UPLOAD_FOLDER = 'static/upload/img' # 경로를 Flask 앱 루트 기준으로 수정
+ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif'}
+
+app = Flask(__name__)
+app.secret_key = 'your secret key'
+app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
+
+
+@app.before_request
+def load_user():
+ if 'user_info' in session:
+ g.is_login = True
+ g.user_info = session['user_info']
+
+
+def allowed_file(filename):
+ return '.' in filename and \
+ filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
+
+
+@app.route('/upload_image', methods=['POST'])
+def upload_image():
+ if 'file' not in request.files:
+ return jsonify(success=False, message='No file part'), 400
+
+ file = request.files['file']
+
+ if file.filename == '':
+ return jsonify(success=False, message='No selected file'), 400
+
+ if file and allowed_file(file.filename):
+ # Secure the filename and keep its extension
+ filename = secure_filename(file.filename)
+ ext = filename.rsplit('.', 1)[1].lower()
+
+ # Generate a random filename using uuid4
+ random_filename = f'{uuid.uuid4().hex}.{ext}'
+
+ file_path = os.path.join(app.config['UPLOAD_FOLDER'], random_filename)
+
+ # Create directories if not exist
+ os.makedirs(os.path.dirname(file_path), exist_ok=True)
+
+ file.save(file_path)
+
+ # Generate the URL of the saved image file
+ file_url = url_for('static', filename='upload/img/' + random_filename)
+
+ return jsonify(success=True, fileUrl=file_url), 200
+
+ return jsonify(success=False, message='File not allowed'), 400
+
+
+# MySQL 데이터베이스 연결 설정
+def connnect_db():
+ return pymysql.connect(
+ host='wxnasso.synology.me',
+ user='wixon5',
+ password='Wixon2022@!',
+ database='wixon',
+ charset="utf8mb4",
+ cursorclass=pymysql.cursors.DictCursor, # DictCursor를 사용하여 딕셔너리 형태로 결과를 반환
+ init_command='SET SQL_SAFE_UPDATES = 0;',
+ )
+
+
+def sql_execute(q, d, is_data=False, is_last_id=False):
+ data = None
+ last_id = None
+ with connnect_db().cursor(pymysql.cursors.DictCursor) as cursor:
+ try:
+ res = cursor.execute(q, d)
+ data = cursor.fetchall() if is_data else None
+ last_id = cursor.lastrowid if cursor.lastrowid != 0 else None
+ cursor.connection.commit()
+ except Exception as e:
+ print(e)
+ cursor.connection.rollback()
+ res = False
+
+ return (res, last_id if is_last_id else data) if is_data or is_last_id else res
+
+
+@app.route('/')
+def index():
+ # MySQL에서 모든 블로그 포스트를 가져옵니다.
+ query = "SELECT `title`, `thumbnail_img`, `contents` FROM `blog` WHERE `use_yn` = 'Y' ORDER BY `add_date` DESC;"
+ r, posts = sql_execute(query, (), is_data=True)
+
+ # `index.html` 템플릿으로 데이터를 전달합니다.
+ return render_template('index.html', posts=posts)
+
+
+@app.route('/login', methods=['GET', 'POST'])
+def login():
+ if request.method == 'POST':
+ username = request.form.get('username')
+ password = request.form.get('password').encode('utf-8')
+
+ # DB에서 사용자 정보 가져오기
+ res, user = sql_execute("SELECT * FROM member WHERE mb_id=%s", (username,), is_data=True)
+ if res and user and bcrypt.checkpw(password, user[0]['mb_passwd'].encode('utf-8')):
+ session['username'] = username
+ session['user_info'] = user[0]
+ return redirect(url_for('index'))
+ else:
+ flash('Username or password is incorrect')
+ return render_template('login.html')
+
+
+@app.route('/logout')
+def logout():
+ session.clear()
+ g.is_login = False
+ g.user_info = None
+
+ return redirect(url_for('index'))
+
+
+@app.route('/write', methods=['GET', 'POST'])
+def write():
+ if request.method == 'POST':
+ title = request.form['title']
+ category = request.form['category']
+ contents = request.form['contents']
+
+ # 본문에서 첫 번째 이미지 추출
+ soup = BeautifulSoup(contents, 'html.parser')
+ first_image = soup.find('img')
+ thumbnail_img = first_image['src'] if first_image else None
+
+ # SQL 쿼리와 삽입할 데이터 설정
+ query = "INSERT INTO blog (title, category, contents, thumbnail_img, use_yn) VALUES (%s, %s, %s, %s, 'Y')"
+ data = (title, category, contents, thumbnail_img)
+
+ # 새로운 글 저장
+ result, last_id = sql_execute(query, data, is_last_id=True)
+
+ if result:
+ # 저장 성공시, 인덱스 페이지로 리다이렉트
+ return redirect(url_for('index'))
+ else:
+ # 저장 실패시, 에러 메시지 반환
+ return "Failed to write post", 500
+ else:
+ return render_template('write.html')
+
+
+if __name__ == '__main__':
+ app.run(host='0.0.0.0', port=8899)
diff --git a/static/upload/img/d6dffe69d6f84a709962b653e7f6eb16.png b/static/upload/img/d6dffe69d6f84a709962b653e7f6eb16.png
new file mode 100644
index 0000000..2e9266a
Binary files /dev/null and b/static/upload/img/d6dffe69d6f84a709962b653e7f6eb16.png differ
diff --git a/static/upload/img/e0cb20c7b5a34b46b117e06f94963996.png b/static/upload/img/e0cb20c7b5a34b46b117e06f94963996.png
new file mode 100644
index 0000000..e73dfe2
Binary files /dev/null and b/static/upload/img/e0cb20c7b5a34b46b117e06f94963996.png differ
diff --git a/static/upload/img/logo.png b/static/upload/img/logo.png
new file mode 100644
index 0000000..e73dfe2
Binary files /dev/null and b/static/upload/img/logo.png differ
diff --git a/templates/base.html b/templates/base.html
new file mode 100644
index 0000000..c3f236c
--- /dev/null
+++ b/templates/base.html
@@ -0,0 +1,75 @@
+
+
+
+ Blog
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+{% block content %}{% endblock %}
+
+
+
+
+
diff --git a/templates/index.html b/templates/index.html
new file mode 100644
index 0000000..f35ec87
--- /dev/null
+++ b/templates/index.html
@@ -0,0 +1,32 @@
+{% extends 'base.html' %}
+
+{% block content %}
+
+
+ {% for post in posts %}
+
+
+
+
{{ post.title }}
+
+
{{ post.contents | striptags | truncate(100) }}
+
Read more
+
+
+ {% endfor %}
+
+
+
+
+{% endblock %}
diff --git a/templates/login.html b/templates/login.html
new file mode 100644
index 0000000..7fe4b45
--- /dev/null
+++ b/templates/login.html
@@ -0,0 +1,32 @@
+{% extends 'base.html' %}
+
+{% block content %}
+
+
윅슨 블로그 로그인
+ {% with messages = get_flashed_messages() %}
+ {% if messages %}
+
+ {% endif %}
+ {% endwith %}
+
+
+{% endblock %}
diff --git a/templates/write.html b/templates/write.html
new file mode 100644
index 0000000..327cfa2
--- /dev/null
+++ b/templates/write.html
@@ -0,0 +1,65 @@
+{% extends 'base.html' %}
+
+{% block content %}
+
+
+
+{% endblock %}