ADR-0012: 로딩 표현의 적용 경계를 층별로 고정하고 판정을 URL 경로 비교로 결정한다
상태
Accepted
날짜
2026-08-10
맥락
ADR-0011이 리졸버와 베일을 기본으로 정했지만, 대기가 발생하는 층은 라우트 전환만이 아닙니다. 첫 진입, 오버레이, 무한 스크롤, 명령 실행은 각각 대기의 성격이 다릅니다.
경계를 명시하지 않으면 오적용이 발생합니다. 정적 생성된 첫 화면에 베일을 씌우거나, 모달 안에 라우트용 베일을 넣거나, 저장 버튼 하나를 눌렀는데 화면 전체가 덮이는 식입니다. 주된 독자가 AI 코딩 에이전트이므로 경계는 규격의 일부입니다.
가장 판단이 갈리는 지점은 목록 화면에서 필터를 조작할 때입니다. 필터·검색·정렬·페이지를 URL 쿼리 파라미터에 두기로 했으므로 필터 조작이 곧 라우트 전환이 됩니다. 문자 그대로 적용하면 필터를 바꿀 때마다 화면 전체가 베일로 덮입니다.
이는 오적용입니다. 사용자는 방금 자신이 조작한 컨트롤을 보고 있는데 그것이 가려지고, 체크박스를 여러 개 연달아 누르는 조작이 막힙니다.
결정
층별 정책
| 층 | 상황 | 데이터 로딩 | 대기 표현 |
|---|---|---|---|
| 정적 첫 진입 | 공개 경로 | 빌드 시점에 완성 | 없음 |
| 앱 부트스트랩 | 최초 로드 | 해당 없음 | index.html의 정적 표시 |
| 화면 전환 | 다른 화면으로 이동 | 리졸버 | 베일 |
| 화면 내 갱신 | 필터·검색·정렬·페이지 | 리졸버 재실행 | 이전 내용 유지 + 인디케이터 |
| 오버레이 | 모달·시트·팝오버 | 오버레이 내부 조회 | 크기 트랜지션. 베일 비대상 |
| 덧붙는 로딩 | 무한 스크롤, 추가 조회 | 추가 요청 | 하단 인라인 인디케이터 |
| 명령 실행 | 저장·승인·삭제 | 명령 요청 | 버튼 내 진행 표시 |
판정 기준
화면 전환과 화면 내 갱신은 URL 경로 비교로 가릅니다.
경로 세그먼트가 바뀌었는가?
예 → 화면 전환. 베일
아니오 (쿼리만 변경) → 화면 내 갱신. 인디케이터NavigationStart 시점에 현재 URL과 대상 URL의 경로를 비교해 계산하므로 사람이 판단하지 않습니다.
/assessments/1 → /assessments/2처럼 경로 파라미터만 바뀌는 이동은 화면 전환으로 처리합니다. 컴포넌트는 재사용되지만 내용이 통째로 교체되므로, 이전 평가표를 보여주며 기다리는 것보다 덮는 편이 맞습니다.
시간 정책의 위치
시간 정책은 컴포넌트가 아니라 라우터 층에 겁니다. 개별 화면이 베일을 직접 제어하는 것을 금지합니다.
에러에는 최소 유지 시간을 적용하지 않고 즉시 베일을 걷어 에러 표현으로 전환합니다. 연속 네비게이션 경합은 라우터가 이전 네비게이션을 취소하며 리졸버 구독을 해제해 처리하므로, 리졸버는 Observable을 반환합니다.
세부 규칙은 로딩 전략이 원본입니다.
근거의 성격
| 구분 | 내용 |
|---|---|
| 구조적 논거 | 층마다 "이전 상태를 유지할 수 있는가"가 다릅니다. 정적 첫 진입은 서버가 대기를 흡수했고, 오버레이는 여는 행위 자체가 피드백이며, 덧붙는 로딩과 화면 내 갱신은 기존 콘텐츠가 맥락을 유지합니다 |
| 프레임워크 동작 | 같은 라우트 설정으로 이동하면 Angular 라우터가 컴포넌트 인스턴스를 재사용하므로, 리졸버가 도는 동안 이전 데이터가 화면에 남습니다. 이전 내용 유지가 별도 구현 없이 성립합니다 |
| 작성자 판단 | 층 구분과 각 층의 정책은 작성자의 이전 프로젝트에서 정립된 것이며 본 표준이 승계합니다. 다만 앱 부트스트랩·화면 내 갱신·명령 실행 세 층은 본 표준에서 추가했습니다 |
검토한 대안
단일 정책 (모든 대기에 베일)
| 구분 | 내용 |
|---|---|
| 장점 | 규칙이 하나라 기억하기 쉽고 판정이 필요 없습니다 |
| 단점 | 정적 첫 진입에서 불필요한 깜빡임, 오버레이에서 이중 덮개, 무한 스크롤에서 기존 콘텐츠 가림, 필터 조작에서 조작 대상 가림이 발생합니다 |
| 기각 사유 | 층별 대기의 성격 차이를 무시하면 각 층에서 오답이 됩니다 |
라우트 데이터에 베일 여부를 명시
| 구분 | 내용 |
|---|---|
| 장점 | 화면마다 원하는 동작을 지정할 수 있습니다 |
| 단점 | 라우트를 추가할 때마다 판단이 들어가고, 지정을 빠뜨리면 기본값으로 조용히 동작합니다 |
| 기각 사유 | 판정을 제거하는 것이 목적인데 판정을 설정 항목으로 옮길 뿐입니다. URL 비교는 지정 없이 자동으로 결정됩니다 |
컴포넌트 재사용 여부로 판정
| 구분 | 내용 |
|---|---|
| 장점 | 프레임워크의 실제 동작과 정확히 일치합니다 |
| 단점 | NavigationStart 시점에는 대상 라우트 설정이 확정되지 않아 계산이 복잡합니다. /assessments/1 → /assessments/2가 재사용으로 분류되는데 이 경우 베일이 필요합니다 |
| 기각 사유 | 구현 비용이 높고 결과도 의도와 어긋납니다. 경로 비교가 더 단순하면서 의도에 부합합니다 |
결과
에이전트와 사람이 같은 표로 오적용을 판정할 수 있습니다. 베일과 인디케이터의 선택이 URL 비교로 자동 결정되어 화면마다 설정할 것이 없습니다.
감수하는 사항은 다음과 같습니다.
- 오버레이의 크기 트랜지션과 인라인 인디케이터는 참조 구현이 없습니다.
shared/ui에 추가하는 것이 후속 작업입니다. runGuardsAndResolvers: 'paramsOrQueryParamsChange'지정을 빠뜨리면 화면 내 갱신이 동작하지 않습니다. 기본값이 쿼리 변경을 감지하지 않기 때문이며, 조용히 실패하므로 발견이 늦습니다. 자동 강제 수단이 없어 코드 리뷰에서 확인합니다.- 필터를 URL이 아닌 컴포넌트 상태에 두면 이 규칙 전체가 적용되지 않습니다. 라우터가 관여하지 않기 때문이며, URL 상태 규칙이 로딩 전략의 전제가 됩니다.