Bear Blog에서 Jekyll로: 100개 글 마이그레이션 + 블로그 전면 개편기
오늘 하루 동안 Bear Blog에서 Jekyll 블로그(theorakim.github.io)로 전체 글을 이전하고, 사이트 구조를 대폭 개편했다. Claude Code와 함께한 작업 기록.
1. 기술철학 글 마이그레이션 (16편)
byminseokcom/post_export.csv에서 기술철학 관련 글을 추출하는 것부터 시작했다. CSV는 889KB짜리 Bear Blog 전체 export 파일. 컬럼 중 all tags에 JSON 배열로 태그가 들어있어서, 기술철학·STS·Art-n-Tech·human-ai 태그가 있는 글을 필터링했다.
17개가 매칭됐는데, 1개는 publish: False여서 16개를 Jekyll 포스트로 변환. Python 스크립트로 front matter 생성 + 본문 추출을 자동화했다.
꺾쇠괄호 문제
알고리즘반항클럽 #1 글이 깨졌다. <모임장 자기소개>, <발제>, <대화> 같은 텍스트가 HTML 태그로 해석되어 통째로 사라지는 문제. 해결:
- 섹션 구분용
<발제>,<대화>→**발제**,**대화**(볼드 처리) - 작품명
<미래와 만날 준비>→⟨미래와 만날 준비⟩(유니코드 꺾쇠)
이후 전체 글에도 같은 패턴을 자동 변환하도록 스크립트에 반영했다.
2. 나머지 글 전체 업로드 (81편)
publish:false와 기술철학 글을 제외한 나머지 81편을 한꺼번에 올렸다. 여기서 추가 작업:
- 태그 정규화:
essay→에세이로 통일 - 무태그 글 11개: 내용을 보고 적절한 태그 수동 할당 (에세이, 독서, 관찰 등)
- 연도 태그 제외:
year-2024같은 연도 태그는 UI에서 숨김 처리
태그 필터 UI
_layouts/archive.html에 클라이언트사이드 태그 필터를 추가했다. Liquid로 빌드 시점에 태그 목록을 수집하고, JS로 클릭 시 포스트를 필터링하는 구조. 연도별 그룹 헤딩도 빈 연도는 자동으로 숨긴다.
3. 페이지 분리
writings·techphil·devlog 세 페이지가 글을 겹치지 않게 분리:
- writings (
exclude_tags): 기술철학·개발기록 태그 글 제외 - techphil (
filter_tags): 기술철학·STS·Art-n-Tech·human-ai만 - devlog (
filter_tags): 개발기록만
archive.html 레이아웃에 filter_tags(포함)와 exclude_tags(제외) 두 가지 필터 모드를 구현해서, 각 페이지의 front matter에서 선언만 하면 동작하게 만들었다.
devlog 혼선 해결
devlog에 기술철학 글이 섞여 나오는 문제가 있었다. 원인 두 가지:
- devlog의
filter_tags에Claude.AI가 포함되어 있어서 기술철학 글이 매칭됨 → 제거 - 기존 devlog 글 3개에
개발기록태그가 아예 없었음 → 추가
4. 모달에서 페이지 이동으로
원래 인덱스 페이지에서 (1) 글을 쓰고 (2) 기술을 사유하고 (3) 코드를 기록합니다를 클릭하면 모달 오버레이로 글 목록이 떴다. 이걸 각 페이지(/writings/, /techphil/, /devlog/)로 직접 이동하도록 변경.
home.html:data-modal링크 → 실제 URL 링크- 모달
<section>3개 삭제 modal.js삭제,default.html에서 스크립트 태그 제거_layout.scss에서.modal,.modal-close,.list-item등 관련 CSS 정리
5. 디자인 조정
- 왼쪽 맞춤:
margin: 0 auto→margin: 0으로 변경. 가운데 정렬 대신 왼쪽 정렬. - 푸터:
contact : summerinloving@gmail.com+© 김민석 · 폰트는 고운돋움입니다.(링크 포함) - 인트로 텍스트: 볼드 넣었다 뺐다 — 결국 원래 스타일 유지
6. 로컬 빌드 환경
Jekyll 로컬 빌드가 안 되는 문제에 꽤 시간을 썼다.
- 시스템 Ruby 2.6: 너무 오래됨, bundler 버전 불일치
- Homebrew Ruby 4.0.1:
github-pagesgem의 Liquid 4.0.3이tainted?메서드를 호출하는데, Ruby 3.2+에서 제거됨 - Jekyll 4.x 시도:
google-protobuf네이티브 익스텐션 빌드 실패 - 최종 해결:
brew install ruby@3.3→ Ruby 3.3.10 + Bundler 2.7.1로github-pagesgem 정상 동작
7. 책 날개 스프레드 레이아웃
오늘의 하이라이트. archive 페이지에 “책 펼침” 2단 레이아웃을 구현했다.
구조
- 왼쪽 패널 (420px): 페이지 제목 + 태그 필터 + 글 목록.
position: sticky로 스크롤 시 고정, 내부적으로 overflow 스크롤. - 구분선:
border-right: 1px solid - 오른쪽 패널 (flex: 1, max-width 640px): 선택한 글의 본문
동작 방식
데스크탑(960px+)에서 글 목록의 링크를 클릭하면 fetch()로 해당 글의 HTML을 가져와 DOMParser로 파싱, .post article만 추출해서 오른쪽 패널에 삽입한다. 캐시 객체로 같은 글 재요청을 방지하고, history.replaceState로 URL도 업데이트.
모바일(960px 미만)에서는 JS가 개입하지 않고 기본 링크 동작으로 페이지 이동.
CSS 인코딩 삽질
SCSS 파일에 한국어 주석(// 책 날개)과 유니코드 문자(content: "●")를 넣었더니 Jekyll의 sass-converter 1.5.2가 Invalid US-ASCII character 에러를 뱉었다. 주석은 영문으로, 특수문자는 CSS escape(\25CF)로 해결.
수정 파일
_layouts/archive.html— spread wrapper + reader JS_sass/_post.scss— spread 레이아웃 CSS_sass/_layout.scss—body:has(.spread)max-width 확장
8. 포트폴리오 페이지
비어있던 /portfolio/ 페이지를 채웠다. 직접 만든 프로젝트, 업무 성과, 커뮤니티 활동, 글쓰기를 카테고리별로 정리하는 구조.
카테고리와 항목
- 프로젝트: 창작하는아침 캘린더(Discourse 연동), 판교 날씨 봇(기상청 API + 카카오톡)
- 업무: 회고 아카이브(tfi-retro-library), 테크포임팩트 깃북
- 커뮤니티: 넷플연가 모임
- 글: 퍼블리 아티클
각 항목은 제목 + 2-3줄 설명 + 기술 태그로 구성. 전체가 하나의 <a> 블록이라 카드 어디를 클릭해도 이동한다. 나중에 스프레드 레이아웃을 붙일 수 있도록 data-id 속성을 미리 심어뒀다.
3단 그리드 카드 레이아웃도 시도해봤는데, 블로그의 미니멀한 톤과 안 어울려서 세로 리스트로 확정.
수정 파일
portfolio.html— 카테고리별 HTML 구조_sass/_portfolio.scss— 새 파일, BEM 구조 스타일assets/css/main.scss—@import "portfolio"추가
9. 홈 페이지 다듬기
이모지 전환
인트로 텍스트의 (1) 글을 쓰고 (2) 기술을 사유하고 (3) 코드를 기록합니다에서 숫자를 이모지로 교체: ✍️ 글을 쓰고 💬 기술을 사유하고 💻 코드를 기록합니다. home.html과 nav.html 두 곳 모두 반영.
헤더 자간
모든 페이지에 표시되는 “김민석” 헤더에 letter-spacing: 0.05em을 추가. 볼드도 넣어봤다가 뺐다 — 고운돋움 폰트에서 볼드는 너무 두꺼웠다.
홈 페이지 폭 확장
default.html의 <body id=""> 기능을 활용해 home.html 레이아웃에 body_id: home을 추가하고, CSS에서 body#home { max-width: 740px }로 홈만 넓혔다. 기본 640px에서 100px 확장. 인트로 텍스트의 줄바꿈 위치를 자연스럽게 조절하기 위해서였는데, 800px → 760px → 750px → 740px까지 내려가며 조절했다.
단, body_id를 레이아웃 front matter에 넣으면 page.body_id로 안 잡히는 문제가 있었다. Jekyll에서 레이아웃의 변수는 layout.body_id로 접근해야 해서, default.html을 page.body_id | default: layout.body_id로 수정.
수정 파일
_layouts/home.html— 이모지 +body_id: home_layouts/default.html—layout.body_id폴백 추가_includes/nav.html— 이모지_sass/_layout.scss— 헤더 자간 +body#home폭
최종 결과
- 100개 포스트 마이그레이션 완료 (16 기술철학 + 81 일반 + 3 기존 devlog)
- 3개 섹션 깔끔하게 분리 (writings / techphil / devlog)
- 태그 필터 UI 동작
- 책 날개 레이아웃 — PC에서 목록과 본문을 나란히 읽기
- 로컬 빌드 정상 동작 (Ruby 3.3)
- 포트폴리오 페이지 — 프로젝트·업무·커뮤니티·글 4개 카테고리
- 홈 페이지 — 이모지, 자간, 폭 조정
10. 테라코타 디자인 적용
블로그 전체 배경색을 순백(#ffffff)에서 테라코타 팔레트의 Pampas(#F4F3EE)로 변경했다. 내가 쓰는 VSCode 커스텀 테마(Terracotta Light)와 톤을 맞추기 위해서. 다크모드는 기존 #1a1a1a 유지.
푸터도 손봤다. 구분선(border-top) 제거, 여백 축소, 내용을 © minseok | summerinloving@gmail.com 한 줄로 정리. 스크롤 없이 한 화면에 푸터까지 보이도록 body 패딩과 헤더 마진을 줄였다.
- body 패딩:
--space-xl(6rem) →--space-lg(4rem) - 헤더 하단:
--space-lg(4rem) →--space-md(2rem) - 푸터 상단:
--space-xl(6rem) →--space-md(2rem)
11. 홈 페이지 스프레드 레이아웃
archive 페이지에서 쓰던 책 날개 패턴을 홈에도 적용했다. 왼쪽에 소개글, 오른쪽에 콘텐츠가 펼쳐지는 구조.
동작
- “만든 것들은 여기에” 클릭 → 오른쪽 패널에
/portfolio/내용을 fetch해서 표시 - “뉴스레터는 여기서” 클릭 →
/newsletter/내용 표시 - 모바일에서는 기존처럼 페이지 이동
기본 상태: 최근 글 7개
오른쪽 패널이 비어있으면 허전해서, 아무것도 선택하지 않은 상태에서는 최근 글 7개를 날짜+태그와 함께 보여주도록 했다. Liquid로 빌드 시점에 생성.
활성 링크 표시
처음에는 archive처럼 ● 동그라미를 붙였는데, 홈에는 링크가 2개뿐이라 음영 처리가 더 어울렸다. 까만 배경으로 했다가 너무 튀어서 테라코타 Crail(#C15F3C) 배경 + Pampas(#F4F3EE) 글자로 변경.
왼쪽 패널 폭 조정
420px → 760px → 600px → 580px까지 반복 조정. 760은 너무 넓어서 오른쪽이 비좁았고, 580이 소개글 줄바꿈과 오른쪽 콘텐츠 폭의 균형이 맞았다. archive 페이지에도 같은 값이 적용되므로 전체 spread 레이아웃이 일관되게 변경됐다.
수정 파일
_layouts/home.html— spread 구조 + fetch JS_sass/_layout.scss—body#home:has(.spread)max-width, 활성 링크 스타일, 최근 글 스타일_sass/_post.scss—.spread__leftwidth 580px,.spread__right > :first-child여백 제거
12. 뉴스레터 페이지
/newsletter/ 페이지를 새로 만들었다. 처음에는 Substack embed iframe을 넣었는데 블로그 디자인과 안 어울려서, iframe을 빼고 소개 문구 + 테라코타 색 구독 버튼으로 대체.
newsletter.html— 소개 텍스트 + Substack 링크 버튼_sass/_layout.scss—.newsletter__btn스타일 (Crail 배경, 호버 시 Crail Dark)
13. 옵시디언 블로깅 워크플로우
글을 편하게 쓰고 발행하는 시스템을 구축했다.
구조
옵시디언 vault의 _claudecode-notes 폴더가 심볼릭 링크로 Jekyll _posts/에 연결되어 있어서, 옵시디언에서 쓴 글이 바로 Jekyll 포스트가 된다.
옵시디언 vault/_claudecode-notes/
→ workspace/_claudecode-notes/ (심볼릭 링크)
→ theorakim.github.io/_posts/ (심볼릭 링크)
템플릿
옵시디언 vault에 _templates/블로그 포스트.md 템플릿을 만들어뒀다. 새 글 생성 시 Cmd+P → 템플릿 삽입으로 front matter를 자동으로 채울 수 있다.
발행 워크플로우
- 옵시디언
_claudecode-notes에서 글 작성 (파일명:YYYY-MM-DD-slug.md) - Claude Code에서 “발행해줘” → 꺾쇠 검사 + 빌드 확인 + git commit & push
초안이나 메모는 옵시디언의 다른 폴더에 두고, 발행할 글만 _claudecode-notes로 옮기면 된다. git push를 의도적으로 수동 스텝으로 둬서 실수로 발행되는 걸 방지했다.
14. 홈 최근 글 → 스프레드 연결
홈 페이지의 “최근 글” 목록을 클릭하면 글 단독 페이지로 이동하던 걸, 목록과 본문이 함께 보이는 아카이브 스프레드 페이지로 연결되도록 바꿨다.
각 포스트의 태그를 Liquid로 검사해서 적절한 아카이브 페이지를 data-archive 속성에 저장하고, 데스크탑에서 클릭 시 ?read= 쿼리 파라미터와 함께 해당 페이지로 이동. archive.html에서는 페이지 로드 시 이 파라미터를 감지해 자동으로 글을 fetch한다. 모바일에서는 기존처럼 글 단독 페이지로 이동.
- 기술철학/STS/Art-n-Tech/human-ai →
/techphil/ - 개발기록 →
/devlog/ - 나머지 →
/writings/
15. 리브랜딩: byminseok.com
헤더의 “김민석”을 “byminseok.com”으로 변경하고 font-weight: 600(semi-bold)으로 조정. 도메인 이름이 사이트 아이덴티티가 되는 구조.
site description도 글을 쓰고, 기술을 사유하고, 코드를 기록합니다로 업데이트. `
` 태그와 중복되던 <title> 태그를 head.html에서 제거.
16. Google Analytics + SEO 정비
- Google Analytics: 측정 ID로 gtag.js 스니펫을 head.html에 추가
- OG 이미지: 아틀리에 구분선 색(
#D7CCC8) 배경에 Baskerville 세리프로 “byminseok.com” — 1200x630 PNG - 파비콘: 같은 베이지 색의 원형 파비콘(32x32, 16x16). 글자 없이 빈 원
- favicon 링크 태그: head.html에 누락되어 있던
<link rel="icon">추가
17. 커스텀 도메인 연결
Porkbun에서 구매한 byminseok.com 도메인을 GitHub Pages에 연결.
- Porkbun DNS: 기존 Bear Blog A 레코드 삭제, GitHub Pages IP 4개 등록 + www CNAME
- 레포에
CNAME파일 추가 (이미 있었음) gh api로 GitHub Pages 커스텀 도메인 등록- SSL 인증서: GitHub이 Let’s Encrypt로 자동 발급 (DNS 변경 후 대기 필요)
18. 소소한 마무리
- 로딩 텍스트: “불러오는 중…” → “천천히 천천히”
- 404 페이지: “어라 여기가 어디죠?” (클릭하면 홈으로)
- robots.txt: sitemap URL을 byminseok.com으로 설정
19. 스프레드 새로고침 복원
스프레드 모드에서 글을 읽다가 새로고침하면 왼쪽 목록 패널이 사라지고 포스트 단독 페이지로 바뀌는 문제를 해결했다. 원인은 history.replaceState로 URL이 포스트 URL로 바뀐 상태에서 새로고침하면 Jekyll이 post.html 레이아웃으로 렌더링하기 때문.
sessionStorage에 아카이브 페이지 URL을 저장하고, post.html에서 데스크탑 로드 시 이를 감지해 ?read= 파라미터와 함께 아카이브 페이지로 리다이렉트하는 방식으로 해결. 모바일에서는 리다이렉트하지 않는다.
20. OG 이미지 메타 태그
카카오톡/슬랙에서 링크 공유 시 미리보기 이미지가 안 뜨는 문제. _config.yml에 image: /assets/og-image.png가 있었지만, jekyll-seo-tag는 site.image가 아니라 page.image만 참조한다는 걸 소스 코드를 까서 확인했다. defaults에 전 페이지 기본 image를 설정해서 해결.
21. 태그 정리
- 태그 필터 범위: writings에만 태그 필터 UI를 표시하고 techphil/devlog에서는 숨김.
archive.html에서page.filter_tags존재 여부로 분기. - 태그명 통일:
shape-of-belonging→내 자리의 모양을 찾아서 - writings 태그 필터 최종 목록: 전체, memo, 공연, 내 자리의 모양을 찾아서, 다거점생활, 독서, 서천시골집, 에세이, 여행, 회고
22. 타이핑 인트로
홈페이지 소개 문장에 타이핑 효과를 추가했다. 한 글자씩 나타나고 커서가 깜빡이는 연출.
- 이모지 처리:
charAt()는 이모지(특히 변형 선택자 포함된 ✍️)를 쪼개서 깨뜨림.Intl.Segmenter로 grapheme 단위 분리해서 해결. - 재방문 처리:
sessionStorage에introSeen플래그를 저장해서, 같은 세션에서 다시 방문하면 타이핑 없이 즉시 표시. 브라우저 닫으면 리셋. - 완료 후 전환: 타이핑이 끝나면 커서가 사라지고, 하단 링크(“만든 것들은 여기에”, “뉴스레터는 여기서”)가 페이드인.
다음에 해볼 것들
- 목차(TOC) 자동 생성
- 태그별 컬러코드
- 인용구 컬렉션 — 포스트에서 좋아하는 문장만 모아 랜덤으로 보여주는 페이지
- 랜덤 글 뽑기 버튼
- 글자 수 히트맵 (GitHub 잔디 스타일)
- 연결 지도 — 글끼리 관련도를 시각화하는 네트워크 그래프
- 계절 테마 — 날짜 기반으로 배경 분위기 변화
- Dependabot 경고 해결