회사에서 매일 업무 스케쥴을 작성하는데, 주간마다 이것들을 모아서 정리한 후에 보고를 해야한다.

그러다보니 귀찮은 일들이 발생하고..

이 부분을 개선하기 위해 어떤분이 (지금은 퇴사한..) 가공하여 추출하는 프로그램을 개발했었다. 

스위프트로 되어있었기때문에 내가 봐도 뭐 알길이 있나..ㅎ;

그래서 그냥 궁금했던 electron과 결합해서 가공, 추출하는 프로그램을 js로도 만들어보자 생각해서 책을 완독하게되었다.

책은 회사 책장에 꽂혀있었다. ㅎㅎ!

yes24 캡쳐

일단 2018년도에 제작된 책이라 특정 버전을 설치해서 진행해야하고 그럼에도 오류가 생기는 부분들이있었다.

그래서 그런 부분은 대략적으로 이해만 하고 넘어갔고, 나중에 보니 약간 다른부분들도 충분히 존재했다.

책을 굳이 안 읽고 dock을 읽고 블로거글들을 보면 이해하는데 문제가 없을 것 같았다.

 

예제 학습

https://github.com/leeyoonseo/study-electron

 

GitHub - leeyoonseo/study-electron: [학습] Electron 애플리케이션개발 - 웹 기술로 구현하는 크로스 플랫폼

[학습] Electron 애플리케이션개발 - 웹 기술로 구현하는 크로스 플랫폼 데스크톱 애플리케이션 - GitHub - leeyoonseo/study-electron: [학습] Electron 애플리케이션개발 - 웹 기술로 구현하는 크로스 플랫폼

github.com

 

electron에서 이해하기 어려웠던 부분은 당연 프로세스 부분이었다.

main process와 renderer process가 있고 main이 node, renderer가 chromium의 프로세스이다.

즉 main이 서버, renderer가 페이지..

이 두개를 이해하고 서로 통신하는데까지 약간의 시간이 걸렸다. 

이때 맥과 윈도우와 같은 os 적인 부분, 보안적은 부분등을 신경을 많이 써야할 것 같았다.

 

책을 다 읽고 난 후에 일렉트론을 설치해서 개발을 해보려고 했는데, 예전 버전과는 약간 달랐다.

reload라는 파일이 처음부터 생성되길래, 실제로 electron을 install한 순간부터는 그냥 공식문서를 봤다;

 

분명 renderer에서는 브라우저라서 전역변수나 그런것을 쓸 수 있을거라 생각했다.

안되는 이유가 있었고...ㅎ;

https://www.electronjs.org/docs/latest/api/context-bridge

 

contextBridge | Electron

Create a safe, bi-directional, synchronous bridge across isolated contexts

www.electronjs.org

 

그리고 책을 다 읽고 난 후에, 기존 개발자가 만들었던 코드들을 대략적으로 분석했다.

완벽하게 이해한 것은 아니지만 대략적인 로직은 아래와 같았다.

웹뷰를 통해 사이트 로드 -> api 호출 -> 응답데이터 가공 -> 추출

이러면서 웹뷰도 겸사겸사... 볼 수 밖에 없었다.

웹뷰가 없으면 보안상 접근이 불가능했다. (= 개발이 불가능...)

이 작업을 하면서 react로 하고 싶었는데, 요즘 vue로만 작업을 계속해서 react를 다시 보고 electron, webview까지 보면 헷갈릴 것 같아서 그냥 js로만 작업을 시작했다.

근데, 솔직히 react로 하는게 더 재밌고, 쉬웠을 것 같다.

 

내가 시작한 토이 플젝이 (=기존 스위프트로 개발된 프로그램, electron으로 따라 개발하기) 완성된 것은 아니다.

아직 데이터 가공을 안하고 있어서.. 노력해야지 으샤으샤..

아 그리구, 지금 하는 플젝은 아쉽게도 회사내에서만 사용가능한 것이라 보안적으로 접근이 안된다. (외부공개 불가)

이게 좀 아쉽기 때문에 책에 나온 채팅 앱 만들기 정도를 간단히 해보는 것도 좋을 것 같다고 생각했다.

채팅 앱은 꼭 react로 해야지..!!

 

책 자체에 좋은 내용이 많지만 기본적으로 같은 내용을 반복해서 길게 말한다는 느낌을 받았다.

그러다보니 조금 지루해지기 시작했고 내 스타일의 도서가 아니란 것을 알게되었다.

또한 오래된 도서였기 때문에 현재의 유행(?)과는 조금 다른 부분도 충분히 존재하는 것 같았다.

이것과 관련해서 동료와 얘기를 했었는데, 동의하지 못하는 부분들도 있었다. 

(정답은 없기때문이지..)

여태 읽은 내용은 머리에 잘 세겨두도록하고 다른 도서로 갈아타야겠다.

 

05. 형식 맞추기

코드 형식을 맞추기 위한 간단한 규칙을 정하고 착실히 따라야 한다.

팀으로 일한다면 팀이 합의하여 규칙을 정한다.

필요하면 규칙을 자동으로 적용하는 도구를 활용한다.

 

형식을 맞추는 목적?

코드 형식은 의사소통의 일환이다.

늘 구현한 기능이 다음 버전에서 바뀔 수도 있다. 그런데 구현한 코드의 가독성에 따라 바뀔 코드의 품질에 영향을 미칠 것이다. 오랜 시간이 지나 원래 흔적을 찾아보기 어려워져도 맨 처음 잡아둔 구현 스타일과 가독성의 수준은 유지보수 용이성과 확장성에 계속 영향을 미칠 것이다. (개발자의 스타일과 규율)

 

적절한 행 길이를 유지하라

 

신문 기사처럼 작성하라

위에서 아래로 기사를 읽는 것처럼 아래로 내려갈 수록 의도를 세세하게 묘사하라.

이름은 간단하면서 설명이 가능하게 짓는다. 이름만 보도고 모듈을 살펴보고 판단할 수 있도록 신경써서 짓는다.

(소스 파일 처음은 고차원 개념과 알고리즘, 끝 부분은 저차원함수와 세부내역이 나오게 작업한다?)

 

개념은 빈행으로 분리하라

패키지 선언부, import문, 각 함수 사이에 빈행이 들어간다.

너무도 간단한 규칙이지만 코드의 세로 레이아웃에 심오한 영향을 미칠수 있다.

빈 행은 새로운 개념을 시작한다는 시각적 단서이다.

 

세로 밀집도

줄 바꿈이 개념을 분리한다면 세로 밀집도는 연관성을 의미한다.

서로 밀접한 코드 행은 세로로 가까이 놓아야한다.

 

수직 거리

서로 밀접한 개념은 세로로 가까이 둬야한다. (변수 선언, 인스턴스 변수, 종속 함수, 개념적 유사성)

 

세로 순서

 

가로 형식 맞추기

예전에는 오른쪽으로 스크롤할 필요가 없도록 코드를 구현했다. 하지만 요즘은 모니터 크기도 커졌고 글꼴 크기도 작게 작업하는 사람들이 많이 생겼다. 하지만 이렇다고해서 200자까지 한 화면에 들어가게는 하지마라.

행 길이를 제한하라.

 

가로 공백과 밀집도

가로로는 공백을 사용해 밀접한 개념과 느슨한 개념을 표현한다.

 

가로 정렬

 

들여쓰기

프로그래머는 들여쓰기 체계에 크게 의존한다. 코드를 맞춰 코드가 속한 범위를 시각적으로 표현한다.

 

가짜 범위

 

팀 규칙

팀은 한가지 규칙에 합의해야한다.

그리고 합의된 규칙을 따라야한다.

그래야 소프트웨어가 일관적이다.

 

밥 아저씨의 형식 규칙

 

 

06. 객체와 자료 구조

이번 장은 어려워서 이해가 잘 안간 부분이 많다.

 

자료 추상화

변수를 비공개로 정의 -> 의존성을 낮추기 위해

그런데 왜 조회(get)와 설정(set) 함수는 공개하여 비공개 변수를 외부에 노출 시킬까?

변수 사이에 함수라는 계층을 넣는다고 구현이 저절로 감춰지지는 않는다. (= 비공개 되지는 않는다?)

-> 구현을 감추려면 추상화가 필요하다? -> 즉, 추상 인터페이스를 제공해 사용가자 구현을 모른 채 자료의 핵심을 조작할 수 있어야 진정한 의미의 클래스다?

 

자료/객체 비대칭

객체는 추상화 뒤로 자료를 숨긴 채 자료를 다루는 함수만 공개(내부 구조를 숨긴다)

자료구조는 자료를 그대로 공개하며 별다른 함수는 제공하지 않는다. (내부 구조 노출)

-> 객체 지향 코드에서 어려운 변경은 절차적인 코드에서 쉽다. 절차적인 코드에서 어려운 변경은 객체 지향 코드에서 쉽다.

 

디미터 법칙

모듈은 자신이 조작하는 객체의 속 사정을 몰라야한다.

 

잡종 구조

절반은 객체, 절반은 자료구조이다. 새로운 함수, 자료 구조를 추가하기 어려우므로 되도록 피하는 것이 좋다.

 

자료 전달 객체

자료 구조체의 전형적인 형태는 공개 변수만 있고 함수가 없는 클래스이다.

 

활성 레코드

DTO의 특수한 형태, 활성 레코드에 비즈니스 규칙 메서드를 추가해(=자료구조가 됨???) 객체로 취급하는 것은 바람직하지 않다. (-> 자료 구조도 객체도 아니게 되는 잡종 구조)

 

결론

개발자는 하나에 편견을 가지지 말고 사실을 이해하고 최적인 선택을 해야한다.

객체는 동작을 공개하고 자료를 숨김. -> 시스템 구현 시 새로운 자료 타입 추가할 경우 적합

자료 구조는 별다른 동작 없이 자료를 노출. -> 새로운 동작을 추가할 경우 자료구조/절차적인 코드가 적합

 

 

07. 오류 처리

오류처리는 프로그램에 반드시 필요한 요소 중 하나이다.

무언가 잘못될 가능성은 늘 존재하며 바로 잡을 책임은 프로그래머에게 있다.

그리고 오류 처리 코드로 인해 프로그램 논리를 이해하기 어려워진다면 깨끗한 코드라 부르기 어렵다.

 

오류 코드보다 예외를 사용하라

호출자 코드에서 분기문으로 오류를 처리할 경우 호출자 코드가 복잡해지기 때문에 예외를 사용한다.

-> 알고리즘과 오류 처리 알고리즘을 분리

 

Try-Catch-Finally문을 유용하게 사용하라

 

미확인(unchecked) 예외를 사용하라

 

예외에 의미를 제공하라

오류 메세지에 정보를 담아 예외와 함께 던진다.

 

호출자를 고려해 예외 클래스를 정의하라

-> 외부 API 사용 시 감싸기 기법이 최선일 것이다.

 

정상 흐름을 정의하라

특수 사례 패턴 -> 클래스를 만들거나 객체를 조작해 특수 사례를 처리하는 방식

 

null을 반환하지 마라, 전달하지 마라

호출자에게 문제를 떠넘기는 코드이므로 예외를 던지거나 특수 사례 객체를 반환하라

 

결론

깨끗한 코드는 읽기도 좋아야하지만 안정적이어야한다.

오류 처리를 프로그램 논리와 분리해 독자적인 사안으로 고려하게된다면 튼튼하고 깨끗한 코드를 작성할 수 있을 것이다. 또한 독립적인 추론이 가능해지고 코드 유지보수성도 높아진다.

 

 

08. 경계

시스템에 들어가는 모든 소프트웨어를 직접 개발하는 경우는 드물다. 때로는 패키지를 사기도하고 오픈소스나 컴포넌트를 사용한다.

외부 코드를 우리 코드에 깔끔하게 통합하여 경계를 깔끔하게 처리하는 기법과 기교를 살펴보는 장이다.

-> 제공자는 많은 환경에서 돌아가야 하기 때문에 적용성을 최대한 넓히여 애를 쓰고 사용자는 자신의 요구에 집중하는 인터페이스만을 바란다. 이 두 경계에서 문제가 생길 가능성을 고려해야한다.

 

외부 코드 사용하기

경계 인터페이스를 이용할 때는 이를 이용하는 클래스나 클래스 계열 밖으로 노출되지않도록 주의한다.

 

경계 살피고 익히기

우리쪽 코드를 먼저 작성해 외부 코드를 호출하는 대신 먼저 간단한 케이스를 작성해 외부 코드를 익힌다.

 

log4j 익히기

 

학습 테스트는 공짜 이상이다

학습 테스트를 이용한 학습이 필요하든, 그렇지 않든, 실제 코드와 동일한 방식으로 인터페이스를 사용하는 테스트 케이스가 필요하다.

 

아직 존재하지 않는 코드를 사용하기

아는 코드와 모르는 코드를 분리하는 경계

 

깨끗한 경계

변경 비용이 커지지 않도록 주의해야한다.

 

 

09. 단위 테스트

과거에는 TDD 개념을 몰랐다. 대다수의 테스트라고 하면 `돌아간다`는 사실만 확인하는 일회성 코드에 불과했다.

지금은 프로그램만 도는 것이 아닌 코드가 제대로 도는 지 확인하는 테스트 코드를 작성한다.

테스트 케이스를 구현하고 이 코드를 사용하는 사람들에게도 공개한다.

(테스트 코드와 내 코드를 같은 소스 패키지로 묶어 배포)

 

TDD 법칙

TDD가 실제 코드를 짜기 전에 단위테스트부터 짜는 것을 요구한다.

방대한 테스트 코드가 나오면 관리 문제가 발생한다.

 

깨끗한 테스트 코드 유지하기

테스트 케이스가 늘어나는 부담, 그리고 이에 따른 유지 보수 비용 증가

즉, 테스트 코드를 막 짜도 좋다고 허용하게 된다면 테스트 코드가 늘어가는 것이 부담되고 이에따라 유지 보수 비용이 증가한다. 결국 이렇게되면 개발자는 테스트 케이스와 코드의 정리 및 수정을 포기하게 된다.

그렇기 때문에 테스트 코드도 실제 코드 못지 않게 중요하게 작성해야한다.

 

테스트는 유연성, 유지보수성, 재사용성을 제공한다.

테스트 코드를 깨끗한게 유지하지 않으면 결국은 잃게된다.

 

깨끗한 테스트 코드

가독성이 가장 중요하다. 최소한의 표현으로 많은 것을 나타내야하며 테스트는 명확히 나누어야한다.

 

도메인에 특화된 테스트 언어

이중 표준

 

테스트 당 asset 하나(=테스트 당 개념 하나)

assert문이 하나일 경우 결론이 하나이기 때문에 코드를 이해하기 쉽고 빠르다.

독자적인 개념 세개를 테스트할 경우 테스트를 세개로 나눈다.

 

결론

테스트 코드는 실제 코드만큼이나 프로젝트 건강에 중요하다. 테스트 코드는 실제 코드의 유연성, 유지보수성, 재사용성을 보존하고 강화하며 테스트 코드가 방치되어 망가지면 실제 코드에도 영향이 있다.

그러므로 테스트 코드는 지속적으로 깨끗하게 관리해야하며 표현력을 높이고 간결하게 정리한다.

 

10. 클래스

클래스 체계

변수 목록 다음에는 공개 함수가 나온다, 비공개 함수는 자신을 호출하는 공개 함수 직후에 넣는다.

즉, 추상화 단계가 순차적으로 내려간다.

 

캡슐화

공개하지 않는 편이 낫지만 반드시 숨겨야 한다는 법칙은 없다. 

하지만 그 전에 비공개 상태를 유지할 온갖 방법을 강구한다.

캡슐화를 풀어주면 결정은 언제나 최후의 수단이다.

 

클래스는 작아야한다.

클래스를 만들때 가장 기본적인 규칙은 크기이다. 그렇다면 얼마나 작아야할까?

-> 물리적인 행 수로 크기를 측정하는 것이 아닌 클래스가 맡은 책임의 갯수이다.

 

단일 책임 원칙

클래스나 모듈을 변경할 이유가 하나여야한다.

 


클린코드 - 04. 주석


주석은 양날의 검과 같습니다.
잘 달린 주석은 그 어떤 정보보다 유용할 수 있지만 반대라면?.. 오히려 코드를 이해하기 어렵게 만들 수도 있습니다.;;

그렇기 때문에 모든 개발자들이 의도에 맞게 코드를 정확히 작성한다면 (=표현) 주석은 필요하지 않을 수도 있습니다.
하지만 현실적으로 많은 어려움이 있기 때문에 코드의 의도를 보충하기 위해 주석을 사용하고는 합니다. 그리고 이에 따라 주석도 유지보수가 되어야하는 불편함이 생기기 때문에 필요할지라도 우리는 주석의 수를 줄이는데 노력해야합니다.

주석은 나쁜 코드를 보완하지 못하며 코드로 의도를 표현해야합니다.


좋은 주석이란? (최고의 주석은, 주석을 달지 않는 방법)

  1. 법적인 주석
    1. 저작권 정보와 소유권 정보는 필요하고도 타당합니다.
  2. 정보를 제공하는 주석
    1. 추상 메서드가 반환할 값을 설명합니다.
    2. 가능하다면 함수 이름에 정보를 담는 편이 더 좋습니다.
  3. 의도를 설명하는 주석
  4. 의미를 명료하게 밝히는 주석
  5. 결과를 경고하는 주석
    1. 때로는 다른 프로그래머들에게 결과를 경고 할 목적으로 쓸 수 있습니다.
  6. TODO 주석
    1. 앞으로 할일을 남기며 주기적으로 주석을 점검하여 없애도 괜찮은 주석을 삭제합니다.
  7. 중요성을 강조하는 주석
  8. 공개 API 에서 Javadocs
    1. 공개 API를 구현한다면 많은 이들에게 정보를 알리기 위해 사용할 수 있습니다.

 


나쁜 주석이란?

  1. 주절거리는 주석
    1. 그저 중요한 정보 없이 의무감으로 작성하는 주석
  2. 같은 이야기를 중복하는 주석
  3. 오해할 여지가 있는 주석
  4. 의무적으로 다는 주석
  5. 이력을 기록하는 주석, 저자를 표시하는 주석
    1. 현재에는 소스 코드 관리 시스템이 존재하기에 혼란을 가중시킵니다.
  6. 있으나 마나한 주석
  7. 함수나 변수로 표현 할 수 있는 주석
  8. 위치를 표시하는 주석
    1. handler나 function, actions등의 위치를 알리기 위한 주석
    2. // Actions ——————————-
  9. 닫는 괄호를 표현하기위해 다는 주석
  10. 주석으로 처리한 코드
    1. 이유가 있어서 주석처리를 했다고 생각하기 때문에 다른 사람들이 지우기를 주저할 수 있습니다.
  11. HTML 주석
  12. 전역 정보
    1. 주석을 달아야한다면 근처에 있는 코드만 기술하여야하며 전반적인 정보를 기술하는 것을 말합니다.
  13. 너무 많은 정보
  14. 모호한관계
  15. 함수 헤더
  16. 비 공개 코드에서의 사용

 

클린코드 - 03. 함수

 

어떤 프로그램이든 가장 기본적인 단위가 함수이다.

우리는 어떤 함수를 읽었을때 프로그램 내부를 직관적으로 파악할 수 있을까?

 

작게 만들어라

  • 블록과 들여쓰기
    • 중첩 구조가 생길 만큼 함수가 커지면 안된다. (if else문 등 주의하여 쓰자)
    • 블록 안에서 호출하는 함수 이름을 적절히 짓는다면 코드를 이해하기 쉽다.
  • 한가지만 해라
    • 예시: 1. 페이지가 테스트 페이지인지 판단 => 2. 설정 페이지와 해제 페이지를 넣는다 => 3. 페이지를 HTML로 렌더링한다.
      • 추상화 수준이 하나인 단계만 수행한다면 그 함수는 한가지 작업을 한다고 할 수 있다.
      • 더이상 줄이기가 불가능 하며 if, else를 따로 뺀다고 해도 다른 표현일 뿐 추상화 수준이 바뀌지 않는다.
  • 함수 당 추상화 수준은 하나로!
    • 함수가 확실히 '한가지' 작업만 하려면 함수 내 모든 문장의 추상화 수준이 동일해야한다.
    • 수준이 섞여있을 경우 근본 개념인지 세부사항인지 구분하기 어려워져 추후에 사람들이 함수에 세부사항을 점점 더 추가하게 된다.
  • 위에서 아래로 코드 읽기 (내려가기 규칙)
    • 함수 다음에는 추상화 수준이 한 단계 낮은 함수가 온다.
    • 즉 위에서 아래로 읽으면 함수 추상화 수준이 한 단계씩 낮아진다.

 

Switch문

  • 다형성을 이용한다.
  • 상속관계를 숨긴 후 절대로 다른 코드에 노출하지 않는다.

 

서술적인 이름을 사용하라

  • 길고 서술적인 이름이 짧고 이해하기 어려운 이름보다 좋다. (일관성있게!)

 

함수 인수

  • 이상적인 인수 개수는 0개 (무항)
    • 4개 이상은 특별한 이유가 필요하다. (이유가 있어도 사용해서는 안된다..)
  • 플래그 인수
    • 함수 안에 여러가지 일을 처리하는 것을 의미한다.
    • 함수로 부울 값을 넘기는 것은 좋지않다.
      • render(boolean isSuite) => renderForSuite(), renderForSingleTest() 로 함수를 나누는 것이 좋다.
  • 이항, 삼항 함수
    • 인수가 많은 함수는 적은 함수보다 이해하기 어렵다.
    • 단항에 비해 위험이 따른다.
      • 순서, 무시로 야기되는 문제가 생길 수도 있다.
  • 동사와 키워드
    • 함수 이름에 인수의 순서, 의도를 넣는것도 좋다.
      • assertEqual(expected, actual) => assertExpectedEqualsActual(expected, actual) 이렇게 할 경우 인수의 순서를 기억할 필요가 없다.

 

부수 효과를 일으키지 마라

  • checkPass-word일 경우 암호 확인 작업만 하라
    • 세션 초기화하는 로직이 있을 경우 문제가 생길 수 있으므로 따로 initializeSession으로 함수를 분리하는 것이 좋다.

 

명령과 조회를 분리하라

  • 수행하거나 답하거나... 분리하라!

 

오류 코드 보다 예외 케이스를 사용하라

  • 명령 함수 에서 오류코드를 반환하는 방식은 명령, 조회 분리 규칙을 미묘하게 위반한다
if (deletePage(page) = E_OK) {
    if (registry.deleteReference(page.name) = E_OK) {
      if (configKeys.deleteKey(page.name.makeKey()) = E_OK){ 
          logger.log('page deleted');
      } else {
          logger.log('configKey not deleted');
      }
    } else {
    	logger.log('deleteReference from registry failed');
    }
} else {
	logger.log('delete failed'); 
    return E_ERR0R;
}

// 수정
try {
  deletePage(page);
  registry.deleteRefe rence(page.name); 
  configKeys.deleteKey(page.name.makeKey());
}catch (Exception e) { 
	logger.log(e.getMessage());
}

 

Try/Catch 블록 뽑아내기

  • 정상 동작과 오류 처리 동작을 뒤섞는다. 그러므로 별도의 함수로 뽑아내는 것이 좋다.
public void delete(Page page) {
  try {
  	deletePageAndAllReferences (page);
  } catch (Exception e) { 
    logError(e);
    private void deletePageAndAHReferences(Page page) 

    throws Exception { 
      deletePage(page);
      registry.deleteReference(page.name); 
      configKeys.deleteKey(page.name.makeKey());
    }
    private void logError(Exception e) { 
      logger.log(e.getNlessage());
	}
}
  • 오류 처리도 한가지 작업이다.
  • Error.java 의존성 자석
    • enum 열거형 타입을 사용할 경우 의존성이 생겨서 문제가 될 수 있다. (import 해서 사용할 떄 변경이 생기면 매번 재 컴파일 해야하는 문제가 생길 수 있다.)
  • 반복하지 마라
    • 중복되는 코드를 없앨경우 가독성이 높아진다.
    • 객체 지향 프로그래밍은 코드를 부모 클래스로 몰아 중복을 없앤다.
  • 구조적 프로그래밍
    • 모든 블록에 입구와 출구는 하나만 존재해야한다
      • return 문이 하나여야한다. 하지만 함수가 작다면 사용해도 괜찮다.
      • goto문은 피해야한다.
  • 함수를 어떻게 짜죠?
    • 먼저 생각한대로 기록한 후 읽기 좋게 다듬는다. 초안은 대개 서투르고 어수선하므로 원하는 대로 읽힐때까지 다듬고 정리한다.

 

 

내 생각....

역시 책은 내가 사용하는 언어로 된 것을 읽는 것이 가장 베스트인 것같다.

모르는 부분들 혹은 공감하지 못하는 부분들이 존재하는 것을 서서히 느끼고 있다.

하지만 큰 틀은 모두 동일하니 완독하는 것이 목표다.

다른 작업들도 같이 하고 있어 언제 완독할지는 모르겠다.

아 그리고 지금까지 읽은 부분으로 주변 사람들과 가끔 대화를 했는데, 

대부분은 맞는 말이지만 아니라고 생각하는 부분들이 존재하기때문에 융통성있게 이점을 배워가면 될 듯하다.

+ Recent posts