-
프로젝트를 다방면으로 생각해보기프론트엔드 2026. 6. 14. 11:41
다시 시작..

프로젝트를 구현하면서 기능을 완성하는 데 집중한 나머지 다양한 관점에서 충분히 고민하지 못했다는 것을 깨달았다.
구현이 끝나면 ‘이제 다 했다’는 안도감이 들었고 그 때문에 사용성이나 예외 상황 개선 가능성을 놓치기도 했다.
이번 기회를 통해 구현에서 끝나는 것이 아니라 완성 이후에도 더 나은 방향을 고민하는 습관을 만들어가고자 한다.
예외 상황 생각해보기 🤔
- 현재 상황을 다이어 그램으로 생각해 봤다.

- 간단하다 유저가 블로그 글 생성을 할때 나가면 api를 연결해서 데이터를 유지해서 db에 insert할지 아니면 네트워크를 abosrtController로 취소할지에 대한 그림이다.
AbortController로 진행 중인 저장 요청 취소하기 또는 글 생성 중 유저 이탈 시 안전하게 요청 취소하기
- 유저가 글 생성 취소할때 연결을 취소 시키는 방법이다. 흐름은 다음과 같다.
1. 글 생성 완료 → 자동으로 DB 저장 시도 (데이터 유지)
2. 저장 중 유저가 나가면 → AbortController로 취소 신호 (AbortSignal)
3. 이미 진행 중인 요청은 최대한 마무리코드로 흐름을 보면
- 아래의 뒤로 가기 버튼이 클릭이 되면
import Link from "next/link"; import { ArrowLeft } from "lucide-react"; export default function GeneratingDraftHeader() { return ( <header className="px-8 py-6 border-b border-navy-700 shrink-0"> <div className="flex items-center gap-4"> <Link href="/write" // 단순히 /write로 이동 className="flex items-center gap-2 text-slate-400 hover:text-white transition-colors"> <ArrowLeft size={20} /> <span>뒤로 가기</span> </Link> <div className="h-6 w-px bg-slate-600" /> <h1 className="text-xl font-semibold text-white">AI 블로그 생성</h1> </div> </header> ); }- 자동으로 저장되도록 구현한 코드를 signal을 통해 신호 응답에 따라 제어를 했다.
- AbortController를 선언하고 ref를 사용해서 컴포넌트 언마운트시 클린업 함수를 통해 자동으로 취소 되도록 구현했다.
activeSaveAbortRef.current?.abort(); -> abort()함수가 요청을 취소 시키는 함수다.
// 진행 중인 저장 API 호출을 추적하는 ref const activeSaveAbortRef = useRef<AbortController | null>(null); useEffect(() => { return () => { // 컴포넌트 언마운트 시 자동 취소 activeSaveAbortRef.current?.abort(); }; }, []); // 자동 저장 시작 const controller = new AbortController(); activeSaveAbortRef.current = controller; const { postId } = await saveGeneratedArticle( article, payload.selectedTemplate, { signal: controller.signal } // ← 신호 전달 );- 다음으로 ai한테 api 요청을 하는 작업을 중단시켜야 한다.
useEffect(() => { if (!payload) { setError("작성 정보가 없습니다."); setIsGenerating(false); return; } const controller = new AbortController(); // fetch 신호 생성 const run = async () => { try { const res = await fetch(aiEndpoint(), { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload), signal: controller.signal, // ← fetch 요청에 신호 연결 }); // ... 처리 } catch (err) { if (isAbortError(err) || controller.signal.aborted) return; // AbortError는 무시 (정상적인 취소) setError(/* 실제 에러만 처리 */); } }; run(); return () => controller.abort(); // ← 언마운트 시 요청 취소 }, [payload, templateKey]);정리
사용자가 "뒤로 가기" 클릭 ↓ Next.js에서 /write 페이지로 네비게이션 ↓ GeneratingDraft 컴포넌트 언마운트 (리액트 라이프사이클) ↓ useGeneratingDraft의 cleanup 함수 실행 │ ├─ activeSaveAbortRef.current?.abort() │ └─ saveGeneratedArticle의 fetch 요청 취소 │ └─ useArticleGeneration의 cleanup 함수 실행 └─ controller.abort() └─ AI API (Gemini/OpenAI) fetch 요청 취소 ↓ 모든 진행 중인 요청 중단 ↓ 상태 업데이트 안 됨 (isAbortError로 필터링) ↓ /write 페이지 렌더링- 또한 현재 코드로만 보면 어떤 효과가 있는지 알고싶어 실제 openai 코드의 토큰 사용량을 비교해봤다. 우선 만약 abortController가 없을시에는 기존 토큰에서 600토큰 가량을 낭비하게 되지만, 이를 abortController를 사용하니 800토큰 만큼의 낭비를 줄일 수 있게 되었다.


2번째 시나리오 유저가 글쓰는 사이트를 나가거나 닫아도 유지해서 보여주기
- 유저가 시도할만한 것들을 막아보자
1. 유저가 뒤로가기 했을때 데이터 유지
2. 유저가 경로를 강제로 쳤을때의 예외처리
유저가 뒤로가기를 한다면??
- 유저가 뒤로가기를 했을때 고려해볼만한 문제는 아무런 예외처리를 토큰이 낭비하는 문제를 발견했다.
- 이를 해결해보고자 세션 스토리지에 상태를 넣어 이를 가져와 상태에 따라 유저가 생성하기 버튼을 클릭해도 값을 재생성하지 못하도록 막아보았다.
export function setGenerationStatus(status: GenerationStatus): void { if (typeof window === "undefined") return; sessionStorage.setItem(WRITE_GENERATION_STATUS_KEY, status); notifyGenerationStatusChange(); }- 우선 간단하게 세션 스토리지에 위와 같이 만들어 상태 값을 넣어서 제어를 해보고자 했다.
- 저 status의 상태는 크게 3가지로 나눴다.export type GenerationStatus = "generating" | "done" | "error";
- generating일때 뒤로가기를 눌렀을때 세션 스토리지의 상태 값을 가져와 상태에 따라 경로 리다이렉트와 버튼text를 다르게 보여수정해봤다. 왼쪽 버튼이 스토리지에 값이 없을때이고 스토리지에 generating이면 생성화면이 나오도록 수정했다.



스토리지 상태 값 유저가 경로를 강제로 리다이렉트 한다면?
- 우선 가장 먼저 떠오른 것은 Next.js의 미들웨어로 처리하는 방법이 먼저 떠올랐다.
- 현재 Next.js 에서 proxt.ts로 미들웨어 이름이 변경이 되었기 때문에 이에 맞게 수정을 했다.
- 우선 기존 세션 스토리지에 값을 넣어놨기때문에 이를 활용해보고자 했다.
🤔 여기서 문제점 미들웨어는 세션스토리지에 접근을 못한다... 그래서 이를 해결해보고자 쿠키에 직접 데이터를 넣어 해결해보고자 했다.
쿠키는 요청할때마다 서버로 데이터가 직접 가기 떄문에 쿠키를 사용했다.function guardGeneratingPage(req) { if (pathname !== "/write/generating") return; // 다른 경로는 무시 const hasEntry = req.cookies.get("write-generating-entry")?.value === "1"; if (!hasEntry) { return redirect("/write"); // ❌ 쿠키 없음 → 차단 } const res = next(); clearGeneratingEntryCookie(res); // ✅ 통과 후 쿠키 삭제 (1회용) return res; }- 그래서 위오같이 쿠키에 임시 토큰 1을 넣어 이를 통해 유저가 특정 경로로 들어오면 들어오지 못하도록 막았다.
⚡️ 토큰 제어??
버튼 누를 때 쿠키 넣고 → 첫 입장 때 proxy가 확인 후 바로 삭제
- 위 플로우를 만들면 경로를 다시 재 진입할때 쿠키에 데이터가 없어 미들웨어를 통해 경로 보호가 된다고 생각이 들었고 현재 잘 작동이 된다.아직 해결해야할 숙제
- 새로고침하면 쿠키가 삭제되는데 이를 어떻게 방지할지 좀 생각해봐야겠다
- 그리고 추가로 세션 스토리지 방식은 보안 적인 측면이나 많은 문제가 생길수 있어 db에 데이터를 저장시켜 이를 보완해보고자 한다.
결론
- 항상 한 방법으로만 생각해보지 말자. 프로젝트 개선할 사항은 정말 무궁무진하다는 것을 깨달았다.
'프론트엔드' 카테고리의 다른 글
Next.js 하이드레이션 에러 우아하게 처리하기 (0) 2026.06.22 미들웨어(프록시)로 유연하게 처리 해보기 (0) 2026.06.21 클라이언트 중복 방어의 한계와 서버 이중 방어 — isSubmitting부터 idempotency key까지 (0) 2026.06.07 AbortController 도입시도 (0) 2026.06.01 React Activity? (0) 2026.03.12