メインコンテンツにスキップ

Kafka schema registry

· 6分の読み時間
Haril Song
Owner, Software Engineer at 42dot

Problems

  • 메세지 스펙이 변경될 경우, 의존하고 있는 모듈 or Git Repository 마다 DTO 업데이트가 필요하다.
    • 하위호환성이나 상위호환성이 자주 깨진다.
    • DTO 관리의 복잡도가 선형적으로 증가한다.
    • Java 는 특히 Json 기반의 메세지를 다루기에 불편한 점이 많다.
  • 카프카는 ByteArray 형태로 메세지를 전송하나, 애플리케이션 레벨에서는 이를 역직렬화하여 관리하는 것이 권장된다.
    • payload 에 데이터를 담을 때마다 ByteArray 로 직렬화하는 과정, 그리고 이 반대 과정이 매번 이루어진다.
    • 코드 복잡도 상승
    • ByteArray - JSON - Object

ByteArray + DTO + ObjectMapper 방식

Kafka 메시지가 JSON 형식이라고 가정:

data class User(
val id: String,
val name: String,
val email: String?,
val age: Int?,
val createdAt: Long
)

val rawBytes: ByteArray = record.value()
val user = objectMapper.readValue(rawBytes, User::class.java)
  • 스키마 정보를 코드에서 직접 관리 (e.g. DTO 클래스)
  • Kafka 메시지의 구조가 JSON 포맷으로 되어 있어야 가능
  • Schema Registry 불필요
  • 메시지 구조가 바뀌면 DTO도 바뀌어야 하고, 호환성 검사 수동

GenericRecord (Avro + Schema Registry)

val record = consumerRecord.value() as GenericRecord
val name = record.get("name").toString()
  • DTO 없이도 동작 가능 (GenericRecord), 또는 generated class 사용 가능
  • 메시지 구조 변경 시 Registry의 호환성 정책으로 안전하게 진화 가능

SpecificRecord (Avro + Schema Registry)

// user.avsc
{
"type": "record",
"name": "User",
"fields": [...]
}
// 자동 생성
public class User extends SpecificRecordBase implements SpecificRecord {
private String id;
private String name;
...
}
@KafkaListener(topics = ["\${kafka.topic.user}"], groupId = "\${spring.kafka.consumer.group-id}")
fun consume(user: User) {
val userId = user.getId()
logger.info("Received user with id: {}, name: {}", userId, user.getName())

users[userId] = user
}

코드가 생성되어있기 때문에 직접 참조 가능

  • 정적 타입 지원
    • 직렬화/역직렬화 시 안정성 보장
    • IDE 지원 우수
  • Kafka Schema Registry와 완전 호환
  • 성능 우수
    • GenericRecord 는 리플렉션을 활용하여 비교적 느림

Schema 정의 및 사용

  • IntelliJ Junie 를 사용해서 샘플 작성
plugins {
id("com.github.davidmc24.gradle.plugin.avro") version "1.9.1"
}

repositories {
mavenCentral()
maven {
url = uri("https://packages.confluent.io/maven/")
}
}

dependencies {
// Avro and Schema Registry
implementation("org.apache.avro:avro:1.11.3")
implementation("io.confluent:kafka-avro-serializer:7.5.1")
implementation("io.confluent:kafka-schema-registry-client:7.5.1")
}

avro {
isCreateSetters.set(true)
isCreateOptionalGetters.set(false)
isGettersReturnOptional.set(false)
fieldVisibility.set("PRIVATE")
outputCharacterEncoding.set("UTF-8")
stringType.set("String")
templateDirectory.set(null as String?)
isEnableDecimalLogicalType.set(true)
}

User schema 정의

{
"namespace": "com.haril.kafkaschemaregistrydemo.schema",
"type": "record",
"name": "User",
"fields": [
{
"name": "id",
"type": "string"
},
{
"name": "name",
"type": "string"
},
{
"name": "email",
"type": ["null", "string"],
"default": null
},
{
"name": "age",
"type": ["null", "int"],
"default": null
},
{
"name": "createdAt",
"type": {
"type": "long",
"logicalType": "timestamp-millis"
}
}
]
}

자동으로 User 클래스가 생성된 것을 확인할 수 있고,

다른 모듈에서 참조하여 사용할 수 있다.

Schema 의 업데이트

  • 레지스트리에 스키마 정보가 없을 경우, kafka 는 메세지가 발행될 때 연결된 schema registry 에 스키마를 업로드한다.
  • Web UI 를 사용하여 업데이트할수도 있다.

Schema 호환성 정책

대표적으로는 아래와 같다.

모드설명예시
BACKWARD이전 버전의 Consumer는 새 메시지를 이해 가능필드 추가 가능, 제거는 불가
FORWARD새 버전의 Consumer는 이전 메시지를 이해 가능필드 제거 가능, 추가는 불가
FULL양방향 모두 호환제한적 변경만 허용
NONE어떤 변경도 호환성 보장 안 함변경 시 consumer crash 위험 ↑

BACKWARD 정책을 사용할 때는 스키마에 필드를 추가할 때 default 값을 지정해줘야 예전 버전의 스키마를 사용하는 Consumer 도 안전하게 스키마를 역직렬화할 수 있다. null 을 default 로 지정하는 것도 가능하며 이 경우 optional 필드임을 의미하게 된다.

Kafka Streams 는 BACKWARD 만 지원한다.

FULL 정책은 양방향 호환성이 유지되므로 편리하지만 Kafka Streams 는 BACKWARD 정책만 지원하므로 선택지가 제한된다.

만약 GenericRecord 방식으로 사용할 경우, 스키마를 동적으로 로드한다. 이 경우 스키마가 변경되더라도 서비스의 재배포가 필요없다.

ConsumerRecord<String, GenericRecord> record = ...
GenericRecord value = record.value();

Integer age = (Integer) value.get("age");
String name = value.get("name").toString();

props.put("specific.avro.reader", false) 설정으로 활성화할 수 있으며, Map 으로 사용하는 것과 비슷한 경험을 제공할 수도 있다.

항목SpecificRecordGenericRecord
사용 방식Avro 스키마로 Java/Kotlin 클래스 미리 생성런타임에 스키마 파싱 후 동적으로 사용
성능빠르고 타입 안전약간 느리고 타입 안정성 떨어짐
유연성스키마 변경 시 코드 재생성 필요스키마 변경에도 유연하게 대응 가능
권장 상황스키마가 고정된 서비스스키마가 자주 바뀌거나 다양할 때

다음과 같은 사용이라면 GenericRecord 사용을 고려할 수 있다.

  • 다양한 스키마를 처리해야 하는 Kafka consumer 플랫폼
  • 스키마 registry 기반 멀티팀 환경 (스키마 버전이 자주 바뀌는 경우)
  • Avro 스키마가 외부에 의해 관리되고 있어 내부에서 클래스를 만들기 곤란할 때

Producer 에서는 명확한 데이터 스키마가 있어야하므로 .avsc 파일을 통해 객체를 생성하고, Consumer 쪽에서는 GenericRecord 를 사용하여 동적으로 대응하는 방법도 유용하다.

Schema management and Monitoring

Landoop UI

스키마 변경 내역을 계속 기록한다.

Kafka UI 에서는 value 가 schema registry 로 변경된 것을 확인할 수 있다.

Conclusion

Pros

  • 여러개의 중첩된 DTO 를 다루는 대신, 하나의 avsc 파일만 관리하면 되서 비교적 관리 부담이 줄어든다.
  • 모든 서비스는 Schema Registry에서 실시간으로 스키마 조회
    • 메시지에 스키마 정보가 포함되지 않으므로, 네트워크 대역폭을 효율적으로 사용하게 된다
  • Kafka 메시지는 스키마 ID (magic byte + schema ID) 를 포함하므로, 컨슈머는 로컬에 .avsc가 없어도 자동 역직렬화 가능
  • 여러 팀에서 하나의 스트림 파이프라인 or 토픽에 메세지를 발행하는 경우 특히 유용
    • 파이프라인에 이상한 데이터가 들어오지 않게 된다

Cons

  • 별도 API 서버를 통해 배포해야 한다.
  • 인프라 팀과 협업이 필요하다.
  • Schema registry 가 다운될 경우 파이프라인이 멈출 수 있기 때문에, 관리 포인트가 오히려 증가할 수 도 있다.

그래서 언제 쓰면 좋을까?

  • 프로젝트 초기여서 설정부터 하는 경우
  • 하나의 파이프라인을 여러 팀이 공유하여 사용하는 경우
  • 회사에 카프카를 전문적으로 다루는 별도의 팀이 있을 경우
  • protobuf 에 익숙한 경우

Reference

Daily note 작성법

· 3分の読み時間
Haril Song
Owner, Software Engineer at 42dot

Overview

회사에서 시켜서, 혹은 개인의 니즈로 인해 daily 라고 부르는 일일 노트를 적곤 한다.

필자 또한 daily 를 적는데, 처음에는 회사의 요구로 인해 적기 시작했지만 적는 방식에 대해 많은 시행착오를 거쳐서 현재는 개인적인 용도로도 많이 적게 되었다.

이런 daily 는 어떻게 작성해야 편하게 적으면서도 활용도를 최대한 높일 수 있는지 간단하게 적어보려고 한다.

知っておくと便利な開発者ツール - CLIエディション

· 7分の読み時間
Haril Song
Owner, Software Engineer at 42dot

概要

最高のアプリを紹介した前回の記事に続いて、Command Line Interface(以下CLI)のおすすめツールを見ていきます。今回紹介するツールは全て、筆者が最低6ヶ月以上使用しており、新しい環境をセットアップする際に必ず導入しているものです。

zoxide

GitHub - ajeetdsouza/zoxide: A smarter cd command. Supports all major shells.

一度でも訪れたディレクトリのパスを覚える必要がなくなるツールです。例えば以下のような場合です:

cd ~/.config/somewhere/longlong/path

# 次回からは以下のコマンドで直接アクセス可能
z path

これは非常に便利で、特定の設定ファイルがどこにあるかを覚えておく必要がなくなります。ziを使用すると優先順位リストが表示され、ファジー検索も可能です。

一度使うと、このツールを使用する前には戻れなくなるでしょう。

mise

GitHub - jdx/mise: dev tools, env vars, task runner

各種環境変数や言語、パッケージのバージョンマネージャーです。構造的に非常に安定性が高く、Rustで実装されているため速度も速いです。直感的なコマンドにより、学習曲線が大幅に緩和されています。

既に本ブログで記事として紹介するほど、非常に愛用しているツールです。miseの役割を果たす類似ツールは他にもありますが、個人的にはその中で最もおすすめできると考えています。例えば、direnvはmiseで完全に代替可能で、nixは過度に複雑で汎用性が低いです。

様々な言語を扱うプログラミングマルチリンガルであれば、ぜひ試してみてください。

chezmoi

chezmoi

複数のデバイスを使用している場合、開発環境の同期は非常に面倒です。特にCLIを頻繁に使用する開発者にとってはなおさらです。

完全に同期を諦めるつもりでなければ、どのように同期できるか悩んでいる場合はchezmoiを試してみてください。新しい機器を購入しても、初期設定に時間を費やす必要がなくなります。

miseと同様に、本ブログに使用方法に関する記事がありますので参考にしてください。

fzf

GitHub - junegunn/fzf: :cherry_blossom: A command-line fuzzy finder

GitHubスター約70k、これ以上の説明が必要でしょうか(参考までに、spring-frameworkは57kです)。

韓国人開発者のjunegunn氏が管理するオープンソースのファジーファインダーで、標準入出力パイプラインを通じて驚異的な汎用性を誇ります。

**検索が必要な場合、種類に関係なくfzfを使えば良いです。**様々なパッケージが利用しているため、fzfの存在を知らなくても、既に間接的に使用していた可能性があります。

fd

GitHub - sharkdp/fd: A simple, fast and user-friendly alternative to 'find'

findコマンドを代替します。

Rustで作成されており、findと比べて最大50%速いとされています。ハイライトも綺麗で、コマンドオプションもfindよりもはるかに直感的です。

ripgrep

GitHub - BurntSushi/ripgrep: ripgrep recursively searches directories for a regex pattern while respecting your gitignore

grepコマンドを代替します。名前はripgrepですが、コマンドはrgを使用します。

fdと同様に、Rustで作成されています。grepと比べて、出力からより多様な情報を得ることができます。コマンドも直感的で使いやすく、速度も速いため、使わない理由がありません。

まさに'RIP, grep'です。

lsd

GitHub - lsd-rs/lsd: The next gen ls command

lsコマンドを代替します。

lsコマンドは非常によく使用されるコマンドです。コマンド自体は古いものですが、出力から得られる情報は多くありません。lsdを使用すると、既存のlsを完全に代替することができます。

bat

GitHub - sharkdp/bat: A cat(1) clone with wings.

catコマンドを代替します。

catコマンドは単純な出力ですが、batを使用するとコードハイライトを楽しむことができます。 ある程度目の利く開発者なら、行出力がシェルパイプラインを妨げるのではないかと心配するかもしれませんが、 全く妨げません。心配せずに新しい技術を楽しみましょう。

筆者はbatコマンドのエイリアスをcatに設定して使用しています。

HTTPie

HTTPie – API testing client that flows with you

curlを代替します。

アプリ版もあるためどちらの記事に含めるべきか悩みましたが、個人的にはCLIでのみ使用しているため、こちらの記事に含めました。

なぜcurlの代わりにHTTPieを好むかというと、非常に直感的だからです。簡単なGETリクエストは以下のように送信できます:

https httpie.io/hello

レスポンスは以下のようにフォーマットされて返ってきます:

curlのレスポンスを思い出してみてください。開発者も綺麗なものが好きです。

Orbstack

OrbStack · Fast, light, simple Docker & Linux

Docker Desktopを代替します。

Dockerコンテナを使用する際に少し速くなり、いくつかのバグも解消されます。しかし、本当の真価はVMを使用する際に発揮されます。従来VMの使用が難しかったMacでも、非常に軽量にVMを実行できます。UbuntuやKali Linuxなどのテストが必要な場合、orbstackを使用すると非常に高速で便利に管理できますので、試してみてください。個人的に非常に面白い経験でした。

atuin

GitHub - atuinsh/atuin: ✨ Magical shell history

chezmoiを使用すると使用するツールの設定は同期できましたが、atuinを使用すると会社で使用したコマンド履歴を同期できます。もう会社で使用したコマンドが何だったか思い出そうと苦労する必要はありません。

警告

一つ残念な点は、ターミナルとしてWarpを使用している場合、atuinを完全に活用することが難しいという点です。Warpは独自の履歴機能を提供しており、atuinと干渉があります。以下のコマンドを使用すると履歴からコマンドを検索できるため、暫定的な対処として使用しています:

atuin history list | fzf

trash-cli

ターミナルに「ゴミ箱」機能を実装します。したがって、もうrm -rf /を恐れる必要はありません。いつでも復元が可能だからです。

開発者最大の敵であるrm -rf /から解放されるというのに、それ以上の理由が必要でしょうか?

まとめ

ここまで、個人的に非常に気に入っている様々なツールを紹介してきました。

実際、これ以外にもおすすめできるツールはいくらでもありますが、誰かが何をおすすめしても、自分の手に馴染んだものが一番良いものです。残りは自分の環境に合わせて探して使ってみましょう。

情報

使用中の全てのツールはこちらで確認できます。

知っておくと便利な開発者ツール - アプリ編

· 10分の読み時間
Haril Song
Owner, Software Engineer at 42dot

概要

MacOSだけを使い続けて、早13年が経ちました。

開発者としてのキャリアを始める前から、Macをより便利に使うためのツールを探るのが趣味でした。新しいツールを試すこと自体が楽しかったのです。

そうして本当に多くのツールが共に歩み、そして消えていくことを繰り返してきました。

今回は、最後まで生き残り、私と共にあらゆる困難を乗り越えてきたツールについて紹介したいと思います。アプリとCLIの分野に分けて紹介する予定で、今回の記事はアプリ編となります。

ネットワークモニタリングのためのツール集

· 6分の読み時間
Haril Song
Owner, Software Engineer at 42dot

banner

概要

プレゼンテーションの準備をどこから始めればよいかわからない方のために簡単にまとめたセッションです。ネットワークモニタリングや実験に使用できる様々なツールを紹介し、その使用方法について説明します。

環境

ネットワークを学ぶためにはどのような環境を準備すべきでしょうか?もちろん、学びたいトピックによって異なりますが、よく使用される方法をいくつか見てみましょう。

Dockerのネットワークタイプ

· 8分の読み時間
Haril Song
Owner, Software Engineer at 42dot

概要

Dockerには合計6つのネットワークタイプがあります:

  • Bridge
  • Host
  • IPvlan
  • MACvlan
  • Overlay
  • None

多くのバックエンド開発者は、ネットワークタイプについてあまり知らないか、知っていてもbridgeだけを使用しているのではないかと思います。 私も関連内容が気になり、勉強会を行いました。この記事は、その勉強会で発表した内容の一部を抜粋したものです。

Orbstackを使用してVMを実行し、実践的な演習を行います。

さようなら2024年、こんにちは2025年

· 15分の読み時間
Haril Song
Owner, Software Engineer at 42dot

reminiscence

概要

警告

個人的な日記をベースに書いているため、少し恥ずかしい内容があるかもしれません 😂

2024年には本当に多くのことがありました。

より良い文章が思い浮かばないので、このような陳腐な文章で振り返りを始めます。誰もが「今日、母が死んだ」のような文章で始められるわけではないので。いや、それは可能なのでしょうか。よくわかりません。

「[書評] コード執筆ガイド」

· 4分の読み時間
Haril Song
Owner, Software Engineer at 42dot

石川宗俊の『コード執筆ガイド』の表紙

情報

このレビューは出版社から提供された本をもとに書かれていますが、内容や評価には影響を与えていません。

概要

コードをレビューしやすくするにはどうすればいいのか?

読みやすくレビューしやすいコードを書くためのガイドは、現役のLINE開発者が自身の経験をもとに執筆した本です。コードの可読性を向上させるためのさまざまな方法と原則を詳しく解説しています。

個人的には、コード規約の入門書として非常に優れていると思います。その理由を説明します。

Naver DAN 24 Review

· 10分の読み時間
Haril Song
Owner, Software Engineer at 42dot

overview

Overview

  • 참가 일시: Nov 11, 2024
  • 장소: 코엑스 그랜드블룸
  • 관련 링크: DAN 24

운좋게도 네이버에서 주관한 DAN24 에 다녀올 수 있었습니다. 결론부터 말씀드리면, 24년에 참여한 컨퍼런스 중 가장 수준이 높았다고 할 수 있을 것 같아요. 아래는 대략적인 내용을 적어둔 것이며, 자세한 내용은 DAN24 공식페이지를 참고해주세요.

KafkaKRU Meetup Review

· 2分の読み時間
Haril Song
Owner, Software Engineer at 42dot

KafkaKRU 밋업 리뷰: Event Sourcing부터 리더 파티션 밸런싱까지

2024년 11월 21일, 서울 중구 삼화타워에서 열린 KafkaKRU 밋업에 참석했습니다. 사실 대기자 명단에 있었어서 참석이 어려운 상태였던 것 같지만, 열정으로 봐주셔서 다행히 쫓겨나지는 않았습니다. 결과적으로는 예상을 훨씬 뛰어넘는 값진 시간이었어요.