지금 보고 계신 이 사이트는 데이터 파일 하나에서 만들어집니다. 이력서를 고칠 때마다 웹, PDF, 노션을 따로 고치다 보면 어딘가 내용이 어긋나기 마련이라, 처음부터 "내용은 한 곳에만 쓴다"를 원칙으로 잡았습니다.
구조
data/portfolio.yaml ← 내용은 여기에만 씀 (활동, 논문, 기술, 수상)
content/posts/*.md ← 이런 글
content/study/ ← 강의 정리 노트
│
▼ scripts/build.py (Jinja2 템플릿)
docs/index.html ← 웹 포트폴리오
docs/resume.html → PDF ← 상세 이력서 (Playwright로 PDF 변환)
docs/resume-1page → PDF ← 1장 이력서
docs/study/ ← 학습 공간
out/notion.md ← 노션에 붙여 넣을 문서
git push를 하면 GitHub Actions가 빌드하고 GitHub Pages로 배포합니다.
데이터 구조를 먼저 정했다
활동 하나를 STAR(상황·과제·행동·결과) 구조로 적습니다. 같은 데이터를 쓰되, 출력물마다 다르게 보여 줍니다.
- 웹: 카드에는 요약과 성과 지표 하나, "자세히 보기"를 누르면 STAR 전체
- 상세 이력서: 행동과 결과 문장
- 1장 이력서: 활동마다 따로 적어 둔 3줄 요약(
brief)
최근에는 "석사과정 / 학부 / 부트캠프"처럼 여러 프로젝트를 한 묶음으로 보여 달라는 요구가 생겨서, 활동 아래에 projects:로 하위 프로젝트를 둘 수 있게 바꿨습니다. 하위 구조를 모르는 출력물(노션 문서 등)에서도 내용이 빠지지 않도록, 빌드 단계에서 하위 프로젝트의 내용을 모아 상위 활동에도 채워 넣습니다.
빌드가 잡아 주는 것
빌드할 때 보완 체크리스트를 같이 만듭니다.
- STAR의 상황·과제·행동·결과 중 빈 칸이 있는 활동
- 결과에 숫자(성과 지표)가 하나도 없는 활동
- 대표 활동이 너무 많거나 적을 때
TODO로 시작하는 문장은 결과물에 내보내지 않고 체크리스트로만 보냄
글을 쓰다 보면 "열심히 했다"로 끝나는 문장이 많아지는데, 이 체크리스트가 그걸 계속 알려 줍니다.
사진은 위치 정보를 지우고 넣는다
활동 사진은 photos/<활동 id>/에 원본을 넣으면 스크립트가 웹용 크기로 줄이고 EXIF(촬영 위치 등)를 모두 지운 뒤 갤러리에 붙입니다. 파일 이름이 그대로 사진 설명이 됩니다. 원본 사진 폴더는 저장소에 올리지 않습니다.
공개하면 안 되는 내용
연락처, 증빙 서류 목록, 자기소개서 소재, 면접 답변 연습처럼 나만 봐야 하는 내용도 같은 사이트 안에 두고 싶었습니다. 다만 공개 저장소에 평문으로 올리면 안 됩니다.
그래서 두 단계로 암호화했습니다.
- 저장소에 올릴 때: 평문은 로컬에만 두고(.gitignore), 공개키로 암호화한 파일만 커밋 (RSA-OAEP + AES-GCM). 개인키는 저장소가 아니라 GitHub Secrets에만 있음
- 배포할 때: Actions가 개인키로 복호화해 페이지를 만들고, 이번에는 비밀번호 기반 키(PBKDF2-SHA256 60만 회 + AES-GCM)로 다시 암호화해 배포
방문자 브라우저에는 암호문만 내려가고, 비밀번호를 입력하면 브라우저의 WebCrypto가 그 자리에서 복호화합니다. 서버 없이도 "로그인해야 보이는 페이지"를 만들 수 있었습니다.
만들고 나서
- 내용과 형식을 분리하니 디자인을 세 가지 시안으로 바꿔 보는 것도 템플릿만 바꾸면 됐습니다.
- 데이터 스키마를 먼저 정하는 것이 결국 가장 오래 남는 결정이었습니다. 출력물은 나중에 얼마든지 늘릴 수 있지만, 스키마를 바꾸면 모든 출력물을 다시 봐야 합니다.
- 보안이 필요한 부분은 "숨긴다"가 아니라 "평문이 남는 곳이 어디인지"를 먼저 따져야 한다는 것도 배웠습니다.