diff --git a/.pnp.cjs b/.pnp.cjs
index ca2f71a..1e2c04d 100755
--- a/.pnp.cjs
+++ b/.pnp.cjs
@@ -2868,6 +2868,37 @@ const RAW_RUNTIME_STATE =
"linkType": "HARD"\
}]\
]],\
+ ["@nestjs/throttler", [\
+ ["npm:5.1.0", {\
+ "packageLocation": "./.yarn/cache/@nestjs-throttler-npm-5.1.0-12d7d9629c-8dc900ca1d.zip/node_modules/@nestjs/throttler/",\
+ "packageDependencies": [\
+ ["@nestjs/throttler", "npm:5.1.0"]\
+ ],\
+ "linkType": "SOFT"\
+ }],\
+ ["virtual:2499dbb93d824027565d71b0716c4fb8b548ad61955d0a0286bfb3c5b4058e227894b6691d96808c00f576db14870018375210362c26ee321ea99fd6ed041c74#npm:5.1.0", {\
+ "packageLocation": "./.yarn/__virtual__/@nestjs-throttler-virtual-676666150b/0/cache/@nestjs-throttler-npm-5.1.0-12d7d9629c-8dc900ca1d.zip/node_modules/@nestjs/throttler/",\
+ "packageDependencies": [\
+ ["@nestjs/throttler", "virtual:2499dbb93d824027565d71b0716c4fb8b548ad61955d0a0286bfb3c5b4058e227894b6691d96808c00f576db14870018375210362c26ee321ea99fd6ed041c74#npm:5.1.0"],\
+ ["@nestjs/common", "virtual:2499dbb93d824027565d71b0716c4fb8b548ad61955d0a0286bfb3c5b4058e227894b6691d96808c00f576db14870018375210362c26ee321ea99fd6ed041c74#npm:10.2.8"],\
+ ["@nestjs/core", "virtual:2499dbb93d824027565d71b0716c4fb8b548ad61955d0a0286bfb3c5b4058e227894b6691d96808c00f576db14870018375210362c26ee321ea99fd6ed041c74#npm:10.2.8"],\
+ ["@types/nestjs__common", null],\
+ ["@types/nestjs__core", null],\
+ ["@types/reflect-metadata", null],\
+ ["md5", "npm:2.3.0"],\
+ ["reflect-metadata", "npm:0.1.13"]\
+ ],\
+ "packagePeers": [\
+ "@nestjs/common",\
+ "@nestjs/core",\
+ "@types/nestjs__common",\
+ "@types/nestjs__core",\
+ "@types/reflect-metadata",\
+ "reflect-metadata"\
+ ],\
+ "linkType": "HARD"\
+ }]\
+ ]],\
["@nestjs/typeorm", [\
["npm:10.0.0", {\
"packageLocation": "./.yarn/cache/@nestjs-typeorm-npm-10.0.0-74e9c291bc-716f7e9596.zip/node_modules/@nestjs/typeorm/",\
@@ -6949,6 +6980,15 @@ const RAW_RUNTIME_STATE =
"linkType": "HARD"\
}]\
]],\
+ ["charenc", [\
+ ["npm:0.0.2", {\
+ "packageLocation": "./.yarn/cache/charenc-npm-0.0.2-aca0c2f207-a45ec39363.zip/node_modules/charenc/",\
+ "packageDependencies": [\
+ ["charenc", "npm:0.0.2"]\
+ ],\
+ "linkType": "HARD"\
+ }]\
+ ]],\
["chart.js", [\
["npm:4.4.1", {\
"packageLocation": "./.yarn/cache/chart.js-npm-4.4.1-bea8f3ff67-ef0cd10118.zip/node_modules/chart.js/",\
@@ -7608,6 +7648,15 @@ const RAW_RUNTIME_STATE =
"linkType": "HARD"\
}]\
]],\
+ ["crypt", [\
+ ["npm:0.0.2", {\
+ "packageLocation": "./.yarn/cache/crypt-npm-0.0.2-033627d94f-adbf263441.zip/node_modules/crypt/",\
+ "packageDependencies": [\
+ ["crypt", "npm:0.0.2"]\
+ ],\
+ "linkType": "HARD"\
+ }]\
+ ]],\
["crypto", [\
["npm:1.0.1", {\
"packageLocation": "./.yarn/cache/crypto-npm-1.0.1-7cb8e3dca6-fcf7dbd68a.zip/node_modules/crypto/",\
@@ -9952,6 +10001,15 @@ const RAW_RUNTIME_STATE =
"linkType": "HARD"\
}]\
]],\
+ ["is-buffer", [\
+ ["npm:1.1.6", {\
+ "packageLocation": "./.yarn/cache/is-buffer-npm-1.1.6-08199d9ccc-ae18aa0b6e.zip/node_modules/is-buffer/",\
+ "packageDependencies": [\
+ ["is-buffer", "npm:1.1.6"]\
+ ],\
+ "linkType": "HARD"\
+ }]\
+ ]],\
["is-callable", [\
["npm:1.2.7", {\
"packageLocation": "./.yarn/cache/is-callable-npm-1.2.7-808a303e61-ceebaeb9d9.zip/node_modules/is-callable/",\
@@ -11563,6 +11621,18 @@ const RAW_RUNTIME_STATE =
"linkType": "HARD"\
}]\
]],\
+ ["md5", [\
+ ["npm:2.3.0", {\
+ "packageLocation": "./.yarn/cache/md5-npm-2.3.0-86c49d3915-14a21d597d.zip/node_modules/md5/",\
+ "packageDependencies": [\
+ ["md5", "npm:2.3.0"],\
+ ["charenc", "npm:0.0.2"],\
+ ["crypt", "npm:0.0.2"],\
+ ["is-buffer", "npm:1.1.6"]\
+ ],\
+ "linkType": "HARD"\
+ }]\
+ ]],\
["mdast-util-find-and-replace", [\
["npm:3.0.1", {\
"packageLocation": "./.yarn/cache/mdast-util-find-and-replace-npm-3.0.1-284ae6ddf8-1faca98c4e.zip/node_modules/mdast-util-find-and-replace/",\
@@ -14705,6 +14775,7 @@ const RAW_RUNTIME_STATE =
["@nestjs/schematics", "virtual:2499dbb93d824027565d71b0716c4fb8b548ad61955d0a0286bfb3c5b4058e227894b6691d96808c00f576db14870018375210362c26ee321ea99fd6ed041c74#npm:10.0.3"],\
["@nestjs/swagger", "virtual:2499dbb93d824027565d71b0716c4fb8b548ad61955d0a0286bfb3c5b4058e227894b6691d96808c00f576db14870018375210362c26ee321ea99fd6ed041c74#npm:7.1.16"],\
["@nestjs/testing", "virtual:2499dbb93d824027565d71b0716c4fb8b548ad61955d0a0286bfb3c5b4058e227894b6691d96808c00f576db14870018375210362c26ee321ea99fd6ed041c74#npm:10.2.8"],\
+ ["@nestjs/throttler", "virtual:2499dbb93d824027565d71b0716c4fb8b548ad61955d0a0286bfb3c5b4058e227894b6691d96808c00f576db14870018375210362c26ee321ea99fd6ed041c74#npm:5.1.0"],\
["@nestjs/typeorm", "virtual:2499dbb93d824027565d71b0716c4fb8b548ad61955d0a0286bfb3c5b4058e227894b6691d96808c00f576db14870018375210362c26ee321ea99fd6ed041c74#npm:10.0.0"],\
["@types/cookie-parser", "npm:1.4.6"],\
["@types/express", "npm:4.17.21"],\
diff --git a/.yarn/cache/@nestjs-throttler-npm-5.1.0-12d7d9629c-8dc900ca1d.zip b/.yarn/cache/@nestjs-throttler-npm-5.1.0-12d7d9629c-8dc900ca1d.zip
new file mode 100644
index 0000000..6feb014
Binary files /dev/null and b/.yarn/cache/@nestjs-throttler-npm-5.1.0-12d7d9629c-8dc900ca1d.zip differ
diff --git a/.yarn/cache/charenc-npm-0.0.2-aca0c2f207-a45ec39363.zip b/.yarn/cache/charenc-npm-0.0.2-aca0c2f207-a45ec39363.zip
new file mode 100644
index 0000000..10ef15c
Binary files /dev/null and b/.yarn/cache/charenc-npm-0.0.2-aca0c2f207-a45ec39363.zip differ
diff --git a/.yarn/cache/crypt-npm-0.0.2-033627d94f-adbf263441.zip b/.yarn/cache/crypt-npm-0.0.2-033627d94f-adbf263441.zip
new file mode 100644
index 0000000..0de38b1
Binary files /dev/null and b/.yarn/cache/crypt-npm-0.0.2-033627d94f-adbf263441.zip differ
diff --git a/.yarn/cache/is-buffer-npm-1.1.6-08199d9ccc-ae18aa0b6e.zip b/.yarn/cache/is-buffer-npm-1.1.6-08199d9ccc-ae18aa0b6e.zip
new file mode 100644
index 0000000..082d5a6
Binary files /dev/null and b/.yarn/cache/is-buffer-npm-1.1.6-08199d9ccc-ae18aa0b6e.zip differ
diff --git a/.yarn/cache/leva-npm-0.9.35-da054c7dd9-9555db5e0b.zip b/.yarn/cache/leva-npm-0.9.35-da054c7dd9-9555db5e0b.zip
index b550a17..68dab39 100644
Binary files a/.yarn/cache/leva-npm-0.9.35-da054c7dd9-9555db5e0b.zip and b/.yarn/cache/leva-npm-0.9.35-da054c7dd9-9555db5e0b.zip differ
diff --git a/.yarn/cache/md5-npm-2.3.0-86c49d3915-14a21d597d.zip b/.yarn/cache/md5-npm-2.3.0-86c49d3915-14a21d597d.zip
new file mode 100644
index 0000000..6770dd3
Binary files /dev/null and b/.yarn/cache/md5-npm-2.3.0-86c49d3915-14a21d597d.zip differ
diff --git a/.yarn/cache/react-joyride-npm-2.7.1-78984ee8a0-4aefbe8a6a.zip b/.yarn/cache/react-joyride-npm-2.7.1-78984ee8a0-4aefbe8a6a.zip
index 54df1e4..6ea818c 100644
Binary files a/.yarn/cache/react-joyride-npm-2.7.1-78984ee8a0-4aefbe8a6a.zip and b/.yarn/cache/react-joyride-npm-2.7.1-78984ee8a0-4aefbe8a6a.zip differ
diff --git a/.yarn/install-state.gz b/.yarn/install-state.gz
index 2e30fbc..6fdfc73 100644
Binary files a/.yarn/install-state.gz and b/.yarn/install-state.gz differ
diff --git a/README.md b/README.md
index e74cf09..4b8a3f2 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,45 @@
-# π λ³ νλμ κΈ νλ π
+# λ³ νλμ κΈ νλ π
+
μμ λ§μ μ°μ£Όμ κΈ°μ΅μ λ΄μ λ³μ λμ°λ μΉ μΆμ΅ μ μ₯μ
+
+
+
+
+## λͺ©μ°¨
+
+### [1. νλ‘μ νΈ μκ°]()
+
+- [<λ³ νλμ κΈ νλ>λ₯Ό λ§λ€κ² λ κ³κΈ°]()
+- [κΈ°λ₯ μ€λͺ
]()
+- [νλ‘μ νΈ μ€ν λ°©λ²]()
+
+### [2. κΈ°μ μ€ν]()
+
+### [3. κΈ°μ μ λμ ]()
+
+### [4. νμ μκ°](#πββοΈ-νμ-μκ°)
+
+### [5. νμ νκ³ ]()
+
+## <λ³ νλμ κΈ νλ> μλΉμ€ μκ°
+
+### κΈ°λ₯ μ€λͺ
+
+1. λ‘κ³ νλ©΄ -> λ‘κ·ΈμΈ νλ©΄ -> λ‘κ·ΈμΈνκ³ μν λ€μ΄κ°λ κ²κΉμ§
+2. νμκ°μ
νκ³ μν λ€μ΄κ°λ κ²κΉμ§
+3. μ½μΉλ§ν¬ 보μ¬μ£ΌκΈ°
+4. levaλ‘ μ‘°μ νλ κ² λ³΄μ¬μ£ΌκΈ°
+5. μν μμ νλ κ² λ³΄μ¬μ£ΌκΈ°
+6. κΈμ°κΈ° μ°½ 보μ¬μ£ΌκΈ°
+7. κΈμ°κΈ° μ΄ν λ³ μ»€μ€ν
νλ μ°½ 보μ¬μ£ΌκΈ° -> μ€μ μμ±λ λ³ λ³΄μ¬μ£ΌκΈ°
+8. λ³ ν΄λ¦ν΄μ μμ νκ³ μμ νλ νλ©΄
+9. 곡μ λͺ¨λ¬μμ κ²μ μν μμ νλ νλ©΄
+10. 곡μ λ§ν¬ 볡μ¬ν΄μ μ€μ λ‘ λ€μ΄κ°λ νλ©΄
+11. λ€λ₯Έ μ¬λ μν κ²μν΄μ λ€μ΄κ°λ νλ©΄
+
### **"λ΄ μΆμ λ°μ§μ΄λ κΈ°μ΅λ€μ μνλ‘ λ§λ€ μ μλ€λ©΄ μ΄λ¨κΉ?"**
λ¨κ²¨λκ³ μΆμ μκ°μ μ°μ μ¬μ§κ³Ό, κ·Έ μκ°μ λ μ¬λ¦¬λ©° μ μ κΈμ λ³μ λ΄μ΅λλ€.
@@ -18,7 +55,6 @@
### βοΈΒ λ³κΈ μμ±νκΈ°
> μμ€ν κΈ°μ΅μ μμ§ μλλ‘
->
μ¬μ§κ³Ό κΈμ λ΄μ κΈ°μ΅μ κΈ°λ‘νκ³ , λλ§μ λ³μ μμ±ν μ μμ΄μ.
@@ -29,7 +65,6 @@
### πΒ μν λλ¬λ³΄κΈ°
> 차곑차곑 μμμ¨ μΆμ΅λ€μ ν λμ λ³Ό μ μλλ‘
->
μμ±λ λ³κΈλ€μ΄ λͺ¨μ¬ μλ¦λ€μ΄ μνκ° λΌμ.
@@ -40,7 +75,6 @@
### πΒ μ°μ£Ό 곡μ νκΈ°
> λ΄ μΆμ κΈ°λ‘λ€μ μμ€ν μ¬λλ€κ³Ό ν¨κ» λλ μ μλλ‘
->
λλ§μ μ°μ£Όλ₯Ό λ§ν¬λ₯Ό μ΄μ©ν΄ 곡μ ν μ μμ΄μ.
@@ -49,11 +83,43 @@
### πͺπ» μμΈν νλ‘μ νΈ μ§νκ³Όμ μ wikiλ₯Ό μ°Έκ³ ν΄μ£ΌμΈμ!
+
https://github.com/boostcampwm2023/web16-B1G1/wiki
-## π <λ³ νλμ κΈ νλ>λ₯Ό κ°μ΄ λ§λ€μ΄κ°λ μ¬λλ€
+## βοΈ κΈ°μ μ€ν
+
+[π wiki κΈ°μ μ€ν μκ° λ°λ‘κ°κΈ°](https://github.com/boostcampwm2023/web16-B1G1/wiki/%EA%B8%B0%EC%88%A0%EC%8A%A4%ED%83%9D-%EC%86%8C%EA%B0%9C)
+
+
+
+
+
+### κΈ°μ μ€ν μ μ μ΄μ μ κ΄ν νμλ€μ κΈ
+
+- [μ°λ¦¬ νμ΄ Zustandλ₯Ό μ°λ μ΄μ ](https://velog.io/@greencloud/%EC%9A%B0%EB%A6%AC-%ED%8C%80%EC%9D%B4-Zustand%EB%A5%BC-%EC%93%B0%EB%8A%94-%EC%9D%B4%EC%9C%A0)
+- [Emotion μ ν μ κ³ λ €μ¬ν](https://velog.io/@200tiger/Emotion-%EC%84%A0%ED%83%9D%EC%8B%9C-%EA%B3%A0%EB%A0%A4%EC%82%AC%ED%95%AD)
+- [Yarn berryλ‘ λͺ¨λ
Έλ ν¬ κ΅¬μ±νκΈ°](https://velog.io/@minboykim/Yarn-berry%EB%A1%9C-%EB%AA%A8%EB%85%B8%EB%A0%88%ED%8F%AC-%EA%B5%AC%EC%84%B1%ED%95%98%EA%B8%B0)
+- [Vite, μ μ°λκ±°μ§?](https://velog.io/@minboykim/Vite-%EC%99%9C-%EC%93%B0%EB%8A%94%EA%B1%B0%EC%A7%80)
+- [κΈ°μ μ€ν μ μ μ΄μ (NestJS, TypeORM, Docker, GitHub Actions)](https://velog.io/@qkrwogk/%EA%B8%B0%EC%88%A0%EC%8A%A4%ED%83%9D-%EC%84%A0%EC%A0%95%EC%9D%B4%EC%9C%A0-NestJS-TypeORM-Docker-GitHub-Actions)
+
+
+
+## πͺπ» κΈ°μ μ λμ
+
+### FE
+
+**Three.js + React-Three-Fiberλ₯Ό μ¬μ©ν μ°μ£Ό κ³΅κ° κ΅¬ν**
+
+### BE
+
+
+
+## πββοΈ νμ μκ°
+
+[π wiki νμ μκ° λ°λ‘κ°κΈ°](https://github.com/boostcampwm2023/web16-B1G1/wiki/%ED%8C%80%EC%9B%90-%EC%86%8C%EA%B0%9C)
+
@@ -91,56 +157,34 @@ https://github.com/boostcampwm2023/web16-B1G1/wiki
|
-
-
-## π νμ μκ°
### π J010 κΉκ°μ (FE)
+
- λΈλ‘κ·Έ: https://velog.io/@greencloud
+- κΉνλΈ: https://github.com/KimGaeun0806
- <λ³ νλμ κΈ νλ>μμμ λͺ©ν: νλ‘μ νΈ κ³Όμ νλνλ λͺ¨λ κΈ°λ‘μΌλ‘ λ¨κΈ°κΈ°. κΈ°μ λΈλ‘κ·Έ μ΄μ¬ν μ¨λ³΄κΈ° π»
-- TMI: μ΅κ·Όμ ν¬μ€ μμνμ΅λλ€. πͺπ»
-- MBTI: ESTJ
-- νμλ€μκ² ν λ§λ: ν볡νμΈμ
### π§ J016 κΉλλ―Ό (FE)
+
- λΈλ‘κ·Έ: https://minboykim.github.io/
+- κΉνλΈ: https://github.com/MinboyKim
- <λ³ νλμ κΈ νλ>μμμ λͺ©ν: μ’μμ¬λλ€κ³Ό μ’μμκ°λ³΄λ΄κΈ° βοΈ
-- TMI: νκ·μ΄ μΈμμ μ§λ°°ν κ²λλ€.
-- MBTI: ENTP
-- νμλ€μκ² ν λ§λ: μμμμ νμ΄ν
### πΎ J053 λ°μ¬ν (BE)
+
- λΈλ‘κ·Έ: https://velog.io/@qkrwogk
+- κΉνλΈ: https://github.com/qkrwogk
- <λ³ νλμ κΈ νλ>μμμ λͺ©ν: λ₯ λ€μ΄λΈ κ²½ν! π
-- TMI: μ λμ΄ μλ§μμγ
γ
-- MBTI: ENTJ
-- νμλ€μκ² ν λ§λ: λͺ¨λ 건κ°ν©μλ€.
### β½οΈ J073 μ‘μ€μ (BE)
+
- λΈλ‘κ·Έ: https://velog.io/@songjseop
+- κΉνλΈ: https://github.com/SongJSeop
- <λ³ νλμ κΈ νλ>μμμ λͺ©ν: νμλ€κ³Ό νν μλ μκ° λ³΄λ΄κΈ°
-- TMI: μ°μ 3λ
μ°¨ γ
γ
νμ λ¨ λλ³΄μ§ λ§μμΌ
-- MBTI: ENFP
-- νμλ€μκ² ν λ§λ: κ°μ΄ μΆκ΅¬ ν μ¬λ?
### π° J098 μ΄λ°±λ² (FE)
+
- λΈλ‘κ·Έ: https://velog.io/@200tiger
+- κΉνλΈ: https://github.com/bananaba
- <λ³ νλμ κΈ νλ>μμμ λͺ©ν: μ¬λ―Έμλ κ²°κ³Όλ¬Ό λ§λ€κΈ°!
-- TMI: μΌμν¬ νμΆμ΄ μΈμ μνμμ!
-- MBTI: INTP
-- νμλ€μκ² ν λ§λ: μ’μ μμΉ¨!
-
-## π κΈ°μ μ€ν
-
-
-
-
-
-| κ΅¬λΆ | κΈ°μ μ€ν |
-| :---: | :----: |
-| Frontend | React-Three-Fiber, Zustand, Emotion |
-| Backend | TypeORM |
-| DB | |
-| CI/CD | |
-| Deployment | Naver Cloud Platform |
diff --git a/packages/client/src/entities/like/Like.tsx b/packages/client/src/entities/like/Like.tsx
index afe400a..83875a6 100644
--- a/packages/client/src/entities/like/Like.tsx
+++ b/packages/client/src/entities/like/Like.tsx
@@ -1,8 +1,9 @@
import { useEffect, useReducer, useState } from 'react';
import { Heart } from 'lucide-react';
-import { Button } from 'shared/ui';
+import { AlertDialog, Button } from 'shared/ui';
import theme from 'shared/ui/styles/theme';
import instance from 'shared/apis/core/AxiosInterceptor';
+import { useNavigate } from 'react-router-dom';
interface PropsType {
postId: string;
@@ -46,6 +47,8 @@ export default function Like({ postId, count }: PropsType) {
likeCount: count,
});
const [isButtonDisabled, setButtonDisabled] = useState(false);
+ const [alert, setAlert] = useState(false);
+ const navigate = useNavigate();
useEffect(() => {
const fetchLike = async () => {
@@ -60,14 +63,21 @@ export default function Like({ postId, count }: PropsType) {
const clickLike = async () => {
if (isButtonDisabled) return;
+ setButtonDisabled(true);
+
+ const path = location.pathname.split('/')[1];
+ if (path === 'guest') {
+ setAlert(true);
+ setButtonDisabled(false);
+ return;
+ }
try {
- setButtonDisabled(true);
- const res = await instance({
+ await instance({
method: 'patch',
url: `/post/${postId}/like`,
});
- if (res.status === 200) dispatch({ type: LIKE });
+ dispatch({ type: LIKE });
} finally {
setButtonDisabled(false);
}
@@ -78,33 +88,48 @@ export default function Like({ postId, count }: PropsType) {
try {
setButtonDisabled(true);
- const res = await instance({
+ await instance({
method: 'patch',
url: `/post/${postId}/unlike`,
});
- if (res.status === 200) dispatch({ type: UNLIKE });
+ dispatch({ type: UNLIKE });
} finally {
setButtonDisabled(false);
}
};
return (
-
+ <>
+
+ {alert && (
+ setAlert(false)}
+ onClickActionButton={() => {
+ setAlert(false);
+ navigate('/login');
+ }}
+ disabled={false}
+ />
+ )}
+ >
);
}
diff --git a/packages/client/src/entities/posts/ui/Post.tsx b/packages/client/src/entities/posts/ui/Post.tsx
index 7ccdda7..2ddb058 100644
--- a/packages/client/src/entities/posts/ui/Post.tsx
+++ b/packages/client/src/entities/posts/ui/Post.tsx
@@ -6,8 +6,8 @@ import styled from '@emotion/styled';
import { useViewStore } from 'shared/store/useViewStore';
import * as THREE from 'three';
import { StarType } from 'shared/lib/types/star';
-import { useLocation, useNavigate } from 'react-router-dom';
-import { useState } from 'react';
+import { useLocation, useNavigate, useParams } from 'react-router-dom';
+import { useState, useEffect } from 'react';
import theme from 'shared/ui/styles/theme';
import Star from 'features/star/Star';
@@ -27,6 +27,12 @@ export default function Post({ data, postId, title }: PropsType) {
const navigate = useNavigate();
const location = useLocation();
+ const { postId: id } = useParams();
+
+ useEffect(() => {
+ if (id && Number(id) === postId) setTargetView(meshRef.current);
+ }, [id]);
+
const handleMeshClick = (e: ThreeEvent) => {
e.stopPropagation();
diff --git a/packages/client/src/features/controls/Controls.tsx b/packages/client/src/features/controls/Controls.tsx
index 6a73b4a..d1bb619 100644
--- a/packages/client/src/features/controls/Controls.tsx
+++ b/packages/client/src/features/controls/Controls.tsx
@@ -56,7 +56,7 @@ export default function Controls() {
useEffect(() => {
setCameraToCurrentView(currentView.distanceTo(state.camera.position));
- }, []);
+ }, [view]);
useEffect(() => {
setTargetView(null);
@@ -77,7 +77,6 @@ export default function Controls() {
distance.z = 0;
if (distance.length() > LENGTH_LIMIT) distance.setLength(LENGTH_LIMIT);
state.camera.position.add(distance);
- setCameraToCurrentView(currentView.distanceTo(state.camera.position));
}
if (targetPosition !== currentView) {
diff --git a/packages/client/src/features/postModal/ui/PostModal.tsx b/packages/client/src/features/postModal/ui/PostModal.tsx
index eae22af..8fb47f4 100644
--- a/packages/client/src/features/postModal/ui/PostModal.tsx
+++ b/packages/client/src/features/postModal/ui/PostModal.tsx
@@ -1,5 +1,5 @@
import { useViewStore } from 'shared/store/useViewStore';
-import { Button, Modal, ModalPortal, TextArea } from 'shared/ui';
+import { Button, Modal, TextArea } from 'shared/ui';
import ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
import styled from '@emotion/styled';
@@ -64,32 +64,31 @@ export default function PostModal() {
}
};
- const rightButton = (
-
- );
-
- const EditButton = (
-
+ const RightButton = (
+
+
+
+
);
const EditCancelButton = (
@@ -127,6 +126,7 @@ export default function PostModal() {
setToast({ text: 'κΈμ΄ μμ λμμ΅λλ€.', type: 'success' });
setView('MAIN');
navigate('/home');
+ setTargetView(null);
} finally {
setIsDeleteButtonDisabled(false);
}
@@ -147,11 +147,11 @@ export default function PostModal() {
return (
data && (
-
+ <>
}
@@ -164,7 +164,7 @@ export default function PostModal() {
)}
{isEdit ? (
-
+
)}
-
+ >
)
);
}
@@ -233,10 +233,10 @@ const Container = styled.div`
const TextContainer = styled.div`
width: 40vw;
- height: 100%;
${({ theme: { colors } }) => ({
color: colors.text.secondary,
})}
+ word-break: break-all;
& ol {
padding-left: 40px;
@@ -263,3 +263,8 @@ const ImageContainer = styled.div`
justify-content: center;
margin-bottom: 26px;
`;
+
+const ButtonContainer = styled.div`
+ display: flex;
+ gap: 8px;
+`;
diff --git a/packages/client/src/features/star/Star.tsx b/packages/client/src/features/star/Star.tsx
index 9adea16..bf67097 100644
--- a/packages/client/src/features/star/Star.tsx
+++ b/packages/client/src/features/star/Star.tsx
@@ -33,9 +33,9 @@ const Star = forwardRef((props, ref) => {
} = props;
useFrame((state, delta) => {
- const cameraDistance = innerRef.current.position.distanceTo(
- state.camera.position,
- );
+ const cameraDistance = innerRef.current
+ .getWorldPosition(new THREE.Vector3())
+ .distanceTo(state.camera.position);
const scale = Math.log((cameraDistance / DISTANCE_LIMIT) * Math.E);
if (cameraDistance > DISTANCE_LIMIT) {
@@ -62,11 +62,6 @@ const Star = forwardRef((props, ref) => {
emissive={color}
emissiveIntensity={brightness}
/>
- {/* */}
{children}
);
diff --git a/packages/client/src/features/writingModal/ui/WritingModal.tsx b/packages/client/src/features/writingModal/ui/WritingModal.tsx
index b7b9472..6d5596d 100644
--- a/packages/client/src/features/writingModal/ui/WritingModal.tsx
+++ b/packages/client/src/features/writingModal/ui/WritingModal.tsx
@@ -1,7 +1,6 @@
import { useState } from 'react';
import { Button, Modal } from 'shared/ui';
import TextArea from 'shared/ui/textArea/TextArea';
-import { ModalPortal } from 'shared/ui';
import Images from './Images';
import { useNavigate, useLocation } from 'react-router-dom';
import { useViewStore, usePostStore } from 'shared/store';
@@ -45,7 +44,7 @@ export default function WritingModal() {
};
return (
-
+ <>
{isClose && (
-
+ >
);
}
diff --git a/packages/client/src/pages/Home/Home.tsx b/packages/client/src/pages/Home/Home.tsx
index 1ec3641..abee5ab 100644
--- a/packages/client/src/pages/Home/Home.tsx
+++ b/packages/client/src/pages/Home/Home.tsx
@@ -13,14 +13,14 @@ import {
} from 'widgets/galaxy/lib/constants';
import useCheckNickName from 'shared/hooks/useCheckNickName';
import { FullScreen, useFullScreenHandle } from 'react-full-screen';
-import styled from '@emotion/styled';
-import { keyframes } from '@emotion/react';
import UnderBar from 'widgets/underBar/UnderBar';
import UpperBar from 'widgets/upperBar/UpperBar';
import CoachMarker from 'features/coachMarker/CoachMarker';
export default function Home() {
- const [isSwitching, setIsSwitching] = useState(false);
+ const [isSwitching, setIsSwitching] = useState<'warp' | 'fade' | 'end'>(
+ 'end',
+ );
const { text, type } = useToastStore();
const { nickName, status } = useCheckNickName();
@@ -30,9 +30,11 @@ export default function Home() {
const custom = useCustomStore();
useEffect(() => {
- setIsSwitching(true);
-
+ if (!JSON.parse(sessionStorage.getItem('isReload') ?? 'false'))
+ setIsSwitching('warp');
if (nickName === '') return;
+ sessionStorage.setItem('isReload', 'false');
+
getGalaxy(nickName).then((res) => {
if (!res.spiral) setSpiral(SPIRAL);
else {
@@ -60,6 +62,14 @@ export default function Home() {
});
}, [nickName]);
+ useEffect(() => {
+ const setReload = () => sessionStorage.setItem('isReload', 'true');
+
+ window.addEventListener('beforeunload', setReload);
+
+ return () => window.removeEventListener('beforeunload', setReload);
+ }, []);
+
const keyDown = (e: KeyboardEvent) => {
if (e.key === 'Escape') {
e.preventDefault();
@@ -80,10 +90,11 @@ export default function Home() {
return (
- {status === 'new' && }
- {isSwitching && }
- {!isSwitching && }
+ {status === 'new' && }
+ {isSwitching !== 'end' && (
+
+ )}
{text && {text}}
@@ -93,24 +104,3 @@ export default function Home() {
);
}
-
-const fadeout = keyframes`
- 0% {
- opacity: 1;
- }
- 100% {
- opacity: 0;
- display: none;
- }
-`;
-
-const FadeoutScreen = styled.div`
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- z-index: 101;
- background-color: white;
- animation: ${fadeout} 0.5s linear forwards;
-`;
diff --git a/packages/client/src/pages/Landing/Landing.tsx b/packages/client/src/pages/Landing/Landing.tsx
index 3aa0f79..e8c7645 100644
--- a/packages/client/src/pages/Landing/Landing.tsx
+++ b/packages/client/src/pages/Landing/Landing.tsx
@@ -1,25 +1,16 @@
import LandingScreen from 'widgets/landingScreen/LandingScreen';
-import { useState } from 'react';
import { useToastStore } from 'shared/store/useToastStore';
import { Toast } from 'shared/ui';
import { Outlet } from 'react-router-dom';
export default function Landing() {
- const [mouse, setMouse] = useState([0.5, 0.5]);
const { text, type } = useToastStore();
return (
- {
- setMouse([
- e.clientX / window.innerWidth,
- e.clientY / window.innerHeight,
- ]);
- }}
- >
+ <>
{text && {text}}
-
-
+
+ >
);
}
diff --git a/packages/client/src/shared/apis/search.ts b/packages/client/src/shared/apis/search.ts
index d7da39d..215c0d7 100644
--- a/packages/client/src/shared/apis/search.ts
+++ b/packages/client/src/shared/apis/search.ts
@@ -8,3 +8,12 @@ export const getNickNames = async (nickName: string) => {
return data;
};
+
+export const checkExistNickname = async (nickName: string) => {
+ const { data } = await instance({
+ method: 'GET',
+ url: `/auth/check-nickname?nickname=${nickName}`,
+ });
+ if (data === false) return Promise.reject();
+ return data;
+};
diff --git a/packages/client/src/shared/hooks/useCheckNickName.ts b/packages/client/src/shared/hooks/useCheckNickName.ts
index d41eb1b..95e0cac 100644
--- a/packages/client/src/shared/hooks/useCheckNickName.ts
+++ b/packages/client/src/shared/hooks/useCheckNickName.ts
@@ -1,18 +1,18 @@
import { useEffect, useState } from 'react';
-import { useLocation } from 'react-router-dom';
+import { useLocation, useParams } from 'react-router-dom';
import { getSignInInfo } from 'shared/apis';
import { getShareLinkHostNickName } from 'shared/apis/share';
+import Cookies from 'js-cookie';
export default function useCheckNickName() {
const location = useLocation();
const [page, setPage] = useState('');
const [nickName, setNickName] = useState('');
const [status, setStatus] = useState('');
- const [owner, setOwner] = useState('');
+ const { hostNickname } = useParams();
useEffect(() => {
const path = location.pathname.split('/')[1];
- const hostNickName = location.pathname.split('/')[2];
switch (path) {
case 'home':
@@ -21,19 +21,19 @@ export default function useCheckNickName() {
const res = await getSignInInfo();
setNickName(res.nickname);
setStatus(res.status);
- setOwner(res.nickname);
+ Cookies.set('userName', res.nickname);
})();
break;
case 'search':
setPage('search');
- setNickName(hostNickName);
+ setNickName(hostNickname!);
break;
case 'guest':
setPage('guest');
(async () => {
- const res = await getShareLinkHostNickName(hostNickName);
+ const res = await getShareLinkHostNickName(hostNickname!);
setNickName(res);
})();
break;
@@ -42,5 +42,5 @@ export default function useCheckNickName() {
}
}, [location]);
- return { page, nickName, status, owner };
+ return { page, nickName, status };
}
diff --git a/packages/client/src/shared/ui/alert/Alert.tsx b/packages/client/src/shared/ui/alert/Alert.tsx
index c9188f7..6a79f10 100644
--- a/packages/client/src/shared/ui/alert/Alert.tsx
+++ b/packages/client/src/shared/ui/alert/Alert.tsx
@@ -1,7 +1,7 @@
import styled from '@emotion/styled';
import { Body02ME, Title01 } from '../styles';
import { css } from '@emotion/react';
-import { Button } from '..';
+import { Button, ModalPortal } from '..';
import { useState, useEffect } from 'react';
interface PropsTypes extends React.HTMLAttributes {
@@ -29,23 +29,25 @@ export default function Alert({ title, description, ...args }: PropsTypes) {
}, []);
return (
-
-
- {title}
- {description && {description}}
-
-
-
-
-
-
+
+
+
+ {title}
+ {description && {description}}
+
+
+
+
+
+
+
);
}
diff --git a/packages/client/src/shared/ui/alertDialog/AlertDialog.tsx b/packages/client/src/shared/ui/alertDialog/AlertDialog.tsx
index 2ed1b0f..62e5499 100644
--- a/packages/client/src/shared/ui/alertDialog/AlertDialog.tsx
+++ b/packages/client/src/shared/ui/alertDialog/AlertDialog.tsx
@@ -1,7 +1,7 @@
import styled from '@emotion/styled';
import { Body02ME, Title01 } from '../styles';
import { css } from '@emotion/react';
-import { Button } from '..';
+import { Button, ModalPortal } from '..';
interface PropsTypes extends React.HTMLAttributes {
title: string;
@@ -24,32 +24,34 @@ export default function AlertDialog({
...args
}: PropsTypes) {
return (
-
-
- {title}
- {description && {description}}
+
+
+
+ {title}
+ {description && {description}}
-
-
-
-
-
-
+
+
+
+
+
+
+
);
}
diff --git a/packages/client/src/shared/ui/buttons/Button.tsx b/packages/client/src/shared/ui/buttons/Button.tsx
index 3fe7d82..d03976a 100644
--- a/packages/client/src/shared/ui/buttons/Button.tsx
+++ b/packages/client/src/shared/ui/buttons/Button.tsx
@@ -24,6 +24,7 @@ const CustomButton = styled.button`
gap: 2px;
border-radius: 4px;
box-shadow: 0px 2px 10px 5px rgba(13, 111, 252, 0.1);
+ white-space: nowrap;
&:disabled {
cursor: default;
diff --git a/packages/client/src/shared/ui/buttons/IconButton.tsx b/packages/client/src/shared/ui/buttons/IconButton.tsx
index d572ed3..39db246 100644
--- a/packages/client/src/shared/ui/buttons/IconButton.tsx
+++ b/packages/client/src/shared/ui/buttons/IconButton.tsx
@@ -18,6 +18,7 @@ const CustomButton = styled.button`
padding: 8px;
gap: 10px;
border-radius: 8px;
+ white-space: nowrap;
${({ theme: { colors } }) => css`
border: 1px solid ${colors.stroke.default};
diff --git a/packages/client/src/shared/ui/buttons/TextButton.tsx b/packages/client/src/shared/ui/buttons/TextButton.tsx
index 874fc6e..afaf6c4 100644
--- a/packages/client/src/shared/ui/buttons/TextButton.tsx
+++ b/packages/client/src/shared/ui/buttons/TextButton.tsx
@@ -22,6 +22,7 @@ const CustomButton = styled.button`
gap: 4px;
background: none;
border: none;
+ white-space: nowrap;
${({ size, theme: { colors } }) => css`
${size === 'm' ? Body02ME : Body03ME}
diff --git a/packages/client/src/shared/ui/modal/Modal.tsx b/packages/client/src/shared/ui/modal/Modal.tsx
index 0a9601c..6d28be9 100644
--- a/packages/client/src/shared/ui/modal/Modal.tsx
+++ b/packages/client/src/shared/ui/modal/Modal.tsx
@@ -3,9 +3,9 @@ import { Body02ME, Title02 } from '../styles';
import { ReactNode } from 'react';
import { css } from '@emotion/react';
import goBackIcon from '@icons/icon-back-32-white.svg';
-import { IconButton } from '..';
+import { IconButton, ModalPortal } from '..';
-interface PropsTypes extends React.HTMLAttributes {
+interface PropsTypes extends React.HTMLAttributes {
title: string;
children: ReactNode;
@@ -29,35 +29,37 @@ export default function Modal({
const isButtonExist = leftButton || rightButton;
return (
-
-
- {onClickGoBack && (
-
-
-
- )}
-
-
-
-
- {title}
- {topButton}
-
-
- {description && {description}}
-
-
- {children}
-
- {isButtonExist && (
-
- {leftButton}
- {rightButton}
-
+
+
+
+ {onClickGoBack && (
+
+
+
)}
-
-
-
+
+
+
+
+ {title}
+ {topButton}
+
+
+ {description && {description}}
+
+
+ {children}
+
+ {isButtonExist && (
+
+ {leftButton}
+ {rightButton}
+
+ )}
+
+
+
+
);
}
@@ -70,7 +72,7 @@ const Overlay = styled.div`
z-index: 998;
`;
-const Layout = styled.div`
+const Layout = styled.form`
position: absolute;
top: 50%;
left: 50%;
diff --git a/packages/client/src/shared/ui/textArea/TextArea.tsx b/packages/client/src/shared/ui/textArea/TextArea.tsx
index d5f87df..8087dc9 100644
--- a/packages/client/src/shared/ui/textArea/TextArea.tsx
+++ b/packages/client/src/shared/ui/textArea/TextArea.tsx
@@ -98,9 +98,8 @@ const Tabs = styled.ul`
}
`;
-const Tab = styled.li`
+const Tab = styled.div`
cursor: pointer;
- list-style: none;
`;
const TextInput = styled.textarea<{ css: SerializedStyles }>`
@@ -150,6 +149,7 @@ const Wrapper = styled.div<{ css: SerializedStyles }>`
margin-top: 9px;
margin-right: 9px;
color: ${theme.colors.text.secondary};
+ word-break: break-all;
& ol {
padding-left: 40px;
diff --git a/packages/client/src/widgets/galaxyCustomModal/GalaxyCustomModal.tsx b/packages/client/src/widgets/galaxyCustomModal/GalaxyCustomModal.tsx
index bdf2c51..258707f 100644
--- a/packages/client/src/widgets/galaxyCustomModal/GalaxyCustomModal.tsx
+++ b/packages/client/src/widgets/galaxyCustomModal/GalaxyCustomModal.tsx
@@ -26,9 +26,11 @@ export default function GalaxyCustomModal() {
useRefresh('CUSTOM');
- const handleSubmit = async () => {
+ const handleSubmit = async (e: React.FormEvent) => {
+ e.preventDefault();
if (isSubmitButtonDisabled) return;
setIsSubmitButtonDisabled(true);
+
const galaxyStyle = {
spiral: galaxy.spiral !== spiral ? spiral : undefined,
start: galaxy.start !== start ? start : undefined,
@@ -40,50 +42,48 @@ export default function GalaxyCustomModal() {
galaxy.setStart(start);
galaxy.setThickness(thickness);
galaxy.setZDist(zDist);
- await postGalaxy(galaxyStyle);
+
+ try {
+ await postGalaxy(galaxyStyle);
+
+ setToast({ text: 'μνκ° μμ λμμ΅λλ€.', type: 'success' });
+ navigate('/home');
+ setView('MAIN');
+ } finally {
+ setIsSubmitButtonDisabled(false);
+ }
};
return (
-
+
+
+
+
+ {dialog && (
+ setDialog(false)}
+ onClickActionButton={() => {
+ navigate('/home');
+ setView('MAIN');
+ }}
+ disabled={false}
+ />
+ )}
+
);
}
diff --git a/packages/client/src/widgets/landingScreen/LandingScreen.tsx b/packages/client/src/widgets/landingScreen/LandingScreen.tsx
index 25290ad..2632cf1 100644
--- a/packages/client/src/widgets/landingScreen/LandingScreen.tsx
+++ b/packages/client/src/widgets/landingScreen/LandingScreen.tsx
@@ -3,23 +3,33 @@ import BackgroundStars from 'features/backgroundStars/BackgroundStars.tsx';
import { Galaxy } from '../galaxy/index.ts';
import { EffectComposer, Bloom } from '@react-three/postprocessing';
import { CAMERA_POSITION, CAMERA_UP, CAMERA_FAR } from './lib/camera.ts';
+import { Group, Object3DEventMap } from 'three';
+import { useRef, useEffect } from 'react';
-interface PropsType {
- mousePosition: number[];
-}
-
-export default function LandingScreen({
- mousePosition: [mouseX, mouseY],
-}: PropsType) {
+export default function LandingScreen() {
+ const galaxyRef = useRef>(null!);
const camera = {
position: CAMERA_POSITION,
up: CAMERA_UP,
far: CAMERA_FAR,
};
+ useEffect(() => {
+ const mouseHandler = (e: MouseEvent) => {
+ const mouseX = e.clientX / window.innerWidth;
+ const mouseY = e.clientY / window.innerHeight;
+ galaxyRef.current.rotation.x = (mouseY - 0.5) / 5;
+ galaxyRef.current.rotation.y = (mouseX - 0.5) / 5;
+ };
+
+ window.addEventListener('mousemove', mouseHandler);
+
+ return () => window.removeEventListener('mousemove', mouseHandler);
+ }, []);
+
return (
-
-
+
);
@@ -74,7 +80,7 @@ export default function Screen() {
const LevaWrapper = styled.div`
position: absolute;
- top: 5%;
+ top: 37px;
right: 50%;
transform: translateX(50%);
z-index: 100;
diff --git a/packages/client/src/widgets/screen/ui/LevaTheme.ts b/packages/client/src/widgets/screen/ui/LevaTheme.ts
new file mode 100644
index 0000000..90f9e5c
--- /dev/null
+++ b/packages/client/src/widgets/screen/ui/LevaTheme.ts
@@ -0,0 +1,24 @@
+import { LevaCustomTheme } from 'leva/dist/declarations/src/styles';
+import theme from 'shared/ui/styles/theme';
+
+const color = theme.colors;
+
+export const LevaTheme: LevaCustomTheme = {
+ colors: {
+ elevation1: color.background.bdp02,
+ elevation2: color.background.bdp01_80,
+ elevation3: color.background.bdp03,
+ accent1: color.stroke.focus,
+ accent2: color.primary.filled,
+ accent3: color.primary.pressed,
+ highlight1: color.stroke.sc,
+ highlight2: color.text.third,
+ highlight3: color.text.secondary,
+ },
+ fontSizes: {
+ root: '12px',
+ },
+ fonts: {
+ mono: 'pretendard_medium',
+ },
+};
diff --git a/packages/client/src/widgets/screen/ui/index.ts b/packages/client/src/widgets/screen/ui/index.ts
index b7d9697..33950ca 100644
--- a/packages/client/src/widgets/screen/ui/index.ts
+++ b/packages/client/src/widgets/screen/ui/index.ts
@@ -1 +1,2 @@
export { default as CameraLight } from './CameraLight';
+export * from './LevaTheme';
diff --git a/packages/client/src/widgets/shareModal/ui/LinkContainer.tsx b/packages/client/src/widgets/shareModal/ui/LinkContainer.tsx
index 1b91c6f..e10cd98 100644
--- a/packages/client/src/widgets/shareModal/ui/LinkContainer.tsx
+++ b/packages/client/src/widgets/shareModal/ui/LinkContainer.tsx
@@ -3,22 +3,22 @@ import { useToastStore } from 'shared/store';
import { Button, Input } from 'shared/ui';
import { useState, useEffect } from 'react';
import { getShareLink } from 'shared/apis/share';
-import useCheckNickName from 'shared/hooks/useCheckNickName';
+import Cookies from 'js-cookie';
export default function LinkContainer() {
const [shareLink, setShareLink] = useState('');
const { setToast } = useToastStore();
- const { owner } = useCheckNickName();
+ const user = Cookies.get('userName');
useEffect(() => {
- if (!owner) return;
+ if (!user) return;
(async () => {
- const shareLinkData = await getShareLink(owner);
+ const shareLinkData = await getShareLink(user);
setShareLink('https://www.xn--bj0b03z.site/' + 'guest/' + shareLinkData);
})();
- }, [owner]);
+ }, [user]);
const copyToClipboard = () => {
navigator.clipboard.writeText(shareLink);
diff --git a/packages/client/src/widgets/starCustomModal/StarCustomModal.tsx b/packages/client/src/widgets/starCustomModal/StarCustomModal.tsx
index de6c87d..bb32cfe 100644
--- a/packages/client/src/widgets/starCustomModal/StarCustomModal.tsx
+++ b/packages/client/src/widgets/starCustomModal/StarCustomModal.tsx
@@ -57,25 +57,26 @@ export default function StarCustomModal() {
const handleSubmit = async () => {
if (isSubmitButtonDisabled) return;
setIsSubmitButtonDisabled(true);
- const existingStars = await getMyPost();
- const starData = {
- shape: shapeTypes[shape],
- color,
- size,
- brightness,
- position: await generateStarPosition(existingStars, size),
- };
- const formData = new FormData();
+ try {
+ const existingStars = await getMyPost();
+ const starData = {
+ shape: shapeTypes[shape],
+ color,
+ size,
+ brightness,
+ position: await generateStarPosition(existingStars, size),
+ };
+ const formData = new FormData();
- formData.append('star', JSON.stringify(starData));
- formData.append('title', title);
- formData.append('content', content);
+ formData.append('star', JSON.stringify(starData));
+ formData.append('title', title);
+ formData.append('content', content);
- if (files) {
- for (let i = 0; i < files.length; i++) formData.append('file', files[i]);
- }
+ if (files) {
+ for (let i = 0; i < files.length; i++)
+ formData.append('file', files[i]);
+ }
- try {
await sendPost(formData);
setToast({ text: 'λ³μ μμ±νμ΅λλ€.', type: 'success' });
setView('MAIN');
@@ -97,33 +98,32 @@ export default function StarCustomModal() {
);
return (
-
+
+
+
);
}
diff --git a/packages/client/src/widgets/starCustomModal/ui/SentimentButton.tsx b/packages/client/src/widgets/starCustomModal/ui/SentimentButton.tsx
index b8bad4e..a9c165d 100644
--- a/packages/client/src/widgets/starCustomModal/ui/SentimentButton.tsx
+++ b/packages/client/src/widgets/starCustomModal/ui/SentimentButton.tsx
@@ -17,9 +17,12 @@ export default function SentimentButton({ content, setColor }: PropsType) {
const handleRecommendColor = async () => {
if (isButtonDisabled) return;
setIsButtonDisabled(true);
- const res = await getSentimentColor(content);
- if (res?.status === 200) setColor(res.data.color);
- setIsButtonDisabled(false);
+ try {
+ const res = await getSentimentColor(content);
+ setColor(res.data.color);
+ } finally {
+ setIsButtonDisabled(false);
+ }
};
return (
diff --git a/packages/client/src/widgets/underBar/UnderBar.tsx b/packages/client/src/widgets/underBar/UnderBar.tsx
index fffb381..61408e8 100644
--- a/packages/client/src/widgets/underBar/UnderBar.tsx
+++ b/packages/client/src/widgets/underBar/UnderBar.tsx
@@ -3,7 +3,7 @@ import { Button } from 'shared/ui';
import { Title01 } from '../../shared/ui/styles';
import PlanetEditIcon from '@icons/icon-planetedit-24-white.svg';
import WriteIcon from '@icons/icon-writte-24-white.svg';
-import { BASE_URL, MAX_WIDTH1, MAX_WIDTH2 } from 'shared/lib/constants';
+import { MAX_WIDTH1, MAX_WIDTH2 } from 'shared/lib/constants';
import { useNavigate } from 'react-router-dom';
import Cookies from 'js-cookie';
import instance from 'shared/apis/core/AxiosInterceptor';
@@ -31,14 +31,18 @@ export default function UnderBar() {
const handleLogoutButton = async () => {
if (isLogoutButtonDisabled) return;
setIsLogoutButtonDisabled(true);
- await instance.get(`${BASE_URL}auth/signout`);
-
- Cookies.remove('accessToken');
- Cookies.remove('refreshToken');
- reset();
-
- setIsLogoutButtonDisabled(false);
- navigate('/');
+ try {
+ await instance.get(`/auth/signout`);
+
+ Cookies.remove('accessToken');
+ Cookies.remove('refreshToken');
+ Cookies.remove('userName');
+ reset();
+
+ navigate('/');
+ } finally {
+ setIsLogoutButtonDisabled(false);
+ }
};
const handleShareButton = () => {
@@ -60,55 +64,58 @@ export default function UnderBar() {
{nickName}λμ μν
-
+ {page === 'home' && }
-
-
-
+ {page !== 'guest' && (
+
+ )}
+
+ {isMyPage && (
+
+ )}
-
-
-
- μν μμ νκΈ°
-
-
-
- κΈμ°κΈ°
-
-
+ {isMyPage && (
+
+
+
+ μν μμ νκΈ°
+
+
+
+ κΈμ°κΈ°
+
+
+ )}
);
@@ -144,7 +151,7 @@ const Layout = styled.div<{ view: string }>`
const NameContainer = styled.div`
display: flex;
align-items: center;
- gap: 24px;
+ gap: 16px;
`;
const ButtonsContainer = styled.div`
diff --git a/packages/client/src/widgets/upperBar/UpperBar.tsx b/packages/client/src/widgets/upperBar/UpperBar.tsx
index 6af6f24..9fd52f0 100644
--- a/packages/client/src/widgets/upperBar/UpperBar.tsx
+++ b/packages/client/src/widgets/upperBar/UpperBar.tsx
@@ -3,11 +3,11 @@ import { IconButton, Search } from 'shared/ui';
import goBackIcon from '@icons/icon-back-32-white.svg';
import { MAX_WIDTH1, MAX_WIDTH2 } from '@constants';
import { useState, useEffect } from 'react';
-import { getNickNames } from 'shared/apis/search';
-import { getIsAvailableNickName } from 'shared/apis';
+import { checkExistNickname, getNickNames } from 'shared/apis/search';
import { useToastStore, useViewStore } from 'shared/store';
import { useNavigate } from 'react-router-dom';
import useCheckNickName from 'shared/hooks/useCheckNickName';
+import Cookies from 'js-cookie';
export default function UpperBar() {
// TODO: ui λΆλ¦¬νκΈ°
@@ -17,8 +17,9 @@ export default function UpperBar() {
const [isSearchButtonDisabled, setIsSearchButtonDisabled] = useState(false);
const { setToast } = useToastStore();
- const { page, nickName, owner } = useCheckNickName();
+ const { page, nickName } = useCheckNickName();
const { view } = useViewStore();
+ const user = Cookies.get('userName');
const navigate = useNavigate();
@@ -44,7 +45,7 @@ export default function UpperBar() {
const nickNameDatas = await getNickNames(debouncedSearchValue);
const nickNames = nickNameDatas
.map((data: { nickname: string; id: number }) => data.nickname)
- .filter((nickName: string) => nickName !== owner)
+ .filter((nickName: string) => nickName !== user)
.slice(0, 5);
setSearchResults(nickNames);
@@ -55,19 +56,21 @@ export default function UpperBar() {
if (isSearchButtonDisabled) return;
setIsSearchButtonDisabled(true);
try {
- await getIsAvailableNickName(searchValue);
- setToast({ text: 'μ‘΄μ¬νμ§ μλ λλ€μμ
λλ€.', type: 'error' });
- } catch (error) {
- if (searchValue === owner)
+ await checkExistNickname(searchValue);
+ if (searchValue === user)
return setToast({
text: 'λ΄ μνλ‘λ μ΄λν μ μμ΅λλ€.',
type: 'error',
});
-
navigate(`/search/${searchValue}`);
setSearchValue('');
setDebouncedSearchValue('');
setSearchResults([]);
+ } catch (error) {
+ setToast({
+ text: 'μ‘΄μ¬νμ§ μλ μ μ μ
λλ€.',
+ type: 'error',
+ });
} finally {
setIsSearchButtonDisabled(false);
}
@@ -93,16 +96,18 @@ export default function UpperBar() {
-
-
-
+ {page !== 'guest' && (
+
+
+
+ )}
);
}
diff --git a/packages/client/src/widgets/warpScreen/WarpScreen.tsx b/packages/client/src/widgets/warpScreen/WarpScreen.tsx
index ebc075a..7085471 100644
--- a/packages/client/src/widgets/warpScreen/WarpScreen.tsx
+++ b/packages/client/src/widgets/warpScreen/WarpScreen.tsx
@@ -12,12 +12,15 @@ import {
import { EffectComposer, Bloom } from '@react-three/postprocessing';
import SpaceWarp from './ui/SpaceWarp';
import BrightSphere from './ui/BrightSphere';
+import styled from '@emotion/styled';
+import { keyframes } from '@emotion/react';
interface PropsType {
- setIsSwitching: React.Dispatch>;
+ isSwitching: 'warp' | 'fade' | 'end';
+ setIsSwitching: React.Dispatch>;
}
-export default function WarpScreen({ setIsSwitching }: PropsType) {
+export default function WarpScreen({ isSwitching, setIsSwitching }: PropsType) {
const camera = {
position: SPACE_WARP_CAMERA_POSITION,
up: SPACE_WARP_CAMERA_UP,
@@ -31,6 +34,7 @@ export default function WarpScreen({ setIsSwitching }: PropsType) {
zIndex: 999,
backgroundColor: '#000000',
};
+ if (isSwitching === 'fade') return ;
return (
@@ -50,3 +54,24 @@ export default function WarpScreen({ setIsSwitching }: PropsType) {
);
}
+
+const fadeout = keyframes`
+ 0% {
+ opacity: 1;
+ }
+ 100% {
+ opacity: 0;
+ display: none;
+ }
+`;
+
+const FadeoutScreen = styled.div`
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ z-index: 101;
+ background-color: white;
+ animation: ${fadeout} 0.5s linear forwards;
+`;
diff --git a/packages/client/src/widgets/warpScreen/ui/SpaceWarp.tsx b/packages/client/src/widgets/warpScreen/ui/SpaceWarp.tsx
index 19f00bc..9764c1f 100644
--- a/packages/client/src/widgets/warpScreen/ui/SpaceWarp.tsx
+++ b/packages/client/src/widgets/warpScreen/ui/SpaceWarp.tsx
@@ -34,7 +34,7 @@ const geSpaceWarpLinesInfo = () => {
};
interface PropsType {
- setIsSwitching: React.Dispatch>;
+ setIsSwitching: React.Dispatch>;
}
export default function SpaceWarp({ setIsSwitching }: PropsType) {
@@ -43,8 +43,10 @@ export default function SpaceWarp({ setIsSwitching }: PropsType) {
useFrame((state, delta) => {
if (state.camera.position.y <= 0) {
state.scene.background = new THREE.Color(0xffffff);
- setIsSwitching(false);
- } else state.camera.position.y -= 75000 * delta;
+ setIsSwitching('fade');
+ return;
+ }
+ state.camera.position.y -= 75000 * delta;
});
return (
diff --git a/packages/server/package.json b/packages/server/package.json
index 212fcff..9cd4c33 100644
--- a/packages/server/package.json
+++ b/packages/server/package.json
@@ -28,6 +28,7 @@
"@nestjs/passport": "^10.0.2",
"@nestjs/platform-express": "^10.0.0",
"@nestjs/swagger": "^7.1.16",
+ "@nestjs/throttler": "^5.1.0",
"@nestjs/typeorm": "^10.0.0",
"@types/multer": "^1.4.11",
"@types/sharp": "^0.32.0",
diff --git a/packages/server/src/app.module.ts b/packages/server/src/app.module.ts
index 25e4be2..22765b6 100644
--- a/packages/server/src/app.module.ts
+++ b/packages/server/src/app.module.ts
@@ -9,6 +9,7 @@ import { StarModule } from './star/star.module';
import { GalaxyModule } from './galaxy/galaxy.module';
import { AdminModule } from './admin/admin.module';
import { SentimentModule } from './sentiment/sentiment.module';
+import { ThrottlerModule } from '@nestjs/throttler';
@Module({
imports: [
@@ -20,6 +21,12 @@ import { SentimentModule } from './sentiment/sentiment.module';
GalaxyModule,
AdminModule,
SentimentModule,
+ ThrottlerModule.forRoot([
+ {
+ ttl: 10000, // 10μ΄μ
+ limit: 5, // 5λ²κΉμ§ μμ² κ°λ₯
+ },
+ ]),
],
})
export class AppModule {}
diff --git a/packages/server/src/board/board.controller.ts b/packages/server/src/board/board.controller.ts
index 48f856a..a2bcfe6 100644
--- a/packages/server/src/board/board.controller.ts
+++ b/packages/server/src/board/board.controller.ts
@@ -36,6 +36,7 @@ import { DeleteResult } from 'typeorm';
import { GetIsLikedSwaggerDecorator } from './decorators/swagger/get-is-liked-swagged.decorator';
import { HttpExceptionFilter } from '../exception-filter/http.exception-filter';
import { BoardService } from './board.service';
+import { ThrottlerGuard } from '@nestjs/throttler';
@Controller('post')
@UseInterceptors(LogInterceptor)
@@ -46,6 +47,7 @@ export class BoardController {
@Post()
@UseGuards(CookieAuthGuard)
+ @UseGuards(ThrottlerGuard)
@UseInterceptors(FilesInterceptor('file', 5))
@UsePipes(ValidationPipe)
@CreateBoardSwaggerDecorator()
@@ -113,6 +115,7 @@ export class BoardController {
@Patch(':id/like')
@UseGuards(CookieAuthGuard)
+ @UseGuards(ThrottlerGuard)
@UsePipes(ValidationPipe)
@PatchLikeSwaggerDecorator()
async patchLike(
diff --git a/packages/server/src/sentiment/sentiment.controller.ts b/packages/server/src/sentiment/sentiment.controller.ts
index a9dce9e..4342bc4 100644
--- a/packages/server/src/sentiment/sentiment.controller.ts
+++ b/packages/server/src/sentiment/sentiment.controller.ts
@@ -1,6 +1,7 @@
-import { Body, Controller, HttpCode, Post } from '@nestjs/common';
+import { Body, Controller, HttpCode, Post, UseGuards } from '@nestjs/common';
import { SentimentService } from './sentiment.service';
import { GetSentimentDto } from './dto/get-sentiment.dto';
+import { ThrottlerGuard } from '@nestjs/throttler';
@Controller('sentiment')
export class SentimentController {
@@ -8,6 +9,7 @@ export class SentimentController {
@Post()
@HttpCode(200)
+ @UseGuards(ThrottlerGuard)
getSentiment(@Body() body: GetSentimentDto) {
return this.sentimentService.getSentiment(body);
}
diff --git a/yarn.lock b/yarn.lock
index ee669ab..32e883d 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1858,6 +1858,19 @@ __metadata:
languageName: node
linkType: hard
+"@nestjs/throttler@npm:^5.1.0":
+ version: 5.1.0
+ resolution: "@nestjs/throttler@npm:5.1.0"
+ dependencies:
+ md5: "npm:^2.2.1"
+ peerDependencies:
+ "@nestjs/common": ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0
+ "@nestjs/core": ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0
+ reflect-metadata: ^0.1.13
+ checksum: 8dc900ca1d2ad303d05b248ab72bfd3d8851b76bb8be407523cd25d7285e3da19160d7023d9a069a9ef0965e2851a7b3407e677ebb36edcc4b42a90d497e21a0
+ languageName: node
+ linkType: hard
+
"@nestjs/typeorm@npm:^10.0.0":
version: 10.0.0
resolution: "@nestjs/typeorm@npm:10.0.0"
@@ -4908,6 +4921,13 @@ __metadata:
languageName: node
linkType: hard
+"charenc@npm:0.0.2":
+ version: 0.0.2
+ resolution: "charenc@npm:0.0.2"
+ checksum: a45ec39363a16799d0f9365c8dd0c78e711415113c6f14787a22462ef451f5013efae8a28f1c058f81fc01f2a6a16955f7a5fd0cd56247ce94a45349c89877d8
+ languageName: node
+ linkType: hard
+
"chart.js@npm:^4.4.1":
version: 4.4.1
resolution: "chart.js@npm:4.4.1"
@@ -5474,6 +5494,13 @@ __metadata:
languageName: node
linkType: hard
+"crypt@npm:0.0.2":
+ version: 0.0.2
+ resolution: "crypt@npm:0.0.2"
+ checksum: adbf263441dd801665d5425f044647533f39f4612544071b1471962209d235042fb703c27eea2795c7c53e1dfc242405173003f83cf4f4761a633d11f9653f18
+ languageName: node
+ linkType: hard
+
"crypto@npm:^1.0.1":
version: 1.0.1
resolution: "crypto@npm:1.0.1"
@@ -7552,6 +7579,13 @@ __metadata:
languageName: node
linkType: hard
+"is-buffer@npm:~1.1.6":
+ version: 1.1.6
+ resolution: "is-buffer@npm:1.1.6"
+ checksum: ae18aa0b6e113d6c490ad1db5e8df9bdb57758382b313f5a22c9c61084875c6396d50bbf49315f5b1926d142d74dfb8d31b40d993a383e0a158b15fea7a82234
+ languageName: node
+ linkType: hard
+
"is-callable@npm:^1.1.3":
version: 1.2.7
resolution: "is-callable@npm:1.2.7"
@@ -8894,6 +8928,17 @@ __metadata:
languageName: node
linkType: hard
+"md5@npm:^2.2.1":
+ version: 2.3.0
+ resolution: "md5@npm:2.3.0"
+ dependencies:
+ charenc: "npm:0.0.2"
+ crypt: "npm:0.0.2"
+ is-buffer: "npm:~1.1.6"
+ checksum: 14a21d597d92e5b738255fbe7fe379905b8cb97e0a49d44a20b58526a646ec5518c337b817ce0094ca94d3e81a3313879c4c7b510d250c282d53afbbdede9110
+ languageName: node
+ linkType: hard
+
"mdast-util-find-and-replace@npm:^3.0.0":
version: 3.0.1
resolution: "mdast-util-find-and-replace@npm:3.0.1"
@@ -11523,6 +11568,7 @@ __metadata:
"@nestjs/schematics": "npm:^10.0.0"
"@nestjs/swagger": "npm:^7.1.16"
"@nestjs/testing": "npm:^10.0.0"
+ "@nestjs/throttler": "npm:^5.1.0"
"@nestjs/typeorm": "npm:^10.0.0"
"@types/cookie-parser": "npm:^1.4.6"
"@types/express": "npm:^4.17.17"