Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] #51 소셜로그인, 회원가입 기능 구현 #129

Merged
merged 53 commits into from
Dec 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
376d13f
📝 Docs: 데이트피커 반환 타입 Date로 수정
wonill Dec 2, 2024
c6bed9e
🐛 Fix: 모달 중첩시 생기는 버그 수정
wonill Dec 2, 2024
78a758d
✨ Feat: 반려견 등록 api
wonill Dec 3, 2024
a9445c8
✨ Feature : Social Login Kakao 연결
kimjuyoung78 Dec 3, 2024
0ed4b64
✨ Feat : 마이페이지 | 회원정보조회 GET 통신, App.tsx 쿼리클라이언트 추가
kimjuyoung78 Dec 3, 2024
b572dc0
✨ Feature : 견주 정보 등록 기능 구현, ENUM값 변경
kimjuyoung78 Dec 3, 2024
0ddf8d0
✨ Feat: 강아지 프로필 등록 api 추가
shlee9999 Dec 3, 2024
4d99391
📝 Docs: api type 병합
wonill Dec 3, 2024
7157d54
✨ Feat: 가족 코드 검증 api 추가
wonill Dec 3, 2024
0795016
🐛 Fix: 강아지 프로필 유효성 검증 로직 수정
wonill Dec 4, 2024
d9c32a8
♻️ Refactore : ENUM 타입 명시
kimjuyoung78 Dec 4, 2024
16d4b50
Merge branch 'develop' of https://github.com/prgrms-web-devcourse-fin…
kimjuyoung78 Dec 4, 2024
b62f033
🐛 fix : 수정사항
kimjuyoung78 Dec 4, 2024
0c6c6bc
Merge branch 'develop' of https://github.com/prgrms-web-devcourse-fin…
kimjuyoung78 Dec 4, 2024
e0acd79
✨ Feature : useSearchParam으로 로그인 토큰 localstorage 저장
kimjuyoung78 Dec 4, 2024
80977b4
✨ Feature : 비로그인 상태일 경우 login 화면 router처리
kimjuyoung78 Dec 4, 2024
5db69b4
✨ Feature : 회원탈퇴 기능 구현
kimjuyoung78 Dec 4, 2024
75a4cdf
Merge branch '113-feature/register-dog-profile-api' of https://github…
kimjuyoung78 Dec 4, 2024
afa8736
🐛 Fix : 소셜로그인 - 견주정보 등록 성공, 강아지정보등록 실패
kimjuyoung78 Dec 4, 2024
99451e7
✨ Feat: 반려견 이미지 업로드시 multipart-formData를 전송하는 형태로 수정
wonill Dec 4, 2024
ee423a7
✨ Feat: 반려견 등록 유효성 검증 로직 추가
wonill Dec 5, 2024
89783e7
Merge branch '113-feature/register-dog-profile-api' of https://github…
kimjuyoung78 Dec 5, 2024
e5c5b43
🐛 Fix: 이미지 업로드 안 되는 문제 수정
wonill Dec 5, 2024
7853119
✨ Feature : mypage 데이터 바인딩, dogProfile 컴포넌트 이미지 바인딩
kimjuyoung78 Dec 5, 2024
ed5725a
✨ Feature : token 없으면 footer 누르면 login 이동
kimjuyoung78 Dec 5, 2024
11abcd9
✨ Add : alert 메세지 추가
kimjuyoung78 Dec 5, 2024
5f59c79
🐛 Fix : 토큰 헤더에서 추출, 회원가입 완료 시 home 으로 navigate
kimjuyoung78 Dec 5, 2024
52e52d3
🐛 Fix : 주석삭제
kimjuyoung78 Dec 5, 2024
a5e8458
♻️ Refactor : Common.ts에 정의된 FamilyRole 타입 활용
kimjuyoung78 Dec 5, 2024
1d6cf2a
🐛 Fix : 소셜로그인 Naver 주석처리 (구현X)
kimjuyoung78 Dec 5, 2024
a6f5b43
Merge branch 'develop' of https://github.com/prgrms-web-devcourse-fin…
kimjuyoung78 Dec 5, 2024
ae1ed27
🔨 Setting : Errorboundery 라이브러리 설치
kimjuyoung78 Dec 5, 2024
7818e50
🐛 Fix : 라이트하우스 오류, 안쓰는 import 주석처리
kimjuyoung78 Dec 5, 2024
66a06d8
🐛 Fix : DeleteMemberResponse 타입을 빈 객체({})에서 Record<string, never>로 변경
kimjuyoung78 Dec 5, 2024
f2fd4ef
♻️ Refactor : DogProfile props 데이터 바인딩
kimjuyoung78 Dec 5, 2024
10a7415
🐛 Fix : 강아지 정보 바인딩 수정
kimjuyoung78 Dec 5, 2024
f556b12
🐛 Fix : 강아지 이미지 업로드 수정
kimjuyoung78 Dec 5, 2024
3d3cd32
🐛 Fix : DogProfileType optional처리
kimjuyoung78 Dec 5, 2024
49693a4
🐛 Fix : birthDate 전역변수명 수정
kimjuyoung78 Dec 5, 2024
c5bcf1f
🐛 Fix : 타입 변환
kimjuyoung78 Dec 5, 2024
73c0d8b
♻️ Refactor: 성별 타입 수정
wonill Dec 5, 2024
53c77b2
📝 Docs: DogProfileType api에 맞게 수정
wonill Dec 5, 2024
c11d18e
🐛 Fix: 타입 수정하면서 발생한 오류 수정
wonill Dec 5, 2024
aa85bea
🐛 Fix : 회원가입 token 수정
kimjuyoung78 Dec 5, 2024
24c1dd7
🐛 Fix : 헤더 토큰 오류 해결
kimjuyoung78 Dec 5, 2024
fa6cb53
🐛 fix : svg 소문자 변경
kimjuyoung78 Dec 5, 2024
3b350a6
✨ Feat : 로그아웃 기능 구현
kimjuyoung78 Dec 6, 2024
34922eb
✨ Feature : 내 주소 바로 불러오기 useEffect 사용
kimjuyoung78 Dec 6, 2024
440fe96
🐛 Fix : 환경변수 파싱, 스니펫 최신버전으로 수정
kimjuyoung78 Dec 6, 2024
0d3be3a
♻️ Refactor : 회원가입 api 스니펫 최신버전으로 수정
kimjuyoung78 Dec 6, 2024
a364551
🐛 Fix : theme 삭제
kimjuyoung78 Dec 6, 2024
be57127
♻️ Refactor : 환경변수 파싱(BACK_URL)
kimjuyoung78 Dec 6, 2024
e9e8b8e
🐛 Fix: 빌드 오류 해결
ruehan Dec 6, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@
"@types/styled-components": "^5.1.34",
"@typescript-eslint/eslint-plugin": "^7.8.0",
"@typescript-eslint/parser": "^7.8.0",
"@types/ol": "^7.0.0",
"@types/react-textarea-autosize": "^8.0.0",
"@vite-pwa/assets-generator": "^0.2.4",
"@vitejs/plugin-react": "^4.2.1",
"@vitejs/plugin-react-swc": "^3.5.0",
Expand Down
2 changes: 0 additions & 2 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,10 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'

const queryClient = new QueryClient()

function App() {
//* 다크모드 확장성 고려
const [theme, setTheme] = useState(lightTheme)
const toggleTheme = () => setTheme(prev => (prev === lightTheme ? darkTheme : lightTheme))

return (
<>
<WebSocketProvider>
Expand Down
1 change: 1 addition & 0 deletions src/apis/axiosInstance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import axios, { AxiosInstance } from 'axios'
export const axiosInstance: AxiosInstance = axios.create({
baseURL: import.meta.env.VITE_SERVER_URL,
timeout: 5000,
withCredentials: true,
headers: {
'Content-Type': 'application/json',
},
Expand Down
44 changes: 44 additions & 0 deletions src/apis/dog/createDogProfile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { AxiosError } from 'axios'
import { APIResponse, ErrorResponse } from '~types/api'
import { axiosInstance } from '~apis/axiosInstance'

interface CreateDogProfileResponse {
dogId: number
name: string
breed: string
birthDate: string
weight: number
gender: 'MALE' | 'FEMALE'
profileImg: File
isNeutered: 'TRUE' | 'FALSE'
familyId: number
comment: string
}

export const createDogProfile = async (formData: FormData): Promise<APIResponse<CreateDogProfileResponse>> => {
try {
const { data } = await axiosInstance.post<APIResponse<CreateDogProfileResponse>>('/dogs/create', formData, {
headers: {
'Content-Type': 'multipart/form-data',
},
})
return data
} catch (error) {
if (error instanceof AxiosError) {
const { response, request } = error as AxiosError<ErrorResponse>

if (response) {
console.error('반려견 등록 오류:', response.data)
throw new Error(response.data.message ?? '요청 실패')
}

if (request) {
console.error('요청 에러:', request)
throw new Error('네트워크 연결을 확인해주세요')
}
}

console.error('예상치 못한 에러:', error)
throw new Error('다시 시도해주세요')
}
}
31 changes: 31 additions & 0 deletions src/apis/dog/deleteDogProfile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { AxiosError } from 'axios'
import { APIResponse, ErrorResponse } from '~types/api'
import { axiosInstance } from '~apis/axiosInstance'

interface DeleteDogProfileResponse {
data: Record<string, never>
}

export const deleteDogProfile = async (id: number): Promise<APIResponse<DeleteDogProfileResponse>> => {
try {
const { data } = await axiosInstance.delete<APIResponse<DeleteDogProfileResponse>>(`/dogs/${id}`)
return data
} catch (error) {
if (error instanceof AxiosError) {
const { response, request } = error as AxiosError<ErrorResponse>

if (response) {
console.error('반려견 삭제 오류:', response.data)
throw new Error(response.data.message ?? '요청 실패')
}

if (request) {
console.error('요청 에러:', request)
throw new Error('네트워크 연결을 확인해주세요')
}
}

console.error('예상치 못한 에러:', error)
throw new Error('다시 시도해주세요')
}
}
44 changes: 44 additions & 0 deletions src/apis/dog/fetchDogProfile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { AxiosError } from 'axios'
import { APIResponse, ErrorResponse } from '~types/api'
import { axiosInstance } from '~apis/axiosInstance'

interface DogProfileDetail {
dogId: number
name: string
breed: string
birthDate: string
weight: number
gender: 'MALE' | 'FEMALE'
profileImg: string
isNeutered: 'TRUE' | 'FALSE'
familyId: number
comment: string
}

export interface FetchDogProfileResponse {
data: DogProfileDetail
}

export const fetchDogProfile = async (id: number): Promise<APIResponse<FetchDogProfileResponse>> => {
try {
const { data } = await axiosInstance.get<APIResponse<FetchDogProfileResponse>>(`/dogs/${id}`)
return data
} catch (error) {
if (error instanceof AxiosError) {
const { response, request } = error as AxiosError<ErrorResponse>

if (response) {
console.error('반려견 조회 오류:', response.data)
throw new Error(response.data.message ?? '요청 실패')
}

if (request) {
console.error('요청 에러:', request)
throw new Error('네트워크 연결을 확인해주세요')
}
}

console.error('예상치 못한 에러:', error)
throw new Error('다시 시도해주세요')
}
}
33 changes: 33 additions & 0 deletions src/apis/family/joinFamily.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { AxiosError } from 'axios'
import { APIResponse, ErrorResponse } from '~types/api'
import { axiosInstance } from '~apis/axiosInstance'

interface JoinFamilyResponse {
familyId: number
memberId: number
familyName: string
}

export const joinFamily = async (inviteCode: string): Promise<APIResponse<JoinFamilyResponse>> => {
try {
const { data } = await axiosInstance.post<APIResponse<JoinFamilyResponse>>('/family/join', { inviteCode })
return data
} catch (error) {
if (error instanceof AxiosError) {
const { response, request } = error as AxiosError<ErrorResponse>

if (response) {
console.error('가족 참여 오류:', response.data)
throw new Error(response.data.message ?? '요청 실패')
}

if (request) {
console.error('요청 에러:', request)
throw new Error('네트워크 연결을 확인해주세요')
}
}

console.error('예상치 못한 에러:', error)
throw new Error('다시 시도해주세요')
}
}
40 changes: 40 additions & 0 deletions src/apis/logout/deleteLogoutMember.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { AxiosError } from 'axios'
import { ErrorResponse } from '~types/api'
import { axiosInstance } from '~apis/axiosInstance'

export type DeleteLogoutMemberResponse = {
code: number
status: '100 CONTINUE'
message: string
data: string
}

export const deleteLogoutMember = async (): Promise<DeleteLogoutMemberResponse> => {
try {
const { data } = await axiosInstance.delete<DeleteLogoutMemberResponse>(`/member/logout`)
return data
} catch (error) {
if (error instanceof AxiosError) {
const { response } = error as AxiosError<ErrorResponse>

if (response) {
const { code, message } = response.data
switch (code) {
case 400:
throw new Error(message || '잘못된 요청입니다.')
case 401:
throw new Error(message || '인증에 실패했습니다.')
case 500:
throw new Error(message || '서버 오류가 발생했습니다.')
default:
throw new Error(message || '알 수 없는 오류가 발생했습니다.')
}
}

throw new Error('네트워크 연결을 확인해주세요')
}

console.error('예상치 못한 에러:', error)
throw new Error('다시 시도해주세요')
}
}
36 changes: 36 additions & 0 deletions src/apis/myPage/deleteMember.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { AxiosError } from 'axios'
import { APIResponse, ErrorResponse } from '~types/api'
import { axiosInstance } from '~apis/axiosInstance'

export type DeleteMemberResponse = Record<string, never>

export const deleteMember = async (): Promise<APIResponse<DeleteMemberResponse>> => {
try {
const { data } = await axiosInstance.delete<APIResponse<DeleteMemberResponse>>(`/member/delete`)
return data
} catch (error) {
if (error instanceof AxiosError) {
const { response } = error as AxiosError<ErrorResponse>

if (response) {
const { code, message } = response.data
switch (code) {
case 400:
throw new Error(message || '잘못된 요청입니다.')
case 401:
throw new Error(message || '인증에 실패했습니다.')
case 500:
throw new Error(message || '서버 오류가 발생했습니다.')
default:
throw new Error(message || '알 수 없는 오류가 발생했습니다.')
}
} else {
// 요청 자체가 실패한 경우
throw new Error('네트워크 연결을 확인해주세요')
}
}

console.error('예상치 못한 에러:', error)
throw new Error('다시 시도해주세요')
}
}
55 changes: 55 additions & 0 deletions src/apis/myPage/fetchMypage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { AxiosError } from 'axios'
import { APIResponse, CommonAPIResponse, ErrorResponse } from '~types/api'
import { axiosInstance } from '~apis/axiosInstance'

export type FetchMypageResponse = Pick<
CommonAPIResponse,
| 'memberId'
| 'name'
| 'address'
| 'gender'
| 'familyRole'
| 'profileImg'
| 'isMatched'
| 'totalDistance'
| 'walkCount'
| 'countWalksWithMember'
| 'dog'
| 'dogId'
| 'breed'
| 'birthDate'
| 'weight'
| 'isNeutered'
| 'familyId'
| 'comment'
>

export const fetchMypage = async (): Promise<APIResponse<FetchMypageResponse>> => {
try {
const { data } = await axiosInstance.get<APIResponse<FetchMypageResponse>>(`/member/mypage`)
return data
} catch (error) {
if (error instanceof AxiosError) {
const { response } = error as AxiosError<ErrorResponse>

if (response) {
const { code, message } = response.data
switch (code) {
case 400:
throw new Error(message || '잘못된 요청입니다.')
case 401:
throw new Error(message || '인증에 실패했습니다.')
case 500:
throw new Error(message || '서버 오류가 발생했습니다.')
default:
throw new Error(message || '알 수 없는 오류가 발생했습니다.')
}
}
// 요청 자체가 실패한 경우
throw new Error('네트워크 연결을 확인해주세요')
}

console.error('예상치 못한 에러:', error)
throw new Error('다시 시도해주세요')
}
}
51 changes: 51 additions & 0 deletions src/apis/register/createRegister.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { AxiosError } from 'axios'
import { APIResponse, CommonAPIRequest, ErrorResponse } from '~types/api'
import { axiosInstance } from '~apis/axiosInstance'

export type CreateRegisterRequest = Pick<
CommonAPIRequest,
'email' | 'provider' | 'name' | 'gender' | 'address' | 'familyRole' | 'profileImg'
>

export type CreateRegisterResponse = Pick<
CommonAPIRequest,
'memberId' | 'name' | 'email' | 'provider' | 'gender' | 'address' | 'familyRole' | 'profileImg'
>

export const createRegister = async (req: CreateRegisterRequest): Promise<APIResponse<CreateRegisterResponse>> => {
try {
const response = await axiosInstance.post<APIResponse<CreateRegisterResponse>>(`/member/join`, req)

// 토큰 추출 및 저장
const accessToken = response.headers['authorization']
if (accessToken) {
localStorage.setItem('token', accessToken)
axiosInstance.defaults.headers.common['Authorization'] = accessToken
}

return response.data
} catch (error) {
if (error instanceof AxiosError) {
const { response } = error as AxiosError<ErrorResponse>

if (response) {
const { code, message } = response.data
switch (code) {
case 400:
throw new Error(message || '잘못된 요청입니다.')
case 401:
throw new Error(message || '인증에 실패했습니다.')
case 500:
throw new Error(message || '서버 오류가 발생했습니다.')
default:
throw new Error(message || '알 수 없는 오류가 발생했습니다.')
}
}
// 요청 자체가 실패한 경우
throw new Error('네트워크 연결을 확인해주세요')
}

console.error('예상치 못한 에러:', error)
throw new Error('다시 시도해주세요')
}
}
3 changes: 3 additions & 0 deletions src/assets/is-neutered-check.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading