2026년 타입스크립트를 사용하는 원격 기업 427개

발행: (2026년 5월 28일 PM 08:46 GMT+9)
6 분 소요
원문: Dev.to

출처: Dev.to

2026년 TypeScript을 사용하는 427개의 원격 기업 표지 이미지

요즘 프론트엔드 개발자는 이력서에 TypeScript를 적는다. 풀스택 엔지니어, JavaScript 팀의 백엔드 엔지니어, 대부분의 React Native 개발자, 대부분의 Next.js 개발자, 그리고 “당연히 써야 한다”는 이유로 거의 쓰지 않지만 패턴 매칭해서 적는 개발자들까지도 마찬가지다.

그 신호는 무력화되었다. “TypeScript 개발자”는 채용 담당자에게 “키보드를 사용한다” 정도의 정보만 제공한다.

TypeScript은 보편적인 수식어다. 바로 그게 함정이다.

핵심 수치

  • 427개 기업이 Remoet에서 프로덕션에 TypeScript를 사용하고 있음
  • 26,884개의 채용 공고가 해당 기업에 존재 – 플랫폼 내 다른 어떤 언어 신호보다 많음
  • **427개 중 346개(81 %)**가 TS와 React를 함께 사용
  • **427개 중 346개(81 %)**가 TS와 Python을 함께 사용 – 동일한 수치, 놀라움
  • **427개 중 297개(70 %)**가 Kubernetes와 함께 운영
  • **427개 중 255개(60 %)**가 PostgreSQL 위에서 실행
  • **427개 중 236개(55 %)**가 Go와 함께 사용

대다수 TS 개발자가 “TypeScript는 어디서든 JavaScript다”라고 가정한다. 데이터는 그렇지 않다. Remoet에 있는 TypeScript 기업의 절반은 전혀 다른 스택 위에 프론트엔드 레이어로 TS를 두고 있다.


실제로 겨냥할 수 있는 기업은 30 ~ 60곳

그 하위 집합 안에서는 당신이 특정 후보자다. 밖에서는 이력서가 블랙홀에 빠진다. 선택할 수 있는 여섯 개 클러스터:

TS + React + Next.js

Vercel, Resend, Linear, PostHog, 1Password, Toptal, Bloomreach.
지난 6개월 동안 App Router 작업을 해왔다면 여기서 제자리다.

TS + Node.js + Postgres

Linear, Supabase, Deel, HE:labs, Stripe.
의도적으로 지루한 스택. 눈을 감고 코딩할 수 있다면 첫 PR은 1주 차에 배포된다.

TS + Python (놀라운 그룹)

Anthropic, OpenAI, Cursor, PostHog, MapBox, Khan Academy, Attio, Scale AI.
346개 기업 – React와 동일한 수치. 파이썬을 쓰라고 채용하는 것이 아니라, 그들의 TS 프론트엔드가 옆에 있는 FastAPI 서비스와 통신하기 때문이다. 파이썬을 읽을 수 있는 TS 개발자는 이 리스트에 있는 모든 기업에서 TS‑전용 개발자보다 뛰어나다.

TS + Go + Kubernetes

Cloudflare, 1Password, Supabase, Cursor, Grafana Labs, Vercel.
채용 공고에 React 라이브러리 3개와 인프라 용어 12개가 적혀 있다면, 이는 신호다. 그 신호에 맞춰라.

TS + Rust

Anthropic, Cloudflare, Supabase, 1Password, MapBox, Cursor.
95개 기업. 백엔드에 Rust, UI에 TS. 이 조합은 드물기 때문에 리크루터가 실제로 지원서를 읽는다. 이 특권을 잘 활용하라.

TS + Vue 또는 Angular

Vue 74개, Angular 63개. Supabase, GitLab, Wolt, NearForm, Bloomreach, Databricks.
React 메인스트림보다 역할당 경쟁이 훨씬 적다. 대부분의 이력서에 없던 프레임워크에 집중하라.


에이전트 없이는 데이터가 무의미

이 글을 읽는 모든 개발자는 기업 8곳을 지나치면 포기한다. 이미 비용을 내고 있는 Claude, Cursor 등을 Remoet에 연결하고, 자신의 스택 형태를 정확히 입력한 뒤 해당 슬라이스를 요청하라. 채용 페이지를 일일이 뒤지는 고통을 피할 수 있다.

  1. 관심 있는 기업에 별표(★)를 표시한다.
  2. 해당 클러스터 내 새로운 포지션을 매주 이메일로 받아본다.

무료 플랜. 끝.


TypeScript은 바닥이다

보편적인 채택이 바로 TS를 신호로서 무력화시켰다. 모든 지원자가 TS를 가지고 있다는 사실이 바로 당신이 그것을 적어도 아무도 움직이지 않는 이유다.

스택에서 흥미로운 부분은 쉼표 뒤에 오는 부분이다: Python, Go, Rust, Vue 등.

그것을 이름 붙여라. 이력서 첫 줄, LinkedIn 헤드라인, 그리고 리크루터에게 처음 말할 때에 넣어라.

“TypeScript plus Go plus Kubernetes”는 정보를 담고 있다.
“TypeScript developer”는 아무 정보도 담고 있지 않다.

당신의 이력서를 보는 기업은 이미 당신이 TS를 쓴다는 것을 안다. 그 외에 무엇을 읽을 수 있는지 보여줘라.

0 조회
Back to Blog

관련 글

더 보기 »

엔티티 생성과 업데이트에 단일 Builder 패턴을 사용하는 것이 좋은 관행일까?

문제 Node.js TypeScript 백엔드에서 Builder 패턴을 구현하여 Permission 객체를 인스턴스화한 뒤 이를 repository 레이어에 전달하려고 합니다. 저는 Permission 객체가 여러 선택적 속성을 가질 수 있기 때문에 Builder를 사용해 가독성을 높이고 싶었습니다. 시도한 내용 ```typescript class PermissionBuilder { private permission: Permission; constructor() { this.permission = new Permission(); } setId(id: string): this { this.permission.id = id; return this; } setUserId(userId: string): this { this.permission.userId = userId; return this; } // … 기타 setter 메서드들 … build(): Permission { return this.permission; } } ``` 위와 같이 Builder를 만든 뒤, 서비스 레이어에서 다음과 같이 사용했습니다. ```typescript const permission = new PermissionBuilder() .setId('123') .setUserId('456') .setRead(true) .setWrite(false) .build(); await permissionRepository.save(permission); ``` 하지만 테스트를 실행하면 `permissionRepository.save`가 `undefined`를 반환하고, 저장된 레코드가 데이터베이스에 나타나지 않습니다. 또한, `PermissionBuilder`를 사용하지 않고 직접 `new Permission()`으로 객체를 만들면 정상적으로 저장됩니다. 원인 분석 1. **Builder가 실제로 새로운 인스턴스를 반환하지 않음** `build()` 메서드가 현재 내부에 보관하고 있는 `this.permission` 객체를 그대로 반환하고 있습니다. 이 객체는 `new Permission()`으로 만든 초기 객체이며, 이후 setter 메서드들이 원본 객체의 속성을 직접 수정합니다. TypeScript에서는 객체가 **참조에 의해 전달**되기 때문에, `PermissionBuilder` 인스턴스가 재사용될 경우 이전에 설정한 값이 남아 있을 수 있습니다. 2. **불변성을 보장하지 않음** Builder 패턴의 일반적인 구현은 `build()` 단계에서 **새로운 복사본**을 만들어 반환합니다. 이렇게 하면 Builder 자체는 재사용 가능하고, 이미 `build()`된 객체는 더 이상 변경되지 않게 됩니다. 현재 구현은 동일한 인스턴스를 계속 반환하므로, 테스트 환경에서 여러 번 `build()`를 호출하면 이전 테스트의 상태가 섞일 위험이 있습니다. 3. **Repository 레이어가 기대하는 형태와 불일치** `permissionRepository.save`는 **완전한 엔티티**(예: TypeORM 엔티티) 인스턴스를 기대합니다. Builder가 반환하는 객체가 엔티티 메타데이터(예: `@PrimaryGeneratedColumn`, `@Column` 데코레이터)와 연결되지 않은 **plain 객체**라면, ORM이 이를 무시하거나 `undefined`를 반환할 수 있습니다. 해결 방안 ### 1. `build()`에서 새로운 객체를 반환하도록 수정 ```typescript class PermissionBuilder { private readonly data: Partial<Permission> = {}; setId(id: string): this { this.data.id = id; return this; } setUserId(userId: string): this { this.data.userId = userId; return this; } // … 기타 setter 메서드들 … build(): Permission { // Permission 엔티티의 생성자를 사용하거나 Object.assign 로 복사 return Object.assign(new Permission(), this.data); } } ``` - `Partial<Permission>`을 사용해 아직 완전하지 않은 상태를 저장하고, `build()` 시점에 **새로운 Permission 인스턴스**를 만든 뒤 속성을 복사합니다. - 이렇게 하면 Builder 자체는 상태를 유지하지만, 반환된 객체는 독립적이며 ORM이 정상적으로 인식합니다. ### 2. Builder를 **불변**하게 설계 ```typescript class PermissionBuilder { private readonly data: Readonly<Partial<Permission>>; private constructor(data: Partial<Permission> = {}) { this.data = data; } static create(): PermissionBuilder { return new PermissionBuilder(); } setId(id: string): PermissionBuilder { return new PermissionBuilder({ ...this.data, id }); } setUserId(userId: string): PermissionBuilder { return new PermissionBuilder({ ...this.data, userId }); } // … 기타 setter 메서드들 … build(): Permission { return Object.assign(new Permission(), this.data); } } ``` - 각 setter가 새로운 Builder 인스턴스를 반환하므로 **동시성 문제**와 **테스트 간 상태 누수**를 방지합니다. ### 3. Repository에 전달하기 전에 엔티티 인스턴스인지 확인 ```typescript const permission = PermissionBuilder.create() .setId('123') .setUserId('456') .setRead(true) .setWrite(false) .build(); if (!(permission instanceof Permission)) { throw new Error('Built object is not a Permission entity'); } await permissionRepository.save(permission); ``` - 타입 가드로 잘못된 객체가 전달되는 것을 사전에 차단합니다. ### 4. 테스트 환경 정리 - 각 테스트 케이스 시작 전에 **Builder 인스턴스를 새로 생성**하거나 `PermissionBuilder.create()`를 호출합니다. - 테스트 후에는 데이터베이스를 **트랜잭션 롤백**하거나 `afterEach` 훅에서 `permissionRepository.clear()`를 호출해 상태를 초기화합니다. 예시 코드 (전체 흐름) ```typescript // permission.builder.ts export class PermissionBuilder { private readonly data: Partial<Permission> = {}; setId(id: string): this { this.data.id = id; return this; } setUserId(userId: string): this { this.data.userId = userId; return this; } setRead(read: boolean): this { this.data.read = read; return this; } setWrite(write: boolean): this { this.data.write = write; return this; } // … 추가 setter … build(): Permission { return Object.assign(new Permission(), this.data); } } // permission.service.ts async function createPermission(dto: CreatePermissionDto) { const permission = new PermissionBuilder() .setId(dto.id) .setUserId(dto.userId) .setRead(dto.read) .setWrite(dto.write) .build(); return await permissionRepository.save(permission); } ``` 결론 - 현재 Builder 구현은 **같은 인스턴스를 재사용**하고 있어 ORM이 기대하는 완전한 엔티티 객체를 제공하지 못합니다. - `build()` 단계에서 **새로운 Permission 인스턴스를 반환**하도록 수정하고, 가능하면 **불변 Builder** 패턴을 적용하면 테스트 간 상태 오염을 방지하면서도 가독성을 유지할 수 있습니다. - 마지막으로, Repository에 전달하기 전에 반환 객체가 실제 엔티티인지 검증하고, 테스트 환경을 깨끗하게 정리하면 `undefined` 반환 문제를 해결할 수 있습니다.

useOptimistic가 실제로 절약해 주는 것

낙관적 UI의 문제점 체크박스 토글은 즉각적인 반응을 보여야 합니다. 토글 상태를 서버에 영구히 저장해야 할 경우 선택할 수 있는 두 가지 방법이 있습니다: 1. 응답을 기다린다.