Skip to content

Conversation

@soohwanpak
Copy link
Collaborator

@soohwanpak soohwanpak commented Feb 9, 2025

요구사항

기본

  • Github에 위클리 미션 PR을 만들어 주세요.
  • React 및 Express를 사용해 진행합니다.
  • TypeScript를 활용해 프로젝트의 필요한 곳에 타입을 명시해 주세요.
  • any 타입의 사용은 최소화해 주세요.
  • 복잡한 객체 구조나 배열 구조를 가진 변수에 인터페이스 또는 타입 별칭을 사용하세요.
  • Union, Intersection, Generics 등 고급 타입을 적극적으로 사용해 주세요.
  • 타입 별칭 또는 유틸리티 타입을 사용해 타입 복잡성을 줄여주세요.
  • 타입스크립트 컴파일러가 에러 없이 정상적으로 작동해야 합니다.

프론트엔드

  • 기존 React(혹은 Next) 프로젝트를 타입스크립트 프로젝트로 마이그레이션 해주세요.
  • TypeScript를 활용해 프로젝트의 필요한 곳에 타입을 명시해 주세요.

멘토에게

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

@soohwanpak soohwanpak added 순한맛🐑 마음이 많이 여립니다.. 진행 중 🏃 스프린트 미션 진행중입니다. labels Feb 9, 2025
@soohwanpak soohwanpak self-assigned this Feb 9, 2025
@soohwanpak soohwanpak requested a review from kimjong95 February 9, 2025 16:11
Copy link
Collaborator

@kimjong95 kimjong95 left a comment

Choose a reason for hiding this comment

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

typescript를 제대로 활용하고싶다면, typescript에서 타입이 어떻게 추론되는지, 또한 어떠한 방식으로 타입을 지정할수있는지 학습이 필요할 것 같습니다.

typescript를 사용하지만 type시스템을 잘 활용하지 못한다면 오히려 javascript로만 작성할때보다 오버헤드가 발생할 것이고 불편하다고 느낄거에요.

productId: string;
}

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.

model 혹은 viewModel의 형태를 가지고있는 타입 또는 interface는 별도의 파일로분리해서 export하고 원하는 사용처에서 가져가서 사용하면 좋을것 같아요. 아래에서 추가로 볼게요

isError,
} = useQuery<Comment[], Error>({
queryKey: ["product-comments", productId],
queryFn: () => axiosProductComments(productId),
Copy link
Collaborator

Choose a reason for hiding this comment

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

현재 여기서 받는 response의 타입은 any일것 같습니다

};

//특정 상품의 댓글 가져오기
export const axiosProductComments = async (productId: string) => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

때문에 여기서 받는 Response의 타입이 Promise<Response> 형태의 타입이여야 하고, 여기서 Comment타입이 사용되어야합니다.

현재는 컴포넌트레벨에서 타입을 정의해주고있는데, 타입추론 관점에서본다면 타입이 시작되는 시점에 타입을 적용해주셔야 해당 값 또는 함수를 사용하는 모든 하위의 코드들에서 타입이 추론될 수 있습니다.
타입스크립트에서는 타입추론을 통해서 모든 곳에 타입을 지정하지 않아도 되는 이유이기도 하고요. 이관점에서본다면 타입을 어디에 지정해주어야할지, 어디서 사용해야할지에 대한 고민이 필요합니다. 이코드를 살짝 수정한다면 다음과같이 수정할 수 있을것 같아요

export type Comment = {
  id: string;
  content: string;
  user: { nickname: string };
  createdAt: string;
}

export const axiosProductComments = async (productId: string): Promise<Comment[]> => {
  const response = await axiosInstance.get<Comment[]>(`/productComment/${productId}`);
  return response.data;
};

그렇다면 이 함수의 값을 를 사용하는 곳에서는 모두 타입이 추론되겟죠?!

Copy link
Collaborator

Choose a reason for hiding this comment

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

그리고 필요하다면 Comment타입을 원하는곳에서 사용할수 있을거에요

const [page, setPage] = useState(1);
const [pageSize, setPageSize] = useState(4);
const [selectedSort, setSelectedSort] = useState("좋아요순");
const [searchQuery, setSearchQuery] = useState("");
Copy link
Collaborator

Choose a reason for hiding this comment

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

필요하다면 이곳에도 state들의 타입을 generic을통해서 지정해주면 좋고, 정렬값또한 타입을두고 사용한다면 더 가독성이 좋을것 같아요. 예를 들어볼게요

const SORT_OPTIONS = {
  LIKE: "좋아요순",
  LATEST: "최신순",
} as const;

// 타입 정의
type SortOptionKey = keyof typeof SORT_OPTIONS;
type SortOptionValue = (typeof SORT_OPTIONS)[SortOptionKey];
type SortApiValue = "like" | "latest";

// 정렬 옵션과 API 값 매핑 객체
const SORT_API_MAP: Record<SortOptionValue, SortApiValue> = {
  [SORT_OPTIONS.LIKE]: "like",
  [SORT_OPTIONS.LATEST]: "latest",
} as const;

...
  const [selectedSort, setSelectedSort] = useState<SortOptionValue>(SORT_OPTIONS.LIKE);
...

const {
    data: { products = [], total = 0 } = {},
    isLoading,
    error,
  } = useQuery({
    queryKey: ["products", searchQuery, selectedSort, page, pageSize],
    queryFn: () =>
      axiosProduct(
        searchQuery,
        SORT_API_MAP[selectedSort],
        page,
        pageSize
      ),
  });

예시에서는 조금 오버한 경향이 있지만, 사용하시는것처럼 key-value형태의 값과 타입이 필요하고 그 관리포인트가 늘어갈때 유용한 방법이기도 합니다.

상품 태그
</div>
<div className="flex flex-wrap gap-2 mt-2">
{product?.tags?.map((tag, index) => (
Copy link
Collaborator

Choose a reason for hiding this comment

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

  1. 위에서 product의 타입이 optional이 아니기때문에 해당 코드에서도 optional이 필요없을것 같아요.
  2. 마찬가지로 tags도 optional이 아니기때문에 필요없을것 같고요.

하지만 의도하지않게 product나 tags가 null이나 undefined형태로 들어올 수 있다면 이 컴포넌트의 상위에서 처리가 필요할수도 있을것 같아요.

위에 설명드린것 처럼 타입의 추론은 코드의선언부부터 사용측으로 추론이되기때문에 선언할때 부터 잘못사용하고있는지 확인이 필요합니다.

selectedValue,
placeholder,
onValueChange,
className = "",
Copy link
Collaborator

Choose a reason for hiding this comment

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

DropdownProps의 각 프로퍼티가 optional이고 이에대한 기본값을 지정해주기위해서 처리해주신것 같은데 좋습니다!

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