메인 컨텐츠로 가기

developerWorks 이용 약관에 동의하시는 경우 제출을 클릭하십시오. 이용 약관 보기.

developerWorks에 처음 로그인하면 developerWorks프로파일이 생성됩니다.귀하의 프로파일에서 동의하신 내용이 공개되지만 이 사항은 언제든지 변경 가능합니다. 귀하의 성명(숨김으로 체크되어 있어도 표시됩니다)과 디스플레이 이름은 게시한 컨텐츠나 사이트 엑세스시 표시됩니다.

모든 정보가 안전하게 전송되었습니다.

  • 닫기 [x]

처음 developerWorks에 로그인할 때 프로파일이 작성되므로, 이를 위해 디스플레이 이름을 선택해야 합니다. 선택하신 디스플레이 이름은 developerWorks에 게시한 컨텐츠에 표시됩니다.

3글자 이상 31글자 이하의 길이로 사용 가능합니다. dW커뮤니티 내에서는 보안상 이메일주소를 제외한 다른 이름을 지정하셔야 합니다.

developerWorks 이용 약관에 동의하시는 경우 제출을 클릭하십시오. 이용 약관 보기.

모든 정보가 안전하게 전송되었습니다.

  • 닫기 [x]

Flex 4 및 Java 웹 애플리케이션 작성

오브젝트 원격화 발견

Arron Ferguson, Giảng viên, British Columbia Institute of Technology
Photo of Arron Ferguson
Arron Ferguson là một giảng viên đại học đã 12 năm, dạy về kỹ thuật phần mềm tại Viện Công nghệ British Columbia. Các lĩnh vực kinh nghiệm và quan tâm của ông là công nghệ Java, XML, các công nghệ Web, hoạt hình 2D và 3D, và viết cho các phương tiện truyền thông kỹ thuật số. Ông là soạn giả và nhà phê bình kỹ thuật tự do và có cuốn sách được xuất bản: Tạo Các Hệ thống Quản trị Nội dung trong Java (Creating Content Management Systems in Java) (Charles River Media, 2006). Arron cũng say mê Linux và công nghệ mã nguồn mở khác.

요약:  리치 인터넷 애플리케이션(RIA)은 종종 다른 번들화된 기술을 활용합니다. 기술을 올바르게 그룹화하도록 선택하면 개발 시간을 신속히 처리하고 사용자에게 완성된 리치 인터넷 경험을 제공할 수 있습니다. 서버측에서 Java™ EE 플랫폼 컴포넌트, 클라이언트측에서 Adobe® Flex™ 플랫폼 및 스토리지 지속성을 위한 MySQL® 데이터베이스 서버를 사용하는 방법을 발견하십시오.

원문 게재일:  2010 년 4 월 27 일 번역 게재일:   2010 년 10 월 04 일
난이도:  중급 영어로:  보기 PDF:  A4 and Letter (104KB | 21 pages)Get Adobe® Reader®
페이지뷰:  3679 회
의견:  


현재 웹 기술에는 증가된 요구가 주어진다. 이들은 사용자 계정, 업로드 내용 및 스트림 비디오를 관리할 수 있어야 한다. 이 요구로 인해 RIA 개발자는 일반적으로 인기있는 기능을 제공하는 동시에 개발 워크플로를 간소화하는 기술을 찾아야 한다. 개발자의 도전 과제는 이러한 서비스를 제공하기 위한 올바른 기술 세트를 선택하는 것이다.

자주 사용하는 약어

  • AMF: Action Message Format
  • API: Application programming interface
  • CSS: Cascading stylesheet
  • GUI: Graphical user interface
  • HTTP: Hypertext Transfer Protocol
  • JAR: Java archive
  • POJO: Plain old Java object
  • RIA: Rich Internet Application
  • RPC: Remote procedure call
  • SDK: Software development kit
  • SQL: Structured Query Language
  • UI: User interface
  • WAR: Web Archive
  • XML: Extensible Markup Language

Adobe Flex는 개발자에게 GUI를 작성하고, 그래픽을 그리고, 미디어를 재생하고 스트리밍하며, 웹 서비스에 연결하기 위한 리치 API 호출 세트를 제공하는 클라이언트측 기술이다. 서버측에서 Java 기술은 관계형 데이터베이스 관리 시스템(RDBMS)으로의 연결, 서비스 요청의 멀티스레드형 프로세싱 및 증가한 요구로 최적의 크기 조정 등과 같은 기능을 제공한다. 이러한 두 가지 기술을 함께 사용하면 RIA 애플리케이션의 요구를 충족하는 강력한 기술 스택이 제공된다.

이 기사에서는 간단하지만 강력한 RIA를 쓰는 방법을 시연하는데, 이는 클라이언트에 대해 Flex, 서버에 대해서는 Java 기술, 백엔드 데이터베이스에 대해서는 MySQL을 활용한다.

샘플 애플리케이션

샘플 애플리케이션(아래 다운로드 섹션에서 사용 가능)은 연락처 정보 작성, 읽기, 업데이트 및 삭제(CRUD)를 지원하는 리치 UI를 제공하는데, 이는 Adobe Flash® (SWF) 애플리케이션을 사용한 것이다. 이 세 계층의 웹 아키텍처는 그림 1에 묘사되어 있다. 클라이언트가 웹 페이지에서 임베드된 SWF 파일로 표현되고, 서버 애플리케이션은 Java 서블릿 컨테이너(이 경우에는 Apache Tomcat)에서 실행되고, 데이터베이스는 MySQL이다. 결합된 이러한 세 계층은 동력 분배형 애플리케이션이다.


그림 1. 연락처 애플리케이션
방금 설명한 대로 동력 분배형 애플리케이션이 표시된 다이어그램

Flash 애플리케이션과 Java 서블릿 컨테이너 사이의 통신을 위해 Adobe BlazeDS 프레임워크가 오브젝트 원격화를 제공한다. —Adobe ActionScript™ 오브젝트가 Java 오브젝트를 호출하도록 허용하는 경우 및 그 반대의 경우에 대한 RPC의 형태이다. Java 서버 애플리케이션과 관계형 데이터베이스 사이의 통신은 Hibernate ORM(Object Relational Mapping) 프레임워크로 처리된다. Hibernate를 통해 Java 오브젝트는 SQL 코드로 전환될 수 있으며, 그 반대의 경우도 될 수 있다.


애플리케이션: 서버 계층

첫 단계는 연락처 정보를 저장하기 위해 필요한 정보를 아우르는 Java 클래스를 작성하는 것이다. 샘플 애플리케이션은 기본 정보를 사용한 간단한 모델이 들어있다. Contact 오브젝트에 필요한 속성과 데이터 유형은 다음과 같다.

- String emailAddress
- String firstName
- long id
- String lastName
- String phoneNumber
- long serialVersionUID
+ Contact()
+ Contact(String first, String last, String email, String number)
+ String getEmailAddress()
+ String getFirstName()
+ long getId()
+ String getLastName()
+ String getPhoneNumber()
+ void setEmailAddress(String address)
+ void setFirstName(String first)
+ void setId(long newId)
+ void setLastName(String last)
+ void setPhoneNumber(String number)
+ StringtoString()

비즈니스 오브젝트 어노테이트하기

Java Contact 클래스는 비즈니스 오브젝트로 작동하는 POJO(Plain old Java object)로 간주되는데, 이는 비즈니스 도메인 특성과 작동을 표현한다는 의미이다. Contact 오브젝트 내의 데이터는 데이터베이스와 지속성을 갖추어야 한다. 그 해결책은 Hibernate와 같은 ORM 프레임워크를 사용하는 것이다. 이는 오브젝트를 데이터베이스 테이블 내에서 레코드를 향하다가 다시 돌아오도록 맵핑하는데 작업의 많은 부분을 수행한다. JPA(Java Persistence API) 어노테이션이 사용되면 ORM을 채우기 위해 필요한 코딩은 매우 적다. Listing 1에서는 어노테이트된 Java 클래스 Contact를 시연한다.


Listing 1. Java Contact 클래스
	
package bcit.contacts;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;

@Entity
@Table(name="contact")
@NamedQueries( {
    @NamedQuery(name = "contact.findAll", query = "from Contact"),
    @NamedQuery(name = "contact.getById", query =
        "select c from Contact c where c.id = :id")
} )
public class Contact {

    private static final long serialVersionUID = 123456789L;

    public Contact() {
        firstName = "N/A";
        lastName = "N/A";
        emailAddress = "N/A";
        phoneNumber = "N/A";
    }

    public Contact(String first, String last, String email, String number) {
        firstName = first;
        lastName = last;
        emailAddress = email;
        phoneNumber = number;
    }

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id", nullable = false, updatable=false)
    private long id;

    @Column(name = "lastName", nullable = false, unique = false)
    private String lastName;

    @Column(name = "firstName", nullable = false, unique = false)
    private String firstName;

    @Column(name = "emailAddress", nullable = false, unique = false)
    private String emailAddress;

    @Column(name = "phoneNumber", nullable = false, unique = false)
    private String phoneNumber;

    public void setPhoneNumber(String number) { phoneNumber = number; }

    public String getPhoneNumber() { return phoneNumber; }

    public String getEmailAddress() { return emailAddress; }

    public void setEmailAddress(String address) { emailAddress = address; }

    public String getFirstName() { return firstName; }

    public void setFirstName(String first) { firstName = first; }

    public String getLastName() { return lastName; }

    public void setLastName(String last) { lastName = last; }

    public long getId() { return id; }

    public void setId(long newId) { id = newId; }

    @Override
    public String toString() {
        return id + " " + firstName + " " + lastName + " " + emailAddress
            + " " + phoneNumber;
    }
}

클래스는 단순하지만 어노테이션과 관련하여 많은 작업이 이루어지고 있다.

  • @Column: 열의 이름이 고유하든지, 열이 널 가능(nullable) 상태이든지 열의 이름을 포함한 선택을 사용하여 데이터베이스에서 열로 특성을 레이블한다.
  • @Entity: 클래스를 엔티티 bean으로 선언하여, 이는 지속성을 위해 POJO 계획된 것임을 의미한다.
  • @GeneratedValue: 기본 키를 생성하기 위한 전략을 지정한다. 선택 사항은 AUTO, IDENTITY, SEQUENCETABLE이다.
  • @Id: 특성이 각 Java 오브젝트에 대해 고유한 식별자(즉, 기본 키)임을 명시한다.
  • @NamedQueries: 이름이 있는 쿼리의 그룹을 나열한다.
  • @NamedQuery: 향후 실행에 참조될 수 있는 문자열 리터럴로 사전정의된 쿼리를 선언한다.
  • @Table: Java 클래스를 데이터베이스 내에서 테이블로 지명한다.

내장 메모리 Java 오브젝트가 지속성을 위해 필요할 때마다 Hibernate는 Java 오브젝트의 상태 정보를 SQL 업데이트로 변환한다. 이와 마찬가지로 결과 세트가 있는 SQL 문은 Java 오브젝트를 입력하기 위해 사용된다. 그 결과, 모든 오브젝트가 데이터베이스 내에서 레코드로 저장될 수 있고 모든 레코드는 검색될 수 있으며 Java 오브젝트로 다시 변환될 수 있다.

어노테이션은 클래스 내에서 지속성이 고려되어야 하는 것을 알려준다. 하지만 이는 그림의 일부분에 불과하다.

비즈니스 서비스: 데이터베이스 연결

서비스 클래스는 ORM을 실행하도록 Hibernate에 호출을 수행하기 위해 필요하다. Listing 2는 애플리케이션 서비스로 작동하는 ContactsService 클래스를 표시한다.


Listing 2. ContactsService 클래스
	
public class ContactsService {

    private static Logger logger = Logger.getLogger(ContactsService.class);

    private static final String PERSISTENCE_UNIT = "contacts";

    private static EntityManagerFactory emf = null;

    static {
        logger.info("LOADING CONTACTSSERVICE CLASS.");
        emf = Persistence.createEntityManagerFactory(PERSISTENCE_UNIT);
    }

    public ContactsService() {
        super();
    }

    public void addContact(Contact c) {
        if(c == null) {
            return;
        }

        EntityManager em = emf.createEntityManager();
        logger.info("PERSISTENCE ENTITYMANAGER ACQUIRED.");

        logger.info("ABOUT TO ADD CONTACT: fName: " + c.getFirstName()
            + ", lName: " + c.getLastName() + ", email:" + c.getEmailAddress()
            + ", phone: " + c.getPhoneNumber());
        EntityTransaction tx = em.getTransaction();
        try {
            tx.begin();
            em.merge(c);
            tx.commit();
        } catch (Exception e) {
            logger.error("CONTACT APP PERSISTING ERROR: " + e.getMessage());
            tx.rollback();
        } finally {
            logger.info("CONTACT APP CLOSING ENTITY MANAGER.");
            em.close();
        }
    }

    public void editContact(Contact c) {
        logger.info("CONTACT TO UPDATE: " + c);
        addContact(c);
    }

    public void deleteContact(Long id) {
        logger.info("ABOUT TO DELETE CONTACT");

        EntityManager em = emf.createEntityManager();
        logger.info("PERSISTENCE ENTITYMANAGER ACQUIRED.");

        Query contactByIdQuery = em.createNamedQuery("contact.getById");
        contactByIdQuery.setParameter("id", id);
        Contact c = (Contact) contactByIdQuery.getSingleResult();
        EntityTransaction tx = em.getTransaction();
        try {
            tx.begin();
            em.remove(c);
            tx.commit();

        } catch (Exception e) {
            logger.error("CONTACT APP PERSISTING ERROR: " + e.getMessage());
            tx.rollback();
        } finally {
            logger.info("CONTACT APP CLOSING ENTITY MANAGER.");
            em.close();
        }
    }

    public List<Contact> getContacts() {
        logger.info("ABOUT TO RETRIEVE CONTACTS");

        EntityManager em = emf.createEntityManager();
        logger.info("PERSISTENCE ENTITYMANAGER ACQUIRED.");

        Query findAllContactsQuery =
            em.createNamedQuery("contact.findAll");
        List<Contact> contacts = findAllContactsQuery.getResultList();

        if (contacts != null) {
            logger.debug("CONTACT APP RETRIEVED: " + contacts.size()
                + " CONTACT(S)");
        }
        return contacts;
    }
}

각 메소드는 내장 메모리 캐시를 표현하는 EntityManager로 참조를 얻는다. 캐싱은 효율성을 보장하는 강력한 기능이다. 왜냐하면 데이터베이스로부터 데이터를 전송하고 수신하는 것은 비용이 많이 드는 조작이 될 수 있기 때문이다. 작성된 모든 캐시가 데이터베이스로 커미트되고 원하지 않는 경우에 롤백되는지만 확인해야 한다.

JPA에서 캐시에 주어진 용어는 지속성 컨텍스트이며, 이는 EntityManager 클래스로 표현된다. 각 지속성 컨텍스트는 @Entity 어노테이션으로 어노테이트된 Java 오브젝트인 엔티티 세트를 관리한다. EntityManagerFactory 클래스는 지속성 단위를 표현하는데, 이는 데이터 저장소로의 연결을 구성하고(예: 관계형 데이터베이스), 엔티티 유형을 관리하며(다시 말해서, 데이터 저장소로 맵핑하는 데 필요한 주어진 컨텍스트 내의 모든 클래스), 마지막으로 지속성 컨텍스트의 인스턴스를 제공하는(즉, EntityManager) 책임이 있다.

DB2 Express 9 데이터베이스 서버 무료 버전 사용해 보기

DB2 Express-C는 는 신속하게 실행할 수 있도록 설계되어 있으며 사용이 쉽고 임베드하기가 용이하며 자체적인 관리 기능이 있다. 또한 pureXML®과 같은 DB2 for Linux®, UNIX® 및 Windows™의 핵심 기능을 모두 갖추고 있다. DB2 Express-C는 다른 DB2 Express 에디션과 동일한 핵심 데이터 서버 기반을 제공하며 또한, C/C++, Java™, .NET®, PHP, Ruby on Rails, Python 및 기타 프로그래밍 언어를 사용하여 개발된 애플리케이션을 빌드하고 전개하는 데 필요한 견고한 기반을 제공한다.

비록 지속성 컨텍스트를 작성하는 프로세스가 저렴하고 시간 절약형이라고 하더라도, 지속성 단위를 작성하는 프로세스는 비용이 많이 든다. 데이터 저장소로 연결을 설정하고 엔티티로 어노테이트된 모든 클래스를 찾고 이러한 클래스를 데이터 저장소에서 엔티티로 바인딩하기 위한 지속성 로직을 구성하는 것은 빠른 조작이 아니다. 따라서 애플리케이션 시작 시에 EntityManagerFactory 인스턴스를 작성해야 한다. 지속성 컨텍스트를 위해 EntityManager를 또 작성하기 전에 파괴되었는지 확인하는 것에 주의하자. 또 다른 중요한 규칙은 entitymanager-per-request 패턴을 따르는 것이다. 이 패턴은 데이터베이스 호출을 그룹화(예를 들어, 요청과 업데이트)하기 때문에, 이 모두를 한 번에 전송할 수 있다. 이렇게 하면 JPA의 캐싱 메커니즘을 완전히 활용하는 것이 보장된다.

다음 요구사항은 클라이언트측이다.


애플리케이션: 클라이언트 계층

Flex 프레임워크를 통해 사용자는 Adobe Flash Player에서 자유롭게 작업할 수 있는 애플리케이션을 작성할 수 있다. Flex는 다음으로 이루어진다.

  • MXML로 알려진 선언적인 XML UI 언어
  • ActionScript 프로그래밍 언어
  • UI, 웹 연결 및 기타 많은 기능을 작성하기 위한 런타임 라이브러리
  • 애플리케이션을 SWF 파일로 컴파일링하기 위한 개발자 도구

이 기사에서 참조된 클라이언트 애플리케이션은 Flex 버전 4를 사용한다. 클라이언트측 애플리케이션에 접근하기 전에 Flex 애플리케이션이 어떻게 작성되었고, 어떻게 Flash Player에서 실행 가능한 상태로 존재하는지 이해하는 것이 중요하다.

먼저 MXML 마크업과 ActionScript 코드의 조합을 사용하여 애플리케이션을 작성할 수 있다. 일반적인 워크플로는 MXML 형식을 사용하여 대부분의 GUI(프리젠테이션)를 작성한 다음에 이벤트 핸들링 및 비즈니스 로직을 위해 ActionScript 코드를 사용한다. MXML과 ActionScript가 둘 다 텍스트 기반이기 때문에 표준 텍스트 편집기와 Flex SDK가 Flash 애플리케이션을 작성하기 위해 필요한 전부이다.

두 번째, Flex 애플리케이션을 쓰면 MXML 컴파일러를 사용하여 코드를 컴파일한다. MXML 컴파일러는 웹 브라우저 내에서 실행될 수 있는 SWF 파일을 작성한다(Flash Player 브라우저 플러그인 사용).

마지막으로 Flash 애플리케이션은 타임라인 패러다임을 사용하는 AVM2(ActionScript Virtual Machine 2) 내에서 실행한다. 이 패러다임은 실행을 프레임으로 분해한다—이는 마치 영화와 같다. 컴파일 시간에 Flash 애플리케이션에서 초당 프레임 수를 지정한다. 추가적으로 Flash Player는 실행을 다음의 순서화된 태스크로 분해한다.

  • 타이머와 마우스 이벤트와 같은 Flash Player 이벤트
  • 사용자 코드
  • Flash Player가 데이터 값 변경으로 인해 GUI를 업데이트해야 하는지 여부를 판단하기 위해 시도하는 사전 렌더링된 로직
  • 데이터 값 변경으로 바인드된 사용자 코드
  • Flash Player 렌더링

렌더링하는 초당 프레임의 수가 적으면 많은 수의 사용자 코드가 실행될 수 있다. 그러나 프레임 비율이 높으면(예: 초당 60프레임), 사용자 코드는 예상할 수 있는 것보다 더 많은 시간이 걸리기 때문에 Flash Player는 많은 수의 사용자 코드를 실행할 수 없을 것이다. Flash Player를 쓸 때에 이를 염두에 두는 것이 중요하다.

MXML

MXML은 다음과 같이 유용한 강력한 선언적인 XML 형식이다.

  • XML 형식의 선언적인 성향으로 인해 GUI를 빌드하는 데 필요한 코드의 양이 최소화된다.
  • 프리젠테이션 로직과 상호 작용 로직을 명백하게 분리할 수 있기 때문에 GUI 코드의 복잡도가 줄어든다.
  • 소프트웨어 개발을 접근할 때에 설계 패턴의 사용을 촉진한다.

Listing 3에는 MXML Application 클래스가 표시된다.


Listing 3. ContactsApp 클래스
	
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
  xmlns:contact="bcit.contacts.*" creationComplete="initPage();"
  layout="vertical" frameRate="30" pageTitle="Contacts Example"
  horizontalAlign="center" verticalAlign="middle" 
  backgroundColor="#A9C0E7">


  <mx:Style>
    .mainBoxStyle {
      borderStyle: solid;
      paddingTop: 5px;
      paddingBottom: 5px;
      paddingLeft: 5px;
      paddingRight: 5px;
    }

    .textMessages {
      fontWeight: bold;
    }
  </mx:Style>


  <mx:RemoteObject id="remotingService" showBusyCursor="false"
    destination="contacts" fault="handleFault(event);"
    result="handleResult(event);"/>

  <mx:Script>
    <![CDATA[

        import mx.rpc.events.FaultEvent;
        import mx.rpc.events.ResultEvent;
        import mx.collections.ArrayCollection;
        import bcit.contacts.dto.Contact;

        [Bindable]
        private var contacts:ArrayCollection = new ArrayCollection();

        // For more on the Bindable metadata tag, see the devguide_flex3.pdf
        // document, page 1249 (1257 in PDF page numbering)
        [Bindable]
        private var message:String = "Status: Ready";

        private var contact:Contact;

        public function setControlBarValid(valid:Boolean):void {
            if(valid) {
                // if the selected item is -1, then no item is selected but at
                // the same time the fields are valid which means the user chose
                // to add a contact, not update one
                if(contactsDataGrid.selectedIndex == -1) {
                    createButton.enabled = valid;
                } else {
                    editButton.enabled = valid;
                }
              } else {
                  // else nothing is valid
                  createButton.enabled = false;
                  editButton.enabled = false;
            }
        }

        private function initPage():void {
            editContactForm.setApp(this);
            contact = new Contact();
            getAllContacts();
            resetPage();
        }

        private function createContact():void {
            contact = editContactForm.getContact();
            remotingService.addContact(contact);
            message = "Status: Contact Added";
            getAllContacts();
        }

        private function editContact():void {
            var id:Number = contact.id;
            contact = editContactForm.getContact();
            contact.id = id;
            remotingService.editContact(contact);
            message = "Status: Contact Edited";
            getAllContacts();
        }

        private function deleteContact():void {
            if(contactsDataGrid.selectedItem != null) {
                var c:Contact = contactsDataGrid.selectedItem as Contact;
                // no sense in sending the whole contact - just send the id
                // to cut down on bandwidth
                remotingService.deleteContact(c.id);
                message = "Status: Contact Deleted";
            }
            getAllContacts();
        }

        private function getAllContacts():void {
            loadButton.enabled = false;
            remotingService.getContacts();
            loadButton.enabled = true;
            resetPage();
        }

        private function populateFormWithContact():void {
            contact = contactsDataGrid.selectedItem as Contact;
            editContactForm.setContact(contact);
            editButton.enabled = true;
            deleteButton.enabled = true;
        }

        private function resetPage():void {
            editContactForm.clearForm();
            contact = new Contact();
            createButton.enabled = false;
            editButton.enabled = false;
            deleteButton.enabled = false;
            contactsDataGrid.selectedIndex = -1;
        }

        private function handleFault(e:FaultEvent):void {
            message = "Status: Error"
                + "\nFault code: " + e.fault.faultCode
                + "\nFault detail: " + e.fault.faultDetail
                + "\nFault string: " + e.fault.faultString;
        }

        private function handleResult(e:ResultEvent):void {
            // can get the results by accessing e.result property
            //mx.controls.Alert.show(e.toString());
            contacts = e.result as ArrayCollection;
            var number:int = contacts.length;
            //if(number == 1) {
            //    message = "Status: Retrieved 1 contact";
            //} else {
            //    message = "Status: Retrieved " + contacts.length + " contacts";
            //}
        }

    ]]>
  </mx:Script>

  <mx:VBox styleName="mainBoxStyle">

    <mx:Text id="titleText" text="Single click to select a contact"/>

    <contact:ContactsDataGrid id="contactsDataGrid" dataProvider="{contacts}"
      itemClick="populateFormWithContact();"
      doubleClick="populateFormWithContact();"/>

    <contact:EditContactForm id="editContactForm"/>

    <mx:ControlBar horizontalAlign="center">
      <mx:Button label="List" id="loadButton" click="getAllContacts()"
        toolTip="Retrieve contacts from the server"/>
      <mx:Button label="Add" id="createButton" click="createContact()"
        toolTip="Create a new contact"/>
      <mx:Button label="Update" id="editButton" click="editContact()"
        toolTip="Edit a selected contact"/>
      <mx:Button label="Delete" id="deleteButton" click="deleteContact()"
        toolTip="Delete a selected contact"/>
      <mx:Button label="Clear Form" id="clearButton" click="resetPage()"
        toolTip="Clear the form"/>
    </mx:ControlBar>

    <mx:TextArea text="{message}" styleName="textMessages" wordWrap="true"
      verticalScrollPolicy="auto" horizontalScrollPolicy="off" editable="false"
      width="100%"/>

  </mx:VBox>

</mx:Application>

여기에 Listing 3에 적용되는 몇 가지의 포인트가 더 있다.

  • MXML 문서의 루트 요소는 Application 클래스의 서브클래스이다.
  • mx:Style 요소는 CSS 특성이 UI 컴포넌트에 로컬 스타일링을 정의하도록 할 수 있다. 스타일링은 로컬 스타일 정의(Listing 3에 있는 대로), 외부 스타일 시트로의 참조, 컴포넌트 내에서의 인라이닝 스타일 및 ActionScript에서 setStyle 메소드를 사용하여 완성될 수 있다.
  • RemoteObject 클래스는 서버와의 원격 조작을 수행하는 HTTP 서비스 오브젝트를 표현한다.
  • mx:Script 요소는 CDATA 섹션에서 ActionScript 코드 블록을 포함한다.
  • 하나의 레이아웃이 있다(다시 말해, VBox 클래스).
  • UI 컴포넌트가 애플리케이션에서 선언될 때마다(예: TextArea) 인스턴스 변수가 생성되는데, 이는 컴포넌트의 id 특성을 사용하여 애플리케이션 내에서 향후 참조될 수 있다.
  • 데이터 바인딩은 중괄호를 사용하여 수행된다(예를 들어, TextArea 요소의 text 속성은 ActionScript message 인스턴스 변수로 바인딩된다).

ActionScript

MXML이 GUI를 정의하는 동안, ActionScript는 이벤트 처리를 위한 작동, 데이터 바인딩 ([Bindable] 메타데이터 태그 사용) 및 원격 서비스로 호출하는 기능을 제공한다. Listing 3에서 메소드 createContact, editContact, deleteContactgetAllContacts는 모두 서버측에서 원격 메소드를 호출한다. 원격 메소드가 호출되면 ActionScript는 콜백 함수의 선언으로 인한 결함과 결과를 처리하기 위한 기회가 제공된다. Listing 3에서 handleResult 함수는 결과를 Object로 수신하고 이를 ArrayCollection로 캐스트한다. BlazeDS는 List를 서버측에서 ArrayCollection으로 변환했다.

Listing 4에 Flash측에서 연락처 오브젝트를 표현하기 위해 작성한 ActionScript 클래스 Contact가 있다.


Listing 4. ActionScript Contact 클래스
	
package bcit.contacts.dto {

[RemoteClass(alias="bcit.contacts.Contact")]
public class Contact {

    public function Contact() { id = -1; }

    public var id:Number;
    public var lastName:String;
    public var firstName:String;
    public var emailAddress:String;
    public var phoneNumber:String;

    public function toString():String {
        return id + ", " + firstName + " " + lastName + " " + emailAddress
            + " " + phoneNumber;
    }
}
}

이러한 ActionScript 오브젝트는 서버측으로 전송되는데, 이는 BlazeDS가 재주를 부려서 ActionScript 오브젝트를 Java 오브젝트로 변환하는 곳이다. ActionScript Contact 클래스는 DTO(Data Transfer Object)로 간주된다.


애플리케이션 구성하기

애플리케이션은 또한 서버의 특정 설정을 명시하는 구성 파일에 의존한다. 이 애플리케이션 내에서 두 개의 기본 구성 영역은 Hibernate와 BlazeDS이다.

Hibernate 구성하기

표준 JPA 구성 파일인 persistence.xml을 사용하여 Hibernate를 구성할 수 있는데, 이는 Listing 5에 표시되어 있다.


Listing 5. persistence.xml 구성 파일의 서브세트
	
<persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
  http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
  <persistence-unit name="contacts" transaction-type="RESOURCE_LOCAL">
    <properties>
      <property name="hibernate.dialect"
        value="org.hibernate.dialect.MySQLDialect" />
      <property name="hibernate.default_schema" value="contacts" />
      <property name="hibernate.connection.driver_class"
        value="com.mysql.jdbc.Driver" />
      <property name="hibernate.connection.url"
        value="jdbc:mysql://localhost:3306/contacts" />
      <property name="hibernate.archive.autodetection" value="class, hbm"/>
      <property name="hibernate.connection.username" value="root"/>
      <property name="hibernate.connection.password" value="root"/>
    </properties>
  </persistence-unit>
</persistence>

persistence.xml 파일은 Hibernate가 읽을 수 있도록 웹 애플리케이션의 WEB-INF/classes/META-INF 폴더로 이동해야 한다. 제자리에 지정되면 Hibernate는 다음 정보가 필요하다.

  • 데이터베이스 방언(다시 말해서, 많은 데이터베이스에 조금씩 다른 SQL 방언이 있기 때문에 말하는 데이터베이스가 어느 것인지 확인)
  • 기본 스키마를 통한 테이블스페이스
  • 데이터베이스로 연결하는 데 사용되는 데이터베이스 드라이버
  • 데이터베이스 URL
  • 자동 인식이 인식해야 하는 것(예를 들어, 어노테이트된 클래스, Hibernate 맵핑 XML 파일 등등)
  • 사용자 이름 및 비밀번호

다른 정보는 Hibernate의 성능에 유용할 수 있지만 필수는 아니다.

BlazeDS 구성하기

BlazeDS는 네 가지의 구성 파일이 있다.

  • messaging-config.xml: 게시-등록 메시징 정보를 정의한다.
  • proxy-config.xml: HTTP와 웹 서비스에 대한 프록시 서비스 정보를 제공한다.
  • remoting-config.xml: 기사 애플리케이션에 나온 것과 같은 원격 서비스에 대한 정보를 정의한다.
  • services-config.xml: 다른 구성 파일을 참조하고 보안 제한조건, 채널 및 로깅도 제공하는 최상위 레벨 구성 파일

Listing 6에서는 services-config.xml 파일을 시연한다. 기사 애플리케이션에서는 애플리케이션이 BlazeDS 원격 서비스만 사용하고 있기 때문에 remoting-config.xml 파일만 관련되는 점에 유의하자.


Listing 6. services-config.xml 구성 파일의 서브세트
	
<?xml version="1.0" encoding="UTF-8"?>
<services-config>
  <services>
    <service-include file-path="remoting-config.xml" />
    <service-include file-path="messaging-config.xml" />
    <service-include file-path="proxy-config.xml" />
    <default-channels>
       <channel ref="contacts-amf"/>
    </default-channels>
  </services>

  <channels>
    <channel-definition id="contacts-amf" class="mx.messaging.channels.AMFChannel">
      <endpoint url="http://localhost:8080/contacts/messagebroker/amf"
        class="flex.messaging.endpoints.AMFEndpoint"/>
      <properties>
        <polling-enabled>false</polling-enabled>
      </properties>
    </channel-definition>
  </channels>

  <logging>
    <target class="flex.messaging.log.ConsoleTarget" level="Error">
      <properties>
        <prefix>[BlazeDS] </prefix>
        <includeDate>false</includeDate>
        <includeTime>false</includeTime>
        <includeLevel>false</includeLevel>
        <includeCategory>false</includeCategory>
      </properties>
      <filters>
        <pattern>Endpoint.*</pattern>
        <pattern>Service.*</pattern>
        <pattern>Configuration</pattern>
      </filters>
    </target>
  </logging>

</services-config>

services-config.xml 구성 파일은 다른 구성 파일을 참조하고(반드시 존재해야 함) BlazeDS 로깅을 구성하며 어느 채널이나 설정한다. 채널은 클라이언트가 서버와 통신하기 위해 사용되는 프로토콜을 위한 추상화이다. 기사 애플리케이션은 폴링 없이 표준 AMF 프로토콜을 사용한다. 폴링은 연결이 여전히 설정되도록 보장하기 위해 클라이언트가 서버와 지속적으로 통신하는 것을 의미한다.—이 애플리케이션에는 필요하지 않은 것이다.

My developerWorks의 Web development 그룹에 참여하기

My developerWorks Web development 그룹에서 다른 개발자와 함께 웹 개발에 대해 논의하고 리소스를 공유하자.

My developerWorks의 멤버가 아니라면 지금 바로 참여하자.

채널 엔드포인트는 서버 URL을 지정한다. 이 엔드포인트는 프로젝트를 컴파일링하기 위해 필요하다. 클라이언트 Flash 애플리케이션은 하드 코드된 값으로 이를 사용하기 때문에 이는 서버가 연결할 대상을 인식한다. 그 대신에 실제로는 엔드포인트 URL을 MXML이나 ActionScript 코드에 바로 정의할 수 있다.

마지막으로 remoting-config.xml 구성 파일(Listing 7에 표시된 대로)은 원격 조작을 처리하기에 필요한 어댑터 클래스뿐만 아니라 원격 호출에 응답하는 실제 클래스도 지정한다. (이 경우에 bcit.contacts.ContactsService 클래스는 원격 요청에 대한 응답자로 주어졌다.)


Listing 7. remoting-config.xml 구성 파일의 서브세트
	
<?xml version="1.0" encoding="UTF-8"?>
<service id="remoting-service"
  class="flex.messaging.services.RemotingService">

  <adapters>
    <adapter-definition id="java-object" default="true"
      class="flex.messaging.services.remoting.adapters.JavaAdapter"/>
  </adapters>

  <default-channels>
    <channel ref="contacts-amf"/>
  </default-channels>

  <destination id="contacts">
    <properties>
      <source>bcit.contacts.ContactsService</source>
      <!--<scope>application</scope>-->
    </properties>
  </destination>

</service>


결론

이 기사에서는 Tomcat 내에서 실행하며 연락처 정보에 대한 요청에 응답하는 Java 서버측 웹 애플리케이션을 쓰는 방법을 살펴보았다. 또한 클라이언트측 Flash 애플리케이션을 작성하기 위해 MXML과 ActionScript를 둘 다 사용하여 Flex 애플리케이션을 쓰는 방법에 대해서도 알아보았다. MySQL은 데이터 저장소로 작동하였고, Hibernate—ORM 프레임워크—는 쿼리하고 MySQL 데이터베이스를 업데이트할 수 있는 SQL 문으로 Java 오브젝트를 변환하는 데 사용되었다. 마지막으로 BlazeDS 프레임워크를 통해 Flash 애플리케이션은 원격 프로시저 호출과 Java 서버측 웹 애플리케이션에서 원격화를 수행할 수 있었다.



다운로드 하십시오

이름크기다운로드 방식
JEE-BlazeDS-Flex-contacts.zip7MBHTTP

다운로드 방식에 대한 정보

더 많은 다운로드

Notes

  1. 이 압축 파일은 이 프로젝트, WAR를 생성하기 위한 Ant 빌드 파일, 구성 파일 및 이 기사가 참조하고 사용하는 써드파티 라이브러리(JAR 파일의 양식)의 모든 소스 코드(Java, ActionScript 3, MXML)가 들어있다.
  2. 이 기사에서 예제 프로젝트로 사용하기 위해 필요한 오픈 소스 RDBMS
  3. 예제 프로젝트를 빌드하기 위한 Java 기반 빌드 도구
  4. 예제 프로젝트에서 Java 소스 코드를 컴파일하기 위해 필요한 Java SDK(JDK) 버전 6
  5. 예제 프로젝트에서 MXML 및 ActionScript 소스 코드를 컴파일링하기 위한 Flex 4 SDK
  6. 예제 프로젝트를 실행하기 위해 Java HTTP 웹 서버 환경을 제공하는 Apache Software Foundation 서블릿 컨테이너
  7. Java Platform인 Enterprise Edition(Java EE)으로 Flex 기술을 연결하기 위한 Adobe 프레임워크. 이 기사의 프로젝트 다운로드에 이미 포함된 대로 이 소프트웨어는 참조용에 불과하다.
  8. Java EE 컨테이너 미들웨어를 위한 Red Hat ORM 프레임워크. 이 기사의 프로젝트 다운로드에 이미 포함된 대로 이 소프트웨어는 참조용에 불과하다.

참고자료

교육

제품 및 기술 얻기

토론

  • developerWorks Community: 개발자가 운영하고 있는 블로그, 포럼, 그룹 및 위키를 살펴보면서 다른 developerWorks 사용자와 의견을 나눌 수 있다.

필자소개

Photo of Arron Ferguson

Arron Ferguson là một giảng viên đại học đã 12 năm, dạy về kỹ thuật phần mềm tại Viện Công nghệ British Columbia. Các lĩnh vực kinh nghiệm và quan tâm của ông là công nghệ Java, XML, các công nghệ Web, hoạt hình 2D và 3D, và viết cho các phương tiện truyền thông kỹ thuật số. Ông là soạn giả và nhà phê bình kỹ thuật tự do và có cuốn sách được xuất bản: Tạo Các Hệ thống Quản trị Nội dung trong Java (Creating Content Management Systems in Java) (Charles River Media, 2006). Arron cũng say mê Linux và công nghệ mã nguồn mở khác.

잘못된 도움말 신고

부정사용 신고

감사합니다. 이 항목은 운영자가 관심을 표시했습니다.


잘못된 도움말 신고

부정사용 신고

제출실패 신고. 나중에 다시 실행해주세요.


디벨로퍼웍스 로그인


IBM ID가 필요하세요?
IBM ID를 잊으셨습니까?


비밀번호를 잊으셨습니까?
비밀번호 변경

developerWorks 이용 약관에 동의하시는 경우 제출을 클릭하십시오. 이용 약관.

 


developerWorks에 처음 로그인하면 developerWorks프로파일이 생성됩니다.귀하의 프로파일에서 동의하신 내용이 공개되지만 이 사항은 언제든지 변경 가능합니다. 귀하의 성명(숨김으로 체크되어 있어도 표시됩니다)과 디스플레이 이름은 게시한 컨텐츠나 사이트 엑세스시 표시됩니다.

화면상에 보여지는 닉네임을 정하세요.

처음 developerWorks에 로그인할 때 프로파일이 작성되므로, 이를 위해 디스플레이 이름을 선택해야 합니다. 선택하신 디스플레이 이름은 developerWorks에 게시한 컨텐츠에 표시됩니다.

3글자 이상 31글자 이하의 길이로 사용 가능합니다. dW커뮤니티 내에서는 보안상 이메일주소를 제외한 다른 이름을 지정하셔야 합니다.

3개의 &이나 대쉬를 포함해주시고 31글자내로 제한해주세요.


developerWorks 이용 약관에 동의하시는 경우 제출을 클릭하십시오. 이용 약관.

 


아티클 순위

의견

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=20
Zone=웹 개발, 자바, 오픈 소스
ArticleID=548838
ArticleTitle=Flex 4 및 Java 웹 애플리케이션 작성
publish-date=04272010
author1-email=arron_ferguson@bcit.ca
author1-email-cc=

태그

Help
검색 필드를 사용하여 My developerWorks 내에서 해당 태그가 사용된 모든 종류의 컨텐츠를 검색하십시오.

태그를 더 많이 보거나 적게 보기 위해 슬라이더 막대를 사용하십시오.

인기 태그는 특정 컨텐츠 존(예를 들어, 자바, 리눅스, WebSphere)의 최고 인기 태그를 보여줍니다.

내 태그는 특정 컨텐츠 존(예를 들어, 자바, 리눅스, WebSphere)의 귀하의 태그를 보여줍니다.

검색 필드를 사용하여 My developerWorks 내에서 해당 태그가 사용된 모든 종류의 컨텐츠를 검색하십시오. 인기 태그는 특정 컨텐츠 존(예를 들어, 자바, 리눅스, WebSphere)의 최고 인기 태그를 보여줍니다. 내 태그는 특정 컨텐츠 존(예를 들어, 자바, 리눅스, WebSphere)의 귀하의 태그를 보여줍니다.