From f00023f02e8f0ff4c43a6c6adadb06e320172cc2 Mon Sep 17 00:00:00 2001 From: Imhwitae Date: Tue, 2 Sep 2025 14:47:45 +0900 Subject: [PATCH 1/4] =?UTF-8?q?fix:=20=EC=BD=94=EB=93=9C=20=EB=A6=AC?= =?UTF-8?q?=EB=B7=B0=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 ++ src/components/Inquiry.jsx | 4 ++-- src/components/InquiryWriteArea.jsx | 3 +-- src/hooks/usePost.jsx | 9 --------- src/hooks/useService.jsx | 12 ++++++------ src/pages/items/ItemDetail.jsx | 4 ++-- src/services/inquiryApi.js | 10 ++++------ src/services/itemsApi.js | 8 ++++---- 8 files changed, 21 insertions(+), 31 deletions(-) delete mode 100644 src/hooks/usePost.jsx diff --git a/.gitignore b/.gitignore index a547bf36..cea8a0ba 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,5 @@ dist-ssr *.njsproj *.sln *.sw? + +*.env \ No newline at end of file diff --git a/src/components/Inquiry.jsx b/src/components/Inquiry.jsx index f1258fca..e669deea 100644 --- a/src/components/Inquiry.jsx +++ b/src/components/Inquiry.jsx @@ -6,7 +6,7 @@ import { import { requestInquiryLists } from "../services/inquiryApi"; import { useNavigate } from "react-router"; import icBack from "../assets/icons/ic_back.svg"; -import useService from "../hooks/useService"; +import useFetch from "../hooks/useService"; import InquiryWriteArea from "./InquiryWriteArea"; import InquiryList from "../pages/components/ItemDetail/InquiryList"; @@ -16,7 +16,7 @@ export default function Inquiry({ id }) { /** * 문의 내역을 가져온다. */ - const { data, isLoading } = useService(() => requestInquiryLists(id)); + const { data, isLoading } = useFetch(() => requestInquiryLists(id)); return ( <> diff --git a/src/components/InquiryWriteArea.jsx b/src/components/InquiryWriteArea.jsx index be264830..66e3d12e 100644 --- a/src/components/InquiryWriteArea.jsx +++ b/src/components/InquiryWriteArea.jsx @@ -1,5 +1,4 @@ import { useState } from "react"; -import usePost from "../hooks/usePost"; import { InquirySubmitButton, InquiryTextArea, @@ -30,7 +29,7 @@ export default function InquiryWriteArea() { }, }; - const { data: success } = usePost(requestPostInquiry(data)); + // const { data: success } = usePost(requestPostInquiry(data)); if (success) { location.reload(true); diff --git a/src/hooks/usePost.jsx b/src/hooks/usePost.jsx deleted file mode 100644 index 681432e1..00000000 --- a/src/hooks/usePost.jsx +++ /dev/null @@ -1,9 +0,0 @@ -import useService from "./useService"; - -const usePost = (postFetching) => { - const { data, isLoading, isError } = useService(() => postFetching); - - return { data, isLoading, isError }; -}; - -export default usePost; diff --git a/src/hooks/useService.jsx b/src/hooks/useService.jsx index eb137f8f..a3943f7f 100644 --- a/src/hooks/useService.jsx +++ b/src/hooks/useService.jsx @@ -3,18 +3,18 @@ import { useEffect, useState } from "react"; /** * 데이터 통신을 위한 공통 커스텀 훅 * @param {Function} fetchFunction 서버와 직접 통신하는 함수 - * @returns {data: object, isLoading: boolean} + * @returns {data: object, isLoading: boolean, error: boolean} */ -const useService = (fetchFunction) => { +const useFetch = (fetchFunction) => { const [isLoading, setIsLoading] = useState(false); const [data, setData] = useState(); const [error, setError] = useState(false); useEffect(() => { - const getService = async (getDataFunction) => { + const fetcher = async (fetching) => { try { setIsLoading(true); - const response = await getDataFunction(); + const response = await fetching(); if (!response) { throw new Error("서버와의 통신에 실패했습니다."); @@ -29,10 +29,10 @@ const useService = (fetchFunction) => { } }; - getService(fetchFunction); + fetcher(fetchFunction); }, []); return { data, isLoading, error }; }; -export default useService; +export default useFetch; diff --git a/src/pages/items/ItemDetail.jsx b/src/pages/items/ItemDetail.jsx index f21deeb3..9e0105fd 100644 --- a/src/pages/items/ItemDetail.jsx +++ b/src/pages/items/ItemDetail.jsx @@ -7,7 +7,7 @@ import { } from "../../styles/items/ItemDetailStyle"; import Inquiry from "../../components/Inquiry"; -import useService from "../../hooks/useService"; +import useFetch from "../../hooks/useService"; import ProductDetails from "../components/ItemDetail/ProductDetails"; export default function ItemDetail() { @@ -24,7 +24,7 @@ export default function ItemDetail() { /** * 상품 정보 받아오기 */ - const { data, isLoading } = useService(() => requestProductDetail(productId)); + const { data, isLoading } = useFetch(() => requestProductDetail(productId)); return ( <> diff --git a/src/services/inquiryApi.js b/src/services/inquiryApi.js index 0e05488b..e22edd35 100644 --- a/src/services/inquiryApi.js +++ b/src/services/inquiryApi.js @@ -1,11 +1,11 @@ +const BASE_URL = import.meta.env.VITE_BASE_URL; + /** * 문의 데이터를 가져온다. * @param {string} productId */ export const requestInquiryLists = async (productId) => { - const url = new URL( - `https://panda-market-api.vercel.app/products/${productId}/comments` - ); + const url = new URL(`${BASE_URL}products/${productId}/comments`); url.searchParams.append("limit", 3); const response = await fetch(url, { @@ -24,9 +24,7 @@ export const requestInquiryLists = async (productId) => { * @returns */ export const requestPostInquiry = async (inquiryData) => { - const url = new URL( - `https://panda-market-api.vercel.app/products/${inquiryData.productId}/comments` - ); + const url = new URL(`${BASE_URL}products/${inquiryData.productId}/comments`); try { const response = await fetch(url, { diff --git a/src/services/itemsApi.js b/src/services/itemsApi.js index 8148c414..2d26357f 100644 --- a/src/services/itemsApi.js +++ b/src/services/itemsApi.js @@ -1,10 +1,12 @@ +const BASE_URL = import.meta.env.VITE_BASE_URL; + /** * 서버에 상품 목록을 요청한다. * @param {string} orderBy * @returns {object} */ export const requestProductList = async (query) => { - const url = new URL("https://panda-market-api.vercel.app/products"); + const url = new URL(`${BASE_URL}products`); url.searchParams.append("page", query.page); url.searchParams.append("pageSize", query.pageSize); url.searchParams.append("orderBy", query.orderBy); @@ -25,9 +27,7 @@ export const requestProductList = async (query) => { * @returns {json} response */ export const requestProductDetail = async (productId) => { - const url = new URL( - `https://panda-market-api.vercel.app/products/${productId}` - ); + const url = new URL(`${BASE_URL}products/${productId}`); const response = await fetch(url, { method: "get", From ec03870e31ba44b69383fe73acdbec9e99ce8b4a Mon Sep 17 00:00:00 2001 From: Imhwitae Date: Tue, 2 Sep 2025 20:16:39 +0900 Subject: [PATCH 2/4] =?UTF-8?q?refactor:=20=EB=A9=94=EC=9D=B8,=20=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=EC=9D=B8=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=ED=83=80?= =?UTF-8?q?=EC=9E=85=EC=8A=A4=ED=81=AC=EB=A6=BD=ED=8A=B8=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- index.html | 2 +- package-lock.json | 15 +++ package.json | 1 + src/{App.jsx => App.tsx} | 0 src/layout/{AuthLayout.jsx => AuthLayout.tsx} | 17 ++-- src/layout/{Footer.jsx => Footer.tsx} | 10 +- src/layout/{Header.jsx => Header.tsx} | 0 src/layout/{MainLayout.jsx => MainLayout.tsx} | 6 +- src/main.jsx | 12 --- src/main.tsx | 12 +++ src/pages/{Home.jsx => Home.tsx} | 14 +-- src/pages/auth/{Login.jsx => Login.tsx} | 52 +++++----- src/pages/auth/{Signup.jsx => Signup.tsx} | 94 ++++++++++--------- src/vite-env.d.ts | 1 + tsconfig.json | 25 +++++ tsconfig.node.json | 11 +++ vite.config.js => vite.config.ts | 0 17 files changed, 174 insertions(+), 98 deletions(-) rename src/{App.jsx => App.tsx} (100%) rename src/layout/{AuthLayout.jsx => AuthLayout.tsx} (59%) rename src/layout/{Footer.jsx => Footer.tsx} (88%) rename src/layout/{Header.jsx => Header.tsx} (100%) rename src/layout/{MainLayout.jsx => MainLayout.tsx} (60%) delete mode 100644 src/main.jsx create mode 100644 src/main.tsx rename src/pages/{Home.jsx => Home.tsx} (88%) rename src/pages/auth/{Login.jsx => Login.tsx} (73%) rename src/pages/auth/{Signup.jsx => Signup.tsx} (71%) create mode 100644 src/vite-env.d.ts create mode 100644 tsconfig.json create mode 100644 tsconfig.node.json rename vite.config.js => vite.config.ts (100%) diff --git a/index.html b/index.html index 2cfc72a7..1cacaed7 100644 --- a/index.html +++ b/index.html @@ -17,6 +17,6 @@
- + diff --git a/package-lock.json b/package-lock.json index ab8993f0..5df50a73 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,6 +22,7 @@ "eslint-plugin-react": "^7.34.1", "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-refresh": "^0.4.6", + "typescript": "^5.9.2", "vite": "^5.2.0" } }, @@ -4688,6 +4689,20 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/typescript": { + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", + "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, "node_modules/unbox-primitive": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", diff --git a/package.json b/package.json index 27c32455..d6271d62 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "eslint-plugin-react": "^7.34.1", "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-refresh": "^0.4.6", + "typescript": "^5.9.2", "vite": "^5.2.0" } } diff --git a/src/App.jsx b/src/App.tsx similarity index 100% rename from src/App.jsx rename to src/App.tsx diff --git a/src/layout/AuthLayout.jsx b/src/layout/AuthLayout.tsx similarity index 59% rename from src/layout/AuthLayout.jsx rename to src/layout/AuthLayout.tsx index 24bc8e9a..8eee3100 100644 --- a/src/layout/AuthLayout.jsx +++ b/src/layout/AuthLayout.tsx @@ -1,13 +1,18 @@ -import { useState } from 'react'; -import { Outlet } from 'react-router'; -import visibleEye_off from '../assets/icons/eyes_off.png'; -import visibleEye_on from '../assets/icons/eyes_on.png'; +import { useState } from "react"; +import { Outlet } from "react-router"; +import visibleEye_off from "../assets/icons/eyes_off.png"; +import visibleEye_on from "../assets/icons/eyes_on.png"; + +export interface visibleValue { + pw: boolean; + checkPw: boolean; +} export default function AuthLayout() { /** * 비밀번호 보이기 / 가리기 상태 */ - const [visible, setVisible] = useState({ + const [visible, setVisible] = useState({ pw: false, checkPw: false, }); @@ -16,7 +21,7 @@ export default function AuthLayout() { * id 별로 비밀번호 가리기 여부를 변경한다. * @param {string} id */ - const onClickVisible = (id) => { + const onClickVisible = (id: keyof visibleValue) => { setVisible((prevState) => ({ ...prevState, [id]: !visible[id], diff --git a/src/layout/Footer.jsx b/src/layout/Footer.tsx similarity index 88% rename from src/layout/Footer.jsx rename to src/layout/Footer.tsx index fc20dca7..7f4769e7 100644 --- a/src/layout/Footer.jsx +++ b/src/layout/Footer.tsx @@ -1,8 +1,8 @@ -import '../styles/layout/footer.css'; -import ic_facebook from '../assets/icons/ic_facebook.svg'; -import ic_twitter from '../assets/icons/ic_twitter.svg'; -import ic_youtube from '../assets/icons/ic_youtube.svg'; -import ic_instagram from '../assets/icons/ic_instagram.svg'; +import "../styles/layout/footer.css"; +import ic_facebook from "../assets/icons/ic_facebook.svg"; +import ic_twitter from "../assets/icons/ic_twitter.svg"; +import ic_youtube from "../assets/icons/ic_youtube.svg"; +import ic_instagram from "../assets/icons/ic_instagram.svg"; /** * 푸터 diff --git a/src/layout/Header.jsx b/src/layout/Header.tsx similarity index 100% rename from src/layout/Header.jsx rename to src/layout/Header.tsx diff --git a/src/layout/MainLayout.jsx b/src/layout/MainLayout.tsx similarity index 60% rename from src/layout/MainLayout.jsx rename to src/layout/MainLayout.tsx index e0e2f203..481b0a02 100644 --- a/src/layout/MainLayout.jsx +++ b/src/layout/MainLayout.tsx @@ -1,6 +1,6 @@ -import Header from './Header'; -import Footer from './Footer'; -import { Outlet } from 'react-router'; +import Header from "./Header"; +import Footer from "./Footer"; +import { Outlet } from "react-router"; export default function MainLayout() { return ( diff --git a/src/main.jsx b/src/main.jsx deleted file mode 100644 index 37fd47b5..00000000 --- a/src/main.jsx +++ /dev/null @@ -1,12 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom/client'; -import App from './App.jsx'; -import { BrowserRouter } from 'react-router'; - -ReactDOM.createRoot(document.getElementById('root')).render( - - - - - -); diff --git a/src/main.tsx b/src/main.tsx new file mode 100644 index 00000000..54517b0e --- /dev/null +++ b/src/main.tsx @@ -0,0 +1,12 @@ +import React from "react"; +import ReactDOM from "react-dom/client"; +import App from "./App.tsx"; +import { BrowserRouter } from "react-router"; + +ReactDOM.createRoot(document.getElementById("root")!).render( + + + + + +); diff --git a/src/pages/Home.jsx b/src/pages/Home.tsx similarity index 88% rename from src/pages/Home.jsx rename to src/pages/Home.tsx index 576cb6ae..30418411 100644 --- a/src/pages/Home.jsx +++ b/src/pages/Home.tsx @@ -1,10 +1,10 @@ -import '../styles/style.css'; -import home_top from '../assets/images/Img_home_top.png'; -import hot_item from '../assets/images/hot_item.png'; -import search_img from '../assets/images/search.png'; -import register_img from '../assets/images/register.png'; -import home_bottom from '../assets/images/Img_home_bottom.png'; -import { Link } from 'react-router'; +import "../styles/style.css"; +import home_top from "../assets/images/Img_home_top.png"; +import hot_item from "../assets/images/hot_item.png"; +import search_img from "../assets/images/search.png"; +import register_img from "../assets/images/register.png"; +import home_bottom from "../assets/images/Img_home_bottom.png"; +import { Link } from "react-router"; /** * 메인화면 diff --git a/src/pages/auth/Login.jsx b/src/pages/auth/Login.tsx similarity index 73% rename from src/pages/auth/Login.jsx rename to src/pages/auth/Login.tsx index 76dd1b74..24546441 100644 --- a/src/pages/auth/Login.jsx +++ b/src/pages/auth/Login.tsx @@ -1,16 +1,28 @@ -import '../../styles/auth.css'; -import ic_kakao from '../../assets/icons/ic_kakao.png'; -import ic_google from '../../assets/icons/ic_google.png'; -import { Link, useOutletContext } from 'react-router'; -import { useForm } from 'react-hook-form'; -import { useEffect } from 'react'; +import "../../styles/auth.css"; +import ic_kakao from "../../assets/icons/ic_kakao.png"; +import ic_google from "../../assets/icons/ic_google.png"; +import { Link, useOutletContext } from "react-router"; +import { useForm } from "react-hook-form"; +import { SetStateAction, useEffect } from "react"; +import { visibleValue } from "../../layout/AuthLayout"; /** * 로그인 화면 */ export default function Login() { - const { visible, setVisible, onClickVisible, visibleEye_off, visibleEye_on } = - useOutletContext(); + const { + visible, + setVisible, + onClickVisible, + visibleEye_off, + visibleEye_on, + }: { + visible: visibleValue; + setVisible: (prevState: SetStateAction) => void; + onClickVisible: (e: string) => void; + visibleEye_off: string; + visibleEye_on: string; + } = useOutletContext(); /** * 기본적으로 비밀번호 숨김 상태 유지 @@ -28,8 +40,8 @@ export default function Login() { const { register, formState: { errors }, - handleSubmit, - } = useForm({ mode: 'onBlur' }); + // handleSubmit, + } = useForm({ mode: "onBlur" }); return ( <> @@ -40,7 +52,7 @@ export default function Login() {
-
+
{ - onClickVisible(e.target.id); + onClickVisible(e.currentTarget.id); }} />
- {errors.password?.type === 'required' && ( + {errors.password?.type === "required" && (

비밀번호를 입력해주세요

)} - {errors.password?.type === 'minLength' && ( + {errors.password?.type === "minLength" && (

비밀번호를 8자 이상 입력해주세요.

diff --git a/src/pages/auth/Signup.jsx b/src/pages/auth/Signup.tsx similarity index 71% rename from src/pages/auth/Signup.jsx rename to src/pages/auth/Signup.tsx index eea9e1d8..af4b0c16 100644 --- a/src/pages/auth/Signup.jsx +++ b/src/pages/auth/Signup.tsx @@ -1,16 +1,28 @@ -import { Link, useOutletContext } from 'react-router'; -import '../../styles/auth.css'; -import ic_kakao from '../../assets/icons/ic_kakao.png'; -import ic_google from '../../assets/icons/ic_google.png'; -import { useEffect, useState } from 'react'; -import { useForm } from 'react-hook-form'; +import { Link, useOutletContext } from "react-router"; +import "../../styles/auth.css"; +import ic_kakao from "../../assets/icons/ic_kakao.png"; +import ic_google from "../../assets/icons/ic_google.png"; +import { SetStateAction, useEffect, useState } from "react"; +import { useForm } from "react-hook-form"; +import { visibleValue } from "../../layout/AuthLayout"; /** * 회원가입 화면 */ export default function Signup() { - const { visible, setVisible, onClickVisible, visibleEye_off, visibleEye_on } = - useOutletContext(); + const { + visible, + setVisible, + onClickVisible, + visibleEye_off, + visibleEye_on, + }: { + visible: visibleValue; + setVisible: (prevState: SetStateAction) => void; + onClickVisible: (e: string) => void; + visibleEye_off: string; + visibleEye_on: string; + } = useOutletContext(); /** * 기본적으로 비밀번호 숨김 상태 유지 @@ -26,16 +38,16 @@ export default function Signup() { * 회원가입 양식 정보를 담고 있는 객체 * 이메일, 닉네임, 비밀번호, 비밀번호 확인 */ - const [signupForm, setSignupForm] = useState({ - email: '', - nickname: '', - pw: '', - }); + // const [signupForm, setSignupForm] = useState({ + // email: "", + // nickname: "", + // pw: "", + // }); - const onValid = (data) => { - console.log('✅ 유효성 검사 성공! 데이터:', data); - setSignupForm(data); - }; + // const onValid = (data) => { + // console.log("✅ 유효성 검사 성공! 데이터:", data); + // setSignupForm(data); + // }; /** * React-Hook-Form 사용 객체 선언 @@ -43,10 +55,10 @@ export default function Signup() { const { register, formState: { errors, isValid }, - handleSubmit, + // handleSubmit, getValues, } = useForm({ - mode: 'onBlur', + mode: "onBlur", }); console.log(isValid); @@ -60,7 +72,7 @@ export default function Signup() { - +
- {errors.nickname?.type === 'required' && ( + {errors.nickname?.type === "required" && (

닉네임을 입력해주세요

)} - {errors.nickname?.type === 'maxLength' && ( + {errors.nickname?.type === "maxLength" && (

닉네임이 너무 깁니다.

)} @@ -115,12 +125,11 @@ export default function Signup() { { - onClickVisible(e.target.id); + onClickVisible(e.currentTarget.id); }} />
- {errors.password?.type === 'required' && ( + {errors.password?.type === "required" && (

비밀번호를 입력해주세요

)} - {errors.password?.type === 'minLength' && ( + {errors.password?.type === "minLength" && (

비밀번호를 8자 이상 입력해주세요.

@@ -151,16 +160,15 @@ export default function Signup() { - getValues('password') === getValues('confirmPassword'), + getValues("password") === getValues("confirmPassword"), })} />
@@ -169,17 +177,17 @@ export default function Signup() { src={visible.checkPw ? visibleEye_on : visibleEye_off} alt="visible-icon" onClick={(e) => { - onClickVisible(e.target.id); + onClickVisible(e.currentTarget.id); }} />
- {errors.confirmPassword?.type === 'minLength' && ( + {errors.confirmPassword?.type === "minLength" && (

비밀번호를 8자 이상 입력해주세요.

)} - {errors.confirmPassword?.type === 'validate' && ( + {errors.confirmPassword?.type === "validate" && (

비밀번호가 일치하지 않습니다.

)} diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts new file mode 100644 index 00000000..11f02fe2 --- /dev/null +++ b/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..a7fc6fbf --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"], + "references": [{ "path": "./tsconfig.node.json" }] +} diff --git a/tsconfig.node.json b/tsconfig.node.json new file mode 100644 index 00000000..97ede7ee --- /dev/null +++ b/tsconfig.node.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true, + "strict": true + }, + "include": ["vite.config.ts"] +} diff --git a/vite.config.js b/vite.config.ts similarity index 100% rename from vite.config.js rename to vite.config.ts From 49cf6d97b3ecc875c2f86d9772eec9777142d13e Mon Sep 17 00:00:00 2001 From: Imhwitae Date: Wed, 10 Sep 2025 16:24:10 +0900 Subject: [PATCH 3/4] =?UTF-8?q?feat:=20=ED=83=80=EC=9E=85=EC=8A=A4?= =?UTF-8?q?=ED=81=AC=EB=A6=BD=ED=8A=B8=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 +- src/layout/AuthLayout.tsx | 12 +++++------- src/pages/auth/Login.tsx | 12 +++--------- src/pages/auth/Signup.tsx | 12 +++--------- src/types/authType.ts | 20 ++++++++++++++++++++ tsconfig.json | 1 + 6 files changed, 33 insertions(+), 26 deletions(-) create mode 100644 src/types/authType.ts diff --git a/package.json b/package.json index d6271d62..cbd4e3c1 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "type": "module", "scripts": { "dev": "vite", - "build": "vite build", + "build": "tsc && vite build", "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", "preview": "vite preview" }, diff --git a/src/layout/AuthLayout.tsx b/src/layout/AuthLayout.tsx index 8eee3100..6f924f9e 100644 --- a/src/layout/AuthLayout.tsx +++ b/src/layout/AuthLayout.tsx @@ -2,17 +2,13 @@ import { useState } from "react"; import { Outlet } from "react-router"; import visibleEye_off from "../assets/icons/eyes_off.png"; import visibleEye_on from "../assets/icons/eyes_on.png"; - -export interface visibleValue { - pw: boolean; - checkPw: boolean; -} +import { isVisibleKey, VisibleValue } from "../types/authType"; export default function AuthLayout() { /** * 비밀번호 보이기 / 가리기 상태 */ - const [visible, setVisible] = useState({ + const [visible, setVisible] = useState({ pw: false, checkPw: false, }); @@ -21,7 +17,9 @@ export default function AuthLayout() { * id 별로 비밀번호 가리기 여부를 변경한다. * @param {string} id */ - const onClickVisible = (id: keyof visibleValue) => { + const onClickVisible = (id: string) => { + if (!isVisibleKey(id)) return; + setVisible((prevState) => ({ ...prevState, [id]: !visible[id], diff --git a/src/pages/auth/Login.tsx b/src/pages/auth/Login.tsx index 24546441..c692a57f 100644 --- a/src/pages/auth/Login.tsx +++ b/src/pages/auth/Login.tsx @@ -3,8 +3,8 @@ import ic_kakao from "../../assets/icons/ic_kakao.png"; import ic_google from "../../assets/icons/ic_google.png"; import { Link, useOutletContext } from "react-router"; import { useForm } from "react-hook-form"; -import { SetStateAction, useEffect } from "react"; -import { visibleValue } from "../../layout/AuthLayout"; +import { useEffect } from "react"; +import { AuthType } from "../../types/authType"; /** * 로그인 화면 @@ -16,13 +16,7 @@ export default function Login() { onClickVisible, visibleEye_off, visibleEye_on, - }: { - visible: visibleValue; - setVisible: (prevState: SetStateAction) => void; - onClickVisible: (e: string) => void; - visibleEye_off: string; - visibleEye_on: string; - } = useOutletContext(); + }: AuthType = useOutletContext(); /** * 기본적으로 비밀번호 숨김 상태 유지 diff --git a/src/pages/auth/Signup.tsx b/src/pages/auth/Signup.tsx index af4b0c16..747b9c93 100644 --- a/src/pages/auth/Signup.tsx +++ b/src/pages/auth/Signup.tsx @@ -2,9 +2,9 @@ import { Link, useOutletContext } from "react-router"; import "../../styles/auth.css"; import ic_kakao from "../../assets/icons/ic_kakao.png"; import ic_google from "../../assets/icons/ic_google.png"; -import { SetStateAction, useEffect, useState } from "react"; +import { useEffect } from "react"; import { useForm } from "react-hook-form"; -import { visibleValue } from "../../layout/AuthLayout"; +import { AuthType } from "../../types/authType"; /** * 회원가입 화면 @@ -16,13 +16,7 @@ export default function Signup() { onClickVisible, visibleEye_off, visibleEye_on, - }: { - visible: visibleValue; - setVisible: (prevState: SetStateAction) => void; - onClickVisible: (e: string) => void; - visibleEye_off: string; - visibleEye_on: string; - } = useOutletContext(); + }: AuthType = useOutletContext(); /** * 기본적으로 비밀번호 숨김 상태 유지 diff --git a/src/types/authType.ts b/src/types/authType.ts new file mode 100644 index 00000000..5898050b --- /dev/null +++ b/src/types/authType.ts @@ -0,0 +1,20 @@ +import { SetStateAction } from "react"; + +export interface VisibleValue { + pw: boolean; + checkPw: boolean; +} + +export type VisibleKey = keyof VisibleValue; + +export interface AuthType { + visible: VisibleValue; + setVisible: (prevState: SetStateAction) => void; + onClickVisible: (e: string) => void; + visibleEye_off: string; + visibleEye_on: string; +} + +export function isVisibleKey(value: string): value is VisibleKey { + return value === "pw" || value === "checkPw"; +} diff --git a/tsconfig.json b/tsconfig.json index a7fc6fbf..33056b8c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,6 +5,7 @@ "lib": ["ES2020", "DOM", "DOM.Iterable"], "module": "ESNext", "skipLibCheck": true, + "outDir": "./transpile", /* Bundler mode */ "moduleResolution": "bundler", From 1d9ff1721cde5d62f503a9d0a9a9b957fda5025d Mon Sep 17 00:00:00 2001 From: Imhwitae Date: Wed, 10 Sep 2025 16:30:37 +0900 Subject: [PATCH 4/4] =?UTF-8?q?feat:=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20=ED=83=80=EC=9E=85=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/auth/Signup.tsx | 14 +++++++------- src/types/authType.ts | 6 ++++++ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/pages/auth/Signup.tsx b/src/pages/auth/Signup.tsx index 747b9c93..af91acf5 100644 --- a/src/pages/auth/Signup.tsx +++ b/src/pages/auth/Signup.tsx @@ -2,9 +2,9 @@ import { Link, useOutletContext } from "react-router"; import "../../styles/auth.css"; import ic_kakao from "../../assets/icons/ic_kakao.png"; import ic_google from "../../assets/icons/ic_google.png"; -import { useEffect } from "react"; +import { useEffect, useState } from "react"; import { useForm } from "react-hook-form"; -import { AuthType } from "../../types/authType"; +import { AuthType, SignupType } from "../../types/authType"; /** * 회원가입 화면 @@ -32,11 +32,11 @@ export default function Signup() { * 회원가입 양식 정보를 담고 있는 객체 * 이메일, 닉네임, 비밀번호, 비밀번호 확인 */ - // const [signupForm, setSignupForm] = useState({ - // email: "", - // nickname: "", - // pw: "", - // }); + const [signupForm, setSignupForm] = useState({ + email: "", + nickname: "", + pw: "", + }); // const onValid = (data) => { // console.log("✅ 유효성 검사 성공! 데이터:", data); diff --git a/src/types/authType.ts b/src/types/authType.ts index 5898050b..abbdf2f8 100644 --- a/src/types/authType.ts +++ b/src/types/authType.ts @@ -18,3 +18,9 @@ export interface AuthType { export function isVisibleKey(value: string): value is VisibleKey { return value === "pw" || value === "checkPw"; } + +export interface SignupType { + email: string; + nickname: string; + pw: string; +}