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

[박상준] Week19 #495

Merged
Show file tree
Hide file tree
Changes from 43 commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
650aaaa
fix: 로그인 유무에 의한 페이지 리다이렉션 설정
sj0724 May 29, 2024
e957f73
fix: userContext 분리 provider 컴포넌트 생성, 커스텀훅 구현
sj0724 May 29, 2024
05c8c2c
fix: react hook form 타입 지정
sj0724 May 30, 2024
55759a3
fix: react-hook-form정상적용
sj0724 Jun 22, 2024
22741b8
Merge pull request #1 from sj0724/fix-input
sj0724 Jun 22, 2024
447cd3b
feat: 리액트 쿼리 설치 및 세팅
sj0724 Jun 22, 2024
1c30413
refactor: user context 데이터 react-query로 변경
sj0724 Jun 23, 2024
23c3207
Merge pull request #2 from sj0724/feature-query
sj0724 Jun 23, 2024
81d32fe
refactor: 폴더 리스트 페이지 전체 로직 리팩토링
sj0724 Jun 24, 2024
7f8a9ba
Merge pull request #3 from sj0724/feature-query
sj0724 Jun 24, 2024
603ec44
feat: 폴더 리스트 리액트 쿼리로 변경
sj0724 Jun 25, 2024
cceee4e
Merge branch 'part3-박상준-week15' into feature-query
sj0724 Jun 25, 2024
5a78e4d
feat: 링크 리스트 리액트 쿼리 적용
sj0724 Jun 25, 2024
31382aa
feat: 검색 기능, debounce기능 추가
sj0724 Jun 25, 2024
5da2477
refactor: 로그인전 user정보 리퀘스트 방지
sj0724 Jun 25, 2024
d06117f
feat: 로그인 페이지 리액트 쿼리 적용
sj0724 Jun 25, 2024
3ac39e8
Merge pull request #4 from sj0724/feature-query
sj0724 Jun 25, 2024
a86d24b
refactor: 링크 공유 페이지 리팩토링
sj0724 Jun 25, 2024
9a3df14
feat: 회원가입 페이지 리액트 쿼리 적용 및 react-hook-form 리팩토링
sj0724 Jun 26, 2024
70afb0d
Merge pull request #5 from sj0724/feature-query
sj0724 Jun 26, 2024
46e5205
refactor: 로그인 페이지 버튼 disabled 옵션 추가 및 스타일 변경
sj0724 Jun 26, 2024
29d4bd4
refactor: 로딩 컴포넌트 포탈에서 렌더링, 페이지 최상단에 위치설정
sj0724 Jun 26, 2024
1478bff
refactor: 회원가입 버튼 disabled 옵션 추가 및 로그인 로직 수정
sj0724 Jun 26, 2024
e8bd9ec
feat: 폴더 추가 리액트 쿼리 적용, 생성시 refetch설정
sj0724 Jun 27, 2024
bbed201
feat: 폴더이름 변경 리액트 쿼리 적용
sj0724 Jun 27, 2024
c158130
fix: api주소 변경으로 인한 코드 수정
sj0724 Jun 27, 2024
dc6e24e
fix: 검색기능 수정
sj0724 Jun 27, 2024
8fcc9b0
feat: shared페이지 리액트 쿼리 적용
sj0724 Jun 27, 2024
cc044d3
fix: 카드 케밥 메뉴, 즐겨찾기 아이콘 설정
sj0724 Jun 27, 2024
9c44f43
feat: 링크 추가 기능 리액트 쿼리 적용 및 사용자 편의성 개선
sj0724 Jun 27, 2024
d8e5e8d
refactor: 카드 리스트 렌더링 오류, 디펜던시값 추가
sj0724 Jun 27, 2024
b647fd5
feat: 링크 삭제 기능 리액트 쿼리 적용
sj0724 Jun 27, 2024
8efc686
feat: 링크 삭제 기능 리액트 쿼리 적용
sj0724 Jun 27, 2024
7d61fd2
refactor: api 리스폰스 도착전 버튼 비활성화 설정
sj0724 Jun 27, 2024
9993d82
Merge pull request #6 from sj0724/feature-query
sj0724 Jun 27, 2024
ba2d670
feat: 링크 즐겨찾기 기능 구현 및 옵티미스틱 업데이트 구현
sj0724 Jun 28, 2024
dc65c45
refactor: 링크 데이터 로직 리팩토링
sj0724 Jun 28, 2024
a7547be
refactor: 즐겨찾기 폴더 수정, 삭제 버튼 제거
sj0724 Jun 28, 2024
fa87972
fix: 빌드 오류 캐시 데이터 타입 지정 및 공유 페이지 수정
sj0724 Jun 28, 2024
60a0ab5
Merge pull request #7 from sj0724/feature-query
sj0724 Jun 28, 2024
1937e8b
fix: 모달 외부 클릭, esc클릭시 모달 close, 케밥 close 옵션 수정
sj0724 Jun 28, 2024
c28f12b
fix: 링크 추가시 인풋 value 초기화
sj0724 Jun 28, 2024
8e81eac
Merge pull request #8 from sj0724/feature-query
sj0724 Jun 28, 2024
2f5349b
feat: nextAuth 적용, 미들웨어 적용, 로그인 로직 리액트 쿼리 삭제
sj0724 Jul 1, 2024
9940b9c
Merge pull request #9 from sj0724/feature-nextauth
sj0724 Jul 1, 2024
e1820b0
refactor: 로그인 페이지 앱라우터로 리펙토링
sj0724 Jul 1, 2024
3239777
refactor: 회원가입 페이지 앱라우터로 마이그레이션
sj0724 Jul 2, 2024
4cf9a4b
refactor: 공유 페이지 앱라우터로 리팩토링
sj0724 Jul 2, 2024
c07080c
fix: 폴더 공유 링크 수정
sj0724 Jul 2, 2024
cae884c
Merge pull request #10 from sj0724/refactor-approuter
sj0724 Jul 2, 2024
e6312ca
fix: 링크 경로 수정
sj0724 Jul 3, 2024
f35a293
Merge pull request #11 from sj0724/refactor-approuter
sj0724 Jul 3, 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
133 changes: 40 additions & 93 deletions api/api.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,6 @@
import { Links } from '@/hooks/useGetFolder';
import axios from '../instance/instance';

axios.interceptors.request.use(
(config) => {
const accessToken = localStorage.getItem('token');
config.headers['Authorization'] = accessToken;

return config;
},
(error) => {
console.log(error);
return Promise.reject(error);
}
);

export async function getSampleUser() {
try {
const { data } = await axios.get('/sample/user');
return data;
} catch (error) {
console.error('Error fetching sample user:', error);
throw error;
}
}

interface Link {
imageSource?: string;
image_source: string;
sj0724 marked this conversation as resolved.
Show resolved Hide resolved
Expand Down Expand Up @@ -52,9 +30,9 @@ export async function getSampleFolder() {
}
}

export async function getFolder(id: string) {
export async function getFolder() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sj0724

getFolder 등 UI에 필요한 불러오는 서비스 레이어에서 각 함수들이 어떤 값을 반환하는지 타입을 설정해주면 UI에서 사용하기 편하지 않을까요?

또한, 에러핸들링의 경우, 던져진 오류를 catch로 잡아주었는데, 굳이 다시 던져주는 이유가 있을까요~?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

api 함수는 초기에 작성하고 리펙토링을 따로 진행하지 않아 다른 부분에서 코드를 인용해서 사용하고 그대로 뒀습니다. 그래서 따로 에러를 다시 던지는 이유는 없습니다.

try {
const { data } = await axios.get(`/users/${id}/folders`);
const data = await axios.get(`/folders`);
return data;
} catch (error) {
console.error('Error fetching folder:', error);
Expand All @@ -64,99 +42,62 @@ export async function getFolder(id: string) {

export async function getFolderData(folderId: string) {
try {
const { data } = await axios.get(`/folders/${folderId}`);
return data.data;
const data = await axios.get(`/folders/${folderId}`);
return data;
} catch (error) {
console.error('Error fetching folder:', error);
throw error;
}
}

export async function getFolderList(id: string, folderId: string) {
export async function getFolderList(folderId: string) {
let query;
if (folderId) {
try {
const query = `/${id}/links?folderId=${folderId}`;
const { data } = await axios.get(`/users${query}`);
return data.data;
} catch (error) {
console.error('Error fetching folderList:', error);
throw error;
}
} else if (!folderId) {
try {
const query = `/${id}/links`;
const { data } = await axios.get(`/users${query}`);
return data.data;
} catch (error) {
console.error('Error fetching folderList:', error);
throw error;
}
query = `/folders/${folderId}/links`;
} else {
query = '/links';
Comment on lines +54 to +58
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sj0724

쿼리는 무언가를 조회하기 위한 조건을 이야기합니다.
그렇다면, 네트워크 요청이 실제 발생하는 요소는 뭐라고 하는게 좋을까요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

url이라는 이름으로 사용해도 될까요?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

url은 전체 주소를 주로 이야기를 하죠, 이 경우는 endpoint라는 변수로 만들어 사용해주면 어떨까 싶습니다

}
const result = await axios.get(`${query}`);
return result;
}

export async function getUser(accessToken: string) {
try {
const { data } = await axios.get('/users', {
headers: {
Authorization: accessToken,
},
});
return data.data;
} catch (error) {
console.error('Error fetching user:', error);
throw error;
}
export async function getUser() {
const { data } = await axios.get('/users');
return data;
Comment on lines +60 to +66
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sj0724

요 녀석들도 적합한 에러 핸들링이 필요해요

}

export async function getUserData(id: string) {
try {
const { data } = await axios.get(`/users/${id}`);
return data.data;
const data = await axios.get(`/users/${id}`);
return data;
} catch (error) {
console.error('Error fetching user:', error);
throw error;
}
}

export async function postSignIn(id: string, password: string) {
try {
const { data } = await axios.post('/sign-in', {
email: id,
password: password,
});
localStorage.setItem('token', data.data.accessToken);
return data;
} catch (error) {
console.error('Error fetching sign-in:', error);
alert('로그인할 수 없습니다! 아이디와 비밀번호를 확인해주세요!');
}
const { data } = await axios.post('/auth/sign-in', {
email: id,
password: password,
});
localStorage.setItem('token', data.accessToken);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sj0724

accessToken은 로그인한 사용자에 대한 중요한 정보를 담고있어요.
누군가가 이 정보를 해킹한다면 바로 사용자 계정으로 접근해 핵심 정보를 추출해올 수 있겠죠.
그렇게 중요한 데이터를 localStorage에 관리하는건 좋ㅈ ㅣ않아 보이는데요, 어떻게 개선해볼 수 있을지 고민해볼까요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

우선 nextauth를 사용해서 세션에 저장하도록 변경했습니다.

return data;
}

export async function postCheckEmail(email: string) {
try {
const { data } = await axios.post('/check-email', {
email: email,
});
return data;
} catch (error) {
console.error('Error fetching sign-in:', error);
alert('이미 가입된 이메일입니다!');
}
const { data } = await axios.post('/users/check-email', {
email: email,
});
return data;
}

export async function postSignUp(id: string, password: string) {
try {
const { data } = await axios.post('/sign-up', {
email: id,
password: password,
});
localStorage.setItem('token', data.data.accessToken);
alert('회원가입이 완료되었습니다!');
return data;
} catch (error) {
console.error('Error fetching sign-in:', error);
alert('회원가입할 수 없습니다! 아이디와 비밀번호를 확인해주세요!');
}
const result = await axios.post('/auth/sign-up', {
email: id,
password: password,
});
return result;
}

export async function postFolder(name: string) {
Expand All @@ -172,7 +113,6 @@ export async function postFolder(name: string) {

export async function deleteFolder(folderId: string) {
try {
const token = localStorage.getItem('token');
const { data } = await axios.delete(`/folders/${folderId}`);
return data;
} catch (error) {
Expand All @@ -182,7 +122,7 @@ export async function deleteFolder(folderId: string) {

export async function postLink(folderId: string, url: string) {
try {
const { data } = await axios.post('/links', {
const data = await axios.post('/links', {
url: url,
folderId: folderId,
});
Expand Down Expand Up @@ -213,3 +153,10 @@ export async function putFolder(folderId: string, name: string) {
console.error('Error fetching put folder:', error);
}
}

export async function putLinkLike(linkId: string, favorite: boolean) {
const data = await axios.put(`/links/${linkId}`, {
favorite: favorite,
});
return data;
}
9 changes: 6 additions & 3 deletions components/Button/Button.styled.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,16 @@ const buttonSize = {
};

export const Cta = styled.button<ButtonProps>`
cursor: pointer;
cursor: ${({ disabled }) => (disabled ? 'auto' : 'pointer')};
text-decoration: none;
display: flex;
justify-content: center;
align-items: center;
border-radius: 0.8rem;
background: var(--Gradient-purpleblue-to-skyblue);
background: ${({ disabled }) =>
disabled
? 'var(--Linkbrary-gray60)'
: 'var(--Gradient-purpleblue-to-skyblue)'};
color: var(--Gray-cta);
padding: 1.6rem 2rem;
font-family: Pretendard;
Expand All @@ -28,7 +31,7 @@ export const Cta = styled.button<ButtonProps>`
white-space: nowrap;

&:hover {
opacity: 0.8;
opacity: ${({ disabled }) => (disabled ? 'none' : '0.8')};
}

@media (max-width: 768px) {
Expand Down
15 changes: 13 additions & 2 deletions components/Button/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,19 @@ import * as S from './Button.styled';

export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
size: 'xs' | 'sm' | 'md' | 'lg';
isActive?: boolean;
buttonActive?: boolean;
Comment on lines +7 to +8
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sj0724

isActive와 buttonActive는 어떻게 다른가요?
같은 목적으로 사용된다면 하나만 사용해주세요!

}

export function Button({ children, size }: ButtonProps) {
return <S.Cta size={size}>{children}</S.Cta>;
export function Button({
children,
size,
isActive,
buttonActive,
}: ButtonProps) {
return (
<S.Cta size={size} disabled={buttonActive || isActive}>
{children}
</S.Cta>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sj0724

ButtonProps는 버튼 요소가 지닌 모든 attribute를 상속받도록 해주셨어요.
그러면 children, size, isActive 외에도 type, onClick 등 다양한 요소들을 사용할 수 있어야 공통 버튼 콤포넌트로 평가할 수 있겠는데요. 우리가 선언한 값 외에 모든 버튼이 지닌 attribute들도 전달되도록 한번 수정해볼까요?

또한, CTA라는 네임은 Call to Action으로, 화면에서 사용자가 고객으로 전환을 하도록 넛징해주는 요소를 뜻해요.따라서 우리가 공통으로 사용하는 버튼을 CTA라고 명세하는건 좋지 않을 것 같아요.

);
}
Loading