사람함께에이전트▾
사람 — 직접 정하고 책임지는 비즈니스 규칙과 흐름. 직접 읽어보세요.
함께 — 개념은 알아두고, 세부 규칙은 에이전트가 따릅니다.
에이전트 — 에이전트가 따르는 규칙과 레퍼런스. 필요할 때 찾아보세요.
CLI 레퍼런스▾
AkanJS 레퍼런스▾
폼 UI
akanjs/ui의 폼 컨트롤입니다. 모델 폼의 상태는 스토어의 <model>Form에 있고, 생성된 setter가 필드 하나씩 값을 바꿉니다. 컨트롤은 현재 값을 보여주고 다음 값을 돌려줄 뿐입니다.이 페이지에서 쓰는 말
용어설명
<model>Form
스토어가 들고 있는, 편집 중인 레코드의 초안입니다. 예를 들면
icecreamOrderForm입니다.st.do.set<Field>On<Model>
자동으로 생성되는 폼 setter입니다. 초안의 필드 하나에 값을 넣습니다.
controlled (제어형)
받은
value를 보여주고 바뀐 값은 onChange로 돌려주는 방식입니다. Switch만 자체 상태로도 동작합니다.에이전트 툴
인페이지 에이전트가 호출할 수 있는 동작입니다. 참조로 넘긴 폼 setter가 툴이 됩니다.
오버라이드 슬롯
_overrides.tsx에서 컴포넌트를 라우트 하위 트리 단위로 바꿔 끼울 때 쓰는 이름입니다.컨트롤 고르기
위에서부터 고르세요. 모델 필드는
Field.*, 라벨 행이 필요 없으면 단독 컨트롤, 마지막 동작은 Button입니다.컴포넌트
라벨 행
label
에이전트 툴
st.do.setXOnY
오버라이드 슬롯
_overrides.tsx
모델 필드 — Template 안에서
Field.*
✓
✓
모델 필드 하나에 라벨이 붙은 컨트롤 하나입니다. Template 안에서 씁니다.
단독 컨트롤 — 검색창, 필터 바, 인라인 셀
Input
✓
✓
라벨 행이 없는 텍스트·숫자·비밀번호·이메일·체크박스 입력입니다.
Select
✓
✓
✓
단일·다중·검색 선택을 하는 드롭다운입니다.
label은 넣어도 되고 빼도 됩니다.Switch
✓
켜고 끄는 boolean 토글입니다. 라벨 행은
Field.Switch가 붙여 줍니다.Radio
✓
짧은 목록에서 하나를 고르는 라디오 버튼입니다.
ToggleSelect
✓
버튼 줄에서 하나 또는 여러 개를 고릅니다. 툴 공개는
Field.ToggleSelect가 합니다.DatePicker
✓
네이티브 입력으로 날짜·일시·기간·시각을 받습니다. 툴 공개는
Field.Date가 합니다.동작 — 폼을 마무리
Button
✓
스스로는 아무것도 공개하지 않습니다.
onClick에 넘긴 st.tool(...) 핸들러가 에이전트의 툴입니다.✓있음없음



setter는 반드시 참조로 넘기세요.
onChange={st.do.setSizeOnTicket}은 필드를 에이전트 툴로 공개하고 컨트롤에 data-akan-action / data-akan-state를 붙입니다. onChange={(size) => st.do.setSizeOnTicket(size)}는 똑같이 동작하지만 아무것도 공개하지 않습니다.함께 볼 페이지
Field
모듈 Template을 쓸 때 사용하는 폼 필드 네임스페이스입니다.
<Field> 자체는 라벨 행이 달린 섹션 래퍼이고, 각 멤버는 라벨이 붙은 컨트롤 하나입니다. 멤버끼리는 주로 value의 형태가 다릅니다.속성과 API
Field{ label?, desc?, nullable?, className?, containerClassName?, labelClassName?, children? }
섹션 래퍼입니다. 라벨 행을 그리고, 그 아래에 자식을
gap-4 간격의 세로 열로 쌓습니다.Field.Label{ label, desc?, unit?, nullable?, mode? }
라벨 행입니다. 문자열 라벨의 첫 글자를 대문자로 바꾸고,
desc를 툴팁으로 달고, nullable이면 (optional)을 붙입니다.Field.Text{ value: string | null, minlength? = 2, maxlength? = 200, inputStyleType? }
한 줄 텍스트입니다.
inputStyleType은 bordered(기본), borderless, underline 중 하나입니다.Field.TextArea{ value: string | null, rows? = 3, minlength? = 2, maxlength? = 1000 }
여러 줄 텍스트이며, 기본 높이는 세 줄입니다.
Field.Email{ value: string | null, maxlength? = 80, inputStyleType? }
올바른 이메일 주소여야 하는 텍스트입니다.
Field.Phone{ value: string | null, maxlength? = 13 }
전화번호여야 하는 텍스트입니다. 기본
transform이 하이픈을 넣어 형식을 맞춥니다.Field.Password{ value, confirmValue?, onChangeConfirm?, showConfirm?, minlength? = 8, maxlength? = 20 }
가려진 텍스트이며, 눈 아이콘으로 보이기를 켜고 끕니다.
showConfirm을 켜면 값이 같아야 하는 확인 칸이 붙습니다.Field.Number{ value: number | null, min?, max?, unit?, formatter?, parser? }
숫자 하나입니다.
unit은 라벨에 표시되고, formatter / parser가 화면에 보이는 숫자를 변환합니다.Field.DoubleNumber{ value: [number, number] | null, min?, max?, separator? }
한 줄에 놓인 숫자 두 개이며, 범위·비율·좌표 같은 값에 씁니다.
min / max도 두 값 쌍으로 넘깁니다.Field.Date{ value: Dayjs | null, min?, max?, showTime? }
브라우저 네이티브 입력으로 받는 날짜 하나입니다.
showTime을 켜면 시각까지 받습니다.Field.DateRange{ from, to, onChangeFrom, onChangeTo, onChange?, min?, max?, showTime? }
기간의 양 끝입니다.
onChange(from, to)는 양 끝이 모두 정해진 뒤에만 호출됩니다.Field.Switch{ value: boolean | null, onDesc?, offDesc? }
라벨이 붙은 boolean입니다.
onDesc / offDesc가 토글 옆에서 현재 상태를 설명합니다.Field.ToggleSelect{ items, value: I | null, nullable?, validate?, btnClassName? }
버튼 줄에서 하나를 고릅니다.
items에 enumOf(...)를 넘기면 값마다 번역된 라벨이 붙습니다.Field.MultiToggleSelect{ items, value: I[] | null, minlength?, maxlength? }
같은 버튼 줄에서 여러 개를 고릅니다.
minlength / maxlength를 어기면 번역된 안내가 뜹니다.Field.TextList{ value: string[] | null, minlength?, maxlength?, minTextlength?, maxTextlength? }
순서가 있는 문자열 목록이며, 항목마다 입력칸이 있습니다. 드래그로 순서를 바꾸고 행마다 지울 수 있습니다.
Field.Tags{ value: string[] | null, minTextlength? = 2, maxTextlength? = 10 }
순서 없는 짧은 문자열을 배지로 그리고, 그 자리에서 추가하는 입력칸이 붙습니다.
Field.List{ value: Item[] | null, onAdd, renderItem: (item, idx) => ReactNode }
내장 객체 목록입니다. 행 하나만 그리면 테두리와 추가·삭제 버튼은 필드가 그립니다.
Field.Parent{ value: Light | null, slice, renderOption, onSearch? }
관련 모델 하나를 Light 인스턴스로 담습니다. 선택지는
fetch.slice.user 같은 slice에서 불러옵니다.Field.ParentId{ value: string | null, slice, onChange: (id, model) => void }
ID 필드용 같은 선택기입니다. id만 담고, 고른 모델은 두 번째 인자로 넘겨 줍니다.Field.Children{ value: Light[] | null, slice, renderOption }
관련 모델 여러 개를 Light 인스턴스로 담습니다.
Field.ChildrenId{ value: string[] | null, slice, renderOption }
관련 모델 여러 개를 id로 담습니다.
- 공통 prop. 대부분의 멤버가
value/onChange,label/desc,nullable,disabled,placeholder,transform,validate,className/labelClassName/inputClassName을 받습니다. - 최소 길이.
Text,TextArea,Email,Password는minlength보다 짧은 입력에 오류를 띄웁니다. 기본값은 2이고,Password는 8,nullable이면 0입니다. 이니셜처럼 짧은 값이면 낮춰 주세요. - setter는 참조로.
onChange={st.do.setNameOnUser}처럼 넘겨야 필드가 에이전트 툴로 공개됩니다. 인라인 화살표 함수는 똑같이 동작하지만 아무것도 공개하지 않습니다. - 래퍼도 괜찮습니다. 값을 변형하거나, 코드를 한 줄 더 실행하거나,
writeOnX로 중첩 경로에 쓰는 래퍼는 써도 됩니다. 가능하면 컨트롤의transformprop으로 정규화하고, 나머지는st.tool로 직접 공개하세요. - lib에 멤버가 더 있습니다.
@libs/shared/ui의Field는 여기에Rich,Coordinate,Postcode,Img/Imgs,File/Files를 더한 것입니다.
apps/koyo/lib/icecreamOrder/IcecreamOrder.Template.tsx
Input
라벨 행 없이 입력칸만 필요한 자리(검색창, 필터 바, 인라인 셀 편집)에 쓰는 제어형(controlled) 입력입니다. 멤버마다 오버라이드 슬롯이 따로 있어
Input, InputTextArea, InputPassword, InputEmail, InputNumber, InputCheckbox를 하나씩 따로 바꿔 입힐 수 있습니다.속성과 API
valuestring
현재 값입니다. 입력칸은 값을 따로 복사해 두지 않습니다.
onChange(value, event?) => void
바뀐 문자열을 받습니다.
validate(value) => boolean | string
유효하면
true를 반환합니다. false나 메시지를 반환하면 입력칸 아래에 오류가 뜹니다.nullableboolean
값이 비어 있어도 경고를 띄우지 않습니다.
inputStyleType"bordered" | "borderless" | "underline"기본값 "bordered"
입력칸을 그리는 표면 모양입니다.
iconReactNode
입력칸 앞에 붙는 아이콘입니다.
onPressEnter(value, event) => void
Enter를 누르면 호출됩니다. form 요소 없이 검색창을 만들 때 씁니다.
onPressEscape(event) => void
Escape를 누르면 입력칸의 포커스를 뺀 뒤 호출됩니다.
Input.TextArea / Password / Email{ value: string, validate, onChange? }
쓰는 법은 같지만
validate가 필수입니다. Email은 형식이 틀린 주소도 거절합니다.Input.Number{ value: number | null, onChange, formatter?, parser? }
숫자 또는
null을 받습니다. formatter / parser가 화면에 보이는 글자를 변환합니다.Input.Checkbox{ checked, onChange: (checked, event) => void }
primary 색을 입힌 네이티브 체크박스입니다.
- 네이티브 속성은 그대로 전달됩니다.
placeholder,maxLength,autoFocus같은 속성은<input>에 그대로 붙습니다.
apps/koyo/ui/Search.tsx
Select
일반 값,
{ label, value } 쌍, enumOf(...) 클래스를 받는 제어형 드롭다운이며 단일·다중·검색 선택을 지원합니다. 옵션 목록은 document.body에 portal로 따로 그려져 필드 너비에 맞춰 뜨므로, 스크롤되는 모달이나 테이블 안에서도 잘리지 않습니다.속성과 API
valueT | T[]
선택된 값입니다.
multiple을 켜면 배열입니다.onChange(value, prev) => void
새 값과 직전 값을 받습니다.
optionsT[] | { label, value }[] | enumOf class
선택지입니다. enum은 원래 값을 그대로 보여주므로, 번역된 라벨이 필요하면 쌍으로 넘기세요.
label / descReactNode
필드 위에 붙는 선택적 라벨 행입니다.
desc는 도움말 툴팁이 됩니다.multipleboolean
여러 값을 고를 수 있게 합니다.
searchableboolean
라벨로 선택지를 거르는 검색칸을 붙입니다. 이때 문자열이 아닌 값은 쌍으로 넘겨야 합니다.
onSearch(text) => void
입력이 멈추고 300ms 뒤에 호출되며, 로컬 필터 대신 동작합니다.
nullableboolean
목록에 비우기 행을, 필드에 지우기 버튼을 붙입니다.
loadingboolean
선택지를 불러오는 동안 빈 목록 안내 대신 스피너를 보여줍니다.
onOpen() => void
목록이 열릴 때 호출됩니다. 선택지를 늦게 불러올 때 씁니다.
renderOption / renderSelected(value) => ReactNode
목록의 행과 고른 값을 직접 그립니다.
placeholder / emptystring / ReactNode
아무것도 고르지 않았을 때의 안내 문구와, 선택지가 없을 때 목록에 보일 내용입니다.
disabledboolean
목록 열기와 고르기를 막습니다. 공개된 툴도 함께 내려갑니다.
apps/koyo/ui/StatusFilter.tsx
Switch
<button role="switch">로 그린 boolean이라 포커스와 Space/Enter 토글을 브라우저가 처리합니다. checked를 넘기면 제어형으로, defaultChecked를 넘기면 자체 상태로 동작합니다. 라벨 행이 필요한 모델 필드라면 Field.Switch를 쓰세요.속성과 API
checkedboolean
제어형으로 쓸 때의 상태입니다. 빼면 스위치가 자체 상태를 가집니다.
defaultCheckedboolean기본값 false
자체 상태로 동작할 때의 시작 위치입니다.
onChange(checked: boolean) => void
바뀐 위치를 받습니다. 폼 setter를 참조로 넘기면 에이전트에 공개됩니다.
variant"primary" | "accent" | "success"기본값 "primary"
켜졌을 때의 색입니다. 꺼졌을 때는 항상
bg-muted입니다.disabledboolean
토글을 막고 흐리게 표시합니다. 공개된 툴도 함께 내려갑니다.
- 툴이 되는 것은 폼 setter뿐입니다.
set<Field>On<Model>는 공개되지만, 아래setNotify같은 일반 스토어 setter는 컨트롤에data-akan-action/data-akan-state표시만 붙습니다. Dropdown메뉴 항목 안에서는<li>에data-dropdown-keep-open을 붙이세요. 그래야 스위치를 눌러도 메뉴가 닫히지 않습니다.
apps/koyo/ui/NotifyToggle.tsx
Radio
role="radiogroup" 안의 role="radio" 버튼으로 하나를 고르며, 화살표 키를 누르면 포커스와 선택이 함께 움직입니다. 자식마다 자기 value를 갖고, 그룹은 그 값으로 선택을 맞춥니다. 숫자 value는 어떤 자식도 그 값을 갖지 않을 때만 index로 해석됩니다.속성과 API
valuestring | number | null
선택된 자식의
value입니다. 그 값을 가진 자식이 없으면 위치로 읽습니다.onChange(value, idx) => void
고른 자식의
value와 index를 받습니다. 화살표 키로 옮길 때도 호출됩니다.disabledboolean
모든 선택지를 비활성화합니다.
childrenReactNode | ReactElement[]
선택지이며 보통
Radio.Item입니다. 점과 행은 그룹이 그리고, 자식은 본문만 그립니다.Radio.Item{ value, children, className?, checked?, onChange? }
선택지 하나의 본문입니다. 그룹과 별개인 오버라이드 슬롯
RadioItem을 가집니다.- 에이전트 툴은 없습니다. 모델 필드라면 툴을 공개하는
Field.ToggleSelect를 쓰세요.
apps/koyo/ui/PlanPicker.tsx
ToggleSelect
드롭다운 대신 눌리는 버튼 줄로 고르는 선택이며, 선택지가 적고 짧을 때 맞습니다.
ToggleSelect는 하나, ToggleSelect.Multi는 여러 개를 고릅니다. 버튼 줄에는 돌아갈 빈 상태가 없으므로 nullable과 validate는 필수이고, 호출하는 쪽에서 둘 다 정합니다.속성과 API
itemsstring[] | number[] | { label, value, disabled? }[]
버튼 목록입니다.
Field.ToggleSelect는 enumOf(...)도 받아 값마다 번역합니다.valueI
선택된 값입니다.
nullableboolean
필수입니다. 선택을 비울 수 있는지 정하며, 켜면 선택된 버튼을 다시 눌렀을 때
onClear가 호출됩니다.validate(value) => boolean | string
필수입니다.
true를 반환하거나, 줄 아래에 띄울 메시지를 반환합니다.onChange / onClear(value, idx) => void / () => void
고르기와 비우기입니다.
onClear는 nullable일 때만 호출됩니다.disabledboolean
모든 버튼을 비활성화합니다. 항목의
disabled는 그 버튼 하나만 막습니다.renderItem(item, { selected, disabled, onToggle }) => ReactNode
버튼 하나를 직접 그립니다.
onToggle이 그 칸의 동작이므로, 그린 요소에 직접 연결하세요.ToggleSelect.Multi{ items, value: string[] | number[], nullable, validate, onChange }
여러 개를 고르는 형태이며, 별도의 오버라이드 슬롯
ToggleSelectMulti를 가집니다.- 에이전트 툴은 없습니다. 모델 필드라면
Field.ToggleSelect/Field.MultiToggleSelect를 쓰세요. 라벨 행을 붙이고 setter도 공개합니다.
apps/koyo/ui/SizePicker.tsx
DatePicker
브라우저 자체의
<input type="date">로 받는 날짜라, 달력·로케일·터치 키보드가 모두 플랫폼의 것입니다. min과 max는 브라우저가 지킵니다. 네이티브 필드는 특정 날짜만 흐리게 만들 수 없으므로, disabledDate에 걸린 선택은 경고 토스트와 함께 거절됩니다.속성과 API
valueDayjs | null
현재 값입니다.
onChange(value: Dayjs | null) => void
바뀐 값을 받습니다.
showTimeboolean
네이티브 입력을
datetime-local로 바꿉니다.min / maxDayjs | null
고를 수 있는 가장 이른 값과 가장 늦은 값이며, 브라우저가 지킵니다.
disabledDate(date: Dayjs) => boolean | null | undefined
거절할 날짜에
true를 반환합니다. 흐리게 보이지 않고, 고를 때 검사합니다.defaultValueDayjs
피커가 마운트될 때, 그리고 이 값이 바뀔 때마다
onChange로 보내집니다.DatePicker.RangePicker{ value: [Dayjs | null, Dayjs | null], onChange, showTime?, disabledDate? }
양 끝을 튜플 하나로 받습니다. 비어 있는 반대쪽은 현재 시각으로 채웁니다. 슬롯은
DatePickerRangePicker입니다.DatePicker.TimePicker{ value: Dayjs | null, onChange, disabled?, disabledDate? }
시각만 받으며, 날짜는
value가 가진 날을 유지합니다. 슬롯은 DatePickerTimePicker입니다.- 에이전트 툴은 없습니다. 모델 필드라면
Field.Date/Field.DateRange를 쓰세요. 라벨 행이 달린 자체 네이티브 입력을 그리고 setter도 공개합니다.
apps/koyo/ui/PeriodFilter.tsx
Button
버튼 컴포넌트는 이것 하나뿐입니다.
onClick이 동기면 평범한 버튼이고, promise를 반환하면 같은 버튼이 로딩·성공·오류 상태를 거치며 그동안 중복 클릭을 막습니다. 비동기용 버튼을 따로 고를 필요가 없습니다.속성과 API
onClick(event, { onError }) => Promise<Result> | Result
선택입니다. promise를 반환하면 비동기 상태가 켜지고, 그 밖의 값이면 평범한 버튼으로 남습니다.
onSuccess(result) => void
성공 체크 표시가 0.7초 동안 보인 뒤 결과와 함께 호출됩니다.
loadingMode"hold" | "replace"기본값 "hold"
버튼 크기는 변하지 않습니다.
hold는 스피너를 겹쳐 그리고, replace는 문구가 있는 표시로 교차 전환합니다.showErrorboolean기본값 true
onError 메시지를 버튼 아래에 띄웁니다. 끄면 어디에도 표시되지 않으니 직접 토스트로 알리세요.variant / size / shape / outlineButtonVariants기본값 "primary" / "md" / "default"
buttonRecipe의 모양입니다. 색, 크기, 모서리 모양, 외곽선 여부를 정합니다.type"button" | "submit" | "reset"기본값 "button"
네이티브 type입니다. 기본값이
button이라 클릭해도 바깥 form이 제출되지 않습니다.disabledboolean
네이티브 prop입니다. 로딩 중과 성공 체크가 보이는 동안에도 비활성화됩니다.
- 던지지 않고 실패 알리기.
onError("<dictionary key>")를 호출하세요. 성공 체크는 건너뛰고, 버튼 아래에 번역된 메시지가 뜹니다. - reject되면 조용히 돌아갑니다. 버튼은 체크 표시 없이 원래 상태로 돌아가고, 에러는 예외를 던진 스토어 액션이나 fetch가 이미 보여 줍니다.
- 바꿔 입히는 방법은 둘입니다. 오버라이드 슬롯
Button은 컴포넌트 전체를, 레시피 슬롯recipes.button은 모양만 바꿉니다.
apps/koyo/ui/Actions.tsx