Skip to content
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

[김현서] Week12 #385

Merged

Conversation

khs0727
Copy link
Collaborator

@khs0727 khs0727 commented May 5, 2024

요구사항

기본

  • TypeScript를 활용해 프로젝트의 필요한 곳에 타입을 명시해 주세요.
  • ‘/shared’, ‘/folder’ 페이지에 현재 폴더에서 검색어가 포함된 링크를 찾을 수 있는 링크 검색 기능을 만들어 주세요.

심화

  • 폴더 페이지에 상단에 있는 링크 추가하기 input이 화면에서 가려질 때 최하단에 고정해 주세요.

주요 변경사항

  • 타입스크립트로 변환
  • 링크 검색 기능 구현

멘토에게

  • 기능을 11주차 브랜치에서 작업하다가 나중에 옮겨서 몇 개의 commit이 반영되지 않았습니다.
  • VSCode상에서 타입스크립트 관련 오류는 나지 않는데 아직 어떻게 실행해봐야할지 잘 모르겠어서 맞게 작성한 건지는 잘 모르겠습니다..!
  • 심화 기능 중 푸터가 시작되는 지점에서 링크 추가하기 영역이 다시 보이지 않게하는 부분은 구현하지 못하였습니다.

@khs0727 khs0727 requested a review from kiJu2 May 5, 2024 13:30
@khs0727 khs0727 added the 미완성🫠 죄송합니다.. label May 5, 2024
@kiJu2
Copy link
Collaborator

kiJu2 commented May 8, 2024

수고 하셨습니다 ! 위클리 미션 하시느라 정말 수고 많으셨어요.
학습에 도움 되실 수 있게 꼼꼼히 리뷰 하도록 해보겠습니다.

@kiJu2
Copy link
Collaborator

kiJu2 commented May 8, 2024

기능을 11주차 브랜치에서 작업하다가 나중에 옮겨서 몇 개의 commit이 반영되지 않았습니다.

헙.. 그렇군요 넵넵 !


VSCode상에서 타입스크립트 관련 오류는 나지 않는데 아직 어떻게 실행해봐야할지 잘 모르겠어서 맞게 작성한 건지는 잘 모르겠습니다..!

심화 기능 중 푸터가 시작되는 지점에서 링크 추가하기 영역이 다시 보이지 않게하는 부분은 구현하지 못하였습니다.

그렇군요 !! 타입스크립트 코드 유심히 보면서 코드리뷰 해보겠습니다 ! 😊

Comment on lines +11 to +17
interface CustomButtonProps {
onClick: () => void;
icon: string;
text: string;
}

const CustomButton = ({ onClick, icon, text }: CustomButtonProps) => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

React에서 제공하는 HTML type을 사용해보시는건 어떨까요?

다음과 같이 리액트에서 제공하는 Attributes를 사용할 수도 있습니다:

import cn from 'classnames';
import { ButtonHTMLAttributes } from 'react';

interface Props extends ButtonHTMLAttributes<HTMLButtonElement> {
  variant?: 'primary' | 'secondary' | 'none';
}

export default function MelonButton({ className, variant, ...rest }: Props) {

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
interface CustomButtonProps {
onClick: () => void;
icon: string;
text: string;
}
const CustomButton = ({ onClick, icon, text }: CustomButtonProps) => {
interface CustomButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
// onClick: () => void; ⭐️ 이미 `ButtonHTMLAttributes`에 정의된 타입
icon: string;
// text: string; ⭐️ 이미 `ButtonHTMLAttributes`에 정의된 타입
}
const CustomButton = ({ onClick, icon, children }: CustomButtonProps) => {

import styled from "styled-components";

const StyledAllButton = styled.button`
const StyledAllButton = styled.button<{ active: boolean }>`
Copy link
Collaborator

Choose a reason for hiding this comment

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

스타일 컴포넌트도 제네릭으로 타입을 주셨군요 !!! 👍👍👍

Comment on lines +5 to +8
interface ButtonProps extends StyledButtonProps {
text?: string;
onClick?: () => void;
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

해당 컴포넌트 타입도 ButtonHTMLAttributes<HTMLButtonElement>를 활용해볼 수 있을 것 같아요 !

Suggested change
interface ButtonProps extends StyledButtonProps {
text?: string;
onClick?: () => void;
}
interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
type: // 기존 `StyledButtonProps`에 있던 값
}

Comment on lines +14 to +20
createdAt?: string;
created_at?: string;
url: string;
title?: string;
description?: string;
imageSource?: string;
image_source?: string;
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
createdAt?: string;
created_at?: string;
url: string;
title?: string;
description?: string;
imageSource?: string;
image_source?: string;
createdAt?: string;
url: string;
title?: string;
description?: string;
imageSource?: string;

백엔드에 요청하여 스네이크 케이스와 카멜 케이스에 대해 요청을 주는 것이 좋겠으나, 현재 상황에서는 어려워보이니 앱 내부에서 조정을 해야할 것 같아요.

Copy link
Collaborator

Choose a reason for hiding this comment

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

어떻게 하나의 키로 관리할 수 있을까요?

API 호출 부에서 하나의 키로 보장받을 수 있게 노멀라이징을 해주면 됩니다 !

노멀라이징: 서로 다른 feature의 크기를 통일하기 위해서 크기를 변환 · 모두 동일한 크기 단위로 비교하기 위해 값을 변환하는 것.

다음과 같은 어댑터 패턴을 활용해볼 수 있어요:

어댑터 패턴?

// API로부터 받은 데이터 형태를 나타내는 타입들
type ApiResponse = {createdAt: Date} | {created_at: Date};

// 프론트엔드에서 사용할 데이터 형태
type User = {
  createdAt: Date;
};

// 어댑터 함수
function adaptUser(data: ApiResponse): User {
  return {
    createdAt: 'createdAt' in data ? data.createdAt : data.created_at,
  };
}

async function fetchUser(): Promise<User> {
  const res = await fetch('/user');
  const data: ApiResponse = await res.json();
  return adaptUser(data); // 변환된 데이터 반환
}

Comment on lines +14 to +20
const CardList = ({ isFolderPage }: CardListProps) => {
const { data: folderData, isLoading } = useFolderList();

if (isLoading) return <div>Loading...</div>;
if (!folderData) return null;

const folderId: string = "";
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
const CardList = ({ isFolderPage }: CardListProps) => {
const { data: folderData, isLoading } = useFolderList();
if (isLoading) return <div>Loading...</div>;
if (!folderData) return null;
const folderId: string = "";
const CardList = ({ isFolderPage }: CardListProps) => {
const { data: folderData, isLoading } = useFolderList();
if (isLoading) return <div>Loading...</div>;
if (!folderData) return null;
const folderId = "";

<SearchBar />
<S.CardContainer>
{folderData.folder.links.map((link: any) => (
<Card key={link.id} link={link} isFolderPage={isFolderPage} />
Copy link
Collaborator

Choose a reason for hiding this comment

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

굳굳 ! 매우 적절한 key군요 😊

closeModal: () => {},
});

const modalReducer = (state: ModalState, action: Action): ModalState => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

헙 !! reducer를 활용하다니 !

정말 잘 사용하고 계시군요 ! 멋져요 현서님

}
};

export const ModalProvider = ({ children }: { children: ReactNode }) => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

이렇게 관리하면 상태 관리를 정말 효율적으로 관리할 수 있겠군요 👍👍👍👍


import { LinkData, fetchLinkData } from "../../services/fetchFolderLinksData";

export type FolderId = number | string | null;
Copy link
Collaborator

Choose a reason for hiding this comment

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

FolderId는 여러 파일/컴포넌트에서 사용될 것 같군요.

Folder 엔티티 type을 지정하는 객체 타입을 만들어서 관리하는 것도 좋을 것 같아요. 예를 들면 Folder['id']와 같이요 !
이는, types/folder.type.ts 등으로 만들어볼 수 있습니다. 😊

다음은 설명을 위해서 user 타입으로 Props에 적용한다면 어떻게 할까? 라는 관점에서 작성해본 코드예요.

/types/user.type.ts

interface IUser  {
  id: number;
  created_at?: string;
  createdAt?: string;
  updated_at?: string;
  url: string;
  description: string;
  image_source?: string;
  imageSource?: string;
  folder_id: number;
}

// interface CreateUser extends Pick<IUser, 'something'> {} ;
// interface SampleUser extends Pick<IUser, 'something'> {};

그리고 다음은 Props예요.

interface AvatarProps extends Pick<IUser, 'id' | 'imageSource'> {}
function Avatar({ id, imageSource }: AvatarProps) {}

Copy link
Collaborator

Choose a reason for hiding this comment

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

또한, 컴포넌트 파일에서 Props 외에 타입을 export하는 경우는 드뭅니다 ! 😊


export type FolderId = number | string | null;

const Folder = ({ folderId }: { folderId: FolderId }) => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

다른 컴포넌트와 마찬가지로 Props 타입을 만들어보는게 어떨까요?

Suggested change
const Folder = ({ folderId }: { folderId: FolderId }) => {
const Folder = ({ folderId }: FolderProps) => {

@kiJu2
Copy link
Collaborator

kiJu2 commented May 8, 2024

정말 수고 많으셨어요 현서님 ...!!
처음과 지금. 정말 너무 성장 많이한 모습 보니 멘토로서 정말 뿌듯합니다 ㅠㅠㅠ
그리고 항상 한결같이 위클리 미션 잘 수행해주셔서 너무 감사드립니다.

전 주강사로 토요일에 항상 상주하고 있으니, 도움 필요하시면 언제든 방문해주세요 ㅎㅎㅎ
앞으로도 승승장구 하시길 기원합니다 !! 💪

@kiJu2 kiJu2 merged commit 8ae766b into codeit-bootcamp-frontend:part2-김현서 May 8, 2024
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.

3 participants