본문 바로가기
IT-개발,DB

[개발/웹] 웹에서의 비동기 호출 : 고전적인 방식(FRAME 이용)

by SB리치퍼슨 2015. 1. 29.

웹에서의 비동기 호출 : 고전적인 방식(FRAME 이용)


http://www.taeyo.pe.kr/lecture/NET/scriptCallback01.asp

강좌 최초 작성일 : 2005년 12월 02일
 i_bullet_03.gif  강좌 최종 수정일 : 2005년 12월 05일

 i_bullet_03.gif  강좌 읽음 수 : 13258 회

 i_bullet_03.gif  작성자 : Taeyo(김 태영)
 i_bullet_03.gif  편집자 : Taeyo(김 태영)

 i_bullet_03.gif  강좌 제목 : 웹에서의 비동기 호출 : 고전적인 방식(FRAME 이용)

강좌 전 태오의 잡담>

다양한 행사가 있다보니 강좌가 많이 늦어졌습니다. 수많은 세미나에 Ready 2005 행사까지...
정신이 없었던 2005년도 가을이네요... 이제 성큼 겨울도 발 앞에 다가와 있습니다. 모두들 한해 마무리 잘 하세여~


이번 강좌에서는 고급 웹 프로그래밍을 위해서 알아두면 매우 좋은 주제인, 스크립트 콜백(Script Callback)이라는 기법에 대해서 알아보도록 하겠습니다. 웹 페이지에서의 비 동기 호출을 가능하게 하는 것이 바로 스크립트 콜백이라는 기법이라 할 수 있는데요. 전혀 새로운 개념이라고 볼 수는 없고요. 기존 ASP 시절부터 이미 편법 식으로 구현해서 사용하고 있던 것이 깔끔하게 정리된 것이라 할 수 있습니다. 물론, IFRAME을 이용했던 ASP 시절의 편법은 완전한 비 동기 호출이라고 볼 수는 없습니다만, 결과적으로는 비동기적으로 구동되는 것처럼 보이기에 저는 그 또한 비 동기 호출방식의 범주에 넣어서 이야기 드리고 있습니다.

핫?? 지난 강좌에서 언급했던 HttpModule 구현 및 적용의 실제 구현사례 강좌는 어디가고 지금 비동기 호출이라는 주제로 마치 춘삼월 새내기의 풋풋한 화장품 냄새마냥 깝죽거리는 것이냐구요??? -_-+++ (넵. 메가작가 스탈의 개그를 한번 써본 것 맞습니다)

그게 말입니다. 인생에는 가끔 기다림도 필요한 것입니다. 그렇지 않습니까??? -_-;;;
엇? 저기 UFO닷!!!! 저기저기!!! (지금이닷!! 냅다 ㅌㅌ)

그렇다면, 이야기를 더 진행하기에 앞서 도대체 스크립트 콜백 즉, 큰 의미로서 비 동기 호출이라는 것이 무엇인지 먼저 살포시 알아보고 넘어가도록 하겠습니다.

스크립트 콜백(Script Callback)이라는 것은 다음과 같습니다.

- 일종의 클라이언트 대 서버의 비 동기 통신
- 웹 페이지를 갱신하지 않고, 서버의 모듈을 실행하여 그 결과를 페이지에 반영하는 기법.
   (페이지 전체를 [새로 고침] 할 필요가 없으며, 스크립트를 이용해 페이지의 일부 구역만을 동적으로 변경하는 방식을 사용합니다)
- ASP 시절에 소개되었던 Remote Scripting의 진화된 버전

사실, 웹(Web)이라는 환경 자체는 비 동기 호출이 어려운 구조입니다. 즉, HTTP 요청/응답 패턴은 콜백이 어려운 구조라는 이야기인 것이죠. 콜백을 가능하게 하려면 요청을 보내는 HTTP 채널과 응답을 받는 채널이 페이지와는 별도로 각기 존재해야 하는데, 그렇게 구성하는 것은 간단한 일이 아닙니다(물론, 매우 복잡한 것도 아니긴 하지만요~~ 헤헤, 이랬다가 저랬다가~). 게다가, 비 동기적인 호출은 사용자의 눈에는 보이지 않게 일어나야 할 뿐 더러, 어찌 어찌하여 서버에서 데이터를 받았다 하더라도, 그 응답 결과를 원래의 페이지에 반영해야 하기 위해서는 다시 동적 스크립트 기술을 이용해야 하기에 해야 할 작업량도 상당합니다.

이러한 상당한 작업량은 실제로 고전적인 접근에서는 손 수 다 작성하기도 했었던 것입니다. 갑자기 지난 노가다(?)의 시절을 떠올리니, 손가락이 떨리면서 글이 쓰기 싫어지려 하네요. 에잇~ 안 좋은 추억에서는 어서 빨리 깨어 나야 하겠습니다. ^^ 레드쏜!

웹 프로그래밍 상에서의 비 동기 호출을 위한 다양한 시도는 현재까지도 꾸준하게 이어지고 있습니다. XMLHTTP를 이용한 비동기 호출방식이 소개되었었고, 그리고, 이를 이용하기 편하게 컴포넌트화 시켰던 WebService Htc 기법을 이용한 비 동기 호출도 소개가 되었었죠. 안타깝게도, WebService Htc 기법은 현재는 지원을 더 이상 하지 않고 있습니다만, 이 기법을 이용해서 만들어진 사이트는 여럿 있는 것으로 알고 있습니다(이 기법은 지금도 이용할 수 있기는 합니다. 단지, 문제 발생시 MS로부터 지원을 받지 못한다는..., 하긴, 직접 고쳐서 쓰면 되긴 하지만..).

그리고, 이러한 비 동기 호출은 최근 주목받는 단어인 AJAX(Asynchronous JavaScript and XML)라는 것으로까지 발전하고 있죠. MS에서도 Atlas라는 코드명으로 AJAX를 위한 프레임워크를 준비 중이기도 하구요. 즉, 웹 프로그래밍 상에서의 비 동기 호출은 더 이상 중, 고급 개발자들만을 위한 어려운 기술이 아닌, 보편적인 기술로서 받아 들여지고 있다는 이야기일 것입니다.

이 연재 강좌에서는 이 모두를 전부 다룰 예정에 있습니다. "과연? -_-+" 이라고 의심의 눈초리를 보내시는 분들이 매우 많네요. 음.. 그리고 보니, 조금은 무리스럽다고 느껴지기도 하긴 하는데요. 하하. 하지만, 그래도 꿋꿋이 진행을 해 보려 합니다. 되던 안되던, 안될 것을 미리 걱정해서 시작조차 안하고 싶지는 않거든요 ^^

이번 연재 강좌는 웹 상의 비 동기 호출에 대한 기존, 현재, 미래 방식을 모두 알아보는 식으로 꾸며보려 합니다. 특히, 이번 강좌에서는 ASP나 ASP.NET 페이지 혹은 다른 동적 웹 페이지(php, jsp)에서도 사용할 수 있는 오래된 비 동기 호출 편법(?)을 먼저 소개하도록 하고요. 이어지는 강좌에서는 다음의 것들을 소개해 보려 합니다.

1. 고전 편법(Hidden IFRAME)을 이용한 비동기 호출 (이번 강좌)
2. XMLHTTP를 이용한 비동기 호출
3. WebService Htc를 이용한 비동기 호출(더이상 지원되지 않는 기술이기에 다루지 않을 수도 있습니다)
4. ASP.NET 2.0 스크립트 콜백
5. AJAX를 이용한 비동기 호출

그럼 이제 시작해 볼까요?

고전 편법(Hidden IFRAME)을 이용한 비동기 호출

기존에는 비 동기식(!) 호출을 구현하기 위해서 프레임을 사용하곤 했는데요. 사용자 몰래 서버로 Request/Response를 수행하기 위해 눈에 보이지 않게 설정한 별도의 프레임(Hidden)을 주로 이용하곤 했습니다. 예를 들면, 다음 그림과 같이 말이죠.



이 그림은 기존 동적 페이지 기술에 비 동기식 호출 기능을 적용하는 방법을 보여주고 있습니다(이 외에도 많은 방법이 있을 수 있지만, 주로 이 방법들을 많이 이용했습니다).

그림을 간단하게 설명하자면, 하단에 눈에 안 보이게 설정된 IFRAME을 통해서 사용자의 눈에 보이지 않게 서버로부터 필요한 데이터를 가져와서, 그 데이터를 스크립트를 통해서 원래의 부모 페이지에 반영하는 방식입니다

예제는 직급별 직원목록을 나열하는 예를 사용하고 있는데요. 일단, 페이지는 두 개의 DropDown 컨트롤을 포함하는 구조로 구성되어 있습니다. 첫 번째 DropDown(직급목록)에는 직급의 목록이 기본적으로 출력되며, 두 번째 DropDown(직원목록)의 경우는 첫 번째 DropDown에서 선택된 직급에 해당하는 직원들의 이름이 출력됩니다. 즉, 매번 직급목록 DropDown이 변경될 때마다, 동적으로 그 직급에 해당하는 직원들의 목록을 가져와서 두 번째 DropDown(직원목록)에 로드 하겠다는 것이죠. 물론, 처리 로직이 어려울 것은 전혀 없습니다. 이런 류의 데이터 조회는 매우 흔하게 하는 작업 중 하나이니까요. 중요한 것은 이번 예제에서는 그러한 처리를 페이지의 새로 고침 없어, 비 동기적인 방식으로 수행한다는 것입니다. 그것이 바로 포인트이죠.

즉, 첫 번째 DropDown(직급목록)에서 선택이 바뀔 경우에는, 해당 직급에 따라 직원들의 목록을 가져오는 asp 페이지(혹은 aspx 페이지)를 하단의 IFRAME에 로드합니다. IFRAME에 asp(혹은 aspx) 페이지가 로드 되면, 그 asp(혹은 aspx) 페이지는 가져온 결과를 클라이언트 스크립트를 통해서 부모 페이지의 두 번째 DropDown(직원목록)에 동적으로 구성해 넣는 것이죠.

이로써, 페이지는 새로 고침이 일어나지 않으면서 동적으로 데이터가 채워지는 형태의 개발이 가능한 것입니다. 물론, 사실상 내부적으로는 별도의 페이지가 로드 되었기에, 새로 고침이 일어난 것이나 별반 차이가 없어 보일 수 있습니다만, 사용자의 측면에서 보면 이것은 매우 효과적입니다. 기존 페이지의 데이터가 사라지는 일도 생기지 않을 뿐 더러, 직급별 데이터를 가져오는 동안 다른 작업도 페이지에서 수행할 수 있으니 말입니다.

그렇다면, 예제 소스를 한번 살펴보도록 하겠습니다. 먼저, 부모 페이지 격에 속하는 메인 페이지를 살펴보도록 할까요? 사실 asp 페이지 소스나 aspx 페이지 소스나 별반 차이가 없을 것이기에, 예제는 aspx 페이지로 진행하도록 하구요. 다운로드 받을 수 있는 소스에는 asp 소스도 같이 넣어드리도록 하겠습니다. ^^

Client.aspx

<%@ Page language="c#" Codebehind="Client.aspx.cs" AutoEventWireup="false" Inherits="CallbackEx.Case_ASP.Client" %>
<HTML>
  <HEAD>
    <LINK href="../Styles.css" type="text/css" rel="stylesheet">
    <script language="javascript">
      function CallScript()
      {
        var title = document.all.title.options[document.all.title.selectedIndex].value;
        CallBackFra.location.href = "Callback.aspx?Title=" + title;
      }
    </script>
  </HEAD>
  <body>
    <form runat="server">
      <asp:DropDownList id="title" runat="server"></asp:DropDownList>
      <select id="Lname" NAME="Lname" disabled>
        <option value="-" selected>직급을 선택해 주세요</option>
      </select>
      <input type="text">
      <p>
        <IFRAME id="CallBackFra" width="400" Height="50"></IFRAME>
      </p>
    </form>
  </body>
</HTML>

위의 소스에서 주의해서 봐야 할 부분은 3군데(파란색 소스) 입니다. 첫 번째 주의 포인트는 DropDown(직급목록)에서 선택이 바뀔 경우에는 호출되는 자바스크립트입니다. 이는 현재 선택된 DropDown(직급목록)의 값을 인자로 하여, IFRAME에 Callback.aspx 페이지를 요청하는 역할을 수행합니다. 즉, 눈에 보이지 않게 비 동기 호출을 시작하는 부분이 되겠네요~

두 번째 주의 포인트는 DropDown(직급목록)을 위한 서버 컨트롤입니다. 코드 비하인드에서는 Page_Load 시에 이 DropDown 컨트롤을 직급 데이터로 채우게 됩니다. 해서, 페이지가 처음 뜰 때, DropDown(직급목록) 컨트롤에는 직급 데이터가 모두 채워져서 나오게 됩니다. 이 부분과 관련된 코드는 잠시 뒤에 보여드리겠습니다.

세 번째 주의 포인트는 바로 IFRAME 입니다. 이 프레임이 바로 비 동기 호출의 별도 채널역할을 하게 됩니다. 현재는 IFRAME이 눈에 보여지고 있습니다만, 개발이 완료되는 시점에서는 이 부분을 눈에 보이지 않게 처리할 것입니다. 그럼으로써, 완벽하게 사용자를 속일 수 있게 되는 것입니다.

그럼, 이제 코드 비하인드 쪽의 소스를 한번 살펴볼까요?

Client.aspx.cs

    public class Client : System.Web.UI.Page
    {
        //데이터베이스 연결 문자열(이 부분은 자신의 PC에 맞게 변경해 주세요)
        private const string conStr = "server=(local);database=Northwind;uid=sa;pwd=1";
        protected System.Web.UI.WebControls.DropDownList title;

        private void Page_Load(object sender, System.EventArgs e)
        {
            //직급용 DropDown에 클라이언트 스크립트 연결하기
            title.Attributes.Add("onchange", "CallScript()");

            if (!this.IsPostBack)
            {
                //직급 데이터를 직급 DropDown 컨트롤에 바인딩한다
                string sql = "SELECT distinct Title as Title FROM Employees";

                SqlConnection con = new SqlConnection(conStr);
                SqlCommand cmd = new SqlCommand(sql, con);
                con.Open();

                title.DataTextField = "Title";
                title.DataValueField = "Title";

                title.DataSource = cmd.ExecuteReader();
                title.DataBind();

                con.Close();
            }
        }

        .. 중략 ..

역시나, 코드 비하인드 쪽에는 크게 어려운 부분은 없습니다. 주석이 설명하고 있는 대로, 서버에서 직급 데이터를 가져와서 DropDown 컨트롤에 채우는 부분이 주가 되겠네요. ^^

자. 소스가 완료되었으니 현재까지의 작업 결과를 브라우저로 한번 살펴보도록 하겠습니다. 다음과 같은 결과가 브라우저로 나타날 것입니다.



현재는 첫 번째 DropDown 컨트롤에서 선택을 바꾸어도 별다른 변화가 없을 것입니다. 왜냐하면, 실제 선택 변경 시, 비동기 작업이 일어나는 Callback.aspx 페이지가 아직 작성되지 않았기 때문이죠.

자. 그럼 이제 Callback.aspx 페이지로 들어가 볼까요? 이 페이지는 별도의 UI가 존재하지 않습니다. 주 역할이 QueryString으로 넘어오는 직급 데이터를 얻어와서, 그를 가지고 데이터베이스를 조회하고, 해당 직급에 맞는 직원들의 이름을 가져와 동적으로 부모 페이지의 DropDown 컨트롤에 꽂아 넣기만 하면 되니까요~

해서, Callback.aspx 페이지의 HTML 코드는 다음과 같습니다.

Client.aspx.cs

<%@ Page language="c#" Codebehind="Callback.aspx.cs" AutoEventWireup="false" Inherits="CallbackEx.Case_ASP.Callback" %>
<script language="javascript">

    //DropDown에서 기존값들을 모두 제거하는 함수
    function ClearAll()
    {
        var count = parent.document.all.Lname.options.length;
        for(var i = 0; i < count; i++)
            parent.document.all.Lname.options.remove(0);
    }

    //DropDown에서 직원 이름을 추가하는 함수
    function AddOption(value, text)
    {
        var oOption = document.createElement("OPTION");
        oOption.text = text;
        oOption.value = value;
        parent.document.all.Lname.options.add(oOption);
    }

    ClearAll();
    parent.document.all.Lname.disabled = false;

</script>
<HTML>
    <body>
        <form runat="server" ID="Form1">

            // 이하에 실제 데이터를 콤보박스에 채우는 클라이언트 스크립트가 들어와야 한다.
            // 이는 서버에서 동적으로 만들어서 넣어주어야 한다.

        </form>
    </body>
</HTML>

그렇습니다. 코드의 대부분은 클라이언트 스크립트가 차지하고 있고요. UI용 HTML 태그는 거의 없는 것을 볼 수 있습니다.

스크립트를 쪽을 보시면, ClearAll()이라는 함수와 AddOption이라는 클라이언트 스크립트 함수가 우선적으로 보일텐데요. 이들은 주석에서 설명하고 있는 것처럼 각기 DropDown에서 모든 데이터를 제거하는 함수, DropDown에 새 직원명을 추가하는 함수입니다.

ClearAll이라는 함수는 함수 정의구역 밑에서 바로~~~!!! 호출되고 있습니다. 이 함수의 호출로 인해 현재 페이지가 브라우저에 출력될 경우, 부모 페이지에 존재하는 Lname 아이디를 갖는 DropDown은 깨끗이 비워지게 될 것입니다.

이제 DropDown 에서 기존 데이터를 모두 지웠으니, Callback.aspx 페이지가 해 주어야 할 남은 작업은 바로 쿼리 결과로 얻어온 데이터(지정된 직급에 해당하는 직원이름)를 DropDown 컨트롤에 넣어주는 것입니다. 데이터를 조회하여 얻어오는 것은 서버 측의 작업이고, 그 데이터를 부모 페이지의 DropDown 에 넣어주는 것은 클라이언트 작업입니다. 해서, aspx 페이지에서는 데이터베이스 조회 로직을 수행하고 그 조회결과 얻어진 직원명 데이터를 가지고 클라이언트 스크립트 문자열을 만들어서 화면에 출력하기만 하면 됩니다. 그 다음 실제로 DropDown에 데이터를 채우는 작업은 화면에 브라우저에 로드 될 때, 클라이언트 스크립트가 동작해서 데이터를 채우겠죠. ^^

바로 그런 목적으로 작성된 소스가 이것입니다. 짜잔~~~ 짠짠짠~

Callback.aspx.cs

    public class Callback : System.Web.UI.Page
    {
        private const string conStr = "server=(local);database=Northwind;uid=sa;pwd=1";

        private void Page_Load(object sender, System.EventArgs e)
        {
            string title = string.Empty;
            if (Request["title"] != null) title = Request["title"];

            if (!this.IsPostBack)
            {
                string sql = "SELECT EmployeeID, LastName FROM Employees WHERE title = @Title";
                SqlConnection con = new SqlConnection(conStr);
                SqlCommand cmd = new SqlCommand(sql, con);

                cmd.Parameters.Add("@Title", SqlDbType.VarChar, 50);
                cmd.Parameters[0].Value = title;
                con.Open();
                SqlDataReader reader = cmd.ExecuteReader();

                // 조회 결과로 얻은 직원명들 루프를 돌면서 스크립트로 꾸며준다.
                // AddOption('사번', '이름')의 형태로 꾸미도록 한다.
                string script = "<script language=javascript>";
                while(reader.Read())
                {
                    script += string.Format("AddOption('{0}', '{1}');\n", reader[0], reader[1]);
                }
                script += "</script>";

                con.Close();

                // 완성된 스크립트 문자열을 화면에 출력하고 클라이언트 스크립트가 기동되게 한다.
                this.RegisterStartupScript("addScript", script);
            }
        }

        .. 중략 ..

소스에서는 이전에 설명드린 것처럼, 데이터베이스를 조회해서 얻어진 결과를 문자열 결합하고 있습니다. 즉, 직원 이름들을 AddOption(직원코드, 직원명) 형태의 문자열로 만들고 있는 것이죠. 그리고, 다 만들어진 문자열을 페이지에 스크립트로 등록하고 있습니다. 해서, 그 결과 최종 페이지(IFRAME에 로드 되는 페이지)의 HTML 결과물은 다음과 같게 됩니다.

<script language="javascript">
    function ClearAll()
    {
        var count = parent.document.all.Lname.options.length;
        for(var i = 0; i < count; i++)
            parent.document.all.Lname.options.remove(0);
    }

    function AddOption(value, text)
    {
        var oOption = document.createElement("OPTION");
        oOption.text = text;
        oOption.value = value;
        parent.document.all.Lname.options.add(oOption);
    }

    ClearAll();
    parent.document.all.Lname.disabled = false;

</script>
<HTML>
    <body>
        <form name="Form1" method="post" action="Callback.aspx?Title=Sales%20Representative" id="Form1">
            <input type="hidden" name="__VIEWSTATE" value="dDwtNjU0MzcyMTk1Ozs+6rHmHpG6XjWEoknzx+x8U2/561s=" />

<script language=javascript>
    AddOption('1', 'Davolio');
    AddOption('3', 'Leverling');
    AddOption('4', 'Peacock');
    AddOption('6', 'Suyama');
    AddOption('7', 'King');
    AddOption('9', 'Dodsworth');
</script>

        </form>
    </body>
</HTML>

파란색 코드 구역이 서버의 처리로 인해 추가적으로 들어가게 되는 부분이며, 그 부분은 클라이언트 브라우저에서 실행되서, 부모 페이지의 DropDown 컨트롤에 직원이름을 동적으로 추가하게 됩니다.

크게 어렵지는 않은 내용일 것이라 생각됩니다. 단, 이 방식은 Callback.aspx 페이지를 다른 용도로 재 사용할 수는 없다는 단점을 가지고 있는 방식입니다. 즉, 각각의 비동기 처리가 필요한 상황마다 별도의 Callback.aspx 페이지가 만들어져야 한다는 단점을 가진다는 것이죠. 하나의 로직을 위해서, 별도의 aspx 페이지가 여러 개 요구된다는 부분이 이 방식의 단점이라면 단점일 것입니다.

이제 완성된 페이지를 한번 실행해 보도록 하겠습니다. 메인 페이지는 처음 로드 될 경우 다음과 같이 직급만이 DropDown 컨트롤에 나타나는 것을 볼 수 있을 것입니다.



그리고, 첫 번째 DropDown에서 원하는 직급을 선택하는 순간, 메인 페이지는 변경됨 없이, IFRAME 구역이 작업을 처리하여, 그 결과는 메인 페이지의 두 번째 DropDown에 반영하는 것을 보실 수 있을 것입니다.



모든 처리가 완료되었다면, 이제 IFRAME 태그를 다음과 같이 스타일을 지정하여 눈에 보이지 않도록 합니다.

<IFRAME id="CallBackFra" width="400" Height="50" style="display:none"></IFRAME>

그러면, 이제 화면은 깔끔하게 나타나게 될 것이며, 비동기 호출도 매끄럽게 이루어지는 것처럼 보이게 될 것입니다.



어떠세요? 나름대로 맘에 드시나요?

그렇습니다. 그런대로 훌륭한 방법임에는 틀림이 없습니다.

하지만, 이 방식은 손도 많이 갈 뿐더러, 각각의 처리에 대해 많은 asp, aspx 페이지가 요구된다는 단점 및 재사용성의 결여라는 단점이 있는 방식입니다. 해서, 좀 더 개선된 방식들도 시간이 지남에 따라 등장하게 되었습니다.

다음 시간에는 비동기 호출의 두번째 방식 XMLHTTP를 이용하는 방식에 대해서 알아보겠습니다.

 


asp.net비동기호출_callback01.zip


반응형

댓글