| 이름 | 깃허브 | 포지션 |
|---|---|---|
| 박가현 | 대장 하마 | 리더/프론트엔드 |
| 김민기 | 도깨비 하마 | 부리더/백엔드 |
| 박재균 | 스마트 하마 | 백엔드 |
| 이규진 | 재간둥이 하마 | 백엔드 |
| 유영탁 | 막내 하마 | 프론트엔드 |
React-Player를 이용한 다양한 기능들
-
react-slick을 적용하여 만든 쇼츠기능
-
useRef를 응용한 타임스탬프 기능
socket을 이용한 실시간 알람기능
- 다양한 활동들에 대한 즉각적인 알람
유저의 성향을 정하기 위한 설문조사 기능
-
설문조사를 통한 총 8가지 성향의 하마선택 가능
-
다양한 활동을 하면 얻는 경험치를 통하여 업그레이드 가능
FrontEnd
💡 프론트엔드 개발자 2명, 백엔드 개발자 3명, 디자이너 2명 총 7명이 참여하여 기획부터 MVP 개발, 서비스 배포, 실제 사용한 고객피드백 적용까지 **풀 사이클 개발** 경험을 하였습니다.BackEnd
- 기간 : 2022-02-24 ~ 2022-04-08 (44일)
현재는 컨텐츠 제작자가 주제를 선정해 컨텐츠를 우선 제작하고 유저들은 다양한 미디어 플랫폼에서 대리만족 하고싶은 미디어를 검색하여 즐기는 형식으로 단방향으로 컨텐츠가 공급이 되고 있는데,
거기서 더 나아가 개개인들이 요청과 답변을 통해 상호적으로 대리만족을 시켜주는 서비스가 있으면 사람들이 재미있게 사용할 수 있겠다라고 생각하여 내가HAMA 서비스를 기획하게 되었습니다.
- React
- 상태관리 : Redux, Reduxjs/toolkit
- 미들웨어 : Reduxjs의 applyMiddleware를 사용
- 배포 : AWS S3, AWS Amplify
- 통신 : Axios
- 라우터 : connected-react-router
- 스타일 : styled-components (theme-provider, createGlobalStyle)
- 분석 : Google Analytics
- 라이브러리
| Library | Appliance |
|---|---|
| axios | 서버 통신 |
| redux-toolkit | 상태관리, 미들웨어 |
| connected-react-router | history 객체 관리 |
| styled-component | 컴포넌트 스타일링 |
| firebase | google analytics 사용 |
| moment | 랭킹 업데이트 시각 표시 |
| material ui | 게시판 글 정렬에 select 사용 |
| react-slick | 배너 슬라이더, 쇼츠, 이미지 상세보기에 사용 |
| react-player | 영상 관련 기능들에 해당 라이브러리의 다양한 이벤트리스너 사용 |
| stompjs | 실시간 알림에 사용 |
| sockjs | 실시간 알림에 사용 |
- 프레임워크 : Spring
- DB : Mysql, Redis
- DB기술: JPA(Spring Data Jpa, Querydsl)
- 배포 : EC2, AWS S3, CodeDeploy, GithubAction(CI/CD),Nginx,https
- 라이브러리
| Library | Appliance |
|---|---|
| ffmpeg | 동영상 인코딩 |
| stomp | 소켓 통신 |
| sentry | 오류 확인 |
Website 내가하마!
instagram 내가하마 인스타그램
FrontEnd
https://github.com/GAHYUN-P/HAMA
BackEnd
GitHub - rlaalsrl715/naegahama
공용 노션 내가하마 Notion
- react slick 라이브러리의 카로셀이 돌아갈때마다 이벤트를 받아오는 내장함수(beforeChange, afterChange),
- ref로 영상 리스트 배열의 index를 select
- html5의 엘리먼트 재생 종료시 실행되는 ended event listener와 같은 기능을 하는 react player의 onEnded 함수
위의 기능들을 조합하여,
영상 재생이 끝나면 다음 영상이 있는 카로셀로 자동으로 넘어가고 그 다음영상이 자동 재생되는 쇼츠 기능을 구현하였습니다.
추가적으로 카로셀의 맨 마지막에서 추가적으로 슬라이드를하면 이벤트를 받아오는 OnEdge 함수를 사용하여 받아온 영상 리스트가 전부 다 재생 되었을 때 옆으로 슬라이드 하면 새로 업데이트된 리스트를 받아올 수 있도록 하였습니다.
styled-components의 theme provider을 index.js에 주입하고 미디어쿼리를 app.js에 적용하여 개발면에서 보다 일관성있고 편리하게 모바일 환경 우선인 반응형 웹사이트를 구현하였습니다.
공통 비지니스 로직에서 일정 조건을 충족했을때 특정 이벤트가 일어나는 로직이 존재했습니다.
예를들어 댓글을 작성하면 게시글 주인에게 알람을 보내고 댓글 작성자에게는 업적 달성을 주는 로직이 있습니다.
즉 댓글을 작성하는 로직(댓글 작성)과 댓글 작성이 완료되고 나서의 로직(알람, 업적획득)으로 나눌 수 있습니다.
ApplicationEventPublisher를 사용하기 전에는 하나의 메소드에서 위 로직들이 하나의 메소드에서 일어나고 있었습니다. 즉 단일 책임 원칙을 위반하여 코드간에 강한 결합이 일어나 객체지향적이지 못한 문제가 발생하였습니다.
위 문제를 ApplicationEventPublisher와 EventListner를 통해 코드를 이벤트 기준으로 2개로 나누어 해결하였습니다. 즉 댓글작성 트랜잭션이 끝나면 새로운 트랜잭션을 만들어 알람과 업적획득 로직을 수행게 끔 바꾸어 강한 결합을 풀어주어 좀 더 객체지향적이고 단일 책임 원칙에 알맞은 코드로 리팩토링하였습니다.
JPA를 기본으로하여 Spring Data Jpa를 이용하여 쿼리를 작성하였는데 여러 단점을 맞이하였습니다.
- 계속 반복되는 같은 쿼리를 재사용하지 못한다는 단점
- @Query를 통해 직접 쿼리를 작성해야 한다는 단점
- 컴파일 시점에 문법 오류를 쉽게 확인할 수 없다는 점
- 동적쿼리를 작성이 어렵다는 점 등
여러가지 문제점을 직면하여 QueryDsl를 도입하여 해결하였습니다.
영상파일이 크기가 크다보니 유저가 영상이 첨부된 게시글을 업로드할 때 로딩화면에서 너무 오랫동안 아무것도 하지 못하고 기다려야 한다는 불편함이 발생하였습니다.
백엔드쪽에서 인스턴스를 더 좋은것을 쓰는 것으로 업로드 자체 속도를 조금 더 빠르게 하였으나
프로젝트 예산 상 카카오톡같은 대형서비스의 속도를 따라갈만큼 좋은 인스턴스를 쓸 수 없기에 여전히 업로드에 시간이 상당히 소요되었습니다.
프론트엔드에서는 이런 불편한 사용성 개선을 위해서,
게시글을 업로드할 때 해당 게시물이 서버쪽에 완전히 업로드 되지 않았더라도 로딩창을 띄우지 않고 요청만 완료되면 바로 다른 기능들을 사용할 수 있도록 하였습니다.
또 서버쪽 게시글 업로드 로직이 완전히 완료되면 게시글이 등록되었다는 실시간 알림을 띄워주고 해당 알림을 누르면 업로드된 게시물을 확인할 수 있도록 하였습니다.
쇼츠에 로드해오는 영상들이 많아 속도 저하가 우려되었습니다.
해당 문제를 해결하기 위해 대규모 쇼핑사이트에서 상품 미리보기용 사진으로 이용한다는 이미지 썸네일 기능을 응용하는 방법을 생각해냈습니다. 영상 업로드할때 서버쪽에서 영상파일을 상세페이지용, 쇼츠용으로 따로 인코딩하여 저장하게 하여 쇼츠에서 받아올 영상은 미리보기용으로 원본보다 압축이 더 많이되고 시간도 더 짧아진 파일을 불러오게끔 하였습니다.
서버쪽 인코딩으로 쇼츠 영상의 로딩은 체감상 굉장히 빨랐지만, 더 빠르고 안정적이며 일관적인 경험을 유저들에게 제공하기 위해 CloudFront의 CDN 기능이 포함된 aws amplify를 이용하여 배포하였습니다.
CDN으로 캐싱된 미디어 파일을 통해 사용성, 가용성, 그리고 보안까지 개선할 수 있었습니다.
-
Amazon S3 + CloudFront가 아닌 AWS amplify를 선택한 이유
- Amplify Console App을 생성하는 절차가 훨씬 간단하다.
- 프론트엔드를 쉽게 자동으로 빌드 및 배포할 수 있다.
- 근본적으로 Amplify Console은 프론트엔드 프로젝트를 배포할 때, 내부적으로 S3와 CloudFront를 이용하여 호스팅한다.
- 프론트엔드에서의 이슈로 인해 서버에 영향이 가지 않는 구조이다.
- 여러 브랜치를 띄울 수 있어서 각 브랜치에서 빌드했을때 어떻게 보이는지 테스트하기가 용이하다.
- 도메인연결이 쉽다.
위와 같은 이유로 결론적으로 이용하기 더 쉽고 협업에 더 용이하며 결국에 배포에는 S3와 CloudFront를 이용하여 호스팅하기때문에 CDN 서비스도 이용할 수 있다는 점에서 AWS amplify 배포를 선택하였습니다.
먼저 이용하지 않는 연관관계 엔티티를 조회하지 않기 위해 기본적으로 모든 연관관계의 fetchType을 Lazy로 설정해 두었습니다.
원인: JPQL은 DB테이블이 아니라 엔티티를 대상으로 쿼리를 검색하기 때문에 join을 하더라도 대상 엔티티의 값만 가져와 영속화하게 됩니다. 즉 따라서 하위 엔티티를 조회할때마다 새로운 select문이 생겨 N+1문제가 발생하게 됩니다.
해결책 : 로직에서 이용되는 하위 엔티티들을 fetch join을 통해 하나의 select문안에서 가져와 영속화시켜 더 이상 select 문이 나가지 않게 했습니다. 하지만 사용되는 하위엔티티가 Collection이고 2개 이상이거나 페이징이 필요할 경우 여러 문제점이 있어 사용하지 못하였습니다. 이 문제는 batch_fetch_size를 주어 hibernate에 size만큼 where에 쓰일 데이터를 모아둔후 일정batch_fetch_size에 다다르면 in절을 통해 한꺼번에 데이터를 가져오게 하여 문제를 해결하였습니다.
원인:
무중단 배포를 하기 떄문에 한 EC2 안에 같은 Redis에 연결된 서버가 2개가 켜져 있었고 두 서버의 스케쥴러가 레디스에 연결되어 유저에게 알림을 중복하여 보내는 문제가 발생하였습니다.
해결책:
redission의 분산 락을 이용하여 하나의 스케쥴러에서만 알람을 보낼 수 있게 변경하여 문제를 해결하였습니다.
서비스 기간 동안 유저들의 사용 패턴을 보았을 때,
유저들이 내가HAMA 서비스를 통해 SNS처럼 서로 소통하면서 즐거움을 느끼고 있다는 것을 파악했습니다.
또한 구글폼을 통한 유저 피드백으로도 다른 유저의 프로필, 작성글을 보거나 서로 소통할 수 있는 창구가 있으면 좋겠다는 의견을 받아 유저페이지와 방명록 기능을 추가하였습니다.
유저페이지는 기존의 마이페이지의 코드를, 방명록은 기존의 댓글 대댓글 코드를 재사용 하여 빠르게 기능 추가를 할 수 있었습니다.

