Nov 1, 2023 - gradlew clean build

(base) ➜  dcode-apis_231101 git:(master) ✗ ./gradlew clean build --refresh-dependencies
FAILURE: Build failed with an exception.

* What went wrong:
  Timeout waiting to lock daemon addresses registry. It is currently in use by another Gradle instance.
  Owner PID: unknown
  Our PID: 69227
  Owner Operation: unknown
  Our operation:
  Lock file: /Users/bymin/.gradle/daemon/6.7/registry.bin.lock

* Try:
  Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Get more help at https://help.gradle.org

–refresh-dependencies: 이 옵션은 프로젝트의 의존성(dependencies)을 새로 고침하는 역할을 합니다.
이는 프로젝트의 Gradle 설정 파일 (보통은 build.gradle 또는 build.gradle.kts)에서 정의된 의존성을 검사하고,
캐시된 의존성 정보를 업데이트합니다.
이렇게 하면 최신 버전의 라이브러리 및 종속성을 사용할 수 있게 됩니다.

(base) ➜  dcode-apis_231101 git:(master) ✗ ./gradlew --stop
Stopping Daemon(s)
2 Daemons stopped

다른 Gradle 데몬 인스턴스가 실행 중인 경우, 해당 프로세스를 종료하고 다시 시도하는 것이 좋습니다.

만약 stopped 가 잘안되면,

ps -ef | grep gradle

로 검색 후 kill -9 프로세스ID 로 처리 가능합니다.

Oct 23, 2023 - 기술 부채를 바라보는 시각

회사 내부 백엔드 팀원들에게 진행했던 소프트한 컨퍼런스 입니다.

주제는 우리가 지금 가진 기술 부채는 무엇이며,
그 부채를 해결하기 위해 우리는 무엇을 해야할 것인지 논의하는 자리였습니다.

기술부채는 무엇입니까?

기술적 부채(技術的 負債, technical debt)란, 
**사회와 각 개인들로 이루어진 소프트웨어 등의 결함에 따라서 새로운 기능을 개발하는 데 장애요인이 발생하는 것을 말함.**

부채가 이자를 내고 돈을 쓰는 시점을 당기는 것처럼 기술 부채는 기술적으로 해결되어야할 문제를 뒤로 미루고, 비즈니스 문제를 해결하는 시점을 당기는 것입니다.

기술 부채 항목들

  • 설계된 것을 문서화 하지 않는다.
  • 유닛 테스트를 작성하지 않는다.
  • 더 이상 사용되지 않는 DB의 항목을 지우지 않는다.
  • 반복되는 일을 자동화 하지 않는다.
  • 긴급하게 스펙을 변경한다.

망해가는 회사 프로세스

악취 나는 코드/작성되지 않는 테스트 => 개발속도 저하/ 품질 저하 => 비지니스 속도 저하/고객만족감소 => 수익감소 => 기술적 채무 상환 보류 => 악취나는 코드/작성되지 않는 테스트(반복)

성공하는 회사의 프로세스

향상된 코드/자동화 테스트 => 개발속도 증가/품질 향상 => 비지니스 속도 증가 => 고객 만족도증가/수익증가 => 기술적 자산에 대한 투자 => 향상된 코드(반복)

기술 부채를 어떻게 하면 없앨 수 있을까요?

  • 이 질문에 대한 답은 ‘No’ 입니다.
  • 많은 회사들이 DDD(Deadline Driven Development)에 의한 기술 부채가 발생하고 이는. 피할 수 없는 자연스러운 현상입니다.

다만 한가지 기억해야하는 것이 어떻게 빚을 갚을 것이냐는 겁니다.
빚을 지고 있으면, 마음이 불편합니다. 특이 빚을 처음 질 때 가장 불편합니다.
하지만 그 상황이 계속 되면 그 빚을 지고 있는 상황이 익숙해집니다.

빌리는 것은 쉽지만 갚은 것은 어렵습니다.
게다가 한 번에 갚는 것은 매우 어렵습니다.

지금 당장 문서를 쓰지 않고, 테스트를 작성하지 않는 것은 쉽지만 나중에 그것을 회복하기는 어렵습니다.

완성도(Completeness) vs 적시성(Timeliness)

완성도(Completeness)는 소프트웨어가 기능적 요구사항을 완벽하게 충족하는지 여부를 나타내는 것입니다.

높은 완성도는 품질 높은 소프트웨어를 만드는데 중요합니다.

완성도가 높은 소프트웨어는 적은 결함과 높은 성능을 가지며, 사용자들의 만족도가 높아지게 됩니다. 하지만 완성도를 높이기 위해서는 시간과 비용이 많이 들어가게 됩니다.
적시성(Timeliness)은 소프트웨어를 빠르게 개발하여 빠른 시장 진입이나 기업 성장에 도움을 주는 것을 의미합니다.
적시성이 높은 소프트웨어는 빠르게 수정되어 사용자들의 요구사항에 더욱 빠르게 대응할 수 있으며, 경쟁 우위를 유지하는데 중요합니다. 하지만 적시성을 높이기 위해서는 완성도를 희생할 수 있으며, 이로 인해 기술부채가 발생할 수 있습니다.
따라서 개발자들은 완성도와 적시성을 모두 고려하여 적절한 기술부채를 유지해야 합니다.

완성도를 높이기 위해 기술부채를 적극적으로 늘리는 것은 바람직하지 않지만, 완성도와 적시성 사이에서 타협점을 찾아야 합니다.

이를 통해 소프트웨어 개발의 질을 유지하면서도 빠른 시장 진입과 경쟁 우위를 유지할 수 있습니다.

documentation, test coverage등의 문제를 해결한 완성도 높은 서비스 vs 부족하더라도 시장성이 있는 서비스

→ 결국 현실적인 상황때문에, 후자를 선택할 수 밖에 없습니다.

우리의 대응

“방망이 깎던 노인” 스토리. 수필 속의 주인공이 방망이를 주문하는데, 노인이 해가 질 때 까지 방망이를 깎고 또 깎고, 재촉하는 주인공에게 노인이 이렇게 이야기합니다.

“글쎄, 재촉을 하면 점점 거칠고 늦어진다니까. 물건이란 제대로 만들어야지, 깎다가 놓치면 되나.”

결국 밤늦게 집에 와서 방망이를 내놨더니 아내는 예쁘게 깎았다고 야단이다.
라는 글에서 보통 개발자들은 보통 이 수필속 노인이 됩니다.
견고한 아키텍처를 만들고 싶지만 현실은 최소기능제품(Minimum Viable Product, MVP) 형태로 서비스를 만들어야 할 때가 많고,
스트레스도 받고, 그렇게 개발하고 싶어하지 않습니다.

돈도 시간도 인적자원도 다 부족한 탓일 겁니다.

이제는 이 빚을 어떻게 안만들까를 고민하기보다, 어떻게 빚을 갚을지를 고민해야할 시간이라고 생각합니다.
돈을 빌리면 갚아야 하고, 원금을 갚을 수 없다면 이자라도 갚는 것이 현실이라 생각합니다.

주기적인 이자 상한

기술 부채를 상환해본다고 했을 때, 당장은 급해 죽겠으니까 이자부터라도 갚는 것을 생각해볼 수 있습니다.

Refactoring: 소프트웨어 코드의 구조나 설계를 개선하여 유지보수 및 확장성을 향상시키는 작업입니다.
주기적인 Refactoring은 기술부채를 줄이고 코드 품질을 높이는데 도움이 됩니다.
또한 코드의 복잡성을 낮춰 개발자들이 코드를 이해하기 쉽게 만들어주기도 합니다.
주기적으로 Refactoring을 수행하면 코드의 유지보수와 확장성을 개선할 수 있습니다.

Pair-programming: 두 명의 개발자가 하나의 컴퓨터로 협력하여 코드를 작성하는 방법입니다.
주기적인 Pair-programming은 코드 품질을 향상시키고 기술 부채를 줄이는 데 도움이 됩니다.
또한 개발자들이 서로 경험을 공유하고 서로의 능력을 향상시키는 데도 도움이 됩니다.
주기적으로 Pair-programming을 수행하면 개발자들 간의 커뮤니케이션도 개선됩니다.

  • 보통 코드를 작성한 인원이 해당 코드의 유지보수까지 맡게 됩니다.
  • 그 인원이 휴가, 퇴사, 입원과 같은 이유로 자리를 비운다면 이 코드는 바로 방치가 될 것이고, dead code가 될 가능성이 높습니다.
  • 한명은 프로덕트 코드를, 한명은 테스트 코드를 작성하는 등의 페어프로그래밍이 이를 방지할 수 있다.

Risk management: 프로젝트에서 발생할 수 있는 위험을 파악하고 그에 따른 대응책을 수립하는 작업입니다.
주기적인 Risk management는 프로젝트의 위험을 예측하고 방지할 수 있도록 도와줍니다.
또한 위험 대응책을 만들어두면 프로젝트가 진행되는 동안 예기치 않은 문제가 발생했을 때 대처할 수 있습니다.
주기적으로 Risk management를 수행하면 프로젝트의 안정성을 높일 수 있습니다.

Testcase: 소프트웨어의 기능을 검증하기 위한 테스트 케이스를 작성하는 작업입니다.
주기적인 Testcase 작성은 코드의 품질을 높이고 기술 부채를 줄이는 데 도움이 됩니다.
또한 테스트 케이스를 작성함으로써 코드의 동작을 확인하고 오류를 발견할 수 있습니다.
주기적으로 Testcase를 작성하면 소프트웨어의 품질을 향상시킬 수 있습니다.

  • 로그는 100% 재현이 불가능합니다.
  • 따라서 사후 처리를 위해서는 디버그 범위를 줄이는 것이 도움이 된다. 테스트 케이스 작성하기가 효과적인 방법이다.

여유(돈)가 있다면 원금 상한

Restructuring: 기존의 코드를 개선하여 유지보수와 확장성을 향상시키는 작업입니다.
코드의 복잡성을 줄이고 개발자가 이해하기 쉬운 구조로 변경함으로써 기술 부채를 해결할 수 있습니다.
이를 위해 코드 리팩토링 작업을 수행하거나, 디자인 패턴을 도입하는 등의 방법을 사용할 수 있습니다.
(외부의 전문가들을 데려와서 노하우를 배우는 등의 방법도 필요에 따라 좋습니다.)

Dead code detection: 프로젝트에서 사용되지 않는 코드를 제거하는 작업입니다.
사용되지 않는 코드는 유지보수 비용만 불필요하게 증가시키고, 코드를 이해하기 어렵게 만들어 기술 부채를 증가시킵니다.
Dead code detection을 수행하여 불필요한 코드를 제거함으로써 기술 부채를 해결할 수 있습니다.

  • static analyzing tool
  • Naver Deview 2일차의 런타임 데드 코드 분석 도구 Scavenger - 당신의 코드는 생각보다 많이 죽어있다. 이 내용도 도움이 될 거 같습니다.

Continuous migration: 기술 부채를 해결하기 위해서는 최신 기술과 도구를 적극적으로 도입해야 합니다.
Continuous migration은 이전 시스템을 새로운 시스템으로 지속적으로 이전시켜가는 작업입니다.
이를 통해 기존 시스템에서 발생하는 기술 부채를 줄이고, 새로운 기술과 도구를 적극적으로 도입하여 기술 부채를 해결할 수 있습니다.

  • 오래된 시스템(legacy)을 마이그레이션 해봅시다.

Risk management: 프로젝트에서 발생할 수 있는 위험을 파악하고 그에 따른 대응책을 수립하는 작업입니다.
예측치 못한 위험이 발생했을 때 대처하기 쉬워지므로 비용과 시간을 절약할 수 있습니다.

Risk management은 프로젝트에서 발생할 수 있는 위험 요소를 파악하고 그 위험을 최소화하기 위한 일련의 절차를 수행하는 활동입니다.
이는 프로젝트에서 발생할 수 있는 모든 위험을 파악하고, 위험의 가능성과 영향을 평가하며, 위험을 감소시키기 위한 계획을 수립하고 실행하는 과정을 포함합니다.

Risk management는 다음과 같은 단계를 거쳐 수행됩니다.

  1. Risk identification: 프로젝트에서 발생할 수 있는 모든 위험을 파악하고 문서화합니다. 이를 위해, 이전 프로젝트나 유사한 프로젝트의 위험을 분석하거나, 프로젝트 팀의 경험을 활용하는 등의 방법을 사용합니다.
  2. Risk analysis: 파악한 위험에 대해 가능성과 영향을 평가합니다. 이를 위해, 각각의 위험에 대해 확률과 영향의 크기를 평가하여 위험의 우선순위를 결정합니다.
  3. Risk response planning: 각각의 위험에 대해 대응책을 수립합니다. 이를 위해, 위험을 회피, 전이, 완화, 수용하는 등의 방법으로 대응책을 마련합니다.
  4. Risk monitoring and control: 대응책을 수행하며 위험을 감시하고, 위험 상황이 발생했을 때 즉각적으로 대처합니다. 이를 위해, 위험 상황을 모니터링하고, 위험 상황 발생 시 팀원들에게 통보하며, 위험 관리 계획을 업데이트합니다.

Risk management는 프로젝트의 성공을 위해 중요한 활동입니다. 위험을 사전에 파악하고 대응책을 마련하여 위험을 감소시키면 프로젝트의 성공 가능성이 높아지며, 기술 부채를 해결하는데에도 큰 도움이 됩니다.

후불코딩

부채를 해결하는 방법은 기술부채를 후불코딩으로 생각하는 마인드 변화가 필요합니다.

  1. Static Analysis (정적 분석)
    정적 분석은 코드를 실행하지 않고 코드의 문법, 구조, 규칙, 패턴 등을 검사하는 방법입니다. 이를 통해 버그나 코드의 품질을 향상시키는 데 도움이 됩니다. 부채 정리를 스마트하게 하기 위해서는 정적 분석을 통해 코드의 품질을 높이고 잠재적인 버그를 사전에 발견할 수 있도록 하는 것이 중요합니다.
  2. Test code (테스트 코드)
    테스트 코드는 코드를 실행해보면서 코드의 동작을 검증하는 것입니다. 테스트 코드를 작성하면 코드를 수정할 때마다 자동으로 검증할 수 있어서 코드의 안정성과 신뢰성을 높일 수 있습니다. 부채 정리를 스마트하게 하기 위해서는 테스트 코드를 작성하여 코드를 검증하고 변경 사항이 있을 때마다 자동으로 실행하도록 구성하는 것이 중요합니다.
  3. CI/CD (지속적 통합/배포)
    CI/CD는 코드를 빌드하고 테스트하고 배포하는 프로세스를 자동화하는 방법입니다. CI/CD를 구성하면 코드 변경 사항을 즉시 테스트하고 배포할 수 있어서 빠른 개발과 안정적인 배포를 동시에 할 수 있습니다. 부채 정리를 스마트하게 하기 위해서는 CI/CD를 구성하여 코드 변경 사항이 발생하면 자동으로 테스트하고 배포하도록 하는 것이 중요합니다.
  4. Communication (의사 소통)
    부채 정리는 여러 사람이 함께 작업해야 합니다. 이때 각각의 역할과 책임을 명확하게 하고 의사 소통을 잘하는 것이 중요합니다. 부채 정리를 스마트하게 하기 위해서는 팀원들과 의견을 공유하고 문제점을 해결하는 데에 적극적으로 참여하는 것이 중요합니다.

Oct 7, 2023 - kafka 에 적용했던 옵션 정보들

2020-11-25-kafka.md

3년만에 다시 작성하는 kafka 추가 내용.

kafka version 1.1.1

프로듀서의 옵션

  • enable.idempotence 옵션을 true로 설정:
    멱등성 : 연산을 여러 번 적용하더라도 결과가 달라지지 않는 성질 [원자성 보장]
    enable.idempotence를 true로 설정하면, 카프카 프로듀서(Producer)가 메시지를 전송할 때 이중 전송 및 재전송을 방지합니다.
    즉, 메시지가 중복으로 전송되지 않도록 보장합니다. 이는 메시지 전달의 신뢰성을 높이는 데 도움이 됩니다.

3개의 브로커에 최소 복제2를 만족하면 전송 성공


import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.Producer;
import org.apache.kafka.clients.producer.ProducerRecord;

// Properties 객체를 생성하고 설정 값 할당
Properties props = new Properties();
props.put("bootstrap.servers", "host etc .... ");
props.put("retries", 1);     // 재시도 횟수
props.put("batch.size", 65536); // 크기기반 일괄처리
props.put("linger.ms", 100);   // 시간기반 일괄처리
props.put("buffer.memory", 33554432); // 전송대기 버퍼
props.put("max.request.size", 1048576); // 요청 최대크기
props.put("min.insync.replicas", 2); // 최소 복제
props.put("enable.idempotence", true); // 데이터 중복 개선.
props.put("max.in.flight.requests.per.connection", 1); // 수신 미확인 요청의 최대 수
props.put("request.timeout.ms", 5000); // 전송후 request 타임아웃 시간 RETRY
props.put("max.block.ms", 100); // 전송후 응답시간
props.put("compression.type", "gzip");
props.put("acks", "all");
props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");

// Kafka 프로듀서를 생성
Producer<String, String> producer = new KafkaProducer<>(props);

// 메시지를 보내려면 ProducerRecord를 생성하고 send 메서드로 전송
ProducerRecord<String, String> record = new ProducerRecord<>("topic-name", "key", "value");
producer.send(record);

// 프로듀서를 닫아 리소스를 정리
producer.close();

  • acks 옵션을 all로 설정:
    acks 옵션을 all로 설정하면 프로듀서가 메시지를 전송한 후 해당 메시지가 모든 인스턴스에 성공적으로 복제될 때까지 대기합니다.
    이는 메시지의 안정성을 높이는데 도움이 되며, 메시지가 분실되지 않도록 보장합니다.
    그러나 전송 지연이 발생할 수 있습니다.

  • replication.factor 옵션을 3으로 설정:
    replication.factor를 3으로 설정하면 카프카 토픽의 파티션(Partition)이 세 개의 복제본을 가지게 됩니다.
    이는 데이터의 내고장성을 보장하고, 하나 이상의 브로커(Broker)가 실패해도 데이터 손실을 방지합니다.
    따라서 데이터의 안정성을 높이는데 도움이 됩니다.

  • min.insync.replicas 옵션을 2로 설정:
    min.insync.replicas를 2로 설정하면 메시지를 전송할 때 적어도 두 개의 복제본이 동기화되어야 한다는 조건을 만족해야 합니다.
    이것은 데이터의 내고장성을 보장하기 위해 사용됩니다.
    즉, 적어도 두 개의 복제본이 성공적으로 메시지를 수신한 경우에만 프로듀서가 성공으로 간주됩니다.
    이렇게 설정 옵션을 변경함으로써, 카프카에서 데이터의 신뢰성과 내고장성을 높일 수 있습니다.
    그러나 이러한 설정 변경은 성능 및 지연에 영향을 미칠 수 있으므로 신중하게 고려해야 합니다.

전송 실패시 아래 이미지의 retry에 파일이 생기고 retryok로 이동

  • retryok 에 있는 재처리완료 파일은 3일후 자동 삭제.
    [DevOps + AI] 카프카, 대규모 클러스터 운영 후기 / if(kakao)dev2022 https://www.youtube.com/watch?v=SuHtHQkRV7g 를 보면, retry 가 아닌, For Fault 라는 별도의 카프카를 또 하나 두어 장애처리는 하는 것을 보고 더 괜찮은 생각이라 생각이 들었으나, 파일로 저장 후 후처리도 그때 당시에는 괜찮은 전략이었습니다.

장애시 처리 / 복구 절차 - 카프카

  1. kafka ISR 그룹에 브로커들이 정상적으로 존재하는지 상태 확인
    $ bin/kafka-topics.sh -describe –zookeeper localhost:2181
    http://:/clusters/kafka/topics/xxxData

  2. kafka 서버 ISR 상태에 따라 순차적인 재시작, 최소 서버 2대이상 살아있어야 안정적인 운영가능.
    현재 카프카는 디스크 full로 다운된 현상 말고는 문제가 없음.

  3. 처리 현황파악
    3-1. 각 was의 에러로그 확인
    $ curl http://:/cronwork/monitoring/kafka_error_retrycnt.txt
    3-2. 텔레그램 연결하여, 알람으로온 전송지연서버의 producer MBeans (record-send-rate) 확인
    3-4. 재처리 진행 여부 확인 : 각 서버의 error log 의 재처리 진행 상황 확인
    $ grep KafkaError /home/xxx/logs/log4j/xxx.infolog.log
    3-5. 각 was 의 컨슈머서버의 인스턴스 확인 http://:/clusters/kafka/consumers/group-billing/topic/xxxx/type/KF Consumer Offset 의 마지막 Offset 값을 확인 3-6. 컨슈머의 파티션별 offset 을 마지막 offset으로 위치 이동하고 $ bin/kafka-consumer-groups.sh –bootstrap-server : –group group2 –reset-offsets –to-latest –all-topics –execute 3-7. row단위 데이터 에서 1번에서 확인된 번호의 시간대부터 재시작한 시간의 범위를 재처리 컨슈머로 구동.


카프카는 이벤트 드리븐 아키텍쳐와 스트림 데이터 파이프라인에서 중요한 역활을 수행하며, 스트림 이벤트를 다루는 데 특화되어 있는 카프카는 실시간 데이터를 처리하는데 적합합니다.
시간 단위 이벤트 데이터를 다루기 위한 타임스탬프 순서를 보장하기 위한 파티션과 메시지 키와 같은 기능이 카프카에 포함되어이 있습니다.

메시지 전달 신뢰성

정확히 한번 (Exactly-once) : 모든 데이터 처리가 단 한번만 처리. 이벤트 데이터가 발생되었을때 단 한번만 처리됨.

적어도 한번 (At least once) : 네트워크, 브로커 장애 등에 의해서 데이터를 중복 발행, 수행, 적재 됨을 의미. 어느 누구도 은행에서 이체되었을떄 한번만 되길 원하지 2번 이상 되길 원하지 않음.

최대 한번 (At most once) : 관련 장애에 의해 유실되지 않는 것을 원함.