크리에이티브 프로그래머 (2)

Uno·2023년 11월 3일
0

Book

목록 보기
7/9
post-thumbnail

최적화된 제약조건: 스위트 스폿

P140

최적 조건을 스위트 스폿이라고 부릅니다.

초보자와 창의성 전문가는 제약 조건을 다르게 처리할 가능성이 높습니다. 모든 창의성과 마찬가지로 제약 조건의 한계는 개인적이며 지속적인 변화에 내재되어 있다는 점을 명심해야 합니다.

여기서 말하는 최적의 조건은 제약이 너무 강해서, 행동이 제약되어선 안된다. 동시에 제약이 너무 느슨해서 문제 해결 방법이 떠오르지 않을 정도는 아니여야 한다.

그러면, 최적 조건인 스위트 스폿의 예시가 어떤 것들이 있을까? 가장 대표적인 예시가 "게임" 일 것이다.

테트리스를 예시로 생각해보자. 테트리스의 제약 조건은 다음과 같다.

  • 블록의 형태와 크기 : 블록의 정해진 형태와 크기를 가지고 있다. 그리고 변경 불가하다.
  • 시간 제한 : 블록은 상단에서 하단으로 떨어진다. 시간이 지날수록 빨리 떨어진다.
  • 공간 제약 : 게임보드의 폭은 제한되어 있다.
  • 줄 완성 : 플레이어는 완전한 수평선을 형성해야 한다.

테트리스의 스위트 스폿은 다음과 같다.

  • 난이도 : 초보자는 낮은 난이도로 시작한다. 블록이 떨어지는 속도가 느리다. 그리고 점점 빨라지도록 난이도를 조절한다. 이 과정에서 사용자는 특정 난이도까지 계속 도달하게될 것이다. 자신의 스위트 스폿을 찾아가게 되는 설계이다.

이러한 스위트 스폿은 플레이어가 지루함을 느끼지 않지만, 도전적인 환경을 유지한다. 동시에 포기하지 않도록 하는 것이 중요하다.

개발을 예시로 생각해보자.

제약조건

  • 성능 제약 조건 : 모든 앱은 제한된 메모리와 처리 능력 내에서 실행되어야 한다.
  • 개발 시간과 예산 : 프로젝트의 범위와 관련된 시간과 자금 내에서 개발해야 한다.
  • UI & UX : 디자인 가이드라인을 준수하며, 사용자에게 직관적이고 반응이 빠른 인터페이스를 제공해야한다.
  • 코드의 유지 보수성과 확장성 : 앱은 미래의 요구사항에 맞춰서 수정하거나 확장 가능해야한다.

스위트 스폿

  • 효율적인 아키텍처 선정 : 유지보수성과 확장성을 고려하며 테스트 커버리지를 준수한다.
  • 라이브러리 선택 : 개발 시간과 예산을 지키도록 도움을 얻되, 외부 종속성으로 인한 복잡성을 피한다.
  • 성능 최적화 : 디버깅툴을 통해서, 랜더링 퍼포먼스나 메모리 사용량을 성능제약조건에 만족시킨다.
  • 코드 품질 유지 : SOLID 원칙을 준수하도록 코드를 작성한다.

예시가 그리 참신하지는 않은 것 같다.


버그는 기회다.

p176

예상치 못한 동작이 발생하면 우리는 작업을 멈추고 생각하게 되고 디버깅하게 됩니다. 노스는 이를 우연한 발견이라고 부릅니다.

그러나 우리는 이러한 우연한 학습의 순간을 의도적인 학습의 순간으로 바꾸려는 노력을 거의 하지 않습니다.

소크라테스의 '철저하게 의식적인 무지'는 모든 진정한 과학 발전의 전주곡이자 창의적인 돌파구의 서곡입니다.

창의적은 프로그래머는 자신의 무지에 대해 주의를 기울입니다. 진정한 소크라테스적 방식으로, 그들은 자신이 무엇을 모르는지 알고, 그것이 앞으로 나아가는 데 도움이 된다면 적극적으로 수정합니다.

이 책에서는 우연한 학습의 순간은 언제든 발생하지만, 그 기회를 잡는 것은 무지의 지 라는 의식적인 노력이 필요하다고 한다.

무지의 지는 플라톤이 대화편에서 소크라테스의 행동을 보면 알 수 있다. 소크라테스는 자신이 아는 것온 내가 모르는 것이라고 말한다. 그래서 어떤 주제나 개념에 대해서 자신이 안다고 하는 사람들을 찾아다니며, 질문을 한다. 이 질문 방식이 "산파술" 이라고 하는 질문법이다.

개발을 열심히 하다가 버그가 발생했다고 가정하자. 직사각형의 Container 안에 선을 긋고, 그 선 그은 이미지를 프레임워크에서 제공해주는 API 를 이용해서, 블러처리를 해야하는 로직을 구현하고 있었다. 그런데, 선을 1~10 개 까지는 특별한 버그가 없었다. 그러다가 20개 이전에 갑자기 앱이 크래시가 발생한다. 코드의 호출에는 문제가 없다. 그리고 크래시 리포트를 봐도 특별한 점은 없고, 그냥 에러가 뜬다.
iOS 개발자라면 한 번쯤 봤을 Thread 1: signal SIGTERM

cf) SIGTERM 은 terminate signal의 약자이다. 운영체제에서 프로그램을 강제종료하기 전에 호출하는 신호라고 보면 된다.

위 개발내용을 정리하면, 특정 로직을 동작시키고 있었고, 운영 체제에서 강제로 종료했으나, 어떤 디버깅 메시지도 주지 않은 경우이다.

그 당시, 내가 했던 활동은 다음과 같다.

  • 일단 에러가 어디에서 정확히 발생하는지 규정한다. 정상적인 로직을 메서드로 분리해서 더이상 신경쓰지 않도록 한다.
  • 어디서 발생하는지 알았다면, 그 부분에서 에러가 안생길만한 코드를 호출해보고, 그래도 에러가 발생하는지 안하는지 확인한다. 만약 코드 변경 시, 에러 발생이 멈춘다면 해당 코드가 에러가 맞는 것이다.
  • 그 코드에서 에러가 발생한 이유가 무엇인지는 모르겠으나, 운영체제에서 강제로 종료시킨 것이다. 그러면, 운영체제의 역할범위에서 무언가 통제했다고 추론할 수 있다.
  • 운영체제는 주로 아래와 같은 역할을 한다.
    - 인터페이스 제공
    - 시스템 자원 관리
    - 파일 관리
    - 메모리 관리
    - 프로세스 관리
  • 지금 케이스는 운영체제게 강제로 프로세스를 종료한 것이다. 그러므로 프로세스가 종료되었을 만한 가능성을 고려해본다.
    - 해당 프로세스에 메모리 누수가 있는가?
    - 프로세스가 실행 중에 과도한 CPU 자원을 사용했는가?
    - 운영체제의 로그에서 에러 메시지를 찾을 수 있는가?
    - 최근에 시스템이나 프로세스에 변경사항이 있었는가?
    - 이 프로세스와 상호작용하는 다른 시스템 컴포넌트에 문제가 있는가?
  • 이것들 중에서 첫 번째나 두 번째라고 판단했다.
  • 그리고 해당 로직을 Background 로 이동시켜서 실행했다. iOS 프로그래밍에서 그 당시 DispatchQueue 라는 API 를 통해서 진행했다.
  • 그리고 메모리 누수의 우려로, 재할당이 아닌 값의 변경을 하도록 코드를 수정했다.

이 문제가 발생할 당시는, 상당히 골치아픈 문제였다. 이 책을 읽고 돌이켜보면, 우연한 기회라는 버그가 있었고, 그것을 해결하면서, 여러 가지 창의적인 해결방법을 생각하는 기회가 되었다. 이 떄, 내가 CS 지식이 필요한 이유를 느낄 수 있었다.


직접 증명해보면서 공부하기

p179

string 변수와 int 변수를 연결하면 문자열 변수는 당연히 정수로 변환되지! 이 루프 안에서 큰 수를 가지고 있으면 boxing(복싱)효과 때문에 성능 문제가 발생할 텐데, 정말 그런지 확인해볼 필요도 없다고..

Dart 언어는 Java 언어에 있는 Boxing 과 Unboxing 이 일어나는 조건이 없다. 그냥 모든 것은 Object 이다. 그래서 확인할 수 있는 것은 객체 생성에 따른 성능저하뿐이다. 아래 코드를 실행하면 결과가 다음과 같이 나온다.

void main() {
  // Stopwatch를 사용하여 더 정밀한 시간 측정
  var stopwatch = Stopwatch()..start();

  for (int i = 0; i < 10000000000; i++) {
    var x = i; // 기본 타입을 사용하여 변수 할당
  }

  print('기본 타입 할당에 걸린 시간: ${stopwatch.elapsedMilliseconds}ms');
  stopwatch..reset()..start();

  for (int i = 0; i < 10000000000; i++) {
    var x = BoxedInt(i); // 객체를 생성해서 변수를 할당
  }

  print('객체 생성에 걸린 시간: ${stopwatch.elapsedMilliseconds}ms');
  stopwatch.stop();
}

class BoxedInt {
  int value;
  BoxedInt(this.value);
}
기본 타입 할당에 걸린 시간: 10319ms
객체 생성에 걸린 시간: 10373ms

호기심은 어디서 오는가

p191

6.1 호기심은 창의력의 원동력

세 가지 사례 모두 다른 사람들의 이야기와 제국의 역사, 자연의 진화와 종의 기원, 하드웨어의 내부 동작에 대한 많은 호기심을 보여줍니다.

미하이 칙센미하이가 창의적인 천재들을 인터뷰한 결과, 창의적인 성공을 위한 가장 중요한 두 가지 특성으로 호기심과 인내심을 꼽았습니다.

호기심이 창의성에 중요하다는 이야기는, 어디서 들었는지는 기억이 안나지만 정말 많이 들었다. 그렇다면, "호기심" 은 어떻게 생기는 걸까?

호기심이란, 새로운 지식이나 새로운 경험을 하고싶어하는 욕구이다. 호기심의 발생은 정보 차이 (Knowledge Gap OR Information Gap) 차이가 발생할 때 발생한다고 한다. 여기서 정보는 인지적 혹은 감각적인 정보를 의미한다.

여러 가지 호기심 중에서 '지적 호기심'

지적 호기심은 알고 있는 것과 알고 싶은 것의 차이를 줄이기 위해 새로운 지식을 얻고자 하는 욕망을 말한다. 과학 이론 / 퀴즈 와 같이 복잡하고 모호한 자극에 의해 유발된다.


고정형 사고방식으로 파악하고, 성장형 사고방식으로 행동하자

p192

6.2.1 고정형 사고방식과 성장형 사고방식

고정형 사고방식은 이미 결정되어 있고, 그것을 바꿀 수 없다고 개인을 바라보는 사고 방식이다.
성장형 사고방식은 결정되어 있을 수 있든지 없든지, 내가 노력하면 성장하여 도달가능하다는 사고 방식이다.

이 두 사고방식은 자기계발서에서 정말 많이 나오는 사고 방식이다. 개인적인 의견은 둘 다 필요하다고 본다. 그래도 우세한 것은 성장형 사고 방식이다.

고정형 사고방식의 관점에서 현재 상태를 정확히 진단을 한다. 그렇게 나의 현황을 파악하고 행동의 단계에서는 오직 성장형 사고방식으로 행동한다. 예를들면, 내가 프로그래밍을 잘하고 싶은 상황이다. 내가 현재 코딩공부한 적이 없고, 남들보다 늦게 시작했다. 이 상황은 나에게 상대적으로 불리하고 쉽게 따라 잡을 수 있는 영역은 아니다. 그럼에도 불구하고 내가 코딩을 하고 싶다면, 따라 잡기 위한 플랜을 구체화한다. 그리고 행동에만 집중하는 것이다.


내재적 동기만으로 열정을 유지하긴 어렵지 않을까?

p205

6.4.3 내재적 동기와 외재적 동기의 결합
일부 소프트웨어 시스템은 내재적 열정 프로젝트로 시작하지만, 시간이 지남에 따라 이러한 동기가 점차 약해질 수 있으므로 외재적 동기를 강화하는 것도 나쁘지 않다는 점은 흥미롭습니다.

이 이야기에 100% 동감한다. 기억은 안나는데 어떤 책에서는 내재적동기의 중요성만 말했다. 그런데, 내 경험상 내재적 동기만으로 열정을 유지하는 것은 어려웠다. 내재적 동기만 있어서도 안되고 외재적 동기만 있어서도 안되었다. 둘 다 있어야한다. 내가 아무리 연기자가 되고 싶어서 연기를 하더라도, 너무 긴 기간동안 생활고를 겪는다면, 유지하는 것이 불가능할 것이다. 먹고는 살아야하니까.

개발도 마찬가지이다. 아무리 코드를 작성하고 켬퓨터를 다루는 것이 재미있다고 하더라도, 내 생계가 해결되지 않거나, 인정욕구를 충족시키지 못한다면, 열정이 유지는 되더라도 점점 식지 않을까 생각한다.


Connecting the dot

p206

6.5 다중 잠재력
진정으로 창의적인 사람들의 호기심은 한 분야에만 국한되지 않습니다.

프로이트는 여러 가지 다양한 경험을 했는데, 이러한 경험들은 인지적 유연성을 높여주는 것...

이와 유사하게 정신적 단일 작물 재배는 우리의 인지 건강에 좋지 않은 영향을 미칩니다.

다중 잠재력이라는 글을 볼 때, 책 중에서 "폴리매스" 라는 책이 떠올랐다. 폴리매스를 간단히 설명하자면, 다재다능한 사람이다. "아리스토텔레스" 와 같은 사람은 물리학, 생물학 그리고 정치학 등을 모두 업적을 남긴 것처럼. "레오나르도 다 빈치" 처럼 예술에 뛰어남과 동시에 헬리콥터나 수중복을 발명하기도 하는 경우이다.

나는 각 분야에 10% 정도 해당하는 지식을 가지게 되면, 폴리매스라고 칭하고 싶다. 이런 환경이 다중 잠재력이라고 말할 수 있다고 생각되고, 이 때 창의성을 가질 확률이 올라갈 것 같다.

나의 사례를 들면, 농구를 선수급으로 잘하진 않지만, 아마추어에서는 열심히 했었다. 교내 대회나 외부 대회도 참여할 정도로 열심히 했다. 이 때 익힌 스포츠를 배우는 패턴이 생겼다. 예를들어, 내가 공을 가지고 할 수 있는 선택지는 3 개이다: 슛, 드리블 그리고 패스. 3 가지 경우의 수를 상대방보다 반박자 이상 빠르게 행동하는 것이 농구에서 중요했다.

요약하면, 다음과 같다.

농구를 통해 득점하는 방식은 슛, 드리블 그리고 패스를 해야한다. 그리고 이것을 상대방보다 반박자 이상 빠르게 행동한다.

이런 지식이 다른 E-Sport 에 적응하는데 도움이 되었다. 국민 게임인 "리그 오브 레전드" 또한 농구와 유사하게 팀 스포츠이다. 리그 오브 레전드에서 다른 부분들도 있지만, 나의 공격을 맞추거나 도주하거나 하는 득점해야하는 상황에서는 비슷하게 동작했다.

리그 오브 레전드 : 스킬을 좌측, 우측 아니면 중앙 에 맞춘다. 동시에 상대방이 반응하기 전에 맞춘다.

상대방과 경쟁하는 스포츠에서는 위처럼 특정 제한된 규칙이 있는 경우, 유사하게 동작했었다.
cf0 스포츠; 규칙을 설정하고, 규칙 내에서 득점을 하는 활동

다른 사례로는 개발을 공부할 때, 철학책이나 문학책을 읽었던 경험이 떠올랐다. 개발의 특정 개념들을 공부할 때, "약속" 에 의해 구성된다는 점이 특히 그랬다. 철학책에서는 용어에 대한 정의를 이해하고, 저자가 이야기하는 논증을 이해한다. 만약 정의에 대해서 먼저 공부하지 않으면, 같은 책을 읽고 다르게 이해할 확률이 올라간다. 문학책 중에서 '시' 도 유사하다. 문학책은 결국 사람 이야기를 하려고 하는 것이 대다수인데, 화자가 어디에 있는지 찾아야 한다. 그리고 그 화자가 어떤 상황에서 어떤 감정표현을 하는지 찾으면 독해하기 쉽다.

개발공부도 유사하게 느껴졌었다.

  • 객체가 어떤 역할을 하는지 확인한다 -> 인터페이스 확인
  • 객체가 어떤 원리로 동작하는지 확인한다. -> 정의 확인
  • 객체가 어떤 맥락에 놓여있는지 확인한다. -> 맥락 이해
  • 객체가 최종적으로 해결하고자 하는 바를 확인한다. -> 결론

철학

  • 저자가 사용하는 단어의 정의를 확인한다. -> 정의 확인
  • 저자가 어떤 서사적(혹은 학문적) 상황에서 이 주장을 했는지 확인한다. -> 맥락이해
  • 저자의 주장이 무엇인지 확인한다. -> 결론

문학

  • 화자가 어디에 비유되었는지 찾는다. -> 정의확인
  • 화자가 어떤 상황에 놓여있는지 확인한다. -> 맥락이해
  • 화자가 어떤 감정을 느끼고 있는지 확인한다. -> 결론

이런 경험들이 틀릴 확률이 있다. 그래도 상관없다고 생각한다. 왜냐하면, 이것을 아무리 설명해봐야, 나와 정확히 같은 경험으로 살아갈 수 있는 사람은 없다. 하지만, 이런 경험들을 엮는 과정이 앞으로 있을 무의미해보이는 경험들에 의미를 부여하는 기회는 된다고 본다.

profile
iOS & Flutter

0개의 댓글