Skip to content

Commit

Permalink
Merge pull request #81 from DDD-Community/feature/77
Browse files Browse the repository at this point in the history
[Feature/77] 로그인/회원가입 서버연결, 프로필 수정
  • Loading branch information
hwanheejung authored Aug 12, 2024
2 parents 4f3c82e + 08a7594 commit 8c0b38c
Show file tree
Hide file tree
Showing 14 changed files with 216 additions and 101 deletions.
4 changes: 4 additions & 0 deletions public/icons/kakaoWbg.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
49 changes: 8 additions & 41 deletions src/app/(onboarding)/signup/components/NicknameForm.tsx
Original file line number Diff line number Diff line change
@@ -1,50 +1,19 @@
'use client'

import Button from '@/components/Button'
import TextInput from '@/components/TextInput'
import NicknameInput from '@/components/TextInput/NicknameInput'
import { useSession } from 'next-auth/react'
import { useRouter } from 'next/navigation'
import { useEffect, useState } from 'react'
import SketchIcon from 'public/icons/sketchIcons-1.svg'

const MAX_NICKNAME_LENGTH = 10
import { useState } from 'react'

const NicknameForm = () => {
const [nickname, setNickname] = useState('')

const [errorMessage, setErrorMessage] = useState('')
const [hasError, setHasError] = useState(false)
const isEmpty = nickname.length === 0
const { data: session, update } = useSession()
const { update } = useSession()
const router = useRouter()

useEffect(() => {
if (session) {
setNickname(session.user!.name!)
}
}, [session])

const validateNickname = (value: string) => {
if (value.length > MAX_NICKNAME_LENGTH) {
setErrorMessage(`${MAX_NICKNAME_LENGTH}자 미만으로 입력해주세요`)
return
}

const regex = /^[가-힣ㄱ-ㅎㅏ-ㅣa-zA-Z0-9]*$/
if (!regex.test(value)) {
setErrorMessage('국문, 영문, 숫자만 입력 가능해요.')
return
}
setErrorMessage('')
}

useEffect(() => {
validateNickname(nickname)
}, [nickname])

const onInput = (value: string) => {
setNickname(value)
}

const createNickname = async () => {
update({
name: nickname,
Expand All @@ -58,19 +27,17 @@ const NicknameForm = () => {
<div className="mb-20 mt-[60px] text-2xl font-thin leading-10">
닉네임을 정해주세요!
</div>
<TextInput
errorMessage={errorMessage}
description={`${nickname.length}/${MAX_NICKNAME_LENGTH}자`}
<NicknameInput
value={nickname}
hasError={errorMessage.length > 0}
setValue={onInput}
setValue={setNickname}
setHasError={setHasError}
icon={<SketchIcon />}
/>
</div>
<Button
size="lg"
className="mb-10"
disabled={errorMessage.length > 0 || isEmpty}
disabled={hasError || isEmpty}
onClick={createNickname}
>
완료
Expand Down
2 changes: 1 addition & 1 deletion src/app/(onboarding)/signup/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import NicknameForm from './components/NicknameForm'
const SignUpPage = async () => {
const session = await auth()

if (!session?.isNewUser) {
if (session && !session.newUser) {
redirect('/board/create')
}
return (
Expand Down
18 changes: 18 additions & 0 deletions src/app/mypage/profileEdit/components/Email.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { auth } from '@/auth'
import KakaoIcon from 'public/icons/kakaoWbg.svg'

const Email = async () => {
const session = await auth()
return (
<div className="mb-6 flex w-[264px] items-center border-b border-gray-950">
<div className="mr-2">
<KakaoIcon />
</div>
<div className="flex-1 bg-transparent p-1 text-gray-400 outline-none">
{session?.user?.email}
</div>
</div>
)
}

export default Email
45 changes: 45 additions & 0 deletions src/app/mypage/profileEdit/components/NicknameForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
'use client'

import Button from '@/components/Button'
import { useSession } from 'next-auth/react'
import { ReactNode, useState } from 'react'
import NicknameInput from '@/components/TextInput/NicknameInput'
import Title from './Title'

const NicknameForm = ({ children }: { children: ReactNode }) => {
const { data: session, update } = useSession()
const [newName, setNewName] = useState<string>(session?.user?.name || '')
const [hasError, setHasError] = useState(false)

const updateNickname = async () => {
update({
name: newName,
})
}

return (
<div className="mt-9 flex flex-1 flex-col px-10">
<div className="mx-auto w-[256px] flex-1">
<Title>닉네임</Title>
<NicknameInput
value={newName}
setValue={setNewName}
setHasError={setHasError}
/>
{children}
</div>
<Button
size="lg"
onClick={updateNickname}
className="mb-10 mt-auto w-full"
disabled={
session?.user?.name === newName || newName.length === 0 || hasError
}
>
저장
</Button>
</div>
)
}

export default NicknameForm
7 changes: 7 additions & 0 deletions src/app/mypage/profileEdit/components/Title.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { ReactNode } from 'react'

const Title = ({ children }: { children: ReactNode }) => (
<h1 className="mb-3 text-lg font-semiBold">{children}</h1>
)

export default Title
23 changes: 17 additions & 6 deletions src/app/mypage/profileEdit/page.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,25 @@
import Header from '@/components/Header'
import Link from 'next/link'
import Email from './components/Email'
import NicknameForm from './components/NicknameForm'
import Title from './components/Title'

const Page = () => {
return (
<div className="min-h-dvh">
<div className="flex min-h-dvh flex-col">
<Header title="프로필 수정" leftButton={<Header.BackButton />} />
<div>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Accusamus amet
atque consectetur culpa, doloribus earum eius eos eum fuga fugiat harum
ipsam laborum omnis quae sint tenetur totam ut voluptates.
</div>

<NicknameForm>
<Title>연결된 계정</Title>
<Email />

<Link
href="/mypage/leave"
className="cursor-pointer text-sm font-semiBold text-gray-400"
>
탈퇴하기
</Link>
</NicknameForm>
</div>
)
}
Expand Down
38 changes: 21 additions & 17 deletions src/auth.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
/* eslint-disable no-param-reassign */

import NextAuth from 'next-auth'
import Kakao from 'next-auth/providers/kakao'
import { getToken } from './lib/api'
import { login } from './lib/api'
import { changeNickname } from './lib/api/user'

export const { handlers, signIn, signOut, auth } = NextAuth({
providers: [
Expand All @@ -9,6 +12,7 @@ export const { handlers, signIn, signOut, auth } = NextAuth({
clientSecret: process.env.AUTH_KAKAO_SECRET,
}),
],

trustHost: true,
session: {
strategy: 'jwt',
Expand All @@ -18,17 +22,17 @@ export const { handlers, signIn, signOut, auth } = NextAuth({
async signIn({ user, account }) {
if (account && user) {
try {
// 신규 유저인지 확인, polabo 백에서 토큰 발급, nickname
const { isNewUser, accessToken, refreshToken } = await getToken({
account,
user,
// 신규 유저인지 확인, polabo 백에서 토큰 발급
const { newUser, nickName, accessToken } = await login({
email: user.email!,
nickName: user.name!,
birthDt: '2024-08-11', // TODO: 기획 대기
gender: 'F', // TODO: 기획 대기
})
// eslint-disable-next-line no-param-reassign
user.customData = {
isNewUser,
accessToken,
refreshToken,
}

user.name = nickName
user.newUser = newUser
user.accessToken = accessToken
} catch (e) {
console.log('error', e)
return false
Expand All @@ -40,16 +44,16 @@ export const { handlers, signIn, signOut, auth } = NextAuth({
async jwt({ token, user, trigger, session }) {
if (trigger === 'update' && session?.name) {
const { name } = session
// eslint-disable-next-line no-param-reassign
token.name = name
// TODO: 서버에 nickname 전송

token.name = name // client update
await changeNickname(name) // server update
}

if (user) {
return {
...token,
accessToken: user.customData.accessToken,
isNewUser: user.customData.isNewUser,
accessToken: user.accessToken,
newUser: user.newUser,
}
}

Expand All @@ -59,7 +63,7 @@ export const { handlers, signIn, signOut, auth } = NextAuth({
return {
...session,
accessToken: token.accessToken,
isNewUser: token.isNewUser,
newUser: token.newUser,
}
},
},
Expand Down
69 changes: 69 additions & 0 deletions src/components/TextInput/NicknameInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
'use client'

import { Dispatch, ReactNode, SetStateAction, useEffect, useState } from 'react'
import { useSession } from 'next-auth/react'
import TextInput from '.'

const MAX_NICKNAME_LENGTH = 10

interface NicknameInputProps {
value: string
setValue: Dispatch<SetStateAction<string>>
setHasError: Dispatch<SetStateAction<boolean>>
icon?: ReactNode
}

const NicknameInput = ({
value,
setValue,
setHasError,
icon = '',
}: NicknameInputProps) => {
const [errorMessage, setErrorMessage] = useState('')

const { data: session } = useSession()

useEffect(() => {
if (session) {
setValue(session.user!.name!)
}
}, [session])

useEffect(() => {
setHasError(errorMessage.length > 0)
}, [errorMessage])

const validateNickname = (name: string) => {
if (name.length > MAX_NICKNAME_LENGTH) {
setErrorMessage(`${MAX_NICKNAME_LENGTH}자 미만으로 입력해주세요`)
return
}

const regex = /^[가-힣ㄱ-ㅎㅏ-ㅣa-zA-Z0-9]*$/
if (!regex.test(value)) {
setErrorMessage('국문, 영문, 숫자만 입력 가능해요.')
return
}
setErrorMessage('')
}

useEffect(() => {
validateNickname(value)
}, [value])

const onInput = (name: string) => {
setValue(name)
}
return (
<TextInput
errorMessage={errorMessage}
description={`${value.length}/${MAX_NICKNAME_LENGTH}자`}
value={value}
hasError={errorMessage.length > 0}
setValue={onInput}
icon={icon}
/>
)
}

export default NicknameInput
29 changes: 7 additions & 22 deletions src/lib/api/auth.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,10 @@
import { GetAccessTokenPayload, Token } from '@/types'
// import { post } from './base'
import { SignInPayload, User } from '@/types'
import { post } from './base'

type GetTokenResponse = {
isNewUser: boolean
} & Token
export const login = async (body: SignInPayload): Promise<User> => {
const res = await post('/api/v1/oauth/sign-in', {
body: JSON.stringify(body),
})

export const getToken = async ({
account,
user,
}: GetAccessTokenPayload): Promise<GetTokenResponse> => {
// const res = await post('/', {
// body: JSON.stringify({
// account,
// user,
// }),
// })
console.log(account, user)

return {
isNewUser: true,
accessToken: 'AT',
refreshToken: 'RT',
}
return res.data
}
6 changes: 6 additions & 0 deletions src/lib/api/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,9 @@ export const withdraw = async (body: WithdrawUserPayload) => {
body: JSON.stringify(body),
})
}

export const changeNickname = async (nickName: string) => {
return put('/api/v1/user/nickname', {
body: JSON.stringify({ nickName }),
})
}
2 changes: 1 addition & 1 deletion src/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ function isMatch(pathname: string, urls: string[]) {
return urls.some((url) => !!match(url)(pathname))
}

const matchersForAuth = ['/mypage/*', '/board/create/*']
const matchersForAuth = ['/mypage/*', '/board/create/*', '/signup']

const matchersForLogin = ['/login']

Expand Down
Loading

0 comments on commit 8c0b38c

Please sign in to comment.