
검색으로 안 나오던 답이 세 번 다 소스에 있었습니다
블로그에 댓글 기능을 붙이면서 크게 세 번 막혔습니다. 세 번 다 검색으로는 답이 안 나왔고, 세 번 다 남이 만든 코드를 직접 열어서 풀었습니다.
우연이라기엔 세 번이라 적어둡니다.
1. “숨기기”가 권한 없이 눌렸습니다
댓글을 하나 달아놓고 휴대폰으로 들어가 봤더니, 남의 댓글에 숨기기 메뉴가 있었습니다. 로그인도 안 했는데요.
검색해도 안 나왔습니다. 이건 버그 신고가 올라올 만한 종류가 아니라서요. 그래서 저장소를 받았습니다.
git clone --depth 1 --branch v1.16.4 \
https://github.com/umputun/remark42.git
전부 읽을 생각은 없었습니다. 화면에 보이는 그 단어부터 찾았습니다.
grep -rn "hideUser" app
그러면 그 기능이 실제로 무슨 일을 하는지 적힌 파일이 바로 나옵니다.
// store/user/actions.ts
hiddenUsers[user.id] = user;
setItem(LS_HIDDEN_USERS_KEY, JSON.stringify(hiddenUsers));
여기서 끝입니다. 서버로 아무 요청도 안 갑니다. 브라우저 저장소에만 적습니다.
권한 문제가 아니었습니다. 시끄러운 사람 글을 내 눈에서만 치우는 기능이라 로그인이 필요 없게 만들어둔 겁니다. 문서에는 “Hide”라고만 적혀 있고 그게 어디까지 적용되는지는 없었습니다.
내친김에 옆 파일도 봤습니다.
// components/comment/comment-actions.tsx
{!currentUser && (
<Button kind="link" onClick={onHideUser}>
{intl.formatMessage(messages.hide)}
</Button>
)}
자기 댓글에는 안 뜨고 남의 댓글에만 뜬다는 것도 여기서 확인했습니다. 세 줄짜리 조건문 하나가 화면 캡처 열 장보다 정확했습니다.
2. 쿠키가 안 실렸습니다
로컬에서 댓글을 달면 “승인되지 않음”이 떴습니다. 서버 로그에는 제 요청이 정상으로 찍혀 있었고요.
브라우저가 쿠키를 안 보내고 있었습니다. 그럼 서버가 쿠키를 어떻게 보내고 있는지 봐야 합니다. 설정에 AUTH_SAME_SITE라는 게 있고 기본값이 default인데, 이 default가 뭔지가 문서로는 분명하지 않았습니다. (당시 문서 한 곳에는 Lax와 같다고 적혀 있었는데 그건 틀린 설명이었고, 지금 문서에는 “lax와 같지 않다”고 고쳐져 있습니다.)
grep -rn -i "samesite" app/cmd/server.go
SameSite string `long:"same-site" env:"SAME_SITE"
choice:"default" choice:"none"
choice:"lax" choice:"strict"
default:"default"`
설정의 기본값은 대체로 이렇게 한 줄에 적혀 있습니다. 문서를 뒤지는 것보다 이걸 찾는 게 빠릅니다.
그런데 여기까지 봐도 default가 무슨 뜻인지는 모릅니다. 이 값은 Go 표준 라이브러리로 넘어갑니다. 그래서 한 단계 더 내려갔습니다.
grep -n -A12 "switch c.SameSite" \
/usr/local/go/src/net/http/cookie.go
case SameSiteDefaultMode:
// Skip, default mode is obtained
// by not emitting the attribute.
주석 한 줄이 답이었습니다. default는 “SameSite를 아예 안 적는다”는 뜻이고, 크롬 계열 브라우저는 안 적힌 쿠키를 Lax로 취급합니다. 그래서 사이트가 다르면 안 실립니다.
표준 라이브러리는 이런 게 많습니다. 문서에 없는 게 소스 주석에는 있습니다. 어차피 컴퓨터에 이미 깔려 있으니 받을 필요도 없습니다.
이 김에 하나 더 봤습니다. 익명 사용자를 어떻게 구분하는지 궁금해서요.
func(user string, r *http.Request) string {
return user + r.RemoteAddr
}
이름 + 접속 IP. 이 한 줄로 “PC와 휴대폰이 왜 다른 사람으로 보이는지”가 설명됩니다. 이건 아예 찾을 생각도 못 했던 답인데, 근처를 보다가 걸렸습니다.
3. 컨테이너가 뜨자마자 죽었습니다
로그가 15MB인데 같은 줄만 반복됐습니다. 로그만 봐서는 그 반복이 뭘 하다 난 건지 알 수가 없었습니다.
컨테이너는 볼 데가 정해져 있습니다. Dockerfile과 시작 스크립트입니다.

COPY docker-init.sh /srv/init.sh
CMD ["/srv/remark42", "server"]
시작 스크립트를 열었더니 이런 게 있었습니다.
if [ -d "/srv/var" ]; then
chown -R app:app /srv/var || echo "WARNING: ..."
else
echo "ERROR: /srv/var doesn't exist..."
exit 199
fi
만든 사람이 “이거 빠뜨리는 사람 많다”고 판단해서 확인을 박아둔 겁니다. 이런 건 문서가 아니라 코드에 남습니다.
그리고 제 로그를 채우던 정체도 여기 있었습니다.
find . -regex '.*\.\(html\|js\|mjs\)$' \
-print -exec sed -i "..." {} \;
-print. 파일 목록을 찍는 옵션입니다. 로그를 채우던 게 이거였습니다.
소스를 봐도 안 풀린 것
여기까지만 적으면 소스를 열면 다 풀리는 것처럼 보이니, 안 풀린 것도 적습니다.
저 준비 스크립트가 왜 끝나지 않았는지는 끝내 못 밝혔습니다. 스크립트를 다 읽었고, 최신 버전과 한 글자씩 비교까지 했는데 다른 데가 없었습니다.
소스를 여는 게 만능은 아닙니다. 다만 “모르겠다”의 범위가 줄어듭니다. 이 경우 “왜 안 되는지 전혀 모르겠다”에서 “이 스크립트는 정상인데 내 환경에서만 안 끝난다”로 좁아졌습니다. 그 상태에서 스크립트를 건너뛰는 우회를 선택했고, 뭘 건너뛰는지 알고 건너뛰었으니 나중에 문제가 생겨도 어디를 볼지 압니다.
정리하면서 알게 된 것
세 번을 겪고 나니 규칙 비슷한 게 생겼습니다.
언제 여는가 — 문서와 실제 동작이 어긋날 때. 검색 결과가 죄다 다른 버전 이야기일 때. 같은 “왜 이렇게 되지”가 두 번 반복될 때.
전부 읽지 않는다 — 세 번 다 실제로 읽은 건 열 줄 남짓입니다. 궁금한 게 화면에 보이는 동작이면 그 단어로 검색하고, 설정값이면 설정 정의부를 보고, 컨테이너면 Dockerfile을 봅니다. 어디를 열지가 정해져 있으면 남의 코드도 무섭지 않습니다.
버전을 맞춰서 받는다 — 이게 은근히 중요합니다.
git clone --depth 1 --branch v1.16.4 ...
내가 돌리는 게 v1.16.4인데 최신 소스를 읽으면 헛수고입니다. --depth 1은 전체 역사를 안 받는다는 뜻이라 훨씬 빠릅니다.
부수입이 있다 — 목적한 답 말고 근처에서 하나씩 더 건집니다. 익명 ID가 이름+IP라는 것, 자기 댓글에는 숨기기가 안 뜬다는 것. 둘 다 안 찾던 답인데 근처에 있었습니다.
마지막으로
전에는 오픈소스 저장소를 “필요하면 기여하는 곳” 정도로 봤습니다. 지금은 매뉴얼로 씁니다.
문서는 사람이 시간 내서 쓰는 거라 늘 실제보다 뒤처집니다. 코드는 지금 돌아가고 있는 것 그 자체입니다. 그리고 대부분의 프로젝트는 우리가 궁금해할 만한 것 옆에 이미 주석을 달아뒀습니다.
git clone 한 줄이면 되는데 저는 너무 오래 미뤘습니다.
고친 곳
2026년 9월 29일, 글에 붙일 참고 문서를 하나씩 다시 열어보다가 본문과 어긋나는 곳을 찾아 고쳤습니다.
- “브라우저는 안 적힌 쿠키를 Lax로 취급한다”를 “크롬 계열 브라우저는”으로 좁혔습니다.
- “문서에 안 적혀 있었다”를 고쳤습니다. 당시 문서에는 틀린 설명이 있었고, 지금은 고쳐졌습니다.
- 코드블록 두 곳의 줄 이어쓰기 기호가
\\로 두 번 찍혀 있어서\하나로 고쳤습니다. 그대로 복사하면 명령이 안 돌았습니다.
참고한 문서
- Remark42 소스 (v1.16.4) — 숨기기가 localStorage에만 기록되는 부분 — 1절. 숨기기는 서버에 안 보냄
- Remark42 소스 (v1.16.4) — server.go의 same-site 기본값 — 2절. 기본값 default
- Go 소스 (go1.25.0) — net/http/cookie.go — 2절. default는 속성을 안 적는다는 주석
- MDN — Set-Cookie의 SameSite 속성 — 2절. 속성이 없는 쿠키를 브라우저가 다루는 방식
- Remark42 소스 (v1.16.4) — docker-init.sh — 3절. -print가 붙은 find와 exit 199
댓글
로그인도 이메일도 필요 없습니다. 이름만 적고 편하게 남겨주세요.
지금은 댓글을 불러올 수 없습니다. 잠시 뒤에 다시 시도해 주세요. 급한 내용이면 ryuma@kakao.com로 보내주셔도 됩니다.