diff --git a/README.md b/README.md index 391644e..32d0d97 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,10 @@

+### :round_pushpin: 바로가기 +[프로젝트 소개](#--프로젝트-소개)   |   [주요 기능](#--주요-기능)   |   [기술 스택](#%EF%B8%8F--기술-스택)   +|   [기술적 도전](#--기술적-도전)  - [FE](#fe의-기술적-도전-과제) / [BE](#be의-기술적-도전-과제)   |   [시스템 아키텍처](#%EF%B8%8F--시스템-아키텍처)   |   [팀원 소개](#--팀원-소개) ## 📢 프로젝트 소개 @@ -86,6 +89,31 @@ Ncloud의 CLOVA Sentiment를 활용하여 작성된 일기의 감정을 분석
+## ⚒️ 기술 스택 + +| 분류 | 기술 | +| ---- | ---- | +| 프론트엔드 | | +| 백엔드 | | +| 배포 | | +| 협업 도구 | | + +
+ +## ⚙️ 시스템 아키텍처 + + + +## 👥 팀원 소개 +||||| +|:--:|:--:|:--:|:--:| +|[J069 손동민](https://github.com/dmson1218)|[J085 유종석](https://github.com/dmson1218)|[J032 김준수](https://github.com/JoonSoo-Kim)|[J114 이정민](https://github.com/mingxoxo)| +|FE|FE|BE|BE| + +- 더 자세한 내용은 [📄Wiki](https://github.com/boostcampwm2023/web08-ByeolSoop/wiki) 를 확인해주세요! + +
+ ## 💥 기술적 도전 ### 프론트엔드 @@ -100,13 +128,100 @@ Ncloud의 CLOVA Sentiment를 활용하여 작성된 일기의 감정을 분석 |개인 데이터 접근 제어| • JWT 인증을 통해 로그인용 액세스 / 리프레시 토큰을 발급하고 저장
• `JwtAuthGuard`를 사용하여 부적절한 로그인 및 중복 로그인 방지
• `PrivateDiaryGuard`를 사용하여 타인의 일기 접근 제어| |명확한 테스트 코드| • 테스트 코드 규칙 정의
• 내부 및 외부 의존성에 대한 테스트 코드 작성
• `typeorm-transactional-tests` 라이브러리를 활용하여 트랜잭션 적용| +## FE의 기술적 도전 과제 + +### R3F로 3D 배경 구현하기 + +[[3D 뷰 구현 과정 개발 일지]](https://byeolsoop.notion.site/3D-01df980774224f63b05003cc1c483c70?pvs=4) + +- React 기반에서 React-Three-Fiber(R3F)을 활용하여 3D 그래픽 환경을 구축하며, 사용자가 밤하늘에 떠있는 별을 탐색하고 만들어나가는 과정을 자연스럽게 처리하는 방법에 대해 고민하였습니다. +1. 사용자 인터페이스: 마우스로 밤하늘을 드래그하여 조작할 수 있도록 R3F에서 제공하는 다양한 컨트롤 방식을 검토하였고, 사용자가 마치 고개를 돌려 밤하늘을 둘러보는 느낌을 주기 위해 OrbitControls를 선택해 사용하였습니다. +2. 3D 밤하늘 배경 구축: 사용자가 밤하늘을 둘러보는 상황에서 밤하늘은 마치 하나의 구와 같기에, sphereGeometry를 활용해 사용자가 구 내부에 위치해있고 이 구의 내부 벽면을 밤하늘로 바라보도록 구현하였습니다. + - sphereGeometry는 많은 segment가 있는 다면체처럼 구현되어있기 때문에, 이 segment값을 최대한 높게 설정하여 구가 최대한 부드럽게 보이도록 하였습니다. + - 사용자가 바라보는 건 위쪽 하늘이기 때문에, pi/theta 값을 조정해 반구 아래 부분을 잘라냈습니다. +3. 3D 바닥 모델 로드 및 배치: 사용자가 바닥에서 하늘을 바라보는 시점을 직관적으로 표현하기 위해, 울퉁불퉁한 3D 바닥 모델을 fbx 확장자 형태로 제작하였습니다. 이를 useFBX 훅을 통해 로드하여 사용자에게 보여주는 형태로 구현하였습니다. + +### 3D 환경에서 자연스러운 카메라 이동 구현 + +- R3F의 Mesh 객체에서는 onDoubleClick 이벤트를 통해 사용자의 더블클릭 동작을 감지하고, 해당 이벤트에서 발생한 정보를 얻을 수 있습니다. 특히, 밤하늘을 더블클릭한 경우, 이벤트에서 얻은 point 정보를 활용하여 사용자가 바라보고 클릭한 좌표를 Vector3 값으로 얻을 수 있습니다. + + 사용자가 더블클릭한 좌표로 자연스럽게 카메라 시점을 이동하는 방법에 대한 고민에서 세 가지 방안을 고려하였습니다. + + **1. OrbitControls의 속성값 활용** + + - **방법**: OrbitControls의 속성값을 활용하여 target을 클릭한 위치로 설정하고, 카메라 시점을 이동하는 방식. + - **문제점**: + - target이 별을 기준으로 생성되어 드래그로 인한 이동이 불규칙하게 발생. + - 별을 생성할 때마다 카메라의 위치가 불규칙하게 변경되는 문제. + + **2. Raycasting** + + - **방법**: 카메라 주변에 작은 구를 추가하고, 더블클릭 이벤트가 발생했을 때 작은 구의 충돌 지점 좌표를 시점 target으로 설정. + - **문제점**: + - 시점 변환이 어색한 문제. + - 여전히 target 위치 문제가 존재. + + **3. 카메라 위치 이동** + + - **방법**: 카메라 위치를 고정하고, 카메라 시점을 이동하는 로직에서 target을 고정하고 카메라의 위치를 이동. + - **장점**: + - 기존의 문제를 깔끔하게 해결 가능. + - 카메라의 위치 이동으로 사용자가 바라보는 시점 조절 가능. +- 첫 번째와 두 번째 아이디어에서 발생한 문제를 해결하기 위해, 세 번째 아이디어를 도입하였습니다. 이로써 카메라의 위치를 고정하면서도 카메라 시점을 자연스럽게 이동시킬 수 있었고, 기존의 어색한 시점 변환 문제와 target 위치 문제를 근본적으로 해결하였습니다. 이를 통해 사용자가 더블클릭한 좌표로 자연스럽게 밤하늘을 관찰할 수 있는 로직을 완성하였습니다. + +### API 요청 및 응답에 따른 클라이언트에서의 처리 + +[[각 status code에 따른 처리 방식 정리]](https://byeolsoop.notion.site/API-341352ff57c84eef99c488bed5bd470f?pvs=4) + +[[useQuery/useMutation 리팩토링을 위한 공통 부분 추출 및 함수화 과정]](https://byeolsoop.notion.site/useQuery-useMutation-Refactoring-eed2ee2f03ea466e851d134ca35f0778?pvs=4) + +- 백엔드 서버에 다양한 요청을 보낼 때 반환되는 응답에 따른 처리 방법에 대해 심도있게 고민하였습니다. 이에는 일기의 CRUD 작업, 별 모양 데이터의 fetch 등 다양한 요청과 그에 따른 응답 처리가 포함되었습니다. +- 각 요청에 따른 응답을 API 문서에 상세히 정리하고, 이를 바탕으로 React-Query를 활용하여 API 요청을 진행하였습니다. +- 응답 상태 코드에 따라 각각 다른 동작을 수행하도록 fetch 이후의 작업 흐름을 설계하였습니다. + - 특히 401 코드는 액세스 토큰이 만료되어 재발급이 필요할 때 받을 수 있는 중요한 상태코드입니다. + 이를 응답으로 받았을 때, 액세스 토큰을 재발급 요청하고, 정상적으로 응답을 받은 후에 새롭게 발급받은 액세스 토큰으로 원래의 요청을 다시 전송하였습니다. 이를 통해 사용자가 불편함 없이 서비스를 이용할 수 있도록 하였습니다. +- React-Query 라이브러리를 사용하여 실시간으로 데이터를 가져오는 과정을 구현하면서, 각 API 요청 과정에서 코드 중복이 발생하는 문제를 인식하였습니다. 이는 코드의 가독성을 저하시키는 주요 요인이었습니다. 이에 따라, 중복되는 코드를 각각의 코드에서 정리하여 하나의 함수로 분리하는 리팩토링 작업을 진행하였습니다. + +### SPA paging + +[[SPA에서의 페이지 이동 관리 개발 일지]](https://byeolsoop.notion.site/SPA-7e5aeb063c0f422fb7728bf6c9729516?pvs=4) + +- 별숲 서비스는 SPA(Single Page Application)를 기반으로 설계되어 페이지 간 이동 시 주소가 변하지 않는 구조를 채택하고 있습니다. 3D view와 리스트 view에서 일기를 조회하는 상황에서, 이전 페이지로 돌아가는 과정에서 두 가지 상황을 어떻게 구분할지에 대한 기술적인 고민을 진행하였습니다. + + 3D view와 리스트 view에서의 이전 페이지로의 이동 시, 두 상황을 명확하게 구분하면서도 데이터의 신선도를 항상 유지하는 것이 필요했습니다. + + 두 가지 주요 방안을 고려하였고, 각각의 특징과 한계를 분석한 결과를 아래에 정리하였습니다. + + **1. History API 활용** + + - **방법**: 브라우저에서 지원하는 History API를 활용하여 페이지 이동을 관리. 페이지 간 상태를 history.pushState 메서드로 저장하고, popState 이벤트를 이용하여 이전 페이지로 돌아감. + - **장점**: + - 주소의 변화 없이 페이지 이동 가능. + - 자유로운 트래킹 가능. + - **단점**: + - 이전 페이지로 돌아갈 때 stale한 데이터 사용 가능성. + - 데이터의 항상 fresh한 상태 유지가 어려움. + + **2. Recoil의 Atom 활용** + + - **방법**: Recoil 라이브러리의 atom을 활용하여 전역 상태로 페이지 데이터를 stack 형식으로 저장. 페이지 이동 시 해당 페이지의 데이터 참조하여 이동. + - **장점**: + - 데이터의 항상 fresh한 상태 유지 가능. + - Recoil의 강력하고 편리한 상태 관리 활용. + - **단점**: + - 브라우저 자체 페이지 이동 기능 사용 불가능. + - UX 저하 우려. +- 데이터의 신선도를 항상 유지해야 하는 별숲 서비스의 특성 상, History API를 활용한 방식은 적합하지 않다고 판단하였습니다. 따라서 현재는 Recoil의 Atom을 활용하여 페이지 이동과 데이터 관리를 구현하고 있습니다. + + 그러나 브라우저 자체의 페이지 이동 기능을 활용하지 못하는 점은 UX에 부정적인 영향을 미칠 수 있다고 인식하고 있습니다. 추후에는 데이터의 신선도를 유지하면서도 History API를 적절히 활용하여 페이지 이동을 조절하는 방향으로 개선해 나갈 계획입니다. + ## BE의 기술적 도전 과제 ### Stateful한 JWT 기반 인증 방식 도입 ![리드미1](https://github.com/boostcampwm2023/web08-ByeolSoop/assets/49023630/cd697065-43f6-4e4c-994b-7f7aa2e0a426) -개발일지 +개인 데이터 접근 제어 개발일지 - 개인 일기 서비스에서 가장 중요한 요소는 일기 데이터가 타인에게 노출되지 않는 것이라고 판단했습니다. - Stateless한 JWT 기반 인증 방식을 도입했으나, 이 방식은 액세스 토큰의 탈취가 쉽고, 서버에서 이를 방지할 수 있는 방법이 없어 보안 이슈가 발생하였습니다. @@ -123,7 +238,7 @@ Ncloud의 CLOVA Sentiment를 활용하여 작성된 일기의 감정을 분석 |JwtAuthGuard 동작 과정|PrivateDiaryGuard 동작 과정| -개발일지 +커스텀 Guard 개발일지 - Stateful한 JWT 기반 인증 방식을 구현한 후, 이를 활용하여 중복 로그인 등의 여러 보안 이슈를 해결해야 했습니다. - JwtAuthGuard를 구현하여 액세스 토큰을 발급 받은 사용자가 현재 사용자가 맞는지 리프레시 토큰을 활용하여 비교하고 접근을 제어하도록 했습니다. @@ -145,7 +260,7 @@ Ncloud의 CLOVA Sentiment를 활용하여 작성된 일기의 감정을 분석 ![image](https://github.com/boostcampwm2023/web08-ByeolSoop/assets/49023630/ae8e8891-3a9a-4521-b879-975e3a7c4347) -개발일지 +테스트 개념 개발일지 - 테스트 코드를 작성하기 시작할 때, 각 테스트의 종류에 대한 개념을 제대로 이해하지 못해 개발 중 혼동이 발생했습니다. - 테스트와 관련된 명확한 개념과 규칙을 정의한 후 테스트 코드를 작성했습니다. @@ -159,7 +274,7 @@ Ncloud의 CLOVA Sentiment를 활용하여 작성된 일기의 감정을 분석 |---|---| |도입 이전|도입 이후| -개발일지 +테스트 트랜잭션 도입 개발일지 - 테스트 데이터베이스 환경을 독립적으로 유지하기 위해, 매 테스트마다 데이터베이스를 초기화해야 했습니다. 이로 인해 테스트에 소요되는 리소스와 시간이 크게 늘어나는 문제가 발생했습니다. - TypeORM Transactional Test를 통해 문제를 해결할 수 있었습니다. @@ -167,90 +282,3 @@ Ncloud의 CLOVA Sentiment를 활용하여 작성된 일기의 감정을 분석 - 트랜잭션을 통해 기능의 검증을 실시합니다. - 테스트가 종료되면 트랜잭션이 롤백되고 커넥션을 풀에 반환합니다. - 데이터베이스 초기화에 대한 부담이 없어지고 커넥션 객체가 풀에 바로 반납 및 재사용되어 테스트에 필요한 시간을 50% 이상 줄이고, 리소스 사용량을 크게 감소시킬 수 있었습니다. - -
- -## FE의 기술적 도전 과제 - -### 3D 뷰 구현 과정 - -- React에서 3D 그래픽 환경을 구축할 수 있는 React-Three-Fiber를 사용하면서, 밤하늘에 떠있는 별을 탐색하고 만들어나가는 그러한 과정을 어떻게 자연스럽게 처리할 수 있을지 고민하였습니다. -- 사용자가 마우스로 밤하늘을 드래그하여 컨트롤할 수 있도록 하기 위해 다음과 같은 컨트롤 방식을 찾아보았습니다. - - [DragControls](https://threejs.org/docs/examples/ko/controls/DragControls.html) - - [FirstPersonControls](https://threejs.org/docs/examples/ko/controls/FirstPersonControls.html) - - [FlyControls](https://threejs.org/docs/examples/ko/controls/FlyControls.html) - - [OrbitControls](https://threejs.org/docs/examples/ko/controls/OrbitControls.html) - - [PointerLockControls](https://threejs.org/docs/examples/ko/controls/PointerLockControls.html) - - [TrackballControls](https://threejs.org/docs/examples/ko/controls/TrackballControls.html) - - [TransformControls](https://threejs.org/docs/examples/ko/controls/TransformControls.html) -- 사용자가 마치 고개를 돌려 밤하늘을 둘러보는 느낌을 주기 위해 OrbitControls를 사용하였습니다. -- 사용자가 밤하늘을 둘러보는 상황에서 밤하늘은 마치 하나의 구와 같기에, sphereGeometry를 활용해 사용자가 구 내부에 위치해있고 이 구의 내부 벽면을 밤하늘로 바라보도록 구현하였습니다. - - sphereGeometry는 많은 segment가 있는 다면체처럼 되어있기 때문에, 이 segment값을 최대한 많이 주어 최대한 부드러운 구를 표현할 수 있도록 하였습니다. - - 사용자가 바라보는 건 위쪽 하늘이기 때문에, pi/theta 값을 조정해 반구 아래 부분을 잘라냈습니다. -- R3F의 Mesh객체에서는 onDoubleClick 이벤트를 통해 그 이벤트에서 발생한 정보를 얻을 수 있습니다. -이를 통해 사용자가 밤하늘을 더블클릭했을 때, 해당 이벤트에서의 point 정보를 통해 밤하늘의 어느 좌표를 사용자가 바라보고 클릭하였는지에 대한 정보를 Vector3 값으로 얻을 수 있었습니다. - - ```jsx - { - distance: 29.94205148934579, - point: Vector3, - object: Mesh, - uv: Vector2, - normal: Vector3, - … - } - ``` - -- 사용자가 더블클릭한 좌표로 카메라 시점을 자연스럽게 이동하는 방법에 대해 고민하였고, 3가지 방안을 생각해보았습니다. - 1. OrbitControls 자체의 속성 값을 활용해, target을 클릭한 위치로 시점을 이동하는 방식을 구현하는 방식. - 2. raycasting. 카메라 주변에 작은 구를 추가하고, 더블 클릭 이벤트가 발생했을 때 작은 구의 충돌 지점 좌표를 시점 target으로 지정. - 3. 카메라 위치 변경은 제어하지 않음. 대신 target을 원점으로 두고 카메라 위치 자체를 작은 위에서 이동. - - 첫번째 아이디어의 경우 React의 useState 및 useFrame을 활용해 target이 유동적으로 바뀌면서 프레임 단위로 카메라를 이동시킬 수 있었으나, target이 별을 기준으로 생성되어 드래그를 통한 이동이 별을 기준으로 실행되고, 별을 생성할 때마다 카메라의 위치가 불규칙하게 변경된다는 문제점이 존재했습니다. - - 두번째 아이디어의 경우 target이 변경되어도 카메라 주변의 작은 구 위의 점이 target이 되기 때문에 첫번째 아이디어의 문제점을 크게 줄일 수 있었습니다. 하지만 여전히 시점 변환이 어색한 문제점이 있었고, target 위치 문제 역시 문제점이 아예 사라진 것이 아닌 것이었기 때문에 근본적으로 해결할 수 있는 방안이 필요했습니다. - - 세번째 아이디어는 첫번째, 두번째 아이디어에서 실패한 부분에서 착안하여, 카메라 이동을 고정하고 카메라 시점을 이동하는 로직에서 카메라가 바라보는 target을 고정하고 카메라의 위치를 이동시키는 로직으로 변경하였고, 이를 통해 기존 두 가지 문제점을 깔끔하게 해결할 수 있었습니다. - - -3D 뷰 구현 과정 개발 일지 - -### API 요청 및 응답에 따른 클라이언트에서의 처리 - -- 백엔드 서버에 일기의 CRUD, 별 모양의 데이터 fetch 등 여러 요청을 보냈을 때 돌아오는 각 응답에 따른 처리에 대해 고민하였습니다. -- 각 요청에 따른 응답이 어떻게 돌아올 지에 대해서 API 문서에 정리를 상세히 하고, 이에 따라 React-Query를 통해 API 요청을 진행하였습니다. -- 각 응답 status code에 따라 case 별로 fetch 이후의 행동을 할 수 있도록 로직을 구현하였습니다. - - 특히 중요한 401 code의 경우, 액세스 토큰이 만료되어 재발급이 필요할 때 받을 수 있는 status code입니다. - 이를 응답으로 받은 경우 액세스 토큰을 재발급받는 요청을 보낸 후, 정상적으로 요청을 돌려받으면 기존에 시도하고자 했던 요청을 새로 발급받은 액세스 토큰으로 다시 요청하여 사용자 입장에서 사용할 때 불편함 없이 자연스럽게 동작할 수 있도록 설계하였습니다. -- React-Query 라이브러리를 통해 데이터를 실시간으로 fetching하는 과정을 거치면서, 각 API 요청 과정에서 중복되는 코드가 상당히 많아지는 것을 확인할 수 있었습니다. - - 이는 가독성의 저하를 크게 불러오는 이슈를 발생시켰고, 따라서 먼저 각 요청에 따른 코드를 모두 작성한 후, 중복되는 코드를 각 코드에서 정리하여 하나의 함수로 분리하는 리팩토링 작업을 진행하였습니다. - - -각 status code에 따른 처리 방식 정리 - -useQuery/useMutation 리팩토링을 위한 공통 부분 추출 및 함수화 과정 - -## ⚒️ 기술 스택 - -| 분류 | 기술 | -| ---- | ---- | -| 프론트엔드 | | -| 백엔드 | | -| 배포 | | -| 협업 도구 | | - - -
- -## ⚙️ 시스템 아키텍처 - - - -## 👥 팀원 소개 -||||| -|:--:|:--:|:--:|:--:| -|[J069 손동민](https://github.com/dmson1218)|[J085 유종석](https://github.com/dmson1218)|[J032 김준수](https://github.com/JoonSoo-Kim)|[J114 이정민](https://github.com/mingxoxo)| -|FE|FE|BE|BE| - -- 더 자세한 내용은 [📄Wiki](https://github.com/boostcampwm2023/web08-ByeolSoop/wiki) 를 확인해주세요!