[Book] 코딩을 지탱하는 기술

미네·2021년 12월 2일
0

목록 보기
1/1
post-thumbnail

더 나은 코딩을 위한, 코딩을 지탱하는 기술

1장 효율적으로 언어 배우기

규칙은 언어마다 다르다.

복수의 언어를 비교해서 학습할 때 알게 되는 것이 있다.
그것은 '규칙은 언어마다 다르다'는 것이다.
프로그래밍 언어의 교과서에는 다양한 규약이 기술되어 있지만, 그것은 절대적인 약속이 아니다.
'지금은 이렇게 약속하는 게 보다 수월하다고 생각하니 그렇게 합시다'라는 의미일 뿐이다.

비교를 통한 배움

특정 언어로 프로그래밍을 배우는 것이 아니라, 다수의 언어를 비교해 가면서 학습하는 것을 의미한다. 이를 통해, 무엇이 언어에 따라 다르고 무엇이 공통적인지 배울 수 있다.

역사를 통한 배움

언어가 어떻게 바뀌었고 바뀌기 전에는 어떤 의문점이 존재했는지 학습하는 것을 의미한다.
이를 통해, 언어가 가지고 있는 다양한 기능이 '왜' 탄생했는지 배울 수 있다.

2장 프로그래밍 언어를 조감하다

프로그래머의 삼대 미덕

프로그래밍 언어 Perl의 설계자인 Larry Wall은 프로그래머가 가져야 할 3가지 자질로서 '나태,조바심,자만심'을 제안했다.

  • 나태(Laziness) : 전체 에너지 소비를 줄이기 위해 대부분의 능력을 집중하는 기질.
  • 조바심(Impatience) : 컴퓨터가 게을러질 때 느끼는 분노.
  • 자만심(Hubris) : 천벌을 내릴 정도의 과도한 자존심. 또는 다른 사람들에게 부끄럽지 않은 프로그램을 만들려고 또한 유지하려는 기질.

언어는 도구다

어떤 언어가 자신의 목적에 적합한지는 그 언어를 사용할 때 자신이 어느 정도의 생산성을 발휘할 수 있는지로 판단할 수 있다.
다른 사람이 말하는 것에 혹하지 말고, 좋은 도구를 현명하게 선택하여 적재적소에 사용하도록 하자.

3장 문법의 탄생

문법은 언어 설계자가 정한 규칙

언어 설계자가 정한 규칙이 바로 문법이다. 문법은 언어에 따라 다르다. 연산자의 종류도 언어에 따라 다르다.

(칼럼)이해력을 확인하기 위해서는 결과물(Output)을 확인한다

프로그래밍 언어의 결과물은 언어 처리계가 틀린 곳을 자세히 지적해준다. 그것을 이해하려 하지 않고서는 언어 처리계와 제대로 된 커뮤니케이션을 할 수가 없다.

(칼럼) 무엇을 배우면 좋을지 모르는 이유

'무엇을 배우면 좋습니까?' 이 질문에 대답하기 전에 우선 한 가지를 물어보고 싶다.
'여러분은 무엇을 만들고 싶은가요?' 목적이 명확하지 않으면 최선의 방법 또한 가르쳐줄 수 없다. 즉, 목적을 명확히 하는 것이다. 그리고 목적을 달성하기 위해 필요한 것부터 배우면 된다.

4장 처리 흐름 제어

이 장에서는 if 문, while 문, for 문 등의 처리 흐름을 제어하기 위한 규칙(문법)에 대해서 설명하고 있다.

5장 함수

함수의 역할

  • 이해(조직을 예로): 코드가 함수로 나눠져 있는 것은 큰 조직이 부서로 나눠져 있는 것과 닮았다. 소스 코드의 행수가 많아지면 전체를 파악하기 어렵게 된다. 그래서 몇 개의 행을 하나의 그룹으로 묶어서 거기에 이름을 붙이는 것이다. 이것이 함수다.
  • 재사용(부품을 예로) : 함수를 만드는 것은 작은 부품을 조립해서 큰 부품을 만드는 것과 비슷하다.

6장 에러 처리

프로그램도 실패를 한다

  • 반환값으로 알린다
    • 이상적으로는, 프로그래머가 어떤 함수를 호출할 때 '이 함수는 실패할 가능성이 있을까?', '실패하면 어떤 값을 반환할까?' 를 확인해야만 한다. 반드시 그렇게만 한다면 반환값으로 에러를 전달하는 방법에서도 실패를 놓치지 않을 수 있다.
  • 실패하면 점프한다.

틀리면 바로 예외를 던진다

프로그래밍 언어를 배울 때나 혼자서 만들고 있는 작은 규모의 프로그램의 경우에는 Python과 같이 바로 예외를 던지는 쪽이 JavaScript 같이 undefined 처리하는 것보다 좋다.
무언가가 이상하다면 빨리 문제를 발견하는 것이 중요하다.
'이상하면 처리를 정지하고 빨리 보고해야 한다'는 설계 이념을 '패일 퍼스트'(fail first)라고 부른다. 소프트웨어의 목적에 따라서는 간단하게 정지되면 곤란한 경우도 있지만, 적어도 학습이나 개발 단계에서는 틀리면 바로 틀린 것을 발견하는 것이 오히려 이점이 많다.

7장 이름과 스코프

충돌 피하기

  • 긴 변수명을 사용한다.
  • 스코프를 이용한다.

스코프의 진화

스코프(Scope)란 이름의 유효 범위다. 프로그램 전체에서 이름이 충돌하지 않도록 관리하는 것은 어려운 일이다. 그래서 이름의 유효 범위를 좁게 한정해서 관리가 편해지도록 한다.

  • 동적 스코프는 변수를 변경한 후 다른 함수를 호출한 경우 호출된 함수에 영향을 미친다.
    동적 스코프로 만들어진 대응표는 소스 코드 전역에서 읽고 쓸 수 있다. 이것이 정적 스코프와의 가장 큰 차이점이다.
  • 정적 스코프는 함수별로 대응표를 나눈다. 특정 함수 안에서의 변경이 해당 함수 밖까지 영향을 주지 않는다는 유효 범위 분할이 가능해졌다.

8장 형

형(型)이란?

형은 사람이 데이터에 붙인 '추가 데이터'이다.
같은 비트열이라도 '그것을 어떤 종류의 값으로 해석할지'에 따라 틀린 값이 되어버린다. 이를 피하기 위해 '어떤 종류의 값인가?'라는 정보를 추가한 것이 형의 시작이다.

(칼럼) 대략적인 부분을 잡아서 조금씩 상세화한다.

소스 코드를 읽을 때는 우선 디렉터리 구조와 파일명을 본다. 그리고 파일을 속독으로 읽고 거기서 정의하고 있는 함수나 클래스 이름, 자주 호출되는 함수명 등을 본다.
'우선 대략적인 구조를 잡고, 조금씩 상세한 정보로 접근한다'는 공통점이 있다.

소스 코드에는 다른 방식의 독해 방법이 있다. 디버거(debugger)의 과정을 사용해서, 실행되는 순서나 호출 계층으로 읽는 방법이다. 이 경우도 동일하게 우선은 대략적인 처리 흐름을 따라가고, 조금씩 깊이를 더해서 함수안의 처리를 따라가는 것이 중요하다.

9장 컨테이너와 문자열

배열과 연결리스트

요소 개수가 적을 때는 배열과 연결 리스트 중 어느 쪽을 사용해도 그다지 큰 차이가 없다. 그러나 요소 개수가 늘어나면 배열은 점점 처리 시간이 늘어나는 반면, 연결 리스트에서는 처리 시간에 변화가 없다. 요소 수가 많고 삽입이 빈번히 발생하는 경우에는 연결 리스트가 적합하다.

(칼럼) O 기법 - 계산 시간과 데이터량의 관계를 간단히 나타내는 것

  • 배열에 삽입하는 경우와 같이 '데이터량 n이 두 배가 되면 계산 시간도 두배가 된다'는 성질을 O(n)이라 쓰고 'n 오더(order)'라고 부른다. => '오더 엔' 또는 '오 엔'
  • 연결 리스트에 삽입하는 경우와 같이 '데이터량 n이 두 배가 되도 계산 시간은 바뀌지 않는다'는 성질을 O(1)이라 쓰고 '정수 오더'라고 부른다. => '오더 일' 또는 '오 원'
  • n이 커지면 다음 순으로 계산 속도가 빠르다.

    O(1) < O(log n) < O(n) < O(n²)

만능 컨테이너란 없다

'결국 어떤 것을 사용하면 좋은 것일까?'라는 의문을 품을 수도 있다.
어떤 목적이든 사용할 수 있는 최선의 컨테이너는 없다.
컨테이너를 사용하는 목적, 어떤 사용법을 적용할 것인가, 어떤 조작이 많은가에 따라 최적의 컨테이너도 달라진다. 메모리를 절약할 필요가 있는가, 아니면 계산 시간을 줄일 필요가 있는가? 또는 어느 쪽도 절약할 필요가 없는지, 절대 정답은 존재하지 않는다.
자신의 상황에 맞게 적합한 균형을 찾는게 중요하다.

문자열

문자열에도 여러 가지 차이가 있다.
우선 '무엇이 문자인지'(문자 집합)의 차이, 다음으로 '어떻게 문자를 비트열로 표현하는지'(문자 부호화 방식)의 차이, 그리고 '어떤 정보를 어떤 메모리에 저장하는지'(문자열 구현)의 차이다. 많은 언어가 문자열을 지원하고 있지만, 서로 동일하다고 말할 순 없다.

10장 병행 처리

병행 처리란?

현재 우리들이 사용하고 있는 PC에서는 음악을 들으면서 문서를 작성하고 브라우저로 웹 서핑을 하는 등 복수의 처리를 동시에 실행할 수 있다. 이와 같이 복수의 처리를 시간축 상에 오버랩에서 실행하는 것을 병행 처리라고 한다.

처리를 변경하는 2가지 방법

  • 협력적 멀티태스크 : '타이밍이 좋은 시점에서 교대'하는 방법이다. 처리가 일단락되는 시점에 자발적으로 처리 교대를 하는 방법
  • 선점적 멀티태스크 : '일정 시간에 교대'하는 것이다. 이 방법에서는 개별 프로그램과 입장이 다른 프로그램(태스크 스케줄러)이 존재한다. 이 프로그램이 일정 시간마다 지금 실행되고 있는 처리를 강제적으로 중단시켜서 다른 프로그램이 실행될 수 있도록 한다.

락의 문제점 - 교착 상태가 발생한다

공유된 X와 Y를 변경하는 처리 A와 처리 B가 있다고 하자.
A가 'X를 락시키고, Y를 락시킨다'는 순서로 락을 걸어두고, B가 'Y를 락시키고, X를 락 시킨다'는 순서로 락을 걸었을 경우 시점에 따라선 문제가 발생한다.
A가 X를 락하고 B가 Y를 락한 상태에서 서로가 상대방 락이 풀리는 것을 기다리게 된다.
이것이 교착 상태(Deadlock, 데드락)라고 불리는 현상이다.
이 문제를 방지하기 위해서 프로그래머는 프로그램 전체에서 락의 순서가 일관되도록 주의해야 한다. '무엇에 락을 걸어야 하는가'뿐만 아니라, '어떤 순으로 락을 걸어야 하는가'도 파악해야 한다.

11장 객체와 클래스

객체는 현실 세계의 모형

우리들은 어떻게 세계를 이해하고 있을까? 자신이 경험한 것을 '사물(object)'이라는 개념으로 정리해두고 있다. '사물(object)'이란 테이블과 의자나 은행 대출과 수식,사람,다항식,삼각형,트랜지스터 같은 것이다. 그리고 우리들의 사고, 언어, 행동은 이런 '사물'을 가리키거나 설명하거나 조작하기 위해 만들어졌다.
우리가 컴퓨터로 문제를 해결하고 있을 때, 현실 세계의 '사물(object)'모형(model)을 컴퓨터 안에 만들 필요가 있다.

현실 세계에 있는 '사물(object)'의 '모형(model)'을 컴퓨터 안에 만들려면 어떻게 하면 될까?
어떻게 하면 보다 편해질 수 있을까?
=> '객체 지향'이라는 개념이 탄생

모듈

모듈은 '하나로 모으는 기법'이다.

왜 클로저(closure)라고 부를까?

function makeCounter(){
  var count = 0;
  function push(){
	count++;
    console.log(count);
  }
  return push;
}

c = makeCounter();
c(); // -> 1
c(); // -> 2
c(); // -> 3

클로저(closure)라는 명칭에서 무언가를 닫는다는 느낌이 든다.
함수 push는 변수 count를 참조하지만 이것은 함수 push 안에 정의되어 있지 않다.
이런 변수를 자유 변수라고 한다.
함수 push는 자유 변수를 포함하고 있기 때문에 열린 함수다.
그리고 함수 makeCounter 대응표에서는 0이라는 값과 count라는 이름이 연결되어 있다.
값에 이름을 연결하는 것을 '바인딩'이라고 한다.
열린 함수 push가 makeCounter 대응표와 세트가 됨으로, 더 이상 그 이외의 스코프에 변수정의를 찾으러 가지 않아도 되는 완결된 상태가 된다.
이것을 '닫았다'라고 표현한다.

클래스의 3가지 역할

  • 결합체를 만드는 생성기(붕어빵을 만들기 위한 붕어빵 틀)
  • 어떤 조작이 가능한지에 대한 사양
  • 코드를 재사용하는 단위(상속)

12장 상속을 통한 재사용

상속에 관한 다양한 접근법

  • 일반화/특수화 : '부모 클래스로 일반적인 기능을 구현하고, 자식 클래스로 목적에 특화된 기능을 구현한다'는 접근이다. 즉, '자식 클래스는 부모 클래스를 특수화한다'는 설계 방침.
    '자식클래스는 부모 클래스의 일종입니까?'라고 물으면 그 대답은 '예'이다.
  • 공통 부분을 추출 : '복수 클래스의 공통 부분을 부모 클래스로서 추출하면 좋다'는 접근법이다. '자식클래스는 부모 클래스의 일종입니까?'라고 물으면 그 대답은 '아니오'가 된다.
  • 차분 구현 : '상속 후 변경된 부부만을 구현하면 효율이 좋다'는 접근법이다. 상속을 재사용을 위해 사용함으로 구현이 편해질 수 있다는 발상이다.
    이 경우 '자식클래스는 부모 클래스의 일종입니까?'라고 물으면 그 대답은 '아니오'가 된다.

상속은 양날의 칼

상속을 사용해서 코드를 재사용하면 코딩량이 줄어서 편하다.
하지만 상속을 반복하면, 코드 영향 범위가 넓어져서 이해하기 어렵게 된다. 이해하기 쉽게 하기 위해서는 상속 트리의 깊이를 낮추는 것이 중요하다.

(칼럼) 끝에서부터 차례대로 베껴간다

방대한 정보 앞에 좌절될 때 어떻게 하면 좋을까?
저자는 시간을 정해서 '25분간 어디까지 베낄 수 있는지' 도전하는 것을 좋아한다.
분량으로 나누는 것도 좋은 방법이다.
중요한 것은 간격을 적절히 해서 목표를 이루었다는 만족감을 얻을 수 있도록 하는 것이다.

책을 마치며

How(어떻게 만들까?)에 주력하기 쉽지만, What(무엇을 만들까?), Why(왜 만들까?)도 잊어서는 안 된다. How는 수단에 불과하다. 또한 시간은 유한한 자원이다. 의욕도 중요한 자원이다.

0개의 댓글