프런트엔드 레포는 여기로! => 프런트엔드 레포
백엔드 레포는 여기로! => 백엔드 레포
🕹️ 데모 : https://softeer-awesome-orange.vercel.app/
🎬 시연영상 : https://www.youtube.com/watch?v=OfGelpl1vD8
이 프로젝트는 The new IONIQ 5 출시 기념 이벤트를 컨셉으로, 다양한 인터랙션을 통해 IONIQ 5의 기능을 친숙하고 명료하게 전달하는 이벤트 페이지입니다. 프로젝트는 IONIQ 5의 기술적 경쟁력을 강조하면서도, 딱딱한 정보를 친숙하게 제공하고자, IONIQ 5의 기능을 나타내는 인터랙션 콘텐츠를 통해 사용자가 주요 성능을 직간접적으로 체험하도록 구성되었습니다. 또한, 선착순 이벤트는 이벤트 기간 동안 지속적인 트래픽을 유도하고자, 누구나 부담없이 참여할 수 있는 카드 뒤집기 방식을 채용했습니다.
또한, 이벤트 페이지이기 때문에 더 많은 사람들이 이벤트 페이지로 유입하고, 참여할 수 있어야 하므로, SSG를 도입하여 검색 엔진 최적화(SEO)를 통해 검색 엔진에 저희 사이트가 쉽게 노출될 수 있도록 구성했으며, 모바일 사용자를 배려한 반응형 웹 및 터치 인터랙션, 시각장애인을 배려한 키보드 인터랙션 조작 및 aria-live="assertive"
를 이용한 적절한 음성 자막 피드백으로 다양한 환경의 사람들이 이벤트에 부담없이 참여할 수 있도록 구성하였습니다.
- 🎮 IONIQ 5를 상징하는 5개의 인터랙션 제공
- 주행거리 인터랙션 - 점 드래그를 통한 거리 예측
- 고속충전 인터랙션 - 다이얼 드래그 회전을 통한 충전 시간 예측
- 유니버설 아일랜드 인터랙션 - 오브젝트 2개 드래그, 오브젝트 스내핑을 통한 무선충전 기능 홍보
- V2L 인터랙션 - 길잇기 연결 퍼즐
- 리셋할 때마다 랜덤한 퍼즐 제공
- 보조금 인터랙션 - 카운트와 로티 애니메이션
- 모바일 환경 지원
- 시각장애인도 이벤트에 참여할 수 있도록 키보드 조작 지원, 인터랙션 시 자막 제공
- 오프라인 폴백 모드 지원
- 🃏 카드 뒤집기 이벤트를 통한 선착순 이벤트
- 다음 선착순 이벤트 실시간 카운트
- 느린 네트워크 환경을 위해 자연스러운 카드 로딩 애니메이션 추가
- 오프라인 폴백 모드 지원
- 🎠 스와이핑 가능한 기대평 캐러셀
@lybell-art | @darkdulgi | @blaxsior | @win-luck |
---|---|---|---|
Front-End | Front-End | Back-End | Back-End |
- 서로 각자를 이해하고 성장할 수 있는 프로세스 운영을 통해 만족스러운 결과물 만들기
- 서로의 의견을 존중하며 화목하고 기억에 남을 만한 팀 프로젝트 진행하기
- 개발 측면에서, "재사용 가능한 시스템" 구축하기
- 모르거나 막히는 것이 있으면, 즉시 상대방 부르기
- 매일 오전 10시에 데일리 스크럼을 진행하고, 매일 오후 6시 30분에 일간 회고를 진행한다.
- 백로그를 기반으로, 각자 수행할 태스크를 밝혀 기능의 충돌을 막는다.
- 크리티컬한 변경이 있을 경우, 팀원과 충분히 의논하고 같이 작업을 수행한다.
- 피처 기반으로 커밋을 수행하며,
[feat] 피처 내용
과 같이 커밋 메시지를 작성한다.- 코드 리뷰를 수행한다. 코드 리뷰가 이루어지지 않으면 머지가 불가능하다.
Front-End
주차 | @lybell-art | @darkdulgi |
---|---|---|
1주차 | 공통 커스텀 훅 및 인터랙션 인터페이스 추가, 차량 상세정보, QnA, 푸터, 각각의 인터랙션 구현 | 인트로, 헤더, 차량 기본정보 푸터 구현 |
2주차 | 기대평, 선착순 이벤트 구현 | 인터랙션 섹션 및 모달 구현 |
3주차 | 어드민 페이지 이벤트 관리 구현 | 어드민 페이지 기대평 관리 구현 |
4주차 | 시각장애인 웹 접근성 개선 및 사용성 개선 | 어드민 페이지 기대평 관리, 유저 관리 구현 |
Back-End
주차 | @win-luck | @blaxsior |
---|---|---|
1주차 | DB 설계, 배포 인프라 및 CI/CD 설정, 유저 로그인 | DB 설계, JPA Entity & Repository 구축, Authorization을 위한 JWT 토큰 관리 구현 |
2주차 | 기대평 기능 구현, 선착순 이벤트 구현 및 기능 검증, 공유 URL | 어드민 기본 기능 구현, 로그 수집 인프라 구축, 추첨 이벤트 알고리즘 구현 |
3주차 | 테스트 컨테이너 조성, 모니터링 서버 도입 | 어드민 핵심 기능 구현, 가중치 반영 추첨 이벤트 구현 |
4주차 | 로드 밸런서 도입, 버그 수정 | 이벤트 검증 로직 보강, QueryDSL 도입, 버그 수정 |
피처 기반 폴더 구조를 채용하고 있습니다. 각각의 feature 폴더 내에 컴포넌트, 훅, 상수 등이 정의되어 있는 형태로, 분류 기반 폴더 구조에 비해 유사한 기능을 가진 파일을 찾기 매우 빠르기 때문에 채용했습니다.
.
├── packages/
│ ├── common/ : 메인과 어드민 공통에서 사용됩니다.
│ │ └── src
│ ├── mainPage/ : 메인 페이지의 루트 디렉토리입니다.
│ │ ├── src/
│ │ │ ├── shared : 2번 이상 사용되는 공통 feature입니다.
│ │ │ ├── features : 1번만 사용되는 주요 feature입니다.
│ │ │ ├── App.jsx
│ │ │ ├── main-client.jsx
│ │ │ ├── main-server.jsx
│ │ │ └── mock.js
│ │ ├── public : 메인 페이지에서만 사용되는 정적 자원입니다.
│ │ └── index.html
│ └── adminPage/ : 어드민 페이지의 루트 디렉토리입니다.
│ ├── src/
│ │ ├── shared
│ │ ├── features
│ │ ├── pages : 어드민 페이지의 각 페이지를 다룹니다.
│ │ ├── App.jsx
│ │ ├── main-client.jsx
│ │ ├── main-server.jsx
│ │ └── mock.js
│ ├── public
│ └── index.html
└── public
기획 상에서는 각 이벤트가 고정된 시간 및 정책, 점수 책정 방식을 가지고 있습니다. 이러한 정보를 서버 수준에서 처리하도록 구현할 수 있겠지만, 차후 새로운 이벤트 타입, 정책 및 점수 책정 방식을 도입해야 하는 경우 문제를 해결하기 어렵습니다. 따라서, 6팀은 데이터베이스를 기획 상의 이벤트 이외에도 다양한 요구사항을 만족할 수 있는, 확장성 있는 구조로 설계할 수 있도록 노력했습니다.
- 이벤트 공통 정보는 event_metadata 로 묶고, 이벤트마다 달라지는 요소는 db 수준에서 분리
현재 기획에 따르면 이벤트는 추첨 / 선착순 2종류만 존재합니다. 그러나 장기적으로 더 많은 이벤트 타입이 등장할 수 있으며, 백엔드 시스템은 새로운 이벤트 타입이 등장하더라도 큰 변경 없이 확장에 대응할 수 있어야 합니다. 모든 이벤트에 대해 공통된 로직에 대한 책임은 EventService에 맡기고, 특정 이벤트에 대한 작업은 EventFieldMapper 인터페이스로 분리하여 특정 이벤트 타입에 대한 구현체가 담당할 수 있게 구성했습니다.
이러한 설계를 통해 차후 새로운 이벤트 타입이 등장하더라도, 해당 이벤트 타입에 대한 EventFieldMapper을 구현하고 bean으로 등록함으로써 기존 시스템에서 확장할 수 있게 되었습니다.
- EventService에서 이벤트 공통 로직(event_metadata 관련) 처리
- EventFieldMapperMatcher에게 구체적 이벤트에 대한 핸들러 요청
- 이벤트 타입에 대응되는 핸들러를 이용하여 구체적 이벤트 타입 관련 작업을 처리
현재 기획에 따르면, 이벤트 유저는 추첨 이벤트에 대해 특정 액션(기대평 작성, 이벤트 참여)을 수행하면 대응되는 가산점을 받습니다. 이 기획을 구현하는 가장 간단한 방법은 코드 수준에서 특정 액션을 수행할 때 가산점을 받도록 구현하는 것입니다. 예를 들어, 댓글 작성 API를 호출하면 유저의 점수를 5점 높이도록 구현하는 방식이 있습니다.
하지만 액션과 가산점을 코드 수준에서 매핑하게 되면 둘이 지나치게 의존하여, 가산점에 관여하지 않는 액션은 수행할 수 없게 됩니다. 앞서 언급한 댓글 작성 API는 다른 이벤트에서 단순히 댓글 작성 용도로 사용하고 싶더라도, 코드 레벨에서 점수에 의존하므로 가산점 증가 없이 사용할 수 없습니다.
따라서, 미래의 확장성을 고려하면 액션과 점수 도메인을 명확하게 분리할 필요가 있습니다. 댓글은 유저와 관계 없이 작성할 수 있어야 하고, 유저 정보 없이도 URL을 공유할 수 있어야 합니다. 이렇듯 액션과 점수 도메인을 디커플링하기 위해, 저희 팀은 ScoreCalculator 객체를 도입하여 모든 액션에 대한 가산점 계산을 책임지도록 구성했습니다. 현재 당첨자를 뽑는 로직에는 누적합 알고리즘이 이용되고 있는데, 추후 다른 추첨 방식이나 더 좋은 방식이 등장할 수 있으므로 WinnerPicker 객체를 인터페이스로 지정하여 확장 가능하게 만들었습니다.
동작 방식은 다음과 같습니다.
- 관리자는 추첨 api를 호출한다.
- DrawEventService는 이벤트 추첨 관련 검증을 마친 후, DrawEventDrawMachine을 이용하여 추첨을 진행한다. 추첨은 비동기로 처리된다.
- DrawEventDrawMachine은 ScoreCalculator에게 점수 계산을 요청한다.
- ScoreCalculator은 현재 추첨 이벤트의 정책(액션 - 점수 매핑)에 따라 세부 ActionHandler에게 점수 계산을 요청하고, 결과 점수를 반환한다.
- DrawEventDrawMachine은 반환된 점수를 WinnerPicker에게 넘겨 추첨을 진행하고, 당첨자 정보를 DB에 저장한다.
- 레포지토리를 클론하세요.
git clone https://github.com/softeerbootcamp4th/Team6-AwesomeOrange-FE.git
- 의존성 모듈을 설치하세요.
npm install
-
프로젝트를 실행하세요.
- 메인 프로젝트
npm run dev
- 어드민 프로젝트
npm run dev-admin
-
만약, 빌드된 것을 보고 싶다면, 다음을 실행하세요.
- 메인 프로젝트
npm run build && npm run preview
- 어드민 프로젝트
npm run build-admin && npm run preview-admin