diff --git a/front/.gitignore b/front/.gitignore index 4d29575..9f0d8d9 100644 --- a/front/.gitignore +++ b/front/.gitignore @@ -4,7 +4,7 @@ /node_modules /.pnp .pnp.js - +.env # testing /coverage diff --git a/front/package.json b/front/package.json index fd17a01..a9be182 100644 --- a/front/package.json +++ b/front/package.json @@ -14,6 +14,7 @@ "@types/react-dom": "^18.3.1", "axios": "^1.7.8", "class-variance-authority": "^0.7.0", + "dotenv": "^16.4.5", "react": "^18.3.1", "react-dom": "^18.3.1", "react-router-dom": "^7.0.1", diff --git a/front/src/Controller/fetch.signup.ts b/front/src/Controller/fetch.signup.ts new file mode 100644 index 0000000..d2867b1 --- /dev/null +++ b/front/src/Controller/fetch.signup.ts @@ -0,0 +1,47 @@ +import supabaseInstance from '../Model/fetch.supabase.instance'; + +interface SignUpParams { + email: string; + password: string; + admin: boolean; + job?: string; // Optional: name은 필수가 아닐 수 있음 + nickname?: string; +} + +const fetchSignUp = async ({ email, password, admin, job, nickname }: SignUpParams) => { + try { + const { data, error } = await supabaseInstance.auth.signUp({ + email, + password, + options: { + data: { + // 사용자 정의 메타데이터로 전달 + admin, + job, + nickname, + }, + }, + }); + + // 구조할당으로 responseMessage를 보내는 이유는 이 구조로 호출하는 쪽에서 명확하게 responseMessage와 data를 구분해서 사용할 수 있게 하기 위함이다. 즉, 반환 값은 객체이기 때문이다. + // 추가적으로 fetchSignup은 promise를 반환했다. + + if (error) { + // error + console.error('SignUp Error:', error); + const errorMessage = error.message || 'Signup failed. Please try again.'; + return { responseMessage: errorMessage, data: undefined }; + } + // 성공적인 경우 + const successMessage = 'Signup successful! Check your email for confirmation.'; + return { responseMessage: successMessage, data }; + + // 예외가 발생할 경우 + } catch (error: any) { + console.error('SignUp Error:', error); + const catchMessage = error.message || 'An unexpected error occurred. Please try again.'; + return { responseMessage: catchMessage, data: undefined }; + } +}; + +export default fetchSignUp; diff --git a/front/src/Model/fetch.instance.ts b/front/src/Model/fetch.instance.ts new file mode 100644 index 0000000..2486454 --- /dev/null +++ b/front/src/Model/fetch.instance.ts @@ -0,0 +1,12 @@ +import axios from 'axios'; + +const axiosInstance = axios.create({ + baseURL: 'https://fsrotjrwllkimiidizgk.supabase.co', // Supabase 프로젝트 URL + headers: { + apikey: + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImZzcm90anJ3bGxraW1paWRpemdrIiwicm9sZSI6ImFub24iLCJpYXQiOjE3MzMwMjgzMTQsImV4cCI6MjA0ODYwNDMxNH0.uiFog4O3BbOZhBC1z0LRFTtEE4p4z3PK5VQ5wKrRHzg', // Supabase API Key + 'Content-Type': 'application/json', + }, +}); + +export default axiosInstance; diff --git a/front/src/Model/fetch.supabase.instance.ts b/front/src/Model/fetch.supabase.instance.ts new file mode 100644 index 0000000..d67cc61 --- /dev/null +++ b/front/src/Model/fetch.supabase.instance.ts @@ -0,0 +1,8 @@ +import { createClient } from '@supabase/supabase-js'; + +const supabaseInstance = createClient( + 'https://bsrffkqymaqhwwqziqee.supabase.co', + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImJzcmZma3F5bWFxaHd3cXppcWVlIiwicm9sZSI6ImFub24iLCJpYXQiOjE3MzMxMTc5NzMsImV4cCI6MjA0ODY5Mzk3M30.1UKuqvG0Ls3WxLvNkAHRASWyv8Y8coIJKe0M9tZ8flA', +); + +export default supabaseInstance; diff --git a/front/src/Page/Signup.tsx b/front/src/Page/Signup.tsx index f11d989..83195aa 100644 --- a/front/src/Page/Signup.tsx +++ b/front/src/Page/Signup.tsx @@ -1,80 +1,77 @@ import React, { useState } from 'react'; -import { createClient } from '@supabase/supabase-js'; +import fetchSignUp from '../Controller/fetch.signup'; -const supabase = createClient( - 'https://fsrotjrwllkimiidizgk.supabase.co', - 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImZzcm90anJ3bGxraW1paWRpemdrIiwicm9sZSI6ImFub24iLCJpYXQiOjE3MzMwMjgzMTQsImV4cCI6MjA0ODYwNDMxNH0.uiFog4O3BbOZhBC1z0LRFTtEE4p4z3PK5VQ5wKrRHzg', -); - -const Signup: React.FC = () => { +const Login = () => { const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); - const [name, setName] = useState(''); + const [admin, setAdmin] = useState(false); + const [job, setJob] = useState(''); + const [nickname, setNickName] = useState(''); const [responseMessage, setResponseMessage] = useState(''); const handleSignUp = async () => { - try { - const { data, error } = await supabase.auth.signUp({ - email, - password, - options: { - data: { - name, // 사용자 정의 메타데이터로 전달 - }, - }, - }); - - if (error) { - throw error; - } - - setResponseMessage('Signup successful! Check your email for confirmation.'); - } catch (error: any) { - setResponseMessage(error.message || 'Signup failed. Please try again.'); - } + const { responseMessage } = await fetchSignUp({ + email, + password, + admin, + job, + nickname, + }); + setResponseMessage(responseMessage); }; return ( -
-

Sign Up

+
+

Sign Up

+ {/* Nickname */} setName(e.target.value)} - style={{ display: 'block', width: '100%', marginBottom: '10px', padding: '8px' }} + placeholder='nickname' + value={nickname} + onChange={(e) => setNickName(e.target.value)} + className='block w-full p-2 mb-3 border border-gray-300 rounded' /> + {/* Job */} + + {/* Email */} setEmail(e.target.value)} - style={{ display: 'block', width: '100%', marginBottom: '10px', padding: '8px' }} + className='block w-full p-2 mb-3 border border-gray-300 rounded' /> + {/* Password */} setPassword(e.target.value)} - style={{ display: 'block', width: '100%', marginBottom: '10px', padding: '8px' }} + className='block w-full p-2 mb-3 border border-gray-300 rounded' /> + {/* Sign Up Button */} - {responseMessage &&

{responseMessage}

} + {responseMessage &&

{responseMessage}

}
); }; -export default Signup; +export default Login; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9f0757c..6a25ea2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -43,6 +43,9 @@ importers: class-variance-authority: specifier: ^0.7.0 version: 0.7.1 + dotenv: + specifier: ^16.4.5 + version: 16.4.5 react: specifier: ^18.3.1 version: 18.3.1 @@ -1932,8 +1935,8 @@ packages: caniuse-api@3.0.0: resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==} - caniuse-lite@1.0.30001684: - resolution: {integrity: sha512-G1LRwLIQjBQoyq0ZJGqGIJUXzJ8irpbjHLpVRXDvBEScFJ9b17sgK6vlx0GAJFE21okD7zXl08rRRUfq6HdoEQ==} + caniuse-lite@1.0.30001685: + resolution: {integrity: sha512-e/kJN1EMyHQzgcMEEgoo+YTCO1NGCmIYHk5Qk8jT6AazWemS5QFKJ5ShCJlH3GZrNIdZofcNCEwZqbMjjKzmnA==} case-sensitive-paths-webpack-plugin@2.4.0: resolution: {integrity: sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw==} @@ -2421,6 +2424,10 @@ packages: resolution: {integrity: sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==} engines: {node: '>=10'} + dotenv@16.4.5: + resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} + engines: {node: '>=12'} + duplexer@0.1.2: resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} @@ -3054,8 +3061,8 @@ packages: has-property-descriptors@1.0.2: resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} - has-proto@1.0.3: - resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} + has-proto@1.1.0: + resolution: {integrity: sha512-QLdzI9IIO1Jg7f9GT1gXpPpXArAn6cS31R1eEZqz08Gc+uQ8/XiqHWt17Fiw+2p6oTTIq5GXEpQkAlA88YRl/Q==} engines: {node: '>= 0.4'} has-symbols@1.0.3: @@ -3305,8 +3312,8 @@ packages: resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} engines: {node: '>= 0.4'} - is-number-object@1.0.7: - resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} + is-number-object@1.1.0: + resolution: {integrity: sha512-KVSZV0Dunv9DTPkhXwcZ3Q+tUc9TsaE1ZwX5J2WMvsSGS6Md8TFPun5uwh0yRdrNerI6vf/tbJxqSx4c1ZI1Lw==} engines: {node: '>= 0.4'} is-number@7.0.0: @@ -3348,8 +3355,8 @@ packages: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} - is-string@1.0.7: - resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} + is-string@1.1.0: + resolution: {integrity: sha512-PlfzajuF9vSo5wErv3MJAKD/nqf9ngAs1NFQYm16nUYFO2IzxJ2hcm+IOCg+EEopdykNNUhVq5cz35cAUxU8+g==} engines: {node: '>= 0.4'} is-symbol@1.0.4: @@ -7762,7 +7769,7 @@ snapshots: es-abstract: 1.23.5 es-object-atoms: 1.0.0 get-intrinsic: 1.2.4 - is-string: 1.0.7 + is-string: 1.1.0 array-union@2.1.0: {} @@ -7806,7 +7813,7 @@ snapshots: es-array-method-boxes-properly: 1.0.0 es-errors: 1.3.0 es-object-atoms: 1.0.0 - is-string: 1.0.7 + is-string: 1.1.0 array.prototype.tosorted@1.1.4: dependencies: @@ -7840,7 +7847,7 @@ snapshots: autoprefixer@10.4.20(postcss@8.4.49): dependencies: browserslist: 4.24.2 - caniuse-lite: 1.0.30001684 + caniuse-lite: 1.0.30001685 fraction.js: 4.3.7 normalize-range: 0.1.2 picocolors: 1.1.1 @@ -8045,7 +8052,7 @@ snapshots: browserslist@4.24.2: dependencies: - caniuse-lite: 1.0.30001684 + caniuse-lite: 1.0.30001685 electron-to-chromium: 1.5.67 node-releases: 2.0.18 update-browserslist-db: 1.1.1(browserslist@4.24.2) @@ -8084,11 +8091,11 @@ snapshots: caniuse-api@3.0.0: dependencies: browserslist: 4.24.2 - caniuse-lite: 1.0.30001684 + caniuse-lite: 1.0.30001685 lodash.memoize: 4.1.2 lodash.uniq: 4.5.0 - caniuse-lite@1.0.30001684: {} + caniuse-lite@1.0.30001685: {} case-sensitive-paths-webpack-plugin@2.4.0: {} @@ -8573,6 +8580,8 @@ snapshots: dotenv@10.0.0: {} + dotenv@16.4.5: {} + duplexer@0.1.2: {} eastasianwidth@0.2.0: {} @@ -8634,7 +8643,7 @@ snapshots: globalthis: 1.0.4 gopd: 1.1.0 has-property-descriptors: 1.0.2 - has-proto: 1.0.3 + has-proto: 1.1.0 has-symbols: 1.0.3 hasown: 2.0.2 internal-slot: 1.0.7 @@ -8644,7 +8653,7 @@ snapshots: is-negative-zero: 2.0.3 is-regex: 1.2.0 is-shared-array-buffer: 1.0.3 - is-string: 1.0.7 + is-string: 1.1.0 is-typed-array: 1.1.13 is-weakref: 1.0.2 object-inspect: 1.13.3 @@ -8679,7 +8688,7 @@ snapshots: is-arguments: 1.1.1 is-map: 2.0.3 is-set: 2.0.3 - is-string: 1.0.7 + is-string: 1.1.0 isarray: 2.0.5 stop-iteration-iterator: 1.0.0 @@ -8695,7 +8704,7 @@ snapshots: globalthis: 1.0.4 gopd: 1.1.0 has-property-descriptors: 1.0.2 - has-proto: 1.0.3 + has-proto: 1.1.0 has-symbols: 1.0.3 internal-slot: 1.0.7 iterator.prototype: 1.1.3 @@ -9296,7 +9305,7 @@ snapshots: dependencies: es-errors: 1.3.0 function-bind: 1.1.2 - has-proto: 1.0.3 + has-proto: 1.1.0 has-symbols: 1.0.3 hasown: 2.0.2 @@ -9400,7 +9409,9 @@ snapshots: dependencies: es-define-property: 1.0.0 - has-proto@1.0.3: {} + has-proto@1.1.0: + dependencies: + call-bind: 1.0.7 has-symbols@1.0.3: {} @@ -9644,8 +9655,9 @@ snapshots: is-negative-zero@2.0.3: {} - is-number-object@1.0.7: + is-number-object@1.1.0: dependencies: + call-bind: 1.0.7 has-tostringtag: 1.0.2 is-number@7.0.0: {} @@ -9675,8 +9687,9 @@ snapshots: is-stream@2.0.1: {} - is-string@1.0.7: + is-string@1.1.0: dependencies: + call-bind: 1.0.7 has-tostringtag: 1.0.2 is-symbol@1.0.4: @@ -12099,7 +12112,7 @@ snapshots: call-bind: 1.0.7 for-each: 0.3.3 gopd: 1.1.0 - has-proto: 1.0.3 + has-proto: 1.1.0 is-typed-array: 1.1.13 typed-array-byte-offset@1.0.3: @@ -12108,7 +12121,7 @@ snapshots: call-bind: 1.0.7 for-each: 0.3.3 gopd: 1.1.0 - has-proto: 1.0.3 + has-proto: 1.1.0 is-typed-array: 1.1.13 reflect.getprototypeof: 1.0.7 @@ -12366,8 +12379,8 @@ snapshots: dependencies: is-bigint: 1.0.4 is-boolean-object: 1.1.2 - is-number-object: 1.0.7 - is-string: 1.0.7 + is-number-object: 1.1.0 + is-string: 1.1.0 is-symbol: 1.0.4 which-builtin-type@1.2.0: