안녕하세요! 그로스로그 입니다!😊 🌱
120만 건을 넣는 데 6시간이 걸리던 코드를 9초로 줄인 기록, 1억 건짜리 검색 인덱스를 쪼개다 정합성이 깨진 이야기, 그리고 패키지 관리도구가 왜 필요한지. 3기 1회차 세 편입니다.
3기 1회차, 2025년 3월입니다. 기수가 막 시작하던 무렵이에요. 이번 회차에는 규모가 커지면서 방법을 바꾼 이야기 세 편이 모였습니다.
01
⚡ 120만 건 데이터 삽입, 6시간에서 9초로
권순한 님
영화 데이터 120만 건을 CSV에서 읽어 DB에 넣는 작업을 다섯 번 고쳐 쓴 기록입니다.
처음이 23,887초(약 6.6시간), 마지막이 9초예요.
각 단계마다 가설 → 코드 → 측정값이 붙어 있습니다. 그리고 시작하기 전에 무엇을 검증할지 미리 적어뒀어요.
테스트 환경도 하드웨어 스펙까지 밝혔습니다.
조금 더 들어가 보면
📋 재기 전에 무엇을 잴지 적는다
이 글이 코드보다 먼저 하는 일이 가설을 세우는 것입니다.
save()가 가장 느릴 것이다. → 트랜잭션 오버헤드 때문JPA
saveAll()을 활용하여 트랜잭션 오버헤드를 줄인다. → 여러 개의 INSERT를 한 번에 수행데이터를 Sub-List로 나누어 비동기 병렬 처리를 적용한다.
버퍼를 도입하여 스트리밍 기반 배치 처리를 구현한다.
JDBC를 활용하여 JPA(ORM) 오버헤드를 제거하고 성능을 최적화한다.
다섯 줄이 그대로 다섯 단계의 목차가 됩니다.
그리고 각 줄에 "왜 빨라질 것인가"가 화살표로 붙어 있어요. 그냥 "이걸 해보겠다"가 아니라 "무엇을 줄여서 빨라질 것이다"입니다.
이게 성능 개선 기록에서 중요한 이유가 있습니다. 결과만 적으면 다음에 다른 상황에서 쓸 수가 없거든요. "JDBC가 빠르더라"는 그 프로젝트에서만 통하는데, "ORM 오버헤드를 제거하니 빨라지더라"는 다른 자리에도 적용됩니다.
측정 조건도 밝혔습니다. Apple M3 Pro(코어 11, 메모리 36G), Spring Boot 3.4.3, Java 21, PostgreSQL 16이요. 같은 숫자를 다른 사람이 기대해도 되는지 판단할 근거입니다.
🧵 하나씩과 한꺼번에
1차와 2차 사이의 차이가 이 글에서 가장 큽니다.
| 단계 | 소요 시간 |
|---|---|
1차 save() |
23,887,860ms |
2차 saveAll() |
134,701ms |
약 177배입니다. 코드는 거의 안 바뀌었는데요.
1차는 CSV 한 줄을 읽을 때마다 movieRepository.save(movie)를 부릅니다. 문제를 이렇게 짚었어요.
save() 호출 → 각 INSERT마다 트랜잭션 오버헤드 발생DB Connection 리소스 낭비 → 매 요청마다 새로운 트랜잭션 생성
네트워크 지연 시간 누적 → 대량의 SQL 실행으로 속도 저하
세 번째가 특히 큽니다. DB에 한 번 다녀오는 데 드는 시간이 아주 짧아도, 120만 번이면 그게 전부입니다. 1밀리초씩만 걸려도 20분이에요.
2차는 엔티티를 리스트에 모았다가 saveAll()을 한 번 부릅니다. 왕복 횟수 자체가 줄어드는 것이죠.
이건 DB에만 해당하는 이야기가 아닙니다. 파일을 쓸 때도, API를 부를 때도, 한 번의 비용이 있는 일은 묶어서 하는 게 답입니다.
🪣 버퍼를 두면 메모리가 줄어든다
3차에서 배치 크기 500으로 나눠 CompletableFuture로 병렬 처리해 38초까지 갔습니다. 그런데 4차에서 다시 27초로 줄었어요. 같은 병렬 처리인데 뭐가 달랐을까요.
for (CSVRecord record : records) {
movies.add(movie);
if (movies.size() >= BATCH_SIZE) { // 500개가 차면
List batch = new ArrayList<>(movies);
movies = new ArrayList<>(BATCH_SIZE); // 바로 비운다
CompletableFuture.runAsync(() -> movieRepository.saveAllAndFlush(batch), executor);
}
}
글이 차이를 이렇게 적었습니다.
4차에서는 즉시 배치 크기만큼 쌓일 때마다 저장 & 초기화 → Buffer 개념
불필요한 리스트 유지 방지 & GC 부담 감소
3차는 120만 개를 전부 메모리에 올려놓고 시작했습니다. 4차는 500개씩만 들고 있어요.
여기서 재미있는 건 메모리를 아꼈더니 시간도 줄었다는 점입니다. 보통 이 둘은 맞바꾸는 관계라고 생각하는데요.
이유가 GC입니다. 큰 리스트가 메모리에 오래 살아 있으면 가비지 컬렉터가 그걸 계속 확인하고 옮기느라 시간을 씁니다. 짧게 살다 사라지는 객체는 훨씬 싸게 정리되고요.
그리고 읽으면서 동시에 쓰기 시작한다는 것도 있습니다. 3차는 다 읽고 나서야 저장을 시작하는데, 4차는 500개 읽자마자 저장이 돌아갑니다.
⬇️ 추상화를 한 층 내리기
마지막 5차가 27초에서 9초로 갑니다. JPA 대신 JDBC Template을 쓴 것요.
private void bulkInsert(List movies) {
String sql = "INSERT INTO movie (duration_min, release_date, ..., title) VALUES (?, ?, ..., ?)";
jdbcTemplate.batchUpdate(sql, movies, BATCH_SIZE, (ps, movie) -> {
ps.setInt(1, movie.getDurationMin());
// ... 컬럼을 하나씩 손으로 채운다
});
}
SQL을 직접 적고, 컬럼을 하나씩 손으로 채웁니다. JPA가 대신 해주던 걸 다시 가져온 거예요.
이유를 이렇게 적었습니다.
JPA는 저장한 엔티티를 영속성 컨텍스트라는 곳에 계속 들고 있습니다. 나중에 값이 바뀌었는지 확인해서 자동으로 업데이트해주려고요. 평소에는 아주 편한 기능인데, 한 번 넣고 다시 안 볼 120만 건에는 순전히 낭비입니다.
그래서 결론에 이렇게 적었어요.
"일 수 있습니다"라고 여지를 둔 게 정확합니다. 늘 그렇다는 게 아니라, 이런 성격의 작업에서는 그렇다는 것이니까요.
📝 원문에서는 이렇게 닫습니다
결론이 다섯 항목인데, 마지막 하나가 다른 넷과 성격이 다릅니다.
앞의 넷은 얻은 결론인데, 이건 아직 안 해본 것입니다.
500이라는 숫자는 어디서 왔을까요. 흔히 쓰는 값이긴 한데, 이 데이터와 이 환경에서 최적인지는 재보지 않았어요. 그걸 밝혀둔 겁니다.
성능 글에서 이런 문장이 드뭅니다. 2,600배를 줄여놓고도 "여기는 아직 안 봤다"고 적었으니까요.
큐레이터 노트
다섯 번을 다 남긴 게 이 글을 고른 이유입니다.
성능 개선 글은 대개 최종 코드만 보여줍니다. "JDBC Bulk Insert로 9초에 끝냈습니다"요. 그러면 읽는 사람은 그 방법만 가져가게 됩니다.
그런데 이 글에는 중간 네 단계가 다 있습니다. 그래서 어느 개선이 얼마나 기여했는지가 보여요. 177배는 트랜잭션을 묶어서 나왔고, 3.5배는 병렬화에서, 나머지는 버퍼와 JDBC에서 나왔습니다.
이게 왜 중요하냐면, 내 상황에 맞는 단계에서 멈출 수 있기 때문입니다. 12만 건이라면 2차에서 이미 충분할 수도 있어요. 전부 다 할 필요는 없습니다.
그리고 각 단계마다 가설을 먼저 적고 측정으로 확인한 순서. 이건 성능 작업의 기본기인데, 실제로 이렇게 남기는 기록은 많지 않습니다.
02
🗂️ Elasticsearch 대용량 인덱스 롤오버와 분할
김가을 님
1억 건, 6TB짜리 인덱스 하나가 클러스터 전체를 흔들고 있던 상황을 푼 기록입니다.
먼저 시도한 것들을 밝히고, 왜 그걸로 안 됐는지 적었어요.
그리고 ILM Rollover를 테스트했다가 문제를 발견하고 포기합니다. 그 문제가 무엇이었는지가 이 글의 핵심이에요.
최종 해법은 MOD 연산으로 인덱스를 나누는 것이었습니다.
조금 더 들어가 보면
🩹 먼저 해본 것들
상황 설명이 구체적입니다.
"다른 인덱스들에도 악영향"이 문제의 성격을 보여줍니다. 하나가 아파서 전부가 느려지는 상황이에요.
그리고 시도한 것 셋을 적었습니다.
인덱스의
refresh_interval 값을 5초 → 60초 → 180초 → 3600초로 순차적으로 증가Elasticsearch 일부 노드를 스케일업하여 처리 성능 향상
일부 노드를 스케일업 했지만 부하는 그대로 → 근본 원인인 게시글 인덱스를 개선
셋 다 "부하를 견디게 만드는" 방향이었습니다. 색인을 덜 자주 하고, 새로고침 주기를 늘리고, 서버를 키우고요.
refresh_interval을 5초에서 3600초까지 밀어 올린 건 꽤 과감한 조치입니다. 검색 결과에 새 문서가 반영되기까지 한 시간이 걸린다는 뜻이니까요. 그렇게까지 했는데도 안 됐다는 게 상황의 심각성을 말해줍니다.
그리고 판단이 나옵니다. 견디게 만드는 게 아니라 인덱스 자체를 쪼개기로 한 거예요.
🔁 롤오버가 깨뜨린 것
첫 시도는 ILM Rollover였습니다. 인덱스가 30GB를 넘으면 자동으로 새 인덱스를 만드는 정책이요.
PUT _ilm/policy/test-rollover-ilm-policy
{
"policy": { "phases": { "hot": { "actions": {
"rollover": { "max_size": "30gb" }
}}}}
}
그런데 테스트에서 문제가 나왔습니다.
무슨 일이 일어났는지 단계별로 적혀 있어요.
test-000001에 문서 하나를 넣었습니다. 데이터가 늘어 롤오버가 일어나 test-000002가 생겼고, 또 늘어 test-000003이 생겼습니다. 그다음이 문제입니다.
document_id: p123123 문서를 업데이트하려고 하면, 기존 test-000001 인덱스에서 업데이트되지 않고 새로운 test-000003 인덱스에 새로운 문서로 삽입됨.결과적으로 중복 문서가 생성되면서 데이터 정합성이 깨지고, 검색 성능 저하로 이어졌다.
같은 문서가 두 군데에 남습니다.
원인은 롤오버가 쓰기 대상을 옮기는 방식에 있습니다. 별칭(alias)은 그대로인데 is_write_index가 최신 인덱스로 넘어가거든요. 그러니 쓰기는 무조건 최신 인덱스로 갑니다. 그 문서가 원래 어디에 있었는지는 보지 않고요.
그래서 이런 결론이 나옵니다.
도구가 나쁜 게 아니라 데이터 성격이 안 맞았던 겁니다. 로그는 한 번 쌓이면 안 고치니까 롤오버가 잘 맞고, 게시글은 계속 수정되니까 안 맞아요.
🧮 같은 문서는 항상 같은 곳으로
그래서 방식을 바꿉니다. 자동으로 옮겨 다니게 두지 않고, 어디에 넣을지를 직접 정하는 거예요.
const indexNumber = document_id % 5; // 5개의 인덱스로 분할
const indexName = `test-2025-${indexNumber}`;
같은
document_id는 항상 동일한 인덱스로 매핑되므로 업데이트 시에도 일관성 유지나머지 연산의 성질을 그대로 쓴 것입니다. 같은 수를 같은 수로 나누면 나머지도 늘 같으니까요.
이 방식의 좋은 점이 둘입니다. 하나는 계산만으로 위치를 알 수 있다는 것. 어디 있는지 찾아볼 필요 없이 id % 5만 하면 됩니다. 다른 하나는 id가 고르게 퍼져 있으면 다섯 인덱스에 고르게 나뉜다는 것이고요.
대신 치르는 값도 있습니다. 나중에 5를 6으로 바꾸면 거의 모든 문서의 자리가 달라집니다. 다시 넣어야 하죠. 롤오버가 자동으로 해주던 "알아서 늘어나는" 성질을 포기한 셈입니다.
정합성을 지키려고 자동 확장을 포기한 것이고, 게시글 데이터에서는 그게 맞는 교환입니다.
📝 원문에서는 이렇게 닫습니다
결론이 두 문장입니다.
로그성 데이터가 아니라면, Index Template과 MOD 연산을 이용한 인덱싱 전략이 더 효과적이다.
조건이 앞에 붙어 있습니다. "로그성 데이터가 아니라면"요.
그리고 참고 문서 세 개를 남겼는데, 그중 하나가 Elastic 공식 토론 게시판의 롤오버 관련 글입니다. 같은 문제를 겪은 사람들이 모인 곳이죠.
공식 문서만 있는 게 아니라 토론 링크가 함께 있다는 게 눈에 띕니다. 문서에 안 적혀 있는 걸 거기서 찾았다는 흔적이니까요.
큐레이터 노트
안 된 시도를 본문에 그대로 남긴 게 이 글의 값어치입니다.
ILM Rollover는 Elasticsearch의 공식 기능이고, 대용량 인덱스를 다룰 때 가장 먼저 나오는 답입니다. 검색하면 다 이걸 추천해요.
그런데 업데이트가 있는 데이터에는 안 맞습니다. 이건 실제로 넣어보고 중복 문서가 생기는 걸 봐야 알게 되는 것이고요.
이 글은 그 과정을 캡처까지 붙여 남겼습니다. 그래서 같은 상황에 있는 사람이 롤오버를 적용하기 전에 멈출 수 있어요.
그리고 순서도 좋습니다. 견디게 만드는 조치를 먼저 다 해보고, 안 되니까 구조를 바꿨어요. "근본 원인인 게시글 인덱스를 개선"이라고 적은 그 줄이 전환점입니다.
03
📦 패키지 관리도구란?
민성조 님
리눅스의 패키지 관리도구를 정리한 글입니다.
OS 마이그레이션 작업을 하면서 도구의 편리함을 체감한 게 계기예요.
패키지란 무엇인가 → 관리 도구란 무엇인가 → 왜 필요한가 → 장점 → 주의사항 순으로 갑니다.
명령어가 apt·yum·dnf 세 계열로 나란히 붙어 있습니다.
조금 더 들어가 보면
🧱 패키지는 무엇을 묶은 것인가
정의가 세 문장으로 쌓아 올려집니다.
패키지는 소프트웨어의 배포 및 설치가 용이하도록 미리 구성한 것입니다.
패키지는 소프트웨어와 소프트웨어의 실행에 필요한 라이브러리, 설정 파일, 문서 등 구성요소를 하나로 묶은 것입니다.
"무엇인가 → 왜 그렇게 만드나 → 안에 뭐가 들었나" 순서입니다.
세 번째가 핵심이에요. 프로그램 하나를 돌리려면 실행 파일만으로는 안 됩니다. 라이브러리도 있어야 하고, 설정 파일이 정해진 자리에 있어야 하고요. 그걸 다 챙기는 게 설치인데, 패키지는 그 목록을 미리 적어둔 겁니다.
🧩 의존성이 얽히면
관리 도구가 필요한 이유로 든 게 의존성입니다. 그리고 에러 메시지를 그대로 붙였어요.
ERROR: Could not find a version that satisfies the requirement
termcolor~=1.1.0 (from tensorflow==2.4.0)
(from versions: 2.0.0, 2.0.1, 2.1.0, ..., 2.4.0)
ERROR: No matching distribution found for termcolor~=1.1.0
메시지 하나에 상황이 다 들어 있습니다.
tensorflow 2.4.0이 termcolor 1.1.x를 원하는데, 받을 수 있는 건 2.0.0부터입니다. 원하는 버전이 이미 사라진 거예요.
이런 게 하나면 손으로 풀 수 있습니다. 그런데 패키지가 수십 개고 서로 물려 있으면 못 풀어요. A가 B의 1.x를 원하고 C가 B의 2.x를 원하면 답이 없는 경우도 생깁니다.
글이 든 정리가 담백합니다.
"모든 오류를 해결할 수 있는 것은 아니지만"이 정직합니다. 도구를 소개하는 글에서 이 단서를 다는 경우가 드물어요.
장점 세 가지를 든 것도 실용적입니다. 간단한 제거, 서버 내 버전 확인, 간단한 다운그레이드요. 그리고 각각에 apt·yum·dnf 명령어를 나란히 적었습니다.
# 의존성 정리(필요없는 패키지 자동 정리) sudo apt autoremove -y sudo dnf autoremove -y
autoremove가 목록에 있는 게 눈에 띕니다. 이건 "A를 깔면서 딸려 온 B를, A를 지운 뒤 같이 치우는" 명령이에요. 손으로 관리하면 절대 못 하는 일입니다. 무엇이 무엇 때문에 깔렸는지를 기록해두는 도구만 할 수 있는 정리니까요.
⚠️ 섞어 쓰지 말 것
주의사항 첫 항목이 이 글에서 가장 실무적입니다.
어떤 때는 rpm으로 패키지를 설치하고, 또 어떤 때는 tar로 패키지를 설치하는 경우가 있을 수 있습니다.
패키지 관리 도구는 시스템에 설치된 패키지를 데이터베이스로 관리하여, 의존성, 업데이트, 제거 등의 작업을 일관되게 처리할 수 있도록 설계되어 있습니다.
반면, tar로 패키지를 설치하는 경우에는 의존성, 업데이트, 제거 등의 메타데이터가 기록되지 않기 때문에
"데이터베이스로 관리"가 핵심입니다.
패키지 관리 도구가 강한 이유는 설치를 대신해줘서가 아니라, 무엇이 어디에 깔렸는지 목록을 들고 있어서입니다. 그 목록이 있으니 지울 때 뭘 지워야 할지 알고, 업데이트할 때 뭐가 영향받는지 알아요.
tar로 직접 풀면 그 목록에 안 올라갑니다. 그러면 도구는 그게 없는 줄 압니다. 나중에 같은 걸 패키지로 깔면 파일이 겹쳐서 이상하게 동작하고, 지울 때도 뭐가 남았는지 알 수 없어요.
한 번의 편의가 시스템 전체의 목록을 흐리는 것입니다. 그래서 "일관성"이라는 제목이 붙었고요.
두 번째 주의사항도 실전에서 자주 만납니다.
배포판 기본 저장소에 없는 소프트웨어가 꽤 많거든요. 그때 tar로 받아서 푸는 대신 저장소를 추가하는 쪽을 택하라는 이야기입니다. 앞의 일관성 원칙과 이어집니다.
📝 원문에서는 이렇게 씁니다
글의 시작이 실제 작업입니다.
운영환경에 적용하기 전, 기존 소프트웨어의 rpm을 VM에 여러 번 설치하고, 각 소프트웨어 간 의존성을 테스트하면서 패키지 관리 도구의 편리함을 체감했습니다.
"여러 번 설치하고"가 이 글이 어디서 나왔는지 말해줍니다. 문서를 읽고 정리한 게 아니라, 같은 일을 반복하다가 도구의 값어치를 알게 된 것이에요.
그리고 참고 자료를 네 곳 남겼습니다. Debian, Ubuntu, Fedora, Red Hat 공식 문서요. 네 배포판을 다 걸어둔 것은, 이 주제가 배포판마다 도구가 다른 영역이기 때문입니다. 본문에 apt·yum·dnf를 나란히 적은 것과 같은 맥락이고요.
큐레이터 노트
"당연한 것"을 정리한 글이라 골랐습니다.
apt install은 개발을 시작하면 며칠 안에 치게 되는 명령어입니다. 그래서 그게 무슨 일을 하는지 따로 배우지 않고 넘어갑니다.
이 글은 거기로 돌아갔어요. 패키지가 뭘 묶은 것인지, 도구가 왜 있는지, 안 쓰면 뭐가 곤란한지요.
그리고 그 답이 "목록을 들고 있다"로 모입니다. 설치를 자동화해서가 아니라 기록을 남겨서 강하다는 것. 이건 실제로 쓰다가 한 번 데어봐야 알게 되는 부분입니다.
앞의 두 글이 규모가 커져서 방법을 바꾼 이야기였다면, 이건 규모가 커지기 전에 방법을 정해두는 이야기입니다. 패키지 세 개일 때는 tar로 풀어도 되거든요.
💡 세 편을 겹쳐 읽으며
세 글의 자리가 다릅니다. 애플리케이션 배치 처리, 검색 엔진 인덱스, 서버 패키지 관리요.
그런데 셋 다 숫자가 커지면서 생긴 문제를 다룹니다. 120만 건, 1억 건, 그리고 얽힌 의존성이요.
적을 때 되던 방법이 안 된다
save()를 한 줄씩 부르는 건 잘못된 코드가 아닙니다. 천 건이었다면 20초에 끝났을 겁니다.
하나의 인덱스에 다 넣는 것도 잘못이 아니에요. 백만 건까지는 아무 문제가 없습니다.
tar로 풀어서 쓰는 것도 그렇습니다. 소프트웨어 세 개면 그게 더 빠릅니다.
세 경우 모두 방법이 틀린 게 아니라 규모가 넘어선 것입니다. 그래서 "어느 방법이 옳은가"라는 질문 자체가 성립하지 않아요. "몇 개까지 이 방법이 되는가"가 맞는 질문입니다.
나누는 방식이 다르다
셋 다 결국 나눕니다. 그런데 기준이 다릅니다.
권순한 님은 개수로 나눕니다. 500개씩 끊어서 처리해요. 순서는 상관없고 개수만 맞으면 됩니다.
김가을 님은 id로 나눕니다. id % 5요. 개수가 아니라 같은 문서가 늘 같은 자리로 가는 것이 목적입니다.
민성조 님이 소개한 패키지는 기능으로 나뉩니다. 실행 파일과 라이브러리와 설정을 한 덩어리로요.
무엇을 목적으로 나누느냐가 나누는 기준을 정합니다. 처리량이면 개수로, 일관성이면 키로, 배포면 기능으로요.
쓰던 도구를 내려놓는 판단
두 글에 같은 장면이 나옵니다. 잘 쓰던 것을 그만두는 순간이요.
권순한 님은 JPA를 내려놓고 JDBC로 갔습니다. JPA가 나빠서가 아니라 이 작업에 필요 없는 기능의 비용이 컸기 때문이에요.
김가을 님은 ILM Rollover를 포기했습니다. 공식 기능이고 대부분의 경우 정답인데, 이 데이터에는 맞지 않았기 때문입니다.
둘 다 "왜 안 맞는지"를 정확히 짚고 나서 내려놨습니다. 그냥 안 되니까 바꾼 게 아니라, 영속성 컨텍스트 비용과 is_write_index 동작이라는 이유를 찾고 나서요.
이유를 알고 내려놓으면 다음에 그 도구를 언제 다시 써야 하는지도 알게 됩니다.
이번 회차에서 가져갈 것
1. 반복 호출을 묶는 것부터 해보기.
120만 건 최적화에서 가장 큰 몫(약 177배)은 save()를 saveAll()로 바꾼 데서 나왔습니다. 왕복 횟수를 줄이는 게 대개 첫 번째 답입니다.
2. 전부 메모리에 올리지 않기.
다 모았다가 나누는 것보다 차는 대로 흘려보내는 편이 메모리도 시간도 낫습니다. GC 부담이 줄고, 읽기와 쓰기가 겹쳐 돌아갑니다.
3. 자동으로 옮겨 다니는 구조는 업데이트와 충돌할 수 있다.
Elasticsearch 롤오버는 로그처럼 안 고치는 데이터에만 맞습니다. 수정이 잦다면 키로 자리를 고정하세요.
4. 견디게 만드는 조치와 구조를 바꾸는 조치를 구분하기.
주기를 늘리고 서버를 키우는 건 시간을 버는 일입니다. 그걸로 안 되면 원인을 다시 봐야 합니다.
5. 패키지는 도구로만 설치하기.
tar로 직접 풀면 관리 도구의 목록에 안 올라갑니다. 저장소에 없으면 저장소를 추가하는 쪽이 낫습니다.
3기 1회차는 2025년 3월, 기수가 막 시작하던 무렵이었습니다.
배치 처리와 검색 엔진과 서버 운영이라는 서로 다른 자리에서, 세 분이 나란히 규모가 방법을 바꾸는 순간을 기록했습니다. 첫 회차부터 밀도가 높았어요.
성장일지는 멤버들이 2주에 한 번씩 자신의 성장을 기록하는 활동이에요. 지난 기수의 성장일지를 다시 읽으면서, 그때 소개해드리지 못한 글을 하나씩 꺼내고 있습니다.
좋은 글 남겨주신 권순한 님, 김가을 님, 민성조 님께 감사드립니다. 그리고 3기를 함께 채워주신 모든 멤버분들께도요! 😊
다음 큐레이션으로 다시 찾아오겠습니다! 🌱
- 에디터 · 성장일지 큐레이터 -
※ 본 큐레이션은 각 저자가 공개한 글을 소개하는 것이며, 모든 원문의 저작권은 저자에게 있습니다. 저자 본인의 요청이 있을 경우 즉시 수정 또는 삭제합니다.