2023.09.25 - 2026.08.31
디벨로켓에듀 · 스마트 훈련 플랫폼 개발팀 / 프론트엔드
LMS V2·V3 프론트엔드 신규 구축과 운영을 중심으로 통합 계정, 모집 백오피스, 기업 홈페이지, 학습 기록 제품을 개발했습니다. 기획·디자인·백엔드·인프라·운영 담당자와 기능의 책임 경계를 조율했습니다.
Resume portfolio
baekbr13@gmail.comMail백종환
Frontend Engineer
React·Next.js 기반 실서비스를 신규 구축하고 운영하며, 복잡한 상태와 실패 비용이 큰 흐름을 다뤄왔습니다.

경력 기록
경력
2023.09.25 - 2026.08.31
LMS V2·V3 프론트엔드 신규 구축과 운영을 중심으로 통합 계정, 모집 백오피스, 기업 홈페이지, 학습 기록 제품을 개발했습니다. 기획·디자인·백엔드·인프라·운영 담당자와 기능의 책임 경계를 조율했습니다.
교육
2023.10 - 2026.08
컴퓨터공학 전공 학위를 이수했습니다.
2022.10 - 2023.07
HTML, CSS, JavaScript의 동작 원리부터 React, Node.js와 Solidity를 학습하고 팀 프로젝트와 해커톤을 경험했습니다.
2013.03 졸업
기초 학력 사항입니다.
프로젝트 기록
역할·평가·영상·파일 업로드처럼 운영 중 반복해서 어긋날 수 있는 상태를 명시적인 경계와 복구 가능한 흐름으로 정리했습니다.
학생·교강사·운영자가 함께 사용하는 LMS에서는 역할과 과정 상태에 따라 같은 기능도 접근 가능 여부와 표시 정보가 달랐습니다. 화면마다 조건문을 추가하면 권한 정책과 캐시가 서로 다른 기준을 갖기 쉬웠습니다.
제한 시간 평가는 새로고침과 뒤로가기에서 답안·현재 문제·남은 시간이 어긋날 수 있었고, 영상은 이용 증가에 따라 비용과 재생 문제가 함께 커졌습니다. 프로젝트·평가·실습에는 10GB가 넘는 파일도 사용됐습니다.
권한과 평가 규칙을 개별 화면의 예외 처리가 아니라 역할·서버 상태·로컬 작성 상태의 경계로 관리하고자 했습니다.
영상과 업로드는 정상 경로뿐 아니라 브라우저 자원, 네트워크 실패와 화면 전환까지 고려해 운영 가능한 구조로 만드는 것을 목표로 했습니다.
ADMIN, COORDINATOR, PROFESSOR, CONTENT_MANAGER, CAREER_MANAGER, CREW, MEMBER를 중앙 설정에서 관리했습니다. 사용자 정보의 roles 배열과 우선순위를 기준으로 최초 진입 경로를 정하고, 보유 역할 사이를 명시적으로 전환하게 했습니다.
공통 RoleGuard와 역할별 query cache를 적용했습니다. 다만 프론트엔드의 화면 진입 제어이며 모든 API의 최종 인가를 대신한다고 확대하지 않았습니다.
평가 시작 여부와 제한 시간은 서버의 startedAt, timeLimit과 status를 기준으로 계산했습니다. 시작 API 성공 뒤 문제를 열고, 화면 타이머는 표시를 갱신하면서 서버 상태를 주기적으로 다시 동기화했습니다.
작성 중인 답변과 문제 위치는 같은 기기에서 복원할 수 있게 했습니다. 서버 자동 저장이나 파일 답안까지 완전히 복원하는 구조는 아니라는 경계도 함께 두었습니다.
ffmpeg WASM 변환 POC로 구현 가능성과 브라우저 자원 비용을 확인했습니다. 긴 변환 시간과 자원 점유를 업로더에게 전가하지 않기 위해 운영안에서는 제외했습니다.
인프라 담당자가 MediaConvert·S3·CloudFront를 구성하고, 프론트엔드는 변환 상태와 결과 URL을 제품 흐름에 연결했습니다. 완료 전 MP4, 완료 후 HLS를 재생하는 fallback과 공통 플레이어 인터페이스를 구성했습니다.
영상 A에서 B로 이동한 뒤 두 contentId의 진도 요청이 함께 발생하는 문제를 Network payload로 재현했습니다. 대상이 바뀌면 이전 interval을 정리해 현재 영상 요청만 남도록 수정했습니다.
10MB 미만은 일반 업로드, 그 이상은 Multipart로 분기했습니다. 1GB 초과 파일은 part 수가 약 500개 이내가 되도록 크기를 계산하고, 실패한 part만 새 서명 URL로 최대 4회 재시도했습니다.
복잡한 프론트엔드 문제는 조건문의 수보다 역할, 서버 상태, 로컬 작성 상태와 자원 생명주기의 경계가 불분명할 때 커졌습니다.
기술 선택에서도 구현 가능성만 보지 않고 비용이 사용자·브라우저·서버·운영 조직 중 어디에 발생하는지를 함께 판단하게 됐습니다.
기존 사용자의 ID와 비밀번호를 유지하면서 계정을 통합하고, 첫 전환 실패를 빠르게 되돌린 뒤 신원과 서비스 데이터의 책임을 다시 나눴습니다.
홈페이지·LMS·백오피스의 계정 체계를 통합하면서 기존 사용자가 비밀번호를 다시 설정하거나 새로운 사용자로 인식되지 않게 해야 했습니다.
이메일이 같아도 동일인이라고 단정할 수 없는 충돌 계정이 있었고, 잘못된 자동 병합은 다른 사용자의 데이터 접근으로 이어질 수 있어 편의보다 정확성과 복구 가능성이 중요했습니다.
사용자 신원과 서비스별 역할·수강 데이터를 분리하면서 기존 ID와 비밀번호 hash를 보존하는 것을 목표로 했습니다.
새 인증 경로에서 문제가 생겨도 기존 로그인으로 빠르게 돌아갈 수 있는 실제 복구 경로를 유지하고자 했습니다.
통합 계정은 신원, credential, social identity와 본인인증을 소유하고 각 서비스는 역할·수강 정보 같은 도메인 데이터를 소유하도록 나눴습니다.
소셜 계정은 이메일이 아니라 provider와 providerUserId 조합으로 식별하고 credential·role·탈퇴 이력을 관계 모델로 분리했습니다.
기존 사용자 UUID와 비밀번호 hash를 보존하고, 레거시 로그인 proxy와 accounts native 인증 경로를 함께 유지했습니다.
이메일만으로 동일인임을 증명할 수 없는 계정은 자동 병합하지 않고 별도 확인 대상으로 분리했습니다.
첫 v2 전환에서 신규 LMS 사용자의 token 생성이 accounts가 소유하지 않는 서비스 정보에 의존해 실패했습니다. 장애 상태를 유지한 채 전면 수정하지 않고 개발팀 접수 후 약 1시간 안에 v1으로 복구했습니다.
이후 인증 서버에 LMS 데이터를 결합하지 않고 신원 확인 뒤 각 서비스가 필요한 컨텍스트를 조회·생성하도록 보완해 재전환했습니다.
인증 전환은 로그인 화면 교체가 아니라 사용자 식별 경계, 데이터 소유권과 실패 복구를 함께 설계하는 문제였습니다.
정상 전환만큼 token 수명과 폐기, 추적 가능한 감사 로그 같은 운영 안전장치를 단계적으로 갖춰야 한다는 기준을 얻었습니다.
LMS 안에서 사라지던 TIL을 공개 성장 기록으로 바꾸는 문제를 제안하고, 약 한 달 안에 기록·발견·축적·공유가 이어지는 제품을 출시했습니다.
기존 LMS의 TIL은 로그인한 과정 참여자만 볼 수 있어 수료 후 외부 포트폴리오나 지속적인 학습 기록으로 연결되기 어려웠습니다.
정해진 기획을 전달받은 프로젝트가 아니어서 어떤 사용자의 문제를 해결할지, 한 달 안에 어디까지 구현해야 제품 가치가 완결되는지, 출시 후 무엇으로 사용 여부를 판단할지 직접 정해야 했습니다.
기록이 피드에서 발견되고 프로필에 축적되며 성장과 관심사를 시각화한 뒤 URL로 공유되고 다시 기록으로 돌아오는 제품 루프를 목표로 정했습니다.
제품 기획과 애플리케이션 코드는 직접 담당하고 초기 큰 레이아웃과 배포 환경은 각 담당자에게 요청해 약 한 달의 일정 안에서 범위를 완결하고자 했습니다.
글 작성·상세, 피드, 프로필, 활동 히트맵, 태그 그래프, 좋아요·댓글과 검색을 초기 범위로 정했습니다. 2026년 2월 중순 데이터 모델부터 API와 사용자 화면을 구현해 3월 말 공개 준비와 사용자 보정을 마쳤습니다.
스트릭, 집중 타이머와 SSO 전환은 최초 MVP 이후의 기능으로 시간순서를 분리했습니다.
Tiptap 입력과 Markdown 저장을 결합하고, 붙여넣은 Markdown은 marked에서 HTML, ProseMirror DOM parser로 변환했습니다.
단순 표는 GFM으로 저장하고 이미지 속성과 복잡한 표는 HTML fallback으로 보존했습니다. 완전한 round-trip을 보장한다고 표현하지 않고 저장 형식의 차이를 명시적으로 다뤘습니다.
뒤로가기로 작성 내용이 사라졌다는 피드백 뒤 기존 Post의 unpublished 상태를 재사용해 변경된 본문을 약 30초마다 저장하고 프로필에서 다시 열게 했습니다.
debounce나 실시간 저장이 아니며 빈 본문과 업로드 전 blob 이미지에는 현재도 보완할 경계가 남아 있습니다.
동적 sitemap, metadata·canonical, Person·BlogPosting·BreadcrumbList JSON-LD와 crawler policy를 구현했습니다. Search Console의 노출·클릭과 GA4의 제품 행동·source/medium을 분리했습니다.
18주 누적 검색 노출 70,841회와 클릭 3,224회, WAW 3~11명을 운영 지표로 확인했습니다. 생성형 AI 노출 704회는 방문으로 환산하지 않았고 AI Overview는 한 사례로만 기록했습니다.
제품을 주도한다는 것은 기능 수가 아니라 문제를 정의하고 제한된 시간 안에서 가치가 완결되는 범위를 선택하며, 공개 이후 행동을 다음 판단으로 연결하는 일이었습니다.
검색 노출·클릭·WAW·제품 행동을 서로 다른 신호로 관리하고 확인하지 못한 인과관계는 성과로 주장하지 않는 기준을 갖게 됐습니다.
Lighthouse에서 시작해 Network와 Performance로 병목을 좁히고, 정적 배포에 맞는 미디어 변환·fallback·검색 기본기를 구성했습니다.
외주 PHP 홈페이지를 사내에서 유지·확장하기 어려웠고, 시각 콘텐츠 비중이 큰 첫 화면의 대용량 미디어가 렌더링을 지연시키고 있었습니다.
정적 배포 구조에서 콘텐츠 담당자의 작업 흐름을 유지하면서 미디어를 가볍게 만들고 검색엔진이 페이지 정보를 이해할 수 있는 기본 구조도 정리해야 했습니다.
조직이 유지할 수 있는 React·Next.js 기반으로 프론트엔드를 재구축하고, 체감이 아니라 같은 조건의 측정값으로 성능 개선 대상을 선택하고자 했습니다.
별도 runtime 변환 서버 없이 배포 전에 미디어를 변환하고 브라우저 호환 fallback을 제공하는 운영 흐름을 목표로 했습니다.
Lighthouse로 LCP를 확인하고 Network에서 초기 화면의 대용량 GIF를 병목 후보로 좁혔습니다. Performance로 다른 렌더링 병목을 함께 확인한 뒤 미디어 형식을 우선 개선했습니다.
대표 파일을 WebM으로 변환하고 MP4 fallback을 두었습니다. 현재 hero source에도 WebM 우선·MP4 fallback 구조가 남아 있습니다.
별도 변환 서버 대신 FFmpeg CLI로 GIF·MP4를 H.264 MP4와 VP9 WebM으로 만드는 배포 전 도구를 구성했습니다.
설치, 출력 형식, 품질 조정, 배치 처리와 문제 해결 방법을 문서화해 콘텐츠 작업자가 같은 규칙을 반복 적용할 수 있게 했습니다.
sitemap, robots.txt, 페이지별 title·description과 OG 이미지를 정비했습니다. 전환 추적 스크립트와 GTM 기본 POC를 연결하고 설정 방법을 마케팅 담당자에게 안내했습니다.
canonical·JSON-LD·AEO/GEO나 결제 퍼널 전체를 이 프로젝트의 개인 성과로 확대하지 않았습니다.
브라우저 성능은 코드만이 아니라 첫 화면 자원, 렌더링 순서와 콘텐츠 제공 흐름을 함께 측정해야 하는 문제였습니다.
이후에는 단일 최고 점수보다 측정 대상과 조건을 먼저 고정하고, 공통화도 외형보다 변경 주기와 요구 수렴 정도를 기준으로 판단하게 됐습니다.
엑셀·전화·외부 캘린더에 흩어진 모집 업무를 지원자의 상태·이력·담당자·다음 행동이 이어지는 하나의 운영 흐름으로 바꿨습니다.
모집 담당자는 전화로 상담하고 결과를 엑셀에 기록했으며 일정은 외부 캘린더에서 관리했습니다. 정보가 담당자별 문서에 흩어져 이전 상담과 다음 행동을 이어보기 어려웠습니다.
같은 지원자도 현재 단계와 연락 가능성에 따라 필요한 행동이 달랐습니다. 기존 문서를 화면으로 옮기는 것만으로는 예외 상태와 인수인계 문제를 해결할 수 없었습니다.
지원자 한 명의 유입부터 과정 합류까지 현재 상태, 변경 이유, 담당자와 다음 행동을 추적할 수 있는 운영 도구를 만들고자 했습니다.
운영자가 같은 조회 조건과 상세 맥락을 동료에게 넘길 수 있도록 화면 상태와 서버 조회 상태를 함께 구조화했습니다.
엑셀의 한 행을 복제하지 않고 캠페인, 지원자, 담당자, 모집 상태와 이력, 상담 예약, 체크리스트와 과정의 관계로 정의했습니다.
정상 퍼널과 별도로 재컨택·대기/관리·부재/관리처럼 다음 행동이 다른 예외 상태를 분리했습니다. 현재 구조는 5개 상위 단계와 27개 상태 라벨로 업무 상황을 표현합니다.
신청서·캠페인·담당자·체크리스트·상담 일정·상태와 메모 이력을 지원자 상세에서 이어보게 했습니다. 예약 현황과 입력 영역을 인접 배치해 빈 시간을 본 흐름에서 상담을 등록하게 했습니다.
상태 변경 시 사유·알림 여부·첨부를 함께 전송하고 성공 후 지원자와 이력 query를 갱신했습니다. 상태 변경의 맥락과 일반 메모는 서로 다른 운영 기록으로 다뤘습니다.
검색어·기간·단계·복수 상태·page를 URL로 표현하고 전체 query string을 TanStack Query key에 포함했습니다.
동료가 같은 화면과 조건을 공유할 수 있게 했지만 일부 필터 변경에서 page가 초기화되지 않는 사용성 과제도 남겼습니다.
백오피스 복잡도는 화면 수보다 운영 규칙과 예외 상태에서 발생했습니다.
모든 기능을 완성도 높게 확장하기보다 실제 사용자 수와 우선순위를 고려해 사용자-facing 프로젝트를 먼저 선택했고, 일부 필터 개선은 기술 부채로 남겼습니다.