-
Notifications
You must be signed in to change notification settings - Fork 26
[김참솔] Sprint 7 #103
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
[김참솔] Sprint 7 #103
The head ref may contain hidden characters: "React-\uAE40\uCC38\uC194-sprint7"
Conversation
외부와 간접적으로 접점이 없는 styled-components로 만든 component들은 `Styled~` 이름 규칙을 따르지 않습니다.
테스트를 위해 comments API 연동 코드를 주석 처리하고 dummy `CommentListItem`을 작성
- 판다마켓 API에 등록된 상품들에 comment 데이터가 없어서 mock data를 사용 - Comment 수정 및 삭제 기능은 실제 comments API 연동 후 추가 예정
|
스프리트 미션 하시느라 수고 많으셨어요. |
코드를 많이 정리하지 못해서 읽기 어려우실 수 있을 것 같습니다. 기초 프로젝트가 끝난 뒤에 다음 미션에서 코드를 더 정리해 보겠습니다 ㅠㅠㅎㅎㅎ 괜찮습니다. 정리할 때 이렇게하면 좋겠다 ~ 싶은거 있으면 제안드려볼게요 ! |
|
Component 분리 질문 확인해보겠습니다 ! |
|
JavaScript에서 enum 사용 방법 질문 넵 안타깝지만 현재는 지원하지 않습니다 🥲 나중에 배우실 타입스크립트에서는 사용하실 수 있어요. const BUTTON_SIZE = Object.freeze({
SMALL: 'small',
MEDIUM: 'medium',
LARGE: 'large',
}); |
| export function createUrl(path, params) { | ||
| const trimmed = trimPath(path); | ||
| const url = new URL(`${import.meta.env.VITE_API_BASE_URL}/${trimmed}`); | ||
|
|
||
| if (!params) { | ||
| return url; | ||
| } | ||
|
|
||
| Object.keys(params).forEach((key) => | ||
| url.searchParams.append(key, params[key]) | ||
| ); | ||
|
|
||
| return url; | ||
| } |
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.
(심화/응용) 해당 함수는 Base URL을 만드는 유틸 함수로 보이는군요 !
다음과 같이 fetch를 간단히 사용할 수 있으면 얼마나 좋을까요?:
const response = codeitApiInstance.get("products") // => fetch("https://bootcamp-api.codeit.kr/api/products")그 외에도 여러 상황들을 고려해볼 수 있어요.
예를 들어서 method를 고려해야 하며, 그 중 post일 경우 body를 고려해볼 수 있고, get일 경우에는 쿼리(?query=1)를 고려하게 될겁니다 !
더 나아가면 공통적인 예외 처리, 응답 실패 시의 예외 처리, 특정 path에 대한 예외 처리 등등도 고려될 수 있겠네요.
이러한 모듈을 만들어 보는 것도 좋은 경험이 될 수 있을거예요.
다음은 GPT로 만들어낸 예시입니다:
class HttpClient {
constructor(baseURL) {
this.baseURL = baseURL;
}
async request(endpoint, { method = 'GET', body, headers = {}, query } = {}) {
const url = new URL(`${this.baseURL}/${endpoint}`);
// GET 요청일 때 쿼리스트링 처리
if (method === 'GET' && query) {
Object.entries(query).forEach(([key, value]) => {
url.searchParams.append(key, value);
});
}
const options = {
method,
headers: {
'Content-Type': 'application/json',
...headers,
},
};
if (body && method !== 'GET') {
options.body = JSON.stringify(body);
}
try {
const response = await fetch(url.toString(), options);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
} catch (error) {
console.error('Fetch error:', error.message);
throw error;
}
}
get(endpoint, config = {}) {
return this.request(endpoint, { ...config, method: 'GET' });
}
post(endpoint, body, config = {}) {
return this.request(endpoint, { ...config, method: 'POST', body });
}
put(endpoint, body, config = {}) {
return this.request(endpoint, { ...config, method: 'PUT', body });
}
delete(endpoint, config = {}) {
return this.request(endpoint, { ...config, method: 'DELETE' });
}
}그리고 사용 예시:
const api = new HttpClient('https://bootcamp-api.codeit.kr/api');
// GET 요청
api.get('products', { query: { page: 1, pageSize: 10 } })
.then((data) => console.log(data));
// POST 요청
api.post('login', {
email: '[email protected]',
password: '1234',
});참솔님이라면 이러한 학습 욕심도 있으실 것 같아서 제안드려봅니다 ~!
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를 사용하는 방법도 있어요 !
fetch 모듈을 잘 만든다는 것은 어렵습니다. 다음 사항들을 고려해볼 수 있어요:
- 만약
get이 아닌 메써드(post,patch,delete등)일 경우는 어떻게 처리할 수 있을까요? query와body가 필요할 때는 어떻게 처리 할 수 있을까요?- 로그인 인가를 위한 토큰을 request 전에 자동으로 삽입할 수는 없을까요? (인증/인가를 자동으로 할 수 없을까요?)
- 처음 한 번에 Base URL을 지정할 수는 없을까요?
- Base URL을 사용하다가 타 Domain에 보내야 될 때는 어떻게 할 수 있을까요?
이 모든 요구사항들을 '잘 만든다는 것'은 어려워요. 따라서 이 모든걸 만들어진fetch모듈을 사용해보고 후에fetch모듈을 만들어 보는 것도 좋은 학습 방법이 될 수 있어요.
- Base URL을 사용하다가 타 Domain에 보내야 될 때는 어떻게 할 수 있을까요?
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.
어떻게 세팅하면 될까? 🤔
instance를 만들어서 export를 하고 사용해보는 것 정도로 시도해보면 좋을 것 같아요. axios-instance 파일을 만들어서 instance를 생성하고 export한 후 사용해보는건 어떨까요?
다음과 같이 만들어볼 수 있어요:
const baseURL = process.env.NEXT_PUBLIC_LINKBRARY_BaseURL;
const instance = axios.create({
baseURL: baseURL,
headers: {
'Content-Type': 'application/json',
},
});
export default instance인가에 필요한 accessToken을 localStorage가 있다면 axios의 인터셉터를 활용할 수 있습니다 !
인터셉터는 혼자 해결해보시는 것을 권장드립니다. 혹시 모르시겠으면 다음 위클리 미션에 질문해주세요. 😊
사용 방법 🚀
사용 방법은 정말 간단해요. 다음과 같이 사용할 수 있습니다:
instance.get(`/user/${userId}`)딱 보니. 마이그레이션도 정말 쉽게 할 수 있겠죠? 😊
| } | ||
| `; | ||
|
|
||
| function ItemCommentList({ comments }) { |
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.
(선택) CommentList라는 이름도 괜찮겠네요 !
CommentList와 CommentItem으로 하셔도 될 것 같고
Comments와 Comment로 하셔도 될 것 같은데 전자가 더 읽기 편해보이네요 ~!
| } | ||
| `; | ||
|
|
||
| function CommentListItem({ writer, updatedAt, content, onEdit, onDelete }) { |
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.
굿굿 ! 해당 컴포넌트는 프라이빗한 컴포넌트군요 !
파일 내부에서만 사용되므로 굳이 export 시키지 않고 있군요.
"내부 구성이 어떻게 되있는지 알 필요 없도록" 설계되어있는 좋은 패턴입니다 !
게다가 나중에 "댓글 중 가장 인기 많은 댓글을 상위에 하나 출력한다"라는 특수한 요구사항이 추가된다면,
해당 컴포넌트만 export 해주면 되겠어요. 낮은 결합도로 잘 구성하셨습니다 👍👍
또한, 별도의 파일로 분리하는게 가독성이 좋을까요?
이건 주관적인 영역이라 이견이 있을거로 보여요.
하지만 하나 확실한건 파일을 분리하게 되면 해당 컴포넌트를 결국 글로벌하게 export를 시켜줘야 할 것으로 보입니다.
또한, 코딩을 직접 해본 입장에서 두 파일을 교차하면서 보는게 더 편하신가요?
아니면 하나의 파일을 보면서 코딩하시는게 편하신가요?
이에 따라 또 다를 것 같습니다.
순전히 주관적인 입장에서. 위와 같은 이유로 저였다면 현재 상태 그대로 두는게 좋겠다고 사료되네요. 😉
| const handleMoreClick = () => { | ||
| setIsMenuOpen((prev) => !prev); | ||
| }; | ||
|
|
||
| const handleEditClick = () => { | ||
| setIsMenuOpen(false); | ||
| setIsEditing(true); | ||
| }; | ||
|
|
||
| const handleDeleteClick = () => { | ||
| setIsMenuOpen(false); | ||
| onDelete(); | ||
| }; | ||
|
|
||
| const handleCancelClick = () => { | ||
| setIsEditing(false); | ||
| }; | ||
|
|
||
| const handleEditDoneClick = () => { | ||
| setIsEditing(false); | ||
| onEdit(inputRef.current.value); | ||
| }; |
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.
크으 ~ 상황에 따른 핸들러들이 아주 깔끔하네요 👍
| const handleInputChange = () => { | ||
| // TODO: Comment 등록 구현 | ||
| }; |
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.
주석으로 메모를 남겨 놓는 것도 정말 좋은 메모습관입니다 !
필요한 메모를 필요한 곳 주변에 배치하는 것. 나중에 까먹지 않고 작업을 이어갈 수 있으므로 좋은 습관이죠 👍
|
수고하셨습니다 참솔님 ! 참솔님께서 기초 프로젝트에 어떤 기여를 하실지 정말 기대가 되는군요 ! 👍 |
요구사항
기본
상품 상세
=> favoriteCount : 하트 개수
=> images : 상품 이미지
=> tags : 상품태그
=> name : 상품 이름
=> description : 상품 설명
상품 문의 댓글
=> image : 작성자 이미지
=> nickname : 작성자 닉네임
=> content : 작성자가 남긴 문구
=> description : 상품 설명
=> updatedAt : 문의글 마지막 업데이트 시간
심화
주요 변경사항
comments-mock.jsonmock 데이터를 사용해서 comment 목록을 보여주고 comment 등록, 수정, 삭제 등 기능은 comments를 가져오는 API를 제대로 활용할 수 있게 되면 추가로 구현하려고 합니다.스크린샷
멘토에게
item-comment-list.jsx에는ItemCommentList와CommentListItem두 개의 component가 구현되어 있습니다.CommentListItemcomponent는ItemCommentListcomponent 내부에서만 사용될 거라 생각해서 따로 파일로 분리하지 않았는데요. 재사용 되지 않더라도 코드 가독성과 유지보수를 위해 별도의 파일로 분리하는게 좋을까요?