우아한테크코스 웹 백엔드 4기
레벨 1 필독 도서 중 하나인 코딩을 지탱하는 기술을 읽었습니다.
감상
전반적인 감상
다른 필독서와 달리 이 책은 발췌독이 아닌 전체를 읽은 뒤 내용을 정리할 것을 권장하고 있어요.
양이 많지 않고, 비교적 가볍게 읽을 수 있는 내용들이었어요.
책 자체에서 챕터마다 적절한 분할과 요약을 해주셔서 읽고 정리하는 데 아주 수월했습니다.
프로그래밍 언어가 어떻게 발전해와서 현재의 모습을 갖추게 되었는지 이해하는 데 도움이 되었습니다.
또한 제 주력 언어인 Java가 아닌 다른 언어들은 왜 다른 형태를 갖추게 되었는지 등도 조금 더 알 수 있었어요.
이 책의 제목 대로 코딩을 지탱하는 기술을 전달해주는 책이었습니다.
프로그래밍 언어의 역사, 언어 간의 발전 과정에서 목표한 것의 차이와 그 이유를 이야기해주고 있어요.
특정 언어에 국한되는 지식이 아닌 맥락을 이해함으로써
기존 사용 언어에 대해서는 목적과 의도를 더 깊이 이해할 수 있게 되고
새로운 언어를 학습할 때에는 더 빠르게 적응할 수 있게 하는 역량을 길러주는 것 같습니다.
문법의 탄생과 발전
사람이 읽고 쓰기 쉬운 구조화 프로그래밍의 탄생과 goto가 if, while 등으로 발전하는 과정은
상당히 흥미로웠습니다.
자바의 대격변을 보통 JDK 1.8로 이야기하는데, 이는 2014년 릴리즈로 불과 8년 전입니다.
현재도 이렇게 많은 발전과 변화가 일어나고 있는데, 이러한 일들이 당시의 노력이 없었다면
불가능했겠구나 하는 생각이 듭니다.
읽고 쓰기 쉬운 구조화 프로그래밍을 위한 노력은 지금도 여전한 것 같습니다.
JDK 1.8에서 추가된 stream도 복잡한 코딩 없이 병렬성을 얻을 수 있게 해준다는 점에서
읽고 쓰기 쉬운 구조화 프로그래밍의 한 부분으로 볼 수 있지 않나 싶은 생각이 듭니다.
부동 소수점 연산 테스트
고정 소수점과 부동 소수점의 장단점 그리고 0.3을 10번 더한 값이 왜 3이 아닌지 등에 대해
글로 읽은 뒤 테스트 코드를 작성해서 직접 실험을 해봤어요.
0.3을 2진수로 표현하면 무한소수가 되어버려서
10번 연산을 해도 3이 아닌 3의 근사치가 나온다 라는 게 이해는 됐지만 막상 실제로 해보니 신기했습니다.
은행과 같이 소수점 연산이 중요한 곳에서는 고정 소수점 방식이나 다른 방식을 사용한다고 하는데,
이러한 이해가 있으면 비즈니스 도메인이 변경되어도 적응하는데 수월하겠구나 싶은 생각이 듭니다.
이하 내용은 각 장을 제 언어로 요약한 것입니다!
1장. 효율적으로 언어 배우기 - 언어간 비교, 언어 탄생 이유
- 비교 : C에서는 0이 거짓, Ruby에서는 0이 참, Java 에서 0은 boolean과 비교 불가.
- 역사 : 어떤 문제 해결을 위해 만들어졌을까?, 미래는 아무도 모름, 언어에 의존적이지 않은 보편 지식을 갖추라
2장. 프로그래밍 언어를 조감하다 - 언어의 목적
- 1946 ENIAC, 17,468 개 진공관과 물리적 케이블 연결을 사용
- 1949 EDSAC, 물리적 케이블 이동이 아닌, 종이에 5개 구멍을 뚫어 표현
- 1954 FORTRAN, 현재의 프로그래밍 언어와 유사한 형태
- 언어 마다 특화된 부분이 있다. 필요에 맞는 언어를 선택하라.
(C++의 가독성과 Python의 성능을 비교하는 것은 유익하지 않다.)
3장. 문법의 탄생 - [1 2 +] vs [1 + 2]
- 문법 : 프로그래밍 언어 설계자가 이렇게 쓰면 이런 의미로 해석된다라고 정한 규칙.
- 절대 불변이 아니라 정하기 나름인 것이다. (연산자 우선순위 사례)
- 작성하기 쉬운 과, 모순 없이 해석 가능한 을 동시에 만족하기 위한 방향으로 발전되어 왔다.
- 그러나 그 과정에서 피할 수 없는, 이해하기 어려운 작성법이 생기기도 했다.
4장. 처리 흐름 제어 - goto → if, while → for → foreach
- 1960년대 후반, 구조화 프로그래밍 : 사람이 읽고 쓰기 쉬운 규칙을 만들자
- C에서 if가 도입되기 전엔 조건에 만족하면 특정 라인으로 점프하라는 goto로 구현되어 있었다.
- while과 break도 goto로 구현이 가능하지만, 읽고 쓰기 쉽게 하기 위해 도입된 것이다.
- for문도 while로 구현이 가능하지만, 루프의 의도를 한 곳에서 쉽게 이해하게 하기 위해 도입되었다.
5장. 함수 - 재사용성, 재귀 호출
- 함수 : 코드의 일부를 한 덩어리로 잘라내어 그것에 이름을 붙이는 기능
- 함수 없어도 구현할 수 있다. 그러나 커질수록 파악하기 어려워진다.
- 한 번 정의해두고 여러번 사용하는 재사용성이 필요했다.
- 함수의 탄생으로 인해 어떤 일을 하는지 파악하기 쉬워졌고, 재귀 호출이라는 기술이 탄생했다.
6장. (1) 에러 처리 - goto → interrupt → 묶어주기 → try/catch → finally
- C언어는 호출처가 반환값을 확인해서 실패 시 goto로 점프해서 에러 시 처리 코드를 실행하는 방식이다.
- 개발자가 예외 가능성 있는 코드라는 것을 놓칠 가능성이 생기고, 실제 처리와 에러 처리가 분리된다.
- 1950, UNIVAC I, 에러 발생 시 어디로 이동하라고 미리 등록해두기. 인터럽트.
- 1964, PL/I, 개발자 정의 실패 추가, 실패 발생 시키기가 도입되었다.
- 1977, CLU, 실패시 처리 코드를 미리 등록하지 않고, 사용시점에 묶어주기 도입.
- 1983, C++, try/catch/throw 도입, finally 없이 소멸자를 작성하고 이를 호출하도록 함
- 1993, Windows 3.1, finally 도입. 짝이 되는 처리를 반드시 실행하기 위함. (ex 자원 점유 해제 등)
- 2001, D, C++개선 목적, scope 개념 도입, scope 벗어날 때 수행할 내용을 스코프 안에서 미리 등록 가능
6장. (2) 언제 에러를 던질까 - 반환값 사용 vs 예외 던지기
- 배열 범위 밖의 것을 꺼내려 하면 - Python, Java : 예외 던지기 VS Ruby, JavaScript : nil, undefined 반환
- fast fail : 배열 밖을 꺼내려 하면 이미 이상 상태 → 예외 던지기 → 실패 놓치지 않기 + 빨리 문제 발견
- 예외 전파 방식을 사용할 경우, 그 함수를 호출하는 입장에선 예외가 전파될 줄 인지하지 못할 수가 있다.
- 이에 대한 대응으로, Java에서는 thorws SomeException 식으로 예외 전파 가능성을 명시하게 했다.
- 그러나 작성 시 불편함으로 인해 이 검사 예외 방식은 다른 언어에서는 많이 채용되지 않았다.
- 결론은, 둘 다 장단점이 있다는 것이다.
7장. 이름과 스코프 - 이름 → 동적 스코프 → 정적 스코프
- 이름이 없던 시절엔, 메모리에 번호를 부여했다. “컴퓨터야, 3,456번에 있는 값을 1 증가시켜!”
- 이름이 처음 생겼을 땐, 지역 스코프가 없었다. 모든 것이 전역 변수가 되어 이름이 충돌되기 쉬웠다.
- 이름의 충돌을 막기 위해, 이름의 유효범위를 지정하기 위해, 스코프가 탄생했다.
- 동적 스코프는, 전역 값이 함수 내에서 바뀔 경우, 그 함수 내에서 다른 함수 호출 시 바뀐 전역값을 바라본다.
- 정적 스코프는, 함수 내 변경이 함수 밖에 영향을 미치지 않는다.
- 함수 내에서 함수 밖 값을 변경하고 싶은 경우에 대한 대응으로 Python 에선 nonlocal을 만들었다.
- 이름 없던 시절 → 이름 생김 → 동적 스코프 → 정적 스코프 → 정적 스코프 + 스코프 밖 변수 변경
8장. 형 - 정수, 고정, 부동소수점 → 자료형 → 사용자 정의형 → 클래스 → OOP
- 가장 효율적으로 정수를 표현하기 위해 2진수가 채용되었다. 그리고 소수점 표현이 필요해졌다.
- 고정 소수점 : 소수점을 어디에 붙일지 미리 정한다.
- 어디까지 소수부로 정했는지 기억해야 한다.
- 부동 소수점과 달리 정확하게 표현할 수 있다. (은행 등에선 고정 소수점이나 10진수 계산법을 사용한다)
- 부동 소수점 : 어디부터 소수부인지를 값에 포함시킨다.
- 소수는 IEEE 754를 대체로 따른다. 부호, 지수부, 가수부로 구성된다.
- 완전한 방법은 아니다.
- 0.3을 2진수로 표현하면 무한 소수가 되어, 0.3을 10번 더해도 2.999999가 되어버린다.
- 그 결과 Math.floor 에 0.3을 10번 더한 값을 전달하면, 2가 반환된다!
- 2진수로 표현된 정수와 소수를 더할 경우, 의도하지 않은 값이 나온다.
- 어떤 이걸 사람이 아닌 컴퓨터가 기억하게 하기 위해 자료형이 탄생했다!
- 값의 종류를 저장하기 위해 시작된 형이, 사용자 정의형, 클래스, 객체지향으로 발전했다.
- public/private → interface → Generics → 형 추론
9장. 컨테이너와 문자열
- Array, LinkedList, HashMap, Tree 를 알아보며 여러 종류의 컨테이너가 필요하게 된 이유를 살폈다.
- 문자 표현 방식 : 부호화 VS 표준화
- 모스부호 → 보코드 (5비트로 숫자, 영문을 모두 표현하기 위해 시프트의 도입) → 1963 ASCII, EBCDIC → 1993 Unicode
- 문자열 : 문자가 정렬해 있는 것
10. 병행 처리
- 동시에 여러 프로세스를 실행하는 비결은, 사람이 눈치채지 못할 정도로 잘개 쪼개 스위칭하는 것이다.
- 협력적 멀티테스킹 : 처리가 일단락 되는 지점에 자발적으로 스위칭. 신뢰 기반.
- 선점적 멀티테스킹 : 일정 시간마다 스위칭.
- 경합 상태 (스레드 세이프하지 않은 상태) : 변수 공유 and 변수 변경 and 처리 완료 전 개입 셋 다 만족
- 공유 막기 : OS레벨에서 프로세스별 메모리 할당 영역을 나눠 공유하지 않기, 메시징을 통한 비동기 처리
- 변경을 막기 : Mark Grand의 Immutable 패턴 : private + getter + no setter
- 개입 막기 : Java의 synchronized 등
- 교착 상태(데드락) : 락의 대상과 순서에 유의.
- 트랜젝션 메모리 : DB의 트랜젝션 개념을 메모리에 적용한 것. 변경 시도 후, 문제 없을 시 반영.
11. 객체와 클래스
- C++ 설계자 Bjarne Stroustrup : OOP란, 사용자 정의형과 상속을 사용한 프로그래밍
- OOP 용어 발명자 Alan Kay : OOP란, 상태를 가진 객체가 메시지를 주고 받아서 커뮤니케이션하는 프로그램.
- 객체지향이 하고자 했던 일 : 현실 세계의 사물(Object)의 모형(model)을 컴퓨터 안에서 재창조하기.
- 언어와 목적에 따라 클래스가 필요하기도 하고 아니기도 하다.
- 1978, 모듈 : 관련성이 높은 함수나 변수의 묶음을 명시 (Python,Ruby는 모듈, Java, Perl은 패키지)
- 일급 시민(first-class citizen) : 차별대상이 아닌 시민을 의미. 반환값, 매개변수 등이 될 수 있는 일급 객체
- 최초의 Class는 현실 세계 Object를 더 세분화하여 분류하기 위해 도입됐다.
- 클래스는 특히 C++에서 어떤 메서드를 가지고 있는지 아닌지를 나타내는 사양으로서 역할을 하기도 했다.
- OOP 용어를 발명한 Alan Kay는 객체에 메시지를 전달하는 수단이 메서드라 생각했고, 어떤 동작을 수행할지는 수신 객체가 자유롭게 결정하는 것이 OOP의 중요한 요소라고 봤다.
- 클래스의 역할 : 결합체를 만드는 생성기, 어떤 조작이 가능한지에 대한 사양, 코드를 재사용하는 단위
12. 상속을 통한 재사용
- 일반화와 특수화 : 부모클래스에 일반기능을 구현, 자식 클래스에 특화 기능 구현
- 공통 부분 추출 : 복수 클래스에서 공통부분을 추출
- 차분 구현 : 변경된 부분만 구현하면 효율이 좋다
- 상속은 영향 범위가 넓어질수록 관리가 어려워진다. 동적 스코프와 비슷한 문제가 있다.
- 다중 상속의 문제점 : 충돌 → 자바는 다중상속 금지
'우아한테크코스 4기' 카테고리의 다른 글
🤔 도메인(domain)은 무슨 뜻일까?! (2) | 2022.03.25 |
---|---|
객체의 행동으로 표현되는 책임과 역할 (객체지향의 사실과 오해) (2) | 2022.03.25 |
🏎️자동차 경주와 💸로또에서 배운 것들 (2) | 2022.03.08 |
컬렉션의 복사 방법을 정리해봅시다! (unmodifiable view / list) (6) | 2022.02.28 |
for-loop vs IntStream, 그리고 멀티 스레딩 (4) | 2022.02.28 |