이 블로그 검색

2012년 8월 7일 화요일

Django 게시판 만들기 1 - 설치 및 설정


Python 을 이용한 웹 개발 프레임워크인 django를 이용해서, 간단한 게시판을 만들어 보자. 앞서 스프링을 이용해서 작성된 게시판 과 동일한 기능을 수행하는 간단한 게시판이다. 먼저 django 프레임워크를 설치해야 한다.

1. django 프레임워크 설치

* python 설치 : 일단 python이 설치가 되어 있어야 한다. django를 사용하기 위해서는 버전 2.5 부터 2.7까지 가능하다. 현재 3.0 버전은 지원안하고 있다.

* Django 설치
직접 다운로드 받아 압축을 풀어서 셋업을 수행해도 되고, Svc, Git 등을 이용해서 최신소스를 받아오는 방법도 있다.  나는 git을 사용하므로 다음처럼 적당한 위치의 폴더에서 clone을 생성하여 소스를 받았다.

git clone https://github.com/django/django.git

그다음, 받은 폴더에서 다음을 수행한다.

python setup.py install

만약 'ImportError: No module named setuptools' 에러를 만나면 다음을 실행한다.
sudo curl https://bootstrap.pypa.io/ez_setup.py -o - | sudo python

파이선에서 다음을 실행해서  django 가 제대로 설치되었는지 확인한다.
>>> import django
>>> print django.get_version()
1.5

버전을 출력하면 설치는 제대로 된것이다.

* 윈도우 환경에서는 환경변수 PATH 설정:
django가 설치되면 python 설치 폴더의 \Lib\site-packages 안에 django가 복사된다. 이 경로를 PATH에 추가한다.  python 설치 폴더\Lib\site-packages\django\bin; 를 추가. python을 처음 설치했다면, python bin 경로도 PATH에 추가하는것을 잊지말자.

2. 프로젝트 생성 및 설정

이제 환경 설정이 완료되었으니, 실제 게시판을 생성해보자. 프로젝트 생성을 원하는 위치에서 다음 명령을 수행한다.

django-admin.py startproject dj_board

django게시판이란 의미로 프로젝트명을 dj_board로 하였다. 다음처럼 디렉토리가 구성된다.

dj_board/
      manage.py
      dj_board/
          __init__.py
          settings.py
          urls.py
          wsgi.py

본격적인 개발에 앞서 간단한 테스트를 해보자, 생성한 프로젝트 폴더에서 django에 내장된 개발용 웹 서버를 실행한다.



D:\kojh_data\Dev\PythonDev\dj_board>python manage.py runserver
Validating models...

  0 errors found
  Django version 1.5, using settings 'dj_board.settings'
  Development server is running at http://127.0.0.1:8000/
  Quit the server with CTRL-BREAK.

그리고 웹 브라우져로 http://127.0.0.1:8000/ 에서 웹페이지를 확인해본다. 정상적으로 프로젝트가 만들어졌다면 동작중이라는 메시지가 보일것이다. 참고로, python manage.py runserver 8080 처럼 실행해서 포트를 지정할수 있다.

* Eclipse 이용한 개발 :
명령을 직접 실행하지 않고, eclipse IDE를 이용해서 django개발도 가능한데, marketplace 에서 PyDev 를 설치하고, new -> other -> PyDev Django Project 를 수행하면 된다.

이제 이 프로젝트를 위한 설정이 필요하다. 이 게시판은 오라클을 이용해서 데이터를 관리할것이다 (Oracle XE 11g). 이 예제처럼 오라클을 사용한다면 다음 사이트에서 관련 모듈을 다운로드 받아 설치한다.

http://cx-oracle.sourceforge.net/

그다음, 설정을 위해서 dj_board/settings.py 파일을 열어서 편집한다. 시간대도 한국으로 설정해주자.

TIME_ZONE = 'Asia/Seoul ROK'
TIME_ZONE = 'Asia/Seoul'

오라클 사용을 위해서 데이터베이스 설정을 해줘야한다.

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.oracle', 
        'NAME': '',                        # Or path to database file if using sqlite3.
        'USER': 'study',                 # Not used with sqlite3.
        'PASSWORD': 'study',     # Not used with sqlite3.
        'HOST': '',                          # Set to empty string for localhost. Not used with sqlite3.
        'PORT': '1521',                 # Set to empty string for default. Not used with sqlite3.
    }
}

각각의 항목에 대한 설정은 다음을 참고한다.

ENGINE : 다음중 하나를 설정.
    'django.db.backends.postgresql_psycopg2'
    'django.db.backends.mysql'
    'django.db.backends.sqlite3'
    'django.db.backends.oracle'

NAME : SQLite 를 사용하는 경우 파일명을 지정한다.
USER, PASSWORD : oracle등을 사용시 지정한다. sqlite 사용시에는 불필요.
HOST : 데이터베이스 서버가 동일한 머신인 경우 생략 가능하다.sqlite 사용시에는 불필요.

* INSTALLED_APPS
dj_board/settings.py 파일에 INSTALLED_APPS 을 보면 django 설치시에 기본적으로 같이 설치된 프로그램이 표시된다. 프로젝트를 처음 생성한 이후 이므로, 다음처럼 보일 것이다.

INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    # Uncomment the next line to enable the admin:
    # 'django.contrib.admin',
    # Uncomment the next line to enable admin documentation:
    # 'django.contrib.admindocs',
)

만약, 최초로 django를 사용하는 경우에는, django가 기본적으로 설치하는 이 어플리케이션들이 사용되어지기 위해서, 먼저 데이터베이스 테이블을 사용할수 있게끔 설정이 필요하다. 다음을 실행한다.

D:\kojh_data\Dev\eclipse_workspace\dj_board>python manage.py syncdb
  Creating tables ...
  Creating table auth_permission
  Creating table auth_group_permissions
  Creating table auth_group
  Creating table auth_user_user_permissions
  Creating table auth_user_groups
  Creating table auth_user
  Creating table django_content_type
  Creating table django_session
  Creating table django_site

  You just installed Django's auth system, which means you don't have any superusers defined.
  Would you like to create one now? (yes/no): yes
  Username (leave blank to use u's33416'): 슈퍼유저 아이디 설정한다.
  E-mail address: 슈퍼유저 메일 설정한다.
  Password:
  Password (again):
  Superuser created successfully.
  Installing custom SQL ...
  Installing indexes ...
  Installed 0 object(s) from 0 fixture(s)

최초로 django를 사용하는 경우, Django's auth system을 위한 슈퍼유저에 대한 설정을 위 에서처럼 해준다. Oracle에서 해당 테이블이 생성되었음을 확인할수 있을것이다. 이 작업은 최초 django를 사용시에 한번만 수행해서 관련 테이블을 생성해주면 된다.

3. django application 생성

django 에는 프로젝트와 어플리케이션이란 개념이 있다. 어플리케이션이란 어떤일을 수행하는 파이썬 패키지를 나타낸다. 프로젝트는 이 모든 설정들과 어플리케이션들의 집합이라고 할수있다. 하나의 프로젝트가 복수의 어플리케이션들을 포함할수 있으며, 하나의 어플리케이션은 복수 프로젝트에 존재할수 있다. 사이트에 원하는 기능을 추가하기 위해 어플리케이션을 작성하는 것이다.  지금까지 위에서 수행한 작업들은 프로젝트 설정을 위한 사전 작업들이었고, 이제 실질적인 게시판 기능을 위한 어플리케이션을 생성할 차례이다.

* 어플리케이션의 이름을 sample_board 라고 하고, 생성한다.

  python manage.py startapp sample_board

그 결과 다음과 같은 구조로 파일들이 생성된다.

dj_board/
      manage.py
      dj_board/
            __init__.py
            settings.py
            urls.py
            wsgi.py
      sample_board/
            __init__.py
            models.py
            tests.py
            views.py

Eclipse 사용한다면 프로젝트 오른쪽 버튼 -> Django-> Create Application을 실행한다.

django 는 잘 알려진 MVC 모델을 이용해서 웹 사이트를 구현해주는데, 모델에 해당하는것이 models.py, 컨트롤러는 약간 헷갈리지만 views.py 가 담당한다. 그리고 화면에 보여지는 부분은 template 이라고 하는 html 에 내포되는 django 만의 고유한 표기법으로 동적 컨텐츠를 나타내고 있다. jsp 와 비교시에 단순한것이 장점이기도 하고 동시에 단점일 수도 있다. 뷰 영역은 말그대로 컨트롤러로부터 받은 내용을 화면에 출력만 하는 용도로 사용하라는 철학이 강하다(jsp 에서서 자바 코드를 마음대로 사용할수 있는것에 비하면, django 의 template기술은 python 언어를 뷰 영역에서 사용할수 없기 때문에 지원이 빈약하다고도 할수 있다. 하지만 내 취향에는 난잡한 jstl, jsp 대신 단순한 template를 사용하는것이 더 나은것 같다).

* Model을 설정한다.

데이터베이스를 사용하는 웹 app 을 만들기 위한 가장 첫번째 단계는 모델을 정의하는것이다. models.py을 열어보면 다음과 같을 것이다.

from django.db import models

# Create your models here.

여기에 게시판 어플리케이션을 구현하기 위해 모델을 추가한다. 간단한 예제를 목적으로 1개의 모델(테이블)만을 사용하도록 하였다. models.py을 다음처럼 수정한다.

from django.db import models

class DjangoBoard(models.Model):
      subject = models.CharField(max_length=50, blank=True)
      name = models.CharField(max_length=50, blank=True)
      created_date = models.DateField(null=True, blank=True)
      mail = models.CharField(max_length=50, blank=True)
      memo = models.CharField(max_length=200, blank=True)
      hits = models.IntegerField(null=True, blank=True)

django는 ORM(Object Relational Mapping) 기능을 자체적으로 제공하고 있다. 모델을 이용해서 실제 데이터 베이스 테이블과 맵핑되서 동작되고, SQL을 직접 작성하지 않고도 데이터의 조작을 가능하게 해준다. 물론 직접 SQL을 설정하여(Raw SQL) 데이터를 조작할수도 있다. 이 게시판에서는 게시물을 페이지별로 가져오는 부분은 Raw SQL 을 이용할것이고, 삭제,변경 처리시에는 django model을 이용할것이다.

python manage.py sqlall sample_board 를 수행해서, 모델이 실제 테이블로 어떻게 생성이 되는지 확인해본다.

D:\kojh_data\Dev\eclipse_workspace\dj_board>python manage.py sqlall sample_board

    CREATE TABLE "SAMPLE_BOARD_DJANGOBOARD" (
        "ID" NUMBER(11) NOT NULL PRIMARY KEY,
        "SUBJECT" NVARCHAR2(50),
        "NAME" NVARCHAR2(50),
        "CREATED_DATE" DATE,
        "MAIL" NVARCHAR2(50),
        "MEMO" NVARCHAR2(200),
        "HITS" NUMBER(11)
    )
    ;

    DECLARE
        i INTEGER;
    BEGIN
        SELECT COUNT(*) INTO i FROM USER_CATALOG
            WHERE TABLE_NAME = 'SAMPLE_BOARD_DJANGOBOARD_SQ' AND TABLE_TYPE = 'SEQUENCE';
        IF i = 0 THEN
            EXECUTE IMMEDIATE 'CREATE SEQUENCE "SAMPLE_BOARD_DJANGOBOARD_SQ"';
        END IF;
    END;
    /

    CREATE OR REPLACE TRIGGER "SAMPLE_BOARD_DJANGOBOARD_TR"
    BEFORE INSERT ON "SAMPLE_BOARD_DJANGOBOARD"
    FOR EACH ROW
    WHEN (new."ID" IS NULL)
        BEGIN
            SELECT "SAMPLE_BOARD_DJANGOBOARD_SQ".nextval
            INTO :new."ID" FROM dual;
        END;
    /

    COMMIT;

django ORM이 실제 생성하는 테이블 구조를 확인할수 있다.

그럼, python manage.py syncdb 를 수행해서 테이블을 실제로 생성한다.

    D:\kojh_data\Dev\eclipse_workspace\dj_board>python manage.py syncdb
    Creating tables ...
    Creating table sample_board_djangoboard
    Installing custom SQL ...
    Installing indexes ...
    Installed 0 object(s) from 0 fixture(s)

    D:\kojh_data\Dev\eclipse_workspace\dj_board>

자 이제 필요한 테이블이 생성이 되었다. 그다음 할일은 새로 추가한 어플리케이션 - 즉 sample_board - 을 dj_board/setting.py 에 추가하는 것이다. 새로운 어플리케이션을 작성할때마다 이 내용을 dj_board/setting.py 에 추가 해야한다.

    INSTALLED_APPS = (
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.sites',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        # Uncomment the next line to enable the admin:
        # 'django.contrib.admin',
        # Uncomment the next line to enable admin documentation:
        # 'django.contrib.admindocs',
        'sample_board' # 우리가 추가한 어플리케이션이다.
    )

4. url 설정

우리가 웹 브라우져로 http://127.0.0.1:8000/ 로 접근시 제일 처음 보여져야 될 화면은 전체 게시물 목록이 될것이다. 이를 수행하기 위해서는 url과 컨트롤러의 맵핑이 필요한데, dj_board/urls.py 를 편집해서 웹페이지에 접근할 url 설정을 한다.

  from django.conf.urls import patterns, include, url
  from sample_board import views # 우리가 만든 컨트롤러를 import한다.

  urlpatterns = patterns('',
      # Examples:
      # url(r'^$', 'dj_board.views.home', name='home'),
      # url(r'^dj_board/', include('dj_board.foo.urls')),

      # Uncomment the admin/doc line below to enable admin documentation:
      # url(r'^admin/doc/', include('django.contrib.admindocs.urls')),

      # Uncomment the next line to enable the admin:
      # url(r'^admin/', include(admin.site.urls)),
      url(r'/$', views.home), # 여기에서 home 컨트롤러를 호출하게 맵핑해준다.
  )

5. 컨트롤러 작성

이제 url에 맵핑된 컨트롤러를 작성하기 위해서 /sample_board/view.py 파일을 수정해야 한다. dj_board/urls.py 에서 최초 화면을 위한 url에 대해 home 컨트롤러를 맵핑하였으므로, 이를 작성한다.

  # -*- coding: utf-8 -*-
  # Create your views here.
  from django.shortcuts import render_to_response
  from django.utils import timezone
  from sample_board.models import DjangoBoard
  from django.views.decorators.csrf import csrf_exempt
  from django.http import HttpResponseRedirect
from sample_board.pagingHelper import pagingHelper

  # 한글!!
  rowsPerPage = 2

  def home(request):       # model을 이용해서 별도 SQL 작성 없이 id 필드의 역순으로 (-id) 데이터를 2개만 조회해온다.
      boardList = DjangoBoard.objects.order_by('-id')[0:2]
      current_page =1

      # model 을 사용해서 전체 데이터 갯수를 구한다.
      totalCnt = DjangoBoard.objects.all().count()

      # 이것은 페이징 처리를 위해 생성한 간단한 헬퍼 클래스이다. 별로 중요하지 않으므로 소스를 참조하기 바란다.
      pagingHelperIns = pagingHelper();
      totalPageList = pagingHelperIns.getTotalPageList( totalCnt, rowsPerPage)
      print 'totalPageList', totalPageList

      # 템플릿으로 필요한 정보들을 넘기는 부분이다. 이를 통해서 정적인 템플릿에 동적인 데이터가 결합되게 되는 것이다.
      # 우리는 게시판 최초 화면 처리를 위해서 listSpecificPage.html 템플릿을 호출했다.
      # 그리고 필요한 정보들을 dictionary 로 전달했다.
      return render_to_response('listSpecificPage.html', {'boardList': boardList, 'totalCnt': totalCnt,
                                                          'current_page':current_page ,'totalPageList':totalPageList} )

한글 처리를 위해서는 제일 첫라인에 # -*- coding: utf-8 -*- 를 추가해 줘야 한다. django 의 모델은 어렵지는 않지만 별도로 공부해야할 부분이긴 하다. 여기서는 간단한 예제를 작성하는것이 주 목적이므로 상세한 내용은 공식 문서를 참조하라. ^^

pagingHelper.py소스 

class pagingHelper:
    "paging helper class"
    def getTotalPageList(self, total_cnt, rowsPerPage):               
        if ((total_cnt % rowsPerPage) == 0):
            self.total_pages = total_cnt / rowsPerPage;
            print 'getTotalPage #1'
        else:
            self.total_pages = (total_cnt / rowsPerPage) + 1;
            print 'getTotalPage #2'
               
        self.totalPageList = []
        for j in range( self.total_pages ):
            self.totalPageList.append(j+1)
                
        return self.totalPageList        

    def __init__(self ):
        self.total_pages = 0
        self.totalPageList  = 0

6. template 설정 및 생성

django에서 view에 해당하는것이 template 이다. 이를 사용하기 위해서는 template의 위치를 먼저 설정해줘야 한다. 다시 dj_board/settings.py 파일을 열어서 TEMPLATE_DIRS 부분을 편집한다.

  TEMPLATE_DIRS = (
    # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
    # Always use forward slashes, even on Windows.
    # Don't forget to use absolute paths, not relative paths.
    'D:/kojh_data/Dev/eclipse_workspace/dj_board/templates',
    # 절대 경로를 이용해서 template 파일들이 저장되는 위치를 지정했다.
    # 다른 설정 방법도 물론 존재하는데 그 부분은 역시 공식 문서를 참고하기 바란다.
  )

우리가 설정해준 dj_board/templates 폴더를 직접 생성해줘야 한다. 생성 후 이 폴더 안에다가 뷰로 사용할 html 파일들을 생성하면 된다. 이제 폴더 구조는 다음과 같을 것이다.

dj_board/
      manage.py
      dj_board/
            __init__.py
            settings.py
            urls.py
            wsgi.py
      sample_board/
            __init__.py
            models.py
            tests.py
            views.py
      templates
 
앞서 home 컨트롤러에서 render_to_response 호출을 통해서 listSpecificPage.html 템플릿을 호출하고 있다. 이 파일을 폴더내에 생성하고 다음처럼 내용을 채운다.

  listSpecificPage.html

  {% comment %}
  totalCnt: <strong>{{ totalCnt }}</strong> current_page: <strong>{{ current_page }}</strong> boardList: <strong>{{ boardList|length }}</strong>
  {% endcomment %}

  <table cellspacing=1 width=700 border=0>
      <tr>
          <td>총 게시물수: {{ totalCnt }}</td>
          <td><p align=right> 페이지:{{ current_page }}
          </td>
      </tr>
  </table>

  <table cellspacing=1 width=700 border=1>
      <tr>
          <td width=50><p align=center>번호</p></td>
          <td width=100><p align=center>이름</p></td>
          <td width=320><p align=center>제목</p></td>
          <td width=100><p align=center>등록일</p></td>
          <td width=100><p align=center>조회수</p></td>
      </tr>

      {% if boardList %}
          <ul>
              {% for boardRow in boardList %}
              <tr>
              <td width=50><p align=center>{{ boardRow.id }}</p></td>
              <td width=100><p align=center>{{ boardRow.name }}</p></td>
              <td width=320>
                  <p align=center>
                      <a href="/viewWork?memo_id={{ boardRow.id }}&current_page={{ current_page }}&searchStr=None" title="{{ boardRow.memo}}">{{ boardRow.subject }}
                  </p>
              </td>
              <td width=100><p align=center>{{ boardRow.created_date }}</p></td>
              <td width=100><p align=center>{{ boardRow.hits }}</p></td>
              </tr>
              {% endfor %}
          </ul>
      {% else %}
          <p>No Data.</p>
      {% endif %}
  </table>

  <table cellspacing=1 width=700 border=1 >
      <tr> <td>
          {% for page in totalPageList %}
              <a href="/listSpecificPageWork?current_page={{page}}" >
              [
              {% ifequal page current_page %}
                  <b>
              {% endifequal %}
              {{page}}

              {% ifequal page current_page  %}
                  </b>
              {% endifequal %}
              ]
          {% endfor %}
          </td> </tr>
  </table>

  <table width=700>
      <tr>
          <td><input type=button value="글쓰기"  OnClick="window.location='/show_write_form'">    </td>
          <td><form name=searchf method=post action="/searchWithSubject/">
              <p align=right><input type=text name=searchStr size=50  maxlength=50>
              <input type=submit value="글찾기"></p>
          </td>
      </tr>
  </table>

중요한것은 동적인 정보를 어떻게 사용하고 있는지 하는 부분이다. 마치 jstl 에서의 ${...} 처럼 {{...}} 의 형태로 컨트롤러가 넘겨준 정보를 참조할수 있다. 그리고 for , if 구문을 어떻게 처리하는지 살펴보길 바란다. 이것은 django의 template에 대한 기술사항인데, 문서를 보면 상세히 알수 있는 내용이고, 여기서는 자세한 설명은 생략한다.

한가지 중요한 사실은 templates 폴더의 html 파일에서 한글을 사용하려면,
파일 형식을 utf-8 로 설정해야 하는것이다. 편집기나 Eclipse에서 파일 속성이 반드시 utf-8 이어야 한다.

여기까지 코딩을 했다면, 테스트 서버를 기동후, http://127.0.0.1:8000/ 을 웹브라우져로 열어보면 다음과 같은 화면을 볼수 있을 것이다.

D:\kojh_data\Dev\PythonDev\dj_board>python manage.py runserver


다음 시간에 계속해서 게시판 기능을 구현해 보기로 한다.


이 예제의 전체 소스는 아래에서 받을 수 있다.
https://github.com/jeremyko/dj_board

이어지는 글 :

Django 게시판 만들기 2 - 글쓰기 기능 구현l


댓글 9개:

  1. 잘 읽었습니다! 혹시 이 파일에서의 슈퍼유저아이디와 비밀번호를 알 수 있을까요?
    실행을 해보려고 하는데 필요해서요ㅠ 답변부탁드려요

    답글삭제
  2. 안녕하세요, 아이디, 비번은 사용자 임의로 입력하시고 잘 기억하시면 됩니다. 그리고 몇년전의 글이라서 지금과는 다른점이 있을수 있으니 양해바랍니다 ^^.

    답글삭제
  3. 우와 잘읽고있습니다~감사합니다!

    답글삭제
  4. 잘 읽었습니다 감사합니다!
    주인장님 글 참고하면서 블로그에 글을 작성하였는데,
    문제가 되면 삭제하겠습니다!
    https://recall2300.github.io/django/2016/03/18/01.html

    답글삭제
  5. 따라해가면서 잘 배우고 있습니다!. global name 'pagingHelper' is not defined 이란 에러가 뜨는데 해결할 방법이 있을까요?!

    답글삭제
    답글
    1. views.py 에 from pagingHelper import pagingHelper를 추가해주세요.
      주인장님이 빠뜨리신 것 같습니다.

      삭제
    2. Exception Value:
      No module named pagingHelper

      라는 에러가 발생합니다.
      혹시 pagingHelper.py 를 따로 파일 생성을 해줘야 하는걸까요????

      삭제
  6. 안녕하세요

    작성하신 샘플을 참고로 공부 잘 하고 있습니다.

    공개해주신 샘플을 SQLite로 작성하고 완성했습니다.

    github에 공개해도 될지 문의드립니다.

    감사합니다.

    drawhan@naver.com

    답글삭제