Hojin Yang

  • TypeScript, Svelte, React로 프론트엔드 개발을 합니다.
  • Node, Deno, Bun, Sqlite, EdgeDB를 이용한 백엔드 개발도 한 경험이 있습니다.
  • 온전한 서비스의 기획, 개발, 배포, 유지보수까지의 전 과정을 수행한 경험이 있습니다.

Projects

4월의 낙원호는 사용자 인증, 재화 시스템, 미니 게임(슬롯머신, 낚시, 선택지 게임), 출석 체크, 우편, 상점, 가방 기능을 제공하는 웹 게임입니다. 또한, 참가자 관리, 보상 지급, 공지 사항 작성, 상점을 관리할 수 있는 운영자 페이지를 제공합니다.

  • EdgeDB 도입 배경
    • EdgeDB는 유연함과 엄격한 스키마를 동시에 가지며, 복잡한 질의도 직관적으로 작성할 수 있습니다. 또한, 비즈니스 로직을 디비에 담아서 중복과 오류 가능성을 사전에 제거할 수 있을 것으로 기대했습니다.
  • API 응답 속도 43배 단축하기
    • 서비스 첫날 API 끝점 응답 속도가 평균 2,322ms, 최대 20초까지 느려지고 트랜잭션 실패가 발생했습니다. 클라이언트 반응성을 올리기 위해 클라이언트에서 처리가 완료된 것으로 표시하고, 디비에서 나중에 처리하는 방식을 사용했는데, 트랜잭션 실패로 사용자의 인식 불일치를 가져왔습니다.
    • 우선 사용자 인식 불일치를 줄이는 방향으로 핫픽스 후 진짜 원인을 분석했습니다. 사용자 인증에 사용하는 ext::auth::ClientTokenIdentity가 느린데, 이것을 여러번 질의하는 것을 확인했습니다.
    • API 끝점 당 한 번씩 질의하고 재사용하도록 변경했습니다.
    • 평균 2,322 ms가 걸리던 응답 속도가 54 ms로 줄어들었습니다.
  • 권한 및 테이블 상속
    • 처음에는 관리자와 사용자가 User를 상속하게 설계했습니다. 하지만, 요구사항이 명확해지고 보니 관리자는 사용자의 슈퍼셋이었습니다.
    • 관리자와 사용자를 하나의 테이블로 합치고 권한을 isAdmin 속성으로 옮겼습니다.
    • 언제든지 사용자와 관리자 전환 가능해지고, 사용자의 모든 기능을 관리자도 사용 가능해졌습니다.
  • 동시 편집
    • 운영자 페이지를 여러 명이 동시에 수정하면 상태가 꼬일 것을 우려했습니다. 처음에는 CRDT 라이브러리인 Y.js와 Liveblocks 서비스를 조사했습니다. 그 중 Liveblocks는 월 사용자 100명 미만이면 완전 무료여서 바로 도입할 수 있었습니다. 다만, 구현하고 보니 엑셀과 유사한 형태가 되어서, 구글 스프레드시트를 쓰기로 했습니다.
  • 타입 세이프한 API 클라이언트
    • 타입 세이프한 api 요청을 구현하고 싶었습니다. tRPC, ElysiaJS를 써봤지만 모두 SvelteKit과 매끄럽게 동작하지 않았습니다.
    • /routes/api/**/+server.ts를 모두 읽고 클라이언트 코드를 생성하게 만들었습니다.
    • 예시: api().mail.post({ id }, body) -> POST /api/mail/[id]
    • 코드를 생성하여 생산성을 끌어올릴 수 있었습니다.
  • 디자인 시스템 및 UI 컴포넌트
    • 기본 요소를 스타일링하는 pico.css에 영향을 받았습니다. portal, modal 같은 간단한 컴포넌트는 직접 구현하고, 복잡한 상태를 가지는 컴포넌트는 Bits UI(Radix UI에 영향을 받은 Svelte 용 헤드리스 컴포넌트)를 사용했습니다.
  • Stylebook
    • 컴포넌트가 늘어나자, Storybook이 필요해졌으나, Svelte 5를 지원하지 않았습니다. 필요한 기능만 들어있는 유사 Storybook을 구현했습니다.
    • 덕분에 다양한 상태를 가지는 컴포넌트를 직관적으로 만들 수 있었습니다.
  • 미니 게임 애니메이션
    • SVG를 Svelte 컴포넌트에서 다루도록 구현했습니다.
  • 인앱 브라우저 대응
    • 특정 환경에서 인앱 브라우저가 쿠키를 유지하지 못해서 매번 로그인해야 하는 문제가 발생했습니다. 카카오톡과 안드로이드의 경우 강제로 브라우저가 열리게 만들 수 있었지만, iOS는 불가능했습니다. 대신, 인앱 브라우저인지 감지하여 안내 문구를 띄우는 방식으로 해결을 할까 고민했지만, 링크로 로그인하는 기능을 추가하여 해결했습니다.
  • 원인을 알 수 없는 버그
    • 특정 주소에서 502 Bad Gateway가 뜨는 문제가 발생했습니다. 개발환경과 빌드 결과물에선 멀쩡한데, 서버에서만 문제를 일으켰습니다.
    • 구체적인 원인은 찾지 못했지만, SSR 중에 발생하는 문제였기에 SSR을 포기하고 SPA로 전환했습니다.
  • 성과 및 통계
    • 사용자 수: 33명
    • 제명된 사용자 수: 4명
    • 보낸 우편 수: 280건
    • 가장 많은 칩을 얻은 사용자의 칩 수: 6,560칩
    • 가장 많은 토큰을 얻은 사용자의 토큰 수: 97토큰
    • 출석 체크를 매일 하신 사용자: 5명
    • 낚시 업적을 모두 달성한 사용자: 2명
    • 가장 많이 낚시를 하신 사용자의 낚시 횟수: 290회
    • 가장 많은 아이템을 보유하신 사용자의 아이템 수: 295개

내가 할 수 있는 실험체를 말해보자

특정 게임의 선호 실험체(캐릭터)를 표시하고 SNS에 공유할 수 있는 서비스입니다.

일주일 최대 방문자 759명, 최대 페이지뷰 2073뷰를 달성했습니다.

  • 모바일 대응
    • PC 이용자에 비해 모바일 이용자가 2.2배 많았습니다. 따라서 레이아웃을 모바일에 맞게 재배치하고 개선했습니다.
    • 디자인 (Figma)
  • 페이지 로딩 속도 개선
    • 웹폰트
      • 웹폰트 크기를 줄이기 위해 웹폰트 서브셋을 제작했습니다. 웹폰트 크기는 1.54 MB에서 10.7 kB로 줄고, 페이지 로드 속도는 168 ms에서 16 ms로 약 10배 개선되었습니다.
      • 다만, 2주마다 새로운 내용이 추가되는 서비스 특성상 시스템 폰트로 변경했습니다.
    • 이미지
      • 이미지를 영구 캐싱하여 전체 페이지 크기를 2.2 MB에서 330 B로 줄였습니다.
  • OG(OpenGraph) 이미지 생성 시간 개선
    • OG 이미지는 링크 미리보기에 나오는 이미지입니다. 본 서비스는 OG 이미지를 동적으로 생성했는데, 링크 미리보기를 보여주는 서비스마다 타임아웃이 있어서 이를 넘기면 표시되지 않는 문제가 있었습니다.
    • 실험체 목록을 가져오는 API를 캐싱하고, 이미지 크기를 줄여 이미지 로딩 속도를 개선했습니다.
    • OG 이미지 생성 시간이 1.5 초에서 200 ms로 감소했습니다.

여러 명이 동시에 즐길 수 있는 온라인 핑퐁입니다.

  • 실시간 서버 구현
    • 서버에서 물리 계산을 수행하고 클라이언트에 WebSocket으로 위치만 전송하는 방식을 선택했습니다. 처음에는 1초에 60번 전체 상태를 전송하게 구현했습니다. 이후, 이전 상태와 이후 상태를 비교해서 변경 점만 전송하도록 수정했습니다. 단, 이 경우 전송 순서가 보장되어야 하고, 이는 논리 시계를 사용하면 어느정도 해결 할 수 있지만 구현하진 않았습니다.
  • 선형 보간 구현
    • 네트워크가 불안정한 환경에서도 자연스럽게 보이도록 클라이언트에서 선형 보간을 하게 수정했습니다. 덕분에 서버에서 상태를 절반만 보내도 이전과 변함없이 보이게 되었습니다.

소프트웨어 래스터라이저

3D를 픽셀(래스터 이미지)로 변환하는 Rasterizer를 JavaScript로 구현한 프로젝트입니다.

  • 구현 동기
    • 수학적 원리 학습과 구현을 목표로 구현했습니다.
  • 구현 기능
    • 질량 중심 좌표계로 삼각형에 색을 입히는 rasterization
    • 폴리곤을 잘라서 삼각형으로 만드는 triangulation
    • 3D 모델 파일인 OBJ, PLY 로더와 텍스처 로더
    • z buffer를 이용한 z-culling
    • 동차좌표계를 이용한 perspective projection
  • 렌더링 속도 개선
    • 매 프레임 새로 생성하는 객체를 최소화했습니다.
    • 4.5만 개의 삼각형을 그리는 속도가 350 ms에서 50 ms로 감소했습니다.

COOPS

게이머를 위한 익명 음성 대화 서비스입니다. 일시적인 모임에서 번거로운 절차나 개인정보 노출 없이 빠르게 음성 대화에 참여할 수 있습니다.

  • 디자인과 개발 담당
  • AWS EC2에서 2개월 간 운영
  • 음성 대화 구현
    • 음성 대화는 WebRTC를 이용해 구현했습니다. 하지만, ICE 서버를 따로 구축하지 않아, 사용자의 네트워크 환경에 따라 연결되지 않는 문제가 존재합니다.

Education