Skip to content

Conversation

@BaeZzi813
Copy link
Collaborator

요구사항

  • Github에 PR(Pull Request)을 만들어서 미션을 제출합니다.
  • 피그마 디자인에 맞게 페이지를 만들어 주세요.
  • 기존의 React, Typescript로 구현한 프로젝트와 별도로 진행합니다.
  • Next.js를 사용합니다

기본

  • ‘로고’ 버튼을 클릭하면 ‘/’ 페이지로 이동합니다. (새로고침)
  • 진행 중인 할 일과 완료된 할 일을 나누어 볼 수 있습니다.
  • 상단 입력창에 할 일 텍스트를 입력하고 추가하기 버튼을 클릭하거나 엔터를 치면 할 일을 새로 생성합니다.
  • 진행 중 할 일 항목의 왼쪽 버튼을 클릭하면 체크 표시가 되면서 완료 상태가 됩니다.
  • 완료된 할 일 항목의 왼쪽 버튼을 다시 클릭하면 체크 표시가 사라지면서 진행 중 상태가 됩니다.

스크린샷

localhost_3000_ localhost_3000_ (1) localhost_3000_ (2)

멘토에게

  • 셀프 코드 리뷰를 통해 질문 이어가겠습니다.

@BaeZzi813 BaeZzi813 requested a review from kiJu2 November 3, 2025 16:11
@BaeZzi813 BaeZzi813 added the 매운맛🔥 뒤는 없습니다. 그냥 필터 없이 말해주세요. 책임은 제가 집니다. label Nov 3, 2025
@BaeZzi813 BaeZzi813 self-assigned this Nov 3, 2025
@kiJu2
Copy link
Collaborator

kiJu2 commented Nov 6, 2025

스프리트 미션 하시느라 수고 많으셨어요.
학습에 도움 되실 수 있게 꼼꼼히 리뷰 하도록 해보겠습니다. 😊

@@ -0,0 +1,56 @@
const BASE_URL = `${process.env.NEXT_PUBLIC_BASE_URL}/items`;
Copy link
Collaborator

Choose a reason for hiding this comment

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

items를 지워도 될 것 같아요 !

현재 itmes 외에도 이미지 업로드에 필요한 images API도 있는 것으로 보이는군요 😉

Suggested change
const BASE_URL = `${process.env.NEXT_PUBLIC_BASE_URL}/items`;
const BASE_URL = `${process.env.NEXT_PUBLIC_BASE_URL}`;

위와 같이 사용해볼 수 있습니다 😆

Copy link
Collaborator

Choose a reason for hiding this comment

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

해당 파일은 index.tsx보다 index.ts 확장자가 더 낫겠군요 !

해당 파일은 jsx 문법을 사용하고 있지 않기에 .ts로 변경하셔도 무방합니다 😉
그럼 확장자만 봐도 JSX 파일인지 아닌지 구분할 수 있겠죠? 😆

Comment on lines +36 to +56
export async function patchData(
itemId: number,
updateData: Partial<{ name: string; isCompleted: boolean }>
) {
try {
const response = await fetch(`${BASE_URL}/${itemId}`, {
method: "PATCH",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(updateData),
});
if (!response.ok) {
throw new Error("서버 요청 실패" + response.status);
}
const data = response.json();
return data;
} catch (error) {
console.log("에러 발생", error);
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Swagger 문서를 보면 다음과 같이 스키마의 타입을 알 수 있습니다:

image

해당 타입으로 다음과 같은 타입을 정의해볼 수 있겠군요 !

interface Item {
  id: number;
  tenantId: string
  name: string;
  memo: string;
  imageUrl: string;
  isCompleted?: boolean;
}

그리고 요청 타입과 응답 타입을 정의해볼 수 있습니다 !

interface PatchTodoRequest extends Partial<Omit<TodoItem, 'id'>> {}

interface PatchTodoResponse extends TodoItem {}

Copy link
Collaborator

Choose a reason for hiding this comment

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

(이어서) 그리고 API 함수에 다음에 요청과 응답 타입을 정의해볼 수 있습니다 😉

Suggested change
export async function patchData(
itemId: number,
updateData: Partial<{ name: string; isCompleted: boolean }>
) {
try {
const response = await fetch(`${BASE_URL}/${itemId}`, {
method: "PATCH",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(updateData),
});
if (!response.ok) {
throw new Error("서버 요청 실패" + response.status);
}
const data = response.json();
return data;
} catch (error) {
console.log("에러 발생", error);
}
}
export async function patchData(
itemId: number,
updateData: PatchTodoRequest
): Promise<ApiResponse<PatchTodoResponse>> {
try {
const response = await fetch(`${BASE_URL}/${itemId}`, {
method: "PATCH",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(updateData),
});
if (!response.ok) {
throw new Error(`서버 요청 실패: ${response.status}`);
}
const data: PatchTodoResponse = await response.json();
return { data };
} catch (error) {
console.log("에러 발생", error);
return {
error: error instanceof Error ? error.message : "알 수 없는 오류가 발생했습니다"
};
}
}

이렇게 정의해두면 요청 타입과 응답 타입이 필요할 때 export 해볼 수 있으며 타입에 별칭이 있어 가독성도 향상될 수 있겠군요 !

Comment on lines +1 to +13
import plusIc from "@/assets/plus.svg";
import Image from "next/image";

export const Button = ({ handleSubmit }) => {
return (
<button
onClick={handleSubmit}
className="w-[168px] border-2 border-slate-900 bg-slate-100 rounded-3xl px-5 py-2 shadow-md shadow-slate-800 items-center font-bold flex justify-center gap-2 text-slate-900"
>
<Image src={plusIc} width={16} height={16} alt="추가 아이콘" /> 추가하기
</button>
);
};
Copy link
Collaborator

Choose a reason for hiding this comment

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

(응용 / 더 나아가기) 해당 컴포넌트는 자칫 공통컴포넌트로 오해할 수 있겠군요.

src/components/ui/button.tsx 이름과 경로를 보더라도 해당 앱 어디서나 사용될 수 있는 공통컴포넌트로 오해될 수 있을 것 같아요.

두 가지 방법을 제안드리고 싶습니다 !

공통 컴포넌트를 진짜 만들어버리기

// components/common/Button.tsx
import { ButtonHTMLAttributes } from "react";
import clsx from "clsx";

interface CommonButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
  variant?: "primary" | "secondary" | "ghost"; // 예시로 작성했습니다 😉
}

export function Button({
  children,
  variant = "primary",
  className,
  ...props
}: CommonButtonProps) {
  const baseStyle =
    "rounded-3xl font-bold flex items-center justify-center gap-2 transition-colors duration-150";

  const variants = {
    primary:
      "bg-slate-100 text-slate-900 border-2 border-slate-900 shadow-md shadow-slate-800 hover:bg-slate-200",
    secondary:
      "bg-white text-slate-800 border border-slate-400 hover:bg-slate-100", // 예시로 작성했습니다 😉
    ghost: "text-slate-600 hover:text-slate-800 hover:bg-slate-100", // 예시로 작성했습니다 😉
  };

  return (
    <button
      {...props}
      className={clsx(baseStyle, variants[variant], className)}
    >
      {children}
    </button>
  );
}

(이어서)

Copy link
Collaborator

Choose a reason for hiding this comment

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

두번째 방법: 컴포넌트 이름 바꾸기

정말 간단합니다 😉 그냥 이름만 바꿔줘도 될 것 같아요 !

Suggested change
import plusIc from "@/assets/plus.svg";
import Image from "next/image";
export const Button = ({ handleSubmit }) => {
return (
<button
onClick={handleSubmit}
className="w-[168px] border-2 border-slate-900 bg-slate-100 rounded-3xl px-5 py-2 shadow-md shadow-slate-800 items-center font-bold flex justify-center gap-2 text-slate-900"
>
<Image src={plusIc} width={16} height={16} alt="추가 아이콘" /> 추가하기
</button>
);
};
import plusIc from "@/assets/plus.svg";
import Image from "next/image";
export const AddItemButton = ({ handleSubmit }) => {
return (
<button
onClick={handleSubmit}
className="w-[168px] border-2 border-slate-900 bg-slate-100 rounded-3xl px-5 py-2 shadow-md shadow-slate-800 items-center font-bold flex justify-center gap-2 text-slate-900"
>
<Image src={plusIc} width={16} height={16} alt="추가 아이콘" /> 추가하기
</button>
);
};

해당 컴포넌트는 특수 목적이 있는 버튼임을 명시하는 방법이 있습니다 !

onChange={handleChange}
handleKeyDown={handleKeyDown}
/>
<Button handleSubmit={handleSubmit} />
Copy link
Collaborator

Choose a reason for hiding this comment

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

(이어서) 해당 코드를 다음과 같이 변경합니다:

Suggested change
<Button handleSubmit={handleSubmit} />
<Button onClick={onClick} />

React.ButtonHTMLAttributes<HTMLButtonElement>에 이미 onClick 속성이 존재하므로 따로 props에 명시할 필요 없이 사용할 수 있습니다 😉

Comment on lines +9 to +31
extend: {
colors: {
slate: {
100: "#F1F5F9",
200: "#E2E8F0",
300: "#CBD5E1",
400: "#94A3B8",
500: "#64748B",
800: "#1E293B",
900: "#0F172A",
},

"accent-violet-600": "#7C3AED",
"accent-violet-100": "#DED9FE",
"accent-rose": "#4F3F5E",
"accent-lime": "#BEF264",
"accent-amber": "#92400E",
},
fontFamily: {
sans: ["NanumSquare", "system-ui", "sans-serif"],
},
},
},
Copy link
Collaborator

Choose a reason for hiding this comment

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

크으 ~ 테며드셨군요 👍

테일윈드 참 편리하고 좋지요 👍 테일윈드 설정을 통해서 팔레트를 정의하셨군요. 훌륭합니다 👍

@kiJu2
Copy link
Collaborator

kiJu2 commented Nov 6, 2025

수고하셨습니다 재영님 !
언제나 정말 열심히 하시는군요 🥺 팀프로젝트 끝나도 여전히 개인 미션 제출하시는 모습 보기 좋습니다 👍

이번 심화 프로젝트에서도 재영님의 역량이 팀에 큰 힘이 될 것이라 확신합니다 😉

미션 수행하시느라 수고 많으셨습니다 !

@kiJu2 kiJu2 merged commit 20c38e2 into codeit-bootcamp-frontend:Next-양재영 Nov 6, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

매운맛🔥 뒤는 없습니다. 그냥 필터 없이 말해주세요. 책임은 제가 집니다.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants