🔥 결론
이론적으로는 전부
.tsx로 써도 동작한다.
실무에서는 명확성 + 타입 안전성 + 빌드 최적화 + 관례 때문에.ts를 구분한다.
1️⃣ TypeScript 컴파일러 관점
TypeScript
TypeScript는 파일 확장자를 보고 파싱 모드를 결정한다.
.ts
→ JSX 문법을 해석하지 않는 모드
.tsx
→ JSX 문법을 해석하는 모드. 단순히 JSX를 허용하는 게 아니라
TypeScript 컴파일러에게
”이 파일은 UI 레이어다”라고 알려주는 신호다.
즉, 파서(parser)가 다르게 동작한다.
2️⃣ 왜 전부 .tsx로 하면 안 되냐?
기술적으로는 가능하다.
하지만 문제는 모호성(ambiguity) 이다.
⚠️ 예시 1: 제네릭 문법 충돌
const identity = <T>(arg: T) => arg;이 코드는 .ts에서는 문제 없음.
하지만 .tsx에서는:
<T>를 JSX 태그로 오해할 수 있다.
그래서 .tsx에서는 종종 이렇게 써야 한다:
const identity = <T,>(arg: T) => arg;콤마를 넣어야 JSX로 오해하지 않는다.
즉,
.tsx는 제네릭 문법이 더 까다롭다.
3️⃣ 파싱 비용과 빌드 최적화
.tsx는 항상 JSX 가능성을 열어둔다.
-
파서가 JSX 문법까지 고려
-
AST 구조가 더 복잡
-
빌드 도구가 JSX 트랜스파일 단계 추가
대규모 프로젝트에서는 이게 성능 차이를 만든다.
4️⃣ 의미적 구분 (가독성)
실무에서 제일 중요한 이유는 이거다.
utils/
math.ts
components/
Button.tsx파일만 봐도 알 수 있다:
-
.ts→ 순수 로직 -
.tsx→ UI 컴포넌트
이게 협업에서 엄청 중요하다.
5️⃣ React 철학과도 연결됨
React
React는 UI와 로직을 분리하지 않고
컴포넌트 단위로 관리한다.
.tsx는 사실상:
“UI 계층”이라는 의미를 가진다.
6️⃣ 타입 체커 동작 차이
TypeScript 설정에 따라:
{
"jsx": "react-jsx"
}이 설정은 .tsx 파일에만 적용된다.
.ts에는 JSX 변환이 적용되지 않는다.
7️⃣ 실무 기준 정리
| 상황 | 확장자 |
|---|---|
| 타입 정의 | .ts |
| API 함수 | .ts |
| 유틸 함수 | .ts |
| React 컴포넌트 | .tsx |
| JSX 포함 | .tsx |
🧠 한 문장 정리
모든 파일을
.tsx로 할 수는 있지만,
문법 충돌 + 의미 구분 + 성능 + 관례 때문에.ts를 구분한다.
원하면 다음으로 더 깊게 갈 수 있다:
-
JSX는 정확히 무엇으로 변환되는가?
-
.tsx → JS컴파일 과정 내부 구조 -
React 17 이후 JSX 자동 변환 원리
-
AST 레벨에서 ts vs tsx 차이
-
왜 JSX는 JS 문법이 아닌가?
어디까지 파볼까?