GROWTH LOG STORIES

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

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

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

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

대표 이야기 읽기

ALL STORIES

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

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

성장일지

[GL 2기 11회차] 비슷해 보이는 둘을 가르기 — Next SSR·CSR 오류, JS 동등과 일치, props 두 방법 🌱

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

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

같아 보이는데 다른 것들이 있습니다. 그걸 가르는 일이 실력이 되기도 하고요. 2기 11회차 세 편입니다.

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

2기 11회차, 2025년 2월 초입니다. 이번 회차에는 구분하는 일을 다룬 세 편이 모였어요. 두 종류의 오류를 가르고, 두 연산자를 가르고, 두 가지 작성법을 가릅니다.

 

01

🧯 next의 ssr, csr로 인한 오류제어

이지은 님

이런 내용이에요
Next 프로젝트에 무한 스크롤 라이브러리를 넣다가 두 종류의 오류를 동시에 만난 기록입니다.
첫 줄이 솔직해요. "지속적인 오류 파티이다."
그런데 이 글의 핵심은 해결책이 아니라 오류를 갈라낸 판단입니다. 타입 오류와 렌더링 오류가 겹쳐 보이는데 원인이 다르다는 걸 짚고, 각각을 따로 풉니다.
그리고 해법을 세 가지 수준으로 정리했어요.

10회차에서 이분의 CJS·ESM 정리를 소개해드렸습니다. 그때 남긴 참고 문서와 개념 정리가 이 글에서 실제로 쓰입니다.

조금 더 들어가 보면

🚧 두 오류가 겹쳐 있다

문제 상황이 이렇습니다. 무한 스크롤 라이브러리를 설치하고 설명대로 컴포넌트를 끼워 넣었는데, 화면도 안 뜨고 편집기에도 빨간 줄이 뜹니다.

편집기 쪽 오류는 이거예요.

'InfiniteScroll' cannot be used as a JSX component.
Property 'refs' is missing in type 'InfiniteScroll' but required in type 'ElementClass'.

그리고 글이 이걸 사람 말로 옮겨놨습니다.

Typescript는 형식을 엄청 따지는 고지식한 친구인지라 .. 타입체크를 위한 도구인 ElementClass 타입 가지고 체크해 보니깐 꼭 필요한 'ref'가 없는데? 아, 넌.. 이상한 코드구나. 입장불가~ 이러면서 되돌려보냈다는 것이다.

에러 메시지를 자기 언어로 다시 쓴 것인데, 이게 이해했다는 증거이기도 합니다.

그런데 화면도 안 뜹니다. 여기서 판단이 나와요.

콘솔에 나타나는 렌더링 오류는 더욱 길고 복잡하지만 typescript가 내보내는 오류와는 상관이 없어보였다.

"상관이 없어 보였다"가 이 글에서 가장 중요한 한 줄입니다.

오류가 두 개 떠 있으면 보통 하나로 묶어 생각하게 됩니다. 하나를 고치면 둘 다 사라질 거라고요. 그런데 여기서는 둘을 갈랐어요.

근거도 있습니다. 그 라이브러리의 코드가 Component를 상속한 클래스형 컴포넌트라서, 타입이 안 맞아도 화면은 떠야 정상이거든요. 타입 검사는 편집기와 빌드 단계의 일이고, 실행은 별개니까요.

그러니까 화면이 안 뜨는 건 다른 이유입니다. 그 판단에서 다음 단계가 나옵니다.

🖥️ 서버에는 window가 없다

렌더링 쪽 원인을 이렇게 짚었습니다.

Next는 SSR 중에는 브라우저 관련 객체(window, document)를 사용할 수 없다. 따라서, 서버에서 컴포넌트를 렌더링할 때 react-infinite-scroller처럼 props에서 window 등의 브라우저 API를 사용하면 오류가 발생할 수 있다.

이게 SSR의 근본 제약입니다.

Next는 페이지를 서버에서 미리 그려서 보냅니다. 그런데 서버에는 브라우저가 없어요. windowdocument도 없습니다. 스크롤 위치를 재는 라이브러리는 당연히 window를 봐야 하는데, 그 자리에 아무것도 없으니 터지는 거죠.

해법은 그 컴포넌트만 서버에서 빼는 것입니다.

import dynamic from "next/dynamic";
const InfiniteScroll = dynamic(() => import("react-infinite-scroller"), {
  ssr: false,
});

ssr: false"이건 브라우저에서만 그려라"라는 표시입니다.

그리고 결과를 확인했어요.

적용해 보니 화면 렌더링은 정상적으로 표현되었다. 다만, 함수상에 Typescript에서 기인하는 타입 오류들이 나타난다.

한쪽만 풀렸습니다. 갈라놓은 판단이 맞았다는 게 여기서 증명됩니다. 렌더링 문제를 고쳤는데 타입 오류는 그대로 남았으니, 애초에 별개였던 거예요.

🙅 제안받은 것 중 안 한 것들

타입 오류 쪽에서 받은 제안이 세 개인데, 두 개를 거절했습니다.

1) Typescript를 무시하고 진행해봐 (ts-ignore에 넣으면 될거라고 한다. 읏.. 이건 아니니깐 안해봤다)
2) 해당 모듈을 typescript를 피해서 require로 가져와봐 (CJS와 ESM의 충돌사고를 피해보란 얘기같다, Next에서는 시도해보고 싶지않다.)
3) type을 React.Component로 캐스트

거절한 이유가 붙어 있는 게 좋습니다.

ts-ignore는 오류를 없애는 게 아니라 가리는 것입니다. 빨간 줄은 사라지는데 문제는 남아 있죠. 타입스크립트를 쓰는 이유 자체가 없어지는 선택이고요.

두 번째 거절이 더 눈에 띕니다. require로 가져오면 타입 검사를 피할 수 있는데, 그건 CJS 방식으로 돌아가는 것이거든요. 10회차에서 정리했던 그 문제로 되돌아가는 셈입니다.

전에 정리해둔 개념이 이번 판단의 근거가 됐어요. 왜 그 방법을 피해야 하는지 알고 있으니 거절할 수 있었던 겁니다.

📶 세 단계로 올라가는 해법

최종 해법을 세 가지로 정리했는데, 뒤로 갈수록 제대로 됩니다.

첫 번째는 dynamic과 타입 캐스트를 함께 쓰는 것입니다.

dynamic을 통해 렌더링의 오류가 사라진다. (…) 다만, ComponentTypeany로 두어 타입스크립트의 타입 제어가 안 되는 좀 아쉬운 상황이다.

"좀 아쉬운"이라고 적었습니다. 오류는 사라졌지만 any를 썼으니 타입 검사를 그 자리에서만 꺼둔 셈이거든요. 동작은 하는데 만족스럽지 않다는 걸 알고 있습니다.

두 번째는 필요한 props의 타입을 직접 적는 것입니다.

interface InfiniteScrollProps {
  pageStart?: number;
  loadMore: (page: number) => void;
  hasMore: boolean;
  loader?: React.ReactNode | null;
  useWindow?: boolean;
  children?: React.ReactNode;
}

any 대신 실제 모양을 적었습니다. 이제 hasMore를 빠뜨리면 편집기가 잡아줘요.

그리고 여기서 관찰 하나가 나옵니다.

InfiniteScrollProps를 보면 useWindow라는 부분이 있다. 아마도, 저 부분 때문에 CSR로 구동되어야 하는 것 같다.

타입을 적다가 원인의 근거를 찾았습니다. props 목록에 useWindow가 있다는 건 이 라이브러리가 window를 쓴다는 뜻이니까요. 앞에서 세운 가설이 여기서 뒷받침됩니다.

세 번째는 모듈 자체를 선언하는 방식입니다.

declare module "react-infinite-scroller" {
  interface InfiniteScrollProps { /* ... */ }
  export default function InfiniteScroll(props: InfiniteScrollProps): ReactElement;
}

타입 정의가 없는 라이브러리에 타입을 붙여주는 정식 방법입니다. 컴포넌트 안에서 캐스트하는 게 아니라, 그 패키지가 원래 이런 모양이라고 선언하는 거예요.

그리고 이렇게 덧붙였습니다.

해당 컴퍼넌트 내부에서 처리했으나 global.d.ts에서 내보내주는 게 보다 자연스럽겠다.

더 나은 자리까지 알아본 셈입니다. 파일 하나에 두면 그 파일에서만 통하는데, 전역 선언 파일에 두면 프로젝트 어디서나 통하니까요.

📝 원문에서는 이렇게 씁니다

이 글의 구성이 "회피 → 우회 → 해결" 순서로 되어 있습니다. 소제목이 그렇게 붙어 있어요.

TypeScript 오류의 회피나 우회
TypeScript 오류와 렌더링 오류를 동시에 사라지게~

임시방편과 진짜 해법을 이름으로 구분했습니다.

에러 해결 글에서 이 구분이 드뭅니다. 되는 걸 찾으면 거기서 멈추게 되니까요. 그런데 "이건 회피", "이건 우회", "이건 해결"로 나눠두면 나중에 다시 볼 때 어느 걸 써야 할지 알 수 있습니다.

그리고 각 단계마다 아쉬운 점을 적었어요. any를 썼다는 것, 컴포넌트 안에서 선언했다는 것요.

큐레이터 노트

오류를 둘로 가른 판단이 이 글의 핵심입니다.

에러가 여러 개 뜨면 보통 위에서부터 하나씩 검색합니다. 그런데 이 경우엔 검색으로 안 풀려요. 타입 오류를 고쳐도 화면이 안 뜨고, 화면을 띄워도 타입 오류가 남으니까요. 둘이 별개라는 걸 먼저 알아야 각각을 풀 수 있습니다.

그 판단의 근거가 좋습니다. "클래스형 컴포넌트니까 타입이 안 맞아도 화면은 떠야 한다"요. 타입 검사와 실행이 다른 단계라는 걸 알고 있어야 나오는 추론입니다.

10회차 큐레이션에서 이분의 글을 두고 "모르는 걸 모른다고 적었다"고 했는데, 이번 글에서는 그때 정리해둔 게 판단 근거로 쓰입니다. require로 우회하자는 제안을 거절할 수 있었던 게 그 덕이고요.

기록이 다음 기록에서 쓰이는 모습이 두 편에 걸쳐 보입니다.

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

02

🧾 JavaScript #1

임준혁 님

임준혁 님 원문에 실린 이미지
이런 내용이에요
자바스크립트 기초를 열네 개 항목으로 정리한 노트입니다.
script 태그와 이벤트에서 시작해 콘솔, 자료형, 연산, 문자열, 변수, 주석, 세미콜론, 연산자까지 갑니다.
후반부가 특히 촘촘해요. 동등 연산자와 일치 연산자, nullundefined, NaN까지 실행 결과를 붙여 확인합니다.
각 항목이 번호 + 소제목 + 코드 + 출력의 같은 틀로 되어 있습니다.

1·2·9회차에서도 이분의 기록을 소개해드렸습니다. Vue 실습을 이어가다가 자바스크립트 기초로 돌아온 편이에요.

조금 더 들어가 보면

🧮 변수는 왜 필요한가

기초 노트에서 흔히 지나치는 항목이 "변수"인데, 이 글은 필요성을 따로 한 절로 잡았습니다.

같은 계산을 두 가지 방법으로 써봤어요. 변수 없이 쓰면 이렇습니다.

console.log(100+10);
console.log((100+10)/10);
console.log((100+10)/10-10);
console.log(((100+10)/10-10)*10);

변수를 쓰면 이렇게 되고요.

let a = 100;
a = a + 10;  console.log(a);
a = a / 10;  console.log(a);
a = a - 10;  console.log(a);
a = a * 10;  console.log(a);

그리고 차이를 짚었습니다.

계산해야 할 값을 100이 아닌 다른 값으로 변경 시 변수 사용 코드는 한 줄만 변경 가능하나 변수 미사용 코드는 변경해야 할 부분이 다량 발생

"한 줄만"과 "다량"이 대비됩니다.

위쪽 코드에서 100을 200으로 바꾸려면 네 줄을 다 고쳐야 합니다. 그리고 하나라도 빠뜨리면 계산이 어긋나는데, 에러가 안 나니 알아채기도 어렵죠.

글이 붙인 인용구가 그 지점을 정확히 짚었습니다.

코드가 길어지거나 변경해야 할 부분이 모여있지 않고 흩어져 있을 경우 변경점 파악 난이도가 상승 결과적으로 유지보수가 난해

"흩어져 있을 경우"요. 8회차에서 소개해드린 권준형 님의 for문 이야기와 같은 구조입니다. 그쪽은 print를 천 번 쓰는 문제였고, 여기는 100을 네 군데 쓰는 문제인데 해법이 같아요. 변하는 것에 이름을 붙이는 것.

⚖️ 같은가, 정확히 같은가

후반부의 중심이 동등 연산자와 일치 연산자입니다.

console.log(1=='1');    // true
console.log(1==="1");   // false

차이를 이렇게 갈랐습니다.

동등 연산자 — 좌우항 값의 동등여부만 판단
일치 연산자 — 값만이 아니라 자료형의 동등여부까지 함께 판단

==는 비교하기 전에 한쪽을 다른 쪽 형태로 바꿉니다. 문자열 '1'을 숫자 1로 만들어놓고 비교하니 참이 되죠.

이게 편할 것 같지만 실제로는 헷갈리는 결과를 만듭니다. 글이 예를 더 들었어요.

console.log(true === 1);  // false
console.log(true == 1);   // true
동등 연산자는 1을 true로, 1이 아닌 수를 false 취급

숫자와 참·거짓까지 섞입니다. 그래서 결론이 붙었어요.

프로그램 동작 시 자료형도 구분하는 만큼 동등 연산자보다는 일치 연산자 사용을 권장

이 권장은 실무에서도 그대로 통합니다. 대부분의 자바스크립트 코딩 규칙이 ===를 기본으로 두고 있어요.

🕳️ null과 undefined

가장 헷갈리는 한 쌍인데, 정의가 명확합니다.

null : 값이 미존재 (프로그래머가 의도한 상황)
undefined : 값이 미정의 (프로그래머가 의도하지 않은 상황)

"의도했는가"로 갈랐습니다.

이게 좋은 기준입니다. 둘 다 "값이 없다"인데, null내가 일부러 비워둔 것이고 undefined아직 아무도 채우지 않은 것이거든요.

그래서 실무에서 쓰임이 갈립니다. 서버에서 "이 필드는 값이 없음"을 알려줄 때는 null을 보냅니다. 반면 객체에 없는 속성을 꺼내면 undefined가 나오죠. 전자는 답이고 후자는 사고에 가깝습니다.

그리고 비교 결과가 붙습니다.

console.log(undefined === null);  // false
console.log(undefined == null);   // true

==로는 같고 ===로는 다릅니다. 두 값이 "없음"이라는 점에서는 통하지만 자료형은 다르니까요. 앞에서 정리한 동등·일치 이야기가 여기서 바로 쓰입니다.

9회차에서 소개해드린 이분의 Vue 실습 기록에도 같은 구분이 있었습니다.

null의 경우 데이터는 존재하지만 특정하지 않은 상태를 의미하며, undefined데이터 자체가 존재하지 않는 상태이다.

두 편에 같은 개념이 나오는데 표현이 다릅니다. Vue를 하다 만난 것과, 자바스크립트 기초를 정리하며 만난 것요. 한 번 정리하고 지나간 게 아니라 다른 각도에서 다시 만난 셈입니다.

🚫 자기 자신과도 다른 값

마지막에 짧지만 재미있는 항목이 있습니다.

console.log(NaN === NaN);  // false
NaN계산 불가라는 의미
NaN은 같아도 거짓으로 판별

자기 자신과 비교해도 거짓입니다.

왜 이렇게 정해졌을까요. NaN은 "계산이 안 되는 값"인데, 계산이 안 된 이유는 서로 다를 수 있기 때문입니다. 0을 0으로 나눈 결과와 문자열을 숫자로 바꾸려다 실패한 결과가 둘 다 NaN인데, 이 둘을 같다고 하기는 어렵죠.

실용적으로는 이 성질이 검사에 쓰입니다. x !== x가 참이면 xNaN이거든요. 자기 자신과 다른 유일한 값이니까요.

📝 원문에서는 이렇게 정리합니다

이 노트의 형식이 일정합니다. 번호 → 소제목 → 코드 → 화살표 출력이요.

Math.round(7.27);
-> 7

출력을 화살표로 붙인 게 읽기 편합니다. 콘솔을 열지 않아도 결과를 알 수 있으니까요.

그리고 중요한 규칙은 인용구로 뺐습니다. 타입 확인 연산자, true/false의 자료형, 일치 연산자 권장, 유지보수 이야기 같은 것들요.

9회차에서 소개해드린 이분의 Vue 실습 기록도 같은 방식이었습니다. 본문에는 한 일을, 인용구에는 알게 된 것을 적는 것요.

주석에 대한 설명도 짧지만 정확합니다.

미래의 자신이나 타인과의 코드 공유 시 도움말 작성
특정 코드 부분의 동작을 일시적으로 방지

두 번째 용도까지 적어둔 게 실용적입니다. 주석은 설명을 다는 것만이 아니라 코드를 잠깐 꺼두는 도구이기도 하니까요.

큐레이터 노트

Vue를 하다가 기초로 돌아온 순서가 이 글을 고른 이유입니다.

1·2·9회차에서 소개해드린 이분의 기록은 웹페이지를 만드는 실습이었습니다. Vue 프로젝트를 세우고 공식 문서를 따라가는 것요. 그러다 이번에 자바스크립트 기초 노트를 썼어요.

이런 순서는 실제로 자주 일어납니다. 프레임워크를 먼저 만지다가 밑에 깔린 언어를 다시 봐야 할 때가 오거든요. 9회차에서 nullundefined를 구분했던 게 이번 노트에서 항목으로 자리 잡은 것도 그 흐름 같습니다.

그리고 열네 개 항목 중 후반부가 훨씬 촘촘합니다. Math 명령어들은 한 줄씩인데, 동등·일치 연산자는 여러 예제를 붙였어요. 뭐가 어려운지 알고 있다는 뜻입니다.

원문 읽으러 가기  ↗velog.io/@diverstar/JavaScript
 

03

🎛️ props 추천 사용방법

이주희 님

이런 내용이에요
리액트에서 props를 받는 두 가지 방법을 나란히 놓은 짧은 메모입니다.
첫 줄에 결론이 있어요. "props 사용 방법 1, 2 중에서 1을 사용하는 것이 좋음"
그리고 각 방법마다 실제 프로젝트 코드를 통째로 붙였습니다.

3·4·7·8·9회차에서도 이분의 리액트 기록을 소개해드렸습니다. 이번에도 두 벌을 나란히 두는 방식이에요.

조금 더 들어가 보면

✌️ 두 줄의 차이

두 방법의 차이가 딱 한 줄입니다.

// 방법 1
function RechargeCashModal(props) {
  const { open, close } = props;
}

// 방법 2
function RechargeCashModal({ open, close }) {
}

방법 1은 props를 통째로 받고 안에서 풀고, 방법 2는 받는 자리에서 바로 풉니다.

둘 다 자바스크립트의 구조 분해 할당을 쓰는 건 같아요. 풀어놓는 위치만 다릅니다.

7회차에서 소개해드린 이분의 state 합치기 글에도 같은 문법이 나왔습니다.

const { name, value } = e.target;

이벤트 객체에서 두 값을 꺼내던 그 문법이 여기서는 props에 쓰이는 거예요.

🧭 왜 1인가

글은 이유를 적지 않고 결론만 남겼습니다. 그런데 두 코드를 나란히 놓고 보면 차이가 나오는 자리가 몇 군데 있어요.

먼저 props라는 이름이 남는다는 것. 방법 1에서는 함수 안에서 props를 그대로 쓸 수 있습니다. 자식 컴포넌트에 통째로 넘겨야 할 때 {...props} 한 줄이면 되고요. 방법 2에서는 그 이름이 없어서, 필요해지면 ...rest를 따로 받아야 합니다.

그리고 어디서 온 값인지 드러난다는 것. const { open, close } = props;라고 적혀 있으면 이 둘이 밖에서 들어온 값이라는 게 한 줄에 보입니다. 방법 2에서는 매개변수 자리에 있으니 마찬가지긴 한데, props가 열 개쯤 되면 함수 선언부가 길어져서 오히려 읽기 어려워지죠.

반대로 방법 2가 유리한 점도 있습니다. 짧고, 컴포넌트가 무엇을 받는지 선언부에서 바로 보입니다. 실무에서도 두 방식이 다 쓰여요.

결론은 팀이나 프로젝트마다 갈리는 문제입니다. 그래서 이런 메모의 값어치는 "어느 쪽이 정답인가"가 아니라 하나를 정해두는 데 있습니다.

📐 정해두면 생기는 것

같은 프로젝트 안에서 두 방식이 섞이면 읽는 사람이 매번 확인해야 합니다. 이 컴포넌트는 어느 쪽이지, 하고요.

하나로 정해두면 그 확인이 사라집니다. 남의 코드를 볼 때도, 몇 달 뒤 내 코드를 볼 때도요.

이게 코딩 컨벤션의 본질입니다. 어느 쪽이 객관적으로 나은지를 가리는 게 아니라, 고민하지 않아도 되게 만드는 것요. 들여쓰기를 탭으로 할지 스페이스로 할지와 같은 성격입니다.

그래서 짧은 메모여도 쓸모가 있습니다. "나는 이렇게 쓴다"를 적어두면 다음부터 안 헷갈리니까요.

📝 원문에서는 이렇게 씁니다

설명 문장이 첫 줄 하나뿐입니다. 나머지는 코드예요.

그런데 붙여둔 코드가 실제로 쓰던 프로젝트 파일입니다. 모달 컴포넌트 하나가 통째로 들어 있어요. useState도 있고, 다른 컴포넌트를 불러오는 import도 있고, 주석 처리한 줄도 그대로 남아 있습니다.

const nextBtn = () => {
  if(true){
    setNextPage(true);
    // console.log('111');
  }
}

작업 중이던 상태 그대로입니다.

정리해서 최소한의 예제만 남길 수도 있었을 텐데 그러지 않았어요. 대신 두 방법에서 달라지는 줄만 빼고는 완전히 같은 코드를 두 번 붙였습니다.

이렇게 하면 차이가 어디인지가 눈으로 보입니다. 두 코드를 위아래로 훑으면 딱 한 줄이 다르니까요. 예제로 다듬으면 오히려 그 대비가 약해집니다.

큐레이터 노트

짧아도 결론이 있는 메모라 골랐습니다.

이 글은 이번 회차 세 편 중 가장 짧습니다. 설명 한 줄과 코드 두 벌이 전부예요. 그런데 첫 줄에 "1을 사용하는 것이 좋음"이라고 정해뒀습니다.

기록을 남기다 보면 "둘 다 있다"에서 멈추기 쉽습니다. 어느 쪽이 나은지 확신이 없으니까요. 그런데 정하지 않으면 다음에 또 같은 고민을 하게 됩니다.

여기서는 정했습니다. 나중에 생각이 바뀌면 고치면 되고요.

3회차부터 이분의 기록을 계속 소개해드리고 있는데, 길이가 들쭉날쭉합니다. State와 생명주기는 코드 두 벌에 두 줄, 조건부 렌더링은 함정까지 파고든 긴 글, 이번 건 메모 한 장이요.

분량을 맞추려 하지 않는 게 이분 기록의 특징입니다. 알게 된 만큼만 적고 넘어가요. 그래서 2주에 한 번씩 계속 쌓입니다.

원문 읽으러 가기  ↗velog.io/@veryverysohappy/props-%EC%82%AC%EC%9A%A9%EB%B0%A…

💡 세 편을 겹쳐 읽으며

세 글의 크기가 많이 다릅니다. 오류를 세 단계로 푼 긴 글, 열네 항목짜리 기초 노트, 코드 두 벌짜리 메모요.

그런데 셋 다 비슷해 보이는 둘을 가르는 이야기를 합니다.

갈라야 풀린다

이지은 님은 타입 오류와 렌더링 오류를 갈랐습니다. 둘이 같이 떠 있으니 한 덩어리로 보였는데, 원인이 달랐어요. 갈라놓고 나서야 각각을 풀 수 있었습니다.

임준혁 님은 =====를 갈랐고, nullundefined를 갈랐습니다. 결과가 다르게 나오는 예제를 붙여서요.

이주희 님은 props를 받는 두 방법을 갈랐습니다. 코드 한 줄 차이인데 나란히 놓으니 보이죠.

가르는 방법도 셋 다 같습니다. 나란히 놓고 결과를 비교하는 것.

1=='1'1==="1"을 위아래로 두고 출력을 적었고, 방법 1과 방법 2를 통째로 두 번 붙였고, 렌더링을 고쳤더니 타입 오류만 남는 걸 확인했습니다.

무엇으로 가를 것인가

그런데 기준을 어디서 가져오는지가 다릅니다.

임준혁 님의 nullundefined 구분은 의도를 기준으로 삼았습니다. "프로그래머가 의도한 상황인가"요. 문법이 아니라 쓰는 사람의 마음으로 가른 겁니다.

이지은 님의 오류 구분은 단계를 기준으로 삼았습니다. 타입 검사는 편집기와 빌드 단계, 렌더링은 실행 단계요. 언제 일어나는 일인가로 갈랐어요.

이주희 님의 props 구분은 기준이 적혀 있지 않습니다. 대신 결론만 정해뒀어요. 컨벤션은 원래 그런 성격이기도 합니다.

셋 다 "결과가 어떻게 다른가"에서 시작해 "왜 다른가"로 갔습니다. 마지막 것만 "왜"를 비워뒀고요.

안 되는 방법도 남긴다

기록 방식에서도 공통점이 하나 보입니다.

이지은 님은 거절한 제안 두 개를 적었습니다. ts-ignorerequire요. 그리고 왜 안 하는지도요.

임준혁 님은 틀리기 쉬운 결과를 남겼습니다. true == 1이 참이라는 것, NaN === NaN이 거짓이라는 것요. 쓰라고 적은 게 아니라 조심하라고 적은 것입니다.

이주희 님은 안 쓰기로 한 방법 2도 코드를 통째로 붙였습니다. 결론이 방법 1인데도요.

고른 것만 적으면 왜 골랐는지가 안 남습니다. 안 고른 쪽이 함께 있어야 비교가 되죠.

이번 회차에서 가져갈 것

1. 오류가 여러 개면 같은 원인인지 먼저 확인하기.

하나를 고쳤을 때 나머지도 사라지는지 봐야 합니다. 안 사라지면 별개의 문제이고, 따로 풀어야 합니다.

2. SSR에서 브라우저 객체를 쓰는 컴포넌트는 dynamic으로 빼기.

서버에는 windowdocument도 없습니다. ssr: false그 컴포넌트만 클라이언트에서 그리게 하세요.

3. 오류를 가리는 것과 푸는 것을 구분해 적기.

ts-ignore는 빨간 줄만 없앱니다. 임시로 쓰더라도 "이건 회피"라고 적어두면 나중에 돌아올 수 있습니다.

4. 비교는 ===로 하기.

==는 자료형을 바꿔서 비교합니다. 1=='1'이 참이고 true==1도 참이에요. nullundefined==로는 같습니다.

5. 둘 중 하나를 고민했으면 결론을 적어두기.

이유를 못 적어도 괜찮습니다. 정해두는 것만으로 다음번 고민이 사라집니다.


2기 11회차는 2025년 2월 초였습니다. 학기 사이의 시기라 각자 자기 페이스로 가던 때예요.

Next 프로젝트와 자바스크립트 기초 노트와 짧은 메모라는 서로 다른 크기의 기록에서, 세 분이 나란히 가르는 일을 했습니다.

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

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

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


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

WRITE YOUR GROWTH

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

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

커뮤니티 둘러보기