ajax로 검색한 결과 :: 시소커뮤니티[SSISO Community]
 
SSISO 카페 SSISO Source SSISO 구직 SSISO 쇼핑몰 SSISO 맛집
추천검색어 : JUnit   Log4j   ajax   spring   struts   struts-config.xml   Synchronized   책정보   Ajax 마스터하기   우측부분

회원가입 I 비밀번호 찾기


SSISO Community검색
SSISO Community메뉴
[카페목록보기]
[블로그등록하기]  
[블로그리스트]  
SSISO Community카페
블로그 카테고리
정치 경제
문화 칼럼
비디오게임 스포츠
핫이슈 TV
포토 온라인게임
PC게임 에뮬게임
라이프 사람들
유머 만화애니
방송 1
1 1
1 1
1 1
1 1
1

ajax로 검색한 결과
등록일:2008-04-02 15:06:30
작성자:
제목:End-to-end Ajax 애플리케이션 개발, Part 2: Ajax 클라이언트 및 서버 티어 구현하기 (한글)


애플리케이션 티어들을 분리하여 깨끗하고 고급스러운 웹 애플리케이션 만들기

developerWorks
문서 옵션

JavaScript가 필요한 문서 옵션은 디스플레이되지 않습니다.

수평출력으로 설정

이 페이지 출력

이 페이지를 이메일로 보내기

이 페이지를 이메일로 보내기

샘플 코드

영어원문

영어원문


제안 및 의견
피드백

난이도 : 중급

Senthil Nathan, Senior Software Engineer, IBM

2007 년 8 월 21 일

ajax (Asynchronous JavaScript + XML)는 데스크탑 품질의 소프트웨어 기능을 브라우저 플랫폼에서 실행되는 웹 애플리케이션으로 가져오는 현대적인 방식으로서 빠르게 급부상 하고 있습니다. 이 글에서는 오픈 소스 커뮤니티의 기술들을 사용하여 엔드투엔드 ajax 애플리케이션을 개발하는 방법을 설명합니다.

본 시리즈 Part 1에 서는, Firefox, Zend Core, MySQL 같은 오픈 소스 기술의 뚜렷한 특징들에 대해 배웠다. ajax 애플리케이션의 세 개의 티어들을 확장하는 은행 시나리오를 소개했다. 또한, 엔드투엔드 ajax 애플리케이션 개발에 필요한 데이터베이스 서버, 중간 티어 서버, Eclipse 기반 IDE도 설정했다. 두 번째 시리즈에서는, 은행 시나리오 중 일부분을 개발할 것이다. 특히, MySQL 데이터베이스를 사용하는 백엔드 데이터베이스를 생성할 것이다. 데이터베이스에서 은행 관련 데이터를 연결, 생성, 정의, 채우는 여러 MySQL 명령행 툴도 소개하겠다. 은행의 비즈니스 로직을 하우징 하는 중간 티어 PHP 모듈을 개발할 것인데, 이는 ODBC를 사용하여 MySQL 데이터베이스로 연결한다. 마지막으로, 은행 포털을 개발하여 사용자들이 이 엔드투엔드 애플리케이션과 인터랙팅 할 수 있는 브라우저 UI도 제공할 것이다. 이는 곧 Zend Core에서 실행할 준비가 된다.

머리말

Part 1에서 설명한 것처럼, 은행 시나리오는 은행 텔러가 수행하는 기본적인 어카운트 서비스를 제공하는 것 중심이다. Part 1에 서 설명한 시나리오를 공부하기 바란다. 고객 데이터는 이 시나리오에서 중요한 부분이다. 모든 고객 데이터는 데이터베이스 테이블에 한번 채워질 것이다. 이 후에, 저장된 고객 데이터는 Zend Core에서 제공하는 ODBC MySQL을 통해 검색 및 업데이트 될 수 있다. 고객 데이터가 관리되면, 초점은 은행 텔러 기능을 제공하는데 필요한 핵심 은행 로직으로 옮겨간다. PHP 코드 모듈을 개발하여 코어 은행 로직에 ODBC를 사용하는 필수 데이터베이스 액세스를 제공한다. 이 로직에 Zend Core와 PHP를 사용할 때의 주요 장점은 MySQL의 빌트인 지원을 사용할 수 있다는 점이다.

데이터 베이스와 PHP 모듈 작업을 수행한 후에, 은행 텔러가 네 가지 핵심 기능을 수행하는데 사용하는 간단한 사용자 인터페이스를 제공할 것이다. 이 시나리오에서 설명한 대로, PHP 모듈로 캡슐화 된 코어 은행 로직은 씬 클라이언트를 통해 액세스 된다. 구체적으로, 웹 기반 씬 클라이언트는 ajax 스타일로 생성된다: XHTML, Cascading Style Sheets (CSS), JavaScript, XMLHttpRequest (XHR). 이는 은행 텔러에게 간단한 사용자 인터페이스를 제공하여 핵심적인 은행 액션을 수행하도록 한다. 이 브라우저 UI는 또한 브라우저 클라이언트 로직이 네트워크를 통해 PHP로 된 서버 측 로직과 통신하는데 사용하는 구체적인 방식도 나타낸다.

브 라우저 애플리케이션을 디자인 하는데 사용되는 ajax 기술은 사용자 인터랙션에 매우 반응적인 애플리케이션을 만든다. 또한 전체 페이지를 다시 읽어와야하는 서버의 부하를 줄여준다. ajax 애플리케이션에서, 브라우저-서버 인터랙션은 애플리케이션 스팩의 데이터 교환으로 되고, 이는 서버 리소스를 더 많은 사용자와 더 많은 애플리케이션들로 최적화 한다. 싱글 페이지 브라우저 애플리케이션을 구현하는 개념을 다룬 글들을 읽어보기 바란다.

이 글의 끝에서는, 데이터베이스, 핵심적인 은행 로직용 PHP 모듈, 단일 페이지 기반 브라우저 사용자 인터페이스를 구현할 것이다.

MySQL 데이터베이스

Part 1에 서 배웠던 것처럼, MySQL은 오픈 소스 데이터베이스다. 우리 시나리오에서, 커뮤니티 서버 에디션을 사용하는데, 이는 많은 유용한 기능들을 갖춘 컴팩트 데이터베이스 서버이다. 은행 시나리오 구현이 오픈 소스 제품에 기반하기 때문에, MySQL은 Zend Core PHP 서버에 잘 맞는다. MySQL은 Zend Core에서 지원된다. MySQL 관리와 프로그래밍을 지원하는 다양한 툴들이 있다. 우리 시나리오에서, MySQL 명령행 클라이언트만 사용하여 MySQL 관리를 수행할 것이다. MySQL 데이터베이스를 사용하여 은행 어카운트 데이터베이스를 만들 것이다.

은행 데이터베이스 생성 및 채우기

본 시나리오에서, 다음과 같은 상세를 사용하여 고객용 어카운트 정보를 저장한다.

  • AccountHolderName
  • AccountNumber
  • CheckingBalance
  • StockName
  • StockQuantity
  • StockValue

고 객용 어카운트 정보에는 어카운트 소유자 이름, 현재 잔액, 고객이 소유한 주식의 시세, 총 주식 단위의 수, 주식 포트폴리오의 현재 가격 등이 포함되어 있다. 다음 섹션에서는 데이터베이스 테이블을 생성하고 테이블에 가상의 은행 고객에 대한 어카운트 상세를 채우는 방법을 설명하겠다. 지금 바로 시작해 보자.

다음 단계를 따라서 데이터베이스를 생성하고 애플리케이션 관련 데이터로 테이블을 채운다.

  1. Eclipse를 시작한다. (c:\eclipse\eclipse.exe).
  2. PHP가 Eclipse에서 활성 퍼스펙티브인지를 확인한다:
    1. Window->Open Perspective->Other->PHP를 선택하고 OK를 클릭한다.
  3. Eclipse에서 File->New->Project를 선택한다.
  4. General->Project를 선택하고 Next를 클릭한다.
  5. 프로젝트 이름 필드에 BankDB를 입력한다.
  6. Finish를 클릭한다.
  7. BankDB project를 오른쪽 클릭하고 New->Other를 선택한다.
  8. General-> File을 선택하고 Next를 클릭한다.
  9. File name 필드에서, BankDB.sql을 입력하고 Finish를 클릭한다.
  10. Listing 1에서 보이는 것처럼 BankDB.sql용 코드 콘텐트를 입력 또는 붙인다.
  11. 파일을 저장한 후 닫는다.
  12. MySQL 명령행 클라이언트를 시작하려면, Windows Start Menu->All Programs->MySQL->MySQL Server->MySQL Command Line Client를 클릭한다.
  13. MySQL 명령행 윈도우에서, 패스워드로 webtech를 입력하고 엔터키를 누른다.
  14. mysql> 프롬프트에서, source c:\eclipse\workspace\BankDB\BankDB.sql을 입력하고 엔터키를 누른다.
  15. BankDB 데이터베이스의 존재를 확인하여 이전 명령어가 올바르게 작동되었는지를 확인한다. 다음 명령어를 타이핑 하여 확인할 수 있다.
    1. show databases;
    2. use bankdb;
    3. show tables;
    4. describe account;
  16. MySQL 명령행 클라이언트에서, exit를 입력하고 닫는다.

Listing 1. BankDB.sql 파일의 콘텐트
                

-- This file is part of the End-to-End ajax development article in
-- the IBM developerWorks. This file contains a simple DB script to
-- create a database and populate it with the data.
--
-- Last Modified: May/10/2007
--
-- To execute the following statements in MySQL, do the following steps.
-- 1) Start MySQL command line client.
-- 2) Enter your MySQL admin password.
-- 3) Type the following line by substituting <YOUR_SQL_FILE_DIR> with the
-- directory name where the file is stored.
-- source <YOUR_SQL_FILE_DIR>\bankdb.sql

--
-- Table structure for table 'BankDB'
--

DROP DATABASE BankDB;

CREATE DATABASE BankDB;

USE BankDB;

CREATE TABLE account (
AccountHolderName VARCHAR(20) NOT NULL,
AccountNumber INTEGER NOT NULL,
CheckingBalance DOUBLE NOT NULL,
StockName VARCHAR(6),
StockQuantity INTEGER,
StockValue DOUBLE,
PRIMARY KEY(AccountHolderName, AccountNumber)
);

--
-- Populating data for table 'account'
--

insert into ACCOUNT values ('Frodo', 435245, 2344.45, 'GOOG', 100, 3453.32);
insert into ACCOUNT values ('Sam', 928462, 7583.32, 'CSCO', 200, 5323.43);
insert into ACCOUNT values ('Pippin', 234233, 3444.62, 'INTC', 300, 4213.76);
insert into ACCOUNT values ('Merry', 642445, 1005.32, 'MSFT', 250, 1353.32);
insert into ACCOUNT values ('Aragorn', 972321, 6424.24, 'HPQ', 525, 12043.94);
insert into ACCOUNT values ('Gandalf', 432134, 5392.23, 'IBM', 400, 10043.78);
insert into ACCOUNT values ('Legolas', 590134, 4313.82, 'DELL', 325, 5926.62);

PHP를 사용하여 MySQL 데이터베이스에 액세스 하기

PHP 의 가장 유명한 특징 중 하나는 단순함과 MySQL을 포함하여 다른 벤더들의 데이터베이스에 저장된 데이터에 액세스를 지원한다는 점이다. 효과적이고 쉬운 방식을 제공하여 비즈니스 로직과 데이터베이스를 통합한다. 지난 몇 년 동안, PHP 커뮤니티는 PHP Data Objects (PDO) 같은 여러 향상을 이룩했는데, 이는 어떤 데이터베이스 서버가 사용되는지 상관 없이 공통의 API 함수들을 노출하는 추상화 레이어를 제공한다. PHP 데이터베이스 API 함수는 절차적 스타일과 객체 지향적 스타일로 사용할 수 있다. 이 시나리오에서, MySQL에 맞춘 직접적인 데이터베이스 API를 사용한다. PHP는 MySQL에 액세스 하는 두 가지 방식을 제공한다.

  • MySQL
  • MySQL Improved

일 반 MySQL 확장은 MySQL 4.1.0의 완전한 기능을 지원하지 않는다. 이 기능에는 저장 프로시저, 트리거, 뷰가 포함된다. MySQL Improved (mysqli) 확장은 최신의 향상된 방식으로 그러한 기능들에 액세스 한다. mysqli용 확장은 PHP 5.0 이후 버전부터 사용할 수 있다. mysqli가 기본적으로 실행되지 않기 때문에, Zend Core 관리 툴을 사용하여 이를 실행해야 한다. 이 글에서는 Zend Core를 설치 및 설정했을 때 mysqli가 Part 1에서 이미 실행되었다.

PHP 데이터베이스 API의 좋은 특징 중 하나는 데이터베이스 연산의 결과를 PHP 데이터 구조로 매핑 할 수 있다는 점이다. 다시 말해서, 데이터베이스 테이블 칼럼 이름이 그 어레이의 키가 되는 상황에서 PHP associative array로서 데이터베이스 결과를 리턴할 수 있다. 또 다른 경우, PHP 클래스 객체로서 결과를 리턴할 수 있다. 여기에서 데이터베이스 칼럼 이름들은 객체 프로퍼티이다. 이는 데이터베이스 쿼리의 결과를 나타내는 매우 유용한 방식이다.

PHP MySQLi 함수의 구현 블록

앞 서 언급했듯이, PHP에서 사용할 수 있는 mysqli 함수의 어레이가 있다. 이 섹션에서는 시나리오 구현에 사용되는 함수들만 설명하겠다. 특히, 연결, 읽기, 쓰기, 연결 해제 같은 데이터베이스 함수들이 이 부분에서 다루어진다. 상세한 내용은 참고자료 섹션의 PHP 공식 문서를 참조하기 바란다.

데이터베이스 연산을 수행하기 전에, 데이터베이스 서버로 연결해야 한다. PHP에서, 다음 함수를 사용하여 데이터베이스로 연결한다.

$link = mysqli_connect("hostname", "user", "password", "dbname");

이 함수는 올바른 크리덴셜로 지정된 호스트에 있는 특정 데이터베이스로 연결한다. 이 함수가 취하는 다른 매개변수들이 있다. (PHP 문서 참조) 데이터베이스에 연결되면, 이 함수는 연결 객체를 리턴하는데, 이는 데이터베이스 읽기 및 쓰기 연산에 필요하다. 데이터베이스 연결 시도가 실패하면, 이 함수는 false를 리턴한다. 데이터베이스 서버 패스워드를 텍스트로서 코드에 직접 삽입하고 싶지 않으면, MySQL 패스워드 다이제스트 기능을 찾아보라.

연결 연산을 보조하는 함수는 데이터베이스 닫기 함수이다.

$result = mysqli_close($link);

이 함수는 이전에 열려있던 데이터베이스 연결을 닫는다. 매개변수로서 MySQL 데이터베이스 연결 객체를 사용하고 데이터베이스 닫기 연산이 성공하면 true를 실패하면 false를 리턴한다.

연결 관리 함수를 보았으니, 이제는 데이터베이스에서 읽고 쓰는 것과 관련한 함수를 보도록 하자. 다음은 읽기/쓰기 연산에서의 유용한 함수 리스트이다.

  • (1) $result = mysqli_query($link, $queryStr);
  • (2) $numberOfRows = mysqli_num_rows($result);
  • (3) $row = mysqli_fetch_assoc($result);
  • (4) $row = mysqli_fetch_object($result);

함 수(1)은 데이터베이스상에서 SQL 쿼리를 실행한다. 매개변수로서 연결 객체와 유효 SQL 쿼리 스트링을 취한다. 성공 시 TRUE를 리턴하고 실패 시 FALSE를 리턴한다. 하지만, SELECT SQL 쿼리의 경우, 이 함수는 결과 객체를 리턴한다.

함수(2)는 SQL 쿼리에서 리턴된 결과 세트에서 행들의 수를 파악한다. 매개변수로서 결과 객체를 취한다.

함 수(3)은 PHP associative array로서 결과 행을 가져온다. 이 어레이에는 어레이 키로서 데이터베이스 칼럼 이름과 어레이 값으로서 데이터베이스 필드 값을 포함하고 있다. 예를 들어, 데이터베이스 쿼리가 "capital"이라는 칼럼의 값을 리턴하면 결과는 다음과 같은 어레이를 참조함으로써 액세스 될 수 있다.

$stateCapital = $row['capital'];

함수(4)는 결과 행을 PHP 객체로서 얻는다. 이 객체에는 객체 프로퍼티로서 데이터베이스 칼럼 이름을 객체 프로퍼티 값으로서 데이터베이스 필드 값을 포함한다. 예를 들어, 데이터베이스 쿼리가 "park"이라고 하는 칼럼 값을 리턴하면, 결과는 다음을 사용하여 객체 값을 참조 해제 함으로서 액세스 될 수 있다.

$nationalPark = $row->park;

함수 3과 4는 데이터베이스 값을 프로그램 변수로 매핑하는 매우 간단한 방식도 가져온다. 앞서 언급했던 것처럼, PHP는 데이터베이스 값 매핑을 수행하는 여러 방식들을 제공한다.

우리 예제에서, 데이터베이스 연산 후에 에러 처리를 수행하는 두 개의 함수를 더 사용할 것이다. 의도한 데이터베이스 연산이 성공했는지 여부를 알아야 한다. 다음 함수들은 이러한 문제를 잘 다룬다.

  • $returnCode = mysqli_errno($link);
  • $errorMsg = mysqli_error($link);

mysqli_errno 함수는 최근의 함수 호출의 에러 코드를 리턴한다. 매개변수로서 연결 객체를 취하고 실행되었던 함수용 코드를 리턴한다. 리턴 코드 0은 에러가 없음을 나타낸다. mysqli_error 함수는 마지막 에러의 스트링 디스크립션을 리턴한다. 이 두 개의 함수들은 데이터베이스 쿼리의 전체 사이클을 완료하는데 필수적이다.

PHP 모듈에서 은행 비즈니스 로직 구현하기

다음 단계를 따라 PHP 모듈을 생성하고 중간 티어 비즈니스 로직을 구현한다.

  1. Eclipse PHP 퍼스펙티브로 전환한다: Window->Open Perspective->Other->PHP를 클릭하고 OK를 클릭한다.
  2. File->New->PHP Project를 클릭한다.
    1. Project name 필드에서 BankTeller를 입력한다.
    2. Finish를 클릭한다.
  3. Project Explorer 뷰에서, BankTeller 프로젝트를 오른쪽 클릭하고 New->PHP File을 선택한다.
    1. File Name 필드에서, BankLogic.php를 입력하고 Finish를 클릭한다.
  4. Listing 2의 소스 코드를 입력하거나 붙여서 파일의 콘텐트를 대체한다.
    1. File->Save를 클릭하여 파일을 저장한다.
  5. 이 파일의 주석을 검토하여 코드를 이해하거나 다음 섹션을 참조한다. 코드 로직에 대한 고급 디스크립션을 제공한다.

PHP 에서 데이터베이스 연산을 수행하는 것이 얼마나 쉬운지를 알 수 있다. Listing 2에서 패스워드가 텍스트로 선언되었다는 것을 알 수 있다. 이것은 이 글을 위해 만든 테스트 데이터베이스에서만 사용되는 패스워드이다. 텍스트 대신 다이제스트로 패스워드를 사용할 수 있다.


Listing 2. BankLogic.php 파일의 콘텐트
                

<?php
/*
============================================================
Project: End-to-End-ajax application development

Purpose: This is an example scenario to be used in
an IBM developerWorks article.

Last modified: May/11/2007.

This PHP module provides the core bank teller logic
required to access the customer related data from the
database. These functions can be called by other PHP
modules present in the Bank scenario. All the logic
involved in bank teller actions are divided into these
three core functions.

a) Get all account information
b) Process Transaction (deposit or debit)
c) Compute stock portfolio value

It uses direct mysqli functions to access the database as
opposed to the PHP PDO functions.
============================================================
*/
// These globals will be used to hold the following values:
// 1) $link is for storing the database connection object.
// 2) $dbResult is for storing the db query result object.
// 3) $finalResult is an associative array where the results
// from individual bank teller actions are packaged and
// returned to the caller.
global $link, $dbResult, $finalResult;

/*
============================================================
Function: connect_to_db

Last modified: May/11/2007.

This function connects to the BankDB MySQL database.
If the connection attempt is successful, it stores the
connection object in a global scoped variable and
returns true.
If the connection attempt is not successful, then it
returns false.
============================================================
*/
function connect_to_db() {
// We will use these global scoped variables here.
global $link, $dbResult, $finalResult;

// Initialize the variables.
$link = null;
$dbResult = null;
$finalResult = null;

// Do a mysqli connect to the local BankDB database using
// proper credentials.
$link = mysqli_connect("localhost", "root", "webtech", "bankdb");

// Check if DB connection worked.
if (mysqli_connect_errno()) {
// It looks like there is some problem.
// Get the MySQL error number and the error string.
$resultMsg = "DB connection error: " . mysqli_connect_errno() .
" [" . mysqli_connect_error() . "]";
// Store the results in an associative array.
// Set the result of the operation as not a successful one.
$finalResult["ResultCode"] = 1;
// Set the result message.
$finalResult["ResultMsg"] = $resultMsg;
// Return false to indicate the DB connection failure.
return (false);
} else {
// DB connection is good.
return(true);
} // End of if (mysqli_connect_errno())
} // End of function connect_to_db

/*
============================================================
Function: close_connection_to_db

Last modified: May/11/2007.

This function closes the connection made earlier to the
BankDB database.
============================================================
*/
function close_connection_to_db() {
// We will use these globally scoped variables.
global $link, $dbResult;

// Close the connection if we have an active
// DB connection object.
if ($link != null) {
if (($dbResult != null) && (is_object($dbResult))) {
// If the DB result contains query data object, free it now.
mysqli_free_result($dbResult);
$dbResult = null;
} // End of if ($dbResult != null)

// Time to close the DB connection.
mysqli_close($link);
// Set the connection object variable to null.
$link = null;
} // End of if ($link != null)
} // End of function close_connection_to_db

/*
============================================================
Function: getAllAccountInformation

Last modified: May/11/2007.

This function reads all the account information stored in
the BankDB and returns them in an associative array.
============================================================
*/
function getAllAccountInformation() {
// We will use these globally scoped variables.
global $link, $finalResult, $dbResult;

// Get a connection to the BankDB.
$result = connect_to_db();

// If db connection failed, return now.
if ($result == false) {
return ($finalResult);
} // End of if ($result == false)

// Make an SQL statement to query all rows in the account table.
$queryStr = "Select * from account";
// Make the SQL query.
$dbResult = mysqli_query($link, $queryStr);

// Process if there are any query errors.
if (mysqli_errno($link)) {
// Close the connection first.
close_connection_to_db();
// Set the error message in the final result assoc. array.
$resultMsg = "DB read error: " . mysqli_errno($link) .
" [" . mysqli_error($link) . "]";
// Set the application specific return code as FAILURE.
$finalResult["ResultCode"] = 1;
$finalResult["ResultMsg"] = $resultMsg;
// Return the final result.
return ($finalResult);
} // End of if (mysqli_errno($link))

// Get the number of rows queried.
$rowCount = mysqli_num_rows($dbResult);

// If the database is empty, return now.
if ($rowCount <= 0) {
// Close the connection first.
close_connection_to_db();
// Set the return code and return message.
$finalResult["ResultCode"] = 1;
$finalResult["ResultMsg"] = "No accounts were found in BankDB.";
// Return the final result.
return ($finalResult);
} // End of if ($rowCount <= 0)

// Set the counter to 0.
$cnt = 0;
// Create an array to hold all the fields from each row.
$accountInfo = array();

// From the query result set, fetch data fields from each row.
while($row = mysqli_fetch_assoc($dbResult)) {
// Initialize it to null.
$data = null;
// Create an associative array on the fly and store the
// individual data fields read from the current database row.
$data["AccountHolderName"] = $row["AccountHolderName"];
$data["AccountNumber"] = $row["AccountNumber"];
$data["CheckingBalance"] = doubleval($row["CheckingBalance"]);
$data["StockName"] = $row["StockName"];
$data["StockQuantity"] = intval($row["StockQuantity"]);
$data["StockValue"] = doubleval($row["StockValue"]);
// Now, store the entire associative array in to a regular array.
$accountInfo[$cnt++] = $data;
} // End of while($row = mysqli_fetch_assoc($dbResult))

// We finished reading all the account information from the database.
// Set the final result code as success.
$finalResult["ResultCode"] = 0;
// Set the final result message as success.
$finalResult["ResultMsg"] = "ReadAllAccountsInfoFromDB successful";
// Make the entire array holding all the account information into the
// final result associative array.
// $finalResult is an associative array containing an
// regular array which in turn contains several associative arrays as
// its elements.
$finalResult["AccountInfo"] = $accountInfo;
// Close the DB connection.
close_connection_to_db();
// Return the final result array now.
return($finalResult);
} // End of function getAllAccountInformation

/*
============================================================
Function: accountTransaction

Last modified: May/11/2007.

This function performs the bank teller account transaction.
This function takes the account holder name, an amount and
the transaction type as function parameter.
The transaction type is 1 for deposit into checking account.
The transaction type is 2 for debit from a checking account.
After the transaction is done, it returns the snapshot of
the account before this transaction and another snapshot of
the account after this transaction.
============================================================
*/
function accountTransaction($accountHolderName, $amount, $transactionType) {
// We will use these globally scoped variables.
global $link, $finalResult, $dbResult;
// Get a database connection.
$result = connect_to_db();

// If the connection failed, return now.
if ($result == false) {
return ($finalResult);
} // End of if ($result == false)

// Make an SQL statement to read a particular account data for a
// given account holder name.
$queryStr = "Select * from account where AccountHolderName='$accountHolderName'";
// Perform the SQL query.
$dbResult = mysqli_query($link, $queryStr);

// Did you encounter a DB error?
if (mysqli_errno($link)) {
// Close the connection now.
close_connection_to_db();
// Set the DB error message.
$resultMsg = "DB read error: " . mysqli_errno($link) .
" [" . mysqli_error($link) . "]";
// Set the application-specific result code.
$finalResult["ResultCode"] = 1;
// Set the result message.
$finalResult["ResultMsg"] = $resultMsg;
// Return the associative array with the error result.
return ($finalResult);
} // End of if (mysqli_errno($link))

// How many rows were read from the DB?
$rowCount = mysqli_num_rows($dbResult);

// If no rows are obtained from DB, return now.
if ($rowCount <= 0) {
// Close the connection now.
close_connection_to_db();
// Setup the return value as not a successful one.
$finalResult["ResultCode"] = 1;
$finalResult["ResultMsg"] = "No accounts were found in BankDB.";
// Return the final result.
return ($finalResult);
} // End of if ($rowCount <= 0)

// Store a record with the current checking balance.
// Create an array to hold the pre-transaction and
// post-transaction account snapshots.
$accountInfo = array();
// Fetch the query result as an associative array.
$row = mysqli_fetch_assoc($dbResult);
$data = null;
// Record the filed values from the database row into
// a new associative array.
$data["AccountHolderName"] = $row["AccountHolderName"];
$data["AccountNumber"] = $row["AccountNumber"];
$data["PreviousCheckingBalance"] = doubleVal($row["CheckingBalance"]);
$data["StockName"] = $row["StockName"];
$data["StockQuantity"] = intval($row["StockQuantity"]);
$data["StockValue"] = doubleVal($row["StockValue"]);
// Store the pre-transaction account snapshot into a regular array.
$accountInfo[0] = $data;

// Initialize the variables.
$newBalance = 0.0;
// Since we are doing a transaction that changes the
// checking balance, we need to update the DB later.
$updateDB = true;

if ($transactionType == 1) {
// It is deposit.
// Ensure that user wants to deposit an amount greater than 0.
if ($amount > 0) {
$newBalance = doubleval($data["PreviousCheckingBalance"]) + $amount;
} else {
// Deposit amount is 0. There is no need to do a database update.
$updateDB = false;
$newBalance = doubleval($data["PreviousCheckingBalance"]);
} // End of if ($amount > 0)
} else if ($transactionType == 2) {
// It is debit.
// Ensure that the user wants to debit an amount greater than 0 and
// the amount is less than the money available in the checking account.
if (($amount > 0) &&
($amount < doubleval($data["PreviousCheckingBalance"]))) {
$newBalance = doubleval($data["PreviousCheckingBalance"]) - $amount;
} else {
// Debit amount is either 0 or it is bigger than a allowed value.
$updateDB = false;
$newBalance = doubleval($data["PreviousCheckingBalance"]);
}
} // End of if ($transactionType == 1)

// Bank teller transaction is completed now.
// Life is good. Send the result back.
$finalResult["ResultCode"] = 0;
$finalResult["ResultMsg"] = "AccountTransaction successful.";

// If we modified the current checking balance because of a
// deposited or debited amount, then update the database.
if ($updateDB == true) {
// Prepare an SQL Update statement.
$updateStr = "update account set CheckingBalance=$newBalance " .
"where AccountHolderName='$accountHolderName'";

// Execute the update query.
$dbResult = mysqli_query($link, $updateStr);

// See if there are any DB errors?
if (mysqli_errno($link)) {
// Close the connection to the database.
close_connection_to_db();
// Prepare the error message to be returned.
$resultMsg = "DB write error: " . mysqli_errno($link) .
" [" . mysqli_error($link) . "]";
$finalResult["ResultCode"] = 1;
$finalResult["ResultMsg"] = $resultMsg;
// Return the error message.
return ($finalResult);
}

if ($dbResult == true) {
// All fine.
// Prepare to send back the result of this operation.
$finalResult["ResultCode"] = 0;
$finalResult["ResultMsg"] =
"New balance for $accountHolderName has been stored in DB.";
} else {
$finalResult["ResultCode"] = 1;
$finalResult["ResultMsg"] =
"New balance for $accountHolderName could not be stored in DB.";
// Return the final result that has an error.
return($finalResult);
} // End of if ($dbResult == true)
} // End of if ($updateDB == true)

// We already stored the pre-transaction account snapshot in an
// array at the top of this function.
// Store a second record that will have the updated checking balance.
$data = null;
$data["AccountHolderName"] = $row["AccountHolderName"];
$data["TransactionAmount"] = $amount;
$data["AccountNumber"] = $row["AccountNumber"];
$data["NewCheckingBalance"] = $newBalance;
$data["StockName"] = $row["StockName"];
$data["StockQuantity"] = intval($row["StockQuantity"]);
$data["StockValue"] = doubleval($row["StockValue"]);
$accountInfo[1] = $data;
$finalResult["AccountInfo"] = $accountInfo;
// Close the DB connection.
close_connection_to_db();
// Return the final result.
return($finalResult);
} // End of function accountTransaction.

/*
============================================================
Function: portfolioValue

Last modified: May/11/2007.

This function stores the current portfolio value of a
given account holder to the database. It takes the
account holder name and the current stock price as input
arguments. It reads the currently held position (in the
Bank scenario, every account holder owns only a single
company's stock. It is done to make the task simpler.)
and computes the current market value. Then it updates
the database with the new total stock value.
============================================================
*/
function portfolioValue($accountHolderName, $stockPrice) {
// We will use the globally scoped variables.
global $link, $finalResult, $dbResult;

// Connect to the MySQL BankDB database.
$result = connect_to_db();

// If error in connecting to the DB, return now.
if ($result == false) {
return ($finalResult);
} // End of if ($result == false)

// Prepare the query string to get account info for a given account holder.
$queryStr = "Select * from account where AccountHolderName='$accountHolderName'";
// Execute the MySQL query.
$dbResult = mysqli_query($link, $queryStr);

// Are there any errors?
if (mysqli_errno($link)) {
// Error there. Close the db connection.
close_connection_to_db();
// Prepare error message to be returned.
$resultMsg = "DB read error: " . mysqli_errno($link) .
" [" . mysqli_error($link) . "]";
$finalResult["ResultCode"] = 1;
$finalResult["ResultMsg"] = $resultMsg;
// Return the error.
return ($finalResult);
} // End of if (mysqli_errno($link))

// Did we read any rows from the DB?
$rowCount = mysqli_num_rows($dbResult);

// If there are no matching rows in DB, return now.
if ($rowCount <= 0) {
// Close the DB connection.
close_connection_to_db();
$finalResult["ResultCode"] = 1;
$finalResult["ResultMsg"] = "No accounts were found in BankDB.";
// Return the final result.
return ($finalResult);
} // End of if ($rowCount <= 0)

// Store a record with the current portfolio value.
// Allocate an array to store the pre-transaction and the
// post-transaction account snapshots.
$accountInfo = array();
// Do a database fetch to get the results as an associative array.
$row = mysqli_fetch_assoc($dbResult);
// Store the data fields of a row to an associative array.
$data = null;
$data["AccountHolderName"] = $row["AccountHolderName"];
$data["AccountNumber"] = $row["AccountNumber"];
$data["CheckingBalance"] = doubleval($row["CheckingBalance"]);
$data["StockName"] = $row["StockName"];
$data["StockQuantity"] = intval($row["StockQuantity"]);
$data["PreviousPortfolioValue"] = doubleval($row["StockValue"]);
// Store the associative array in a regular array.
$accountInfo[0] = $data;

// Calculate the portfolio value.
$finalResult["ResultCode"] = 0;
$finalResult["ResultMsg"] = "PortfolioValue successfully calculated.";
$newPortfolioValue = intval($row["StockQuantity"]) * doubleval($stockPrice);

// Since the stock portfolio value has changed, update it in DB.
$updateStr = "update account set StockValue=$newPortfolioValue " .
"where AccountHolderName='$accountHolderName'";

// Perform an SQL update.
$dbResult = mysqli_query($link, $updateStr);

// Check if there are any DB errors.
if (mysqli_errno($link)) {
// Close the connection to DB.
close_connection_to_db();
// Prepare the error message to returned.
$resultMsg = "DB write error: " . mysqli_errno($link) .
" [" . mysqli_error($link) . "]";
$finalResult["ResultCode"] = 1;
$finalResult["ResultMsg"] = $resultMsg;
// Return the error message.
return ($finalResult);
} // End of if (mysqli_errno($link))

if ($dbResult == true) {
// All fine.
// Prepare the success message to be returned.
$finalResult["ResultCode"] = 0;
$finalResult["ResultMsg"] =
"New stock value for $accountHolderName has been stored in DB.";
} else {
$finalResult["ResultCode"] = 1;
$finalResult["ResultMsg"] =
"New stock value for $accountHolderName could not be stored in DB.";
// Update failed. Hence return the error result.
return($finalResult);
} // End of else in if ($dbResult == true)

// Store a second record that will have the updated portfolio value.
// We have already stored the pre-transaction account snapshot in an array.
// Let us store the post-transaction account snapshot also there.
$data = null;
$data["AccountHolderName"] = $row["AccountHolderName"];
$data["AccountNumber"] = $row["AccountNumber"];
$data["CheckingBalance"] = doubleval($row["CheckingBalance"]);
$data["StockName"] = $row["StockName"];
$data["CurrentStockPrice"] = doubleval($stockPrice);
$data["StockQuantity"] = intval($row["StockQuantity"]);
$data["NewPortfolioValue"] = $newPortfolioValue;
// Store the associative array in a regular array.
$accountInfo[1] = $data;
$finalResult["AccountInfo"] = $accountInfo;
// Close the DB connection and return the result.
close_connection_to_db();
return($finalResult);
} // End of function portfolioValue.
?>

BankLogic PHP 모듈 로직

Listing 2의 PHP 파일에는 핵심 은행 텔러 함수에 필요한 비즈니스 로직이 포함되어 있다. 특히, 이러한 함수에는 데이터베이스에 저장된 모든 어카운트 정보를 가져오고, 예금 또는 대출 트랜잭션을 수행하고, 주식 포트폴리오 값을 계산하는 것이 포함된다. 이 모든 함수들은 특정 인풋 매개변수를 허용하고 관련 어카운트 정보를 포함하고 있는 애플리케이션 스팩의 associative array를 리턴한다. 이러한 함수 내의 로직들에는 BankDB MySQL 데이터베이스로 연결하고 읽기 또는 업데이트 중심의 액션을 수행하는 것이 포함된다. Listing 2는 세 개의 글로벌 범위의 PHP 변수를 처음에 정의한다. 이러한 변수들은 데이터베이스 연결 객체($link), SQL 쿼리 결과 객체($dbResult), PHP 모듈의 콜러에게 리턴될 최종 결과 ($finalResult)를 저장한다. 이 PHP 모듈은 MySQL 데이터베이스로 연결 및 연결 해제하는 두 개의 유틸리티 함수를 갖고 있다. 연결이 이루어지면, 연결 객체가 $link 변수에 생성 및 저장되는데, 이는 글로벌 범위이기 때문에 PHP 파일을 통해 사용할 수 있다. 이와 마찬가지로, DB 읽기 또는 업데이트가 수행될 때 SQL 쿼리 결과는 $dbResult 변수에 저장되고, 이 역시 글로벌 범위 변수이다. 데이터베이스 연결이 닫힐 때, 다른 유틸리티 함수 (close_connection_to_db)가 사용되어 데이터베이스 연결을 해제한다. 데이터베이스 연결을 닫기 전에, 유틸리티 함수는 $dbResult의 결과 세트가 차지한 메모리가 비워졌는지를 확인한다.

이 파일에는 세 개의 핵심 함수가 있다.

  • getAllAccountInformation 함수는 콜러에 의해 호출되어 데이터베이스에 저장된 모든 가용 어카운트 데이터를 가져온다. 여기의 코드는 MySQL 확장 API를 사용하여 MySQL 데이터베이스로 ODBC 연결을 만들고 어카운트 테이블에 저장된 모든 어카운트 정보를 보낸다. 데이터베이스 읽기 연산이 성공하면, 결과 세트를 반복하고 개별 어카운트 데이터 정보를 모은다. 모든 어카운트 데이터 정보를 PHP associative array에 저장하고 그 어레이를 콜러에 리턴한다.
  • accountTransaction 함수는 콜러에 의해 호출되어 예금과 대출 연산을 수행한다. 이 메소드는 어카운트 홀더 이름, 트랜잭션 양, 트랜잭션 유형 같은 매개변수로 호출되어 이것이 예금 연산인지 대출 연산인지를 나타낸다. ODBC 연결이 MySQL 데이터베이스로 이루어지고, 해당 어카운트 홀더에 대한 현재 어카운트 데이터가 데이터베이스에서 보내져서 associative array에 저장된다. 이는 이 어카운트의 사전 트랜잭션 스냅샷이다. 필수 트랜잭션(예금 또는 대출)이 수행된다. 데이터베이스는 메모리 트랜잭션에서 기인한 새로운 잔액으로 업데이트 된다. 사후 트랜잭션 데이터 역시 또 다른 associative array에 저장된다. 이제, 계정의 사전 트랜잭션 및 사후 트랜잭션 스냅샷이 일반 어레이에 저장되고 최종 결과로 콜러로 리턴된다. 또한, 코드는 트랜잭션 양이 의도한 트랜잭션 유형에 유효할 경우에만 트랜잭션을 수행한다.
  • portfoliovalue 메소드는 콜러에 의해 호출되어 해당 어카운트 홀더 이름의 주식 포트폴리오 값을 계산한다. 이 메소드는 어카운트 홀더 이름과 이 어카운트 홀더가 소유한 주식 값 같은 매개변수로 호출된다. 이 메소드는 어카운트 데이터의 사전 트랜잭션 스냅샷을 취하고 이를 associative array에 저장한다. 그리고 나서, 새로운 총 주식 값을 계산하고 Bank 데이터베이스에 상응하는 데이터베이스 필드를 업데이트 한다. 또한, 또 다른 associative array에 어카운트 데이터의 사후 트랜잭션 스냅샷을 저장한다. 일반 어레이에 어카운트 데이터 스냅샷을 저장하고 이들을 최종 결과로서 클라이언트로 리턴한다.

소스 파일은 코드 로직을 이해하는데 도움이 되는 주석도 제공한다.

싱글-페이지 ajax 사용자 인터페이스

은 행 텔러 사용자 인터페이스의 경우, 싱글 페이지 브라우저 애플리케이션을 개발할 것이다. 싱글 페이지 브라우저 애플리케이션은 전통적인 "Click and Wait" 웹 애플리케이션과는 다르다. "Click and Wait" 웹 애플리케이션에서, 애플리케이션에 대한 모든 웹 페이지는 사용자가 새로운 페이지로 갈 때, 전체 페이지 콘텐트를 다운로드 함으로써 서버에서 보내진다. 반면, 싱글 페이지 브라우저 애플리케이션의 모델은 정확히 그 반대이다. 여기에서, 애플리케이션에 필요한 모든 프리젠테이션 콘텐트(XHTML, CSS, JavaScript)는 서버에서 단 한번만 보내진다. 이러한 원타임 다운로드는 사용자가 브라우저를 애플리케이션 URL로 포인팅 할 때 시작 시 발생한다. CSS 파일은 사용자 인터페이스의 다른 부분들에 필요한 모든 스타일링을 코딩한다. 특별한 Document Object Model (DOM) 기술은 사용자 인터페이스의 관련 부분들을 숨기고 보여주는데 사용되며, 또 다른 페이지를 가져오기 위해 서버로 갈 필요 없이 멀티페이지 작동을 시뮬레이트 한다. 브라우저에서 실행되는 애플리케이션 스팩의 JavaScript 코드는 대부분의 애플리케이션 로직을 핸들할 수 있다. 브라우저 애플리케이션을 디자인하는데 사용되는 ajax 기술은 사용자 인터랙션에 매우 반응적인 애플리케이션을 만든다. 이들은 또한 전체 페이지를 가져오는데 따른 큰 부하로부터 서버를 부담을 경감시킨다. ajax 애플리케이션에서, 브라우저-서버 인터랙션은 애플리케이션 스팩의 데이터 교환으로 귀결되며, 이는 서버 리소스를 최적화 하는 결과가 된다.

이 글에서 설명한 장점 이상으로, 데이터 보안, 데이터 프라이버시, 브라우저 클라이언트에서 실행될 수 있을 정도로 신뢰를 받는 비즈니스 로직의 적절한 파티셔닝 같은 기타 아키텍처 영역이 있다. 게다가, HTTP 트랜잭션이 인터럽트 될 때 에러에서 복구도 해야 한다. 이러한 사항들은 이 글의 초점이 아니므로 단순한 특성을 가진 싱글 페이지 브라우저 애플리케이션을 구현하는 데만 초점을 맞추겠다. 브라우저 애플리케이션에 풍부한 그래픽을 제공하기 위해 결합할 수 있는 많은 ajax 기술들이 있다. 이 글에서, 싱글 페이지 브라우저 애플리케이션을 구현하는 것의 개념을 소개하겠다.

싱글 페이지 브라우저 애플리케이션의 다른 부분들을 숨기고 보여주는데 사용되는 기술 중 하나에는 <DIV>, <SPAN>, <TABLE> HTML 태그를 사용하는 것이 포함된다. 그림 1은 이 태그들의 개요도이다. <DIV> 태그는 HTML 문서를 여러 섹션들로 나눌 수 있다. <DIV> 태그 내에 특정 스크린의 사용자 인터페이스 컨트롤을 배치하는 것은 애플리케이션이 가질 수 있는 많은 스크린들을 나누는 첫 단계이다. 이것이 일단 수행되면, JavaScript를 통해서 어떤 스크린 또는 <DIV>가 애플리케이션 실행 시 특정 단계에서 보일 수 있는지를 쉽게 조작할 수 있다. 또 다른 태그는 <SPAN> 태그인데, 이는 스타일을 브라우저 애플리케이션의 텍스트 세그먼트에 적용할 수 있다. 은행 시나리오에서, 이 태그를 사용하여 스크린 상에 메뉴 컨트롤을 만든다. 또한, <TABLE> 태그를 사용하여 문서 레이아웃을 관리한다.


그림 1. <SPAN>, <DIV>, <TABLE> 태그
HTML SPAN, DIV, TABLE 태그

브 라우저 애플리케이션에 풍부함을 가져오는 또 다른 방법은 개별 사용자 인터페이스 엘리먼트의 레벨에서 스타일링을 하는 기능이다. CSS는 브라우저 애플리케이션의 전체적인 모양을 관리하는데 사용된다. CSS는 고유의 신택스를 가진 개별 코드이다. CSS는 어떤 플러그인도 필요로 하지 않기 때문에 설정하기 쉽다. 규칙을 정의하는 것으로 텍스트 파일에서 모든 설정이 이루어진다. CSS 규칙은 셀렉터와 선언 블록으로 구성된다. 셀렉터는 각 규칙을 시작한 다음 중괄호를 붙인다. 선언 블록인 중괄호로 둘러싸이고 선언들로 구성된다. 선언들은 세미콜론으로 분리된 프로퍼티와 값 쌍이다. Listing 3은 CSS 규칙의 예제이다. 그림 2에는 다양한 종류의 CSS 셀렉터들이 나타나 있다.

CSS 셀렉터의 모든 기능들을 설명하려면 또 하나의 기술자료가 필요하다. 시간을 들여 CSS 관련 자료를 읽어보기 바란다. 참고자료 섹션의 CSS Zen garden URL은 CSS의 힘을 경험할 수 있는 좋은 장소이다.

Listing 3. 간단한 CSS 규칙
                
body {
color: maroon;
font-size: medium;
text-align: center;
background:green;
}


그림 2. CSS 셀렉터 유형
CSS 셀렉터들

여 러 ajax 프레임웍들은 I/O, 네트워크, 그래픽, 데이터를 위해 코드 라이브러리를 추상화 한다. Eclipse 기반 Aptana 툴은 하나의 아이디에서 이 같은 많은 ajax 프레임웍들과 잘 통합된다. 하지만, 이와 같은 ajax 프레임웍들에 대한 논의는 이 글의 범위가 아니다. 은행 시나리오에 대한 ajax 관련 코드는 플레인 JavaScript를 사용하여 구현되며, 프레임웍 라이브러리 보다는 애플리케이션에서 직업 코드를 경험할 수 있도록 단순함을 유지하고 있다. 본 시리즈를 마친 후에, Dojo, Rico, Script.aculo.us, Prototype 같은 ajax 프레임웍을 연구하기 바란다.

ajax는 반응적인 사용자 인터페이스를 만들며, 데이터용 서버와 작동하지만, 사용자에게 보이는 시각적 인터럽션이 없다.

ajax HTTP 인터랙션

이 전 섹션은 ajax의 일부인 XHTML, CSS, DOM 기술에 대해 검토했다. 또한 HTTP 인터랙션(중간 티어 서버)이 수행되는 방식이 ajax 애플리케이션의 중심이라는 것도 설명했다. ajax는 브라우저 애플리케이션들이 비동기식 방식으로 서버와 인터랙팅 하는 새로운 방식을 제공한다. 이것은 사용자가 HTML 폼에서 Submit 버튼을 클릭할 때 모래시계가 돌아갈 때까지 기다리게 하지 않는다는 점에서 기존의 브라우저 애플리케이션과는 다르다. 비동기식 HTTP 통신은 XHR에서 지원되는데, 이는 브라우저 측 JavaScript 코드가 비동기식 HTTP 서버 요청을 할 수 있도록 하는 브라우저 객체이다. 여러분은 HTTP 요청을 만들고, 응답을 받고 페이지의 일부를 백그라운드에서 업데이트 하기 때문에 사용자에게 보이는 시각적인 인터럽션이 없다. ajax는 사용자 인터페이스 반응성을 유지하면서 서버와 작동한다. 참고자료 섹션에서 W3C의 XHR 표준화에 대해 설명하고 있다. 그림 3은 다양한 객체 메소드와 XHR 프로퍼티를 보여주고 있다.


그림 3. XML HTTP Request (XHR) 객체 모델과 프로퍼티
XHR 객체 메소드

다음은 XHR의 연산 시퀀스에 대한 설명이다.

  1. XMLHttpRequest object의 인스턴스를 만든다.
  2. XMLHttpRequest 객체를 사용하여 서버 응답을 받을 때 자동으로 실행되는 콜백 함수를 정의함으로써 서버로 호출한다.
  3. 콜백 함수에서 서버의 응답을 다룬다.
  4. 필요할 경우, Step 2로 가서 서버와 비동기식으로 인터랙션 한다.

나중에 이 은행 시나리오에서 XHR을 사용하는 것에 대한 구체적인 구현 상세를 설명하겠다.

ajax 브라우저 애플리케이션의 블록 구현하기

우리의 브라우저 애플리케이션은 다음으로 구성되어 있다.

  • XHTML 파일
  • CSS 파일
  • XHR JavaScript 파일
  • JavaScript의 클라이언트 측 비즈니스 로직
  • Utility JavaScript 파일

XHTML 파일은 애플리케이션을 시작하기 위해 사용자가 브라우저를 가리키는 앵커 파일이다. 이 파일에는 주로 <DIV>, <SPAN>, <TABLE>, <FORM> 같은 HTML 태그를 사용하여 만들어지는 모든 사용자 인터페이스 엘리먼트가 들어있다. 이 파일은 다양한 브라우저 기술을 사용하여 스타일링, 통신, 데이터 포맷 지원을 추가한다.

CSS 파일은 필요한 스타일링을 사용자 인터페이스 엘리먼트에 추가하는 규칙을 제공한다. CSS는 그 자체로 큰 주제이기 때문에 이 시나리오에서 CSS의 모든 기능들을 언급할 수는 없다. 기본적이고 중요한 CSS 기능들만 설명하겠다.

XHR 파일은 브라우저가 인지하지 못하는 방식으로 XMLHttpRequest 객체를 초기화 하는 로직을 설명한다. 비동기식 통신 모드에서 XHR을 사용하는 방법도 설명한다. XHR 로직을 다시 만드는 대신, XHR이 오늘날 웹 개발 커뮤니티에서 사용되는 방식을 설명하겠다.

클라이언트 측 비즈니스 로직에서, JavaScript는 은행 시나리오 스크린의 ajax 조작, 서버와의 데이터 전용 인터랙션을 돕는 이벤트 모델, 서버 응답의 비동기식 핸들링을 설명한다. ajax 개념에 익숙하지 않은 사람을 위해, 이 모듈은 자체적으로 싱글 페이지 기반 ajax 브라우저 애플리케이션에 적용될 수 있는 중심 개념들을 설명한다.

게다가, 브라우저 애플리케이션의 구현 블록들 중 하나에는 JavaScript Object Notation (JSON) 데이터 핸들링을 위한 오픈 소스 유틸리티가 포함되어 있다. 본 시리즈 Part 3에서 설명하도록 하겠다.

ajax 브라우저 애플리케이션으로서 Bank 포털 구현하기

우 리의 은행 시나리오는 은행 텔러가 사용하는 브라우저가 PHP 모듈로 구현했던 핵심 함수들을 수행하도록 되어있다. Aptana 웹 IDE를 사용하여 이를 구현한다. 이 글을 쓰고 있는 현재, Aptana Web IDE는 XHTML, CSS, JavaScript 기반 브라우저 애플리케이션들을 구현하는 간단한 방식을 제공한다. Aptana는 PDT 툴과 함께 Eclipse 환경에 잘 맞는 무료 플러그인이다. 이는 아직 개발 중이지만 이 은행 시나리오를 개발하는데 사용될 수는 있다. 미가공 JavaScript를 사용하여 은행 시나리오에서 ajax 기능을 구현하겠지만, Aptana는 다른 오픈 소스 ajax 프레임웍 라이브러리들과 기능 통합도 제공한다.

다음 단계는 싱글 페이지 ajax 브라우저 애플리케이션을 생성하는 단계이다.

  1. Eclipse에서 Aptana 퍼스펙티브로 바꾼다: Window->Open Perspective->Other->Aptana를 선택하고 OK를 클릭한다.
  2. Aptana 퍼스펙티브에서, Project 탭 뷰를 선택한다.
  3. BankTeller 프로젝트를 오른쪽 클릭하고 New->HTML file을 선택한다.
    1. File name 필드에서, index.html을 입력하고 Finish를 클릭한다.
  4. Listing 4의 소스 코드를 입력 또는 붙여서 index.html 파일의 콘텐트를 대체하고 File->Save를 클릭한다.
  5. BankTeller 프로젝트를 오른쪽 클릭하고 New->CSS file을 선택한다.
    1. File name 필드에서, BankTeller.css를 입력하고 Finish를 클릭한다.
  6. Listing 5의 소스 코드를 입력하거나 붙여서 BankTeller.css 파일의 콘텐트를 바꾸고, File->Save를 클릭한다.
  7. BankTeller 프로젝트를 오른쪽 클릭하고 New->JavaScript file을 선택한다.
    1. File name 필드에서, xhr.js를 입력하고 Finish를 클릭한다.
  8. Listing 6의 소스 코드를 입력 또는 붙여서 이 파일의 콘텐트를 바꾸고 File->Save를 클릭한다.
  9. 이 파일들의 주석을 검토하여 코드를 파악하거나 다음 섹션을 참조하라. 코드 로직에 대한 상세한 설명을 볼 수 있다.

지금까지, 싱글 페이지 ajax 브라우저 애플리케이션에 필요한 생성물의 3분의 2를 완성했다. 본 시리즈의 Part 3에서는 이 브라우저 애플리케이션의 나머지 부분을 만들도록 하겠다.


Listing 4. index.html
                
<!--
============================================================
Project: End-to-End-ajax application development

Purpose: This is an example scenario to be used in
an IBM developerWorks article.

Last modified: May/12/2007.

This HTML file provides the required user-interface elements
for the bank teller browser application. This is a
single-page based ajax browser application. It simply
means that this application doesn't do any page fetches
at all from the server other than the initial download
of this single HTML file.

This file liberally makes use of <DIV>, <SPAN> and
<TABLE> tags to do some of the well-known ajax tricks.
It is a self-contained file that contains all the
HTML markup required by the bank teller application.
============================================================
-->
<!DOCTYPE HTML PUBLIC
"-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<!--
HTML document header describes the various properties of the document including
its title and relationship with other documents such as CSS and JavaScript files.
In the Bank Teller single-page application, the following documents will be used:

1) json.js (It is an open-source JSON parser developed by Douglas Crockford.
2) xhr.js (It is a generic utility that provides XHR related functions.
3) BankTeller.js (All the client-side logic in this JavaScript file)
4) BankTeller.css (All the styling needed for the browser application is here)
-->
<head>
<title>ajax Bank Teller Application</title>
<script language"JavaScript" type="text/javascript" src="json.js"></script>
<script language"JavaScript" type="text/javascript" src="xhr.js"></script>
<script language"JavaScript" type="text/javascript" src="BankTeller.js"></script>
<link rel="stylesheet" style="text/css" href="BankTeller.css" media="all" />
</head>

<!--
Set an event handler to initialize the application-specific stuff.
This event handler will be triggered during page load time.
In this event handler, initial screen for the bank teller application will be
set and the other screens will be hidden.
-->
<body onload="initOnPageLoad();">
<!--
This application has seven different sections that are divided
using the <DIV> elements. Those sections are:
1) Main section
2) Teller Options (Menu) section
3) Deposit section
4) Debit section
5) Stock portfolio section
6) Bank teller action result section
7) Page footer section

All these sections will be shown selectively depending the application
context where the user is in at a given time. This is done in the
client side logic coded in JavaScript. This technique provides the
dynamism and rich response to user actions. It elevates the user
experience level drastically as compared to the traditional Web applications
developed using the click and wait page refresh model.
-->
<!--
The following the main DIV container for the entire application.
All the other sections are children of this. They all hang on to
this DIV container node in DOM.
-->
<div id="mainPage"
title="This is an ajax based single-page browser application.
No browser Back button please. HTML download from server occurs ONLY ONCE.
From then on, back-end server is contacted asynchronously ONLY
for data exchanges.">
<h1 title="This web application provides Bank teller services.">
Bank Teller Operations
</h1>

<!--
The tellerOptions DIV element holds the table that will provide the
user with available teller options to select from. This container
holds the menu choices for the entire bank teller application.
-->
<div id="tellerOptions" title="You can perform these teller operations.">
<center><font color="Olive">
<u>Click an operation below</u>
</font></center>

<!--
Each menu option is a HTML text that is embedded within a
SPAN tag. That will enable us to set the mouseover and mouseout
events for that text element. We can combine those events with
CSS styling to emulate a desktop-like menu behavior. When the user
clicks on that menu, then the corresponding application logic can be
performed.

These different menu option are laid out inside a TABLE tag
as individual rows.
-->
<table class="tellerOptionsTable" align="center">
<tr>
<!--
Note that we are using class attribute in span to style
the td value so that we can dynamically assign style classes
(in JavaScript) as the user moves the mouse over different
teller operation names. If we use the id attribute to
CSS style, we can't dynamically assign styles in JavaScript.
-->
<!--
The event handler functions are called with a
function parameter that indicates the menu option number.
i.e. 1, 2 or 3.
-->
<!-- Deposit menu option -->
<td>
<span id="tellerOptionsLink1" class="tellerOptionsLink"
title="Deposit to an account."
onmouseover="changeOptionsLinkStyleForSelection(1);"
onmouseout="changeOptionsLinkStyleToDefault(1);"
onclick="processTellerOperation(1);">
1) Deposit to an account
</span>
</td>
</tr>

<tr>
<!-- Debit menu option -->
<td>
<span id="tellerOptionsLink2" class="tellerOptionsLink"
title="Debit from an account."
onmouseover="changeOptionsLinkStyleForSelection(2);"
onmouseout="changeOptionsLinkStyleToDefault(2);"
onclick="processTellerOperation(2);">
2) Debit from an account
</span>
</td>
</tr>

<tr>
<!-- Get stock portfolio value menu option -->
<td>
<span id="tellerOptionsLink3" class="tellerOptionsLink"
title="Get Stock portfolio value."
onmouseover="changeOptionsLinkStyleForSelection(3);"
onmouseout="changeOptionsLinkStyleToDefault(3);"
onclick="processTellerOperation(3);">
3) Current portfolio value
</span>
</td>
</tr>
</table>
</div> <!-- End of <div id="tellerOptions"> -->

<!--
The tellerOptions DIV element holds a HTML form required for
depositing money to an account.
-->
<div id="depositAction" title="Deposit to an account" class="depositAction">
<!-- This HTML form provides the UI elements for deposit action -->
<form name="depositActionForm">
<span id="depositActionFormTitle" class="depositActionFormTitle">
Deposit to an account
</span>

<!--
This table provides a drop-down selection box and an
input control to enter the deposit amount.
-->
<table class="depositActionTable" align="center">
<tr>
<td>Select Account Owner:</td>
<td>
<select id="depositAccountOwner" size=7
name="depositAccountOwner">
</select>
</td>
</tr>

<tr>
<td>Amount:</td>
<td><input type="text" name="depositAmount"
id="depositAmount" maxlength="15" size="15"
title="e-g: 100"/></td>
</tr>
</table>

<!--
This table provides an action button. When pressed, it will
trigger an onclick event handler that will perform the
required client-side logic.
-->
<table align="center" class="depositAmountButtonTable">
<tr>
<td>
<input id="depositAmountActionButton"
value="Deposit" type="button"
title="Click here to deposit to an account."
onclick="depositAmountAction_Async();"/>
</td>
</tr>
</table>
</form>
</div> <!-- End of <div id="depositAction"> -->

<!--
The tellerOptions DIV element holds a HTML form required for
debiting money from an account.
-->
<div id="debitAction" title="Debit from an account" class="debitAction">
<form name="debitActionForm">
<span id="debitActionFormTitle" class="debitActionFormTitle">
Debit from an account
</span>

<table class="debitActionTable" align="center">
<tr>
<td>Select Account Owner:</td>
<td>
<select id="debitAccountOwner" size=7
name="debitAccountOwner">
</select>
</td>
</tr>

<tr>
<td>Amount:</td>
<td><input type="text" name="debitAmount" id="debitAmount"
maxlength="15" size="15" title="e-g: 100"/></td>
</tr>
</table>

<!--
This table provides an action button. When pressed, it will
trigger an onclick event handler that will perform the
required client-side logic.
-->
<table align="center" class="debitAmountButtonTable">
<tr>
<td>
<input id="debitAmountActionButton" value="Debit"
type="button"
title="Click here to debit from an account."
onclick="debitAmountAction_Async();"/>
</td>
</tr>
</table>
</form>
</div> <!-- End of <div id="depositAction"> -->

<!--
The portfolioOption DIV element holds the form for
updating the current portfolio value.
-->
<div id="portfolioAction" title="Update portfolio value"
class="portfolioAction">
<form name="portfolioActionForm">
<span id="portfolioActionFormTitle" class="portfolioActionFormTitle">
Get portfolio value
</span>

<table class="portfolioActionTable" align="center">
<tr>
<td>Select Account Owner:</td>
<td>
<select id="portfolioAccountOwner" size=7
name="portfolioAccountOwner">
</select>
</td>
</tr>
</table>

<!--
This table provides an action button. When pressed, it will
trigger an onclick event handler that will perform the
required client-side logic.
-->
<table align="center" class="portfolioValueButtonTable">
<tr>
<td>
<input id="portfolioActionButton"
value="Get Stock Portfolio Value" type="button"
title="Click here to get the portfolio value."
onclick="portfolioAction_Async();"/>
</td>
</tr>
</table>
</form>
</div> <!-- End of <div id="portfolioAction"> -->

<!--
The tellerActionResult DIV element holds the form for
getting portfolio value.
-->
<div id="tellerActionResult" title="Result of teller operation"
class="tellerActionResult">
<form name="tellerActionResultForm">
<span id="tellerActionResultTitle" class="tellerActionResultTitle">
Result from Teller Operation
</span>

<!--
This table includes a text area that summarizes the result of
the operation performed by the teller.
-->
<table class="tellerActionResultTable" align="center">
<tr>
<td>
<textarea id="tellerActionResultArea" rows="15"
cols="80" readonly></textarea>
</td>
</tr>
</table>
</form>
</div> <!-- End of <div id="tellerActionResult"> -->

<!--
The pageFooter DIV element holds the footer information such as the
currently logged in user and Go to main menu option. This footer
section will be stapled at the bottom of every other section of
this application.
-->
<div id="pageFooter">
<h1></h1>

<table id="footerTable" class="footerTable" align="center">
<tr>
<!--
Note that we are using class attribute in span to style
the td value so that we can dynamically assign style classes
(in JavaScript) as the user moves the mouse over the footer
options. If we use the id attribute to CSS style, we can't
dynamically assign styles in JavaScript.
-->
<td>
<span id="currentUserDisplay" class="currentUserDisplay"
title="Currently logged in user's name.">
Teller: John Doe
</span>
</td>

<!--
The event handler functions are called with a
function parameter that indicates which footer option to be
highlighted.
-->
<td>
<span id="goToMainMenuLink" class="goToMainMenuLink"
title="Click here to go to main menu."
onmouseover="changeFooterLinkStyleForSelection(1);"
onmouseout="changeFooterLinkStyleToDefault(1);"
onclick="processFooterOperation(1);">
Back to main menu
</span>
</td>
</tr>
</table>

</div> <!-- End of <div id="PageFooter"> -->
</div> <!-- End of <div id="mainPage"> -->
</body>
</html>


Listing 5. BankTeller.css
                
/*
============================================================
Project: End-to-End-ajax application development

Purpose: This is an example scenario to be used in
an IBM developerWorks article.

Last modified: May/12/2007.

This file provides CSS rules that are used to style the
UI controls for the bank teller browser application.
This application doesn't address all the features of
CSS. However, basic and important aspects of CSS
are included here in the context of the bank teller
application.
============================================================
*/
/*
This is a HTML selector that redefine the body tag with
font and background attributes.
*/
body {
font-family: Verdana, Geneva, Arial, sans-serif;
font-size: medium;
text-align: center;
background: rgb(220, 220, 220);
}

/*
This is a HTML selector used to style the <h1> element.
*/
h1 {
color: #cc6600;
border-bottom: medium double #888888;
font-size: 1.7em;
}

/*
This CSS ID class is applicable for the DIV element that holds the table in which
all the available teller operations are listed.
*/
#tellerOptions {
margin-left: 3%;
margin-right: 3%;
padding-top: 10px;
padding-bottom: 10px;
border: red;
border-width: thick;
border-style: double;
border-collapse: collapse;
}

/*
This qualified dependent selector applies style to the teller option table.
*/
table.tellerOptionsTable td {
padding-top: 10px;
padding-bottom: 10px;
}

/*
This CSS generic class is applicable to style the margins for the
tables in the teller actions form.
*/
.tellerOptionsTable,
.depositActionTable,
.debitActionTable,
.portfolioActionTable {
margin-top: 30px;
}

/*
This CSS generic class is applicable for the SPAN elements that hold the clickable text
representing various teller operations. User can select an operation to perform from
this list. Generic class is used here so that inside of JavaScript, we can dynamically
change the style of the text representing the teller operations as the user mouseover or
mouseout of those text regions defined within the SPAN elements.
The following is the default style when there is no user mouse activity or when the user
moves the mouse out.
*/
.tellerOptionsLink {
cursor: pointer;
background-color: rgb(220, 220, 220);
color: blue;
font-weight: bold;
font-style: normal;
text-decoration: none;
border-style: none;
}

.tellerOptionsLinkSelection,
.goToMainMenuLinkSelection {
cursor: pointer;
background-color: green;
color: yellow;
font-weight: normal;
font-style: italic;
text-decoration: none;
border-style: ridge;
border-width: 7px;
border-top-width: 7px;
border-color: black;
}

.depositAction {
margin-left: 3%;
margin-right: 3%;
padding-top: 10px;
padding-bottom: 10px;
cursor: default;
border: navy;
border-width: thick;
border-style: double;
border-collapse: collapse;
}

.debitAction {
margin-left: 3%;
margin-right: 3%;
padding-top: 10px;
padding-bottom: 10px;
cursor: default;
border: lightseagreen;
border-width: thick;
border-style: double;
border-collapse: collapse;
}

.portfolioAction {
margin-left: 3%;
margin-right: 3%;
padding-top: 10px;
padding-bottom: 10px;
cursor: default;
border: purple;
border-width: thick;
border-style: double;
border-collapse: collapse;
}

.tellerActionResult {
margin-left: 3%;
margin-right: 3%;
padding-top: 10px;
padding-bottom: 10px;
cursor: default;
border: rosybrown;
border-width: thick;
border-style: double;
border-collapse: collapse;
}

/*
This qualified CSS generic class defines the cell padding for the buttons in the
teller actions form and others.
*/
table.depositAmountButtonTable td,
table.debitAmountButtonTable td,
table.portfolioValueButtonTable td,
table.tellerActionResultTable td {
padding-left: 25px;
padding-right: 25px;
}

/*
This qualified CSS generic class defines the margin for the tables holding
the teller actions form and others.
*/
table.depositAmountButtonTable,
table.debitAmountButtonTable,
table.portfolioValueButtonTable,
table.tellerActionResultTable {
margin-top: 40px;
}

.depositActionFormTitle,
.debitActionFormTitle,
.portfolioActionFormTitle,
.tellerActionResultTitle {
color: olive;
font-weight: normal;
font-style: normal;
text-decoration: underline;
text-align: center;
}

/*
This qualified CSS generic class is applicable in order to set the cellpadding for
the table <td> where the footer options are listed.
*/
table.footerTable td {
padding-left: 100px;
padding-right: 100px;
}

/*
This qualified CSS generic class is applicable in order to set the margin for the
table where the footer options are listed.
*/
table.footerTable {
margin-top: 20px;
}

/*
This CSS generic class is applicable for the static display of the current username
who is logged into the system.
*/
.currentUserDisplay {
cursor: default;
background-color: rgb(220, 220, 220);
color: darkred;
font-weight: normal;
font-style: normal;
}

/*
This CSS generic class is applicable for the SPAN elements that hold the clickable text
representing footer options. User can select to logout from the system.
Generic class is used here so that inside of JavaScript, we can dynamically
change the style of the text representing the logout link as the user mouseover or
mouseout of those text regions defined within the SPAN elements.
The following is the default style when there is no user mouse activity or when the user
moves the mouse out.
*/
.gotoMainMenuLink {
cursor: pointer;
background-color: rgb(220, 220, 220);
color: black;
font-weight: bold;
font-style: normal;
text-decoration: none;
border-style: none;
}

/*
This CSS ID class is applicable for the Teller actions button and others.
*/
#depositAmountActionButton,
#debitAmountActionButton,
#portfolioActionButton {
background-color: saddlebrown;
color: white;
border: purple;
border-width: thin;
border-style: inset;
font-weight: bold;
font-style: normal;
font-size: 90%
}

/*
The following overrides a style defined previously in the same class.
*/
#debitAmountActionButton {
background-color: midnightblue;
}

/*
The following overrides a style defined previously in the same class.
*/
#portfolioActionButton {
background-color: teal;
}


Listing 6. xhr.js
                
/*
============================================================
Project: End-to-End-ajax application development

Purpose: This is an example scenario to be used in
an IBM developerWorks article.

Last modified: May/12/2007.

This JavaScript file provides specific functions related to
XHR (XML HTTP Request). The logic explained here is a
generic one which is being used hundreds of other
Web applications. This logic can be found in any
ajax related book or article that describes the inner
workings of XHR.
============================================================
*/

/*
============================================================
Function: createRequest

Last Modified: May/12/2007

This function creates an XHR object that is browser
agnostic. As of this writing, XHR is somewhat browser
dependent. We can divide XHR support in two major classes
of browsers i.e. Internet Explorer and non-IE browsers.
Then, within IE, there are different implementations of
XHR in pre-IE6 and the rest. In summary, Microsoft
supports XHR through its ActiveX. After listening to
complaints from the Web developer community, Microsoft
realized the need to expose XHR as a native browser
object as done in other browsers. The rumor is that
we will soon see XHR support in IE browsers via
XMLHttpRequest object. Until then, we have to take care of
browser dependency as shown in this function.
============================================================
*/
function createRequest() {
// Define a local variable and set it to null.
var request = null;

// Try different things to see which browser is being used.
try {
// Non Microsoft browsers (Firefox, Safari etc.)
request = new XMLHttpRequest();
} catch(trymicrosoft) {
try {
// IE6 and above.
request = new ActiveXObject("Msxml2.XMLHTTP");
} catch(othermicrosoft) {
try {
// Older versions of IE i.e. pre-IE6.
request = new ActiveXObject("Microsoft.XMLHTTP");
} catch(failed) {
// No support for XHR
request = null;
} // End of catch(failed)
} // End of catch(othermicrosoft)
} // End of catch(trymicrosoft)

// Check if we have a valid XHR object.
if (request == null) {
alert("Error creating the XMLHttpRequest object!");
} else {
// Return the valid XHR object that was created.
return(request);
} // End of if (request == null)
} // End of function createRequest.

/*
============================================================
Function: sendHttpRequest

Last Modified: May/12/2007

This function transmits any arbitrary data to a server URL.
It takes four arguments:
1) XHR object
2) Callback function (if any) to receive the server response
3) URL to which the content is to be sent.
4) Data to be sent.

The depending on the input parameters, it will either
communicate with the server asynchronously or
synchronously to do the data interchange. It uses the POST
verb of HTTP to do the REST-style call.
============================================================
*/
function sendHttpRequest(request, callbackFunction, url, postData) {
// Initialize a local variable to false.
// This variable indicates if we need to communicate with the
// server in an asynchronous mode.
var async_request = false;

// Did the caller give us a Callback function?
if (callbackFunction != null) {
// We have a callback function.
// Set that function to XHR object's onreadystatechange property.
request.onreadystatechange = callbackFunction;
// Set the local variable to indicate that
// we need to send and receive the response in a non-blocking mode
// i.e. Async.
async_request = true;
}

// Open a HTTP connection to the provided URL.
request.open("POST", url, async_request);
// Set a HTTP request header.
request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
// Send it now.
// If async mode was not true, then this call will block until a
// HTTP response is received from the server.
// Otherwise, this statement will just send and return without waiting for a
// server response.
var response = request.send(postData);

if (async_request == false) {
// We sent the request synchronously.
// Hence, return the response received from the server.
return(response);
} else {
// If the request was made asynchronously, we need not bother to return
// anything meaningful. The response will be sent directly to the
// callback function that was provided by the caller.
return(true);
}
} // End of function sendHttpRequest.

Bank 포털 브라우저 애플리케이션의 로직

XHTML 과 CSS 생성물과 XHR과 관련된 유틸리티 파일을 만들었다. XHTML 파일 (index.html)은 이 애플리케이션의 시작점이다. 이것은 은행 텔러 애플리케이션의 사용자가 브라우저의 주소 바에서 볼 수 있는 유일한 파일이다. 모든 다른 생성물들은 서버에서 브라우저 세션으로 index.html 파일과 함께 온다. index.html 파일의 <HEAD> 섹션을 보면, 다음과 같은 HTML 태그들을 볼 수 있다.

<script language="JavaScript" type="text/javascript" src="xyz.js"></script>

이 스크립트 태그는 문서 내에서 엘리먼트에 의해 호출될 수 있는 한 개 이상의 스크립트를 정의한다. 이러한 특수 상황에서, 외부 파일에서 JavaScript 코드를 로딩한다. 이는 세 개의 다른 JavaScript 파일에서 은행 텔러 애플리케이션의 JavaScript 코드를 로딩하는 방법이다.

<link rel="stylesheet" type="text/css" href="xyz.css" media="all" />

링크 태그는 외부 파일에 정의된 스타일 시트(CSS) 규칙을 연결한다. 이 문장은 지정된 CSS 파일을 HTML 문서로 연결하면서, 전체 웹 애플리케이션의 스타일에 영향을 준다.

은 행 텔러 애플리케이션을 위한 프리젠테이션 콘텐트와 연관된 모든 생성물들은 시작 시 한번 다운로드 된다. 애플리케이션의 일부가 될 개별 스크린들을 분할하여 필요할 경우 사용자에게 보이도록 한다는 의미이다. 이는 개별 <DIV> 엘리먼트 내에 스크린용 사용자 인터페이스 컨트롤 모두를 포함시킴으로써 쉽게 수행된다. 전체 애플리케이션은 mainPage라고 하는 <DIV> 엘리먼트 내부에 있다. <DIV> 엘리먼트에 모두 배치된 브라우저 애플리케이션에서 여섯 개의 다른 섹션들이 있다. 이 모든 여섯 개의 <DIV> 섹션들은 mainPage <DIV> 엘리먼트 안에 인클로즈 된다. 이러한 방식으로 다른 UI들을 구성하면, DOM 조작을 통해서 컨트롤 하기가 쉬워진다. (Part 3)

애플리케이션이 로딩되면, 이벤트 핸들러 (initOnPageLoad)가 <body> 엘리먼트에서 설정된다. 이 이벤트 핸들러는 시작 시 호출되어 초기 스크린 콘텐트들이 보이도록 설정된다. <DIV> 태그와 비슷하게, <SPAN> 태그들은 <SPAN> 태그 내에 인클로징 된 간단한 텍스트를 사용하여 초기 스크린에 메뉴와 비슷한 구조를 설정하는데 유용하게 쓰인다. 이러한 <SPAN> 엘리먼트들은 세 개의 다른 마우스 액션 관련 이벤트 핸들러를 갖고 있다. 이러한 이벤트 핸들러는 CSS 스타일 규칙을 사용하여 메뉴 아이템을 강조한다. 이러한 메뉴 텍스트 아이템들 중 하나가 클릭되면, 상응하는 온클릭 이벤트 핸들러가 브라우저에 상응하는 스크린이 나타나도록 한다. 이 모든 일이 서버로 가지 않고 단지 브라우저 DOM을 로컬에서 조작하여 수행된다. 또 다른 재미있는 사용자 인터페이스 컨트롤은 풋터(footer) 섹션이다. 언제라도, 스크린 레이아웃은 mainPagepageFooter <DIV> 엘리먼트를 갖고 있다. 다른 섹션들 중 하나(예금, 대출, 포트폴리오, 은행 텔러 액션 결과)는 두 개의 <DIV> 엘리먼트의 중간에 배치되지만, 이것 역시 유연한 ajax 디자인 원리이다.

CSS 파일(BankTeller.css) 에는 브라우저 애플리케이션의 사용자 인터페이스 엘리먼트에 적용되는 CSS 규칙이 포함된다. CSS의 여러 기능들이 이러한 CSS 규칙들을 통해 설명된다. HTML 셀렉터, 아이디 셀렉터, 클래스 셀렉터들은 이 애플리케이션에서 광범위하게 사용된다.

예를 들어, CSS 파일에서, 바디 HTML 셀렉터는 전체 브라우저 애플리케이션 페이지의 폰트와 배경색을 재 정의한다. CSS 파일 밑에서는 portfolioActionButton이 사용되는데, 이것은 아이디 셀렉터이다. 이는 그 아이디 스트링이 있는 HTML 파일(index.html)에 정의된 모든 HTML 태그들이 #portfolioActionButton CSS 규칙 밑에서 정의되는 스타일을 수행한다. 비슷하게, .tellerOptionsLink CSS 규칙에서 클래스 셀렉터가 사용됨을 볼 수 있는데, 이는 tellerOptionsLink 클래스에 할당된 모든 HTML 태그에 적용된다. 또한, 그룹 셀렉터의 사용에 주목하라. 여기에서 두 개 이상의 셀렉터들이 그룹핑 되어, 같은 스타일 선언을 갖고 있다. 그룹 셀렉터의 예제는 BankTeller.css 파일의 여러 곳에서 참조할 수 있다. CSS 파일의 밑 부분에, 세 개의 모든 액션 버튼이 함께 그룹핑 되어(#depositAmountActionButton, #debitAmountActionButton, #portfolioActionButton) 같은 버튼 스타일 프로퍼티를 받는다는 것을 알 수 있다. descendant selector라고 하는 고급 CSS 기능 역시 나타나 있다. 이 descendant selector는 부모 셀렉터에 기반하여 개별적인 자손 엘리먼트들을 스타일링 할 수 있다. 특정 셀 패딩을 할당함으로써, <td> in <table> 같이, 테이블에 모든 칼럼들을 스타일링 하는 예제도 CSS 파일에는 있다. table.depositAmountButtonTable td를 검색하면 descendant 셀렉터 애플리케이션을 볼 수 있다. 그림 2를 참조하여 HTML, 아이디, 클래스 셀렉터용 CSS 신택스를 확인해 보라.

XHR 파일 (xhr.js)에는 두 개의 함수가 포함된다. 한 개는 애플리케이션 스팩의 정보를 중간 티어 서비스와 교환하는(송수신 하는)데 사용될 수 있는 XHR 객체를 만드는 것이다. createRequest 함수는 XHR 객체를 준비한다. 이 글을 쓰고 있는 현재, XMLHttpRequest는 다양한 브라우저에서 여러 방식으로 지원된다. 특히, Internet Explorer (IE)와 Mozilla Firefox는 XHR을 다르게 지원한다.

XHR은 다양한 버전의 IE에서 다른 방식으로 구현된다. 이 함수는 브라우저의 기술을 사용하여 XHR 객체를 만들려고 한다. 비 IE 브라우저에서, XHR 객체는 빌트인 XMLHttpRequest 클래스를 인스턴스화 함으로써 단순히 생성될 수 있다. IE 버전 6.0에서, Msxml2의 ActiveXObjectXMLHTTP를 사용하여 수행된다. 또 다른 버전의 IE에서, ActiveXObjectXMLHTTP의 다른 인스턴스를 사용하여 수행된다. Microsoft는 다른 브라우저와 비슷한 XHR 지원을 제공한다. 이것이 발생하기 전까지, 이 함수의 로직이 필요하다. 다른 함수인 sendHttpRequest는 임의의 데이터를 중간 티어 서비스로 보내는데 사용된다. 이것은 POST HTTP를 사용하여 REST 스타일 액세스 메커니즘을 사용하여 중간 티어 서비스와 인터랙팅 한다. 이 함수는 다음과 같은 네 개의 매개변수를 취한다.

  • XHR 객체
  • Callback 함수 (어떤 콜백도 필요하지 않을 경우 null이다.)
  • 데이터가 보내질 URL
  • 데이터 (중간 티어 서비스로 보내질 애플리케이션 스팩의 데이터)

sendHttpRequest 메소드는 XHR 객체를 사용하여 중간 티어 서비스로 데이터를 보낸다. 콜백 함수가 제공되면, 이 함수는 XHR 객체의 onreadystatechange 프로퍼티를 콜백 함수로 설정한다. 그리고 나서, HTTP를 중간 티어 서비스로 연결한다. 필요한 HTTP 헤더를 설정하고 데이터를 중간 티어 서비스로 보낸다. 콜백 함수가 제공되면, XHR 객체의 send 메소드는 서버 응답을 기다리지 않고 즉시 리턴한다. 이 경우, 서버 응답은 서버 응답이 브라우저에 도착할 때 콜백 함수에 의해 핸들된다. 자세한 내용은 Part 3에서 다루도록 하겠다. 콜백 함수가 제공되지 않으면, send 함수는 차단되고 서버 응답이 도착할 때까지 기다리거나, HTTP 에러를 일으킨다.

Listing 4의 싱글 페이지 브라우저 애플리케이션은 예금, 대출 포트폴리오 값 계산 같은 은행 연산을 수행할 때 은행 텔러에 의해 사용되는 간단한 브라우저 인터페이스를 만든다. 이 코드는 싱글 페이지 웹 애플리케이션의 일부로서 세 가지 폼을 제공한다. 이러한 세 개의 폼 각각은 어카운트 홀더 이름으로 채워진 HTML 드롭다운 박스를 제공한다. 그리고 나서, 사용자가 예금 또는 대출 할 금액을 입력할 수 있는 편집 필드가 제공된다. 세 가지 액션 버튼으로 은행 텔러는 필요한 은행 함수를 수행한다. 이 애플리케이션의 세 가지 버튼은 중간 티어 REST 서비스에 액세스 하도록 설계되는데, Part 3에서 개발할 것이다. PHP 모듈을 개발하기 까지, 이러한 은행 텔러 함수들은 기능을 하지 않는다. 마찬가지로, Part 3에서 JavaScript 파일(BankTeller.js)을 개발할 것이다.

새 로운 많은 ajax 기술들은 은행 시나리오를 개발하는데 사용되었다. 여러분이 신참 개발자라면, 이 글의 논의가 생소할 것이다. 반 구현된 브라우저 코드와 중간 티어 코드를 분석하기 바란다. 여러분이 숙련된 웹 개발자라면, 나머지 태스크를 직접 수행해 보기 바란다. 나머지 태스크를 끝마치지 못했다고 걱정하지 말라. -- Part 3에서 계속 진행할 것이다.

소셜 북마크

mar.gar.in mar.gar.in
digg Digg
del.icio.us del.icio.us
Slashdot Slashdot

결론

지금까지 은행 시나리오의 주요 부분들을 구현했다. 엔드투엔드 ajax 애플리케이션 시나리오의 일부를 통해서 데이터베이스, PHP 모듈에 구현된 핵심 비즈니스 로직, ajax 기술을 통해 전달되는 웹 사용자 인터페이스를 배웠다. Part 1의 은행 시나리오 다이어그램에서, 다음 아이템들을 구현하여 전체 시나리오를 완성해야 한다.

  • 서버와 데이터 교환을 수행하는 클라이언트 측 로직과 비동기식 통신 로직을 갖고 있는 JavaScript 모듈
  • 원격 주식 시세 웹 서비스용 웹 서비스 SOAP 클라이언트 코드를 포함하고 있는 PHP 모듈
  • 이 시나리오의 일부인 다른 PHP 모듈에서 비즈니스 로직에 대한 프론트엔드 호출에 대한 REST 서비스로 작동하는 PHP 모듈
  • 시나리오를 완성하는 모든 생성물들의 통합
  • end-to-end ajax-PHP 시나리오 전개 및 테스트
  • 클라이언트 측 및 서버 측 디버거를 사용한 end-to-end 시나리오 디버깅

지금까지 Part 1과 Part 2에서 설명한 내용을 통해, 세 개의 티어에서 사용할 수 있는 오픈 소스 기술들의 다양한 기능과 Eclipse ajax-PHP 개발 툴에 대한 이해가 생겼으리라 믿는다. SQL의 DB 생성용 코드 생성물, PHP로 된 서버 측 Bank Logic, Bank Portal (XHTML, CSS, XHR)이 다운로드 파일 섹션에서 제공된다. 이 시리즈의 세 번째이며 마지막 Part인 기술자료를 기다려 주기 바란다. 그때까지 ajax와 PHP 개념을 익히고 지금까지 시나리오에서 적용된 개발 기술들을 복습하기 바란다.





위로


다운로드 하십시오

이름크기다운로드 방식
wa-aj-end2end2.zip14KBHTTP
다운로드 방식에 대한 정보


참고자료

교육

제품 및 기술 얻기


필자소개

Photo of Senthil Nathan

Senthil Nathan은 뉴욕에 위치한 IBM T.J. Watson Research Center의 소프트웨어 엔지니어이다. 22년 이상을 다양한 종류의 엔터프라이즈 애플리케이션 구현했다. 현재 SOA, Web services, Java 2 Platform, Enterprise Edition (J2EE), PHP, Ruby On Rails, Web 2.0, ajax 개발에 관심을 갖고 있다.