recap

LawLens μ„œλΉ„μŠ€ μ˜€ν”ˆμ†ŒμŠ€ κΈ°μ—¬! + μŠ€ν„°λ”” 회고

yooncandooit 2026. 7. 8. 15:57
λ°˜μ‘ν˜•

πŸ§‘‍βš–οΈ 듀어가기에 μ•žμ„œ

무슨 ν”„λ‘œμ νŠΈμΈκ°€μš”?

μ†Œκ°œ


πŸ§‘‍βš–οΈ “λ²•μ œμ²˜ 곡식 DB + 자체 RAG (예규·νŒλ‘€ + LLM 컴파일 wiki)둜 λͺ¨λ“  μΈμš©μ„ κ΅μ°¨κ²€μ¦ν•΄μ„œ LLM ν™˜κ°μ„ μ°¨λ‹¨ν•˜λŠ” ν•œκ΅­ 법령·μ„Έλ¬΄ Q&A μ–΄μ‹œμŠ€ν„΄νŠΈ”
→ 단일 Next.js ν”„λ‘œμ νŠΈλ‘œ 두 개 도메인(LawLens + SalesLens)을 μš΄μ˜ν•˜λŠ” ν•œκ΅­ 법령 AI SaaS

Github

Url


ꡬ쑰 뢄석

λ¦¬λ·°ν•˜κΈ° 전에 ꡬ쑰 λ¨Όμ € 짚고 κ°€μžλ©΄!

총 10개의 도메인, 14개의 νŽ˜μ΄μ§€

❯ tree
.
β”œβ”€β”€ app
β”‚   β”œβ”€β”€ _components
β”‚   β”œβ”€β”€ account
β”‚   β”‚   β”œβ”€β”€ organization
β”‚   β”‚   β”‚   β”œβ”€β”€ [id]
β”‚   β”‚   β”‚   β”‚   └── page.tsx
β”‚   β”‚   β”‚   └── page.tsx
β”‚   β”‚   └── page.tsx
β”‚   β”œβ”€β”€ billing
β”‚   β”‚   └── return
β”‚   β”‚       └── page.tsx
β”‚   β”œβ”€β”€ chat
β”‚   β”‚   β”œβ”€β”€ [id]
β”‚   β”‚   β”‚   └── page.tsx
β”‚   β”‚   └── page.tsx
β”‚   β”œβ”€β”€ disclaimer
β”‚   β”‚   └── page.tsx
β”‚   β”œβ”€β”€ layout.tsx
β”‚   β”œβ”€β”€ login
β”‚   β”‚   β”œβ”€β”€ layout.tsx
β”‚   β”‚   └── page.tsx
β”‚   β”œβ”€β”€ page.tsx
β”‚   β”œβ”€β”€ pricing
β”‚   β”‚   └── page.tsx
β”‚   β”œβ”€β”€ privacy
β”‚   β”‚   └── page.tsx
β”‚   β”œβ”€β”€ refund
β”‚   β”‚   └── page.tsx
β”‚   β”œβ”€β”€ saleslens
β”‚   β”‚   β”œβ”€β”€ layout.tsx
β”‚   β”‚   └── page.tsx
β”‚   └── terms
β”‚       └── page.tsx

문제 상황

전체적인 νŽ˜μ΄μ§€ λ Œλ”λ§ μ†λ„λŠ” λ¬΄λ‚œν–ˆμ§€λ§Œ, μœ λ… μ‚¬μ΄λ“œλ°”μ—μ„œ λŒ€ν™”λ₯Ό ν΄λ¦­ν•˜λ©΄, μ±„νŒ… 창으둜 μ „ν™˜λ˜κΈ°κΉŒμ§€ λ―Έλ¬˜ν•œ λ”œλ ˆμ΄κ°€ μžˆμ—ˆμ–΄μš”

λ”°λΌμ„œ λΆ„λŸ‰μ΄ λ„ˆλ¬΄ λ°©λŒ€ν•œ κ΄€κ³„λ‘œ, "μ±„νŒ… λΆ€λΆ„μ˜ νŽ˜μ΄μ§€ μ „ν™˜ 속도가 λŠλ¦¬λ‹€"λŠ” λ¬Έμ œμ— μ§‘μ€‘ν•΄μ„œ /src/chat λΆ€λΆ„λ§Œ λ¦¬νŒ©ν† λ§μ„ μ§„ν–‰ν–ˆμ–΄μš”.

원인

LawLensλŠ” μƒˆ λŒ€ν™”λ₯Ό μ‹œμž‘ν•  λ•Œ μ•„λž˜μ™€ 같은 νλ¦„μ΄μ—μš”

  1. /chat μ—μ„œ λ©”μ‹œμ§€ μž…λ ₯
  2. μ„œλ²„μ—μ„œ AI 응닡 슀트리밍 μ‹œμž‘
  3. 응닡이 λλ‚˜λ©΄ νŽ˜μ΄μ§€κ°€ λ¦¬λ Œλ”λ§ λ˜λ©΄μ„œ(깜빑 ν›„) → URL이 /chat/uuid-1234 둜 λ°”λ€œ

흐름을 보면 μ•Œ 수 μžˆλ“―, 응닡이 λλ‚˜λŠ” μˆœκ°„ λ©”μ‹œμ§€κ°€ μœ„μ—μ„œλΆ€ν„° λ‹€μ‹œ λ‘œλ“œλ˜λ©°, κΉœλΉ‘μ΄λ©΄μ„œ 이미 λ‹€ 그렀쑌던 λ‚΄μš©μ΄ μ²˜μŒλΆ€ν„° λ‹€μ‹œ λ Œλ”λ§λ˜λŠ” κ±Έ 확인할 수 μžˆμ—ˆμ–΄μš”.

useEffect(() => {
  if (isNew && !navigatedRef.current && status === 'ready' && messages.length > 0) {
    navigatedRef.current = true;
    router.replace(`/chat/${conversationId}`);
  }
}, [isNew, status, messages.length, router, conversationId]);

router.replace() λŠ” URL을 λ°”κΎΈλŠ” ν•¨μˆ˜κ°€ μ•„λ‹Œ App Routerμ—μ„œλŠ” "이 경둜둜 λ„€λΉ„κ²Œμ΄μ…˜ν•˜λŠ” ν•¨μˆ˜"μ˜ˆμš”. λ”°λΌμ„œ μž¬νŒ¨μΉ˜κ°€ λ”°λΌμ™€μš”.

Next.js App RouterλŠ” μ„œλ²„ μ»΄ν¬λ„ŒνŠΈ RSC κΈ°λ°˜μ΄λ―€λ‘œ, router.replace()λ₯Ό μ‚¬μš©ν•˜λ©΄ μƒˆ URL에 ν•΄λ‹Ήν•˜λŠ” μ„œλ²„ μ»΄ν¬λ„ŒνŠΈλ₯Ό λ‹€μ‹œ νŒ¨μΉ˜ν•΄μš”.

즉 URL이 λ°”λ€Œμ—ˆμœΌλ―€λ‘œ μ„œλ²„μ—μ„œλŠ” 바뀐 URL의 RSCλ₯Ό λ‹€μ‹œ νŒ¨μΉ˜ν•˜κ³  ChatLayout.tsx νŒŒμΌμ— ν•΄λ‹Ήν•˜λŠ” 쿼리λ₯Ό λͺ¨λ‘ λ‹€μ‹œ μ‹€ν–‰ ν›„ 이후 μƒˆλ‘œ λ¦¬λ Œλ”λ§μ„ ν•˜κ²Œ λ˜λŠ” νλ¦„μ΄μ—ˆμ–΄μš”.

μˆ˜μ •

λ‹΅λ³€ μ΄ν›„μ—λŠ” URL만 λ°”κΎΈκ³  싢은 μ˜λ„λ₯Ό μœ μ§€ν•˜λ €λ©΄, Next.jsλ₯Ό μš°νšŒν•΄μ„œ window.history.replaceState λ₯Ό 써주면 λΌμš”.

// Before
router.replace(`/chat/${conversationId}`);

// After
window.history.replaceState(null, '', `/chat/${conversationId}`);

λ‹€λ§Œ ν•œκ°€μ§€ μ£Όμ˜ν•  점은, ‘Next.jsλ₯Ό μš°νšŒν•΄μ„œ’ URL을 λ³€κ²½ν•˜λŠ” 것이기 λ•Œλ¬Έμ— λΌμš°ν„° μƒνƒœμ— μ˜μ‘΄ν•˜λŠ” λ’€λ‘œκ°€κΈ° λ™μž‘ 등을 μˆ˜ν–‰ν–ˆμ„ λ•Œ λ‚΄λΆ€ μƒνƒœκ°€ μ˜ˆμƒκ³Ό λ‹€λ₯Ό 수 μžˆλ‹€λŠ” νŠΈλ ˆμ΄λ“œ μ˜€ν”„κ°€ μžˆμ–΄μš”.


πŸ₯­ React μŠ€ν„°λ”” 회고

10μ£Ό λ™μ•ˆ React 쀑급 μŠ€ν„°λ””λ₯Ό λ§ˆμ³€λ‹€. μ»΄ν¬λ„ŒνŠΈ 섀계뢀터 ν…ŒμŠ€νŠΈ μ „λž΅κΉŒμ§€, 늘 κΆκΈˆν–ˆμ§€λ§Œ ν˜Όμžμ„œλŠ” νŒŒλ³΄μ§€ λͺ»ν–ˆλ˜ λ¬Έμ œλ“€μ„ μŠ€ν„°λ””μ›λ“€κ³Ό ν•˜λ‚˜μ”© μ§šμ–΄κ°€λŠ” μ‹œκ°„μ΄μ—ˆλ‹€. "μ™œ μ΄λ ‡κ²Œ 써야 ν•˜λŠ”κ°€"λ₯Ό 계속 묻고, μ–΄λ–»κ²Œ ν•˜λ©΄ 더 μ‰½κ²Œ μ„€λͺ…ν•  수 μžˆμ„μ§€ κ³ λ―Όν•˜λŠ” μž¬λ―Έκ°€ 있던 μŠ€ν„°λ””μ˜€λ‹€.


컀리큘럼 ν•œλˆˆμ— 보기

주차 주제
1μ£Όμ°¨ μ»΄ν¬λ„ŒνŠΈ 섀계 원칙
2μ£Όμ°¨ μ»€μŠ€ν…€ ν›…κ³Ό μ±…μž„ 뢄리
3μ£Όμ°¨ μƒνƒœ 관리 μ „λž΅ 비ꡐ
4μ£Όμ°¨ TanStack Query 심화
5μ£Όμ°¨ λ Œλ”λ§ μ΅œμ ν™” μ‹€μ „
6μ£Όμ°¨ μ—λŸ¬ 핸듀링 & μ•ˆμ „μ„±
7μ£Όμ°¨ 폴더 ꡬ쑰 & μ•„ν‚€ν…μ²˜
8μ£Όμ°¨ ν…ŒμŠ€νŠΈ μ „λž΅
9μ£Όμ°¨ 포트폴리였 μ‚¬μ΄νŠΈ λ§Œλ“€μ–΄μ˜€κΈ° (MVP)
10μ£Όμ°¨ μŠ€ν„°λ””ν•œ λ‚΄μš©μ„ 기반으둜 μ„œλ‘œ μ½”λ“œ 리뷰해주기

5μ£Όμ°¨ λ Œλ”λ§ μ΅œμ ν™” μ‹€μ „ (λ°œν‘œ μ£Όμ°¨) 회고

λ Œλ”λ§ μ΅œμ ν™”λŠ” λ‚΄κ°€ 직접 λ°œν‘œλ₯Ό 맑은 주차이자, μ‹€μ œ 10μ£Όμ°¨ μ½”λ“œλ¦¬λ·°κΉŒμ§€ μ‚¬μš©λœ κ°œλ…μ΄μ—ˆλ‹€.
λ Œλ”λ§ μ΅œμ ν™” μ‹€μ „ κ°€μ΄λ“œ: React DevTools Profiler ν™œμš©λ²•, useMemo / useCallback / memo 적용 κΈ°μ€€, λΆˆν•„μš”ν•œ λ Œλ”λ§ μ°Ύμ•„μ„œ μ—†μ• κΈ°

μ€€λΉ„ν•˜λ©΄μ„œ κ°€μž₯ 크게 λŠλ‚€ 건 μΈ‘μ • 없이 μ΅œμ ν™”ν•˜λ©΄ μ•ˆ λœλ‹€λŠ” κ²ƒμ΄μ—ˆλ‹€. μ²˜μŒμ—λŠ” "λͺ¨λ“  곳에 useMemo와 useCallback을 뢙이면 빨라지지 μ•Šμ„κΉŒ"λΌλŠ” 생각이 μžˆμ—ˆλŠ”λ°, μ‹€μ œλ‘œ Profilerλ₯Ό 돌렀보면 병λͺ©μ΄ μ˜ˆμƒκ³Ό λ‹€λ₯Έ 곳에 μžˆλŠ” κ²½μš°κ°€ λ§Žμ•˜λ‹€.

Profiler의 Flame Graphμ—μ„œ μ…€ν”„ μ‹œκ°„μ΄ 높은 μ»΄ν¬λ„ŒνŠΈλ₯Ό μ°Ύκ³ , "μ™œ 이 μ»΄ν¬λ„ŒνŠΈκ°€ λ Œλ”λ§λλŠ”κ°€"λ₯Ό ν™•μΈν•œ 뒀에 μ΅œμ ν™” 도ꡬλ₯Ό μ μš©ν•˜λŠ” μˆœμ„œκ°€ ν•΅μ‹¬μ΄μ—ˆλ‹€. μ‹€μ œ LawLens μ½”λ“œμ—μ„œλŠ” λΆˆν•„μš”ν•œ useCallback을 μ œκ±°ν•˜λŠ” 것이 였히렀 μ„±λŠ₯ κ°œμ„ μœΌλ‘œ μ΄μ–΄μ§€λŠ” 사둀도 μžˆμ—ˆλ‹€. μ΅œμ ν™”λž€ 무언가λ₯Ό μΆ”κ°€ν•˜λŠ” 것이 μ•„λ‹ˆλΌ λΆˆν•„μš”ν•œ 것을 μ—†μ• λŠ” 것이기도 ν•˜λ‹€λŠ” κ±Έ μ‹€κ°ν–ˆλ‹€.

λ°œν‘œ μ€€λΉ„ κ³Όμ •μ—μ„œ νŒ€μ›λΆ„μ΄ 남겨주신 질문이 기얡에 λ‚¨λŠ”λ‹€.

"λͺ¨λ“  뢀뢄에 λ©”λͺ¨μ΄μ œμ΄μ…˜ vs ν•„μš”ν•œ λΆ€λΆ„λ§Œ λ©”λͺ¨μ΄μ œμ΄μ…˜ 쀑 μ–΄λ–€ 의견이 더 본인과 λ§žλŠ” 것 κ°™μ•˜λ‚˜μš”?"

λ‹Ήμ‹œμ—λŠ” "ν•„μš”ν•œ λΆ€λΆ„λ§Œ"이라고 λ‹΅ν–ˆμ§€λ§Œ, κ·Έ 기쀀이 λ­”μ§€ μŠ€μŠ€λ‘œλ„ νλ¦Ών–ˆλ‹€. λ°œν‘œλ₯Ό μ€€λΉ„ν•˜λ©΄μ„œ κ·Έ 기쀀이 μ‘°κΈˆμ”© λͺ…ν™•ν•΄μ‘Œλ‹€. λΉ„μ‹Ό 계산인지, ν•¨μˆ˜ 참쑰의 μ•ˆμ •μ„±μ΄ ν•„μš”ν•œμ§€, λΆ€λͺ¨ λ¦¬λ Œλ”λ§μ΄ μžμ‹μ—κ²Œ μ „νŒŒλ˜λŠ”μ§€

이 μ„Έ κ°€μ§€λ₯Ό ν™•μΈν•˜λŠ” 것이 μ•žμœΌλ‘œ μ΅œμ ν™”μ˜ 기쀀이 λ˜μ—ˆλ‹€


전체 회고

κ°€μž₯ 크게 μ„±μž₯ν•œ λΆ€λΆ„

"μ™œ"λ₯Ό λ¨Όμ € λ¬»λŠ” μŠ΅κ΄€μ΄ 생겼닀. 도ꡬλ₯Ό μ‚¬μš©ν•˜κΈ° 전에 μ™œ 이 도ꡬ가 ν•„μš”ν•œμ§€, λ‹€λ₯Έ μ„ νƒμ§€λŠ” 무엇인지λ₯Ό λ¨Όμ € κ³ λ―Όν•˜κ²Œ 됐닀. λ Œλ”λ§ μ΅œμ ν™” λ°œν‘œλ₯Ό μ€€λΉ„ν•˜λ©΄μ„œ 특히 κ·Έ μŠ΅κ΄€μ΄ κ°•ν•΄μ‘Œλ‹€κ³  μƒκ°ν•œλ‹€.

μ•„μ‰¬μ› λ˜ λΆ€λΆ„

μ‹œν—˜κΈ°κ°„μ—λŠ” 학ꡐ 곡뢀에 μ§‘μ€‘ν•˜λ‹€ λ³΄λ‹ˆ 4, 8주차의 λ‚΄μš©μ„ 깊이 있게 따라가지 λͺ»ν•œ 뢀뢄이 μžˆλ‹€. Tanstack Query, μ—λŸ¬ 핸듀링과 ν…ŒμŠ€νŠΈ μ „λž΅μ€ λ³„λ„λ‘œ 더 κ³΅λΆ€ν•˜κ³  싢은 주제둜 남아 μžˆλ‹€.

μŠ€ν„°λ””μ—μ„œ 배운 것

기술 μ§€μ‹λ§ŒνΌμ΄λ‚˜ ν•¨κ»˜ κ³΅λΆ€ν•˜λŠ” 방식도 λ°°μ› λ‹€. μ§ˆλ¬Έν•˜λŠ” 것을 λ‘λ €μ›Œν•˜μ§€ μ•ŠλŠ” λΆ„μœ„κΈ°, λ‹€λ₯Έ μ‚¬λžŒμ˜ μ½”λ“œμ—μ„œ λ°°μš°λŠ” κ²½ν—˜, λ°œν‘œλ₯Ό μ€€λΉ„ν•˜λ©΄μ„œ κ°œλ…μ„ λ‹€μ‹œ μ •λ¦¬ν•˜λŠ” κ³Όμ • 같은 λͺ¨λ“  것이 혼자 곡뢀할 λ•ŒλŠ” μ–»κΈ° μ–΄λ €μš΄ κ²ƒλ“€μ΄μ—ˆλ‹€.


마치며

10μ£Όκ°€ 생각보닀 λΉ λ₯΄κ²Œ μ§€λ‚˜κ°”λ‹€. λ§€ μ£Όμ°¨λ§ˆλ‹€ "이건 λ‚˜μ€‘μ— λ‹€μ‹œ 봐야겠닀"λŠ” μ£Όμ œκ°€ μŒ“μ˜€κ³ , 그게 μ§€κΈˆμ˜ ν•™μŠ΅ λ‘œλ“œλ§΅μ΄ 됐닀.

μŠ€ν„°λ””λŠ” λλ‚¬μ§€λ§Œ, 각 μ£Όμ°¨μ—μ„œ 닀룬 μ£Όμ œλ“€μ€ 계속 μ΄μ–΄μ„œ 곡뢀할 μ˜ˆμ •μ΄λ‹€. 특히 λ Œλ”λ§ μ΅œμ ν™”μ™€ ν…ŒμŠ€νŠΈ μ „λž΅μ€ μ‹€μ œ ν”„λ‘œμ νŠΈμ— 더 깊이 μ μš©ν•΄λ³΄κ³  μ‹Άλ‹€!

λ°˜μ‘ν˜•