-
Notifications
You must be signed in to change notification settings - Fork 78
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
[최윤석] Sprint10 #732
The head ref may contain hidden characters: "Next.js-\uCD5C\uC724\uC11D-sprint10"
[최윤석] Sprint10 #732
Conversation
…ithub-actions [Fix] delete merged branch github action
수고 하셨습니다 ! 스프리트 미션 하시느라 정말 수고 많으셨어요. |
미완성입니다. addboard 페이지 스타일링 밖에 못했습니다괜찮습니다 ! 완성보다 중요한 것은 꾸준함이지요 =) 다음에 기능 추가해서 올리겠습니다넵넵 ~! |
<Image src={medal} width={16} height={16} alt='메달 아이콘' /> | ||
<span>Best</span> |
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.
단순 장식용으로 있을 경우 ""
로 나타낼 수 있습니다 !
<Image src={medal} width={16} height={16} alt='메달 아이콘' /> | |
<span>Best</span> | |
<Image src={medal} width={16} height={16} alt="" /> | |
<span>Best</span> |
장식 이미지는 페이지 콘텐츠에 정보를 추가하지 않습니다. 예를 들어, 이미지에서 제공하는 정보는 인접한 텍스트를 사용하여 이미 제공될 수도 있고, 웹 사이트를 시각적으로 더욱 매력적으로 만들기 위해 이미지가 포함될 수도 있습니다.
이러한 경우 스크린 리더와 같은 보조 기술에서 무시할 수 있도록 null(빈) alt텍스트를 제공해야 합니다( ). alt=""이러한 유형의 이미지에 대한 텍스트 값은 화면 판독기 출력에 청각적 혼란을 추가하거나 주제가 인접한 텍스트의 주제와 다른 경우 사용자의 주의를 산만하게 할 수 있습니다. 속성 을 생략하는 alt것도 옵션이 아닙니다. 속성이 제공되지 않으면 일부 화면 판독기가 이미지의 파일 이름을 대신 알려주기 때문입니다.
export default function BestPostArticles({ | ||
title, | ||
image, | ||
likeCount, | ||
updatedAt, | ||
writer, | ||
}) { |
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.
굳굳 UI 컴포넌트군요 ?
따로 components/ui
로 파일을 분류해도 되겠어요. 😊
const instance = axios.create({ | ||
baseURL: "https://panda-market-api.vercel.app", | ||
}); |
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
를 따로 생성해두셨군요 !
import axios from "axios"; | ||
|
||
const instance = axios.create({ | ||
baseURL: "https://panda-market-api.vercel.app", |
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은 환경 변수에 저장하시는게 좋습니다!
환경 변수(Environment Variable):
process.env
에 내장되며 앱이 실행될 때 적용할 수 있는 값입니다!
다음과 같이 적용할 수 있습니다:
// .env.development
REACT_APP_BASE_URL="http://localhost:3000"
// .env.production
REACT_APP_BASE_URL="http://myapi.com"
// 사용시
<a href={`${process.env.REACT_APP_BASE_URL}/myroute`}>URL</a>
왜 환경 변수에 저장해야 하나요?
개발(dev
), 테스트(test
), 실제 사용(prod
) 등 다양한 환경에서 앱을 운영하게 되는 경우, 각 환경에 따라 다른 base URL을 사용해야 할 수 있습니다. 만약 코드 내에 하드코딩되어 있다면, 각 환경에 맞춰 앱을 배포할 때마다 코드를 변경해야 하며, 이는 매우 번거로운 작업이 됩니다. 하지만, 환경 변수를 .env.production
, .env.development
, .env.test
와 같이 설정해두었다면, 코드에서는 단지 다음과 같이 적용하기만 하면 됩니다.
const apiUrl = `${process.env.REACT_APP_BASE_URL}/api`;
이러한 방식으로 환경 변수를 사용하면, 배포 환경에 따라 쉽게 URL을 변경할 수 있으며, 코드의 가독성과 유지보수성도 개선됩니다.
실제 코드 응용과 관련해서는 다음 한글 아티클을 참고해보세요! => 보러가기
useEffect(() => { | ||
async function getBestArticles() { | ||
try { | ||
const res = await axios.get("articles?page=1&pageSize=3&orderBy=like"); |
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.
보통 api
함수들은 따로 파일을 분류하여 사용하는게 일반적입니다 ! 😊
프로젝트에서는 API 호출 로직을 별도의 파일로 분리하여 관리하는 것이 일반적이예요. 이렇게 하면 컴포넌트, 페이지, 훅 등 어디든 사용될 수 있겠죠?😊
코드의 재사용성을 높이고, 유지보수가 쉬워질 수 있습니다 ! API 함수를 모듈화하여 사용하면 코드가 더 깔끔하고 읽기 쉬워집니다. 다음은 프로젝트의 디렉토리 구조와 API 함수 예제입니다:
// src/services/apis/best-articles.api.ts (예시입니다 !)
export const getBestArticles = async () => {
try {
const { data } = await axios.get('articles?page=1&pageSize=3&orderBy=like');
return data;
} catch(error) {
throw error;
}
};
export default function Button({ children }: ButtonProps) { | ||
return <button>{children}</button>; | ||
} |
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 default function Button({ children }: ButtonProps) { | |
return <button>{children}</button>; | |
} | |
export default function Button({ children, ...rest }: ButtonProps) { | |
return <button {...rest}>{children}</button>; | |
} |
그럼 다음과 같은 코드 작성을 할 수 있을거예요:
<Button type="button" onClick={() => {}}>버튼 !</Button>
type
, onClick
등의 타입들은 별도로 작성할 필요 없고 추 후 필요하더라도 수정할 필요 없으니 확장성에 유리할거예요 😊
interface InputProps { | ||
id: string; | ||
type: HTMLInputTypeAttribute; | ||
placeholder: string; | ||
className: string; | ||
} |
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.
해당 컴포넌트를 포함한 기본 컴포넌트들은 위 버튼처럼 타입을 지정해볼 수 있습니다 =)
{!image ?? ( | ||
<Image src={image} width={72} height={72} alt='상품 이미지' /> | ||
)} |
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.
오잉?
이렇게 하면 image
가 falsy
일 때 true
가 되어 ??
연산이 안되지 않을까요?
Not(!
)을 사용하면 undefined
혹은 null
일 때의 상황은 없을 것 같아요.
falsy란?: 거짓 같은 값이예요.
null
,undefined
,false
,NaN
,0
,-0
,0n
,""
과 같은 값들을 의미해요.
async function getArticles() { | ||
try { | ||
const res = await axios.get("/articles?page=1&pageSize=6&orderBy=recent"); | ||
const nextArticles = res.data.list ?? []; | ||
setArticles(nextArticles); | ||
} catch (error) { | ||
console.error(error); | ||
} | ||
} | ||
|
||
useEffect(() => { | ||
getArticles(); | ||
}, []); |
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.
다음과 같이 useEffect
내에 선언할 수 있어요:
async function getArticles() { | |
try { | |
const res = await axios.get("/articles?page=1&pageSize=6&orderBy=recent"); | |
const nextArticles = res.data.list ?? []; | |
setArticles(nextArticles); | |
} catch (error) { | |
console.error(error); | |
} | |
} | |
useEffect(() => { | |
getArticles(); | |
}, []); | |
useEffect(() => { | |
async function getArticles() { | |
try { | |
const res = await axios.get("/articles?page=1&pageSize=6&orderBy=recent"); | |
const nextArticles = res.data.list ?? []; | |
setArticles(nextArticles); | |
} catch (error) { | |
console.error(error); | |
} | |
} | |
getArticles(); | |
}, [setArticles]); |
만약 useEffect
바깥에 선언하게 되면 리렌더링에 의한 불필요한 재선언이 될 수 있습니다 😊
|
||
export default function Document() { | ||
return ( | ||
<Html lang="en"> | ||
<Html lang='ko'> |
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.
굳굳~! 꼼꼼하시네욤 ㅎㅎㅎ
굳굳 ~! 스프린스 미션 하시느라 수고 정말 많으셨어요 윤석님. |
요구사항
기본
게시글 등록 페이지 주소는 “/addboard” 입니다.
게시판 이미지는 최대 한개 업로드가 가능합니다.
각 input의 placeholder 값을 정확히 입력해주세요.
이미지를 제외하고 input 에 모든 값을 입력하면 ‘등록' 버튼이 활성화 됩니다
게시글 상세 페이지 주소는 “/board/{id}” 입니다.
댓글 input 값을 입력하면 ‘등록' 버튼이 활성화 됩니다.
자유게시판 페이지에서 게시글을 누르면 게시물 상세 페이지로 이동합니다.
게시글 상세 페이지 주소는 “/board/{id}” 입니다.
댓글 input 값을 입력하면 ‘등록' 버튼이 활성화 됩니다.
심화
주요 변경사항
스크린샷
멘토에게