[REAL Python – Flask] – “인스타그램 클론코딩 – Instagram Clone (1)”
[REAL Python – Flask] – “인스타그램 클론코딩 – Instagram Clone (1)”

프로젝트 세팅
먼저, 저는 플라스크 프로젝트들을 flaskprojects 라는 디렉토리에 넣어서 관리하고 있습니다.

입력하여 flastagram 폴더를 하나 만들어 주겠습니다.

이후, cd flastagram
으로 해당 폴더로 이동한 다음 mkdir backend, mkdir frontend
로 각각 디렉토리를 만들어 주세요.

frontend 폴더 안에는 위와 같은 디렉토리 구조를 만들어 주세요. index.html 파일의 내용은 아무것도 없습니다.

터미널을 통해 backend 폴더로 이동한 다음, python -m venv venv 를 입력하여 가상환경을 설치해 줍니다.

가상환경을 활성화해 줍니다. 필자는 WSL 우분투를 이용하고 있어서 위의 명령어를 사용했는데, Windows의 경우는 venv/scripts/activate 를 입력하면 될 겁니다.
mkdir config
mkdir models
mkdir requirements
mkdir resources
mkdir schemas
터미널에 위의 줄들을 한 줄씩 입력하여 아래와 같은 디렉토리 구조를 만듭니다. 터미널의 현재 위치가 flastagram/backend 에 있는 것을 꼭 확인해 주세요. (오타 수정 : shcemes -> schemas)

다음으로는 우리가 필요한 라이브러리들을 설치하겠습니다.
pip install flask

pip install flask-jwt-extended

pip install python-dotenv

pip install flask-restful

pip install flask-migrate

pip install flask-uploads

pip install flask-marshmallow

pip install marshmallow-sqlalchemy

pip freeze
를 입력하면 아래와 같이 설치된 패키지들의 목록이 뜰 겁니다.

pip freeze > requirements/common.txt 로 해당 패키지들의 버전을 명시해 두겠습니다. 주의할 점은, backend/ 폴더 안에서 명령어를 수행해야 한다는 것입니다. 아래 스크린샷의 하늘색 부분의 오른쪽 맨 끝을 보면 이해할 수 있습니다.

backend / requirements 폴더의 common.txt 파일로 가 보면 아래와 같이 패키지 버전들이 잘 명시된 것을 확인할 수 있습니다.

이후 requirements/ 아래에 dev.txt 파일을 하나 만들어 주고 내용은 -r common.txt 로 채워 줍니다. (common.txt 의 패키지들을 모두 설치하겠다는 이야기입니다)

또, requirements/ 아래에 prod.txt 파일을 하나 만들어 주고, 내용은 같이 -r common.txt 로 채워 줍니다.
pip install -r backend/requirements/dev.txt
를 입력해 보세요. 아래와 같이 “모든 패키지는 다 설치되었어!” 라고 알려주면 성공입니다.

위와 같이 requirements 를 분리하는 이유는, 개발 시에 필요한 패키지들과 배포 시에 필요한 패키지들을 분리하기 위합입니다.

이후 backend/ 아래에 .env 파일, .env.example 파일을 하나 만들어 줍니다.
.env.example 파일에는 아래의 내용을,
JWT_SECRET_KEY =
APP_SECRET_KEY =
APPLICATION_SETTINGS = config/dev.py
DATABASE_URL =
.env 파일에는 아래의 내용을 넣어 줍니다. (SECRET_KEY 에는 어떤 것을 넣던 관계없습니다.)
JWT_SECRET_KEY = flastagramsecretkey
APP_SECRET_KEY = flaskappsecretkey
APPLICATION_SETTINGS = ../config/dev.py
다음 config 파일을 만질 차례입니다. config 파일 또한 개발용, 배포용을 따로 만들어 분리해 두겠습니다.
config 폴더 아래에 비어있는 __init__.py 를 만들어 주세요.

그리고 아래와 같은 이름을 가진 파일들을 만들어 줍니다.

backend/config/common.py
에 아래의 내용을 넣어줍니다. 맨 아래의 print 를 수행한다면 끝이 /backend 가 되어야 합니다.
import os
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
# print(BASE_DIR)
backend/config/dev.py
에는 아래의 내용을,
from config.common import *
import os
DEBUG = True
SQLALCHEMY_DATABASE_URI = 'sqlite:///{}'.format(os.path.join(BASE_DIR, 'flastagram.db'))
SQLALCHEMY_TRACK_MODIFICATIONS = False
PROPGATE_EXECPTIONS = True
JWT_SECRET_KEY = os.environ["JWT_SECRET_KEY"]
SECRET_KEY = os.environ["APP_SECRET_KEY"]
JWT_BLACKLIST_ENABLED = True
JWT_BLACKLIST_TOKEN_CHECKS = ["access", "refresh"]
prod.py
에는 아래의 내용을 넣어줍니다. 아직 배포용 설정은 하지 않도록 하겠습니다!
from .common import *
그 다음으로는, backend 아래에 api
폴더와 __init__.py
를 하나 만들어 주겠습니다.

그리고 이 세 개의 디렉토리들을…

api 폴더 아래로 옮겨줍니다.
현재의 디렉토리 구조는 아래와 같습니다.
flastagram
├── backend
│ ├── api
│ │ ├── __init__.py
│ │ ├── models
│ │ ├── resources
│ │ └── schemes
│ ├── config
│ │ ├── __init__.py
│ │ ├── common.py
│ │ ├── dev.py
│ │ └── prod.py
│ ├── requirements (설치된 패키지들)
│ │ ├── common.txt
│ │ ├── dev.txt
│ │ └── prod.txt
│ └── venv (파이썬 가상환경)
└── frontend
├── assets
│ ├── css
│ ├── img
│ └── js
└── index.html
그리고 __init__.py 에 create_app() 함수를 작성해 주겠습니다.

그리고, backend
폴더 아래에 app.py
를 만들어 줍니다.


이후 터미널의 위치가 flastagram/backend
인 것을 확인한 후, 아래의 명령어를 입력해 보세요.

서버가 위와 같이 열린다면 성공입니다!
백엔드 단에서 간단한 준비
backend/api 아래에 db.py 를 작성하고, 아래의 내용을 입력해 주겠습니다.

이번에는 flask-marshmallow 를 사용하기 위해서, backend/api 아래에 ma.py 를 작성하고, 아래의 내용을 입력합니다.

models.py 아래에 비어있는 __init__.py 파일을 작성해 줍니다.

그리고 각각 user.py, post.py, comment.py 를 작성해 주세요. 먼저 User 모델을 구현하겠습니다.


이후, api/__init__.py 에 아래의 내용을 추가합니다.
# 아래의 줄에 jsonify 추가!
from flask import Flask, jsonify
from flask_restful import Api
from dotenv import load_dotenv
# 추가!
from flask_jwt_extended import JWTManager
from flask_migrate import Migrate
from marshmallow import ValidationError
# 추가!
from .db import db
from .ma import ma
from .models import user
def create_app():
app = Flask(__name__)
load_dotenv(".env", verbose=True)
app.config.from_object("config.dev")
app.config.from_envvar("APPLICATION_SETTINGS")
api = Api(app)
# 추가!
jwt = JWTManager(app)
migrate = Migrate(app, db)
db.init_app(app)
ma.init_app(app)
migrate.init_app(app, db)
# 추가!
@app.before_first_request
def create_tables():
db.create_all()
# 추가!
@app.errorhandler(ValidationError)
def handle_marshmallow_validation(err):
return jsonify(err.messages), 400
return app
이 작업 후, flask db init
을 입력해줍니다. 터미널의 현재 폴더 위치가 backend/ 에 있는 것을 꼭 확인하세요!

이후 backend/ 에 migrations 라는 폴더가 생성된 것을 확인할 수 있을 겁니다.

다음으로는, flask db migrate 명령을 입력해줍니다.


그러면 migrations 아래에 40… 라는 리비전 파일이 들어가있는 것을 확인할 수 있을 겁니다.

잠시 생성된 파일을 살펴보겠습니다. 우리가 만들어낸 User 모델을 인식하고, 어떤 필드를 만들지, 제약 조건은 어떻게 할 지에 대해서 적혀 있는 것을 볼 수가 있네요. upgrade 라는 함수를 수행하면 create_table 이 되고, downgrade 라는 함수를 수행하면 User 테이블을 drop 합니다.

flask db upgrade
명령을 수행해 보세요. 아래와 같이 데이터베이스에 테이블이 잘 추가되었을 겁니다.

팔로우 / 언팔로우를 위한 유저 모델 구현
우리는 팔로우/언팔로우를 구현할 것이므로, 모델을 수정해야 합니다. 한 명의 유저는 여러 명의 유저를 팔로우할 수 있고, 또 다른 유저도 다른 여러 명의 유저를 팔로우할 수 있습니다. 이는 다대다 관계입니다.
“게시물과 태그” 는 제가 제일 좋아하는 다대다 관계로서의 예시입니다. 그런데, “게시물 테이블” 과 “태그 테이블” 은 따로 존재합니다. 팔로우와 언팔로우의 경우에는 유저가 유저를 팔로우합니다. 유저 테이블 딱 하나만 존재합니다. 이를 구현하기 위해서 이 둘을 매핑해 주는 연관 테이블을 작성하겠습니다.

이후 유저 테이블에서 팔로우 관계를 설정합니다. 주석은 달아 놓았는데, 더 정확한 설명은 아래의 링크를 참고하면 좋을 듯 싶습니다.

이후, 아래의 함수를 작성합니다.

follow()
메서드는 호출되었을 때에 만약 본인이 누군가를 팔로우하지 않고 있다면 팔로우를 수행할 것이고,
unfollow()
메서드는 호출되었을 때에 만약 본인이 누군가를 팔로우하고 있다면 그 사람을 언팔로우할 것입니다.
이를 위해서 누군가를 팔로우하는 중인지, 아닌지를 판단하는 is_following()
메서드를 추가합니다.
flask db migrate
를 수행하고,

flask db upgrade
를 수행하세요.

그러면, 새롭게 followers 테이블이 생성된 것을 확인할 수 있습니다.

본격적인 구현은 다음 포스팅부터 시작됩니다.
프론트엔드 단에서 간단한 준비
그냥 아래 것 다운로드 받아서 넣어 두세요..
사진은 알아서 구하는 걸로..

이렇게 압축을 풀어서 같은 레벨에 놓아두면 됩니다.
https://drive.google.com/drive/folders/1YUMEyvK0AHDl937Ofgc4lKaiJS1XlpnB 에서 이미지들을 다운받고, 연결해 주세요! (어차피, 나중에 바뀔 것이므로 취향껏..
index.html 을 열었을 때에, 큰 문제 없이 뜬다면 완성입니다~