안녕하세요! 그로스로그 입니다!😊 🌱
Pod를 만들지 않고 Kafka가 필요하다고 선언하는 법, 언제 부를지는 받는 쪽이 정하게 하는 콜백, 그리고 화면 크기를 모른 채 레이아웃을 짜는 방법. 3기 9회차 세 편입니다.
3기 9회차, 2025년 7월 중순입니다. 이번에는 직접 정하지 않고 규칙만 남기는 이야기 세 편이 모였습니다.
01
🐘 Helm chart로 Kafka 배포하기
이태형 님
쿠버네티스 위에 Kafka 클러스터를 올린 기록입니다.
순서가 이렇습니다. kind 클러스터 생성 → Helm 저장소 추가 → Strimzi Operator 설치 → Kafka·Topic·Bridge 생성이요.
핵심은 오퍼레이터입니다. Pod나 Service를 직접 만들지 않고,
kind: Kafka라고 선언하면 오퍼레이터가 알아서 만들어줘요.그 점을 글이 직접 짚었습니다.
3기 3·5·8회차에서도 이분의 도커 강의 기록을 소개해드렸습니다. 1주차부터 4주차까지 이어온 연재의 다음 단계예요.
조금 더 들어가 보면
🔌 포트를 먼저 열어둔다
시작이 클러스터 설정인데, 주석 하나가 붙어 있습니다.
extraPortMappings: # 외부에서 접근 가능하도록 30080 을 열어줘야 함
- containerPort: 30080
hostPort: 30080
protocol: TCP
kind로 만든 클러스터의 특성입니다. 3기 3회차 큐레이션에서 소개해드린 이혜원 님의 노트에 이런 설명이 있었죠. "kind는 도커 컨테이너 안에서 쿠버네티스 클러스터 환경을 구성"하는 것이라고요.
그러니까 클러스터가 컨테이너 안에 있어서, 바깥에서 들어가려면 포트를 미리 뚫어둬야 합니다. 그리고 이건 클러스터를 만들 때만 정할 수 있어요. 나중에 추가하려면 다시 만들어야 하죠.
"먼저 열어둬야 한다"는 게 이런 작업의 순서를 만듭니다.
🤖 kind가 Pod가 아닌 이유
이 기록에서 가장 중요한 설명이 이 대목입니다.
쿠버네티스 명령어
kubectl apply -f로 해당 yaml을 실행하면 strimzi operator가 자동으로 생성해줌여기가 오퍼레이터 패턴을 이해하는 문턱입니다.
보통 쿠버네티스에서는 kind: Pod, kind: Deployment, kind: Service를 씁니다. 쿠버네티스가 아는 종류들이죠.
그런데 이 YAML에는 이렇게 적혀 있습니다.
apiVersion: kafka.strimzi.io/v1beta2 kind: Kafka
쿠버네티스는 Kafka라는 종류를 모릅니다. Strimzi가 설치되면서 "이런 종류가 있다"고 등록해준 거예요. 글이 앞에서 짚은 CRD가 그겁니다.
그리고 오퍼레이터가 그 선언을 보고 실제 Pod와 Service와 볼륨을 만듭니다. 사람이 "카프카 브로커 Pod 세 개, 그 앞에 Service 하나, 각각 100Gi 볼륨"을 적는 게 아니라, "Kafka 클러스터가 필요하다"만 적는 거예요.
3기 7회차 큐레이션에서 소개해드린 이지은 님의 GitOps 기록에 나온 이야기와 이어집니다. 쿠버네티스가 선언한 상태를 유지한다면, 오퍼레이터는 선언할 수 있는 것의 종류를 늘리는 셈입니다.
⚙️ 오퍼레이터가 대신 하는 일
Strimzi가 무엇을 대신 해주는지도 목록으로 적었습니다.
클러스터의 브로커 또는 파티션 수를 동적으로 조절하고, 클러스터 업그레이드도 자동화
Prometheus, Grafana 등 모니터링 도구와 연동이 용이
두 번째 줄이 오퍼레이터의 진짜 값어치입니다.
Kafka 브로커를 늘리는 건 Pod를 하나 더 띄우는 게 아닙니다. 파티션을 다시 나눠야 하고, 데이터를 옮겨야 하고, 다른 브로커들에게 알려야 해요. 운영 지식이 필요한 절차입니다.
오퍼레이터는 그 절차를 코드로 담고 있습니다. 그래서 YAML의 replicas 숫자를 고치면 나머지를 알아서 합니다.
그리고 실제 Kafka 설정에도 눈에 띄는 대목이 있습니다.
config: offsets.topic.replication.factor: 1 transaction.state.log.replication.factor: 1 min.insync.replicas: 1
전부 1입니다. 실습용 단일 노드라서요. 운영이라면 3 이상으로 두는 값들인데, 연습 환경에 맞춰 낮춰둔 것입니다.
KafkaBridge도 붙였어요.
Kafka 프로토콜을 몰라도 HTTP로 메시지를 보낼 수 있게 해주는 부품입니다. 클라이언트 라이브러리를 붙이기 어려운 환경에서 쓰죠.
📝 원문에서는 이렇게 씁니다
이 기록은 명령어와 YAML만으로 되어 있습니다. 설명 문장이 거의 없고, 대신 필요한 자리에 주석이 붙어요.
helm install strimzi-kafka-operator strimzi/strimzi-kafka-operator \ --namespace strimzi-kafka \ # 네임스페이스 지정 # --version 0.8.2 # 특정 버전 지정
주석 처리된 --version이 눈에 띕니다. 지금은 최신을 쓰지만 버전을 고정하고 싶을 때 쓰라고 남겨둔 거예요.
3기 3회차에서 소개해드린 박성훈 님의 도커 노트가 출력을 통째로 남긴 형식이었다면, 이 기록은 입력만 남긴 형식입니다. 같은 스터디에서 나온 두 가지 노트 방식이에요.
큐레이터 노트
"kind가 Pod가 아닌 이유"를 짚은 한 문장 때문에 골랐습니다.
오퍼레이터를 처음 쓰면 여기서 헷갈립니다. YAML을 적는 것도 kubectl apply를 쓰는 것도 똑같은데, 적는 내용이 다르거든요. 그리고 왜 다른지 설명 없이 따라 하면 계속 남의 코드처럼 느껴집니다.
이분은 그 자리를 정확히 짚었어요. 쿠버네티스가 아는 종류와 Strimzi가 추가한 종류가 다르다는 것요.
도커 1주차부터 시작해 쿠버네티스를 거쳐 여기까지 온 순서도 자연스럽습니다. 컨테이너 하나를 띄우는 데서 시작해서, 클러스터 위에 미들웨어를 얹는 단계까지 왔어요.
02
📞 Effective C# — 델리게이트를 사용한 콜백
박준형 님
『Effective C#』 8장을 정리한 노트입니다. 델리게이트로 콜백을 표현하라는 항목이요.
개념 → 내장 델리게이트 타입 → 실제 쓰이는 자리 → 멀티캐스트의 함정 순서로 갑니다.
List<T>와 LINQ의 각 메서드가 어떤 델리게이트 타입을 받는지 표로 정리했어요.마지막에 멀티캐스트 델리게이트의 문제점 세 가지를 짚습니다.
조금 더 들어가 보면
🤝 인터페이스보다 느슨한 것
델리게이트를 쓰는 이유가 인터페이스와의 비교로 나옵니다.
왜? 콜백을 받는 객체는 반드시 인터페이스를 구현해야 하고, 여러 곳에 동시에 알릴 수가 없음
두 가지 이유를 들었습니다.
첫째, 인터페이스를 쓰려면 클래스 전체가 그 약속을 따라야 합니다. 메서드 하나만 넘기고 싶은데 클래스를 만들고 인터페이스를 구현해야 하죠.
둘째가 더 큽니다. 인터페이스 방식은 한 명에게만 알릴 수 있어요. setListener(x)를 두 번 부르면 앞의 게 덮어써집니다.
델리게이트는 다릅니다.
한 델리게이트에 여러 함수 등록 가능 = 멀티캐스트 델리게이트
여러 명에게 동시에 알릴 수 있습니다. C#의 이벤트가 이 위에 서 있고요.
그리고 콜백의 정의도 정확합니다.
실행할 타이밍에 대한 결정
"타이밍에 대한 결정"이 핵심입니다. 함수를 넘겨준 쪽은 언제 불릴지 모릅니다. 받은 쪽이 정해요.
🔤 이름만 알면 되는 세 가지
내장 델리게이트 타입 정리가 실용적입니다.
Predicate<T> : T → boolFunc<T> : 여러 입력 → 값 반환Action<T> : 여러 입력 → 반환 없음(void)세 개만 알면 대부분이 됩니다.
그리고 조건이 붙어 있어요.
타입 안정적이라는 게 이 뜻입니다. 함수 포인터처럼 아무거나 넘길 수 없고, 모양이 맞아야 컴파일이 됩니다.
그다음이 이 노트에서 가장 쓸모 있는 부분입니다. List<T>와 LINQ의 메서드가 각각 무엇을 받는지 표로 정리했어요.
| 구문 | 델리게이트 타입 |
|---|---|
numbers.Find(n => n % 2 == 1) |
Predicate<int> |
numbers.ForEach(item => ...) |
Action<int> |
Where |
Func<T, bool> |
Select |
Func<T, TResult> |
Aggregate |
Func<TAcc, T, TAcc> |
람다를 쓸 때 무엇이 오가는지가 보입니다.
이게 왜 유용하냐면, 평소에는 델리게이트를 쓰고 있다는 걸 의식하지 못하기 때문입니다. 글도 그 점을 짚었어요.
Where(x => x > 3)을 쓸 때 우리는 조건을 적는다고 생각하지, 함수를 인자로 넘긴다고 생각하지 않습니다. 그런데 실제로는 후자예요.
Aggregate의 Func<TAcc, T, TAcc>가 특히 그렇습니다. 누적값과 현재 항목을 받아 새 누적값을 내놓는 함수라는 게 타입에 다 적혀 있어요.
🧵 UI 스레드로 건너가기
실무 사례로 WPF와 WinForms의 마샬링을 들었습니다.
Thread thread = new Thread(() => {
string text = "업데이트 완료!";
// UI 스레드로 델리게이트를 마샬링
Application.Current.Dispatcher.Invoke(() => {
myLabel.Content = text; // ✅ UI 스레드 안으로 들어와서 안전
});
});
UI는 아무 스레드에서나 건드릴 수 없습니다. 화면을 그리는 스레드가 정해져 있고, 다른 데서 손대면 터지거나 이상하게 동작해요.
그래서 Invoke에 함수를 통째로 넘깁니다. "이 일을 UI 스레드에서 해달라"고요. 값을 보내는 게 아니라 할 일을 보내는 것이죠.
3기 7회차 큐레이션에서 소개해드린 박상훈 님의 Flutter 글에 compute가 나왔는데, 방향이 반대입니다. 그쪽은 무거운 계산을 화면 스레드 밖으로 내보내는 것이고, 이쪽은 화면 갱신을 안으로 들여오는 것이에요.
둘 다 함수를 다른 실행 맥락으로 옮기는 일입니다.
⚠️ 여럿을 부를 때 생기는 일
마지막이 멀티캐스트의 함정입니다. 세 가지를 짚었어요.
마지막으로 호출된 함수의 리턴값만 유효
정확한 제어가 필요한 멀티캐스트 콜백 구조는
GetInvocationList()를 써서 직접 제어해야 함첫 번째가 특히 위험합니다.
함수 다섯 개를 등록해뒀는데 두 번째에서 예외가 나면, 세 번째부터는 아예 안 불립니다. 그리고 부르는 쪽에서는 왜 나머지가 안 됐는지 알기 어려워요.
두 번째도 조용한 함정입니다. 반환값이 있는 델리게이트에 여러 함수를 걸면 마지막 것만 남습니다. 나머지 결과는 그냥 버려져요.
그래서 해법이 GetInvocationList()입니다.
멀티캐스트에서 콜백 결과에 대한 누적 처리가 필요할 경우 따로 호출해야 한다는 것
목록을 꺼내서 하나씩 부르는 것이죠. 그러면 각각을 try로 감쌀 수 있고, 반환값도 다 모을 수 있습니다.
편한 기능에는 조건이 붙는다는 이야기이기도 합니다. 8회차 큐레이션에서 다룬 것과 같은 성격이에요.
📝 원문에서는 이렇게 씁니다
이 노트는 책 한 항목을 정리한 형식입니다. 제목이 「델리게이트를 이용하여 콜백을 표현하라」인데, 『Effective』 시리즈의 항목 제목을 그대로 쓴 거예요.
그리고 표가 두 개 들어갑니다. List<T> 메서드 네 개, LINQ 메서드 일곱 개요. 책에는 없는 정리입니다. 개념을 읽고 나서 실제로 어디서 만나는지를 직접 찾아 붙인 거죠.
이분의 3기 기록을 보면 Effective C# Chapter1의 item1·2부터 시작해 Chapter8까지 이어집니다. 항목 단위로 계속 쌓고 있어요.
큐레이터 노트
이미 쓰고 있던 걸 이름 붙여 본 기록이라 골랐습니다.
Where와 Select는 C#을 쓰면 매일 만납니다. 그런데 그게 델리게이트를 넘기는 일이라는 건 따로 배워야 알게 돼요. 표로 정리한 부분이 그 연결을 만듭니다.
그리고 멀티캐스트의 함정 세 가지. 잘 쓰다가 규모가 커지면 걸리는 자리입니다. 이벤트에 핸들러를 여러 개 붙였는데 하나가 예외를 던져서 뒤가 안 불리는 상황요.
책을 정리하면서 책에 없는 표를 만들어 붙인 것이 이 노트의 성격입니다. 읽고 끝내지 않았어요.
03
📐 리액트 네이티브 (2) — 화면 Layout
이지은 님
강의를 따라가며 정리한 화면 레이아웃 기록입니다.
네 부분으로 갑니다. 기기별 사이즈 대응 → 헤더 구성 → 푸터 구성 → 페이지별 옵션이요.
첫 부분에 폰트 크기를 화면 너비에 연동하는 계산식이 나옵니다.
헤더는 세 종류로 나눠 정리했어요. 글로벌·로컬·특수요.
3기 1·5·7회차에서도 이분의 기록을 소개해드렸습니다. Next 에러 해결에서 쿠버네티스 GitOps까지 갔다가 다시 프런트로 돌아왔어요.
조금 더 들어가 보면
📏 화면 너비에 폰트를 매단다
첫 부분이 사이즈 단위 정리입니다.
rem : 루트(html)요소의 font-size에 연동 (default 16px)
차이가 기준점입니다. em은 부모를 따라가고 rem은 항상 루트를 봐요. 그래서 em은 중첩되면 곱해지고, rem은 어디서든 같습니다.
그리고 여기서 나온 계산이 이 기록의 핵심입니다.
특정 모바일 휴대기기의 vw(100%)를 360과 비교, 폰트 사이즈 = 100vw / 360 × 16
html {
font-size: calc((40 / 9) * 1vw)
}
100 / 360 × 16 = 4.44…이고, 이게 40 / 9입니다.
무슨 일이 일어나는 걸까요. 1vw는 화면 너비의 1%예요. 그러니 루트 폰트 크기가 화면 너비에 비례하게 됩니다.
그러면 모든 rem 값이 함께 따라옵니다. 360px 화면에서는 16px이던 게, 400px 화면에서는 17.8px이 되죠. 디자인 시안의 비율이 어느 기기에서든 유지됩니다.
한 줄로 전체 화면의 스케일을 잡는 방법이에요. 그리고 이게 "규칙만 남기고 맡기는" 방식의 전형입니다. 기기마다 값을 정해주는 게 아니라 비례식 하나를 남겨두는 것이니까요.
기기 종류별 대응 방향도 갈랐습니다.
데스크톱 — 휴대용 모바일용 화면에서 가로 늘리기와 여백 늘리기를 통해 대응
폰트까지 계속 키우지는 않는다는 것. 어느 지점부터는 글자를 키우는 대신 가로로 펴거나 여백을 늘리는 게 맞습니다.
🎩 헤더를 세 종류로
헤더 정리가 세 갈래입니다.
로컬 헤더 — 헤더가 페이지의 독특한 특성을 반영하도록 구성하여 유지·보수 편의성 확보
특수한 헤더 — 지도, 맵 등의 경우 투명한 헤더 구성
둘로 나누는 게 아니라 셋으로 나눈 게 눈에 띕니다.
글로벌만 있으면 특수한 페이지에서 억지로 맞추게 되고, 페이지마다 따로 만들면 공통 부분이 흩어집니다. 중간 층이 하나 있어야 둘 다 피할 수 있어요.
로컬 헤더가 필요한 조건도 밝혔습니다.
헤더가 데이터를 알아야 할 때요. 게시글 제목을 헤더에 띄운다면 그 헤더는 게시글을 알아야 하니까요.
그리고 겹침 처리에 대한 설명이 실무적입니다.
아래에 콘텐츠로 인해 스크롤이 있는 경우 해당 층에 헤더와 같은 크기의 공간을 확보
(헤더가 콘텐츠와 서로 의존하지 않도록 함)
괄호 안이 이유입니다. 헤더를 fixed로 띄우면 콘텐츠 위에 뜨는데, 그러면 첫 줄이 가려지죠. 그래서 콘텐츠 쪽에 같은 높이의 빈 공간을 둡니다.
높이를 계산해서 콘텐츠를 밀어내는 대신 이렇게 하면, 둘이 서로를 몰라도 됩니다. 헤더 높이가 바뀌어도 그 빈 공간 값만 바꾸면 되고요.
🦶 짧은 화면에서도 바닥에
푸터 문제가 명확히 서술되어 있습니다.
콘텐츠가 짧은 경우 푸터와의 사이에 길이 조정이 가능한 요소를 배치(푸터를 내려줌)
이게 흔히 sticky footer라 부르는 문제입니다. 내용이 짧으면 푸터가 화면 중간에 떠 있게 되죠.
해법을 세 단계로 적었습니다.
➡ 화면의 전체 height 설정 (기기의 vh를 minHeight로 하여 스크롤도 가능하도록)
➡ div 추가 (flex 설정, 콘텐츠와 푸터 사이의 공간이 생기는 경우 푸터를 아래로 밀어냄)
빈 div 하나가 남는 공간을 다 먹는 방식입니다. flex: 1을 주면 여유가 있을 때만 늘어나요.
height가 아니라 minHeight를 쓴 것도 짚어둘 만합니다. height로 고정하면 내용이 길 때 넘쳐버리거든요. 최소값으로 두면 짧을 땐 화면을 채우고 길 땐 늘어납니다.
그리고 주의사항 하나.
flex는 바로 아래 자식에게만 적용됩니다. 중간에 div가 하나 끼면 그 안쪽은 영향을 못 받아요. 실제로 이것 때문에 안 되는 경우가 많습니다.
📝 원문에서는 이렇게 씁니다
첫 문단에 상황을 밝혔습니다.
거의 클론 코딩 수준으로 따라해보는 중이다
"클론 코딩 수준"이라고 적었어요. 자기가 설계한 게 아니라 따라 하는 중이라는 것을 밝힌 겁니다.
그런데 정리된 내용을 보면 단순히 따라 친 게 아닙니다. 40 / 9가 어디서 나온 숫자인지 계산식을 복원해뒀고, 헤더를 세 종류로 분류했고, 푸터 해법을 세 단계로 나눴어요.
따라 하면서 왜 그런지를 붙여둔 것입니다.
그리고 마지막에 HEADER_OPTIONS 상수 파일이 나옵니다. 페이지 경로마다 헤더 설정을 객체로 적어두는 방식이에요.
"/section02/02-04-layout-header-transparent": {
hasLogo: true,
hasBack: true,
title: "사진이 보여요",
isTransparent: true,
},
컴포넌트마다 분기하는 대신 표 한 장으로 모은 것입니다. 2기 6회차 큐레이션에서 소개해드린 정원석 님의 get-img.js와 발상이 같아요. 흩어질 설정을 한 파일에 모으는 것요.
큐레이터 노트
계산식을 복원해둔 것이 이 기록을 고른 이유입니다.
calc((40 / 9) * 1vw)만 적혀 있으면 다음에 봐도 무슨 숫자인지 모릅니다. 디자인 기준 폭이 360px에서 375px로 바뀌면 다시 계산해야 하는데 방법을 모르게 되죠.
이분은 100vw / 360 × 16이라는 유도 과정을 남겼습니다. 그러면 기준이 바뀌어도 다시 만들 수 있어요.
3기 1회차에서 CJS·ESM 정리를, 7회차에서 GitOps를 소개해드렸는데, 이번엔 다시 화면 쪽입니다. 프런트에서 인프라로 갔다가 돌아온 궤적이 3기 기록에 그대로 남아 있어요.
💡 세 편을 겹쳐 읽으며
세 편의 자리가 다릅니다. 쿠버네티스 위의 미들웨어, C# 언어 기능, 모바일 화면 레이아웃이요.
그런데 셋 다 내가 정하지 않고 규칙만 남기는 이야기입니다.
무엇을 남기고 무엇을 맡기나
이태형 님의 Strimzi는 "Kafka 클러스터가 필요하다"만 남깁니다. Pod를 몇 개 만들지, 볼륨을 어떻게 붙일지는 오퍼레이터가 정해요.
박준형 님의 델리게이트는 "이 함수를 쓰라"만 남깁니다. 언제 부를지는 받는 쪽이 정하고요.
이지은 님의 레이아웃은 "화면 너비의 이 비율"만 남깁니다. 실제 픽셀 값은 기기가 정합니다.
셋 다 결정을 미뤘습니다. 그런데 미룬 게 아니라 더 잘 아는 쪽에게 넘긴 것이에요. 클러스터 상태는 오퍼레이터가 더 잘 알고, 호출 시점은 부르는 쪽이 더 잘 알고, 화면 크기는 기기가 압니다.
넘기려면 표현할 수 있어야 한다
그런데 넘기려면 표현 수단이 있어야 합니다.
Strimzi는 CRD로 kind: Kafka라는 새 어휘를 만들었습니다. 쿠버네티스가 원래 모르던 종류예요. 이 어휘가 없으면 "Kafka가 필요하다"를 적을 방법이 없습니다.
C#은 Func·Action·Predicate라는 타입을 준비해뒀습니다. 함수를 값처럼 넘기려면 그 함수의 모양을 적을 수 있어야 하니까요.
CSS는 vw와 rem이라는 상대 단위를 갖고 있습니다. 절대 픽셀만 있으면 비례를 표현할 수가 없어요.
어휘가 먼저 있어야 위임이 가능합니다.
맡긴 뒤에 남는 함정
세 편에 공통으로 맡긴 뒤에 생기는 문제도 나옵니다.
박준형 님이 짚은 멀티캐스트 델리게이트가 가장 분명합니다. 여러 함수를 걸어두면 편한데, 하나가 예외를 던지면 뒤가 안 불리고 반환값도 마지막 것만 남아요.
이태형 님의 설정에서도 보입니다. replication.factor: 1은 실습이니까 괜찮지만, 오퍼레이터가 알아서 해준다고 값까지 안 보면 운영에서 데이터를 잃습니다.
이지은 님의 flex 주의사항도 같은 성격이에요. 중간에 div 하나가 끼면 전체 레이아웃 규칙이 안 통합니다.
맡긴다는 게 안 봐도 된다는 뜻은 아닙니다. 3기 7회차에서 본 Probe와 같은 이야기고요.
이번 회차에서 가져갈 것
1. 오퍼레이터의 kind는 쿠버네티스 것이 아니다.
CRD로 등록된 그 오퍼레이터의 어휘입니다. Pod나 Service를 직접 적는 게 아니라 원하는 것을 선언하세요.
2. kind 클러스터는 포트를 만들 때 열어야 한다.
extraPortMappings는 생성 시점에만 정할 수 있습니다. 나중에 필요하면 다시 만들어야 합니다.
3. 멀티캐스트 델리게이트에서 예외를 조심하기.
하나가 던지면 뒤에 등록된 함수가 안 불립니다. 각각을 제어하려면 GetInvocationList()로 꺼내 쓰세요.
4. 루트 폰트를 vw에 매달면 전체가 비례한다.
100vw / 기준폭 × 기준폰트가 계산식입니다. 숫자만 적지 말고 유도 과정을 남겨두세요.
5. sticky footer는 minHeight와 빈 flex 요소로.
height 고정은 내용이 길 때 넘칩니다. 그리고 중간에 감싸는 요소가 끼면 flex가 안 통합니다.
3기 9회차는 2025년 7월 중순이었습니다.
Kafka 클러스터와 C# 언어 기능과 모바일 레이아웃이라는 서로 다른 자리에서, 세 분이 나란히 맡기는 구조를 기록했습니다.
성장일지는 멤버들이 2주에 한 번씩 자신의 성장을 기록하는 활동이에요. 지난 기수의 성장일지를 다시 읽으면서, 그때 소개해드리지 못한 글을 하나씩 꺼내고 있습니다.
좋은 글 남겨주신 이태형 님, 박준형 님, 이지은 님께 감사드립니다. 그리고 3기를 함께 채워주신 모든 멤버분들께도요! 😊
다음 큐레이션으로 다시 찾아오겠습니다! 🌱
- 에디터 · 성장일지 큐레이터 -
※ 본 큐레이션은 각 저자가 공개한 글을 소개하는 것이며, 모든 원문의 저작권은 저자에게 있습니다. 저자 본인의 요청이 있을 경우 즉시 수정 또는 삭제합니다.