WEB/Backend

[Python] Flask 블로그 만들기

Jerry_K 2024. 4. 24. 13:47

✏️ 이전 내용

 

[Python] Flask 로그인/회원가입 기능 만들기

✏️ 이전 내용 [Python] Firebase 시작하기 🙄 Firebase란 ? 구글이 소유하고 있는 모발일 애플리케이션 개발 플랫폼으로, 개발자가 모바일 및 웹 애플리케이션을 모두 쉽게 생성,실행 및 확장 할 수

fishking.tistory.com

지난 포스팅에서는 flask 서버를 통해 로그인과 회원가입을 하는 코드에 대해 소개를 했다.

이번에는 추가적으로  유저가 포스팅을 하고, 포스팅 리스트들을 보는 코드를 작성한다.

(글은  Firebase와 Flask 로그인/회원가입 포스터를 봤다는 전제로 작성된다.)

 

📚포스터 내용

이번 포스터에서는 flask로 간단하게 포스터를 작성하고 글 목록을 확인하는 기능을 만들어본다.

(CSS/JS 부분은 기능 구현에 필수가 아니므로 생략)

코드 진행 순서대로 포스팅을 해보려 했는데, 너무 복잡해져서 핵심 부분만 쓴다.


🎄파일 tree 구조

│  app.py
│  DB_handler.py
│  README.md

├─auth
│      firebaseAuth.json

├─static
│  └─image
│      └─css
└─templates
        base.html
        index.html
        login.html
        post_detail.html
        post_list.html
        signin.html
        user_detail.html
        write_post.html

(기존 파일)

- app.py : flask 서버 구현

- DB_handler.py : firebase 클래스 / 객체 생성

- firebaseAuth.json : firebase SDK 키 

- base.html : 코드 공통 부분

- index.html : 시작 페이지

- login.html : 로그인 페이지

- signin.html : 회원가입 페이지

 

(+ 새로 추가된 파일)

- write_post.html : 포스터 작성

- post_list.html : 포스터 목록들

- post_detail.html : 포스터 세부 사항

- user_detail.html : 유저 세부 사항

 

(해당 파일은 다 만들었다는 전제로 진행 / static 폴더는 형식상 생성 )


📗핵심 부분 정의

 

📃app.py

- write 함수

@app.route("/write")
def write():
    if "uid" in session :
        return render_template("write_post.html")
    else :
        return redirect(url_for("login"))

uid가 있는 상태(로그인)이면 write_post.html 파일은 렌더링 해준다.

그게 아닌 경우 login 페이지로 redirect 한다.

 

 

- write_done 함수

@app.route("/write_done",methods=["GET"])
def write_done():
    title = request.args.get("title")
    content = request.args.get("content")
    uid = session.get("uid")
    DB.write_post(uid=uid,content=content,title=title)
    return redirect(url_for("index"))

 

write_post 페이지에 form 형태는 글 제목과 본문을 입력 받는다.

그리고 submit을 통해 write_done 함수에 title과 content 파라미터를 넘겨주고 호출한다.

그리고 session.get("uid")를 통해 uid 변수를 정의한다.

DB.write_post는 내가 정의한 객체로 위에 선언한 변수를을 파라미터로 넘겨주어 파이어 베이스에 저장.

(DB 클래스는 app.py  다음 설명)

 

 

 

- list 함수

@app.route("/list")
def post_list():
    post_list = DB.post_list()
    if post_list is None:
        length = 0
    else : 
        length = len(post_list)
    return render_template("post_list.html",post_list=post_list.items(),length=length)

 

DB.write_list는 내가 정의한 객체로 글 목록 정보들을 반환해준다.

만일 포스팅 한 글이 하나도 없으면 length=0,  그게 아니면 post_list의 개수로 length 값을 할당해준다.

그리고 post_list.html 을 렌더링하고 변수 post_list, length를 각각 넘겨준다.

 

 

 

- post 함수

@app.route("/post/<string:pid>")
def post(pid) : 
    post = DB.post_detail(pid=pid)
    return render_template("post_detail.html",post=post)

 

post_list.html에서는 모든 포스터들의 pid와 title을 보여주는데, 여기서 pid를 post_list로 전달한다.

정의한 DB.post_detail 함수에 pid 를 건네주어, 해당 pid와 일치하는 포스터의 items를 리턴하고 post에 저장한다.

그리고 post_detail.html에 렌더링하고 post 값을 파라미터 값으로 건네준다.

 

 

- user_post 함수

@app.route("/user/<string:uid>")
def user_post(uid) : 
    u_post = DB.get_user(uid)
    if u_post is None : 
        length = 0
    else : 
        length = len(u_post)
    return render_template("user_detail.html",post_list=u_post,length=length,uid=uid)

현재 로그인 중인 유저가 쓴 글을 보여주는 함수이다. 

정의한 DB.get_user에 uid를 넘겨주면, 해당 uid를 가지고 있는 모든 포스터들을 반환한다.

만약 반환된 포스터가 한개도 없으면 length = 0 을 주고, 그게 아니면 그 포스트 개수만큼 length 값을 할당한다.

그리고 user_detail.html을 렌더링하여 post_list, length, uid를 파라미터값으로 건네준다.

 


📃DB_handler.py

📕DBModule 클래스

    ...
    
    def write_post(self,title,content,uid):
        pid = str(uuid.uuid4())[:10]
        informations = {
            "title" : title,
            "contents" : content,
            "uid" : uid
        }
        self.db.child("posts").child(pid).set(informations)

    def post_list(self):
        post_list = self.db.child("posts").get().val()
        return post_list
    
    def post_detail(self,pid):
        post = self.db.child("posts").get().val()[pid]
        return post
    
    def get_user(self,uid):
        post_list = []
        users_post = self.db.child("posts").get().val()
        for post in users_post.items():
            if post[1]["uid"] == uid : 
                post_list.append(post)
        return post_list

 

(로그인/회원가입 관련 코드는 지난 포스팅에 작성했기 때문에 생략했다.)

 

- write_post  함수

중복되지 않은 값을 주기위해 uuid를 pid (포스터 id)로 주었다.

그리고 파라미터 title.content,uid를  informations에 넣어 firebase의 posts에 넣어주었다.

[데이터 베이스 posts 구조]
├─ POSTS─  PID
│                        └─TITLE
│                             CONTENT
│                             UID

 

 

- post_list 함수

저장된 posts의 값들을 가져와 반환해준다.

 

- post_detail 함수

파라미터 pid에 해당하는 ITEMS들(TITLE,CONTENT,UID)을 반환해준다.

 

- get_user 함수

모든 포스터들을 다 가져와 users_post에 넣어준다.

그런 다음 각각 post들의 pid를 반복문으로 돌린다.

전달 받은 uid와 각각 post들의  uid가 일치하는 post를  post_list에 다 넣어준다. (해당 유저가 작성한 글 확인)

그리고 post_list 값들을 반환해준다. 

 


📃 write_post.html

{%extends "base.html"%}

{% block contents %}
<form action="{{url_for('write_done')}}">
    <input type="text" value="제목을 입력해주세요" name="title" id="title" required>
    <input type="text" value="본문을 입력해주세요" name="content" id="content" required>
    <button type="submit">작성하기 </button>
</form>
{% endblock contents %}

base.html의 기본 템플릿을 가져온다.

input 태그에  포스터의 제목과 내용을 입력받아  form을 통해 write_done에 전해준다.


 

📃 post_list.html

{%extends "base.html"%}


{% block contents %}
<h2>글 목록</h2>

{% if length == 0 %}
<p>{{user}}의 post </p>

{% else %}
{% for post in post_list %}
<a href="{{url_for('post',pid=post[0])}}">
    <div>
        {{post[0]}}
        {{post[1]["title"]}}
    </div>
</a>

{% endfor %}
{% endif %}
{% endblock contents %}

똑같이 base.html 템플릿을 가져오고,

if 문을 통해 length가 0일 경우와 0이 아닐 경우를 나눠준다.

그리고 전달 받은 post_list를 반복문으로 돌리고, 

a 태그를 사용해서 post[0] (PID)를 post 함수로 전달하게 한다.

아래의 div 태그는 각각 포스터들의 PID와 ITEMS(TITLE)을 나타낸다.

 


📃 post_detail.html

{%extends "base.html"%}

{% block contents %}
<p>제목 : {{post["title"]}}</p>
<p>작성자 : {{post["uid"]}}</p>
<p>내용 : {{post["contents"]}}</p>

{% endblock contents %}

post_detail 페이지에서는 flask post 함수의 pid로 부터 추출한 post를 가져온다.

그리고 post의 ITEMS(TITLE,UID,CONTENTS)를 보여준다.


📃 user_detail.html

{%extends "base.html"%}

{% block contents %}
<h2>글 목록</h2>

{% if length == 0 %}
<p>작성된 포스트가 없습니다.</p>

{% else %}
{% for post in post_list %}
<a href="{{url_for('post',pid=post[0])}}">
    <div>
        {{post[0]}}
        {{post[1]["title"]}}
    </div>
</a>

{% endfor %}
{% endif %}

{% endblock contents %}

user_detail.html에서는 현재 로그인 사용자의 uid를 통해, 해당 uid와 일치하는 post들을 리턴값으로 준다.

리턴값으로 받은 post_list들은 반복문을 통해 주어지고,

a 태그를 통해 각각의 post들에 들어갈 수 있는 링크를 만들어준다.

div 태그는 a 태그안에 있어, 해당 post의 pid와 title을 나타낸다.

 


🧐 마무리

간단한 포스팅과 포스트 목록들을 구현해봤다.

session의 uid를 정의해서,  uid와 직결된 url을 만드는 방법을 알아서 좋았다.

pid 또한 중복 될 수 없는 값으로 정의해주고, 각각 post들의 pid에 직결된 url을 만들었다.

 

flask / DB / HTML 파일들이 서로 유기적 관계를 이뤄 헷갈린다. 

아직 익숙하지 않아서 그런것 같다.

하지만 나중에 유지보수에는 객체로 해서 훨씬 편할 것 같다.


📑참고 코드 파일

login.html
0.00MB
post_detail.html
0.00MB
post_list.html
0.00MB
signin.html
0.00MB
user_detail.html
0.00MB
write_post.html
0.00MB
base.html
0.00MB
index.html
0.00MB
app.py
0.00MB
DB_handler.py
0.00MB