django에서 github flavored markdown 쓰기
github API를 이용하면 github의 markdown문법을 동일하게 쓸 수 있다.

by saintdragon2


Posted on March 1, 2017, 3:58 p.m.



Github API를 이용하여 django에서 Markdown문서 렌더링하기

Django를 이용해 개인 블로그를 개발하면서, Markdown 문법을 사용하기로 했다. 그런데 Markdown 문법이 조금씩 다 다르기 때문에, 어떤 parser를 쓰느냐에 따라 결과가 달라지는 문제가 있었다. 원래는 django-markdown2을 사용했으나, github의 README.md를 작성하고 그 내용을 내가 만든 django 기반의 블로그에 옮겨 붙이면, 렌더링이 될 때 깨지고 난리도 아니었다.

그래서 어떻게 하든, Github랑 똑같이 렌더링해주는 걸 찾아 헤맸고, Github에서 API를 제공하고 있다는 것을 알게 되었다.

Django 프로젝트 시작하기

언제나 django 프로젝트는 가상환경 만들기 부터!

$ mkdir django-gfm
$ cd django-gfm
$ virtualenv venv
$ source venv/bin/activate
$ pip install django

이 문서는 ubuntu 16.04를 기반으로 작성되었다. 윈도우나 다른 환경에서 이 문서를 따라하고 있고, 위 과정을 혼자 해결할 능력이 아직 없다면, Django Girls의 튜토리얼을 따라하시기 바란다.

django 설치가 끝났으니, 프로젝트를 만든다. (맨 뒤에 점을 꼭 찍어주기 바란다. 점을 찍지 않으면 하위 폴더가 또 생성된다.) 그 다음, blogengine이라는 앱을 추가한다.

$ django-admin startproject django_gfm_prj .
$ python manage.py startapp blogengine

settings.pyblogengine이라는 앱을 만들었다고 알려준다.

# django_gfm_prj/settings.py
#...
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'blogengine',	# <--- add this line
]
# ...

블로그 Post 클래스 만들기

이제 블로그 포스트 클래스를 만들자. 실제 블로그를 만드려고 하는 것은 아니니까 titletext만 있는 간단한 클래스를 예로 들겠다.

# blogengine/models.py
from django.db import models


class Post(models.Model):
    title = models.CharField(max_length=256)
    text = models.TextField()

더 그럴 싸하게 만들 수 있겠지만, 일단은 github api를 사용하는데에 목표가 있으니까, 첫 화면에서 블로그 포스트의 제목과 내용이 디스플레이 되도록 해보자. 먼저 사용자가 127.0.0.1로 들어왔을 떄, blogengine 앱 쪽에서 저리할 수 있도록 아래와 같이 수정한다.

# django_gfm_prj/urls.py
from django.conf.urls import url, include
from django.contrib import admin

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'', include('blogengine.urls')),
]

하지만, 우리는 아직 blogengine쪽에 urls.py를 만들어 놓지 않았다. 파일을 만들고 아래와 같이 작성한다.

# blogengine/urls.py
from django.conf.urls import url, include
from .views import post_list

urlpatterns = [
    url(r'^$', post_list, name='post_list'),
]

같은 폴더의 views.py파일에서 post_list를 import 해야하는데, post_list도 아직 안 만들었다. 해당 파일을 열고, 아래와 같이 수정한다.

# blogengine/views.py
from django.shortcuts import render


def post_list(request):
    return render(
        request,
        'blogengine/post_list.html',
        {
            'title': 'gfm test',
        }
    )

현재 상태에서 127.0.0.1:8000으로 접속하면 우리가 의도한 대로, post_list 쪽으로 오는지 확인해볼 차례다. 너무 오랫동안 테스트를 안하고 지나가면 불안하니까. 아까 blogenginepost모델을 만들어 놓고 마이그레이션을 한번도 안했다. 터미널에서 아래와 같이 마이그레이션을 하자.

$ python manage.py makemigrations
$ python manage.py migrate

그리고 runserver를 하면

$ python manage.py runserver

아래와 같이 성공적(?)으로 Template이 없다는 오류 화면이 뜬다. Template Does Not Exists

template을 만들자. blogengine폴더 아래에 templates 폴더를 만들고 그 밑에 또 blogengine이라는 폴더를 만든다. 그리고 그 아래에 post_list.html파일을 만든다. 그 파일을 아래와 같이 작성한다.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{{ title }}</title>
</head>
<body>

</body>
</html>

다시 브라우저를 열어보면!!! Template Does Not Exists 음 여전히 안된다. 여전히 template 파일이 없다고 한다. settings.py를 수정하는 것을 깜빡 했다. settings.py파일을 수정해서, template파일들이 각 앱의 templates폴더에 있음을 알려준다.

# django_gfm_prj/settings.py
#...
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': ['templates'],	# <-- change this line
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]
#...

이제 다시 해보면, 잘 된다. Empty screen

Admin페이지에서 post 작성하기

admin페이지에 들어가려면 superuser를 만들어야 한다. Ctrl+c로 돌아가고 있던 서버를 죽이고, 아래와 같이 입력한다.

$ python manage.py createsuperuser
Username (leave blank to use 'saintdragon2'): supertester
Email address: super@tester.com
Password: 
Password (again): 
Superuser created successfully.

password는 입력을 해도 화면상 아무런 변화도 없는데, 잘 되고 있는 것이니 놀라지 말고 입력하자. 필자는 supersuper를 비밀번호로 입력했다.

다시 터미널에서 python manage.py runserver를 입력하고, 브라우저에서 127.0.0.1:8000/admin/에 가서 로그인을 해보자. Empty screen 잘 되긴 했으나, 블로그가 없다. 등록하자. blogengine/admin.py를 아래와 같이 수정하면 된다.

# blogengine/admin.py
from django.contrib import admin
from .models import Post


admin.site.register(Post)

post 를릭클릭하고 들어가서 post를 작성하자. new post

저장 버튼을 클릭하면, 역시나 잘 된다. 그런데, 저장되고 나서 나오는 post list 화면에서 모든 객체가 똑같이 Post object라고 나올 모양이다. Post 모델에 __str__을 추가 해야겠다.
post list in admin

# blogengine/models.py
from django.db import models


class Post(models.Model):
    title = models.CharField(max_length=256)
    text = models.TextField()

    def __str__(self):			#<--- added this line
        return self.title		#<-- and this line

잘 된다! post list with str

첫 화면에서 포스트 보기

post_list에서 모든 post를 넘겨줘야 template파일에서 post를 화면에 뿌려줄 수 있다. 아래와 같이 blogengine/views.py를 수정한다.

# blogengine/views.py
from django.shortcuts import render
from .models import Post


def post_list(request):
    posts = Post.objects.all()			# <-- added this line
    return render(
        request,
        'blogengine/post_list.html',
        {
            'title': 'gfm test',
            'posts': posts,				# <-- and this line
        }
    )

이제 template파일은 post_list.html을 수정한다.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{{ title }}</title>
</head>
<body>
{% for post in posts.iterator %}		<!-- added from this line -->
    <h1>{{ post.title }}</h1>
    {{ post.text }}
{% endfor %}							<!-- to this line -->
</body>
</html>

잘 된다. 하지만, markdown이 적용되지는 않았다.... post list on index page

Github API 이용하기

이걸 설명하기 위해 이 먼길을 왔다. 추가하는 방법은 매우 간단하다. markdown으로 작성한 text를 github api에 날리면, 거기서 html로 변환된 결과를 돌려준다. 그럼 그걸 우리 화면에 뿌려주면 되는거다.

일단 requests를 설치하고,

$ pip install requests

models.py에 아래 함수를 추가한다.

# blogengine/models.py
from django.db import models
import requests


def md_to_gfm(text):
    headers = {'Content-Type': 'text/plain'}
    data = text.encode('utf-8')
    r = requests.post('https://api.github.com/markdown/raw', headers=headers, data=data)

    return r.text.encode('utf-8')


class Post(models.Model):
    title = models.CharField(max_length=256)
    text = models.TextField()

    def __str__(self):
        return self.title

    def gfm(self):
        return md_to_gfm(self.text)

그리고 마지막으로 template파일을 아래와 같이 수정하면 된다.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{{ title }}</title>
</head>
<body>
{% for post in posts.iterator %}
    <h1>{{ post.title }}</h1>
    {{ post.gfm | safe }}		<!-- changed this line -->
{% endfor %}
</body>
</html>

잘 되는 것 같다. gfm index 조금 더 예쁘게 하고 싶어서 https://bootswatch.com/에서 css를 하나 골랐다. 그리고 아래와 같이 적용.

...
<link rel="stylesheet" href="https://bootswatch.com/cosmo/bootstrap.min.css">
...

짜!잔! with_css

정리하며...

맨 마지막 파트만 설명하면 되었을 것을 너무 많은 내용을 불필요하게 담은 것이 아닐까 싶다. 더 구체적인 활용방법은 여러분의 응용력에 맡기기로 하고 이쯤에서 정리한다.

요약

  • django model에 textfield로 markdown 문서 저장
  • github api에 보내고 결과를 받음
  • template에서 그 결과를 받아 화면에 출력

소스코드는 github에 있다. https://github.com/saintdragon2/django-gfm


아직 댓글이 없습니다. 첫번째로 댓글을 남겨보세요.


Blog Search

Blog Categories

Side Widget Well

Lorem ipsum dolor sit amet, consectetur adipisicing elit. Inventore, perspiciatis adipisci accusamus laudantium odit aliquam repellat tempore quos aspernatur vero.