Skip to content

Commit 0dc5f30

Browse files
authored
Merge pull request #317 from Woolegend/Next-우재현-sprint10
[우재현] Sprint10
2 parents 4cf0f38 + c5c2007 commit 0dc5f30

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+1897
-137
lines changed

api/article.api.tsx

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ArticleList } from "@/types/Article.type";
1+
import { Article, ArticleList } from "@/types/Article.type";
22
import axios from "./axios";
33

44
interface GetArticleListParams {
@@ -27,5 +27,33 @@ async function getArticleList({
2727
return response.data;
2828
}
2929

30-
export { getArticleList };
30+
async function getArticle({ id }: { id: number }): Promise<Article> {
31+
const response = await axios.get(`/articles/${id}`);
32+
return response.data;
33+
}
34+
35+
interface PostArticle {
36+
image?: string | undefined;
37+
content: string;
38+
title: string;
39+
}
40+
41+
async function postArticle({
42+
image,
43+
content,
44+
title,
45+
}: PostArticle): Promise<Article> {
46+
const response = await axios({
47+
method: "post",
48+
url: "/articles",
49+
data: {
50+
content,
51+
title,
52+
image,
53+
},
54+
});
55+
return response.data;
56+
}
57+
58+
export { getArticleList, getArticle, postArticle };
3159
export type { GetArticleListParams, OrderBy };

api/auth.api.tsx

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import { User } from "@/types/User.type";
2+
import axios from "./axios";
3+
4+
interface SignUpParams {
5+
email: string;
6+
nickname: string;
7+
password: string;
8+
passwordConfirmation: string;
9+
}
10+
11+
interface SignInParams {
12+
email: string;
13+
password: string;
14+
}
15+
16+
interface AuthResponse {
17+
accessToken: string;
18+
refreshToken: string;
19+
user: User;
20+
}
21+
22+
async function postSignUp({
23+
email,
24+
nickname,
25+
password,
26+
passwordConfirmation,
27+
}: SignUpParams): Promise<AuthResponse> {
28+
const response = await axios({
29+
method: "post",
30+
url: "/auth/signUp",
31+
data: {
32+
email,
33+
nickname,
34+
password,
35+
passwordConfirmation,
36+
},
37+
});
38+
return response.data;
39+
}
40+
41+
async function postSignIn({
42+
email,
43+
password,
44+
}: SignInParams): Promise<AuthResponse> {
45+
const response = await axios({
46+
method: "post",
47+
url: "/auth/signIn",
48+
data: {
49+
email,
50+
password,
51+
},
52+
});
53+
return response.data;
54+
}
55+
56+
async function postRefresh(refreshToken: string) {
57+
const response = await axios({
58+
method: "post",
59+
url: "/auth/refresh-token",
60+
data: {
61+
refreshToken,
62+
},
63+
});
64+
return response.data;
65+
}
66+
67+
export { postSignUp, postSignIn, postRefresh };
68+
export type { SignUpParams, SignInParams, AuthResponse };

api/axios.js

Lines changed: 0 additions & 9 deletions
This file was deleted.

api/axios.tsx

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import axios from "axios";
2+
3+
const BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL;
4+
5+
const instance = axios.create({
6+
baseURL: BASE_URL,
7+
headers: {
8+
"Content-Type": "application/json",
9+
},
10+
});
11+
12+
let interceptorId: any;
13+
14+
export const setInstanceHeaders = (token?: string) => {
15+
const value = token ? `Bearer ${token}` : undefined;
16+
// 기존 인터셉터 제거
17+
if (interceptorId !== undefined) {
18+
instance.interceptors.request.eject(interceptorId);
19+
}
20+
21+
// 새로운 인터셉터 추가
22+
interceptorId = instance.interceptors.request.use((config) => {
23+
config.headers["Authorization"] = value;
24+
return config;
25+
});
26+
};
27+
export default instance;

api/comment.api.tsx

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import axios from "./axios";
2+
3+
interface GetCommentListByArticleId {
4+
articleId: number;
5+
limit: number;
6+
cursor?: number;
7+
}
8+
9+
async function getCommentListByArticleId({
10+
articleId,
11+
limit,
12+
cursor,
13+
}: GetCommentListByArticleId) {
14+
const response = await axios.get(`/articles/${articleId}/comments`, {
15+
params: {
16+
limit,
17+
cursor,
18+
},
19+
});
20+
return response.data;
21+
}
22+
23+
interface PostCommentByArticleId {
24+
articleId: number;
25+
content: string;
26+
}
27+
28+
async function postCommentByArticleId({
29+
articleId,
30+
content,
31+
}: PostCommentByArticleId) {
32+
const response = await axios({
33+
method: "post",
34+
url: `/articles/${articleId}/comments`,
35+
data: {
36+
content,
37+
},
38+
});
39+
return response.data;
40+
}
41+
42+
export { getCommentListByArticleId, postCommentByArticleId };
43+
export type { GetCommentListByArticleId, PostCommentByArticleId };

api/image.api.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import axios from "./axios";
2+
3+
async function postImage(file: File): Promise<string> {
4+
const response = await axios.post("/images/upload", {
5+
data: { image: file },
6+
});
7+
return response.data.url;
8+
}
9+
10+
export { postImage };

components/BestPostBoard.tsx

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,17 @@
11
import { Article, ArticleList } from "@/types/Article.type";
22
import styles from "./BestPostBoard.module.css";
33
import Image from "next/image";
4-
import formatDate from "../lib/formatDate";
4+
import formatDate from "@/lib/formatDate";
55
import { useDeviceType } from "@/contexts/DeviceTypeContext";
6-
import { useState } from "react";
6+
import ImageSafe from "./ImageSafe";
7+
import Link from "next/link";
78

89
const PAGE_SIZE = {
910
desktop: 3,
1011
tablet: 2,
1112
mobile: 1,
1213
};
1314

14-
const IMAGE_PLACEHOLDER = "/images/landscape-placeholder.svg";
15-
1615
export default function BestPostBoard({ articles }: { articles: ArticleList }) {
1716
const deviceType = useDeviceType();
1817

@@ -22,8 +21,8 @@ export default function BestPostBoard({ articles }: { articles: ArticleList }) {
2221
<h2 className={styles.BoardTitle}>베스트 게시글</h2>
2322
</header>
2423
<div className={styles.PostItemList}>
25-
{articles.list.map((article, i) => {
26-
if (i < PAGE_SIZE[deviceType]) {
24+
{articles.list.map((article, postIndex) => {
25+
if (postIndex < PAGE_SIZE[deviceType]) {
2726
return <PostItem key={article.id} article={article} />;
2827
}
2928
})}
@@ -33,12 +32,10 @@ export default function BestPostBoard({ articles }: { articles: ArticleList }) {
3332
}
3433

3534
function PostItem({ article }: { article: Article }) {
36-
const [imgSrc, setImgSrc] = useState(article.image || IMAGE_PLACEHOLDER);
37-
3835
const createdAt = formatDate(article.createdAt);
3936

4037
return (
41-
<div className={styles.Item}>
38+
<Link className={styles.Item} href={`/board/${article.id}`}>
4239
<div className={styles.badge}>
4340
<div className={styles.medal}>
4441
<Image fill src="/images/ic_medal.svg" alt="베스트" />
@@ -48,15 +45,7 @@ function PostItem({ article }: { article: Article }) {
4845
<div className={styles.main}>
4946
<h3 className={styles.title}>{article.title}</h3>
5047
<div className={styles.preview}>
51-
<Image
52-
fill
53-
src={article.image}
54-
alt={article.title}
55-
style={{
56-
objectFit: "cover",
57-
}}
58-
onError={() => setImgSrc(IMAGE_PLACEHOLDER)}
59-
/>
48+
<ImageSafe src={article.image} alt={article.title} />
6049
</div>
6150
</div>
6251
<div className={styles.util}>
@@ -69,6 +58,6 @@ function PostItem({ article }: { article: Article }) {
6958
</div>
7059
<span>{createdAt}</span>
7160
</div>
72-
</div>
61+
</Link>
7362
);
7463
}

components/Container.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { ReactNode } from "react";
2+
3+
export default function Container({ children }: { children: ReactNode }) {
4+
return <div className="container">{children}</div>;
5+
}

components/ImageSafe.tsx

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import getConfig from "next/config";
21
import Image from "next/image";
32
import { useState, useEffect } from "react";
43

@@ -13,16 +12,12 @@ export default function ImageSafe({ src, alt }: { src: string; alt: string }) {
1312
const res = await fetch(
1413
"/api/check-image?url=" + encodeURIComponent(src)
1514
);
16-
console.log(res);
1715
if (res.ok) {
1816
setImageSrc(src);
1917
} else {
20-
console.error("Image source not configured in next.config.js");
21-
console.log(res);
2218
setImageSrc(IMAGE_PLACEHOLDER);
2319
}
2420
} catch (error) {
25-
console.error("Error checking image configuration:", error);
2621
setImageSrc(IMAGE_PLACEHOLDER);
2722
}
2823
};

components/Navigation.module.css

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,10 @@
2222
gap: 10px;
2323
}
2424

25-
.logo .icon {
25+
.logoIcon {
26+
position: relative;
2627
width: 40px;
28+
height: 40px;
2729
}
2830

2931
.logo .text {
@@ -56,6 +58,11 @@
5658
.tabList .tab.current {
5759
color: var(--blue);
5860
}
61+
.profileIcon {
62+
position: relative;
63+
width: 40px;
64+
height: 40px;
65+
}
5966

6067
@media screen and (min-width: 1600px) {
6168
.Navigation {

0 commit comments

Comments
 (0)