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

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

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

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

그래서 그냥 궁금했던 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로 해야지..!!

클린코드 - 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문은 피해야한다.
  • 함수를 어떻게 짜죠?
    • 먼저 생각한대로 기록한 후 읽기 좋게 다듬는다. 초안은 대개 서투르고 어수선하므로 원하는 대로 읽힐때까지 다듬고 정리한다.

 

 

내 생각....

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

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

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

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

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

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

클린코드 - 02. 의미있는 이름

의도를 분명히 밝혀라

변수(혹은 함수나 클래스)

  • 존재이유?
  • 수행기능?
  • 사용방법?

이 모든 것을 답하기 위해 주석이 필요하다면 의도를 분명히 드러내지 못했다는 뜻.

 

그릇된 정보를 피하라

그릇된 단서는 코드의 의미를 흐린다

  • 나름대로 널리 쓰이는 의미가 있는 단어를 다른 의미로 사용해도 안된다
    • 실제 컨테이너가 List가 아닐 경우 List로 명명하면 그릇된 정보를 제공하므로 이렇게 명명하지 않는다
    • 실제 List여도 컨테이너 유형의 이름에 넣지 않는 것이 바람직하다
  • 유사한 개념은 유사한 표기법을 사용한다
    • 이름만 보고 정보를 추측하기 때문에 일관성이 떨어지는 표기법은 그릇된 정보다
    • 연관성이 없는 것에 대해 비슷한 단어를 사용했을 경우 연관이 있다고 생각하는 오류를 범하기 때문에 흡사한 이름을 사용하지 않는다.
  • I과 O는 숫자와 유사하기 때문에 그릇된 정보가 될 수 있다

 

의미있게 구분하라

단순 컴파일러나 인터프리터를 통과하는 네이밍은 문제를 일으킨다.

  • 연속적인 숫자를 덧붙이지 않는다
    • a1, a2, ....aN
    • 의도적인 이름과 정반대이며 아무런 정보를 제공하지 못하는 이름이다
    • 저자의 의도가 드러나지 않는다
  • 불용어를 추가한 이름은 아무런 정보도 제공하지 못한다
    • Info, Data는 a, an, the와 마찬가지로 의미가 불분명한 불용어이다
      • 접두어를 사용하지 말라는 소리가 아니다
      • Name, NameString에서 Name은 String일 확률이 대부분 이므로 불용어다
      • customerInfo - customer, accountData - account, TheMessage - message와 구분이 안되기 때문에 차이를 알 수 있도록 한다 

 

발음하기 쉬운 이름을 사용하라

발음하기 어려운 이름은 토론하기도 어렵다

  • 긴 단어를 합쳐서 줄임말로 쓸 경우 의사소통에 문제가 생길 수도 있다

 

검색하기 쉬운 이름을 사용하라

문자 하나를 사용하는 이름(짧은)보다 길더라도 검색해서 찾아내기 쉬운 이름이 좋다

  • e로만 검색할 경우 무수히 많은 검색 결과가 나온다
  • 버그가 있을 경우 찾기 쉬운 이름이 짧은 이름보다 때론 좋다

 

인코딩을 피하라

  • 헝가리식 표기법
    • 에디터 등의 발전으로 변수 이름에 타입을 인코딩할 필요가 없다
    • 타입을 바꾸기가 어려워지고 읽기도 힘들다
    • 독자를 오도할 가능성도 커진다
  • 멤버 변수 접두어
    • 클래스와 함수는 접두어가 필요없을 정도로 작아야한다
  • 인터페이스 클래스와 구현 클래스

 

자신의 기억력을 자랑하지 마라

  • 코드를 읽으면서 변수 이름을 자신이 아는 이름으로 변환해야한다면 바람직하지않다
    • 일반적인 변수 이름이 아니었기에 생기는 문제
  • 문자 하나만 사용하는 변수 이름은 문제가 있다
    • 루프에서 범위가 작고 다른 이름과 충돌하지 않을 때는 괜찮다
      • 루프에서 반복 횟수 변수는 전통적으로 한 글자를 사용하기 때문에...
    • a와 b를 사용한다고 가정했을때 c를 선택해 사용한다면 최악이다

 

클래스 이름

  • 명사나 명사구가 적합하며 동사는 사용하지 않는다.
  • Manager, Processor, Data, Info 등과 같은 단어는 피한다

 

메서드 이름

  • 동사나 동사구가 적합
  • Javabean 표준에 따라 값 앞에 get, set, is를 붙인다

 

기발한 이름은 피하라

  • 의도를 분명하고 솔직하게 표현해야한다

 

한 개념에 한 단어를 사용하라

  • 추상적인 개념 하나에 단어 하나를 선택해 이를 고수한다
    • fetch, retrieve, get | controller, manager, driver으로 제각각 쓰면 혼란스럽다
  • 이름이 다를 경우 당연히 클래스, 타입도 다르다 생각한다

 

말장난을 하지마라

  • 한 단어를 두 가지 목적으로 사용하지 마라
    • add가 하나의 값에 더한다는 의미라면 집합에 값을 추가하는 경우에는 add보다 insert나 append라는 이름을 사용한다

 

해법 영역에서 가져온 이름을 사용하라

기술 개념에는 기술 이름이 가장 적합한 선택이다

  • 전산 용어, 알고리즘 이름, 패턴 이름, 수학 용어 등을 사용해도 괜찮다

 

문제 영역에서 가져온 이름을 사용하라

'프로그래머 용어'에 없다면 문제 영역에서 이름을 가져와도 괜찮다

 

의미있는 맥락을 추가하라

스스로 의미가 분명한 이름이 없지 않다

  • 접두어를 붙인다
    • firstName -> addrFirstName이라는 접두어를 통해 주소의 일부라는 사실을 분명히한다

 

불필요한 맥락을 없애라

애플리케이션 네임을 모든 클래스의 이름의 시작에 넣는 것은 바람직하지 않다

 


 

 

문헌

클린코드 - 로버트 C. 마틴

클린코드 - 01. 깨끗한 코드

원초적 난제

기한에 맞추려면 나쁜코드를 양산할 수 밖에 없다고 느낄 것이다.

하지만 코드를 최대한 깨끗하게 유지하는 습관이 기한 맞추는 유일한 방법일지 모른다.

 

깨끗한 코드라는 예술?

"깨끗한 코드가 무엇일까?"

"어떻게 작성할까?"

'깨끗한 코드'가 무엇인지 조차 모른다면 노력해봤자 소용없다.
하지만 이 말이 '깨끗한 코드'와 '나쁜 코드'를 구분할 줄 알아야만 '깨끗한 코드'를 작성할 수 있다는 의미는 아니다.
‘코드 감각’이 있다면 개선할 방안이 떠오를 것이다.

다만 '코드 감각'은 누군가는 타고날수도 있고 누군가는 경험을 통해 얻을 수도 있다. 

 

깨끗한 코드란?

비야네 스트롭스트룹

"우아하고 효율적인 코드를 좋아한다."

우아하고 효율적인 코드를 좋아한다. 효율적인 코드는 단순 속도만을 의미하지 않고 CPU의 자원또한 포함할 것인다.

함수가 너무 많은 일을 할 경우 의도가 뒤섞이고 목적이 흐려진다. 한가지를 제대로해야하며 논리가 간단해야 버그가 숨어들지 못한다. 또한 서로에 대한 의존성을 줄여야 유지보수가 쉬워진다.

많이 놓치는 오류처리, 메모리누수, 경쟁상태, 일관성 없는 명명법 등도 신경써야하며 오류는 전략에 의거해 처리한다. 

성능을 최적으로 유지해야만 원칙없는 최적화를 하지않게된다. 원칙없는 최적화는 코드를 망칠 확률이 있다.

 

그래디 부치

"깨끗한 코드는 단순하고 직접적이다. 
가독성을 강조하고 잘 쓰여진 문장처럼 읽혀야한다."

깨끗한 코드는 단순하고 직접적이다. 

가독성을 강조하여 잘 쓰여진 문장처럼 읽혀야하며 설계자의 의도를 숨기지 않고 명쾌한(사실에 기반하여) 추상화와 단순한 제어문을 사용한다.

*명쾌한: 힘차고 단호하고 사실적인

 

데이브 토마스

"테스트 없는 코드는 깨끗한 코드가 아니다."

작성자가 아닌 사람이 읽기는 물론 고치기도 쉬울만큼 가독성 있는 코드가 깨끗한 코드이다.

읽기 쉬운 것과 고치기 쉬운 것은 엄연히 다르다.

테스트 주도 개발(TDD)이 오늘날 가장 근본적인 원칙 중 하나가 된 것을 보아라, 테스트 없는 코드는 깨끗한 코드가 아니다.

- 의미 있는 이름을 붙이고 목적을 달성하는 방법을 하나만 제공하라.

- 의존성은 최소로하고 각 의존성을 명확히 정의한다.

- API는 명확하며 최소로 줄인다. 최소는 큰 코드보다 작은 코드에 가치를 둔다는 의미로 코드는 작을 수록 좋다.

- 언어마다 모든 정보를 코드만으로 표현할 수 없기에 코드는 문학적(인간이 읽기 좋게)으로 표현해야한다


마이클 페더스

"깨끗한 코드란 언제나 누군가 주의 깊게 짰다는 느낌을 주며
고치려봐도 손 댈 곳이 없다."


론 제프리스

"중복이 없으며 간단한 코드, 단순한 코드 규칙으로 시작하고 끝낸다."

초반부터 간단한 추상화를 고려하며 시스템 내 모든 설계 아이디어를 표현한다.

의미 있는 이름으로 명명하며 객체나 메서드가 여러 기능을 수행한다면 여러개로 나누고 클래스, 메서드, 함수 등을 최대한의 크기로 줄인다.

모든 테스트를 통과해야한다.

 

워드 커닝햄

"코드가 그 문제를 풀기위한 언어처럼 보인다면 아름다운 코드라 불러도된다."

코드를 읽으면서 짐작했던 기능을 각 루틴이 그대로 수행한다면 깨끗한 코드다.

 

우리들 생각

"깨끗한 변수 이름, 함수, 클래스"

 

 

결론

책을 읽는다고 뛰어난 프로그래머가 된다는 보장은 없다.

그저 뛰어난 프로그래머가 생각하는 방식과 그들이 사용하는 기술, 기교, 도구(경험적 교훈, 체계, 절차, 기법 등)를 소개하는 것을 보고 난 이후는 본인들이 하기 나름이다.

 


“연습해, 연습!”

 


문헌

클린코드 - 로버트 C. 마틴

+ Recent posts