허동영 프론트엔드 개발자 포트폴리오 (상세)

2025.07.11

1. 소개

안녕하세요. 프론트엔드 개발자 취업을 준비 중인 허동영입니다.

저는 사용자 경험과 유지보수성을 고려한 구조적이고 안정적인 프론트엔드 개발을 지향합니다.

  • 폴더와 파일의 구조 및 이름만으로 각 요소의 역할과 기능을 직관적으로 파악할 수 있도록 구성합니다.

  • 외부 라이브러리나 블로그에서 소개된 방식을 도입할 때는 프로젝트에 맞는지, 도입 전·중·후 비용을 고려해 판단합니다.

  • 예외가 발생할 수 있는 로직에는 가능한 모든 예외 처리를 사전에 구현합니다.

  • 적절한 추상화를 통해 모듈이나 함수를 사용할 때 내부 로직을 유추하거나 불필요한 설정 없이 활용할 수 있도록 설계합니다.

  • 제네릭, 타입 가드, 오버로딩 등을 활용해 유연하고 정교한 TypeScript 기반 타입 설계를 구현합니다.


2. 프로젝트 소개

2.1. Next.js 풀스택 블로그 플랫폼 서비스 (15 AppRouter, 개인)

개요

기획 + 디자인 + FE + BE 를 전부 담당한 개인 프로젝트입니다.

네이브 블로그, 카카오 티스토리, velog 에서 개인적으로 느낀 부족함을 보완하고, Next.js의 특화된 기능(서버 컴포넌트, 서버 fetch, 내부 캐싱 로직)을 활용하여 SEO에 최적화된 블로그 플랫폼 서비스를 구현하는 것이 목표입니다.

[블로그 서비스 루트 페이지 이동](/)

[지원자 블로그 홈](/@vavoya)


기술

  • **Next.js 15**

  • **React 19**

  • **TypeScript**

  • **Zustand**

  • **React Query**

  • **Sass (SCSS)**

  • **Vitest + Testing Library**

  • **MongoDB**

  • **next-auth**


주요 특징

  1. **블로그 백오피스에 창 관리 시스템을 도입**

    대부분의 블로그들은 하나의 페이지에 하나의 작업 만을 지원하기에 여러 페이지를 켜놓거나 왔다갔다해야하는 부분이 있습니다. 그래서 저는 블로그 관리라는 것을 하나의 작업 단위로 보고, 사용자가 여러 일반적인 운영체제 처럼 블로그 자체를 하나의 파일 시스템 처럼 다룰 수 있게 만들었습니다.

    창 관리 시스템은 C++의 윈도우 프로시져의 명령형 방식을 React에 맞게 만들었으며, WindowProvider와 WindowLayout이라는 컴포넌트에 의해 제어됩니다.

    윈도우 포커싱 여부, 윈도우 크기 조절, 윈도우 위치 조절, 윈도우 z-index 순서 조절이 가능합니다.

  2. **비동기 작업들의 동기화를 관리하는 모듈**

    DB에는 사용자 별로 버전이 기록되어있고, 사용자는 백오피스로 여러 요청을 병렬로 생성할 수 있습니다. 이때 비동기 작업들을 동기화 해주지 않으면 버전 충돌이 발생합니다.

    이를 해결하기 위해 비동기 작업(fetch를 포함한) 콜백 함수를 순차처리하고, 새로운 버전 정보를 다음 비동기 작업 콜백에 전달해주는 비동기 작업 관리자 모듈을 만들었습니다.

    이 모듈은 ESM 모듈 캐싱을 활용하여 유사 싱글톤으로 동작하게 했습니다.

    또한 비동기 작업에 대한 성공과 실패에 따른 비동기 작업의 return과 각 콜백 함수의 param의 타입을 연결하기 위해 내부적으로 제네릭과 타입 가드를 활용하여, 타입 안정성을 챙겼습니다.

  3. **공통 API, 내부 API 분리**

    Nextjs는 서버컴포넌트, 서버에서 호출 가능한 fetch가 따로 존재하고 fetch 캐시도 존재합니다.

    따라서, 서버 내부에서만 호출 가능한 API를 api/server에 따로 관리하고 있으며, 해당 API에 접근하기 위해서는 HTTP 헤더에 *암호학적으로 안전한 32바이트 난수를 생성하고, 이를 base64로 인코딩한 문자열*으로 생성된 키를 입력해야하고, 미들웨어에서 환경변수에 기록된 키랑 비교해서 내부 API 접근을 제어합니다.

  4. **기능별 폴더 분리**

    서버에 요청하는 fetch, 내부의 서비스로직, DB에 접근하는 data-access 로직들은 향후 유지보수의 편의를 위해 프로젝트의 루트에 따로 분리해두었습니다.

  5. **Toast 함수 추상화**

    Toast를 구현하는 방법에는 전역 상태 또는 내부 상태 + createPortal을 이용한 방법이 있습니다.

    이러한 방법은 추가적인 설정 코드가 필요하기에, 본 프로젝트에는 단순 함수 호출로 메시지만 전달해도 Toast가 렌더링 되도록 했습니다.

    createRoot와 ESM 모듈 캐싱을 활용하여 단일 인스턴스를 유지하고, popEvent가 발생하면 Toast VDOM은 제거되며, useToast로 반환된 addToast 함수에 string을 전달하는 것 만으로 Toast가 렌더링되게 했습니다.

  6. Chrome Lighthouse 점수

    블로그 페이지를 Lighthouse 한 결과, 퍼포먼스 100, SEO 100, 접근성 92가 나옵니다.

    BestPractice는 유튜브 영상이 들어갈 경우 쿠키 관련해서 점수 차감이 있습니다.

    ![222](https://lh3.googleusercontent.com/d/18_44r49OfcOreP6_YkmyNWrqNrZJa8ay)222

주요 동작 시연 영상

백오피스 폴더 CRUD

[백오피스 폴더 CRUD](https://www.youtube.com/watch?v=7wFrWxA7SMw)


백오피스 시리즈 CRUD

[백오피스 시리즈 CRUD](https://youtu.be/SwSVdyQCm_E)


그 외 백오피스 기능

[그 외 백오피스 기능](https://youtu.be/4kyABKAPXpk)


프로젝트 구조 요약

📁 fetch              # API 요청 모듈. client/server/utils로 세분화  
📁 services           # 서비스 계층. 비즈니스 로직 및 유효성 처리 담당  
📁 data-access        # DB 접근 계층. 콜렉션별로 디렉토리 분리되어 있음  
📁 app                # Next.js App Router 구성. 라우팅과 페이지 진입점 관리  
📁 components         # 재사용 UI 컴포넌트. 모달, 사이드바, 마크다운 렌더러 포함  
📁 hook               # 커스텀 훅. 비동기 큐, 바깥 클릭 처리 등  
📁 lib                # 싱글톤 유틸, MongoDB 클라이언트, 로거 등  
📁 store              # 전역 상태 관리 (Zustand 기반)  
📁 utils              # 범용 유틸리티 함수  
📁 validation         # client/server 입력값 유효성 검증  
📁 const              # 도메인/라우팅 단위 상수 관리

각 디렉토리는 데이터 흐름(fetch → service → data-access)과 관심사(app, components, utils 등)의 명확한 분리를 바탕으로 구성되었습니다. 기능 추가 및 유지보수 시 수정 범위 최소화와 테스트 용이성을 고려했습니다.



기타

vitest, react test 라이브러리를 활용한 테스트 코드가 작성되어있습니다.

FE는 백오피스의 폴더 CRUD 시나리오 보장을 위한 통합 테스트 코드가 작성되어있습니다 (api 모킹)

BE는 전체적인 서비스 로직의 보장을 위해 services, data-access에 대해 단위 테스트 코드가 작성되어있습니다.


구글 서치 콘솔에 sitemap 자동 생성 페이지 링크를 연결해 두었습니다.


설정된 프로젝트 범위에 대한 커버리지는 아래와 같습니다.

![222](https://lh3.googleusercontent.com/d/1jHkLU8LvdR2Ut9WltMkBp2bsrkN7On4a)222


FE 부분으로 테스트 코드가 부족하지만, 이어서 통합 테스트로 주어진 시나리오에 맞춰 대부분 진행할 예정입니다.

추가적으로, ~~네이버 로그인 방식으로 직접 로그인하여 서비스를 이용해보실 수 있습니다. (데이터 조작을 분당 10회 초과하면 HTTP 429 로 1분 동안 차단됩니다.)~~ DB값으로 회원가입을 막아두었습니다.

*실제 서비스로 배포 중인 프로젝트라서 프로젝트는 비공개 처리 되어있습니다. 특정 소스코드 요청시 제공하겠습니다.*


2.2. Softeer 부트캠프 팀 프로젝트 (팀플)

개요

현대차 소프티어 5기 2025.02에 진행한 팀프로젝트입니다.

팀 FE로 진행했습니다.

본 프로젝트는 대학 통학 버스를 여러명이서 예매하고, 인원 충족시에 예매가 성공되는 서비스입니다.

![딩동이미지](https://lh3.googleusercontent.com/d/1V-jJ5Kzwi3MW1E92ZxVGNstP5oE9HkpU)딩동이미지


기술

  • **React 19**

  • **TypeScript**

  • **React Query**

  • **StyledComponent**


주요 특징

  1. **WebSocket을 활용한 실시간 상태 변경**

    예매 시스템, 버스 서비스를 제공하기에 실시간 통신과 그에 따른 UI제어가 필요한 서비스입니다.

    이를 위해, WebSocket을 통해 상태에 따른 flag를 전달받으면 그에 따른 UI 변경과 Toast를 렌더링합니다.

    또한, 실시간 버스 위치를 전달받아 UI에 반영합니다. 이때, 좌표 실수 값을 문자열로 전달하면 수십바이트가 소모됩니다. 그래서, 서버에서 실수를 바이너리로 인코딩 하고, 그것을 클라이언트가 실수로 디코딩하는 방식으로 데이터 전송 크기를 줄였습니다.

  2. **React Query 캐시 적극 활용**

    React Query는 캐시를 클라이언트에서 조작할 수 있습니다. 이 기능을 활용하여 서버 통신의 성공 시, 새로 데이터를 받아오지 않고 클라이언트에서 직접 수정하여 빠르게 데이터를 반영합니다.

    무한 스크롤에도, **데이터 수정에 따른 DB의 상태를 추측할 수 있는 경우**라면, 데이터의 일부분만 수정하는 것으로 빠른 UX를 고려했습니다,

  3. **미들웨어 기반 에러 핸들링 및 UX 최적화**

    페이지 접근 제어, 네트워크 오류 등 다양한 에러를 미들웨어에서 사전 감지하고, Custom React Router Framework의 기본 throw 방식 대신 return 기반으로 전달하여 dataStrategy.tsx에서 **중앙 집중식으로 처리**합니다.

    에러는 기능 단위(middleware, network, reservation 등)로 **모듈 분리**되었고, 각 에러는 `CustomError` 클래스를 통해 생성되어 **instanceof 기반 식별**이 가능합니다.

    모든 에러 핸들러는 index.tsx에서 통합 관리되며, `executeErrorHandler`로 자동 실행됩니다.

    에러 발생 시에는 **navigate로 이동하지 않고**, URL은 유지한 채 **모달을 띄워 에러 메시지를 표시**해 사용자 흐름을 유지했습니다.

  4. **결제 페이지 세션 보호 구조**

    결제 페이지는 직접 URL 접근이나 새로고침으로 인해 비정상적인 진입이 발생할 수 있습니다. 이를 방지하기 위해, 페이지 요청이 오면 미들웨어에서 서버에 결제 토큰의 유효성을 먼저 검증합니다. 서버는 HttpOnly 쿠키에 저장된 토큰을 확인하고, 유효하지 않은 경우에는 결제 페이지 접근을 차단합니다.

    토큰이 유효한 경우에만 페이지 진입이 허용되며, 이후 클라이언트는 sessionStorage에 저장된 결제 상태를 기반으로 결제 흐름을 복원합니다.

    이렇게 서버에서 진입 조건을 판단하고, 클라이언트에서는 흐름만 유지하도록 역할을 분리해 새로고침이나 비정상 접근 상황에서도 안정적인 결제 처리가 가능하도록 구성했습니다.


주요 동작 시연 영상



기타

본 프로젝트를 진행하면서 얻은 경험들이, 앞선 1번 블로그 플랫폼 서비스에 많은 도움이 되었습니다.

(React Query, 깃헙 관리 등)


2.3. React를 직접 구현하고 Taskify 페이지 제작 (9일 React, 4일 페이지 제작, 개인)

개요

바닐라 JS로 Taskify 페이지를 제작하는 과제가 주어졌었습니다. 단독으로, 주어진 과제에 더 나아가서 React를 직접 구현해보고, 그것을 기반으로 페이지를 제작했습니다.

![딩동이미지](https://lh3.googleusercontent.com/d/1tJHnCjwYcqeozNY_hxaDa0jBC0-jR161)딩동이미지


기술

  • **JavaScript**


주요 특징

  1. **React 렌더링 사이클 구현**

    React의 시작인 createRoot, render 부터 렌더 단계, 커밋 단계의 로직을 직접 구현해보았습니다.

    여기서 만들어진 React는 setState에 의한 리렌더링으로, 해당 컴포넌트부터 VDOM을 생성과 동시에 비교를 진행하며, DOM 조작을 따로 큐에 쌓은 다음에, 커밋 단계에서 일괄 적용됩니다.

  2. **React 생명주기 구현**

    컴포넌트의 렌더링, useEffect의 콜백 함수와 클린업을 React의 생명주기에 맞춰 구현했습니다. useEffect 관련 함수는 커밋 단계에서 DOM 조작과 우선순위에 맞게 실행됩니다.

  3. **setState 배치처리 구현**

    React의 setState는 실행 즉시 적용되는 것이 아닌, 작업을 모두 모은 다음에 한번에 처리합니다.

    이것을 구현하기 위해, js 작업의 매크로와 마이크로 큐 개념을 활용했습니다.

    일반적으로 setState는 이벤트 리스너로 실행되고, 해당 작업은 매크로 작업으로 처리 됩니다. 여기서 setState를 Promise를 통해 마이크로 작업으로 등록합니다. 그러면 이벤트 리스너(매크로 작업)이 끝나면 js 엔진에 의해 자연스레 모든 setState가 마지막에 한번에 적용됩니다.

  4. **React node & hook & key 구현**

    React Node 객체를 유사하게 따라했으며, 여기서 React는 hook을 node 내에 연결리스트로 처리하지만, 저는 개발 편의성을 위해 배열로 처리하고 index를 전역으로 두게 했습니다. hook에 대한 index가 전역이면 위험하다고 생각할 수 있으나, js 자체가 기본적으로 싱글 스레드이고, 매번 0으로 초기화를 시켜서 안전하다고 생각됩니다.

    useEffect, useRef, useState, createPortal 을 구현했습니다.

  5. **이벤트 위임**

    Js weakMap을 활용하여 노드 자체를 key, 콜백 함수를 value로 하여 루트 노드에서 weakMap을 읽으며 이벤트 처리를 가능하게 했습니다.

    weakMap의 GC 기능을 활용하여 메모리 누수 방지를 js엔진에 맡겼습니다.


기타

아래 링크로 깃헙 프로젝트 이동합니다.

[깃헙 이동](https://github.com/vavoya/vanillaJsReact_Taskify)


아래 링크로 구현 페이지로 이동합니다.

[구현 페이지 이동](https://vanilla-js-react-taskify.vercel.app/)



2.4. Obsidian 스타일 Markdowon -> ast 파서 라이브러리 제작 (개인)

개요

2.1. 블로그 플랫폼 서비스의 텍스트 에디터에 적용하기 위한 용도로 개발된 파서 입니다.

Commonmark와 깃헙 마크다운이 아닌, Obsidian 문법을 파싱하기 위한 용도로 개발되었습니다.

현재 NPM에 테스트 버전으로 배포되어있는 상태입니다.

[NPM 이동](https://www.npmjs.com/package/md-ast-parser)

실제로, 블로그 플랫폼에 npm으로 설치하여 이용 중입니다.


기술

  • **TypeScript**


주요 특징

  1. **함수형 프로그래밍 도입**

    본 프로젝트를 진행할 때, FP에 대해서 공부하고 있었습니다. 따라서, 순수 함수와 불변성을 인지하고, 나름의 해석으로 프로젝트를 진행했습니다.

    완전 순수 함수를 하면, AST 트리 생성에 불필요한 리소스 낭비가 존재하기에, 외부적으로 계산 함수(순수 함수)로 인지되는 것을 기준으로 했습니다. (ex. 얕은 복사)

  2. **지원 문법**

    CommonMark 기준, html 태그를 제외하면 대부분 문법이 적용됩니다.

  3. **파싱 방법(토큰화 or AST 트리)**

    • 마크다운 블럭: 이전 라인(블럭)과 현재 라인을 prefix(접두어) 방식 객체 매핑으로 상태 전이를 통해 결정됩니다.

    • 마크다운 인라인: JS 정규 머신(상태 머신)을 활용하여, 일반 문자열과 링크(alt에 스타일 가능)를 1차적으로 토큰화하여 분리하고, 토큰들을 다시 상태 머신(스타일)으로 토큰화를 진행합니다.

    이후, 토큰을 순차적으로 읽으면서 텍스트 스타일 객체(AST 노드)를 생성합니다.

  4. **LRU 캐시 적용**

    블럭은 여러 줄을 고려해야하기에 캐싱이 어렵지만, 인라인은 똑같은 내용이면 똑같은 ast 트리를 반환하는 순수 함수 형태를 띄우기에, 내부적으로 모든 인라인은 LRU 캐시를 활용합니다.

    코드 블럭의 스타일 파싱이 성능 테스트 결과 큰 부하가 걸리기에, 이 또한 LRU 캐시가 적용되었습니다.


기타

테스트 코드가 작성은 되어 있으나, 이 당시에는 테스트 코드에 대한 개념이 부족하여, 커버해야할 입력 시나리오에 대한 통합 테스트들만 적혀있고 통과된 상태입니다.


아래에서 README를 보실 수 있습니다.

[NPM 이동](https://www.npmjs.com/package/md-ast-parser)


2.5. 대학 동아리 팀 프로젝트 (팀플)

개요

대학생 개발 연합 동아리(UMC)에서 진행한 팀 프로젝트입니다.

2023.06에 진행된 프로젝트입니다.

프로젝트 주제는 매장과 고객을 중개하여 떨이식품을 처리하는 서비스 입니다.

PC는 백오피스, 모바일은 프론트 오피스로 제작되었습니다.

[깃허브로 이동](https://github.com/jaetteoli)


기술

  • **React**

  • **React Native**

  • **StyledComponent**

  • **Redux**


주요 특징

  • **팀 프로젝트를 통해 협업 개발 경험을 처음 쌓은 프로젝트**

    본격적으로 Git을 사용해 브랜치 전략, PR 리뷰, 커밋 컨벤션 등을 처음 접하고 적용한 프로젝트입니다. 각자의 작업 분담을 통해 코드가 병합되는 과정을 경험하며 협업의 중요성과 기본적인 Git 흐름을 경험했습니다.


기타

실전 협업 환경에서 Git 충돌, API 요청 오류 등 다양한 시행착오를 겪었고, 이를 통해 브라우저 캐시, API 요청 흐름, 상태 관리의 중요성 등 프론트엔드 개발 전반에 대한 이해를 넓히는 계기가 되었습니다.

허동영 프론트엔드 개발자 포트폴리오 (상세) - sim-log