Akan.js
Docs
문서컨벤션레퍼런스Cheatsheet
Akan.js
문서컨벤션레퍼런스Cheatsheet
Akan.js

MIT 라이선스 하에 배포되었습니다.

Akan.js 공식 컨설팅 서비스AkansoftCopyright © 2026 Akan.js 모든 권리 보유.시스템 관리자bassman
사람함께에이전트▾
사람 — 직접 정하고 책임지는 비즈니스 규칙과 흐름. 직접 읽어보세요.
함께 — 개념은 알아두고, 세부 규칙은 에이전트가 따릅니다.
에이전트 — 에이전트가 따르는 규칙과 레퍼런스. 필요할 때 찾아보세요.
일반▾
인증과 권한에이전트를 위한 OAuth스키마 설계텍스트 검색엣지 컴퓨팅파일 관리Single Sign-OnDataList & Enum
인터페이스▾
CRUDEndpointMCP 서버에이전트 채팅Form
관측성▾
로깅의존성 주입에러 처리메트릭
성능▾
캐싱이미지 최적화지연 로딩쿼리변경큐실시간
모바일▾
설정Push NotificationsDeep LinksUI & Keyboard데스크톱 배포
개발▾
문서화스키마 문서스크립트콘솔도커쿠버네티스PWA테스트
사람함께에이전트▾
사람 — 직접 정하고 책임지는 비즈니스 규칙과 흐름. 직접 읽어보세요.
함께 — 개념은 알아두고, 세부 규칙은 에이전트가 따릅니다.
에이전트 — 에이전트가 따르는 규칙과 레퍼런스. 필요할 때 찾아보세요.
일반▾
인증과 권한에이전트를 위한 OAuth스키마 설계텍스트 검색엣지 컴퓨팅파일 관리Single Sign-OnDataList & Enum
인터페이스▾
CRUDEndpointMCP 서버에이전트 채팅Form
관측성▾
로깅의존성 주입에러 처리메트릭
성능▾
캐싱이미지 최적화지연 로딩쿼리변경큐실시간
모바일▾
설정Push NotificationsDeep LinksUI & Keyboard데스크톱 배포
개발▾
문서화스키마 문서스크립트콘솔도커쿠버네티스PWA테스트
이전Deep Links다음데스크톱 배포

페이지 전환

앱은 네이티브 셸 안에서 돌아가는데, 화면이 바뀔 때마다 웹 페이지가 교체되듯 툭 바뀝니다. 사용자는 더 깊이 들어간 건지 옆으로 간 건지 알 수 없고, 빠져나오는 길은 뒤로 가기 버튼뿐입니다.
페이지의 .config()에 transition을 적으세요. 그러면 모바일 앱 셸이 네이티브 앱처럼 화면 전환을 애니메이션으로 보여 줍니다.
이 페이지에서 쓰는 말
용어설명
경로 깊이
언어와 basePath를 뺀 경로 조각 수로, /chat은 1, /chat/[chatId]는 2입니다.
safe area
노치, 상태 표시줄, 홈 인디케이터처럼 기기 자체가 가리는 영역입니다.
inset
위쪽 내비게이션 바나 아래쪽 입력창처럼 앱이 자기 바를 위해 비워 두는 공간입니다.
keyboard accessory layer
소프트웨어 키보드 바로 위에 붙어 키보드와 함께 움직이는 레이어입니다.
전환 지정하기
목록 위로 밀려 들어오는 상세 페이지입니다:
apps/myapp/page/article/[articleId].tsx
  • 값은 다섯 가지입니다. stack, bottomUp, fade, scaleOut, none.
  • 값이 드래그 동작도 정합니다. 실제 뒤로 가기 제스처가 달린 것은 stack과 bottomUp 둘뿐입니다.
전환별 모습
stack
현재 페이지 위로 새 페이지를 쌓습니다. 상세, 편집, 설정처럼 한 단계 깊이 들어가는 화면에 씁니다.
bottomUp
아래에서 집중 화면을 올리고, 끌어내리면 닫힙니다. 작성, 선택, 카메라처럼 모달 같은 흐름에 씁니다.
fade
더 깊이 들어간다는 느낌 없이 맥락만 바꿉니다.
scaleOut
살짝 커지며 나타나는 전환입니다. Android에서 깊은 경로의 기본값입니다.
플랫폼별 기본값
transition을 적지 않으면 플랫폼과 경로 깊이에 따라 알아서 정해집니다:
전환
iOS
depth ≥ 2
Android
depth ≥ 2
웹 · 루트
드래그로 돌아가는 전환
stack
✓
오른쪽에서 밀려 들어옵니다.
bottomUp
아래에서 올라옵니다.
드래그가 없는 전환
scaleOut
✓
살짝 커지며 자리를 잡습니다.
fade
두 페이지가 겹쳐지며 바뀝니다.
none
✓
애니메이션 없이 바로 바뀝니다.
✓여기서 기본값직접 적을 때만
iOS와 Android 열은 깊이 2 이상의 경로입니다. 마지막 열은 깊이와 상관없는 웹, 그리고 깊이 1 이하인 모든 플랫폼입니다.

뒤로 가기 제스처

특별한 이유가 없다면 gesture는 플랫폼 기본값에 맡기세요. iOS는 루트 아래 페이지에서 켜고, Android와 웹은 끕니다. 각 플랫폼 사용자가 이미 기대하는 동작입니다.
값이 정해지는 순서
  1. 직접 적은 값이 우선입니다. layout 체인 어디에든 gesture를 적었다면 그 값을 그대로 씁니다.
  2. 애니메이션이 없으면 드래그도 없습니다. 적지 않았고 transition이 none이면 false입니다. 드래그로 움직일 애니메이션이 없기 때문입니다.
  3. 그 밖에는 플랫폼이 정합니다. iOS에서 경로 깊이가 2 이상이면 true, 나머지는 모두 false입니다.
두 가지 드래그
stack
  • 페이지 어디서든 오른쪽으로 끕니다.
  • 화면 너비의 3분의 1을 넘기면 돌아갑니다. 덜 끌었어도 빠르게 튕기면 인정됩니다.
  • 터치가 드래그로 판정된 뒤에만 키보드를 내립니다.
bottomUp
  • 화면 위쪽에서 시작해 아래로 끌어내립니다.
  • 화면 너비의 절반보다 더 내리면 닫히고, 덜 내리면 제자리로 돌아옵니다.
  • 드래그가 시작되자마자 키보드를 내립니다.
  • 움직이기 전에 의도부터 봅니다. stack의 터치는 8px을 움직일 때까지 보류되고, 그다음 확실히 더 많이 움직인 축(1.25배)에 따라 드래그나 스크롤로 고정됩니다.
  • 평범한 스크롤은 방해받지 않습니다. 보류 중에는 아무것도 닫히지 않고 키보드도 그대로 있습니다.
fade, scaleOut, none 페이지에서는 gesture: true가 아무 일도 하지 않습니다. 이 전환들은 드래그 핸들러를 아예 붙이지 않습니다.

프레임 설정

.config() 객체 하나가 페이지 프레임 전체를 정합니다. 애니메이션, 제스처, 비워 둘 공간, 캐시까지입니다. layout에 적으면 그 아래 모든 라우트가 물려받습니다.
transition"none" | "fade" | "bottomUp" | "stack" | "scaleOut"기본값 iOS stack · Android scaleOut · 웹과 깊이 ≤ 1은 none
이 라우트로 들어올 때 재생되는 애니메이션입니다.
gestureboolean기본값 iOS 깊이 ≥ 2면 true, 그 외 false
드래그로 뒤로 가는 동작이며, stack과 bottomUp 전환에만 붙습니다.
topInsetnumber | boolean기본값 0
위쪽 바를 위해 비워 둘 공간(px)이며, true는 48px, false와 미지정은 0입니다.
bottomInsetnumber | boolean기본값 0
아래쪽 바를 위해 비워 둘 공간(px)이며, true는 똑같이 48px입니다.
safeAreaboolean | "top" | "bottom" | { top?, bottom?, android? }기본값 iOS true · Android { android: "auto" } · 웹 false
비워 둘 기기 safe area이며, "top"이나 "bottom"은 한쪽만 비웁니다.
safeArea.android"auto" | "edge-to-edge" | "none"기본값 "auto"
Android에서 safe area를 재는 방식이며, none이면 아무것도 비우지 않습니다.
cacheboolean기본값 깊이 ≤ 1이면 true, 그 외 false
라우트에 페이지 하나를 두고, 다른 화면으로 나가도 숨은 캐시 레이어에 마운트된 채로 둡니다. 다시 보일 때까지 effect는 멈춥니다.
topSafeAreaColorstring기본값 배경색
위쪽 safe area 띠에 칠할 CSS 색입니다.
bottomSafeAreaColorstring기본값 배경색
아래쪽 safe area 띠에 칠할 CSS 색입니다.
  • 가장 가까운 설정이 이깁니다. 페이지 값이 layout 값을 덮어쓰고, safeArea 객체만 키 단위로 합쳐집니다.
  • 최상위 라우트는 움직이지 않습니다. 깊이 1 이하에서는 모든 플랫폼이 none, 제스처 없음, cache: true가 기본이라 탭이 즉시 바뀝니다.
  • 아무도 보지 않는 페이지는 effect를 돌리지 않습니다. 캐시된 페이지와 전환 없이 떠난 페이지는 state와 DOM을 그대로 두고, 다시 보일 때까지 effect를 멈춥니다. stack 전환 아래 페이지는 스와이프 뒤로가기를 위해 살아 있으므로, 카메라·폴링·키 입력은 akanjs/webkit의 usePageFocusEffect로 묶으세요. usePageActivity()는 페이지가 current·prev·pending·hidden 중 어디에 있는지 알려 줍니다. 페이지 안 에이전트에는 현재 페이지의 도구와 state만 보입니다.
  • 히스토리 항목마다 페이지가 따로 있습니다. 지금 라우트로 push하면 새 페이지가 기존 페이지 위에 마운트되고, 기존 페이지는 state를 그대로 가진 채 아래에서 기다립니다. 같은 라우트 안의 replace는 페이지를 그 자리에서 갱신합니다. 스와이프 뒤로가기로 드러나는 페이지 아래로 세 항목을 더 숨긴 채 마운트해 두고, 더 오래된 항목은 해제합니다. 해제된 항목은 뒤로 갈 때 다시 마운트되고 스크롤은 복원됩니다. cache 라우트는 세션 내내 페이지 하나로 유지됩니다.
  • 스택은 리로드 뒤에도 남습니다. 리로드하거나 WebView의 콘텐츠 프로세스가 죽어 다시 로드되면 현재 페이지와 그 아래 페이지가 돌아옵니다. 나머지 스택은 뒤로 가서 닿을 때까지 기다리고, 뒤로가기는 전과 같이 스택을 따라갑니다. 앱이 백그라운드에 있는 동안에는 현재 페이지 아래 페이지도 멈춥니다.
  • Android에서 뒤로가기는 갈 곳이 있을 때만 페이지의 것입니다. 아래에 아무것도 없는 인덱스에서는 시스템이 뒤로가기를 가져가 홈으로 가는 자체 애니메이션을 보여 주고, 앱은 종료되지 않고 살아 있습니다. Android 14 이상에서는 뒤로가기 스와이프를 하는 동안 페이지가 손가락을 따라 움직인 뒤 확정됩니다. 두 플랫폼 모두 시스템 메모리가 부족해지면 숨은 페이지를 해제하고, 다시 방문할 때 마운트합니다.
CSS 변수로 쓰기
계산된 값은 CSS 커스텀 속성으로 공개됩니다. 그래서 컴포넌트는 JavaScript 없이도 페이지와 같은 공간을 비울 수 있습니다:
변수설명
--akan-top-safe-area
페이지가 비워 둔 상단 safe area(px)입니다.
--akan-bottom-safe-area
페이지가 비워 둔 하단 safe area(px)입니다.
--akan-top-inset
계산된 topInset 값(px)입니다.
--akan-bottom-inset
계산된 bottomInset 값(px)입니다.
--akan-page-padding-top
상단 safe area와 상단 inset을 더한, 페이지 본문에 필요한 위쪽 여백입니다.
--akan-page-padding-bottom
하단 safe area와 하단 inset을 더한, 페이지 본문에 필요한 아래쪽 여백입니다.
Tailwind에서 바로 읽을 수 있습니다. h-(--akan-bottom-inset)은 바의 높이를 페이지가 비워 둔 공간과 정확히 맞춥니다.

키보드 따라 움직이기

채팅 입력창, 댓글 입력창, 상담 입력창처럼 아래에 고정된 것은 소프트웨어 키보드를 따라 움직여야 합니다. 그런데 키보드 정보를 알려 주는 방식은 플랫폼마다 다릅니다.
  1. 바가 들어갈 공간을 비웁니다. .config()에 bottomInset을 적습니다.
  2. 입력창을 감쌉니다. keyboardSticky를 붙인 Layout.BottomInset으로 감싸면 Akan이 keyboard accessory layer로 옮겨 줍니다.
  3. 메시지 위치를 지킵니다. contentAnchor="bottom"을 붙입니다. 다음 절에서 다룹니다.
세 단계를 모두 적용한 채팅 페이지입니다:
apps/myapp/page/chat/_index.tsx
  • 적어 둔 높이가 우선입니다. .config()에 bottomInset이 있으면 BottomInset은 그 높이를 쓰고, 없으면 자기 내용의 높이를 잽니다.
  • 바 높이를 예약한 공간에 맞춥니다. h-(--akan-bottom-inset)을 쓰면 바와 비워 둔 공간이 어긋나지 않습니다.
키보드 높이를 얻는 곳
Akan은 세 곳 중 하나에서 높이를 얻고, 어디서 얻었는지가 offset의 정확도를 좌우합니다:
출처설명
native
네이티브 런타임의 keyboard 플러그인이 키보드가 열리기 시작할 때 정확한 높이를 알려 준 경우입니다.
visualViewport
보이는 viewport가 줄어든 만큼이며, Android는 이것을 먼저 쓰고 다른 곳에선 플러그인이 답하지 않을 때 씁니다.
fallback
둘 다 높이를 알려 주지 않아 0이며, 키보드가 닫혀 있거나 잴 수 없는 경우입니다.
키보드 상태 값
모바일 앱 셸에서는 useCsr().frameLayout.keyboard가 높이와 함께 세 가지 값을 담고 있습니다:
필드설명
sticky
이 경로에 keyboardSticky 슬롯이 하나 이상 있다는 뜻이며, false면 키보드 레이어가 숨겨집니다.
frozen
페이지 전환이 진행 중이라, 전환과 부딪히지 않도록 offset을 0으로 붙잡아 둡니다.
visible
높이가 있고 frozen도 아니라는 뜻이며, 컴포넌트는 높이만이 아니라 이 값으로 분기해야 합니다.

메시지 위치 지키기

입력창을 옮기는 건 절반이고, 그 위의 메시지도 있던 자리에 남아야 합니다. contentAnchor="bottom"은 화면 크기가 바뀌는 동안 스크롤 위치의 하단 기준 거리를 지켜, 메신저처럼 자연스럽게 다시 배치합니다.
Android
WebView 프레임은 그대로 두고 Akan이 키보드 offset을 적용합니다. 그래서 입력창이 키보드 위로 튀지 않고 키보드에 붙어 움직입니다.
iOS
BottomInset이 네이티브 키보드 애니메이션을 따라가고, 메시지는 입력창과의 거리를 그대로 유지합니다.
BottomInset 안의 입력창은 store에 연결된 필드 하나를 가진 평범한 Zone입니다:
apps/myapp/lib/chatMessage/ChatMessage.Zone.tsx
  • keyboardSticky는 BottomInset을 keyboard accessory layer로 옮겨 키보드를 따라 움직이게 합니다.
  • contentAnchor="bottom"은 화면 크기가 바뀌는 동안 하단 기준 거리를 지킵니다. 값은 bottom 하나뿐이고, keyboardSticky와 함께일 때만 동작합니다.
  • 페이지는 서버 컴포넌트로 둡니다. 처음부터 맨 아래로 스크롤된 채 열려야 한다면, page나 Zone 안에 작은 클라이언트 헬퍼를 넣어 Akan 페이지 콘텐츠 컨테이너를 스크롤합니다.
자기가 들어 있는 페이지를 한 번 맨 아래로 스크롤하는 헬퍼입니다:
apps/myapp/ui/Chat/ScrollToBottomOnMount.tsx
  • 문서 전체가 아니라 위로 찾아 올라갑니다. 전환 중에는 여러 페이지가 마운트되어 있고 모두 .akan-page-content를 가집니다. document.querySelector는 다른 페이지를 집을 수 있지만, closest()는 이 페이지의 것을 찾습니다.
  • 페이지 안 아무 곳에나 넣으면 됩니다. <ScrollToBottomOnMount />는 숨은 표식 하나만 그립니다.
contentAnchor는 .config()가 아니라 BottomInset의 옵션입니다. 일반 폼은 기본 키보드 동작을 그대로 쓰고, 메신저형 화면만 그 자리에서 켭니다.

이 페이지

페이지 전환
뒤로 가기 제스처
프레임 설정
키보드 따라 움직이기
메시지 위치 지키기