diff --git a/next.config.js b/next.config.js index 91ef62f0..5875f726 100644 --- a/next.config.js +++ b/next.config.js @@ -1,6 +1,9 @@ /** @type {import('next').NextConfig} */ const nextConfig = { reactStrictMode: true, + images: { + domains: ['posepicker-image.s3.ap-northeast-2.amazonaws.com'], + }, }; module.exports = nextConfig; diff --git a/package.json b/package.json index f636a06d..e4ea5211 100644 --- a/package.json +++ b/package.json @@ -11,9 +11,11 @@ }, "dependencies": { "@emotion/css": "^11.11.2", + "@tanstack/react-query": "^4.32.6", "@types/node": "20.4.5", "@types/react": "18.2.17", "@types/react-dom": "18.2.7", + "axios": "^1.4.0", "eslint": "^8.46.0", "eslint-config-next": "^13.4.12", "framer-motion": "^10.15.0", @@ -33,7 +35,6 @@ "eslint-plugin-jsx-a11y": "^6.7.1", "eslint-plugin-prettier": "^5.0.0", "eslint-plugin-react": "^7.33.1", - "eslint-plugin-react-hooks": "^4.6.0", "husky": "^8.0.3", "postcss": "^8.4.27", "prettier": "^3.0.0", diff --git a/src/apis/apis.ts b/src/apis/apis.ts new file mode 100644 index 00000000..eeba0dbd --- /dev/null +++ b/src/apis/apis.ts @@ -0,0 +1,35 @@ +import { UseQueryOptions, useQuery } from '@tanstack/react-query'; + +import publicApi from './config/publicApi'; + +export const getPoseTalk = () => publicApi.get('/pose/talk'); +export const usePoseTalkQuery = () => + useQuery(['poseTalk'], getPoseTalk, { + enabled: false, + }); + +export const getPoseDetail = (poseId: number) => publicApi.get(`/pose/${poseId}`); +export const usePoseDetailQuery = (poseId: number) => + useQuery(['poseId', poseId], () => getPoseDetail(poseId)); + +export interface PosePickResponse { + poseInfo: { + createdAt: string; + frameCount: number; + imageKey: string; + peopleCount: number; + poseId: number; + source: string; + sourceUrl: string; + tagAttributes: string; + updatedAt: string; + }; +} + +export const getPosePick = (peopleCount: number) => + publicApi.get(`/pose/pick/${peopleCount}`); + +export const usePosePickQuery = ( + peopleCount: number, + options?: UseQueryOptions +) => useQuery(['posePick', peopleCount], () => getPosePick(peopleCount), options); diff --git a/src/apis/config/publicApi.ts b/src/apis/config/publicApi.ts new file mode 100644 index 00000000..1a780c38 --- /dev/null +++ b/src/apis/config/publicApi.ts @@ -0,0 +1,13 @@ +import axios from 'axios'; + +import { CustomInstance } from './type'; +import { BASE_API_URL } from '@/constants'; + +const publicApi: CustomInstance = axios.create({ + baseURL: `${BASE_API_URL}/api`, + withCredentials: true, +}); + +publicApi.interceptors.response.use((response) => response.data); + +export default publicApi; diff --git a/src/apis/config/type.ts b/src/apis/config/type.ts new file mode 100644 index 00000000..5e0c6614 --- /dev/null +++ b/src/apis/config/type.ts @@ -0,0 +1,22 @@ +import { + AxiosInstance, + type AxiosInterceptorManager, + AxiosResponse, + type InternalAxiosRequestConfig, +} from 'axios'; + +export type CustomResponseFormat = T; + +export interface CustomInstance extends AxiosInstance { + interceptors: { + request: AxiosInterceptorManager; + response: AxiosInterceptorManager>; + }; + get(...params: Parameters): Promise; + delete(...params: Parameters): Promise; + post(...params: Parameters): Promise; + put(...params: Parameters): Promise; + patch(...params: Parameters): Promise; +} + +export type ErrorStatus = 400 | 401 | 403 | 412 | 500; diff --git a/src/app/(Main)/components/Tab.tsx b/src/app/(Main)/components/Tab.tsx index 8b0ebd9a..3c58854b 100644 --- a/src/app/(Main)/components/Tab.tsx +++ b/src/app/(Main)/components/Tab.tsx @@ -26,7 +26,7 @@ export default function Tab() {
) : ( - +
{item.title}
)} diff --git a/src/app/(Main)/pick/components/PickSection.tsx b/src/app/(Main)/pick/components/PickSection.tsx index 9411cf70..fe05ac5b 100644 --- a/src/app/(Main)/pick/components/PickSection.tsx +++ b/src/app/(Main)/pick/components/PickSection.tsx @@ -1,20 +1,31 @@ 'use client'; -import { BottomFixedButton } from '@/components/Button'; -import { Spacing } from '@/components/Spacing'; import Image from 'next/image'; import { useState } from 'react'; -import lottieJson from '../../../../../public/lotties/posepicker.json'; import Lottie from 'react-lottie-player'; +import lottieJson from '../../../../../public/lotties/posepicker.json'; +import { usePosePickQuery } from '@/apis/apis'; +import { BottomFixedButton } from '@/components/Button'; +import { Spacing } from '@/components/Spacing'; + const countList = ['1인', '2인', '3인', '4인', '5인+']; export default function PickSection() { const [countState, setCountState] = useState('1인'); const [isLoading, setIsLoading] = useState(false); + const [image, setImage] = useState(''); + const { refetch, data } = usePosePickQuery(+countState[0], { + enabled: false, + onSuccess: (data) => { + if (!data) return; + setImage(data.poseInfo.imageKey); + }, + }); const handlePickClick = () => { setIsLoading(true); + refetch(); setTimeout(() => { setIsLoading(false); }, 3000); @@ -38,7 +49,7 @@ export default function PickSection() { {isLoading ? ( ) : ( - image + image )}
diff --git a/src/app/QueryProvider.tsx b/src/app/QueryProvider.tsx new file mode 100644 index 00000000..4c4a5b6d --- /dev/null +++ b/src/app/QueryProvider.tsx @@ -0,0 +1,22 @@ +'use client'; + +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { useState } from 'react'; + +import type { StrictPropsWithChildren } from '@/types'; + +export default function QueryProvider({ children }: StrictPropsWithChildren) { + const [queryClient] = useState(() => new QueryClient()); + + queryClient.setDefaultOptions({ + queries: { + retry: 1, + refetchOnMount: false, + refetchOnWindowFocus: false, + refetchOnReconnect: false, + suspense: true, + }, + }); + + return {children}; +} diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 5ab3f375..4d6c3cb9 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -5,6 +5,8 @@ import '../../styles/typography.css'; import type { Metadata } from 'next'; +import QueryProvider from './QueryProvider'; + export const metadata: Metadata = { title: 'PosePicker', description: 'PosePicker FE by @guesung, @seondal', @@ -48,8 +50,10 @@ export default function RootLayout({ children }: { children: React.ReactNode })
- {children} -
+ + {children} +
+
diff --git a/src/constants/index.ts b/src/constants/index.ts new file mode 100644 index 00000000..a6539ee4 --- /dev/null +++ b/src/constants/index.ts @@ -0,0 +1 @@ +export const BASE_API_URL = process.env.NEXT_PUBLIC_API_URL; diff --git a/yarn.lock b/yarn.lock index 00337fc6..b396dfa6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -363,6 +363,19 @@ dependencies: tslib "^2.4.0" +"@tanstack/query-core@4.32.6": + version "4.32.6" + resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-4.32.6.tgz#cf7df91ab1542e67a82624fefb12a55f580b4c01" + integrity sha512-YVB+mVWENQwPyv+40qO7flMgKZ0uI41Ph7qXC2Zf1ft5AIGfnXnMZyifB2ghhZ27u+5wm5mlzO4Y6lwwadzxCA== + +"@tanstack/react-query@^4.32.6": + version "4.32.6" + resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-4.32.6.tgz#f88c2d57f423d4cd295c501df2edeb3285a301bd" + integrity sha512-AITu/IKJJJXsHHeXNBy5bclu12t08usMCY0vFC2dh9SP/w6JAk5U9GwfjOIPj3p+ATADZvxQPe8UiCtMLNeQbg== + dependencies: + "@tanstack/query-core" "4.32.6" + use-sync-external-store "^1.2.0" + "@types/json-schema@^7.0.12": version "7.0.12" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.12.tgz#d70faba7039d5fca54c83c7dbab41051d2b6f6cb" @@ -696,6 +709,11 @@ ast-types-flow@^0.0.7: resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.7.tgz#f70b735c6bca1a5c9c22d982c3e39e7feba3bdad" integrity sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag== +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + autoprefixer@^10.4.14: version "10.4.14" resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.14.tgz#e28d49902f8e759dd25b153264e862df2705f79d" @@ -718,6 +736,15 @@ axe-core@^4.6.2: resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.7.2.tgz#040a7342b20765cb18bb50b628394c21bccc17a0" integrity sha512-zIURGIS1E1Q4pcrMjp+nnEh+16G56eG/MUllJH8yEvw7asDo7Ac9uhC9KIH5jzpITueEZolfYglnCGIuSBz39g== +axios@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.4.0.tgz#38a7bf1224cd308de271146038b551d725f0be1f" + integrity sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA== + dependencies: + follow-redirects "^1.15.0" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + axobject-query@^3.1.1: version "3.2.1" resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-3.2.1.tgz#39c378a6e3b06ca679f29138151e45b2b32da62a" @@ -884,6 +911,13 @@ color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + commander@^4.0.0: version "4.1.1" resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" @@ -984,6 +1018,11 @@ define-properties@^1.1.3, define-properties@^1.1.4, define-properties@^1.2.0: has-property-descriptors "^1.0.0" object-keys "^1.1.1" +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + dequal@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be" @@ -1239,11 +1278,6 @@ eslint-plugin-react-hooks@5.0.0-canary-7118f5dd7-20230705: resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.0.0-canary-7118f5dd7-20230705.tgz#4d55c50e186f1a2b0636433d2b0b2f592ddbccfd" integrity sha512-AZYbMo/NW9chdL7vk6HQzQhT+PvTAEVqWk9ziruUoW2kAOcN5qNyelv70e0F1VNQAbvutOC9oc+xfWycI9FxDw== -eslint-plugin-react-hooks@^4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz#4c3e697ad95b77e93f8646aaa1630c1ba607edd3" - integrity sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g== - eslint-plugin-react@^7.31.7, eslint-plugin-react@^7.33.1: version "7.33.1" resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.33.1.tgz#bc27cccf860ae45413a4a4150bf0977345c1ceab" @@ -1462,6 +1496,11 @@ flatted@^3.1.0: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787" integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== +follow-redirects@^1.15.0: + version "1.15.2" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" + integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== + for-each@^0.3.3: version "0.3.3" resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" @@ -1469,6 +1508,15 @@ for-each@^0.3.3: dependencies: is-callable "^1.1.3" +form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + fraction.js@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.2.0.tgz#448e5109a313a3527f5a3ab2119ec4cf0e0e2950" @@ -2057,6 +2105,18 @@ micromatch@^4.0.4, micromatch@^4.0.5: braces "^3.0.2" picomatch "^2.3.1" +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.12: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" @@ -2458,6 +2518,11 @@ prop-types@^15.8.1: object-assign "^4.1.1" react-is "^16.13.1" +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + punycode@^2.1.0: version "2.3.0" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" @@ -3002,6 +3067,11 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" +use-sync-external-store@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a" + integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA== + util-deprecate@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"