[React/Node.js/Express/MongoDB] 소셜 가계부 프로젝트 구현 일지: JavaScript → TypeScript 마이그레이션 3(가계부 페이지, Button 컴포넌트: onClick 함수 타입 지정, 자잘한 오류들 해결)

2024. 4. 4. 22:26·웹 프로젝트/👨‍👨‍👧‍👧소셜 가계부
반응형

이전 마이그레이션 관련 포스팅

  • JavaScript → TypeScript 마이그레이션 1(세팅, App, AppRouter)
  • JavaScript → TypeScript 마이그레이션 2(대시보드 페이지, 대시보드 관련 컴포넌트: state 빈 배열 초기화 never type 에러 해결)

이전에 이어서 이번에는 Transactions 페이지와 관련한 컴포넌트 마이그레이션을 진행해 볼 예정이다.

 

 

Transactions 페이지

파일 형식을 jsx에서 tsx로 바꾼 뒤에 에러가 뜨는 코드를 확인하고 수정해 주었다.

// Transactions.tsx 

import React, { useEffect, useState } from "react";
...
import { UserInfo } from "../interfaces/UserData";
import { StoreData } from "../interfaces/StoreData";
import { TransactionData } from "../interfaces/TransactionData";

export default function Transactions() {
  const transactionList: TransactionData[] = useSelector((state: StoreData) => state.transactions.transactions);
  const userInfo: UserInfo = useSelector((state: StoreData) => state.user.userInfo);
  ...
  const [data, setData] = useState<TransactionData[]>([]);

  ...

  return (
    <Section>
      <div className="container">
        <Header
          text={dateText}
          leftChild={<Button text="◀" onClick={decreaseMonth} color="grey" />}
          rightChild={<Button text="▶" onClick={increaseMonth} color="grey" />}
        />
        ...
    </Section>
  );
}

이전 마이그레이션 파트에서 자주 등장하던 `userInfo` 이외에,

리덕스 저장소에서 가계부 리스트를 가져와 저장하는 `transactionList`라는 변수가 새로 생겼다.

가계부 리스트 타입을 정의하기 위해 새로운 인터페이스를 생성했다.

 

// TransactionData.ts

export interface TransactionList {
  transactions: TransactionData[];
}

export interface TransactionData {
  uid: string;
  transaction_type: boolean;
  date: number;
  category: string;
  title: string;
  amount: number;
  memo: string;
}

어제 배웠던 대로 배열 내부에 객체가 들어가는 데이터 타입을 지정하기 위해 `TransactionData[]`를 사용했다.

`TransactionData` 내부에는 가계부에 사용되는 데이터의 모든 속성과 타입이 들어가 있다.

가계부 코드 짠 지 워낙 오래돼서 속성명이나 데이터 타입을 뭘로 정했는지 생각이 안 났다; 몽고DB에 데이터 저장할 때 작성해 둔 스키마를 참고해 타입 지정을 해주었다.

 


 

이제 `Transactions` 내부에서 해결할 수 있는 에러는 모두 해결되었다.

대신 `Transactions` 컴포넌트에서 불러와 사용하고 있는 공용 컴포넌트 `Button`에서 에러가 발생하고 있어서, 오류 수정을 위해 `Button.jsx` 파일로 이동해 보겠다.

Button 컴포넌트

// Button.jsx

import React from 'react';
import { MouseEventHandler } from "react";
import styled from "styled-components";

export default function Button({ text, type, onClick, color }) {
  return (
    <CustomButton type={type} onClick={onClick} color={color}>
      {text}
    </CustomButton>
  );
}

const CustomButton = styled.button`
  ...
`;

 

 

컴포넌트에 있어도 되고 없어도 되는 props 가 있다면 ? 문자를 사용

// Transactions.tsx
  ...
          leftChild={<Button text="◀" onClick={decreaseMonth} color="grey" />}
          rightChild={<Button text="▶" onClick={increaseMonth} color="grey" />}
  ...

 

`Transactions` 컴포넌트에서는 `Button` 컴포넌트에 text, onClick, color 총 3개의 props를 전달하고 있다.

그러나 `Button` 컴포넌트에서는 3개의 props에 type까지 총 4개의 props를 받는다. 

자바스크립트로 코드를 작성할 때는 필요 없는 props는 굳이 전달하지 않아도 되었는데, 타입스크립트에서는 정의된 그대로 props를 모두 전달해 주어야 하고 이 때문에 에러가 발생하고 있었다.

 

😲 해결

전에 TS 강의에서 타입스크립트의 선택적 프로퍼티에 대해 배운 적이 있다.

변수를 선택적 프로퍼티로 설정하기 위해서는 단순히 변수 뒤에 ? 기호를 붙여주기만 하면 된다.

이러한 선택적 프로퍼티 기능을 컴포넌트에서 props를 받을 때도 동일하게 사용할 수 없나 하고 찾아봤다.

import React from 'react';

type GreetingsProps = {
  name: string;
  mark: string;
  optional?: string;
};

function Greetings({ name, mark, optional }: GreetingsProps) {
  return (
    <div>
      Hello, {name} {mark}
      {optional && <p>{optional}</p>}
    </div>
  );
}

위 예시처럼 컴포넌트에서 props를 받을 때도 optional 기호를 사용할 수 있다고 한다.

(출처: https://react.vlpt.us/using-typescript/02-ts-react-basic.html)

위 코드대로 진행하기 위해 `Button.jsx`를 `Button.tsx`로 바꾸고 프롭스를 optional하게 지정해 주었다.

다른 속성은 쉽게 타입 지정을 했는데, onClick 함수는 어떻게 초기값 지정을 해야 하는지 헷갈렸다.

 

 

props로 전달하는 onClick 함수의 타입 지정하기

서치 해 보니 리액트 마우스 이벤트 핸들러 타입은 다음과 같이 저장되어 있다고 한다.

type MouseEventHandler<T = Element> = EventHandler<MouseEvent<T>>;

interface Props {
  onClick: React.MouseEventHandler<HTMLButtonElement>;
}

따라서 해당 `MouseEventHandler<HTMLButtonElement>` 코드를 그대로 타입으로 지정해 주었다.

(참고: https://velog.io/@eenaree/props-onClick-type)

type ButtonProps = {
  text: string;
  type?: string;
  onClick: MouseEventHandler<HTMLButtonElement>;
  color?: string;
};

export default function Button({ text, type, onClick, color }: ButtonProps) {
  return (
    <CustomButton type={type} onClick={onClick} color={color}>
      {text}
    </CustomButton>
  );
}

위와 같이 타입을 optional하게 지정해 주니 에러가 사라졌다!

 

 

🚫 에러: styled-components Could not find declaration file

jsx를 tsx로 바꾸면서부터 `styled-components Could not find declaration file`라는 에러가 뜨고, styled-component를 정상적으로 불러오지 못하는 에러가 발생했다. 

이는 TypeScript가 styled-components와 연관된 모듈에 대한 선언 파일을 가져오지 못했을 때 발생하는 문제였다.

에러를 해결하기 위해 간단하게 @type이 추가된 styled-components 모듈을 재설치해주었다.

npm i --save-dev @types/styled-components

 

😲 TypeScript를 사용하는 프로젝트에 모듈을 설치할 때 @type을 붙이는 이유

👉 @type 없이 설치할 경우 모듈 내부 코드에 대한 type이 정의되어 있지 않아 TypeScript 파일에서 type추론이 불가능하기 때문

(출처: https://garniel23.tistory.com/entry/%EC%97%90%EB%9F%AC-%ED%95%B4%EA%B2%B0-styled-components-Could-not-find-declaration-file)

 

이렇게 styled-components를 불러오지 못하는 문제는 해결 완료 하였다.

 

 

🚫 에러: 'React' refers to a UMD global, but the current file is a module

이건 또 뭔지...?

`'React' refers to a UMD global, but the current file is a module` 에러 발생.

해결 방법은 간단했다. `import React from 'react'` 코드를 추가하면 된다.

(참고: https://student513.tistory.com/82)

이렇게 하니 에러는 사라졌다. 이제 남은 건 옆의 type오류...

 

 

🚫 에러:  이 호출과 일치하는 오버로드가 없습니다. 2의 오버로드 1, '(프롭스: {type?: "button" | submit" | "reset" | undefined; on Click?: MouseEventHandler <HTMLButtonElement> | undefined; color?: string | undefined; ... 271 more...; onTransitionEndCapture?: TransitionEventHandler <...> | undefined; } & {...; } & {...} & {...}; }): ReactElement <...>에서 다음 오류가 발생했습니다.

`<Button>` 컴포넌트 호출 시 인자로 전달된 값들이 해당 컴포넌트의 기대 props 값과 다르기 때문에 발생하는 오류였다. 

해당 오류를 해결하기 위해서는 `<Button>` 컴포넌트를 호출할 때 필요한 props를 정확히 전달해 주어야 한다.

오류 메시지를 잘 확인해 누락된 프로퍼티를 확인하고, 필요한 경우 optional로 설정해 주는 식으로 타입 오류를 해결했다.

이렇게 완성된 최종 `<Button>` 컴포넌트는 아래와 같다.

// Button.tsx

import React from "react";
import { MouseEventHandler } from "react";
import styled from "styled-components";

type ButtonProps = {
  text?: string;
  type?: "button" | "submit" | "reset";
  onClick: MouseEventHandler<HTMLButtonElement>;
  color?: string;
};

export default function Button({ text, type, onClick, color }: ButtonProps) {
  return (
    <CustomButton type={type} onClick={onClick} color={color}>
      {text}
    </CustomButton>
  );
}

const CustomButton = styled.button`
  ...
`;

이제 모든 코드가 에러 없이 잘 동작한다.

반응형
저작자표시 비영리 변경금지 (새창열림)

'웹 프로젝트 > 👨‍👨‍👧‍👧소셜 가계부' 카테고리의 다른 글

[React/Node.js/Express/MongoDB] 소셜 가계부 프로젝트 구현 일지: JavaScript → TypeScript 마이그레이션 6(index, 리덕스 코드)  (0) 2024.04.17
[React/Node.js/Express/MongoDB] 소셜 가계부 프로젝트 구현 일지: JavaScript → TypeScript 마이그레이션 5(Auth: Test 계정 Auto Fill 구현, Community: pagination 마이그레이션)  (1) 2024.04.14
[React/Node.js/Express/MongoDB] 소셜 가계부 프로젝트 구현 일지: JavaScript → TypeScript 마이그레이션 4(가계부 페이지 관련 컴포넌트: TransactionList, Modal)  (0) 2024.04.08
[React/Node.js/Express/MongoDB] 소셜 가계부 프로젝트 구현 일지: JavaScript → TypeScript 마이그레이션 2(대시보드 페이지, 대시보드 관련 컴포넌트: state 빈 배열 초기화 never type 에러 해결)  (0) 2024.04.03
[React/Node.js/Express/MongoDB] 소셜 가계부 프로젝트 구현 일지: 개인정보, 토큰 저장 방식 변경2 (redux-persist 보안: redux-persist-transform-encrypt)  (0) 2024.03.31
[React/Node.js/Express/MongoDB] 소셜 가계부 프로젝트 구현 일지: 개인정보, 토큰 저장 방식 변경1 (LocalStorage → redux-persist)  (0) 2024.03.31
'웹 프로젝트/👨‍👨‍👧‍👧소셜 가계부' 카테고리의 다른 글
  • [React/Node.js/Express/MongoDB] 소셜 가계부 프로젝트 구현 일지: JavaScript → TypeScript 마이그레이션 5(Auth: Test 계정 Auto Fill 구현, Community: pagination 마이그레이션)
  • [React/Node.js/Express/MongoDB] 소셜 가계부 프로젝트 구현 일지: JavaScript → TypeScript 마이그레이션 4(가계부 페이지 관련 컴포넌트: TransactionList, Modal)
  • [React/Node.js/Express/MongoDB] 소셜 가계부 프로젝트 구현 일지: JavaScript → TypeScript 마이그레이션 2(대시보드 페이지, 대시보드 관련 컴포넌트: state 빈 배열 초기화 never type 에러 해결)
  • [React/Node.js/Express/MongoDB] 소셜 가계부 프로젝트 구현 일지: 개인정보, 토큰 저장 방식 변경2 (redux-persist 보안: redux-persist-transform-encrypt)
청량리 물냉면
청량리 물냉면
프로그래밍 공부를 하고 있습니다. 공부 내용 정리 겸 정보 공유를 목적으로 합니다.
    반응형
  • 청량리 물냉면
    노력중인 블로그
    청량리 물냉면
  • 전체
    오늘
    어제
    • 분류 전체보기
      • 프로그래밍
        • Programming
        • C | C++
        • Java
        • Python
      • 웹 프로그래밍
        • HTML | CSS
        • JavaScript | TypeScript
        • React
        • Vue.js
        • Next.js
        • Spring & Spring Boot
        • JSP & Servlet
        • DB
      • 웹 프로젝트
        • 웹 프로젝트
        • 🥨스낵몰
        • 👨‍👨‍👧‍👧소셜 가계부
        • 🌜꿈 일기장
        • 🔮포트폴리오 사이트
        • 🏃‍♂️팀 프로젝트: 일정관리 프로그램
        • 📈팀 프로젝트: AI기반 주식 분석 플랫폼
        • 😺Just Meow It: 고양이의 조언
      • 앱 프로그래밍
        • Flutter
        • Kotlin
      • Problem Solving
        • 백준
        • 프로그래머스
        • SWEA
      • Computer Science
        • 알고리즘
        • 컴퓨터 네트워크
        • 이산수학
      • Developer
        • 후기
        • 자료정리
        • 취업 | 취준
        • 웹개발 교육 프로그램
        • TIL
  • 블로그 메뉴

    • 홈
    • Github
  • 공지사항

    • 프로그래밍 공부 중😊
  • 인기 글

  • 태그

    SWEA
    리액트
    컴퓨터네트워크
    파이썬
    ZeroCho
    웹사이트
    뉴렉처
    React
    AWS
    bfs
    플러터
    Til
    자바
    mysql
    구현
    Next.js
    공식문서
    자바스크립트
    프로그래머스
    타입스크립트
    프로젝트
    알고리즘
    클론 프로젝트
    강의내용정리
    블로그 제작
    포트폴리오
    spring boot
    Jiraynor Programming
    백준
    d3
  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
청량리 물냉면
[React/Node.js/Express/MongoDB] 소셜 가계부 프로젝트 구현 일지: JavaScript → TypeScript 마이그레이션 3(가계부 페이지, Button 컴포넌트: onClick 함수 타입 지정, 자잘한 오류들 해결)
상단으로

티스토리툴바