Skip to content

Commit ad500c0

Browse files
committed
feat: 이미지 업로드 및 에러처리 구현
1 parent 91b9e2c commit ad500c0

File tree

7 files changed

+134
-20
lines changed

7 files changed

+134
-20
lines changed
Lines changed: 4 additions & 0 deletions
Loading

vite-project/src/App.css

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,17 @@ p {
106106
background-color: #9ca3af;
107107
}
108108

109+
.ic-x-btn {
110+
background-color: var(--gray400);
111+
color: #f9fafb;
112+
width: 20px;
113+
height: 20px;
114+
border-radius: 100%;
115+
display: flex;
116+
justify-content: center;
117+
align-items: center;
118+
}
119+
109120
.container {
110121
max-width: 1200px;
111122
margin: 0 auto;

vite-project/src/common/TagBadge/TagBadge.style.css

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,3 @@
1414
font-weight: 400;
1515
font-size: 16px;
1616
}
17-
18-
.tag-delete-badge-btn {
19-
background-color: var(--gray400);
20-
color: #f9fafb;
21-
width: 20px;
22-
height: 20px;
23-
border-radius: 100%;
24-
display: flex;
25-
justify-content: center;
26-
align-items: center;
27-
}

vite-project/src/common/TagBadge/index.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ const TagBadge = ({ name, onDelete }) => {
44
return (
55
<div className="tag-badge">
66
<span>#{name}</span>
7-
<button className="button tag-delete-badge-btn" onClick={onDelete}>
7+
<button className="button ic-x-btn" onClick={onDelete}>
88
<img src="images/ic_x.svg" alt="태그 삭제 이미지" />
99
</button>
1010
</div>

vite-project/src/pages/AddItemPage/AddItemPage.style.css

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
display: flex;
44
flex-direction: column;
55
gap: 16px;
6-
height: 1206px;
6+
padding-bottom: 50px;
77
}
88

99
.addItem-form {
@@ -33,8 +33,8 @@
3333
}
3434

3535
.addItem-input,
36-
.addItem-textArea {
37-
width: 100%;
36+
.addItem-textArea,
37+
.addItem-image-input {
3838
background-color: var(--gray100);
3939
padding: 16px 24px;
4040
border-radius: 12px;
@@ -44,7 +44,53 @@
4444
color: #1f2937;
4545
}
4646

47+
.addItem-input,
48+
.addItem-textArea {
49+
width: 100%;
50+
}
51+
52+
.addItem-image-input {
53+
cursor: pointer;
54+
display: flex;
55+
flex-direction: column;
56+
justify-content: center;
57+
align-items: center;
58+
gap: 10px;
59+
color: var(--gray400);
60+
}
61+
62+
.addItem-image-input,
63+
.addItem-image-card {
64+
aspect-ratio: 1;
65+
width: 282px;
66+
}
67+
4768
.addItem-textArea {
4869
height: 282px;
4970
resize: none;
5071
}
72+
73+
.addItem-image-area {
74+
display: flex;
75+
gap: 16px;
76+
}
77+
78+
.addItem-image-card {
79+
position: relative;
80+
}
81+
82+
.addItem-image-card > button {
83+
position: absolute;
84+
right: 10px;
85+
top: 10px;
86+
}
87+
88+
@media screen and (max-width: 767px) {
89+
.addItem-image-input,
90+
.addItem-image-card {
91+
width: 160px;
92+
}
93+
.addItem-image-area {
94+
gap: 10px;
95+
}
96+
}
Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,38 @@
1-
import React from "react";
1+
import ErrorMessage from "../../../../common/ErrorMessage";
22

3-
const AddItemImage = () => {
4-
return <div>index</div>;
3+
const AddItemImage = ({ ref, error, image, onChange, onDelete }) => {
4+
return (
5+
<div>
6+
<h4>상품 이미지</h4>
7+
<div className="addItem-image-area">
8+
<label htmlFor="file-upload" className="addItem-image-input">
9+
<img src="images/ic_plus.svg" />
10+
<span>이미지 등록</span>
11+
</label>
12+
<input
13+
id="file-upload"
14+
type="file"
15+
ref={ref}
16+
className="addItem-image-input"
17+
style={{ display: "none" }}
18+
onChange={onChange}
19+
/>
20+
{image && (
21+
<div className="addItem-image-card">
22+
<button
23+
type="button"
24+
className="button ic-x-btn"
25+
onClick={onDelete}
26+
>
27+
<img src="images/ic_x.svg" />
28+
</button>
29+
<img src={image} style={{ width: "100%", height: "100%" }} />
30+
</div>
31+
)}
32+
</div>
33+
{error && <ErrorMessage errorMessage={error} />}
34+
</div>
35+
);
536
};
637

738
export default AddItemImage;

vite-project/src/pages/AddItemPage/index.jsx

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useState } from "react";
1+
import { useRef, useState } from "react";
22
import "./AddItemPage.style.css";
33
import AddItemDescription from "./components/AddItemDescription";
44
import AddItemFormHeader from "./components/AddItemFormHeader";
@@ -18,8 +18,35 @@ const AddItemPage = () => {
1818
});
1919

2020
const [inputValueTag, setInputValueTag] = useState("");
21+
const [previewImage, setPreviewImage] = useState("");
2122
const [errors, setErrors] = useState({ image: "", tag: "" }); // POST요청 에러까지 추후에 들어갈지도
2223

24+
const fileInputRef = useRef(null);
25+
26+
const handleChangeImage = (e) => {
27+
if (previewImage) {
28+
setErrors((prev) => ({
29+
...prev,
30+
image: "이미지 파일은 1개만 등록할 수 있습니다",
31+
}));
32+
return;
33+
}
34+
const file = e.target.files[0];
35+
const url = URL.createObjectURL(file);
36+
setPreviewImage(url);
37+
const newImages = [url];
38+
setFormData((prev) => ({ ...prev, images: newImages }));
39+
};
40+
41+
const handleDeleteImage = () => {
42+
if (fileInputRef.current) {
43+
fileInputRef.current.value = "";
44+
}
45+
setPreviewImage("");
46+
setFormData((prev) => ({ ...prev, images: [] }));
47+
setErrors((prev) => ({ ...prev, image: "" }));
48+
};
49+
2350
const handleChange = (e, category) => {
2451
setFormData((prev) => ({ ...prev, [category]: e.target.value }));
2552
};
@@ -57,7 +84,13 @@ const AddItemPage = () => {
5784
<div className="addItem-page-layout">
5885
<form className="addItem-form" onSubmit={handleSubmitAddItem}>
5986
<AddItemFormHeader />
60-
<AddItemImage images={formData.images} error={errors.image} />
87+
<AddItemImage
88+
image={previewImage}
89+
ref={fileInputRef}
90+
error={errors.image}
91+
onChange={handleChangeImage}
92+
onDelete={handleDeleteImage}
93+
/>
6194
<AddItemName
6295
value={formData.name}
6396
onChange={(e) => handleChange(e, "name")}

0 commit comments

Comments
 (0)