Languages/Ruby or Rails2008.10.10 11:00

Ruby를 주마간산식으로 배운 사람에게는 감동적인 코드...

module ActionController
  module StatusCodes #:nodoc:
    # Defines the standard HTTP status codes, by integer, with their
    # corresponding default message texts.
    # Source: http://www.iana.org/assignments/http-status-codes
    STATUS_CODES = {
      100 => "Continue",
      101 => "Switching Protocols",
      102 => "Processing",

      200 => "OK",
      201 => "Created",
      202 => "Accepted",
      203 => "Non-Authoritative Information",
      204 => "No Content",
      205 => "Reset Content",
      206 => "Partial Content",
      207 => "Multi-Status",
      226 => "IM Used",

      300 => "Multiple Choices",
      301 => "Moved Permanently",
      302 => "Found",
      303 => "See Other",
      304 => "Not Modified",
      305 => "Use Proxy",
      307 => "Temporary Redirect",

      400 => "Bad Request",
      401 => "Unauthorized",
      402 => "Payment Required",
      403 => "Forbidden",
      404 => "Not Found",
      405 => "Method Not Allowed",
      406 => "Not Acceptable",
      407 => "Proxy Authentication Required",
      408 => "Request Timeout",
      409 => "Conflict",
      410 => "Gone",
      411 => "Length Required",
      412 => "Precondition Failed",
      413 => "Request Entity Too Large",
      414 => "Request-URI Too Long",
      415 => "Unsupported Media Type",
      416 => "Requested Range Not Satisfiable",
      417 => "Expectation Failed",
      422 => "Unprocessable Entity",
      423 => "Locked",
      424 => "Failed Dependency",
      426 => "Upgrade Required",

      500 => "Internal Server Error",
      501 => "Not Implemented",
      502 => "Bad Gateway",
      503 => "Service Unavailable",
      504 => "Gateway Timeout",
      505 => "HTTP Version Not Supported",
      507 => "Insufficient Storage",
      510 => "Not Extended"
    }

    # Provides a symbol-to-fixnum lookup for converting a symbol (like
    # :created or :not_implemented) into its corresponding HTTP status
    # code (like 200 or 501).
    SYMBOL_TO_STATUS_CODE = STATUS_CODES.inject({}) do |hash, (code, message)|
      hash[message.gsub(/ /, "").underscore.to_sym] = code
      hash
    end

    # Given a status parameter, determine whether it needs to be converted
    # to a string. If it is a fixnum, use the STATUS_CODES hash to lookup
    # the default message. If it is a symbol, use the SYMBOL_TO_STATUS_CODE
    # hash to convert it.
    def interpret_status(status)
      case status
      when Fixnum then
        "#{status} #{STATUS_CODES[status]}".strip
      when Symbol then
        interpret_status(SYMBOL_TO_STATUS_CODE[status] ||
          "500 Unknown Status #{status.inspect}")
      else
        status.to_s
      end
    end
    private :interpret_status

  end
end

해쉬 hash는 처음에 {}로 초기화된다. STATUS_CODES 내의 아이템은 하나씩 (code, message)로 전달된다. 이 때 message에 포함된 공백을 전부 ""로 대체하고, 소문자로 바꾼 다음, 심볼 (:ok와 같은)로 바꾸고 hash 객체의 key로 삼는다. 해당 심볼에 매핑되는 값은 code가 된다.

신고
Posted by 이병준

소중한 의견, 감사합니다. ^^

(역시 지난 글에 이어집니다.) 사용자 식별을 대강 마쳤으니 이제 사용자 스토리를 작성해볼 순서입니다. CRM 관리자가 하는 일이 과연 있을까? 네트워크 관리자랑 상당부분 중복되지 않을까? 하는 생각이 들었습니다만, 스토리를 작성해 보기 이전에 사용자 범주를 과도하게 축소해버리는 것도 위험하겠다, 는 생각도 들었습니다. 사용자가 줄어든다는 것은, 그만큼 스토리 작성에 있어 유용하게 써먹을 수 있는 관점들이 줄어드는 것을 의미하기도 하니까요.

그래서 일단은 사용자들은 그대로 두고, 대략적인 사용자 스토리들을 작성해봤습니다. 과연 필요한 모든 스토리들을 다 작성했을까? 하는 의구심도 들었습니다만, User Stories Applied라는 책에도 나와있다시피, 사용자 스토리를 만드는 것 조차도 점진적으로 해 나갈 수 있는 부분이니까 크게 걱정하지 않아도 될 것 같습니다.

사용자 스토리 추출 과정에서 도출된 스토리들은 다음 그림과 같습니다. (역시 너무 작아서 잘 안보이는군요ㅋ)

만들어진 사용자 스토리들

만들어진 사용자 스토리들


원래는 사용자 스토리 하나 당 카드 하나씩을 사용해야 합니다만, 모니터 화면이 너무 작아서 그렇게는 못했습니다. 아무래도 카드 묶음을 하나 사는게 좋을것 같기도 해요. 여기 저기서 스토리 구현을 마치고 스토리 카드를 찢을때의 쾌감에 대해서 이야기를 하는데, 이런 식으로 진행하면 그런 쾌감을 느끼기는 힘들거든요. ㅋ

아무튼, 이 스토리들을 좀 잘 보이게 다시 나열해 보면, 아래와 같습니다.

  1. 일반 VoIP 사용자는 음성 통화를 할 수 있다
  2. 일반 VoIP 사용자는 통화 종료 후에 통화 시간을 확인할 수 있다
  3. 일반 VoIP 사용자는 통화 종료 후에 대략적인 통화 요금을 확인할 수 있다
  4. 음성+화상 VoIP 사용자는 음성+화상 통화를 할 수 있다
  5. 음성+화상 VoIP 사용자는 통화 종료 후에 통화 시간을 확인할 수 있다
  6. 음성+화상 VoIP 사용자는 통화 종료 후에 대략적인 통화 요금을 확인할 수 있다
  7. 음성+화상 VoIP 사용자는 음성 통화 중에 화상 통화를 시작할 수 있다
  8. 음성+화상 VoIP 사용자는 음성 통화 중에 화상 통화를 종료할 수 있다
  9. VoD 사용자는 웹 상에서 영화를 선택한 다음 재생할 수 있다
  10. VoD 사용자는 재생중인 영화를 종료할 수 있다
  11. 네트워크 관리자는 시스템의 상태(예: 가동/중지/비정상적 종료 등을 포함하는)를 살펴볼 수 있다
  12. 네트워크 관리자는 시스템에서 발생한 중요한 사건 기록(예: 장애 내역, 장애 발생 시간, 장애 발생 지점 등)들을 살펴볼 수 있다
  13. 네트워크 관리자는 접속망의 네트워크 사용량에 대한 정보를 실시간으로 살펴볼 수 있다
  14. 네트워크 관리자는 시도된 호, 승락된 호, 거부된 호에 대한 통계 정보를 살펴볼 수 있다
  15. 네트워크 관리자는 거부된 호가 발생한 네트워크 링크의 위치를 추적하고, 그 사용량을 확인할 수 있다
  16. 네트워크 관리자는 시스템 설정 정보를 입력할 수 있어야 한다
  17. CRM 관리자는 접수된 고객 불만을 확인할 수 있어야 한다
  18. CRM 관리자는 고객 불만이 접수된 시간에 문제가 있었던 네트워크 링크를 찾아내고, 그 링크의 네트워크 사용량 정보를 확인할 수 있어야 한다
  19. CRM 관리자는 고객 불만에 응대할 수 있도록 고객의 정보를 확인할 수 있어야 한다
뭐 이정도로군요. 사실 1~10까지의 사용자 스토리는 너무 단순하긴 해요. 그런데 제가 구현할 시스템이 다른 더 거대한 시스템의 일부인데다, 제 입장에서 보면 저 정도 스토리만 알고 있으면 나머지 부분 (주로 사용자 인터페이스에 해당하는) 은 크게 모르고 있어도 상관이 없어서, 우선은 개의치 않기로 했습니다.

그런데 이 스토리들을 보면, 사실 구현될 시스템의 대략적인 정보는 얻을 수 있어도 정말로 구체적인 정보는 알 수가 없습니다. 가령 13번을 보면 네트워크 사용량에 대한 정보을 실시간으로 살펴볼 수 있어야 한다는 스토리가 있는데, 어떤 정보를 실시간으로 살펴볼 수 있어야 한다는 것인지에 대해서는 아무런 이야기가 없지요.

이런 정보는 나중에 그 스토리를 구현할 사람이 고객과 의사소통을 해 나가면서 알아내야 합니다. 그런대 대체 언제 알아내야 하는 걸까요?

'언제' 알아내야 하는가가 '왜' 중요한지를 이해하려면, '추정'의 문제를 생각해 봐야 합니다. 나중에 이 스토리를 놓고, 그 스토리를 완료하는 데 얼마나 걸릴지를 추정해야 할 것이거든요. 그 추정치를 놓고 이터레이션 계획이나 릴리즈 계획을 세워야 하니, 추정은 정말로 중요한 과정이죠. 그런데 스토리를 완료하는 데 드는 시간을 추정하려면 그 스토리가 정확하게 무엇을 요구하는지를 알 필요가 있을 수 있거든요.

그러니 가급적이면 스토리를 추정하는 자리에 고객도 참여하도록 하는 것이 중요할 겁니다. 그러면 추정하는 과정에서 개발자들이 궁금한 게 생기면 고객에게 물어보면 되지요.

물론, 방금 예로 든 '네트워크 사용량 정보를 확인할 수 있어야 한다'는 스토리는 그런 세부사항까지 미리 알지 않아도 괜찮기는 해요. 네트워크 사용량 정보라는 게 질의하는 순간에 주어지는 값을 기반으로 계산해서 얻어지는 값이라면 복잡하겠지만, 저장된 값을 간단히 조작해서 보여주는 수준이라면 굳이 세부사항을 생략하더라도 나름 정확한 추정을 할 수 있겠죠.

TIP: 추정하는 자리에는 고객을 참여시켜라


그런데 정말로 이 정도 사용자 스토리면 충분한걸까요? ㅋㅋ

네. 뭐 현재로서는 이정도면 충분할 겁니다. 뭔가 빠진 것이 있다면 스토리 추정 과정에서 발견될 것이고, 성에 차지 않는다면 추정을 끝내고 난 다음에 릴리즈 계획을 짜기 전에 스토리들을 전반적으로 다시 한 번 재검토 해 봐도 될 테니까요. 거기다 CRM 관리자라는 사용자를 지우지 않고 남겨둔 덕분에, 그 사용자 관점에서 유용한 스토리를 얻을 수 있었습니다. 네트워크 관리자와 비슷한 기능을 요구하는 대목도 있긴 합니다만, 보는 시각이 다르니까 나중에 고객과 이야기를 하다보면 조금 다른 방향으로 가지를 쳐 나가겠지요.

그러면 지금부터는 사용자 스토리들에 대한 추정을 해 봐야 하겠군요. 위의 스토리들을 다시 한 번 재검토하지 않는 이유는, 추정하는 과정이 어차피 검토를 수반하는 데다, 그 와중에 에픽(epic: 너무 큰 사용자 스토리)이 발견되어 더 작은 스토리로 나누어야 하는 경우에는 그 자리에서 그렇게 하면 될 것 같기 때문입니다.

(다음 글에 이어서...)

신고
Posted by 이병준

소중한 의견, 감사합니다. ^^

(앞 글에 이어서 계속됩니다)  그런데 생각해 보니, 그것보다는 더 다양한 사용자가 있을 법도 합니다. 그래서 고민을 좀 해봤습니다. (같이 할 사람이 좀 있었으면 고민을 안했어도 되었을텐데.. ㅎㅎ)

생각해보니, VoIP 사용자로는 두 가지 부류가 있을 수 있겠더군요. 하나는 일반 VoIP 전화 사용자이고, 다른 하나는 음성/화상 겸용 VoIP 전화 사용자. 어떤 사용자냐에 따라서 시스템에 바라는 바도 좀 달라질 수 있겠습니다.

그리고 생각해보니까, 관리자도 두 가지 부류가 있을 수 있겠더군요. 네트워크를 관리할 네트워크 관리자와, 고객과의 관계를 관리하는 것이 주 업무인 CRM 관리자. CRM 관리자 입장에서는 고객의 불평불만을 처리할 근거 자료가 필요하니까, 혹시라도 전화가 안된다면 그 원인을 신속하게 파악하는 것이 필요하겠어요. 그러니 시스템이 그런 기능을 지원할 수 있다면 좋겠지요.


추가된 사용자들

추가된 사용자들


그래서, 새로운 부류의 사용자 두 가지가 추가되었습니다. 위의 그림을 보시면 (그림이 넘 작나요? -_-;;) 어떤 사용자가 추가되었는지 보실 수 있을 거에요. 그런데 그냥 그렇게 두자니 조금 심심한 감도 있고 해서, 젤 윗단에 사용자의 '대분류'를 넣어봤습니다.

제가 만든 '대분류' 체계에 따르면, 사용자는 (1) VoIP 전화 사용자 (2) VoD 사용자 (3) 관리자의 세 가지 부류로 나눌 수 있어요. VoIP 전화 사용자에는 a. 일반 VoIP 전화 사용자 b. 음성+화상 VoIP 전화 사용자의 두 가지 사용자가 있는 것이고, 관리자에는 a. CRM 관리자 b. 네트워크 관리자의 두 가지 사용자가 있는 것이죠. VoD 사용자에 대해서는 특별히 하위 범주의 사용자들을 찾아낼 수가 없어서, 그냥 그대로 두었습니다.

자. 여기까지 해서 대충 사용자들을 식별해 봤습니다. 이제 '시스템을 성공적으로 구축하는 데 도움이 되지 않을 것 같은 역할을 하는 사용자에 대한 카드'를 찢어버려야 하는데, 뭐 지금으로선 다들 나름대로 의미가 있을 것 같군요. (사실 사용자를 많이 식별하지 못했기 때문에 그런 것일수도 있습니다.) 그러니 찢어버리는 작업은 생략하도록 하겠습니다. :-P

그럼 이제 역할을 좀 다듬어 봐야 할 것 같네요. '역할 모델링'이라고도 하는 작업인데, 사용자의 '속성'을 고민해 보는 작업입니다. 사용자의 속성을 잘 파악하게 되면, 나중에 스토리를 찾아내는 과정이 좀 더 편해질 수도 있을 것 같습니다.

가령 음성/화상 겸용 VoIP 전화 사용자의 경우, 저는 그 사용자가 다음과 같은 속성을 갖는다고 가정했습니다.

사용자 역할 : 음성/화상 겸용 VoIP 전화 사용자

전화비 절약을 위해 VoIP 전화기를 선택했으며, 화상통화에 관심이 있어 음성/화상 겸용 VoIP 전화를 구매했다. VoIP라는 개념이 낯설기는 하지만 '일반 전화와 똑같은 방법으로 통화를 할 수 있다면' 써볼만 하다고 생각하며, 통화를 시작할 때나 혹은 통화가 진행중인 도중 언제라도 상대의 얼굴을 보고 싶다면 화상 통화 기능을 활성화시켜 상대를 보고싶어 한다. 인터넷을 통한 전화라고 해서 품질이 일반 전화보다 나빠야 한다고 생각하지는 않는다. 전화의 사용법은 가급적 일반 전화와 유사하기를 기대한다.

아까 식별했던 모든 사용자에 대해서 이런 작업을 반복해서 적당한 곳에 붙여놓으면, 나중에 필요할 때 마다 참고할 수 있을 겁니다. 시간 관계 지면 관계(?)도 있고 하니, 다른 사용자에 대한 속성 탐색 작업은 생략하겠습니다. X-)

그런데 이쯤 해 놓고 "User Stories Applied"를 다시 읽어보니, 대부분의 팀들은 이 이상 작업할 필요는 없다고 하는군요. 저도 이 쯤에서 사용자 식별에 관한 작업은 마무리 지어야 할 것 같습니다.

다음으로 해야 할 일은 이제 스토리를 작성하는 일이 되겠군요. 스토리 작성을 위해 프로토타입을 한 두 번 정도 만들어야 할 일이 생길지도 모르겠습니다.

(다음 글에 이어서...)
신고
Posted by 이병준

소중한 의견, 감사합니다. ^^

User Stories에 대해서 관심을 가지기 시작한지는 솔직히 얼마 되지 않습니다. ^^; 몸담고 있는 조직이 개발 조직이 아니라서, 사실 개발 방법론쪽으로 관심을 가지는 것이 그다지 도움이 되는 상황은 아니죠.

하지만 그렇다고 개발을 아주 안 할 수는 없는 노릇이라, 가급적이면 개발을 잘 하는 것이 좋겠다... 뭐 이런 생각은 항상 가지고 있었습니다. 그래서 최근에 Extreme Programming, User Stories, TDD, CVS 등등 연관되어 있는 방법론, 기법, 기술 등을 열심히 보고 있는 중입니다. 과연 이 상태 (지적 욕구에 충만한 이런 상태) 가 얼마나 오래 갈 지는 알 수 없습니다만 ㅋㅋ 지속되는 동안에는 이 블로그에도 엇비슷한 글들이 계속 올라오게 되리라 생각합니다.

각설하고.

이 글은 "User Stories: 실무 적용 사례"의 첫 번째 글입니다. 제가 하고 있는 업무에 실제로 User Story를 적용해 본 사례에 대해서 이야기를 해 보려고 합니다. (사실 완료된 업무는 아니고 지금 진행중인 업무입니다.) 업무 내용에 대해서도 소상히 말씀을 드릴 수 있으면 좋겠습니다만 사실 업무 성격상 외부에 공개가 조금 어려울 수도 있는 부분이 있기 때문에, 자세히 말씀 드리지 못하고 건너뛰는 부분도 있을 수 있습니다. (어쩌면 잘 몰라서 그러는 것일지도 모릅니다 *쿨럭*) 그런 부분에 대해서는 양해를 부탁드립니다. :-)

- - -

올해, 저는 대략 다음과 같은 이름의 시스템을 개발하게 되었습니다. (저는 네트워크 분야에서 일을 하고 있습니다.)

"VoIP, VoD 세션 요청에 대한 대역폭 기반 호 수락 제어 시스템"

이름 자체는 거창하기 짝이 없습니다만 하는 내용은 그렇지 않을지도 모릅니다. :-) 아무튼 위의 시스템이 하려고 하는 바는 이런 겁니다. 네트워크의 자원은 유한합니다. 대역폭은 무한히 주어질 수 없습니다. 그런데 대략적으로나마 그 자원의 크기(즉, 링크당 대역폭의 크기)를 알수 있다고 가정하고 (그 정보는 장비에 질의해보면 알 수 있다고 치겠습니다) 하나의 VoIP(혹은 VoD) 플로우가 보장받아야 할 대역폭의 양을 절대적으로 지원해 줄 수 있는 네트워크 장비가 있다고 치면, '이미 알고 있는 자원의 크기'를 기준으로 해서, '얼마나 많은 VoIP(혹은 VoD) 세션을 지원할 수 있는지'를 알 수 있습니다. 간단하죠. 그냥 나눗셈을 하면 됩니다. :-)

그런 간단한 가정에 기반한 호 수락 제어 시스템을 만들겠다는 겁니다. 현재 몇 개의 플로우가 특정한 링크에 흘러다니고 있는지를 알면, 현재 얼마만큼의 대역폭이 그 링크에 대해서 사용되고 있는지를 알 수 있습니다. 그런 정보에 의거하여 '새로운 VoIP/VoD세션을 받아들일 것인지 말 것인지'를 결정하는 수락제어 시스템을 만들어 보겠다, 는 겁니다. 이런 시스템을 통상 "Accounting Based 수락제어 시스템"이라고도 부르죠.

수락제어 시스템을 만드는 동기도 단순합니다. 어떤 특정한 링크에 허용되어 있는 대역폭 이상의 트래픽이 그 링크에 흘러들어오게 되면, 패킷들이 그 순간부터 랜덤하게 drop되기 시작합니다. 그러면 네트워크 링크상의 모든 플로우들이 성능 저하를 경험하게 되죠. 그런 상황을 피해보겠다는 겁니다.

그런데 말로 풀어보면 간단하기 짝이 없는 이 시스템을 실제로 구현하려고 해 보니, 그다지 간단하지가 않습니다. 우선 망에서 자원들(주로 네트워크 노드들과 그 사이의 링크들) 및 그 자원에 부여된 대역폭 크기를 수집을 해 와야 하고, Call Server는 SIP 요청들(INVITE, BYE 등등)을 처리해야 하고, ... 등등 할 일이 꽤나 많습니다.

개발을 어떻게 진행할까, 머리를 싸매던 끝에

기왕 하는 김에 Extreme Programming을 해 볼까

하는 무모한 생각을 하게 됩니다. -_-

그래서 일단 "들은 풍월도 있고 하니" 유저 스토리부터 한 번 작성을 해 봐야겠다, 하는 생각을 하게 되었습니다. User Story하면 모두들 "User Story Applied"를 읽는 것 같아, 저도 읽어봤습니다. 그리고 나서 아무 생각없이 유저 스토리를 작성하기 시작했(?)습니다. 유저 스토리는 시스템 사용자가 '시스템을 어떻게 사용하느냐'를 기술하는 것을 그 목적으로 합니다. 그래서 '누가' '무엇을 위해' 시스템을 사용하는지를 중심으로 기술하게 되죠. 하지만 Use Case 보다는 매우 간단합니다. Use Case는 가능한 모든 경우를 자세하게 기술하는 것을 목적으로 하는 반면, 사용자 스토리는 그 밑바탕부터가 '점진적인 개선'이나 '점진적 상세화'를 목적으로 하기 때문에, 그렇게 자세할 필요는 없습니다. 상세히 적어야 할 부분이 있다면 테스트 항목에 나열하면 되죠. (뭐 다 아시는 내용이겠습니다만...)

그러니 이런 것도 스토리가 될 수 있을 것 같습니다.

사용자는 VoIP 전화기를 사용해 전화를 걸 수 있다.

이 경우에는 '사용자'가 성취하려는 목적이 '전화를 거는' 것이죠. 그런데 이 사용자 스토리는 너무 커서 구현이 일이주 안에는 좀 어려울 것 같네요. 이런 스토리를 흔히 에픽(Epic)이라고 합니다. 이런 것은 나중에 사용자 스토리를 더 작은 단위로 나누는 과정에서 그 크기가 줄어들게 됩니다.

아무튼 그건 그렇고.

스토리가 이렇다 보니, '좋은 스토리'를 작성하려면 '시스템을 사용할 사용자'가 명확하면 명확할 수록 좋겠다', 뭐 이런 결론에 이르게 됩니다. 책에서는 사실 사용자 부류를 식별하려면 워크샵같은 자리를 마련해서 관련된 사람들이 전부 모인 다음에 사용자들을 식별해 나가도록 하는 게 좋다고 권고하고 있습니다만(뭐 일종의 브레인 스토밍이죠), 저는 저 혼자 실습을 진행하는 관계로 -_- 혼자 해 보기로 했습니다.

혼자 하려니 사용자 식별 과정에서 이용되는 '카드' 같은것을 만들기도 귀찮아서, 일단은 Note+라는 포스트잇 프로그램을 써 보기로 했습니다.

처음에 식별된 사용자들

처음에 식별된 사용자들


우선 생각할 수 있는 것으로, 네트워크 서비스 가입자가 있습니다. (저도 집에서는 그런 가입자 중 한사람입니다.) 이 가입자들을 좀 더 세분화해 보자면 VoIP 서비스 사용자가 있을 수 있고, VoD 서비스 사용자가 있을 수 있습니다. 그 이외에, 개발될 시스템을 사용할 주 고객으로, 네트워크 운영자가 있을 수 있습니다. 네트워크 운영자는 VoIP나 VoD 서비스에 의해 대역폭이 어떻게 소모되고 있는지를 항시 모니터링할 수 있었으면 좋겠다, 는 바램을 가지고 있는 부류의 사람들입니다.

그런데 이정도의 사용자만 식별하면 충분한걸까요?

(다음 글에 이어서...)
 

신고
Posted by 이병준

소중한 의견, 감사합니다. ^^

  1. daliot

    켄트 벡이 한국에 옵니다. 관심이 있으실 것 같아 링크를 남깁니다.
    http://www.sten.or.kr/bbs/board.php?bo_table=news&wr_id=1807

    2009.08.27 19:00 신고 [ ADDR : EDIT/ DEL : REPLY ]
  2. daliot

    9월 4일 열리는 Kent Beck의 Responsive Design 세미나에 대한 호응이 너무 좋아서 정원이 이미 꽉차고 말았습니다. 9월 2일에도 같은 세미나를 하기 때문에 혹시라도 선착순에 밀리신 분이시라면 한번더 기회가 있다는 것을 알려 드립니다.

    http://sten.or.kr/bbs/board.php?bo_table=news&wr_id=1822

    2009.08.30 17:07 신고 [ ADDR : EDIT/ DEL : REPLY ]
    • 저도 알고 있었습니다. 다만 참석할 시간 내기 어려운 상황이어서요. 제가 9월 8일까지는 프로젝트 마무리에 전념해야 하는 상황이라...

      2009.08.31 10:01 신고 [ ADDR : EDIT/ DEL ]