-
Notifications
You must be signed in to change notification settings - Fork 6
Feat: AddLinkInput, FolderTag 하위 작업 merge함, 검색, 폴더선택, 링크추가 기능 작업중 #65
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
The head ref may contain hidden characters: "feature/Link-\uD398\uC774\uC9C0"
Changes from all commits
6f17240
bcebebc
49ab5df
536f59f
91de8a2
9af6e59
7c23e60
0924ff1
f129f67
4027ea8
dcef227
98d1f38
75fe98b
787d8e8
b13ed23
45ae2bd
38b81fd
f6f58b7
b1a0e41
bac1868
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| import Image from "next/image"; | ||
|
|
||
| const ActionButtons = () => ( | ||
| <div className="w-[192px] h-[18px] flex justify-between gap-[12px] text-gray400"> | ||
| {[ | ||
| { src: "/icons/share.svg", alt: "공유", text: "공유" }, | ||
| { src: "/icons/pen.svg", alt: "이름 변경", text: "이름 변경" }, | ||
| { src: "/icons/delete.svg", alt: "삭제", text: "삭제" }, | ||
| ].map(({ src, alt, text }) => ( | ||
| <button key={text} className=" flex items-center gap-[4px] text-sm "> | ||
| <Image width={18} height={18} src={src} alt={alt} /> | ||
| <span>{text}</span> | ||
| </button> | ||
| ))} | ||
| </div> | ||
| ); | ||
|
|
||
| export default ActionButtons; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,32 +1,41 @@ | ||
| import axiosInstance from "@/lib/api/axiosInstanceApi"; | ||
| import axios from "axios"; | ||
| import { NextApiRequest, NextApiResponse } from "next"; | ||
| import { parse } from "cookie"; | ||
| import axiosInstance from "@/lib/api/axiosInstanceApi"; | ||
|
|
||
| const handler = async (req: NextApiRequest, res: NextApiResponse) => { | ||
| const token = req.cookies.accessToken; | ||
| console.log("Token:", token); // 쿠키 확인 | ||
| const cookies = parse(req.headers.cookie || ""); | ||
| const accessToken = cookies.accessToken; | ||
|
|
||
| if (!token) { | ||
| return res.status(401).json({ error: "사용자 정보를 찾을 수 없습니다." }); | ||
| } | ||
| switch (req.method) { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. switch문을 사용하는게 코드가 더 정리된 느낌이네요 ! 👍 |
||
| case "GET": | ||
| // 유저의 모든 폴더 조회 | ||
| try { | ||
| const response = await axiosInstance.get("/folders", { | ||
| headers: { Authorization: `Bearer ${accessToken}` }, | ||
| }); | ||
| return res.status(201).json(response.data); | ||
| } catch (err) { | ||
| console.error(err); | ||
| return res | ||
| .status(500) | ||
| .json({ message: "모든 폴저 조회에 실패했습니다." }); | ||
| } | ||
|
|
||
| if (req.method === "POST") { | ||
| try { | ||
| await axiosInstance.post("/folders", req.body, { | ||
| headers: { | ||
| Authorization: `Bearer ${token}`, | ||
| }, | ||
| }); | ||
| return res.status(200).json({ message: "폴더 생성 성공" }); | ||
| } catch (error) { | ||
| if (axios.isAxiosError(error) && error.response) { | ||
| const status = error.response.status; | ||
| const message = error.response.data?.message || "알 수 없는 오류 발생"; | ||
| return res.status(status).json({ message }); | ||
| case "POST": | ||
| // 유저의 폴더 생성 | ||
| try { | ||
| const response = await axiosInstance.post("/folders", req.body, { | ||
| headers: { Authorization: `Bearer ${accessToken}` }, | ||
| }); | ||
| return res.status(201).json(response.data); | ||
| } catch (err) { | ||
| console.error(err); | ||
| return res.status(500).json({ message: "폴더 생성에 실패했습니다." }); | ||
| } | ||
| } | ||
| } else { | ||
| res.status(405).json({ message: "허용되지 않은 접근 방법" }); | ||
|
|
||
| default: | ||
| res.setHeader("Allow", ["GET", "POST"]); | ||
| return res.status(405).end(`메서드 ${req.method}는 허용되지 않습니다.`); | ||
| } | ||
| }; | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,32 +1,41 @@ | ||
| import axiosInstance from "@/lib/api/axiosInstanceApi"; | ||
| import axios, { isAxiosError } from "axios"; | ||
| import { NextApiRequest, NextApiResponse } from "next"; | ||
| import { parse } from "cookie"; | ||
| import axiosInstance from "@/lib/api/axiosInstanceApi"; | ||
|
|
||
| const handler = async (req: NextApiRequest, res: NextApiResponse) => { | ||
| const token = req.cookies.accessToken; | ||
| console.log("Token:", token); | ||
| const cookies = parse(req.headers.cookie || ""); | ||
| const accessToken = cookies.accessToken; | ||
|
|
||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 제거된 코드와 작성하신 코드에서 쿠키를 가져오는 방법의 차이가 뭔지 궁금합니다!
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 차이 없습니다! 병합하다가 삭제된 것 같아요 |
||
| if (!token) { | ||
| return res.status(401).json({ error: "사용자 정보를 찾을 수 없습니다." }); | ||
| } | ||
| switch (req.method) { | ||
| case "GET": | ||
| // 유저의 전체 링크 조회 | ||
| try { | ||
| const response = await axiosInstance.get("/links", { | ||
| headers: { Authorization: `Bearer ${accessToken}` }, | ||
| }); | ||
| return res.status(201).json(response.data); | ||
| } catch (err) { | ||
| console.error(err); | ||
| return res | ||
| .status(500) | ||
| .json({ message: "전체 링크 조회에 실패했습니다." }); | ||
| } | ||
|
|
||
| if (req.method === "POST") { | ||
| try { | ||
| await axiosInstance.post("/links", req.body, { | ||
| headers: { | ||
| Authorization: `Bearer ${token}`, | ||
| }, | ||
| }); | ||
| return res.status(201).json({ message: "링크 추가 성공" }); | ||
| } catch (error) { | ||
| if (isAxiosError(error) && error.response) { | ||
| const status = error.response.status; | ||
| const message = error.response.data?.message || "알 수 없는 오류 발생"; | ||
| return res.status(status).json({ message }); | ||
| case "POST": | ||
| // 링크 생성 로직 | ||
| try { | ||
| const response = await axiosInstance.post("/links", req.body, { | ||
| headers: { Authorization: `Bearer ${accessToken}` }, | ||
| }); | ||
| return res.status(201).json(response.data); | ||
| } catch (err) { | ||
| console.error(err); | ||
| return res.status(500).json({ message: "링크 생성에 실패했습니다." }); | ||
| } | ||
| } | ||
| } else { | ||
| res.status(405).json({ message: "허용되지 않은 접근 방법" }); | ||
|
|
||
| default: | ||
| res.setHeader("Allow", ["GET", "POST"]); | ||
| return res.status(405).end(`메서드 ${req.method}는 허용되지 않습니다.`); | ||
| } | ||
| }; | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| import { GetServerSidePropsContext } from "next"; | ||
| import { proxy } from "@/lib/api/axiosInstanceApi"; | ||
| import { LinkData } from "@/types/linkTypes"; | ||
| import { FolderData } from "@/types/folderTypes"; | ||
| import { SearchInput } from "../../components/Search/SearchInput"; | ||
| import Container from "@/components/Layout/Container"; | ||
| import CardsLayout from "@/components/Layout/CardsLayout"; | ||
| import ActionButtons from "@/components/Link/ActionButtons"; | ||
| import AddLinkInput from "@/components/Link/AddLinkInput"; | ||
| import FolderTag from "../../components/FolderTag"; | ||
| import LinkCard from "../../components/LinkCard"; | ||
|
|
||
| interface LinkPageProps { | ||
| linkList: LinkData[]; | ||
| folderList: FolderData[]; | ||
| } | ||
|
|
||
| export const getServerSideProps = async ( | ||
| context: GetServerSidePropsContext | ||
| ) => { | ||
| const { req } = context; | ||
|
|
||
| const fetchData = async (endpoint: string) => { | ||
| const response = await proxy.get(endpoint, { | ||
| headers: { | ||
| Cookie: req.headers.cookie, | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 아 여기서 헤더가 다르기때문에 쿠키를 불러오는 방식이 달라진거군요!
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 클라이언트 -> 서버로 요청을 보낼 때는 헤더에 쿠키가 자동으로 실어져 갑니다! 여기는 getServerSideProps 함수이니 서버 -> 서버로 요청을 보내는 것이라 헤더에 쿠키가 없습니다. 대책으로 context.req 객체에 접근해 cookie를 받아와 헤더에 직접적으로 실어주는 것이고요 (�get 요청 함수는 인자가 순서대로 url,body,header 입니다) 이렇게 하면 서버-> 서버라 할지라도 요청받는 서버에서 헤더에 쿠키가 실어져있으니 쿠키를 읽어 그 안에 token을 꺼내 요청을 보낼 수 있는 원리입니다. |
||
| }, | ||
| }); | ||
| return response.data; | ||
| }; | ||
|
|
||
| const [links, folders] = await Promise.all([ | ||
| fetchData("/api/links"), | ||
| fetchData("/api/folders"), | ||
| ]); | ||
|
|
||
| return { | ||
| props: { | ||
| linkList: links.list || [], | ||
| folderList: folders || [], | ||
| }, | ||
| }; | ||
| }; | ||
|
|
||
| const LinkPage = ({ linkList, folderList }: LinkPageProps) => { | ||
| return ( | ||
| <> | ||
| <div className="bg-gray100 w-full h-[219px] flex justify-center items-center"> | ||
| <AddLinkInput folderList={folderList} /> | ||
| </div> | ||
| <main className="mt-[40px]"> | ||
| <Container> | ||
| <SearchInput /> | ||
| <div className="flex justify-between mt-[40px]"> | ||
| {folderList && <FolderTag folderList={folderList} />} | ||
| <button className="w-[79px] h-[19px] text-purple100"> | ||
| 폴더 추가 + | ||
| </button> | ||
| </div> | ||
| <div className="flex justify-between items-center mt-[24px]"> | ||
| <h1 className="text-2xl ">유용한 글</h1> | ||
| <ActionButtons /> | ||
| </div> | ||
| <CardsLayout> | ||
| {linkList.map((link) => ( | ||
| <LinkCard key={link.id} info={link} /> | ||
| ))} | ||
| </CardsLayout> | ||
| </Container> | ||
| </main> | ||
| </> | ||
| ); | ||
| }; | ||
|
|
||
| export default LinkPage; | ||
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.
클릭 가능한 요소임을 안내하기 위해 cursor-pointer 도 추가하면 좋을것같아요 !