VanillaFront 튜토리얼 — 4. 라우터
VanillaFront 튜토리얼 — 4. 라우터
뷰가 화면 단위라면, 라우터는 그 뷰들을 URL로 연결하는 시스템입니다. 브라우저 주소창의 URL 변화, 앞/뒤로가기 버튼, 새로고침 후에도 같은 화면 유지 — 모두 라우터가 해줍니다.
VanillaFront는 SPA(Single Page Application) 라우팅의 복잡성을 최소화하는 데 집중했습니다. 이번 편에서 그 구조를 정리합니다.
왜 라우터가 필요한가
과거 MPA(Multi Page Application)에서는 HTML 파일 하나가 페이지 하나였습니다. URL이 곧 파일이었으므로 브라우저가 알아서 이전/다음 페이지를 관리해줬죠.
SPA 시대에는 이야기가 달라졌습니다:
- HTML은 시작 페이지 하나뿐 — 모든 화면 이동은 JavaScript가 담당
- 브라우저 주소창의 URL은 실제 파일 경로가 아니라 논리적 위치
- 뒤로가기 / 앞으로가기 / 북마크 / 새로고침 → 모두 개발자가 직접 처리해야 함
라우터는 이 짐을 대신 져주는 시스템입니다. URL과 뷰를 매핑해두고, URL 변화에 맞춰 뷰를 자동 교체해줍니다.
⚠️ 라우터 사용은 선택입니다. 프로젝트 성격에 따라 안 쓰는 게 나을 수도 있어요. 예를 들어 멀티탭 형식의 다중 문서 UI라면 라우터 대신 탭 내부 뷰 교체로 처리하는 게 자연스럽습니다. 라우터를 안 쓴다면 뒤로가기 버튼 클릭 시 "화면을 나가시겠습니까?" 같은 경고를 대신 붙여야 하는 점에 유의하세요.
URL 구조
VanillaFront 라우터가 다루는 URL은 이렇게 생겼습니다.
https://example.com/#main#apibutton?theme=light
└────────┬────────┘ └──┬─┘ └───┬───┘ └────┬────┘
Origin 라우터 라우터 URL 파라미터
영역 (라우터명)
- Origin — 도메인·경로. HTML 파일 위치.
- 라우터 영역(area) — 뷰가 표시될 영역 이름. main, sub, sidebar 등 개발자가 정한 이름.
- 라우터 URL (라우터명) — 뷰의 짧은 별칭. 실제 뷰 경로 /view/api/buttons/ApiButton을 apibutton 같은 짧은 이름으로 축약해 씁니다.
- 파라미터 — GET 방식으로 URL 뒤에 붙는 값. ?key=value
- State (URL에 안 보임) — 브라우저의 history 객체와 연계된 상태값. 뒤로가기해도 유지됨.
다중 라우터 영역
여러 영역을 동시에 라우팅할 수도 있습니다.
https://example.com/#main#apibutton/sub#configpage
└영역1┘└뷰1┘ └영역2┘ └뷰2┘
/로 각 영역-뷰 쌍을 구분합니다. 좌우 분할 화면이나 사이드바+본문 구조에서 유용해요.
3단계로 세팅
라우터 사용은 세 단계로 나뉩니다.
1) 라우터 경로 등록 — Va.setRouterPath
라우터명과 뷰 파일 경로를 매핑합니다. 보통 별도 router.js 파일에 정의해 앱 시작 시 로드합니다.
// router.js
import Va from './lib/va.js';
const routerPath = {
main: { path: '/view/main/Main' },
apibutton: { path: '/view/api/buttons/ApiButton' },
apicompoundbutton: { path: '/view/api/buttons/ApiCompoundButton' },
tutconcept: { path: '/view/tutorial/Concept' },
orderlist: { path: '/view/order/OrderList' }
// ...
};
Va.setRouterPath(routerPath);
의미: apibutton이라는 짧은 이름으로 /view/api/buttons/ApiButton 뷰를 부르게 됩니다.
- URL에 apibutton 입력 → /view/api/buttons/ApiButton.js 로드
- 반대로 프로그램에서 apibutton 부르면 → URL에 apibutton 표시
경로가 많다면 서버에서 조회해 setRouterPath()에 넘겨도 됩니다.
2) 라우터 영역 지정 — Va.setRouterAreaAsName
뷰가 표시될 화면 영역을 이름으로 등록합니다.
Va.setRouterAreaAsName(this.getRef('mainArea'), 'main');
이제 main이라는 이름으로 mainArea 영역을 지목할 수 있습니다.
3) 뷰 이동 — Va.setRouterUrl
라우터 이동 트리거.
Va.setRouterUrl('main', 'apibutton', params);
// └─┬──┘ └───┬────┘ └──┬──┘
// 영역명 라우터명 파라미터
이 호출로:
- main 영역에 apibutton 뷰가 표시됨
- URL이 #main#apibutton?...으로 자동 변경됨
- history에 기록되어 뒤로가기 지원
라우터 없이 뷰 교체 — setArea / setAreaUrl
라우터가 부담스럽거나 필요 없을 때는 URL을 건드리지 않고 그냥 뷰만 바꿔줄 수 있어요.
// 영역 지정 (URL 반영 안 함)
Va.setAreaAsName(this.getRef('mainArea'), 'main');
// 뷰 교체 (URL 반영 안 함, history 추가 안 됨)
Va.setAreaUrl('main', '/view/login/Login', params);
// 또는 이미 import 한 뷰라면
Va.setAreaView('main', LoginView, params);
정리:
함수URL 반영History 기록뒤로가기
| setAreaAsName / setAreaUrl | ✕ | ✕ | ✕ |
| setRouterAreaAsName / setRouterUrl | ✓ | ✓ | ✓ |
로그인 후 메인으로 진입할 때는 라우터, 모달 안 서브 뷰 교체는 그냥 setArea — 이런 식으로 조합해 씁니다.
파라미터 전달
뷰 간 파라미터는 JSON 객체로 넘깁니다.
Va.setRouterUrl('main', 'orderdetail', {
orderId: '12345',
mode: 'edit'
});
브라우저 URL은 이렇게 표시됩니다:
https://example.com/#main#orderdetail?orderId=12345&mode=edit
대상 뷰에서 파라미터 받기:
class OrderDetail extends Va.View {
mounted() {
const params = this.getParams();
console.log(params.orderId); // '12345'
console.log(params.mode); // 'edit'
this.loadOrder(params.orderId);
}
}
포인트:
- 파라미터는 URL에 노출됩니다. 민감한 값은 넣지 말 것.
- 브라우저 새로고침·북마크·공유 시에도 그대로 복원됩니다.
- 값은 모두 문자열로 들어옵니다. 숫자 계산 필요 시 Number() 변환.
상태값 관리 — setStateValue / getStateValue
URL에 노출되면 안 되지만 뒤로가기 후에도 유지되어야 하는 값을 위한 도구입니다. React의 useState와 개념적으로 비슷하지만, VanillaFront는 뷰 단위로 상태가 관리됩니다.
class Fruit extends Va.View {
onSelectFruit(btn) {
this.setStateValue('fruit', 'orange');
}
mounted() {
const fruit = this.getStateValue('fruit');
console.log(fruit); // 이전에 저장했던 값
}
}
언제 쓰나:
- 스크롤 위치, 열린 아코디언 상태, 선택된 탭 등 UI 세부 상태
- URL에 노출하기엔 지저분한 필터 조건 다수
- 뒤로가기 시 이전 화면의 세팅을 복원해야 하는 상황
브라우저 history의 state 객체와 연계되므로, URL이 바뀌어도 그 URL에 연결된 상태값이 함께 복원됩니다.
실전 흐름 — 로그인 → 메인 → 상세
전형적인 폼 흐름을 라우터로 짜본다면:
router.js
Va.setRouterPath({
login: { path: '/view/login/Login' },
main: { path: '/view/main/Main' },
orderlist: { path: '/view/order/OrderList' },
orderdetail: { path: '/view/order/OrderDetail' }
});
앱 시작 (App.js)
class App extends Va.View {
mounted() {
Va.setRouterAreaAsName(this.getRef('rootArea'), 'root');
Va.setRouterUrl('root', 'login', {});
}
config() {
return {
tagName: 'div',
ref: 'rootArea',
style: 'width:100%;height:100%'
};
}
}
로그인 성공 후
onLoginSuccess(user) {
Va.setRouterUrl('root', 'main', { userId: user.id });
}
URL: #root#main?userId=abc
메인에서 주문 목록 이동
onGoOrders() {
Va.setRouterUrl('main', 'orderlist', {});
}
URL: #root#main/main#orderlist (다중 영역 예시)
주문 클릭 → 상세
onOrderClick(order) {
Va.setRouterUrl('main', 'orderdetail', { orderId: order.id });
}
URL: #root#main/main#orderdetail?orderId=12345
뒤로가기
브라우저 뒤로가기 → history 이전 항목 → 뷰가 자동으로 원위치. 새로 코드 짤 필요 없이 그냥 지원됩니다.
잘 쓰는 팁 몇 가지
1) 라우터명은 짧고 소문자로
apibutton, orderlist, tutconcept 처럼 소문자 + 짧게. URL이 지저분해지지 않습니다.
2) 파라미터 값은 항상 문자열
const orderId = Number(this.getParams().orderId);
숫자·boolean 계산엔 명시적 변환 필요.
3) 새로고침해도 같은 화면이 뜨는지 반드시 테스트
라우터의 진짜 목적은 URL이 유일한 화면 식별자가 되는 것입니다. 사용자가 URL을 복사해서 다른 창에 붙이면 정확히 같은 화면이 떠야 합니다. 개발 중에 자주 확인하세요.
4) 라우터 vs 그냥 setArea 선택 기준
상황추천
| 페이지 이동 (사용자가 뒤로가기 기대) | 라우터 |
| 모달·다이얼로그 내부 뷰 교체 | setArea |
| 탭 전환 (탭 자체는 뒤로가기 안 됨) | setArea |
| 새로고침 시 복원 필요 | 라우터 |
| 딥링크로 공유 가능해야 함 | 라우터 |
5) 테마 전환 시 URL hash 보존 필수
CLAUDE.md에도 명시된 주의사항입니다. 테마 전환으로 페이지를 리로드할 때 window.location.hash를 새 URL에 붙여야 sub-view 라우트가 유지됩니다.
onChangeTheme(newTheme) {
window.location = `?theme=${newTheme}${window.location.hash}`;
}
hash 안 붙이면 사용자가 보고 있던 뷰가 초기 화면으로 리셋됩니다.
요약 — 라우터 5대 규칙
- 라우터 경로는 Va.setRouterPath로 한 번에 등록 — 짧은 별칭 ↔ 뷰 파일 매핑
- 영역은 Va.setRouterAreaAsName으로 이름 부여 — 뷰가 표시될 자리
- 이동은 Va.setRouterUrl(영역, 라우터명, params) — URL 반영 + history 기록
- URL 없이 교체는 setArea 계열 — 모달·탭 내부 등
- 상태는 setStateValue / getStateValue — URL에 노출 없이 뒤로가기 시 복원