XHTML 왜 써야 하나요?

개발 노트 2009. 3. 31. 14:27 posted by 무병장수권력자


작성자 : 김문규
최초 작성일 : 2009.03.31



1. 들어가며
웹으로 생계를 유지하는 사람이라면 웹표준에 대한 말을 많이 듣게 됩니다. 이 중에 DTD, HTML4.01, XHTML1.0, XHTML 1.1 이런 듣보잡 같은 스펙 용어 들이 난무하게 되지요.

간단하게 설명하면,
DTD : 문서의 표현 방식을 일정한 형식으로 정의하는 것입니다. markup(마크업)문서를 작성하는 일종의 약속을 몇가지 버젼으로 정해놓고 어떤 약속에 따라서 마크업 문서가 작성되었는 지 브라우져에게 알려주는 것이지요. 브라우져는 규약에 따라서 렌더링을 하면 되기 때문에 브라우져에 관계없이 비슷한 모양을 보여 줄 수 있게 됩니다.
HTML4.01 : 흔히 많이 사용하는 약속으로 지금까지의 웹에서 무의식 중에 사용하던 것들이 대부분 여기에 해당한다고 보시면 됩니다.
XHTML : 이 녀석이 오늘의 주제 입니다. HTML4를 XML 형식을 적용해서 전체적인 문서의 형식을 구조적으로 재구성한 것입니다.

2. 잘 정리된 문서들
XHTML에 대한 자세한 설명의 다음의 링크들이 끝내주게 잘 정리되어 있습니다.
한글 번역문 XHTML 1.0: http://trio.co.kr/webrefer/xhtml/overview.html#guidelines
HTML -> XHTML 로 가기 : http://titicat.egloos.com/1370487

3. 그럼 왜 XHTML을 써야 할까요?
The Web Standard Project의 http://www.webstandards.org/learn/articles/askw3c/oct2003 문서를 발췌 번역하였습니다.

1) 구조적이기 때문에 차세대 웹 환경인 Semantic Web(시맨틱 웹)에서 기계가 페이지를 해석할 때 도움이 됩니다.

2) 정해진 규약에 따라 코딩되기 때문에 관리가 수월합니다.
 - 이놈 저놈이 이놈 스타일, 저놈 스타일로 코딩하면 완전 짜증나지요. 하지만 XHTML을 따른다면 택도 없습니다.

3) XSL을 바로 사용할 수 있습니다.
 - XSLT(Extensible Style sheet Language transformation) 을 이용하면 다양한 문서 포맷으로 자유롭게 변환이 가능합니다.

4) 이는 가까운 미래에 XHTML 2.0이 나오면 쉽게 변환이 가능하다는 것을 의미하지요.

4. 마치면서
당장 귀찮다고 하시는 건가요? 어차피 습관이라는 것은 들이기 나름입니다. 습관만 고친다면 XHTML이 어렵거나 귀찮은 일이 아닐겁니다. 당신도 차세대 웹의 기여자가 될 수 있습니다.
^^


WWT, WorldWide Telescope

Microsoft 2009. 3. 31. 09:25 posted by 무병장수권력자


Microsoft WorldWide Telescope

작성자 : 김문규
최초 작성일 : 2009.03.31

작년 5월경에 시작된 서비스 입니다. 각종 전체 망원경으로 촬영된 사진을 Microsoft에서 잘 정리해서 사용자에게 보여주는 서비스 이지요~. 물론 멋진 UI로 사용자의 눈과 지적 호기심을 만족시켜 주고 있고요.

최근, NASA와 상호 협력해서 화성과 달에서 촬영된 컨텐츠를 추가로 제공하게 될 것이라고 발표했다고 하는 군요. 그 양은 100T급의 용량에 달하는 컨텐츠라고 합니다.

천체 망원경이 없는 저에게는 재미나 경험을 주는 서비스가 아닌가 싶습니다.

다음은 달을 선택했을 때 보여지는 화면입니다.



이 것은 달과 관련된 우주 탐사의 역사(?)를 설명해주는 나레이션의 일부이고요~

우주에 대해 관심이 있는 사람들, 특히 아이들에게 매우 유익한 경험의 기회가 되지 않을까 합니다.
울 아들도 좀 더 자라면 보여줘야 겠어요.

항상 느끼지만,
MS와 구글에게서 빛나는 점은 이런 점이라고 생각되네요.
인터넷에서 자신들의 능력을 자랑하고 싶어한다는 것.....당장 돈이 되지 않지만... 꽤 많은 돈이 들지만...

그래서 인재들이 모이나 봅니다. 자신들의 능력을 자랑하고 싶어서...




'Microsoft' 카테고리의 다른 글

Microsoft OneApp 을 소개합니다.  (2) 2009.08.25
윈도 7 열풍!  (0) 2009.07.30
웹조각 (Web Slice)  (0) 2009.06.16
마이크로소프트가 생각하는 미래의 홈  (0) 2009.05.20
Windows Rally Program - Web Service on Device  (0) 2008.07.09


HTTP Session Hijacking

보안, 암호화 2009. 3. 27. 11:21 posted by 무병장수권력자


원문 링크
http://unix.co.kr/bbs/board.php?bo_table=02_06&wr_id=46

주옥같은 강좌이어서 공유 차원에서 퍼담아 봅니다.

HTTP Session Hijacking

앞서 해킹기법에서 스니핑(sniffing)에 대해서 살펴보았다. telnet, ftp, pop3 등의 비암호화 프로토콜 어플리케이션은 스니핑 공격을 통하여 사용자 계정 및 암호 도용에 취약할 수 있음을 알게 되었다. 마찬가지로 우리가 웹 브라우징시 사용하는 HTTP 프로토콜도 이러한 도용에 취약할 수 있다.

HTTP Session Hijacking(혹은 Session ID Hijacking)이라는 공격 기법은 웹 브라우징시 세션 관리를 위해 사용되는 Session ID를 스니핑이나 무작위 추측 공격(brute-force guessing)을 통해서 도용하는 기법이다. 먼저 이러한 공격에 대한 기초적인 배경지식으로 HTTP 프로토콜의 특성 및 Session ID에 대해 이해해보도록 하겠다.

HTTP 프로토콜의 특성

HTTP는 기본적으로 비연결유지(stateless) 프로토콜이다. 반면, telnet과 ftp와 같은 프로토콜은 클라이언트와 서버 사이에 하나의 연결(session)이 성립되어 통신하는 프로토콜이다. 따라서, 우리가 보통 웹 브라우저를 열어 URL을 입력하고 해당 홈페이지에 들어간다는 것은 해당 홈페이지에 포함되어 있는 페이지(html), 그림(jpg, gif 등), 자바스크립트(js) 등을 다운받기 위해 개별적인 여러 개의 80 요청(request)을 발송한 후 서버로부터 각각의 응답(reply)을 받는 것을 의미한다.

이러한 일련의 요청과 응답이 이루어진 후 해당 서버와의 통신은 다시 종료된다. 위와 같은 기본적인 지식을 알고 있다면 다음과 같은 질문을 할 수 있다. HTTP는 비연결유지 프로토콜이라고 하였는데 Session Hijacking 이란 공격은 어떻게 가능한 것인가? 이는 HTTP 세션 관리를 위해 사용되는 Session ID를 통해서 가능하다.


Session ID란 무엇인가?

웹 서버는 다수의 웹 페이지 요청자를 구별하기 위하여 각각의 사용자의 세션에 대해서 임의의 긴 문자열 값인 Session ID를 부여한다. 사용자가 홈페이지 방문시 혹은 인증 로그인시에 생성된다. 이러한 Session ID는 사용자의 계정, 암호, 그 밖의 IP 주소, timestamp 등의 여러 파라미터들을 조합하여 생성할 수 있다.


Session ID는 사용자와 일련의 웹 서핑 동작을 연결시켜줌으로써 웹 사이트 로그인 후 다른 페이지 방문시마다 매번 로그인을 하지 않아도 되는 편리함을 제공해준다.

우리가 신문 홈페이지나 포털 사이트에 들어갈 때 광고 배너가 자동으로 바뀐다던지, 쇼핑몰이나 인터넷 서적몰에서 구매 카트의 목록이 유지되는 것은 모두 이러한 원리이다. 즉, Session ID를 통해 인증과 인가(authentication
& authorization)라는 세션 관리를 수행할 수 있다.


Session ID는 어디에 존재하는가?

Session ID는 우리가 흔히 듣는 쿠키(cookie)라는 곳에 저장되는 것이 일반적이다. 그러나 가끔은 웹 브라우저 주소창 URL이나 HTML 페이지 폼 소스 상의 hidden 필드에 포함되어 드러나기도 한다.

1) 쿠키


2) 웹 브라우저 주소창의 URL


3) 웹 페이지 폼 소스 상의 hidden field



Session ID의 취약성은 무엇인가?

웹 서버에서의 Session ID 생성 기법 및 관리 기법에 따라서 다음과 같은 취약점이 존재할 수 있다.

  • 강력하지 못한 알고리즘(Weak Algorithm) : session ID 스트링 값을 생성함에 있어서 공격자가 reverse 엔지니어링이 가능한 쉬운 알고리즘으로 생성될 경우 cracking이나 brute-force guessing 공격의 위험이 있다.

  • 길이가 짧은 Session ID : 강력한 암호 알고리즘을 사용하더라도 그 길이가 충분하지 않고 짧은 경우에는 cracking이나 brute-force guessing 공격의 위험이 있다.

  • 계정 잠금 기능 미비 : 로그인 패스워드의 특정 회수 실패에 대해서는 보통 계정잠금 기능이나 해당 IP 차단 기능을 구현하고 있습니다. 그러나 보통 Session ID에 대한 무결성 침해나 특성 회수 실패에 대해서는 이러한 잠금 기능 구현이 미비하다. 따라서, brute-force guessing 공격의 위험이 있다.

  • 무한 만료의 Session ID : 사용자의 로그 아웃 이후에도 서버측에서 해당 세션 ID값을 폐기하지 않고 무한정 유효 인정한다면 cookie sniffing이나 프락시 서버의 로그 취득을 통하여 session ID 공격이 가능하다.

  • 평문으로 전달되는 Session ID : 서버에서 클라이언트로의 session ID 쿠키 전달 방식이 비암호화 방식일 경우에는 sniffing을 통하여 해당 값이 노출되어 공격 받을 수 있다. 특히 Session ID 값 자체가 사용자명이나 암호 등의 평문으로 구성되어 있는 경우에는 직접적인 공격이 가능하다.

    위와 같은 취약성에 대한 Session ID 공격의 유형은 다음과 같다.


    Session ID 공격유형

  • 직접적인 Cookie Sniffing을 통한 Session ID 도용
  • 간접 우회 공격을 통한 Session ID 도용
  • Brute-force guessing을 통한 Session ID 도용

    지금까지 Session ID가 무엇인지, 어떤 형태로 존재하는지, 왜 취약한지에 대해서 알아보았다. 다음에는 실제 공격 유형에 대해 살펴보고, 대응 방안에 대해서도 논의해 보도록 하겠다.

  • Session ID 공격 유형

    Session ID에 대한 공격은 크게 2 단계로 이루어진다. 1단계는 Session ID 취득 단계이고 2단계는 취득한 Session ID 공략 단계이다.

     

    [그림1] HTTP Session Hijacking 공격 흐름도

    공격자는 Session ID를 취득하기 위하여 웹 서버와 웹 클라이언트의 트래픽을 직접적으로 스니핑하거나, 웹 서버 상에 공격 코드를 삽입하여 두고 사용자가 클릭할 때 Cookie, Session ID 값을 전송받을 수 있도록 한다. 웹 서버와 웹 클라이언트 사이의 트래픽을 직접적으로 스니핑하는 방법은 일반적인 네트워크 트래픽 스니핑 기법을 통해 가능하다.
  •  

    POST /xxxxxx HTTP/1.1Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, application/x-shockwave-flash, */*Referer: http://xxx.xxx..comAccept-Language: koContent-Type: application/x-www-form-urlencodedAccept-Encoding: gzip, deflateUser-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)Host: ibn.kbstar.comContent-Length: 297Connection: Keep-AliveCache-Control: no-cacheCookie: Session=QJ48621878865

    [그림2] 네트워크 트래픽 스니핑을 통한 HTTP 요청 헤더 안의 쿠키값



    웹 서버 상의 HTML 코드 삽입이 가능한 페이지는 주로 사용자가 글을 게시할 수 있는 게시판이나 자료실 등에 존재한다. 정상적인 글을 게재하는 대신 공격자는 HTML 코드 및 스크립트를 심어 넣는다. 일반 사용자는 해당 게시물을 열람하게 될 때 자신도 모르는 사이 Cookie, Session ID 정보가 제 3 의 공격자 서버나 이메일로 전송되게 된다. (이러한 공격 기법을 Cross-Site Scripting이라고 부른다.)

    <form name=f method=POST action="http//host/hello.php"><input type=hidden name="name"value="<script>alert(document.cookie)</script>"></form><script>f.submit()</script>

    [그림3] 쿠키값 탈취를 위한 HTML 삽입 코드 예


    결과적으로 공격자는 취득한 타인의 Session ID 값을 웹 서버에 요청함으로써 HTTP Session Hijacking 공격을 시도할 수 있다. 물론 이 공격이 성공하려면 Session ID 값이 유효해야 하므로, 사용자가 로그온 한 상태에서 공격이 이루어져야 하거나 Session ID 값의 유지 시간이 긴 경우라는 제한 사항이 필요하다.


    그러나, 기본적으로 잘못 설계된 세션 관리 기법을 사용하고 있는 웹 서버는 이러한 Hijacking 공격에 취약할 수 밖에 없다. 굳이 타인의 Session ID 값을 직.간접적으로 취득하지 않고도 무작위 추측 대입(Brute-force Guessing)함으로써 공격이 가능하다. 공격자는 정상적인 로그인 과정시 분석한 자신의 Cookie 값, 웹 브라우저의 주소창의 URL, 웹 페이지 폼 소스 hidden field 내에 노출된 Session ID 값 자체를 분석한다.

    Session ID 생성 방식의 취약점을 파악한 후, 공격자의 컴퓨터에서 로컬 프록시 툴이나 웹 브라우저 창 URL 주소창에서 직접 Sessioion ID 값 단일 대입을 시도한다. 더 나아가 자동적인 Session ID 대입 스크립트를 작성하여 공격할 수도 있다.

    http://mmail.xxx.co.kr/mletter1/read_mail.asp?id=2266&tableName=musicMail1927&fromEmail=xxx@xxx.co.kr
    http://www.xxx.co.kr/view/UID2305670410000
    http://www.xxx.co.kr/view/UID2305670410341
    http://www.xxx.co.kr/view/UID2305670411302

    [그림4] 정상적인 Session ID 값

    http://mmail.xxx.co.kr/mletter1/read_mail.asp?id=3734&tableName=musicMail1927&fromEmail=xxx@xxx.co.kr
    http://www.xxx.co.kr/view/UID2305670410001
    http://www.xxx.co.kr/view/UID2305670410002
    http://www.xxx.co.kr/view/UID2305670419999

    [그림5] 무작위 추측 대입된 Session ID 값


    대응 방안

    HTTP Session Hijacking 공격에 대하여 웹 어플리케이션 개발자는 다음과 같은 점을 고려하여 세션 관리 기법을 구현하여야 한다.

    1. Session ID 생성 범위값을 사용자 수에 대비하여 충분히 큰 값으로 설정한다.
    2. Session ID는 가능한 한 추측 불가능(random)하게 생성한다. 무작위 추측 대입 공격을 할 때 공격자는 그만큼 더 많은 시간을 투입하여야 하며 현재 연결되어 활성화 되어 있는 유효한 Session ID 값을 찾는 확률은 낮아진다.
    3. Session Timeout 기능과 Session ID 재생성 기능을 사용한다. 일정 시간 동안 활동이 없는 사용자는 새로운 Session ID로 재로그인하도록 하고, 사용자 로그 아웃 시에는 Session ID 값을 폐기한다. 장시간 접속이 필요한 어플리케이션의 경우에는 일정 주기마다 Session ID값을 자동으로 재생성하여 세션을 유지하도록 한다.
    4. 무작위 추측 대입(Brute-force Guessing)에 대비하여 일정 회수 이상의 인증 실패시에는 사용자 잠금 기능을 구현한다.
    5. 로그인 이후에도 중요한 서비스 이용시에는 사용자 인증을 통하여 인가된 사용자만이 해당 서비스를 이용할 수 있도록 통제한다.
    6. Cookie 내용 안의 Session ID와 기타 변수값 자체를 암호화한다.
    7. 웹 서비스 자체의 중요성에 따라 Cookie가 전달되는 방식을 SSL로 구현함으로써 스니핑 공격에 대응할 수도 있다.
    8. 웹 서비스 상에 공격자가 HTML 공격 코드 삽입이 가능한 페이지가 있는지 점검한다. 직접적인 공격 코드 삽입을 차단할 수 있도록 특수 문자 및 스크립트 코드를 필터링하여야 한다.


    지금까지 HTTP Session Hijacking 공격 기법에 대해서 알아보았다. 대부분이라고 말할 수는 없지만 요즘 인터넷에 서비스되고 있는 인기 있는 웹 컨텐츠 서버는 기본적으로 안전한 세션 관리 기법으로 구현되어 있다. 그러나, 웹 컨텐츠의 인지도와는 별도로 이러한 공격 기법에 대한 인지 없이 비보안적으로 구현된 e-메일 카드 서비스, 전자 앨범 서비스 사이트 등이 다수 존재하는 것 또한 현실이다.

    HTTP Session Hijacking공격에 대한 대비는 안전한 웹 어플리케이션 구축을 위한 여러가지 항목 중에 필수적인 한 가지 사항이며, 기획 및 개발구현 단계에서 반드시 고려되어야 한다.

    [출처] 코코넛 시큐레터 2004년 5월호






    _CrtDumpMemoryLeaks() 을 이용한 메모리 누수 탐지법

    작성자 : 김문규
    최초 작성일 : 2009. 3.25

    1. 들어가며
    메모리 누수를 탐지는 방법은 매우 많습니다. 하지만, 실은 잘 모르는 경우가 많습니다. 매크로를 이용하시는 분도 있고, 툴을 사용하시는 분도 있고.... 다양한 방법이 있지만 이번 포스트에서는 _CrtDumpMemoryLeaks()라는 함수를 이용해서 간단하게 누수 지점을 탐지하는 법을 소개하겠습니다.

    2. 문제의 코드
    int main()
    {
     char* pA = new char[20];
     return 0;
    }

    메모리를 할당하였지만, 이를 해제하고 있지 않습니다. (물론 해당 예제에서는 어플리케이션이 종료되기 때문에 상관없지만 할당 후 해제 하지 않았다는 점에 주목하세요.)

    이렇게 간단한 코드이라면 상관없겠지만, 1000 라인 정도에 new만 50개 정도하는 모듈이라고 가정하면 delete[]를 빼먹는 것은 흔하게 있는 실수일 지도 모릅니다.

    3. 탐지하기
    #include <crtdbg.h>
    int main()
    {
     char* pA = new char[20];
     _CrtDumpMemoryLeaks();
     
    return 0;
    }

    적색으로 표시된 2개의 코드를 삽입했습니다. 결과창은 다음과 같습니다.

    메모리 누수가 발견되었다고 알려주네요. 그런데, 어딘지 잘 알 수없는 암호문 같네요.
    하지만, 여기서 주목할 점은 {56} 입니다. {56}이라는 메모리 참조값을 가지는 지점이 해제되지 않았다는 뜻입니다.
    그럼 {56}이 어떤 지점인지 찾으면 되겠네요~
    그래서, 코드를 아래처럼 수정합니다.

    #include <crtdbg.h>
    int main()
    {
     _CrtSetBreakAlloc(56);
     char* pA = new char[20];
     _CrtDumpMemoryLeaks();
     return 0;
    }

    그러면, {56} 메모리 지점을 생성하려고 할 때, 비줠스튜디오가 break할 것인지를 물어봅니다. 여기서, SHIFT+F11 신공으로 몇번만 함수를 빠져나오면 우리 코드 레벨에서 어떤 지점이 문제인지 알 수 있게 됩니다.


    참 쉽죠잉~

    4. 주의할 점
    _CrtDumpMemoryLeaks() 이 함수는 호출되는 시점을 기준으로 해제되지 않은 메모리 영역을 알려주게 됩니다. 즉 정상적으로 delete 함수를 호출하는 코드라고 하더라도 CrtDumpMemoryLeaks()가 delete 함수보다 먼저 호출된다면, 메모리 누수로 감지하게 됩니다. 따라서, 함수의 위치에 따라서 지혜롭게 개발자가 잘 판단해야 합니다. 특히 전역변수의 경우를 잘 판단하셔야 겠지요.

    5. 마치면서
    메모리 누수는 꼭 피해야 하는 경우이며, 이를 위한 기술을 많이 가지고 있으면 좋습니다. 이 중에 가장 간편한 방법이 아닐까 합니다. 주의할 점만 확실히 이해하셨다면 매우 간편하지만 강력한 탐지법이 될 것입니다.




    삼성 모바일 닷컴 오픈

    Samsung Electronics 2009. 3. 25. 12:59 posted by 무병장수권력자



    드디어 삼성모바일닷컴이 오픈했습니다.
    사이트의 기능 및 사이즈에 비해서는 너무나도 짧은 시간에 개발되어서 고생도 너무 많았고, 이슈도 너무 많았습니다.

    23일 0시에 해당 사이트를 대중에게 공개하는 순간, 그 간의 고생과 어려움은 잊혀지고 지금 제 가슴곳에는 수고했다는 나 자신에게의 격려와 충분한 기여를 했다는 자부심을 가지게 되는 군요.

    최신의 기술로 무장한 사이트는 아니지만, 많은 상처를 가지고 절단되어 있던 괴물을 잘 꼬매고 성형 수술도 말끔하게 해내서 이제 제 역할을 하게 되었다는 생각입니다.

    이제 저 녀석이 운영에 넘어가서 훌륭한 놈으로 성장하길 바랄 뿐입니다.

    짧다면 짧은 기간 동안 고생하신 개발팀 여러분 들, 그리고 관계사 여러분들 고생 많으셨습니다.
    spcially, 유,고,김 3분께는 스페셜 땡스를 보냅니다.

    아... 이제 끝났다.


    관련 기사

    삼성전자가 23일부터 국내 휴대폰, PC, MP3플레이어 웹사이트인 애니콜 (www.anycall.com), 자이젠 (www.zaigen.co.kr), 옙(www.yepp.co.kr) 사이트를 통합한 삼성모바일닷컴(www.samsungmobile.com)을 오픈한다.

    삼성전자는 이번 통합으로 휴대폰뿐만 아니라 PC, MP3플레이어의 제품 정보, 쇼핑, 커뮤니티 등 다양한 서비스를 원 스톱(One Stop)으로 제공하여 국내 소비자는 기존에 여러 사이트를 찾아다니던 불편함에서 벗어나 편리하게 이용이 가능하다.

    또한, 삼성전자는 앞으로 삼성모바일닷컴을 통해 음악, 게임과 같이 휴대폰, PC, MP3플레이어에서 공통으로 사용할 수 있는 One Source Multi Use 컨텐츠 제공으로 서비스 경쟁력을 강화할 예정이다.

    삼성전자 미디어 솔루션 센터 이호수 부사장은 "이번 삼성모바일닷컴 통합 오픈으로 소비자들이 보다 편리하고 다양한 삼성전자 서비스를 경험하게 되기를 기대한다"라며 "앞으로도 소비자의 요구를 반영한 다양한 컨텐트와 서비스를 제공할 계획"이라고 밝혔다.

    한편, 기존 애니콜, 자이젠, 옙 브랜드 사이트의 회원들은 간단한 동의 절차를 걸쳐 새로 오픈하는 삼성모바일닷컴 회원으로 전환이 가능하며 오픈 기념으로 23일부터 5주간 다양한 이벤트를 진행한다.
    삼성전자 소개: 삼성전자는 반도체, 통신, 디지털 미디어와 디지털 컨버전스 기술을 보유한 글로벌 리더로, 2007년 63조원의 매출과 7조3천억원의 순이익을 달성했다. 전세계에서 12만8천명의 직원을 고용하고 있으며 세계 51개국에 90개가 넘는 오피스를 열고 있다. 삼성전자는 디지털 어플라이언스 부문, 디지털 미디어 부분, LCD부분, 반도체 부분, 통신 네트워크 부분 등 5개 부문으로 이루어져 있다. 세계에서 가장 빠르게 성장하는 브랜드인 삼성전자는 디지털 TV, 메모리 반도체, 휴대폰, TFT-LCD 분야에서 세계 선두 주자이다.



    'Samsung Electronics' 카테고리의 다른 글

    리더쉽 교육을 마치고 왔습니다.  (0) 2009.08.14
    혁신 제품을 만들어 주세요!  (0) 2009.08.06


    struts2 샘플 프로젝트 시작하기

    개발 노트 2009. 3. 6. 16:26 posted by 무병장수권력자


    Struts2 샘플 프로젝트로 시작하기

    작성자 : 김문규
    최초 작성일 : 2009.3.6

    1. struts2 란?
    모델 2 기반 MVC패턴으로 웹 애플리케이션을 쉽게 개발하기 위한 프레임워크입니다.
    여러가지 좋은 기능이 있지만, View(jsp)와 Controller(beans)간에 논리의 이동을 정의하기가 매우 수월하다는 장점이 있습니다.

    2. 프레임워크 설치하기
    http://struts.apache.org/ 에서 프레임워크를 다운 받을 수도 있고, 관련된 튜토리얼도 제공 받을 수 있습니다.

    3. 샘플 웹 프로젝트
    본 포스트는 JAVA/JSP를 이용해서 웹 애플리케이션을 구현하는 것의 기본적인 지식이 있으신 분들을 대상으로 합니다.
    다음은 소스 프로젝트 입니다. 이클립스 프로젝트 이며, ant 빌드 파일을 포함합니다.
    성공적인 빌드를 위해서는 위에서 다운로든 받은 jar 중 다음을 buildpath에 추가해 주세요.
     - common-fileupload-1.2.1.jar
     - commons-logging-1.0.4.jar
     - freemarker-2.3.13.jar
     - ognl-2.6.11.jar
     - struts2-core-2.1.6.jar
     - xwork-2.1.2.jar



    1) web.xml 에 struts 필터 걸기
    아래의 내용이 추가되어야 합니다. 모든 입력을 struts라는 필터에서 처리하라는 의미입니다.

    <web-app>
      <filter>
       <filter-name>struts2</filter-name>
       <filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
      </filter>
     
      <filter-mapping>
       <filter-name>struts2</filter-name>
       <url-pattern>/*</url-pattern>
      </filter-mapping>
    </web-app>

    2) struts.xml 정의하기
    해당 파일은 classes
    해당 파일은 struts의 FilterDispatcher가 사용하게 되며, view와 controller간의 관계를 명시하고 있습니다.
    다음은 helloworld, logon, menu라는 action을 정의하고 있으며, 
    http://주소/context_root/helloworld.action
    http://주소/context_root/logon.action
    http://주소/context_root/menu.action
    라는 주소로 호출될 수 있습니다.
    action element를 간략하게 설명하면 다음과 같습니다.
     name : 이름
     class : 호출되는 controller
     result : result-name에 따라 이동할 다음 액션, 페이지

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE struts PUBLIC
      "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
      "http://struts.apache.org/dtds/struts-2.0.dtd">
      
    <struts>
     <package name="tutorial" extends="struts-default">
      <action name="helloworld" class="tutorial.HelloWorld">
       <result>/HelloWorld.jsp</result>
      </action>
      
      <action name="logon" class="tutorial.Logon">
       <result name="success" type="redirectAction">menu</result>
       <result name="input">/Logon.jsp</result>
      </action>

      
      <action name="menu" class="tutorial.Menu">
       <result name="success">/Menu.jsp</result>
       <result name="error">/Logon.jsp</result>
      </action>
     </package>
    </struts>

    3) Controller 구현하기
    위의 struts.xml에 정의된 tutorial.Logon의 코드를 보여드리고 중요한 부분만 알아보겠습니다.
    request를 처리하는 Controller 클래스는 ActionSupport를 상속하며, 기본적으로 execute()로 request를 전달합니다.
    중간에 비지니스 로직이 약간 있어서 헷갈리실 수 있지만, 중요한 것은 return 값입니다.
    SUCCESS는 실제로는 "success"를 가리키는 값이며 이는 struts.xml의 result-name이 success인 설정과 연관을 맺게 됩니다.
    따라서, execute()가 success를 반환하면, struts는 현재 request를 menu.action으로 재전송 하게됩니다.
    참 쉽죠? ^^
    그 외에도 ERROR, INPUT, LOGIN, NONE가 정의되어 있습니다.

    package tutorial;
    import java.util.Map;
    import com.opensymphony.xwork2.ActionContext;
    import com.opensymphony.xwork2.ActionSupport;
    public class Logon extends ActionSupport{ 
     private String username;
     private String password;
     
     @Override
     public String execute() throws Exception {
      if (isInvalid(getUsername())) return INPUT;
      if (isInvalid(getPassword())) return INPUT;
      
      Map<String, Object> session = ActionContext.getContext().getSession();
      session.put("id", getUsername());
      
      return SUCCESS;
     }

     
     public String getUsername() {
      return username;
     }
     public void setUsername(String username) {
      this.username = username;
     }
     public String getPassword() {
      return password;
     }
     public void setPassword(String password) {
      this.password = password;
     }
     private boolean isInvalid(String value)
     {
      // Originally, I should authenticate this id/passwd.
      // Because of test project, I am not willing to implement this functionality. 
      return (value == null || value.length() == 0);
     } 
    }

    4) 돌려보기
    아파치 설치하시고
    build.properties에서 아파치 설치 폴더를 맞추시고
    ant로 빌드하시면
    아파치의 webapp에 꽂히고 실행이 됩니다.
    enjoy~

    4. 마치면서
    struts는 꽤 오래전부터 굉장히 인기를 끌고 있는 MVC 프레임워크 입니다. 특히 말씀드린데로 view와 controller의 연결이 매우 편리하기 때문에 다른 프레임워크와도 같이 혼용되는 경우도 많습니다. 대표적으로 spring과 struts의 조합을 들 수 있습니다.
    아직 경험이 없는 웹 개발자이시라면, 빨리 수박 겉핥기라도 시작하시길 바랍니다. like me~









    [C/C++] 매크로 함수

    개발 노트 2009. 1. 30. 10:11 posted by 무병장수권력자


    유용하게 사용하는 매크로 함수

    작성자 : 김문규
    최초 작성일 : 2009. 1.30

    예시 겸 작성법
    #include <stdio.h>
    // 최대값 구하기
    #define MAX(a,b) ( (a) > (b) ) ? (a):(b)
    // 대문자로
    #define UPCASE(c) (( (c)>='a' && (c)<='z') ? (c)-('a'-'A') : (c) )
    // 소문자로
    #define LOWCASE(c) (( (c)>='A' && (c)<='Z') ? (c)+('a'-'A') : (c) )
    // 행 분리 시에는 '\' 사용
    #define ASSERT(x) if (!(x) ) \
                      {          \
                         printf("assert failed. %s(%d) at %s %s(%s)\n", __FILE__, __LINE__, __DATE__, __TIME__, __TIMESTAMP__); \
                      }
    // #은 "" 처럼 동작
    #define DPRINT(expr) printf(#expr " = %g\n", expr )
    // ##은 문자 붙이기
    #define TPRINT(expr) printf("%d, %d\n", expr##1, expr##2)
    void main()
    {
     printf("%d\n", MAX(5,3) );
     printf("%g\n", MAX(5.4,3.2) );
     printf("%c\n", UPCASE('a'));
     printf("%c\n", LOWCASE('A'));
     ASSERT(0);
     double x = 3.4;
     double y = 2.0;
     DPRINT(x/y);    // printf("x/y" " = %g\n", expr)
     int a1 = 10, a2 = 20;
     TPRINT(a);
    }


    [C/C++] Singleton Class (싱글톤 클래스)

    개발 노트 2009. 1. 29. 15:04 posted by 무병장수권력자


    Singleton Class (싱글톤 클래스) in C++

    작성자 : 김문규
    최초 작성일 : 2009. 1.29

    1. 들어가며
    싱글톤은 전역 객체가 필요할 경우에 유용하게 사용할 수 있습니다. 이에 대해서 구현하는 방법을 생각해 볼까 합니다.

    2. 가벼운 구현
    #include <iostream>
    using namespace std;
    class CSingleton
    {
    private:
     static CSingleton *pInstance;
     CSingleton() : m_iValue(10) {};
     ~CSingleton() {}
     
     int m_iValue;
    public:
     static CSingleton& getInstance()
     {
      if(pInstance == NULL) pInstance = new CSingleton;
      return *pInstance;
     }
     static CSingleton* getInstancePtr()
     {
      if(pInstance == NULL) pInstance = new CSingleton;
      return pInstance;
     }
     static void releaseInstance()
     {
      if(pInstance != NULL) delete pInstance;
     }
     void setValue(int in) { cout << m_iValue << "-->" ; m_iValue = in; cout << m_iValue << endl; }
     int getValue() { return m_iValue; }
    };
    CSingleton *CSingleton::pInstance = NULL;
    int main(){
     CSingleton::getInstance().setValue(3);
     CSingleton::getInstance().setValue(5);
     CSingleton::releaseInstance();

     return 0;
    }

    구현의 핵심은 아래와 같습니다.
    1) 생성자를 private으로 감추어서 새로운 객체가 외부에서 마구 생성되는 것을 방지한다.
    2) 유일하게 생성된 객체에 접근하는 방법을 제공한다. getInstance()가 여기에 해당!


    대부분의 경우에는 위와 같은 패턴을 그대로 적용하시면 Singleton Class를 사용하실 수 있습니다.

    3. 고민 1 : 메모리 해제
    new로 생성한 인스턴스를 삭제하지 않아도 될까요? 프로그램이 종료되는 시점에는 어차피 new로 생성한 메모리까지 싸그리 해제가 됩니다. 따라서, 어차피 생성한 목적이 프로그램 종료시까지 단 하나의 객체가 동작하는것이라면 그 크기가 어마어마한 것이 아니라면 명시적으로 메모리 해제를 수행할 필요는 없다는 거지요.
    다만, 외부 시스템과 정리해야 하는 것이 있다면 명시적인 해제 함수(releaseInstance)를 만드시고 실행하여야 하겠습니다.

    4. 고민 2
    그냥 static 변수로 CSingleton을 정의하면 어떨까요? 왠지 찝찝하지 않고 깔끔하게~
    static CSingleton pInstance;

    좋은 생각이네요~ 하지만 다른 문제가 다시 발생합니다. static 객체는 생성의 시점이 명확하게 정의되지 않습니다. 따라서, 다른 전역 객체에서 이를 참조하고자 할 때는 생성 순서 때문에 문제가 발생할 수 있습니다. 그래서, 소위 위의 예시와 같은 '늦은초기화'를 수행합니다. (new를 이용한 동적 생성)
    의견이 분분하지만, new를 이용한 방법이 조금 더 안전해 보입니다.

    5. 템플릿을 활용한 구현
    그런데, 앞선 구현은 영 모양새가 안 나옵니다. 이런식이라면 싱글톤으로 구현해야 하는 클래스마다 해당 패턴을 적용해야 해야 합니다.
    템플릿과 상속을 이용해서 약간 폼나게 바꾸어 보겠습니다.
    #include <iostream>
    using namespace std;
    template <typename T>
    class CSingleton
    {
    private:
     static T *pInstance;
    public:
     static T& getInstance()
     {
      if(pInstance == NULL) pInstance = new T;
      return *pInstance;
     }
     static T* getInstancePtr()
     {
      if(pInstance == NULL) pInstance = new T;
      return pInstance;
     }
     static void releaseInstance()
     {
      if(pInstance != NULL) delete pInstance;
     }
    };
    class Test
    {
    public:
     Test() : m_iValue(10) {};
     ~Test() {};
     void setValue(int in) { cout << m_iValue << "-->" ; m_iValue = in; cout << m_iValue << endl; }
     int getValue() { return m_iValue; }
    private:
     int m_iValue;
    };
    Test* CSingleton<Test>::pInstance = NULL;
    int main(){
     cout << "Singleton 객체 생성" << endl;
     CSingleton<Test>::getInstancePtr()->setValue(3);
     CSingleton<Test>::getInstancePtr()->setValue(5);
     cout << "Singleton 객체 해제" << endl;
     CSingleton<Test>::releaseInstance();
     
     return 0;
    }

    이제 뭔가 기능의 분리가 확실히 되었다는 느낌이 옵니다. Test라는 클래스를 Singleton 객체로 사용할 수 있게 했습니다. 제 생각에는 이 정도면 충분해요 ^^
    물론 Singleton의 속성을 상속하고 있지 않기 때문에 new로 개별 생성도 가능합니다. 이 문제는 제가 고민을 많이 했지만 결론은 프로그래머의 선택에 넘겨야 할 듯합니다. 이게 정상인지..싱글톤 클래스라면 new로 생성이 안되야 하는데... Test의 생성자를 private으로 선언할 수도 없고...흠 하튼 이부분은 잘 모르겠어요 ^^;
    이 부분에 대해서 좀 더 깊은 지식이 있으신 고수분께서 좋은 방법을 설명해 주시면 좋겠어요~


    6. 마치며
    싱글톤은 전역변수를 대신에 사용할 수 있는 구현 패턴입니다. 전역 변수 자체는 생성되는 시점과 해제되는 시점이 매우 모호하기 때문에 칼코딩시에 문제를 일으킬 수 있기 때문입니다. 이에 반해 싱글톤은 getInstance가 호출되는 순간이 해당 변수가 생성되는 시점이기 때문에 그 시점이 명확하다 할 수 있습니다.
    이처럼 싱글톤은 프로그램의 상태를 관리할 때나 전역에서 관리되어야 할 변수가 있는 경우에는 편리하게 혼돈없이 에러의 가능성을 줄이면서 사용할 수 있습니다.
    근데..아직도 모호해요. ㅎㅎ







    [C/C++] 연산자 오버로딩 (Operator Overloding)

    개발 노트 2009. 1. 28. 17:09 posted by 무병장수권력자


    연산자 오버로딩 (Operator Overloding)

    작성자 : 김문규
    최초 작성일 : 2009. 1.28

    1. 들어가며
    클래스끼리도 연산이 필요할 때가 있습니다. 단순한 예로 string + string = concatenated string 입니다. 바로 어떻게 하면 되는 지 간단하게 알아 보겠습니다.

    2. 연산자 오버로딩

    반환타입 operator 연산자 (인자...)

    뭐 감이 오지 않습니다. 예제를 보세요.
    #include <iostream>
    #include <string>
    using namespace std;
    class CStr
    {
    public:
     CStr(string _in) : m_pString(_in) {}
     CStr operator + (CStr _in);
     string getStr() { return m_pString; }
    private:
     string m_pString;
    };
    CStr CStr::operator + (CStr _in)
    {
     m_pString += _in.m_pString;
     return *this;
    }
    int main(){
     CStr a("aaa");
     CStr b("bbb");
     CStr c = a + b;
     cout << c.getStr() << endl;
     return 0;
    }

    결과는 아래와 같습니다.


    주목할 부분은 빨간색으로 표시된 부분입니다. 입력 인자와 반환 객체의 관계만 잘 이해하면 됩니다.
    예시에서는 _in은 CStr b가 됩니다. 그리고 this는 CStr a가 됩니다.

    3. 마치며
    간단하지만 엄청나게 유용하고 자주 사용하는 기술입니다. 꼭 알아둡시다.



    [C/C++] 동적 바인딩, virtual의 사용

    개발 노트 2009. 1. 28. 15:19 posted by 무병장수권력자


    동적 바인딩 (Dynamic Binding)

    작성자 : 김문규
    최초 작성일 : 2009. 1.28

    1. 들어가며
    C++은 다형성을 지원하는 객체 지향 언어입니다. overriding을 통해 함수 재정의가 가능하지요. 그런데 복잡한 상속 개념은 때로는 우리가 기대하지 않는 방향으로 프로그램을 동작하게 합니다. 동적 바인딩은 그 대표적인 예 입니다.

    2. 나의 기대

    #include <iostream>
    using namespace std;
    class CParent
    {
    public:
     CParent() { cout << "Creating Parent" << endl; };
     ~CParent() { cout << "Deleting Parent" << endl; }
     void DoSomething1() { cout << "Doing something 1 in Parent" << endl; }
     void DoSomething2() { cout << "Doing something 2 in Parent" << endl; }
    };
    class CChild : public CParent
    {
    public:
     CChild() { cout << "Creating Child" << endl; };
     ~CChild() { cout << "Deleting Child" << endl; }
     void DoSomething1() { cout << "Doing something 1 in Child" << endl; }
     void DoSomething2() { cout << "Doing something 2 in Child" << endl; }
    };
    int main(){
     
     cout << "Case 1 : parent class를 그대로 사용할 경우" << endl;
     CParent *pParent1 = new CChild();
     pParent1->DoSomething1();
     pParent1->DoSomething2();

     delete (pParent1);
     return 0;
    }

    인터페이스처럼 사용해볼 요량으로 빨간색처럼 코딩을 했습니다. 실체는 CChild이기 때문에 파란색으로 표현된 코드에서는 CChild의 DoSomething2()가 호출될 것으로 생각했습니다. 결과는 아래와 같습니다. 제 기대와는 딴판이었지요. 이런! 메모리 해제도 제대로 되지 않았네요. 흠 좌절입니다...



    3. 동적 바인딩
    앞에서의 오동작의 이유는 pParent 객체 포인터가 동적 바인딩이 되지 않았기 때문입니다. C++은 기본적으로는 정적 바인딩을 지원합니다. 즉, 해당 객체를 담는 변수에 따라서 컴파일 시에 정적으로 링크가 되어 버린다는 뜻이지요. pParent는 실제로는 CChild를 가리키고 있지만 CParent 타입의 포인터이기 때문에 pParent는 CParent 객체를 가리키고 있는 것이지요.
    그럼 어떻게 해야 할까요? virtual 키워드로 함수를 선언하면 됩니다.
    virtual로 선언된 경우에는 런타임에 동적으로 바인딩되기 때문에 pParent가 가리키고 있는 CChild의 함수를 호출하게 됩니다.

    비교를 쉽게 하기 위해서 DoSomething1()만 virtual로 선언해 보았습니다.
    class CParent
    {
    public:
     CParent() { cout << "Creating Parent" << endl; };
     ~CParent() { cout << "Deleting Parent" << endl; }
     virtual void DoSomething1() { cout << "Doing something 1 in Parent" << endl; }
     void DoSomething2() { cout << "Doing something 2 in Parent" << endl; }
    };

    결과는 이렇습니다. 오호~ 진작 이랬어야죠. (근데 아직도 소멸자는 제대로 호출이 안되네요. TT)



    4. 소멸자 동적 바인딩
    여기서 하나의 의문을 더 해결해 보겠습니다. 왜 Child의 소멸자는 호출되지 못했을까요? 이유는 앞선 예들과 같습니다. 정적바인딩 되어 있는 경우에는 CParent 객체의 소멸자만 호출하면 되기 때문이지요. 따라서, CChild의 소멸자가 제대로 호출되게 하려면 소멸자를 virtual 키워드로 선언해야 합니다.
    class CParent
    {
    public:
     CParent() { cout << "Creating Parent" << endl; };
     virtual ~CParent() { cout << "Deleting Parent" << endl; }
     virtual void DoSomething1() { cout << "Doing something 1 in Parent" << endl; }
     void DoSomething2() { cout << "Doing something 2 in Parent" << endl; }
    };

    이제야 드디어 의도한 대로 동작하게 되었습니다.



    5. 마치면서
    대부분 상속을 압니다. 단순하게 상속만 알아서 그 개념만 사용하면 문제가 안되지요. 위의 예들도
    CChild *pChild = new CChild()
    로 선언했다면 아예 문제가 되지 않았을 겁니다.
    하지만, CParent로 선언해야 하는 경우가 있을 겁니다. 객체 추상화를 시도하는 경우가 좋은 예일 겁니다.
    이처럼 C++의 객체 지향의 속성을 점점 알아가면서 하나둘씩 적용하다 보면 의도하지 않던 오류가 발생하기도 합니다. 그래서 항상 기본이 중요한 것 같네요.
    그럼 즐프하세요!