댓글·작성·fallback 라우트
댓글과 작성 및 client fallback 화면을 연결하고, route 입력 오류와 host HTTP 404 경계를 검증합니다.
PostComments.js (중첩 라우팅 자식)
PostDetailPage의 <Outlet /> 내부에 렌더링될 댓글 목록 컴포넌트입니다. 자식 route는 부모의 동적 파라미터를 상속하므로 comments 경로를 선언할 때 :postId를 다시 적지 않아도 useParams로 읽을 수 있습니다. 부모가 형식과 게시글 존재 여부를 먼저 검증하므로 이 컴포넌트는 정상 게시글의 댓글 상태만 담당합니다.
import React from 'react';
import { useParams } from 'react-router';
// (가상 데이터) 댓글
const mockComments = {
'1': [
{ id: 1, author: '방문자1', text: '좋은 글 잘 읽었습니다!' },
{ id: 2, author: '리액트팬', text: 'React Router가 정말 편리하네요.' }
],
'2': [
{ id: 3, author: '후크초보', text: 'useState 너무 헷갈렸는데 덕분에 이해했습니다.' }
]
};
function PostComments() {
const { postId } = useParams(); // 부모 라우트의 파라미터도 자식에서 접근 가능
const comments = postId ? (mockComments[postId] ?? []) : [];
return (
<div style={{ marginTop: '30px', borderTop: '1px solid #eee', paddingTop: '20px' }}>
<h3 style={{ color: '#555', marginBottom: '15px' }}>댓글 목록</h3>
{comments.length > 0 ? (
comments.map(comment => (
<div key={comment.id} style={{ border: '1px solid #eee', borderRadius: '5px', padding: '10px', marginBottom: '10px', backgroundColor: '#fdfdfd' }}>
<p style={{ margin: '0 0 5px 0', fontWeight: 'bold' }}>{comment.author}</p>
<p style={{ margin: 0, fontSize: '0.95em', color: '#666' }}>{comment.text}</p>
</div>
))
) : (
<p style={{ color: '#888' }}>아직 댓글이 없습니다.</p>
)}
<p style={{ fontSize: '0.9em', color: '#999', marginTop: '20px' }}>
(이 댓글들은 postId: {postId} 에 대한 가상 데이터입니다.)
</p>
</div>
);
}
export default PostComments;NewPostPage.js (useNavigate)
새 게시글을 작성하고 저장 성공 후 목록 페이지로 이동합니다. useNavigate는 폼 검증과 서버 저장을 대체하지 않으므로 실제 앱에서는 POST 요청이 성공한 시점에만 호출해야 합니다.
import React, { useState } from 'react';
import { useNavigate } from 'react-router';
function NewPostPage() {
const navigate = useNavigate();
const [title, setTitle] = useState('');
const [content, setContent] = useState('');
const [category, setCategory] = useState('React');
const handleSubmit = (e) => {
e.preventDefault();
// 실제 앱에서는 여기에서 서버에 게시글을 POST 요청하고, 성공 시 목록으로 이동합니다.
console.log('새 게시글 제출:', { title, content, category });
alert('게시글이 성공적으로 작성되었습니다!');
navigate('/posts'); // 게시글 목록 페이지로 이동
};
return (
<div style={{ maxWidth: '600px', margin: '0 auto', padding: '30px', border: '1px solid #eee', borderRadius: '8px', backgroundColor: '#fdfdfd' }}>
<h2 className="text-center" style={{ marginBottom: '30px' }}>새 게시글 작성</h2>
<form onSubmit={handleSubmit} style={{ display: 'flex', flexDirection: 'column', gap: '15px' }}>
<div>
<label htmlFor="title" style={{ display: 'block', marginBottom: '5px', fontWeight: 'bold' }}>제목:</label>
<input
type="text"
id="title"
value={title}
onChange={(e) => setTitle(e.target.value)}
required
style={{ width: '100%', padding: '10px', border: '1px solid #ccc', borderRadius: '5px', boxSizing: 'border-box' }}
/>
</div>
<div>
<label htmlFor="content" style={{ display: 'block', marginBottom: '5px', fontWeight: 'bold' }}>내용:</label>
<textarea
id="content"
value={content}
onChange={(e) => setContent(e.target.value)}
required
rows="10"
style={{ width: '100%', padding: '10px', border: '1px solid #ccc', borderRadius: '5px', boxSizing: 'border-box', resize: 'vertical' }}
></textarea>
</div>
<div>
<label htmlFor="category" style={{ display: 'block', marginBottom: '5px', fontWeight: 'bold' }}>카테고리:</label>
<select
id="category"
value={category}
onChange={(e) => setCategory(e.target.value)}
style={{ width: '100%', padding: '10px', border: '1px solid #ccc', borderRadius: '5px', boxSizing: 'border-box' }}
>
<option value="React">React</option>
<option value="JavaScript">JavaScript</option>
<option value="CSS">CSS</option>
<option value="Optimization">Optimization</option>
</select>
</div>
<button type="submit" className="button" style={{ marginTop: '20px' }}>게시글 제출</button>
</form>
</div>
);
}
export default NewPostPage;NotFoundPage.js (client fallback)
선언한 구체 route가 하나도 매칭되지 않을 때 path="*"가 표시하는 client fallback 화면입니다. 이미 앱 문서가 로드된 뒤 React가 렌더하는 UI이므로 이 컴포넌트만으로 document의 HTTP 상태가 404로 바뀌지는 않습니다.
import React from 'react';
import { Link } from 'react-router';
function NotFoundPage() {
return (
<div className="text-center" style={{ padding: '50px 20px', backgroundColor: '#fefefe', borderRadius: '8px' }}>
<h2 style={{ color: '#e74c3c', fontSize: '2.5em', marginBottom: '15px' }}>페이지를 찾을 수 없습니다</h2>
<p style={{ fontSize: '1.2em', color: '#555', marginBottom: '30px' }}>
요청한 client route가 이 앱에 없습니다.
</p>
<Link to="/" className="button secondary">홈으로 돌아가기</Link>
</div>
);
}
export default NotFoundPage;App.js (최종 라우터 설정)
REACT ROUTING · URL & RESPONSIBILITY
정상 상세의 Outlet에 comments 자식을 렌더합니다
정적 new와 동적 postId는 형제 branch이고 comments는 상세의 상대 child입니다. 부모는 ID 형식과 게시글 존재를 검사하고 정상 상세에서만 Outlet을 렌더합니다.
실제 /posts/1/comments · 부모 파라미터 상속
postId = "1" → 형식 정상 → 게시글 존재
PostDetailPage + Outlet의 PostComments
mockComments["1"] → 댓글 2개부모의 comments 링크는 /posts/1/comments를 만들고 “게시글로 돌아가기”의 to="."은 부모 /posts/1로 돌아갑니다. /posts/abc/comments와 /posts/999/comments에는 정상 상세·댓글 Outlet이 렌더되지 않습니다.
/· 홈형제 정적 route의
HomePage입니다./posts· 목록category검색값을 허용 목록으로 검증합니다./posts/new· 정적 작성정적 세그먼트라서 동적
:postId보다 구체적인 branch입니다./posts/:postId· 동적 상세형식 오류와 올바른 형식의 조회 실패를 부모가 서로 다르게 렌더합니다.
comments· 상대 child부모의 정상 상세와 함께
Outlet에 렌더되고postId를 상속합니다.*· client fallback구체 route가 없는 URL만 처리하며 document의 HTTP 상태를 설정하지 않습니다.
branch ranking
/posts/new와 /posts/:postId가 모두 형태상 맞아도 정적 branch가 더 구체적입니다. 이 충돌은 선언 순서에 기대지 않습니다.
parent guard
/posts/abc는 형식 오류, /posts/999는 조회 실패입니다. 둘 다 동적 상세 route가 소유합니다.
Outlet contract
정상 게시글에서만 부모가 Outlet을 렌더합니다. child path는 comments이고 최종 URL은 부모 경로와 결합됩니다.
모든 라우트를 설정하고 BrowserRouter로 감쌉니다. React Router는 route branch의 구체성을 계산하므로 정적 /posts/new가 동적 /posts/:postId보다 선택 우선순위가 높습니다. 따라서 이 정적·동적 충돌은 선언 순서와 무관하게 해소됩니다. 상대 child path인 comments는 부모 경로에 이어지고, 매칭된 자식 element는 PostDetailPage의 <Outlet />에 렌더됩니다.
import React from 'react';
import { BrowserRouter, Routes, Route } from 'react-router';
import Navbar from './components/Navbar';
import HomePage from './pages/HomePage';
import PostListPage from './pages/PostListPage';
import PostDetailPage from './pages/PostDetailPage';
import PostComments from './pages/PostComments';
import NewPostPage from './pages/NewPostPage';
import NotFoundPage from './pages/NotFoundPage';
function App() {
return (
<BrowserRouter>
<Navbar /> {/* 내비게이션 바는 항상 상단에 표시 */}
<div className="main-content"> {/* 모든 페이지 내용이 들어갈 컨테이너 */}
<Routes>
{/* 기본 라우트 */}
<Route path="/" element={<HomePage />} />
<Route path="/posts" element={<PostListPage />} />
<Route path="/posts/new" element={<NewPostPage />} />
{/* 동적 라우트 및 중첩 라우트 */}
<Route path="/posts/:postId" element={<PostDetailPage />}>
{/* 자식 라우트: /posts/:postId/comments */}
<Route path="comments" element={<PostComments />} />
</Route>
{/* 구체 route가 없을 때 client fallback UI */}
<Route path="*" element={<NotFoundPage />} />
</Routes>
</div>
</BrowserRouter>
);
}
export default App;URL, 오류, host 경계 검증
실습 검증은 주소창 직접 입력 -> Link 또는 useNavigate 이동 -> 뒤로/앞으로 이동 순서로 수행합니다. 첫 단계는 host가 문서를 반환하는지, 두 번째 단계는 이미 로드된 앱 안에서 어떤 branch가 선택되는지, 마지막 단계는 history가 URL과 화면을 함께 복원하는지를 각각 확인합니다.
BrowserRouter는 브라우저의 pathname과 history를 사용하지만 정적 host의 rewrite 규칙을 만들지는 않습니다. 배포 환경은 SPA가 맡는 document 탐색 요청을 index.html로 보내야 합니다. 이 범위에는 client wildcard가 표시할 미등록 app path도 포함하되, 정적 asset 경로와 API 요청은 fallback에서 제외해야 합니다.
host document fallback과 client route 경계 — 상태 추적
실제 /posts/1 직접 진입은 rewrite 없으면 host 404로 종료하고, HTML 뒤에만 router가 구체/splat view를 고른다. Link는 host document 단계를 생략하며 같은 화면과 HTTP 상태를 분리한다.
REACT ROUTING · URL & RESPONSIBILITY
client NotFound UI와 host HTTP 404는 다른 경계입니다
정적 SPA의 직접 요청은 host가 index.html을 반환해야 React Router에 도달합니다. 실행 중인 앱의 적합한 내부 Link·navigate는 Router 단계로 들어갑니다.
실제 /abcd · 두 호스트 설정의 결과
document fallback 있음
→ entry HTML 보통 200
→ React 실행 → NotFound UI
fallback 없음 → host HTTP 404
→ 앱 실행 못 함이 흐름은 본문의 정적 client-only SPA 조건입니다. asset·API 요청은 document fallback에서 제외하고 실제 상태 코드가 필요하면 host·서버 정책으로 결정합니다.
직접 진입은 host부터
주소창과 새로고침은 먼저 document 요청을 보냅니다.
앱 URL은
index.html로 fallbackfallback이 없으면 host HTTP 404에서 끝나고 React는 실행되지 않습니다.
앱 문서가 로드되면 branch 선택
React Router가 pathname을 읽어 구체 route를 고릅니다.
동적 route는 입력을 다시 검증
/posts/abc와/posts/999는 둘 다 상세 branch 안의 서로 다른 상태입니다.구체 route가 없으면 client wildcard
path="*"는 NotFound UI를 렌더하지만 HTTP 상태를 자동 설정하지 않습니다.앱 내부 이동은 Router부터
Link와useNavigate는 새 document 요청 없이 history와 화면을 갱신합니다.
동적 branch의 실패
:postId는 문자열 세그먼트를 먼저 매칭합니다. 형식 검증과 게시물 조회 실패는 상세 컴포넌트가 구분합니다.
client NotFound
wildcard 화면은 앱의 렌더 결과입니다. 정적 SPA에서 fallback 문서가 200으로 반환됐다면 UI만으로 404 응답이 되지 않습니다.
host fallback 범위
SPA의 document 탐색 경로를 fallback합니다. client wildcard가 표시할 미등록 app path도 포함하되, asset 경로와 API 요청은 index.html로 바꾸지 않습니다.
이 예의 조건
- 원도표의 전체 구조와 설명은 위 관계도와 표에서 유지합니다. 아래 인쇄 표에도 원도표의 관계·조건·라벨·설명·주석을 보존합니다.
- 두 관찰 칸의 위치는 고정입니다. 관찰 상태는 해당 순간의 값이며, 동작·이유는 그 조건에서의 판정과 변화 원인입니다. 독립 비교와 실패 분기는 별도 조건으로 읽고 모든 분기를 한 요청의 필수 실행 순서로 이어 읽지 않습니다.
- 직접 /posts/1 요청의 독립 host 설정 A(fallback 없음)와 B(fallback 있음)를 비교합니다. A의 404 요청은 종료되며 B는 별도 요청입니다. 세 번째 장면은 B의 HTML 이후 client route 선택입니다.
관계도의 보충 설명
| 구분 | 내용 |
|---|---|
| 원도표 조건·주석 | 직접 진입은 host부터: 주소창과 새로고침은 먼저 document 요청을 보냅니다. |
| 원도표 설명 | 앱 URL은 index.html로 fallback |
| 원도표 조건·주석 | 앱 URL은 index.html로 fallback: fallback이 없으면 host HTTP 404에서 끝나고 React는 실행되지 않습니다. |
| 원도표 조건·주석 | 앱 문서가 로드되면 branch 선택: React Router가 pathname을 읽어 구체 route를 고릅니다. |
| 원도표 설명 | /posts/abc와 /posts/999는 둘 다 상세 branch 안의 서로 다른 상태입니다. |
| 원도표 조건·주석 | 동적 route는 입력을 다시 검증: /posts/abc와 /posts/999는 둘 다 상세 branch 안의 서로 다른 상태입니다. |
| 원도표 설명 | path="*"는 NotFound UI를 렌더하지만 HTTP 상태를 자동으로 설정하지 않습니다. |
| 원도표 조건·주석 | 구체 route가 없으면 client wildcard: path="*"는 NotFound UI를 렌더하지만 HTTP 상태를 자동으로 설정하지 않습니다. |
| 원도표 설명 | Link와 useNavigate는 새 document 요청 없이 history와 화면을 갱신합니다. |
| 원도표 조건·주석 | 앱 내부 이동은 Router부터: Link와 useNavigate는 새 document 요청 없이 history와 화면을 갱신합니다. |
| 원도표 설명 | :postId는 문자열 세그먼트를 먼저 매칭합니다. 형식 검증과 게시물 조회 실패는 상세 컴포넌트가 구분합니다. |
| 원도표 설명 | SPA의 document 탐색 경로를 fallback합니다. client wildcard가 표시할 미등록 app path도 포함하되, asset 경로와 API 요청은 index.html로 바꾸지 않습니다. |
| 장면 | 현재 관찰값 | 설명 | 변화 이유 |
|---|---|---|---|
| 설정 A · fallback 없음 | HTTP: 404 / React: 실행 안 됨 | 직접 /posts/1을 요청하나 rewrite가 없으면 HTTP 404로 끝난다 | client의 wildcard보다 앞에 있는 경계다 |
| 설정 B · fallback HTML | HTTP: 보통 200 / router: 실행 가능 | 설정된 host는 index.html을 보내고 현재 pathname을 유지한다 | asset과 API는 앱 문서의 fallback에서 제외해야 한다 |
| 설정 B · client 선택 | UI: 구체 route/NotFound / status: 자동 변경 없음 | 구체적인 post route 또는 wildcard UI를 고르고 Link는 이 단계로 바로 온다 | client의 실패 표시와 host의 status를 분리한다 |
host document fallback과 client route 경계 — 상태 추적
실제 /posts/1 직접 진입은 rewrite 없으면 host 404로 종료하고, HTML 뒤에만 router가 구체/splat view를 고른다. Link는 host document 단계를 생략하며 같은 화면과 HTTP 상태를 분리한다.
REACT ROUTING · URL & RESPONSIBILITY
client NotFound UI와 host HTTP 404는 다른 경계입니다
정적 SPA의 직접 요청은 host가 index.html을 반환해야 React Router에 도달합니다. 실행 중인 앱의 적합한 내부 Link·navigate는 Router 단계로 들어갑니다.
실제 /abcd · 두 호스트 설정의 결과
document fallback 있음
→ entry HTML 보통 200
→ React 실행 → NotFound UI
fallback 없음 → host HTTP 404
→ 앱 실행 못 함이 흐름은 본문의 정적 client-only SPA 조건입니다. asset·API 요청은 document fallback에서 제외하고 실제 상태 코드가 필요하면 host·서버 정책으로 결정합니다.
직접 진입은 host부터
주소창과 새로고침은 먼저 document 요청을 보냅니다.
앱 URL은
index.html로 fallbackfallback이 없으면 host HTTP 404에서 끝나고 React는 실행되지 않습니다.
앱 문서가 로드되면 branch 선택
React Router가 pathname을 읽어 구체 route를 고릅니다.
동적 route는 입력을 다시 검증
/posts/abc와/posts/999는 둘 다 상세 branch 안의 서로 다른 상태입니다.구체 route가 없으면 client wildcard
path="*"는 NotFound UI를 렌더하지만 HTTP 상태를 자동 설정하지 않습니다.앱 내부 이동은 Router부터
Link와useNavigate는 새 document 요청 없이 history와 화면을 갱신합니다.
동적 branch의 실패
:postId는 문자열 세그먼트를 먼저 매칭합니다. 형식 검증과 게시물 조회 실패는 상세 컴포넌트가 구분합니다.
client NotFound
wildcard 화면은 앱의 렌더 결과입니다. 정적 SPA에서 fallback 문서가 200으로 반환됐다면 UI만으로 404 응답이 되지 않습니다.
host fallback 범위
SPA의 document 탐색 경로를 fallback합니다. client wildcard가 표시할 미등록 app path도 포함하되, asset 경로와 API 요청은 index.html로 바꾸지 않습니다.
이 예의 조건
- 원도표의 전체 구조와 설명은 위 관계도와 표에서 유지합니다. 아래 인쇄 표에도 원도표의 관계·조건·라벨·설명·주석을 보존합니다.
- 두 관찰 칸의 위치는 고정입니다. 관찰 상태는 해당 순간의 값이며, 동작·이유는 그 조건에서의 판정과 변화 원인입니다. 독립 비교와 실패 분기는 별도 조건으로 읽고 모든 분기를 한 요청의 필수 실행 순서로 이어 읽지 않습니다.
- 직접 /posts/1 요청의 독립 host 설정 A(fallback 없음)와 B(fallback 있음)를 비교합니다. A의 404 요청은 종료되며 B는 별도 요청입니다. 세 번째 장면은 B의 HTML 이후 client route 선택입니다.
관계도의 보충 설명
| 구분 | 내용 |
|---|---|
| 원도표 조건·주석 | 직접 진입은 host부터: 주소창과 새로고침은 먼저 document 요청을 보냅니다. |
| 원도표 설명 | 앱 URL은 index.html로 fallback |
| 원도표 조건·주석 | 앱 URL은 index.html로 fallback: fallback이 없으면 host HTTP 404에서 끝나고 React는 실행되지 않습니다. |
| 원도표 조건·주석 | 앱 문서가 로드되면 branch 선택: React Router가 pathname을 읽어 구체 route를 고릅니다. |
| 원도표 설명 | /posts/abc와 /posts/999는 둘 다 상세 branch 안의 서로 다른 상태입니다. |
| 원도표 조건·주석 | 동적 route는 입력을 다시 검증: /posts/abc와 /posts/999는 둘 다 상세 branch 안의 서로 다른 상태입니다. |
| 원도표 설명 | path="*"는 NotFound UI를 렌더하지만 HTTP 상태를 자동으로 설정하지 않습니다. |
| 원도표 조건·주석 | 구체 route가 없으면 client wildcard: path="*"는 NotFound UI를 렌더하지만 HTTP 상태를 자동으로 설정하지 않습니다. |
| 원도표 설명 | Link와 useNavigate는 새 document 요청 없이 history와 화면을 갱신합니다. |
| 원도표 조건·주석 | 앱 내부 이동은 Router부터: Link와 useNavigate는 새 document 요청 없이 history와 화면을 갱신합니다. |
| 원도표 설명 | :postId는 문자열 세그먼트를 먼저 매칭합니다. 형식 검증과 게시물 조회 실패는 상세 컴포넌트가 구분합니다. |
| 원도표 설명 | SPA의 document 탐색 경로를 fallback합니다. client wildcard가 표시할 미등록 app path도 포함하되, asset 경로와 API 요청은 index.html로 바꾸지 않습니다. |
| 장면 | 현재 관찰값 | 설명 | 변화 이유 |
|---|---|---|---|
| 설정 A · fallback 없음 | HTTP: 404 / React: 실행 안 됨 | 직접 /posts/1을 요청하나 rewrite가 없으면 HTTP 404로 끝난다 | client의 wildcard보다 앞에 있는 경계다 |
| 설정 B · fallback HTML | HTTP: 보통 200 / router: 실행 가능 | 설정된 host는 index.html을 보내고 현재 pathname을 유지한다 | asset과 API는 앱 문서의 fallback에서 제외해야 한다 |
| 설정 B · client 선택 | UI: 구체 route/NotFound / status: 자동 변경 없음 | 구체적인 post route 또는 wildcard UI를 고르고 Link는 이 단계로 바로 온다 | client의 실패 표시와 host의 status를 분리한다 |
패키지 확인: 이 교재의 예제 기준은 [email protected]입니다. 별도 프로젝트라면 같은 패키지를 설치하고 모든 DOM 컴포넌트와 훅을 react-router에서 가져옵니다.
정상 route와 중첩 렌더: /, /posts, /posts/new, /posts/1을 열고 /posts/1/comments에서 상세 화면 아래 댓글이 함께 렌더되는지 확인합니다. 댓글에서 부모로 돌아오면 URL이 /posts/1이 되고 <Outlet />의 자식만 사라져야 합니다.
동적 입력 오류 분리: /posts/abc는 :postId branch가 매칭된 뒤 형식 오류를 표시하고, /posts/999는 올바른 형식이지만 조회 결과가 없다는 상태를 표시해야 합니다. 두 URL 모두 top-level path="*"의 NotFound 화면으로 넘기지 않습니다.
검색값 검증: /posts?category=React는 React 게시글만 표시하고, /posts와 /posts?category=Unknown은 이 예제의 정책에 따라 전체 목록을 표시하는지 확인합니다.
작성 성공 뒤 이동: /posts/new가 동적 상세가 아니라 정적 작성 branch를 선택하는지 확인합니다. 폼 제출 예제에서는 저장 성공을 모의한 뒤 useNavigate로 /posts에 이동합니다.
client fallback과 HTTP 상태 분리: 앱 안에서 /abcd로 이동하거나, host가 /abcd를 index.html로 rewrite한 뒤 앱이 로드되면 path="*"의 NotFound UI가 렌더됩니다. 정적 SPA의 document 응답은 보통 200이며 이 UI가 HTTP 404를 자동 설정하지 않습니다. rewrite가 없으면 host가 실제 HTTP 404를 반환하고 React는 실행되지 않습니다.
history 복원: 링크 이동과 작성 후 이동을 수행한 다음 뒤로·앞으로 이동하며 pathname, 검색 문자열, 상세·댓글 조합이 함께 복원되는지 확인합니다.
이 실습의 핵심은 URL을 화면 이름에만 연결하지 않고 branch 선택, URL 입력 검증, 부모·자식 렌더 소유권, host의 첫 문서 응답까지 하나의 계약으로 다루는 것입니다. useParams, useSearchParams, useNavigate, Link, Routes, Route, Outlet은 이 계약에서 서로 다른 책임을 맡습니다.
이로써 6장 React 라우팅 기초가 모두 끝났습니다. 작성 모의와 실제 저장의 결과를 구별하기
위 NewPostPage는 콘솔 출력·성공 알림·목록 이동을 시험하는 코드입니다. mockPosts를 추가·갱신하거나 서버에 저장하지 않습니다. 제목과 내용을 입력해 제출한 뒤 목록으로 이동해도 기존 게시글 수는 다섯 개이고 새 글은 나타나지 않습니다. 알림의 문구는 모의 흐름의 일부이며 저장 증거로 사용하지 않습니다.
폼의 required는 일반 브라우저 제출에서 빈 값의 제출을 막는 보조 기능입니다. 공백뿐인 문자열의 정책, 서버 입력 검증·인가, 저장 실패와 중복 제출은 구현되어 있지 않습니다. 실제 저장 기능을 만들 때에는 그 계약을 정하고, 성공 응답 뒤 목록의 데이터 갱신과 이동을 연결해야 합니다.
완성 앱의 두 독립 흐름 점검하기
다음 조건을 원래 코드 그대로 판단해 보세요.
/posts/3/comments에서는 상세와 어떤 댓글 상태가 나오나요? 부모에서to="."링크를 누르면 무엇만 사라지나요?- 새 글을 제출하여 목록으로 돌아간 다음 새로고침하면 새 글과 기존 다섯 글은 각각 어떻게 되나요? 목록 이동이 성공했는데도 새 글이 없는 현상을 호스트 fallback 오류라고 할 수 있나요?
- host가
/abcd를 진입 HTML로200 OK응답한다고 가정합니다. 앱의 NotFound 화면과 document 응답 상태는 각각 무엇인가요?
자식 화면·저장·문서 응답의 해설
ID 3은 존재하는 게시글이지만 댓글 가상 데이터가 없어 “아직 댓글이 없습니다”를 표시합니다. 부모 route 기준의 . 링크는 /posts/3으로 돌아가 상세는 남고 Outlet의 댓글 화면만 사라집니다. 댓글이 없다는 상태와 게시글이 없다는 상태도 다릅니다.
새 글은 원래 제출 코드에서 저장하지 않았으므로 이동 뒤와 새로고침 뒤 모두 목록에 없습니다. 기존 가상 배열의 다섯 글은 다시 렌더됩니다. 저장 기능 부재와 새 문서 진입을 가능하게 하는 host fallback을 구별합니다.
마지막은 NotFound UI와 HTTP 200이 함께 성립합니다. JavaScript가 그 UI를 렌더했다고 해서 이미 받은 HTTP 응답 상태가 바뀌지 않습니다.