최종 발표 초안 #506
Yongmin0423
started this conversation in
Discussions
최종 발표 초안
#506
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
1 안녕하세요. 1팀 발표를 맡은 유용민입니다. 1팀 발표 시작하겠습니다.용민님 초안
2
발표는 다음 순서로 진행하겠습니다.
3
(넘기기)
4
저희는 이번 심화 프로젝트 주제로 글로벌 노마드를 선택하였습니다. 이유는 정해진 기간 내에 완성도 있게 구현하기 알맞는 난이도라고 생각했기 때문입니다.
프로젝트에 사용한 주요 기술 스택입니다. 프레임워크는 Next.js, 스타일링 도구는 Tailwind CSS를 사용했으며, API 통신에는 ky와 TanStack Query를 활용했습니다.
6.
저희팀의 프로젝트 구조는 Feature Based 구조를 사용하였습니다. 기본적으로 Next.js App라우터 방식을 따르며 app폴더에는 도메인 관련 page.tsx만 넣어놓고, domain이라는 폴더에 각 도메인에서 사용할 컴포넌트나 hook등을 넣어 사용하였습니다. 공통으로 사용하는 부분은 shared 폴더 안에 저장해서 사용하였습니다.
7
넘기기
저희 팀은 화면에 보이시는 것과 같이 역할과 책임을 나누어 프로젝트를 체계적으로 진행하였습니다.
다음으로는 프로젝트 수행 절차에 대해 설명드리겠습니다.
저희는 프로젝트 시작에 앞서 킥오프 미팅을 통해 코드 컨벤션, Git 전략, 그리고 프로젝트의 전체적인 아키텍처에 대한 표준을 수립했습니다.
프로젝트 진행 하면서 협업은 실시간 소통은 디스코드로 진행하고, 나머지 개발 관련 작업은 깃허브를 통해 관리하였습니다.
저희는 업무 효율성을 높이기 위해 6일 단위의 스프린트 방식으로 프로젝트를 운영했습니다. 각 스프린트마다 구체적인 목표를 설정하고 이를 달성하기 위해 노력했습니다.
스프린트를 진행하는 동안에는 Github Projects의 칸반 보드를 적극 활용했습니다. 이를 통해 팀의 전체적인 진행 상황과 작업 현황을 시각적으로 관리하고, 태스크의 우선순위를 명확히 하여 개발을 진행했습니다.
매 스프린트가 끝날 때마다 팀 회고를 진행했습니다. 회고를 통해 잘한 점과 아쉬운 점을 공유하고, 개인이 설정했던 성장 목표를 얼마나 달성했는지 점검하며 지속적인 개선을 추구했습니다.
이번 프로젝트에서는 단순히 요구사항을 구현하는 것을 넘어, 개발 중 마주친 문제들을 팀원들과 함께 깊이 있게 고민하고 해결하는 과정을 모두 문서로 남겼습니다. 이를 통해 개개인의 문제 해결 역량을 강화하고, 팀의 귀중한 기술 자산으로 만들고자 노력했습니다. 모든 논의 및 해결 과정은 Github Discussion에 기록되어 있습니다.
또한, Storybook을 도입하여 저희가 만든 공통 컴포넌트의 UI를 체계적으로 문서화했습니다. 덕분에 팀원 누구나 각 컴포넌트의 사용법과 동작 방식을 명확하게 파악하고 일관된 UI를 유지할 수 있었습니다.
각 도메인 별 구현 내용을 설명드리도록 하겠습니다.
먼저 로그인 및 회원가입 기능입니다. 보안 강화를 위해 http-only 쿠키를 사용했으며, 회원가입 시 별도 과정 없이 자동 로그인되도록 구현했습니다. 또한, 입력 중 페이지를 이탈하더라도 세션 스토리지를 통해 작성 내용이 유지되도록 하여 사용자 편의성을 높였습니다.
메인 페이지는 기존 키워드 검색만 가능했던 구조에서, 날짜와 위치 검색까지 확장했습니다.
검색창은 서버 프리 렌더링으로 빠르게 인터랙션이 가능하고, 오토포커스나 키보드 접근성 같은 세부 UX도 함께 개선했습니다.
인기 체험 캐러셀은 서버 프리페칭 기반으로 초기 로딩 속도와 SEO 성능을 확보했고, 스크롤 위치 계산도 뷰포트 기준으로 정밀하게 처리했습니다.
액티비티 리스트와 필터는 캐러셀과 독립적인 Streaming SSR로 구성했고, URL 쿼리 기반 필터 구조로 검색 친화적인 URL 설계를 적용했습니다.
데이터 깜빡임 없이 부드럽게 전환되도록 상태 처리를 다뤘습니다.
상세 페이지는 서버 컴포넌트를 기반으로 구성하여 초기 로딩 성능을 최적화하고, 상호작용이 필요한 부분은 클라이언트 컴포넌트로 분리했습니다.
체험 상세 데이터는 ISR로 렌더링하여 사용자가 페이지에 빠르게 접근할 수 있도록 했습니다.
리뷰 데이터는 SSR 방식을 적용해, 초기 데이터를 서버에서 미리 렌더링하고 이후 추가 데이터는 TanStack Query로 비동기적으로 불러오도록 구현했습니다.
예약 가능 날짜와 지도처럼 사용자의 상호작용이 필수적인 영역은 CSR 방식으로 처리했습니다
사용자를 분리하여 예약 달력을 보여줄 수 있도록 예외 케이스를 처리하였으며, 이미지를 클릭하면 dialog를 활용하여 이미지를 크게 확인할 수 있도록 구현하였습니다.
체험 등록/수정 페이지는 정적인 콘텐츠가 거의 없고, 상호작용이 필수적이고, 이탈 방지 다이얼로그의 필요성 때문에 CSR 방식으로 렌더링 되도록 구현하였습니다.
사용자가 양식을 수정하다가 페이지를 이탈하려고 할 경우, 데이터 손실을 방지하기 위해 React Hook Form의
isDirty상태를 감지하여 커스텀 다이얼로그를 통해 재확인하도록 구현했습니다.이미지 업로드 처리 방식은 클라이언트에서
createObjectURL을 사용해 이미지를 선택하는 즉시 미리보기를 보여주고, 실제 서버 업로드는 최종 '제출' 시에만 동작하도록 하여 불필요한 서버 부하를 최소화했습니다.예약 내역 페이지는 여러 API를 동시에 호출해야 합니다. 클라이언트에서 개별적으로 API를 요청하면 응답 속도가 저하될 수 있기에, 서버에서 필요한 데이터를 미리 모두 가져와 한 번에 페이지를 생성하는 SSR 방식을 채택하여 사용자 경험을 최적화했습니다.
Lazy loading을 활용하여 SSR방식으로 서버에서 미리 받아온 데이터를 활용해 각각의 필요한 순간에 API요청을 하도록 구현하였습니다.
또한 캐시 무효화를 통해 예약 승인/거절 등의 상태 변경 후, 데이터가 즉각적으로 UI 반영될 수 있도록 하였습니다.
에러 처리는 글로벌 에러, 세그먼트 에러, API 에러, 렌더링 에러 네 가지로 유형을 나누어 체계적으로 대응했습니다.
특히 API 에러의 경우, HTTP 상태 코드에 따라 에러 메시지와 UI 피드백을 다르게 제공하여 사용자에게 일관된 경험을 주도록 처리했습니다.
넘기기
비동기 작업이 있을 경우 스켈리톤이나 로딩 스피너등을 활용해 로딩 처리를 진행하였습니다.
앱 레벨 구현 - 테스트
(사진 보여주기)
다음은 시연 영상을 보여드리도록 하겠습니다.
37.
(시연영상)
프로젝트를 진행하면서 각 팀원들의 기술 회고 및 개선 사례에 대해 설명하겠습니다.
저는 체험 등록/수정 페이지의 복잡한 폼을
useActionState로 관리하려 했습니다. 하지만 이 훅은 서버에서 페이지를 리렌더링하기 때문에, 클라이언트에서 생성한 이미지 미리보기 URL이 유효성 검사 실패 시 사라지는 문제가 있었습니다. 결국 저는useActionState를 포기하고,FormData와 일반적인 submit 함수로 전환했습니다. 이 경험을 통해 새로운 기술을 무조건 도입하기보다, 문제 상황에 가장 적합한 기술을 선택하는 것의 중요성을 깨달았습니다.서연님은 알림 기능을 구현해야 했지만, WebSocket이나 SSE를 사용할 수 없는 환경이라 실시간 반영이 어려웠습니다. 이에 10초 주기로 알림을 요청하는 폴링(Polling) 방식을 대안으로 도입했습니다. 또한, Zustand를 활용해 이전 상태와 신규 상태를 비교하여 새로운 알림이 있을 때만 'red-dot'을 표시하는 시각적 피드백을 제공했습니다.
재현님은 메인 페이지에서 개인화된 검색 경험을 제공하기 위해, ‘날짜’와 ‘위치’ 기반 검색 기능을 새롭게 도입했습니다. 그러나 위치 정보는 전체 액티비티 API에서 제공되는 반면, 날짜 정보는 각 액티비티의 상세 API에서만 확인 가능해, 사용자 검색 시 매번 N개의 상세 API를 호출해야 하는 비효율적인 구조였습니다.
이를 해결하기 위해 Cloudflare Workers와 KV Storage를 활용한 Edge-level ETL 계층을 구축하고, 간단한 액티비티 전용 백엔드를 Worker 내에 직접 구현했습니다. 이로써 기존 API의 한계를 보완하고 병목을 제거하여, API 호출 횟수를 O(N)에서 O(1)로 줄이고, 평균 응답 속도도 약 94.7% 개선할 수 있었습니다.
(사진 넘기기)
44.
마지막으로 프로젝트 달성도와 팀원들의 전체적인 회고에 대해서 말씀드리고 발표를 마무리하도록 하겠습니다.
저희는 프로젝트 시작 전 다음과 같은 목표를 세워두웠고, 해당 지표를 기준으로 얼마나 달성했는가를 내부적으로 판단하여 90%정도 달성되었다고 결론을 내렸습니다.
각 팀원들을 프로젝트의 종합적인 회고입니다.
이거 1가지씩 정해주셨으면 합니다.
이상으로 1팀의 발표를 마치겠습니다.
질문이 있으시면 편하게 해주시면 되겠습니다.
첨삭본
1. PPT 썸네일
안녕하세요. 1팀 발표를 맡은 유용민입니다. 1팀 발표 시작하겠습니다.
2. 목차 소개
발표는 다음 순서로 진행하겠습니다.
3. 프로젝트 개요 썸네일
먼저 프로젝트 개요입니다.
4. 프로젝트 주제 선정
저희 팀은 프로젝트 주제로 글로벌 노마드를 선택했습니다.
액티비티 플랫폼이라는 도메인의 매력과 함께, 제한된 기간 내에 구현 가능성과 완성도를 동시에 충족할 수 있는 적절한 난이도라고 판단했습니다.
5. 프로젝트 기술 스택
프로젝트는 Next.js App router, Typescript 기반으로 구현했습니다.
스타일링과 애니메이션은
tailwind css,motion을 사용했고,HTTP 클라이언트는
ky, 상태 관리 도구는zustand와tanstack-query를 사용했습니다.CI/CD에는 프로젝트 전역에
lefthook과git actions를 적용했습니다.6. 프로젝트 구조
프로젝트 구조는 도메인 별로 파일 응집도를 높이는 Feature Based 구조를 적용했습니다.
크게
app,domain,shared로 폴더를 구분하고,app폴더에는 next.js app router 파일,domain폴더에는 각 도메인에서 사용하는 파일,shared폴더에는 전역적으로 사용되는 파일을 배치하여 관리했습니다.7. 팀원 소개 썸네일
이어서 팀원 소개를 하겠습니다.
8. 팀원 소개
저희 팀은 팀장인 저와 김서연 팀원, 박재현 팀원, 송시은 팀원 4명으로 구성되어 있습니다.
R&R은 다음과 같이 나누었습니다.
(3초 이후 넘기기)
9. 프로젝트 수행 절차 썸네일
다음으로 프로젝트 수행 절차에 대해 설명드리겠습니다.
10. 킥오프
프로젝트 시작 전에, 3일 간 킥오프를 진행하여 스크럼, 컨벤션 등 프로젝트 계획을 수립했습니다.
11. 스프린트 플래닝
스프린트는 일주일 단위로 나누고, 각 스프린트 별로 마일스톤을 정의하여 프로젝트 성공 지표를 설정했습니다.
12. 스크럼
스크럼은 디스코드로 진행하고, 데일리 스크럼을 통해 팀원 간 진행 상황 및 블로킹 이슈를 신속하게 파악하여 해결했습니다.
13. 일정관리
일정은 Github Project의 칸반 보드를 활용해 일정과 우선순위를 관리했고,
스프린트 단위로 정의한 마일스톤을 기반으로 목표 기반의 진행 체계를 수립했습니다.
14. 회고 및 마일스톤 관리
매 스프린트가 종료 될때마다 팀 회고를 진행하여 다음 스프린트 개선 방향을 도출하고,
브랜치 통합 QC를 진행하여 진행 상황을 점검하고 목표 달성 여부를 확인했습니다.
15.
이번 프로젝트에서는 구현 중 마주하는 문제들을 팀원들과 함께 깊이 고민하고 해결하는 과정을 문서화하여
문제 해결 능력을 기르고, 팀의 자산으로 형성하기 위해 노력하였습니다.
모든 문서화 내용은 github discussion에 정리해두었습니다.
문서화의 일환으로 StoryBook을 도입하여 공통 컴포넌트 UI를 문서화를 진행하였습니다.
이를 통해 각 props 별로 컴포넌트가 어떻게 변하는지 스토리북을 통해 쉽게 파악하고 확인할 수 있었습니다.
각 도메인 별 구현 내용을 설명드리도록 하겠습니다.
로그인 회원가입은 쿠키를 http-only를 활용해 안정성을 확보하고, 회원가입 후 자동 로그인이 되게 구현하였습니다.
로그인/회원가입 중 페이지를 이탈했다 다시 돌아와도 입력 값을 세션에 저장하여 입력 값이 유지되도록 구현하였습니다.
메인 페이지는 기존 키워드 검색만 가능했던 구조에서, 날짜와 위치 검색까지 확장해 사용자 탐색 흐름을 유연하게 만들었습니다.
검색창은 서버 선 렌더링으로 빠르게 인터랙션이 가능하고, 오토포커스나 키보드 접근성 같은 세부 UX도 함께 개선했습니다.
인기 체험 캐러셀은 서버 프리페칭 기반으로 초기 로딩 속도와 SEO 성능을 확보했고, 스크롤 위치 계산도 뷰포트 기준으로 정밀하게 처리했습니다.
액티비티 리스트와 필터는 캐러셀과 독립적인 Streaming SSR로 구성했고, URL 쿼리 기반 필터 구조로 검색 친화적인 URL 설계를 적용했습니다.
데이터 깜빡임 없이 부드럽게 전환되도록 상태 처리를 다뤘습니다.
상세 페이지에서는 서버 컴포넌트 기반으로 페이지를 구성하고 클라이언트 기능을 분리하였습니다.
내용 확인
체험 상세 데이터 -> SSR이 아닌가
리뷰 데이터 (ISR) 방식으로 SSR방식으로 초기 데이터를 미리 가져오고 이후엔 TanstackQuery를 활용해 추가 데이터를 가져오도록 구현하였습니다.
예약 가능 날짜와 지도는 상호작용이 필요한 영역이기에 컴포넌트화하여 CSR 방식으로 구현하였습니다.
사용자를 분리하여 예약 달력을 보여줄 수 있도록 예외 케이스를 처리하였으며, 이미지를 클릭하면 dialog를 활용하여 이미지를 크게 확인할 수 있도록 구현하였습니다.
설명이 많이 겹치는데 빼도 되지 않을까 (시간을 줄이자)
체험 등록/수정 페이지는 정적인 콘텐츠가 거의 없고, 상호작용이 필수적이고, 이탈 방지 다이얼로그의 필요성 때문에 CSR 방식으로 렌더링 되도록 구현하였습니다.
사용자가 폼을 입력하다가 이탈을 하게 될 경우, react-hook-form의 isDirty 상태를 활용하여 폼 변경을 감지하고 커스텀 다이얼로그를 보여주도록 구현하였습니다.
이미지 업로드 처리를 미리보기는 client쪽의 createObjectURL을 활용하여 빠른 미리보기를 보여주고, 실제 서버 업로드는 폼 제출 시 한 번만 수행되도록 구현하여, 불필요한 서버 부하를 방지하였습니다.
그 이유는, 이 페이지에서 다양한 API를 호출해야 하며, 이러한 데이터를 클라이언트에서 일일이 요청하는 대신 서버에서 미리 데이터를 받아와 정적으로 생성하는 방식이 사용자 경험에 더 적합하다고 판단했기 때문입니다.
Lazy loading을 활용하여 SSR방식으로 서버에서 미리 받아온 데이터를 활용해 각각의 필요한 순간에 API요청을 하도록 구현하였습니다.
또한 캐시 무효화를 통해 예약 승인/거절 등의 상태 변경 후, 데이터가 즉각적으로 UI 반영될 수 있도록 하였습니다.
다양한 에러를 글로벌 에러, 세그먼트 에러, API 에러, 렌더링에러로 구분하고 처리하였습니다.
API에러의 경우 상태 코드별 에러에 따라 UX를 일관되게 제공해줍니다.
로딩은 그냥 보여주기라.. 처리했다고만 하고 넘어갈까요?
테스트는 스토리북이랑 엮어서 이야기해도 될 거 같지않나.
다음은 시연 영상을 보여드리도록 하겠습니다.
37.
(시연영상)
프로젝트를 진행하면서 팀원들의 기술 회고 및 개선 사례에 대해 설명하겠습니다.
마지막으로 자체평가 및 팀원들의 의견으로 마무리하려고 합니다.
Beta Was this translation helpful? Give feedback.
All reactions