사람함께에이전트▾
사람 — 직접 정하고 책임지는 비즈니스 규칙과 흐름. 직접 읽어보세요.
함께 — 개념은 알아두고, 세부 규칙은 에이전트가 따릅니다.
에이전트 — 에이전트가 따르는 규칙과 레퍼런스. 필요할 때 찾아보세요.
DataList와 Enum
Enum과 DataList는 Akan 코드 곳곳에서 만나는 작은 도구 두 가지입니다. Enum은 정해진 값 목록에, DataList는 id가 있는 항목 목록에 씁니다.
Enum
정해진 값 목록
상태, 역할, 종류, 카테고리처럼 늘 몇 가지 중 하나인 값입니다.
enumOf("postStatus", [...] as const)DataList
id로 찾는 목록
사용자, 파일, 게시글, 선택된 행처럼 id로 찾고 바꾸는 레코드 목록입니다.
userList.pick("u1")둘 다
akanjs/base에서 import합니다.Enum
값이 몇 가지 선택지 중 하나여야 한다면 Enum을 씁니다. 한 번 선언해 두면 폼, API, 화면 라벨이 모두 같은 목록을 따릅니다.
1. constant 파일에 선언하기
모델 클래스들 위에 Enum 클래스를 선언하고, 필드 타입으로 씁니다:
apps/myapp/lib/post/post.constant.ts
as const는 꼭 붙입니다. 빠지면PostStatus["value"]가string으로 넓어져 오타도 그대로 컴파일됩니다.- 첫 번째 인자가 refName입니다. 클래스 이름을 camelCase로 바꿔 씁니다.
PostStatus→postStatus - 숫자도 됩니다. 모두 정수면
Int필드가 되고, 소수가 하나라도 있으면Float이 됩니다. - TypeScript enum은 쓰지 않습니다. Akan은
enum키워드를 쓰지 않고, 선택지 필드는 늘enumOf클래스입니다.
2. 값마다 라벨 붙이기
dictionary의
.enum() 단계에서 refName을 키로 값마다 번역을 적습니다:apps/myapp/lib/post/post.dictionary.ts
- 값마다 항목이 있어야 합니다. 이 단계의 타입이
PostStatus에서 나오므로, 빠진 값은 타입 에러가 됩니다. - 라벨마다 키가 생깁니다.
draft값은l("postStatus.draft")로 읽습니다.
3. 화면에서 쓰기
폼에서는 Enum 클래스를 토글 필드에 그대로 넘깁니다:
apps/myapp/lib/post/Post.Template.tsx
- 라벨은 dictionary에서 옵니다.
Field.ToggleSelect와Field.MultiToggleSelect는 Enum 클래스를 받아 선택지마다 해당 키의 라벨을 붙입니다. - setter는 그대로 넘깁니다. 화살표 함수로 감싸지 않은
onChange={st.do.setStatusOnPost}라야 인페이지 에이전트에도 이 필드가 공개됩니다.
저장된 값을 보여줄 때도 같은 키를 찾습니다. 값마다 스타일을 주려면 모듈 스코프 맵을 씁니다:
apps/myapp/lib/post/Post.Unit.tsx
- 맵은 모든 값을 빠짐없이 담습니다.
{ [key in cnst.PostStatus["value"]]: string }로 타입을 주면, 새 값에 클래스를 빠뜨렸을 때 타입 에러가 납니다.
4. 코드에서 값 다루기
Enum 클래스는 값 목록과 몇 가지 배열 도우미를 직접 갖고 있습니다:
멤버설명
PostStatus["value"]
props와 파라미터에 쓰는 값 타입
"draft" | "published" | "archived"입니다.values
선언한 순서 그대로의 값 배열입니다.
has(value)
어떤 값이 이 Enum에 속하는지 런타임에 확인합니다.
indexOf(value)
values 안에서 값의 위치를 돌려주며, Enum에 없는 값이면 에러를 던집니다.mapfilterforEach
values를 대상으로 도는 일반 배열 메서드입니다.findfindIndex
배열 메서드와 같지만, 맞는 값이 없으면 에러를 던집니다.
refName
enumOf에 넘긴 이름으로, 여기서는 postStatus입니다.Select처럼 label/value 쌍을 받는 컨트롤에는 라벨을 직접 매핑해 넘깁니다:apps/myapp/lib/post/Post.Zone.tsx
Select는 값을 그대로 보여줍니다.options={cnst.PostStatus}도 되지만, 목록에 번역된 라벨이 아니라draft가 보입니다.
DataList
이미 불러온 목록을 id 기준으로 다루고 싶다면 DataList를 씁니다. 추가, 교체, 선택, 필터링이 호출 한 번이라 UI 상태에 잘 맞습니다.
기본 사용
DataList는 id 하나에 항목 하나만 둡니다:
apps/myapp/lib/user/user.test.ts
set은 추가하거나 교체합니다. 새 id는 끝에 붙고, 이미 있는 id는 그 자리에서 바뀝니다.pick과get. 없는 id에pick(id)는 에러를 던지고,get(id)는undefined를 돌려줍니다.filter는 더 작은 DataList를 만듭니다. 원래 목록은 그대로입니다.
메서드 한눈에 보기
메서드설명
new DataList(items)
배열이나 다른 DataList로 목록을 만들며, 같은 id가 여러 번 오면 마지막 항목이 남습니다.
set(item)delete(id)
id 기준으로 추가·교체하거나 삭제하며, 이 목록 자체를 바꾼 뒤 그대로 돌려줍니다.
get(id)pick(id)has(id)
id로 찾습니다.
get은 undefined를 돌려줄 수 있고, pick은 에러를 던지고, has는 참·거짓을 답합니다.indexOf(id)at(idx)pickAt(idx)
위치로 찾으며,
indexOf와 pickAt은 찾는 것이 없으면 에러를 던집니다.filterslicesort
새 DataList를 돌려주지만
sort는 원본 배열의 순서도 바꾸므로, 복사본을 정렬합니다.mapfindsomeeveryreduceforEachflatMap
항목을 대상으로 도는 배열 메서드이며,
map은 일반 배열을 돌려줍니다.lengthvalues
항목 수와 내부 배열이며, 목록에 바로
for…of를 돌려도 됩니다.save()
같은 항목으로 새 DataList를 만들어 돌려주며, store에 넘길 때 필요한 것이 이것입니다.
store 속 DataList
slice마다 store에 DataList 키가 세 개 생기고,
Load.Units도 하나를 넘겨줍니다. post 모델이라면 이렇습니다:이름설명
postList
slice가 불러온 행이며, 이름 있는 slice는
postListInPublic처럼 뒤에 접미사가 붙습니다.postInitList
마지막
init이 불러온 그대로의 행입니다.postSelection
st.do.selectPost(post)로 채우는, 사용자가 선택한 행입니다.renderList
Load.Units가 이 콜백에 목록을 DataList로 넘겨줍니다.store 목록 바꾸기
직접 만든 store 액션은 바뀐 목록을
save()로 되돌려 씁니다. shared lib의 admin store가 이렇게 합니다:libs/shared/lib/admin/admin.store.ts
set다음에save.set은 목록 자체를 바꾸고,save()가 그것을 새 DataList로 감싸 store가 새 값으로 알아보게 합니다.- 생성된 액션은 이미 이렇게 합니다. 생성, 수정, 삭제는
postList를 알아서 갱신하므로,addAdminRole같은 커스텀 endpoint에만 액션을 씁니다.


store에는 늘 새 DataList를 넘깁니다.
adminList.set(admin)은 같은 인스턴스를 돌려주고 store는 참조로 값을 비교하므로, 화면이 바뀌지 않습니다. 끝에 .save()를 붙이세요.언제 무엇을 쓰나
라벨 같은 값이면 Enum, id를 가진 레코드 모음이면 DataList입니다.
| 구분 | Enum | DataList |
|---|---|---|
| 담는 것 | 정해진 선택지 중 값 하나 | 각자 id가 있는 레코드 목록 |
| 예 | 상태, 역할, 종류, 크기, 공개 범위 | 사용자, 파일, 게시글, 선택된 행 |
| 있는 곳 | *.constant.ts의 필드 타입 | store 상태, 또는 new DataList(items) |
| 대표 호출 | PostStatus.has(value) | postList.pick(id) |
- DataList는 DB 쿼리가 아닙니다. 이미 앱에 불러온 데이터만 다루므로,
filter는 테이블 전체가 아니라 불러온 행만 봅니다. - 줄이는 일은 서버에서 합니다. 가져올 행 자체를 줄이려면 쿼리 필터나 slice를 추가합니다.
팁
- refName은 바꾸지 않습니다. dictionary,
postStatus.draft같은 라벨 키, API 스키마가 모두 이 이름으로 Enum을 찾으므로, 바꾸면 이들이 따라오지 못합니다. - DataList 항목은 가볍게 둡니다. store 목록은 목록에 필요한 필드만 가진 light model을 담습니다.
- 정렬은 복사본에 합니다.
list.filter(fn).sort(compare)나new DataList(list).sort(compare)로 쓰고, store 목록에 바로sort를 부르지 않습니다. - 이것만 기억하세요. 값 선택지는 Enum, id 목록은 DataList입니다.