♻️ JS 프로젝트를 TS로 변환하기

Doozuu·2023년 11월 3일
0

TypeScript

목록 보기
13/13

1. 타입 정보를 제공하는 패키지 추가 설치

npm i @types/node @types/react @types/react-dom @types/jest



2. TS 컴파일러 옵션 설정하기

  • 프로젝트 루트에 tsconfig.json 파일을 생성하고 아래와 같이 작성한다.
{
  "compilerOptions": {
    "target": "ES5",
    "module": "CommonJS",
    "strict": true,
    "allowJs": true,
  },
  "include": ["src"]
}

이때, "~~ 파일은 입력 파일을 덮어쓰므로 쓸 수 없습니다."라는 에러가 발생한다.

  • TS는 JS 파일 속 JSX를 해석할 수 없기 때문에 모든 js 파일의 확장자를 .jsx로 변경한다.

  • 이제 파일 확장자를 하나씩 jsx 에서 tsx로 바꿔준다.

"기본 내보내기가 없습니다." 라는 에러가 발생하면 tsconfig.json에 "esModuleInterop": true를 추가한다.

  • 기본 내보내기는 ESM 시스템에서 default 키워드로 내보낸 값으로, 라이브러리를 사용한다면 default 내보내기가 없는 경우가 있을 수 있다.
  • 이때 위의 옵션을 설정하면 CommonJS 모듈에서도 ES6 스타일의 디폴트 내보내기를 사용할 수 있다.

"--jsx 플래그를 제공하지 않으면 JSX를 사용할 수 없습니다." 라는 에러가 발생하면 tsconfig.json에 "jsx": "react-jsx"를 추가한다.

  • TS 컴파일러는 JSX 문법을 해석할 수 없다.
  • 해당 옵션을 설정하면 JSX 코드를 효율적으로 JavaScript 코드로 변환할 수 있다.

index.tsx에서 document.getElementId("root") 부분에 에러가 발생하면 document.getElementById("root") as HTMLElement로 바꾸어준다.

  • getElementById는 Null 을 반환할 가능성이 있다.
  • createRoot는 null 타입의 값을 인수로 받을 수 없기 때문에 타입 단언으로 해결한다.

최종 형태

{
  "compilerOptions": {
    "target": "ES5",
    "module": "CommonJS",
    "strict": true,
    "allowJs": true,
    "esModuleInterop": true,
    "jsx": "react-jsx"
  },
  "include": ["src"]
}



3. 오류 해결하기

1. png 파일을 못 읽을 경우

  • TypeScript 환경에서 이미지 파일을 import할 때, TypeScript는 .png 확장자를 직접 해석할 수 없다.
  • 따라서 .png 파일을 TypeScript에서 정상적으로 인식하려면 추가적인 설정이 필요하다.

확장자 정의 파일 생성 (declarations)

  • 먼저 .png 파일에 대한 타입 정의 파일을 생성해야 한다.
  • 이렇게 하면 TypeScript가 이미지 파일을 모듈로 인식할 수 있다.
  • declarations.d.ts 파일을 루트 디렉토리에 생성하고 다음 내용을 추가한다.
declare module "*.png" {
  const value: string;
  export default value;
}

2. 데이터 매핑할 때 속성을 못 읽는 경우

  • el에 어떤 속성이 있는지 정의되어 있지 않으므로 에러가 발생한다.
  1. 이를 해결하기 위해 savedImages의 각 아이템의 타입을 나타내는 interface를 정의한다.
export interface ImageInfo {
  id: number;
  download_url: string;
  author: string;
  private_note: string;
}
  1. el의 type으로 해당 interface를 지정해준다.
{savedImages.map((el: ImageInfo) => (
  <Figure
    key={el.id}
    onMouseEnter={() => setHoveredIndex(el.id)}
    onMouseLeave={() => setHoveredIndex(-1)}
  >
    {hoveredIndex === el.id && (
      <EditIcon
        src={edit}
        onClick={() => {
          setIsModal(true);
          setClickedIndex(el.id);
        }}
      />
    )}

3. 직접 만든 속성을 사용할 수 없는 경우

  • 아래에서 isHover는 내가 직접 만든 속성이기 때문에 이대로 사용하면 에러가 발생한다.
<Img
  key={el.id}
  src={el.download_url}
  isHover={hoveredIndex === el.id ? 1 : 0}
/>
const Img = styled.img`
  width: 100%;
  border-radius: 18px;
  cursor: pointer;

  transition: filter 100ms ease;

  filter: ${(props) =>
    props.isHover === 1 ? "brightness(55%)" : "brightness(100%)"};
`;

  • isHover 프로퍼티를 사용할 때 TypeScript에서 에러가 발생하는 이유는 해당 프로퍼티가 Img 컴포넌트의 정의에 없기 때문이다.
  • TypeScript에서 프로퍼티를 사용하려면 해당 프로퍼티가 정의된 인터페이스나 타입에 존재해야 한다.
  • styled-components로 스타일드 컴포넌트를 정의할 때, 컴포넌트에 추가적인 프로퍼티를 넣어 사용하려면 다음과 같이 정의할 수 있다.
  1. 프로퍼티를 컴포넌트에 추가하도록 인터페이스를 확장한다.
interface ImgProps {
  isHover: number;
}
  1. 컴포넌트의 정의를 변경하여 ImgProps를 사용하도록 한다.
const Img = styled.img<ImgProps>`
  width: 100%;
  border-radius: 18px;
  cursor: pointer;

  transition: filter 100ms ease;

  filter: ${(props) =>
    props.isHover === 1 ? "brightness(55%)" : "brightness(100%)"};
`;

4. 매개변수 타입 지정하기

  • 함수에서 매개변수의 타입을 지정해두지 않고 사용하면 에러가 난다.
  • 따라서 아래처럼 타입을 지정해주어야 한다.
const handleSaveClick = (imgInfo: ImageInfo) => {};
const EditModal = ({ setIsModal, idx }: { setIsModal: any; idx: number }) => {}



4. 코드 리팩토링

1. interface들을 공통된 파일로 분리하기

  • 특히 여러 파일에서 중복 사용되는 인터페이스가 있을 때, 아래처럼 공통된 파일로 분리해두면 매번 같은 내용의 인터페이스를 생성하지 않아도 되므로 코드 중복을 줄일 수 있다.

CommonTypes.ts

export interface ImageInfo {
  id: number;
  download_url: string;
  author: string;
  private_note: string;
}

export interface ImgProps {
  isHover: number;
}

export interface Save {
  save: number;
}

export interface IsMobile {
  mobile: number;
}

아래처럼 import해서 사용하면 된다.

import { ImageInfo, ImgProps } from "../CommonTypes";

참고 | 루트 디렉토리에 있는 파일은 tsconfig.json에 아래와 같이 파일 경로를 추가해준다.

{
  "compilerOptions": {
    "target": "ES5",
    "module": "CommonJS",
    "strict": true,
    "allowJs": true,
    "esModuleInterop": true,
    "jsx": "react-jsx"
  },
  "include": ["src", "declaration.d.ts", "CommonTypes.ts"]
}

2. 배열 내부 타입 지정하기

data를 아래와 같이 일반 배열로 초기화해두면 data 내부 값의 타입은 정해지지 않았으므로, 내부 요소를 사용할 때마다 아래와 같이 타입을 매번 지정해주어야 한다.

const [data, setData] = useState([]); // 이미지 데이터
{savedImages.map((el: ImageInfo) => (
  <Figure
    key={el.id}
    onMouseEnter={() => setHoveredIndex(el.id)}
    onMouseLeave={() => setHoveredIndex(-1)}
  >
    {hoveredIndex === el.id && (
      <EditIcon
        src={edit}
        onClick={() => {
          setIsModal(true);
          setClickedIndex(el.id);
        }}
      />
    )}

이런 번거로움을 덜기 위해 dat의 타입을 배열로 지정하고, 배열 내부 값의 타입을 ImageInfo로 설정할 수 있다.

const [data, setData] = useState<ImageInfo[]>([]); // 이미지 데이터



😮 실수로 node_modules 폴더가 푸시된 경우

업로드되는 파일의 개수가 너무 많아지기 때문에 gitignore 해주어야 한다.

  1. 프로젝트 루트 디렉토리에 .gitignore 파일 생성
  2. node_modules 디렉토리 무시하도록 작성
# node_modules 디렉토리 무시
node_modules/
  1. .gitignore 파일을 Git 저장소에 커밋하여 설정 적용
git add .gitignore
git commit -m "Add .gitignore to exclude node_modules"
  1. 이미 Git 저장소에 추가된 node_modules 디렉토리 삭제하기
git rm -r --cached node_modules
  1. 변경 사항 푸시
git add .
git commit -m "Remove node_modules and update .gitignore"
git push origin
profile
모든게 새롭고 재밌는 프론트엔드 새싹

0개의 댓글