-
Notifications
You must be signed in to change notification settings - Fork 39
[전지윤] sprint6 #202
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
The head ref may contain hidden characters: "React-\uC804\uC9C0\uC724-sprint6"
[전지윤] sprint6 #202
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,3 +1,168 @@ | ||||||||||||
| import styles from "./AddItem.module.scss"; | ||||||||||||
| import { useState, useRef } from "react"; | ||||||||||||
|
|
||||||||||||
| export default function AddItem() { | ||||||||||||
| return <div>addItem</div>; | ||||||||||||
| const [imagePreview, setimagePreview] = useState(null); | ||||||||||||
| const [formData, setFormData] = useState({ | ||||||||||||
| image: "", | ||||||||||||
| name: "", | ||||||||||||
| about: "", | ||||||||||||
| price: "", | ||||||||||||
| tag: [], | ||||||||||||
| }); | ||||||||||||
| const inputRef = useRef(); | ||||||||||||
| const active = Object.values(formData).every((value) => | ||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💊 제안
Suggested change
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ❗️ 수정요청 |
||||||||||||
| Array.isArray(value) ? value.length > 0 : value | ||||||||||||
| ); | ||||||||||||
|
|
||||||||||||
| function handleChange(e) { | ||||||||||||
| const { name, value } = e.target; | ||||||||||||
| if (name === "image") { | ||||||||||||
| const newImage = e.target.files[0]; | ||||||||||||
| setimagePreview(URL.createObjectURL(newImage)); | ||||||||||||
| setFormData((prev) => ({ ...prev, image: newImage })); | ||||||||||||
| } else { | ||||||||||||
| setFormData((prev) => ({ ...prev, [name]: value })); | ||||||||||||
| } | ||||||||||||
| } | ||||||||||||
| function handleTag(e) { | ||||||||||||
| if (e.key === "Enter") { | ||||||||||||
| e.preventDefault(); //폼 내부에서 엔터 -> 자동 제출되는거 막음 | ||||||||||||
| setTimeout(() => { | ||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ❓ 질문 https://developer.mozilla.org/ko/docs/Web/API/KeyboardEvent/isComposing |
||||||||||||
| const newValue = inputRef.current.value.trim(); | ||||||||||||
| if (newValue) { | ||||||||||||
| setFormData((prev) => ({ | ||||||||||||
| ...prev, | ||||||||||||
| tag: [...prev.tag, newValue], | ||||||||||||
| })); | ||||||||||||
| } | ||||||||||||
| inputRef.current.value = ""; | ||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💊 제안 |
||||||||||||
| }, 0); | ||||||||||||
| } | ||||||||||||
| } | ||||||||||||
| const handleSubmit = (e) => { | ||||||||||||
| e.preventDefault(); | ||||||||||||
| console.log(formData); // 서버 전송 등 처리 | ||||||||||||
| }; | ||||||||||||
|
|
||||||||||||
| console.log(formData); | ||||||||||||
| function handleRemoveTag(tagToRemove) { | ||||||||||||
| setFormData((prev) => ({ | ||||||||||||
| ...prev, | ||||||||||||
| tag: prev.tag.filter((tag) => tag !== tagToRemove), | ||||||||||||
| })); | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| return ( | ||||||||||||
| <> | ||||||||||||
| <form className={styles.form} onSubmit={handleSubmit}> | ||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍
Comment on lines
+57
to
+58
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ❗️ 수정요청
Suggested change
|
||||||||||||
| <header className={styles["form__header"]}> | ||||||||||||
| <h1 className={styles.h1}>상품 등록하기</h1> | ||||||||||||
| <button | ||||||||||||
| className={`${styles["submit--active"]} ${ | ||||||||||||
| active ? "" : styles["submit--disabled"] | ||||||||||||
| }`} | ||||||||||||
| type="submit" | ||||||||||||
| disabled={!active} | ||||||||||||
| > | ||||||||||||
| 등록 | ||||||||||||
| </button> | ||||||||||||
| </header> | ||||||||||||
| <div className={styles["form__body"]}> | ||||||||||||
| <div className={styles["input__image"]}> | ||||||||||||
| <label | ||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ❗️ 수정요청 |
||||||||||||
| htmlFor="input-image" | ||||||||||||
| className={`${styles["input__label"]} ${styles["image--input"]}`} | ||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ❗️ 수정요청
Suggested change
|
||||||||||||
| > | ||||||||||||
| 상품 이미지 | ||||||||||||
| </label> | ||||||||||||
| <input | ||||||||||||
| id="input-image" | ||||||||||||
| type="file" | ||||||||||||
| accept="image/png, image/gif, image/jpeg" | ||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💊 제안 (사용자가 업로드창에서 옵션을 열어 확장자를 바꾸면 아래처럼 보입니다) https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/accept |
||||||||||||
| name="image" | ||||||||||||
| onChange={handleChange} | ||||||||||||
| placeholder="이미지 등록" | ||||||||||||
| style={{ display: "none" }} | ||||||||||||
| /> | ||||||||||||
| {formData.image && ( | ||||||||||||
| <div className={styles["image--preview"]}> | ||||||||||||
| <img src={imagePreview} alt="미리보기" /> | ||||||||||||
| <button | ||||||||||||
| className={`${styles.xButton} ${styles["delete-image"]}`} | ||||||||||||
|
Comment on lines
+91
to
+92
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ❗️ 수정요청
Suggested change
|
||||||||||||
| onClick={() => | ||||||||||||
| setFormData((prev) => ({ ...prev, image: "" })) | ||||||||||||
| } | ||||||||||||
| /> | ||||||||||||
| </div> | ||||||||||||
| )} | ||||||||||||
| </div> | ||||||||||||
| {formData.image && ( | ||||||||||||
| <p className={styles.warning}> | ||||||||||||
| *이미지 등록은 최대 1개까지 가능합니다. | ||||||||||||
| </p> | ||||||||||||
| )} | ||||||||||||
| <label htmlFor="input-name" className={styles["input__label"]}> | ||||||||||||
| 상품명 | ||||||||||||
| </label> | ||||||||||||
| <input | ||||||||||||
| id="input-name" | ||||||||||||
| className={styles["input__short-text"]} | ||||||||||||
| type="text" | ||||||||||||
| name="name" | ||||||||||||
| value={formData.name} | ||||||||||||
| onChange={handleChange} | ||||||||||||
| placeholder="상품명을 입력해주세요" | ||||||||||||
| /> | ||||||||||||
| <label htmlFor="input-about" className={styles["input__label"]}> | ||||||||||||
| 상품 소개 | ||||||||||||
| </label> | ||||||||||||
| <textarea | ||||||||||||
| id="input-about" | ||||||||||||
| className={styles["input__long-text"]} | ||||||||||||
| type="text" | ||||||||||||
| name="about" | ||||||||||||
| value={formData.about} | ||||||||||||
| onChange={handleChange} | ||||||||||||
| placeholder="상품 소개를 입력해주세요" | ||||||||||||
| /> | ||||||||||||
| <label htmlFor="input-price" className={styles["input__label"]}> | ||||||||||||
| 판매가격 | ||||||||||||
| </label> | ||||||||||||
| <input | ||||||||||||
| id="input-price" | ||||||||||||
| className={styles["input__short-text"]} | ||||||||||||
| type="text" | ||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💊 제안 |
||||||||||||
| name="price" | ||||||||||||
| value={formData.price} | ||||||||||||
| onChange={handleChange} | ||||||||||||
| placeholder="판맥 가격을 입력해주세요" | ||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ❗️ 수정요청
Suggested change
|
||||||||||||
| /> | ||||||||||||
| <label htmlFor="input-tag" className={styles["input__label"]}> | ||||||||||||
| 태그 | ||||||||||||
| </label> | ||||||||||||
| <input | ||||||||||||
| id="input-tag" | ||||||||||||
| className={styles["input__short-text"]} | ||||||||||||
| type="text" | ||||||||||||
| name="tag" | ||||||||||||
| ref={inputRef} | ||||||||||||
| onKeyDown={handleTag} | ||||||||||||
| placeholder="태그를 입력해주세요" | ||||||||||||
| /> | ||||||||||||
| <ul className={styles.tags}> | ||||||||||||
| {formData.tag.map((tag, index) => ( | ||||||||||||
| <li key={index} className={styles.tag}> | ||||||||||||
| <span>#{tag}</span> | ||||||||||||
| <button | ||||||||||||
| className={`${styles.xButton} ${styles["delete-tag"]}`} | ||||||||||||
| onClick={() => handleRemoveTag(tag)} | ||||||||||||
| /> | ||||||||||||
| </li> | ||||||||||||
| ))} | ||||||||||||
| </ul> | ||||||||||||
| </div> | ||||||||||||
| </form> | ||||||||||||
| </> | ||||||||||||
| ); | ||||||||||||
| } | ||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,180 @@ | ||
| @import "../../styles/variables.scss"; | ||
| .form { | ||
| width: 1200px; | ||
| margin: 0 auto; | ||
| } | ||
| .form__header { | ||
| display: flex; | ||
| justify-content: space-between; | ||
| align-items: center; | ||
| margin: 24px 0; | ||
| } | ||
| .h1 { | ||
| @include font-20-bold; | ||
| } | ||
| .submit--active { | ||
| @include font-16-semiBold; | ||
| text-align: center; | ||
| width: 74px; | ||
| height: 42px; | ||
| border-radius: 8px; | ||
| color: #f3f4f6; | ||
| background-color: #3692ff; | ||
| } | ||
| .submit--disabled { | ||
| background-color: #9ca3af; | ||
| } | ||
|
|
||
| .form__body { | ||
| display: flex; | ||
| flex-direction: column; | ||
| font-size: 20px; | ||
| } | ||
|
|
||
| .input__label { | ||
| @include font-18-bold; | ||
| margin: 32px 0 16px 0; | ||
| } | ||
|
|
||
| .input__short-text, | ||
| .input__long-text { | ||
| @include font-16-regular; | ||
| flex-basis: 100%; | ||
| min-height: 56px; | ||
| background-color: #f3f4f6; | ||
| color: #1f2937; | ||
| padding-left: 24px; | ||
| border-radius: 12px; | ||
| } | ||
| .input__long-text { | ||
| min-height: 282px; | ||
| padding-top: 16px; | ||
| } | ||
|
|
||
| .input__image { | ||
| position: relative; | ||
| margin-bottom: 298px; | ||
| } | ||
|
|
||
| .image--input::after { | ||
| @include font-16-regular; | ||
| content: "이미지 등록"; | ||
| position: absolute; | ||
| top: 200px; | ||
| left: 104px; | ||
| color: #9ca3af; | ||
| } | ||
| .image--input::before { | ||
| content: ""; | ||
| width: 282px; | ||
| height: 282px; | ||
| position: absolute; | ||
| top: 42px; | ||
| left: 0px; | ||
| border-radius: 12px; | ||
| background-color: #f3f4f6; | ||
| background-image: url("../../image/plus.svg"); | ||
| background-repeat: no-repeat; | ||
| background-position-x: 117px; | ||
| background-position-y: 98px; | ||
| } | ||
| .image--preview { | ||
| position: absolute; | ||
| top: 42px; | ||
| left: 306px; | ||
| border-radius: 12px; | ||
| background-color: #f3f4f6; | ||
| } | ||
| .image--preview img { | ||
| width: 282px; | ||
| height: 282px; | ||
| border-radius: 12px; | ||
| -o-object-fit: cover; | ||
| object-fit: cover; | ||
| } | ||
|
|
||
| .xButton { | ||
| width: 20px; | ||
| height: 20px; | ||
| border-radius: 50%; | ||
| background-color: #9ca3af; | ||
| background-image: url("../../image/x.svg"); | ||
| background-repeat: no-repeat; | ||
| background-position: center; | ||
| } | ||
| .delete-image { | ||
| position: absolute; | ||
| top: 14px; | ||
| left: 249px; | ||
| } | ||
| .warning { | ||
| @include font-16-regular; | ||
| margin-top: 16px; | ||
| color: #f74747; | ||
| } | ||
| .tags { | ||
| display: flex; | ||
| flex-wrap: wrap; | ||
| justify-content: flex-start; | ||
| width: 100%; | ||
| gap: 12px; | ||
| margin: 14px 0 69px 0; | ||
| } | ||
| .tag-wrapper { | ||
| position: relative; | ||
| } | ||
| .tag { | ||
| @include font-16-regular; | ||
| padding: 5px 12px; | ||
| border-radius: 26px; | ||
| color: #1f2937; | ||
| background-color: #f3f4f6; | ||
| display: flex; | ||
| gap: 10px; | ||
| align-items: center; | ||
| } | ||
|
|
||
| @media (max-width: 744px) { | ||
| .form { | ||
| width: 696px; | ||
| } | ||
|
|
||
| .image--input::after { | ||
| top: 138px; | ||
| left: 47px; | ||
| color: #9ca3af; | ||
| } | ||
| .image--input::before { | ||
| width: 168px; | ||
| height: 168px; | ||
| background-position-x: 60px; | ||
| background-position-y: 41px; | ||
| } | ||
|
|
||
| .input__image { | ||
| margin-bottom: 184px; | ||
| } | ||
| .input__label { | ||
| margin-top: 24px; | ||
| } | ||
| .image--preview { | ||
| top: 37px; | ||
| left: 178px; | ||
| } | ||
| .image--preview img { | ||
| width: 168px; | ||
| height: 168px; | ||
| } | ||
| .delete-image { | ||
| top: 14px; | ||
| left: 135px; | ||
| } | ||
| .warning { | ||
| margin-top: 8px; | ||
| } | ||
| } | ||
| @media (max-width: 376px) { | ||
| .form { | ||
| width: 346px; | ||
| } | ||
| } |

There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💊 제안
하나의 파일이 너무 큰 것 같아요~
내부적으로 아이템 생성을 위한 로직들과 state가 custom hook으로 분리되고 UI 들도 컴포넌트로 분리되면,
가독성 측면에서 더 좋을 것 같아요.
컴포넌트로 분리하는 것은 생각의 단위를 나누는 것과 같이 때문에 너무 크게 가지고 가시면 로직을 파악하기도 어렵고 유지보수시에도불리합니다!