Skip to content

Conversation

@Moon-ju-young
Copy link
Collaborator

요구사항

기본

  • 네모 박스 안의 화면을 TypeScript로 마이그레이션해 주세요.

심화

  • any타입을 최소한으로 써주세요

주요 변경사항

  • util 함수를 제외하고 모두 typescript로 마이그레이션 하였습니다.

스크린샷

멘토에게

  • 기존 React 프로젝트에서 Typescript를 추가로 적용한 것이라 설정 파일이 맞는 건지 잘 모르겠습니다.
  • 타입 단언은 최대한 사용을 자제하는 편이 좋을까요?
  • 셀프 코드 리뷰를 통해 질문 이어가겠습니다.

MacOS & Chrome 환경에서 keydown이 두 번 실행되는 버그 해결
api 함수 반환 데이터 type 정의
Copy link
Collaborator

@GANGYIKIM GANGYIKIM left a comment

Choose a reason for hiding this comment

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

주영님 8주차 미션 수고하셨습니다!
저희 기수에서 처음으로 해당 주차를 작업하셨네요. 타입 스크립트에 익숙하지 않으실텐데 굉장히 잘 작성하셨습니다.
제가 코멘트에도 남겨드렸지만 타입 단언은 에러가 날 수 있기 때문에 꼭 필요한 경우에 사용하시는 것이 좋습니다.
다만 지금은 학습을 하시는 중이시라 어려우실 수 있으니, as 사용을 최대한 자제하되 필요하시면 사용하시고 점차 줄여나가시는 것을 추천드려요!

Comment on lines +5 to +14
export async function getProducts({ page=1, pageSize=10, orderBy="recent", keyword='' }: {
page?: number;
pageSize?: number;
orderBy?: "recent" | "favorite";
keyword?: string;
}): Promise<Products> {
const response = await fetch(`${BASE_URL}/products?page=${page}&pageSize=${pageSize}&orderBy=${orderBy}${keyword && "&keyword="+keyword}`);
if (!response.ok) { throw Error("Request Error"); }
return (await response.json()) as Products;
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

💬 여담
keyword는 옵셔널 파람인데 기본값으로 빈 문자열을 줄 필요가 있을까요?
위의 코드도 동작상 문제가 없을 것 같지만 가독성 측면에서 기본값이 없는것이 더 좋을 것 같아요~

Suggested change
export async function getProducts({ page=1, pageSize=10, orderBy="recent", keyword='' }: {
page?: number;
pageSize?: number;
orderBy?: "recent" | "favorite";
keyword?: string;
}): Promise<Products> {
const response = await fetch(`${BASE_URL}/products?page=${page}&pageSize=${pageSize}&orderBy=${orderBy}${keyword && "&keyword="+keyword}`);
if (!response.ok) { throw Error("Request Error"); }
return (await response.json()) as Products;
}
export async function getProducts({ page=1, pageSize=10, orderBy="recent", keyword }: {
page?: number;
pageSize?: number;
orderBy?: "recent" | "favorite";
keyword?: string;
}): Promise<Products> {
const response = await fetch(`${BASE_URL}/products?page=${page}&pageSize=${pageSize}&orderBy=${orderBy}${keyword && "&keyword="+keyword}`);
if (!response.ok) { throw Error("Request Error"); }
return (await response.json()) as Products;
}

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

기본값을 주지 않으면 query parameter에 그대로 undefined가 뒤에 붙어서 기본값을 줬습니다~

Comment on lines +5 to +14
export async function getProducts({ page=1, pageSize=10, orderBy="recent", keyword='' }: {
page?: number;
pageSize?: number;
orderBy?: "recent" | "favorite";
keyword?: string;
}): Promise<Products> {
const response = await fetch(`${BASE_URL}/products?page=${page}&pageSize=${pageSize}&orderBy=${orderBy}${keyword && "&keyword="+keyword}`);
if (!response.ok) { throw Error("Request Error"); }
return (await response.json()) as Products;
}
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
export async function getProducts({ page=1, pageSize=10, orderBy="recent", keyword='' }: {
page?: number;
pageSize?: number;
orderBy?: "recent" | "favorite";
keyword?: string;
}): Promise<Products> {
const response = await fetch(`${BASE_URL}/products?page=${page}&pageSize=${pageSize}&orderBy=${orderBy}${keyword && "&keyword="+keyword}`);
if (!response.ok) { throw Error("Request Error"); }
return (await response.json()) as Products;
}
export async function getProducts({ page=1, pageSize=10, orderBy="recent", keyword='' }: {
page?: number;
pageSize?: number;
orderBy?: "recent" | "favorite";
keyword?: string;
}): Promise<Products> {
const response = await fetch(`${BASE_URL}/products?page=${page}&pageSize=${pageSize}&orderBy=${orderBy}${keyword && "&keyword="+keyword}`);
if (!response.ok) {
const errorText = await response.text(); // 서버에서 온 응답을 활용
throw new Error(`Failed to fetch products: ${response.status} - ${errorText}`);
}
const data: Products = await response.json();
return data;
}

list: Product[];
}

export interface Comment {
Copy link
Collaborator

Choose a reason for hiding this comment

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

💊 제안
추후 Component 정의시 중복될 수 있고 이름을 통해 타입을 추론할 수 있도록 Comment보다 CommentProps 같은 이름이 좋습니다.

Comment on lines +21 to +25
writer: {
image: string;
nickname: string;
id: number;
};
Copy link
Collaborator

Choose a reason for hiding this comment

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

💬 여담
Comment의 writer 타입은 추후 재사용될 가능성이 높기 때문에, 분리해두시는 것도 좋을 것 같아요.

export interface CommentWriterProps {
    image: string;
    nickname: string;
    id: number;
}

export interface CommentProps {
    writer: WriterProps;
}

위에처럼 분리하시면 아래처럼 작성이 가능합니다.

// 분리된 타입을 사용하는 경우
export const CommentWriter = ({ image, nickname, id }: CommentWriterProps) => { ... }
// 또는 유틸리티 타입을 사용하는 방법도 있습니다
export const CommentWriter = ({ image, nickname, id }: Pick<Comment, 'writer'>) => { ... }

Comment on lines +15 to +18
export interface Products {
totalCount: number;
list: Product[];
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

👍 칭찬
Product와 Products를 분리해서 타입 선언하신 것 좋습니다!

Comment on lines +12 to +13
state: any;
setState: Dispatch<SetStateAction<any>>;
Copy link
Collaborator

Choose a reason for hiding this comment

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

💊 제안
구조를 좀 많이 바꿔야겠지만, any를 사용하지 않는 방법으로 작성하시는 것을 추천드려요!
이렇게 하셔야 Dropdown의 type이 명확해집니다.

어떻게 구현해야하는지 간략히 보여드리자면 아래와 같습니다.

// 사용처
export type SortState = "recent" | "favorite";
const options: {
  [K in SortState]: string;
} = {
  recent: "최신순",
  favorite: "좋아요순",
};

function Items() {
  const [orderBy, setOrderBy] = useState<SortState>("recent");
  ...
  return  <Dropdown<T> mode={mode} state={orderBy} setState={setOrderBy} options={options}/>
}

// DropdownSort.tsx
export type SortOption<T extends string> = {
  [key in T]: string;
};

interface Props<T extends string>
  extends ButtonHTMLAttributes<HTMLButtonElement> {
  options: SortOption<T>;
  state: T;
  setState: Dispatch<SetStateAction<T>>;
  mode: string;
}

function Dropdown<T extends string>(props: Props<T>) {

이런식으로 추상화를 하시면 추후 options 에 다른 항목이 나타나야할때 재사용이 가능합니다.

@GANGYIKIM GANGYIKIM merged commit 4bc45a3 into codeit-bootcamp-frontend:React-문주영 Jun 5, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants