3 minute read

.ts 와 .tsx 차이 그리고 모듈의 독립성

리팩토링 과정중

src/api/ApplyWrite.ts 파일에서 setApplicationData: (value: React.SetStateAction<ApplicationData>) 와 같은 함수와 value의 type을 지정해주는데 import React from 'react'; 이 자동으로 import 되지 않아 의아했다.
다른 것들은 자동으로 import 되는데 해당 부분만 문제가 있는게 이상하다 싶어 찾다가, ts파일은 tsx와 달리 자동으로 import 되지 않음을 알 수 있었다

그렇다면 .ts파일과 .tsx 파일의 역할은 무엇이 다를까?

기본적으로 .ts는 TypeScript만 사용하고 JSX 문법을 사용하지 않을 때, .tsx는 react component와 JSX 문법을 사용할 때라고 볼 수 있다.

그러면 결국 .ts보단 .tsx 파일이 더 넓은 범위인데 굳이 파일 확장자를 나누는 기준이 무엇일까? 🤔

jsx와 tsx에서는 JavaScript XML을 처리하기 위한 추가적인 변환 과정이 발생한다고 한다. 때문에 불필요한 .tsx 파일을 사용하면 불필요한 변환 과정이 발생하고, 이는 곧 성능의 저하로 이루어질 수 있다.
추가로, ts, tsx 확장자를 분리해두면 ts 파일만 봐도 로직을 다루고 있는 코드구나!와 같이 파일별 역할을 파악할 때 수월해진다. ts파일은 주로 순수 로직, API 호출, type 정의 등에서 사용한다.

정리하자면

  • 불필요한 JavaScript XML 처리를 위한 변환 과정으로 인해 빌드 속도, 번들링에 영향을 주어 성능의 저하로 이루어질 수 있다
  • 작은 규모가 아닌 큰 규모의 프로젝트 경우 컴포넌트, 컴포넌트와 관련있는 유틸리티 코드 외에도 라이브러리 및 내부 시스템 파일이 존재한다. 이때, .tsx를 남발하면 리액트를 다른 것으로 바꿀 때도 .tsx만 보고 모두 바꿔버릴 위험도 있다. 즉, 해당 코드가 리액트랑 관련이 있는지 없는지 확인하기 어려워진다
  • 파일별 역할 파악에 있어 수월해진다

정도로 정리해냈다.

다시 본론으로 돌아와서,, 본 게시물의 주제인 모듈의 독립성은 무슨 관련이었을까?

현재 리팩토링을 하던 파일 구조의 일부는 아래와 같다! 현재 ApplyWrite 페이지와 관련하여 리팩토링을 진행중이었고, 해당 페이지와 관련한 component는 writePage로 분리해두었다.
때문에 재활용을 하지 못한다는 단점이 있지만 작은 규모의 프로젝트이니 편리성을 위해 이와 같은 구조로 진행을 했다 🤧

📦src
  📂api
  📂assets
  📂components
   📂CardLanyard
   📂Header
   📂NotFound
   📂checkPage
   📂mainPage
   📂writePage
  📂hooks
  📂messages
  📂pages
   📂ApplyWrite
    📂data
   📂NotFound
  📂styles
  📂types
  📂validation
  📜기타 파일들

가장 상위 컴포넌트였던 index.tsx와 관련한 로직이 담긴 src/api/ApplyWrite.ts를 리팩토링 하면서 모듈의 독립성에 대해 학습할 수 있었다. ApplyWrite.js 파일의 일부는 아래와 같았다.

// ApplyWrite.js
export const getApplicationData = (
  part,
  setApplicationDataFunction,
  navigateFunction
) => {
  Axios.get(`/application/${part}`)
    .then((res) => {
      setApplicationDataFunction(res.data.data);
      return res;
    })
    .catch((err) => {
      const DetailStatusCode = err.response.data.statusCode;
      const statusCode = DetailStatusCode.substring(0, 3);
      if (
        statusCode === "400" ||
        statusCode === "500" ||
        statusCode === "900"
      ) {
        alert(
          "서버에 의도치 않은 에러가 발생했습니다. \n 공식 사이트 채널톡 혹은 인스타 DM으로 문의 부탁드립니다."
        );
      }
      navigateFunction("/");
    });
};

index.js에서는 아래와 같이 사용이 되고있었다

// index.js
useEffect(() => {
  setStudentIdValue(sessionStorage.getItem("studentId"));
  getApplicationData(selectedPart, setApplicationData, navigate);
}, []);

때문에 TypeScript로 리팩토링 하면서 setApplicationData: (value: React.SetStateAction<ApplicationData>) 처럼 타입을 지정하다보니 ApplyWrite.ts가 아닌 ApplyWrite.tsx 파일로 작성을 해야만 했다. 로직이 담긴 파일의 역할을 제대로 나누기 위해서 배우게 된 것이 모듈의 독립성이었다!

모듈의 독립성은 결합도와 응집도에 의해 측정이 되는데 결합도는 약하게 응집도는 높게, 모듈의 크기는 작을 수록 좋다.

  • 결합도: 모듈 간에 상호 의존하는 정도 또는 두 모듈 사이의 연관 관계
  • 응집도: 정보 은닉 개념을 확장한 것으로, 명령어나 호출문 등 모듈의 내부 요소들의 서로 관련되어 있는 정도, 즉 모듈이 독립적인 기능으로 정의되어 있는 정도를 의미

그렇다면 현재 코드에서 그나마 독립성을 어떤 방식으로 높일 수 있을까? 🧐

// ApplyWrite.ts
export const getApplicationData = (
  part: IPartKey,
  setApplicationDataFunction: (param: any) => void,
  navigateFunction?: NavigateFunction
) => {
  Axios.get(`/applications/${part}`)
    .then((res) => {
      setApplicationDataFunction(res.data.data);
      return res;
    })
    .catch((err) => {
      const DetailStatusCode = err.response.data.statusCode;
      const statusCode = DetailStatusCode.substring(0, 3);
      if (
        statusCode === "400" ||
        statusCode === "500" ||
        statusCode === "900"
      ) {
        alert(
          "서버에 의도치 않은 에러가 발생했습니다. \n 공식 사이트 채널톡 혹은 인스타 DM으로 문의 부탁드립니다."
        );
      }
      navigateFunction?.("/");
    });
};
 // index.tsx
 const **setApiData** = (param: any) => {
    setApplicationData(param);
  };

  const handlePartClick = (part: IPartKey) => {
    setSelectedPart(part);
    getApplicationData(part, **setApiData**);
  };

현재 getApplicationDatasetApplicationDataFunction의 역할을 하는 함수를 인자로 받고있다.

때문에 이전의 index.js에서는 해당 기능을 하는 함수를 직접적으로 넘기고 있었고, 때문에 서로 종속적으로 작동을 하고 있었다. 이를 해결하기 위해 setApplicationData()를 감싸는 새로운 setApiData 함수를 만들어서 넘겨 주었다. 해당 작업은 ‘resolver’라고 불리기도 하고 프로젝트에 따라 부르는 이름이 다양한 것 같다..! 이처럼 함수를 래핑함으로써 기존 함수의 동작을 확장하거나 변경할 수 있게 되어, 모듈 간의 종속성을 줄일 수 있었다

참고
https://velog.io/@skyoffly/개발-지식-Typescript-.ts와-.tsx의-차이점은 https://m.blog.naver.com/gluestuck/221899977072