2012-12-20

'쉽고 빠른 웹개발 Django' 버전 1.4 이상에서 진도나가기



'쉽고 빠른 웹개발 Django'은 1.0버전 기준이어서 최신버전 (1.4.3)에서 막히면 진도를 더이상 나갈 수 없게 됩니다. 이에, 최신 버전에서도 진행할 수 있도록 변경된 부분을 반영하여 수정본을 작성해보았습니다. 1.4 이후 부터 디렉토리 구조가 조금 변경이 되었는데, 대부분 이 것 때문에 기존버전과 맞지 않는 부분이 생기는 것입니다. 추후 더 변경할 것이 발견되면 추가하도록 하겠습니다.

P. 15

Before
django_bookmarks/
    __init__.py
    manage.py
    settings.py
    urls.py
After
django_bookmarks/
    manage.py
    django_bookmarks/
        __init__.py
        settings.py
        urls.py
        wsgi.py

P. 17

Before
DATABASE_ENGINE = 'sqlite3'
DATABASE_NAME = 'bookmarksdb'
DATABASE_USER = ''
DATABASE_PASSWORD = ''
DATABASE_HOST = ''
DATABASE_PORT = ''
After
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'.
        'NAME': 'bookmarksdb',                  # Or path to database file if using sqlite3.
        'USER': '',                      # Not used with sqlite3.
        'PASSWORD': '',                  # Not used with sqlite3.
        'HOST': '',                      # Set to empty string for localhost. Not used with sqlite3.
        'PORT': '',                      # Set to empty string for default. Not used with sqlite3.
    }
}

P.30

Before
INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'django_bookmarks.bookmarks',
)
After
INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'bookmarks',
)

P. 37

Before
import os.path
 
TEMPLATE_DIRS = (
    os.path.join(os.path.dirname(__file__), 'templates'),
)
After
import os
SETTINGS_DIR = os.path.abspath(os.path.dirname(__file__))
(SITE_ROOT, dummy) = os.path.split(SETTINGS_DIR)
 
TEMPLATE_DIRS = (
    os.path.join(SITE_ROOT, 'templates'),
)

P. 48

Before
...
<form method="post" action=".">
...
After
...
<form method="post" action=".">{% csrf_token %}
...

P. 55

Before
import os.path
 
site_media = os.path.join(
    os.path.dirname(__file__), 'site_media'
)
After
import os
 
URLS_DIR = os.path.abspath(os.path.dirname(__file__))
(SITE_ROOT, dummy) = os.path.split(URLS_DIR)
site_media = os.path.join(SITE_ROOT, 'site_media')

P. 58

Before
from django import newforms as forms
After
# -*- coding: utf-8 -*-
from django import forms

2012-12-14

Ramaze Simple Tutorial 2 (with Sequel)



이 튜토리얼에서는 migration 을 사용하지 않고, Model 에서 직접 스키마를 작성하고 Table 을 생성하도록 하였다. 그리고, Ramaze (2012.12.08) 에서 테스트를 진행하였다.

  • 관련 모듈 설치
    $ apt-get install libsqlite3-dev sqlite3
    $ gem install ramaze sequel thin sqlite3
    
  • 프로젝트 생성
    $ mkdir -p /opt/project/ramaze
    $ cd /opt/project/ramaze
    $ ramaze create web
    
  • 기본 웹서버 변경 -> task/ramaze.rake 에서 :webrick 을 :thin 으로 수정
  • 웹서버 시작 테스트
    $ cd /opt/project/ramaze/web
    $ thin start
    
  • db / model 폴더 생성
    $ cd /opt/project/ramaze/web
    $ mkdir -p db
    $ mkdir -p model
    
  • app.rb 에 다음 추가
    require 'sequel'
    
    # Open the accounts database
    DB = Sequel.connect('sqlite://db/web.db')
    
    # Initialize controllers and models
    require __DIR__('model/init')
    require __DIR__('controller/init')
    
  • model/init.rb 에 다음 추가
    require __DIR__('admin')
    
  • model/admin.rb 생성
    # coding : utf-8
    require 'sequel'
    
    class Admin < Sequel::Model(:admins)
      plugin :schema
      
      set_schema do
        primary_key :id
    
        String :login, :unique => true, :empty => false
        String :password, :empty => false
        String :name
        String :email
        String :mobile_phone
        String :level, :default => '00'
      end
    
      # Create the database table if it doesn't exists
      # Also add a admin
      if ! table_exists?
        create_table
        create :login => 'hong', :password => 'hong1234', :name => '홍길동', :email => 'hong@test.com', :mobile_phone => '010-5555-5555', :level => '00'
        create :login => 'jang', :password => 'jang1234', :name => '장영실', :email => 'jang@test.com', :mobile_phone => '010-6666-6666', :level => '01'
      end
    end
    
  • controller/init.rb 에 다음을 추가
    require __DIR__('admin')
    
  • controller/admin.rb 생성
    class AdminController < Controller
      map '/admin'
      def index
        'Admin Test'
      end
    end
    
  • 프로젝트를 실행하고 브라우저에서 http://localhost:3000/admin 을 접근해본다.
  • 이제, controller/admin.rb 의 index 를 다음과 같이 변경한다.
      def index
        @admins = Admin.all
      end
    
  • view 디렉토리 생성
    $ cd /opt/project/itsm_ramaze/web/view
    $ mkdir admin
    
  • view/admin/index.xhtml 생성
    <h1> Admin Test Page </h1>
    <?r @admins.each do |admin| ?>
      #{admin.login}
      #{admin.name}
      #{admin.email}
      #{admin.mobile_phone}
      #{admin.level}
      <br/>
    <?r end ?>
    
  • 프로젝트를 실행하고 브라우저에서 http://localhost:3000/admin 을 접근해서 데이터 목록이 뜨면 정상이다.

2012-11-28

Python : 이미지 파일 일괄 자르기



ImageMagick 을 이용하여 이미지 파일들을 일괄로 Crop 하는 스크립트이다. ImageMagick 모듈을 이용하지 않고, convert 툴을 이용하여 구현하였다. 이번엔 급한대로 써야 해서 대충했지만, 다음에는 관련 모듈을 이용해서 제대로 해봐야겠다.
  • 관련 모듈
    • Wand is a ctypes-based ImagedMagick binding library for Python.
    • PythonMagickWand is an object-oriented Python interface to MagickWand based on ctypes.
    • PythonMagick is an object-oriented Python interface to ImageMagick.
import os, glob, shutil
 
# 원하는 사이즈와 출력 디렉토리를 정한다.
CROP_SIZE = '1739x2617+364+0'
OUT_DIR   = 'output'
 
if os.access(OUT_DIR, os.F_OK) :
    shutil.rmtree(OUT_DIR)
os.mkdir(OUT_DIR)
file_list = glob.glob('*.tif') # 이미지 확장명을 정한다.
 
cnt = 1
for filename in file_list :
    print('%s/%s : convert %s -crop "%s" %s\\new_%s' % (cnt, len(file_list), filename, CROP_SIZE, OUT_DIR, filename))
    os.system('convert '+filename+' -crop "'+CROP_SIZE+'" '+OUT_DIR+'\\new_'+filename)
    cnt = cnt + 1



2012-11-21

Ramaze 에서 JSON 으로 응답하기



AJAX 로 동작하는 웹프로그램을 개발하려면, JSON 으로 응답하는 컨트롤러를 작성할 필요가 있다. Ramaze 기본 문서에는 대략만 나와 있어서, 써먹기 어려워서 테스트해보고 다시 정리해본다.

  • Controller 의 init.rb 에 다음 내용을 추가해준다.
    require 'json' # --> 추가된 부분
    
    class Controller < Ramaze::Controller
      layout :base
      helper :xhtml
      engine :etanni
      provide( :json, :type => "application/json") { |action, val| val.to_json } # --> 추가된 부분
    end
    
  • Controller 작성 예제
    class TestController < Controller
      map '/test'
      
      def action
        json = {}
        json['a'] = '1234'
        json['b'] = '5678'
        json
      end
    end
    
  • 해당 액션을 호출할 때에는 http://test.com/test/action.json 처럼 끝에 .json 을 붙여준다.

2012-11-19

Ramaze Simple Tutorial (with Sequel)



흔히들 많이 이용되는 Rails 를 안쓰고 Ramaze + Sequel 조합을 시도해 본 이유는 설치되는 모듈이 정말 적기 때문이다. 너무 많은 의존성을 가지게 되면, 시간이 지날 수록 관리하는데 많은 문제를 읽으킨다. 이 때문에, 원래는 python/Django 를 사용했었는데, python 대신 ruby 를 사용하고 싶어서 대체할만한 웹프레임워크를 찾고 있었다. 그러다가, Ramaze + Sequal 이 초간단으로 설치되는 것을 확인하고, 관련 문서를 찾아보고 테스트했지만 Rails 나 Django 처럼 충분한 문서를 제공하지 않더라. 쓸만한 Tutorial 도 없고 해서, 초간단으로 Tutorial 을 만들어보았다.

  • 관련 모듈 설치
    $ apt-get install libsqlite3-dev sqlite3
    $ gem install ramaze sequel thin sqlite3
    
  • 프로젝트 생성
    $ mkdir -p /opt/project/ramaze
    $ cd /opt/project/ramaze
    $ ramaze create web
    
  • 기본 웹서버 변경 -> start.rb 에서 :webrick 을 :thin 으로 수정
  • 웹서버 시작 테스트
    $ ramaze start -s thin
    or 
    $ ruby start.rb
    
  • db 생성
    $ cd /opt/project/ramaze/web
    $ mkdir -p db/migration
    $ vi db/migration/001_AdminMigration.rb
    
    # db/gigration/001_AdminMigration.rb
    # Run:
    # sequel -m db/migration -M 1 sqlite://db/web.db
    
    class CreateAdminsTable < Sequel::Migration
      def up
        create_table(:admins) do
          primary_key :id
          String :login, :unique => true
          String :password
          String :name
          String :email
          String :mobile_phone
          String :level
        end
      end
    
      def down
        drop_table(:admins)
      end
    end
    
    DB Migration
    $ sequel -m db/migration -M 1 sqlite://db/web.db
    
  • app.rb 에 다음 추가
    # coding : utf-8
    require 'sequel'
    
    # Open the accounts database
    DB = Sequel.connect('sqlite://db/web.db')
    
    # Initialize controllers and models
    require __DIR__('model/init')
    require __DIR__('controller/init')
    
    # 아래는 처음 실행할 때 한번만 적용하고 주석으로 막아준다.
    # Admin.create(:login => 'hong', :password => 'hong1234', :name => '홍길동', :email => 'hong@test.com', :mobile_phone => '010-5555-5555', :level => '00')
    # Admin.create(:login => 'jang', :password => 'jang1234', :name => '장영실', :email => 'jang@test.com', :mobile_phone => '010-6666-6666', :level => '01')
    
  • model/init.rb 에 다음 추가
    require __DIR__('admin')
    
  • model/admin.rb 생성
    require 'sequel'
    
    class Admin < Sequel::Model
    
    end
    
  • controller/init.rb 에 다음을 추가
    require __DIR__('admin')
    
  • controller/admin.rb 생성
    class AdminController < Controller
      map '/admin'
      def index
        'Admin Test'
      end
    end
    
  • 프로젝트를 실행하고 브라우저에서 http://localhost:7000/admin 을 접근해본다.
  • 이제, controller/admin.rb 의 index 를 다음과 같이 변경한다.
      def index
        @admins = Admin.all
      end
    
  • view 디렉토리 생성
    $ cd /opt/project/itsm_ramaze/web/view
    $ mkdir admin
    
  • view/admin/index.xhtml 생성
    <h1> Admin Test Page </h1>
    <?r @admins.each do |admin| ?>
      #{admin.login}
      #{admin.name}
      #{admin.email}
      #{admin.mobile_phone}
      #{admin.level}
      <br/>
    <?r end ?>
    
  • 프로젝트를 실행하고 브라우저에서 http://localhost:7000/admin 을 접근해서 데이터 목록이 뜨면 정상이다.

2012-11-05

(Perl) 모듈 관리



Perl 의 모듈관리 기능을 정리해보았다. Ruby 의 gem 처럼 하나의 툴로 이쁘게 관리되면 좋겠는데, 아직까지는 번거롭게 몇가지 툴을 설치해서 따로 따로 사용해야 된다는 것이 아쉽다. Windows 용 Perl 인 ActivePerl 에서는 ppm 이라는 툴을 제공하긴 하지만, cpan 을 이용할수 없다는 것이 흠이다.

  • 설치할 모듈 검색
    https://metacpan.org/
    http://www.cpan.org/
  • 모듈관리도구 설치
    $ cpan App::cpanminus App::cpanoutdated Devel::Loaded App::pmuninstall
    
  • 모듈 설치
    $ cpanm [모듈이름]
    or
    $ cpan [모듈이름]
    
  • 모든 모듈 업데이트
    $ cpan-outdated | cpanm
    
  • 설치된 모듈 검색
    $ pminst [모듈이름]
    or 
    $ pmall | grep [모듈이름] --> 시간 오래 걸림
    
  • 모듈 설명 보기
    $ pmdesc [모듈이름]
    
  • 모듈 삭제
    $ pm-uninstall [모듈이름]
    

2012-06-28

Groovy : Rotate Backup file or Log file



Windows 에서 백업파일을 Rotate 하여 최근 N일 동안의 백업파일을 유지하도록 하였다.

final DAY_LIMIT  = 14      // N일
final TARGET_DIR = "M:/"


def today   = new Date()
def command = "cmd /c m: & cd \\ & mkdir ${today.format('YYYYMMdd')} & move backup\\*.tib ${today.format('YYYYMMdd')}"
command.execute().waitFor()

new File(TARGET_DIR).eachFile { path ->
    if (path.directory && path.name =~ /201[2-9]\d{4}/) {
        def lastModified = new Date(path.lastModified())
        def diffDay = new Date() - lastModified
        if(diffDay > DAY_LIMIT) {
            // println(lastModified.format("YYYY-mm-dd") + " : ${path}")
            println("cmd /c rd /q /s ${path}")
            "cmd /c rd /q /s ${path}".execute().waitFor()
        }
    }
}

2012-06-15

Groovy : 여러 디렉토리의 사이즈를 구하고 크기 순으로 정렬하기

Ruby 버전에 이어서 Groovy 버전도 만들어보았다. 거의 생김새가 비슷한 언어여서, 몇가지만 수정하면 바로 동작한다.

def FOLDER_LIST = [
                    "/path/to1",
                    "/path/to2",
                    "/path/to3",
                    "/path/to4",
                    "/path/to5",
                  ]

def startDate = new Date().format('yyyy/MM/dd HH:mm:ss')

def size_folder_list = []
FOLDER_LIST.each { folder_name ->
    def folder_size = 0
    new File(folder_name).eachFileRecurse { path ->
        folder_size += path.size()
    }
    size_folder_list << "${folder_size}|${folder_name}"
}

size_folder_list = size_folder_list.sort { a, b ->
    (a_size, a_file) = a.split(/\|/)
    (b_size, b_file) = b.split(/\|/)
    // b_size as int <=> a_size as int
    b_size.toInteger() <=> a_size.toInteger()
}

size_folder_list.each { folder ->
    (f_size, f_path) = folder.split(/\|/)
    println("${f_size},${f_path}")
}


def endDate = new Date().format('yyyy/MM/dd HH:mm:ss')
println "\n\n\n"
println "================================================================================"
println "Start Time : $startDate"
println "End   Time : $endDate"
println "================================================================================"

2012-06-14

Ruby : 여러 디렉토리의 사이즈를 구하고 크기 순으로 정렬하기

Ruby 에서 여러 디렉토리의 사이즈를 구하고 크기 순으로 정렬하는 방법을 구현해보았다. 여러 팀에서 공유하는 파일의 서버의 경우, 각 디렉토리별(팀)로 어느 정도 사용하는 지를 알아보고 싶을 때 이용하면 좋을 것이다.

require 'find'


$FOLDER_LIST = [
                'C:\\path\\to1',
                'C:\\path\\to2',
                'C:\\path\\to3',
                'C:\\path\\to4',
                'C:\\path\\to5',
               ]

start_time = Time.now


size_folder_list = Array.new
$FOLDER_LIST.each() do | folder_name |
  folder_size = 0
  Find.find(folder_name) do | path |
    if(File.exist?(path))
      folder_size += File.size(path)
    end
  end
  size_folder_list << "#{folder_size}|#{folder_name}"
end

size_folder_list = size_folder_list.sort do |a, b| 
  (a_size, a_file) = a.split(/\|/)
  (b_size, b_file) = b.split(/\|/)
  b_size.to_i <=> a_size.to_i
end

size_folder_list.each do | folder |
  (f_size, f_path) = folder.split(/\|/)
  print f_size + "," + f_path + "\n"
end


end_time = Time.now
puts "\n\n========================================================================="
puts "Start Time : #{start_time}"
puts "End Time   : #{end_time}"

2012-05-25

Ruby : 특정 디렉토리 밑에 있는 파일중에 200MB 넘는 파일 찾기

다음은 특정 디렉토리 아래에 있는 파일 중에서 200MB 넘는 파일을 찾아서, 크기로 정렬하여 출력하는 프로그램이다. 어려운 것은 아니지만, 필요할 때 찾아보면 좋을 듯 하여~ ^^

# -*- coding: cp949 -*-
require 'find'

$TARGET     = "C:\\"
$SIZE_LIMIT = 200_000_000

start_time = Time.now

puts "Script Start..."
puts "=========================================================================\n\n"

file_list = Array.new
Find.find($TARGET) do |path|
  if File.file?(path) and File.size(path) > $SIZE_LIMIT
    file_size = File.size(path)
    file_list << file_size.to_s + "|" + path
  end
end


file_list = file_list.sort do |a, b| 
  (a_size, a_file) = a.split(/\|/)
  (b_size, b_file) = b.split(/\|/)
  b_size.to_i <=> a_size.to_i
end


file_list.each do |file|
  (f_size, f_path) = file.split(/\|/)
  print f_size + " -> " + f_path + "\n"
end


end_time = Time.now


puts "\n\n========================================================================="
puts "Start Time : #{start_time}"
puts "End Time   : #{end_time}"

puts "Script End..."

2012-05-23

Grails : ROOT Context 로 Grails App 실행하기

Grails 로 App 을 만들어서, 실행을 하면, App Name 을 콘텍스트로 사용하여 아래와 같이 접속하여야 한다. URL 뒤에 콘텍스트 이름까지 입력하려니 많이 번거롭다.

http://localhost:8080/myApp

사이트를 만들면 ROOT 콘텍스트로 바로 접속할 수 있도록 하여, http://localhost:8080/ 로 접근을 하려는 것이 보통일 것이다.
이를 위해서 2가지 방법이 제공이 되는데, 첫번째는 application.properties 파일에 아래 내용을 추가 하는 것 :

app.context=/

두번째 방법은 Config.groovy 에 아래 내용을 추가하는 것이다

grails.app.context = "/" 

Groovy : 버전 정보 가져오기

Groovy 스크립트를 실행하면서 현재 실행되고 있는 Groovy 인터프리터의 버전을 알고 싶을 때, 버전에 따라서 아래와 같이 할 수 있으며, 현재 주력 버전이 1.8.6 이고, 곧 2.0.0 도 나올 것이라서 거의 대부분 첫번째 방법으로 사용하면 될 것이다.

groovy 1.6.6 and 1.7-rc-1 released 이후의 방법

import groovy.lang.GroovySystem
println GroovySystem.version
// or
println GroovySystem.getVersion()

if (GroovySystem.version >= "1.8.0")
    println "1.8.0 이상"

예전 방법

import org.codehaus.groovy.runtime.InvokerHelper  
println InvokerHelper.version 

2012-05-14

(Perl) StrawberryPerl 에서 PAR::Packer 설치하기

보통은 아래와 같이 CPAN 을 이용해서 모듈을 설치할 수 있다.

C:\> cpanm PAR::Packer

그런데, 최신 버전인 1.013은 에러 발생하며 설치 안된다. 부엉이님 께서 Perl / Win32 / PAR::Packer - PAR::Packer 1.013 설치 중 windres: can’t open file pp.manifest 오류가 발생하는 경우 에서 1.013 설치법을 설명을 해주었지만, 뭔가 좀 깔끔치 못한 느낌이다. 그래서, 바로 밑에 버전 1.012 을 설치하려고 하는데, 어떻게 해야될지 모르겠더라. CPAN 사이트에서 직접 다운로드 하여, 컴파일해서 설치할 수도 있겠지만, 좀 원시적인 느낌이다. cpan 을 사용해서 좀 더 편하게 설치할 수는 없을까? 하고 찾아보니, 방법이 없지는 않더라. PAR::Packer 1.012 의 실제 다운로드 주소가 http://search.cpan.org/CPAN/authors/id/R/RS/RSCHUPP/PAR-Packer-1.012.tar.gz 이며, 아래와 같이 설치한다.

C:\> cpanm RSCHUPP/PAR-Packer-1.012.tar.gz

의외로 간단한 방법이었다. 여전히 뭔가 좀 불편하지만 말이다.

2012-04-09

Django 1.4 에서 Template 디렉토리 설정

Django 1.4 에서 가장 큰 변화라면, 전체 어플리케이션의 레이아웃이 바뀌었다는 점이다.
이전 버전에서는 프로젝트를 생성하면 아래와 같이 파일들이 생성되었다.

project_name/
            `- __init__.py
            `- manage.py
            `- settings.py
            `- urls.py
            `- wsgi.py
1.4에서는 이렇게 바뀌었다.
project_name/
            `- manage.py
            `- project_name/
                           `- __init__.py
                           `- settings.py
                           `- urls.py
                           `- wsgi.py

여기에서 주의 깊게 볼 것은 상위에 있는 project_name 디렉토리에는 __init__.py 파일이 없기 때문에 패키지명으로 사용할 수 없다는 것과 settings.py 에서 템플릿 디렉토리를 지정할 때 주의해야 한다는 것이다. 템플릿 디렉토리를 절대경로로 하는 경우에는 상관 없지만, 보통(?) settings.py 를 기준으로 상대경로를 지정하는 경우가 많이 있다.
1.4 이전에는 다음과 같이 하면 문제 없었다.

import os.path
TEMPLATE_DIRS = (
    os.path.join(os.path.dirname(__file__), 'templates'),
)

그러나, 1.4 에서는 settings.py 의 위치가 한단계 밑으로 내려갔기 때문에, 좀 더 복잡해지긴 했지만, 아래와 같은 방법으로 설정해주어야 한다.

import os

SETTINGS_DIR = os.path.abspath(os.path.dirname(__file__))
(SITE_ROOT, dummy) = os.path.split(SETTINGS_DIR)


TEMPLATE_DIRS = (
    os.path.join(SITE_ROOT, 'templates'),
)

2012-04-04

node.js : nodebrew 를 이용한 node 설치

여러 버전의 node 를 사용하기 위해서 기존에 nvm 을 이용했으나, 다른 언어(perl, python)에서 사용하던 ~~~brew 를 node 에서도 이용할 수 있게 되었다. 사용법은 조금 다르긴 하지만, nvm 보다 낫지 않을까 싶어서 설치해보고 정리해보았다.

$ curl https://raw.github.com/hokaccha/nodebrew/master/nodebrew | perl - setup
$ echo 'export PATH=$HOME/.nodebrew/current/bin:$PATH' >> ~/.profile; source ~/.profile
$ nodebrew help
$ nodebrew ls-remote
$ nodebrew install 0.6.14
$ nodebrew alias default 0.6.14
$ nodebrew use default
$ node -v
v0.6.14

2012-03-27

Django 마이그레이션 1.3.1 -> 1.4

이번에 Django 기반으로 서비스하는 시스템을 1.3.1 에서 1.4 로 마이그레이션하였다. settings.py 에서 다음 2가지를 변경해주니, 잘 동작되는 것 같다. 사실, 이 외에도 몇가지 있으나, Django 가 불평은 하지 않았다. 그래도 미래를 위해서 미리 바꾸어주는 것이 좋을 것이다. https://docs.djangoproject.com/en/dev/releases/1.4/를 참고해서 시도해보자.


DATABASE

# 변경전
DATABASE_ENGINE = 'postgresql_psycopg2'
DATABASE_NAME = '디비이름'
DATABASE_USER = '사용자'
DATABASE_PASSWORD = '비밀번호'
DATABASE_HOST = '호스트'
DATABASE_PORT = '포트'

# 변경후
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': '디비이름',
        'USER': '사용자',
        'PASSWORD': '비밀번호',
        'HOST': '호스트',
        'PORT': '포트',
    }
}

TEMPLATE_LOADERS

# 변경전
TEMPLATE_LOADERS = (
    'django.template.loaders.filesystem.load_template_source',
    'django.template.loaders.app_directories.load_template_source',
#     'django.template.loaders.eggs.load_template_source',
)

# 변경후
TEMPLATE_LOADERS = (
    'django.template.loaders.filesystem.Loader',
    'django.template.loaders.app_directories.Loader',
#     'django.template.loaders.eggs.Loader',
)

2012-03-12

Debian/Ubuntu : 시간 지역설정/동기화

어려운 내용은 아니지만, 서버를 구축하면서 반드시 해야 할 작업중의 하나가 시간을 제대로 맞추는 것이다. Debian/Ubuntu 에는 ntp 데몬을 쉽게 설치할 수 있으며, 이것이 주기적으로 인터넷에 있는 시간서버와 주시적으로 시간을 맞추도록 되어 있다. 그리고, 해당 지역의 로컬 시간을 설정하기 위해서는 /etc/localtime 을 원하는 지역의 것으로 바꾸어주면 된다. 방법은 직접 수동으로 하는 것과, dpkg-reconfigure 라는 툴을 이용하는 2가지 방법을 이용할 수 있다. 간단하니 아래와 같이 직접 설정해보자. 사실 지금도 내 개발서버의 시간이 맞지 않아서, 백업이 엉뚱한 시간이 이루어지고 있었다. ^^;

수동 설정

$ cd /etc
$ sudo rm localtime
$ sudo cp /usr/share/zoneinfo/Asia/Seoul localtime
$ sudo apt-get install ntp
$ date
Mon Mar 12 10:05:40 KST 2012

툴을 이용한 설정

$ sudo dpkg-reconfigure tzdata --> Asia, Seould 선택
$ sudo apt-get install ntp
$ date
Mon Mar 12 10:05:40 KST 2012

2012-03-09

Groovy : 수정된지 N일이 넘는 파일들 삭제하기

특정 디렉토리 밑에 있는 파일 중에서 일정시간이 지난 파일을 삭제하고자 할 때 필요한 스크립트이다. 파일 서버를 관리하다보면, 주기적으로 필요 없는 파일들을 삭제해야 하는데, Unix(Linux) 환경에서는 find, rm 등을 조합하여 사용할 수도 있다. 그런 것이 여의치 않고, 굳이 Groovy 를 이용하려고 할 때, 활용하면 좋겠다.

final DAY_LIMIT = 2 // N일
final TARGET_DIR = "./"

println("[Deleted Files]\n")
new File(TARGET_DIR).eachFileRecurse { file ->
    if( file.file ) {
        def lastModified = new Date(file.lastModified())
        def diffDay = new Date() - lastModified
        if(diffDay > DAY_LIMIT) {
            println(lastModified.format("YYYY-mm-dd") + " : $file")
            file.delete()
        }
    }
}

2012-03-07

Groovy : 클로져(반복자)에서 빠져나오기

보통의 반복문에서 중간에 빠져나오려면 break 를 사용하지만, 클로져(반복자)에서 break를 사용하면 에러가 발생한다. 다음 코드를 작성하여 실행해보면, 에러를 확인할 수 있을 것이다.

10.times { item -> 
    println item
    if(item == 6) break
}

이에 대한 대안으로는 Exception 을 사용하면 된다. 이제 다시 Exception 을 이용해서 클로져를 빠져나오는 코드를 보자. 좀 번거롭기는 하지만, 확실하게 break 와 같은 효과를 볼 수 있다.

try {
    10.times { item -> 
        println item
        if(item == 6) throw new Exception("break")
    }
} catch (e) {}

Groovy : 100MB 이상의 파일중에서 중복된 파일 찾기

Groovy 에서도 100MB 넘는 파일 중에서 중복된 파일을 찾는 프로그램을 만들어보았다. 모든 언어를 공부하면서 만들어보는 것인데, MD5 Checksum 이 같으면 중복된 파일로 인식하고, 카운트하여 정렬하고 출력하도록 하였다. 파일에 대한 MD5 Checksum 기능이 없어서 직접 구현되었고, 이 부분을 제외하면 Ruby 와 거의 비슷하다.

// -----------------------------------------------------------------------------
// 100MB 이상의 파일중에서 중복된 파일 찾기
// -----------------------------------------------------------------------------
 
import java.security.MessageDigest
 
final TARGET_DIR = "C:\\"
final LIMIT_SIZE = 100000000
 
def md5sum(final file) {
    MessageDigest digest = MessageDigest.getInstance("MD5")
    file.withInputStream() { is ->          
        byte[] buffer = new byte[8192]
        int read = 0
        while( (read = is.read(buffer)) > 0) {
            digest.update(buffer, 0, read);
        }
    }                                                        
    byte[] md5 = digest.digest()
    BigInteger bigInt = new BigInteger(1, md5)
    return bigInt.toString(16).padLeft(32, '0')
}
 
def file_list     = []
def distinct_list = [:]
def startDate = new Date().format('yyyy/MM/dd HH:mm:ss')
 
new File(TARGET_DIR).eachFileRecurse { file -> 
    if( file.size() > LIMIT_SIZE ) {
        def md5 = null
        try {
            md5 = md5sum(file)
        } catch(ex) {
            md5 = null
        }
        if (md5 != null) { 
            if(distinct_list[md5] == null)
                distinct_list[md5] = 1
            else
                distinct_list[md5] += 1
            file_list << (md5+"|"+file)
        }
    }
}
 
distinct_list = distinct_list.sort { a, b -> b.value <=> a.value }
 
distinct_list.each { md5, cnt ->
    if( cnt > 1) {
       println "\n[ $md5 ]"
       file_list.each { file -> 
           def (md5_2, filename) = file.split(/\|/)
           if(md5 == md5_2) { println filename }
       }
    }
}
 
def endDate = new Date().format('yyyy/MM/dd HH:mm:ss')
println "\n\n\n"
println "================================================================================"
println "Start Time : $startDate"
println "End   Time : $endDate"
println "================================================================================"

2012-03-06

Groovy : 파일 md5sum 구하기

Groovy에서 md5sum 을 구하려면, Java 의 MessageDigest 모듈을 이용해야 한다. 안타깝게도 file 에 대한 md5 checksum 을 구하는 부분은 구현되어 있지 않기 때문에 별도로 구현해야 한다. 다른 언어에서의 방법과 유사하므로 그리 어렵지는 않다. Java 의 모듈을 그대로 이용해야하기 때문에, 구현은 Java 의 그것과 거의 같다.

import java.security.MessageDigest
 
def md5sum(final file) {
    MessageDigest digest = MessageDigest.getInstance("MD5")
    file.withInputStream() { is ->          
        byte[] buffer = new byte[8192]
        int read = 0
        while( (read = is.read(buffer)) > 0) {
            digest.update(buffer, 0, read);
        }
    }                                                        
    byte[] md5 = digest.digest()
    BigInteger bigInt = new BigInteger(1, md5)
    return bigInt.toString(16).padLeft(32, '0')
}
 
println md5sum(new File("md5sum.groovy"))

2012-03-05

Groovy : 하위 디렉토리의 모든 파일 출력하기

Groovy에서 하위 디렉토리의 모든 파일을 출력하는 방법을 알아보자. 인터넷에서 검색하면 금방 나오는 내용이지만, 일단 적어둔다.

new File("C:\\").eachFileRecurse { filename -> 
    println "Filename: $filename"
}

2012-02-27

NODE.JS : 파일 MD5SUM 구하기

기본 모듈 crypto 를 이용해서 파일의 md5sum 을 구하는 스크립트를 만들어보자. 별도로 제공이 안되므로 직접 만들어써야 한다. 일단 아래와 같이 만들어보면 md5 뿐만 아니라 sha1 에 대한 checksum 을 쉽게 구할 수도 있을 것이다.
  • 동기IO를 이용한 방법 (파일을 조금씩 읽어서 처리하는 방법) => Windows 에서는 에러가 발생하고 제대로 실행되지 않는다. 원인을 찾아보려고 했으나, 알 수가 없다. 혹시 누구 아시는분? node.js v0.6.11 에서 문제가 있었으나, v0.6.12 에서 잘 되고 있다.
    var fs     = require('fs');
    var crypto = require('crypto');
     
    var filename = 'walk_test.js';
    function md5sum(filename) {
      var genChecksum = null;
      var checksum = crypto.createHash('md5');
      var bytesRead = 1;
      var pos = 0
      var buffer = new Buffer(1024*64); // 64Kbyte
      var fd = fs.openSync(filename, 'r');
      var data = null;
      while (bytesRead > 0) {
        bytesRead = fs.readSync(fd, buffer, 0, buffer.length, pos);
        pos += bytesRead;
        if (bytesRead === buffer.length) {
          checksum.update(buffer);
        } else {
          data = buffer.slice(0, bytesRead);
          checksum.update(data);
        }
      }
      fs.closeSync(fd);
      genChecksum = checksum.digest('hex');
      return genChecksum;
    }
    
    console.log('checksum is : ' + md5sum(filename));
    
  • 동기IO를 이용한 방법 (파일은 한번에 읽어서 처리하는 방법) ⇒ 주의 : 한번에 큰 파일을 읽어서 처리하므로, 메모리를 많이 소모하게 될 것이다.
    var fs     = require('fs');
    var crypto = require('crypto');
     
    var filename = 'walk_test.js';
     
    function md5sum(filename) {
      var genChecksum = null;
      var checksum = crypto.createHash('md5');
      var data = fs.readFileSync(filename);
      checksum.update(data);
      genChecksum = checksum.digest('hex');
      return genChecksum;
    }
     
    console.log('checksum is : ' + md5sum(filename));
    
  • 비동기IO를 이용한 방법
    var fs     = require('fs');
    var crypto = require('crypto');
     
    var filename = 'walk_test.js';
    var genChecksum = null;
    var checksum = crypto.createHash('md5');
    var fin = fs.ReadStream(filename);
    fin.on('data', function(data){
      checksum.update(data);
    });
     
    fin.on('end', function(){
      genChecksum = checksum.digest('hex');
      console.log('checksum is : ' + genChecksum);
    });
    

2012-02-23

nvm 이용하여 여러 버전의 node.js 설치하기

node.js 에도 Ruby 의 rvm 와 비슷한 툴이 제공됩니다. 바로 nvm 이지요.

설치방법은 아래와 같습니다. (FreeBSD에서는 ./nvm/nvm.sh 를 열어서 make 를 gmake 로 바꾸어주고 사용합니다.)

$ git clone git://github.com/creationix/nvm.git ~/.nvm
$ echo 'source ~/.nvm/nvm.sh' >> ~/.profile; source ~/.profile
$ nvm install v0.6.11
$ nvm use v0.6.11
$ nvm alias default v0.6.11
$ node -v
v0.6.11
이외에도 아래의 툴들도 고려해볼 만 하니 살펴봐주세요.
  • https://github.com/isaacs/nave
  • https://github.com/visionmedia/n
  • http://pypi.python.org/pypi/nodeenv/

2012-02-17

more(less) ANSI Color 제대로 보여주기

보통 Linux 나 FreeBSD에서 ls 명령어를 이용하면 파일/디렉토리 속성에 따라서 다양한 ANSI Color 로 나오게 되어 있다.


목록이 좀 긴 경우, more (or less) 를 이용해서 한 화면씩 끊어서 보게 되는데, 어떤 경우, ANSI 제어문자가 그대로 나오며, 색깔이 전혀 표시되지 않는 경우도 발생하는데, 보는데 상당히 괴로워진다. 다음은 ls -al | more 를 실행한 화면이다.


이것을 Color가 제대로 나오게 하려면 more (or less) 에 -R 옵션을 추가해주면 된다. ls -al | more -R 과 같이 해주면 다음과 같이 정상적으로 색깔이 잘 나오게 될 것이다.



2012-02-15

FreeBSD 9.0 에서 pysqlite 설치

보통 pysqlite 를 설치하려면, pip install pysqlite 명령을 이용해서 쉽게 할 수 있다. 그러나, FreeBSD 9.0 에 pythonbrew를 이용해서 직접 python 을 설치한 경우에는 sqlite3 의 include, library 경로의 문제로 바로 되지 않는다. 이 때문에 직접 소스를 받아서, 올바른 경로를 설정하고 설치해주어야 한다.

설치 순서는 아래와 같다.

$ pkg_add -r sqlite3
$ wget http://pysqlite.googlecode.com/files/pysqlite-2.6.3.tar.gz
$ tar xvfz pysqlite-2.6.3.tar.gz
$ cd pysqlite-2.6.3
$ vi setup.cfg
include_dirs=/usr/local/include
library_dirs=/usr/local/lib
$ python setup.py install

2012-02-13

JRuby 1.6.6 인코딩 관련 버그

오랜만에 JRuby 를 PC에 설치해보고 테스트하려고 설치하고 간단히 jruby -v 명령을 실행하는데, 에러를 뱉어낸다.


뭔가 하고 찾아보니, 인코딩 관련 버그라고 한다.
관련 버그에 대한 내용은 http://jira.codehaus.org/browse/JRUBY-6398?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel#issue-tabs 에서 찾아볼 수 있으며, 1.6.7 에서는 해결될 것이라고 한다. 조만간 릴리즈한다고는 하던데~

그 전까지는 1.6.6 을 삭제하고 1.6.5 를 받아서 다시 설치해서 사용해야 할 듯 싶다.

참고로, Linux/FreeBSD 등에서는 기본 인코딩을 UTF-8을 사용하고 있는데, 별 문제 없이 잘 동작되는 것 같다.

2012-02-09

FreeBSD 에 Scala 설치하기

이번에는 FreeBSD에 Scala 설치하는 방법을 알아보자.
  • 우선, bash 와 openjdk 가 설치되어 있다고 가정한다. bash 의 Path는 /bin/bash 여야 한다.
  • 직접 다운로드 하여 설치하는 방법
    $ cd
    $ wget http://www.scala-lang.org/downloads/distrib/files/scala-2.9.1.final.tgz
    $ tar xvfz scala-2.9.1.final.tgz
    $ mv scala-2.9.1.final scala
    $ echo 'export PATH=$PATH:$HOME/scala/bin' >> ~/.profile
    $ source ~/.profile
    $ scala -version
    Scala code runner version 2.9.1.final -- Copyright 2002-2011, LAMP/EPFL
    
  • 패키지로 설치하기
    $ pkg_add -r scala
    $ scala -version
    Scala code runner version 2.9.1.final -- Copyright 2002-2011, LAMP/EPFL
    
    FreeBSD 9.0 에서 패키지로 설치하는 경우 다음과 같이 에러가 발생하지만, 먼저 openjdk 를 설치해 놓으면 문제 없이 실행된다. 찝찝한 생각이 들면 직접 다운로드하여 설치하자.
    Error: Unable to get ftp://ftp.freebsd.org/pub/FreeBSD/ports/i386/packages-9.0-release/All/jdk-1.6.0.3p4_27.tbz: File unavailable (e.g., file not found, no access)
    

FreeBSD 에서 Java 사용하기

기존에 Sun JDK 를 사용하려면 많이 불편했었지요. FreeBSD 에서도 OpenJDK 를 사용할 수 있습니다. 설치는 아래와 같이 간단합니다.

# pkg_add -r openjdk7
# mount -t fdescfs fdesc /dev/fd
# echo "fdesc   /dev/fd         fdescfs         rw      0       0" >> /etc/fstab

혹시라도 JAVA_HOME 을 잡아주어야 하는 상황이라면, 아래와 같이 잡아주시면 되겠습니다.

export JAVA_HOME="/usr/local/openjdk7"

이제 실행해보면

# java -version
openjdk version "1.7.0"
OpenJDK Runtime Environment (build 1.7.0-root_2011_12_03_14_00-b00)
OpenJDK Client VM (build 21.0-b17, mixed mode)

어때요? FreeBSD 에서도 OpenJDK 덕분에 Java 환경을 쉽게 구성할 수 있어서 좋네요~

2012-01-08

Cygwin 설치 및 환경구성

Windows 에 Cygwin 을 이용하면 Linux 와 같은 환경을 이용할 수 있어서 다행이다. 요즘에는 mintty 를 기본 터미널로 제공하여, 기존의 윈도우 cmd 의 제약을 제거하였고, apt-cyg 와 같은 툴을 설치하면, 패키지도 간단하게 설치/제거 할 수 있다. Windows 를 거의 Linux 로 사용할 수 있다. 아래와 같이 설치하면 그 맛을 볼 수 있을 것이다.
  • IE 또는 Explorer 에서 http://www.cygwin.com/setup.exe 실행
  • Choose A Download Source 에서 Install from Internet 선택
  • Select Root Install Directory 에서 기본값인 C:\cygwin 사용
  • Select Local Package Directory 에 C:\cygwin_pkg 입력
  • Select Your Internet Connection 에서 Direct Connection 선택
  • Choose A Download Site 에서 http://ftp.jaist.ac.jp 선택
  • Select Packages 에서 curl, wget, patch 선택 → 설치완료
  • 환경변수 PATH 에 C:\cygwin;C:\cygwin\bin 추가
  • 환경변수 CYGWIN_HOME 에 C:\cygwin 추가
  • 시작 → 실행 에서 mintty - 입력
  • .minttyrc 작성
    $ echo 'Font=NanumGothicCoding' >> ~/.minttyrc; \
    echo 'FontHeight=10' >> ~/.minttyrc; \
    echo 'CursorType=Block' >> ~/.minttyrc
    
  • apt-cyg 설치 (Patch file : enable-execute.zip)
    $ curl http://apt-cyg.googlecode.com/svn/trunk/apt-cyg > apt-cyg; \
    curl http://gauryan.ivyro.net/wiki/lib/exe/fetch.php?media=util:cygwin:enable-execute.zip > enable-execute.patch; \
    patch apt-cyg enable-execute.patch; chmod +x apt-cyg; \
    mv apt-cyg /usr/local/bin/; rm -rf enable-execute.patch; \
    apt-cyg update -m http://ftp.jaist.ac.jp/pub/cygwin
    
  • 기본 패키지 설치
    $ apt-cyg install openssh lftp python autoconf bison libtool gcc gcc-core gdb make \
    git libgdbm libgdbm-devel zlib zlib-devel openssl-devel curl rebase libncurses-devel \
    ncurses ncursesw sqlite3 libsqlite3-devel openssl openssl-devel bzip2 libbz2-devel \
    gdbm cygwin coreutils gnupg vim ctags readline wget