[파이썬] 나만의 스팀잇 블로그 만들기 #2 - 글 목록

in #kr6 years ago (edited)

안녕하세요. @anpigon입니다.

나만의 스팀잇 블로그 만들기 두 번째 강좌입니다. 이 시리즈는 개발 지식이 전혀 없는 분들도 쉽게 따라 할 수 있습니다.


이전 글



blog url 추가하기


내 블로그 주소(예: anpigon.pythonanywhere.com)로 접속하면 첫 페이지에 나의 스팀잇 글 목록이 보이게 만들어 보자.

pythonanywhere에 접속하여 Files 페이지로 이동한다. 그리고 web/web 디렉토리로 이동한다. 만약 사용자명이 anpigon이고 프로젝트명이 web이면 전체 디렉토리 경로는 /home/anpigon/web/web/ 가 된다. 디렉토리를 정확하게 잘 찾아들어갔다면 아래와 같은 화면을 볼 수 있다.

web디렉토리이동

urls.py 파일을 클릭하여 편집화면으로 이동한다. 그리고 코드 내용을 아래와 같이 수정한다.

from django.urls import path, include

urlpatterns = [
    path('', include('blog.urls'))
]

여기서 path('', include('blog.urls')) 코드는 URL로 접속하는 모든 요청을 blog.urls 에서 처리하겠다는 의미이다.

실제 편집화면의 스크린 캡처을 바로 아래에 넣었다.
urls.py 수정
편집화면 상단에 있는 새로고침 버튼 새로고침 버튼을 클릭하면 수정된 내용이 서버에 바로 반영된다.

Save 버튼을 클릭하여 저장한다. 그리고 새로고침 버튼을 클릭해보자. 그리고 블로그 주소(예: anpigon.pythonanywhere.com)로 접속하면 아래와 같은 화면을 볼 수 있다.

이렇게 화면에 오류 내용이 보이는 것이 정상이다. 왜냐하면 아직 blog를 구현하지 않았기 때문이다.


이제 본격 블로그를 개발해보자.




블로그 글 목록 URL 생성하기


방금 수정한 urls.py 파일이 있는 디렉토리에서 상위 디렉토리로 이동한다. 그리고 blog 디렉토리를 생성한다. blog가 생성된 전체 경로는 /home/anpigon/web/blog 이다. 아래 화면을 참고하여 생성된 디렉토리 경로를 확인하자.

blog디렉토리생성

blog/ 디렉토리에 들어가서 urls.py 파일을 신규 생성한다.

blog/urls.py파일생성

생성된 blog/urls.py 파일을 클릭하여 편집화면으로 이동한다. 편집창에 아래 코드를 입력하고 저장한다.

from django.urls import re_path
from . import views

urlpatterns = [
    re_path('^$', views.post_list, name='post_list')
]

re_path('^$', views.post_list, name='post_list') 코드를 설명하면 ^$ URL에 글 목록을 보여줄 뷰(View) views.post_list를 맵핑하였다. 여기서 정규식 ^$아무 문자열이 없을 경우라는 의미이다. 아직은 정규식에 대해서 공부할 필요가 없다. 마지막으로 name='post_list'은 URL을 식별하기 위해 붙인 이름이다.

이 부분이 잘 이해가 안 되면 그냥 넘어가도 된다. 시간이 지난 뒤에 나중에 다시 보면 이해가 될 것이다.




블로그 글 목록 View 생성하기


여기서부터는 난이도가 조금 올라간다. 하지만 걱정할 필요가 없다. 천천히 따라하면 쉽게 할 수 있다. 참고로 이 글에서는 steemit에서 제공하는 파이썬 sdk를 사용하지 않았다.

방금 blog/urls.py 파일을 생성한 디렉토리에서 views.py 파일을 신규 생성한다. 그리고 views.py 파일을 클릭하여 편집화면으로 이동한다. 그리고 아래 설명서를 보면서 코드를 천천히 입력해보자. 코드 양을 보고 겁낼 필요가 없다.

우선 글 목록 뷰에 필요한 파이썬 모듈들을 임포트(import) 한다.

from django.shortcuts import render # 장고 View 렌더러 모듈
from datetime import datetime       # 날짜시간 모듈
import markdown                     # 마크다운 HTML 변환 모듈
import requests                     # 서버 API 요청 모듈
import json                         # json 파싱 모듈

해당 모듈에 대한 설명은 간략하게 주석(#)을 달아놓았다.


마크다운 문서를 HTML로 변환하기 위한 모듈인 Markdown객체를 생성하여 md 변수에 담는다. 나는 마크다운 문서가 HTML로 잘 변환되길 바라면서 확장 기능 두개를 같이 포함하여 Markdown객체를 생성하였다.

md = markdown.Markdown([
  'markdown.extensions.extra', 
  'markdown.extensions.codehilite'
])

여기서 사용한 Markdown의 확장 기능을 설명하면, 'markdown.extensions.extra'는 마크다운의 확장 문법을 파싱하는데 도움을 준다. 그리고 'markdown.extensions.codehilite'Syntax highlighting를 적용하는데 매우 유용하다. Syntax highlighting가 궁금하신 분은 https://highlightjs.org를 방문해보면 무슨 뜻인지 이해될 것이다.


다음 라인에는 parse_time 함수를 구현한다. parse_time 함수는 날짜 텍스트를 파싱하는데 사용할 것이다. 참고로 파이썬에서 def로 시작하면 무조건 함수라고 보면 된다.

def parse_time(date):
    return datetime.strptime(date, '%Y-%m-%dT%H:%M:%S')



스팀잇 API서버에서 글을 가져오기 위해 URLUSERNAME를 변수를 정의하자. USERNAME 변수에는 본인의 스팀잇 아이디를 입력한다. 하지만 제 블로그를 만들어 주실 분은 ‘anpigon’을 그대로 입력해도 좋다.

URL = 'https://api.steemit.com' # 스팀잇 API URL 주소
USERNAME = 'anpigon'            # 본인의 스팀잇 아이디



이제 제일 중요한 메인 함수인 post_list 함수를 구현한다. post_list 함수는 내 스팀잇 글을 가져와서 화면에 출력할 것이다. 아래 코드 내용을 복사&붙여넣기 한다.

# 스팀잇 글 목록 가져오기
def post_list(request, limit=20):
  
    # 스팀잇 API 요청하여 글을 가져온다.
    data = '''{
        "jsonrpc": "2.0",
        "method": "follow_api.get_blog",
        "params": {"account": "%s", "start_entry_id": %i, "limit": %i},
        "id": 1
    }''' % (USERNAME, 0, limit)
    response = requests.post(URL, data=data)

    # 스팀잇 글 목록을 담을 변수
    posts = [] 
    
    # 응답이 성공이라면 다음 로직을 진행한다.
    if response.status_code == 200: 
        # JSON 텍스트를 파싱한다.
        data = json.loads(response.text) 
                
        # 파싱한 JSON 데이터에서 blog 정보를 꺼내온다.
        blogs = data['result']['blog']
        
        # 반복문을 돌면서 필요한 정보를 받아오자.
        for blog in blogs: 
            blog = blog['comment']

            # 마크다운을 HTML로 변환
            html = md.convert(blog['body']) 
            post = {
                'title': blog['title'],       # 제목
                'author': blog['author'],     # 작성자
                'permlink': blog['permlink'], # 고유식별ID
                'created': parse_time(blog['created']), # 작성일
                'body': html[:200],    # 본문내용 200글자로 자르기
            }
            posts.append(post)
    
    # 템플릿에 데이터 전달
    return render(request, 'blog/post_list.html', {'posts': posts})

위 코드 내용을 설명하면 post_list 함수는 스팀잇 API 서버에서 게시글을 가져와 화면에 출력한다. 한 번에 가져와서 보여줄 글 수는 limit=20으로 설정했다. md.convert() 함수는 마크다운으로 작성된 텍스트를 HTML로 변환해준다. 그리고 목록 페이지에는 본문의 전체 내용이 필요 없기 때문에 html[:200]를 사용하여 본문 내용을 200 글자로 잘랐다. 마지막으로 작성일은 앞에서 구현한 parse_time 함수를 이용하여 날짜로 변환하였다. 참고로 위 코드 내용을 이해하지 못해서 괜찮다.




디자인 템플릿 적용하기


디자인 템플릿 파일을 사용하기 위해서는 템플릿 파일 경로를 설정해야 한다. web/web/settings.py 파일을 찾아서 편집한다. 57번 라인의 DIRSos.path.join(BASE_DIR, 'templates')를 추가한다. 아래 화면을 참고하자.

settings.py파일수정

이렇게 설정하면 이제 템플릿 파일이 위치한 경로는 /home/anpigon/web/templates가 된다.


web/ 디렉토리에 templates/blog 디렉토리를 생성한다. templates/blog 디렉토리에 디자인 템플릿 html 파일을 생성할 것이다.

templates/blog디렉토리생성


기본 템플릿 html 생성

기본 템플릿은 모든 페이지에 확장되어 사용되는 템플릿이다. 앞으로 구현할 글 목록 페이지와 글 상세 페이지는 기본 템플릿을 베이스로 사용한다.

web/templates/blog 디렉토리에 base.html 파일을 생성한다. 그리고 base.html 파일에 아래 내용을 복사&붙여넣기 한다. 아래 코드는 Clean Blog 템플릿 파일을 장고에서 사용할 수 있게 수정하였다.

{% load static %}
<!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>Clean Blog - Start Bootstrap Theme</title>
    <link href="{% static 'vendor/bootstrap/css/bootstrap.min.css' %}" rel="stylesheet">
    <link href="{% static 'vendor/fontawesome-free/css/all.min.css' %}" rel="stylesheet" type="text/css">
    <link href='//fonts.googleapis.com/css?family=Lora:400,700,400italic,700italic' rel='stylesheet' type='text/css'>
    <link href='//fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800' rel='stylesheet' type='text/css'>
    <link href="{% static 'css/clean-blog.min.css' %}" rel="stylesheet">
  </head>
  <body>

    <nav class="navbar navbar-expand-lg navbar-light fixed-top" id="mainNav">
      <div class="container">
        <a class="navbar-brand" href="/">Start Bootstrap</a>
        <button class="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse" data-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 ml-auto">
            <li class="nav-item">
              <a class="nav-link" href="/">Home</a>
            </li>
            <li class="nav-item">
              <a class="nav-link" href="#">About</a>
            </li>
            <li class="nav-item">
              <a class="nav-link" href="#">Sample Post</a>
            </li>
            <li class="nav-item">
              <a class="nav-link" href="#">Contact</a>
            </li>
          </ul>
        </div>
      </div>
    </nav>

    {% block content %}
    {% endblock %}

    <hr>

    <footer>
      <div class="container">
        <div class="row">
          <div class="col-lg-8 col-md-10 mx-auto">
            <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>
            <p class="copyright text-muted">Copyright &copy; Your Website 2018</p>
          </div>
        </div>
      </div>
    </footer>
    <script src="{% static 'vendor/jquery/jquery.min.js' %}"></script>
    <script src="{% static 'vendor/bootstrap/js/bootstrap.bundle.min.js' %}"></script>
    <script src="{% static 'js/clean-blog.min.js' %}"></script>
  </body>
</html>

위 내용 중에서 중간쯤을 살펴보면 {% block content %} {% endblock %} 코드가 있다. 이 {% block %} 영역은 base.html을 확장하여 다른 템플릿에 사용할 수 있게 구멍을 뚫어 놓은 거라고 보면 된다. 참고로 {% %}은 장고에서 사용할 수 있는 템플릿 태그라고 한다.


블로그 글 목록 템플릿 html 생성

이제 실제로 글 목록을 보여줄 템플릿 html를 만들자. 템플릿 디렉토리 templates/blogpost_list.html 파일을 신규 생성한다. 그리고 post_list.html 파일에 아래 내용을 복사&붙여넣기 한다.

{% extends 'blog/base.html' %}

{% load static %}

{% block content %}
<header class="masthead" style="background-image: url({% static 'img/home-bg.jpg' %})">
  <div class="overlay"></div>
  <div class="container">
    <div class="row">
      <div class="col-lg-8 col-md-10 mx-auto">
        <div class="site-heading">
          <h1>Clean Blog</h1>
          <span class="subheading">A Blog Theme by Start Bootstrap</span>
        </div>
      </div>
    </div>
  </div>
</header>

<div class="container">
  <div class="row">
    <div class="col-lg-8 col-md-10 mx-auto">

      {% for post in posts %}
          <div class="post-preview">
            <a href="#">
              <h2 class="post-title">
                {{ post.title }}
              </h2>
              <h3 class="post-subtitle">
                {{ post.body|striptags|truncatechars:50 }}
              </h3>
            </a>
            <p class="post-meta">Posted by
              <a href="#">{{ post.author }}</a>
              on {{ post.created }}</p>
          </div>
          <hr>
      {% endfor %}

      <div class="clearfix">
        <a class="btn btn-primary float-right" href="#">Older Posts &rarr;</a>
      </div>
    </div>
  </div>
</div>
{% endblock %}

위 코드에서 {% extends 'blog/base.html' %}blog/base.html 템플릿을 확장해서 사용하겠다는 선언이다. 그리고 {% block content %}{% endblock %} 사이에 있는 코드 내용이 위에서 설명한 기본 템플릿에 뚫어놓은 구멍인 {% block content %} {% endblock %} 영역에 들어갈 것이다.

그리고 {{ post.body|striptags|truncatechars:50 }}코드에서 사용한 striptags 필터는 HTML태그를 제거해준다. 그리고 truncatechars 필터는 문자열을 원하는 길이(50)로 잘라낸 다음 끝에 을 붙여준다.


내용이 너무 길어져서 이번 시간은 여기까지~~~




이제 내 블로그 주소로 들어가면 아래와 비슷한 화면을 볼 수 있다.

http://anpigon.pythonanywhere.com

여기까지 성공하셨다면 축하합니다.


kr-dev에 개발 관련 글을 올리는 뉴비분들은 제가 보팅해드립니다. 저에게 보팅을 약 2000번 정도 받으면 원활한 활동에 필요하다는 100SP를 모을 수 있을 거예요. 그렇게 되려면 글을 2000개는 쓰셔야겠네요.🙊

여기까지 읽어주셔서 감사합니다.


크리에이티브 커먼즈 라이선스
이 저작물은 크리에이티브 커먼즈 저작자표시-동일조건변경허락 4.0 국제 라이선스에 따라 이용할 수 있습니다.

Sort:  

헐 kr-dev 지원까지. 안피곤님 큰 손이네요. ^^ 글 퀄리티도 훌륭하시고 멋지십니다. 저도 좀 보고 따라해봐야겠어요. ㅎㅎㅎ

요즘 kr-dev에 올라오는 글이 많이 줄어서 제가 보팅을 해주면 글이 좀 올라오지 않을까 생각했어요.
제가 글을 잘 쓰는 편이 아닌데 칭찬을 받으니 부끄럽네요. 칭찬해주셔서 감사합니다.^^
유로보틱스님도 훌륭하고 멋지십니다. 그리고 궁금한게 있는데 혹시 고양이 게임 출시됬나요?ㅎㅎ

올 그렇게 건설적인 생각을 ㅎㅎㅎ 예 감사합니다. ㅎㅎㅎ 고양이 게임은 오늘도 저를 보고 있던데요. ㅇ_ㅇ 아직 계류중입니다. 게임성 높여볼려다가 이런 저런 일이 겹치는 바람에 ㅎㅎㅎㅎ 이번 주 내에는 출시해볼려고 합니다. (사실 출시라기도 민망합니다만 ㅎㅎ)

고양이 게임성을 높이고 계셨군요. 사실 저도 게임을 만들어 보고싶어요. 스팀잇을 이용해서 어떤 게임을 만들수 있을까 고민했었거든요. 그런데 게임을 한번도 만들어본적이 없어서...ㅎㅎㅎ
고양이 게임 출시되면 스팀잇에 알려주세요~🐴

한 씬 뿐인데 ㅎㅎ너무 그냥 던지면 뭐해서요 ㅎㅎ 뭐라도 넣어볼까 궁리중입니다. ㅋㅋ아 게임 첨이시군요. 오호. 쩐에 배운거 한 번 공유할게요.네네 감사합니다. 고양이 게임 기대도 해주시고 ㅎㅎ

스티밋을 이용해 블로그를~
대단하십니다~

스티미언이 아닌 사람이 쉽게 접근할 수 있는 블로그를 만들어보면 어떨까? 하고 생각해봤어요.
그리고 응원 감사합니다.😊

오 대박... 멋지네요. bbbb
리스팀합니다!

오 리스팀 감사합니다~😀

공부한거 올려서 저두 kr-dev 이거 써볼게염
ㅋㅋㅋㅋ안선생님ㅋㅋㅋ

kr-dev에 올려주세요~
그럼 숙제 검사 들어갑니다.ㅋㅋ

대단한 코딩 솜씨이십니다.

코딩아트님 칭찬 감사합니다. 열심히 코딩 실력을 갈고 닦아야죠 ㅋㅋ

1탄에도 방금 적었지만 파이썬 공부 해보고 싶은데 스팀잇도 하고 파이썬도 공부하는 1석2조!!! 이번에 새로운 플젝 들어가는데 2주정도 쉴때 공부 해야겠어요~

http://hjh0827.pythonanywhere.com/

포스팅 1탄 2탄 보고 20분 가량 해서 완성!!! 소스 코드 내용은 담에 시간 있을때 확인해볼께요~능력자님 감사합니다~

와우, 1탄 2탄을 보고 20분 만에 만드셨다니,
정말 따라해보고 싶군요.

오오 20분에 만드셨다니 놀랍습니다.
따라 하는 분이 있어서 기분이 좋습니다.
댓글에 보팅 들어갑니다아~~ㅎㅎ

좋은 글도 써주셨는데 보팅까지! 진짜 포스팅따라서 쭉 복붙하면 되서 진짜 간단하더라고요 최고입니다!

오 멋진 시리즈네요ㅎㅎ 그런데 pythonanywhere webapp말고 console에서 api접속잘되나요? 예전에 해보니 접속이 안되더라고요. proxy세팅을 해줘야하는거 같던데 단순히 steempy library자체에서 옵션으로 넘길수있는건 아닌것 같아 찾아보려다 귀찮아서 관뒀는데 혹시 잘 되시면 알려주세요. 감사합니다~

파이썬 steem sdk 가 잘안되서 sdk 없이 구현중이었는데 proxy 문제가 원인이었군요.
나중에 혹시라도 하게되면 알려드릴께요.ㅎㅎ

Hi @anpigon!

Your post was upvoted by @steem-ua, new Steem dApp, using UserAuthority for algorithmic post curation!
Your UA account score is currently 1.726 which ranks you at #30255 across all Steem accounts.
Your rank has dropped 274 places in the last three days (old rank 29981).

In our last Algorithmic Curation Round, consisting of 407 contributions, your post is ranked at #275.

Evaluation of your UA score:
  • Only a few people are following you, try to convince more people with good work.
  • The readers like your work!
  • You have already shown user engagement, try to improve it further.

Feel free to join our @steem-ua Discord server

저도 2일차까지 했습니다. ㅋㅋㅋ

2일차.jpg

와 멋집니다. 금방 하셨네요.
이제 곧 완성하시겠어요.ㅎㅎ

시간 걸리는데요 ㅎㅎ 감사합니다. 덕분입니다. ㅎㅎ

2단계두 1시간 걸렸어염 ^^
컴맹이라 좀 느리네요 ㅎㅎ

1시간이면 금방 하신 겁니다.
전 구현하고 글 작성하는데 반나절 걸렸습니다.ㅋ
뽀돌님의 결과물을 빨리 보고싶네요.

Coin Marketplace

STEEM 0.26
TRX 0.13
JST 0.032
BTC 61133.31
ETH 2887.29
USDT 1.00
SBD 3.64