Raible's Wiki

Raible Designs
Wiki Home
News
Recent Changes

AppFuse

Homepage
  - Korean
  - Chinese
  - Italian
  - Japanese

QuickStart Guide
  - Chinese
  - French
  - German
  - Italian
  - Korean
  - Portuguese
  - Spanish
  - Japanese

User Guide
  - Korean
  - Chinese

Tutorials
  - Chinese
  - German
  - Italian
  - Korean
  - Portuguese
  - Spanish

FAQ
  - Korean

Latest Downloads

Other Applications

Struts Resume
Security Example
Struts Menu

Set your name in
UserPreferences


Referenced by
Articles
Articles_ko
CreateActions_ko




JSPWiki v2.2.33

[RSS]


Hide Menu

SpringControllers_ko


Difference between current version and current version:

At line 1 added 530 lines.
__struts 기반의 Controller 작성 요령을 복사해 놓은 상태.. Spring 문서와 거의 동일 하기 때문에 차후에 다른 부분만 수정할 계획__
__Part III:__ [Actions 클래스 들과 JSP 작성하기|CreateActions] - [Struts|http://jakarta.apache.org/struts] Action 클래스들과 JSP 페이지들을 만드는 방법.
;:''이 tutorial 은 __Part II:__ [새로운 Managers 작성하기|CreateManager] 에 의존 되어 있습니다.''
!!About this Tutorial
이번 tutorial 에서는 Action 과 JSP, 그리고 [StrutsTestCase|http://strutstestcase.sourceforge.net/] 를 이용한 JUnit Test 를 작성하는 방법을 알아봅니다.\\
우리가 만들 Action 은 [새로운 Managers 작성하기|CreateManager] 에서 만들었던 PersonManager 과 대화를 할 겁니다.
기본적으로 AppFuse 는 웹프레임워크로 [Struts|http://struts.apache.org/]를 사용합니다.\\
1.6 버젼에서는 [Spring|http://www.springframework.org/] 또는 [WebWork|http://www.opensymphony.com/webwork/] 를 웹 프레임 워크로 사용할 수 있습니다.\\
Tapestry 와 JSF 는 각각 1.7과 1.8버젼에 계획되어 있습니다.
Spring MVC 를 사용하려면 extras/spring 의 README.txt 를 읽어보세요. WebWork 를 사용하려면 extras/webwork/README.txt 를 보시면 됩니다.\\
"ant install-springmvc" 또는 "ant install-webwork" 를 실행함으로서 위 옵션들을 쉽게 설치 할 수 있습니다.
위 옵션에 해당하는 이 튜토리얼은 다음에서 찾을수 있습니다.
* __<span style="color: green">Spring:</span>__ [Creating Spring Controllers and JSPs|SpringControllers]
* __<span style="color: blue">WebWork:</span>__ [Creating WebWork Actions and JSPs|WebWorkActions]
&#51088; &#44536;&#47100; tutorial &#51012; &#49884;&#51089;&#54644; &#48389;&#49884;&#45796;.
!Table of Contents
* [1] PersonForm &#51012; &#47564;&#46308;&#44592; &#50948;&#54620; XDoclet &#53468;&#44536;&#47484; Person &#53364;&#47000;&#49828;&#50640; &#52628;&#44032; &#54633;&#45768;&#45796;.
* [2] XDoclet &#51060;&#50857;&#54644; JSP &#51032; &#48904;&#45824;&#47484; &#47564;&#46317;&#45768;&#45796;
* [3] PersonAction &#51012; &#53580;&#49828;&#53944; &#54616;&#44592; &#50948;&#54644; PersonActionTest &#47484; &#51089;&#49457;&#54633;&#45768;&#45796;.
* [4] PersonAction &#51012; &#47564;&#46317;&#45768;&#45796;.
* [5] PersonActionTest &#51012; &#49892;&#54665;&#54633;&#45768;&#45796;.
* [6] JSP &#47484; &#51221;&#47532;&#54633;&#45768;&#45796;.
* [7] &#48652;&#46972;&#50864;&#51200; &#46041;&#51089; &#52376;&#47100; &#53580;&#49828;&#53944; &#54616;&#44592; &#50948;&#54644; Canoo WebTests &#47484; &#47564;&#46317;&#45768;&#45796;.
!!PersonForm &#51012; &#47564;&#46308;&#44592; &#50948;&#54620; XDoclet &#53468;&#44536;&#47484; Person &#53364;&#47000;&#49828;&#50640; &#52628;&#44032; &#54633;&#45768;&#45796; [#1]
Struts &#50752; &#50937; &#44228;&#52789;&#51012; &#50948;&#54620; PersonForm(Struts ActionForm) &#51012; &#47564;&#46308;&#44592; &#50948;&#54644;&#49436; &#50864;&#47532;&#45716; Person.java &#50640; XDoclet &#53468;&#44536;&#47484; &#52628;&#44032; &#54644;&#50556; &#54633;&#45768;&#45796;.
Person.java &#54028;&#51068;&#51032; JavaDoc &#48512;&#48516;&#50640; &#45796;&#51020; @struts.form &#53468;&#44536;&#47484; &#52628;&#44032;&#54633;&#45768;&#45796;.(&#50696;&#51228;&#44032; &#54596;&#50836;&#54616;&#45796;&#47732; User.java &#54028;&#51068;&#51012; &#48372;&#49464;&#50836;):
[{Java2HtmlPlugin
* @struts.form include-all="true" extends="BaseForm"
}]
''&#50864;&#47532;&#45716; org.appfuse.webapp.form.BaseForm &#47484; &#49345;&#49549;&#48155;&#46020;&#47197; &#54616;&#50688;&#49845;&#45768;&#45796;. &#44536; &#51060;&#50976;&#45716; BaseForm &#50640;&#45716; toString() &#47700;&#49548;&#46300;&#44032; &#51080;&#44592; &#46412;&#47928;&#51077;&#45768;&#45796;. toString() &#47700;&#49548;&#46300;&#45716; log.debug(formName)&#47484; &#54840;&#52636;&#54664;&#51012;&#46412; &#54268;&#51032; &#45236;&#50857;&#51012; &#51069;&#44592; &#51339;&#44172; &#54532;&#47536;&#53944; &#54624; &#49688; &#51080;&#46020;&#47197; &#54633;&#45768;&#45796;.
&#47564;&#50557; &#44592;&#51316;&#51032; "org.appfuse" &#54056;&#53412;&#51648; &#51060;&#47492;&#51012; &#45817;&#49888;&#51032; "com.company" &#54056;&#53412;&#51648;&#51032; &#51060;&#47492;&#51004;&#47196; &#48148;&#44984;&#51648; &#50506;&#50520;&#44144;&#45208; model &#53364;&#47000;&#49828;&#44032; &#45796;&#47480; &#54056;&#53412;&#51648;&#50640; &#51080;&#45796;&#47732; @struts.form &#53468;&#44536;&#50640; BaseForm &#51012; fully-qualify (ex: org.appfuse.webapp.form.BaseForm) &#51004;&#47196; &#51201;&#50612;&#50556; &#54633;&#45768;&#45796;. ''
!!XDoclet &#51060;&#50857;&#54644; JSP &#51032; &#48904;&#45824;&#47484; &#47564;&#46317;&#45768;&#45796; [#2]
&#51060; &#45800;&#44228;&#50640;&#49436;&#45716; PersonForm &#51032; &#51221;&#48372;&#47484; &#48372;&#50668;&#51452;&#44592; &#50948;&#54620; JSP &#51032; ''&#48904;&#45824;''&#47484; &#47564;&#46308;&#50612; &#48389;&#49884;&#45796;. \\
&#45236;&#44032; ''&#48904;&#45824;'' &#46972;&#44256; &#47568;&#54620; &#51060;&#50976;&#45716; &#50864;&#47532;&#44032; &#47564;&#46308; JSP &#44032; &#45800;&#51648; form &#47564;&#51012; &#44032;&#51648;&#44256; &#51080;&#44592; &#46412;&#47928;&#51077;&#45768;&#45796;.\\
&#51060; JSP &#50640;&#45716; &#53580;&#51060;&#48660; &#44396;&#51312; &#45236;&#50640; PersonForm.java &#51032; &#44033; &#49549;&#49457;&#51012; &#45208;&#53440;&#45236;&#45716; Struts' &lt;html:text&gt; &#53468;&#44536;&#47484; &#44032;&#51648;&#44256; &#51080;&#49845;&#45768;&#45796;.\\
&#51060; JSP &#47484; &#47564;&#46308;&#44592; &#50948;&#54620; Tool &#51008; [Erik Hatcher|http://www.blogscene.org/erik/] &#50640; &#51032;&#54644; &#47564;&#46308;&#50612; &#51276;&#49845;&#45768;&#45796;.\\
&#51060; Tool &#51008; &#54616;&#45208;&#51032; &#53364;&#47000;&#49828; (FormTagsHandler.java) &#50752; &#46160;&#44060;&#51032; XDoclet templates (FormKeys.xdt &#50752; StrutsForm_jsp.xdt) &#51004;&#47196; &#51060;&#47336;&#50612; &#51276;&#49845;&#45768;&#45796;. \\
&#51060; &#54028;&#51068;&#46308;&#51008; extras/viewgen &#46356;&#47113;&#53664;&#47532;&#50640; &#46308;&#50612;&#51080;&#49845;&#45768;&#45796;.
<div class="note" style="margin: 5px; background-color: #fcc">
__WARNING:__ "viewgen" &#53812;&#51008; 1.6.1 &#48260;&#51260;&#50640;&#49436; deprecated &#46104;&#50632;&#51004;&#47728; 1.8 &#50640;&#49436;&#45716; &#49325;&#51228;&#46112; &#44163;&#51077;&#45768;&#45796;. &#45824;&#49888;&#54644;&#49436; [appgen|CreateDAO] &#53812;&#51060; &#44057;&#51008; &#44592;&#45733;&#51012; &#51228;&#44277;&#54624; &#44163;&#51077;&#45768;&#45796;.
</div>
JSP &#50752; &#47484; properties(form elements&#51012; &#50948;&#54620; label &#44050;) &#54028;&#51068;&#51012; &#47564;&#46300;&#45716; &#49692;&#49436;:
* __ant compile__ &#47484; &#49892;&#54665;&#54633;&#45768;&#45796; - Person.java POJO &#47196; &#48512;&#53552; PersonForm.java &#47484; &#47564;&#46308;&#50612; &#45253;&#45768;&#45796;.
* "extras/viewgen" &#51004;&#47196; &#51060;&#46041;&#54633;&#45768;&#45796;
* extras/viewgen/build &#50640; &#50500;&#47000; &#49464;&#44060;&#51032; &#54028;&#51068;&#51012; &#47564;&#46308;&#44592; &#50948;&#54644; __ant -Dform.name=PersonForm__ &#53468;&#44536;&#47484; &#49892;&#54665;&#54633;&#45768;&#45796;:
** PersonForm.properties (form elements&#51012; &#50948;&#54620; label &#44050;)
** personForm.jsp (&#54620;&#44060;&#51032; Person &#51012; &#48372;&#44592; &#50948;&#54620; JSP &#48904;&#45824; &#54028;&#51068;)
** PersonFormList.jsp (Person &#47532;&#49828;&#53944;&#47484; &#48372;&#44592; &#50948;&#54620; JSP &#48904;&#45824; &#54028;&#51068;)
* PersonForm.properties &#51032; &#45236;&#50857;&#51012; web/WEB-INF/classes/ApplicationResources_en.properties &#50640; &#48373;&#49324; &#54633;&#45768;&#45796;. \\&#50668;&#44592; ApplicationResources_en.properties &#50640; &#52628;&#44032;&#54644;&#50556; &#54624; &#49549;&#49457;&#46308;&#51032; &#50696;&#51228;&#44032; &#51080;&#49845;&#45768;&#45796;:\\{{{
# -- person form --
personForm.firstName=First Name
personForm.id=Id
personForm.lastName=Last Name
}}}
* personForm.jsp &#51012; web/pages/personForm.jsp PersonFormList.jsp &#47484; web/pages/personList.jsp &#51004;&#47196; &#48373;&#49324; &#54633;&#45768;&#45796;. &#44033; &#54028;&#51068;&#51032; &#52395; &#44544;&#51676;&#45716; &#49548;&#47928;&#51088;&#46972;&#45716;&#44152; &#50508;&#50500;&#46160;&#49464;&#50836;.\\ \\''"pages" &#46356;&#47113;&#53664;&#47532;&#51032; &#54028;&#51068;&#46308;&#51008; deployment &#46112;&#46412; "WEB-INF/pages" &#51060;&#46041;&#46121;&#45768;&#45796;. &#52968;&#53580;&#51060;&#45320;&#45716; &#51060; WEB-INF &#50640; &#51080;&#45716; &#47784;&#46304; &#54028;&#51068;&#46308;&#50640; &#45824;&#54620; &#48372;&#50504;&#51012; &#51228;&#44277;&#54633;&#45768;&#45796;.\\ &#51060; &#48372;&#50504;&#51008; &#53364;&#46972;&#51060;&#50616;&#53944;&#47196; &#48512;&#53552;&#51032; &#50836;&#52397;&#50640;&#45716; &#51201;&#50857; &#46104;&#51648;&#47564;, Struts'ActionServlet &#50640; &#51032;&#54620; forward &#50640;&#45716; &#51201;&#50857; &#46104;&#51648; &#50506;&#49845;&#45768;&#45796;. &#51593;, JSP &#54168;&#51060;&#51648;&#46308;&#51012; WEB-INF &#46356;&#47113;&#53664;&#47532;&#50640; &#45347;&#45716;&#44163;&#51008; &#51060; &#54168;&#51060;&#51648;&#46308;&#51060; &#53364;&#46972;&#51060;&#50616;&#53944;(&#45796;&#47480; jsp)&#47196;&#48512;&#53552; &#51649;&#51217;&#51201;&#51004;&#47196; &#50641;&#49464;&#49828; &#46104;&#51648; &#50506;&#44256; &#50724;&#47196;&#51648; Action &#51004;&#47196;&#48512;&#53552;&#47564; &#50641;&#49464;&#49828; &#46108;&#45796;&#45716; &#44163;&#51012; &#48372;&#51109;&#54616;&#44172; &#46104;&#45716; &#44163;&#51077;&#45768;&#45796;.\\&#51060;&#44163;&#51008; &#54532;&#47532;&#51232;&#53580;&#51060;&#49496; &#50689;&#50669;&#51060; &#50500;&#45772; &#48372;&#45796; Action(&#54952;&#44284;&#51201;&#51004;&#47196; &#51312;&#51221;&#54624;&#49688; &#51080;&#45716;) &#51901;&#50640; &#48372;&#50504;&#51012; &#54728;&#46973;&#54616;&#44172; &#47564;&#46317;&#45768;&#45796;.''
AppFuse &#50640;&#49436;&#45716; &#47784;&#46304; *.html url-pattern &#50640; &#54644;&#45817;&#54616;&#45716; &#51088;&#50896;&#51012; &#48372;&#54840;&#54616;&#44172; &#46104;&#50612; &#51080;&#49845;&#45768;&#45796;. (/signup.html, /passwordHint.html &#51008; &#51228;&#50808;)
&#51060;&#44163;&#51008; &#53364;&#46972;&#51060;&#50616;&#44032; JSP &#54168;&#51060;&#51648;&#47484; &#50619;&#44592;&#50948;&#54644;&#49436;&#45716; &#48152;&#46300;&#49884; Action &#51012; &#53685;&#54644;&#50556; &#54620;&#45796;&#45716; &#44163;&#51012; &#48372;&#51109;&#54616;&#45716; &#44163;&#51077;&#45768;&#45796;.
<div class="note">
<b>NOTE:</b> &#47564;&#50557; &#50612;&#46500; &#53945;&#51221;&#54620; &#54168;&#51060;&#51648;&#51032; CSS &#47484; customize &#54616;&#44256; &#49910;&#45796;&#47732; &#44536; &#54168;&#51060;&#51648;&#51032; &#49345;&#45800;&#50640; <body id="pageName"/> &#47484; &#52628;&#44032;&#54616;&#49464;&#50836;.\\
&#51060;&#44163;&#51008; SiteMesh &#50640; &#51032;&#54644; &#51069;&#54784;&#51648;&#44256; &#47560;&#51648;&#47561; &#54168;&#51060;&#51648;(SiteMesh &#50640; &#51032;&#54644; &#54028;&#49905;&#46108; &#44208;&#44284; &#54168;&#51060;&#51648;)&#50640; &#50416;&#50668;&#51648;&#44172; &#46121;&#45768;&#45796;.\\
&#44536;&#47532;&#44256; &#44033; &#54168;&#51060;&#51648; &#47560;&#45796;&#51032; CSS &#47484; &#45796;&#51020;&#52376;&#47100; &#47564;&#46308;&#47732; &#46121;&#45768;&#45796;:\\
{{{
body#pageName element.class { background-color: blue }
}}}
</div>
* JSP &#50640; &#50416;&#50668;&#51656; title &#44284; heading &#53412;&#47484; ApplicationResources_en.properties &#50640; &#52628;&#44032; &#54633;&#45768;&#45796;.
&#47564;&#46308;&#50612;&#51652; JSP &#54028;&#51068;&#50640;&#45716; title &#44284; header &#51032; &#53412; &#44050;&#51060; &#50416;&#50668;&#51256; &#51080;&#49845;&#45768;&#45796;. &#44536;&#47084;&#48064;&#47196; &#51060; &#53412; &#44050;&#46308;(personDetail.title, personDetail.heading)&#51012; ApplicationResources_en.properties &#50640; &#52628;&#44032;&#54644;&#50556; &#54633;&#45768;&#45796;.
web/WEB-INF/classes/ApplicationResources_en.properties &#54028;&#51068;&#51012; &#50676;&#44256; &#45796;&#51020; &#46972;&#51064;&#51012; &#54028;&#51068;&#51032; &#44032;&#51109; &#46244;&#50640; &#52628;&#44032; &#54633;&#45768;&#45796;.
{{{
# -- person detail page --
personDetail.title=Person Detail
personDetail.heading=Person Information
}}}
;: ''&#50948;&#50640;&#49436; &#50864;&#47532;&#45716; "personForm.*" &#53412;&#46308;&#51012; &#51060;&#54028;&#51068;&#50640;&#49436; &#52628;&#44032; &#54664;&#50632;&#49845;&#45768;&#45796;. &#44536;&#47088;&#45936; &#50780; &#51060;&#48264;&#50640;&#45716; personDetail &#53412;&#47484; &#49324;&#50857;&#54664;&#51012;&#44620;&#50836;? &#44032;&#51109; &#53360; &#51060;&#50976;&#45716; form lebel &#44284; text &#47484; &#44396;&#48516;&#54616;&#44592; &#50948;&#54644;&#49436;&#51077;&#45768;&#45796;. &#45796;&#47480; &#51060;&#50976;&#45716; &#47784;&#46304; "Form.*" &#45716; &#45936;&#51060;&#53440;&#48288;&#51060;&#49828;&#51032; &#54596;&#46300;&#44050;&#46308;&#51012; &#45208;&#53440;&#45240;&#45796;&#45716; &#44163;&#51012; &#50508; &#49688; &#51080;&#44592; &#46412;&#47928;&#51077;&#45768;&#45796;. ''
&#52572;&#44540;&#50640; &#45208;&#51032; &#53364;&#46972;&#51060;&#50616;&#53944;&#44032; &#45936;&#51060;&#53440;&#48288;&#51060;&#49828;&#50640;&#49436; &#44160;&#49353; &#44032;&#45733;&#54620; &#54596;&#46300;&#46308;&#51032; &#47784;&#46304; &#47532;&#49828;&#53944;&#47484; &#50836;&#44396;&#54620;&#51201;&#51060; &#51080;&#50632;&#45796;. &#44536;&#44163;&#51008; &#47924;&#52377; &#49772;&#50912;&#45796;. &#45212; &#45800;&#51648; ApplicationResources.properties &#50640;&#49436; "Form." &#51012; &#44032;&#51648;&#44256; &#51080;&#45716; key &#46308;&#51012; &#52286;&#50500;&#45256;&#45796;. &#44536;&#47532;&#44256; &#53364;&#46972;&#51060;&#50616;&#53944;&#45716; &#44160;&#49353;&#54616;&#44592; &#50896;&#54616;&#45716; &#52972;&#47100;&#46308;&#51012; &#49440;&#53469;&#54664;&#45796;. &#45212; &#51060; &#54532;&#47196;&#51229;&#53944;&#50640;&#49436; Form&#44284; Detail &#51012; &#48516;&#47532;&#54620;&#44163;&#50640; &#45824;&#54644; &#47588;&#50864; &#55137;&#51313;&#54644; &#54664;&#45796;.
!!Action &#51012; &#53580;&#49828;&#53944; &#54616;&#44592; &#50948;&#54644; ActionTest &#47484; &#47564;&#46317;&#45768;&#45796; [#3]
Action &#51012; &#50948;&#54620; StrutsTestCase &#53580;&#49828;&#53944;&#47484; &#54616;&#44592; &#50948;&#54644;&#49436;&#45716; &#50864;&#49440; test/web/**/action &#46356;&#47113;&#53664;&#47532;&#50640; PersonActionTest.java &#54028;&#51068;&#51012; &#47564;&#46308;&#50612;&#50556; &#54633;&#45768;&#45796;.
;:<span style="color: blue">''&#51060;&#48120; &#47564;&#46308;&#50612; &#45459;&#51008; ActionTest (i.e. UserActionTest) copy &rarr; save &#47484; &#54644;&#49436; [[Uu]ser &#47484; [[P]erson &#47196; &#48148;&#44984;&#47732; &#46121;&#45768;&#45796;. ''</span>
;:<span style="color: blue">''&#47564;&#50557; UserActionTest &#47484; &#52852;&#54588; &#54664;&#45796;&#47732; UserFormEx(UserForm &#51060; &#50500;&#45772;) &#47484; PersonForm &#51004;&#47196; &#48320;&#44221;&#54644;&#50556; &#54633;&#45768;&#45796;. UserFormEx &#45716; UserForm &#51012; &#54869;&#51109;&#54620; &#44163;&#51004;&#47196; &#49324;&#50857;&#51088; &#44428;&#54620;&#50640; &#45824;&#54620; String[] setter &#47700;&#49548;&#46300;&#47484; &#44032;&#51648;&#44256; &#51080;&#49845;&#45768;&#45796;. User.java &#47196; &#48512;&#53552; UserForm &#51060; &#47564;&#46308;&#50612; &#51276;&#51012;&#46412;&#45716; &#50948; &#44592;&#45733;&#51012; &#49892;&#54665; &#54624;&#49688; &#50630;&#45716; &#49345;&#53468;&#50688;&#49845;&#45768;&#45796;. ''</span>
[{Java2HtmlPlugin
package org.appfuse.webapp.action;
import org.appfuse.Constants;
import org.appfuse.webapp.form.PersonForm;
public class PersonActionTest extends BaseStrutsTestCase {
public PersonActionTest(String name) {
super(name);
}
public void testEdit() throws Exception {
setRequestPathInfo("/editPerson");
addRequestParameter("method", "Edit");
addRequestParameter("id", "1");
actionPerform();
verifyForward("edit");
assertTrue(request.getAttribute(Constants.PERSON_KEY) != null);
verifyNoActionErrors();
}
public void testSave() throws Exception {
setRequestPathInfo("/editPerson");
addRequestParameter("method", "Edit");
addRequestParameter("id", "1");
actionPerform();
PersonForm personForm =
(PersonForm) request.getAttribute(Constants.PERSON_KEY);
assertTrue(personForm != null);
setRequestPathInfo("/savePerson");
addRequestParameter("method", "Save");
// update the form from the edit and add it back to the request
personForm.setLastName("Feltz");
request.setAttribute(Constants.PERSON_KEY, personForm);
actionPerform();
verifyForward("edit");
verifyNoActionErrors();
}
public void testRemove() throws Exception {
setRequestPathInfo("/editPerson");
addRequestParameter("method", "Delete");
addRequestParameter("id", "2");
actionPerform();
verifyForward("mainMenu");
verifyNoActionErrors();
}
}
}]
src/dao/**/Constants.java &#53364;&#47000;&#49828;&#50640; PERSON_KEY="personForm" &#48320;&#49688;&#47484; &#52628;&#44032;&#54644;&#50556; &#54633;&#45768;&#45796;. &#51060; &#51060;&#47492;&#51008; struts-config.xml &#54028;&#51068;&#51032; form &#50648;&#47532;&#47676;&#53944;&#50752; &#51068;&#52824;&#54616;&#44172; &#46121;&#45768;&#45796;.
[{Java2HtmlPlugin
/**
* The request scope attribute that holds the person form.
*/
public static final String PERSON_KEY = "personForm";
}]
&#51060; &#53580;&#49828;&#53944;&#47484; &#49905;&#54665;&#49884;&#53412;&#47732; NoSuchMethodErrors &#44032; &#48156;&#49373;&#54633;&#45768;&#45796;. - &#44536;&#47100; &#51060;&#51228; PersonAction &#53364;&#47000;&#49828;&#50640; edit, save, delete &#47700;&#49548;&#46300;&#47484; &#52628;&#44032;&#54644; &#48380;&#44620;&#50836;.
!!PersonAction &#51012; &#47564;&#46317;&#45768;&#45796; [#4]
src/web/**/action &#50640; &#45796;&#51020;&#51032; &#45236;&#50857;&#51012; &#44032;&#51648;&#45716; PersonAction.java &#54028;&#51068;&#51012; &#47564;&#46317;&#45768;&#45796;.
[{Java2HtmlPlugin
package org.appfuse.webapp.action;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionMessage;
import org.apache.struts.action.ActionMessages;
import org.appfuse.model.Person;
import org.appfuse.service.PersonManager;
import org.appfuse.webapp.form.PersonForm;
/**
* @struts.action name="personForm" path="/editPerson" scope="request"
* validate="false" parameter="method" input="mainMenu"
*/
public final class PersonAction extends BaseAction {
public ActionForward cancel(ActionMapping mapping, ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws Exception {
return mapping.findForward("mainMenu");
}
public ActionForward delete(ActionMapping mapping, ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws Exception {
if (log.isDebugEnabled()) {
log.debug("Entering 'delete' method");
}
ActionMessages messages = new ActionMessages();
PersonForm personForm = (PersonForm) form;
// Exceptions are caught by ActionExceptionHandler
PersonManager mgr = (PersonManager) getBean("personManager");
mgr.removePerson(personForm.getId());
messages.add(ActionMessages.GLOBAL_MESSAGE,
new ActionMessage("person.deleted",
personForm.getFirstName() + ' ' +
personForm.getLastName()));
// save messages in session, so they'll survive the redirect
saveMessages(request.getSession(), messages);
return mapping.findForward("mainMenu");
}
public ActionForward edit(ActionMapping mapping, ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws Exception {
if (log.isDebugEnabled()) {
log.debug("Entering 'edit' method");
}
PersonForm personForm = (PersonForm) form;
// if an id is passed in, look up the user - otherwise
// don't do anything - user is doing an add
if (personForm.getId() != null) {
PersonManager mgr = (PersonManager) getBean("personManager");
Person person = mgr.getPerson(personForm.getId());
personForm = (PersonForm) convert(person);
updateFormBean(mapping, request, personForm);
}
return mapping.findForward("edit");
}
public ActionForward save(ActionMapping mapping, ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws Exception {
if (log.isDebugEnabled()) {
log.debug("Entering 'save' method");
}
// Extract attributes and parameters we will need
ActionMessages messages = new ActionMessages();
PersonForm personForm = (PersonForm) form;
boolean isNew = ("".equals(personForm.getId()));
if (log.isDebugEnabled()) {
log.debug("saving person: " + personForm);
}
PersonManager mgr = (PersonManager) getBean("personManager");
Person person = (Person) convert(personForm);
mgr.savePerson(person);
// add success messages
if (isNew) {
messages.add(ActionMessages.GLOBAL_MESSAGE,
new ActionMessage("person.added",
personForm.getFirstName() + " " +
personForm.getLastName()));
// save messages in session to survive a redirect
saveMessages(request.getSession(), messages);
return mapping.findForward("mainMenu");
} else {
messages.add(ActionMessages.GLOBAL_MESSAGE,
new ActionMessage("person.updated",
personForm.getFirstName() + " " +
personForm.getLastName()));
saveMessages(request, messages);
return mapping.findForward("edit");
}
}
}
}]
&#50948; &#53076;&#46300;&#50640;&#49436; PersonForm &#51060;&#45208; Person &#51012; &#48320;&#54872;&#54616;&#44592; &#50948;&#54644; convert &#47700;&#49548;&#46300;&#47484; &#51088;&#51452; &#54840;&#52636;&#54616;&#45716; &#44163;&#51012; &#50508;&#50500;&#52264;&#47160;&#51012;&#44161;&#45768;&#45796;. BaseAction &#50640; &#51080;&#45716; convert &#47700;&#49548;&#46300;&#45716; POJOs &#8594; ActionForms &#44536;&#47532;&#44256; ActionForms &#8594; POJOs &#51004;&#47196; &#48320;&#54872;&#54616;&#44592; &#50948;&#54644; ConvertUtil.convert()&#47484; &#54840;&#52636;&#54633;&#45768;&#45796;. &#46608; ConvertUtil.convert() &#47700;&#49548;&#46300;&#45716; [BeanUtils.copyProperties|http://jakarta.apache.org/commons/beanutils/api/org/apache/commons/beanutils/BeanUtils.html#copyProperties(java.lang.Object,%20java.lang.Object)] &#47484; &#49324;&#50857;&#54633;&#45768;&#45796;.
;: ''&#47564;&#50557; Eclipse &#47484; &#49324;&#50857;&#54620;&#45796;&#47732; PersonForm &#51012; &#48372;&#44592; &#50948;&#54644;&#49440; "refresh" &#47484; &#54644;&#50556; &#54633;&#45768;&#45796;. PersonForm &#51008; &#54532;&#47196;&#51229;&#53944; &#49548;&#49828; &#54260;&#45908; &#51473; &#54616;&#45208;&#51064; build/web/gen &#50640; &#50948;&#52824;&#54616;&#44172; &#46121;&#45768;&#45796;. "refresh" &#54616;&#45716;&#44163;&#51060; XDoclet&#50640; &#51032;&#54644;&#49436; PersonForm &#51060; &#48124;&#46308;&#50612;&#51652;&#54980; Eclipse &#50640;&#49436; &#48372;&#44256; &#46608; &#45796;&#47480; java &#54028;&#51068;&#50640;&#49436; import &#54624; &#49688; &#51080;&#46020;&#47197; &#54616;&#45716; &#50976;&#51068;&#54620; &#48169;&#48277;&#51077;&#45768;&#45796;. &#54028;&#51068;&#51008; build/web/gen/org/appfuse/webapp/form/PersonForm.java &#50640;&#49436; &#52286;&#51012; &#49688; &#51080;&#49845;&#45768;&#45796;.''
;: ''BeanUtils.copyProperties &#47700;&#49548;&#46300;&#50640;&#44172; &#50612;&#46523;&#44172; Strings &#8594; Objects &#47484; &#48320;&#54872;&#54644;&#50556; &#54616;&#45716;&#51648; &#50508;&#47140;&#51460; &#49688; &#51080;&#45716; Converters (i.e. [DateConverter|http://raibledesigns.com/downloads/apptracker/api/org/apptracker/util/DateConverter.java.html]) &#47484; [BaseAction|http://raibledesigns.com/downloads/apptracker/api/org/appfuse/webapp/action/BaseAction.java.html] &#50640; &#52628;&#44032;&#51201;&#51004;&#47196; &#46321;&#47197; &#54624; &#49688; &#51080;&#49845;&#45768;&#45796;. &#47564;&#50557; POJO &#44032; List &#47484; &#44032;&#51648;&#44256; &#51080;&#45796;&#47732;( i.e. for parent-child relationships) convertLists(java.lang.Object) &#47700;&#49548;&#46300;&#47484; &#51060;&#50857;&#54644;&#49436; &#48320;&#54872;&#54624; &#49688;&#46020; &#51080;&#49845;&#45768;&#45796;.''
&#51060;&#51228; PersonActionTest &#50640;&#49436; &#49324;&#50857;&#54664;&#45912; ''edit'' forward &#50752; ''savePerson'' action-mapping &#47484; &#52628;&#44032; &#54644;&#50556; &#54616;&#44592; &#50948;&#54644;&#49436; PersonAction.java &#54028;&#51068;&#51032; &#49345;&#45800;(class declaration) &#48512;&#48516;&#51032; &#45796;&#51020;&#44284; &#44057;&#51008; XDoclet tag &#47484; &#52628;&#44032;&#54633;&#45768;&#45796;.
[{Java2HtmlPlugin
/**
* @struts.action name="personForm" path="/editPerson" scope="request"
* validate="false" parameter="method" input="mainMenu"
*
* @struts.action name="personForm" path="/savePerson" scope="request"
* validate="true" parameter="method" input="edit"
*
* @struts.action-forward name="edit" path="/WEB-INF/pages/personForm.jsp"
*/
public final class PersonAction extends BaseAction {
}]
&#50948; XDoclet &#50640;&#49436; editPerson &#44284; savePerson &#51032; &#44032;&#51109; &#53360; &#52264;&#51060;&#51216;&#51008; savePerson validation &#51012; &#49324;&#50857;(see validation="true") &#54620;&#45796;&#45716; &#44163;&#51060;&#45796;. &#50668;&#44592;&#49436; input &#49549;&#49457;&#51008; &#51068;&#48152;&#51201;&#51064; path(i.e. /editPerson.html) &#44032; &#50500;&#45772; &#48152;&#46300;&#49884; forward &#47484; &#44032;&#47532;&#53020;&#50556; &#54620;&#45796;&#45716; &#44163;&#51060;&#45796;. &#47564;&#50557; edit &#50752; save &#47484; &#50948;&#54644; save &#54056;&#49828;&#47564; &#49324;&#50857; &#54616;&#44256; &#49910;&#45796;&#47732; validate="false" &#47196; &#48148;&#44984;&#44256; save &#47700;&#49548;&#46300;&#50640;&#49436; form.validate() &#50752; &#50640;&#47084; &#52376;&#47532;&#47484; &#54616;&#47732; &#46121;&#45768;&#45796;.
success &#47700;&#49464;&#51648;&#47484; &#49100;&#47140;&#51452;&#44592; &#50948;&#54644; ApplicationResources_en.properties &#50640; &#52628;&#44032;&#54624; &#47751;&#44032;&#51648; key(ActionMessages) &#46308;&#51060; &#51080;&#49845;&#45768;&#45796;. web/WEB-INF/classes &#50640;&#49436; &#51060; &#54028;&#51068;&#51012; &#50676;&#50612;&#49436; &#45796;&#51020; &#45236;&#50857;&#51012; &#52628;&#44032; &#54633;&#45768;&#45796;.
;: ''&#45208;&#45716; &#51452;&#47196; &#51060;&#47088; &#47700;&#49464;&#51648; &#50948;&#50640; # -- success messages -- &#51452;&#49437;&#51012; &#45813;&#45768;&#45796;.''
{{{
person.added=Information for <strong>{0}</strong> has been added successfully.
person.deleted=Information for <strong>{0}</strong> has been deleted successfully.
person.updated=Information for <strong>{0}</strong> has been updated successfully.
}}}
;: ''&#52628;&#44032;, &#49325;&#51228;, &#44081;&#49888; &#46321;&#51012; &#50948;&#54620; &#48276;&#50857;&#51201;&#51064; message &#47484; &#49324;&#50857; &#54624; &#49688;&#46020; &#51080;&#49845;&#45768;&#45796;. &#44536;&#47111;&#44172; &#54616;&#47732; &#44033; &#50644;&#53552;&#54000; &#47560;&#45796;&#51032; &#48320;&#54868;&#47196; &#48512;&#53552; message &#46308;&#51012; &#48516;&#47532; &#54624; &#49688; &#51080;&#44172;&#46121;&#45768;&#45796;.''
&#50948; &#53076;&#46300;&#46308;&#51060; PersonManagerTest &#50640;&#49436; PersonManager &#51012; &#49324;&#50857;&#54616;&#45716; &#44163;&#44284; &#44057;&#51008; &#53076;&#46300;&#46972;&#45716;&#44163;&#51012; &#50508;&#50500;&#52264;&#47160;&#51012;&#44161;&#45768;&#45796;. &#44536;&#44163;&#51008; PersonAction &#50752; PersonManagerTest &#46168;&#45796; PersonManagerImpl &#51032; &#53364;&#46972;&#51060;&#50616;&#53944; &#51060;&#44592; &#46412;&#47928;&#51077;&#45768;&#45796;.
&#51060; &#53916;&#53664;&#47532;&#50620;&#51032; &#44144;&#51032; &#47784;&#46304;&#44172; &#45149;&#45228;&#49845;&#45768;&#45796;. &#51088; &#51060;&#51228; &#53580;&#49828;&#53944;&#47484; &#54644;&#48380;&#44620;&#50836;.
!!PersonActionTest &#51012; &#49892;&#54665;&#54633;&#45768;&#45796;. [#5]
PersonActionTest &#47484; &#48372;&#47732; &#47784;&#46304; &#53580;&#49828;&#53944;&#44032; id=1(testRemove &#50640;&#49436;&#45716; id=2) &#51064; database &#47112;&#53076;&#46300;&#50640; &#51032;&#51316;&#46104;&#50612; &#51080;&#51004;&#48064;&#47196; &#49368;&#54540; &#45936;&#51060;&#53440; &#54028;&#51068;(metadata/sql/sample-data.xml)&#50640; &#50948; &#47112;&#53076;&#46300;&#46308;&#51012; &#52628;&#44032;&#54616;&#46020;&#47197; &#54633;&#45768;&#45796;.\\
&#50500;&#47000; &#45236;&#50857;&#51012; sample-data.xml &#54028;&#51068;&#51032; &#44032;&#51109; &#45149;(&#45796;&#47480; &#53580;&#51060;&#48660;&#44284; &#50672;&#44288; &#44288;&#44228;&#44284; &#51080;&#44592; &#51204;&#50640;&#45716; &#49692;&#49436;&#45716; &#51473;&#50836;&#54616;&#51648; &#50506;&#49845;&#45768;&#45796;.)&#50640; &#52628;&#44032; &#54633;&#45768;&#45796;.
{{{
<table name='person'>
<column>id</column>
<column>first_name</column>
<column>last_name</column>
<row>
<value>1</value>
<value>Matt</value>
<value>Raible</value>
</row>
<row>
<value>2</value>
<value>James</value>
<value>Davidson</value>
</row>
</table>
}}}
&#50612;&#46500; &#53580;&#49828;&#53944;&#47484; &#49892;&#54665; &#49884;&#53412;&#46304;&#51648; &#44036;&#50640; &#44536; &#51204;&#50640; DBUnit &#51060; &#51060; &#54028;&#51068;&#51012; &#51069;&#44172; &#46104;&#48064;&#47196; PersonActionTest &#50640;&#49436; &#51060; &#47112;&#53076;&#46300;&#46308;&#51060; &#49324;&#50857; &#46104;&#50612; &#51656;&#49688; &#51080;&#45716; &#44161;&#45768;&#45796;.
&#51088; &#51060;&#51228; __ant test-web -Dtestcase=PersonAction__ - &#47784;&#46304;&#44172; &#51221;&#49345;&#51201;&#51004;&#47196; &#51089;&#46041; &#46112;&#44161;&#45768;&#45796;. &#51060; &#53468;&#49828;&#53356;&#47484; &#49892;&#54665;&#54616;&#44592; &#51204;&#50640; Tomcat &#51008; &#48152;&#46300;&#49884; &#51221;&#51648;&#46104; &#51080;&#50612;&#50556; &#54633;&#45768;&#45796;.
<span style="color: green">BUILD SUCCESSFUL\\
Total time: 1 minute 21 seconds</span>
!!JSP &#47484; &#51221;&#47532;&#54633;&#45768;&#45796;. [#6]
web/pages/personForm.jsp &#50640;&#49436; &#45796;&#51020; &#53076;&#46300; &#48660;&#47085;&#51012; &#51228;&#44144; &#54616;&#44256; &#45824;&#49888;&#50640; "id" &#47484; hidden field &#47196; &#52628;&#44032; &#54633;&#45768;&#45796;.
[{Java2HtmlPlugin
<tr>
<th>
<appfuse:label key="personForm.id"/>
</th>
<td>
<html:text property="id" styleId="id"/>
<html:errors property="id"/>
</td>
</tr>
}]
&#44536;&#47532;&#44256; <table> &#53468;&#44536; &#50948;&#50640; &#45796; &#45236;&#50857;&#51012; &#52628;&#44032; &#54633;&#45768;&#45796;:
[{Java2HtmlPlugin
<html:hidden property="id"/>
}]
<html:form> &#51032; ''action'' &#49549;&#49457;&#51012; "savePerson" &#51004;&#47196; &#48148;&#45012;&#50556;&#47564; &#51200;&#51109;&#46112;&#46412; validation &#44592;&#45733;&#51060; &#51089;&#46041;&#46112; &#44163;&#51077;&#45768;&#45796;. &#44536;&#47532;&#44256; focus="" &#47484; focus="firstName" &#51004;&#47196; &#48148;&#44984;&#47732; &#54168;&#51060;&#51648;&#44032; &#47196;&#46377;&#46112;&#46412; &#52964;&#49436;&#44032; firstName &#54596;&#46300;&#50640; &#51105;&#55176;&#44172; &#46112; &#44161;&#45768;&#45796;.
&#51088; &#51060;&#51228; __ant db-load deploy__&#47484; &#49892;&#54665;&#54616;&#44256;, &#53680;&#52899;&#51012; &#44396;&#46041; &#49884;&#53412;&#44256; browser &#51032; &#51452;&#49548;&#47484; [http://localhost:8080/appfuse/editPerson.html?id=1] &#50640; &#47582;&#52628;&#47732; &#45796;&#51020;&#44284; &#44057;&#51008; &#54868;&#47732;&#51012; &#48372;&#44172; &#46112; &#44161;&#45768;&#45796;.
<div style="border: 1px solid black; margin: 0 auto; height: 166px; width: 337px">
[personForm-final.png]
</div>
<div class="note">
<b>NOTE:</b> web &#46356;&#47113;&#53664;&#47532; &#48145;&#50640; &#54028;&#51068;&#51012; &#49688;&#51221;&#54664;&#45796;&#47732; __deploy-web__ target &#51012; &#49324;&#50857;&#54616;&#44256;, &#44536;&#47111;&#51648; &#50506;&#45796;&#47732; compile &#44284; deploy &#47484; &#49892;&#54665;&#54616;&#45716; __deploy__ &#53468;&#49828;&#53356;&#47484; &#49324;&#50857; &#54616;&#49464;&#50836;
</div>
&#47560;&#51648;&#47561;&#51004;&#47196; &#48372;&#45796; &#50976;&#51200; &#52828;&#54868;&#51201;&#51004;&#47196; &#47564;&#46308;&#44592; &#50948;&#54644; &#54268;&#51032; &#49345;&#45800;&#48512;&#50640; &#50612;&#46500; &#47700;&#49464;&#51648;&#47484; &#52628;&#44032;&#54616;&#44600; &#50896;&#54620;&#45796;&#47732; personForm.jsp &#51032; &#44032;&#51109; &#50948;&#50640; &#44036;&#45800;&#55176; text(<fmt:message>&#47484; &#49324;&#50857;)&#47484; &#52628;&#44032; &#54616;&#47732; &#46121;&#45768;&#45796;.
!![[Optional]&#48652;&#46972;&#50864;&#51200; &#46041;&#51089; &#52376;&#47100; &#53580;&#49828;&#53944; &#54616;&#44592; &#50948;&#54644; Canoo WebTests &#47484; &#47564;&#46317;&#45768;&#45796;. [#7]
&#51060; &#53916;&#53664;&#47532;&#50620;&#51032; &#47560;&#51648;&#47561;(Optional)&#51008; JSP &#47484; &#53580;&#49828;&#53944; &#54616;&#44592; &#50948;&#54644; Canoo WebTest &#47484; &#47564;&#46300;&#45716; &#44161;&#45768;&#45796;.
;:''&#51060; &#44163;&#51060; optional &#51064; &#51060;&#50976;&#45716; &#46609;&#44057;&#51008; &#53580;&#49828;&#53944;&#47484; &#50668;&#47084;&#48516;&#51032; &#50937;&#48652;&#46972;&#50864;&#51200;&#47484; &#53685;&#54644;&#49436; &#54624; &#49688; &#51080;&#44592; &#46412;&#47928;&#51077;&#45768;&#45796;.''
You can use the following URLs to test the different actions for adding, editing and saving a user.
* Add - [http://localhost:8080/appfuse/editPerson.html].
* Edit - [http://localhost:8080/appfuse/editPerson.html?id=1] (make sure and run ant db-load first).
* Delete - [http://localhost:8080/appfuse/editPerson.html?method=Delete&id=1] (or edit and click on the Delete button).
* Save - Click [edit|http://localhost:8080/appfuse/editPerson.html?id=1] and then click the Save button.
Canoo tests are pretty slick in that they're simply configured in an XML file. To add tests for add, edit, save and delete, open test/web/web-tests.xml and add the following XML. You'll notice that this fragment has a target named PersonTests that runs all the related tests.
;:''I use CamelCase target names (vs. the traditional lowercase, dash-separated) because when you're typing -Dtestcase=Name, I've found that I'm used to doing CamelCase for my JUnit Tests.''
[{Java2HtmlPlugin
<!-- runs person-related tests -->
<target name="PersonTests"
depends="EditPerson,SavePerson,AddPerson,DeletePerson"
description="Call and executes all person test cases (targets)">
<echo>Successfully ran all Person JSP tests!</echo>
</target>
<!-- Verify the edit person screen displays without errors -->
<target name="EditPerson"
description="Tests editing an existing Person's information">
<webtest name="editPerson">
&config;
<steps>
&login;
<invoke description="click Edit Person link" url="/editPerson.html?id=1"/>
<verifytitle description="we should see the personDetail title"
text=".*${personDetail.title}.*" regex="true"/>
</steps>
</webtest>
</target>
<!-- Edit a person and then save -->
<target name="SavePerson"
description="Tests editing and saving a user">
<webtest name="savePerson">
&config;
<steps>
&login;
<invoke description="click Edit Person link" url="/editPerson.html?id=1"/>
<verifytitle description="we should see the personDetail title"
text=".*${personDetail.title}.*" regex="true"/>
<setinputfield description="set lastName" name="lastName" value="Canoo"/>
<clickbutton label="Save" description="Click Save"/>
<verifytitle description="Page re-appears if save successful"
text=".*${personDetail.title}.*" regex="true"/>
</steps>
</webtest>
</target>
<!-- Add a new Person -->
<target name="AddPerson"
description="Adds a new Person">
<webtest name="addPerson">
&config;
<steps>
&login;
<invoke description="click Add Button" url="/editPerson.html"/>
<verifytitle description="we should see the personDetail title"
text=".*${personDetail.title}.*" regex="true"/>
<setinputfield description="set firstName" name="firstName" value="Abbie"/>
<setinputfield description="set lastName" name="lastName" value="Raible"/>
<clickbutton label="${button.save}" description="Click button 'Save'"/>
<verifytitle description="Main Menu appears if save successful"
text=".*${mainMenu.title}.*" regex="true"/>
<verifytext description="verify success message"
text="Information for &lt;strong&gt;Abbie Raible&lt;/strong&gt; has been added successfully."/>
</steps>
</webtest>
</target>
<!-- Delete existing person -->
<target name="DeletePerson"
description="Deletes existing Person">
<webtest name="deletePerson">
&config;
<steps>
&login;
<invoke description="click Edit Person link" url="/editPerson.html?id=1"/>
<clickbutton label="${button.delete}" description="Click button 'Delete'"/>
<verifytitle description="display Main Menu" text=".*${mainMenu.title}.*" regex="true"/>
<verifytext description="verify success message"
text="Information for &lt;strong&gt;Matt Canoo&lt;/strong&gt; has been deleted successfully."/>
</steps>
</webtest>
</target>
}]
After adding this, you should be able to run __ant test-canoo -Dtestcase=PersonTests__ with Tomcat running or __ant test-jsp -Dtestcase=PersonTests__ if you want Ant to start/stop Tomcat for you. To include the PersonTests when all Canoo tests are run, add it as a dependency to the "run-all-tests" target.
You'll notice that there's no logging in the client-side window by Canoo. If you'd like to see what it's doing, you can add the following between </webtest> and </target> at the end of each target.
{{{
<loadfile property="web-tests.result"
srcFile="${test.dir}/data/web-tests-result.xml"/>
<echo>${web-tests.result}</echo>
}}}
<span style="color: green">BUILD SUCCESSFUL\\
Total time: 11 seconds</span>
----
''Next Up:'' __Part IV:__ [Validation &#44284; List &#54168;&#51060;&#51648; &#52628;&#44032;&#54616;&#44592;|ValidationAndList] - Adding validation logic to the personForm so that firstName and lastName are required fields and adding a list screen to display all person records in the database.

Back to SpringControllers_ko, or to the Page History.