티스토리 뷰

728x90
SMALL

배경

타사 개발자분으로부터 공부할 자료를 추천받았다. 그리고 그것들중에 하나가 코드스피츠 OOP 영상 강의다. OOP는 평소에도 관심이 있었고 직접 추천해준 자료이기 때문에 무슨일이 있어도 보겠다고 생각했으며 몇분지나지 않아 정말 좋은 강의라는 것을 깨달았다.

서론

강사분은 flow, routine이라는 용어로 강의를 시작했다. 그리고 이 얘기를 하기까지 꽤 많은 배경설명을 한다. 그만큼 이 개념이 중요하다는 것이다. 나는 flow, routine이라는 용어를 알고 있지만 명확히 설명하지 못했다. 심지어 영상을 한번 보고 지금 블로그 정리를 할 때도 영상을 돌려봤다. 강사분은 명확히 설명할 수 없으면 모르는 거라고 말한다. 예전부터 듣던말이고 내가 가장 부족한 분야다. cs수업은 아니지만 기저에 깔려있는 것들부터 하나하나 설명해주는게 정말 괜찮았다. 저정도는 머리속에 이미 그려져있고 표현할 수 있어야 진정한 의미의 개발자인가? 하는 생각도 든다. 또한 서브 루틴을 설명할 때 상대성에 대한 얘기도 많이 한다. 자바스크립트 es6 코드로 설명해줘서 더욱 좋았던 것 같다. 이런 강의를 한국어로 그리고 자바스크립트를 기반으로 설명들을 수 있는 기회는 흔치 않다. 이걸 유튜브로 올려주신 것에 감사하고 자료를 공유하신 분께 다시 한번 감사 말씀을 드리고 싶다.

  • flow: 메모리에 적재되있는 명령이 순차적으로 alu로 인해 실행되는과정. 이 과정에는 아무도 개입할 수 없고 한번 적재된 메모리의 명령들은 한번에 실행이 된다.(sync)
  • routine : 플로우 그 자체. 한번만 실행하면 플로우, 여러번이면 루틴
  • sub routine : 메인루틴과 상대되는 개념. 절대적인 개념은 아님. 관점에 따라 서브루틴이 될수도 메인 루틴이 될 수도 있다.

본론

communicaate with routine

메인 플로우에서 루틴을 실행하는 것을 설명한다. 또한 지금까지 내가 생각하지 못한 것을 하나 얘기했다.

A = routineA(B) + routineA(C) + routineA(D);

이걸 보고 떠오르는게 있으신가요?? 나는 없었다. (반말 존댓말 죄송합니다...)
사실 어려운 코드는 아니다. 개발 1주일차도 아마 알것이다. 그냥 routineA함수에 B, C, D를 인자로 넣어서 실행하고 모든 값을 더한 값이 A에 할당된다. 이게 끝이다. 그런데 그건 개발 1주일 공부한 사람들도 생각할 수 있는 내용이고 진짜 개발자라면 다른 것을 느껴야된다.
조금 하나하나 나눠서 생각해보자. 이 강의에서는 루틴, 플로우라고만 얘기하고 실행컨텍스트나 자바스크립트의 동작 방식에 대해서는 얘기하지 않는다. 좀 더 원론적으로 사고하라고 의도해서 그런 것 같다. 먼저 routine(B)를 실행한다. 루틴에 인자를 넣어서 실행하고 루틴에서는 결과값을 return 받아서 정해진 위치로 전달한다. 일단 여기서 첫번째 힌트가 나왔다. 콜스택 같은건 생각하지 않아도 된다.(몰라도 된다는 건 아니다. 여기 강의를 들으려면 그정도 사전 지식은 있어야 편할 듯하다.) 그리고 routineA(C)를 실행하고 결과값을 정해진 위치로 받아온다. 왜 이런 당연한 얘기를 하는걸까??
다시 생각해보자. 결과값을 정해진 위치로 전달한다. 보통의 경우엔 변수에 할당하지만 지금같은 경우엔 바로 할당할 수 없다. 덧셈 연산자가 있으니 말이다. 즉 그말은 임시로 저장할 공간이 필요하다는 것이다. 즉 이건 메모리적으로 좋지 않은 코드다. 단, 일반적으로는 사용해도 상관없다. 일반 웹개발하면서는 생각할 일이 많이 있을까?? 싶다. 리액트, 뷰같은 라이브러리 프레임워크를 만든다면 필요하겠지만...
이런 연산자를 제거하는 것이 꼬리물기최적화의 핵심이 된다.

강사는 위 코드를 보고 이정도 생각을 당연히 해야한다고 말한다. 물론 신입 혹은 주니어 개발자에게는 쉽지 않을 것 같다. 다만 이런 관찰력이나 기본적인 원리 정도는 주니어에게도 필요하다. 생각나지 않았다면 반성하자. (반성중....)

sub routine in sub routine

이제 서브루틴안에서 서브루틴을 생각해보자. 함수를 생각하면 a함수에서 b함수를 호출하고 b함수에서 c함수를 호출하고 이런걸 생각하면 된다. 위에서 적은 내용을 바탕으로 일어나는 일을 해석해보자. 이렇게되면 a함수를 호출할 때 b함수를 기억하고 c함수를 기억(저장) 해야 한다. 이걸 자바스크립트에서는 콜스택이고 부른다. 메모리를 차지하기 때문에 스택을 적절히 정리해주는 것이 좋다. 물론 프론트 주니어 레벨에서는 쓸일이 거의 없을것이다.

value vs reference

call by value, call by reference. 이걸배우기위해 c언어를 배운다고 해도 과언이 아니다.(아닐수도??) 자바스크립트에서 call by ~~라고 말하지는 않지만 value와 reference는 구별되어서 존재한다. 간단히 말하면 value는 값이고 reference는 참조값이다. 더 풀어서 말하면 reference는 참조되는 주소를 가르키기 때문에

const ex={1:10};
const ex2=ex;

라는 코드를 작성했을 때 값이 복사되지 않고 가르키는 값만 같게된다. 그래서 ex2의 객체에 key, value를 추가하면 ex에도 영향이 있다. 더 정확히 말하면 참조값을 변경했고 ex와 ex2는 같은 값을 가르킬뿐이다.

너무 당연한 얘기를 했고 이제 플로우와 루틴 관점에서 바라보자. reference를 인자값으로 넘겨서 호출하고 플로우에서 값을 변경하면 어덯게 될까?? 당연하게도 변경이 된다. 이걸 지양하는게 함수형 프로그래밍의 기본이다. referece를 넘기면 결과를 예측하기 힘들다. 물론 무조건 좋지 않은것은 아니다. 상황에 따라 적절하게 사용해야 한다. 하지만 머리가 나쁜 나같은 보통의 프로그래머는 reference를 플로우를 통해 전달하고 직접 변경하는 것이 버그를 초래한다. 모든걸 기억하고 실수하지 않는 사람은 함수형프로그래밍이 필요없다. 더 나아가면 객체지향도 필요가 없다. 하지만 우리 모두 천재가 아니고 회사에서 협업을 하기 때문에 reference가 아닌 value를 다루는 것이 더 적절해보인다. 예시코드는 따로 적지 않겠다. es6부터는 좋은 기능들이 있기 때문에 reduce, map, filter, spread 연산자 등을 이용해 어렵지 않게 다룰 수 있다.(복잡하지 않은 배열의 경우) 아 그리고 객체를 새롭게 생성하면 성능 문제가 있을 수 있다. 그러나 페이스북팀에서 리액트 코드에 기여하는 정도의 사람이 아니라면 생각하지 말자.
마지막으로 value는 어떤걸 기준으로 정할까?? 숫자 문자열은 당연히 value인걸까?? 아니다. 그냥 언어를 개발하는 사람 마음이다. 자바는 문자열이 reference인것처럼 말이다. 여기서 다시 상기한다. 고정관념을 깨트리자. 생각을 유연하게

structured design

우리는 높은 응집도, 낮은 결합도를 목표로 해야한다. 주의해야 할점은 결합도와 의존성은 다르다. 또한 응집도를 높이면 결합도도 높아진다. ??? 잘못쓴게 아니다. 마치 코드퀄리티와 많은 기능의 차이같은 느낌?? 둘다 잡으면 좋지만 절충안을 찾아야한다.

COUPLING(결합도)

content(나쁨)

A클래스 속성이 변경되면 즉시 B 클래스가 깨진다.


const A = class {
  constructor(v) {
    this.v = v;
  }
};
const B = class {
  constructor(a) {
    this.v = a.v;
  }
};
const b = new B(new A(3));

common(나쁨)

common 클래스 변경 시 즉시 A,B 클래스가 깨짐


const Common = class {
  constructor(v) {
    this.v = v;
  }
};

const A = class {
  constructor(c) {
    this.v = c.v;
  }
};

const B = class {
  constructor(c) {
    this.v = c.v;
  }
};
const a = new A(new Common(3));
const b = new A(new Common(5));

external(나쁨)

A, B 클래스는 외부의 정의에 의존함. member의 json 구조가 변경되면 깨짐


const A = class {
  constructor(member) {
    this.v = member.name;
  }
};

const B = class {
  constructor(member) {
    this.v = member.age;
  }
};
fetch('/member')
  .them((res) => res.json())
  .then((member) => {
    const a = new A(member);
    const b = new B(member);
  });

DATA(좋음)

아래 코드에서 의존성을 살펴보자. 이 위의 결합은 사용하면 안되는 결합도다.


const A = class {
  add(count) {
    return count + 1;
  }
};

const B = class {
  constructor(counter) {
    this.counter = counter;
    this.data = { a: 1, count: 0 };
  }

  count() {
    this.data.count = this.counter.add(this.data.count);
  }
};

const b = new B(new A());
b.count();
b.count();

살펴보면 결합도(coupling)가 낮은 코드다. B가 A에 의존적이지만 메서드에서 referenc가 아닌 value값을 조정하고 있다. 이것도 결합도가 높다고 생각할지도 모르지만 이정도는 보통 허용한다. 그리고 아까 value vs reference를 얘기했는데 이번에도 value가 나왔다. 생략했지만 위의 결합도가 높은 코드에서는 객체를 넘기고 객체의 프로퍼티에 접근해서 값을 변경한다. 이렇게 보면 지금 패턴이 좀 괜찮아보이지 않나??

COHESION(응집도)

coincidential(나쁨)

아무런 관계가 없음. 다양한 이유로 수정됨. 우리가 흔히 사용하는 유틸함수를 생각하면 된다. 그냥 무지성으로 유틸함수에 때려박지 않는가?? 내얘기??

logical(중간)

사람이 인지할 수 있는 논리적 집합. 언제나 일부만 사용됨.

const Account = class {
  login() {
    p = this.ptoken();
    s = this.stoken(p);
    if (!s) this.newLogin();
    else this.auth(s);
  }
};

이 예시로 강사분은 math에 관한 예시를 들었다. sin, cos, random 등의 메서드가 있을 때 나는 math와 관련된것을 안다. 그런데 수학을 평생 공부해보지 않은 사람은 당연하게도 모른다. 다른걸로 예를들자면 주식에 관련된 것들을 모아놨을 때 주식을 아는 사람은 알지만 나머지 사람은 알지 못한다. 즉 경우에 따라서는 응집도가 있을수도 없을 수도 있다. 그래서 중간정도의 응집도를 가진다고 말한다.

temporal(중간)

시점을 기준으로 관계없는 로직을 묶음. 관계가 아니라 코드의 순서가 실행을 결정. 역할에 맞는 함수에게 위임해야 함.
나는 백엔드코드를 짤 대 로더를 만들고 처음에 실행할 로직을 묶어놨다. 딱 그경우다. 그런데 좋은 형태는 아니라고 말한다. 순서가 변경된다던가 이런 경우에 위험하기 때문이다. 동시에 가장 많이 사용되기도 한다. 좋지는 않지만 안쓸수는 없는?? 그런것같다.

procedual(중간)

외부에 반복되는 흐름을 대체하는 경우. 순서정책변화에 대응 불가

아래 코드는 토큰으로 로그인을 하는 예시코드다.

const Account = class {
  login() {
    p = this.ptoken();
    s = this.stoken(p);
    if (!s) this.newLogin();
    else this.auth(s);
  }
};

temporal보다 더 높은 응집도를 가진다. 변수에 저장하고 그 변수를 이용해서 로직이 진행된다. 하지만 순서를 변화하는데 대응할 수 없어서 이것도 중간단계다.

communicational(중간, 바람직)

하나의 구조에 대해 다양한 작업이 모여있음

const Array = class {
  push(v) {}
  pop() {}
  shift() {}
  unshift(v) {}
};

sequential(중간)

실행순서가 밀접하게 관계되며 같은 자료를 공유하거나 출력결과가 연계됨


const Account = class {
  ptoken() {
    return this.pk || this.pk === IO.cookie.get('ptoken');
  }
  stoken() {
    if (this.sk) return this.sk;
    if (this.pk) {
      const sk = Net.getSessionFromPToken(this.pk);
      sk.then((v) => this.sk);
    }
  }
  auth() {
    if (this.isLogin) return;
    Net.auth(this.sk).then((v) => this.isLogin);
  }
};

functional(좋음)

역할모델에 충실하게 단일한 기능이 의존성 없이 생성된 경우

앞으로 이수업에서 functional을 이해하고 사용하기 위한 수업을 한다.

또한 결합도와 응집도의 적절한 절충안을 충족시키게 만드는게 개발자가 하는 역할이라고 했다.

 

 

출처

https://www.youtube.com/watch?v=YsMhHGG-9Ow&list=PLBNdLLaRx_rKOFzA3txlG5rf9ZaVUuvmv&index=1&t=2313s

728x90
LIST
댓글
공지사항