2012. 7. 27. 19:20 IT


서버에서 로그를 지우고 싶은데,,, 쉘 스크립트를 짜고 싶지 않았다. 

그래서 그루비 쉘로... ;;



로그 파일을 대상으로 lastModified date가 오늘보다 1달 전인 것들을 

월별 디렉토리에 백업해주는 기능.  cron에 등록해놓으면 편할듯. 


cf) TimeCategory 기능 같은게 편하다... 



import groovy.time.TimeCategory;

def backup( def searchPath, def backupdate, def postfix = ~/.*log$/ ){

        def today = new Date();

        def loglist = new File(searchPath).listFiles().grep(postfix);

        def cnt = 0;

        loglist.each{ file ->

                def dt = new Date( file.lastModified())

                        if( backupdate > dt ){

                                def backupdir = searchPath + "/" + dt.format("yyyyMM")

                                new File(backupdir).mkdir();

                                "mv ${searchPath}/${file.name}  ${backupdir}".execute();

                                cnt++;

                       }

        }

        println " ${cnt} files moved "

}

def log_dir_list = ["/software/log", "/software/log/access" ];

def backupdate;

use(TimeCategory) {

        backupdate = new Date()  - 1.month

}

println "[ LOG Backup ] log < " + backupdate.format("yyyyMMdd") + " (lastModified date)"

log_dir_list.each{

        print "\t log backup target :  ${it}  -> "

        backup(it, backupdate);

}


posted by smplnote
2012. 7. 27. 11:29 IT

a mircro Groovy web framework 


https://github.com/bleedingwolf/Ratpack 

License : Apache License, Version 2.0


groovy 하면 RoR에 영감을 얻었다는 grails가 떠오르지만.. 마찬가지로  Sinatra 에 영향을 받은 Ratpack 이라는 경량 web framework이 있습니다. 


물론 아직 beta 수준이라서 어디 써먹기에는 참 애매하지만... 하여튼 잠깐의 틈을 내서  study. 


홈페이지에 간단하게 설치절차가 나와있지만,

eclipse 기반으로 작업할때 어떻게 하면 좋은지에 대한 경험을 기록하고자 합니다. 




- Requirements

jdk 1.5 이상?

groovy 1.7.1+

gradle

eclipse , groovy eclipse plugin (option)


- ratpack build 

1. 다운로드 받은 ratpack project를 새로운 eclipse groovy project로 생성하고나서 복사합니다.

2. gradle buildDistro 를 실행

cf) [gradle] Using Gradle from Ant (via gradlew) 글을 이용하여 ant build 파일을 이용해 돌릴 수 도 있습니다. 

3. build/libs 위치에 library 들이 모두 생성되었는지 확인되면 OK.


- make ratpack project 

1. eclipse에서 dynamic web project로 HelloRatpack 을 생성.

2. $webapp/WEB-INF/lib  에 위에서 생성한 build/libs 의 모든 jar 를 복사. 
cf) except servlet-*.jar , jetty-*.jar

3. web.xml 에 servlet 을 등록 

  <servlet>

    <servlet-name>Ratpack</servlet-name>

    <servlet-class>com.bleedingwolf.ratpack.RatpackServlet</servlet-class>
    <init-param>
      <param-name>app-script-filename</param-name>
      <param-value>hello.groovy</param-value><!--  WEB-INF/lib/xxx.groovy  -->
    </init-param>
  </servlet>
  <servlet-mapping>
    <servlet-name>Ratpack</servlet-name>
    <url-pattern>*</url-pattern>
  </servlet-mapping>
이경우 app-script 파일의 위치는 WEB-INF/lib  으로 고정됨. 

따라서 원하는 곳으로 수정하려면... RatpackServlet 을 상속받아서 하나 만듭니다.
ex) 
class RatpackExtendServlet extends RatpackServlet{
void init() {
if(app == null) {
def appScriptName = getServletConfig().getInitParameter("app-script-filename")
 def fullScriptPath = getServletContext().getRealPath("WEB-INF/${appScriptName}")
app = new RatpackApp()
app.prepareScriptForExecutionOnApp( fullScriptPath )
}
mimetypesFileTypeMap.addMimeTypes(this.class.getResourceAsStream('mime.types').text)
}
  }
}
cf) mime.types 파일도 위 클래스와 같은 위치에 있어야 합니다. (없으면 에러 나죠.. )
cf) 새로 만들었으면 web.xml 의 servlet-class 이름도 바꿔주어야 합니다. 이제부터는 application groovy 파일을 WEB-INF 아래부터 원하는 경로로 배치하면 됩니다. ( WEB-INF 밑으로 잡은 이유는... 웹에서 직접 접근이 안되는 곳이니까요. )
    <servlet-class>com.yourcompany.yourpkgname.RatpackExtendServlet</servlet-class>
      <param-value>ratpack/hello.groovy</param-value><!--  WEB-INF/ratpack/hello.groovy  -->
4.  write your application 
ex) /WEB-INF/ratpack/hello.groovy
set 'port', 8282  // jetty 에서 쓸때만 적용됨. 
get("/") {
    def ua = headers['user-agent']
    "Hello, World! ${ua}"
}
get("/person/:personid") { // path parameter sample
"This is the page for person ${urlparams.personid}"
}
get("/person"){ // paramter sample
"this is your param ${params}"
}

hello.groovy  파일이 바로 ratapck application



5. 배포 및 테스트 (war로 만들어도 됨.)
ant task
<target name="ratpack-deploy">
<war destfile="dist/${ant.project.name}.war" webxml="${web.dir}/WEB-INF/web.xml">
<fileset dir="${web.dir}">
<exclude name="**/*test/**"/>
</fileset>
</war>
</target>

6. 확인.
앗 썰렁해. 

기타. 에러페이지는 다음과 같습니다.  ( def x = 1 /0 ; 을 실행하게 했음. ) 



- 평가
GOOD : 
쉽고 가볍다.  (Grails는 Spring Hibernate 같은 거인들 위에 올라타 앉아서 보기엔 쉽지만 까보면 복잡하다는 단점이 있다. 사실 그 모든기능이 필요하진 않을수도 있는데.)
간단한 REST SERVICE 만들때 편하겠다. 
앞으로 성장 가능성이 있다. 

BAD : 
URL 바인딩이 늘어날 경우 한개 파일에 들어가 복잡해진다. (  당연히 분리/모듈화가 진행될듯) 
아직 베타라서 장난감 수준이 아닌가 싶다. Sinatra 수준이 되려면 좀 걸리겠다. 
충분히 성장할 수 있을까? 글쎄... ratpack 자체는 아닐 수 있지만 경량화는 추세인듯. 

ANOTHER THINKING:
최근의 기술 경향은 유연하고 확장성 있는 범용 시스템을 제공하기보다는,
특정 도메인의 필요에 따라 적절한 기능과 성능을 제공하는 SMART한 시스템을 추구하는 경향이 보인다. 
Ratpack도 그렇고, 이벤트방식의 vert.x 도 그렇고... 
기술 트렌드에 대해 계속 관심을 가져야 겠다는 생각을.... 

참고자료 : 
Presentation : Ratpack ( Classy and Compact Groovy WebApps ) - JamesWilliams
동영상 : http://www.infoq.com/presentation/Ratpack 

posted by smplnote
2012. 7. 18. 18:12 IT

배경 : 

1. shell script 짜는 것보다 자바 코딩이 더 쉽다. (사실은 그루비가.. )

2. 컴파일은 하기 싫다.



준비사항 : 

1. jdk가 설치되어야함 (대부분 있음)

2. groovy-all-X.X.X.jar 를 준비할것

3. groovy 문법을 모르면 java 문법이라도 알고 있어야 함. 



방법 : 

1. 대신 실행해줄 쉘을 만든다. 

touch groovy.sh

#/bin/sh

JAVA_HOME=/usr/local/java/jdk1.6.0_30

CP=/groovy/groovy-all-2.0.0.jar:.

echo $1 run

$JAVA_HOME/bin/java -classpath $CP groovy.ui.GroovyMain  $1

# 실행모드 변경

chmod 744 groovy.sh 


2. groovy example

# ls 명령을 실행한 결과를 출력해주는...  

touch hello.groovy

import java.util.*;

class HelloGroovy{

        public static void main(String[] args) {

                println args.length

                def result = "ls".execute().text;

                println result

        }

}


주의사항 :

1. 지나치게 중독되지 말 것.

2. 뭔가 안될 경우 classpath 설정을 확인할 것 


posted by smplnote
2012. 4. 16. 08:54 IT

- WHY?


gradle의 멀티 플로젝트 설정 가이드를 따라가면 늘 최상위 경로에 settings.gradle 을 구성하게된다.

당연히 전체 프로젝트에 대해 계층적인 구조를 가져가는 것이 상식이다.

하지만 eclipse 관점에서만 볼때, 모든 프로젝트는 항상 같은 레벨에서 관리가 된다. 

때문에 상위 경로에서 멀티 프로젝트를 관리하는 개념을 도입할때 어떻게 해야 할지 고민하게 된다.

eclipse project 단위로 형상을 관리할 경우, 상위 경로에 있는 멀티프로젝트 설정파일은 버전관리 대상에서 제외 되어 버리기 때문이다. 


물론... gradle user guide에서 설명을 안했을 뿐이지, 멀티프로젝트 관리를 반드시 계층적으로 해야 하는 것은 아니다. 


다음과 같이 설정할 수 있다.


- project-emma : multi-project관리용 root project

   settings.gradle

   build.gradle

- project-navi : child project 1. Type : java library

   build.gradle

- project-bovary : child project 2. Type: web application. it depends on project-navi

   build.gradle


[$project-emma/settings.gradle]

// define projects

include 'navi', 'bovary'


// define sub-projects location

project(':navi').projectDir = new File(rootDir, '../project-navi')

project(':bovary').projectDir = new File(rootDir, '../project-bovary')

[$project-emma/build.gradle]
// define subproject configuration
subprojects{
// define plugins
apply plugin: 'java'
repositories {
mavenCentral()
}
// set compile encoding
[compileJava, compileTestJava]*.options*.encoding = 'UTF-8'
}
[$project-navi/build.gradle]
jar {
destinationDir = file("dist"); // 별도 위치에 저장하고 싶을 경우에만 명시.
}
[$project-bovari/build.gradle]
apply plugin: 'war' // compile -> war -> assemble
dependencies {
compile project(':navi') // navi project에 대한 의존성을 명시.
 }

당연하지만, 멀티프로젝트 관리용으로 빈 프로젝트를 만들어야 할 필요는 없다. (navi의 하위 경로에 지정해도 가능하다는 말임) 앞으로도 유용하게 사용할 수 있을 듯 싶음. 


posted by smplnote
2012. 4. 12. 09:14 IT

[WHY] 

eclipse에서 grails plugin을 설치하지 않고도  ant  view에서 편리하게 각각의 gradle 명령을 사용하고 싶었음.


참고한 reference를 보면 gradle이 설치되어 있고 GRADLE_HOME 환경변수가 설정된 상태를 전제로 한다.


앞글에서 설명했던 gradlew 를 이용하면 미리 gradle binary들 설치하고 환경변수를 설정하지 않고도 빌드실행이 가능해진다. 




[Prerequisite]

== gradle wrapper생성을 통해 만들어진 파일들 (3)

gradlew.bat or gradlew

gradle/wrapper/gradle-wrapper.jar

gradle/wrapper/gradle-wrapper.properties


== 실제 build를 수행할 build 파일

build.gradle

// this is sample...

apply plugin: 'java'

repositories {

mavenCentral()

}

[compileJava, compileTestJava]*.options*.encoding = 'UTF-8'

jar {

destinationDir = file("dist")

}


== gradle을 수행할 ant build wrapper 

build.xml

<project name="build-gradle"  default="help"  basedir=".">

<property environment="env" />

<property name="gradle.executable" location="./gradlew.bat" /><!-- for window -->

<target name="help">

<exec executable="${gradle.executable}" dir=".">

<arg value="tasks" />

</exec>

</target>

<target name="build"> <!-- 모든 포함된 project의 build task를 실행 -->

<exec executable="${gradle.executable}" dir=".">

<arg value="build" />

</exec>

</target>

<!--

<target name="subproject-build" description=" 특정 subproject의 build task만 실행시키고 싶을때 사용">

<exec executable="${gradle.executable}" dir=".">

<arg value=":common:build" />

</exec>

</target>

-->

</project>

이제, 필요할 때마다 ant target을 추가하면된다. 



reference : https://gist.github.com/735788 

posted by smplnote
2012. 4. 12. 08:47 IT

maven이든 ant든 다른 어떤 툴이든, 우선 많은 용량의 binary를 다운 받아서 작업을 해야한다.

plugin으로 제공되는 경우도 용량 자체는 크게 다르지 않거니와 행여나 버전이 낮아 쓰기 불편한 경우도 있어

새로 시작할 때마다 부담이 되는 경우가 있다. 

gradle은 eclipse 지원을 일찍 포기한 것으로 보이는데, 상위버전에서 제공되는 명령이 제대로 먹히지 않는다.

(물론 별도의 GRADLE_HOME 을 설정할 수 있도록 preference 에서 설정화면을 제공하고 있다.)


gradle wrapper의 핵심 idea는  gradle을 설치하지 않은 상태에서도 gradle을 사용할 수 있도록 제공하는 기능이다.


물론 아무것도 없이 실행되는 것은 아니다. 

단지 최소한의 쉘스크립트와 바이너리를 통해 이 작업을 수행할 수 있다. 



먼저, gradle wrapper 스크립트를 만들기 위해서는 당연하게도 gradle이 설치되어 있는 곳에서 시작하여야 한다.

build.gradle을 다음과 같이 작성한다.

// 현재 gradle 버전은'1.0-milestone-9' 이다. 

task wrapper(type: Wrapper) {

gradleVersion = '1.0-milestone-9'

}

defaultTasks 'wrapper'


실행이 되면

쉘파일과 (윈도우용인 gradlew.bat , *nix용인  gradlew)

실행을 위한 jar (gradle/wrapper/gradle-wrapper.jar )

설정정보를 포함한 property ((gradle/wrapper/gradle-wrapper.properties)

가 만들어진다. 

(cf. 새 버전이 나올 경우 wrapper task를 실행하여 새로 생성하거나 단순히 gradle-wrapper.properties 에서 binary를 다운 받을 경로를 변경해주면 된다.)


이제 준비완료.


gradlew가 해주는 역할은 단순하다.

gradle 배포본이 있는지 체크해서 없으면 gradle-wrapper.properties에 정의되어 있는 경로에서 다운로드를 받고,

있을 경우에는 모든 argument를 gradle 에게 전달한다. 


현재 gradle-1.0-milestone-9-bin.zip의 크기는 29M다. ( gradle-wrapper.jar 는 39K )


기본으로 설치되는 위치는 

$USER_HOME/.gradle/wrapper/dists


이제 신규 프로젝트에

저 파일을 같이 포함하면 준비완료.

gradlew 를 실행하면 된다. 



reference : http://www.gradle.org/docs/current/userguide/gradle_wrapper.html


posted by smplnote
2012. 1. 28. 16:56 IT
Cédric Champeau 가 만든 groovy 2.0에서 선보일 static type checking 에 대한 슬라이드 bit.ly/Ays6Dl 를 요약
 
아직 2.0은 진행중이지만 슬라이드를 통해 현재상태를 보여준다

STC의 목적은 빠른 오류 검출이다. : 동적 언어의 약점으로 지적되는  실행시점 오류를 컴파일 시점에 미리 체크하여 오류를 최소화하고자함. 그외 타이핑 오류, 할당오류등을 사전 검출

현재 제안중인 방식은
Class나 method에 TypeChecked annotation으로 지정하고 하는 것이다
(사실 완전히 정적타입체킹을 기본으로 정한다면 동적언어의 장점 전부를 포기하게 된다.)

물론 TypeChecking은 정적 컴파일을 의미하진 않는다. 또한 모든 groovy언어의 기능에 대해 검사해주지는 않는다
Metaclass변경이나 use, binding 사용성의 검사는 지원하지 못한다. 

Static compile의 목적은 성능, 타입안전성이다 
STC의 장점에 byte code size를 줄일수 있고 성능도 개선할수 있다 또한 메타클래스 변경으로 인한 사이드이펙트도 없다. 대신 groovy가 지닌  dynamic한 장점을 잃게된다 (dynamic method dispatch,meta class etc)

그래서 여전히 선택사항으로 남을것으로 보인다
CompileStatic annotation을 사용할 예정이다

앞으로 좀더 효과적으로 처리하기 위한 방법이나 개발중인 코드의 버그수정,개선이 진행되겠지만 groovy라는 매력적인 도구를 좀더 선택적으로 사용할수 있도록 하는 노력이 활발히 진행되고 있어 내심 반갑다.

이왕이면 어노테이션 수준외에도 보다 상위의 컴파일 옵션 상에서 정적타입체킹이나 정적컴파일을 선택할 수 있도록 해주는건 어떨까 싶다.










 
posted by smplnote
2012. 1. 18. 15:17 IT

최근 chef에 대해 이야기를 들었다. 
그게 뭐지? puppet은 들어봤지만.

puppet, chef 는 인프라 구성 자동화 도구라고 하면 되겠다.   (cfengine, slack 이란것도 있다.  )

system 설정 자동화 listup : Bcfg2, cfengine, Chef, Puppet, SmartFrog
app 서비스 설정 자동화 : Capistrano, Fabric, Func, ControlTier, Glue 


puppet : http://puppetlabs.com/
Enterprise버전은 상용이나 10개 노드 이하는 Free 
고유 DSL 제공 
사례 :  징가, 트위터 
단점 : 새로 익혀야 하는 DSL

chef : APL
http://wiki.opscode.com/display/chef/Home
ruby 코드 
참고 :  https://www.ibm.com/developerworks/mydeveloperworks/blogs/9e635b49-09e9-4c23-8999-a4d461aeace2/entry/215
사례 :  RightScal

glue :  deployment and monitoring automation platform APL2.0
https://github.com/linkedin/glu 
소개글 :  http://linkedin.github.com/glu/slides/glu-tech-talk-201107.pdf 
-> 여기서 glue와 puppet을 비교하면서 puppet은 machine infra에 대한 configuration, glue는 dynamic application 에 대한 provisioning 에 좋다고 평한다. (물론 glue를 만든 사람이 하는 말이다.)
재미있는건 ZooKeeper를 이용하여 정보를 저장하고 이벤트 통지 등을 관리 한다는것.. 
 groovy 코드 
 
slack : 구글에서 쓴다고 하네요 

cf) 분산시스템 모니터링은  nagios, opsview, etc 
posted by smplnote
2012. 1. 18. 08:49 IT

블로깅 때문에 검색하다가 우연히 발견. 

http://grailsboston.com  

무려 h2 database로 운영한다 ㅎㅎ

플러그인 목록을 보면서 이것저것 생각하게 되는데 ( http://grailsboston.com/twittersearcher/plugin/list  )

(정말 작은 커뮤니티를 가지고 많이도 만들었네... )
 
posted by smplnote
2012. 1. 18. 08:20 IT

Jump Into Grails 2.0 스크린캐스트 ( http://grails.org/screencast/show/31 ) 를 따라해봄
 

1. 플러그인 추가
 

conf/BuildConfig.groovy
---------------------------
    plugins {
                ... 
runtime ":spring-security-core:1.2.7"
                ... 
    }

2. compile을 이용하여 플러그인 자동설치 
>grails compile

실행이 잘되면 "s2-quickstart" script를 실행하라는 메세지가 나온다. 

cf) 여기서 제대로 안되고 다운로드를 실패하면 바로 이전 글 "Grails에서 플러그인 설치실패" http://pffm.tistory.com/273  내용을 참고바람.

3. spring security 초기화 및 도메인 클래스 생성 
다음과 같이 실행하여 초기화 및 도메인 클래스를 생성하는 작업을 수행한다. 
>grails s2-quickstart org.example.demo User Role
 
작업이 끝나면 conf/Config.groovy 에 수정이 반영되었으니 확인하라는 메세지가 나온다. 

하단에 세줄이 추가된 것을 알 수 있다. 
conf/Config.groovy
 
---------------------------
grails.plugins.springsecurity.userLookup.userDomainClassName = 'org.example.demo.User'
grails.plugins.springsecurity.userLookup.authorityJoinClassName = 'org.example.demo.UserRole'
grails.plugins.springsecurity.authority.className = 'org.example.demo.Role'

여기에 한줄 더 추가 입력 (로긴 성공시 기본으로 갈 Url)
grails.plugins.springsecurity.successHandler.defaultTargetUrl = '/home' 


4. 테스트용 컨트롤러 생성
이제  home 과  publiccontroller 를 만들고.. 
>grails create-controller home
>grails create-controller  public


5. 각각 컨트롤러의 접근 권한을 다르게 부여

- 접근 권한이 불필요한곳
class PublicController{
    def index() { render 'public' }

- 명시적으로 접근권한 부여 (Secured annotation 사용)
import grails.plugins.springsecurity.Secured
@Secured(['ROLE_USER'])
class HomeController {
    def index() { render 'home' }

    @Secured(['ROLE_ADMIN'])
    def adminOnly() { render 'admin' }
}


6. 부트스트랩에 USER, ROLE  사전 등록작업  
 conf/BootStrap.groovy
--------------------------
 import org.example.demo.* // import 문 추가 
...
    def init = { servletContext ->   // init 안에 추가 
        // admin Role과 userRole 을 생성 
def adminRole = Role.findByAuthority('ROLE_ADMIN') ?: new Role(authority: 'ROLE_ADMIN').save(failOnError: true)
def userRole = Role.findByAuthority('ROLE_USER') ?: new Role(authority: 'ROLE_USER').save(failOnError: true)

        // user role을 가진 bobby와 admini role을 가진 adminuser를 생성  
def user1 = User.findByUsername('bobby') ?: new User(username: 'bobby', enabled:true, password: 'pass').save(failOnError: true)
if(!user1.authorities.contains(userRole)){
UserRole.create user1, userRole, true
}
def user2 = User.findByUsername('admin') ?: new User(username: 'adminuser', enabled:true, password: 'pass').save(failOnError: true)
        if(!user2.authorities.contains(userRole)){ UserRole.create user2, userRole, true }
        if(!user2.authorities.contains(adminRole)){ UserRole.create user2, adminRole, true } 
 
 7. 확인작업
>grails run-app
 | Server running. Browse to http://localhost:8080/demo

1) http://localhost:8080/demo/public 은 로그인 없이 접근 OK
2) http://localhost:8080/demo/home 은 로그린 화면으로 redirection
3) bobby 로그인 후 http://localhost:8080/demo/home/index 접근 OK
4) bobby 로그인 후 http://localhost:8080/demo/home/adminOnly 접근 불가
5) adminuser 로그인 후  http://localhost:8080/demo/home/adminOnly 접근 OK

8. User에 필드 추가하기 (option)
도메인 클래스만 손보면 알아서 추가해줌... (당연한거잖아.. )

ex) 사용자 정보에 nick 을 추가한다면.. 
User.groovy 
----------------
String nick; // 사용자 정보에 nick 필드 추가 

// 참고로 마지막  encodePassword 메소드를 보면 password 필드를 암호화처리해주고 있음을 알 수 있다.
 
물론..  BootStrap.groovy 안에서도 유저 생성할때 nick 을 추가해주세요. 
... new User(username: 'nickuser', enabled:true, password: 'pass', nick: 'blah blah')  

cf) BootStrap 파일을 수정할 경우에는 grails를 재시작 해주어야 합니다. 당연하긴 하지만.. 뭔가 BootStrap도 재기동 할수 있는 기능을 제공해 해줬으면... 


기타.

1. 한글 문제
한글을 BootStrap.groovy 에 넣어보았는데 안되더라. 원인은 .groovy 파일을 UTF-8 로 저장하지 않아서.  
혹시 이걸로 해결이 안된다면 다음 링크를 참조하세요.  
http://jgrasstechtips.blogspot.com/2008/05/how-to-learn-new-groovy-friend-italian.html 
comment를 보면 JAVA_OPTS 에 groovy.source.encoding 을 전달하라는 이야기가 나온다. 

2.  LDAP, ActiveDirectory 연계는?
찾아보니.. 
spring-security-ldap 플러그인이 제공된다. 


마치며... 
 
이걸 더 진행시킬 기회가 올지는 잘 모르겠다. 아마 힘들듯... 

비교할 자리는 아니지만...

grails와 playframework을 다시 생각해보면 grails는 위와같이 spring 위에 올라탄 느낌을 편리하게 제공해준다.

일단 groovy에 익숙해진다면 플러그인을 직접 만드는 것도 그리 어려워보이지 않는다.

하지만 편리함 뒤에는 내부의 복잡함이 지나치게 숨겨진게 아닌가 싶기도 하다.
 
반면 playframework은 java 고유의 호흡법을 유지할 수 있다는 장점과 컴팩트함을 장점으로 가지는 것 같다. 

물론 다시 안을 들여다 보게 된다면 SCALA의 무덤에 기겁을 할 지도 모른다... (뭘 그리 겁내냐!) 


아무튼...  개인 신변이 조금 정리되어 이제 다시 링크... 
posted by smplnote