본문 바로가기

프로젝트/엘리스 AI 트랙

나의 첫 프로젝트(1), "Flask에서 React까지"

엘리스 AI 트랙을 달려온지 8주가 지났고

드디어 개인 프로젝트를 하게 되었다!!!

설렘 반, 두려움 반

 

프로젝트 주제는 아래 3가지 중에 선택을 할 수 있었다.

 

직업 심리 검사 서비스 (프론트엔드)

도서관 대출 서비스 (백엔드)

레이서 포트폴리오 서비스 (풀스택)

 

엘리스 1주차부터 8주차까지 배웠던 모든 내용을 프로젝트 안에서 녹여내보고 싶다는 생각에

세번째 포트폴리오 서비스 사이트를 과감히 선택했다. 세번째 주제는 역시 1분만에 마감되기도 했다.

첫주에는 부족한 개념을 다시 공부하고 로그인, 회원가입 기능을 구현하는 것에 모든것을 쏟았고, 

둘째주에는 서버(MySQL)와 백엔드(Flask) 그리고 프론트엔드(React)를 넘나들면서 웹사이트에 살을 붙여갔다.

코딩의 코도 모르던 코맹맹이가 2주동안 얼마나 많은 달걀을 바위에 내려치며 작업했는지 남겨두고 싶었다.

 


[01] Gitlab Repository 생성

맨 처음에는 엘리스 프로젝트를 진행할 레포지토리를 Gitlab에다가 만들어야 하는데,

시작하자마자 레포지토리 생성하는것부터 막혀서 달걀 하나를 일단 바위에 던지고 시작했다.

Gitlab 레포지토리에 Project name은 아무렇게나 해도 괜찮지만, Project slug는 꼭 영문으로 해야 되더라...

 

▷▶ Project slug 이름은 프로젝트 개발이 끝나고 서버를 배포하는 단계에서 보게 될 것이다^^ 배포헬

 

Project name => 2기-포트폴리오-강경모 / Project slug => First_portfolio

레포지토리 생성을 마치고, VScode를 열어 로컬에서 원격저장소를 연결해주었다.

 

git clone [GitLab Clone with HTTPS 주소] . 
"git clone을 하면 git init이 자동으로 된다. 뒤에 .은 현재 위치에 불러오겠다는 뜻이다."
git remote add origin [GitLab Clone with HTTPS 주소]
"로컬(내컴퓨터)과 원격저장소(Gitlab)을 연결!"
git push --set-upstream origin master
"git push를 처음할 때에는 origin master를 한번 지정해줘야한다. 그 이후부터는 git push만 해도 가능"

[02] Venv 가상환경

프로젝트를 개발하다보면 다양한 패키지, 모듈을 설치하게 되는데 보통 venv라는 가상환경을 만들어주고 시작한다.

나중에 다른 프로젝트를 새로 시작할 때 기존 패키지, 모듈이 서로 충돌하거나 쌓여서 관리가 안되기 때문이다.

 

▷▶ 무지성으로 프로젝트를 시작하고나서 이후에 venv로 패키지 관리를 해야한다는 조언을 받았다 (이미 늦었다.. ㅎㅎ)

 

python -m venv ./myenv 
"venv 가상환경 생성"
. myenv/Scripts/activate 
"venv 활성화하면 터미널 앞에 (myvenv)라고 나오게 된다."
. myenv/bin/activate 
"위 명령어가 실행안될 때 사용해보자^^"
deactivate 
"venv 비활성화"

[03] React-Router-Dom

이번 프로젝트에서 React는 SPA(Single Page Application)이라는 중요한 역할을 하게 되었다.

HTML밖에 몰랐었던 나는 <a href="">를 사용해서 페이지를 이동하는 방법밖에 몰랐었는데, 

react-router-dom을 공부해보니 각 컴포넌트마다 경로주소를 지정해줄 수도 있었고,

하위 컴포넌트에 history를 porps로 받아서 history.push()를 쓰면 페이지 이동을 할 수도 있었다.

 

▷▶ React는 처음이라 떨리는걸... 나중 가보니 컴포넌트 재사용, 페이지 이동이 엉켜서 스파게티 맛집이 되었다.

 

응애~ 나 애기 웹사이트 >.<

<BrowserRouter>
    <HeadPage />
    <Switch>
        <Route component={LoginPage} path="/" exact /> 
        <Route component={JoinPage} path="/join" />
        <Route component={NavPage} path="/main" />
    </Switch>
</BrowserRouter>

<!-- exact를 쓰지 않으면 "/join", "/main"도 "/"으로 인식해서 원하는 화면으로 가지 않는다. -->

 

const LoginPage =({ history }) => {
    
    return (
        <form>
            <label>아이디</label> <br/>
            <input type="email" id="user_id"/> <br/>
            <label>비밀번호</label> <br/>
            <input type="password" id="user_pw"/> <br/>
            <button type="submit" onClick={() => history.push("/main")}>로그인</button> <br/>
            <button onClick={() => history.push("/join")}>회원가입</button>
        </form>
    )
}

[04] Flask 서버 구축

이번 프로젝트에서 Flask는 사용자가 API요청을 보낼 때 서버와 연결해주는 중간다리 역할을 한다.

 

Flask서버를 열어주는 기본적인 app.py

Flask API 요청들을 정리해둔 main_view.py

데이터(SQL)와 연결해주는 db_connect.py, config.py

데이터 타입을 설정하고 1차로 검증해주는 models.py

 

▷▶ Flask는 Django와 비교하여 최소한의 구조만을 제공해준다. 배우기 쉽고(?) 가볍다(!)

물론 Django를 사용하면 다양한 도구를 쓸 수 있지만, Flask를 통해서 파이썬 웹 개발에 대한 기본적인 이해를 높일 수 있었다.

 

# app.py

from flask import Flask
from db_connect import db
from models import *
from views import main_view, info_view
import config

app = Flask(__name__)
app.config.from_object(config) # config.py 연결
app.register_blueprint(main_view.bp) 

if __name__ == '__main__':
    app.run(debug=True)

 

# main_view.py

from flask import Blueprint, request, session, jsonify
from models import *

bp = Blueprint('main', __name__, url_prefix='/')

@bp.route("/join", methods=["GET", "POST"])
def join() :
    return ;

 

# db_connect.py

from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

 

# config.py

SQLALCHEMY_DATABASE_URI = "mysql+pymysql://root:dmlah7536@localhost/elice_db"
# "mysql+pymysql://(아이디)):(mysql설치했을 때 비밀번호)@(호스트 서버 url : 호스트 서버 port)/(db 이름))"
SQLALCHEMY_TRACK_MODIFICATIONS = 1
# 이걸 켜면 메모리 사용량이 늘어나서 꺼주는게 좋다

SECRET_KEY = "lkj234kjkl2n3b42kh3l2b3"

SESSION_TYPE = "filesystem"

 

# models.py

from db_connect import db

class User(db.Model):
    __tablename__ = 'user'

    id        = db.Column(db.Integer,  primary_key=True, nullable=False, autoincrement=True)
    user_id   = db.Column(db.String(100), nullable=False, unique=True)
    user_pw   = db.Column(db.String(100), nullable=False)
    user_name = db.Column(db.String(100), nullable=False)

    def __init__(self, user_id, user_pw, user_name):
        self.user_id = user_id
        self.user_pw = user_pw
        self.user_name = user_name

[05] CORS (React <-> Flask)

CORS란, Cross-Origin Resource Sharing의 약자로 주고받는 URL 주소가 일치하지 않으면 발생하는 에러다.

React는 localhost:3000, Flask는 localhost:5000이기 때문에 CORS오류가 발생하게 된다.

보안 취약점을 노린 공격을 방어할 수 있기 때문에 코치님은 우리를 위한 방화벽(?)이라고 말씀하셨다.

 

CORS 에러로 이틀내내 머리가 아팠다.. 그런데 여기에 반전이...?!

CORS오류는 해결하기 위해서는 3가지 방법이 있다.

 

1. Flask에 CORS 모듈 설치하기

# app.py

from flask_cors import CORS

CORS(app) # CORS 애러 해결

2. React에서 axios.post 요청시 headers 붙이기

axios.post(url, data, {
    headers: {"Access-Control-Allow-Origin": "*"}
})

3. package.json에 proxy 넣기

"proxy": "http://localhost:5000/"

▷▶ 위의 3가지 방법을 다 했는데도 CORS오류가 계속 나와서 너무 머리가 아팠다.

결국 나중에 알고보니 Flask문법을 잘못 사용해도 CORS오류를 내보내는 게 반전이었다.

(Flask를 꼼꼼이 살펴보도록 ㅠㅜ)


[06] MySQL DB 생성

cmd나 터미널 열어서 mysql -u root -p 입력하고 비밀번호를 통해 접속한다.

MySQL에 접속해 데이터베이스를 만들고 Schema를 만들어주면 실제 DB가 만들어진다.

Schema란 데이터베이스의 구조와 제약조건에 관해 전반적인 명세를 적어둔 것이다.

 

▷▶ VS-code에서 mySQL 데이터 테이블을 편리하게 확인할 수 있다. VScode 확장프로그램에서 MySQL 설치하고

왼쪽에 데이터베이스 모양(원반3개) 클릭하고 + 눌러서 connect창 열은 다음 mysql 비밀번호 입력하고 connect 하면 끝!!!

 

mysql> CREATE DATABASE testdb CHARACTER SET utf8 COLLATE utf8_general_ci; 
# 데이터 베이스 생성 (testdb가 데이터베이스 이름)
mysql> show databases;
# 데이터 베이스 목록 보기
mysql> use testdb;
# 데이터 베이스 사용하기
mysql> show tables;
# 테이블 목록 보기
mysql> SELECT * FROM 테이블명;
# 각 테이블내 데이터 조회하기
mysql> DROP DATABASE testdb; 
# 데이터 베이스 삭제

[07] Migration (Flask <-> MySQL)

Migration이란, 데이터 베이스 Schema의 버전을 관리하기 위한 하나의 방법이다. 

Flask 서버에 만들어둔 Models.py도 DB에 대해서 명시를 해두었지만 실제 DB가 생성된 것이 아니다.

따라서, Flask-Migrate 패키지를 통해 Models.py에 있는 자료를 실제 DB로 구현해주고 버전관리를 용이하게 해준다.

pip install Flask-Migrate 명령어로 설치하면 파이썬의 ORM 라이브러리인 alembic, SQL-Alchemy가 함께 설치된다.

 

▷▶ models.py 수정할 때마다 터미널 명령어로 flask db migrate, flask db upgrade 계속 해줘야 실제 DB에도 반영된다!

 

flask db init # models.py를 이용하여 DB migration 파일 설치
flask db migrate
flask db upgrade # 실제 models.py파일에 정의된 class에 해당되는 테이블을 생성

 

# app.py

from flask import Flask
from db_connect import db
from models import *
from flask_cors import CORS
from flask_migrate import Migrate
from views import main_view, info_view
import config

app = Flask(__name__) # Flask 연결
CORS(app) # CORS 애러 해결
app.config.from_object(config) # config.py 연결
app.register_blueprint(main_view.bp) 
app.register_blueprint(info_view.bp) 
migrate = Migrate() # DB migration 사용
migrate.init_app(app, db) # DB migration 사용
db.init_app(app) # 데이터 베이스와 연결

if __name__ == '__main__':
    app.run(debug=True)

 

▷▶ DB migration 초기화하는 방법

 

migration 폴더를 삭제한다 해도 버전은 남아있다. mysql로 접속한 후 show databases;

해당 데이터베이스 선택 use (DB이름);한 다음, 삭제할 테이블 확인하고 show tables;

테이블 삭제하기 drop table alembic_version; 까지 직접 해주어야 한다.

migration 다시 깔기위해 flask db init > flask db migrate > flask db upgrad를 진행한다. 

models.py에 새로운 테이블 만들 때마다 migrate & upgrade해서 실제 DB에 반영되도록 한다.


[*] 코치님의 프로젝트 개발 Tip

 - Front-end와 Back-end 폴더를 따로 만들어서 정리하기

- node_modules, pycache, .venv 폴더는 .gitignore에 넣기

- requirements.txt 만들어서 버전관리 용이하도록 하기

 

pip freeze > requirements.txt
pip install -r requirements.txt
# 명령어에 치면 자동으로 requirements.txt 생성됨

pip install -r requirements.txt
나중에 서버 배포시 필요한 패키들을 설치할 때 사용


프로젝트를 처음 시작하려고할 때는 정말 막막하기만 했다. Flask에 대한 지식이 많이 부족해서 백엔드 서버를 구축하는데만 이틀내내 구글링을하며 삽질을 계속 하였다. 컴퓨터를 계속 보고 있으니 눈이 너무 아프고 머리도 아프고 목도 거북이가 되어가는 느낌이 들 정도였다. 그래도 정말 안풀릴 것 같은 한가지 문제를 해결했을 때 희열감이 강해서 짜릿한 느낌마저 들었다. Axios.post로 Flask API서버 요청에 성공했다는 로그를 봤을 때는 의자에서 방방뛰며 소리지르기도 했다. 차라리 나중에 판교에 있는 IT회사 근처에 한의원을 차려서 거북목, 라운드숄더, 허리디스크 특화로 침치료만 해도 환자들이 줄을 서지않을까 하는 재밌는 상상을 했다. 일주일동안 삽질을 열심히 하면서 퍼올린 흙으로 MySQL과 Flask와 React의 다리를 연결해보았는데 정말정말 내 자신이 뿌듯했다!! 너무 행복해서 그림판에 굳이 마우스로 그려가면서까지 혼자 정리해보았는데 투박하지만 그 여윤이 아직도 그림판에 남아있다. 아직 해결해야할 기능들이 많지만 어떻게든 구현했다는 것 자체 하나로도 일주일이 아깝지 않았다.

 

일주일간의 노오오오오력!!! JwT 냥이가 기쁨의 눈물을 흘리고 있다ㅠㅜ