ZIO - 카프카 컨슈머에서 ZSink.collectAllN 무한 대기

 이슈 zio-kafka 의 컨슈머에서 컨슘하는 레코드들을 N 개씩 Chunk 로 묶어 일괄 처리하고 싶었다. 그래서 ZSink.collectAllN 함수를 사용했는데, N 개씩 Chunk 로 잘 묶다가 마지막 레코드들이 N 개를 채우지 못하면 무한히 대기하는 현상이 발생했다. ex) n = 3, 레코드를 5개 컨슘한다고 했을 때, 3 개는 Chunk 로 잘 묶어 처리하지만 나머지 2 개는 처리하지 않고 대기했다. ZSink.collectAllN 함수는 ZStream 을 N 개씩 Chunk 로 묶어주는 함수이다. N 개를 채우지 못해도 Chunk로 묶는다. (이 부분 때문에 헷갈렸다.) 코드 예시 ZStream ( 1 , 2 , 3 , 4 , 5 ). run ( ZSink . collectAllN ( 3 ) ) // Output: Chunk(1,2,3), Chunk(4,5) 공식 문서 :  https://zio.dev/reference/stream/zsink/creating-sinks/ Why? 생각해보니 답은 간단했다. 위 코드 예시에선 ZStream 의 끝을 알 수 있어 (1, 2, 3), (4, 5) 로 나눠주지만, 컨슈머는 끝이 없다.  무한히 컨슘하기 때문에 ZSink.collectAllN  함수 입장에선 N 개가 확정된 순간에만 Chunk 로 묶을 수 있다. 결국 ZSink.collectAllN  함수말고 다른 함수를 사용했지만, 어떤 방식으로 컨슘을 하길래 collect 하지 못하고 기다리게 되는지가 궁금했다. 그래서 먼저 ZSink.collectAllN 함수를 봤는데 def collectAllN [ In ]( n : => Int )( implicit trace : Trace ) : ZSink [ Any , Nothing , In , In , Chunk [ In ]] = fromZIO ( ZIO . succeed ( ChunkBuilder . make [ In ]( n )))

Cake Pattern(케이크 패턴) - Scala

함수형 프로그래밍 - 트램폴린(Trampoline)

 트램폴린(Trampoline) 트램폴린이란? 보통의 재귀 함수는 꼬리 재귀 최적화로 스택 오버플로우를 방지할 수 있다. 하지만 각 함수가 서로를 참조하는 상호 재귀 함수는 꼬리 재귀 최적화가 불가능하다. 트램폴린은 함수의 지연 처리를 이용해 상호 재귀 함수에 꼬리 재귀 최적화를 적용시킬 수 있다. thunk: () => Bounce[A] 는 함수를 바로 실행하지 않고 지연 처리시킨다. trampoline(evenWithTrampoline(5)) 의 동작을 살펴보자. n 이 5 → 4 → 3 → 2 → 1 → 0 이 되면 oddWithTrampoline(0) 에서 Done(false) 를 리턴한다. 그러면 다시 반대로 evenWithTrampoline(1) 에선 More(() ⇒ Done(false)) 를 리턴한다. oddWithTrampoline(2) 에선 More(() ⇒ More(() ⇒ Done(false))) 를 리턴한다. 이렇게 More 이 중첩되서 쌓이고, n 개 중첩된 More 를 trampoline 에서 인자로 받게 된다. trampoline(More(() ⇒ More(() ⇒ More(…)))) trampoline 함수는 꼬리 재귀 함수다. More 인 경우 thunk 의 결과값을 인자로 넘기기 때문에(acc 값) 콜스택이 쌓이지 않는다. 결국 콜스택을 쌓지 않고 More 을 모두 깐 뒤, 최종 Done 의 값을 얻어올 수 있다.

브랜치 전략(git-flow) 정리

 최근 어플리케이션 개발을 거의 마치고 배포 단계에 들어섰는데, 나중에 버전 관리나 배포 자동화를 위해 Git 의 브랜치부터 체계적으로 잡고 가야한다는 생각이 들었다. 그래서 git-flow 전략을 찾아봤고, 프로젝트에 어떻게 적용할 것인지 흐름을 정리해 보고자 한다. git-flow 의 내용을 설명하기보단 개발 -> 배포까지의 흐름 위주로 정리했다. develop 브랜치 master 브랜치에서 분기되는 브랜치로, 해당 master 브랜치 버전의 기능 개발을 develop 브랜치에서 하게 된다. feature 브랜치 develop 브랜치에서 분기되는 브랜치로, 특정 기능을 개발하기 위한 브랜치이다. feature/{기능} 으로 이름지으며, 기능이 완성되면 develop 브랜치에 병합하고 feature 브랜치는 지운다. release 브랜치 기능 개발이 어느정도 이루어지고, 정식 배포가 얼마 남지 않았을 때, 테스트를 위해 develop에서 분기하는 브랜치이다. - 이번 프로젝트에서 release 브랜치를 테스트 브랜치로 두고, release 브랜치에 코드가 푸시되면 테스트 및 빌드 후 테스트 서버에 자동으로 배포되게끔 적용하려 한다.  정식 배포 기간이 되면 master, develop 두 브랜치에 모두 병합해야 한다. (버전을 태깅해 커밋하면 버전 관리에 용이함) hotfix 브랜치 release 나 master 브랜치에서 빠르게 고쳐져야 할 사항이 생길 경우 만들고, 수정 후 master, release, develop 모두에 병합해야 한다. master 브랜치 정식 출시가 되는 브랜치로, 버전이 업데이트 될 때만 병합되어야 한다. 정식으로 릴리즈된 내용을 기록하고, 버전을 관리하는 브랜치이다. 브랜치를 개발 흐름대로 정리해 보았다. 즉, 1. develop 브랜치에서 feature 브랜치로 기능을 개발하고 2. 기능 개발이 완료되면 release 브랜치로 테스트 3. hotfix 브랜치로 오류 수정 후 4. master 브랜치로 정식 출시 까지의 흐

쉘 스크립트 - 테스트 서버 한번에 배포하기

개발중인 앱의 테스트 서버를 Springboot, EC2로 구축했다. 하지만 매번 테스트 서버를 배포할 때마다 빌드, 배포 코드를 똑같이 쳐주는 게 번거로워 이 작업을 한 번에 해 줄 수 있는 쉘 스크립트를 짜보려고 한다.(쉘 스크립트 연습 겸!) 먼저, 빌드, 배포 순서는 다음과 같다. 1. BackEnd 폴더에서 github 코드를 pull 한다. (1번은 수동으로 진행하기로 했다. 어차피 이후에 github actions와 연동할 계획이므로) 2. gradle로 빌드한다. 3. 미리 실행 중인(실행 중이 아니라면 생략) nohup 프로세스를 종료한다. 4. build/libs 의 .jar 파일을 배포한다. 위 과정을 쉘 스크립트 실행 한 번으로 대체해보자! 쉘 스크립트 파일 생성 vi deploy.sh 스크립트 파일 권한 부여 chmod +x deploy.sh  => 실행 권한 부여 쉘 스크립트 파일의 맨 위에 쉘 스크립트임을 알려주는 코드를 넣어주어야 한다. #!/bin/bash   => 스크립트 상단에 작성 스크립트 파일을 만들었으니, 그 동안 터미널에서 사용한 명령어들을 넣어주자 1. 수작업 2. gradle 빌드 필자의 빌드 커맨드는 다음과 같다. 스크립트에 그대로 추가해 주자. sudo ./gradlew bootjar 3. 실행중인 nohup 프로세스 종료 바로 nohup 명령어를 실행해주면 이전에 실행중인 프로세스와 충돌이 일어나 배포가 진행되지 않는다. 따라서 기존 nohup 프로세스를 종료하는 로직을 추가해주자. sudo kill -9 $(ps -ef | grep {실행중인 파일 명} | awk '{print $2}') - 명령어 정리 kill : 해당 프로세스 id 의 프로세스를 종료한다. ps -ef | grep : ps로 현재 실행중인 프로세스를 검색하는데, grep으로 원하는 문자열이 포함된 프로세스만 검색할 수 있다. awk : 테이블 형태로 된 값들을 조작하는 명령어이다. $1, $2, $3... 들

Git 명령어 정리

이미지
git init 현재 폴더에 git을 등록한다. git clone {PATH} PATH의 깃 원격 저장소에 있는 내용들을 모두 가져온다. git clone은 다음과 같은 명령어들로 원격 저장소의 내용을 가져온다. 1. 저장소 폴더 생성 2. 저장소 폴더 내에서 git init 3. git remote add origin {PATH} 4. git fetch 5. git checkout {최종 커밋} git branch [-l], git branch -v, git branch -r, git branch -a -l 옵션은 생략이 가능하며, 로컬 브랜치들의 목록을 보여준다. -v 옵션은 로컬 브랜치들의 목록을 마지막 커밋 내용과 함께 보여준다. -r 옵션은 원격 브랜치들의 목록을 보여준다. -a 옵션은 원격/로컬 브랜치들의 목록을 모두 보여준다. git branch {이름}, git branch {생성 브랜치} {기준 브랜치} 새로운 브랜치를 생성한다.(단, 생성만 하고 이동은 하지 않는다) branch 명령어의 인자로 이름을 두 개 주면 뒤 브랜치를 기준으로 새로운 브랜치를 생성한다.(마찬가지로 새로운 브랜치로의 이동은 하지 않는다) git branch (-merged | -no-merged) -merged 옵션은 이미 merge 된 브랜치들, -no-merged 옵션은 아직 merge 되지 않은 브랜치들을 보여준다.(*이 붙지 않은 브랜치들은 이미 merge되었기 때문에 삭제가 가능하다) git branch -d (이름) 해당 브랜치를 삭제한다.(*이 붙어 있으면 삭제가 불가능하다) git branch -m (기존 이름 A) (바꿀 이름 B) 브랜치 A를 브랜치 B로 변경한다.(-M 옵션을 사용할 경우 동일한 이름의 브랜치도 덮어 쓴다) git add {파일 이름}, git add . , git add -u, git add -A, git add --all 파일 이름만 적게 되면 해당 파일만 스테이징한다. . 옵션을 주면 변화된 파일, 추가된 파일 을 스테이징한다.

백준 - 영재의 산책(19953번)

  문제 t 가 10억이므로 매 초마다 이동해주면 시간 초과가 날 수 밖에 없다. (v * m) % 10 의 규칙을 활용해야 한다. 여러 수를 대입해 본 결과 v, m 이 어떤 조합이던 간에 결과 값은 4개의 패턴만을 가진다. v = 1, m = 2 일 때 -> 2, 4, 8, 6 v = 123, m = 127 일 때 -> 1, 7, 9, 3 즉, 맨 처음 북쪽으로의 이동을 제외하면 다음 이동부터는 동, 서, 남, 북 네 방향으로의 이동 거리가 일정하다는 뜻이다. 처음 북쪽으로 이동한 뒤 그 지점을 시작점으로 잡고, 동, 서, 남, 북으로 각각 몇 번 갈 수 있는지 구한 뒤 이동 거리만큼 계산해주면 된다.