Overview
약 한 달간 진행한 AllClass 리라이팅 프로젝트가 드디어 완료되었다. 창업팀 해체라는 예상치 못한 변화 속에서도 시작한 프로젝트는 끝까지 마무리하고 싶었고, 이전에 세운 목표들을 성공적으로 달성할 수 있었다. 정량적 성과 분석과 함께 이번 리라이팅에서 얻은 인사이트들을 정리해본다.
예상치 못한 변화들
리라이팅을 진행하던 도중 많은 일들이 있었다. 가장 큰 변화는 9개월간 함께 창업을 도전하던 팀이 각자의 길을 찾게 되면서 자연스럽게 해체된 것이었다.
하지만 시작한 프로젝트는 끝을 보고 싶었다. 개인적으로도 기술적 성장을 위해 시작한 리라이팅이었고, 무엇보다 지난 2년간 쌓아온 경험과 학습을 완전히 정리하고 싶었다. 그래서 혼자서라도 끝까지 완료하기로 했다.
목표 달성 결과
목표 1: 렌더링 전략 최적화 ✅
리라이팅 계획에서 세웠던 첫 번째 목표를 성공적으로 달성했다:
- 랜딩 페이지(
/): SSG 적용으로 정적 생성 - 강의 목록 페이지(
/[universityName]): On-Demand ISR로 캐싱 최적화 - 강의 상세 페이지(
/[universityName]/[lectureId]): 하이브리드 렌더링으로 강의 정보는 정적 생성, 리뷰는 클라이언트 페칭

Before: 8개 정적 페이지

After: 84개 정적 페이지
기존 getServerSideProps로만 서버사이드 페칭을 하던 방식에서 On-Demand ISR과 React Query를 조합한 하이브리드 렌더링 전략으로 전환했다. generateStaticParams를 통해 동적으로 생성하던 페이지들을 정적 빌드로 바꾸면서 84개의 정적 페이지를 생성할 수 있었다.
각 페이지별로 필요한 렌더링 전략을 고민하고 클라이언트 캐싱과 서버 캐싱의 적절한 분배를 구현하는 과정에서 많은 것을 배울 수 있었다. 상세한 구현 과정은 여기서 확인할 수 있다.
목표 2: 안정적인 리라이팅 환경 구축 ✅
Cypress를 통한 E2E 테스트를 구축하고 GitHub Actions로 자동화하여 push나 PR이 감지되면 테스트가 자동으로 실행되도록 했다. 상세한 과정은 여기서 확인할 수 있다.
특히 인상 깊었던 경험: 리뷰 작성 후 revalidate가 되지 않아 리뷰가 즉시 반영되지 않는 버그가 있었는데, 내가 알지 못했던 이 버그를 테스트 코드가 찾아줬다. 테스트 코드의 중요성을 몸소 체험할 수 있었던 소중한 경험이었다.
아쉬웠던 점들
목표는 달성했지만 아쉬운 부분들도 있었다. 특히 기존 백엔드 API와의 호환성을 유지하려다 보니 몇 가지 제약사항이 발생했다.
좋아요 기능의 한계
가장 아쉬웠던 부분은 좋아요 기능이다. 필요한 건 "현재 로그인한 사용자가 해당 리뷰에 좋아요를 눌렀는지 여부"를 나타내는 isLikedByUser 같은 필드인데, 기존 API에는 이런 정보가 없어서 좋아요 상태를 정확히 체크할 수 없었다.
로컬 스토리지를 사용하는 방법도 고려했지만, 브라우저를 바꾸거나 다른 기기에서 접속할 때 동기화 문제가 생길 수 있어서 결국 구현하지 않기로 했다.
정적 생성의 확장성 이슈
현재는 모든 강의 상세 페이지를 정적 빌드하고 있지만, 페이지가 늘어날수록 빌드 타임이 증가한다는 단점이 있다.
이를 해결하기 위해 조회수가 많거나 인기가 많은 몇몇 강의만 선별해서 정적 생성하려고 했지만, 현재 그런 API가 존재하지 않아 구현하지 못했다. 작은 부분이지만 UX 개선에 도움이 될 수 있는 아쉬운 부분이다.
새롭게 생긴 관심사
테스트에 대한 관심
이번 프로젝트를 진행하면서 Cypress로 E2E 테스트를 작성해보니 생각보다 재미있었다. 테스트 코드를 작성하고 그게 통과하는 걸 보니 뭔가 성취감도 있고, 코드 변경할 때도 좀 더 안심이 되었다. 아직 테스트에 대해 모르는 게 많지만, 이런 재미가 있다는 걸 알게 되어서 조금씩 더 공부해보고 싶다는 생각이 들었다.
초기 설계의 중요성
이번 리라이팅을 통해 처음부터 잘못 설계된 것을 고치는 게 얼마나 힘든지 몸소 체험했다. 새로운 프로젝트를 하게 된다면 이런 아쉬운 점이 생기지 않도록 초기 설계 단계에서 더 면밀하게 요구사항을 분석하고, 렌더링 전략이나 데이터 페칭 방식을 더 신중하게 고민해봐야겠다는 교훈을 얻었다.
정량적 성과 분석
1️⃣ 기술 스택 현대화
| 항목 | 구버전 (Before) | 신버전 (After) | 개선사항 |
|---|---|---|---|
| Next.js | 14.2.5 (Pages Router) | 15.3.5 (App Router) | 버전 업그레이드 + App Router 적용 |
| React | 18 | 19 | React 19 안정 버전 |
| TypeScript | ❌ JavaScript only | ✅ 완전 적용 | 100% 타입 안전성 확보 |
| 라우터 | Pages Router (Legacy) | App Router (Modern) | Pages → App Router 마이그레이션 |
2️⃣ 의존성 최적화
| 지표 | 구버전 | 신버전 | 개선율 |
|---|---|---|---|
| dependency | 23개 | 6개 | 74% 감소 |
| dev dependency | 5개 | 17개 | 개발 도구 강화 |
| 총 패키지 크기 | 397MB | 485MB | TypeScript + 개발도구 추가 |
3️⃣ 코드 품질 지표
| 지표 | 구버전 | 신버전 | 개선사항 |
|---|---|---|---|
| 총 파일 수 | 45개 | 69개 | +53% (모듈화) |
| 총 코드 라인 | 6,750줄 | 5,488줄 | -18.7% |
| 평균 파일 크기 | 80줄 | 51줄 | -36% |
| TypeScript 적용률 | 0% | 100% | 완전한 타입 안전성 |
| 테스트 커버리지 | ❌ 없음 | ✅ E2E | 자동화된 품질 보증 |
| 컴포넌트당 평균 크기 | 156줄 | 101줄 | 35% 감소 |
| 100줄 이상 큰 파일 | 41개 | 17개 | 58% 감소 |
에셋 최적화
| 지표 | 구버전 | 신버전 | 개선사항 |
|---|---|---|---|
| 총 에셋 크기 | 17MB | 5.3MB | 68.8% 감소 |
| 이미지당 평균 크기 | 400KB | 189KB | 52.7% 최적화 |
4️⃣ 도메인 단위 아키텍처 재설계
Before: 기능별 분산 구조 (도메인 로직이 여러 곳에 흩어짐)
📁 리뷰 도메인 로직이 분산된 예시:
/components/common/reviews/ → 리뷰 컴포넌트들
/components/common/modal/writereview/ → 리뷰 작성 모달
/pages/[universityName]/detail/[lectureId].jsx → 리뷰 페이지
/stores/useReviewStateStore.js → 리뷰 상태관리
/hooks/useReviewForm.js → 리뷰 폼 훅
/hooks/useReviewHandlers.js → 리뷰 핸들러 훅
❌ 문제점: 하나의 기능을 수정하려면 여러 폴더를 돌아다녀야 함
After: 도메인 단위 구조
📁 리뷰 도메인 로직이 한 곳에 집중:
/domains/review/
/client/ → 리뷰 컴포넌트, 훅, 모달 등
/server/ → 서버 컴포넌트
/shared/ → 리뷰 타입, 유틸리티
/styles/ → 리뷰 관련 스타일
✅ 개선점: 리뷰 관련 모든 것이 한 폴더에 응집되어 관리 편함
/domains/auth/ → 인증 관련 모든 로직
/domains/lecture/ → 강의 관련 모든 로직
/domains/mypage/ → 마이페이지 관련 모든 로직
주요 개선사항:
- Pages Router → App Router: 새로운 라우터 시스템 적용
- 도메인별 분리: 4개 도메인으로 명확한 구조
- 계층별 분리: client/server/shared로 관심사 분리
- 배럴 익스포트: 11개→45개로 확대하여 모듈 인터페이스 체계화
결합도/응집도 개선:
- 결합도 감소: 리뷰 수정 시 6개 폴더 → 1개 도메인만 수정하면 됨
- 응집도 증가: 관련 기능들(컴포넌트+훅+타입+스타일)이 한 도메인에 집중
- 의존성 명확화: 도메인 간 경계가 명확해져 사이드 이펙트 최소화
- 코드 재사용성: 배럴 익스포트를 통한 명확한 인터페이스 제공
5️⃣ 빌드 및 번들링 성과
빌드 성능
| 버전 | 빌드 시간 | 정적 페이지 수 | .next 폴더 크기 |
|---|---|---|---|
| 구버전 | 6.66초 | 8개 | 36MB |
| 신버전 | 10.64초 | 84개 | 374MB |
신버전이 더 오래 걸리는 이유: TypeScript 컴파일 + 10.5배 더 많은 정적 페이지 생성
번들 크기 최적화
| 페이지 | 구버전 | 신버전 | 개선율 |
|---|---|---|---|
| 강의 목록 페이지 | 183 kB | 138 kB | 25% 감소 |
| 강의 상세 페이지 | 249 kB | 146 kB | 41% 감소 |
핵심 개선사항:
- 코드 스플리팅 최적화: 무거운 단일 청크(77.4 kB) → 작은 모듈 단위 분할(최대 4.72 kB)
- Tree Shaking 효과: 의존성 74% 감소로 번들 경량화
6️⃣ 성능 개선 결과

Before

After
Core Web Vitals 개선
| 메트릭 | Before | After | 개선율 |
|---|---|---|---|
| First Contentful Paint | 2.1s | 0.5s | 76% 개선 |
| Largest Contentful Paint | 2.5s | 0.7s | 72% 개선 |
| Speed Index | 4.6s | 0.9s | 80% 개선 |
| Total Blocking Time | 0ms | 20ms | 약간 증가 |
| Cumulative Layout Shift | 0 | 0 | 동일 |
Lighthouse 점수
| 카테고리 | Before | After | 개선사항 |
|---|---|---|---|
| Performance | 69 | 100 | +31점 |
| Accessibility | 94 | 96 | +2점 |
| Best Practices | 89 | 98 | +9점 |
| SEO | 100 | 92 | -8점 |
마무리
개선 전과 개선 후의 수치들을 비교해봤을 때 나름 유의미한 성과라고 생각이 들어서, 아쉬운 만큼 기분이 좋았다.
약 2년동안 진행한 프로젝트에서 얻은 소중한 경험들과 기술적인 이슈들을 포함한 모든 과정들은 앞으로의 개발에도 큰 자산이 될 것이라고 생각한다.
이번 리라이팅을 통해 많은 것을 배울 수 있었다:
- 렌더링 전략에 대해 조금 더 이해하게 되었고, 상황에 맞는 선택의 중요성을 느꼈다
- 테스트 자동화가 생각보다 재미있고 유용하다는 걸 알게 되었다
- 아키텍처 설계를 처음부터 잘못하면 나중에 고치기 얼마나 힘든지 깨달았다
- 정량적 지표로 성과를 측정해보니 개선사항이 더 명확하게 보였다
창업팀이 해체되어 혼자 마무리하게 된 프로젝트였지만, 오히려 끝까지 완주했기 때문에 더 의미 있는 경험이 되었다. 앞으로 새로운 프로젝트를 시작할 때는 이번에 얻은 교훈들을 바탕으로 더 나은 설계와 구현을 할 수 있을 것 같다.