[강의]https://www.youtube.com/watch?v=_Ms1Z4xfqv4&feature=emb_title
[강의 자료]https://missing-semester-kr.github.io/2020/metaprogramming/
[참고]https://m.blog.naver.com/PostView.nhn?blogId=scw0531&logNo=221305718407&proxyReferer=https:%2F%2Fwww.google.com%2F
메타프로그래밍(metaprogramming)?
“프로세스(process)” 에 더 관련된 것들을 두루 일컫는 총칭.
“프로그램들을 대상으로 작동하는 프로그램들"을 의미할 수도 있다.
대부분의 프로젝트들에는, 그것들이 코드를 포함하든 포함하지 않든, “빌드 프로세스“라고 하는 것이 있다. 이는 인풋(Input)에서 아웃풋(Output)으로 가기 위해서 수행할 필요가 있는 몇몇 순차적인 작업들을 뜻한다.
종종, 그 프로세스에는 많은 단계들과 많은 브랜치(branch)들이 있을 수도 있다.
도구를 빌드 도구, 빌드 시스템이라고 한다.
[참고] http://developinghappiness.com/?p=26 [빌드 시스템]
make는 소프트웨어 개발을 위해 유닉스, 리눅스 계열 운영 체제에서 주로 사용되는 프로그램 빌드 도구이다. 주어진 규칙에 따라 작성된 입력 파일의 내용을 바탕으로, 자동으로 일을 수행한다. make를 바탕으로 여러가지 작업들을 할 수 있지만, 개발자는 소스코드를 컴파일하고 링킹하여 최종결과물을 만드는데 중요하게 이용한다.
왜 Makefile을 사용할까?
프로젝트의 규모가 커지면 최종 결과물들을 빌드하는데 시간도 오래 걸리기 때문에, 수정된 소스 파일만 새로 컴파일하는 기능이 필요하게 된다.
paper.pdf: paper.tex plot-data.png
pdflatex paper.tex
plot-%.png: %.dat plot.py
./plot.py -i $*.dat -o $@
만들 파일(타겟 target) : 필요한 재료들(의존성 dependencies)
.....(들여쓰기) 프로그램의 순서
첫번째 명령(directive)는 최종 타겟의 초기값(default)를 정의한다.
아무 인자도 없이 make를 실행하면 초기값이 최종 타겟이 된다.
make plot-data.png와 같이 만들 파일 이름을 인자로 주어서 실행하면 make는 그 타겟을 빌드한다.
우변의 규칙(rule)에 있는 %는 문자열의 변수같은 것.
입력값을 받았을 때 %에 들어온 문자열을 바탕으로 의존성 파일을 검사한다.
ex) 타겟 plot-foo.png가 요청되면 make는 foo.dat과 plot.py 의존성 파일들이 있는지 검사한다.
$ touch paper.tex
$ make
make: *** No rule to make target 'paper.tex', needed by'paper.pdf'. Stop.
더 거시적인 수준에서는, 여러분의 소프트웨어 프로젝트들은 또 다른 프로젝트들에 의존하고 있을 수 있습니다.
여러분은 설치된 프로그램(예: 파이썬(python)), 시스템 패키지(예: openssl), 또는 여러분이 사용하는 프로그래밍 언어 안에 있는 라이브러리(예: matplotlib)에 의존하고 있을지도 모릅니다.
요즘에는, 대부분의 의존성들이 레포지토리(repository) 를 통해서 가능할 텐데, 레포지토리는 단일한 공간에서 수많은 의존성들을 관리하고, 그것들을 설치할 수 있는 편리한 방법을 제공합니다.
레포지토리 예시:
우분투 시스템 패키지(Ubuntu system packages)를 위한 우분투 패키지 레포지토리(the Ubuntu package repository) - apt 툴(tool)을 통해 접근할 수 있음
프로그래밍 언어 루비(Ruby) 라이브러리들을 위한 루비젬(RubyGem)
파이썬(Python) 라이브러리들을 위한 파이파이(Pypi)
아치 리눅스 사용자-기여 패키지들을(Arch Linux user-contributed packges) 위한 아치 사용자 레포지토리(Arch User Repository)
버저닝(versioning)은 프로젝트가 다른 프로젝트의 어떤 특정한 버전, 또는 버전들의 범위에 의존하는지를 나타내도록 함으로써 이러한 문제를 해결하려 시도합니다.
그런 식으로, 비록 라이브러리에 변화가 생길 지라도, 그 라이브러리에 의존하는 소프트웨어는 그 라이브러리의 더 예전 버전을 사용함으로써 계속 빌드할 수 있게 됩니다.
[참고] https://velog.io/@slaslaya/Semantic-Versioning-2.0.0-MAJOR-MINOR-PATCH%EC%99%80-%EB%AA%85%EC%84%B8%EC%97%90-%EA%B4%80%ED%95%98%EC%97%AC [시멘틱 버저닝]
Major, Minor, Patch
Minor 버전이 올라가면 Patch는 0으로 초기화되어야 하고,
Major 버전이 올라가면 Minor과 Patch는 0으로 초기화되어야 한다.
만약 내 소프트웨어가 어떤 사람의 특정 버전의 라이브러리(소프트웨어)에 의존적이라면, 내 소프트웨어가 동작가능한 라이브러리는 처음 의존했던 버전과 Major버전이 필수적으로 같아야하고, Minor버전이 본인버전 이하로 같아야한다.. (Patch can be whatever)
[참고]https://movefast.tistory.com/117 [의존성 관리 시스템?]
의존성 관리 시스템을 이용하면, 여러분은 잠금 파일(lock files) 의 개념 또한 접할 수도 있습니다. 잠금 파일(lock file)이란, 그아먈로 여러분이 각각의 의존성에 현재 의존하고 있는 정확한 버전을 나열하는 파일입니다. 보통, 여러분은 의존성들의 더 새로운 버전으로 업그레이드 하기 위해서는 명시적으로 업데이트 프로그램을 실행시킬 필요가 있습니다. 여기에는 많은 이유가 있는데, 쓸데없는 리컴파일(recompile)을 피하고, 재현가능한(reproducible) 빌드를 하고, (고장났을 수도 있는) 최신 버전으로 자동 업데이트를 하지 않기 위함이죠.
의존성 잠금(dependency locking) 중 극단적인 경우: 벤더링(vendoring)
모든 의존성들의 모든 코드를 프로젝트 안에 복사하는 것.
여러분이 더 큰 프로젝트에서 작업하게 될 수록, 프로젝트에 변화를 줄 때마다 추가적으로 수행해야 하는 일들이 있다는 것을 알게 될 것입니다. 여러분은 아마 기록문서(documentation)의 새로운 버전을 업로드하고, 컴파일된 버전을 어딘가에 업로드하고, 코드를 파이파이(pypi)에 배포하고, 테스트 스위트(test suite)를 실행하고, 별의 별 것들을 다 해야 할 수도 있습니다.
아마 누군가가 깃허브(GitHub)에서 여러분에게 풀 리퀘스트(pull request;PR)을 보낼 때마다, 여러분은 그 코드가 스타일 체크(style check)되기를 바라고 성능 테스트(benchmark)를 실행하고 싶을 지도 모르겠죠?
이러한 필요가 생길 때가 바로 지속적 통합(continuous integration)을 고려해 봐야 할 때입니다.
지속적 통합(continuous integration), 또는 CI는 “코드가 바뀔 때마다 실행되는 것”에 대한 포괄적인 용어입니다, 그리고 다양한 종류의 CI를 제공하는 많은 회사들이 있습니다 (흔히 오픈소스 프로젝트인 경우 무료). 큰 것들 중에는 트레비스 CI(Travis CI), 아주르 파이프라인(Azure Pipelines), 그리고 깃허브 액션즈(GitHub actions)가 있습니다. 이것들은 거의 같은 방식으로 작동합니다:
여러분이 여러분의 레포지토리에 다양한 일들이 발생할 때 어떤 일이 일어나야 하는지에 대해 설명하는 파일을 그 레포지토리에 추가합니다. 단연코 가장 흔한 것은 “누군가 코드를 푸쉬(push)하면, 테스트 스위트(test suite)를 실행한다”와 같은 규칙입니다. 그 이벤트가 촉발될 때, CI 제공자는 가상머신(virtual machine) 혹은 그 이상의 것을 돌리고, 여러분이 작성한 “레시피” 안에 있는 명령들을 실행시키고는, 보통 그 다음에는 결과들을 어딘가에 적어둡니다. 여러분은 테스트 스위트가 통과되는 것이 중지될 때 여러분이 통지를 받도록, 또는 테스트가 통과되는 한 여러분의 레포지토리에 작은 뱃지가 나타나도록 설정을 할 수도 있을 것입니다.
CI 시스템의 예시로서, 수업 웹사이트가 깃허브 페이지스(GitHub Pages)를 이용해서 준비되어 있습니다.
[참고] https://github.com/missing-semester/missing-semester
페이지스(Pages)는 master 브랜치에 올라오는 모든 푸쉬(push)에 대해서 제킬 블로그 소프트웨어(Jekyll)를 실행하고 빌드된 사이트가 특정 깃허브 도메인에서 사용 가능하도록 만드는 CI 동작입니다. 이게 우리가 웹사이트를 업데이트하는 걸 굉장히 쉽게 만들어 주죠!
우린 그냥 로컬에서 변화를 만들고, 깃(git)을 통해서 커밋(commit)하고, 그 다음에 푸쉬(push)할 뿐이에요. 나머지는 CI가 책임져 주는 거죠.
github pages와 같은 곳에서 플러그인을 직접 사용하지 못하므로 low level의 코드를 직접 이용해서 Travis에 코드를 올리면 자동화해서 빌드해 줌
Travis를 사용해서 jekyll을 적용해야 함.
대부분의 큰 소프트웨어 프로젝트에는 “테스트 스위트(test suite)가 딸려 있습니다. 여러분은 이미 테스팅의 일반적인 개념에 익숙할지도 모르지만, 우리는 여러분이 야생에서 마주할 수도 있는 몇몇 테스팅 접근법들과 테스팅 용어들에 대해 짧게 언급하는 게 나을 거라 생각했습니다:
테스트 스위트(Test suite): 모든 테스트들에 대한 총칭
유닛 테스트(Unit test, 단위 테스트): 특정 기능만 떼어놓고 테스트하는 “마이크로-테스트(micro-test)”
통합 테스트(Integration test): 그 달라진 기능이나 요소들이 함께 제대로 작동하는 지 확인하기 위해 시스템의 더 큰 부분을 실행하는 “매크로-테스트(macro-test)”
회귀 테스트(Regression test): 이전에 버그를 일으켰던 특정 패턴을 구현함으로써 그 버그가 다시 나타나지 않는다는 것을 보장하기 위한 테스트
모킹(Mocking): 관련 없는 기능을 테스팅하는 것을 피하기 위해 가짜 구현(fake implementation)으로 함수, 모듈, 또는 타입을 대체하는 것. 예를 들면, “네트워크를 모킹”하거나 “디스크를 모킹”할 수 있다.
[참고] https://www.daleseo.com/python-unittest-mock/ [모킹]