백승현
전체 프로젝트
2026·프론트엔드 개발 (3인)

SSUPORT

모두의 특별장학금, 가장 든든한 서포터. 특별장학금 신청 과정을 디지털로 전환하여 서류 제출부터 평가까지 편리하게 관리하는 숭실대학교 특별장학금 전용 웹 서비스.

ReactTypeScriptpnpm workspaceTurborepoEmotionLefthookopenapi-typescriptGitHub ActionsMixpanelGA4

주요 기여

모노레포 세팅부터 시작해 실제 장학금 신청 기간에 열흘간 7번 배포하며 운영까지 했습니다. 초기 번들을 절반으로 줄였고, SSO 로그인과 토큰 갱신을 붙였고, 직접 만든 PDF 뷰어가 한글 파일을 못 읽길래 버리고 브라우저 기본 뷰어로 바꿨습니다.

문제

여러 앱(ssuport-apply, ssuport-grading)이 공용 디자인 시스템·타입·유틸을 공유해야 하는 구조에서 일관된 개발 환경과 도메인 경계가 필요했습니다. 오픈 이후에는 20개 페이지가 전부 초기 번들에 묶여 있는 문제, 일부 한글 PDF가 미리보기에서 표시되지 않는 문제, SSO·토큰 갱신·라우트 가드가 흩어져 있는 문제가 실제 사용자 제보와 함께 드러났습니다.

목표

학생이 특별장학금을 온라인으로 신청하고 서류를 미리보고 신청 내역을 관리하는 웹앱을 모노레포 기반으로 구축하고, 실제 신청 기간에 안정적으로 운영하는 것.

구현

pnpm workspace 모노레포에 공용 패키지 7개(analytics, api, config, constants, types, ui, utils)를 분리하고, Lefthook으로 prettier·eslint·type-check·build를 커밋/푸시 단계에서 자동 검증했습니다. FSD 레이어를 엄격 적용해 공지사항 도메인 40개 파일을 4개 레이어로 재배치. 백엔드 Swagger에서 openapi-typescript로 API 타입을 자동 생성해 수동 타입 정의를 제거했습니다. 인증은 SSO 콜백 → Access Token + HttpOnly Refresh Token 갱신(401 시 1회 재발급 후 원 요청 재시도) → 새로고침 세션 복원 → ProtectedRoute/PublicOnlyRoute 라우트 가드까지 전 구간을 연동했습니다. AppRouter를 React.lazy 동적 import로 전환하고 barrel import를 제거해 코드 분할이 실제로 적용되게 했으며, 에러 페이지만은 청크 로드 실패 시에도 보여야 하므로 lazy에서 제외했습니다. 오픈 전후 v1.0.1~v1.0.7 릴리즈 7회를 수행하며 분석 이벤트를 운영 환경에서만 발송하도록 게이트를 걸었습니다. 파일 첨부는 드래그 앤 드롭 경로를 추가하면서 검증 로직을 공용 모듈로 합쳤습니다.

어려웠던 점

1) 파일 미리보기에서 STEP 컴포넌트 리렌더링마다 PDF document 객체가 재생성되는 성능 문제, 2) pdfjs-dist로 직접 만든 뷰어가 구형 한글 PDF(CID 매핑 범위 초과·CP949 폰트명)를 파싱하지 못하는 코어 파서 한계, 3) 세션 만료 후 재로그인 시 인증은 정상인데 로그인 모달이 다시 뜨는 버그, 4) 20개 페이지가 전부 초기 번들에 포함돼 entry JS가 1.6MB에 달했던 문제.

해결 방법

useMemo 대신 useRef + WeakMap 캐싱으로 파일 객체 단위 재사용. PDF 뷰어는 버전 업그레이드로도 해결되지 않는 파서 한계임을 확인한 뒤 직접 만든 뷰어를 버리고 브라우저 기본 뷰어(iframe)로 교체해 코드 −852줄·의존성 2개를 제거했고, Safari 다운로드 문제는 백엔드에 preview/download API 분리를 헤더 이름까지 적어 요청해 해결했습니다. 재로그인 버그는 useBlocker 콜백이 렌더 시점의 accessToken을 클로저로 캡처하고 있던 stale closure가 원인임을 규명해 수정. 코드 분할로 entry JS raw 1,594kB → 805kB(−49.5%), 청크 1개 → 41개로 분리했습니다.

회고

직접 만든 것을 버리는 판단이 가장 어려웠습니다. pdfjs 뷰어에 들인 시간이 아까워 버전 업그레이드로 버티려 했지만, 라이브러리 한계를 확인했으면 라이브러리를 바꾸는 게 맞았습니다. 배포는 코드를 올리는 일이 아니라 올리기 전에 무엇을 걷어낼지 정하는 일에 가까웠고, 오픈 직후 가장 무서운 제보는 에러가 아니라 '수정 시 점수가 조용히 사라지는' 데이터 유실이었습니다. 같은 조건에서 전/후를 직접 재지 않으면 '개선했다'고 말할 근거가 없다는 것도 번들 측정으로 체감했습니다.