본문 바로가기
파이썬/파이썬 플라스크

ep3. 블로그 웹 애플리케이션 개발(0) - 프로젝트 생성, 패키지 설치, 기본 작업

by L_SU 2022. 7. 17.

예시

먼저 실습을 위해 blog란 이름의 폴더를 생성하고, 그 안에 __init__.py라는 이름의 파일을 생성한다.

ORM 사용을 위한 설치 1

pip install Flask-SQLAlchemy

ORM  사용을 위한 설치 2

pip install Flask-Login

 

각각의 설치가 끝났다면 다음 과 같이 코드를 작성해준다.

실습을 위한 예제 코드

1번째 줄부터 4번째 줄까지는 기능 사용을 위한 import 작업을 해준 것인데, 1번째 줄부터 flask 사용, 2번째 모델을 파이썬 클래스로 다루기, 3번째 데이터 베이스에서의 경로 설정, 4번째 로그인 기능으로 각각의 기능(?)들을 사용하기 위한 것이다.

다음으로 6번째 줄은 create_app()이라는 함수를 선언해, 이를 호출해 쓸 수 있게 만들어 준 것이다.

7번째 줄은 전에 설명한 관계로 제외하고,

8번쨰 줄은 나중에 데이터베이스에서의 사용에 있어 에러를 방지하고자 key값을 설정해준 것이다.

그 아래의 줄들은 7번째 줄과 같이 전에 설명했던 내용임으로 생략하겠다.

실습을 위한 예제 코드2

1번째 줄: debug를 수행하기 위한 import

2번째 줄: 위에 파일에서 만든 함수를 사용하기 위한 import

3번째 줄: 지금 현재 파일이 main파일일 경우의 아래 코드를 실행시킨다는 의미 -> 처음 실행된 파일이 __main__이 된다. 즉, 다른 파일에서 실행돼 app.py 파일이 import 되어 사용된다면, if문에 걸치지 않는다.

4번째 줄: app라는 변수에 create_app()함수를 받는 것이다. 사용에 용이하기때문

5번쨰 줄: 가져온 함수를 통해 run()으로 실행시켜 debug하기 위해 옵션으로 debug=True로 준 것이다.

 

예제 결과 1
예제 결과 2

그래서, 터미널에서 python ./app.py로 앱을 실행시키면 다음과 같은 결과들을 얻고, 서버가 성공적으로 열렸다는 것을 알 수 있는 것이다. 이외에 다시 실습으로 돌아와 우리가 블로그 구현에 있어 가장 단순한 방법(?)은 create_app()함수에 필요한 주소들을 모두 구현하는 것이지만 그렇게 되면 create_app()함수 코드의 길이가 엄청나게 길어질 것이고, 나중에 수정이 필요하거나 필요한 부분을 찾는데에 있어 불편함을 겪게 될 것이다. 이러한 문제를 해결하기 위해 우리는 BluePrint 라는 것을 활용할 것이다.

 

블루프린트(blueprint)란?

보통 객체지향 프로그래밍에서 "청사진"을 뜻하는 용어인데 플라스크에서는 URL과 함수의 매핑을 관리하기 위해 사용하는 도구이다.

그냥 쉽게 말해서 위처럼 길어지는 코드를 기능에 따라 나눠서 관리해주는 느낌이라고 생각하면 될 것 같다.

블루 프린트 사용을 위한 예제

블루 프린트를 사용하기 위해 auth.py 와 views.py라는 파일을 만들었다.

auth.py에선 로그인 관련 기능을 views.py에선 페이지에 관한 것들을 다룰 것이다.

 

 

블루 프린트 예제 코드 1

views.py에는 다음과 같이 코딩해줬다.

1번째 줄: blueprint를 사용하기 위한 import

2번째 줄:views라는 변수에 blueprint를 사용하기 위해 'views'라는 이름으로 설정하고 __name__으로 views의 위치를 알려준 것이다.

3번째 줄이후론 이전에 설명한 내용임으로 패스.

 

블루 프린트 예제 코드(수정)

위처럼 views.py파일을 코딩했으면 __init__.py 파일을 다음과 같이 수정해준다.

config()이후 문장이 바뀐걸 알 수 있는데 한줄씩 살펴보자면

역시 views를 사용하기 위한 import로 시작한다.

blueprint를 등록해주고, url의 기본값을 주소/blog 로 설정해주었다.

블루 프린트 예제 결과

그럼 다음과 같은 값이 기본 주소, 홈 주소가 된다고 생각하면 된다.

 

다음은 HTML 템플릿을 사용해 페이지를 구성해보겠다.

 

템플릿 사용을 위해 views.py 와 auth.py에 render_template를 import해주면 된다.

템플릿 사용을 위한 예제 1

import가 완료 되었다면, 다음 과 같이 폴더와 html 파일 하나를 생성해준다.

템플릿 사용을 위한 예제 코드 1

그리고 html 파일에 다음과 같이 짧게 코딩을 해준다. 본 글은 flask를 다루기 위한 글임으로 html 코드는 상세히 다루지 않겠다. 그냥 결과를 보기위한 간단한 예제라고 생각해주면 좋겠다.

 

템플릿 사용을 위한 예제 코드 2

그러곤 views.py 파일의 코드를 이렇게 바꿔주면 된다.

템플릿 사용을 위한 예제 2

그럼 다음과 같은 결과를 얻을 수 있다. 이것이 템플릿을 활용해 페이지를 구성하는 방법이다.

 

다음으론 Bootstrap을 사용해 flask에서 정적 파일을 다뤄보겠다. Clean Blog - Bootstrap Blog Theme - Start Bootstrap 사이트에서 템플릿을 다운받을 수 있다.

정적 파일 다루기

다운 받은 파일들 중 html 파일들을 templates 폴더로 이동시켜준다. 아까 전 이해를 위해 사용한 예제들은 모두 삭제하는 것이 좋다.

정적 파일 다루기를 위한 코드 수정

views.py에 home.html을 index.html로 수정해준다.

정적 파일 결과

그럼 다음과 같이 화면이 바뀐 걸 알 수 있다. 디자인이 없이 글씨만 있는건, 우리가 css를 적용하지 않아서 그렇다.

정적 파일 다루기를 위한 파일 이동

그리곤 static이라는 파일을 만들어 다음과 같은 폴더들을 이동시켜주고

 

아까 html파일들을 이동시켰던 템플릿 폴더에 base.html이란 파일을 만들어 다음과 같이 코딩해준다.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1, shrink-to-fit=no"
    />
    <meta name="description" content="" />
    <meta name="author" content="" />

    {# 변화하는 부분 #}
    <title>{% block title %}{% endblock %}</title>

    <link rel="icon" type="image/x-icon" href="assets/favicon.ico" />
    <!-- Font Awesome icons (free version)-->
    <script
      src="https://use.fontawesome.com/releases/v6.1.0/js/all.js"
      crossorigin="anonymous"
    ></script>
    <!-- Google fonts-->
    <link
      href="https://fonts.googleapis.com/css?family=Lora:400,700,400italic,700italic"
      rel="stylesheet"
      type="text/css"
    />
    <link
      href="https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800"
      rel="stylesheet"
      type="text/css"
    />

    <!-- Core theme CSS (includes Bootstrap)-->
    <link
      href="{{ url_for('static', filename='css/styles.css') }}"
      rel="stylesheet"
    />
  </head>
  <body>
    <!-- Navigation-->
    <nav class="navbar navbar-expand-lg navbar-light" id="mainNav">
      <div class="container px-4 px-lg-5">
        <a class="navbar-brand" href="index.html">Start Bootstrap</a>
        <button
          class="navbar-toggler"
          type="button"
          data-bs-toggle="collapse"
          data-bs-target="#navbarResponsive"
          aria-controls="navbarResponsive"
          aria-expanded="false"
          aria-label="Toggle navigation"
        >
          Menu
          <i class="fas fa-bars"></i>
        </button>
        <div class="collapse navbar-collapse" id="navbarResponsive">
          <ul class="navbar-nav ms-auto py-4 py-lg-0">
            <li class="nav-item">
              <a class="nav-link px-lg-3 py-3 py-lg-4" href="index.html"
                >Home</a
              >
            </li>
            <li class="nav-item">
              <a class="nav-link px-lg-3 py-3 py-lg-4" href="about.html"
                >About</a
              >
            </li>
            <li class="nav-item">
              <a class="nav-link px-lg-3 py-3 py-lg-4" href="post_detail.html"
                >Sample Post</a
              >
            </li>
            <li class="nav-item">
              <a class="nav-link px-lg-3 py-3 py-lg-4" href="contact.html"
                >Contact</a
              >
            </li>
          </ul>
        </div>
      </div>
    </nav>

    {# 변화하는 부분 #} {% block header %}{% endblock %} {# 변화하는 부분 #}
    <div class="content-wrapper">{% block content %}{% endblock %}</div>

    <!-- Footer-->
    <footer class="border-top">
      <div class="container px-4 px-lg-5">
        <div class="row gx-4 gx-lg-5 justify-content-center">
          <div class="col-md-10 col-lg-8 col-xl-7">
            <ul class="list-inline text-center">
              <li class="list-inline-item">
                <a href="#!">
                  <span class="fa-stack fa-lg">
                    <i class="fas fa-circle fa-stack-2x"></i>
                    <i class="fab fa-twitter fa-stack-1x fa-inverse"></i>
                  </span>
                </a>
              </li>
              <li class="list-inline-item">
                <a href="#!">
                  <span class="fa-stack fa-lg">
                    <i class="fas fa-circle fa-stack-2x"></i>
                    <i class="fab fa-facebook-f fa-stack-1x fa-inverse"></i>
                  </span>
                </a>
              </li>
              <li class="list-inline-item">
                <a href="#!">
                  <span class="fa-stack fa-lg">
                    <i class="fas fa-circle fa-stack-2x"></i>
                    <i class="fab fa-github fa-stack-1x fa-inverse"></i>
                  </span>
                </a>
              </li>
            </ul>
            <div class="small text-center text-muted fst-italic">
              Copyright &copy; Your Website 2022
            </div>
          </div>
        </div>
      </div>
    </footer>
    <!-- Bootstrap core JS-->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>

    <!-- Core theme JS-->
    <script src="{{ url_for('static', filename='js/scripts.js') }}"></script>
  </body>
</html>

 

그리고 index.html 파일은 다음과 같이 수정해준다.

{% extends 'base.html' %}

{% block title %}This is home.{% endblock %}

{% block header %}
    <!-- Page Header-->
    <header class="masthead" style="background-image: url('assets/img/home-bg.jpg')">
        <div class="container position-relative px-4 px-lg-5">
            <div class="row gx-4 gx-lg-5 justify-content-center">
                <div class="col-md-10 col-lg-8 col-xl-7">
                    <div class="site-heading">
                        <h1>Clean Blog</h1>
                        <span class="subheading">A Blog Theme by Start Bootstrap</span>
                    </div>
                </div>
            </div>
        </div>
    </header>
{% endblock %}

{% block content %}
    <!-- Main Content-->
    <div class="container px-4 px-lg-5">
        <div class="row gx-4 gx-lg-5 justify-content-center">
            <div class="col-md-10 col-lg-8 col-xl-7">
                <!-- Post preview-->
                <div class="post-preview">
                    <a href="post_detail.html">
                        <h2 class="post-title">Man must explore, and this is exploration at its greatest</h2>
                        <h3 class="post-subtitle">Problems look mighty small from 150 miles up</h3>
                    </a>
                    <p class="post-meta">
                        Posted by
                        <a href="#!">Start Bootstrap</a>
                        on September 24, 2022
                    </p>
                </div>
                <!-- Divider-->
                <hr class="my-4"/>
                <!-- Post preview-->
                <div class="post-preview">
                    <a href="post_detail.html"><h2 class="post-title">I believe every human has a finite number of
                        heartbeats. I don't intend to waste any of mine.</h2></a>
                    <p class="post-meta">
                        Posted by
                        <a href="#!">Start Bootstrap</a>
                        on September 18, 2022
                    </p>
                </div>
                <!-- Divider-->
                <hr class="my-4"/>
                <!-- Post preview-->
                <div class="post-preview">
                    <a href="post_detail.html">
                        <h2 class="post-title">Science has not yet mastered prophecy</h2>
                        <h3 class="post-subtitle">We predict too much for the next year and yet far too little for the
                            next ten.</h3>
                    </a>
                    <p class="post-meta">
                        Posted by
                        <a href="#!">Start Bootstrap</a>
                        on August 24, 2022
                    </p>
                </div>
                <!-- Divider-->
                <hr class="my-4"/>
                <!-- Post preview-->
                <div class="post-preview">
                    <a href="post_detail.html">
                        <h2 class="post-title">Failure is not an option</h2>
                        <h3 class="post-subtitle">Many say exploration is part of our destiny, but it’s actually our
                            duty to future generations.</h3>
                    </a>
                    <p class="post-meta">
                        Posted by
                        <a href="#!">Start Bootstrap</a>
                        on July 8, 2022
                    </p>
                </div>
                <!-- Divider-->
                <hr class="my-4"/>
                <!-- Pager-->
                <div class="d-flex justify-content-end mb-4"><a class="btn btn-primary text-uppercase" href="#!">Older
                    Posts →</a></div>
            </div>
        </div>
    </div>
{% endblock %}

 

여기서 html 코드들은 설명을 하진 않지만 우리가 필수로 알아야 할 부분들이 있다. 

1.<script src="{{ url_for('static', filename='js/scripts.js')}}</script>와 같이 url_for을 이용해 css를 연결해준 것

2. {% extends 'base.html' %} 을 아래 index코드에서 활용해 html, nave, title과 같은태그들이 생략되었다는 것, base.html을 import해준 것과 같은 의미기 때문

3.index.html도 비슷한 형식({%block %})의 코드들을 사용해 content, header, title을 처리했다는 것

 

이정도는 필히 알아야 다음에 있을 내용을 이해하는데 도움이 된다.

 

위와 같은 작업으로 css연결 작업을 마쳤다면 아까처럼 깨지는 화면이 아닌 아래와 같은 결과물을 얻을 수 있을 것이다.

 

from flask import Blueprint, render_template

views = Blueprint("views", __name__)

@views.route("/")
def home():
    return render_template("index.html")

@views.route("/about")
def about():
    return render_template("about.html")

@views.route("/categories-list")
def categories_list():
    return render_template("categories_list.html")

@views.route("/post-list")
def post_list():
    return render_template("post_list.html")

@views.route('posts/<int:id>')
def post_detail():
    return render_template("post_detail.html")

@views.route("/contact")
def contact():
    return render_template("contact.html")
from flask import Blueprint, render_template, redirect

auth = Blueprint("auth", __name__)

@auth.route("/login")
def login():
    return render_template("login.html")

@auth.route("/logout")
def logout():
    return redirect("views.blog_home")

@auth.route("/sign-up")
def signup():
    return render_template("signup.html")

 

각각 views.py auth.py의 코드들이다. 앞으로 구현할 페이지들을 미리 만들어준 모습이다. 다음 내용에선 로그인 기능을 다루겠다.

'파이썬 > 파이썬 플라스크' 카테고리의 다른 글

ep 5. 관리자 페이지, 카테고리, 게시물 관리  (0) 2022.07.29
ep 4. 로그인 처리  (0) 2022.07.26
2-5. sqlite3  (0) 2022.07.10
2-4. Python DB API (PEP 249)  (0) 2022.07.10
2-3 쓰기 구현  (0) 2022.07.10