Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
87e3f15
chore: 머지 후 브랜치 삭제 github action 추가
withyj-codeit Sep 3, 2023
1472169
Initial commit from Create Next App
withyj-codeit Nov 23, 2023
671d4d5
reset
hanseulhee Oct 10, 2023
14baca2
fix: 머지 후 브랜치 삭제 github action 수정
hanseulhee Oct 10, 2023
8d163dd
env: workflows 폴더로 이동
hanseulhee Oct 10, 2023
cf2e3df
Merge branch 'Next.js-고한샘' of https://github.com/codeit-bootcamp-fron…
gohansaem1 May 30, 2024
398ac6c
fix: 불필요파일 제거, 필요아이콘 세팅
gohansaem1 May 30, 2024
a04698a
test css, 라우팅, 검색쿼리
gohansaem1 May 30, 2024
8f535bd
배포 브랜치 변경용 커밋
gohansaem1 May 30, 2024
921aec0
배포 중 오류 해결
gohansaem1 May 30, 2024
f56d696
배포 중 오류 해결 2
gohansaem1 May 30, 2024
2ee994e
배포 중 오류 해결 3
gohansaem1 May 30, 2024
d80a89a
모르겠다
gohansaem1 May 30, 2024
5d76a22
.
gohansaem1 May 30, 2024
6f12196
.
gohansaem1 May 31, 2024
6cdee27
..
gohansaem1 May 31, 2024
af57418
버셀, 빌드코드제거
gohansaem1 May 31, 2024
c0b2b55
dfd
gohansaem1 May 31, 2024
c5cd127
게시판 글 가져오기 api 확인
gohansaem1 May 31, 2024
069ebed
feat: 검색 기능 구현
gohansaem1 May 31, 2024
c59c0eb
feat: 드롭다운 버튼 기능구현
gohansaem1 May 31, 2024
365a414
feat: 공통 헤더 컴포넌트 추가
gohansaem1 May 31, 2024
b690f65
feat:설정페이지 테마기능 구현
gohansaem1 May 31, 2024
d3bb2bb
fix: header css 수정, 구글폰트 적용하기
gohansaem1 May 31, 2024
eb445b0
fix: 네트리파이 빌드오류
gohansaem1 May 31, 2024
b3d39fd
fix: 타입스크립트 변환
gohansaem1 Jun 3, 2024
fbb8fcf
feat/ 글쓰기 버튼 컴포넌트, addboard 링크 연결
gohansaem1 Jun 6, 2024
270af41
feat: 개별 게시글 페이지, 댓글 달기 기본레이아웃 구성 구현
gohansaem1 Jun 7, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions components/BlueButton.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@
display: flex;
align-items: center;
justify-content: center;
border: none;
}
8 changes: 6 additions & 2 deletions components/BlueButton.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import styles from "@/components/BlueButton.module.css";

export default function BlueButton({ children }) {
return <div className={styles.write}>{children}</div>;
export default function BlueButton({ type = "", children }) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

타입스크립트를 사용한다면 타입을 꼭 지정해주세요!

return (
<button type={type} className={styles.write}>
{children}
</button>
);
}
1 change: 1 addition & 0 deletions components/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export default function Header() {
alt="Home Logo"
width={153}
height={51}
priority
/>
</Link>
<Link href="/boards">
Expand Down
7 changes: 0 additions & 7 deletions pages/addboard.tsx

This file was deleted.

111 changes: 111 additions & 0 deletions pages/addboard/[id].tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import BlueButton from "@/components/BlueButton";
import axios from "@/lib/axios";
import Image from "next/image";
import { useRouter } from "next/router";
import { useEffect, useState } from "react";

export function UserProfile({ article = {} }) {
return (
<div>
<Image
src="/icon/profile-login-icon.png"
alt="user profile"
width={24}
height={24}
priority
/>
<span>{article.writer?.nickname}</span>
<span>{article.createdAt}</span>
<span>하트</span>
<span>{article.likeCount}</span>
</div>
);
}

export function ActicleItem({ article = {} }) {
return (
<div className="">
<h3>{article.title}</h3>
<UserProfile article={article} />
<p>{article.content}</p>
</div>
);
}

export function CommentForm() {
return (
<form>
<h4>댓글 달기</h4>
<label htmlFor="content"></label>
<textarea
id="content"
name="content"
placeholder="댓글을 입력해주세요."
/>
<BlueButton type="submit">등록</BlueButton>
</form>
);
}

export function CommentItem({ comments }) {
return (
<>
<p>{comments[0]?.content}</p>
<UserProfile />
<Image
src="/icon/kebab-icon.png"
alt="kebab"
width={24}
height={24}
priority
/>
</>
);
}

export function CommentList({ comments = [] }) {
return (
<>
<CommentItem comments={comments} />
</>
);
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

export function CommentList({ comments = [] }) {
  return (...

와 같이 선언과 반환 사이에 아무런 로직이 들어있지 않은 컴포넌트들은 한번쯤 고민해보실 필요가 있을것 같아요
이런 컴포넌트는 어떤 역할을 할까요?
단순히 코드 나누기 그 이상의 역할이 있을까요?
오히려 의미없이 많아진 컴포넌트들 덕분에 가독성이 떨어지진 않을까요?
등등에 대해 한번 고민해보시면 좋을것 같습니다.

컴포넌트를 나누는 이유에는 여러 이유가 있을 수 있습니다.
UI로 나눌수도 있고, 비즈니스 로직으로 나눌 수도 있지만 언제나 그 이유는 명확해야 합니다.
가령 UI를 기준으로 컴포넌트를 나눈다면 해당 컴포넌트는 UI를 처리하는 로직을 명확히 가지고 있어야합니다.
반대로 비즈니스 로직만을 위해 컴포넌트를 나누었다면 해당 컴포넌트도 어떠한 비즈니스 로직을 처리하겠다와 같은 명확한 이유를 가지고 있어야합니다

만약 스스로 생각했을때 명확한 이유가 딱히 보이지 않는다면,
해당 컴포넌트를 나누는 부분에 대해 한번더 생각해보는게 어떨까요? ㅎ

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

그리고 따로 만든 컴포넌트들은 왠만하면 다른 파일로 구분해주시는게 좋을것 같네요 최대한 pages내부의 파일에는 해당 파일에 대한
export default만 존재하는게 더 좋을것 같아요 ㅎ 그 이유는 해당 파일이 단순히 파일의 역할만 하는게 아니라 next js 내부의 동적 라우팅을 담당하기 때문이에요 ㅎ


export default function Article() {
const router = useRouter();
const { query } = router;
const id = query["id"];
const [article, setArtcle] = useState({});
const [comments, setComments] = useState([]);

async function getArticle(articleId) {
if (!articleId) return;
const res = await axios.get(`/articles/${articleId}`);
const nextArticle = res.data;
setArtcle(nextArticle);
}
async function getComments(articleId) {
if (!articleId) return;
const res = await axios.get(`/articles/${articleId}/comments?limit=5`);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const res = await axios.get(`/articles/${articleId}/comments`, params: {
  limit: 5
});

axios를 사용하신다면 위와같이 searchParam을 객체로 뺄 수 있어요 ㅎ

const nextComments = res.data.list;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

 const nextComments = res.data.list;

과 같이 axios결과값을 한번더 변수에 할당하는건 불필요해 보입니다 ㅎ

그리고 axios가 반환하는 data에는 비동기 함수의 결과값이 담기는데요,
서버 통신하는 비동기함수는 언제나 실패할 수 있다는 점을 염두해두어야합니다.
api가 만약 에러를 throw하면 res.data는 undefined가 되고 그러면 undefined.list 가 되어 런타임 에러가 발생할거에요
언제나 axios가 반환하는 data는 undefiend가 될 수 있다는 점을 꼭 주의해주세요!

setComments(nextComments);
}

useEffect(() => {
if (id) {
getArticle(id);
getComments(id);
}
}, [id]);
return (
<>
<ActicleItem article={article} />
<CommentForm />
{!comments ? (
<p>댓글이 없습니다.</p>
) : (
<CommentList comments={comments} />
)}
</>
);
}
34 changes: 34 additions & 0 deletions pages/addboard/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import BlueButton from "@/components/BlueButton";
import { useRouter } from "next/router";

export default function AddBoard() {
const router = useRouter();
const { articleId } = router.query;
console.log(articleId);

return (
<form>
<div>
<h2>게시글 쓰기</h2>
<BlueButton type="submit">등록</BlueButton>
</div>
<label htmlFor="title">*제목</label>
<input
type="text"
id="title"
name="title"
placeholder="제목을 입력해주세요"
/>
<label htmlFor="content">*내용</label>
<textarea id="content" name="content" placeholder="내용을 입력해주세요" />
<label htmlFor="imageUpload">이미지</label>
<input
type="file"
id="imageUpload"
name="imageUpload"
accept="image/*"
placeholder="이미지 등록"
/>
</form>
);
}
Binary file added public/icon/kebab-icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.