Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

타입으로 견고하게 다형성으로 유연하게 1주차 - 김영명 #445

Merged
merged 1 commit into from
Jan 17, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 88 additions & 0 deletions 2025/RobustWithTypeFlexibleWithPolymorphism/ymkim97/chapter1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# 타입으로 견고하게 다형성으로 유연하게
## 1장 - 타입 검사 흝어보기
---
Java를 주로 사용해오면서 당연하게 느낀 타입 검사에 대해 다시 한번 생각해보는 좋은 기회인 것 같습니다.
때마침 약 3달전 다른 회사의 채용 과정 중 과제로 Javascript(+ Typescript)를 이용한 API 개발을 2주간 진행했던 경험이 있었는데, 이때 제가 느낀점을 어느정도 정리해 볼 수 있었습니다.

저는 최근에 들어서 개발자는 거의 항상 trade-off를 두고 선택의 기로에 서있다는 것을 꽤나 느끼고 있습니다.
비록 현재 주언어가 Java인 것으로 인해 정적 타입 언어에 대한 강한 긍정적인 입장을 가지고 있으나, 이것이 늘 답은 아닐 수 있다는 마음가짐으로 책을 읽어야겠습니다.

책이 엄청 깊게 파기보다 아직은 생각보다 쉽게, 일정한 깊이까지만 가는 느낌이 들었습니다.
그렇다고 그냥 쭉 읽기보다는, 이번 기회에 주로 사용했던 언어가 어떤 철학과 목적을 가지고 만들어졌는지 정리하면서 읽어야겠다는 생각이 들었습니다.

### 타입 검사의 정의와 필요성
타입은 프로그램에 존재하는 값들을 그 능력에 따라 분류한 것이다.
값은 변수에 저장거나, 함수에서 반환 될 수 있는 1, true, “hello” 등을 말한다.
정수 타입은 사칙 연산이 적용 가능하지만 문자열을 그러지 못하는 것 처럼 서로의 능력이 제각각이라는 것을 알 수 있고 언어마다 각 타입의 능력과 범위가 조금씩 다를 수 있다.
어떤 타입의 값을 그 타입이 갖고 있지 않은 능력이 사용되도록 프로그램을 작성하면 실행에 문제가 생긴다.
이때를 `타입 오류` 라고 말하며, 버그의 흔한 원인 중 하나가 된다.
버그는 프로그램이 개발자의 의도와는 다르게 작동하는 것이고 항상 발생한다.
따라서 타입 오류가 발생하는지 잘 확인하는 것이 중요하다.
이를 도와주는 존재가 바로 타입 검사기(type checker)인데, ***이상적인 타입 검사기***와 ***현실적인 타입 검사기***로 나누어진다.
**이상적인 타입 검사기**는 주어진 프로그램이 타입 오류를 절대 일으키지 않는다면 “통과”를 출력하고, 타입 오류를 일으키는 경우의 수가 단 하나라도 있다면 “거부”를 출력한다.
하지만 1930년대에 Alan Turing에 의해 이런 타입 검사기는 절대로 생길 수 없다는 사실이 증명 되었다 (검색 결과 1936년 논문 *On Computable Numbers, with an Application to the Entscheidungsproblem* 에서 나온 *멈춤 문제 (Halting Problem)* 를 통해 증명 되었을지 않았을까 생각합니다).
**현실적인 타입 검사기**는 오류를 일으키는 경우의 수가 단 하나라도 있다면 “거부”를 출력하는 것에서 이상적인 타입 검사기와 똑같지만, 실제로는 타입 오류가 없는데도 “거부”라고 출력하는 경우가 있다.
실제로 오류가 없지만 타입 검사기로 인해 코드를 수정해야 하는 상황이 발생할 수 있지만, 타입 검사기가 알려주는 “통과”라는 결과로 실행 중에 타입 오류가 절대 일어나지 않는다는 보장과 같이 가질 수 있는 이점이 너무 크기 때문에 어느 정도의 타협은 불가피하다.

### 정적 타입 언어
타입 검사기는 따로 만들어지는 것이 아닌, 언어와 함께 제작된다.
따라서 개발자는 이미 잘 만들어진 타입 검사기를 사용하여 자신의 프로그램이 타입 오류를 일으키는지 확인만 하면 된다.
타입 검사기를 제공하는 언어는 Java, C, C++, C#, Go, Rust, Kotlin 등이 있으며 이러한 언어들을 **정적 타입 언어**라고 한다.
“정적”은 "프로그램을 실행하기 전”을 뜻하며, 정적 타입 언어는 프로그램을 실행하기 전에 타입이 올바르게 사용되었는지 확인하는 언어를 뜻한다.
이와 반대로 타입 검사기가 제공되지 않는 Javascript, Python, Ruby, LISP 등 **동적 타입 언어**가 있다.
동적 타입 언어는 프로그램 실행 중에야 타입이 잘못 사용되었다는 것을 파악할 수 있다.

### 타입 검사의 원리
정적 타입 언어를 잘 사용하기 위해서는 타입 검사기의 작동 원리와 오류 메시지를 잘 이해해야 한다.
타입 검사기는 더 이상 쪼갤 수 없는 기본 부품에서 큰 부품으로 가면서 프로그램을 검사하고 이런 작동 원리를 보다보면 ***분할 정복 알고리즘***과 아주 흡사하다는 생각이 났다.
*타입 검사기는 아무리 간단한 부품이라도 계산 결과를 절대로 알아내려 하지 않고 오직 타입만 신경쓴다.* 계산 시간만큼 타입 검사가 이루어질 수 있기 때문이다.
우리가 당연하게도 변수나 함수 앞에 써주었던 타입은 결국 검사기에게 정보를 제공하기 위해서고, 이를 토대로 검사가 이루어진다.

### 타입 검사 결과의 활용
정적 타입 언어의 장점:
* 타입 오류를 빠트리지 않고 모두 찾을 수 있다
* 오류 메시지로 인해 쉽게 수정이 가능하다
* Code editor의 기능을 보조하여 개발자의 생산성을 높인다
* 프로그램의 실행 시간이 짧다 -> 타입 검사에서 얻은 정보를 바탕으로 실행 중에 할 일을 줄일 수 있기 때문

### 타입 추론
물론 정적 타입 언어에 단점도 존재한다.
그중 하나는 타입 표시 - 개발자는 항상 변수, 함수의 타입의 표시를 제공해야한다.
매번 표시하기 번거롭게 느껴질 수 있으며, 이를 타입 검사기가 ***타입 추론***을 통해 수고를 덜어준다.
OCaml, Pascal은 타입 추론을 특히 극한까지 발전시킨 언어다.
***타입 추론은 생략된 타입 표시를 복구할 뿐, 타입 검사를 생략하는 것이 아니다.***
따라서 타입 추론을 이용한다고 하더라도 검사를 실패한 프로그램이 작동하지 않는다.

> *“정적 타입 언어에서 타입 표시는 양날의 검이다. 타입 표시를 모든 곳에 붙였다가는 코드가 너무 장황해지기 십상이다. 그렇다고 타입 추론이 허용하는 범위 안에서 무작정 다 생략하는게 꼭 좋지만도 않다.*
> *……*
> *그러니 항상 과유불급의 자세로 타입 표시를 적절히 활용하는 지혜가 필요하다.”*

과유불급의 자세는 여기서 뿐만 아니라, 개발자로서 마주한 문제들을 해결 해 나아갈때 항상 가져야하는 자세라고 생각한다.

### 더 세밀한 타입
다른 단점으로는 타입 검사기는 타입 오류로 인한 버그만 찾을 수 있다는 것이다.
즉, 0으로 나누기, Null에 대한 접근 등 오류는 검출 할 수 없는 것이다.
이런 오류들을 실행하기 전까지 자동으로는 찾을 수 없다.
그럼에도 또다시 타입 검사가 해결책이 될 수 있다.
값을 더 세밀하게 분류하면 된다. -> Java에서는 null이 String에 속함, Kotlin은 아님.
그렇다고 모든게 편해지는 것은 아니고 Kotlin은 오히려 null을 사용하기 불편해졌다.
이런 trade-off가 존재하고 개발자의 상황에 맞는 언어를 고르는 것이 중요하다.

### 다형성
타입 검사기가 ‘거부’라고 틀리게 말하는 단점이 있고, 이는 절대로 완벽하게 해결이 불가능하다.
대신 이렇게 잘못 말하는 경우를 줄여 단점을 최대한 줄일 수 있다.

* 타입 검사의 틀 안에서 개발자를 돕는 ‘안전한’ 기능
* 타임 검사를 무력화하는 ‘위험한’ 기능으로, 타입 검사의 원칙을 아예 파괴

‘위험한’ 기능의 경우 개발자의 판단이 잘못되면 참사가 발생할 수 있지만, 안전한 기능으로도 타입 검사기의 ‘거부’를 고치지 못하는 경우 고려해 볼 수는 있다.
다형성은 타입 검사기의 오판을 줄이는 안전한 기능의 대부분을 차지하는 개념이다.
다형성은 다음과 같이 크게 나눌 수 있다.

* 서브타입에 의한 다형성
* 매개변수에 의한 다형성
* 오버로딩에 의한 다형성

## [논의 내용]
* 현실적인 타입 검사기는 타입 오류가 없음에도 오류 및 거부를 출력하는 경우가 있다는데, 실제로 이것 때문에 코드를 수정해야 했던 경험을 겪어보신 분이 계신가요?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

책에 설명한대로라면 null 값 참조가 가장 흔한 케이스일 것 같습니다.
컴파일 할 때는 문제 없지만
실제 런타임에서 null 이면 반드시 예외를 던지니까요.

이런 경험을 몇 번 하다 보면 항상 다음과 같은 코드가 장황하게 작성된다고 봅니다.

if (service == null)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

생각해보니 그렇네요! 어제만해도 이것 때문에 보다 더 깔끔한 방법을 찾고 싶었고, 그나마 생각해서 적용한게 'Objects.requireNonNull()'입니다ㅎㅎ

* 정적 타입 언어인 Java를 주로 쓰다보니, 이전에 Javascript로 한 회사의 과제를 진행하면서 타입 검사가 없는 것에 큰 불편함을 느꼈던 적이 있습니다. Typescript를 적용하면서 어느 정도 보완 되었지만 그래도 Java보다는 한참 약하다고 생각했습니다. 저는 정적 타입 검사기로 인해 타입 오류에 대한 버그가 온전히 사라진다는 보장을 가짐으로써, 코드 작성 중 로직에 의해 발생하는 버그에 더 신경 쓸 수 있다고 생각합니다. 그럼에도 전세계적으로 동적 타입 언어가 강세인 이유가 무엇일까요? 또한, 많은 스타트업의 초기 언어로 사용 되는데, 생산성 외에 다른 이유도 있는지 궁금합니다.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저도 생산성이라고 생각해 보지만
현실에서는 어떤 테크 리더나 팀장이 선호하는 방향으로 가는 걸 많이 봤습니다.
제가 실제로 그러기도 했고요.

처음부터 정적 타입 언어를 썼던 사람이라면 동적 타입 언어를 선호하지 않을 것이고
반대도 마찬가지라고 봅니다.

그렇다면, 이건 언어의 특징이나 장단점으로 따져 볼 것이 아니라
회사와 조직이 가져가려고 하는 비즈니스에 맞게 기술이 정해져야 하고 그게 타당성이 있어야 한다고 보는게 제 의견입니다.

여러 책을 섭렵하다 보니 이런 프로세스를 ADR이라고 부르는 것 같습니다.
물론 우리나라에서 ADR를 겪어보지도 해보지도 못했지만요.

https://adr.github.io/

ADR은 Architecture Decision Record의 약자로
아키텍처 설계시 결정 이유와 대안에 대한 기록 문서를 뜻합니다.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저는 회사에서 백엔드 개발에 파이썬을 사용하고 있기 때문에 백엔드 개발을 위한 파이썬에 한정지어서 말씀드리자면, 현 시점에 엔터프라이즈 환경에서는 사실상 파이썬을 동적언어가 아닌, 정적언어 처럼 사용하고 있는 것(type hint와 mypy라는 타입체커를 같이 사용함)이 현실이고, 동적언어의 장점 때문에 사용하는 경우는 별로 없는 것 같습니다 말씀주신대로 타입을 쓰고 안쓰고의 유지보수성의 차이가 너무 심하기 때문 입니다.

제가 생각하기에 파이썬이 아직도 많은 분야에서 사용되고 인기가 많은 이유는, AI 산업 등이 발전하고, 관련된 라이브러리와 프레임워크 생태계가 커진것도 주된 이유겠지만, 그것과 더불어서 타입을 유연하게 사용할 수 있게 허용하고 있는 것(type hint의 도입과 mypy를 통한 타입체크, pydantic를 통한 타입을 기준으로 데이터 유효성 검사 까지..)이 기존부터 파이썬을 쓰고 있던 사람들이 엔터프라이즈 환경에서도 계속 사용하게 만드는 이유이지 않을까 생각 합니다

스타트업 초기언어로 동적언어라고 일컬어지는 파이썬과 자바스크립트 등이 많이 사용되는 이유는 제 생각엔, 일단은 파이썬이나 자바스크립트로 이뤄진 웹 프레임워크들이 개발하기 쉽다는 편견(?)이 업계에 전반적으로 퍼져있기 때문이라고 생각합니다(저는 이게 큰 착각이라고 생각합니다) 개인적으로 생각하기로는 스타트업 초기언어는 뭘로 개발하든 상관없다고 생각합니다(그냥 백엔드개발자 본인이 젤 익숙하고 잘하는거 하면 머든 상관없다고 생각)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

현실에서는 어떤 테크 리더나 팀장이 선호하는 방향으로 가는 걸 많이 봤습니다.

저도 많이 봤습니다.. 팀장의 선호는 무적논리죠 ㅎㅎ

Copy link
Collaborator

@TaeHyoungKwon TaeHyoungKwon Jan 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

그렇다면, 이건 언어의 특징이나 장단점으로 따져 볼 것이 아니라
회사와 조직이 가져가려고 하는 비즈니스에 맞게 기술이 정해져야 하고 그게 타당성이 있어야 한다고 보는게 제 의견입니다.

저희 회사의 경우는 어떤 아키텍쳐적으로 큰 변경하는 프로젝트가 있을 때, 말씀주신 ADR 문서를 필수로 작성�하고 발표하고 리뷰를 받아야 합니다

제가 최근에 작성한 문서 목차를 첨부 해봅니다(더 상세한 내용은 캡쳐해서 첨부하긴 힘들고, 모임날에 공유 원하신다면 화면공유로 간단히 보여드리겠습니다)

스크린샷 2025-01-07 오전 2 46 47

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

확실히 현실적으로는 리더의 방향을 다소 많이 따라가는 것 같습니다.
또한 많이들 말하는 _개발하기 쉽고 빠르다_라는 말이 잘 와닿지 않았는데, 말씀하신 것 처럼 편견이라는 것을 보니 궁금증이 해소 되었습니다ㅎㅎ 물론 이건 개개인이 어떤 타입으로 주로 개발 해왔느냐에 따라 다르겠지만요!

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

정적타입 언어만 사용해보다가 동적타입언어를 접할 기회가 있었는데
로직을 짜는 쪽에 포커스를 맞출 수 있어서 그러지 않을까 합니다.
다른곳에 신경쓸 필요가 없다는 느낌을 강하게 받았습니다.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

동의합니다. 다른 이유로는 생산성과 생태계를 꼽을 수 있겠네요. 생산성을 다시 강조하는 이유는, 빠르게 가치 있는 코드를 작성하는 능력이 매우 중요하기 때문입니다. 생태계를 두 번째로 꼽은 이유는, 생산성이 높아지면 작은 코드를 작성하며 쉽게 학습할 수 있고, 많은 사람들이 사용하게 되면서 관련 라이브러리와 프레임워크가 함께 성장하는 선순환 구조가 만들어지기 때문입니다.

Loading