Claude-skill-registry dev-asp-net
DevExpress 25.1.6.0을 활용한 ASP.NET 웹 페이지 개발 전문가 스킬. ASP.NET Web Forms, MVC 패턴, DevExpress Bootstrap 컨트롤, Bootstrap 반응형 디자인 작업 시 사용. (1) ASP.NET 웹 페이지 생성, (2) DevExpress Bootstrap 컨트롤(BootstrapGridView, BootstrapComboBox, BootstrapDateEdit, BootstrapButton, BootstrapSpinEdit 등) 구현, (3) XPO DataBinding 설정, (4) PopupEditForm 편집 패턴 구현, (5) Code-behind 작성 시 사용.
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/dev-asp-net" ~/.claude/skills/majiayu000-claude-skill-registry-dev-asp-net && rm -rf "$T"
manifest:
skills/data/dev-asp-net/SKILL.mdsource content
DevAspNet Skill
DevExpress Bootstrap 25.1.6.0 기반 ASP.NET 웹 개발 가이드.
핵심 원칙
아키텍처 패턴
- MVC 패턴: Controller에서 비즈니스 로직, View에서 UI 분리
- DataBinding: List<ViewModel> 기반 바인딩, 세션 캐싱 활용
- 반응형 디자인: Bootstrap 5.3 + DevExpress Bootstrap 컨트롤
코드 작성 규칙
- ASPX 주석:
(HTML 주석 금지)<%-- 주석 --%> - 한글 주석: 복잡한 로직에 필수
- File Encoding: 한글이 안깨지도록 UTF-8로 저장
- 출력 형식: 변경 파일 리스트와 소스만 제공
- 문서: 최소한의 요약 md만 생성
DevExpress Bootstrap 컨트롤
Register 지시문
<%@ Register Assembly="DevExpress.Web.Bootstrap.v25.1, Version=25.1.6.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a" Namespace="DevExpress.Web.Bootstrap" TagPrefix="dx" %>
BootstrapGridView 기본 구조
<dx:BootstrapGridView ID="gridToDoList" runat="server" ClientInstanceName="gridToDoList" Width="100%" KeyFieldName="COMPANY_NO;CASE_NO;PROJECT_NO;ORDER_NO" AutoGenerateColumns="False" EnableCallBacks="true" OnPageIndexChanged="grid_PageIndexChanged" OnCustomCallback="grid_CustomCallback" OnRowUpdating="grid_RowUpdating" OnHtmlRowPrepared="grid_HtmlRowPrepared"> <Settings ShowFilterRow="True" ShowFilterRowMenu="True" VerticalScrollBarMode="Visible" VerticalScrollableHeight="450" HorizontalScrollBarMode="Auto" /> <SettingsBehavior AllowFocusedRow="True" AllowSelectByRowClick="True" /> <SettingsDataSecurity AllowEdit="True" AllowInsert="False" AllowDelete="False" /> <SettingsPager Mode="ShowPager" PageSize="50" Position="Bottom"> <PageSizeItemSettings Visible="true" Items="50,100,200,500" /> </SettingsPager> <Columns> <%-- 컬럼 정의 --%> </Columns> </dx:BootstrapGridView>
BootstrapGridView 컬럼 타입
<%-- 텍스트 컬럼 --%> <dx:BootstrapGridViewTextColumn FieldName="ORDER_NAME" Caption="지시명칭" Width="200px" ReadOnly="true" VisibleIndex="1"> <CssClasses HeaderCell="header-basic text-center" /> </dx:BootstrapGridViewTextColumn> <%-- 날짜 컬럼 --%> <dx:BootstrapGridViewDateColumn FieldName="COMP_DATE" Caption="완료일" Width="120px" VisibleIndex="2"> <PropertiesDateEdit DisplayFormatString="yyyy-MM-dd" AllowNull="true" /> <CssClasses HeaderCell="header-editable text-center" /> </dx:BootstrapGridViewDateColumn> <%-- 숫자(SpinEdit) 컬럼 --%> <dx:BootstrapGridViewSpinEditColumn FieldName="PLAN_MHR" Caption="계획 M/H" Width="100px" VisibleIndex="3"> <PropertiesSpinEdit DisplayFormatString="N1" MinValue="0" MaxValue="99999" AllowNull="true" /> <CssClasses HeaderCell="header-editable text-center" /> </dx:BootstrapGridViewSpinEditColumn> <%-- 명령 버튼 컬럼 --%> <dx:BootstrapGridViewCommandColumn Width="60px" ShowEditButton="True" VisibleIndex="0" Caption=" "> <CssClasses HeaderCell="text-center" /> </dx:BootstrapGridViewCommandColumn> <%-- 숨김 Key 컬럼 --%> <dx:BootstrapGridViewTextColumn FieldName="COMPANY_NO" Visible="false" />
PopupEditForm 설정
<dx:BootstrapGridView ...> <%-- PopupEditForm 방식 설정 --%> <SettingsEditing Mode="PopupEditForm" /> <SettingsPopup> <EditForm HorizontalAlign="WindowCenter" VerticalAlign="WindowCenter" Modal="True" PopupAnimationType="Auto" /> </SettingsPopup> <SettingsText CommandEdit="수정" CommandUpdate="저장" CommandCancel="취소" PopupEditFormCaption="작업지시 수정" /> <%-- EditForm 템플릿 --%> <Templates> <EditForm> <div style="padding: 20px; min-width: 350px;"> <table class="table" style="margin-bottom: 15px;"> <tr> <td style="width: 120px; font-weight: bold;">완료일</td> <td> <dx:BootstrapDateEdit ID="edtCompDate" runat="server" Width="100%" DisplayFormatString="yyyy-MM-dd" EditFormat="Date" AllowNull="true" Value='<%# Bind("COMP_DATE") %>' /> </td> </tr> <tr> <td style="font-weight: bold;">계획 M/H</td> <td> <dx:BootstrapSpinEdit ID="edtPlanMhr" runat="server" Width="100%" DisplayFormatString="N1" MinValue="0" MaxValue="99999" AllowNull="true" Value='<%# Bind("PLAN_MHR") %>' /> </td> </tr> </table> <div style="text-align: right; padding-top: 10px; border-top: 1px solid #ddd;"> <dx:BootstrapButton ID="btnUpdate" runat="server" Text="저장" AutoPostBack="false"> <SettingsBootstrap RenderOption="Primary" /> <ClientSideEvents Click="function(s, e) { gridToDoList.UpdateEdit(); }" /> </dx:BootstrapButton> <dx:BootstrapButton ID="btnCancel" runat="server" Text="취소" AutoPostBack="false" CssClasses-Control="ml-2"> <SettingsBootstrap RenderOption="Secondary" /> <ClientSideEvents Click="function(s, e) { gridToDoList.CancelEdit(); }" /> </dx:BootstrapButton> </div> </div> </EditForm> </Templates> </dx:BootstrapGridView>
클라이언트 메시지 콜백 패턴
<ClientSideEvents EndCallback="function(s, e) { if (s.cpMessage) { alert(s.cpMessage); s.cpMessage = null; } }" />
BootstrapComboBox
<dx:BootstrapComboBox ID="cmbCompany" runat="server" Width="250px" AutoPostBack="false" /> <%-- AutoPostBack 사용 시 --%> <dx:BootstrapComboBox ID="cmbCompanyType" runat="server" Width="150px" OnSelectedIndexChanged="cmbCompanyType_SelectedIndexChanged" AutoPostBack="true" />
BootstrapDateEdit
<dx:BootstrapDateEdit ID="dtBaseDate" runat="server" Width="150px" DisplayFormatString="yyyy-MM-dd" EditFormat="Date" AllowNull="false" />
BootstrapButton
<%-- Primary 버튼 (아이콘 포함) --%> <dx:BootstrapButton ID="btnSearch" runat="server" Text="<i class='fas fa-search'></i> 조회" EncodeHtml="false" OnClick="btnSearch_Click"> <SettingsBootstrap RenderOption="Primary" /> </dx:BootstrapButton> <%-- 확인 다이얼로그 --%> <dx:BootstrapButton ID="btnDelete" runat="server" Text="<i class='fas fa-times'></i> 삭제" EncodeHtml="false" OnClick="btnDelete_Click"> <SettingsBootstrap RenderOption="Danger" /> <ClientSideEvents Click="function(s, e) { e.processOnServer = confirm('선택한 데이터를 삭제하시겠습니까?'); }" /> </dx:BootstrapButton>
Code-behind 패턴
기본 구조
using DevExpress.Web; using DevExpress.Web.Bootstrap; using DevExpress.Web.Data; public partial class ToDoList : System.Web.UI.Page { private ToDoListController _controller = new ToDoListController(); private const string SESSION_KEY_DATA = "ToDoList_Data"; // 세션 기반 그리드 데이터 관리 private List<ToDoListViewModel> GridData { get { return Session[SESSION_KEY_DATA] as List<ToDoListViewModel>; } set { Session[SESSION_KEY_DATA] = value; } } protected void Page_Load(object sender, EventArgs e) { // 로그인 체크 if (Session["UserID"] == null) { Response.Redirect("Login.aspx", false); Context.ApplicationInstance.CompleteRequest(); return; } if (!IsPostBack && !IsCallback) InitializePage(); else BindGridFromSession(); } }
복합 키 RowUpdating 처리 (중요!)
protected void grid_RowUpdating(object sender, ASPxDataUpdatingEventArgs e) { // List<T> 데이터소스는 기본 업데이트 미지원 → 항상 Cancel e.Cancel = true; try { // 복합키: e.Keys로 접근 (e.OldValues 아님!) string companyNo = e.Keys["COMPANY_NO"]?.ToString(); string caseNo = e.Keys["CASE_NO"]?.ToString(); string projectNo = e.Keys["PROJECT_NO"]?.ToString(); string orderNo = e.Keys["ORDER_NO"]?.ToString(); // 수정 필드: e.NewValues로 접근 DateTime? compDate = e.NewValues["COMP_DATE"] as DateTime?; decimal? planMhr = e.NewValues["PLAN_MHR"] as decimal?; bool result = _controller.UpdateWorkOrder( caseNo, companyNo, projectNo, orderNo, compDate, planMhr, userId); if (result) { // 성공 시: 팝업 닫고 데이터 새로고침 gridToDoList.CancelEdit(); LoadData(); ShowMessageCallback("데이터가 수정되었습니다."); } } catch (Exception ex) { ShowMessageCallback($"수정 오류: {ex.Message}"); } }
콜백 메시지 표시
// 일반 메시지 private void ShowMessage(string message) { string script = $"alert('{message.Replace("'", "\\'")}');"; ScriptManager.RegisterStartupScript(this, GetType(), "alertScript", script, true); } // 콜백 모드 메시지 (JSProperties 사용) private void ShowMessageCallback(string message) { gridToDoList.JSProperties["cpMessage"] = message; }
세션 바인딩
private void BindGridFromSession() { if (GridData != null) { gridToDoList.DataSource = GridData; gridToDoList.DataBind(); } }
행 삭제 (FocusedRow 사용)
protected void btnDelete_Click(object sender, EventArgs e) { if (gridToDoList.FocusedRowIndex < 0) { ShowMessage("삭제할 데이터를 선택하세요."); return; } // 복합키 가져오기 object[] keyValues = (object[])gridToDoList.GetRowValues( gridToDoList.FocusedRowIndex, new string[] { "COMPANY_NO", "CASE_NO", "PROJECT_NO", "ORDER_NO" }); string companyNo = keyValues[0]?.ToString(); string caseNo = keyValues[1]?.ToString(); // ... }
주의사항
- ASPxGridView vs BootstrapGridView: 이 프로젝트는
사용BootstrapGridView - PopupEditForm 성공 시:
호출로 팝업 닫기gridToDoList.CancelEdit() - 복합 키:
세미콜론 구분,KeyFieldName="Key1;Key2;Key3"
로 접근e.Keys - List<T> 데이터소스:
필수 (기본 업데이트 미지원)e.Cancel = true - 콜백 메시지:
+JSProperties["cpMessage"]
이벤트 조합EndCallback