이 장을 읽기 전에
컴파일러와 인터프리터에서 다룬 “오류를 언제 발견하는가”(컴파일 시점 vs 실행 시점) 논의를 이어받는다. 타입 시스템은 그 논의를 “이 값의 타입이 맞는지 언제, 얼마나 엄격하게 검사하는가"로 구체화한다.
두 개의 독립된 축: 검사 시점과 엄격함
타입 시스템을 이야기할 때 정적/동적과 강/약을 흔히 혼동하지만, 이 둘은 서로 다른 질문에 답하는 독립된 축이다. **정적(Static) vs 동적(Dynamic)**은 “타입 오류를 언제 검사하는가"를 묻는다 — 정적 타입은 컴파일러와 인터프리터에서 다룬 컴파일(또는 변환) 시점에, 동적 타입은 실제로 그 코드가 실행되는 순간에 검사한다. **강(Strong) vs 약(Weak)**은 “서로 다른 타입 사이의 암묵적 변환을 얼마나 허용하는가"를 묻는다.
정적 타입: 실행 전에 타입 오류를 잡는다
| |
동적 타입: 실행하는 순간에야 알 수 있다
| |
같은 종류의 실수인데도, TypeScript는 코드를 실행하기 전에 오류를 알려주지만 Python은 그 함수가 실제로 문제의 인자로 호출될 때까지 오류를 발견하지 못한다 — 그 함수를 호출하는 코드 경로가 테스트에서 다뤄지지 않았다면, 이 버그는 운영 환경에서야 발견될 수도 있다.
강 타입 vs 약 타입: 암묵적 변환의 허용 정도
Python은 동적 타입이면서 동시에 비교적 강 타입이다 — "2" + 1처럼 문자열과 정수를 암묵적으로 섞으면 오류를 낸다. 반면 JavaScript는 동적 타입이면서 약 타입에 가깝다 — 서로 다른 타입끼리도 암묵적으로 변환해 연산을 시도한다.
| |
| |
"2" + 1이 언어에 따라 "21"이 되거나, 오류가 나거나, 아예 컴파일이 안 되는 이 차이가 바로 강/약 타입 축이 정적/동적 축과 독립적이라는 것을 보여준다.
네 조합으로 보는 언어 분류
| 정적 타입 | 동적 타입 | |
|---|---|---|
| 강 타입 | Java, Rust, TypeScript(컴파일 후) | Python, Ruby |
| 약 타입 | C(포인터·정수 간 암묵적 변환 허용) | JavaScript, PHP |
이 네 칸 중 한 언어가 정확히 한 칸에만 속한다고 단정하기는 어렵다 — 예를 들어 C는 정적 타입이지만 int와 char 사이의 암묵적 변환을 폭넓게 허용해 약 타입에 가깝게 분류되며, TypeScript는 컴파일 단계에서는 강한 정적 검사를 하지만 결국 약 타입인 JavaScript로 변환돼 실행된다는 점에서 완전히 깔끔한 분류는 아니다.
언제 정적 타입을, 언제 동적 타입을 선택할 것인가
판단 기준은 프로젝트 규모와 협업 방식에 있다. 팀 규모가 크고 여러 사람이 같은 코드베이스를 오래 유지보수한다면, 정적 타입이 함수 시그니처만 보고도 어떤 값이 들어와야 하는지 알 수 있게 해줘 협업 비용을 줄여준다 — 리팩토링 시에도 타입이 맞지 않는 호출부를 컴파일러가 즉시 찾아준다. 반대로 빠른 프로토타이핑이나 스크립트처럼 코드 수명이 짧고 한두 명이 다루는 경우, 동적 타입의 유연성(타입 선언 없이 바로 값을 다룰 수 있음)이 개발 속도에서 유리하다. 강/약 타입 축도 마찬가지다 — JavaScript처럼 약 타입 언어는 암묵적 변환이 편의를 주지만 "2" + 1 같은 예상치 못한 동작의 원인이 되므로, 실무에서는 TypeScript처럼 강 타입에 가까운 정적 검사를 얹어 이 위험을 줄이는 방향으로 수렴하는 경우가 많다.
흔한 오개념
“정적 타입이 항상 더 안전하다” — 정적 타입은 타입 불일치라는 한 종류의 오류를 실행 전에 잡아줄 뿐이다. 로직 오류(잘못된 계산식, 잘못된 조건문)는 타입이 아무리 정적이어도 컴파일러가 잡아주지 못한다. 동적 타입 언어도 소프트웨어 설계 갈래에서 다룬 테스트로 충분히 안전하게 개발할 수 있다 — 정적 타입은 안전성을 얻는 한 가지 방법이지 유일한 방법이 아니다.
“동적 타입 언어는 타입이 아예 없다” — Python의 모든 값은 런타임에 명확한 타입(int, str 등)을 갖는다. “동적"은 “타입이 없다"가 아니라 “타입 검사가 실행 시점에 이뤄진다"는 뜻이다. type(x)로 언제든 실제 타입을 확인할 수 있다는 것이 이를 보여준다.
다른 개념과의 연결
정적 타입 검사가 컴파일러와 인터프리터에서 다룬 컴파일 단계의 일부로 이뤄진다는 점에서 두 챕터는 직접 이어진다. 다음 챕터에서는 프로그래밍 언어론 갈래를 이어, 메모리 관리를 언어 런타임이 자동화하는 가비지 컬렉션을 다룬다.
평가 기준
이 챕터를 읽은 후에는 다음을 할 수 있어야 한다. 정적/동적(검사 시점)과 강/약(암묵적 변환)이 서로 독립된 축임을 설명할 수 있다. 같은 코드가 언어에 따라 오류를 컴파일 시점에 내는지, 실행 시점에 내는지, 아니면 암묵적으로 변환해 실행되는지 구분할 수 있다. “정적 타입이 항상 더 안전하다"는 단순화가 왜 부정확한지 설명할 수 있다.
참고 자료
Pierce, B. C. (2002). Types and Programming Languages, Chapter 1: Introduction. MIT Press.
- TypeScript Handbook: Everyday Types — 정적 타입 검사가 실제로 오류를 잡아내는 예시
- MDN: Equality comparisons and sameness — 동등 비교 시 발생하는 JavaScript의 암묵적 타입 변환 규칙
![Featured image of post [Computer Terms] 타입 시스템: 정적/동적, 강/약 타입](/post/computerterms/type-systems/wordcloud_hu_bcd964bfcb366260.webp)
![[Computer Terms] 웹소켓과 CORS (WebSocket, CORS)](/post/computerterms/websockets-and-cors/wordcloud_hu_961dca3ff221db15.webp)
![[Computer Terms] 컴파일러와 인터프리터 (Compiler, Interpreter)](/post/computerterms/compilers-and-interpreters/wordcloud_hu_7d1b9d2c3757b707.webp)
![[Computer Terms] 타입 시스템: 정적/동적, 강/약 타입](/post/computerterms/type-systems/wordcloud_hu_11e612e21280e94c.webp)
![[Computer Terms] 버전 관리의 내부 구조 (Version Control, Git Internals)](/post/computerterms/version-control-internals/wordcloud_hu_79c3a040c1a84dc.webp)
![[Computer Terms] CI/CD와 테스트 유형 (Continuous Integration, Test Pyramid)](/post/computerterms/ci-cd-and-testing-types/wordcloud_hu_e0af07581ce0ccaf.webp)
![[Computer Terms] 가비지 컬렉션 (Garbage Collection)](/post/computerterms/garbage-collection/wordcloud_hu_22d0f6f1dc2ef90e.webp)
![[Computer Terms] 제네릭과 다형성 (Generics, Polymorphism)](/post/computerterms/generics-and-polymorphism/wordcloud_hu_232c150df4efcb7b.webp)
![[Computer Terms] 피처 플래그 (Feature Flags)](/post/computerterms/feature-flags/wordcloud_hu_28502f74722a16f5.webp)
![[Computer Terms] 디자인 패턴 개요 (Design Patterns)](/post/computerterms/design-patterns-overview/wordcloud_hu_24f0125600c9cc49.webp)