티스토리 뷰



[Spring WebFlow 가이드] 3. Defining Flows



  3.1. 소개



이번 챕터부터 유저 섹션(User Section)이 시작됩니다. 앞으로 Flow Definition Language를(xml기반의 플로우 정의 언어) 활용해서 어떻게 플로우를 구현하는지 알아볼 것입니다. 이번 챕터가 끝나면, 당신은 플로우가 어떤 구조로 정의되는지 이해 할 수 있을 것이며 플로우를 설정 할 수 있는 능력을 얻을 것입니다.


  3.2. 플로우란 무엇인가?


플로우란 재사용이 가능한(reusable)한 일련의 단계들을 캡슐화(encapsulation) 한 것을 말합니다. 다음은 호텔 예약 과정을 캡슐화한 다이어그램입니다.


각각의 독립적인 기능들이 단계별로 수행되며 호텔 예약을 이뤄낸다. 이러한 흐름의 묶임을 플로우라고 한다.



  3.3. 플로우는 보통 어떻게 구성되는가?



스프링 웹 플로우에서, "States"라는 일련의 상태 단계로 플로우가 구성됩니다. 보통 state로 진입하게 되면 해당하는 뷰가 보여지게 됩니다. 이 뷰에서는 해당 state가 처리 할 수 있는 유저 이벤트가 발생하게 되고, 이러한 이벤트의 발생으로 다른 state로 전환이 가능합니다.

다음은 그 예로서 호텔 예약 플로우를 나타내는 다이어그램입니다.


위 그림에서 Book Hotel 부분을 확대한 모습.


  3.4. 플로우는 어떻게 작성하는가?



플로우들은 웹 어플리케이션 개발자가 간단한 XML파일을(정확히는 XML을 기반으로 한 flow definition language) 사용해서 작성합니다.


  3.5. 필수 엘리먼트



3.5.1 <flow>


모든 플로우는 다음과 같은 루트 엘리먼트로 시작됩니다.


<?xml version="1.0" encoding="UTF-8"?>

<flow xmlns="http://www.springframework.org/schema/webflow"

      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

      xsi:schemaLocation="http://www.springframework.org/schema/webflow

                          http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd">


</flow>


플로우의 모든 state들은 이 엘리먼트 안에 정의됩니다. 이 중 첫번째 state가 플로우의 시작점이 됩니다.


3.5.2. <view-state>


view-state 엘리먼트는 뷰를 보여줄 state를 정의 할 때 사용합니다.


<view-state id="enterBookingDetails" />


관습적으로, view-state는 id를 플로우가 있는 디렉토리의 뷰 템플릿과 맵핑합니다. 예를 들어, 위의 경우 플로우가 /WEB-INF/hotels/booking 폴더 안에 위치한다는 가정하에 state는 /WEB-INF/hotels/booking/enterBookingDetails.xhtml을 렌더링 할 것입니다.


3.5.3. <transition>


transition 엘리먼트는 state안에서 발생하는 이벤트를 처리할 때 사용합니다.


<view-state id="enterBookingDetails">

    <transition on="submit" to="reviewBooking" />

</view-state>


위 transition은 submit 이벤트 발생 시 reviewBooking 뷰로 연결됩니다.


3.5.4 <end-state>


end-state 엘리먼트는 플로우의 출구점을 지정하는데 사용합니다.


<end-state id="bookingCancelled" />


플로우가 end-state로 전환되면 본 플로우가 종료되며 출구점이 리턴됩니다.


3.5.5. 체크 포인트 : 필수 엘리먼트


view-state, transition, end-state 이 3가지 엘리먼트를 활용하면, 당신은 빠르게 뷰 로직을 표현 할 수 있습니다. 팀은 보통 플로우를 추가 하기전에 이런 과정을 수행함으로서, 어플리케이션의 UI 개발에 초점을 맞출 수 있도록 합니다. 다음은 구현 예시입니다.


<flow xmlns="http://www.springframework.org/schema/webflow"

      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

      xsi:schemaLocation="http://www.springframework.org/schema/webflow

                          http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd">


    <view-state id="enterBookingDetails">

        <transition on="submit" to="reviewBooking" />

    </view-state>


    <view-state id="reviewBooking">

        <transition on="confirm" to="bookingConfirmed" />

        <transition on="revise" to="enterBookingDetails" />

        <transition on="cancel" to="bookingCancelled" />

    </view-state>


    <end-state id="bookingConfirmed" />


    <end-state id="bookingCancelled" />


</flow>


첫번째 state인 enterBookingDetails이 시작점이 되며 submit 이벤트 발생시 reviewBooking view-state로 넘어갑니다. 이런 방법으로 end-state 중 하나에 도달하면 플로우가 종료됩니다.


  3.6. Actions



대부분의 플로우는 뷰 이동 로직 이상을 표현해야 합니다. 가령 어플리케이션이나 다른 액션의 비지니스 서비스를 호출할 필요가 있을 수도 있습니다.

플로우 안에서 액션이 수행 될 수 있는 지점은 다음과 같습니다.

  • On flow start : 플로우가 시작할 때
  • On state entry : 스테이트에 들어갈 때
  • On view render : 뷰가 보여질 때
  • On transition execution : 트랜지션이 실행될 때
  • On state exit : 스테이트를 나갈 때
  • On flow end : 플로우가 종료 될 때
액션들은 간결한 expression language를 사용해서 정의합니다. 스프링 웹 플로우는 기본값으로 Unified EL을 사용하고 있습니다. 다음 섹션들에서는 필수 엘리먼트를 사용해서 액션을 정의하는 법을 다뤄 볼 것입니다.

3.6.1. <evaluate>

액션 엘리먼트 중에 가장 많이 쓰이는 엘리먼트가 evaluate입니다. evaluate 엘리먼트는 당신의 플로우의 어떤 지점에 expression을 넣는데 사용합니다. 이 태그를 활용해서 스프링 빈의 메소드나 다른 플로우 변수를 가져올 수 있습니다. 그 예는 다음과 같습니다.

<evaluate expression="entityManager.persist(booking)" />


Assigning an evaluate result


만약 expression이 값을 리턴하면, 그 값을 flowScope라 불리는 플로우 데이터 모델에 저장 할 수 있습니다.


<evaluate expression="bookingService.findHotels(searchCriteria)" result="flowScope.hotels" />


Converting an evaluate result


만약 expression이 변환 과정을 거쳐야 하는 값을 리턴한다면, 요구되는 타입을 result-type 속성으로 지정해주어야 합니다.


<evaluate expression="bookingService.findHotels(searchCriteria)" result="flowScope.hotels" result-type="dataModel"/>


3.6.2. 체크 포인트 : 플로우 액션


다음은 액션이 추가된 플로우 예제이다.


<flow xmlns="http://www.springframework.org/schema/webflow"

      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

      xsi:schemaLocation="http://www.springframework.org/schema/webflow

                          http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd">


    <input name="hotelId" />


    <on-start>

        <evaluate expression="bookingService.createBooking(hotelId, currentUser.name)"

                  result="flowScope.booking" />

    </on-start>


    <view-state id="enterBookingDetails">

        <transition on="submit" to="reviewBooking" />

    </view-state>


    <view-state id="reviewBooking">

        <transition on="confirm" to="bookingConfirmed" />

        <transition on="revise" to="enterBookingDetails" />

        <transition on="cancel" to="bookingCancelled" />

    </view-state>


    <end-state id="bookingConfirmed" />


    <end-state id="bookingCancelled" />


</flow>


이제 이 플로우는 시작 할 때 flow scope에 Booking 오브젝트를 생성합니다. 이 때 예약할 호텔의 id는 flow input 속성으로부터 얻고 있습니다.


  3.7. Input/Output Mapping



각 플로우는 명확한 input/output 규칙을 갖고 있습니다. 플로우는 시작 할 때 input 속성을 넣을 수 있고, 끝날 때 output 속성을 리턴 할 수 있습니다. 이런 점에서, 플로우를 호출하는 것은 개념적으로 다음과 같이 메소드를 호출 하는 것과 비슷합니다.


FlowOutcome flowId(Map<String, Object> inputAttributes);


위 메소드 호출은 inputAttributes를 인풋값으로 받고 아웃풋으로 다음과 같은 FlowOutcome을 리턴합니다.


public interface FlowOutcome {

   public String getName();

   public Map<String, Object> getOutputAttributes();

}


3.7.1. <input>


input 엘리먼트는 플로우의 input 속성을 선언하는데 사용합니다.


<input name="hotelId" />


이 값은 속성의 name으로 flow scope에 저장되어집니다. 가령 위의 경우 hotelId로 저장됩니다.


Declaring an input type


type 속성은 input 속성의 타입을 선언하는데 사용합니다.


<input name="hotelId" type="long" />


만약 인풋 값이 선언된 타입과 맞지 않을 경우, 자동적으로 타입 변환이 시도 될 것입니다.


Marking an input as required


required 속성은 input을 강제적으로 not null 혹은 empty가 되도록 해줍니다.


<input name="hotelId" type="long" value="flowScope.hotelId" required="true" />


3.7.2. output


output 엘리먼트는 플로우의 output 속성을 선언하는데 사용합니다. 아웃풋 속성은 end-state 사이에 선언됩니다.


<end-state id="bookingConfirmed">

    <output name="bookingId" />

</end-state>


아웃풋 값은 name 속성을 기준으로 flow scope에서 값을 얻습니다. 위의 경우, bookingId 변수에서 값을 가져옵니다.


Specifying the source of an output value


value 속성은 아웃풋 값의 expression을 명시 할 때 사용합니다.


<output name="confirmationNumber" value="booking.confirmationNumber" />


3.7.3. 체크포인트 : input/output mapping


다음은 input / output이 추가된 예제입니다.


<flow xmlns="http://www.springframework.org/schema/webflow"

      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

      xsi:schemaLocation="http://www.springframework.org/schema/webflow

                          http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd">


    <input name="hotelId" />


    <on-start>

        <evaluate expression="bookingService.createBooking(hotelId, currentUser.name)"

                  result="flowScope.booking" />

    </on-start>


    <view-state id="enterBookingDetails">

        <transition on="submit" to="reviewBooking" />

    </view-state>


    <view-state id="reviewBooking">

        <transition on="confirm" to="bookingConfirmed" />

        <transition on="revise" to="enterBookingDetails" />

        <transition on="cancel" to="bookingCancelled" />

    </view-state>


    <end-state id="bookingConfirmed" >

        <output name="bookingId" value="booking.id"/>

    </end-state>


    <end-state id="bookingCancelled" />


</flow>


이제 플로우는 hotelId를 인풋으로 받아서 bookingId를 아웃풋으로 리턴 할 수 있습니다.


  3.8. Variables



플로우는 하나 혹은 그 이상의 인스턴스 변수를 선언 할 수도 있습니다. 이 변수들은 플로우가 시작할 때 생성됩니다. @Autowired를 취하는 값은 플로우가 재개될 때 새로 갱신됩니다.


3.8.1. var


var 엘리먼트는 플로우 변수를 생성하는데 사용됩니다.


<var name="searchCriteria" class="com.mycompany.myapp.hotels.search.SearchCriteria"/>


이 때 클래스가 java.io.Serializable을 implements해야 합니다. 위 경우 public class SearchCriteria implements Serializable { ... } 이렇게 구현되어 있습니다.


  3.9. Variable Scopes



웹 플로우는 변수를 몇 가지 스코프(scope)로 구분해서 저장 할 수 있습니다.


3.9.1. Flow Scope


플로우 스코프(Flow Scope)는 플로우가 시작할 때 할당되며, 종료될 때 해지됩니다. 기본적인 구조로 구현했다면, 모든 오브젝트는 플로우 스코프에 저장 될 때 직렬화가(Serializable) 되어야 합니다.


3.9.2. View Scope


뷰 스코프(View Scope)는 view-state에 진입 할 때 할당되며, 나올 때 해지됩니다. 뷰 스코프는 오직 view-state 내부에서 접근가능하다. 기본 구현 상에는, 모든 오브젝트는 뷰 스코프에 저장 될 때 직렬화가 되어야 합니다.


3.9.3. Request Scope


리퀘스트 스코프(Request Scope)는 플로우가 요청(call)되었을 때 할당되며, 리턴(return)될 때 해지됩니다.


3.9.4. Flash Scope


플래쉬 스코프(Flash Scope)는 플로우가 시작될 때 할당되며, 모든 뷰가 보여진 후에는 클리어(clear)되며, 플로우가 종료될 때 해지됩니다. 기본 구현 상에는, 모든 오브젝트는 플래쉬 스코프에 저장 될 때 직렬화가 되어야 합니다.


3.9.5. Conversation Scope


컨버세이션 스코프(Conversation Scope)는 최상위 플로우가 시작될 때 할당되며, 마찬가지로 최상위 플로우가 종료될 때 해지된다. 컨버세이션 스코프는 최상위 플로우뿐만 아니라 하위에 존재하는 모든 플로우에도 공유됩니다. 기본 구현 상에는, 컨버세이션 스코프 오브젝트는 HTTP session에 저장되어지며 일반적으로 직렬화가 되어야 합니다.(직렬화의 이유가 to account for typical session replication이라는데 세션 복제를 위해서? 이 부분은 잘 모르겠습니다.)


  3.10. Calling subflows



플로우는 다른 하위 플로우를 호출하기도 합니다. 그럴 경우 호출을 한 플로우는 호출된 서브 플로우가 리턴될 때까지 대기하게 됩니다. 그리고는 서브 플로우의 outcome을 받습니다.


3.10.1. <subflow-state>


subflow-state 엘리먼트는 다른 플로우를 서브 플로우로 호출하는데 사용합니다.


<subflow-state id="addGuest" subflow="createGuest">

    <transition on="guestCreated" to="reviewBooking">

        <evaluate expression="booking.guests.add(currentEvent.attributes.guest)" />

    </transition>

    <transition on="creationCancelled" to="reviewBooking" />

</subflow-state>


위 예시는 createGuest 플로우를 호출한 후, 리턴될 때까지 기다립니다. 서브 플로우가 guestCreated로 리턴이 되면 booking의 리스트에 새로운 guest가 추가됩니다.


Passing a subflow input


여기서 input 엘리먼트는 input값을 서브 플로우에 넣는데 사용됩니다.


<subflow-state id="addGuest" subflow="createGuest">

    <input name="booking" />

    <transition to="reviewBooking" />

</subflow-state>


Mapping subflow output


서브 플로우의 output은 outcome transition 내부의 name을 기준으로 연결된다.


<transition on="guestCreated" to="reviewBooking">

    <evaluate expression="booking.guests.add(currentEvent.attributes.guest)" />

</transition>


위 예시에서 guest는 guestCreated outcome에 의해 리턴되는 output 속성의이름이다.


3.10.2. 체크포인트 : calling subflows


<flow xmlns="http://www.springframework.org/schema/webflow"

      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

      xsi:schemaLocation="http://www.springframework.org/schema/webflow

                          http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd">


    <input name="hotelId" />


    <on-start>

        <evaluate expression="bookingService.createBooking(hotelId, currentUser.name)"

                  result="flowScope.booking" />

    </on-start>


    <view-state id="enterBookingDetails">

        <transition on="submit" to="reviewBooking" />

    </view-state>


    <view-state id="reviewBooking">

        <transition on="addGuest" to="addGuest" />

        <transition on="confirm" to="bookingConfirmed" />

        <transition on="revise" to="enterBookingDetails" />

        <transition on="cancel" to="bookingCancelled" />

    </view-state>


    <subflow-state id="addGuest" subflow="createGuest">

        <transition on="guestCreated" to="reviewBooking">

            <evaluate expression="booking.guests.add(currentEvent.attributes.guest)" />

        </transition>

        <transition on="creationCancelled" to="reviewBooking" />

    </subflow-state>


    <end-state id="bookingConfirmed" >

        <output name="bookingId" value="booking.id" />

    </end-state>


    <end-state id="bookingCancelled" />


</flow>


이제 플로우는 createGuest 서브 플로우를 호출해서 새로운 guest를 리스트에 추가한다.




원문링크

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함