-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #15 from MOVIEJOJO7/Feature/#2
Feat : 로그인 & 회원가입 구현
- Loading branch information
Showing
12 changed files
with
359 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
'use client'; | ||
|
||
import React from 'react'; | ||
import { useForm, SubmitHandler } from 'react-hook-form'; | ||
import { Button } from '@material-tailwind/react'; | ||
import { fetchJoin } from '@/app/join/join.utils'; | ||
|
||
type RequestBody = { | ||
id: string; // 사용자 아이디 (필수!, 영어와 숫자만) | ||
password: string; // 사용자 비밀번호, 5자 이상 (필수!) | ||
name: string; // 사용자 이름, 20자 이하 (필수!) | ||
picture: string; // 사용자 이미지(url or base64, under 1MB) | ||
}; | ||
|
||
const JoinForm = () => { | ||
const { | ||
register, | ||
handleSubmit, | ||
watch, | ||
formState: { errors }, | ||
} = useForm<RequestBody>(); | ||
|
||
// 로그인 시 api 요청 | ||
const onSubmit: SubmitHandler<RequestBody> = ({ | ||
id, | ||
password, | ||
name, | ||
picture, | ||
}) => { | ||
fetchJoin(id, password, name, picture); | ||
}; | ||
|
||
const image = watch('picture'); | ||
console.log(image); | ||
|
||
return ( | ||
<> | ||
<form | ||
onSubmit={handleSubmit(onSubmit)} | ||
className="w-[300px] h-[500px] bg-white flex flex-col items-center " | ||
> | ||
<div className="flex flex-col items-center justify-center h-screen"> | ||
{/* 이미지 */} | ||
{image ? ( | ||
<img src={image} className="h-14 w-14 rounded-full bg-blue-500" /> | ||
) : ( | ||
<div className="h-20 w-20 rounded-full bg-blue-500" /> | ||
)} | ||
{/* 이미지 url */} | ||
<div className="relative m-3 group"> | ||
<input | ||
placeholder=" " | ||
{...register('picture', { | ||
required: true, | ||
})} | ||
className="border-b py-1 focus:outline-none focus:border-purple-600 focus:border-b-2 transition-colors peer" | ||
/> | ||
<label className="absolute left-0 top-1 text-gray-600 cursor-text peer-focus:text-xs peer-focus:-top-4 peer-focus:text-purple-600 transition-all"> | ||
Image URL | ||
</label> | ||
</div> | ||
{/* id */} | ||
<div className="relative m-3 group"> | ||
<input | ||
placeholder=" " | ||
{...register('id', { | ||
required: true, | ||
pattern: /^[a-zA-Z0-9]*$/, | ||
})} | ||
className="border-b py-1 focus:outline-none focus:border-purple-600 focus:border-b-2 transition-colors peer" | ||
/> | ||
<label className="absolute left-0 top-1 text-gray-600 cursor-text peer-focus:text-xs peer-focus:-top-4 peer-focus:text-purple-600 transition-all"> | ||
id | ||
</label> | ||
</div> | ||
{/* 비밀번호 */} | ||
<div className="relative m-3"> | ||
<input | ||
{...register('password', { | ||
required: true, | ||
minLength: 5, | ||
})} | ||
className="border-b py-1 focus:outline-none focus:border-purple-600 focus:border-b-2 transition-colors peer" | ||
/> | ||
<label className="absolute left-0 top-1 text-gray-600 cursor-text peer-focus:text-xs peer-focus:-top-4 peer-focus:text-purple-600 transition-all"> | ||
password | ||
</label> | ||
{errors?.password?.type === 'minLength' && ( | ||
<p>입력은 최소 5자 이상이어야 합니다.</p> | ||
)} | ||
</div> | ||
{/* 이름 */} | ||
<div className="relative m-3"> | ||
<input | ||
{...register('name', { | ||
required: true, | ||
maxLength: 20, | ||
})} | ||
className="border-b py-1 focus:outline-none focus:border-purple-600 focus:border-b-2 transition-colors peer" | ||
/> | ||
<label className="absolute left-0 top-1 text-gray-600 cursor-text peer-focus:text-xs peer-focus:-top-4 peer-focus:text-purple-600 transition-all"> | ||
name | ||
</label> | ||
{errors?.name?.type === 'maxLength' && ( | ||
<p>입력은 최대 20자 이상이어야 합니다.</p> | ||
)} | ||
|
||
{errors?.id ? <p className="error">{errors.id?.message}</p> : null} | ||
</div> | ||
</div> | ||
|
||
<Button type="submit" className="w-full bg-main"> | ||
회원가입 | ||
</Button> | ||
</form> | ||
</> | ||
); | ||
}; | ||
|
||
export default JoinForm; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import { Cookies } from 'react-cookie'; | ||
|
||
const cookies = new Cookies(); | ||
|
||
type CookieOptions = { | ||
expires?: Date; | ||
path?: string; | ||
domain?: string; | ||
secure?: boolean; // true : 웹 브라우저와 웹 서버가 https로 통신하는 경우에만 쿠키 저장 | ||
httpOnly?: boolean; // true : document.cookie라는 자바스크립트 코드로 쿠키에 비정상적으로 접속하는 것을 막는 옵션 | ||
// 다른 속성이 있다면 추가할 수 있습니다. | ||
}; | ||
|
||
// setCookie: 쿠키를 저장하는 함수 | ||
export const setCookie = ( | ||
name: string, | ||
value: string, | ||
option: CookieOptions, | ||
) => { | ||
return cookies.set(name, value, { ...option }); | ||
}; | ||
|
||
// getCookie: 쿠키를 가지고 오는 함수 | ||
export const getCookie = (name: string) => { | ||
return cookies.get(name); | ||
}; | ||
|
||
// removeCookie: 쿠키를 삭제하는 함수 | ||
export const removeCookie = (name: string, option: CookieOptions) => { | ||
return cookies.remove(name, { ...option }); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
'use client'; | ||
|
||
import React from 'react'; | ||
import { useForm, SubmitHandler } from 'react-hook-form'; | ||
import { fetchLogin } from '../../app/login/login.utils'; | ||
import { setCookie } from '@/Components/Login/Cookie'; | ||
import { Button } from '@material-tailwind/react'; | ||
|
||
type IFormInput = { | ||
id: string; // 사용자 아이디 (필수!, 영어와 숫자만) | ||
password: string; // 사용자 비밀번호, 5자 이상 (필수!) | ||
}; | ||
|
||
const LoginForm = () => { | ||
const { | ||
register, | ||
handleSubmit, | ||
formState: { errors }, | ||
} = useForm<IFormInput>(); | ||
|
||
// 로그인 버튼 클릭 시 | ||
const onSubmit: SubmitHandler<IFormInput> = async ({ id, password }) => { | ||
console.log('id: ', id, 'password:', password); | ||
const { accessToken, refreshToken } = await fetchLogin(id, password); | ||
console.log('accessToken:', accessToken); | ||
console.log('refreshToken:', refreshToken); | ||
// 현재 시간 | ||
const time = new Date(); | ||
// 1일 뒤 | ||
time.setMinutes(time.getMinutes() + 60 * 24); | ||
|
||
setCookie('accessToken', accessToken, { path: '/', expires: time }); | ||
setCookie('refreshToken', refreshToken, { path: '/' }); | ||
}; | ||
|
||
return ( | ||
<form onSubmit={handleSubmit(onSubmit)}> | ||
<label>id</label> | ||
{/* 영어와 숫자만 */} | ||
<input | ||
{...register('id', { | ||
required: true, | ||
})} | ||
/> | ||
{errors?.id ? <p className="error">{errors.id?.message}</p> : null} | ||
|
||
<label>password</label> | ||
{/* 5자 이상 */} | ||
<input {...register('password')} /> | ||
<Button type="submit" className=" bg-pink-200"> | ||
로그인 | ||
</Button> | ||
</form> | ||
); | ||
}; | ||
|
||
export default LoginForm; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
type RequestBody = { | ||
id: string; | ||
password: string; | ||
name: string; | ||
picture: string; | ||
}; | ||
|
||
export const fetchJoin = async ( | ||
id: string, | ||
password: string, | ||
name: string, | ||
picture: string, | ||
) => { | ||
const requestData: RequestBody = { | ||
id, | ||
password, | ||
name, | ||
picture, | ||
}; | ||
|
||
const res = await fetch('https://fastcampus-chat.net/signup', { | ||
method: 'POST', | ||
headers: { | ||
'Content-Type': 'application/json', | ||
serverId: process.env.NEXT_PUBLIC_SERVER_ID as string, | ||
}, | ||
body: JSON.stringify(requestData), | ||
}); | ||
const data = await res.json(); | ||
console.log(data); | ||
return data; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import JoinForm from '@/Components/Join/JoinForm'; | ||
import React from 'react'; | ||
|
||
const Join = () => { | ||
return ( | ||
<div> | ||
<JoinForm></JoinForm> | ||
</div> | ||
); | ||
}; | ||
|
||
export default Join; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
type RequestBody = { | ||
id: string; | ||
password: string; | ||
}; | ||
|
||
type LoginResult = { | ||
accessToken: string; | ||
refreshToken: string; | ||
// 다른 필드들도 있을 수 있습니다. | ||
}; | ||
|
||
export const fetchLogin = async (id: string, password: string) => { | ||
const requestData: RequestBody = { | ||
id, | ||
password, | ||
}; | ||
console.log(process.env.NEXT_PUBLIC_SERVER_ID); | ||
const res = await fetch('https://fastcampus-chat.net/login', { | ||
method: 'POST', | ||
headers: { | ||
'Content-Type': 'application/json', | ||
serverId: process.env.NEXT_PUBLIC_SERVER_ID as string, | ||
}, | ||
// Content-Type이 JSON이니까 JSON.stringify | ||
body: JSON.stringify(requestData), | ||
}); | ||
// 응답 데이터를 JSON 형식으로 파싱한 다음 data 변수 저장 | ||
const data: LoginResult = await res.json(); | ||
// const { accessToken, refreshToken } = await res.json(); | ||
// console.log('accessToken:', accessToken, 'refreshToken:', refreshToken); | ||
return data; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import React from 'react'; | ||
import LoginForm from '../../Components/Login/LoginForm'; | ||
|
||
const Login = () => { | ||
return ( | ||
<div> | ||
<LoginForm></LoginForm> | ||
</div> | ||
); | ||
}; | ||
|
||
export default Login; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.