Frontend Developer

Jiwon Jeon

GitHub badgeInstagram badge
hocldy@naver.com010-9490-5033
Jiwon Jeon profile

Summary

요약

TypeScript를 중심으로 Next.js와 SolidJS를 사용해 제품을 개발해왔습니다.

빠르게 변화하는 환경에서 필요한 기술을 신속하게 실험하고, 실제 서비스에 안정적으로 적용하는 데 초점을 맞춥니다.

기술 선택과 구현 과정에서 제품과 비즈니스 맥락을 함께 고려하는 것을 중요하게 생각합니다.

Experience

프로젝트 경험

2023.03 - 현재

Front-End Developer @ Audai

VOX Factory

AI 기반 보컬 신디사이저를 위한 DAW-like Electron + SolidJS 앱으로, 음성합성 엔진 기반의 타임라인 오디오 에디터를 개발하고 있습니다. Electron 런타임 아키텍처, 클라우드 프로젝트 구조, 세션 제어, 실시간 오디오·MIDI 레코딩까지 제품의 핵심 기능을 설계하고 개발했습니다. 복잡하게 얽히는 런타임 경계와 상태 흐름을 중앙화된 계약과 관리 구조로 정리해, 기능이 늘어나는 환경에서도 제어 가능성을 유지하는 데 집중했습니다.

홈페이지에서는 제품 소개, 캐릭터/스토어, 플랜, 계정 흐름을 포함한 SolidJS 기반 글로벌 홈페이지를 개발하고 운영하고 있으며, 다국어 라우팅, 측정 동의 체계, 제품/결제 유입 흐름 안정화까지 담당했습니다.

  • 시스템 전반을 지탱하는 타입 시스템 및 상태 계약 설계
    • Electron 빌드 경계를 고려한 타입 모듈 분리
      • Electron 빌드는 웹과 포함 대상이 달라, 런타임 코드에서 타입을 직접 import할 경우 불필요한 코드가 번들에 포함될 수 있었습니다. 또한 분리된 tsconfig 환경에서는 타입 정의 위치와 import 구조에 따라 빌드 경계가 흐려질 수 있었습니다.
      • 여러 영역에서 함께 쓰는 타입은 공용 타입 모듈에 먼저 정의하고, API나 Electron 쪽에서는 이를 참조만 하도록 구조를 정리했습니다. 런타임 코드 파일에서 타입 export를 늘리기보다 타입 전용 모듈을 분리해, Electron 빌드에는 필요한 타입 정의만 포함되고 불필요한 런타임 코드가 따라오지 않도록 import 경계를 관리했습니다. 여기에 ESLint의 no-restricted-imports 규칙까지 걸어 타입 파일이 런타임 코드를 역참조하지 못하게 막아, 구조적 실수를 코드 작성 단계에서부터 방지했습니다.
    • 샌드박스 환경에서 코드베이스의 일관성 확보
      • Electron의 샌드박스 환경은 Renderer와 Main process의 권한을 분리해, Renderer에서 파일 시스템이나 OS 기능에 직접 접근하지 못하도록 제한하는 구조입니다. 이 환경에서는 두 프로세스 간 코드 공유가 불가능해 IPC 채널 정의와 메시지 구조가 분산될 수밖에 없었고, 채널 이름이나 payload가 한쪽에서만 변경될 경우 런타임 불일치가 발생할 수 있었습니다.
      • IPC 채널과 메시지 구조를 공통 타입 기준으로 정리하고, Renderer, Preload, Main process가 동일한 payload 구조를 바라보도록 구성했습니다. 이를 통해 구조 변경 시 불일치를 컴파일 단계에서 검출할 수 있게 했습니다.
    • 프로젝트 전반의 응답 타입 추론 흐름 정리
      • 프로젝트 규모가 커질수록 API 응답, SSE 이벤트, 백그라운드 작업 상태처럼 화면 로직까지 길게 이어지는 값이 많아졌고, 이 연결이 중간에서 끊기면 데이터 구조 변경 시 영향 범위를 추적하기 어려워지고 런타임에서야 잘못된 값 사용이 드러나기 쉬웠습니다.
      • 엔드포인트 정의, 함수 반환값, 화면 로직이 자연스럽게 이어지도록 래퍼와 타입 구조를 정리해 프로젝트 전반에서 응답 타입 추론이 최대한 유지되게 했습니다. 특히 타입 지원이 약한 SSE 라이브러리 구간도 직접 래핑해, 이벤트 payload 구조의 타입 정보가 화면 로직까지 이어지도록 구성했습니다.
    • 에러 메시지와 에러 처리 흐름의 타입 계약 정리
      • 에러 키, 상태 코드, 다국어 메시지, 화면별 에러 핸들러가 서로 분리된 채 관리되면 잘못된 에러 메시지가 노출되거나, 특정 에러에만 맞는 처리 로직이 다른 화면에서 뒤늦게 깨질 수 있었습니다.
      • 에러 키와 상태 코드, 메시지 매핑, 화면별 에러 액션을 타입으로 연결해 어떤 에러에 어떤 메시지와 처리가 붙는지 추론 가능하게 정리했습니다. 이를 통해 공통 에러 처리 흐름을 유지하면서도 화면별 예외 처리를 타입 안전하게 확장할 수 있게 했습니다.
  • Electron 데스크톱 런타임 아키텍처 설계 및 운영 안정성 개선
    • 다중 렌더러 상태 동기화
      • Figma처럼 여러 탭을 독립 렌더러로 다루는 데스크톱 구조가 필요했고, 각 탭의 활성 상태, 프로젝트명, 재생 상태, 테마 변경, 로그아웃 이후 상태가 앱 상단 프레임, Renderer, Main process 사이에서 일관되게 동기화되지 않으면 상태 불일치와 중복 탭 문제가 생길 수 있었습니다.
      • 앱 상단 프레임을 직접 제어하는 구조와 다중 렌더러 구조를 구축하고, Renderer, Preload, Main process 사이의 통신 계약을 정리해 탭 전환, 프로젝트명 동기화, 재생 상태 표시, 테마 반영까지 연결했습니다. 또한 저장된 탭을 다시 열 때 유효한 프로젝트만 복원되도록 검증 로직을 넣어 탐색 연속성을 확보했습니다.
    • macOS 권한 체계와 패키징 제약을 고려한 Electron 런타임 설계
      • macOS에서 녹음 기능을 안정적으로 제공하려면 웹처럼 한 번의 권한 요청으로 끝나지 않았습니다. 시스템 권한 상태 확인과 최초 권한 요청, Electron 보안 격리 환경의 페이지 권한 허용, 실제 장치 접근 실패 처리가 따로 엮여 있었고, 패키징 이후에는 Info.plist와 macOS 권한 설정까지 맞아야 같은 녹음 시작 흐름이 제대로 동작했습니다.
      • 녹음 시작 전에 macOS 시스템 권한을 먼저 확인하고 필요한 경우 직접 요청한 뒤, Electron 쪽 페이지 권한은 별도 handler로 허용하는 흐름으로 정리했습니다. 여기에 Info.plist의 마이크 권한 안내 문구, macOS 권한 설정, 런타임 권한 확인/요청 IPC, 장치 접근 실패 분기까지 함께 맞춰, 마이크·MIDI 접근이 개발 환경과 패키징 환경에서 일관되게 동작하도록 구성했습니다.
    • 운영체제 의존 기능 안정화
      • 오디오 기능 외에도 전체화면 전환, 자동 업데이트, 딥링크 실행, 창 리사이즈 같은 운영체제 의존 이슈가 불안정하면 데스크톱 앱의 완성도에 영향을 줬습니다. 특히 웹/데스크톱 실행 환경이 공존하는 구조에서는 분기 실수 하나로 버그가 생길 수 있었습니다.
      • native fullscreen 상태 추적, 자동 업데이트 중복 다이얼로그 방지, 패키징 이후 딥링크 프로토콜 처리와 창 리사이즈 시 탭 뷰 bounds 동기화까지 정리해 데스크톱 실행 환경의 안정성을 높였습니다.
    • 서비스 도메인을 직접 이해하며 UX 개선
      • 사용자가 오디오를 다른 DAW나 편집 도구로 옮겨 작업하려면 두 프로그램을 계속 오가야 했고, 단순 다운로드 방식으로는 작업 흐름이 끊기곤 했습니다. 또한 Electron 환경에서는 브라우저 UI에서 외부 애플리케이션으로 데이터를 직접 전달할 수 있는 경로가 제한적이었습니다.
      • MIDI 강의를 수강하며 DAW 작업 흐름을 이해하던 중, 작곡가들이 실제로 사용하는 도구에서 오디오를 옮겨 작업하는 방식에서 영감을 얻어 cmd/ctrl + drag로 선택한 오디오를 외부 프로그램으로 전달하는 워크플로우를 제안했습니다. 이후 Electron이 제공하는 drag 이벤트 브릿지를 활용해 파일 생성과 전달을 연결함으로써, 브라우저 UI의 결과를 데스크톱 애플리케이션으로 직접 전달할 수 있도록 구현했습니다.
  • 클라우드 기반 프로젝트 관리 시스템 설계
    • 프로젝트 데이터와 리소스를 분리한 클라우드 저장 구조
      • 초기 구조는 프로젝트를 파일처럼 저장하고 다시 여는 흐름에 가까웠지만, 클라우드 환경에서는 프로젝트 메타데이터와 대용량 오디오 리소스를 같은 방식으로 다룰 수 없었습니다. 단일 파일 중심 구조를 유지하면 저장 비용이 커지고, 리소스 중복 업로드나 다운로드 지연이 누적되며, 프로젝트 관리 기능도 점점 복잡해졌습니다.
      • 프로젝트 데이터와 오디오 리소스를 분리하는 구조로 재편하고, 오디오 리소스는 pre-signed URL로 개별 다운로드하도록 구성했습니다. 이를 통해 API 서버가 대용량 파일 전송을 직접 중계하지 않게 해 응답 지연과 서버 부담을 줄였고, 프로젝트 본문은 먼저 불러오면서 실제로 필요한 리소스만 선택적으로 내려받을 수 있게 했습니다. 저장 시에도 마지막 저장 상태와 현재 리소스를 비교해 실제로 필요한 리소스만 업로드하도록 만들어, 파일 저장 방식에서 클라우드 프로젝트 방식으로 제품 구조를 전환했습니다.
    • 프로젝트 목록 상태 동기화
      • 비동기 백그라운드 작업이 포함된 구조에서는 프로젝트 복제 요청이 먼저 끝나더라도 오브젝트 스토리지에 저장된 오디오 리소스 복제는 아직 진행 중일 수 있었고, 이 상태에서 목록을 다시 불러오면 새 프로젝트는 보이지만 필요한 리소스가 아직 준비되지 않아 복제 직후 열기나 새로고침에서 문제가 생길 수 있었습니다. 여기에 파일 크기와 메타데이터 조회를 한 번에 몰아 보내면 상대적으로 덜 중요한 후속 조회 때문에 더 중요한 API 응답까지 함께 느려질 수 있었고, Electron에서는 탭 상태와 제목 표시까지 같이 맞춰야 해 갱신 흐름이 더 복잡해졌습니다.
      • 프로젝트 리스트 시스템을 별도 매니저로 분리하고, SSE를 통해 백그라운드 작업 완료 신호를 기다린 뒤에만 목록과 탭 상태를 갱신하도록 정리했습니다. 또한 프로젝트 목록에는 열린 세션 정보를 함께 반영하고, 파일 크기와 악기 메타데이터는 동시 요청 수를 3개로 제한한 큐로 뒤에서 보강하도록 분리해 기본 목록 응답과 후속 메타데이터 조회를 나눴습니다. Electron에서는 갱신된 프로젝트명을 상단 탭 제목과 저장된 탭 복원 로직에도 함께 반영해 이름 변경·삭제·복제 이후에도 홈 화면과 탭 상태가 같은 기준을 유지하도록 구성했습니다.
    • 세션 충돌 방지와 연결 복구
      • 하나의 프로젝트를 동시에 한 사용자 세션만 열도록 제어하지 않으면, 서로 다른 세션에서 같은 프로젝트를 수정하면서 저장 충돌이 생기고 열린 상태를 모른 채 삭제·이름 변경 같은 관리 작업이 들어오며 데이터 정합성이 깨질 수 있었습니다.
      • 프로젝트 열기·삭제 같은 주요 동작마다 먼저 세션 상태를 조회해 충돌 가능성을 확인하고, 실제 진입 시에는 WebSocket으로 현재 프로젝트와 토큰을 다시 검증하도록 구성했습니다. 이미 다른 세션에서 열려 있으면 진입을 막거나 기존 세션을 정리한 뒤 이어서 열 수 있게 했고, 연결이 끊겼을 때는 재연결 시도 간격을 단계적으로 늘리는 backoff 전략과 강제 종료 처리를 함께 정리했습니다. 또한 프로젝트 목록에도 현재 열려 있는 세션 정보를 함께 반영해, 단일 사용자용 클라우드 편집기의 세션 제어를 전체 흐름 단위로 안정화했습니다.
    • 페이지 전환 중 오디오 데이터 유지 구조 설계
      • 유저가 홈 화면에서 녹음한 오디오 데이터(audioData 객체)는 다음 화면으로 이동한 뒤에도 클라이언트단에서 그대로 필요했습니다. 하지만 Chrome에서 WebSocket을 사용하는 페이지는 BFCache 복원이 불안정한 경우가 있었고, 화면 전환도 client-side routing 대신 location.href 기반 전체 이동으로 처리하고 있었습니다. 이 구조에서는 메모리에만 저장된 오디오 데이터를 다음 화면으로 안전하게 넘기기 어려웠습니다.
      • 페이지 이동 직전에 오디오 바이너리와 파일명, 추가 파라미터를 IndexedDB에 임시 저장하고, 다음 화면에서 이를 다시 읽어 오디오 트랙 생성, 프로젝트 저장, 후처리까지 이어지도록 구성했습니다. 페이지 전환 방식을 바꾸지 않고도 긴 오디오 입력 흐름을 안전하게 넘길 수 있게 했습니다.
  • 실시간 오디오 처리 시스템 설계
    • AudioWorklet 기반 실시간 레코딩·미터링 파이프라인 구축
      • 레코딩은 입력받은 소리를 오디오 파일로 저장하는 것으로 끝나지 않았습니다. 같은 입력 신호를 기준으로 실시간 모니터링이 이뤄져야 했고, 화면에서는 입력 레벨 표시와 트랙 위 클립 길이 갱신이 함께 맞아야 했습니다. 이 흐름이 어긋나면 meter 값이나 결과 음량을 신뢰하기 어려웠습니다.
      • AudioWorklet 기반 레코더 노드와 미터 노드를 오디오 그래프에 연결하고, processor 코드에서 입력 버퍼를 직접 읽어 각 채널 샘플을 하나의 Float32Array에 번갈아 담아 메인 스레드로 전달하는 구조를 구현했습니다. 메인 스레드에서는 이를 임시 버퍼에 누적한 뒤 녹음 완료 시점에만 최종 오디오 데이터로 조립했고, 같은 입력 흐름 안에서 입력 레벨 표시, 마스터 채널 레벨 계산, 클립 길이 갱신이 함께 맞도록 정리했습니다. 또한 출력 볼륨 제어를 프로젝트 상태와 히스토리에 연결하고, 직접 녹음 전환 시에는 중복 시작을 막는 상태 플래그와 타이머 정리 순서를 함께 맞춰 시작·종료 흐름이 겹치지 않도록 했습니다.
    • 레코딩 상태와 편집 상태가 충돌하지 않도록 흐름 제어
      • 레코딩 기능은 오디오 입력만 받으면 끝나는 것이 아니라, 권한 확인, 카운트인, 클립 생성, 임시 버퍼 누적, 히스토리 처리, 트랙 변경, 루프 구간 처리까지 함께 맞아야 했습니다. 이 흐름이 어긋나면 녹음 중인 클립이 다른 편집 동작에 의해 깨지거나, 트랙을 옮길 때 입력 대상과 UI 상태가 서로 다른 값을 가리킬 수 있었습니다.
      • 권한 확인 이후 카운트인과 녹음 시작 순서를 분리하고, 레코딩 중인 트랙과 클립은 별도로 보호되도록 상태를 관리했습니다. 오디오 레코딩은 임시 버퍼에 데이터를 쌓은 뒤 완료 시점에만 최종 오디오 데이터로 반영하고, 트랙 변경 시에는 새 트랙 기준으로 녹음을 다시 시작하도록 흐름을 정리했습니다. 또한 히스토리에는 녹음 전·중·후 상태가 의도한 단위로 남도록 예외 처리를 넣어 편집 흐름이 깨지지 않게 했습니다.
    • 루프 레코딩 구간의 시간축 정합성 유지
      • 루프 레코딩에서는 재생 위치는 반복 구간의 처음으로 돌아오지만, 실제 녹음 데이터는 계속 이어서 쌓여야 했기 때문에 시간축 관리가 특히 까다로웠습니다. 이 연결이 어긋나면 오디오 녹음본이 루프 경계에서 잘리거나 덮어써지고, 같은 흐름을 따르는 MIDI 데이터도 타이밍이 어긋난 결과가 만들어질 수 있었습니다.
      • 루프가 한 바퀴 돌 때마다 새로 들어온 오디오와 MIDI 데이터를 기존 녹음 결과 뒤에 안전하게 이어 붙일 수 있도록 시간 계산과 상태 갱신 순서를 정리했습니다. 반복 구간을 넘겨 지속되는 음이 중간에서 끊기지 않도록 보완했고, 레코딩 중에는 관련 데이터가 다른 편집 동작에 의해 깨지지 않도록 보호하며 Electron 환경에서도 포커스 흐름을 함께 보완했습니다.
    • 트리플렛·퀀타이즈를 포함한 리듬 그리드 계산 체계 정리
      • DAW-like 편집기에서 사용자가 보는 격자 한 칸은 단순한 UI 선이 아니라 실제 시간 계산 기준이었습니다. 특히 퀀타이즈나 스냅, 트리플렛처럼 박자를 다시 나누는 기능은 음악 이론 개념을 코드로 정확히 옮겨야 해서, 그리드 표시와 노트 이동, 레코딩 시점 계산이 조금만 어긋나도 편집 결과 전체가 어색해질 수 있었습니다.
      • 그리드 해상도를 음표 단위와 시간 단위로 함께 해석하는 구조를 정리하고, 일반 분할과 트리플렛 분할을 같은 계산 체계 안에서 다룰 수 있도록 구현했습니다. 그 과정에서 퀀타이즈와 스냅, 줌 레벨에 따른 자동 그리드 변경, 위치값과 음표 단위 시간값·MIDI 이벤트 시간값 변환 규칙을 함께 맞췄고, 직접 공부한 음악 이론 개념을 실제 편집기 동작으로 연결해 제품에 반영했습니다.
    • 데스크톱 녹음 환경의 권한 및 장치 초기화 안정화
      • 녹음 기능은 브라우저 API 한 번 호출로 끝나는 구조가 아니었습니다. 웹에서는 페이지 권한과 OS 권한이 간접적으로 겹쳐 있었고, Electron에서는 여기에 macOS 시스템 권한과 보안 격리 환경 기반 페이지 권한이 한 단계 더 추가돼, 같은 "녹음 시작" 동작도 실행 환경에 따라 다른 분기와 실패 케이스를 가졌습니다.
      • Electron에서는 시스템 권한 확인·요청과 페이지 권한 허용 흐름을 분리했고, 웹에서는 브라우저 권한 흐름을 별도로 설계했습니다. 여기에 시스템 기본 마이크 선택과 장치 접근 실패, 권한 거부, 기타 예외 케이스까지 분기해, 실행 환경별로 일관된 녹음 시작 흐름을 구성했습니다.
    • 오디오 컨텍스트 생명주기와 Worklet 정리 전략 분석
      • DAW-like 앱에서는 오디오 컨텍스트가 여러 경로에서 생성·교체되기 때문에, 라이브러리가 내부적으로 만든 컨텍스트나 튜토리얼에서 사용한 임시 컨텍스트, 재초기화 과정에서 남는 AudioNode·MessagePort가 누적되면 장기적으로 디버깅과 안정성 관리가 어려워질 수 있었습니다. 특히 겉으로는 소리가 멈춘 것처럼 보여도 실제로 어떤 노드와 포트가 남아 있는지 추적하기 쉽지 않았습니다.
      • Tone.js와 자체 오디오 시스템이 만드는 컨텍스트를 분리해 점검하고, close, disconnect, MessagePort 정리 기준과 필요한 정리 대상 노드를 직접 분석해 팀 문서로 공유했습니다. 실제 코드 수정뿐 아니라 오디오 파이프라인의 생명주기를 추적 가능한 형태로 정리해, 이후 컨텍스트 재초기화와 Worklet 관리 기준을 팀이 공통적으로 이해할 수 있게 만들었습니다.
  • 효율적인 CSS 시스템 설계
    • pre-compiled CSS 환경을 고려한 동적 테마 변수 시스템 설계
      • vanilla-extract 같은 pre-compiled 스타일 체계는 스타일을 컴파일 타임에 확정해 CSS 양과 런타임 비용을 줄이기에는 유리했지만, 그만큼 컴파일 타임에 스타일 규칙이 먼저 고정되기 때문에 런타임에 계산된 색상을 그 규칙 안에 바로 넣기 어려웠습니다. 컬러 시스템도 처음부터 RGB 기반이 아니라 hex 코드 중심으로 짜여 있었기 때문에, 같은 선택 색을 쓰더라도 평소에는 원색으로, 선택 강조 상태에서는 20% 투명도를 섞은 오버레이 색으로 계산해 적용하는 흐름을 단일 hex 변수만으로 처리하기 어려웠습니다.
      • 빌드 시점에는 정해진 CSS 변수 이름만 사용하고, 실제 컬러 테마 값은 런타임에 채워 넣는 구조를 설계했습니다. 이때 #RRGGBB 원본 색과 RR/GG/BB를 정수화한 값을 document root의 CSS 변수로 따로 주입하는 트릭을 고안해, 기존 hex 기반 컬러 시스템을 유지하면서도 합성된 색상값을 바로 계산할 수 있게 했습니다. 이 과정을 정리하던 중 스타일 라이브러리 문서 예제의 잘못된 부분을 발견해 직접 수정 PR을 올리기도 했습니다.
    • 공용 스타일 컴포넌트를 통한 스타일시트 길이와 중복 관리
      • 초기에는 공통화되지 않은 스타일과 컴포넌트가 많았고, 한 화면 안에 들어가는 요소와 상태도 많아 레이아웃과 타이포그래피 스타일을 화면마다 반복해서 작성하면 HTML이 로드하는 스타일시트 양이 빠르게 커질 수 있었습니다.
      • UI 라이브러리의 레이아웃 패턴을 직접 분석해 공용 레이아웃 컴포넌트로 정리했고, Text, Button 같은 공통 컴포넌트는 피그마 디자인 토큰 기준과 용어에 맞춰 다시 구성했습니다. 아이콘 이름까지 피그마와 일치시키며 디자인-개발 간 네이밍 체계를 정리했고, 스타일을 화면마다 새로 쓰기보다 공통 규칙으로 흡수해 CSS 양과 중복을 줄이고 화면이 늘어나도 일관된 방식으로 스타일을 확장할 수 있게 구성했습니다.
  • 글로벌 서비스 랜딩 페이지 개발
    • 글로벌 다국어 라우팅과 locale fallback 일관성 확보
      • 글로벌 제품 사이트 특성상 한국어/영어/일본어 페이지를 URL 단위로 분리해야 했고, 사용자가 루트 경로(/)로 진입하거나 내부 링크를 이동할 때도 현재 언어가 자연스럽게 유지되어야 했습니다. 이 흐름이 흔들리면 영어 페이지에서 한국어 URL로 이동하거나, 한국어로 서비스명을 검색했을 때 원하는 언어 페이지가 잘 노출되지 않는 문제가 생길 수 있었습니다.
      • URL의 언어 prefix를 읽는 로직, 경로 변경 시 언어 상태를 맞추는 로직, 내부 링크에 언어 prefix를 붙이는 규칙을 분리해 정리했습니다. 이를 통해 기본 언어 fallback과 내부 이동 흐름을 일관되게 맞추고, 언어별 문서 식별이 흔들리지 않도록 메타/경로 체계를 유지했습니다.
    • 동의 기반 측정 제어와 consent mode 운영 안정화
      • Clarity, Google Analytics 같은 측정 도구는 도입이 필요했지만, 사용자 동의 이전에 활성화되면 글로벌 서비스에서 규제 리스크가 생길 수 있었습니다.
      • cookie consent 시스템을 도입하고, persistent storage와 consent mode 초기화/재시도 로직을 분리해 측정 제어를 안정화했습니다. 배너 노출, 상태 저장, 초기 실행 순서를 명확히 해 분석 도입과 규제 대응을 동시에 만족시켰습니다.

2021.03 - 2023.02

Front-End Developer @ Rawlabs

에어서플라이

B2B 구매관리 플랫폼을 전반적으로 개발했습니다. 주문, 견적, 스토어, 관리기능 등 여러 도메인이 맞물린 구조를 Next.js + TypeScript 기반으로 운영했습니다.

  • 주문·견적·스토어·관리기능을 아우르는 B2B 플랫폼 전반 개발
    • 단일 서비스 안에 다양한 서비스가 구조여서 화면 수가 많고 도메인 규칙도 복잡했습니다. 여러 성격의 기능이 계속 확장되는 환경에서 구조를 일관되게 유지하면서도 운영 이슈에 빠르게 대응할 수 있어야 했습니다.
    • Next.js와 TypeScript를 기반으로 서비스 전반을 전담하여 개발하고 유지보수했습니다. 여러 성격의 화면을 하나의 제품 안에서 일관된 방식으로 관리하면서도, 실제 비즈니스 변경 사항이 빠르게 반영될 수 있도록 개발 속도와 유지보수성을 함께 챙겼습니다.
  • 번들 크기와 빌드 시간 최적화
    • 서비스가 커지면서 _app.tsx를 포함한 공통 번들에 무거운 라이브러리가 계속 쌓였고, 아이콘 패키지나 문서 출력용 라이브러리처럼 페이지별로만 필요한 코드도 초기 로드에 함께 포함되고 있었습니다. 그 결과 초기 번들 크기와 빌드 시간이 함께 증가해 배포 속도와 화면 진입 성능에 영향을 주고 있었습니다.
    • 번들 분석을 기준으로 트리 쉐이킹이 되지 않는 import 구문을 개별 경로 import로 바꾸고, html2canvas, jspdf, xlsx 계열처럼 무거운 라이브러리는 dynamic import로 분리했습니다. 또한 공통 번들에 실린 모달과 전역 의존성을 정리하고 SWC 기반 minify와 캐시 전략까지 함께 적용해, 번들 크기와 빌드 시간을 모두 줄였습니다.
  • 운영 서버 빌드 의존성을 줄인 Next.js 배포 파이프라인 개선
    • 초기 배포 방식은 운영 EC2에서 직접 Next.js 빌드를 수행하는 구조여서, 배포 중 .next 결과물이 교체되는 동안 화면이 비거나 응답이 흔들리는 문제가 있었습니다. 또한 빌드와 의존성 설치가 모두 서버 자원에 의존해, 운영 환경 안정성과 배포 시간이 함께 영향을 받는 구조였습니다.
    • 빌드 과정을 GitHub Actions로 옮기고, 빌드가 끝난 산출물만 서버로 전달하는 방식으로 배포 파이프라인을 재구성했습니다. 여기에 yarn dependency cacheNext build cache를 함께 붙여 CI 환경의 설치 시간과 빌드 시간을 줄였고, 서버에서는 최신 산출물 반영과 프로세스 재기동만 담당하도록 정리해 배포 중 서비스 공백을 줄였습니다.
  • 불완전한 API 문서를 보완하는 타입 생성 도구 개발
    • 사내 OpenAPI 문서는 엔드포인트와 메서드 정보만 있는 경우가 많아, 프론트에서 필요한 응답 타입과 axios 래퍼 함수를 매번 손으로 작성해야 했습니다. 특히 response schema가 비어 있는 경우에는 실제 API를 호출해 본 뒤 객체 구조를 보고 타입을 다시 옮겨 적어야 해서 반복 비용이 컸습니다.
    • YAML을 붙여 넣으면 엔드포인트별 실제 응답을 호출해 TypeScript 타입과 axios wrapper 코드를 생성하는 사내 도구를 만들었습니다. 배열·중첩 객체 구조를 기반으로 구체적인 타입을 추론하고, 메서드·파라미터·baseURL·헤더 커스터마이징까지 같이 다룰 수 있게 구성해 반복적인 API 연동 작업을 줄였습니다.
  • 신규 사용자 튜토리얼을 실제 가입부터 검증하는 E2E 시나리오 자동화
    • 신규 사용자 튜토리얼은 회원가입 직후부터 시작해 여러 단계를 끝까지 통과해야 했고, 완료 시 실제 상품과 연결되는 흐름이어서 비즈니스적으로도 민감했습니다. 테스트용 상태를 미리 주입하는 방식만으로는 실제 사용자 여정을 충분히 검증하기 어려워, 가입부터 튜토리얼 완료까지 전체 흐름을 반복 가능하게 보장할 장치가 필요했습니다.
    • 초기 상태의 신규 사용자 계정을 실제로 생성한 뒤 튜토리얼 전 과정을 끝까지 수행하는 E2E 시나리오를 작성해, 신규 사용자 온보딩 흐름을 실제 사용자 경로에 가깝게 검증할 수 있도록 만들었습니다. 단순 화면 렌더링이 아니라 회원가입, 권한 부여, 튜토리얼 진행 상태, 최종 상품 노출까지 이어지는 핵심 비즈니스 플로우를 안정적으로 확인할 수 있었습니다.

Skills

기술 스택

  • TypeScript
  • SolidJS
  • React
  • Next.js
  • Electron
  • Web Audio API