diff --git a/.github/workflows/deploy-backend.yml b/.github/workflows/deploy-backend.yml new file mode 100644 index 0000000..2e45fbd --- /dev/null +++ b/.github/workflows/deploy-backend.yml @@ -0,0 +1,55 @@ +name: Build and Push Backend + +on: + push: + paths: + - "BE/**" + branches: + - main + +jobs: + build-and-push-backend: + runs-on: ubuntu-20.04 + + steps: + - name: Checkout Repository + uses: actions/checkout@v2 + + - name: Set up Docker Compose + run: | + sudo apt-get update + sudo apt-get install -y curl + sudo curl -L "https://github.com/docker/compose/releases/download/1.27.4/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose + sudo chmod +x /usr/local/bin/docker-compose + docker-compose --version + + - name: Login to NCP Container Registry + uses: docker/login-action@v3 + with: + registry: byeolsoop-registry.kr.ncr.ntruss.com + username: ${{ secrets.API_ACCESS_KEY }} + password: ${{ secrets.API_SECRET_KEY }} + + - name: Build and Push Backend Docker Image + run: | + docker build -t byeolsoop-registry.kr.ncr.ntruss.com/backend:byeolsoop -f ./docker/be/Dockerfile . + docker push byeolsoop-registry.kr.ncr.ntruss.com/backend:byeolsoop + + pull-and-deploy: + needs: build-and-push-backend + runs-on: ubuntu-20.04 + + steps: + - name: SSH-deploy + uses: appleboy/ssh-action@master + with: + host: ${{secrets.VPC_HOST}} + username: ${{secrets.VPC_USER}} + password: ${{secrets.VPC_PASSWORD}} + port: ${{secrets.VPC_PORT}} + script: | + cd /home/docker-byeolsoop + docker system prune -a + docker pull byeolsoop-registry.kr.ncr.ntruss.com/backend:byeolsoop + docker compose up --force-recreate --build -d --quiet-pull 2>log.out + cat log.out diff --git a/.github/workflows/deploy-frontend.yml b/.github/workflows/deploy-frontend.yml new file mode 100644 index 0000000..fdd2b86 --- /dev/null +++ b/.github/workflows/deploy-frontend.yml @@ -0,0 +1,55 @@ +name: Build and Push frontend + +on: + push: + paths: + - "FE/**" + branches: + - main + +jobs: + build-and-push-frontend: + runs-on: ubuntu-20.04 + + steps: + - name: Checkout Repository + uses: actions/checkout@v2 + + - name: Set up Docker Compose + run: | + sudo apt-get update + sudo apt-get install -y curl + sudo curl -L "https://github.com/docker/compose/releases/download/1.27.4/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose + sudo chmod +x /usr/local/bin/docker-compose + docker-compose --version + + - name: Login to NCP Container Registry + uses: docker/login-action@v3 + with: + registry: byeolsoop-registry.kr.ncr.ntruss.com + username: ${{ secrets.API_ACCESS_KEY }} + password: ${{ secrets.API_SECRET_KEY }} + + - name: Build and Push Frontend Docker Image + run: | + docker build -t byeolsoop-registry.kr.ncr.ntruss.com/frontend:byeolsoop -f ./docker/fe/Dockerfile . + docker push byeolsoop-registry.kr.ncr.ntruss.com/frontend:byeolsoop + + pull-and-deploy: + runs-on: ubuntu-20.04 + needs: build-and-push-frontend + + steps: + - name: SSH-deploy + uses: appleboy/ssh-action@master + with: + host: ${{secrets.VPC_HOST}} + username: ${{secrets.VPC_USER}} + password: ${{secrets.VPC_PASSWORD}} + port: ${{secrets.VPC_PORT}} + script: | + cd /home/docker-byeolsoop + docker system prune -a + docker pull byeolsoop-registry.kr.ncr.ntruss.com/frontend:byeolsoop + docker compose up --force-recreate --build -d --quiet-pull 2>log.out + cat log.out diff --git a/FE/package-lock.json b/FE/package-lock.json index b3b4346..43a40e7 100644 --- a/FE/package-lock.json +++ b/FE/package-lock.json @@ -8,6 +8,7 @@ "name": "fe", "version": "0.1.0", "dependencies": { + "@react-three/drei": "^9.88.17", "@react-three/fiber": "^8.15.10", "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", @@ -20,6 +21,7 @@ "styled-components": "^6.1.1", "styled-reset": "^4.5.1", "three": "^0.158.0", + "three.js": "^0.77.1", "web-vitals": "^2.1.4" }, "devDependencies": { @@ -3188,6 +3190,11 @@ "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==" }, + "node_modules/@mediapipe/tasks-vision": { + "version": "0.10.8", + "resolved": "https://registry.npmjs.org/@mediapipe/tasks-vision/-/tasks-vision-0.10.8.tgz", + "integrity": "sha512-Rp7ll8BHrKB3wXaRFKhrltwZl1CiXGdibPxuWXvqGnKTnv8fqa/nvftYNuSbf+pbJWKYCXdBtYTITdAUTGGh0Q==" + }, "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { "version": "5.1.1-v1", "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", @@ -3347,6 +3354,128 @@ } } }, + "node_modules/@react-spring/animated": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@react-spring/animated/-/animated-9.6.1.tgz", + "integrity": "sha512-ls/rJBrAqiAYozjLo5EPPLLOb1LM0lNVQcXODTC1SMtS6DbuBCPaKco5svFUQFMP2dso3O+qcC4k9FsKc0KxMQ==", + "dependencies": { + "@react-spring/shared": "~9.6.1", + "@react-spring/types": "~9.6.1" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@react-spring/core": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@react-spring/core/-/core-9.6.1.tgz", + "integrity": "sha512-3HAAinAyCPessyQNNXe5W0OHzRfa8Yo5P748paPcmMowZ/4sMfaZ2ZB6e5x5khQI8NusOHj8nquoutd6FRY5WQ==", + "dependencies": { + "@react-spring/animated": "~9.6.1", + "@react-spring/rafz": "~9.6.1", + "@react-spring/shared": "~9.6.1", + "@react-spring/types": "~9.6.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/react-spring/donate" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@react-spring/rafz": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@react-spring/rafz/-/rafz-9.6.1.tgz", + "integrity": "sha512-v6qbgNRpztJFFfSE3e2W1Uz+g8KnIBs6SmzCzcVVF61GdGfGOuBrbjIcp+nUz301awVmREKi4eMQb2Ab2gGgyQ==" + }, + "node_modules/@react-spring/shared": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@react-spring/shared/-/shared-9.6.1.tgz", + "integrity": "sha512-PBFBXabxFEuF8enNLkVqMC9h5uLRBo6GQhRMQT/nRTnemVENimgRd+0ZT4yFnAQ0AxWNiJfX3qux+bW2LbG6Bw==", + "dependencies": { + "@react-spring/rafz": "~9.6.1", + "@react-spring/types": "~9.6.1" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@react-spring/three": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@react-spring/three/-/three-9.6.1.tgz", + "integrity": "sha512-Tyw2YhZPKJAX3t2FcqvpLRb71CyTe1GvT3V+i+xJzfALgpk10uPGdGaQQ5Xrzmok1340DAeg2pR/MCfaW7b8AA==", + "dependencies": { + "@react-spring/animated": "~9.6.1", + "@react-spring/core": "~9.6.1", + "@react-spring/shared": "~9.6.1", + "@react-spring/types": "~9.6.1" + }, + "peerDependencies": { + "@react-three/fiber": ">=6.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "three": ">=0.126" + } + }, + "node_modules/@react-spring/types": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@react-spring/types/-/types-9.6.1.tgz", + "integrity": "sha512-POu8Mk0hIU3lRXB3bGIGe4VHIwwDsQyoD1F394OK7STTiX9w4dG3cTLljjYswkQN+hDSHRrj4O36kuVa7KPU8Q==" + }, + "node_modules/@react-three/drei": { + "version": "9.88.17", + "resolved": "https://registry.npmjs.org/@react-three/drei/-/drei-9.88.17.tgz", + "integrity": "sha512-WEYAkikzw0juG3F1aMy1AEeuRmsGmKytb8ETZARd4E6Q61Z1ceoL7fRvrnrXE/A2MHKgSzJgkckMEhKlbbJlyw==", + "dependencies": { + "@babel/runtime": "^7.11.2", + "@mediapipe/tasks-vision": "0.10.8", + "@react-spring/three": "~9.6.1", + "@use-gesture/react": "^10.2.24", + "camera-controls": "^2.4.2", + "cross-env": "^7.0.3", + "detect-gpu": "^5.0.28", + "glsl-noise": "^0.0.0", + "lodash.clamp": "^4.0.3", + "lodash.omit": "^4.5.0", + "lodash.pick": "^4.4.0", + "maath": "^0.9.0", + "meshline": "^3.1.6", + "react-composer": "^5.0.3", + "react-merge-refs": "^1.1.0", + "stats-gl": "^1.0.4", + "stats.js": "^0.17.0", + "suspend-react": "^0.1.3", + "three-mesh-bvh": "^0.6.7", + "three-stdlib": "^2.28.0", + "troika-three-text": "^0.47.2", + "utility-types": "^3.10.0", + "uuid": "^9.0.1", + "zustand": "^3.5.13" + }, + "peerDependencies": { + "@react-three/fiber": ">=8.0", + "react": ">=18.0", + "react-dom": ">=18.0", + "three": ">=0.137" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/@react-three/drei/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/@react-three/fiber": { "version": "8.15.10", "resolved": "https://registry.npmjs.org/@react-three/fiber/-/fiber-8.15.10.tgz", @@ -4115,6 +4244,11 @@ "@types/node": "*" } }, + "node_modules/@types/draco3d": { + "version": "1.4.9", + "resolved": "https://registry.npmjs.org/@types/draco3d/-/draco3d-1.4.9.tgz", + "integrity": "sha512-4MMUjMQb4yA5fJ4osXx+QxGHt0/ZSy4spT6jL1HM7Tn8OJEC35siqdnpOo+HxPhYjqEFumKfGVF9hJfdyKBIBA==" + }, "node_modules/@types/eslint": { "version": "8.44.7", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.7.tgz", @@ -4474,6 +4608,11 @@ "@types/node": "*" } }, + "node_modules/@types/offscreencanvas": { + "version": "2019.7.3", + "resolved": "https://registry.npmjs.org/@types/offscreencanvas/-/offscreencanvas-2019.7.3.tgz", + "integrity": "sha512-ieXiYmgSRXUDeOntE1InxjWyvEelZGP63M+cGuquuRLuIKKT1osnkXjxev9B7d1nXSug5vpunx+gNlbVxMlC9A==" + }, "node_modules/@types/parse-json": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", @@ -4593,6 +4732,12 @@ "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==" }, + "node_modules/@types/stats.js": { + "version": "0.17.3", + "resolved": "https://registry.npmjs.org/@types/stats.js/-/stats.js-0.17.3.tgz", + "integrity": "sha512-pXNfAD3KHOdif9EQXZ9deK82HVNaXP5ZIF5RP2QG6OQFNTaY2YIetfrE9t528vEreGQvEPRDDc8muaoYeK0SxQ==", + "peer": true + }, "node_modules/@types/stylis": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/@types/stylis/-/stylis-4.2.3.tgz", @@ -4606,6 +4751,18 @@ "@types/jest": "*" } }, + "node_modules/@types/three": { + "version": "0.158.3", + "resolved": "https://registry.npmjs.org/@types/three/-/three-0.158.3.tgz", + "integrity": "sha512-6Qs1rUvLSbkJ4hlIe6/rdwIf61j1x2UKvGJg7s8KjswYsz1C1qDTs6voVXXB8kYaI0hgklgZgbZUupfL1l9xdA==", + "peer": true, + "dependencies": { + "@types/stats.js": "*", + "@types/webxr": "*", + "fflate": "~0.6.10", + "meshoptimizer": "~0.18.1" + } + }, "node_modules/@types/trusted-types": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.6.tgz", @@ -4860,6 +5017,22 @@ "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==" }, + "node_modules/@use-gesture/core": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@use-gesture/core/-/core-10.3.0.tgz", + "integrity": "sha512-rh+6MND31zfHcy9VU3dOZCqGY511lvGcfyJenN4cWZe0u1BH6brBpBddLVXhF2r4BMqWbvxfsbL7D287thJU2A==" + }, + "node_modules/@use-gesture/react": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@use-gesture/react/-/react-10.3.0.tgz", + "integrity": "sha512-3zc+Ve99z4usVP6l9knYVbVnZgfqhKah7sIG+PS2w+vpig2v2OLct05vs+ZXMzwxdNCMka8B+8WlOo0z6Pn6DA==", + "dependencies": { + "@use-gesture/core": "10.3.0" + }, + "peerDependencies": { + "react": ">= 16.8.0" + } + }, "node_modules/@webassemblyjs/ast": { "version": "1.11.6", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz", @@ -5813,6 +5986,14 @@ "node": ">= 8.0.0" } }, + "node_modules/bidi-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz", + "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==", + "dependencies": { + "require-from-string": "^2.0.2" + } + }, "node_modules/big-integer": { "version": "1.6.51", "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", @@ -6123,6 +6304,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/camera-controls": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/camera-controls/-/camera-controls-2.7.3.tgz", + "integrity": "sha512-L4mxjBd3u8qiOLozdWrH2P8ZybSsDXBF7iyNyqNEFJhPUkovmuARWR8JTc1B/qlclOIg6FvZZA/0uAZMMim0mw==", + "peerDependencies": { + "three": ">=0.126.1" + } + }, "node_modules/caniuse-api": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", @@ -6520,6 +6709,23 @@ "node": ">=10" } }, + "node_modules/cross-env": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", + "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "bin": { + "cross-env": "src/bin/cross-env.js", + "cross-env-shell": "src/bin/cross-env-shell.js" + }, + "engines": { + "node": ">=10.14", + "npm": ">=6", + "yarn": ">=1" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -7272,6 +7478,14 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/detect-gpu": { + "version": "5.0.37", + "resolved": "https://registry.npmjs.org/detect-gpu/-/detect-gpu-5.0.37.tgz", + "integrity": "sha512-EraWs84faI4iskB4qvE39bevMIazEvd1RpoyGLOBesRLbiz6eMeJqqRPHjEFClfRByYZzi9IzU35rBXIO76oDw==", + "dependencies": { + "webgl-constants": "^1.1.1" + } + }, "node_modules/detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -7475,6 +7689,11 @@ "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==" }, + "node_modules/draco3d": { + "version": "1.5.6", + "resolved": "https://registry.npmjs.org/draco3d/-/draco3d-1.5.6.tgz", + "integrity": "sha512-+3NaRjWktb5r61ZFoDejlykPEFKT5N/LkbXsaddlw6xNSXBanUYpFc2AXXpbJDilPHazcSreU/DpQIaxfX0NfQ==" + }, "node_modules/duplexer": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", @@ -8791,6 +9010,11 @@ "bser": "2.1.1" } }, + "node_modules/fflate": { + "version": "0.6.10", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.6.10.tgz", + "integrity": "sha512-IQrh3lEPM93wVCEczc9SaAOvkmcoQn/G8Bo1e8ZPlY3X3bnAxWaBdvTdvM1hP62iZp0BXWDy4vTAy4fF0+Dlpg==" + }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -9419,6 +9643,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/glsl-noise": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/glsl-noise/-/glsl-noise-0.0.0.tgz", + "integrity": "sha512-b/ZCF6amfAUb7dJM/MxRs7AetQEahYzJ8PtgfrmEdtw6uyGOr+ZSGtgjFm6mfsBkxJ4d2W7kg+Nlqzqvn3Bc0w==" + }, "node_modules/gopd": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", @@ -12829,6 +13058,11 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "node_modules/lodash.clamp": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/lodash.clamp/-/lodash.clamp-4.0.3.tgz", + "integrity": "sha512-HvzRFWjtcguTW7yd8NJBshuNaCa8aqNFtnswdT7f/cMd/1YKy5Zzoq4W/Oxvnx9l7aeY258uSdDfM793+eLsVg==" + }, "node_modules/lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", @@ -12844,6 +13078,16 @@ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" }, + "node_modules/lodash.omit": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.omit/-/lodash.omit-4.5.0.tgz", + "integrity": "sha512-XeqSp49hNGmlkj2EJlfrQFIzQ6lXdNro9sddtQzcJY8QaoC2GO0DT7xaIokHeyM+mIT0mPMlPvkYzg2xCuHdZg==" + }, + "node_modules/lodash.pick": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz", + "integrity": "sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q==" + }, "node_modules/lodash.sortby": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", @@ -12889,6 +13133,15 @@ "lz-string": "bin/bin.js" } }, + "node_modules/maath": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/maath/-/maath-0.9.0.tgz", + "integrity": "sha512-aAR8hoUqPxlsU8VOxkS9y37jhUzdUxM017NpCuxFU1Gk+nMaZASZxymZrV8LRSHzRk/watlbfyNKu6XPUhCFrQ==", + "peerDependencies": { + "@types/three": ">=0.144.0", + "three": ">=0.144.0" + } + }, "node_modules/magic-string": { "version": "0.25.9", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", @@ -12978,6 +13231,20 @@ "node": ">= 8" } }, + "node_modules/meshline": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/meshline/-/meshline-3.1.7.tgz", + "integrity": "sha512-uf9fPI9wy0Ie0kZjvKuIkf2n7gi3ih0wdTeb/kmSvmzpPyEL5d9lFohg9+JV9VC4sQUBOZDgxu6fnjn57goSHg==", + "peerDependencies": { + "three": ">=0.137" + } + }, + "node_modules/meshoptimizer": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/meshoptimizer/-/meshoptimizer-0.18.1.tgz", + "integrity": "sha512-ZhoIoL7TNV4s5B6+rx5mC//fw8/POGyNxS/DZyCJeiZ12ScLfVwRE/GfsxwiTkMYYD5DmK2/JXnEVXqL4rF+Sw==", + "peer": true + }, "node_modules/methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", @@ -15031,6 +15298,11 @@ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, + "node_modules/potpack": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/potpack/-/potpack-1.0.2.tgz", + "integrity": "sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ==" + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -15319,6 +15591,17 @@ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" }, + "node_modules/react-composer": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/react-composer/-/react-composer-5.0.3.tgz", + "integrity": "sha512-1uWd07EME6XZvMfapwZmc7NgCZqDemcvicRi3wMJzXsQLvZ3L7fTHVyPy1bZdnWXM4iPjYuNE+uJ41MLKeTtnA==", + "dependencies": { + "prop-types": "^15.6.0" + }, + "peerDependencies": { + "react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/react-dev-utils": { "version": "12.0.1", "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz", @@ -15458,6 +15741,15 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" }, + "node_modules/react-merge-refs": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/react-merge-refs/-/react-merge-refs-1.1.0.tgz", + "integrity": "sha512-alTKsjEL0dKH/ru1Iyn7vliS2QRcBp9zZPGoWxUOvRGWPUYgjo+V01is7p04It6KhgrzhJGnIj9GgX8W4bZoCQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, "node_modules/react-query": { "version": "3.39.3", "resolved": "https://registry.npmjs.org/react-query/-/react-query-3.39.3.tgz", @@ -16705,6 +16997,16 @@ "node": ">= 0.8.0" } }, + "node_modules/stats-gl": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/stats-gl/-/stats-gl-1.0.7.tgz", + "integrity": "sha512-vZI82CjefSxLC1bjw36z28v0+QE9rJKymGlXtfWu+ipW70ZEAwa4EbO4LxluAfLfpqiaAS04NzpYBRLDeAwYWQ==" + }, + "node_modules/stats.js": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/stats.js/-/stats.js-0.17.0.tgz", + "integrity": "sha512-hNKz8phvYLPEcRkeG1rsGmV5ChMjKDAWU7/OJJdDErPBNChQXxCo3WZurGpnWc6gZhAzEPFad1aVgyOANH1sMw==" + }, "node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -17378,6 +17680,46 @@ "resolved": "https://registry.npmjs.org/three/-/three-0.158.0.tgz", "integrity": "sha512-TALj4EOpdDPF1henk2Q+s17K61uEAAWQ7TJB68nr7FKxqwyDr3msOt5IWdbGm4TaWKjrtWS8DJJWe9JnvsWOhQ==" }, + "node_modules/three-mesh-bvh": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/three-mesh-bvh/-/three-mesh-bvh-0.6.8.tgz", + "integrity": "sha512-EGebF9DZx1S8+7OZYNNTT80GXJZVf+UYXD/HyTg/e2kR/ApofIFfUS4ZzIHNnUVIadpnLSzM4n96wX+l7GMbnQ==", + "peerDependencies": { + "three": ">= 0.151.0" + } + }, + "node_modules/three-stdlib": { + "version": "2.28.7", + "resolved": "https://registry.npmjs.org/three-stdlib/-/three-stdlib-2.28.7.tgz", + "integrity": "sha512-E7NuztilCswBKnEoyqydvA7N4dy0cf/gLA0bKrrg6+Q6j4WtusGa/+t9oK2HVq47S1AHRH2CvFHpdIGNjPKo/A==", + "dependencies": { + "@types/draco3d": "^1.4.0", + "@types/offscreencanvas": "^2019.6.4", + "@types/webxr": "^0.5.2", + "draco3d": "^1.4.1", + "fflate": "^0.6.9", + "potpack": "^1.0.1" + }, + "peerDependencies": { + "three": ">=0.128.0" + } + }, + "node_modules/three.js": { + "version": "0.77.1", + "resolved": "https://registry.npmjs.org/three.js/-/three.js-0.77.1.tgz", + "integrity": "sha512-lVMYlBXhhHlZtsF+cQdev6WCm2fSWs3I+A6Z63j6TBmkVXOYgdo9AUCRemfsw5mllqWx23VubJ5Qt8/uWKQpJA==", + "dependencies": { + "three": "0.77.0" + } + }, + "node_modules/three.js/node_modules/three": { + "version": "0.77.0", + "resolved": "https://registry.npmjs.org/three/-/three-0.77.0.tgz", + "integrity": "sha512-YWEp8ahs2l+6fAFVbanLVQoSBwVq5jDct3nZdBSHHXb5I/w5oy/LCMAzIOAguKWclRqcjR4W1BeP5QBnftGbpA==", + "engines": { + "node": "*" + } + }, "node_modules/throat": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.2.tgz", @@ -17465,6 +17807,33 @@ "node": ">=8" } }, + "node_modules/troika-three-text": { + "version": "0.47.2", + "resolved": "https://registry.npmjs.org/troika-three-text/-/troika-three-text-0.47.2.tgz", + "integrity": "sha512-qylT0F+U7xGs+/PEf3ujBdJMYWbn0Qci0kLqI5BJG2kW1wdg4T1XSxneypnF05DxFqJhEzuaOR9S2SjiyknMng==", + "dependencies": { + "bidi-js": "^1.0.2", + "troika-three-utils": "^0.47.2", + "troika-worker-utils": "^0.47.2", + "webgl-sdf-generator": "1.1.1" + }, + "peerDependencies": { + "three": ">=0.125.0" + } + }, + "node_modules/troika-three-utils": { + "version": "0.47.2", + "resolved": "https://registry.npmjs.org/troika-three-utils/-/troika-three-utils-0.47.2.tgz", + "integrity": "sha512-/28plhCxfKtH7MSxEGx8e3b/OXU5A0xlwl+Sbdp0H8FXUHKZDoksduEKmjQayXYtxAyuUiCRunYIv/8Vi7aiyg==", + "peerDependencies": { + "three": ">=0.125.0" + } + }, + "node_modules/troika-worker-utils": { + "version": "0.47.2", + "resolved": "https://registry.npmjs.org/troika-worker-utils/-/troika-worker-utils-0.47.2.tgz", + "integrity": "sha512-mzss4MeyzUkYBppn4x5cdAqrhBHFEuVmMMgLMTyFV23x6GvQMyo+/R5E5Lsbrt7WSt5RfvewjcwD1DChRTA9lA==" + }, "node_modules/tryer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz", @@ -17842,6 +18211,14 @@ "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==" }, + "node_modules/utility-types": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.10.0.tgz", + "integrity": "sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg==", + "engines": { + "node": ">= 4" + } + }, "node_modules/utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", @@ -17937,6 +18314,16 @@ "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-2.1.4.tgz", "integrity": "sha512-sVWcwhU5mX6crfI5Vd2dC4qchyTqxV8URinzt25XqVh+bHEPGH4C3NPrNionCP7Obx59wrYEbNlw4Z8sjALzZg==" }, + "node_modules/webgl-constants": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/webgl-constants/-/webgl-constants-1.1.1.tgz", + "integrity": "sha512-LkBXKjU5r9vAW7Gcu3T5u+5cvSvh5WwINdr0C+9jpzVB41cjQAP5ePArDtk/WHYdVj0GefCgM73BA7FlIiNtdg==" + }, + "node_modules/webgl-sdf-generator": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/webgl-sdf-generator/-/webgl-sdf-generator-1.1.1.tgz", + "integrity": "sha512-9Z0JcMTFxeE+b2x1LJTdnaT8rT8aEp7MVxkNwoycNmJWwPdzoXzMh0BjJSh/AEFP+KPYZUli814h8bJZFIZ2jA==" + }, "node_modules/webidl-conversions": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", diff --git a/FE/package.json b/FE/package.json index 8bcc1db..1f5e35d 100644 --- a/FE/package.json +++ b/FE/package.json @@ -3,6 +3,7 @@ "version": "0.1.0", "private": true, "dependencies": { + "@react-three/drei": "^9.88.17", "@react-three/fiber": "^8.15.10", "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", @@ -15,6 +16,7 @@ "styled-components": "^6.1.1", "styled-reset": "^4.5.1", "three": "^0.158.0", + "three.js": "^0.77.1", "web-vitals": "^2.1.4" }, "scripts": { diff --git a/FE/src/atoms/diaryAtom.js b/FE/src/atoms/diaryAtom.js index b7408a7..96641a4 100644 --- a/FE/src/atoms/diaryAtom.js +++ b/FE/src/atoms/diaryAtom.js @@ -9,6 +9,8 @@ const diaryAtom = atom({ isDelete: false, isList: false, diaryUuid: "", + diaryPoint: "", + diaryList: [], isLoaded: false, }, }); diff --git a/FE/src/components/DiaryModal/DiaryCreateModal.js b/FE/src/components/DiaryModal/DiaryCreateModal.js index abc5e5f..bb5b369 100644 --- a/FE/src/components/DiaryModal/DiaryCreateModal.js +++ b/FE/src/components/DiaryModal/DiaryCreateModal.js @@ -10,20 +10,23 @@ import ModalWrapper from "../../styles/Modal/ModalWrapper"; import DiaryModalHeader from "../../styles/Modal/DiaryModalHeader"; import deleteIcon from "../../assets/deleteIcon.svg"; -// TODO: 일기 데이터 수정 API 연결 -function DiaryCreateModal() { +function DiaryCreateModal(props) { + const { refetch } = props; const [isInput, setIsInput] = useState(false); - const [diaryData, setDiaryData] = useState({ - title: "test", - content: "test", - date: "2023-11-20", - point: "0,0,0", + const diaryState = useRecoilValue(diaryAtom); + const userState = useRecoilValue(userAtom); + const setDiaryState = useSetRecoilState(diaryAtom); + + // TODO: 날짜 선택 기능 구현 + const [diaryData, setDiaryData] = React.useState({ + title: "", + content: "", + date: "2023-11-19", + point: diaryState.diaryPoint, tags: [], - shapeUuid: "cf3a074a-0707-40c4-a598-c7c17a654476", + shapeUuid: "", }); const [newShapeData, setNewShapeData] = useState(null); - const userState = useRecoilValue(userAtom); - const setDiaryState = useSetRecoilState(diaryAtom); useEffect(() => { const handleBeforeUnload = (e) => { @@ -38,13 +41,6 @@ function DiaryCreateModal() { }; }, []); - const closeModal = () => { - setDiaryState((prev) => ({ - ...prev, - isCreate: false, - })); - }; - const addTag = (e) => { if (e.target.value.length > 0 && !diaryData.tags.includes(e.target.value)) { setDiaryData({ @@ -85,20 +81,12 @@ function DiaryCreateModal() { body: JSON.stringify(data.diaryData), }) .then((res) => res.json()) - .then((res) => { + .then(() => { + refetch(); setDiaryState((prev) => ({ ...prev, isLoading: true, })); - setTimeout(() => { - setDiaryState((prev) => ({ - ...prev, - isCreate: false, - isRead: true, - isLoading: false, - diaryUuid: res.uuid, - })); - }, 3000); }); } @@ -159,16 +147,10 @@ function DiaryCreateModal() { } = useMutation(createDiaryFn); return ( - + 새로운 별의 이야기를 적어주세요. - - {new Date().toLocaleDateString("ko-KR", { - year: "numeric", - month: "long", - day: "numeric", - })} - + {diaryData.date} - + { + setDiaryState((prev) => ({ ...prev, isCreate: false })); + }} + > delete {isInput ? ( @@ -228,7 +214,7 @@ function DiaryCreateModal() { width='5rem' onClick={() => { createDiary({ diaryData, accessToken: userState.accessToken }); - closeModal(); + setDiaryState((prev) => ({ ...prev, isCreate: false })); }} > 생성 diff --git a/FE/src/components/DiaryModal/DiaryDeleteModal.js b/FE/src/components/DiaryModal/DiaryDeleteModal.js index af33750..d5cac6e 100644 --- a/FE/src/components/DiaryModal/DiaryDeleteModal.js +++ b/FE/src/components/DiaryModal/DiaryDeleteModal.js @@ -6,20 +6,24 @@ import diaryAtom from "../../atoms/diaryAtom"; import userAtom from "../../atoms/userAtom"; import ModalWrapper from "../../styles/Modal/ModalWrapper"; -async function deleteDiaryFn(data) { - return fetch(`http://223.130.129.145:3005/diaries/${data.diaryUuid}`, { - method: "DELETE", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${data.accessToken}`, - }, - }); -} - -function DiaryDeleteModal() { +function DiaryDeleteModal(props) { + const { refetch } = props; const diaryState = useRecoilValue(diaryAtom); const userState = useRecoilValue(userAtom); const setDiaryState = useSetRecoilState(diaryAtom); + + async function deleteDiaryFn(data) { + return fetch(`http://223.130.129.145:3005/diaries/${data.diaryUuid}`, { + method: "DELETE", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${data.accessToken}`, + }, + }).then(() => { + refetch(); + }); + } + const { mutate: deleteDiary, // isLoading, diff --git a/FE/src/components/DiaryModal/DiaryListModal.js b/FE/src/components/DiaryModal/DiaryListModal.js index cc6ab85..544e572 100644 --- a/FE/src/components/DiaryModal/DiaryListModal.js +++ b/FE/src/components/DiaryModal/DiaryListModal.js @@ -1,35 +1,18 @@ import React, { useEffect, useLayoutEffect } from "react"; -import { useQuery } from "react-query"; import styled from "styled-components"; -import { useRecoilValue, useSetRecoilState } from "recoil"; -import userAtom from "../../atoms/userAtom"; +import { useRecoilState } from "recoil"; import diaryAtom from "../../atoms/diaryAtom"; import zoomIn from "../../assets/zoomIn.svg"; function DiaryListModal() { const [selectedDiary, setSelectedDiary] = React.useState(null); - const userState = useRecoilValue(userAtom); - const setDiaryState = useSetRecoilState(diaryAtom); - - const { - data: DiaryList, - isError, - isLoading, - } = useQuery("diaryList", () => - fetch("http://223.130.129.145:3005/diaries", { - method: "GET", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${userState.accessToken}`, - }, - }).then((res) => res.json()), - ); + const [diaryState, setDiaryState] = useRecoilState(diaryAtom); useLayoutEffect(() => { - if (DiaryList) { - setSelectedDiary(DiaryList[0]); + if (diaryState.diaryList) { + setSelectedDiary(diaryState.diaryList[0]); } - }, [DiaryList]); + }, [diaryState.diaryList]); useEffect(() => { if (selectedDiary) { @@ -40,26 +23,6 @@ function DiaryListModal() { } }, [selectedDiary]); - if (isLoading) - return ( - - - - - - ); - - if (isError) - return ( - - - - - 일기 목록을 불러오는데 실패했습니다. - - - ); - return ( @@ -87,7 +50,7 @@ function DiaryListModal() { e.target.focus(); }} > - {DiaryList?.map((diary) => ( + {diaryState.diaryList?.map((diary) => ( { diff --git a/FE/src/components/DiaryModal/DiaryLoadingModal.js b/FE/src/components/DiaryModal/DiaryLoadingModal.js index aea039b..d0229a3 100644 --- a/FE/src/components/DiaryModal/DiaryLoadingModal.js +++ b/FE/src/components/DiaryModal/DiaryLoadingModal.js @@ -1,5 +1,7 @@ -import React from "react"; +import React, { useLayoutEffect } from "react"; import styled from "styled-components"; +import { useSetRecoilState } from "recoil"; +import diaryAtom from "../../atoms/diaryAtom"; import ModalWrapper from "../../styles/Modal/ModalWrapper"; import { LoadingAnimationWrapper, @@ -7,17 +9,35 @@ import { } from "../../styles/Modal/LoadingAnimation"; function DiaryLoadingModal() { + const setDiaryState = useSetRecoilState(diaryAtom); + + useLayoutEffect(() => { + setDiaryState((prev) => ({ + ...prev, + isCreate: false, + isRead: false, + isUpdate: false, + isDelete: false, + })); + setTimeout(() => { + setDiaryState((prev) => ({ + ...prev, + isLoading: false, + })); + }, 1000); + }, []); + return ( <> - 일기 저장 중 + 로딩 중입니다. - 감정 분석 결과와 함께 + 당신의 새로운 별자리를
- 보여드릴게요. + 보여드릴게요
diff --git a/FE/src/components/DiaryModal/DiaryReadModal.js b/FE/src/components/DiaryModal/DiaryReadModal.js index 3268a16..7feecf9 100644 --- a/FE/src/components/DiaryModal/DiaryReadModal.js +++ b/FE/src/components/DiaryModal/DiaryReadModal.js @@ -53,7 +53,8 @@ async function getDiary(accessToken, diaryUuid) { }).then((res) => res.json()); } -function DiaryReadModal() { +function DiaryReadModal(props) { + const { refetch } = props; const [diaryState, setDiaryState] = useRecoilState(diaryAtom); const userState = useRecoilValue(userAtom); const [shapeData, setShapeData] = React.useState(""); @@ -116,7 +117,7 @@ function DiaryReadModal() { ); return ( - + {data.title} - {diaryState.isDelete ? : null} + {diaryState.isDelete ? : null} ); } diff --git a/FE/src/components/DiaryModal/DiaryUpdateModal.js b/FE/src/components/DiaryModal/DiaryUpdateModal.js index c258024..3c8fedd 100644 --- a/FE/src/components/DiaryModal/DiaryUpdateModal.js +++ b/FE/src/components/DiaryModal/DiaryUpdateModal.js @@ -1,5 +1,5 @@ import React, { useEffect, useRef } from "react"; -import { useRecoilValue } from "recoil"; +import { useRecoilState, useRecoilValue } from "recoil"; import { useMutation, useQuery } from "react-query"; import styled from "styled-components"; import userAtom from "../../atoms/userAtom"; @@ -17,17 +17,6 @@ async function getShapeFn() { }).then((res) => res.json()); } -async function updateDiaryFn(data) { - return fetch("http://223.130.129.145:3005/diaries", { - method: "PUT", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${data.accessToken}`, - }, - body: JSON.stringify(data.diaryData), - }); -} - async function getDiary(accessToken, diaryUuid) { return fetch(`http://223.130.129.145:3005/diaries/${diaryUuid}`, { method: "GET", @@ -39,22 +28,40 @@ async function getDiary(accessToken, diaryUuid) { } // TODO: 일기 데이터 수정 API 연결 -function DiaryUpdateModal() { +function DiaryUpdateModal(props) { + const { refetch } = props; const titleRef = useRef(null); const contentRef = useRef(null); const [isInput, setIsInput] = React.useState(true); const userState = useRecoilValue(userAtom); - const diaryState = useRecoilValue(diaryAtom); + const [diaryState, setDiaryState] = useRecoilState(diaryAtom); const [diaryData, setDiaryData] = React.useState({ title: "test", content: "test", date: "2023-11-20", - point: "0,0,0", + point: diaryState.diaryPoint, tags: [], shapeUuid: "cf3a074a-0707-40c4-a598-c7c17a654476", uuid: diaryState.diaryUuid, }); + async function updateDiaryFn(data) { + return fetch("http://223.130.129.145:3005/diaries", { + method: "PUT", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${data.accessToken}`, + }, + body: JSON.stringify(data.diaryData), + }).then(() => { + refetch(); + setDiaryState((prev) => ({ + ...prev, + isLoading: true, + })); + }); + } + useEffect(() => { const handleBeforeUnload = (e) => { e.preventDefault(); @@ -117,6 +124,7 @@ function DiaryUpdateModal() { ...diaryData, title: data.title, content: data.content, + date: data.date, tags: data.tags, }); titleRef.current.value = data.title; @@ -127,20 +135,20 @@ function DiaryUpdateModal() { if (isLoading) return ( - + Loading... ); if (isError) return ( - + 에러 발생 ); return ( - + 바뀐 별의 이야기를 적어주세요. diff --git a/FE/src/pages/MainPage.js b/FE/src/pages/MainPage.js index a58a9cc..a0e926b 100644 --- a/FE/src/pages/MainPage.js +++ b/FE/src/pages/MainPage.js @@ -1,6 +1,7 @@ /* eslint-disable */ import React, { useEffect } from "react"; +import { useQuery } from "react-query"; import styled from "styled-components"; import { useRecoilState, useSetRecoilState, useRecoilValue } from "recoil"; import diaryAtom from "../atoms/diaryAtom"; @@ -8,16 +9,33 @@ import shapeAtom from "../atoms/shapeAtom"; import userAtom from "../atoms/userAtom"; import DiaryCreateModal from "../components/DiaryModal/DiaryCreateModal"; import DiaryReadModal from "../components/DiaryModal/DiaryReadModal"; -import background from "../assets/background.png"; import DiaryListModal from "../components/DiaryModal/DiaryListModal"; import DiaryUpdateModal from "../components/DiaryModal/DiaryUpdateModal"; import DiaryLoadingModal from "../components/DiaryModal/DiaryLoadingModal"; +import StarPage from "./StarPage"; function MainPage() { const [diaryState, setDiaryState] = useRecoilState(diaryAtom); const userState = useRecoilValue(userAtom); const [shapeState, setShapeState] = useRecoilState(shapeAtom); + const { refetch } = useQuery( + "diaryList", + () => + fetch("http://223.130.129.145:3005/diaries", { + method: "GET", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${userState.accessToken}`, + }, + }).then((res) => res.json()), + { + onSuccess: (data) => { + setDiaryState((prev) => ({ ...prev, diaryList: data })); + }, + }, + ); + useEffect(() => { setDiaryState((prev) => { const newState = { @@ -90,9 +108,11 @@ function MainPage() { dangerouslySetInnerHTML={{ __html: shape.data }} /> ))} - {diaryState.isCreate ? : null} - {diaryState.isRead ? : null} - {diaryState.isUpdate ? : null} + + + {diaryState.isCreate ? : null} + {diaryState.isRead ? : null} + {diaryState.isUpdate ? : null} {diaryState.isList ? : null} {diaryState.isLoading ? : null} @@ -102,10 +122,6 @@ function MainPage() { // TODO: 배경 이미지 제거하고 영상으로 대체할 것 const MainPageWrapper = styled.div` height: 100vh; - background-image: url(${background}); - background-repeat: no-repeat; - background-position: center; - background-size: cover; display: flex; justify-content: center; diff --git a/FE/src/pages/StarPage.js b/FE/src/pages/StarPage.js new file mode 100644 index 0000000..d447e39 --- /dev/null +++ b/FE/src/pages/StarPage.js @@ -0,0 +1,193 @@ +/* eslint-disable react/no-unknown-property */ + +import React from "react"; +import { useRecoilState, useSetRecoilState } from "recoil"; +import styled from "styled-components"; +import { Canvas, useThree } from "@react-three/fiber"; +import { Sphere, OrbitControls } from "@react-three/drei"; +import * as THREE from "three"; +import diaryAtom from "../atoms/diaryAtom"; + +function StarPage() { + return ( + + + + + + + + ); +} + +function StarView() { + const { scene, raycaster, camera } = useThree(); + const [diaryState, setDiaryState] = useRecoilState(diaryAtom); + + const material = new THREE.ShaderMaterial({ + uniforms: { + color1: { value: new THREE.Color("#656990") }, + color2: { value: new THREE.Color("#182683") }, + gradientStart: { value: 0.001 }, + }, + vertexShader: ` + varying vec2 vUv; + void main() { + vUv = uv; + gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); + } + `, + fragmentShader: ` + uniform vec3 color1; + uniform vec3 color2; + uniform float gradientStart; + varying vec2 vUv; + void main() { + vec3 finalColor = mix( + color1, + color2, + smoothstep(gradientStart, gradientStart + 0.15, vUv.y) + ); + gl_FragColor = vec4(finalColor, 1.0); + } + `, + }); + + const moveToStar = async (e, fn) => { + raycaster.set(new THREE.Vector3(0, 0, 0), e.point); + const intersects = raycaster.intersectObjects(scene.children); + + if (intersects.length > 0) { + const [ntx, nty, ntz] = intersects[0].point.toArray().map((v) => -v); + const targetPosition = new THREE.Vector3(ntx, nty, ntz); + + const animate = () => { + const currentPosition = camera.position.clone(); + const direction = targetPosition.clone().sub(currentPosition); + const distance = direction.length(); + + if (distance > 0.05) { + const moveDistance = Math.min(0.05, distance); + direction.normalize().multiplyScalar(moveDistance); + camera.position.add(direction); + requestAnimationFrame(animate); + } else { + fn(e.point.toArray()); + } + }; + + animate(); + } + }; + + return ( + <> + { + setDiaryState((prev) => ({ + ...prev, + isRead: false, + })); + }} + onDoubleClick={(e) => { + setDiaryState((prev) => ({ + ...prev, + isCreate: false, + isRead: false, + diaryPoint: `${e.point.x},${e.point.y},${e.point.z}`, + })); + + moveToStar(e, () => { + setDiaryState((prev) => ({ + ...prev, + isCreate: true, + isRead: false, + })); + }); + }} + > + + + + + + + + {diaryState.diaryList?.map((diary) => ( + + ))} + + ); +} + +function Star(props) { + const { uuid, position, moveToStar } = props; + const setDiaryState = useSetRecoilState(diaryAtom); + + return ( + { + e.stopPropagation(); + e.object.scale.set(1.5, 1.5, 1.5); + e.object.material.emissiveIntensity = 1; + }} + onPointerLeave={(e) => { + e.stopPropagation(); + e.object.scale.set(1, 1, 1); + e.object.material.emissiveIntensity = 0.7; + }} + onClick={(e) => { + e.stopPropagation(); + setDiaryState((prev) => ({ + ...prev, + isCreate: false, + isRead: false, + diaryUuid: uuid, + diaryPoint: `${e.point.x},${e.point.y},${e.point.z}`, + })); + moveToStar(e, () => { + setDiaryState((prev) => ({ + ...prev, + isCreate: false, + isRead: true, + })); + }); + }} + > + + + + + ); +} + +const CanvasContainer = styled.div` + width: 100%; + height: 100%; + background-color: #000000; + + position: absolute; + top: 0; + left: 0; +`; + +export default StarPage; diff --git a/docker/be/Dockerfile b/docker/be/Dockerfile new file mode 100644 index 0000000..c5623cb --- /dev/null +++ b/docker/be/Dockerfile @@ -0,0 +1,21 @@ +FROM node:20.10.0-alpine3.18 + +LABEL author jeongmin + +WORKDIR /usr/src/app + +ENV LANG=ko_KR.UTF-8 \ + LANGUAGE=ko_KR.UTF-8 + +RUN apk add --update git && \ + apk --no-cache add tzdata && \ + cp /usr/share/zoneinfo/Asia/Seoul /etc/localtime && \ + echo "Asia/Seoul" > /etc/timezone + +RUN git clone https://github.com/boostcampwm2023/web08-ByeolSoop . \ + && cd BE && npm install --force + +WORKDIR /usr/src/app/BE + +CMD [ "npm", "run", "start" ] + diff --git a/docker/fe/Dockerfile b/docker/fe/Dockerfile new file mode 100644 index 0000000..4cc57f5 --- /dev/null +++ b/docker/fe/Dockerfile @@ -0,0 +1,22 @@ +FROM node:20.10.0-alpine3.18 + +LABEL author jeongmin + +WORKDIR /usr/src/app + +ENV LANG=ko_KR.UTF-8 \ + LANGUAGE=ko_KR.UTF-8 + +RUN apk add --update git && \ + apk --no-cache add tzdata && \ + cp /usr/share/zoneinfo/Asia/Seoul /etc/localtime && \ + echo "Asia/Seoul" > /etc/timezone + +RUN git clone https://github.com/boostcampwm2023/web08-ByeolSoop . \ + && cd FE && npm install --force + +WORKDIR /usr/src/app/FE + +RUN find ./src/ -type f |xargs sed -i 's/223.130.129.145:3005/223.130.129.145\/api/g' + +CMD [ "npm", "run", "start" ]