개발팀장이 되면서 겪게된 점들 1

이미지
                                                         <팀원을 모집하기 위해 고군분투하는 모습이다. > 첫 한달  개발팀장을 맡다 2021년 5월 , 기존에 있던 CTO분이 휴직(개인사)을 하게 되면서    개발에 대한 모든 권한을 내게 일임하였다.   개발에 대한 모든 의사결정을 전부 내게 맡긴 것으로 ,   어느 정도 규모가 있는 회사의 의사결정권한을 갖게 된 것은 그만큼 내게 큰 신뢰가 있었음을   알수 있게해주는 대목이었다. 그러나 전혀 예측하지 않았던 상황이기에 준비가 되어있지 않았던만큼 처음에는 삐걱거렸다. 가장 첫번째로 어려움을 겪었던 것은 업무의 배분이었다.   관리자가 되니까 해야할일은 업무를 만들고 또 그것을 팀원들에게 분배하고 잘 되고있는지 취합하고 관리감독을 하는것이었다.   군 시절 장교로 복무하면서 겪어봤던 일이긴 했지만, 군복무 당시에도 그닥 잘 하지는 않았던 것 같다.   그럼에도 어쨌든 전반적인 시스템을 이해하고 있었고, 어떻게 구현해야할지에 대해서는 어느정도 경험이 쌓여있었기때문에 큰 문제가 없을 줄 알았다.   실무자로 일을 할 때에도 항상 업무를 받아서 하지는 않았다. 스스로 돌이켜보건대, 나는 주어진 업무가 없으면 스스로 만들어서 제안하고 기획하여 업무를 진행했다.  조그마한 스타트업이었던 첫 회사에서부터  내가 할일은 내가 만들어서 곧 잘했다. 어떤 큰 방향만 정해져있다면 그건 큰 어려움은 아니었다. 나에게 일은 항상 있었다.   매니저가되면서 달라진게있다면 내가 할일만 만드는 것은 아니라는 점이다 . 남이 할 일도 만들어줘야했다.  다행히 팀원들에 대한 면담을 실시한 결과,(팀원을 맡게되자마자 했던 부분)   마이크로 매니징을 원하지는 않았기때문에 큰 그림을 그리는 정도만 준비하면 됐었다.   문제는 내 실무를 동시에 진행하면서 팀원들의 업무 방향도 설정해야했기때문에 시간이 배로 들게 되었다는 점이다. 물론 두배로 일하지는 않았다. 대신에 내 실무시간을 줄였고

비전공자를 위한 자바스크립트 메모리에 대하여!!!

jsimg.png
자바스크립트를 사용하면서 메모리단에서의 동작 및 주의점에 대해 공부해 보았습니다.
일반적인 정적 언어와 달리 자바스크립트에서의 메모리 동작은 조금 다른 부분이 있어 궁금증을 가지고 구글링를 했습니다.
궁금한 대로 조사를 하다보니 순서가 조금은 뒤죽 박죽입니다.
틀린 부분이 있다면 언제든지 지적해주시기 바랍니다.

메모리?

프로그램이 동작하기 위해선 메모리가 필요하다. 재료를 손질하기 위한 도마가 필요하달까.
다수의 플립플롭 (flip-flop) 으로 구성된 메모리에는 바이트 단위로 데이터를 저장된다.
크게 코드영역, 데이터 영역, 콜스택 영역, 힙 영역 4가지로 나뉜다.
스크린샷 2018-07-15 오후 8.13.31.png
  • 코드영역은 우리가 적은 코드가 적재되는 영역이다. 컴파일이 끝난 기계어로 들어간다고 한다.
  • 데이터 영역은 전역 변수 static 변수들이 적재되는 곳이다. 프로그램이 끝날때까지 존재한다.
  • 스택 영역은 지역변수, 매개 변수들이 저장이 되는데 함수 호출시 사용되고 끝나면 반환된다.
    함수가 호출될때 그 메모리가 할당된다고 하여 콜 스택이라고 불리운다. 선입후출의 자료구조이다. 컴파일 시에 크기가 결정된다
  • 힙 영역은 메모리가 동적으로 할당되는 곳이다. 런타임시에 크기가 결정된다고 한다.
자 하나하나 궁금증을 풀어나가본다.

스택과 힙의 방향이 저렇게 자라나는 이유는 ?

스택의 bottom 쪽에는 프로그램이 닿아서는 안되는 커널 영역의 메모리가 들어간다고 한다.
커널과 닿을 수 없는 방향에 두어야해서 그렇게 된다는 썰을 보았다.
또 힙과 스택이 서로 자라나는 방향이 마주보게된다면 공유 라이브러리 영역의 메모리를 참조하는 곳이 가까워져서 효율적이라고 한다.

JS는 인터프리터언어잖아? 스택은 컴파일시에 그 크기가 결정된다고 하는데 , JS 스택 크기는 언제 결정되는 거야???

크롬과 Node js에서 사용되는 V8 엔진을 통해 알아보았다.
V8엔진은 JIT(Just In Time) 컴파일을 채택했다. 인터프리터와 정적 컴파일러의 중간단계라고 볼 수 있다.
인터프리터처럼 실행하면서 코드를 읽어들여 그에 대응하는 기계어 코드를 생성하고 , 해당 코드를 캐싱해둔다.
그 후에 코드를 읽어들이면서 전에 사용된 코드가 있다면 ? 캐시에서 가져다 쓰는 형식이다.
그럼 스택 크기를 컴파일 후에 결정 지을 수 있는가?
아닌 것 같다. JIT 컴파일이라는 것 자체가 실행단계에서 컴파일이 되는 것이기 때문에 미리 그 스택 크기를 결정 지을 수 없기 때문이다.
크롬과 Node에서 통상 1만개의 스택이 쌓이면 스택 오버 플로우가 일어난다고 한다.
즉 1만개 정도의 스택을 쌓을만한 크기를 결정 짓는다는 의미이다.
스택에 쌓이는 하나하나의 메모리를 스택프레임이라고 한다. 스택프레임 하나의 크기는 얼마나 되는지
그러니까 스택이 차지하는 총 용량은 어느정도일지 가늠해보려고 했는데 아무리 구글링을 해도 나오지가 않는다.
궁금하고 알고싶다...
스택이 총 얼마나 크기를 차지하는지 궁금하긴 하지만 크게 걱정하지 않아도 되는 듯 하다.
먼저 스택은 무한 재귀함수가 일어나지 않는 이상 오버플로우가 일어날 일이 거의 없다고 한다.
스택영역에서 사용되는 메모리의 경우 그 데이터의 크기가 크게 영향을 끼치지 않는다고 한다.
데이터는 힙 메모리 영역에 할당되고 그 포인터만 스택으로 가져와서 연산을 하기 때문이라고 한다.
1만개 정도 스택이 쌓일만한 크기라면 큰일나지 않는 다는 것이다.
더군다나 JS는 비동기 함수를 많이 사용하는데, 비동기의 경우 스택을 빠르게 비울 수 있다.
JS 는 싱글 쓰레드 기반이라고 잘 알려져 있다. 하지만 실상 돌아가는 구조를 보면 싱글 쓰레드라 보기도 어렵다.
스택에 해당 함수를 호출하여 넣는 것은 JS 엔진의 싱글쓰레드가 담당하는데, 만약 비동기 콜백을 만나게 되면 싱글쓰레드는 콜백으로 WEB API 나 엔진으로 넘겨준다. JS 싱글쓰레드는 계속해서 작업을 실행한다. 엔진의 이벤트 루프는 계속 돌고 있으며 엔진은 여러개의 쓰레드로 구성되어 있다.
말이 좀 어려운데, 어쨌든 스택오버플로우가 날 일이 거의 없다는 뜻이다. 다음에 이어지는 AOT 컴파일에서는 이러한 부분을 미연에 방지할 수 있다.
나는 지금껏 코딩을 하면서 한번도 스택오버플로우 에러를 만난적이 없긴 하다.

JIT 컴파일에 대해 알아가다보니... Angular 빌드시에 AOT 컴파일은 있던데 이 컴파일은 무엇일까?

ahead-of-time compile 의 약자로써, 우리말로 직역하면 사전 컴파일정도가 되겠다.
즉 미리 컴파일을 해놓는다는 의미이다. 인터프리터나 JIT 컴파일러가 컴파일 하기 좋은 중간단계의 언어로 바꿔놓는 것이다.
Angular 같은 SPA 개념에 잘 들어맞는 것 같다. 특히나 타입스크립트를 쓰고 있다면 보다 정형화되고 정리된 JS로 컴파일 해두는 것이 더 빨리 실행할 수 있는 트리거가 될 것이다.
다음은 Angular 공홈에 있는 AOT 컴파일의 장점이다.
왜 AOT로 컴파일합니까?
  • 보다 빠른 렌더링
    AOT를 사용하면 브라우저가 사전 컴파일(AOT) 된 응용 프로그램 버전을 다운로드합니다. 브라우저는 앱을 먼저 컴파일하지 않고 즉시 애플리케이션을 렌더링 할 수 있도록 실행 가능한 코드를 로드합니다. (즉 미리 컴파일 해놔서 브라우저 니가 컴파일 할 필요없어!)
  • 비동기 요청 감소
    컴파일러는 애플리케이션 JavaScript 내에서 외부 HTML 템플리트 및 CSS 를 인라인하게 부여하여 해당 소스 파일에 대한 별도의 ajax 요청을 제거합니다.
  • 다운로드 크기 감소
    앱이 이미 컴파일 된 경우 Angular 컴파일러를 다운로드 할 필요가 없습니다. 컴파일러는 Angular의 절반 정도이므로 컴파일러를 생략하면 응용 프로그램의 페이로드가 크게 줄어 듭니다.
  • 템플릿 오류를 더 빨리 감지
    AOT 컴파일러는 사용자가 빌드 단계를 볼 수 있기 전에 템플릿 바인딩 오류를 감지하고보고합니다.
    실제로 이 기능은 너무 강력하다. 자바스크립트에서 컴파일전에 에러요소를 확인할 수 있다는건.... 너무 좋다!
  • 보안 강화
    AOT는 HTML 템플리트와 구성 요소를 클라이언트에 제공되기 훨씬 전에 JavaScript 파일로 컴파일합니다. 템플릿을 읽을 필요가 없으며 위험한 클라이언트 측 HTML 또는 JavaScript 평가가 없으므로 주입 공격 기회가 줄어 듭니다.

js에서 메모리는 어떻게 관리되며 어떻게 동작을 할까?

프로그래밍에서 메모리관리는 다음과 같은 플로우로 진행이 된다고 한다.
[메모리 할당] = > [메모리 사용] => [메모리 해제]
  • 메모리 할당. JS에서는 메모리할당을 변수의 선언시에 자동으로 해준다. 즉 C언어처럼 프로그래머가 하나하나 명시적으로 해줄 필요가 없다는 뜻이다. 자 여기 메모리 할당 부분에서 궁금한 것이 많이 있었다.
  • 메모리 사용. 프로그램이 실제로 메모리를 읽거나 사용하는 단계이다
  • 메모리 해제. 메모리가 더 이상 필요로 하지 않는 경우 이 메모리를 해제해주어야 한다. 그래야 이 공간을 다른 메모리로 활용할수 있을 테니 말이다. C언어의 경우 명시적으로 해제를 해주어야 하지만, 고수준 언어들은 가비지 컬렉터라는 것이 있어 이 부분을 자동적으로 해결해준다. 이 부분도 궁금한 점이 많이 있었다.

정적할당? 동적할당? 그건 그렇고 자바스크립트는 프로토타입 기반에 타입도 명확하지 않는데 어떻게 메모리를 할당하지?

와... 진짜 헷갈리고 어려운 부분이다.
먼저 자바와 같은 정적타입의 언어를 살펴본다.
자바는 타입을 명확히 지정해준다. 즉, INT형식의 데이터가 배열로 얼마나 들어갈거야! 라고 말을 해줌으로써 메모리를 얼마나 할당할지 예측 할 수 있다. 인트를 4바이트 짜리 타입이라고 생각하고 다음 그림을 본다면.
스크린샷 2018-07-15 오후 9.16.58.png
자바에는 클래스가 있다. 클래스는 멤버 변수와 메소드를 담고 있는 하나의 설계도로 인스턴스를 생성할수 있게끔 해준다.
이 인스턴스는 클래스에 정의된 만큼 메모리를 할당할 수 있다. 클래스에 정의되고 나서부터 동적으로 클래스의 변수를 지우거나 삭제 할 수가 없기 때문이다.
이런식의 정적인 메모리 할당의 경우 컴파일하는데 시간이 좀 걸리긴해도, 그 속도가 빠르다.
하나의 배열로부터 정해진 타입의 Offset 만큼의 주소로 찾아가기 때문이다.
하지만 자바스크립트는 어떤가? 프로토타입 기반에 타입역시 동적으로 바뀔 수 있다.
그럼 메모리를 어떻게 할당한다는 거야????
스크린샷 2018-07-15 오후 9.24.14.png

스크린샷 2018-07-15 오후 9.23.27.png
첫번째, 자바스크립트는 배열은 길이와 타입이 정해져 있지 않아서 아무렇게나 들어갈 수 있다.
자바처럼 타입이 명확하다면 저 배열이 얼마나 메모리를 차지할지 알수 있겠지만... 저 상태라면 알수 있을까?
두번째, 자바스크립트 객체에는 멤버를 동적으로 할당 할 수가 있다.
그렇다면 객체가 차지한 다음에 오는 메모리를 두번째 그림과 같이 배열이 차지했다고 생각해보자.
그럼 객체에 동적으로 할당된 메모리는 어디에 저장될지를 알아 차릴 수가 없다...

그래서 자바스크립트는 모든 객체가 키 밸류 값으로 메모리 저장을 한다.

그렇구나!!! 이제 어떻게 메모리가 할당되는지 머릿속에 조금 이해가 된다.
그렇기 때문에 메모리 검색 속도에서 다른 언어들 보다 느리다고 한다.
그러나 V8 엔진은 그것을 극복하기 위해 다음과 같은 기법을 사용했다.
인라이닝과 히든클래스
인라이닝은 함수를 호출하는 부분을 그냥 호출되는 함수로 치환해버리는 기법이다.
예를 들어
function test(){
a+b
}

test();

가 실행되었다고 치면 인라이닝 기법으로 test() 부분은 실제 test 함수 코드로 대체되는 것이다.
//test()
a+b
가능한 한 많은 코드를 인라이닝 시킨다고 한다.
히든클래스는 자바스크립트가 클래스처럼 멤버에 접근 할 수 있게 해주는 장치이다.
자바스크립트는 객체가 생성이되면 키, 밸류로 메모리 위치가 형성이 된다고 위에서 이야기하였다. 해당 위치에 대한 해석이 필요없게끔 보이지 않는 클래스를 붙여주는데 (C0, C1의 이름으로 붙여진다. 타입스크립트를 컴파일하면 볼 수 있더라!) 여기에는 해당 변수로 접근 할수 있게끔 해주는 위치값이 이미 선정되어 들어가 있다.

자바스크립트 가비지 컬렉터

자바스크립트도 가비지 컬렉터가 있다. V8 엔진은 마크 앤 스윕 (mark-and-sweep) 기법을 통해 가비지 컬렉팅을 수행하는데,
점진적인 마킹을 이용한다고 한다. 즉, 어떤 객체를 치워야하는지에 대해서 전체를 훑어보는게 아니라 일부만 보고, 해당 위치를 기억해두었다가 다음 마킹작업때 그 위치로가서 다시 일부만 한다는 것이다. 가비지 컬렉팅이 일어나는 동안에는 자바스크립트는 수행을 멈춘다고한다. 즉 가비지 컬렉팅이 자주 일어나는 웹 어플리케이션의 경우 그 UX가 별로 유쾌하지 않을 수 있다는 것이다.
크롬 개발자 도구의 Performance 탭에서 가비지 컬렉팅이 얼마나 일어나는지 확인 해 볼 수 있다.
스크린샷 2018-07-15 오후 9.51.32.png
만약 내가 만든 어플리케이션에서 이런 톱니바퀴 모양의 그래프가 그려진다면 흠....코드를 다시 뒤엎으셔야 하겠다.
좋은 모양은 다음과 같다.
스크린샷 2018-07-15 오후 9.52.34.png
메모리 변동이 없도록 하는 것이다.

그렇다면... 가비지 컬렉팅이 언제 일어나는지를 알아야 저 상태를 막을 수 있겠지? 어떤 상태일까?

가비지 컬렉터는 할당된 메모리가 사용되는지 아닌지의 여부를 추정 한다고 한다.
응? 무슨 소린고 하니, 메모리의 필요 여부는 결정 불가능하다는 것이다. 슈뢰딩거의 고양이 같은 건가?
더 알아보니, 가비지 컬렉터의 알고리즘은 참조에 의존적이라고 한다.
참조란 다른 객체가 객체를 알고 있느냐의 의미이다. 이 참조 횟수를 계산하는 것은 가장 단순한 형태의 가비지 컬렉팅 알고리즘이다.
아무도 알아주지 않는 녀석들 데려다가 내보낸다는 이야기이다. 아싸는 어딜가도 환대를 받지 못하는 구나.
그렇다면 순환 참조라는 녀석은 어떨까? A가 B를 알고 B가 A를 알고있다. 이 둘은 서로를 알고 있기때문에 절대 참조 횟수가 0이 되지 않는다. 그렇다면 이 둘이 필요가 없어졌다면, 이녀석들은 메모리를 차지한채 눌러앉고 있을 것이다.
이런 불필요한 메모리 낭비를 메모리 leak 이라고 부르기도 한다. 이런 상황을 만들지 않는 것이 좋다.

마크스위프 알고리즘

참조 여부를 어떻게 알 수 있을까? 브라우저든 Node js 든 root가 되는 전역 객체가 있다.
이 객체로부터 시작하여 참조에 참조를 찾아 자식으로 쭉 타고 내려간다.
닿을수 없는 대상 unreachable 한 녀석들은 가비지 대상이 되어 마킹되고 스윕되어 OS에 메모리를 반환한다.
이를 통해 순환 참조가 일어나더라도 전역객체로부터 닿을 수 없는 녀석들은 가비지가 되어 반환 시킬 수 있다.

그럼에도 가비지 컬렉팅 시점을 개발자가 알 수는 없다.

어쨌든 비결정적이기 때문에 개발자가 가비지 컬렉팅 시점을 명확히 알수는 없다. 하지만 가비지 컬렉팅을 피하는 방법은
있다. 메모리 할당이 일어나고 그 메모리가 필요없어지는 경우를 만들지 않는 것이다.
간단하면서도 어려운 문제!
그러나 가비지 컬렉팅 자체가 엄청 나쁜건 아니다.
가비지 컬렉팅이 자주 일어나지만 않는다면 큰 성능이슈는 없는 듯 하니 안심해도 좋다. 위에서 언급한 톱니바퀴 패턴만 조심하자.
톱니바퀴를 해결하기 위한 방법으로 객체 풀 (Object Pool) 개념이 있다.
데이터 베이스의 커넥션 풀과 비슷한 개념으로, 사용하는 객체들을 미리 사전에 정해놓고 객체를 새로 만들때 힙영역에 동적으로 할당하는 것이 아니라 기존에 있던 객체들 중에서 안쓰는 객체들이 있으면 가져다 쓰는 형식이다.
장단점이 있다. 앞서말한 가비지컬렉팅 관련 이슈를 해결하는데 유용해보이지만 초기화 시점에 객체들을 준비하느라 시간이 더 걸린다는 점. 객체들이 탐욕스럽게 메모리를 차지 하기 때문에 적은 객체만 사용하는 경우에도 큰 메모리를 잡아 먹는다는 것이다.
객체 풀은 쓸 녀석들만 명확히 알고 있고 그것만 갖다쓸 자신이 있으면 매우 좋을 것같다.

마치며

어제 새벽부터 시작해서 오늘 밤까지 계속해서 조사하고 글을 구성했다.
완벽히 아는 것도 아니지만 알아가는 과정이 재미있긴 했다.
컴퓨터 지식에 대한 부분이 좀 더 성장함과 동시에 자바스크립트의 프로토 타입에 대해서도 좀 더 면밀히 공부할 시간이 되었기 때문이다.
다음에는 자바스크립트의 클래스는 있다 없다? 주제로 포스팅을 해볼까 한다. 이 부분도 좀 헷갈렸던 부분이기 때문이다.

댓글

이 블로그의 인기 게시물

iframe 보안 문제 우회 및 해결법 1

iframe 보안 문제 우회 및 해결법 2

개발팀장이 되면서 겪게된 점들 1