포스트

연구자 홈페이지를 만들어주는 서비스

연구자 홈페이지를 만들어주는 서비스

thumbnail

주변 연구원분들은 어떻게 포폴을 관리할까

AI 랩실 홈페이지를 보면 많은 연구자분들이 개인 홈페이지를 운영하고 있어요. 인공지능 분야에서는 개인 홈페이지로 소속, 관심 분야, 연구 실적 등을 알리는 게 보편적인 문화예요. 그런데 AI 연구는 프론트엔드와 거리가 멀어서, 주변에 프론트를 다루지 못하는 연구자분들이 많았어요. 그분들이 어떻게 홈페이지를 만드는지 궁금해져서, 약식 설문을 만들어 프론트 기술 숙련도, 홈페이지 제작 경험, 네트워킹 경험 등을 조사했어요. 적은 수의 설문이었지만 홈페이지 제작이 어렵다는 불편함을 명확히 확인했어요. 이 문제를 해결하기 위해 프로토타입을 기획했어요. 제가 인지한 문제는 다음과 같아요.

왜 기존 기술은 사용하기 힘들까

우선 대학원생은 바빠요. 홈페이지를 만들기 위해 많은 시간을 투자하기 어렵죠. 그래서 최대한 빠르고 쉽게 만들 수 있는 방법이 필요해요. 하지만 기존 기술에는 진입장벽이 있어요. 먼저 Jekyll을 살펴볼게요. Jekyll은 연구자뿐만 아니라 엔지니어분들도 홈페이지나 블로그 운영을 위해 많이 선택해요. 저도 현재 Jekyll로 글을 쓰고 있고요. 그런데 Jekyll을 사용하려면 (1) ruby를 설치하고, (2) 템플릿을 찾고, (3) 템플릿 파일 구조를 익히고, (4) 마크다운 작성법을 익히고, (5) Github Pages 사용법을 익혀야 해요. 진입장벽이 분명히 존재해요. 초보 연구자의 경우, 마크다운이나 yaml 형식이 익숙하지 않은 경우도 많았어요.

노션은 훌륭한 대안이 될 수 있어요. 하지만 무료 계정을 사용하면 URL이 지저분하고 블록 수 제한이 있어요. 초기 로딩 속도도 느리고 SEO 최적화도 좋지 않아요. 그렇다고 Google Site를 사용하자니 마땅한 무료 템플릿이 없고, 직접 만들기에는 디자인 작업이 익숙하지 않아요. 이처럼 웹사이트 하나를 만들기 위해 많은 시간을 들여야 하는 상황이에요.

그래서 최대한 빠르고 쉽게 만들 수 있는 서비스를 기획했어요. 연구자 홈페이지는 양식이 어느 정도 정해져 있어요. 이름, 소속, 관심 분야, 최근 소식, 논문 내용이 기본으로 들어가고, 개인 기호에 따라 프로젝트, 이력 등 추가 정보가 들어가요. 이 필수 항목을 순서대로 입력할 수 있게 만들면, 별다른 인지 부담 없이 내용을 채워넣을 수 있을 거라 생각했어요.

시간을 더 단축하기 위해 ORCID에서 정보를 가져오는 기능도 고안했어요. ORCID를 입력하면 정보를 자동으로 불러와 일부 내용을 채워주는 기능이에요. 마지막으로 배포까지 원클릭으로 진행되어야 해요. AI/CS 분야 연구자라면 GitHub 계정을 가지고 있는 경우가 많아요. 사용자가 GitHub 권한을 일시적으로 열어주면, 파일 형식을 맞추고, 레포를 만들고, 파일을 푸시해서 배포하는 과정은 서비스 단에서 대신 처리해줄 수 있어요.

정리하면, 연구자 정보를 불러오고, 채워넣고, 배포하는 흐름을 하나의 서비스 안에서 해결할 수 있도록 구성했어요.

아키텍처를 그려보자

architecture

먼저 서버리스 구조를 위해 Vercel을 사용하기로 결정했어요. 학생 신분으로 인스턴스를 빌릴 여유가 없고, 서비스 규모가 작아서 서버를 직접 운영할 필요도 없었어요. 서버가 결정되고 나니 프론트는 SvelteKit을 사용하기로 했어요. SvelteKit은 빌드 타임에 바닐라 JS로 컴파일해 DOM을 직접 조작하기 때문에 용량이 작고 콜드 스타트가 빨라요. React나 Vue만큼은 아니지만 커뮤니티 규모가 있는 편이라 Shadcn 같은 라이브러리도 충분히 활용할 수 있고, AI가 참고할 자료도 많은 편이에요.

홈페이지 컴파일을 위해 Liquid를 사용했어요. Liquid를 사용하면 서버 사이드에서 템플릿에 정보를 채워넣을 수 있어요. 템플릿만 미리 만들어두면 사용자 정보가 들어왔을 때 빈칸에 넣어주기만 하면 돼요. 사용자 정보 저장에는 JSON을 선택했어요. 사용자가 직접 수정하기 편하도록 YAML이나 TOML 같은 형식도 고민했어요. 그런데 주변에 물어봤을 때 YAML 같은 형식이 익숙하지 않은 경우가 많았어요. 어차피 웹 서비스에서 온전한 편집이 가능하도록 제공해야 하고, 사용자가 직접 파일을 열어볼 일이 없도록 만들어도 되는 상황이에요. 이런 맥락에서 범용성이 좋고 안전하게 정보를 파싱할 수 있는 JSON을 선택했어요. 사용자가 입력한 정보는 JSON 형식으로 저장되고, 이후 수정이 필요할 경우 JSON만 업로드하면 웹 에디터에 정보가 불러와져요.

이렇게 구성된 서비스는 GitHub Actions를 통해 배포되고, Umami로 사용자 데이터를 수집해요. 이와 관련된 내용은 아래에서 자세히 다룰게요.

디자인 시스템 구축과 프론트 구현

코드 구현을 위해 Claude Sonnet 4.6과 Gemini 3.1 Pro, Gemini 3 Flash를 사용했어요. 프론트 구현을 위해 AI 모델을 어떻게 활용했는지 이야기해 볼게요.

일단 잘 하는 거 먼저 해봐

일관된 디자인 생성을 위해 Gemini 3.1 Pro로 계획을 세우고 구현을 시도했어요. 그런데 계획한 내용을 잘 따르지 않고 디자인도 일관되지 않았어요. 그래서 전략을 바꿔 일단 만들고 수정하는 방향을 선택했어요.

Gemini 3.1 Pro가 만든 미완성품을 바탕으로 Gemini 3 Flash에게 하나하나 지시하며 컴포넌트를 수정했어요. 완성된 코드에서 공통적으로 보이는 디자인 특징을 문서로 정리하도록 지시했어요. 이 문서를 Stitch나 Claude design 같은 툴로 일관된 디자인 시스템으로 재구성했어요. 정리한 디자인 시스템을 DESIGN.md에 저장하고, Claude Sonnet 4.6에게 문서에 맞춰 리팩토링하도록 지시했어요. 대략적인 컴포넌트는 이미 완성되어 있었기 때문에 색상, 폰트 등 세부 요소만 다듬으면 되는 작업이었어요. 이 작업을 마치고 나니 예상대로 일관된 디자인이 잘 나왔어요.

모델이 규모 있는 작업을 수행할 성능이 부족하다면, 큰 틀은 사람과 peer-programming으로 만들고 이후에 디테일을 잡는 방식이 효과적이에요.

Card UI를 찍어내는 제미나이

Card UI

Gemini 3.1 Pro에게 디자인을 맡겼을 때 카드 UI를 남발한다는 문제가 있었어요. 좌측 예시처럼 카드 안에 카드를 넣는 방식으로 계층을 구분했어요. 이 때문에 CTA 버튼이 3-depth 안에 들어가는 디자인이 되어버렸어요. 배경색이 겹치다 보니 시각적인 밸런스가 깨지고 CTA가 강조되지 않았어요. 이를 해결하기 위해 우측처럼 카드를 걷어내고 선과 여백으로 계층을 구분했어요. CTA 버튼이 주변 요소에 방해받지 않아 시각적으로 잘 강조돼요. 세로로 항목을 확장할 수 있어서 이후 기능이 추가되어도 어렵지 않게 대응할 수 있어요. 이처럼 아직 AI에게 UX 요소를 온전히 맡기기에는 부족한 부분이 있어요.

Safe-area 색상 통일을 위한 디자인 선택

Safe Area

또 다른 문제로 safe area를 고려하지 못했어요. Playwright MCP를 활용해도 safe area는 확인하지 못하기 때문으로 보여요. 3번째 예시처럼 하단 버튼이 가리는 문제는 지시를 통해 해결했지만, 상하단 영역에 다른 색을 주는 문제는 고치지 못했어요. Opus 4.6 (High), Gemini 3.1 Pro (High) 모두 해결하지 못한 문제예요. 1/2번 예시를 보면 상단에는 헤더가 있기 때문에 safe area가 흰색으로 보여야 시각적으로 통일된 느낌을 줘요. 반면 1번 하단에는 아무런 요소가 없기 때문에 배경색과 동일한 색상을 주어야 자연스럽게 확장된 느낌을 줄 수 있어요. 이 문제가 해결되지 않아 배경과 헤더/푸터 색상을 통일하는 방식으로 디자인 자체를 수정했어요. 코드로 해결되지 않아 디자인으로 문제를 풀어낸 거예요.

백엔드 API 동작 원리

SvelteKit은 풀스택 프레임워크이기 때문에 별도의 서버를 구축하지 않고 TypeScript로 API를 구현했어요. ORCID에서 정보를 가져오기 위해 ORCID에서 제공하는 public API를 사용했어요. GitHub 배포를 위해 OAuth 앱을 등록하고 octokit으로 API를 처리했어요. 가장 핵심이 되는 템플릿 렌더링은 LiquidJS를 사용했어요. JSZip으로 사용자에게 번들을 제공하는데, 렌더링된 HTML과 CSS뿐만 아니라 favicon, 정보를 저장한 JSON 파일까지 모두 포함돼요. HTML 내부에는 meta tag를 자동으로 채워넣어 SEO 최적화까지 고려한 홈페이지가 만들어져요.

Github Actions로 테스트 코드 돌리기

백엔드는 Gemini 3.1 Pro가 생성한 계획을 Claude Sonnet 4.6이 잘 구현해 냈어요. 하지만 세부적인 요소는 신경 쓰지 못했어요. 예를 들어, 템플릿을 렌더링할 때 사용자 정보를 검증하는 단계가 없어 XSS가 발생할 우려가 있었어요. 또 GitHub OAuth 인증 시 state 파라미터를 생성하지 않아 CSRF 공격이 발생할 가능성도 있었어요. 이처럼 기능 구현 너머에 있는 문제를 찾기 위해 단위 테스트를 작성하도록 요청했어요. 테스트 코드 생성도 Claude를 사용했어요. 테스트 케이스에 사용할 예시 데이터를 자동으로 생성할 수 있어 매우 편리했어요. 총 100개 이상의 테스트 케이스를 생성했고, GitHub Actions를 이용해 ESLint → Vitest → Vercel 순으로 배포되도록 파이프라인을 구축했어요. 이후 기능을 추가할 때 AI가 테스트 코드를 실행해 기능을 검증할 수 있었어요.

사용자 행동 분석을 위한 이벤트 추적

기능 구현을 넘어 사용자 데이터를 추적하고 분석하는 일도 중요해요. 여러 후보 중 umami를 선택했어요. 수집하고 싶은 정보는 접속자 수, 접속 경로, 이벤트(GitHub 배포, JSON 불러오기 등) 실행 횟수 정도예요. 복잡한 추적은 필요하지 않아요. 초반에는 트래픽이 많지 않을 거라 예상했어요. 그래서 많은 정보를 저장하기보다, 적은 정보라도 영구히 유지할 수 있는 툴이 필요했어요. 무료로 사용할 수 있으면 더 좋고요. 이 조건을 모두 만족하면서 개발 경험이 뛰어난 툴이 umami였어요. Umami는 헤더에서 스크립트를 불러오고, 이벤트가 발생하는 버튼에 data-umami-event로 이벤트 이름을 작성하면 끝이에요. 수집된 정보는 umami cloud 대시보드에서 확인할 수 있어요.

처음에는 SQLite 기반의 Turso DB를 사용해 GitHub Pages로 배포한 사용자 아이디를 직접 수집하는 방법도 생각해 봤어요. 하지만 이 방식은 사용자가 특정되기 때문에 정보 수집 동의가 필요해요. 지금 단계에서 동의 절차를 추가하면 사용자 플로우가 깔끔하지 않을 거라 생각했어요. 그 대안으로 /export 페이지에 GitHub star를 요청하는 메시지와 함께 팝업 애니메이션을 추가했어요. 지속 사용할 의향이 있는 사용자라면 star를 눌러줄 거라 생각했어요. 그렇다면 실제 페이지가 잘 배포되었는지, 코너 케이스는 없는지 추적할 수 있을 거예요.

Rico를 만들며

주변의 불편함을 해결하기 위해 빠른 프로토타이핑으로 서비스를 만들고 배포해 봤어요. 생성형 AI가 발전하고 개발을 돕는 툴이 많아지면서, 도메인 지식만 있다면 구현 속도가 확실히 빨라졌다는 걸 실감했어요. 그럼에도 기획이나 운영에서 배운 경험은 무시할 수 없다고 느꼈어요. 예전에 재미삼아 해요를 배포한 적이 있었는데, 그때 쌓았던 경험과 판단 기준 덕분에 더 빠르게 의사결정을 내릴 수 있었어요. 앞으로 rico를 지속적으로 모니터링하며 운영 경험을 쌓아 나갈게요.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.