GROWTH LOG STORIES

함께 만든 성장을,
기록으로 연결합니다.

AI와 함께 배우고, 직접 만들고, 서로 나눈
그로스로그 멤버들의 생생한 성장 기록을 만나보세요.

성장 기록
5기+이어진 커뮤니티
HEALTHIO
PROJECT

사진 한 장으로 시작하는 건강 루틴, Health IO

대표 이야기 읽기

ALL STORIES

성장의 순간을
한곳에서 만나보세요.

프로젝트의 시작부터 모임에서 나눈 인사이트까지,
그로스로그가 함께 만든 기록을 모았습니다.
개의 성장 기록

성장일지

[GL 2기 10회차] 늦게 온 사람에게 무엇을 줄까 — RxJS 서브젝트, CJS와 ESM, Vue 개발환경 가이드 🌱

GROWTH LOG🌱 2026. 8. 9. 04:08

안녕하세요! 그로스로그 입니다!😊 🌱

이미 시작된 흐름에 누군가 나중에 합류하면, 그 사람에게는 무엇을 보여줘야 할까요. 2기 10회차 세 편입니다.

성장일지는 멤버들이 2주에 한 번씩 자신의 성장을 기록하는 활동이에요. 지난 기수의 성장일지를 다시 읽으면서, 그때 소개해드리지 못한 글을 하나씩 꺼내고 있습니다.

2기 10회차, 2025년 1월 하순입니다. 이번 회차에는 여럿이 같은 것을 나눠 쓰는 이야기가 세 편 모였어요. 스트림을 나눠 쓰고, 코드를 나눠 쓰고, 개발 환경을 나눠 씁니다.

 

01

📡 11장. 서브젝트 요약

남기준 님

남기준 님 원문에 실린 이미지
이런 내용이에요
RxJS의 서브젝트(Subject) 한 장을 통째로 정리한 글입니다. 이번 회차 세 편 중 가장 긴 분량이에요.
콜드·핫 옵저버블 구분에서 시작해, 서브젝트를 옵저버블로 쓰는 법과 옵저버로 쓰는 법, 옵저버블과 연결하는 법, 에러·완료 처리, 그리고 네 가지 종류까지 갑니다.
모든 항목에 코드와 실행 결과가 짝으로 붙어 있습니다.

3·4·5회차에서도 이분의 RxJS 정리를 소개해드렸습니다. 필터링 연산자, tap, 조건 연산자에 이어 서브젝트 차례예요.

조금 더 들어가 보면

❄️ 구독할 때마다 처음부터

이 글이 서브젝트를 설명하기 전에 문제부터 보여줍니다.

const intervalSource$ = interval(500).pipe(take(5));

intervalSource$.subscribe(observerA);
setTimeout(() => {
  intervalSource$.subscribe(observerB);
}, 2000);

A가 먼저 구독하고, 2초 뒤에 B가 구독합니다. 실행 결과가 이렇게 나와요.

observerA: 0
observerA: 1
observerB: 0     ← B가 0부터 시작한다
observerA: 2
observerB: 1
observerA: 3
observerB: 2
observerA: 4
observerA: complete
observerB: 3
observerB: 4
observerB: complete

B도 0부터 받습니다. A가 이미 2까지 갔는데도요.

글이 이유를 한 줄로 적었습니다.

subscribe 함수를 호출하는 각 옵저버블 구독이 따로 동작하며 매번 새로 구독하는 구조다.

이걸 콜드 옵저버블이라고 부릅니다. 구독할 때마다 흐름이 새로 만들어지는 것요.

라디오와 녹음테이프에 비유하면 이해가 빠릅니다. 라디오는 늦게 켜면 앞부분을 놓치지만, 테이프는 언제 틀어도 처음부터죠. 콜드 옵저버블은 테이프입니다.

문제는 이게 늘 좋은 건 아니라는 겁니다. 서버에 요청을 보내는 옵저버블이라면, 구독자가 셋이면 요청이 세 번 나갑니다.

🔗 하나로 묶는 다리

그래서 서브젝트가 나옵니다. 정의가 이래요.

멀티캐스팅을 지원하기 위해 옵저버이면서 옵저버블이라는 특성이 있다.

두 역할을 동시에 한다는 게 핵심입니다. 옵저버블이니까 구독당할 수 있고, 옵저버니까 남을 구독할 수 있어요.

그래서 이런 연결이 됩니다.

subject.subscribe(observerA);
intervalSource$.subscribe(subject);   // 서브젝트가 옵저버 노릇
setTimeout(() => subject.subscribe(observerB), 2000);

가운데에 서브젝트를 하나 두었습니다. 원본은 서브젝트 하나만 구독하고, 옵저버들은 서브젝트를 구독해요.

글이 이 구조를 한 줄로 요약했습니다.

서브젝트는 서브젝트 하나에서 스트림 하나가 여러 옵저버로 전파되는 구조이다.

그리고 intervalSource$.subscribe(subject)라고 서브젝트를 그대로 넘긴 대목을 따로 짚었어요.

서브젝트는 옵저버이기도 하므로 옵저버블에서 값을 바로 서브젝트로 전달해줘서 함수 중복을 피할 수 있다.

원래는 next, error, complete 세 함수를 손으로 적어 넘겨야 했는데, 서브젝트가 이미 그 셋을 갖고 있으니 통째로 넘기면 되는 겁니다.

이제 B는 2초 뒤에 합류하면 그때 흐르고 있는 값부터 받습니다. 테이프가 라디오로 바뀐 셈이에요.

⏰ 늦게 온 사람에게 무엇을 줄까

그런데 라디오로 바뀌면 새 문제가 생깁니다. 늦게 온 사람은 앞부분을 놓친다는 것요.

이 글의 후반부가 통째로 그 문제를 다룹니다. 서브젝트 네 종류가 답을 하나씩 갖고 있어요.

기본 Subject아무것도 안 줍니다.

next 함수 — subscribe 함수 호출 전 전달한 값은 이후 구독하는 옵저버로 전달하지 않는다.

BehaviorSubject가장 최근 값 하나를 줍니다.

초기값이 있어 언제 구독해도 항상 값이 있다.

ReplaySubject정해둔 개수만큼 되감아 줍니다.

생성할 때 인자로 설정한 수만큼 최근 전달받은 아이템을 갖고 있다가 다음 구독할 때 해당 수만큼 이벤트를 전달한다.

AsyncSubject끝날 때 마지막 하나만 줍니다.

서브젝트 구독 완료 후 가장 마지막에 있는 아이템을 전달한다.

"아무것도 / 하나 / 몇 개 / 마지막 하나". 네 가지가 같은 질문에 다르게 답합니다.

쓰이는 자리도 자연스럽게 갈립니다. 지금 상태를 보여주는 화면이면 BehaviorSubject, 최근 알림 몇 개를 보여줘야 하면 ReplaySubject, 오래 걸리는 계산의 결과만 필요하면 AsyncSubject요.

⚠️ 개수를 안 정하면

ReplaySubject에 붙은 경고가 짧지만 중요합니다.

개수를 지정하지 않으면 메모리와 관련한 성능 문제가 발생한다.

되감아 주려면 값을 어딘가 들고 있어야 하는데, 개수를 안 정하면 온 값을 전부 쌓습니다. 오래 도는 스트림이면 메모리가 계속 늘어나요.

unsubscribe에 대한 설명도 정확합니다.

unsubscribe 함수를 호출하면 아무 일도 일어나지 않은 것 같지만 이후 모든 옵저버블 대상으로 멀티캐스팅할 수 없다. (…) 등록된 옵저버가 있는 배열을 null로 만들며, 더 사용할 수 없는 서브젝트로 취급해 closed 플래그를 true로 인식해 에러가 발생하는 것이다.

"아무 일도 일어나지 않은 것 같지만"이 좋습니다. 겉으로는 조용한데 내부 상태가 바뀌어 있다는 것요.

그리고 여기서도 내부 구현을 열어봤습니다. 5회차에서 소개해드린 defaultIfEmpty 정리에서도 구현 코드를 가져왔는데, 같은 방식으로 확인하고 있어요.

📝 원문에서는 이렇게 확인합니다

이 정리의 형식이 일정합니다. 코드 → [코드 11-N] 번호 → 실행 결과 → 설명이요.

특히 errorcomplete를 확인하는 방법이 꼼꼼합니다.

subject.error(new Error('error!'));
subject.next(4);
subject.complete();

에러를 낸 다음에 일부러 nextcomplete를 부릅니다. 실행 결과에는 에러만 찍히고 나머지는 안 나오죠.

complete 쪽도 같은 방식이에요. 완료시킨 다음 nexterror를 부르고, 완료만 찍히는 걸 확인합니다.

"이 뒤에는 아무것도 안 통한다"를 코드로 증명한 셈입니다. 문장으로 "완료 후에는 값이 전달되지 않는다"고 적는 것보다 훨씬 확실해요.

주석 한 줄도 눈에 띕니다.

// 하나씩만 주석 처리를 해제한 후 코드를 실행한다.

여러 경우를 한 파일에 넣어두고 번갈아 실행하는 방식입니다. 5회차 큐레이션에서 이분이 count를 인자로 받아 0과 3을 넣어본 이야기를 했는데, 여기서도 같은 습관이 보입니다.

큐레이터 노트

한 장을 통째로, 실행 결과까지 붙여 정리한 글이라 골랐습니다.

RxJS 서브젝트는 개념이 겹겹입니다. 콜드와 핫을 알아야 하고, 옵저버와 옵저버블을 알아야 하고, 그 둘을 겸한다는 게 무슨 뜻인지 알아야 하죠. 글로만 읽으면 어느 순간 길을 잃습니다.

이 정리가 그걸 막는 방법이 실행 결과입니다. B가 0부터 받는 로그와, 2부터 받는 로그를 나란히 놓으면 말이 필요 없어요.

3회차부터 이어진 이분의 RxJS 연재를 보면 순서가 있습니다. 값을 거르고, 흘려보내며 관찰하고, 비어 있는지 판단하고, 이제 여럿이 나눠 쓰는 단계까지 왔어요. 교재를 따라가는 것이지만 매 장마다 직접 돌려본 결과가 붙어 있습니다.

원문 읽으러 가기  ↗paekma09.tistory.com/15
 

02

🧱 CJS와 ESM

이지은 님

이런 내용이에요
Next.js 프로젝트에서 만난 모듈 관련 에러를 해결한 기록입니다.
형식이 분명해요. 에러 내용 → 해결 방법 → 개념 정리 → 참고 문서요.
에러 메시지를 통째로 붙였고, 해결까지 시도한 것들을 다 적어뒀습니다.
그리고 끝에 참조한 문서 링크를 남겼어요.

1·5회차에서도 이분의 기록을 소개해드렸습니다. 이번엔 막힌 자리를 파고든 글이에요.

조금 더 들어가 보면

🔍 파일은 있는데 못 찾는다

에러 메시지의 핵심이 이 부분입니다.

Cannot find module '.../node_modules/rc-util/es/hooks/useMemo' imported from '.../node_modules/rc-util/es/ref.js'

그리고 글이 확인한 것을 적었어요.

코드 문제는 아니고 어딘가 설정에 미흡함이 있는것이 분명한데, ref.jshooks 내의 useMemo 함수도 분명히 잘 있어서, 이런저런 설정을 바꾸어도 에러가 계속 발생했다.

"분명히 잘 있어서"가 이 에러의 성격을 보여줍니다.

"모듈을 찾을 수 없다"는 메시지는 보통 오타이거나 설치가 안 된 경우예요. 그런데 파일이 실제로 있으면 메시지를 그대로 믿으면 안 되는 상황이 됩니다. 실제로는 "찾을 수 없다"가 아니라 "찾긴 했는데 읽을 수 없다"에 가깝거든요.

증상도 이상합니다.

최초로 antd를 불러서 적용하고 라이브서버를 열면 반영이 잘 되었다. 그러나 페이지를 새로고침 하거나, antd 관련된 코드를 반영한 상태로 열면 저런 오류가 나타났다.

될 때가 있고 안 될 때가 있습니다. 첫 로딩은 브라우저에서 처리되고 새로고침은 서버에서 처리되는데, 둘의 모듈 해석 방식이 다르기 때문에 생기는 차이예요.

⚙️ 원인은 두 표준이 만나는 자리

해결 방법과 함께 원인이 정리되어 있습니다.

Next.js가 CommonJS 방식으로 구동을 하고 ESM 형식의 파일에 대해 트랜스파일링을 하는데, node_modules 내의 파일에 대해서는 이러한 번역 작업을 적용하지 않도록 구성되어 있으므로 이에 예외를 적용해 주기 위해 사용하는 모듈이라 한다.

세 조각이 맞물린 문제입니다.

첫째, Next는 서버에서 CommonJS로 돕니다. 둘째, ESM으로 된 코드는 번역해서 씁니다. 셋째, 그 번역을 node_modules 안에는 적용하지 않습니다.

셋째가 왜 그럴까요. 설치된 패키지는 보통 이미 변환이 끝난 상태로 배포되거든요. 매번 다시 번역하면 빌드가 한없이 느려집니다. 그래서 기본적으로 건너뜁니다.

그런데 어떤 패키지는 ESM 그대로 배포합니다. 그러면 건너뛴 게 문제가 되죠. 그래서 "이 패키지만은 번역해라"라고 예외를 적어주는 설정이 필요합니다.

next-transpile-modules에서 withTMnext.config.mjs에 불러와서 antd 사용에 관련된 모듈들에 적용해줌으로써 해결할수 있었다.

막연한 설정 문제가 아니라 "예외 목록에 추가하는 일"이었던 셈입니다.

📚 두 모듈 시스템을 갈라두기

문제를 풀고 나서 개념을 정리했습니다. 짧은데 요점이 다 들어 있어요.

ESModule(ESM)과 CommonJS(CJS) — code 공유, 재사용을 위한 모듈
ESM은 브라우저 표준, CJS는 Node의 표준
ESM에서의 CJS 이용은 간편하나 (기존 모듈 대부분 CJS) 반대의 경우 불가능

첫 줄이 좋습니다. 둘 다 결국 "코드를 나눠 쓰기 위한" 장치라는 것요.

그리고 역사가 짧게 붙습니다.

브라우저가 모듈 시스템이 없었음 (Node.js만 CommonJS로 모듈 형태 구현)
ES Module은 브라우저에서 모듈 작업을 수행 (ES6 by ECMAScript standardization)

Node가 먼저 만들었고, 나중에 표준이 생겼습니다. 오늘날의 혼란이 여기서 나와요. 먼저 있던 방식과 나중에 온 표준이 같은 자리에서 공존해야 하니까요.

세 번째 줄의 "반대의 경우 불가능"도 중요합니다. ESM에서 CJS 모듈을 부르는 건 되는데, CJS에서 ESM을 직접 부르는 건 안 돼요. ESM은 파일을 읽기 전에 무엇을 가져올지 정하는 구조인데, CJS의 require는 실행 중에 부르는 함수라 시점이 안 맞거든요.

이번에 만난 에러가 정확히 그 자리에서 났습니다. CJS로 도는 Next가 ESM 패키지를 부르려다 막힌 것요.

우회로도 세 가지로 정리했습니다. .mjs 확장자, package.jsontype: "module", 그리고 번들러요.

번들러 (Rollup 등) — 빌드 타임에 (런타임 아님) 코드의 가져오기와 내보내기 해결

"빌드 타임에"라는 괄호가 정확합니다. 실행할 때 푸는 게 아니라 미리 풀어두는 것요.

📝 원문에서는 이렇게 남깁니다

해결한 방법만 적고 끝내지 않았습니다. 안 통한 것들도 적어뒀어요.

chatGPT에게 묻고 버전도 바꾸고, 캐시 지우고, 설정을 바꾸고 의존성 다시 깔고 이런걸 30번은 한거 같다.
바벨을 깔아서 antd에 대한 옵션을 줘보거나, nextConfig에서 experimental.esmExternals로 설정하거나, next-with-lesswithLess 설정, next/dynamic을 가져와서 동적 로드 설정 등 여러가지 삽질을 다양하게 했다.

시도한 목록이 다섯 개 넘게 나열되어 있습니다.

이게 왜 쓸모 있냐면, 같은 에러를 만난 사람이 이미 해본 길을 또 가지 않아도 되기 때문입니다. 검색해서 나오는 해법 대부분이 여기 적힌 것들이거든요.

그리고 솔직한 한 줄이 붙습니다.

사실 정확한 용어의 의미도 분명히 몰라서 확인하기 어려웠다.

에러를 못 푸는 이유가 종종 이겁니다. 검색 결과에 나온 단어를 모르니 그 답이 맞는지 판단할 수가 없어요. 그래서 되는 걸 찾을 때까지 이것저것 해보게 되고요.

글을 남긴 이유도 거기서 나옵니다.

필요한 경우에 좀 더 구체적으로 살펴보기 위해 간략히 내용을 정리하고 관련 블로그와 표준 문서의 위치에 대한 흔적을 남긴다.

"흔적을 남긴다"요. 지금 다 이해하지 못했으니 나중에 돌아올 자리를 표시해두는 것입니다.

그리고 이런 문장도 있습니다.

이 부분은 추가적인 배움이 있은 후에 미래의 내가 코멘트 할 예정이다^^

모르는 걸 모른다고 적고, 언제 채울지도 적어뒀습니다.

큐레이터 노트

해결 과정을 통째로 남긴 게 이 글을 고른 이유입니다.

에러 해결 글은 보통 "이렇게 하면 됩니다"로 끝납니다. 빠르게 답을 찾기엔 좋은데, 왜 그게 답인지는 안 남아요.

이 글은 순서가 다릅니다. 에러 메시지 원문, 이상한 증상, 안 통한 시도들, 통한 방법, 그리고 왜 그런지의 개념 정리까지요. 다음에 비슷한 에러를 만나면 개념 정리 쪽이 먼저 쓰입니다.

특히 모르는 걸 모른다고 적은 부분. "정확한 용어의 의미도 분명히 몰라서"라거나 "미래의 내가 코멘트 할 예정"이라거나요. 이런 문장이 있으면 나중에 다시 읽을 때 어디까지 알고 있었는지가 남습니다.

앞의 남기준 님 글과 나란히 놓으면 대비가 재미있어요. 한쪽은 교재를 순서대로 밟은 정리이고, 한쪽은 막힌 자리에서 파고든 기록입니다. 둘 다 결국 "코드를 나눠 쓰는 구조"를 다루고 있고요.

원문 읽으러 가기  ↗vapiano.tistory.com/13
 

03

🛠️ [Vue.js] 개발환경 설정 가이드

한지수 님

한지수 님 원문에 실린 이미지
이런 내용이에요
Vue 프로젝트를 처음부터 세팅하는 가이드 문서입니다.
필수 도구, 프로젝트 생성 세 가지 옵션, 프로젝트 구조 설명, 추가 도구 추천, 참고 문서 순으로 정리했어요.
명령어에 링크까지 다 붙어 있어서 위에서부터 따라 하면 됩니다.
그런데 마지막 한 줄이 반전이에요. "Vue 공부하려고 기록. 아래 가이드 보면서 따라가는 중"

6회차에서 이분의 MyBatis 비교 글을 소개해드렸습니다. 두 방식을 표로 갈라놓던 그 글이요.

조금 더 들어가 보면

🧾 왜 필요한지를 함께 적기

첫 항목이 Node.js인데, 설치하라고만 하지 않습니다.

Node.js — Vue 프로젝트를 생성하고 실행하기 위해 필요. Node.js 설치 시 기본적으로 포함되는 npm(Node Package Manager)을 사용하기 위함.
설치 버전 : LTS 버전 다운로드 (안정성과 장기 지원)

"npm을 쓰기 위해서"라는 이유가 붙어 있습니다.

Vue는 자바스크립트 프레임워크인데 왜 Node를 깔아야 하는지, 처음 하는 사람은 헷갈리거든요. 브라우저에서 도는 코드인데 왜 서버용 런타임이 필요한지요. 이유를 한 줄 적어두면 그 의문이 안 생깁니다.

"LTS 버전"에 "(안정성과 장기 지원)"을 괄호로 붙인 것도 같습니다. 최신 버전이 더 좋아 보이는데 왜 LTS인지를 미리 답해뒀어요.

플러그인도 필수와 선택을 갈랐습니다. Volar는 필수, Prettier와 ESLint는 (선택)이라고요. 처음 세팅하는 사람이 어디까지 꼭 해야 하는지 알 수 있습니다.

🔢 세 가지 길을 열어두기

프로젝트 생성을 세 가지로 나눴는데, 순서에 뜻이 있습니다.

옵션 1 (기본) : 최신 템플릿을 사용한 Vue 프로젝트 생성 — npm create vue@latest
옵션 2 : Vue CLI를 사용한 프로젝트 생성
옵션 3 : 기존 프로젝트에 수동으로 Vue.js를 추가할 때

첫 번째에 "(기본)"이라고 표시했습니다. 셋을 나열만 하면 뭘 골라야 할지 모르는데, 기본값을 정해주면 고민이 사라져요.

그리고 각각의 성격을 한 줄씩 적었습니다. 옵션 1은 "Vue CLI를 따로 설치하지 않고", 옵션 2는 "컴퓨터 환경 전역에 Vue CLI를 설치함으로써, 더 많은 설정 옵션을 추가하여"요.

-g 옵션의 의미를 "컴퓨터 환경 전역에"로 풀어 적은 게 친절합니다. 명령어만 적어두면 그게 프로젝트 폴더에 설치되는 건지 컴퓨터 전체에 설치되는 건지 모르거든요.

실행 명령이 다르다는 것도 짚었습니다. 옵션 1은 npm run dev, 옵션 2는 npm run serve요. 템플릿마다 스크립트 이름이 달라서 실제로 자주 틀리는 자리입니다.

🙅 모르겠으면 전부 No

이 가이드에서 가장 좋은 한 줄이 이겁니다.

설정 마법사 진행 : 프로젝트 이름 입력. TypeScript, Vue Router, Pinia 등 추가 도구 선택.
모르겠으면 전부 No! 나중에 설정 변경 가능.

처음 세팅하는 사람이 가장 막히는 자리를 정확히 짚었습니다.

npm create vue@latest를 치면 예/아니오 질문이 열 개 가까이 쏟아집니다. TypeScript를 쓸 거냐, JSX를 쓸 거냐, Router를 넣을 거냐, Pinia를 넣을 거냐요. 처음이라면 이 중 절반은 뭔지도 모릅니다.

거기서 멈추게 됩니다. 잘못 고르면 처음부터 다시 해야 할 것 같으니까요.

그래서 "전부 No"라는 기본값과 "나중에 변경 가능"이라는 안심을 함께 준 겁니다. 두 문장으로 그 자리를 넘기게 만들었어요.

가이드 문서에서 이런 문장이 드뭅니다. 정확한 정보를 적는 데 집중하다 보면 읽는 사람이 어디서 멈추는지는 놓치기 쉽거든요.

🗺️ 구조를 먼저 보여주기

프로젝트를 만든 다음에 폴더 구조 설명이 이어집니다.

App.vue — Vue 앱의 최상위 컴포넌트
main.js — Vue 앱을 초기화하고 DOM에 마운트하는 초기 시작점
src/components/재사용 가능한 컴포넌트 파일을 저장하는 폴더
src/views/Vue Router와 연결된 페이지 단위 컴포넌트를 저장하는 폴더

componentsviews의 차이를 밝혀둔 게 실용적입니다.

생성기가 두 폴더를 만들어주는데, 뭐가 다른지는 안 알려주거든요. 둘 다 .vue 파일이 들어가니 처음엔 구분이 안 됩니다. "재사용"과 "페이지 단위"라는 기준이 있으면 새 파일을 어디에 둘지 정할 수 있어요.

마지막에 추가 도구 두 가지를 추천했습니다.

Vue Router — 페이지 간 라우팅(경로)을 관리하는 공식 라이브러리
Axios — 서버와 데이터를 주고받을 때 주로 사용

설치 마법사에서 "전부 No"라고 해놓고, 나중에 필요할 것들을 여기 적어뒀습니다. 앞의 조언과 짝이 맞아요.

📝 원문에서는 이렇게 끝납니다

마지막 줄이 이 글의 성격을 완전히 바꿉니다.

🫠
Vue 공부하려고 기록. 아래 가이드 보면서 따라가는 중

아직 끝내지 않은 상태에서 쓴 문서입니다.

위쪽을 다시 보면 그 흔적이 보여요. 옵션 3에는 이렇게 적혀 있습니다.

필요하면 부연 설명 추가하겠음.

아직 안 써본 부분은 안 쓴 겁니다. 대신 자리는 만들어뒀어요.

가이드를 다 완성하고 올리는 게 아니라 따라가면서 동시에 적는 방식입니다. 그래서 막히는 자리가 그대로 반영돼요. "모르겠으면 전부 No"가 나올 수 있었던 것도 자기가 방금 그 자리에서 헤맸기 때문입니다.

다 배운 사람이 쓰는 가이드에는 이런 문장이 안 나옵니다. 어디서 멈추는지를 이미 잊었으니까요.

큐레이터 노트

배우면서 쓴 가이드라는 점에서 고른 글입니다.

이런 문서는 값어치가 두 겹입니다. 하나는 내용 자체고, 하나는 초심자의 시선이 살아 있다는 것이에요.

6회차에서 소개해드린 이분의 MyBatis 글도 그랬습니다. 두 방식을 비교하면서 SqlSession이 뭔지부터 설명하고 들어갔거든요. 이번 글에서도 Node.js를 왜 깔아야 하는지, -g가 무슨 뜻인지를 풀어 적었습니다.

설명을 건너뛰지 않는 습관이 두 글에 다 보입니다.

그리고 "필요하면 부연 설명 추가하겠음"이라고 적어둔 것. 미완성인 채로 올린다는 뜻인데, 그래야 올릴 수 있는 글도 있습니다. 다 끝난 다음에 쓰려면 영영 못 쓰기도 하니까요.

원문 읽으러 가기  ↗velog.io/@haanjisu/Vue.js-%EA%B0%9C%EB%B0%9C%ED%99%98%EA%B…

💡 세 편을 겹쳐 읽으며

세 글의 소재가 다 다릅니다. 스트림, 모듈 시스템, 개발 환경 세팅이요.

그런데 셋 다 여럿이 같은 것을 나눠 쓰는 문제를 다룹니다. 하나의 흐름을 여러 구독자가, 하나의 코드를 여러 파일이, 하나의 프로젝트를 여러 사람이요.

나눠 쓰려면 가운데에 무언가를 둔다

세 글의 해법이 같은 모양입니다. 중간에 규약이나 장치를 하나 세우는 것.

남기준 님의 서브젝트는 가운데 선 다리입니다. 원본은 서브젝트만 알면 되고, 구독자들도 서브젝트만 알면 돼요. 서로를 직접 알 필요가 없습니다.

모듈 시스템도 마찬가지입니다. importexport라는 약속을 가운데 두고 파일들이 서로를 부릅니다. 상대가 어떻게 생겼는지 몰라도 되죠.

한지수 님의 가이드 문서도 그렇습니다. 사람들이 서로 물어보는 대신 문서 하나를 보고 같은 환경을 만듭니다.

중간에 무언가를 세우면 연결선이 줄어듭니다. 셋이 서로를 알아야 하면 선이 세 개인데, 가운데를 두면 세 개면 되니까요. 열 명이면 차이가 훨씬 커집니다.

늦게 온 사람에게 무엇을 줄까

그런데 중간에 무언가를 세우면 새로운 질문이 생깁니다. 나중에 합류한 사람에게 무엇을 보여줄 것인가요.

남기준 님의 글은 이 질문에 네 가지 답을 내놓습니다. 아무것도 안 준다, 최근 하나를 준다, 몇 개를 되감아 준다, 끝날 때 마지막 하나를 준다요.

이지은 님의 CJS와 ESM도 실은 같은 문제입니다. 먼저 있던 방식과 나중에 온 표준이 한자리에 있게 됐거든요. 이번 에러가 그 경계에서 났고요.

한지수 님의 가이드는 처음 오는 사람을 위한 문서입니다. 이미 세팅을 끝낸 사람에게는 필요 없는 글이죠. 늦게 오는 사람에게 지금까지의 흐름을 압축해서 건네주는 셈입니다.

셋 다 "지금 상태를 어떻게 전달할 것인가"를 다룹니다. 층위가 스트림, 표준, 문서로 다를 뿐이에요.

다 알기 전에 적어두기

기록 방식에서도 공통점이 보입니다. 모르는 채로 남긴 부분이 있다는 것.

이지은 님은 "정확한 용어의 의미도 분명히 몰라서"라고 적고, "미래의 내가 코멘트 할 예정"이라고 남겼습니다.

한지수 님은 "필요하면 부연 설명 추가하겠음"이라고 자리만 만들어뒀고, 마지막에 "따라가는 중"이라고 밝혔어요.

남기준 님도 책 한 장을 요약하는 형식이라 자기 해석보다 원문 정리에 가깝습니다. 대신 직접 돌려본 실행 결과를 붙였고요.

셋 다 "다 이해한 다음에 쓴다"를 기다리지 않았습니다. 지금 확인한 것까지만 적고, 나머지는 자리를 표시해뒀어요.

기록이 쌓이지 않는 흔한 이유가 이겁니다. 완성될 때까지 기다리다가 못 쓰는 것요.

이번 회차에서 가져갈 것

1. 구독자가 늘면 요청도 느는지 확인하기.

콜드 옵저버블은 구독할 때마다 흐름이 새로 만들어집니다. 같은 데이터를 여럿이 봐야 하면 가운데에 서브젝트를 하나 두세요.

2. ReplaySubject에는 개수를 꼭 지정하기.

안 정하면 흘러간 값을 전부 쌓습니다. 오래 도는 스트림에서 메모리가 계속 늘어납니다.

3. "모듈을 찾을 수 없다"인데 파일이 있으면 모듈 형식을 의심하기.

못 찾는 게 아니라 CJS와 ESM이 어긋난 것일 수 있습니다. Next라면 해당 패키지를 트랜스파일 대상에 추가해보세요.

4. 안 통한 시도도 함께 적어두기.

검색으로 나오는 해법 대부분을 이미 해봤다면, 그 목록 자체가 다음 사람에게 지름길이 됩니다.

5. 가이드에는 기본값과 "나중에 바꿀 수 있다"를 함께 적기.

선택지를 나열만 하면 거기서 멈춥니다. "모르겠으면 전부 No" 같은 한 줄이 사람을 다음 단계로 넘깁니다.


2기 10회차는 2025년 1월 하순이었습니다. 새 학기를 앞두고 각자 다음 것을 준비하던 때예요.

RxJS 교재와 Next 프로젝트와 Vue 첫 세팅이라는 서로 다른 자리에서, 세 분이 나란히 나눠 쓰는 구조를 기록했습니다.

좋은 글 남겨주신 남기준 님, 이지은 님, 한지수 님께 감사드립니다. 그리고 2기를 함께 채워주신 모든 멤버분들께도요! 😊

다음 큐레이션으로 다시 찾아오겠습니다! 🌱

- 에디터 · 성장일지 큐레이터 -


※ 본 큐레이션은 각 저자가 공개한 글을 소개하는 것이며, 모든 원문의 저작권은 저자에게 있습니다. 저자 본인의 요청이 있을 경우 즉시 수정 또는 삭제합니다.

WRITE YOUR GROWTH

오늘의 배움을
내일의 성장으로.

기록은 나를 돌아보게 하고, 공유는 우리를 연결합니다.
당신의 다음 성장 이야기도 그로스로그에서 시작해보세요.

커뮤니티 둘러보기