-
Notifications
You must be signed in to change notification settings - Fork 79
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
[최윤석] 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 값을 입력하면 ‘등록' 버튼이 활성화 됩니다.
심화
주요 변경사항
스크린샷
멘토에게