Skip to content

Commit

Permalink
Merge pull request #222 from cksrlcks/React-김찬기-sprint6
Browse files Browse the repository at this point in the history
[김찬기] Sprint6
  • Loading branch information
withyj-codeit authored Dec 3, 2024
2 parents 78f9ab8 + f5f4499 commit 30ceb04
Show file tree
Hide file tree
Showing 148 changed files with 2,240 additions and 979 deletions.
37 changes: 37 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,40 @@ state 관리하고 있는 params과 usePageSize로 관리하는 pageSize의 변
- useList에서 보정을 하려면, pagination 훅에서 관리하는 값을 가져와야했고,
- usePageSize에서 보정을 하려면 List컴포넌트에서 관리하고 있는 params state의 정보와 관리하는 함수들을 가져와야했습니다.
- 최종적으로 내린 결론은, 통신쪽을 수정하여 이전 요청을 취소하고 통신을 하는 방향으로 수정을 했습니다.

#### useList vs useFetch vs useAsync

데이터를 받는 훅을 만들때, 처음에는 리스트에 특화된 훅을 만드려고 했는데 다른곳에서는 재사용하기 힘들어서
범용적인 데이터를 받는 훅을 만드는게 좋을것 같아서 고민을 했습니다.

1. 첫번째 시도(기존코드) (리스트형 데이터 패칭에 특화된 useList)

- 장점

- useList를 호출할때 넣어야되는 필요로하는 인자가 정해져 있어서 편리(눈에 잘보임)
- params 객체를 넘겨받아도 인자가 정해져있으니 구조분해하여 useEffect의 의존성배열에 넣기 수월

- 단점
- 하지만 모든 요청에 동일한 인자를 보내는건 아니므로 범용성이 떨어져보임
- 좀더 일반적인 함수로 바꿔보려고 두번째 시도인 useFetch를 생각

2. 두번째 시도 (데이터요청함수를 실행만 하고 그 함수에 보낼 인자들을 넘겨주기만 하는 useFetch)

- 장점
- 훅 자체는 요청함수, 인자들에대해 모르고 있어도 되어서 범용성이 좋은것 같음
- 단점
- 그대신 넘어가는 인자들을 객체로 보내는데 useMemo로 참조를 보정해줘야함(어떤게 올지 몰라서, 구조분해로 분리못함)
- 그리고 무슨 인자로 요청하는지는 파악이 안됨 (사용하는 입장에서)

3. 세번째 시도 (둘다 이용해볼까?)

- useList가 useFetch를 이용하면 안될까? 필요한곳에서 useFetch를 재사용도 가능짐
- useList가 받은 객체를 메모지이에션해서 useFetch로 넘겨주는 방식으로 작업했습니다.
- 효과 : useList에서 받아야하는 인자값을 나타낼수 있고, useFetch를 useList 뿐만 아니라 다른곳에서도 사용 가능

4. 네번째 마지막 시도 ㅜㅜ(useFetch와 useAsync를 쓰는 방법, 차이점)

- 생각해보니 useFetch는 '주소'를 받아서 fetch처리하는 훅으로 사용되어야할 것 같음 (구글링의 예제 코드들을 분석해봄)
- url주소 기반으로 처리하는 useFetch를 쓰게되면, 만약에 api url이 변경되거나 요청전 작업이 수정되면 수정하러 이곳저곳을 돌아다녀야 할 것 같음.
- 비동기 요청함수를 받아서 처리하는 훅으로 만들어둔 useAsync를 쓰는게 더 적합해보임
- 주소를 기반으로 fetch만을 처리하는 훅으로 useFetch를 수정하고 남겨두기로 결정
1 change: 0 additions & 1 deletion jsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"],
"@components/*": ["src/components/*"],
"@hooks/*": ["src/hooks/*"],
"@context/*": ["src/context/*"],
Expand Down
7 changes: 7 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"private": true,
"dependencies": {
"clsx": "^2.1.1",
"lodash": "^4.17.21",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.28.0"
Expand Down
8 changes: 6 additions & 2 deletions src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import { Outlet } from "react-router-dom";
import { AuthProvider } from "./context/AuthContext";
import { RouterProvider } from "react-router-dom";
import { router } from "./router";

export default function App() {
return (
<>
<Outlet />
<AuthProvider>
<RouterProvider router={router} />
</AuthProvider>
</>
);
}
4 changes: 4 additions & 0 deletions src/assets/img/icon/icon_double_arrow_left.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions src/assets/img/icon/icon_double_arrow_right.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions src/assets/img/icon/icon_error.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions src/assets/img/icon/icon_plus.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions src/assets/img/icon/icon_warn.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 2 additions & 1 deletion src/assets/scss/base/_reset.scss
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ button {
img {
vertical-align: top;
}
input {
input,
textarea {
padding: 0;
margin: 0;
border: none;
Expand Down
1 change: 1 addition & 0 deletions src/assets/scss/common/_index.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@forward "input";
38 changes: 38 additions & 0 deletions src/assets/scss/common/_input.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
.field {
position: relative;

.field-box {
display: block;
width: 100%;
height: 5.6rem;
padding: 0 2.4rem;
border: 1px solid transparent;
border-radius: 12px;
background: var(--color-secondary-100);

&:focus,
&.valid {
border: 1px solid var(--color-primary-100);
outline: none;
}

&.error {
border: 1px solid var(--color-error);
outline: none;
}

&::placeholder {
color: var(--color-secondary-400);
}

&:has(~ button) {
padding-right: 5rem;
}
}

textarea.field-box {
height: 28.2rem;
padding: 2.4rem;
resize: none;
}
}
1 change: 1 addition & 0 deletions src/assets/scss/style.scss
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
@use "base";
@use "common";
5 changes: 0 additions & 5 deletions src/components/Container/index.jsx

This file was deleted.

12 changes: 12 additions & 0 deletions src/components/Field/Error.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import styles from "./Error.module.scss";

export function Error({ error }) {
if (!error) {
return null;
}
return (
<div className={styles["item-error"]}>
{error || "오류가 발생했습니다."}
</div>
);
}
7 changes: 7 additions & 0 deletions src/components/Field/Error.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.item-error {
margin-top: 0.8rem;
padding-left: 1.6rem;
font-size: 1.4rem;
font-weight: 600;
color: var(--color-error);
}
15 changes: 15 additions & 0 deletions src/components/Field/FieldItem.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import styles from "./FieldItem.module.scss";

export function FieldItem({ children }) {
return <div className={styles["form-item"]}>{children}</div>;
}

function Label({ htmlFor, children }) {
return (
<label className={styles["item-label"]} htmlFor={htmlFor}>
{children}
</label>
);
}

FieldItem.Label = Label;
26 changes: 26 additions & 0 deletions src/components/Field/FieldItem.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
@use "@assets/scss/base/mixins";

.form-item {
margin-bottom: 2.4rem;
position: relative;

@include mixins.mobile {
margin-bottom: 1.6rem;
}

.item-label {
display: inline-block;
margin-bottom: 1.6rem;
font-size: 1.8rem;
font-weight: 700;

@include mixins.mobile {
margin-bottom: 0.8rem;
font-size: 1.4rem;
}
}

.item-field {
position: relative;
}
}
13 changes: 13 additions & 0 deletions src/components/Field/Form.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Alert, LoadingSpinner } from "@components/ui";

export function Form({ isLoading, error, onSubmit, children }) {
return (
<>
{isLoading && <LoadingSpinner />}
{error && (
<Alert mode="error">{error.message || "오류가 발생했습니다."}</Alert>
)}
<form onSubmit={onSubmit}>{children}</form>
</>
);
}
55 changes: 55 additions & 0 deletions src/components/Field/ImageUpload.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import useSingleFile from "@hooks/useSingleFile";
import { Thumbnail } from "@components/ui";
import { Error } from "@components/Field";
import iconPlus from "@assets/img/icon/icon_plus.svg";
import styles from "./ImageUpload.module.scss";

const LIMIT_SIZE_MB = 2;

export function ImageUpload({
error,
value,
id,
name,
onChange,
placeholder = "이미지 등록",
}) {
const { fileProps, fileError, handleRemove, preview } = useSingleFile({
name,
value,
accept: "image/*",
limiSize: LIMIT_SIZE_MB,
onChange: (file) => onChange(name, file),
errorMessage: {
max: "이미지 등록은 최대 1개까지 가능합니다.",
accept: "이미지 파일만 업로드 가능합니다.",
},
});

// 두가지 에러 동시에 보내려고 문자열로 합침
const fileInputError = [fileError, error].filter((err) => err).join(" / ");

return (
<>
<div className={styles["thumbnail-list"]}>
<label className={styles["upload-button"]}>
<span className={styles["upload-label"]}>
<img src={iconPlus} alt="이미지 업로드" />
{placeholder}
</span>
<input id={id} name={name} className="a11y" {...fileProps} />
</label>
<div className={styles.preview}>
{preview && (
<Thumbnail
src={preview}
alt="상품 이미지 등록"
onRemove={handleRemove}
/>
)}
</div>
</div>
<Error error={fileInputError} />
</>
);
}
41 changes: 41 additions & 0 deletions src/components/Field/ImageUpload.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
.thumbnail-list {
display: flex;
gap: 2.4rem;
}

.upload-button {
overflow: hidden;
position: relative;
display: block;
width: 100%;
max-width: 28.2rem;
border-radius: 1.6rem;
background: var(--color-secondary-100);
cursor: pointer;

&:before {
content: "";
display: block;
width: 100%;
height: 0;
padding-bottom: 100%;
}

.upload-label {
position: absolute;
left: 0;
top: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 1.3rem;
width: 100%;
height: 100%;
color: var(--color-secondary-400);
}
}

.preview {
max-width: 28.2rem;
}
Loading

0 comments on commit 30ceb04

Please sign in to comment.