[쉽길] 프론트엔드 개발 회고
쉽길 프로젝트를 마무리하며 bfcache 이슈 해결, 렌더링 최적화 등 기술적 성과와 사용자 중심의 UI/UX 개선 과정을 회고합니다.
[쉽길] 프론트엔드 개발 회고
“기획의 의도를 기술로 실현하고, 팀과 소통하여 약속된 마감을 사수하는 프론트엔드 개발자”
개요
- 기술 스택: HTML5, CSS3, JavaScript (Vanilla), Django Template
- 핵심 목표: 서비스의 이용이 지친 현대인들에게 피로감을 더하지 않도록, 직관적이고 미니멀한 지하철 경로 안내 서비스 구현
- 프로젝트 철학: “조금 돌아가더라도 편안하게”. 복잡한 최단 거리보다 에스컬레이터 등 편의시설 중심의 경로를 안내하여 이동의 피로도를 최소화합니다.
1. UI/UX: 사용자 경험을 최우선으로 한 직관적인 인터페이스 설계
이번 프로젝트의 UI/UX 설계 핵심은 ‘지하철 길찾기’라는 본질에 집중하여 사용자에게 직관적이고 군더더기 없는 경험을 제공하는 것이었습니다. 이에 따라 제한된 정보만 제공하는 ‘One Screen, One Function’ 철학을 세웠습니다. 초기에는 디자인 경험이 부족하여 피그마(Figma) AI와 MCP(Model Context Protocol)를 활용해 프로토타입을 빠르게 제작하고, 이를 코드로 변환하는 방식을 시도했습니다. 하지만 단순히 AI가 생성한 결과물에 의존하기보다, 실제 사용성을 고려해 다음과 같은 개선 작업을 주도했습니다.
- 지하철 노선도 스타일의 경로 안내 시각화: 텍스트 위주의 경로 안내가 직관적이지 않다는 점을 개선하기 위해, 프로그레스 바를 ‘지하철 노선도 스타일’로 전면 리뉴얼했습니다. 경로상의 환승역을 동적으로 생성하여 호선별 고유 색상을 적용하고, 이동 수단(도보, 에스컬레이터 등)에 맞는 아이콘을 시각화하여 정보 전달력을 높였습니다.
- 커스텀 아이콘 시스템 구축: 무료 아이콘 라이브러리(Font Awesome)에서 핵심 기능인 ‘에스컬레이터’ 아이콘을 지원하지 않는 문제가 발생했습니다. 이를 해결하기 위해 유료 결제나 의미가 모호한 대체 아이콘을 사용하는 대신, CSS 클래스를 활용해 커스텀 이미지를 기존
<i>태그 시스템에 주입하는 방식을 고안했습니다. 이를 통해 기존 HTML 구조를 변경하지 않으면서도 디자인 일관성을 유지했습니다. - 검색 경험(UX) 개선: 역 검색 시 동일한 역 이름(예: 강남역 2호선, 신분당선)이 중복 노출되는 문제를 해결하기 위해, 리스트에는 ‘역이름 (호선)’ 형태로 구체적인 정보를 제공하되, 실제 입력창에는 ‘역이름’만 들어가도록 로직을 분리하여 사용자의 혼란을 줄였습니다. 또한, 기존의
alert창을 모달(Modal)로 교체하여 앱 전반의 디자인 톤앤매너를 통일했습니다. - CSS 레이아웃 구조 개선 (Grid vs Flex): 하단 버튼 배치 시, 내부 태그 구조(
form)로 인해Flex레이아웃이 깨지는 현상을 발견했습니다. 이를 HTML 구조 변경 없이Grid레이아웃(1fr 1fr 1fr)과display: contents속성을 활용하여 해결함으로써, 마크업 의존성을 줄이고 스타일의 견고함을 높였습니다.
2. 기술적 회고: Django와 JavaScript의 효율적인 역할 분담 및 최적화
백엔드 프레임워크인 Django와 프론트엔드 언어인 JavaScript의 강점을 결합하여 안정적이면서도 인터랙티브한 서비스를 구현했습니다.
- 하이브리드 아키텍처 (SSR + CSR): SEO와 데이터 안정성이 중요한 페이지 초기 렌더링과 세션 관리는 Django의 템플릿 엔진(SSR)을 활용하고, 검색 자동완성이나 모달 팝업과 같은 동적 상호작용은 JavaScript의 Fetch API(CSR)를 사용하는 하이브리드 방식을 채택했습니다. 이를 통해 백엔드와 프론트엔드가 명확한 역할 분담을 가지면서도, API 명세서를 기반으로 병렬 개발이 가능한 환경을 구축했습니다.
- 검색 성능 최적화 (Debouncing): 실시간 역 검색 기능을 구현하면서 매 입력마다 서버에 요청을 보내는 비효율을 방지하기 위해 ‘디바운싱(Debouncing)’ 기술을 적용했습니다. 사용자의 입력이 300ms 동안 멈췄을 때만 API를 호출하도록 최적화하여 불필요한 서버 부하를 줄이고 클라이언트 리소스를 절약했습니다.
- 브라우저 캐시 이슈 해결 (bfcache): 사용자가 뒤로 가기 버튼을 눌렀을 때, 이전 페이지의 버튼이 ‘로딩 중’ 상태로 멈춰있는 문제를 발견했습니다. 이는 브라우저의 bfcache(Back-Forward Cache) 때문임을 파악하고,
pageshow이벤트를 통해 페이지가 복원될 때 버튼의 상태를 강제로 초기화하는 로직을 추가하여 해결했습니다. - 비동기 병렬 처리를 통한 속도 개선 (
Promise.all): 초기에는 호선별 데이터를 순차적으로 호출하여 로딩이 지연되는 문제가 있었습니다. 이를 해결하기 위해Promise.all을 도입하여 호선별/역별 데이터를 병렬로 동시에 호출함으로써, 데이터 로딩 속도를 획기적으로 단축하고 사용자 대기 시간을 최소화했습니다. - PRG 패턴 및 세션 관리: 경로 안내 로직에서 중복 연산을 방지하고 세션 상태 꼬임을 막기 위해 PRG(Post-Redirect-Get) 패턴을 도입했습니다. 또한,
?new=1파라미터를 활용해 사용자가 ‘길찾기’ 진입 시 명시적으로 새로운 세션을 시작하도록 강제하여 UX 흐름의 안정성을 확보했습니다.
3. 개인적인 소감 및 회고
아쉬운 점: 기술 부채와 초기 설계의 한계
프로젝트를 진행하며 기능 구현에 집중하다 보니 발생한 구조적 문제와 타협했던 부분들이 아쉬움으로 남습니다. 이를 통해 초기 설계와 유지보수성의 중요성을 절감했습니다.
- 앱 간 의존성 문제 (Technical Debt): 역 검색 기능을 구현하면서
Station모델을journeys(경로) 앱에 정의한 상태로stations(역 정보) 앱의 뷰에서 참조하게 되었습니다. 구조적으로 ‘역 정보’는stations앱이 관리하는 것이 옳지만, 개발 속도를 위해 의존성을 남겨둔 것이 ‘기술 부채’가 되었습니다. 향후 리팩토링을 통해 모델을 올바른 위치로 이동시키고 의존성을 해결하고 싶습니다. - 커스텀 유저 모델 미적용: Django 강의에서는 프로젝트 초기에 커스텀 유저 모델을 구축할 것을 권장했지만, 소셜 로그인(Google OAuth) 도입과 일정 문제로 이를 적용하지 못했습니다. 확장성을 고려했을 때 표준을 따르지 못한 점이 아쉬우며, 다음 프로젝트에서는 초기 세팅 단계부터 확장 가능한 모델링을 적용할 계획입니다.
- 로컬과 배포 환경의 디버깅 격차: 로컬 개발 환경에서는 즉시 확인 가능했던 오류들이 배포 환경(Docker/AWS 등)에서는 확인하기 어려워 디버깅에 많은 시간을 소요했습니다. 로깅 시스템 구축이나 배포 파이프라인에서의 테스트 자동화에 대한 필요성을 크게 느꼈습니다.
배운 점: 원리를 파고드는 기술적 성장
코드를 작성하는 것을 넘어, 브라우저의 동작 원리와 프레임워크의 철학을 이해하게 되었습니다.
- 브라우저 동작 원리 (bfcache): 뒤로 가기 시 버튼이 로딩 상태로 멈춰있는 문제를 겪으며, 브라우저가 최적화를 위해 페이지 상태를 스냅샷처럼 저장하는
bfcache의 존재를 알게 되었습니다. 단순히 버그라 치부하지 않고 원인을 파고들어pageshow이벤트로 상태를 초기화하는 해결책을 찾아내며, 프론트엔드 생명주기에 대한 이해를 넓혔습니다. - AI 도구의 올바른 활용법: 초기에는 AI 도구(Cursor, Figma MCP)에 의존하여 결과물을 빠르게 얻으려 했으나, 의도와 다른 코드가 생성되거나 수정이 어려워지는 문제를 겪었습니다. 이를 통해 AI는 단순한 코드 생성기가 아니라, 명확한 기능 정의서와 프롬프트를 기반으로 내가 주도적으로 활용해야 하는 ‘도구’임을 깨달았습니다.
- Django와 JS의 역할 분담: 초기에는 모든 것을 JS로 처리하려 했으나, Django의 템플릿 엔진(SSR)과 JS(CSR)의 장점을 결합하는 하이브리드 방식을 익혔습니다. 데이터의 안정성이 필요한 부분은 Django 세션으로, 실시간 상호작용이 필요한 검색 기능은 JS fetch API로 분리하며 웹 아키텍처에 대한 시야를 넓혔습니다.
느낀 점: 사용자 중심의 사고와 협업의 가치
개발자의 편의보다 사용자의 경험을 최우선으로 두는 태도와, 다양한 배경을 가진 팀원들과의 소통 과정을 통해 협업의 즐거움을 배웠습니다.
- 개발자 편의보다 사용자 경험(UX): 무료 아이콘 라이브러리에 ‘에스컬레이터’ 아이콘이 없어 다른 아이콘으로 대체할 수 있었지만, 사용자의 직관적인 길찾기 경험을 위해 CSS 커스텀 클래스를 만들어 기어이 구현해냈습니다. “개발자가 조금 고생하면 사용자는 훨씬 편해진다”는 사실을 체감하며, UX를 최우선 가치로 두는 개발 마인드를 갖게 되었습니다.
- 다양한 관점의 융합: 경제학, 데이터 분석, 교육학 등 서로 다른 전공을 가진 팀원들과 기획하며 ‘타겟 유저’를 정의하는 시각이 다름을 느꼈습니다. 초기에는 의견 조율이 어려웠지만, 매일 회의를 통해 각자의 관점을 공유하고 API 명세서와 같은 공통의 기준을 문서화하면서, 서로의 부족한 점을 채워주는 시너지를 경험했습니다.
- 두려움을 넘어서는 실행력: Git 커밋이나 PR(Pull Request)을 날리는 것에 대한 막연한 두려움이 있었지만, “일단 해보고 안 되면 고치자”는 마음으로 부딪히며 협업 프로세스에 익숙해졌습니다. 이러한 경험은 앞으로 마주할 새로운 기술적 도전에도 주저하지 않고 뛰어들 수 있는 자신감이 되었습니다.
This post is licensed under CC BY 4.0 by the author.