-
Notifications
You must be signed in to change notification settings - Fork 20
[김희진] sprint7 #62
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-\uAE40\uD76C\uC9C4-sprint3"
[김희진] sprint7 #62
Changes from all commits
13a20d6
6cceb5f
e958833
999a929
a752cd5
d08c310
62d455d
e3bfa2b
1d7f90c
e94f517
320f069
f09ee29
4aff5bd
c18a8b0
f276e89
4d22130
aeff1c9
a2e3d27
d3ade0d
f0f6aff
23a453a
6595f78
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 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,11 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import axios from "axios"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const BASE_URL = process.env.REACT_APP_BASE_URL; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export async function getComments(productId) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const response = await axios.get( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| `${BASE_URL}/products/${productId}/comments?limit=3` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return response.data.list; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+5
to
+11
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. (더 나아가서)쿼리는
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export async function getComments(productId) { | |
| const response = await axios.get( | |
| `${BASE_URL}/products/${productId}/comments?limit=3` | |
| ); | |
| return response.data.list; | |
| } | |
| export async function getComments(productId, params) { | |
| const query = (new URLSearchParams(params)); | |
| const response = await axios.get( | |
| `${BASE_URL}/products/${productId}/comments?limit=3`, | |
| { params } | |
| ); | |
| return response.data.list; | |
| } |
axios를 사용하실 경우 URLSearchParams와 함께 객체로 손쉽게 핸들링할 수 있습니다 !
객체로 구성할 수 있어 가독성이 좋고, URL 인코딩을 자동으로 처리하여 특수 문자나 공백이 포함된 값에서도 안전하게 동작합니다 !
URLSearchParams:
URLSearchParams인터페이스는 URL의 쿼리 문자열을 대상으로 작업할 수 있는 유틸리티 메서드를 정의합니다.
쿼리를 생성하실 때에 참고해서 사용해보세요 😊
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| import * as S from "./Comment.styles"; | ||
| import CommentInput from "./CommentInput"; | ||
| import CommentList from "./CommentList"; | ||
|
|
||
| export default function Comment() { | ||
| return ( | ||
| <S.Container> | ||
| <CommentInput /> | ||
| <CommentList /> | ||
| </S.Container> | ||
| ); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| import styled from "styled-components"; | ||
|
|
||
| export const Container = styled.div` | ||
| max-width: 1200px; | ||
| width: 100%; | ||
| display: flex; | ||
| flex-direction: column; | ||
| gap: 24px; | ||
| `; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| import Input from "../common/Input/Input"; | ||
| import * as S from "./CommentInput.styles"; | ||
| import { useState } from "react"; | ||
|
|
||
| export default function CommentInput() { | ||
| const [comment, setComment] = useState(""); | ||
|
|
||
| return ( | ||
| <S.CommentContainer> | ||
| <Input | ||
| label="문의하기" | ||
| placeholder="개인정보를 공유 및 요청하거나, 명예 훼손, 무단 광고, 불법 정보 유포시 모니터링 후 삭제될 수 있으며, 이에 대한 민형사상 책임은 게시자에게 있습니다." | ||
| height="104px" | ||
| largeHeight="140px" | ||
| isTextarea | ||
| value={comment} | ||
| onChange={(e) => setComment(e.target.value)} | ||
| /> | ||
| <S.RegisterBtn> | ||
| <button disabled={!comment.trim()} onClick={() => setComment("")}> | ||
| 등록 | ||
| </button> | ||
| </S.RegisterBtn> | ||
| </S.CommentContainer> | ||
|
Comment on lines
+9
to
+24
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.
|
||
| ); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| import styled from "styled-components"; | ||
| import theme from "../../styles/theme"; | ||
|
|
||
| export const CommentContainer = styled.div` | ||
| width: 100%; | ||
| `; | ||
|
|
||
| export const RegisterBtn = styled.div` | ||
| width: 100%; | ||
| display: flex; | ||
| justify-content: flex-end; | ||
|
|
||
| button { | ||
| width: 74px; | ||
| height: 42px; | ||
| border-radius: 8px; | ||
| font: ${theme.font.H5Bold}; | ||
| color: ${theme.color.gray100}; | ||
| background-color: ${theme.color.blue}; | ||
| margin-top: 24px; | ||
| cursor: pointer; | ||
|
|
||
| &:disabled { | ||
| background-color: ${theme.color.gray400}; | ||
|
|
||
| &:hover { | ||
| background-color: ${theme.color.gray400}; | ||
| } | ||
| } | ||
|
|
||
| &:hover { | ||
| background-color: ${theme.color.blueHover}; | ||
| } | ||
| } | ||
| `; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,101 @@ | ||
| import * as S from "./CommentList.styles"; | ||
| import dots from "../../assets/icons/dots.svg"; | ||
| import { useState, useEffect } from "react"; | ||
| import { getComments } from "../../api/comment"; | ||
| import { useParams } from "react-router-dom"; | ||
| import User from "../User/User"; | ||
| import Input from "../common/Input/Input"; | ||
| import NoneComment from "../NoneComment/NoneComment"; | ||
|
|
||
| export default function CommentList() { | ||
| const [comments, setComments] = useState([]); | ||
| const [commentId, setCommentId] = useState(null); | ||
| const [editCommentId, setEditCommentId] = useState(null); | ||
| const { productId } = useParams(); | ||
|
|
||
| useEffect(() => { | ||
| getComments(productId) | ||
| .then((result) => setComments(result)) | ||
| .catch((error) => console.error(error)); | ||
| }, []); | ||
|
|
||
| function SelectBox({ comment }) { | ||
| const options = [ | ||
| { | ||
| id: 1, | ||
| option: "수정하기", | ||
| onClick: () => { | ||
| setEditCommentId(comment.id); | ||
| setCommentId(null); | ||
| }, | ||
| }, | ||
| { | ||
| id: 2, | ||
| option: "삭제하기", | ||
| onClick: () => { | ||
| setCommentId(null); | ||
| }, | ||
| }, | ||
| ]; | ||
| return ( | ||
| <S.SelectList> | ||
| {options.map((s) => ( | ||
| <S.SelectItem key={s.id} onClick={s.onClick}> | ||
| {s.option} | ||
| </S.SelectItem> | ||
| ))} | ||
| </S.SelectList> | ||
| ); | ||
| } | ||
|
|
||
| const handleOpenClick = (commentId) => { | ||
| setCommentId((prevId) => (prevId === commentId ? null : commentId)); | ||
| }; | ||
|
|
||
| if (comments.length === 0) return <NoneComment />; | ||
|
|
||
| return ( | ||
| <S.ListContainer> | ||
| {comments.map((comment) => ( | ||
| <S.Comment key={comment.id}> | ||
| {editCommentId === comment.id ? ( | ||
| <Input | ||
| placeholder="개인정보를 공유 및 요청하거나, 명예 훼손, 무단 광고, 불법 정보 유포시 모니터링 후 삭제될 수 있으며, 이에 대한 민형사상 책임은 게시자에게 있습니다." | ||
| value={comment.content} | ||
| style={{ height: "80px" }} | ||
| isTextarea | ||
| /> | ||
| ) : ( | ||
| <S.Content> | ||
| <S.Text>{comment.content}</S.Text> | ||
| <S.Select> | ||
| <S.Dots | ||
| src={dots} | ||
| onClick={() => handleOpenClick(comment.id)} | ||
| /> | ||
| {commentId === comment.id && <SelectBox comment={comment} />} | ||
| </S.Select> | ||
| </S.Content> | ||
| )} | ||
| <S.UserWrapper> | ||
| <User | ||
| owner={comment.writer.nickname} | ||
| createdAt={ | ||
| comment.updatedAt !== "" ? comment.updatedAt : comment.createdAt | ||
| } | ||
| detail={false} | ||
| /> | ||
| {editCommentId === comment.id && ( | ||
| <S.EditBtn> | ||
| <span onClick={() => setEditCommentId(null)}>취소</span> | ||
| <button onClick={() => setEditCommentId(null)}> | ||
| 수정 완료 | ||
| </button> | ||
| </S.EditBtn> | ||
| )} | ||
| </S.UserWrapper> | ||
| </S.Comment> | ||
| ))} | ||
| </S.ListContainer> | ||
| ); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,105 @@ | ||
| import styled from "styled-components"; | ||
| import theme from "../../styles/theme"; | ||
|
|
||
| export const ListContainer = styled.div` | ||
| display: flex; | ||
| flex-direction: column; | ||
| gap: 40px; | ||
|
|
||
| @media (max-width: 375px) { | ||
| gap: 16px; | ||
| } | ||
| `; | ||
|
|
||
| export const Comment = styled.div` | ||
| width: 100%; | ||
| display: flex; | ||
| flex-direction: column; | ||
| gap: 24px; | ||
| padding-bottom: 12px; | ||
| border-bottom: 1px solid ${theme.color.gray200}; | ||
| `; | ||
|
|
||
| export const Content = styled.div` | ||
| display: flex; | ||
| justify-content: space-between; | ||
| `; | ||
|
|
||
| export const Text = styled.div` | ||
| font: ${theme.font.H7Regular}; | ||
| color: ${theme.color.gray800}; | ||
| `; | ||
|
|
||
| export const Select = styled.div` | ||
| position: relative; | ||
| `; | ||
|
|
||
| export const Dots = styled.img` | ||
| width: 24px; | ||
| height: 24px; | ||
| cursor: pointer; | ||
| `; | ||
|
|
||
| export const SelectList = styled.ul` | ||
| width: 139px; | ||
| display: flex; | ||
| flex-direction: column; | ||
| background-color: ${theme.color.white}; | ||
| border: 1px solid ${theme.color.gray300}; | ||
| border-radius: 8px; | ||
| position: absolute; | ||
| right: 0; | ||
| `; | ||
|
|
||
| export const SelectItem = styled.li` | ||
| width: 100%; | ||
| display: flex; | ||
| justify-content: center; | ||
| align-items: center; | ||
| padding: 16px 0; | ||
| font: ${theme.font.H5Regular}; | ||
| color: ${theme.color.gray500}; | ||
| cursor: pointer; | ||
|
|
||
| &:hover { | ||
| font: ${theme.font.H5Bold}; | ||
| color: ${theme.color.gray800}; | ||
| } | ||
| `; | ||
|
|
||
| export const UserWrapper = styled.div` | ||
| display: flex; | ||
| justify-content: space-between; | ||
| align-items: center; | ||
| `; | ||
|
|
||
| export const EditBtn = styled.div` | ||
| display: flex; | ||
| justify-content: center; | ||
| align-items: center; | ||
| gap: 4px; | ||
|
|
||
| span { | ||
| padding: 0 20px; | ||
| font: ${theme.font.H5Bold}; | ||
| color: ${theme.color.gray500}; | ||
| cursor: pointer; | ||
| } | ||
|
|
||
| button { | ||
| width: 106px; | ||
| height: 42px; | ||
| display: flex; | ||
| justify-content: center; | ||
| align-items: center; | ||
| background-color: ${theme.color.blue}; | ||
| border-radius: 8px; | ||
| font: ${theme.font.H5Bold}; | ||
| color: ${theme.color.gray100}; | ||
| cursor: pointer; | ||
|
|
||
| &:hover { | ||
| background-color: ${theme.color.blueHover}; | ||
| } | ||
| } | ||
| `; |
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.
axios는 instance를 만드셔서 사용해볼 수 있습니다 ! 🤗instance를 만들어서export를 하고 사용해보는 것 정도로 시도해보면 좋을 것 같아요.axios-instance파일을 만들어서instance를 생성하고export한 후 사용해보는건 어떨까요?다음과 같이 만들어볼 수 있어요: