2010년 12월 7일 화요일

[Link] Javascript Closure

google검색 중 한국어 검색 결과에서 얻은 링크들

  • http://decoder.tistory.com/689
  • http://asrada2001.tistory.com/201
  • http://azraid.springnote.com/pages/5676725
  • http://blog.outsider.ne.kr/506
  • http://olpost.com/v/234581

2010년 12월 2일 목요일

RSS

RSS는 버전도 다양하고 RSS를 지칭하는 말들도 다양하게 존재했다.

RSS 0.9는 RDF Site Summary의 약자로 Netscape에서 my.netscape.com 에서 사용하기 위해 디자인되었으며 기존의 scriptingNews format 을 지원하고 RDF를 통해 header를 구성하고 나머지는 xml 로 되어 있다. (1999년 3월)

RSS 0.91은 Rich Site Summary의 약자로 Netscape의 Dan Libby에 의해 스펙이 만들어졌으며 RDF header를 제외하고 scriptingNews 2.0b1 의 대다수 기능을 포함하고 있다. (1999년 7월)

RSS 1.0은 오라일리의 Rael Dornfest가 이끄는 RSS-DEV 에서 발표하였다. RDF와 namespace 기반으로 되어 있으며 앞선 버전들의 format을 모듈로 받아 들였다. (2000년 8월)

RSS 0.92는 0.91 버전에 선택 요소들을 포함하고 있다. (2000년 12월)

RSS 2.0은 Really Simple Syndication의 약자로 0.92버전에 선택 요소들을 포함하고 있으며 0.92버전과의 하위호환성을 위해 namespace 를 2.0 내의 요소들에게는 적용되지 않지만 RSS 2.0 feed 들에게 적용되게 하였다.

참고
- http://en.wikipedia.org/wiki/RSS
- http://cyber.law.harvard.edu/rss/rssVersionHistory.html

RSS 구조에 대해 알아보기 위해 위키피디아의 문서를 열심히 번역하던 중...
키보드를 잘못 건드려 모두 날라가는 불상사를 겪었다...
이런 젝일...
다소 잘못된 번역이 있을테니 지적해 주시기 바란다.


그럼 이중에서 RSS 2.0 규격에 대해 알아보자.
이 문서에서 다루는 내용은 Harvar의 Berkman Center에서 제공하는 RSS 2.0 Specification을 기준으로 한다.

선언

RSS는 xml 문서이므로 xml 문서선언이 이뤄져야 하고 <rss>를 root element로 갖는다.

<?xml version="1.0"?>
<rss version="2.0">



본문 구조
본문은 다음 두개의 Section으로 이뤄져 있다.

<channel>
<item>

<channel> section은 현재 RSS를 제공하는 사이트(?)에 대한 정보를 담고 있으며
<item> 은 RSS로 제공되는 개별 내용이다.
마치 신문에서 <channel>은 신문사명, 제호 등 발간되는 신문에 대한 정보에 해당하고
<item>은 개별 기사에 해당한다.
<channel>은 optional 요소라고 하나 실질적으로 필수 요소의 역할을 한다.
개별 section의 하위 요소들에 대해 알아보자.
 


<channel> section
  • 필수 하위 요소
    
Element 명 설명 Example
title

현재 채널의 이름.

Website에서 제공되는 정보는 동일 RSS에서 제공하는 정보와 동일해야 한다.

Website의 사이트명과 동일해야 한다.

yoonani's news
link 해당 Website 의 url http://www.yoonani.pe.kr
description 채널에 대한 설명 yoonani가 제공하는 정보입니다.

  • 선택 가능 하위 요소
    
Element 명 설명 Example
language

W3C에서 정의한 채널에서 사용하는 언어 코드

(두자리 언어코드-두자리 국가코드)

en-us

ko-kr

copyright 채널의 저작권 http://creativecommons.org/licenses/by-nc-sa/2.0/
manageEditor 채널 Editor의 email yoonani@gmail.com
webMaster Webmaster email yoonani@gmail.com
pubDate 정기적인 배포 날짜로 신문사의 경우 24시간 주기등을 사용한다.기본적으로 RFC 822에 의한 시간 포맷을 사용하나 년도에 대해서는 두자리, 네자리 모두 가능 Sat, 07 Sep 2002 00:00:01 GMT+9
lastBuildDate 채널의 내용이 마지막으로 바뀐 날짜
category 채널에서 제공하는 카테고리 정보로 <item>이 사용하는 방법과 동일하다. <category>Info</category>
generator RSS를 제공하는 프로그램 명 MightyInHouse Content System v2.3
docs RSS file에서 사용하는 포맷에 대한 문서를 가리키는 URL http://blogs.law.harvard.edu/tech/rss
cloud 채널에 대한 업데이트를 공지하는 cloud에 등록하는 과정을 허가한다. <cloud domain="rpc.sys.com" port="80" path="/RPC2" registerProcedure="pingMe" protocol="soap"/>
ttl 원본 내용을 다시 읽어오기 전 캐쉬되는 시간을 분으로 나타낸다. <ttl>60</ttl>
image 채널에 표시될 GIF, JPEG, PNG 이미지를 지정한다. 예제의 하위 요소를 갖는다.

<url>

<title>

<link>

rating 채널의 PICS 등급을 나타낸다.
textInput 채널에 표시될 text 입력상자를 지정하는 것으로 예의 sub element를 통해 내용을 지정한다. <title>
<description>
<name>
<link>
skipHours RSS 정보를 읽어가는 측에 몇시간에 한번씩 정보를 확인하는지 알려준다.
skipDays RSS 정보를 읽어가는 측에 며칠에 한번씩 정보를 확인하는지 알려준다.


<item> 요소
Element 설명 Example
title 해당 아이템의 제목 Venice Film Festival Tries to Quit Sinking
link 해당 아이템의 URL http://nytimes.com/2004/12/07FEST.html
description 아이템의 간략 내용 Some of the most heated chatter at the Venice Film Festival this week was about the way that the arrival of the stars at the Palazzo del Cinema was being staged.
author 저자의 이메일 oprah@oxygen.net
category 해당 아이템의 카테고리
comments 해당 아이템의 댓글을 볼 수 있는 주소 http://www.myblog.org/cgi-local/mt/mt-comments.cgi?entry_id=290
enclosure 첨부한 미디어에 대한 설명
guid 아이템의 고유한 아이디 http://inessential.com/2002/09/01.php#a2
pubDate 해당 아이템의 작성 날짜 Sun, 19 May 2002 15:21:36 GMT
source 아이템이 들어있는 RSS 채널



간략히 RSS의 구조에 대해 알아보았으니...

다음엔 RSS Feeder를 만들고 이 Feeder로 부터 정보를 받아오는 Reader를 작성해 보자

2010년 11월 29일 월요일

Rockmelt(락멜트) 사용기

어제 rockmelt에 등록을 하고 나니 어제 새벽에 사용가능 메일이 왔다.
아직은 처음이라 본격적으로 다운로드를 지원하지 않고 사용신청 후 메일을 통한 승인이 이뤄질거 같은데...
잠깐 써본 바로는...
정말 SNS에 특화된 녀석이다...
아니 SNS 자체보가 커뮤니케이션을 위한 브라우져라고 해야 할까?

첫 모습을 한번 담아 보았다.

처음 RockMelt를 실행시키면 facebook과의 연결을 시도한다.


여기서 facebook 으로 로그인을 하면 다음과 같은 정보를 RockMelt에 보내고 이를 기반으로 서비스가 이뤄지는 것 같다.




기존 브라우저의 데이터를 가져올 것인지 확인하고




잠시 기다리면 RockMelt의 첫모습을 볼 수 있다.


화면을 보면 좌측에 facebook의 친구 리스트가 나오고 이중에서 RockMelt에 로그인되어 있는 친구는 초록색 버튼으로 표기되어 간단한 메세지를 주고 받을 수 있다.





마침 작성 중에 후배가 말을 걸어와 채팅화면을 보여줄 수 있어 다행이다.





우측은 기본적으로 facebook이 업데이트 상황과 twitter의 상태를 알아 볼 수 있는 탭이 위치하고 있다.




트위터의 경우 처음 접속시 로그인정보를 넘겨주어야 한다.



로그인 정보를 전달하고 나면 확인하지 않은 트윗이 몇개 있는지 보여주고 트위터 위젯 위로 마우스를 올리면 타임라인을 출력해 준다.




그리고 다른 블로그나 SNS 서비스 사이트를 등록할수 있는 위젯을 제공한다.



다른 서비스 추가 기능을 이용해 본인의 블로그를 추가해 보았다.





RockMelt를 오래 써 본것도 잘 아는 것도 아니다.
하지만 첫 인상만으로 상당히 잘 만들어졌다는 생각이 든다.
특히나 facebook, twitter, blog 등을 자주 사용하는 사용자들에게 모든 상황을 한눈에 알아볼 수 있어 더 없이 편한 네트워킹이 가능하리라 여겨진다.

이와 유사한 형태로 iPad App 나온다면 정말 좋을 것 같은 생각을 해본다...
(으....만들수 있는 능력이....)
 

2010년 7월 29일 목요일

ckEditor와 ckFinder 설치 및 테스트 (php)

많은 사람들이 사용했던 FCKeditor가 3.x 버전이 되면서 CKEditor로 불리면서 배포되고 사용되고 있습니다.
FCKEditor 의 성능을 그대로 이어받고 더욱 빨라진 CKEditor의 기본적인 설치 방법을 알아보겠습니다.
다시한번 말씀드리지만 기본적인입니다.
그중에서도 PHP를 기반으로 하고 있으며 php 코드를 불러와 사용하는 설치 및 테스트 방법입니다.
보다 발전되고 최적화 되어 있는 방법은 구글링을 통해 찾아보실 수 있으실 것입니다.

1. 소스 다운로드

위의 주소로부터 소스코드를 다운로드 받습니다.
CKFinder는 CKEditor를 통해서 파일(일반 파일, 이미지, 플래쉬 파일)들을 업로드 할 때 사용됩니다. FCKEditor에서는 두 기능이 합쳐졌다고 표현들을 하시는 것을 보았는데 제 생각에는 CKFinder는 보다 기능이 업그레이드 되었고 완전히 다른 기능이라고 생각됩니다. 그리고 CKFinder의 경우 이 문서에서는 PHP 를 사용할 것이므로 위의 다운로드 URL에서 php를 선택하시면 됩니다. (php외에 ASP.net, ASP, ColdFusion을 지원합니다.)

다운로드 받은 소스를 웹에서 접근 가능한 곳에 압축을 풉니다.
본 문서의 경우 http://localhost/phpTest/include/ 아래에 ckeditor와 ckfinder라는 디렉토리 명으로 되어 있습니다.

2. 설정
CKEditor의 경우 별 다른 설정 필요없이 사용하였습니다.
이에 반해 CKEditor는 파일업로드 등의 이유로 경로 및 업로드 파일에 대한 설정을 해 주어야 합니다.
ckfinder/config.php 를 여시면 다음과 같은 php 파일이 나타날 것입니다. (길이상 주석문은 모두 제거해 주었습니다.)

<?php
function CheckAuthentication()
{
// WARNING : DO NOT simply return "true". By doing so, you are allowing
// "anyone" to upload and list the files in your server. You must implement
// some kind of session validation here. Even something very simple as...

// return isset($_SESSION['IsAuthorized']) && $_SESSION['IsAuthorized'];

// ... where $_SESSION['IsAuthorized'] is set to "true" as soon as the
// user logs in your system. To be able to use session variables don't
// forget to add session_start() at the top of this file.

        // return false;
return true;                                // false를 true로 변경하여 upload 가능하도록 만듭니다.
                                                       // 보안상 true 보다는 주석문에 나타나는 것처럼
                                                       // 세션 변수 점검 등으로 true/false를 결정하는 것이 좋을 것입니다.
}


$config['LicenseName'] = '';
$config['LicenseKey'] = '';


// 호스트명을 뺀url 상의 경로입니다.
$baseUrl = '/phpTest/data/upload/';

// 파일 시스템 상의 절대 경로를 나타냅니다.
// $baseDir = resolveUrl($baseUrl);
// 위의 resolveUrl()이라는 메소드를 쓰는 대신 저는 아래와 같이 절대경로를 적어주었습니다.
// 이 경로는 chmod 777 로 잡혀있어야 합니다.
$baseDir = '/home/yoonani/Dropbox/works/phpTest/html/data/upload/';


$config['Thumbnails'] = Array(
'url' => $baseUrl . '_thumbs',
'directory' => $baseDir . '_thumbs',
'enabled' => true,
'directAccess' => false,
'maxWidth' => 100,
'maxHeight' => 100,
'bmpSupported' => false,
'quality' => 80);

$config['RoleSessionVar'] = 'CKFinder_UserRole';

$config['AccessControl'][] = Array(
'role' => '*',
'resourceType' => '*',
'folder' => '/',

'folderView' => true,
'folderCreate' => true,
'folderRename' => true,
'folderDelete' => true,

'fileView' => true,
'fileUpload' => true,
'fileRename' => true,
'fileDelete' => true);

$config['AccessControl'][] = Array(
'role' => '*',
'resourceType' => 'Images',
'folder' => '/Logos',

'folderView' => true,
'folderCreate' => true,
'folderRename' => true,
'folderDelete' => true,

'fileView' => true,
'fileUpload' => false,
'fileRename' => false,
'fileDelete' => false);

$config['DefaultResourceTypes'] = '';

// 업로드될 일반 파일들에 대한 설정입니다.
// url 키에 값으로 $baseUrl .files를
// directory 키에 값으로 $baseDir.files로 설정되어 있는 관계로
// $baseDir에 하위에 files 디렉토리가 있어야 합니다.
// 또한 files는 chmod 777 권한이어야 합니다.
// 이 경로를 바꾸시려면 두 값을 바꾸시면 됩니다.
// maxSize 키 값이 0으로 되어 있는데 이것은 업로드될 파일의 크기를 나타냅니다.
// 여러분들이 원하시는 값으로 나타내실 수 있으며 bytes, K, M, G 등으로 그 크기를 정할 수 있습니다.
// allowExtensions : 업로드 허용가능한 확장자입니다.
// deniedExtensions : 업로드 불가능한 확장자입니다.
$config['ResourceType'][] = Array(
'name' => 'Files', // Single quotes not allowed
'url' => $baseUrl . 'files',
'directory' => $baseDir . 'files',
'maxSize' => 0,
'allowedExtensions' => '7z,aiff,asf,avi,bmp,csv,doc,docx,fla,flv,gif,gz,gzip,jpeg,jpg,mid,mov,mp3,mp4,mpc,mpeg,mpg,ods,odt,pdf,png,ppt,pptx,pxd,qt,ram,rar,rm,rmi,rmvb,rtf,sdc,sitd,swf,sxc,sxw,tar,tgz,tif,tiff,txt,vsd,wav,wma,wmv,xls,xlsx,zip',
'deniedExtensions' => '');


//위의 File과 동일하지만 이 곳은 이미지와 관계된 것들입니다.
$config['ResourceType'][] = Array(
'name' => 'Images',
'url' => $baseUrl . 'images',
'directory' => $baseDir . 'images',
'maxSize' => "16M",
'allowedExtensions' => 'bmp,gif,jpeg,jpg,png,avi,iso,mp3',
'deniedExtensions' => '');


// 마찬가지로 위의 File과 동일하지만 플래쉬와 관계된 것들입니다.
$config['ResourceType'][] = Array(
'name' => 'Flash',
'url' => $baseUrl . 'flash',
'directory' => $baseDir . 'flash',
'maxSize' => 0,
'allowedExtensions' => 'swf,flv',
'deniedExtensions' => '');


// 아래는 그냥 기본값으로 두었습니다.
$config['CheckDoubleExtension'] = true;
$config['FilesystemEncoding'] = 'UTF-8';
$config['SecureImageUploads'] = true;
$config['CheckSizeAfterScaling'] = true;
$config['HtmlExtensions'] = array('html', 'htm', 'xml', 'js');
$config['HideFolders'] = Array(".svn", "CVS");
$config['HideFiles'] = Array(".*");
$config['ChmodFiles'] = 0777 ;
$config['ChmodFolders'] = 0755 ;
$config['ForceAscii'] = false;

include_once "plugins/imageresize/plugin.php";
include_once "plugins/fileeditor/plugin.php";

$config['plugin_imageresize']['smallThumb'] = '90x90';
$config['plugin_imageresize']['mediumThumb'] = '120x120';
$config['plugin_imageresize']['largeThumb'] = '180x180';



3. 사용 예제
php 방식으로 사용하는 예제입니다.

경로는 ckeditor, ckfinder는 include 디렉토리의 하위에 있으며 예제 파일들은 include와 같은 경로상에 위치합니다.

먼저 form 생성 파일입니다.

<html>
<head>
<?php
// ckeditor 경로 설정
include_once './include/ckeditor/ckeditor.php';
// ckfinder 경로 설정
include_once './include/ckfinder/ckfinder.php';

// ckEditor 객체 생성
$ckeditor = new CKEditor();
// 경로 지정
$ckeditor->basePath = '/phpTest/include/ckeditor/';
// ckeditor 객체와 ckfinder의 경로를 이용하여 ckfinder를 ckeditor에 포함시킨다.
// 소스코드를 보시면 더욱 자세한 내용을 알 수 있습니다.
CKFinder::SetupCKEditor($ckeditor, '/phpTest/include/ckfinder/');
?>
</head>
<body>
<form name="ckTest" method="POST" action="getCKtest.php">
<?php
// textarea 의 name이 editor1 이 되는 ckEditor를 포함하는 textarea 생성
$ckeditor->editor('editor1');
?>
<input type="submit" />
</form>
</body>
</html>



실행화면입니다.




먼저 웹 브라우저로 전송된 소스코드를 살펴봅시다.
다음과 같이 파란색으로 되어 있는 코드가 생성되었습니다.


<html>
<head>
</head>
<body>
<form name="ckTest" method="POST" action="getCKtest.php">
<textarea name="editor1" rows="8" cols="60"></textarea>
<script type="text/javascript">//<![CDATA[
window.CKEDITOR_BASEPATH='/phpTest/include/ckeditor/';
//]]></script>
<script type="text/javascript" src="/phpTest/include/ckeditor/ckeditor.js?t=A5AB4B6"></script>
<script type="text/javascript">//<![CDATA[
CKEDITOR.replace('editor1', { "filebrowserBrowseUrl": "\/phpTest\/include\/ckfinder\/ckfinder.html", "filebrowserImageBrowseUrl": "\/phpTest\/include\/ckfinder\/ckfinder.html?type=Images", "filebrowserFlashBrowseUrl": "\/phpTest\/include\/ckfinder\/ckfinder.html?type=Flash", "filebrowserUploadUrl": "\/phpTest\/include\/ckfinder\/core\/connector\/php\/connector.php?command=QuickUpload&type=Files", "filebrowserImageUploadUrl": "\/phpTest\/include\/ckfinder\/core\/connector\/php\/connector.php?command=QuickUpload&type=Images", "filebrowserFlashUploadUrl": "\/phpTest\/include\/ckfinder\/core\/connector\/php\/connector.php?command=QuickUpload&type=Flash" });
//]]></script>
<input type="submit" />
</form>
</body>
</html>

php 코드를 사용하여 위와 같이 웹 페이지 안에 CKEditor를 삽입하였으며 이 코드에 의해 CKFinder 역시 추가되었습니다. 이를 이미지 버튼을 눌러 확인해 보겠습니다.

다음과 같이 보조 창이 뜹니다.
여기서 서버 보기 (Browse Server) 를 클릭해 봅시다.



다음과 같은 판타스틱한 파일 업로드 상자가 나옴을 알 수 있습니다.



여기서 파일을 업로드 하고 업로드한 사진을 클릭하면 ckfinder의 config.php에서 설정한 $baseDir에 _thumnail 이란 디렉토리가 생기고 거기에 지금 보이면 썸네일 사진 또한 같이 저장됩니다.

자 이제 텍스트 상자에 입력을 마치고 서버로 값을 전송하고 그 값을 어떻게 받아오는지 봅시다.
다음은 폼으로부터 값을 전달받아 단순히 화면에 출력시키는 코드입니다.




<?php
$eData = $_POST["editor1"];
?>
<html>
<head>
</head>
<body>
<?php
echo $eData;
?>
</body>
</html>



일반 php에서 폼을 통한 값의 전달시 사용하는 방법을 그대로 따릅니다.
앞선 폼에서 이름을 editor1이라 하였으므로 editor1으로 POST로 전달된 값으로부터 받아와서 그대로 출력합니다.
다음과 같은 결과를 보여줍니다.











아주 기본적인 ckEditor, ckFinder에 대해 알아봤습니다.

제가 계속해서 사용해 보고 더욱 자세한 사항 올리도록 하겠습니다.

감사합니다.

2010년 7월 28일 수요일

ubuntu 10.04에 설치된 eclipse에 SQL Explorer 사용

SQL Explorer를 eclipse Add-On으로 설치하였는데 update sites는 다음과 같다
http://eclipsesql.sourceforge.net/
위의 주소를 Help > Install Software 메뉴에서 SQL Explorer로 추가하여 설치하였다.

그 다음으로 SQL Explorer에서 Connector로 사용할 드라이버를 잡아줘야 하는데
MySQL의 경우 jdbc connector가 필요하다 다음의 링크에서 다운받을 수 있다.
(참고 : http://www.64bitjungle.com/tech/eclipse-pdt-and-mysql-sql-explorer-plugin/)
http://dev.mysql.com/downloads/connector/j/5.1.html

다운받은 후 압축을 풀고 /usr/share/mysql/ 아래로 옮기자 (ex. $ sudo cp -r mysql-connector-java-5.1.13/ /usr/share/mysql/)

SQL Explorer Perspective를 열어 Connectors에서 새로운 연결(Connection)을 생성하는 버튼을 클릭하면 다음과 같은 Create New Connection Profile 창이 나온다.



여기서 Add/Edit Drivers를 클릭하면 다음 창이 활성화된다.


위의 그림과 같이 MySQL을 선택하고 Edit를 클릭하여 설정창을 띄우고 위에서 다운로드 받은 MySQL Connector를 설정하자.

Extra Class Path로 이동하여 Add JARs를 클릭하고 다운받은 드라이버의  jar 파일을 선택한다.


OK를 클릭하자.

그럼 처음띄운 Connection Profile 창이 뜨는 데 이 창에서 URL을 아래 그림에서 처럼

"jdbc:mysql://localhost:3306/데이터베이스명
"
으로 변경하고 로그인 정보를 입력하자.
Namedms  적절하게 자신이 원하는 대로 입력하면 된다.



이상과 같이 연결 설정을 마쳤으면 Eclipse의 Connections 창에서 방금 생성한 Connection이 생기고 이 Connection을 클릭하면 서버와 통신후 다음과 같은 Eclipse Perspective를 볼 수 있다.










Eclipse 에서 SQL Explorer를 설치하고 MySQL 과 연결하는 과정에 대해 간단하게 알아보았다.
Eclipse 에서 모든 것을 관리하는 것이 보다 용이할 거 같아 이렇게 진행하였지만 어디까지나 개인의 취향대로 작업을 진행하면 되겠다.







2010년 7월 21일 수요일

[단문] WordPress 에서 Plugin을 관리하기 위해

Manual로 다운 받아서 할 수도 있지만 웹에서 쉽게 plugin을 다운받아 설치하려면
ftp Server 를 구축하고 (sudo apt-get install vsftpd)
WordPress 소스 아래의 wp-content 를 웹서버가 쓸 수 있도록 하여야 한다.

ubuntu 10.04에서 wordpress 3.0 설치하기

먼저 apache와 PHP, MySQL을 확인해야 한다.
없을 경우 시냅틱 패키지 관리자로 설치하면 된다. (물론 shell에서 dpkg, apt-get 등을 이용해도 되고)

WordPress의 설치 요구 사항은 다음과 같다. (2.9, 3.0 기준)

웹호스팅을 위한 사양은 아래의 사양을 권장한다.
  • PHP version 5.2 or greater
  • MySQL version 5.0 or greater

요구조건을 확인할 수 있는 checklist는 다음 장소에 있다.
http://codex.wordpress.org/User:Hakre/Technical_Installation



1. mod_rewrite

위와 같은 사양을 갖춰다고 하면 mod_rewrite를 apache2에서 사용할 수 있도록 하여야 하는데 ubuntu에 패키지 매니저로 설치한 apache의 경우 설정파일은 /etc/apache2 에 위치한다.
그리고 mod_rewrite는 기본적으로 설치되는데 so 파일의 경로는 /usr/lib/apache2/modules/mod_rewrite.so 이다.
이를 apache2.conf(httpd.conf 도 존재하나 ubuntu 에서는 apache2.conf를 기본 설정파일로 사용한다.) 파일을 직접 편집하여 사용하거나 다음과 같이 shell에서 명령어를 입력해도 된다.

$ sudo a2enmod rewrite

위와같이 입력하고 apache2를 재시작 하자.

mod_rewrite를 위한 설정 파일은 /etc/apache2/mods-available 에 "본인이 원하는 이름.conf" 저장하면 apache2.conf 가 읽어 들인다.


2. MySQL 사용자 및 DB 준비
WordPress 가 사용할 DB와 사용자를 준비한다.
예제로 사용할 MySQL 사용자는 wordpress, 암호는 1234test 이고
DB명은 wpDB 이고 Character Set은 utf8로 할 계획이다. (웹서버에서도 Default CharacterSet을 utf8로 한다.)
여기서 wordpress 사용자는 wpDB에 모든 권한을 갖도록 설정한다.

MySQL 관리자 권한으로 MySQL에 접속한다.

Shell > mysql -u root -p mysql
암호 : .......

mysql> CREATE USER 'wordpress'@'localhost' IDENTIFIED BY '1234test';
Query OK, 0 rows affected (0.00 sec)

mysql> CREATE DATABASE wpDB CHARACTER SET utf8;
Query OK, 1 row affected (0.00 sec)

mysql> GRANT ALL PRIVILEGES ON wpDB.* TO 'wordpress'@'localhost'  WITH GRANT OPTION;
Query OK, 0 rows affected (0.00 sec)

기본적인 DB 준비는 끝났다.
다음으로 넘어가자.


3. 다운로드 받은 WordPress 파일 중 최상위에 있는 wp-config-sample.php 를 wp-config.php 로 복사하자.
그리고 나서 wp-config.php 안에 있는 DB 정보와 사용자명과 암호를 지정하도록 하자.
또한 파일을 보면 각종 key 값을 지정하게 되어있는데 이 키들은 다음의 url을 방문하면 임의로 만들어 주므로 여기서 만들어준 값을 사용하자. 키 생성 url : https://api.wordpress.org/secret-key/1.1/
본인이 사용한 값은 다음과 같다.

// ** MySQL settings - You can get this info from your web host ** //
/** The name of the database for WordPress */
define('DB_NAME', 'wpDB');
 
/** MySQL database username */
define('DB_USER', 'wordpress');
 
/** MySQL database password */
define('DB_PASSWORD', '1234test');
 
/** MySQL hostname */
define('DB_HOST', 'localhost');
 
/** Database Charset to use in creating database tables. */
define('DB_CHARSET', 'utf8');
 
/** The Database Collate type. Don't change this if in doubt. */
define('DB_COLLATE', '');
 
/**#@+
 * Authentication Unique Keys and Salts.
 *
 * Change these to different unique phrases!
 * You can generate these using the {@link https://api.wordpress.org/secret-key/1.1/salt/ WordPress.org secret-key service}
 * You can change these at any point in time to invalidate all existing cookies. This will force all users to have to log in again.
 *
 * @since 2.6.0
 */
define('AUTH_KEY',        'W8<&(0#ocWofuhBus!v-au0`vCC:pncm1qGOVTzHd<u-k-B1{dq84k$1+TuGA=S`');
define('SECURE_AUTH_KEY', ')ev+6kxJ*oA[!c)3lzP@7+9ZZl?i<+U)9nOTjie#*x{2=1~G:K p6K}Pd-yI#ery');
define('LOGGED_IN_KEY',   '-UZ8 &Js3/ `rV6e9|kgO>~B6y1<Civb/BW(zY2_e@?l|a8#bLi2xys.Ll+{oJ0B');
define('NONCE_KEY',       '_C!<2uu>~o_<k#b0.#NZS:|2xZ#-;$+op-9ak#- X!]buuxz,c; /<jvq%K3t{L=');



4. 웹서버가 WordPress 경로를 웹 상에서 요청받을 수 있도록 DocumentRoot로 옮기자.
여기서 본인은 하위 경로명으로 /wp/로 하고자 압축을 해제한 wordpress 디렉토리명을 wp로 변경하였다.
$ sudo mv wordpress/ /var/www/wp/


5. 웹 브라우저를 띄워 다음과 같이 입력하여 설정 파일을 브라우저 상에서 요청하자.

http://localhost/wp/wp-admin/install.php

아래와 같이 나오면 성공한 것이다.




빈칸을 본인이 원하는 대로 입력하고 아래에 있는 다음으로 진행하면 아래와 같이 성공되었다는 메세지를 볼 수 있을 것이다.



성공 메세지 아래의 Log In을 눌러 관리자로 로그인해보자.
로그인을 하고 나면 아래와 같이 관리자의 대쉬보드가 보인다.






성공적으로 설치된 것이다.

그럼 시간이 나는대로 도대체 이 녀석을 어떻게 써 먹을지 알아보자.



PS : 설명중 반말로 하여 죄송합니다. 급히 작성하다 보니....
또한 사이트들을 참고하여 작성하였으니 혹시나 잘못된 내용이 있으시면 댓글로 지적해 주세요






2010년 6월 18일 금요일

ubuntu 10.04 TLS 64bit 버전에서 Andorid 개발환경구축

ubuntu 10.04 버전에는 "시스템 > 관리"를 통해서 들어가면 사앙할 수 있는 시냅틱 패캐지 관리자가 제공된다.

일종의 소프트웨어 관리자로 윈도우즈 소프트웨어 관리자와는 조금 다른 구조를 갖는다.

 

이 녀셕은 시스템에 설치된 SW 뿐만이 아니라 ubuntu에서 제공하는 모든 SW에 대한 리스트를 보여주고 현재 자신에게 설치된 SW들은 별도의 표시를 해서 보여준다.

 

 

Eclipse 설치

기본적으로 이 시냅틱 패키지 관리자를 통해서 eclipse를 설치하였다.

 

스크린샷-시냅틱_꾸러미_관리자_.png

 

위의 스크린샷은 설치된 후이며 위에서 선택된 eclipse를 선택하고 상단의 적용을 클릭하면 디펜던시 등을 맞춰서 필요한 것들을 모두 설치해 준다.

 

 

32Bit 호환 모듈 및 sun-java6-sdk 설치

Android SDK를 설치하여야 하는데 현재 Android SDK는 32Bit 버젼만 등록되어 있다.

이 32Bit를 사용하기 위해서 호환성을 위한 패키지를 설치한다.

안드로이드의 설치 문서를 보면 호환성을 위한 ia32-libs를 설치하고

ubuntu 설치시 제공되는 기본 JDK (openJDK) 대시 Sun의 JDK인 sun-java6-jdk 를 설치하는 것을 기본으로 하고 있다.

 

먼저 ia32-libs 부터 설치하자 (기본적으로 설치되는지 여부를 확인하지 못했다. 만일 설치되어 있다면 넘어가도 된다.)

$ sudo apt-get install ia32-libs

 

다음으로 Sun의 JDK를 설치하자.

여기서 한가지 문제가 있는데 기본 Repository 중 에 Sun의 JDK를 포함하고 있는 곳이 없기 때문이다.

이를 위해 Sun의 JDK를 갖고 있는 Repository를 다음의 순서대로 추가하고 Source List를 Update하자


$ sudo add-apt-repository "deb http://archive.canonical.com/ lucid partner"
$sudo apt-get update

위와 같이 Repository를 변경하고 Sun의 JDK를 설치한다.

 

$ sudo apt-get install sun-java6-jdk

몇가지 묻는 화면이 나오는데 라이센스 동의 여부 등이니 확인하고 넘어가면 된다.

 

 

안드로이드 SDK 설치

이제 안드로이드 SDK를 다운로드 받아 설치하자.

다운로드는 이곳에서 받을 수 있다.  http://developer.android.com/sdk/index.html

일단은 Linux(i386)용을 받고 원하는 곳에서 압축을 해제한다.

 

$ mv android-sdk_r06-linux_86.tgz Somewhere

$ cd Somewhere

$ tar zxvf android-sdk_r06-linux_86.tgz

 

압축을 해제하면 하위에 tools/ 디렉토리가 있다.

이 디렉토리를 본인의 경로에 추가하자

사용자 홈 디렉토리의 .bashrc 파일에 다음을 추가하도록 하자.

 

export PATH=${PATH}:/Addroid SDK 경로/tools

 

 

ADT 플러그인 설치

그 다음으로 ADT 를 Eclipse 플러그인으로 설치하자.

ADT는 Android Development Tools의 약자로 Eclipse 플러그인으로 개발된 개발환경이다.

다음의 경로를 통해 Elipse 플러그인으로 설치한다.

 

https://dl-ssl.google.com/android/eclipse/

 

설치시 Android DDMS 와  Android Development Tools 두가지가 포함되는데 모두 선택하고 설치를 진행하자.

 

 

설치 후 설정 : SDK와 ADT 연결

설치가 완료된 후 다음과 같은 간략한 설정을 거치면 모든 과정이 완료된다.

  1. Window > Preferences... 를 통해 Preferences panel 을 열어 Android 를 선택하자
  2. 에러메세지가 나오는데 이것은 SDK 위치를 입력하라는 것이다. 앞서 설치한 SDK 설치 경로를 찾아서 지정한 후 "Apply"를 클릭한 후 "OK"를 클릭하여 설정을 마무리 하자.

 

스크린샷-Preferences_-1.png

 

 

SDK Component 추가

Eclipse에서 Window > Android SDK and AVD Manager 를 클릭하자

다음의 세가지가 있다.

Virtual Devices 를 통해서 안드로이드 버츄얼 머신인 AVD를 작성한다.

Installed Packages 는 설치된 Package가 나오면 현재 Android SDK Tools, Revision 6 하나만 있다.

Avaliable Packages 는 지정된 Repository를 통해 업그레이드 및 설치 가능한 Package를 보여주는데 일단 현재 Repositoy에 있는 것을 모두 선택하여 업데이트 하자. 이 과정을 거쳐야지만 사용이 가능하며 설치가 완료된다.(윈도우즈에서는 이 과정이 설치과정중에 포함된다.)

 

스크린샷-Android_SDK_and_AVD_Manager_-1.png

 

 

스크린샷-Installing_Archives_.png

 

설치가 되었으면 아주 간단하게 SD 1GB를 갖는 가상머신을 만들어보자 (Virtual Devices)

이름을 sdTest로 하고 SD 카드 부분만 1024MiB로 하고 나머지는 기본 값으로 하자.

스크린샷-Create_new_Android_Virtual_Device_(AVD)_.png

 

 

안드로이드 SDK 2.2 를 통해 테스트할 가상머신이 준비되었다.

 

 

이상으로 기본적인 안드로이드 개발 환경을 갖추었다.

64Bit 버전에서의 사용은 ia32-libs 설치 여부가 중요한 사항이다.

아직 안드로이드 SDK가 64Bit 버전을 지원하지 않기 때문이다.

 

이 환경에서 작업을 하면서 생기는 점들에 대해 추후 기술해 나가도록 하겠다.

 

 

이 글은 스프링노트에서 작성되었습니다.

2010년 6월 11일 금요일

Exponential Family

어떤 확률밀도함수(pdf, pmf)들의 모임을 Exponential Family라고 부를때 이는 다음과 같이 표현된다.

 

f(x|\theta)=h(x)c(\theta)exp \left[ \sum_{i=1}^{k} w_{i}(\theta)t_{i}(x) \right],                   식(1)

여기서

  • h(x) \ge 0,~t_{1}(x),t_{2}(x), \cdots , t_{k}(x)t_{i}(x) 는 모수 \theta에 depend 되지 않은 실값을 갖는 함수
  • c(\theta) \ge 0,~w_{1}(\theta),w_{2}(\theta), \cdots , w_{k}(\theta), w_{i}(\theta)는 값 x에 depend  되지 않은 실값을 갖는 함수

이다.

위의 식은 책마다 표현하는 형태는 다양하지만 위의 두가지 조건을 갖는다. 일례로 위의 식에서 h(x)c(\theta)c(\theta)를 지수 안에 넣어서 다음과 같이 표현하는 책들도 많다.

 

f(x|\theta)=h(x)exp \left[ \sum_{i=1}^{k} w_{i}(\theta)t_{i}(x) - A(\theta) \right]

 

예제) 이항분포

f(x | p) & = & \binom{n}{x} p^{x}(1-p)^{n-x} \ & = & \binom{n}{x} (1-p)^{n} \left( \frac{p}{1-p} \right) ^ {x} \ & = & \binom{n}{x} (1-p)^{n} exp \left( log \left( \frac{p}{1-p} \right) x \right) \" class=

 

 

이 글은 스프링노트에서 작성되었습니다.

2010년 6월 7일 월요일

Android SDK-r06(2.2 Froyo) - avd List

기본 출처 : http://developer.android.com/guide/developing/tools/avd.html


>android list targets

Available Android targets:
id: 1 or "android-2"
     Name: Android 1.1
     Type: Platform
     API level: 2
     Revision: 1
     Skins: HVGA (default), HVGA-L, HVGA-P, QVGA-L, QVGA-P
id: 2 or "android-3"
     Name: Android 1.5
     Type: Platform
     API level: 3
     Revision: 4
     Skins: HVGA (default), HVGA-L, HVGA-P, QVGA-L, QVGA-P
id: 3 or "android-4"
     Name: Android 1.6
     Type: Platform
     API level: 4
     Revision: 3
     Skins: HVGA (default), QVGA, WVGA800, WVGA854
id: 4 or "android-5"
     Name: Android 2.0
     Type: Platform
     API level: 5
     Revision: 1
     Skins: HVGA (default), QVGA, WQVGA400, WQVGA432, WVGA800, WVGA854
id: 5 or "android-6"
     Name: Android 2.0.1
     Type: Platform
     API level: 6
     Revision: 1
     Skins: HVGA (default), QVGA, WQVGA400, WQVGA432, WVGA800, WVGA854
id: 6 or "android-7"
     Name: Android 2.1-update1
     Type: Platform
     API level: 7
     Revision: 2
     Skins: HVGA (default), QVGA, WQVGA400, WQVGA432, WVGA800, WVGA854
id: 7 or "android-8"
     Name: Android 2.2
     Type: Platform
     API level: 8
     Revision: 1
     Skins: HVGA (default), QVGA, WQVGA400, WQVGA432, WVGA800, WVGA854




Hardware Emulation Options

Characteristic Description Property
Device ram size The amount of physical RAM on the device, in megabytes. Default value is "96". hw.ramSize
Touch-screen support Whether there is a touch screen or not on the device. Default value is "yes". hw.touchScreen
Trackball support Whether there is a trackball on the device. Default value is "yes". hw.trackBall
Keyboard support Whether the device has a QWERTY keyboard. Default value is "yes". hw.keyboard
DPad support Whether the device has DPad keys. Default value is "yes". hw.dPad
GSM modem support Whether there is a GSM modem in the device. Default value is "yes". hw.gsmModem
Camera support Whether the device has a camera. Default value is "no". hw.camera
Maximum horizontal camera pixels Default value is "640". hw.camera.maxHorizontalPixels
Maximum vertical camera pixels Default value is "480". hw.camera.maxVerticalPixels
GPS support Whether there is a GPS in the device. Default value is "yes". hw.gps
Battery support Whether the device can run on a battery. Default value is "yes". hw.battery
Accelerometer Whether there is an accelerometer in the device. Default value is "yes". hw.accelerometer
Audio recording support Whether the device can record audio. Default value is "yes". hw.audioInput
Audio playback support Whether the device can play audio. Default value is "yes". hw.audioOutput
SD Card support Whether the device supports insertion/removal of virtual SD Cards. Default value is "yes". hw.sdCard
Cache partition support Whether we use a /cache partition on the device. Default value is "yes". disk.cachePartition
Cache partition size Default value is "66MB". disk.cachePartition.size
Abstracted LCD density Sets the generalized density characteristic used by the AVD's screen. Default value is "160". hw.lcd.density

13 - 3) 태그 페이지 만들기

우리가 갖고 있는 또 다른 정보를 출력하는 방법을 알아보도록 합시다.

사용자로부터 우리는 태그를 입력받았습니다.

이제 이 태그를 중심으로 하여 북마크 링크를 출력해 보도록 하겠습니다.

 

먼저 태그들을 중심으로 한 페이지의 사용자 요청은 "/tag/찾고자 하는 태그"이고 이에 따른 뷰는 tag_page가 되게 하려고 합니다.

복습의 의미로 각자 한번 위의 정보를 가지고 urls.py를 작성해 봅시다. (Hint. 실제 사용은 이와 다르나 사용자 홈페이지의 경우를 참고하여 작성합니다.)

 

사용할 template 입니다.

  1. template/tag_page.html
  2. {% extends "base.html" %}
  3. {% block title %} 태그 : {{ tag_name }} {% endblock %}
  4. {% block head %} 태그 "{{ tag_name }}"의 북마크 {% endblock %}
  5. {% block content %}
  6.     {% include "bookmark_list.html" %}
  7. {% endblock %}

 

이 템플릿을 보시면 아시겠지만 앞서 우리가 작성한 bookmark_list.html을 사용합니다.

앞선 사용자 홈에서의 북마크 리스트 출력과는 사용되는 지점이 다릅니다.

앞에서는 사용자 홈에서 사용하였으므로 show_user 를 통해 누가 북마크 하였는지 알아볼 필요가 없으나 이번 예제의 중심은 Tag이므로 해당 Tag를 누가 적용하였는지도 보여줄 계획입니다.

 

그리고 출력시 해당 태그를 클릭하면 해당 태그의 페이지가 되도록 bookmark_list.html의 {% if show_tags 부분 %} 을 다음과 같이 수정합니다.

 

  1. template/bookmark_list.html
  2. ...
  3.         {% if show_tags %}
  4.        태그 :
  5.            {% if bookmark.tag_set.all %}
  6.            <ul class="tags">
  7.                {% for tag in bookmark.tag_set.all %}
  8.                 <li><a href="/tag/{{ tag.name }}/">{{ tag.name }}</a></li>
  9.                {% endfor %}
  10.            </ul>
  11.            {% else %}
  12. ...

 

이 템플릿과 연동하여 사용자에게 결과를 보여줄 뷰입니다.

 

  1. 애플리케이션/views.py
  2. def tag_page(request, tag_name):
  3.    tag = get_object_or_404(Tag, name=tag_name);
  4.    bookmarks = tag.bookmarks.order_by('-id')
  5.    variables = RequestContext(request, {
  6.                                         'bookmarks': bookmarks,
  7.                                         'tag_name': tag_name,
  8.                                         'show_tags': True,
  9.                                         'show_user': True
  10.                                         })
  11.    return render_to_response('tag_page.html', variables);

 

show_user가 True로 전달되어 누가 해당 태그를 적용하였는지 출력합니다.

 

결과는 다음과 같습니다.

 

shot03.png

 

 

 

간략하게 저장된 값을 출력하는 방법에 대해 알아보았습니다.

지금까지의 예제들을 잘 숙지해 주시기 바랍니다.

수고하셨습니다.

이 글은 스프링노트에서 작성되었습니다.

13 - 2) 입력한 Bookmark 보기

앞선 북마크 작성하기에서 사용자가 생성한 북마크(DB에 저장된 북마크)를 확인하는 페이지를 만들어 보겠습니다.

저장된 북마크를 확인한다는 것은 저장된 내용을 보는 것을 의미하며 우리가 앞서 수립한 데이터 모델(테이블)에 저장되어 있습니다. Bookmark 확인은 다음의 과정을 통해 이뤄집니다.

  1. django 데이터 모델 API를 사용해서 북마크 목록을 가져옵니다.
  2. 템플릿에 북마크 목록을 전달해서 출력합니다.

 

북마크 목록을 가져오는 것은 상황에 따라 달라집니다만 우리는 일단 전체 목록을 가져오는 것으로 할 것입니다. 그 다음으로 목록을 출력하는 페이지의 구성을 거의 유사합니다. 북마크를 링크로 표현하고 태그와 사용자 정보를 그 아래에 출력하도록 할 계획입니다.

 

먼저 다음과 같은 페이지를 만들어 봅시다.(template 디렉토리 아래에 bookmark_list.html 로 저장합니다.)

 

  1. template/bookmark_list.html
  2. {% if bookmarks %}
  3. <ul class="bookmarks">
  4.    {% for bookmark in bookmarks %}
  5.    <li>
  6.        <a href="{{ bookmark.link.url }}" class="title">{{ bookmark.title }}</a><br />
  7.        {% if show_tags %}
  8.        태그 :
  9.            {% if bookmark.tag_set.all %}
  10.            <ul class="tags">
  11.                {% for tag in bookmark.tag_set.all %}
  12.                <li>{{ tag.name }}</li>
  13.                {% endfor %}
  14.            </ul>
  15.            {% else %}
  16.            입력된 태그가 없습니다.
  17.            {% endif %}
  18.            <br />
  19.        {% endif %}
  20.        {% if show_user %}
  21.            추가한 사용자 :
  22.            <a href="/usr/{{ bookmark.user.username }}/" class="username">{{ bookmark.user.username }}</a>
  23.        {% endif %}
  24.    </li>
  25.    {% endfor %}
  26. </ul>
  27. {% else %}
  28.    <p>북마크가 없습니다.</p>
  29. {% endif %}

 

이 파일에서  {% if bookmarks %} 부분은 뷰와 연결시 사용자가 저장한 북마크가 있는지를 판단하는 부분입니다. 사용자가 입력한 북마크가 있을 경우 저장된 북마크를 보는 것이고 그렇지 않다면 아래로 내려와 "북마크가 없습니다"를 화면상에 출력합니다.

사용자가 저장한 북마크가 있을 시 모든 북마크들이 bookmarks 변수에 사용자의 북마크들이 리스트의 형태로 저장됩니다.

여기서 개별 값에 접근하기 위해 {% for bookmark in bookmarks %} 를 사용합니다. bookmarks 변수에 저장된 북마크 만큼 순회하면서 각 순회시 변수 bookmark에 각 북마크들이 임시 저장됩니다. 이제 변수 bookmark는 개별 북마크 값을 갖고 있습니다.

{% if show_tags %}는 뷰에서 전달된 show_tags 변수가 True이면 해당 내용을 보여주는 코드로 아래에 보시면 show_user라는 변수도 있습니다. 역시 show_tags와 동일한 역할이겠죠? 이런 변수들을 추가한 이유는 위에서 생성한 북마크를 보기위한 페이지인 bookamark_list.html을 보다 유연하게 사용하기 위해서 입니다. show_ 계열의 변수들의 참/거짓을 통해 사용자에게 보여주는 부분을 달리 할 수 있기 때문입니다. 지금 우리가 보여주고자 하는 북마크 리스트는 사용자 홈에서 보여지는 것이므로 show_user는 전달되지 않을 계획입니다(뷰에서 전달하지 않으면 False가 됩니다) .

이상으로부터 우리는 북마크 리스트를 보여주기 위한 기본 Template을 만들었습니다.

 

위의 파일은 일종의 모듈화로써 이 bookmark_list.html 파일은 북마크 리스트들을 보여주는 기능을 갖고 있습니다.

이 기능을 사용자 홈페이지에 넣어보도록 하겠습니다.

기존의 userHome.html 을 다음과 같이 변경해 봅시다. (template 아래에 사용자 홈페이지를 보여주기 위해 작성한 파일입니다.)

 

  1. template/userHome.html
  2. {% extends "base.html" %}
  3. {% block title %} Main page {% endblock %}
  4. {% block head %} 안녕하세요! {% endblock %}
  5. {% block content %}
  6.    {{username}} 님의 개인정보 페이지
  7.     <hr />
  8.    {% include "bookmark_list.html" %}
  9. {% endblock %}

 

다음으로 전달하고자 하는 북마크 리스트, 사용자 홈에서 Tag를 보여주기 위해 변수 show_tags를 True로 하여 템플릿에 적절한 변수를 보내기 위해 뷰 함수를 변경해 보겠습니다.

다음과 같이 views.py의 userHomePage 뷰를 변경합니다.

 

  1. 애플리케이션/views.py
  2. from django.shortcuts import get_object_or_404
  3. ...
  4. @login_required
  5. def userHomePage(request, username):
  6.     user = get_object_or_404(User, username=username)
  7.    bookmarks = user.bookmark_set.order_by('-id')
  8.    variables =RequestContext(request,
  9.                              {'username' : username,
  10.                               'bookmarks': bookmarks,
  11.                               'show_tags': True
  12.                               }   )
  13.    return render_to_response('userHome.html', variables)

 

코드상으로 많은 부분이 변경된 것 같지만 기존의 사용자 로그인 확인을 @login_required 데코레이터로 변경하고 사용자의 입력(여기서는 사용자 아이디를 통해 사용자 홈페이지로 이동하는 것입니다.)을 get_object_or_404 를 통해 확인합니다. 즉, 사용자가 입력한 사용자 아이디를 가져와서 User 데이터 모델의 username 열에 있는지 검사하여 없으면 404에러(페이지가 존재하지 않음을 나타내는 에러)를 발생하고 그렇지 않으면 즉, 해당 사용자 아이디가 존재하면 user 변수에 저장합니다. 북마크는 order_by를 이용하여 입력된 역순으로 다시말해 최근 저장된 북마크부터 목록을 가져옵니다. 가져온 값들은 bookmarks 변수에 저장됩니다. 그리고 이제 username과 bookamrks, 그리고 show_tags변수를 True로 하여 Template과 연결하여 사용자에게 보여줍니다.

 

위와 같이 작성을 한 결과는 다음과 같습니다.

 

shot02.png

 

 

이 글은 스프링노트에서 작성되었습니다.

13 - 1) Bookmark 작성하기

먼저 다음과 같이 북마크 등록에 사용될 폼을 작성해 봅시다.

 

  1. myTest/BookmarkSaveForm.py
  2. # -*- coding: utf-8 -*-
  3. from django import forms

  4. class BookmarkSaveForm(forms.Form):
  5.    url = forms.URLField( label = '주소', widget=forms.TextInput(attrs={'size':64}) )
  6.    title = forms.CharField( label = '제목', widget=forms.TextInput(attrs={'size':64}) )
  7.    tags = forms.CharField( label = '태그', widget=forms.TextInput(attrs={'size':64}), required=False )

 

BookmarkSaveForm()은 주소, 제목, 태그 총 세 개의 사용자 입력을 받습니다.

TextInput에 공히 사용된 attr={'size':64} 는 화면상에 나타날 길이 속성을 의미합니다.

자 이에 이 Form과 함께 사용될 View 를 작성해 봅시다.

 

  1. views.py
  2. from webDB.myTest.BookmarkSaveForm import *

  3. from webDB.myTest.models import *


  4. def bookmark_save_page(request):

  5.    if request.method == "POST" :

  6.        form = BookmarkSaveForm(request.POST)

  7.        if form.is_valid() :

  8.            link, dummy = Link.objects.get_or_create(

  9.                                                     url = form.cleaned_data['url']

  10.                                    )

  11.            bookmark, created = Bookmark.objects.get_or_create(

  12.                                                     user = request.user,

  13.                                                      link = link

  14.                                    )

  15.            bookmark.title = form.cleaned_data['title']

  16.            if not created :

  17.                bookmark.tag_set.clear()

  18.            tag_names = form.cleaned_data['tags'].split()

  19.            for tag_name in tag_names :

  20.                tag, dummy = Tag.objects.get_or_create(name=tag_name)

  21.                bookmark.tag_set.add(tag)

  22.            bookmark.save()

  23.            return HttpResponseRedirect('/home/%s/' % request.user.username)

  24.    else :

  25.        form = BookmarkSaveForm()

  26.        variables = RequestContext(request, {'form' : form})

  27.    return render_to_response('bookmark_save.html', variables)  

 

화면상에 보여줄 template을 만들어 보도록 하겠습니다.

template 디렉토리에 아래에 bookmark_save.html 로 다음의 코드를 작성해 주세요.

 

  1. template/bookmark_save.html
  2. {% extends "base.html" %}
  3. {% block title %} 북마크를 저장하세요 {% endblock %}
  4. {% block head %} 북마크를 저장하세요 {% endblock %}
  5. {% block content %}
  6. <form method="post" action=".">
  7.        {{form.as_p}}
  8.        <input type="submit" value="북마크 저장" />
  9. </form>
  10. {% endblock %}

 

 

사용자의 url 요청을 통해 이제까지 작성된 내용을 보여줄 수 있도록 urls.py에 다음을 추가합니다.

 

  1. urls.py
  2. ...
  3. (r'^save/$', bookmark_save_page),
  4. ...

 

 

서버를 가동시켜 이상의 내용을 확인해 봅시다.

> python manage.py runserver 8888

 

shot01.png

 

 

위의 Form에

주소 : http://www.djangoproject.com

제목 : Django 홈페이지

태그 : django homepage

라고 입력하고 북마크 저장을 눌러봅시다.

현재는 저장된 Bookmark를 보여주는 페이지가 없으니 다음과 같이 Shell에서 확인해 봅시다.

 

> python manage.py shell
>>> from webDB.myTest.models import *
>>> bookmark = Bookmark.objects.get(id=1)
>>> bookmark
<Bookmark: test1, http://www.djangoproject.com/>
>>> bookmark.link.url
u'http://www.djangoproject.com/'
<Bookmark: test1, http://www.djangoproject.com/>
>>> bookmark.title
u'Django \ud648\ud398\uc774\uc9c0'

>>> tag = Tag.objects.get(id=1)

>>> tag

<Tag: django>

>>> bookmark.tag_set.all()
[<Tag: django>, <Tag: homepage>]

>>> tag.bookmarks.all()
[<Bookmark: test1, http://www.djangoproject.com/>]

 

 

다음 시간에는 우리가 지금 생성해 놓은 Bookmark를 웹에서 살펴보는 것을 만들어 보겠습니다.

수고하셨습니다.

 

 

학습활동
  1. 앞서 base.html에 보시면 로그인, 로그아웃 등의 Link 버튼을 보여주는 Navigation 부분이 있습니다. 이 Navigation에 사용자가 로그인 되어 있을 때만 "북마크 추가" 라는 Link를 생성하여 save/ 페이지로 연결되게 합시다.
    shot01(1).png

  2. 로그인한 사용자만 북마크를 생성하는 페이지(사용자의 URL 요청이 save 일 때  bookmark_save_page 뷰가 실행되는 페이지)에 접근하도록 view를 변경해 봅니다.

 

이 글은 스프링노트에서 작성되었습니다.

13. Web Application 제작

실제로 웹 상에서 서비스가 가능한 Web Application을 개발해 봅시다.

 

Learning Website Developement with Django(2008, Packt, 번역본 쉽고 빠른 웹 개발 Django, 인사이트)에서 사용된 예제를 인용하여 제작할 Web Application은 Bookmark Application입니다.

이 Application을 제작하면서 실제 웹 상에서의 서비스가 어떻게 이뤄지는 지 알아보도록 하겠습니다.

 

그리고 수업에 사용된 내용은 그대로 유지한 채 Application을 제작할 것이므로 기존 수업 내용에 추가하시면 될 것입니다.

Bookmark Application을 위해 models.py에 다음을 추가해주시기 바랍니다.

  1. models.py
  2. class Link(models.Model):
        url = models.URLField(unique=True)
        def __unicode__(self):
            return self.url
        
    class Bookmark(models.Model):
        title = models.CharField(max_length=200)
        user = models.ForeignKey(User)
        link = models.ForeignKey(Link)
        def __unicode__(self):
            return '%s, %s' % (self.user.username, self.link.url)
        
    class Tag(models.Model):
        name = models.CharField(max_length=64, unique=True)
        bookmarks = models.ManyToManyField(Bookmark)
        def __unicode__(self):
            return self.name

 

작성후 실제 생성되어 작동하는 sql문을 출력해 봅시다.

 

> python manage.py sqlall myTest
BEGIN;

...
CREATE TABLE "myTest_link" (
    "id" integer NOT NULL PRIMARY KEY,
    "url" varchar(200) NOT NULL UNIQUE
)
;
CREATE TABLE "myTest_bookmark" (
    "id" integer NOT NULL PRIMARY KEY,
    "title" varchar(200) NOT NULL,
    "user_id" integer NOT NULL REFERENCES "auth_user" ("id"),
    "link_id" integer NOT NULL REFERENCES "myTest_link" ("id")
)
;
CREATE TABLE "myTest_tag" (
    "id" integer NOT NULL PRIMARY KEY,
    "name" varchar(64) NOT NULL UNIQUE
)
;
CREATE TABLE "myTest_tag_bookmarks" (
    "id" integer NOT NULL PRIMARY KEY,
    "tag_id" integer NOT NULL REFERENCES "myTest_tag" ("id"),
    "bookmark_id" integer NOT NULL REFERENCES "myTest_bookmark" ("id"),
    UNIQUE ("tag_id", "bookmark_id")
)
;
CREATE INDEX "myTest_bookmark_user_id" ON "myTest_bookmark" ("user_id");
CREATE INDEX "myTest_bookmark_link_id" ON "myTest_bookmark" ("link_id");
...
COMMIT;

 

Tag 데이터 모델에 ManyToManyField가 적용되어 myTest_tag_bookmarks 라는 우리가 생성하지 않았던 테이블이 생성됨을 확인할 수 있습니다. (참고 : Insert, Update, Delete)

 

이제 위의 데이터 모델을 적용해 봅시다.

> python manage.py syncdb

 

이제 데이터 모델에 대한 기본적인 준비가 끝났으니 기능들을 하나씩 추가해 보도록 하겠습니다.

 

이 글은 스프링노트에서 작성되었습니다.

12 - 1) 사용자 등록하기

앞선 django Form을 이용하여 새로운 사용자를 등록하여 봅시다.

 

Application에 registrationForm.py라는 파일 만들고 다음과 같이 입력합니다.

 

  1. from django import forms

    class RegistrationForm(forms.Form):
       username = forms.CharField(label='Username', max_length=30)
       email = forms.EmailField(label='e-mail')
       password1 = forms.CharField(label='Password', widget=forms.PasswordInput())
       password2 = forms.CharField(label='Confirm password', widget=forms.PasswordInput())

 

입력된 코드는 사용자 등록을 위한 Form을 생성하기 위해 django에서 제공하는 Form 기능을 이용하기 것입니다.

코드를 살펴보시면 아시겠지만 사용자 등록에 필요한 사용자의 입력은 사용자명, 이메일, 암호입력, 암호 확인의 4가지입니다.

암호 입력을 위한 Form 필드 생성시 forms.PasswordInput()을 widget의 속성값으로 사용하여 암호 입력 필드로 만들고 있음을 잘 보시기 바랍니다.

위의 사용자 입력 폼 생성 코드를 shell에서 확인해 봅시다.

 

> python manage.py shell
>>> from myTest.registrationForm import *
>>> user = RegistrationForm({
... 'username' : 'test2',
... 'email' : 'abc@def.com',
... 'password1' : 'test123',
... 'password2' : 'test123'
... })
>>> user.is_valid()
True

 

잘 사용됨을 알 수 있습니다. 만일 여기서 이메일 입력하는 부분에 이메일 형태와 다른 값을 입력하면 어떻게 될까요?

다음 예를 살펴보겠습니다.

 

>>> user2 = RegistrationForm({
... 'username' : 'test3',
... 'email' : 'abcdef ghijkl',
... 'password1' : 'test123',
... 'password2' : 'test123'
... })
>>> user2.is_valid()
False

 

유효하지 않음을 알 수 있습니다. 유효하지 않다는 것은 Form에 사용자 입력이 잘못되었다는 것을 뜻하는 데 어떤 오류가 있는지 확인해 보겠습니다.

 

>>> user2.errors
{'email': [u'Enter a valid e-mail address.']}

 

이메일 필드의 입력이 잘못되었으며 이메일 형식이 아닌 입력임을 알 수 있습니다.

 

 

다음으로 입력값에 대한 확인을 하기 위한 메소드를 추가해 보도록 하겠습니다.

입력값 확인은 다음의 두가지입니다.

  • 입력받은 두개의 비밀번호가 일치하는지 검사 (is_valid_password2())
  • username이 올바른지, 이미 사용되고 있는지는 아닌지 검사 (is_valid_username())

 

각각에 대해 위에서 작성한 registrationForm.py에 추가해 주시기 바랍니다.

 

  1. import re
    from django.contrib.auth.models import User
    from django.core.exceptions import ObjectDoesNotExist
  2.  
  3. class registrationForm(self) :
  4. ...
  5.     def is_valid_password2(self):
           if 'password1' in self.cleaned_data:
               password1 = self.cleaned_data['password1']
               password2 = self.cleaned_data['password2']
               if password1 == password2 :
                   return password2
               raise forms.ValidationError('입력한 패스워드가 일치하지 않습니다.')
         
       def is_valid_username(self):
           username = self.cleaned_data['username']
           if not re.search(r'^\w+$', username):
               raise forms.ValidationError('username은 알파벳, 숫자, 밑줄(_)만 가능합니다.')
           try:
               User.objects.get(username=username)
           except ObjectDoesNotExist:
               return username
           raise forms.ValidationError('이미 사용중인 username입니다.')


앞서 설명드린 내용을 참고하시면 is_valid_password2()는 쉽게 이해하시리라 믿습니다...제발...

두번째 정의한 메소드에서 re.search(r'^\w+$', username) 는 정규표현식에 대한 검색 함수입니다(import re). 이 함수는 username에서 단어가 한개 이상 등장하면 true를 반환입니다. 단어를 구성하는 문자는 알파벳, 숫자, 밑줄(_)로써 이 세가지 이외의 문자가 username에 있으면 False를 반환합니다. False를 반환할 경우 ValidationError() 예외를 발생시켜(raise) Form으로 되돌아 갈 수 있도록 합니다.

그외 나머지 부분도 이해하시는데 큰 어려움은 없으리라 다시 한번 착각에 빠져봅니다.

 

RequestContext의 사용 (http://docs.djangoproject.com/en/dev/ref/templates/api/#id1)

앞선 11. 페이지 개선의 학습활동에 문제점과 해결책을 말씀드렸습니다.

이제 RequestContext의 사용법을 알아보도록 하겠습니다.

RequestContext 객체는 일반 Context와는 조금 달라서 별도로 지정하지 않아도 자동으로 user 객체를 전달합니다.

따라서 별도의 user 객체를 연결시키지 않더라도 request객체와 템플릿 변수들로 RequestContext 를 만들 수 있습니다.

site_main과 userHomepage 뷰를 수정해 보겠습니다.

 

  1. views.py
  2. from django.template import RequestContext
  3.  
  4. def site_main(request) :
  5. return render_to_response('index.html', RequestContext(request))

  6.  
  7. def userHomePage(request, username):
       if request.user.is_authenticated() :
           try :
               user = User.objects.get(username=username)
           except :
               raise Http404('사용자를 찾을 수 없습니다.')
         
           variables =RequestContext(request, {'username' : username})
           return render_to_response('userHome.html', variables)
       else :
           return HttpResponseRedirect('/accounts/login/')

 

 

사용자 등록 폼이 사용할 템플릿 페이지를 작성해 봅시다.

프로젝트/template/registration/register.html로 다음을 작성합시다.

 

  1. template/registration/register.html

    {% extends "base.html" %}
    {% block title%}사용자 등록 페이지{% endblock %}
    {% block head%}사용자 등록을 합니다.{% endblock %}
    {% block content%}
    <form method="post" action=".">
       {{ form.as_p }}
       <input type="submit" value="가입" />
    </form>
    {% endblock %}

 

 

{{ form.as_p }} 부분에 우리가 작성한 django Form을 사용한 Form 코드가 들어갑니다. 여기서 한가지 주의할 것은 이름이 form.as_p 이므로 이 template과 들어갈 내용을 결합할 때 우리가 작성한 form 코드가 변수명 form으로 결합되어야 합니다. 이를 위한 view 코드가 아래에 나오는데 이 부분을 잘 살펴주시기 바랍니다.

 

  1. views.py

  2. from webDB.myTest.registrationForm import *

  3.  

  4. def register_page(request):
       if request.method == 'POST':
           form = RegistrationForm(request.POST)
           if form.is_valid():
               user = User.objects.create_user(
                                               username=form.cleaned_data['username'],
                                               password=form.cleaned_data['password1'],
                                               email=form.cleaned_data['email']
                                               )
               return HttpResponseRedirect('/')
       else :
           form = RegistrationForm()
       variables = RequestContext(request, {'form': form})
       return render_to_response('registration/register.html', variables)

 

 

이 뷰를 위한 url 세팅은 다음과 같습니다.

 

  1. urls.py

  2. ...

  3.     (r'^register/$', register_page),

  4. ...

 

이 글은 스프링노트에서 작성되었습니다.

12. django Form

django에서 제공하는 Form 기능에 대해 알아보려고 합니다.

 

앞서 우리는 로그인 폼을 직접 작성(로그인 과정)하였습니다.

하지만 django에서는 폼의 하위요소를 HTML로 직접 작성하지 않고 django에서 지원하는 기능을 이용하여 폼 요소를 생성하고 각 폼으로 부터 전달되는 값을 검사할 수 있는 기능을 갖고 있습니다.

 

앞선 로그인 폼을 django에서 지원하는 기능을 이용하여 작성하여 봅시다.

애플리케이션 하위에 loginForm.py라는 이름으로 다음을 만들어 봅시다.

 

  1. loginForm.py
  2. # -*- coding: utf-8 -*-
    from django import forms

    class LoginForm(forms.Form):
        username = forms.CharField(label='사용자 이름', max_length=30, required=True)
        password = forms.CharField(label='암호', widget=forms.PasswordInput(), required=True, max_length=127) 

 

생긴 형태가 마치 데이터 모델과 닮았습니다. 하지만 실제 사용되는 것은 많이 다릅니다.

 

django의 Form은 HTML Form 에 대한 wrapper 클래스를 지원합니다.

자주 사용되는 django Form필드는 forms에 의해 정의되며 다음과 같습니다.

  • CharField : 문자열, 기본 widget : Textinput
  • IntegerField : 숫자, 기본 widget : Textinput
  • DateField : 파이썬의 datetime.date 객체, 기본 widget : Textinput
  • DateTimeField : 파이썬의 datetime.datetime 객체, 기본 widget : Textinput
  • EmailField : 형식이 정확한 이메일 주소, 기본 widget : Textinput
  • URLField : 형식이 정확한 URL, 기본 widget : Textinput
  • 참고 : http://docs.djangoproject.com/en/1.2/ref/forms/fields/#ref-forms-fields

 

사용자로부터 전달될 내용이 위와 같을 경우 이 wrapper 클래스를 통해  비교적 편하게 사용할 수 있습니다.

 

그리고 위의 기본적인 필드 형식에 추가적으로 다음과 같은 인수를 넣어 보다 세밀한 Form을 생성할 수 있습니다.

  • label : 필드의 HTML 코드에서 사용되는이름(HTML의 <label>)
  • required : 필수 필드 여부(True/False)
  • widget : 표현될 HTML Form 요소 다음과 같은 widget이 주로 사용된다.

    • TextInput : 한줄 글입력 필드 <input type="text" />
    • PasswordInput : 비밀번호 입력 필드 <input type="password" />
    • HiddenInput : 숨김 필드 <input type="hidden" />
    • Textarea : 여러줄 글상자 필드 <textarea></textarea>
    • FileInput : 파일선택 필드 <input type="file" />
    • 참고 : http://docs.djangoproject.com/en/1.2/ref/forms/widgets/#ref-forms-widgets
  • help_text : 필드 설명, 필드 아래에 출력

 

앞선 예제에서 사용한 django의 Form에 대해 django shell에서 사용해 보도록 합시다.

 

> python manage.py shell

>>> from myTest.loginForm import *
>>> frm = LoginForm()                        # 1
>>> print frm.as_table()                       # 2
<tr><th><label for="id_username">사용자 이름:</label></th><td><input id="id_username" type="text" name="username" maxlength="30" /></td></tr>
<tr><th><label for="id_password">암호:</label></th><td><input id="id_password" type="password" name="password" maxlength="127" /></td></tr>
>>> print frm["username"]                   # 3
<input id="id_username" type="text" name="username" maxlength="30" />
>>> frm = LoginForm({                        #4
... 'username' : 'test1',
... 'password' : 'password123'
... })
>>> frm.is_valid()                              #5
True
>>> frm = LoginForm({                       #6
... 'username' : '',
... 'password' : 'password123'
... })
>>> frm.is_valid()
False

 

각각에 대한 설명은 다음과 같습니다.

  1. 앞서 작성한 LogiinForm()을 통해 새로운 Form을 생성합니다.
  2. Form을 생성한 결과를 HTML Table 형태로 출력합니다.
  3. 전체 Form 대신 Form의 특정한 하위요소 하나만 출력합니다.
  4. 작성한 Form을 통해 사용자 입력값을 저장합니다.
  5. 입력된 값이 유효한지 검사합니다.
  6. username을 required=True 로 하였으므로 빈값이 들어가면 유효하지 않은 입력이 됩니다.

 

django에서 제공하는 Form은 위와 같이 Form요소에 대한 wrapper 클래스 뿐만이 아니라 메소드를 추가할 수 있습니다.

예를 들어 LoginForm에 입력값에 대한 검사 메소드를 추가해 보도록 하겠습니다.

제작할 메소드는 username이 존재여부입니다.

 

  1. from django.contrib.auth.models import User
    from django.core.exceptions import ObjectDoesNotExist
  2.  
  3. class LoginForm(forms.Form) :
  4. ...
  5.     def checkUsername(self):
            username = self.cleaned_data['username']              # 1
            username = username.lstrip().rstrip()                     # 2
            try :                                                                # 3
                User.objects.get(username=username)
            except  ObjectDoesNotExist:
                raise forms.ValidationError('존재하지 않는 사용자입니다.')               # 4
            else :
                return True

 

  1. django의 Form은 사용자 입력값이 없는 빈 폼을 검사(validation)하려고 하면 예외를 발생합니다. 사용자가 입력한 값은 사전형 데이터로 표현된 form.data로 접근할 수 있으며 검사를 통과한 사용자 입력값은 froms.cleaned_data로 접근할 수 있습니다. 즉, 사용자가 입력한 빈 값이 아닌 username을 가져옵니다.
  2. 입력된 username 문자열의 좌우의 공백문자를 제거합니다.
  3. 예외 처리구문입니다. User.object.get()은 조건으로 넣어준 값이 없을 경우 ObjectDoesNotExist 예외를 발생시킵니다. 이 #3 구문은 입력된 사용자명이 있는지 검사하여 입력된 사용자가 없으면 ObjectDoesNotExist 예외가 발생됩니다.
  4. #4에서 예외가 발생되면 다시 Form의 ValidationError()예외를 발생시켜서 Form 입력을 처음으로 되돌립니다.

 

 

이상으로부터 django에서 제공하는 Form 기능을 알아보았습니다.

다음 시간에 이 Form을 이용하여 사용자를 추가하는 과정에 대해 알아보도록 하겠습니다.

 

 

 

이 글은 스프링노트에서 작성되었습니다.

11. 페이지 개선

웹 페이지의 Presentation을 담당하는 CSS 를 추가하는 방법에 대해 알아보겠습니다.

CSS는 일반 파일로 작성되고 정적인 페이지로 작성이 됩니다.

정적인 페이지이므로 웹 서버의 특정위치에 저장되어 있는데 이 저장된 위치를 나타내는 urls.py 세팅을 알아 보겠습니다.

CSS 파일의 요청 경로는 "웹 서버/site_media" 이며 해당 요청 경로의 실제 경로는 "프로젝트/site_media" 라고 가정하도록 하겠습니다. (즉, 프로젝트 아래에 site_media를 먼저 작성해 주시기 바랍니다.)

 

  1. urls.py
  2. import os.path
  3. ...
  4. site_media = os.path.join(os.path.dirname(__file__), 'site_media')
  5. urlpatterns = patterns('',
  6.   ...
  7.   (r'^site_media/(?P<path>.*)$', 'django.views.static.serve', {'document_root': site_media}),
  8.   ...
  9. )

 

위와 같이 작성을 하면 "서버명/site_media/*.*' 으로 들어오는 요청에 대해 '프로젝트명/site_media'에서 해당 파일을 찾습니다.

(r'^site_media/(?P<path>.*)$', 'django.views.static.serve', {'document_root': site_media}),

을 자세히 살펴보기로 하겠습니다.

사용자가 '/site_media/(임의의 경로.확장자)'의 형태로 요청시 django의 내장 뷰(View)에서 정적 파일에 대한 기능을 지원(static)하는 모듈중에 serve 모듈을 연결합니다('django.views.static.serve') serve 모듈의 원형은 serve(request, path, document_root=None, show_indexes=False) 로써 3개의 전달인자를 전달받습니다(request는 제외). 첫번째 전달인자인 path는 소괄호() 안에 묶여진 값이 전달되고 두번째 전달인자 document_root에 대해 사전형 자료로 {'document_root': site_media} 를 전달하고 세번째 전달인자는 기본값인 False를 사용합니다.

두번째 전달인자인 document_root에 대해 위에서 생성한 경로인 os.path.join(os.path.dirname(__file__), 'site_media') 이가 전달됨을 확인해 주시기 바랍니다.

이제 CSS 파일 하나를 '프로젝트/site_media' 디렉토리 아래에 style.css라는 이름으로 다음 내용을 저장해 주시기 바랍니다.

 

  1. /site_media/style.css
  2. #nav {
            float: right, /* 우측 정렬 */
            color: black;  /* 텍스트 색상은 검정 */
            background: gray;  /* 내용, 패딩(padding)은 회색 */
            margin: 0px 12px 0px 12px; /* 위와 아래의 마진은 0px */
            padding: 12px 0px 12px 0px; /* 오른쪽과 왼쪽 패딩은 0px */
            border-style: dashed;
            border-width: medium;  /* 모든 면들에  테두리 너비를 설정 함 */
            border-color: black;
    }

 

 

이제 CSS를 적용하여 보도록 하겠습니다.

base.html을 다음과 같이 수정하여 CSS 문서를 HTML 이 읽어오도록 합니다.

 

  1. base.html
  2. <!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html>
        <head>
            <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
            <title>Django Page | {% block title %}{% endblock %}</title>
            <link rel="stylesheet" href="/site_media/style.css" type="text/css" />
        </head>
        <body>
            <h1>{% block head %}{% endblock %}</h1>
  3.         {% if user.username %}
  4.             <h4 align="right">{{user.username}} 님 반갑습니다.(<a href='/accounts/logout'>로그아웃</a>)</h4>
  5.         {% else %}
  6.             <h4 align="right"><a href="/accounts/login">로그인</a>하세요</h4>

            {% endif %}
            {% block content%}{% endblock %}
            <hr align="center" />
            <p align="center">Site managed by yoonani(yoonani72@gmail.com)</p>
        </body>
    </html>

 

Template 중에 가장 기본이 되는 base.html을 수정하였으니 모든 Template에 적용이 될 것입니다.

메인 페이지에 대해 적용한 사례를 보도록 하겠습니다.

 

index.html을 다음과 같이 수정해 보시기 바랍니다.

  1. {% extends "base.html" %}
    {% block title %} Main page {% endblock %}
    {% block head %} 안녕하세요! {% endblock %}
    {% block content %}
    django 서비스에 오신것을 환영합니다.
    {% endblock %}

 

수정된 결과는 다음과 같습니다.

 

스크린샷-Django_Page___Main_page_-_Mozilla_Firefox.png

 

 

본 연습을 통해서 고정된 자원인 CSS를 사용할 수 있도록 하는 방법에 대해 알아보았습니다.

이제 site_media에는 웹 사이트의 고정된 자원을 저장하고 그 내용을 사이트에서 필요한 곳에서 불러서 사용할 수 있게 되었습니다.

고정된 자원은 CSS 뿐만이 아니라 JavaScript, 각종 이미지 등입니다.

 

 

학습활동

앞선 10. Template - 상속의 학습활동을 수행해 본 학생들은 본 시간에 학습한 내용대로 base.html을 저장하면 사용자 홈페이지로 이동시 문제가 발생합니다.

그것은 index.html에는 user 객체가 전달되지만 사용자 홈페이지에는 user 객체가 전달되지 않기 때문입니다.

이를 해결하기 위해 user 객체를 사용자 홈페이지에 전달할 수 있지만 다른 페이지에도 이런 일이 필요하다면 여간 귀찮은 일이 아닐 수 없습니다. 이런 문제를 해결하기 위해 RequestContext()라는 객체를 사용하는 데요...

이 객체는 앞서서 보았던 Context() 객체와는 조금 달라서 별도로 지정하지 않더라도 알아서 user 객체를 전달하며 request 객체와 템플릿 변수들로 생성합니다.

이런 RequestContext 객체에 대해 알아보고 개인노트에 "학습활동 - RequestContext"라는 이름으로 조사한 내용을 기록하세요

이 글은 스프링노트에서 작성되었습니다.