2014년 5월 16일 금요일

IT - EIP


최근(2014) IT 프로젝트에서 오픈소스 이용증가
- 최근 많이 사용하는 오픈소스
  1. NoSQL: MongoDB, CouchDB, Hbase, Infinispan
  2. Messaging: ActiveMQ, ZeroMQ, JMS, RabbitMQ, AMQP
  3. SNS: twitter, facebook
  4. Framework: String, Vert.x, iBatis/MyBatis
  5. Networking: MINA 1/2, Netty, MQTT, SIP
  6. Cloud: Amazon, Google

- 대용량 데이터의 실시간 처리/분석, 고속으로 다양한 시스템과 프로토콜로 메시지 전달
--> EIP(Enterprise Integration Patterns) 을 기반으로 오픈 소스 통합 프레임워크 ==> Apache Camel

EIP: http://www.enterpriseintegrationpatterns.com/

* Apache Camel
  - Concise Application Messageing Exchange Language
  - Apache camel is a powerful open source integration framework based on known Enterprise Integration Patterns

1)Design Pattern: Adapter, Facade, Proxy, Observer....
2)Enterprise Application Architecture: Gateway, MVC, Registry, Repository, Service Layer...
3)EIP: Aggregator, Content Filter, Recipient List, Request/Relay, Publish-Subscribe Channel


2014년 5월 6일 화요일

2014년 4월 8일 화요일

Let it go

Let It Go


The snow glows white on the mountain tonight
오늘 밤 산 위에 눈이 하얗게 빛나
Not a footprint to be seen
발자국조차 보이지 않아
A kingdom of isolation
고립의 왕국
And it looks like I'm the queen
내가 그곳 여왕인거 같네

The wind is howling like this swirling storm inside
지금 내 맘 속의 돌풍처럼 바람은 휘몰아치고
Couldn't keep it in, heaven knows I've tried
숨길수 없었어, 하늘만이 내 노력을 알겠지

Don't let them in, don't let them see
그들을 들여보내지 마, 그들에게 보이지 마
Be the good girl you always have to be
항상 착한 소녀 모습을 보여주렴
Conceal, don't feel, don't let them know
숨기고, 느끼려 하지 말고, 그들이 알게 하지 마
Well, now they know
하지만 이젠 알아버렸네

Let it go, let it go
다잊어, 다잊어
Can't hold it back anymore
더이상 감추고 있을수 없어
Let it go, let it go
다잊어, 다잊어
Turn away and slam the door
뒤돌아서서 문을 닫아버려
I don't care what they're going to say
그들이 뭐라하던 상관 안할거야
Let the storm rage on
폭풍아 계속 몰아쳐라
The cold never bothered me anyway
어차피 추위는 날 괴롭히지 못하니까

It's funny how some distance makes everything seem small
거릴 두고 보니까 모든 것들이 사소해보여 우스워
And the fears that once controlled me can't get to me at all
날 구속했던 두려움도 이젠 날 잡을 수가 없네
It's time to see what I can do
내가 뭘 할수 있는지 알아볼거야
To test the limits and break through
한계를 찾고 돌파하기 위해
No right, no wrong, no rules for me
옳고그름, 규칙따윈 없어
I'm free
난 이제 자유야

Let it go, let it go
다잊어, 다잊어
I am one with the wind and sky
나는 바람과 하늘과 하나야
Let it go, let it go
다잊어, 다잊어
You'll never see me cry
이제 절대 울지 않을거야
Here I stand and here I'll stay
우뚝 여기 서서 바로 이곳에 자리잡을래
Let the storm rage on
폭풍아 계속 몰아쳐라

My power flurries through the air into the ground
내 힘이 하늘에서 땅까지 흩날리네
My soul is spiraling in frozen fractals all around
내 영혼은 눈모양으로 용솟음치고
And one thought crystallizes like an icy blast
각각의 생각는 빠르게 얼음으로 결정화되네
I'm never going back, the past is in the past
절대 안돌아갈거야, 과거는 과거일 뿐

Let it go, let it go
다잊어, 다잊어
And I'll rise like the break of dawn
새벽의 여명처럼 솟아오를거야
Let it go, let it go
다잊어, 다잊어
That perfect girl is gone
착하기만 했던 소녀는 이제 없어
Here I stand in the light of day
여기 햇살 가운데 우뚝 서있어
Let the storm rage on
폭풍아 계속 몰아쳐라
The cold never bothered me anyway
어차피 추위는 날 괴롭히지 못하니까

2014년 3월 24일 월요일

First English Class

Hello! Welcome to the First Course English Class.
This is Silvie Marquez. Ms. Marquez is your English Teacher.
Have fun with Ms. Marquez and the other students in the English class.
Click Next to continue.

In this unit you will learn
classroom directions.
the letters of the alphabet.
how to introduce yourself.
how to write your name on a form.

This is Miguel Morales.
Miguel works at a café.
He is from Mexico.
Miguel speaks a little English.

Miguel does not understand.

Ryan is Miguel's boss.
Ryan says, "Please learn more English."

Miguel is at the adult school.
He is in English class.
He is learning new English words.
Click Next to learn new English words.

Classroom Directions - Learn new words
Click new words to see and hear a word. Click Listen to hear the word again.
ask
listen
point
read
say
spell
type
watch
write

Click Listen. Choose the matching picture. Then click Check.

You say, "Nice to meet you, too"
Now let's watch some people at a party'
Listen to the introductions
Now you ask me
Point to the letter you hear
Pleas spell that
Write your classmates' names on this paper

Watch the students practice introductions in the first English class.

Let me remember!
First and Last Name
practice introduction!

Hello. I'm Natasha.
Hi, I'm Debbie.
Nice to meet you.
Nice to meet you, too.

Listen to the letters of the alphabet

Click Listen. Click the 5 letters you hear. Click Check.
Watch Miguel practice spelling the words on the menu.
pie, coffee, hamburger

Watch Ms. Marquez ask a student his first and last name.

What's your first name?
What's your last name?

Click Listen to hear the names. Read the two forms.
Click the correct form. Then click Check.

Watch Ramon in the café. He is looking for Miguel.


Click play to watch the video again. Read the 2 sentences.
Click the correct sentence. Then click Check.

Miguel is happy in his first English class. Watch and listen.


I can introduce myself
I can say the letters of the alphabet
I can spell my first and last name in English

2013년 12월 27일 금요일

Reactor 패턴

Reactor pattern

reactor pattern 에 대해서 알아보자. Node.js 에서 Event-driven network programming 이 유명해 졌는데, 찾아보니 이미 여러개의 framework 들이 존재했다. 그런데 이런 pattern 을 Reactor pattern 이라고 해서, Reactor pattern 에 대해 찾아보고 설명을 몇자 적어놓는다.

정의
일단 정의를 보면,

서비스를 처리하는 쪽(서버라고 생각하면 될 듯 하다.)으로 동시에 요청이 들어온 service request 를 처리하기 위한 event handling pattern 이라고 한다.
이 때 서버에서는 들어온 요청들을 분류(demux)해서 이 녀석들을 적절한 request handler 에 dispatch 한다. 이 때 동시에 여러개의 요청을 처리하는 것이 아니라, 하나에 대한 처리를 끝내고 그 다음 것을 처리하는 방식으로 하나씩 처리한다.
The reactor design pattern is an event handling pattern for handling service requests delivered concurrently to a service handler by one or more inputs. The service handler then demultiplexes the incoming requests and dispatches them synchronously to the associated request handlers.[ref. 1]
이런 Reactor pattern 을 이용한 여러 web server 가 있는데, Node.js 도 그중에 하나이다. 이외에 여러 개들이 있는데, ref. 1에서 확인하자.

기존의 Design


from : ref. 3
기존의 ThreadPool 을 이용하는 경우를 고려 해 보자.
  1. ServerSocket 으로 request A 가 들어오면 Thread 를 할당 해 준다.
  2. 그럼 이 Thread 는 그 socket 을 가지고 read, write 작업(IO) 등을 할 것이다. 그런데 이 와중에 ServerSocket 에 request B가 들어오면, context switching 이 일어난다.
  3. 그럼 새로 들어온 request 에 대해 Thread 를 배분해주고, 또 이 socket 으로 read()/write() 등의 작업을 할 것이다.
  4. 그러면서 A 의 작업을 하기 위해 중간에 다시 context switching 하고, 그러다 다시 B 작업하려고 context switching 하면서 IO 작업을 모두 처리할 것이다.

그런데 reqest A 가 들어와서 Thread 가 만들어진 후에 또다른 reqeust 가 들어오지 않는 상황을 가정 해 보자.

그럼 request A 를 처리하다가 중간에 request 가 들어왔는지 확인하기 위해 context switching 을 해야 한다. 만약 들어왔으면 Thread 를 만들어주고, 그렇지 않으면 다시 request A 를 처리하러 돌아갈 것이다.

그런데 이렇게 확인하는 시간이 아깝다.

codes

class Server implements Runnable {
   public void run() {
       try {
           ServerSocket ss = new ServerSocket(PORT);
           while (!Thread.interrupted())
               new Thread(new Handler(ss.accept())).start();
        // or, single-threaded, or a thread pool
       } catch (IOException ex) { /* ... */ }
   }
   static class Handler implements Runnable {
       final Socket socket;
       Handler(Socket s) { socket = s; }
       public void run() {
           try {
               byte[] input = new byte[MAX_INPUT];
               socket.getInputStream().read(input);
               byte[] output = process(input);
               socket.getOutputStream().write(output);
           } catch (IOException ex) { /* ... */ }
       }
       private byte[] process(byte[] cmd) { /* ... */ }
    }
}

Reactor Pattern

그래서 나온 것이 Reactor Pattern 이라고 한다.[ref. 2]

Reactor 는 우리나라 말로 하면 "반응로" 이다. 그러니까 event 가 발생하면 반응하는 녀석이다.

Reactor 는 event 가 발생하기를 기다리고, event 가 발생하면 Reactor 는 이 event 를 적절한 event handler 에 넘겨주는 역할을 하게 된다.


********

여기서 부터는 ref. 2 의 내용을 번역하고 각색했다.

ref. 3 에서는 java code 로 Reactor pattern 을 설명해 주는 ppt 이다. ref. 3 의 코드를 바탕으로 설명할 것이다. 참고로 이 java code 는 non blocking io library 인 nio package 를 사용한다.

Reactor pattern 에 중요한 2가지 요소가 있다. 하나는 event 를 받고 전달해주는 Reactor 와 다른 하나는 Reactor 가 보낸 event 를 실제로 처리하는 Handler 가 있다. ref. 3 에서 UI programming 과 연관지어서 설명한 것을 인용하면,
  • Reactor 는 UI 를 처리하는 thread 이고,
  • Handlers 는 UI의 ActionListeners 라고 생각하면 된다.
다시 얘기하면,
  • Reactor 로 event 가 들어오면 알맞는 handler 로 dispatch 주는 역할을 담당하고, 
  • Handler 는 이 dispatch 된 event 를 받아서 처리하는 역할을 하게 된다.
이 추상적인 이야기를 ref. 3 에 있는 java 코드를 통해서 확인하자.

Sigle-threaded codes
먼저, 하나의 thread 를 사용해서 동작하는 것만 고려하자.

소스코드를 이해하기 위해서 몇가지 class 에 대한 이해를 해야 한다.
  1. Channels :non-blocking read 를 지원해 주는 녀석이다.
  2. Selector : Selector 는 어느 channel set 이 IO event 를 가지고 있는지를 알려준다. Selector.select() 는 I/O 가 발생한 channel 을 return 해주게 되는데, return 할 channel 이 없다면 계속 기다리게 된다.(block). 이 block 된 녀석을 바로 return 시켜주는 녀석이 Selector.wakeup() 이다.
    Selector.selectedKeys() 는 Selection Key 를 return 해 준다. Reactor 는 이 Selection Key 를 보고 어떤 handler 로 넘겨줄 지를 결정한다.
  3. SelectionKeys : SelectionKey 는 Channel 과 Handler 에 대한 reference 를 가지고 있다. ServerSocketChannel 에 selector 를 등록하면 key 를 준다. 이 key 가 SelectionKey 이다.
  4. Acceptor : Acceptor 는 handler 다. connection 을 만들기 위한 special handler 이다.


final class Handler implements Runnable {
    final SocketChannel socket;
    final SelectionKey sk;
    ...
   
    Handler(Selector sel, SocketChannel c) throws IOException {
        socket = c;
        c.configureBlocking(false);
        // Optionally try first read now
        sk = socket.register(sel, 0);
        sk.attach(this);
        sk.interestOps(SelectionKey.OP_READ);
        sel.wakeup();
    }
    ...
}

Reactor 동작 : Acceptor 등록

Reactor 는 event 가 들어오면, ServerSocketChannel 을 만들기 위해 accept 를 해야 한다.

class Reactor implements Runnable {
    final Selector selector;
    final ServerSocketChannel serverSocket;

    Reactor(int port) throws IOException {
        selector = Selector.open();
        serverSocket = ServerSocketChannel.open();
        serverSocket.socket().bind(
                new InetSocketAddress(port));
        serverSocket.configureBlocking(false);
        SelectionKey sk =
                serverSocket.register(selector,
                        SelectionKey.OP_ACCEPT);
        sk.attach(new Acceptor());
    }

그러므로 SelectionKey 0 는 Acceptor 에 묶이게 된다.(Bind 된다.)
Acceptor 는 connection 을 만들기 위한 special handler 이다.
Reactor 가 SelectionKey 0 를 보고 Acceptor 에게 넘겨야 겠다는 것을 알 수 있다.
  • SelectionKey sk = serverSocket.register(selector,SelectionKey.OP_ACCEPT);
이 부분을 통해서 selector 와 channel 을 함께 등록을 하고, return 되는 SelectionKey 가 OP_ACCEPT 에 반응하도록 설정한다.

Reactor 동작 : dispatch
public void run() { // normally in a new Thread
        try {
            while (!Thread.interrupted()) {
                selector.select();
                Set selected = selector.selectedKeys();
                Iterator it = selected.iterator();
                while (it.hasNext())
                    dispatch((SelectionKey) (it.next());
                selected.clear();
            }
        } catch (IOException ex) { /* ... */ }
    }

Selector 는 IO event 를 계속해서 찾는다.

Selector.select() 를 호출한 후 selectedKeys() 를 통해 pending 된 event 가 있는 Channel 들의 SelectionKey 들을 가져올 수 있다.

이 때 SelectionKey 0 가 있으면, ServerSocketChannel 에 event 가 발생했다는 의미이고, Reactor 는 이 event 에 대한 connection 을 만들기 위해 Acceptor 에 보낸다.

Acceptor 동작

class Acceptor implements Runnable { // inner

        public void run() {
            try {
                SocketChannel c = serverSocket.accept();
                if (c != null)
                    new Handler(selector, c);
            } catch (IOException ex) { /* ... */ }
        }
    }


final class Handler implements Runnable {
    final SocketChannel socket;
    final SelectionKey sk;
    ...
   
    Handler(Selector sel, SocketChannel c) throws IOException {
        socket = c;
        c.configureBlocking(false);
        // Optionally try first read now
        sk = socket.register(sel, 0);
        sk.attach(this);
        sk.interestOps(SelectionKey.OP_READ);
        sel.wakeup();
    }
    ...
}


그렇게 Acceptor 가 client 1 의 connection 에 대한 channel 인 ServerSocketChannel 1 을 만들게 된다. 그리고 나서 Selector 를 ServerSocketChannel 1 에 등록하고 SelectionKey 를 얻고, 이 key 에 handler 를 attach 한다.

여기서
  • interestOps(SelectionKey.OP_READ)
를 통해서 selector 가 이 selectionKey 를 READ event 가 발생했을 때만 return 하도록 설정해 준다. 쉽게 얘기하면, 이 selectionKey 와 관련된 event type 을 정해주는 것이다.

이제 client 는 이 ServerSocketChannel 1을 이용해서 read from server / write to server 를 할 수 있다. connection 은 Acceptor 를 통해 이미 했기 때문에 이제는 필요없다.

그러고나서 sel.wakeup()[ref. 7] 을 호출하면, select()(code 에서는 selector.select() 가 될 것이다.) 가 바로 return 할 수있는 channel 이 없어도 바로 return 해 주고 그로 인해 blocked 상태에서 벗어나서 thead 가 다시 깨어나게 된다.



정리

정리해서 다시 이야기 하면, Server 는 Selector.select() 를 호출한다.
  • 이 때 SelectionKey 1 이 return 된다면, 이 것은 ServerSocketChannel 1 이 event 를 가지고 있다는 이야기가 되고, Reactor 는 이 SelectionKey1 이 bind 한 Handler 1 에 dispatch 해주게 된다.
  • 만약 SelectionKey 0 이 return 된다면, 이것은 ServerSocketChannel 0 에서 event 가 발생한 것이고, 새로운 client 가 접속한 것이다. 이 SelectionKey 0 는 Acceptor 에 bind 되어 있기 때문에 이 event 는 Acceptor 로 dispatch 된다.

"특정 ServerSocketCannel" 에서 "특정 event" 가 발생했을 때, 해당하는 SelectionKey 가 selector 에게 return 된다. 그러면 "selector 에서 가져온 SelectionKey" 가 가지고 있는 handler 를 실행한다.



여기까지 구현으로는 Reactor 는 어차피 Handler 의 작업이 끝나기를 기다려야 한다. selectionKey 하나를 다 처리하고 나야 다음 selectionKey 를 처리하게 된다. 우리는 이것을 극복하기 위해 multi-thread 를 이용할 수 있다.


codes

class Reactor implements Runnable {
    final Selector selector;
    final ServerSocketChannel serverSocket;

    Reactor(int port) throws IOException {
        selector = Selector.open();
        serverSocket = ServerSocketChannel.open();
        serverSocket.socket().bind(
                new InetSocketAddress(port));
        serverSocket.configureBlocking(false);
        SelectionKey sk =
                serverSocket.register(selector,SelectionKey.OP_ACCEPT);
        sk.attach(new Acceptor());
    }

    /*
    Alternatively, use explicit SPI provider:
    SelectorProvider p = SelectorProvider.provider();
    selector = p.openSelector();
    serverSocket = p.openServerSocketChannel();
    */
    public void run() { // normally in a new Thread
        try {
            while (!Thread.interrupted()) {
                selector.select();
                Set selected = selector.selectedKeys();
                Iterator it = selected.iterator();
                while (it.hasNext())
                    dispatch((SelectionKey) (it.next());
                selected.clear();
            }
        } catch (IOException ex) { /* ... */ }
    }

    void dispatch(SelectionKey k) {
        Runnable r = (Runnable) (k.attachment());
        if (r != null)
            r.run();
    }

    class Acceptor implements Runnable { // inner

        public void run() {
            try {
                SocketChannel c = serverSocket.accept();
                if (c != null)
                    new Handler(selector, c);
            } catch (IOException ex) { /* ... */ }
        }
    }
}

final class Handler implements Runnable {
    final SocketChannel socket;
    final SelectionKey sk;
    ByteBuffer input = ByteBuffer.allocate(MAXIN);
    ByteBuffer output = ByteBuffer.allocate(MAXOUT);
    static final int READING = 0, SENDING = 1;
    int state = READING;

    Handler(Selector sel, SocketChannel c)
            throws IOException {
        socket = c;
        c.configureBlocking(false);
        // Optionally try first read now
        sk = socket.register(sel, 0);
        sk.attach(this);
        sk.interestOps(SelectionKey.OP_READ);
        sel.wakeup();
    }

    boolean inputIsComplete() { /* ... */ }

    boolean outputIsComplete() { /* ... */ }

    void process() { /* ... */ }


    // class Handler continued
    public void run() {
        try {
            if (state == READING) read();
            else if (state == SENDING) send();
        } catch (IOException ex) { /* ... */ }
    }

    void read() throws IOException {
        int readCount = socket.read(input);
        if (readCount > 0) {
            process();
        }

        state = SENDING;
        // Normally also do first write now
        sk.interestOps(SelectionKey.OP_WRITE);
    }

    void send() throws IOException {
        socket.write(output);
        if (outputIsComplete()) sk.cancel();
    }
}

Multi-threaded
Handler 부분을 thread pool 을 이용해서 동시에 여러개를 처리할 수 있도록 한다.

ref. 9 에서는 상속을 이용해서 구현하였지만, 여기서는 차이점을 잘 알아볼 수 있도록 변경사항을 기존의 Handler 에 추가했다.

아래를 보면 PROCESSING 이라는 state 를 더 추가했다.

final class Handler implements Runnable {
    final SocketChannel socket;
    final SelectionKey sk;
    ByteBuffer input = ByteBuffer.allocate(MAXIN);
    ByteBuffer output = ByteBuffer.allocate(MAXOUT);
    static final int READING = 0, SENDING = 1;
    int state = READING;

    
    static ExecutorService pool = Executors.newFixedThreadPool(2);
    static final int PROCESSING = 2;
    

    Handler(Selector sel, SocketChannel c)
            throws IOException {
        socket = c;
        c.configureBlocking(false);
        // Optionally try first read now
        sk = socket.register(sel, 0);
        sk.attach(this);
        sk.interestOps(SelectionKey.OP_READ);
        sel.wakeup();
    }

    boolean inputIsComplete() { /* ... */ }

    boolean outputIsComplete() { /* ... */ }

    
    synchronized void process() { /* ... */ }
    


    // class Handler continued
    public void run() {
        try {
            if (state == READING) read();
            else if (state == SENDING) send();
        } catch (IOException ex) { /* ... */ }
    }

    
    synchronized void read() throws IOException {
        int readCount = socketChannel.read(input);
        if (readCount > 0) {
            
            state = PROCESSING;
            pool.execute(new Processer(readCount));
            
        }
        //We are interested in writing back to the client soon after read processing is done.
        selectionKey.interestOps(SelectionKey.OP_WRITE);
    }
    

    void send() throws IOException {
        socket.write(output);
        if (outputIsComplete()) sk.cancel();
    }

    
    synchronized processAndChageState(int readCount){
        process(readCount)
        state = SENDING;
    }
    
    class Processer implements Runnable {
        int readCount;
        Processer(int readCount) {
            this.readCount =  readCount;
        }
        public void run() {
            processAndChageState(readCount);
        }
    }
    
}

See Also

  1. Proactor Pattern 발표 자료
  2. proactor design pattern (번역),2012년 5월 18일 금요일
  3. http://www.tornadoweb.org/en/stable/

Reference
  1. http://en.wikipedia.org/wiki/Reactor_pattern
  2. http://jeewanthad.blogspot.kr/2013/02/reactor-pattern-explained-part-1.html
  3. Scalable IO in Java, Doug Lea, State University of New York at Oswego
  4. SelectionKey (Java Platform SE 7 )
  5. Selector (Java Platform SE 7 )
  6. SelectableChannel (Java Platform SE 7 )
  7. Java NIO Selector By Jakob Jenkov
  8. http://jeewanthad.blogspot.kr/2013/03/reacter-pattern-explained-part-2.html
  9. http://jeewanthad.blogspot.kr/2013/03/reacter-pattern-explained-part-3.html


2013년 10월 24일 목요일

Vincent by Don McLean







Stary stary night 
Paint your palette 
blue and gray 
Look out on a summer's day 
With eyes that know 
the darkness in my soul 
Shadows on the hills 
Sketch the trees and the daffodils 
Catch the breeze 
and the winter chills In colors 
on the snowy linen land 

Now I understand 
What you tried to say to me 
And how you suffered 
for your sanity 
And how you tried 
to set them free 
They would not listen 
they did not know how 
Perhaps they listen now

Stary stary night 
Flaming flowers that brightly blaze 
Swirling clouds in violet haze 
Reflect in vincent's eyes of China blue 
Colors changing hue 
Morning field of amber grain 
Weathered faces lined in pain 
are soothed Beneath artist's loving hand

Now I understand 
What you tried to say to me 
And how you suffered 
for your sanity 
And how you tried 
to set them free 
They would not listen 
they did not know how 
Perhaps they listen now 

For they could not love you
but still 
your love was true
and when no hope was left 
in sight on that 
starry starry night.
You took your life
as lovers often do;
But I could have told you
Vincent
this world was never meant for one
as beautiful as you.

Starry starry night
portraits hung in empty halls
frameless heads on nameless walls
with eyes that watch the world and can't forget.
Like the stranger that you've met
the ragged men in ragged clothes
the silver thorn of bloddy rose
lie crushed and broken on the virgin snow.

now I think I know 
what you tried to say to me
how you suffered for your sanity
how you tried to set them free.
They would not listen
they're not list'ning still
perhaps they never will.



별이 총총한 밤 
파랑, 회색으로 팔레트를 
물들이고 
여름날, 
내 영혼의 어두운 면을 
궤뚫는 눈으로 밖을 바라봐요 
언덕에 드리운 그림자, 
나무와 수선화를 스케치하고 
눈처럼 하얀 리넨 캔버스에 
미풍과 겨울의 싸늘함을 
색깔로 그려내요 

당신이 뭘 말하려 했는지 
난 이제 알 것 같아요 
온전한 정신은 찾으려 
당신이 얼마나 
고통스러워 했는지, 
사람들을 자유롭게 하려고 
얼마나 노력했는지. 사람들은 
들으려 하지 않았죠 어떻게 듣는지도 몰랐죠 
아마도 지금은 귀기울일 거에요

별이 총총한 밤 
붉은 빛깔로 빛나는 화려한 꽃들
보랏빛 안개 속의 소용돌이치는 구름들
빈센트의 청자빛 눈 속에 비쳐지네요
색조를 바꾼 색들 
아침 들판의 호박색 곡물들
고통에 주름진 풍화된 얼굴들
화가의 사랑스러운 손길 아래 진정이 되네요

당신이 뭘 말하려 했는지 
난 이제 알 것 같아요 
온전한 정신은 찾으려 
당신이 얼마나 
고통스러워 했는지, 
사람들을 자유롭게 하려고 
얼마나 노력했는지. 사람들은 
들으려 하지 않았죠 어떻게 듣는지도 몰랐죠 
아마도 지금은 귀기울일 거에요 

그들은 당신을 사랑할 수 
없었기에, 하지만 
당신의 사랑은 여전히 진실했죠 
그리고 아무런 희망도 
남지 않은 별이 총총한 밤 
연인들이 그러듯이 
당신도 목숨을 끊어버렸죠 
하지만 난 당신께 이렇게 
말했어야 했어요 
이 세계는 당신과 같이 아름다운 사람에게는 
어울리지 않는 것이었답니다


별이 총총한 밤 
빈 벽에 걸려 있는 초상화들
이름 없는 벽의 액자 없이 걸린 이들
세상을 바라보고 있는 눈들 그리고 있을 수 없는
당신이 만났던 이방인들
누더기 옷을 입은 남루한 남자들
피투성이 장미의 은빛 가시들
순백의 눈 위에 누워 부서지고 짓밟히네요

이젠 알 것 같아요 
당신이 뭘 말하려 했는지 
온전한 정신을 갈구하며 
얼마나 괴로워 했고 
사람들은 자유롭게 하려고 
얼마나 노력했는지 
사람들은 들으려 하지 않았죠 
지금도 귀기울지 않고, 
어쩌면 앞으로도 그럴테죠.