3주차 이후 - 4~5일차: 실전 CRUD - 상세/등록/수정 화면 개발
학습 목표
- 게시글 상세 정보를 조회하고, 등록 및 수정 기능을 구현한다.
- 넥사크로에서 서버로 데이터를 전송하는
transaction사용법을 익힌다.
1. 상세/등록/수정 Form 개발
새 Form 생성:
frm_board_detail.xfdl을 생성합니다.데이터셋 정의:
Invisible Objects에ds_board_detail데이터셋을 생성하고, SpringBoard엔티티의 필드(id,title,content,author,createdAt)와 동일한 컬럼을 정의합니다. (단,id는Long타입이므로INT로,createdAt은String으로 정의할 수 있습니다.)ds_board_detail은 단일 게시글의 정보를 담을 것이므로, 초기에는 1개의 빈 행을addRow()로 추가해두는 것이 편리합니다.
컴포넌트 배치:
frm_board_detail에 다음 컴포넌트들을 배치합니다.Static과Edit컴포넌트를 조합하여 ID, 제목, 작성자, 작성일, 내용을 표시/입력하는 폼을 만듭니다.id는Edit컴포넌트의readonly속성을true로 설정하여 수정 불가하게 합니다.content는TextArea컴포넌트를 사용합니다.
Button컴포넌트:name을btn_save로,text를 "저장"으로 설정합니다.Button컴포넌트:name을btn_delete로,text를 "삭제"로 설정합니다.Button컴포넌트:name을btn_close로,text를 "닫기"로 설정합니다.
데이터 바인딩:
ds_board_detail데이터셋의 각 컬럼을 해당Edit또는TextArea컴포넌트에 바인딩합니다.
2. 상세 정보 조회 기능 구현
frm_board_detail이 열릴 때, 전달받은 boardId가 있다면 해당 게시글의 상세 정보를 조회합니다.
Form
onload이벤트:frm_board_detail의onload이벤트에 스크립트를 작성합니다.// frm_board_detail.xfdl Script this.fv_boardId = null; // 전역 변수로 게시글 ID 저장 this.frm_board_detail_onload = function(obj:nexacro.Form,e:nexacro.LoadEventInfo) { // 부모 Form으로부터 전달받은 boardId가 있는지 확인 if (!this.gfn_IsNull(this.parent.boardId)) { this.fv_boardId = this.parent.boardId; this.fn_searchDetail(this.fv_boardId); // 상세 조회 함수 호출 } else { // 신규 등록 모드일 경우, ds_board_detail에 빈 행 추가 (바인딩을 위해) this.ds_board_detail.addRow(); this.btn_delete.set_enable(false); // 신규 등록 시 삭제 버튼 비활성화 } }; // 게시글 상세 조회 함수 this.fn_searchDetail = function(boardId) { var strUrl = "http://localhost:8080/api/board/" + boardId; // Spring Boot API 주소 this.transaction( "tr_board_detail", strUrl, "", "ds_board_detail=this.ds_board_detail", // 응답 데이터를 ds_board_detail에 매핑 "", "fn_callback" ); }; // 콜백 함수 (목록 조회와 동일하게 사용) this.fn_callback = function(strSvcID, nErrorCode, strErrorMsg) { if (nErrorCode < 0) { this.alert("통신 실패: " + strErrorMsg); return; } if (strSvcID == "tr_board_detail") { // 조회된 데이터가 ds_board_detail에 자동으로 바인딩됩니다. this.alert("게시글 상세 정보가 조회되었습니다."); } else if (strSvcID == "tr_board_save") { this.alert("게시글이 성공적으로 저장되었습니다."); this.close(); // 저장 후 Form 닫기 } else if (strSvcID == "tr_board_delete") { this.alert("게시글이 성공적으로 삭제되었습니다."); this.close(); // 삭제 후 Form 닫기 } }; // 공통 Null 체크 함수 (이전 주차에서 작성) this.gfn_IsNull = function (sValue) { if (new String(sValue).valueOf() == "undefined") return true; if (sValue == null) return true; if (("x" + sValue == "xNaN") && (new String(sValue.length).valueOf() == "undefined")) return true; if (sValue.length == 0) return true; return false; }
3. 게시글 등록/수정 기능 구현
btn_save 버튼 클릭 시 ds_board_detail의 데이터를 Spring API로 전송합니다.
btn_save_onclick이벤트:btn_save버튼의onclick이벤트에 스크립트를 작성합니다.// btn_save_onclick 이벤트 this.btn_save_onclick = function(obj:nexacro.Button,e:nexacro.ClickEventInfo) { // 데이터 유효성 검사 (예: 제목, 작성자, 내용 필수 입력) if (this.gfn_IsNull(this.ds_board_detail.getColumn(0, "title"))) { this.alert("제목을 입력해주세요."); return; } if (this.gfn_IsNull(this.ds_board_detail.getColumn(0, "author"))) { this.alert("작성자를 입력해주세요."); return; } if (this.gfn_IsNull(this.ds_board_detail.getColumn(0, "content"))) { this.alert("내용을 입력해주세요."); return; } var strUrl = "http://localhost:8080/api/board"; // Spring Boot API 주소 this.transaction( "tr_board_save", strUrl, "this.ds_board_detail=this.ds_board_detail", // InDatasets (ds_board_detail의 데이터를 서버로 전송) "", // OutDatasets (응답 데이터셋 없음) "", "fn_callback", false, // 비동기 여부 (true: 비동기, false: 동기 - 일반적으로 비동기 사용) 0, // Timeout "POST" // HTTP Method 지정 (POST) ); };
4. 게시글 삭제 기능 구현
btn_delete 버튼 클릭 시 현재 게시글을 삭제합니다.
btn_delete_onclick이벤트:btn_delete버튼의onclick이벤트에 스크립트를 작성합니다.// btn_delete_onclick 이벤트 this.btn_delete_onclick = function(obj:nexacro.Button,e:nexacro.ClickEventInfo) { if (this.gfn_IsNull(this.fv_boardId)) { this.alert("삭제할 게시글 정보가 없습니다."); return; } if (this.confirm("정말로 삭제하시겠습니까?")) { var strUrl = "http://localhost:8080/api/board/" + this.fv_boardId; // Spring Boot API 주소 this.transaction( "tr_board_delete", strUrl, "", "", "", "fn_callback", false, 0, "DELETE" // HTTP Method 지정 (DELETE) ); } };
5. 목록 화면과 상세 화면 연동
frm_board_list에서 btn_new 클릭 시 신규 등록 모드로, grd_board 더블클릭 시 수정 모드로 frm_board_detail을 띄우도록 수정합니다.
frm_board_list스크립트 수정:frm_board_list의btn_new_onclick과grd_board_oncelldblclick함수를 수정합니다.// frm_board_list.xfdl Script // [새 글] 버튼 클릭 이벤트 this.btn_new_onclick = function(obj:nexacro.Button,e:nexacro.ClickEventInfo) { this.gfn_openDetailForm(null, "insert"); // 신규 등록 모드 }; // grd_board.oncelldblclick 이벤트 (수정) this.grd_board_oncelldblclick = function(obj:nexacro.Grid,e:nexacro.GridClickEventInfo) { var nBoardId = this.ds_board_list.getColumn(e.row, "id"); if (this.gfn_IsNull(nBoardId)) { return; } this.gfn_openDetailForm(nBoardId, "update"); // 수정 모드 }; // 상세 Form을 띄우는 공통 함수 (팝업으로 띄우는 예시) this.gfn_openDetailForm = function(boardId, mode) { var objParam = { boardId: boardId, mode: mode }; // 상세 Form으로 전달할 파라미터 var sPopupId = "popup_board_detail"; var sUrl = "Base::frm_board_detail.xfdl"; // 상세 Form 경로 var sOption = "width=800,height=600,showtitlebar=true,titletext=게시글 상세"; this.gfn_openPopup(sPopupId, sUrl, objParam, sOption, "fn_popup_callback"); }; // 팝업 콜백 함수 this.fn_popup_callback = function(sPopupId, sReturn) { if (sPopupId == "popup_board_detail") { if (sReturn == "save" || sReturn == "delete") { this.fn_search(); // 상세 화면에서 저장/삭제 후 목록 재조회 } } }; // 팝업 오픈 공통 함수 (프로젝트 공통 스크립트에 정의) this.gfn_openPopup = function(sPopupId, sUrl, objParam, sOption, sCallbackFunc) { var objNewForm = new nexacro.Form(); objNewForm.init(sPopupId, 0, 0, 0, 0, null, null); objNewForm.set_url(sUrl); objNewForm.set_openalign("center,middle"); objNewForm.set_showtitlebar(true); objNewForm.set_titletext("팝업"); // 파라미터 전달 if (objParam) { for (var p in objParam) { if (objParam.hasOwnProperty(p)) { objNewForm.set_formscrolltype("none"); objNewForm[p] = objParam[p]; } } } objNewForm.showModal(this.getOwnerFrame(), objParam, this, sCallbackFunc); };frm_board_detail스크립트 수정:btn_close클릭 시 팝업을 닫고, 저장/삭제 후에는 부모 Form에 알림을 보냅니다.// frm_board_detail.xfdl Script // [닫기] 버튼 클릭 이벤트 this.btn_close_onclick = function(obj:nexacro.Button,e:nexacro.ClickEventInfo) { this.close(); // Form 닫기 }; // 저장/삭제 콜백 함수에서 부모 Form에 결과 전달 // (fn_callback 함수 내부에 추가) // ... else if (strSvcID == "tr_board_save") { this.alert("게시글이 성공적으로 저장되었습니다."); this.close("save"); // 저장 후 Form 닫고, 부모에 "save" 문자열 전달 } else if (strSvcID == "tr_board_delete") { this.alert("게시글이 성공적으로 삭제되었습니다."); this.close("delete"); // 삭제 후 Form 닫고, 부모에 "delete" 문자열 전달 } // ...
오늘의 과제
-
frm_board_detail.xfdlForm 생성 및 컴포넌트 배치, 데이터 바인딩 - 상세 조회, 등록/수정, 삭제 기능 구현 및 Spring Boot 서버와 연동 확인
- 목록 화면과 상세 화면을 팝업으로 연동하여 전체 CRUD 흐름 테스트
'프론트엔드 > 넥사크로' 카테고리의 다른 글
| [Nexacro] 3주차 이후 - 6일차: 심화 학습 및 다음 단계 (0) | 2025.09.24 |
|---|---|
| [Nexacro] 3주차 이후 - 2~3일차: 실전 CRUD - 목록 화면 개발 (0) | 2025.09.24 |
| [Nexacro] 3주차 이후 - 1일차: 실전 CRUD 프로젝트 - 기획 및 백엔드 준비 (0) | 2025.09.24 |
| [Nexacro] 2주차 - 4~5일차: 서버 통신 (Transaction) (0) | 2025.09.24 |
| [Nexacro] 2주차 - 3일차: 데이터 바인딩(Data Binding) (0) | 2025.09.24 |
| [Nexacro] 2주차 - 1~2일차: 데이터셋(Dataset) 완전 정복 (0) | 2025.09.24 |
| [Nexacro] 1주차 - 4~5일차: "Hello World" 및 이벤트 처리 (0) | 2025.09.24 |
| [Nexacro] 1주차 - 2~3일차: 핵심 용어 및 넥사크로 스튜디오 탐색 (0) | 2025.09.24 |